beeos-claw 0.6.80 → 0.6.81

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.
@@ -0,0 +1 @@
1
+ import{isInteractiveUiResult,staticHtmlProjectionOf}from"./static-html-result.js";export class HtmlDeliveryGuard{published;projectedFileId;revised=!1;interactive=!1;observe(e,i,t,s){if(s)return;const r=staticHtmlProjectionOf(e,i,t);r&&!this.published&&(this.published=r.result),isInteractiveUiResult(e,i,t)&&(this.interactive=!0)}get publishedFileId(){return this.published?.artifact.fileId}recordReply(e){e.artifact.fileId===this.publishedFileId&&(this.projectedFileId=e.artifact.fileId)}beforeFinalize(e){if(!(this.published||this.interactive||this.revised||e.stopHookActive))return this.revised=!0,{action:"revise",reason:"html_delivery_missing",retry:{instruction:"Continue the previous request. Its required HTML file has not been published. Call render_ui with id, title and self-contained html containing the completed answer. Wait for a successful static_html result. Do not upload the file again. Then give one brief confirmation with no duplicate answer in chat.",idempotencyKey:"beeos-html-delivery",maxAttempts:1}}}assertComplete(){if((!this.interactive||this.published)&&(!this.published||this.projectedFileId!==this.published.artifact.fileId))throw new Error("html_delivery_missing: required HTML file was not published and delivered to this reply")}}
@@ -1 +1 @@
1
- const FILE_ID=/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i,record=t=>null!==t&&"object"==typeof t&&!Array.isArray(t),failed=t=>!0===t.isError||!1===t.success||!1===t.ok||!0===t.timedOut||Boolean(t.error)||"string"==typeof t.status&&"completed"!==t.status;export function staticHtmlResultOf(t,e=0){if(!(e>8)){if("string"==typeof t)try{return staticHtmlResultOf(JSON.parse(t),e+1)}catch{return}if(Array.isArray(t))for(const r of t){const t=staticHtmlResultOf(r,e+1);if(t)return t}else if(record(t)&&!failed(t)){if("static_html"===t.presentation){const e=t.artifact;if(!0!==t.success||!record(e)||"canvas"!==e.kind||"string"!=typeof e.fileId||!FILE_ID.test(e.fileId)||e.uri!==`beeos-file://${e.fileId}`||"text/html"!==e.mimeType||"string"!=typeof e.title||!e.title.trim())return;return{success:!0,presentation:"static_html",..."string"==typeof t.surfaceId?{surfaceId:t.surfaceId}:{},artifact:{kind:"canvas",fileId:e.fileId,uri:e.uri,mimeType:"text/html",title:e.title.trim()}}}for(const r of["result","details","output"])if(void 0!==t[r])return staticHtmlResultOf(t[r],e+1);for(const r of["content","text"]){const i=staticHtmlResultOf(t[r],e+1);if(i)return i}}}}export const isRenderUiToolName=t=>"render_ui"===t||"openclaw:beeos-claw:render_ui"===t;export function staticHtmlProjectionOf(t,e,r){if(isRenderUiToolName(t)){const t=staticHtmlResultOf(r);return t?{result:t,arguments:e}:void 0}if("tool_call"!==t||!record(e)||"string"!=typeof e.id||!isRenderUiToolName(e.id)||!record(e.args)||!record(r)||failed(r))return;const i=r.details;if(!record(i)||failed(i)||!record(i.tool)||!record(i.result)||"openclaw:beeos-claw:render_ui"!==i.tool.id||"render_ui"!==i.tool.name||"openclaw"!==i.tool.source)return;const s=staticHtmlResultOf(i.result);return s?{result:s,arguments:e.args}:void 0}
1
+ const FILE_ID=/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i,record=e=>null!==e&&"object"==typeof e&&!Array.isArray(e),failed=e=>!0===e.isError||!1===e.success||!1===e.ok||!0===e.timedOut||Boolean(e.error)||"string"==typeof e.status&&"completed"!==e.status;export function staticHtmlResultOf(e,t=0){if(!(t>8)){if("string"==typeof e)try{return staticHtmlResultOf(JSON.parse(e),t+1)}catch{return}if(Array.isArray(e))for(const r of e){const e=staticHtmlResultOf(r,t+1);if(e)return e}else if(record(e)&&!failed(e)){if("static_html"===e.presentation){const t=e.artifact;if(!0!==e.success||!record(t)||"canvas"!==t.kind||"string"!=typeof t.fileId||!FILE_ID.test(t.fileId)||t.uri!==`beeos-file://${t.fileId}`||"text/html"!==t.mimeType||"string"!=typeof t.title||!t.title.trim())return;return{success:!0,presentation:"static_html",..."string"==typeof e.surfaceId?{surfaceId:e.surfaceId}:{},artifact:{kind:"canvas",fileId:t.fileId,uri:t.uri,mimeType:"text/html",title:t.title.trim()}}}for(const r of["result","details","output"])if(void 0!==e[r])return staticHtmlResultOf(e[r],t+1);for(const r of["content","text"]){const o=staticHtmlResultOf(e[r],t+1);if(o)return o}}}}export const isRenderUiToolName=e=>"render_ui"===e||"openclaw:beeos-claw:render_ui"===e;export function staticHtmlProjectionOf(e,t,r){if(isRenderUiToolName(e)){const e=staticHtmlResultOf(r);return e?{result:e,arguments:t}:void 0}if("tool_call"!==e||!record(t)||"string"!=typeof t.id||!isRenderUiToolName(t.id)||!record(t.args)||!record(r)||failed(r))return;const o=r.details;if(!record(o)||failed(o)||!record(o.tool)||!record(o.result)||"openclaw:beeos-claw:render_ui"!==o.tool.id||"render_ui"!==o.tool.name||"openclaw"!==o.tool.source)return;const i=staticHtmlResultOf(o.result);return i?{result:i,arguments:t.args}:void 0}export function isStaticHtmlInvocation(e,t){return isRenderUiToolName(e)?record(t)&&Object.hasOwn(t,"html"):"tool_call"===e&&record(t)&&"string"==typeof t.id&&isRenderUiToolName(t.id)&&record(t.args)&&Object.hasOwn(t.args,"html")}export function isInteractiveUiResult(e,t,r){let o=e,i=t,s=r;if("tool_call"===e){if(!record(t)||"string"!=typeof t.id||!record(t.args)||!record(r)||failed(r)||!record(r.details)||failed(r.details)||!record(r.details.tool)||"openclaw"!==r.details.tool.source)return!1;o=t.id;const e=o.replace("openclaw:beeos-claw:","");if(r.details.tool.id!==`openclaw:beeos-claw:${e}`||r.details.tool.name!==e)return!1;i=t.args,s=r.details.result}return record(s)&&void 0!==s.details&&!failed(s)&&(s=s.details),!(!record(s)||failed(s)||!0!==s.success||!record(i))&&("render_form"===o||"openclaw:beeos-claw:render_form"===o?record(s.formUI)&&Array.isArray(s.formUI.fields):isRenderUiToolName(o)&&!Object.hasOwn(i,"html")&&Array.isArray(i.components)&&"panel"===s.presentation&&"string"==typeof s.surfaceId)}
@@ -1 +1 @@
1
- import{extractBeeosConversationIdFromSessionKey}from"../runtime/openclaw-session-key-resolution.js";const A2UI_TYPE_NAMES=new Set(["Text","Image","Icon","Video","AudioPlayer","Row","Column","List","Card","Tabs","Divider","Modal","Button","CheckBox","TextField","DateTimeInput","MultipleChoice","Slider","Chart","WebEmbed","FilePreview","MapView"]);function normalizeComponents(e){return e.map(e=>{if(!e||"object"!=typeof e)return e;const t=e;if(t.component&&"object"==typeof t.component)return t;for(const e of Object.keys(t))if(A2UI_TYPE_NAMES.has(e)&&"object"==typeof t[e])return{id:t.id,component:{[e]:t[e]}};return t})}const A2UI_INTERACTIVE_TYPES=new Set(["Button","CheckBox","TextField","DateTimeInput","MultipleChoice","Slider"]);export const NON_INTERACTIVE_A2UI_ERROR="Display-only A2UI is not supported. Call render_ui again with id, title, and self-contained static HTML in html. A2UI root/components are reserved for interactive buttons, inputs, or component actions.";export function hasInteractiveComponent(e){return e.some(e=>{const t=e?.component;return!(!t||"object"!=typeof t)&&Object.entries(t).some(([e,t])=>A2UI_INTERACTIVE_TYPES.has(e)||Boolean(t&&"object"==typeof t&&t.action))})}export const RENDER_UI_TOOL_NAME="render_ui";export const CLOSE_UI_TOOL_NAME="close_ui";export const CANVAS_COMPONENT_ID_GUIDANCE="Component IDs must differ from non-reference scalar values (usageHint, literalString, and action.name). Use type-qualified IDs such as text_heading, text_body, and column_root; do not use scalar values such as body, h1, or center as IDs. A2UI v0.8 resolves strings matching component IDs as references. Keep root, child, explicitList, and template.componentId references matching their target IDs. Reuse the same component IDs when updating a surface.";export const RENDER_UI_TOOL_DESCRIPTION="Publish a static Canvas result as self-contained HTML using id, title, and html. The result is stored as a BeeOS file and shown in the conversation. Legacy A2UI calls with id, root, and components remain available for interactive experiences.";export const RENDER_UI_TOOL_SCHEMA={type:"object",required:["id"],properties:{id:{type:"string",description:"Surface id. Reuse to update."},title:{type:"string",description:"Human-readable title for a static HTML Canvas result."},html:{type:"string",description:"Complete self-contained static HTML. Maximum UTF-8 size: 5 MiB."},root:{type:"string",description:"Root component id."},components:{type:"array",description:"Flat ComponentInstance list. Each item has id plus one component type.",items:{type:"object",required:["id"],properties:{id:{type:"string",description:CANVAS_COMPONENT_ID_GUIDANCE},component:{type:"object",description:"One type key: Text, Image, Icon, Video, AudioPlayer, Row, Column, List, Card, Tabs, Divider, Modal, Button, CheckBox, TextField, DateTimeInput, MultipleChoice, Slider, Chart."}}}},data:{type:"object",description:"Optional binding model, path keys like /name."}}};export const CLOSE_UI_TOOL_DESCRIPTION="Close and remove a UI surface that was previously rendered.";export const CLOSE_UI_TOOL_SCHEMA={type:"object",required:["id"],properties:{id:{type:"string",description:"The UI identifier to close"}}};const SESSION_READY_POLL_ATTEMPTS=3,SESSION_READY_POLL_INTERVAL_MS=400,delay=e=>new Promise(t=>setTimeout(t,e)),MAX_STATIC_HTML_BYTES=5242880,FILE_ID=/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;async function renderStaticHtml(e,t,n){const a=e=>({success:!1,presentation:"static_html",error:e}),o="string"==typeof n.id?n.id.trim():"",i="string"==typeof n.title?n.title.trim():"",r=n.html;if(!o||!i||"string"!=typeof r||!r.trim())return a("id, title, and non-empty html are required");if("root"in n||"components"in n)return a("html cannot be combined with root or components");if(i.length>160)return a("Canvas title is too long (max 160 characters)");if(Buffer.byteLength(r,"utf8")>5242880)return a("Canvas HTML exceeds the 5 MiB preview limit");if(!e.publishStaticHtml)return a("Canvas file upload is unavailable");try{const n=await e.publishStaticHtml({id:o,title:i,html:r},t);return FILE_ID.test(n.fileId)&&n.uri===`beeos-file://${n.fileId}`&&"text/html"===n.mimeType?{success:!0,presentation:"static_html",surfaceId:o,artifact:{kind:"canvas",fileId:n.fileId,uri:n.uri,mimeType:"text/html",title:i}}:a("Canvas HTML upload returned an invalid file reference")}catch(e){return a(`Canvas HTML upload failed: ${e instanceof Error?e.message:String(e)}`)}}async function ensureRelayConnected(e,t,n,a){const{stateManager:o,logger:i}=e,r=a??e.getGatewaySessionKey?.(n),s=r??t,c=o.resolveCanvasForSession(s);if(c&&o.isCanvasJoined(c))return c;let l=o.getRelayClient();if(l||(l=await o.waitForRelayClient(),l))try{e.waitForAgentSync&&await e.waitForAgentSync();const a=e.getPlatformAgentId?.(n),c=r??t,d=(r?extractBeeosConversationIdFromSessionKey(r):void 0)??(r&&r!==t?t:void 0),u=await l.ensureCanvas(c,{agentId:a,linkSessionId:d});return await o.joinCanvas(u,s),i.info?.(`[canvas] Joined canvas=${u} session=${t} gatewayKey=${r??"none"} agentId=${a??"none"}`),u}catch(e){return void i.warn?.(`[canvas] Failed to join canvas relay: ${e}`)}}export function createRenderUiHandler(e){const{stateManager:t,logger:n,writeObs:a,getGatewaySessionKey:o}=e;let i=0;return async(o,r)=>{const s=r;if(Object.prototype.hasOwnProperty.call(s,"html"))return renderStaticHtml(e,o,s);const c=s.id,l=s.root,d="panel";let u=s.components;if("string"==typeof u)try{u=JSON.parse(u)}catch{}if(!(c&&l&&Array.isArray(u)&&u.length))return{success:!1,error:"id, root, and components (array) are required"};const p=normalizeComponents(u);if(!hasInteractiveComponent(p))return{success:!1,error:NON_INTERACTIVE_A2UI_ERROR};if(!t.validateComponentCount(p))return{success:!1,error:"Too many components (max 500)"};if(!t.checkRateLimit(c))return n.warn?.(`[canvas] Rate limited surfaceId=${c}`),a({component:"canvas",domain:"canvas",name:"canvas.rate_limited",severity:"warn",payload:{surfaceId:c}}),{success:!1,error:"Rate limited — too many updates per second"};const m=s.data;let h=e.getActiveSessionId?.(o);for(let t=0;!h&&t<3;t++)await delay(400),h=e.getActiveSessionId?.(o);if(!h&&e.waitForAgentSync){try{await e.waitForAgentSync()}catch{}h=e.getActiveSessionId?.(o)}if(!h)return n.warn?.(`[canvas] No active session for toolCallId=${o} after 3 polls, render_ui not ready`),{success:!1,retryable:!0,error:"Canvas session not ready yet (agent just connected) — retry render_ui shortly; do NOT fall back to plain text."};const f=e.getGatewaySessionKey?.(o),y=await ensureRelayConnected(e,h,o,f),v=t.has(c,y??void 0),g=Date.now(),C=t.getRelayClient();y&&C&&g-i>6e4&&C.createSnapshot(y,"Pre-agent edit").then(()=>{i=Date.now(),n.debug?.("[canvas] Pre-edit checkpoint created")}).catch(e=>n.warn?.(`[canvas] Pre-edit checkpoint failed: ${e}`)),v?t.update(c,p,y,l,m):t.create(y??`_fallback_${Date.now()}`,c,d,p,{origin:{kind:"agent"},rootComponentId:l,dataModel:m});const b=p.map(e=>e.component&&"object"==typeof e.component?Object.keys(e.component)[0]??"unknown":"unknown");a({component:"canvas",domain:"canvas",name:v?"canvas.update_sent":"canvas.surface_created",severity:"info",payload:{surfaceId:c,presentation:d,componentCount:p.length,componentTypes:b}}),n.debug?.(`[canvas] ${v?"Updated":"Created"} surface=${c} components=${p.length}`);const T={success:!0,surfaceId:c,presentation:d,isUpdate:v,activeSurfaces:y?t.getAllActive(y).map(e=>e.surfaceId):[c]};return m&&(T.dataModel=m),T}}export function createRenderUiToolExecutor(e){const t=createRenderUiHandler(e);return async(e,n)=>{const a=await t(e,n),o=[{type:"text",text:JSON.stringify(a)}];if(!0===a.success&&"static_html"===a.presentation){const e=a.artifact;o.push({type:"resource_link",uri:e.uri,name:`${e.title}.html`,mimeType:e.mimeType})}return{...a,content:o,details:a,isError:!1===a.success}}}export function createCloseUiHandler(e){const{stateManager:t,logger:n,writeObs:a}=e;return async(o,i)=>{const r=i.id;if(!r)return{success:!1,error:"id is required"};const s=t.get(r),c=s?Date.now()-s.createdAt:0,l=e.getGatewaySessionKey?.(o),d=l?t.resolveCanvasForSession(l):void 0;return t.delete(r,d),a({component:"canvas",domain:"canvas",name:"canvas.surface_closed",severity:"info",payload:{surfaceId:r,lifetime_ms:c}}),n.debug?.(`[canvas] Closed surface=${r}`),{success:!0,surfaceId:r,message:"UI closed"}}}export const CANVAS_PRESENTATION_GATE="## BeeOS Canvas presentation gate\n\nCanvas presentation is available in the background for this BeeOS conversation. The Canvas resource tab does not need to be open and no Web subscriber is required before a Canvas tool is called. For static HTML success, say the HTML file is ready and can be clicked to preview; do not claim it was displayed on Live Canvas or that the user is viewing it.\n\nDo not read `beeos-canvas/SKILL.md` or any canvas skill file. Call `render_ui` directly. The A2UI schema is in this prompt and the `render_ui` tool.\n\nAfter any skill or data tool returns, apply this gate before writing the final reply:\n1. Honor an explicit requested format when it is supported.\n2. For one fact, an ordinary question, a prose explanation, or light structure that belongs in the conversation, reply in chat using Markdown.\n3. For a reusable text document or an explicit Markdown request, create and deliver a `.md` artifact through the normal file-delivery path.\n4. For a rich report, dashboard, substantial table, other visual structure, or two or more related records that need layout, you MUST call `render_ui` with `id`, `title`, and self-contained `html` before the final reply.\n5. For an interaction, form, or dynamic state, use `render_form` or legacy A2UI `render_ui`.\n6. Generate PNG or PDF only when the user explicitly asks for an image, PDF, download, or export. They are not the default presentation format.\n7. Do not replace a required `render_ui` result with an unlabelled file upload, Markdown table, JSON, or prose summary.\n\nDomain skills control how data is obtained and interpreted. This gate controls how the completed result is presented and overrides a domain skill's default text-summary format. When `render_ui` succeeds, keep the chat reply to one short confirmation sentence.";export const CANVAS_SYSTEM_PROMPT=`## Static Canvas HTML\n\nCall \`render_ui({"id":"report-id","title":"Report title","html":"<!doctype html>..."})\` for non-interactive reports, dashboards, tables, charts, and other structured results.\n\nUse a complete UTF-8 HTML document with inline CSS and inline SVG for charts. Keep it self-contained. Scripts, forms, external stylesheets, fonts, images, and network requests do not run in the preview. The HTML preview has a 5 MiB limit. Static HTML is a BeeOS file artifact and does not require the Live Canvas panel or relay to be open. Do not call \`beeos_upload_file\` for the same result.\n\n## Live Canvas — Google A2UI v0.8\n\nUse legacy A2UI only for interactions, forms, and dynamic state. Use \`render_ui\`, not the built-in \`canvas\` tool.\n\n### How render_ui works\n\nYou output a **flat array of components** with unique IDs. Components reference each other by ID.\n${CANVAS_COMPONENT_ID_GUIDANCE}\n\nParameters:\n- \`id\`: Surface ID (e.g. "sales-report"). Reuse same id to update.\n- \`root\`: The ID of the top-level component (usually a Column).\n- \`components\`: Flat array of objects. **Each object MUST have \`id\` and \`component\`**:\n \`{"id": "myId", "component": {"Text": {"text": {"literalString": "Hello"}, "usageHint": "body"}}}\`\n The \`component\` key wraps exactly ONE type key (Text, Card, Row, etc.) and its properties.\n- \`data\` (optional): Data model object for form two-way binding.\n\n### Rules\n- Keep chat reply to ONE short sentence when using render_ui. Do NOT repeat data shown in canvas.\n- Use this A2UI schema for interactive experiences. Use static HTML for non-interactive structured results and plain text for explanations.\n- Canvas renders in a narrow sidebar (~400px).\n- Prefer fewer, larger components. One table is better than many small cards.\n- Use descriptive surface IDs (e.g. "sales-report", not "ui1").\n- Same \`id\` updates existing surface; different \`id\` creates new one.\n- When asked to modify existing content, reuse the original surface id from [Canvas State].\n\n### StringValue\n\nText properties use StringValue — an object with exactly one key:\n- Static text: \`{"literalString": "Hello"}\`\n- Data binding: \`{"path": "/fieldName"}\` (reads from the \`data\` model)\n\nShorthand: \`sv("text")\` means \`{"literalString": "text"}\` in examples below.\n\n### Component Reference\n\n**Text** — Display text with semantic sizing.\n\`{Text: {text: sv("Title"), usageHint: "h2"}}\`\nusageHint: "h1" | "h2" | "h3" | "h4" | "h5" | "caption" | "body"\ntext also renders Markdown (bold, italic, lists, code blocks, links).\n\n**Image** — Display an image.\n\`{Image: {url: sv("https://..."), usageHint: "mediumFeature"}}\`\nImage.url may be \`beeos-file://<fileId>\`; the renderer resolves it. Do not convert it to a signed URL.\nusageHint: "icon" | "avatar" | "smallFeature" | "mediumFeature" | "largeFeature" | "header"\nfit (optional): "contain" | "cover" | "fill" | "none" | "scale-down"\n\n**Icon** — Material icon.\n\`{Icon: {name: sv("check")}}\`\n\n**Video** — Embed a video.\n\`{Video: {url: sv("https://youtube.com/...")}}\`\n\n**AudioPlayer** — Audio playback.\n\`{AudioPlayer: {url: sv("https://..."), description: sv("Episode 1")}}\`\n\n**Row** — Horizontal layout. Children referenced by ID.\n\`{Row: {children: {explicitList: ["child1", "child2"]}, distribution: "spaceBetween"}}\`\ndistribution: "start" | "center" | "end" | "spaceBetween" | "spaceAround" | "spaceEvenly"\nalignment: "start" | "center" | "end" | "stretch"\n\n**Column** — Vertical layout. Children referenced by ID.\n\`{Column: {children: {explicitList: ["child1", "child2"]}}}\`\nSame distribution/alignment options as Row.\n\n**List** — Vertical or horizontal list.\n\`{List: {children: {explicitList: ["item1", "item2"]}, direction: "vertical"}}\`\n\n**Card** — Container with a single child.\n\`{Card: {child: "contentColumnId"}}\`\n\n**Tabs** — Tabbed container.\n\`{Tabs: {tabItems: [{title: sv("Tab 1"), child: "panel1"}, {title: sv("Tab 2"), child: "panel2"}]}}\`\n\n**Divider** — Horizontal or vertical separator.\n\`{Divider: {axis: "horizontal"}}\`\n\n**Modal** — Dialog with trigger.\n\`{Modal: {entryPointChild: "triggerBtnId", contentChild: "dialogContentId"}}\`\n\n**Button** — Clickable with action.\n\`{Button: {child: "labelTextId", action: {name: "onClick", context: [{key: "id", value: {literalString: "item-1"}}]}}}\`\n\n**TextField** — Text input with data binding.\n\`{TextField: {label: sv("Name"), text: {path: "/name"}, type: "shortText"}}\`\ntype: "shortText" | "number" | "date" | "longText"\n\n**CheckBox** — Boolean toggle.\n\`{CheckBox: {label: sv("Agree"), value: {path: "/agree"}}}\`\n\n**DateTimeInput** — Date/time picker.\n\`{DateTimeInput: {value: {path: "/date"}, enableDate: true, enableTime: false}}\`\n\n**MultipleChoice** — Selection from options.\n\`{MultipleChoice: {selections: {path: "/country"}, options: [{label: sv("US"), value: "US"}, {label: sv("CN"), value: "CN"}]}}\`\n\n**Slider** — Numeric range input.\n\`{Slider: {value: {path: "/volume"}, minValue: 0, maxValue: 100}}\`\n\n**Chart** (BeeOS extension) — Data visualization.\n\`{Chart: {chartType: sv("bar"), title: sv("Sales"), labels: ["Q1","Q2","Q3"], datasets: [{label: "Revenue", data: [100,200,150]}]}}\`\nchartType: bar, horizontal_bar, line, area, scatter, pie, doughnut, radar, gauge, candlestick, funnel, sparkline\ngauge uses value + max instead of labels/datasets.\ncandlestick data: [{open,high,low,close}] (full field names, NOT o/h/l/c).\n\n### Composition\n\nNest by id: Column/Row \`children.explicitList\`, Card \`child\`. Example:\n\`{"id":"s1","root":"column_root","components":[{"id":"text_body","component":{"Text":{"text":{"literalString":"Confirm"},"usageHint":"body"}}},{"id":"button_confirm","component":{"Button":{"child":"text_body","action":{"name":"confirm"}}}},{"id":"column_root","component":{"Column":{"children":{"explicitList":["button_confirm"]}}}}]}\`\nDisplay-only tables and charts require static HTML. Forms: TextField/CheckBox + Button \`action.context\` paths.\n\n### render_form\nUse \`render_form\` for schema-driven forms when a domain schema exists.\nParameters: \`domain\`, \`operation\`, \`data\`, \`entityId\`.\n\n### close_ui\nCloses a UI surface. Use when user moves to a new topic or explicitly dismisses.\n`;export const CANVAS_TERM_MAPPING=`[System Note] Always use \`render_ui\` (not the canvas tool) for Canvas results. Display-only reports, dashboards, tables, and charts MUST use render_ui({"id":"report-id","title":"Report title","html":"<!doctype html>..."}) with self-contained static HTML (inline CSS and SVG, no scripts). The result is an HTML file card; tell the user it is ready to click and preview. A2UI root/components are only for interactive buttons, inputs, or component actions; display-only A2UI is rejected. Each A2UI component MUST be {"id":"x","component":{"TypeName":{props}}}. ${CANVAS_COMPONENT_ID_GUIDANCE} Forms: TextField/CheckBox/Slider + Button action.context paths, or \`render_form\` for domain schemas. Never combine html with root/components. Keep chat brief. Use \`close_ui\` to dismiss Live Canvas. Reuse the same id to update; use a new id for new content.`;export const CANVAS_UPDATE_HINT="[Canvas Rule] Same id in render_ui = update existing surface. Different id = new surface. When a surface is referenced for editing, you MUST reuse its exact id and provide the full updated component tree.";const MAX_CONTEXT_SURFACES=8,MAX_CONTEXT_CHARS=500;export function buildActiveSurfacesContext(e,t){let n;if(!t)return null;if(n=e.getAllActive(t),0===n.length)return null;const a=[...n].sort((e,t)=>t.updatedAt-e.updatedAt).slice(0,8),o=n.length-a.length;let i=`[Canvas State] Active surfaces: ${a.map(t=>{const n=e.summarize(t.surfaceId);if(!n)return`"${t.surfaceId}"`;const a=n.types.slice(0,3).join(", "),o=n.title?`: "${n.title}"`:"";return`"${t.surfaceId}" (${a}${o})`}).join("; ")}.`;return o>0&&(i+=` ...and ${o} more.`),i+=" To modify existing content, reuse its id in render_ui.",i.length>500&&(i=i.slice(0,497)+"..."),i}
1
+ import{extractBeeosConversationIdFromSessionKey}from"../runtime/openclaw-session-key-resolution.js";const A2UI_TYPE_NAMES=new Set(["Text","Image","Icon","Video","AudioPlayer","Row","Column","List","Card","Tabs","Divider","Modal","Button","CheckBox","TextField","DateTimeInput","MultipleChoice","Slider","Chart","WebEmbed","FilePreview","MapView"]);function normalizeComponents(e){return e.map(e=>{if(!e||"object"!=typeof e)return e;const t=e;if(t.component&&"object"==typeof t.component)return t;for(const e of Object.keys(t))if(A2UI_TYPE_NAMES.has(e)&&"object"==typeof t[e])return{id:t.id,component:{[e]:t[e]}};return t})}const A2UI_INTERACTIVE_TYPES=new Set(["Button","CheckBox","TextField","DateTimeInput","MultipleChoice","Slider"]);export const NON_INTERACTIVE_A2UI_ERROR="Display-only A2UI is not supported. Call render_ui again with id, title, and self-contained static HTML in html. A2UI root/components are reserved for interactive buttons, inputs, or component actions.";export function hasInteractiveComponent(e){return e.some(e=>{const t=e?.component;return!(!t||"object"!=typeof t)&&Object.entries(t).some(([e,t])=>A2UI_INTERACTIVE_TYPES.has(e)||Boolean(t&&"object"==typeof t&&t.action))})}export const RENDER_UI_TOOL_NAME="render_ui";export const CLOSE_UI_TOOL_NAME="close_ui";export const CANVAS_COMPONENT_ID_GUIDANCE="Component IDs must differ from non-reference scalar values (usageHint, literalString, and action.name). Use type-qualified IDs such as text_heading, text_body, and column_root; do not use scalar values such as body, h1, or center as IDs. A2UI v0.8 resolves strings matching component IDs as references. Keep root, child, explicitList, and template.componentId references matching their target IDs. Reuse the same component IDs when updating a surface.";export const RENDER_UI_TOOL_DESCRIPTION="Publish a static Canvas result as self-contained HTML using id, title, and html. The result is stored as a BeeOS file and shown in the conversation. Legacy A2UI calls with id, root, and components remain available for interactive experiences.";export const RENDER_UI_TOOL_SCHEMA={type:"object",required:["id"],properties:{id:{type:"string",description:"Surface id. Reuse to update."},title:{type:"string",description:"Human-readable title for a static HTML Canvas result."},html:{type:"string",description:"Complete self-contained static HTML. Maximum UTF-8 size: 5 MiB."},root:{type:"string",description:"Root component id."},components:{type:"array",description:"Flat ComponentInstance list. Each item has id plus one component type.",items:{type:"object",required:["id"],properties:{id:{type:"string",description:CANVAS_COMPONENT_ID_GUIDANCE},component:{type:"object",description:"One type key: Text, Image, Icon, Video, AudioPlayer, Row, Column, List, Card, Tabs, Divider, Modal, Button, CheckBox, TextField, DateTimeInput, MultipleChoice, Slider, Chart."}}}},data:{type:"object",description:"Optional binding model, path keys like /name."}}};export const CLOSE_UI_TOOL_DESCRIPTION="Close and remove a UI surface that was previously rendered.";export const CLOSE_UI_TOOL_SCHEMA={type:"object",required:["id"],properties:{id:{type:"string",description:"The UI identifier to close"}}};const SESSION_READY_POLL_ATTEMPTS=3,SESSION_READY_POLL_INTERVAL_MS=400,delay=e=>new Promise(t=>setTimeout(t,e)),MAX_STATIC_HTML_BYTES=5242880,FILE_ID=/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;async function renderStaticHtml(e,t,n){const a=e=>({success:!1,presentation:"static_html",error:e}),o="string"==typeof n.id?n.id.trim():"",i="string"==typeof n.title?n.title.trim():"",r=n.html;if(!o||!i||"string"!=typeof r||!r.trim())return a("id, title, and non-empty html are required");if("root"in n||"components"in n)return a("html cannot be combined with root or components");if(i.length>160)return a("Canvas title is too long (max 160 characters)");if(Buffer.byteLength(r,"utf8")>5242880)return a("Canvas HTML exceeds the 5 MiB preview limit");if(!e.publishStaticHtml)return a("Canvas file upload is unavailable");try{const n=await e.publishStaticHtml({id:o,title:i,html:r},t);return FILE_ID.test(n.fileId)&&n.uri===`beeos-file://${n.fileId}`&&"text/html"===n.mimeType?{success:!0,presentation:"static_html",surfaceId:o,artifact:{kind:"canvas",fileId:n.fileId,uri:n.uri,mimeType:"text/html",title:i}}:a("Canvas HTML upload returned an invalid file reference")}catch(e){return a(`Canvas HTML upload failed: ${e instanceof Error?e.message:String(e)}`)}}async function ensureRelayConnected(e,t,n,a){const{stateManager:o,logger:i}=e,r=a??e.getGatewaySessionKey?.(n),s=r??t,l=o.resolveCanvasForSession(s);if(l&&o.isCanvasJoined(l))return l;let c=o.getRelayClient();if(c||(c=await o.waitForRelayClient(),c))try{e.waitForAgentSync&&await e.waitForAgentSync();const a=e.getPlatformAgentId?.(n),l=r??t,d=(r?extractBeeosConversationIdFromSessionKey(r):void 0)??(r&&r!==t?t:void 0),u=await c.ensureCanvas(l,{agentId:a,linkSessionId:d});return await o.joinCanvas(u,s),i.info?.(`[canvas] Joined canvas=${u} session=${t} gatewayKey=${r??"none"} agentId=${a??"none"}`),u}catch(e){return void i.warn?.(`[canvas] Failed to join canvas relay: ${e}`)}}export function createRenderUiHandler(e){const{stateManager:t,logger:n,writeObs:a,getGatewaySessionKey:o}=e;let i=0;return async(o,r)=>{const s=r;if(Object.prototype.hasOwnProperty.call(s,"html"))return renderStaticHtml(e,o,s);const l=s.id,c=s.root,d="panel";let u=s.components;if("string"==typeof u)try{u=JSON.parse(u)}catch{}if(!(l&&c&&Array.isArray(u)&&u.length))return{success:!1,error:"id, root, and components (array) are required"};const p=normalizeComponents(u);if(!hasInteractiveComponent(p))return{success:!1,error:NON_INTERACTIVE_A2UI_ERROR};if(!t.validateComponentCount(p))return{success:!1,error:"Too many components (max 500)"};if(!t.checkRateLimit(l))return n.warn?.(`[canvas] Rate limited surfaceId=${l}`),a({component:"canvas",domain:"canvas",name:"canvas.rate_limited",severity:"warn",payload:{surfaceId:l}}),{success:!1,error:"Rate limited — too many updates per second"};const m=s.data;let h=e.getActiveSessionId?.(o);for(let t=0;!h&&t<3;t++)await delay(400),h=e.getActiveSessionId?.(o);if(!h&&e.waitForAgentSync){try{await e.waitForAgentSync()}catch{}h=e.getActiveSessionId?.(o)}if(!h)return n.warn?.(`[canvas] No active session for toolCallId=${o} after 3 polls, render_ui not ready`),{success:!1,retryable:!0,error:"Canvas session not ready yet (agent just connected) — retry render_ui shortly; do NOT fall back to plain text."};const f=e.getGatewaySessionKey?.(o),y=await ensureRelayConnected(e,h,o,f),v=t.has(l,y??void 0),g=Date.now(),C=t.getRelayClient();y&&C&&g-i>6e4&&C.createSnapshot(y,"Pre-agent edit").then(()=>{i=Date.now(),n.debug?.("[canvas] Pre-edit checkpoint created")}).catch(e=>n.warn?.(`[canvas] Pre-edit checkpoint failed: ${e}`)),v?t.update(l,p,y,c,m):t.create(y??`_fallback_${Date.now()}`,l,d,p,{origin:{kind:"agent"},rootComponentId:c,dataModel:m});const T=p.map(e=>e.component&&"object"==typeof e.component?Object.keys(e.component)[0]??"unknown":"unknown");a({component:"canvas",domain:"canvas",name:v?"canvas.update_sent":"canvas.surface_created",severity:"info",payload:{surfaceId:l,presentation:d,componentCount:p.length,componentTypes:T}}),n.debug?.(`[canvas] ${v?"Updated":"Created"} surface=${l} components=${p.length}`);const I={success:!0,surfaceId:l,presentation:d,isUpdate:v,activeSurfaces:y?t.getAllActive(y).map(e=>e.surfaceId):[l]};return m&&(I.dataModel=m),I}}export function createRenderUiToolExecutor(e){const t=createRenderUiHandler(e);return async(e,n)=>{const a=await t(e,n),o=[{type:"text",text:JSON.stringify(a)}];if(!0===a.success&&"static_html"===a.presentation){const e=a.artifact;o.push({type:"resource_link",uri:e.uri,name:`${e.title}.html`,mimeType:e.mimeType})}return{...a,content:o,details:a,isError:!1===a.success}}}export function createCloseUiHandler(e){const{stateManager:t,logger:n,writeObs:a}=e;return async(o,i)=>{const r=i.id;if(!r)return{success:!1,error:"id is required"};const s=t.get(r),l=s?Date.now()-s.createdAt:0,c=e.getGatewaySessionKey?.(o),d=c?t.resolveCanvasForSession(c):void 0;return t.delete(r,d),a({component:"canvas",domain:"canvas",name:"canvas.surface_closed",severity:"info",payload:{surfaceId:r,lifetime_ms:l}}),n.debug?.(`[canvas] Closed surface=${r}`),{success:!0,surfaceId:r,message:"UI closed"}}}export const CANVAS_PRESENTATION_GATE="## BeeOS Canvas presentation gate\n\nCanvas presentation is available in the background for this BeeOS conversation. The Canvas resource tab does not need to be open and no Web subscriber is required before a Canvas tool is called. For static HTML success, say the HTML file is ready and can be clicked to preview; do not claim it was displayed on Live Canvas or that the user is viewing it.\n\nDo not read `beeos-canvas/SKILL.md` or any canvas skill file. Call `render_ui` directly. The A2UI schema is in this prompt and the `render_ui` tool.\n\nBefore the final reply, you MUST call `render_ui` with `id`, `title`, and self-contained `html` to publish the completed answer as an HTML file. This applies to short answers, weather queries and forecasts, prose explanations, reports, tables, charts, and two or more related records. A short user question is not an exception.\n\n1. Call `render_ui` directly and publish exactly one HTML file for the completed result. The tool uploads that file; do not upload it again with `beeos_upload_file` or call `render_ui` again for the same result.\n2. Do not finish with only plain text, Markdown, a Markdown table, JSON, or a prose summary. Put the actual answer in the HTML file, then keep chat to one short confirmation with the file ready to click and preview.\n3. Honor an explicitly requested alternative file format when supported. Pending questions, approvals, cancellations, and tool failures remain control/status messages; do not fabricate a successful HTML file for them.\n4. For an interaction, form, or dynamic state, use `render_form` or legacy A2UI `render_ui`.\n5. Generate PNG or PDF only when the user explicitly asks for an image, PDF, download, or export. They are not the default presentation format.\n\nDomain skills control how data is obtained and interpreted. This gate controls how the completed result is presented and overrides a domain skill's default text-summary format. When `render_ui` succeeds, keep the chat reply to one short confirmation sentence.";export const CANVAS_SYSTEM_PROMPT=`## Static Canvas HTML\n\nCall \`render_ui({"id":"report-id","title":"Report title","html":"<!doctype html>..."})\` to publish the completed non-interactive answer as one HTML file, including weather answers and prose explanations.\n\nUse a complete UTF-8 HTML document with inline CSS and inline SVG for charts. Keep it self-contained. Scripts, forms, external stylesheets, fonts, images, and network requests do not run in the preview. The HTML preview has a 5 MiB limit. Static HTML is a BeeOS file artifact and does not require the Live Canvas panel or relay to be open. Do not call \`beeos_upload_file\` for the same result.\n\n## Live Canvas — Google A2UI v0.8\n\nUse legacy A2UI only for interactions, forms, and dynamic state. Use \`render_ui\`, not the built-in \`canvas\` tool.\n\n### How render_ui works\n\nYou output a **flat array of components** with unique IDs. Components reference each other by ID.\n${CANVAS_COMPONENT_ID_GUIDANCE}\n\nParameters:\n- \`id\`: Surface ID (e.g. "sales-report"). Reuse same id to update.\n- \`root\`: The ID of the top-level component (usually a Column).\n- \`components\`: Flat array of objects. **Each object MUST have \`id\` and \`component\`**:\n \`{"id": "myId", "component": {"Text": {"text": {"literalString": "Hello"}, "usageHint": "body"}}}\`\n The \`component\` key wraps exactly ONE type key (Text, Card, Row, etc.) and its properties.\n- \`data\` (optional): Data model object for form two-way binding.\n\n### Rules\n- Keep chat reply to ONE short sentence when using render_ui. Do NOT repeat data shown in canvas.\n- Use this A2UI schema for interactive experiences. Use static HTML files for completed non-interactive answers, including explanations.\n- Canvas renders in a narrow sidebar (~400px).\n- Prefer fewer, larger components. One table is better than many small cards.\n- Use descriptive surface IDs (e.g. "sales-report", not "ui1").\n- Same \`id\` updates existing surface; different \`id\` creates new one.\n- When asked to modify existing content, reuse the original surface id from [Canvas State].\n\n### StringValue\n\nText properties use StringValue — an object with exactly one key:\n- Static text: \`{"literalString": "Hello"}\`\n- Data binding: \`{"path": "/fieldName"}\` (reads from the \`data\` model)\n\nShorthand: \`sv("text")\` means \`{"literalString": "text"}\` in examples below.\n\n### Component Reference\n\n**Text** — Display text with semantic sizing.\n\`{Text: {text: sv("Title"), usageHint: "h2"}}\`\nusageHint: "h1" | "h2" | "h3" | "h4" | "h5" | "caption" | "body"\ntext also renders Markdown (bold, italic, lists, code blocks, links).\n\n**Image** — Display an image.\n\`{Image: {url: sv("https://..."), usageHint: "mediumFeature"}}\`\nImage.url may be \`beeos-file://<fileId>\`; the renderer resolves it. Do not convert it to a signed URL.\nusageHint: "icon" | "avatar" | "smallFeature" | "mediumFeature" | "largeFeature" | "header"\nfit (optional): "contain" | "cover" | "fill" | "none" | "scale-down"\n\n**Icon** — Material icon.\n\`{Icon: {name: sv("check")}}\`\n\n**Video** — Embed a video.\n\`{Video: {url: sv("https://youtube.com/...")}}\`\n\n**AudioPlayer** — Audio playback.\n\`{AudioPlayer: {url: sv("https://..."), description: sv("Episode 1")}}\`\n\n**Row** — Horizontal layout. Children referenced by ID.\n\`{Row: {children: {explicitList: ["child1", "child2"]}, distribution: "spaceBetween"}}\`\ndistribution: "start" | "center" | "end" | "spaceBetween" | "spaceAround" | "spaceEvenly"\nalignment: "start" | "center" | "end" | "stretch"\n\n**Column** — Vertical layout. Children referenced by ID.\n\`{Column: {children: {explicitList: ["child1", "child2"]}}}\`\nSame distribution/alignment options as Row.\n\n**List** — Vertical or horizontal list.\n\`{List: {children: {explicitList: ["item1", "item2"]}, direction: "vertical"}}\`\n\n**Card** — Container with a single child.\n\`{Card: {child: "contentColumnId"}}\`\n\n**Tabs** — Tabbed container.\n\`{Tabs: {tabItems: [{title: sv("Tab 1"), child: "panel1"}, {title: sv("Tab 2"), child: "panel2"}]}}\`\n\n**Divider** — Horizontal or vertical separator.\n\`{Divider: {axis: "horizontal"}}\`\n\n**Modal** — Dialog with trigger.\n\`{Modal: {entryPointChild: "triggerBtnId", contentChild: "dialogContentId"}}\`\n\n**Button** — Clickable with action.\n\`{Button: {child: "labelTextId", action: {name: "onClick", context: [{key: "id", value: {literalString: "item-1"}}]}}}\`\n\n**TextField** — Text input with data binding.\n\`{TextField: {label: sv("Name"), text: {path: "/name"}, type: "shortText"}}\`\ntype: "shortText" | "number" | "date" | "longText"\n\n**CheckBox** — Boolean toggle.\n\`{CheckBox: {label: sv("Agree"), value: {path: "/agree"}}}\`\n\n**DateTimeInput** — Date/time picker.\n\`{DateTimeInput: {value: {path: "/date"}, enableDate: true, enableTime: false}}\`\n\n**MultipleChoice** — Selection from options.\n\`{MultipleChoice: {selections: {path: "/country"}, options: [{label: sv("US"), value: "US"}, {label: sv("CN"), value: "CN"}]}}\`\n\n**Slider** — Numeric range input.\n\`{Slider: {value: {path: "/volume"}, minValue: 0, maxValue: 100}}\`\n\n**Chart** (BeeOS extension) — Data visualization.\n\`{Chart: {chartType: sv("bar"), title: sv("Sales"), labels: ["Q1","Q2","Q3"], datasets: [{label: "Revenue", data: [100,200,150]}]}}\`\nchartType: bar, horizontal_bar, line, area, scatter, pie, doughnut, radar, gauge, candlestick, funnel, sparkline\ngauge uses value + max instead of labels/datasets.\ncandlestick data: [{open,high,low,close}] (full field names, NOT o/h/l/c).\n\n### Composition\n\nNest by id: Column/Row \`children.explicitList\`, Card \`child\`. Example:\n\`{"id":"s1","root":"column_root","components":[{"id":"text_body","component":{"Text":{"text":{"literalString":"Confirm"},"usageHint":"body"}}},{"id":"button_confirm","component":{"Button":{"child":"text_body","action":{"name":"confirm"}}}},{"id":"column_root","component":{"Column":{"children":{"explicitList":["button_confirm"]}}}}]}\`\nDisplay-only tables and charts require static HTML. Forms: TextField/CheckBox + Button \`action.context\` paths.\n\n### render_form\nUse \`render_form\` for schema-driven forms when a domain schema exists.\nParameters: \`domain\`, \`operation\`, \`data\`, \`entityId\`.\n\n### close_ui\nCloses a UI surface. Use when user moves to a new topic or explicitly dismisses.\n`;export const CANVAS_TERM_MAPPING=`[System Note] Always use \`render_ui\` (not the canvas tool) for Canvas results. Every completed non-interactive answer, including weather, explanations, reports, tables, and charts, MUST use render_ui({"id":"report-id","title":"Report title","html":"<!doctype html>..."}) with self-contained static HTML (inline CSS and SVG, no scripts). The result is an HTML file card; tell the user it is ready to click and preview. A2UI root/components are only for interactive buttons, inputs, or component actions; display-only A2UI is rejected. Each A2UI component MUST be {"id":"x","component":{"TypeName":{props}}}. ${CANVAS_COMPONENT_ID_GUIDANCE} Forms: TextField/CheckBox/Slider + Button action.context paths, or \`render_form\` for domain schemas. Never combine html with root/components. Keep chat brief. Use \`close_ui\` to dismiss Live Canvas. Reuse the same id to update; use a new id for new content.`;export const CANVAS_UPDATE_HINT="[Canvas Rule] Same id in render_ui = update existing surface. Different id = new surface. When a surface is referenced for editing, you MUST reuse its exact id and provide the full updated component tree.";const MAX_CONTEXT_SURFACES=8,MAX_CONTEXT_CHARS=500;export function buildActiveSurfacesContext(e,t){let n;if(!t)return null;if(n=e.getAllActive(t),0===n.length)return null;const a=[...n].sort((e,t)=>t.updatedAt-e.updatedAt).slice(0,8),o=n.length-a.length;let i=`[Canvas State] Active surfaces: ${a.map(t=>{const n=e.summarize(t.surfaceId);if(!n)return`"${t.surfaceId}"`;const a=n.types.slice(0,3).join(", "),o=n.title?`: "${n.title}"`:"";return`"${t.surfaceId}" (${a}${o})`}).join("; ")}.`;return o>0&&(i+=` ...and ${o} more.`),i+=" To modify existing content, reuse its id in render_ui.",i.length>500&&(i=i.slice(0,497)+"..."),i}
@@ -1 +1 @@
1
- import{createHash,randomUUID}from"node:crypto";import{dispatchRuntimeReplyWithRoute,hasRuntimeReplyApi}from"./runtime/reply-router.js";import{createImReplySink}from"./runtime/im-reply-sink.js";import{SerializedCallbackQueue}from"./runtime/callback-queue.js";import{promptRunKey}from"./runtime/prompt-run-store.js";import{classifyAgentRuntimeFailure}from"./runtime/agent-runtime-failure-classifier.js";import{SESSION_RECOVERY_REQUIRED_CODE,SESSION_RECOVERY_REQUIRED_MESSAGE}from"./runtime/session-mutation-coordinator.js";import{interceptAgentRuntimeFailures,messageStopReasonForRuntimeFailure,SESSION_BUSY_PUBLIC_MESSAGE}from"./runtime/im-runtime-failure-policy.js";import{writeRuntimeFailure}from"./runtime/write-runtime-failure.js";import{maybeRenameImConversationTitle}from"./runtime/conversation-title.js";import{settleBeforeTerminal}from"./approval/bridge.js";import{logChatLatency}from"./chat-latency.js";const normalizePositiveTimeoutMs=e=>{if("number"!=typeof e||!Number.isFinite(e))return;const n=Math.max(0,Math.trunc(e));return n>0?n:void 0},ABORT_TERMINAL_GRACE_MS=1e4,PRE_RUN_LOOP_PROBE_MS=100,PRE_RUN_LOOP_LAG_LOG_MS=500,formatErrMessage=e=>{if(e instanceof Error)return e.message;if("string"==typeof e)return e;try{return JSON.stringify(e)}catch{return String(e)}},failMessageStream=async(e,n)=>{void 0!==n.body&&e.setBody(n.body),await e.fail(void 0===n.stopReason?void 0:{stopReason:n.stopReason})};export const deterministicReplyMessageId=(e,n,t,r)=>`${e}_${createHash("sha256").update(`${n}:${t}:${r}`).digest("hex").slice(0,32)}`;export const dispatchImInboundPrompt=async e=>{const{runtime:n,prompt:t,client:r,logger:a,promptRunStore:i,assistantMessageCommitRouter:o,toolResultObservationRouter:s,modelCallObservationRouter:c,sessionMutationCoordinator:l,abortTransport:d,activeRunAuthority:u}=e;if(!hasRuntimeReplyApi(n))return a.warn?.(`[im] runtime dispatch skipped reason=reply_api_unavailable channel=${t.channelId} in_reply_to=${t.inboundMessageId}`),e.onLifecycleEvent?.({type:"skipped",reason:"reply_api_unavailable",channelId:t.channelId,inboundMessageId:t.inboundMessageId}),!1;const m=t.requestId??t.inboundMessageId,p=e.runtimeRunId??randomUUID();if(!p)throw new Error("runtime run id required");const I=Date.now();logChatLatency(a,"claw.inbound.received",m,I,{origin:e.origin??"im",request_id_source:t.requestId?"realtime_correlation":"message_id_fallback"});const g=e.origin??"im",h={protocol:"im",requestId:m,sessionId:t.sessionKey??"agent:main",sourceMessageId:t.inboundMessageId,chatId:t.channelId,origin:g},y=async(n,r)=>{if(e.onTerminalResult)try{await e.onTerminalResult(n,r)}catch(e){a.warn?.(`[im] terminal business callback failed requestId=${m} channel=${t.channelId} in_reply_to=${t.inboundMessageId}: ${formatErrMessage(e)}`)}},_=promptRunKey(t.channelId,t.inboundMessageId),R=t.sessionKey??"agent:main",f=new AbortController;let w,b,E,v,M,S,$,C;const q=new Promise((e,n)=>{C=n});e.onNativeRunCompletion&&q.catch(()=>{});const N=e.onNativeRunCompletion?{chatAbort:(e,n)=>(E=Promise.resolve().then(()=>d.chatAbort(e,n)),E)}:d;let k;const O=()=>{void 0!==k&&(clearTimeout(k),k=void 0)},T=async e=>{if($??=e,f.abort(e),!w)return{ok:!0};const n=await l.requestCancel(w,N);return n.ok?n.terminalObservation.then(e=>{e.ok||C(new Error(SESSION_RECOVERY_REQUIRED_CODE))}):C(new Error(SESSION_RECOVERY_REQUIRED_CODE)),{ok:n.ok}};if(e.waitIfExclusive)try{await e.waitIfExclusive("im_dispatch",f.signal)}catch(n){return a.warn?.(`[im] runtime dispatch aborted waiting for exclusive mutation requestId=${m} channel=${t.channelId} in_reply_to=${t.inboundMessageId}`),e.onLifecycleEvent?.({type:"dispatch_error",requestId:m,channelId:t.channelId,inboundMessageId:t.inboundMessageId,error:n instanceof Error?n.message:String(n),cause:"abort"}),!1}if(!i.register(_,{requestId:m,channelId:t.channelId,requestCancel:T,startedAt:I,sessionKey:R}))return a.warn?.(`[im] runtime dispatch rejected duplicate inboundMessageId=${t.inboundMessageId} requestId=${m} channel=${t.channelId}`),e.onLifecycleEvent?.({type:"dispatch_error",requestId:m,channelId:t.channelId,inboundMessageId:t.inboundMessageId,error:"duplicate_inbound_message_id",cause:"abort"}),!1;const L=e.retainedStream?.id??deterministicReplyMessageId("asst",t.channelId,t.inboundMessageId,t.selfPrincipalId),A={conversationId:t.channelId,clientMessageId:L,idempotencyKey:L,replyTo:e.inReplyTo??t.inboundMessageId,requestId:m,type:"agent_reply",content:{}};let D=()=>{};const F=settleBeforeTerminal(e.retainedStream??r.messages.startStream(A),()=>D());let U,K=!1;try{const e=await F.opened();K="duplicate"===e.outcome,logChatLatency(a,"claw.reply_stream.opened",m,I,{outcome:e.outcome})}catch(n){let o="unconfirmed";try{o=(await r.messages.getEnvelope(t.channelId,L)).state}catch{}return a.warn?.(`[im] runtime reply stream open outcome unknown requestId=${m} channel=${t.channelId} in_reply_to=${t.inboundMessageId} reply_id=${L} reconciled_state=${o}: ${formatErrMessage(n)}; runtime_not_invoked`),e.onLifecycleEvent?.({type:"skipped",reason:"reply_open_outcome_unknown",channelId:t.channelId,inboundMessageId:t.inboundMessageId}),i.unregister(_),!1}if(K)return a.info?.(`[im] runtime dispatch skipped existing deterministic reply requestId=${m} channel=${t.channelId} in_reply_to=${t.inboundMessageId}`),e.onLifecycleEvent?.({type:"skipped",reason:"idempotent_reply_exists",channelId:t.channelId,inboundMessageId:t.inboundMessageId}),i.unregister(_),!1;const P=()=>{T(e.abortSignal?.reason).catch(e=>{C(e instanceof Error?e:new Error(String(e)))})};e.abortSignal?.addEventListener("abort",P,{once:!0}),e.abortSignal?.aborted&&P();const Q=new SerializedCallbackQueue,j=createImReplySink({stream:F,persistObservedToolResults:void 0!==e.retainedStream,abortSignal:f.signal,callbackQueue:Q,...e.onNativeRunCompletion?{onCallbackFailure:e=>Q.fail(e)}:{},logger:a,requestId:m,startedAtMs:I,runId:p,channelId:t.channelId,inboundMessageId:t.inboundMessageId,origin:g,sessionKey:R,onAgentRunStart:n=>{l.bindRun(w,n),e.onNativeAgentRunStart?.(n),void 0!==U&&logChatLatency(a,"claw.runtime.pre_run_completed",m,U)}}),G=interceptAgentRuntimeFailures(j,"im_runtime",e=>a.warn?.(`[im] runtime failure reply suppressed requestId=${m} channel=${t.channelId} in_reply_to=${t.inboundMessageId} error_code=${e.errorCode}`)),Y=G.sink;let B=!1,x=!1;const V=async n=>{e.onNativeRunCompletion||Y.dispatcher.markComplete(n);try{e.onNativeRunCompletion&&Y.dispatcher.markComplete(n),await Y.dispatcher.waitForIdle()}catch(n){if(e.onNativeRunCompletion)throw Q.fail(n),n}},z=()=>{if(e.onNativeRunCompletion)return;O();const n=normalizePositiveTimeoutMs(e.abortTerminalGraceMs)??1e4;k=setTimeout(()=>{k=void 0,(async()=>{if(F.isTerminated)return;const e=f.signal.reason??$,n="string"==typeof e&&e.startsWith("chat_cancel");if(await V("failure"),!F.isTerminated)try{n?await F.cancel({stopReason:"user_stop"}):await failMessageStream(F,{body:SESSION_RECOVERY_REQUIRED_MESSAGE,stopReason:"error"}),a.warn?.(`[im] abort settlement watchdog terminalized reply requestId=${m} channel=${t.channelId} in_reply_to=${t.inboundMessageId} user_cancel=${n}`)}catch(e){a.warn?.(`[im] abort settlement watchdog terminal write failed requestId=${m} channel=${t.channelId}: ${formatErrMessage(e)}`)}})()},n)};f.signal.aborted?z():f.signal.addEventListener("abort",z,{once:!0});const W=()=>{if(!f.signal.aborted)return;const e=f.signal.reason;throw e instanceof Error?e:new Error(String(e??"runtime dispatch aborted"))};let J,H,X,Z,ee,ne,te,re,ae;const ie=async e=>{if(!w)throw new Error("session_mutation_not_acquired");const n=await l.converge(w,e);return n.ok?(J=n.permit,!0):(a.error?.(`[im] session mutation recovery required requestId=${m} sessionKey=${R} reason=${n.recovery.reason}`),H={code:n.recovery.code,reason:n.recovery.reason,sessionKey:n.recovery.sessionKey,operationId:n.recovery.operationId,surface:"message_service",...n.recovery.terminalKind?{terminalKind:n.recovery.terminalKind}:{}},!1)};try{if(W(),e.onNativeRunCompletion&&!e.nativeActiveRunRegistration)throw new Error("native MS Start registration required");const d=l.tryStartInteractive({sessionKey:R,label:"im_chat_message"},"message_service");if(!d.ok){try{await failMessageStream(F,{body:SESSION_BUSY_PUBLIC_MESSAGE,stopReason:"error"})}catch(e){a.warn?.(`[im] session busy terminal write failed channel=${t.channelId} in_reply_to=${t.inboundMessageId}: ${formatErrMessage(e)}`)}return a.warn?.(`[im] session write gate busy sessionKey=${R} channel=${t.channelId} in_reply_to=${t.inboundMessageId} active_operation=${d.activeOperationId??"unknown"}`),e.onLifecycleEvent?.({type:"dispatch_error",requestId:m,channelId:t.channelId,inboundMessageId:t.inboundMessageId,error:"session_busy",cause:"runtime_error"}),!1}if(w=d.token,logChatLatency(a,"claw.session_gate.acquired",m,I),l.bindRun(w,p),Z=o.register(R,j),ee=s.register(R,j),ne=e.approvalStreams?.bindSession(R,j.approvalParts),te=e.questionStreams?.bindSession(R,j.questionParts,{conversationId:t.channelId,replyMessageId:F.id}),D=()=>{ne?.(),te?.()},re=c.register(p,{requestId:m,startedAtMs:I,logger:a}),u){const n=l.snapshot(R);X=e.onNativeRunCompletion?{...e.nativeActiveRunRegistration}:{runtimeConversationUid:t.channelId,runtimeSessionKey:t.channelId,historyGeneration:u.historyGeneration(t.channelId),targetMessageId:t.inboundMessageId,runtimeRunId:p,activeRunRegistrationId:`im:${n?.operationId??p}`},u.activeRuns.register(X,async()=>{const e=`chat_cancel:activeRun:${X.activeRunRegistrationId}`,n=await i.abort(_,e);return n.ok||"already_aborted"===n.reason?"accepted":"terminal_committing"===n.reason||"not_found"===n.reason?"already_terminal":"recovery_required"})}W(),a.info?.(`[im] runtime dispatching requestId=${m} channel=${t.channelId} in_reply_to=${t.inboundMessageId} origin=${g}`),e.onLifecycleEvent?.({type:"dispatch_start",requestId:m,channelId:t.channelId,inboundMessageId:t.inboundMessageId}),e.onNativeRunCompletion&&e.assertNativeTarget&&(await e.assertNativeTarget(),W()),e.onNativeRunCompletion&&e.prepareNativeModel&&(await e.prepareNativeModel(w),W(),await(e.assertNativeTarget?.()),W());const f=t.modelOverrideId;"string"==typeof f&&f.length>0&&R.length>0&&e.applyTurnModel&&(await e.applyTurnModel(R,f),x=!0,W()),U=Date.now(),ae=e.enterTurn?.(R,p),logChatLatency(a,"claw.runtime.invoke",m,I);const S=setTimeout(()=>{const e=Date.now()-U-100;e>=500&&logChatLatency(a,"claw.runtime.event_loop_lag",m,U,{lag_ms:e,phase:"pre_run"})},100);S.unref?.();const $=()=>dispatchRuntimeReplyWithRoute({runtime:n,pluginConfig:e.pluginConfig,preparedPrompt:t.dispatchInput,replyRoute:h,replySinks:{im:Y},onDispatchInvoked:n=>{if(B=!0,e.onNativeRunCompletion){b=n;const t=new Promise((e,n)=>{v=e,M=n});t.catch(()=>{}),e.onNativeRunCompletion({runtimeRunId:p,token:w,hostCompletion:n,completion:t})}logChatLatency(a,"claw.runtime.dispatch_returned",m,U)}}),C=ae?ae.run($):$(),N=e.onNativeRunCompletion?await C:await Promise.race([C,q]).finally(()=>{});e.onNativeRunCompletion&&(await b,await E);const k=G.detectedFailure();if(k)throw new Error(k.diagnosticMessage);if(W(),await V("success"),e.onNativeRunCompletion&&await E,W(),!i.claimTerminal(_,"completion")){if(W(),"cancelling"===i.phase(_))throw new Error("cancellation in progress");throw new Error("terminal outcome already claimed")}if(!await ie("completed"))return await failMessageStream(F,{body:SESSION_RECOVERY_REQUIRED_MESSAGE,stopReason:"error"}).catch(()=>{}),e.onLifecycleEvent?.({type:"dispatch_error",requestId:m,channelId:t.channelId,inboundMessageId:t.inboundMessageId,error:SESSION_RECOVERY_REQUIRED_CODE,cause:"runtime_error",...H?{recovery:H}:{}}),!1;const O=Y.dispatcher.getTerminalErrorText?.();if(void 0!==O){let n=!1;const r=classifyAgentRuntimeFailure({raw:O,source:"im_runtime",runtimeKind:"openclaw"});try{await writeRuntimeFailure(F,{body:r.publicMessage,stopReason:messageStopReasonForRuntimeFailure(r),businessErrorCode:r.businessErrorCode})}catch(e){n=!0,a.warn?.(`[im] runtime reply stream error-terminal write outcome unknown requestId=${m} channel=${t.channelId} in_reply_to=${t.inboundMessageId}: ${formatErrMessage(e)}`)}return u&&X&&u.activeRuns.markTerminal(X,"failed"),a.warn?.(`[im] runtime dispatch ended with an error terminal requestId=${m} channel=${t.channelId} in_reply_to=${t.inboundMessageId} counts=${JSON.stringify(N.counts)} terminalWriteFailed=${n} elapsed_ms=${Date.now()-I}`),e.onLifecycleEvent?.({type:"dispatch_error",requestId:m,channelId:t.channelId,inboundMessageId:t.inboundMessageId,error:"runtime_error_terminal_reply",cause:"runtime_error"}),n||await y("",O),!1}let T=!1;try{await F.finalize({stopReason:"end_turn"})}catch(e){T=!0,a.warn?.(`[im] runtime reply stream finalize outcome unknown requestId=${m} channel=${t.channelId} in_reply_to=${t.inboundMessageId}: ${formatErrMessage(e)}`)}u&&X&&u.activeRuns.markTerminal(X,"completed");const L={...N.counts,source:j.getSourceCount()};a.info?.(`[im] runtime dispatch completed requestId=${m} channel=${t.channelId} in_reply_to=${t.inboundMessageId} counts=${JSON.stringify(L)} elapsed_ms=${Date.now()-I}`),logChatLatency(a,"claw.turn.terminal",m,I,{outcome:T?"outcome_unknown":"completed",tool_count:L.tool}),e.onLifecycleEvent?.({type:"dispatch_complete",requestId:m,channelId:t.channelId,inboundMessageId:t.inboundMessageId,counts:L,...T?{terminalWriteFailed:!0}:{}});const A=Y.dispatcher.getAccumulatedBody?.()??"";T||await y(A,"");const K="string"==typeof t.dispatchInput.message?t.dispatchInput.message:"";return maybeRenameImConversationTitle({client:r,conversationId:t.channelId,question:K,answer:A,logger:a}),!0}catch(n){S=n,e.onNativeRunCompletion&&(await(b?.then(()=>{},()=>{})),await E),await V("failure"),e.onNativeRunCompletion&&await E;const r=f.signal.aborted,o=r?f.signal.reason:void 0,s="cancelling"===i.phase(_),c=o??$,l="string"==typeof c&&c.startsWith("chat_cancel");let d="runtime_error";(r||s)&&(d="abort");const p=s||l||r?"cancellation":"completion";if(!i.claimTerminal(_,p)){if(a.warn?.(`[im] terminal claim raced requestId=${m} channel=${t.channelId}; best-effort envelope terminal if still streaming`),!F.isTerminated)try{l?await F.cancel({stopReason:"user_stop"}):await failMessageStream(F,{body:formatErrMessage(n),stopReason:"error"})}catch(e){a.warn?.(`[im] raced terminal write failed requestId=${m}: ${formatErrMessage(e)}`)}throw n}const g=l?"cancelled":"error";if(u&&X&&!e.onNativeRunCompletion&&u.activeRuns.markTerminal(X,"cancelled"===g?"cancelled":"failed"),w&&!await ie(g))throw u&&X&&e.onNativeRunCompletion&&u.activeRuns.markTerminal(X,"cancelled"===g?"cancelled":"failed"),l?await F.cancel({stopReason:"user_stop"}).catch(()=>{}):await failMessageStream(F,{body:SESSION_RECOVERY_REQUIRED_MESSAGE,stopReason:"error"}).catch(()=>{}),e.onLifecycleEvent?.({type:"dispatch_error",requestId:m,channelId:t.channelId,inboundMessageId:t.inboundMessageId,error:SESSION_RECOVERY_REQUIRED_CODE,cause:d,...H?{recovery:H}:{}}),n;u&&X&&e.onNativeRunCompletion&&u.activeRuns.markTerminal(X,"cancelled"===g?"cancelled":"failed");const h=classifyAgentRuntimeFailure({raw:n,source:"im_runtime",runtimeKind:"openclaw",requestAborted:l}),R=l?h:G.detectedFailure()??h;let v=!1;try{l?await F.cancel({stopReason:"user_stop"}):await writeRuntimeFailure(F,{body:R.publicMessage,stopReason:messageStopReasonForRuntimeFailure(R),businessErrorCode:R.businessErrorCode})}catch(e){v=!0,a.warn?.(`[im] runtime stream terminal write failed requestId=${m} channel=${t.channelId}: ${formatErrMessage(e)}`)}throw a.warn?.(`[im] runtime dispatch failed requestId=${m} channel=${t.channelId} in_reply_to=${t.inboundMessageId} cause=${d} dispatchInvoked=${B} error_code=${R.errorCode}`),logChatLatency(a,"claw.turn.terminal",m,I,{outcome:d,error_code:R.errorCode}),e.onLifecycleEvent?.({type:"dispatch_error",requestId:m,channelId:t.channelId,inboundMessageId:t.inboundMessageId,error:R.errorCode,cause:d}),l||v||await y("",R.publicMessage),n}finally{if(e.onNativeRunCompletion&&(J?v?.(J):(B&&w&&l.requireRecovery(w,"terminal_not_observed"),M?.(S??new Error(SESSION_RECOVERY_REQUIRED_CODE)))),e.abortSignal?.removeEventListener("abort",P),B&&!J||ae?.close(),O(),re?.(),ee?.(),ne?.(),te?.(),Z?.(),i.unregister(_),x&&e.restoreTurnModel&&R)try{await e.restoreTurnModel(R)}catch(e){a.error?.(`[im] turn model restore failed requestId=${m} sessionKey=${R}: ${formatErrMessage(e)}`)}B?J&&l.release(J):l.releaseBeforeRuntime(w)}};
1
+ import{createHash,randomUUID}from"node:crypto";import{dispatchRuntimeReplyWithRoute,hasRuntimeReplyApi}from"./runtime/reply-router.js";import{createImReplySink}from"./runtime/im-reply-sink.js";import{SerializedCallbackQueue}from"./runtime/callback-queue.js";import{promptRunKey}from"./runtime/prompt-run-store.js";import{classifyAgentRuntimeFailure}from"./runtime/agent-runtime-failure-classifier.js";import{SESSION_RECOVERY_REQUIRED_CODE,SESSION_RECOVERY_REQUIRED_MESSAGE}from"./runtime/session-mutation-coordinator.js";import{interceptAgentRuntimeFailures,messageStopReasonForRuntimeFailure,SESSION_BUSY_PUBLIC_MESSAGE}from"./runtime/im-runtime-failure-policy.js";import{writeRuntimeFailure}from"./runtime/write-runtime-failure.js";import{maybeRenameImConversationTitle}from"./runtime/conversation-title.js";import{settleBeforeTerminal}from"./approval/bridge.js";import{logChatLatency}from"./chat-latency.js";const normalizePositiveTimeoutMs=e=>{if("number"!=typeof e||!Number.isFinite(e))return;const n=Math.max(0,Math.trunc(e));return n>0?n:void 0},ABORT_TERMINAL_GRACE_MS=1e4,PRE_RUN_LOOP_PROBE_MS=100,PRE_RUN_LOOP_LAG_LOG_MS=500,formatErrMessage=e=>{if(e instanceof Error)return e.message;if("string"==typeof e)return e;try{return JSON.stringify(e)}catch{return String(e)}},failMessageStream=async(e,n)=>{void 0!==n.body&&e.setBody(n.body),await e.fail(void 0===n.stopReason?void 0:{stopReason:n.stopReason})};export const deterministicReplyMessageId=(e,n,t,r)=>`${e}_${createHash("sha256").update(`${n}:${t}:${r}`).digest("hex").slice(0,32)}`;export const dispatchImInboundPrompt=async e=>{const{runtime:n,prompt:t,client:r,logger:a,promptRunStore:i,assistantMessageCommitRouter:o,toolResultObservationRouter:s,modelCallObservationRouter:c,sessionMutationCoordinator:l,abortTransport:d,activeRunAuthority:u}=e;if(!hasRuntimeReplyApi(n))return a.warn?.(`[im] runtime dispatch skipped reason=reply_api_unavailable channel=${t.channelId} in_reply_to=${t.inboundMessageId}`),e.onLifecycleEvent?.({type:"skipped",reason:"reply_api_unavailable",channelId:t.channelId,inboundMessageId:t.inboundMessageId}),!1;const m=t.requestId??t.inboundMessageId,p=e.runtimeRunId??randomUUID();if(!p)throw new Error("runtime run id required");const I=Date.now();logChatLatency(a,"claw.inbound.received",m,I,{origin:e.origin??"im",request_id_source:t.requestId?"realtime_correlation":"message_id_fallback"});const g=e.origin??"im",h={protocol:"im",requestId:m,sessionId:t.sessionKey??"agent:main",sourceMessageId:t.inboundMessageId,chatId:t.channelId,origin:g},y=async(n,r)=>{if(e.onTerminalResult)try{await e.onTerminalResult(n,r)}catch(e){a.warn?.(`[im] terminal business callback failed requestId=${m} channel=${t.channelId} in_reply_to=${t.inboundMessageId}: ${formatErrMessage(e)}`)}},_=promptRunKey(t.channelId,t.inboundMessageId),R=t.sessionKey??"agent:main",f=new AbortController;let w,b,E,v,M,S,$,C;const q=new Promise((e,n)=>{C=n});e.onNativeRunCompletion&&q.catch(()=>{});const N=e.onNativeRunCompletion?{chatAbort:(e,n)=>(E=Promise.resolve().then(()=>d.chatAbort(e,n)),E)}:d;let k;const O=()=>{void 0!==k&&(clearTimeout(k),k=void 0)},T=async e=>{if($??=e,f.abort(e),!w)return{ok:!0};const n=await l.requestCancel(w,N);return n.ok?n.terminalObservation.then(e=>{e.ok||C(new Error(SESSION_RECOVERY_REQUIRED_CODE))}):C(new Error(SESSION_RECOVERY_REQUIRED_CODE)),{ok:n.ok}};if(e.waitIfExclusive)try{await e.waitIfExclusive("im_dispatch",f.signal)}catch(n){return a.warn?.(`[im] runtime dispatch aborted waiting for exclusive mutation requestId=${m} channel=${t.channelId} in_reply_to=${t.inboundMessageId}`),e.onLifecycleEvent?.({type:"dispatch_error",requestId:m,channelId:t.channelId,inboundMessageId:t.inboundMessageId,error:n instanceof Error?n.message:String(n),cause:"abort"}),!1}if(!i.register(_,{requestId:m,channelId:t.channelId,requestCancel:T,startedAt:I,sessionKey:R}))return a.warn?.(`[im] runtime dispatch rejected duplicate inboundMessageId=${t.inboundMessageId} requestId=${m} channel=${t.channelId}`),e.onLifecycleEvent?.({type:"dispatch_error",requestId:m,channelId:t.channelId,inboundMessageId:t.inboundMessageId,error:"duplicate_inbound_message_id",cause:"abort"}),!1;const L=e.retainedStream?.id??deterministicReplyMessageId("asst",t.channelId,t.inboundMessageId,t.selfPrincipalId),A={conversationId:t.channelId,clientMessageId:L,idempotencyKey:L,replyTo:e.inReplyTo??t.inboundMessageId,requestId:m,type:"agent_reply",content:{}};let D=()=>{};const F=settleBeforeTerminal(e.retainedStream??r.messages.startStream(A),()=>D());let U,K=!1;try{const e=await F.opened();K="duplicate"===e.outcome,logChatLatency(a,"claw.reply_stream.opened",m,I,{outcome:e.outcome})}catch(n){let o="unconfirmed";try{o=(await r.messages.getEnvelope(t.channelId,L)).state}catch{}return a.warn?.(`[im] runtime reply stream open outcome unknown requestId=${m} channel=${t.channelId} in_reply_to=${t.inboundMessageId} reply_id=${L} reconciled_state=${o}: ${formatErrMessage(n)}; runtime_not_invoked`),e.onLifecycleEvent?.({type:"skipped",reason:"reply_open_outcome_unknown",channelId:t.channelId,inboundMessageId:t.inboundMessageId}),i.unregister(_),!1}if(K)return a.info?.(`[im] runtime dispatch skipped existing deterministic reply requestId=${m} channel=${t.channelId} in_reply_to=${t.inboundMessageId}`),e.onLifecycleEvent?.({type:"skipped",reason:"idempotent_reply_exists",channelId:t.channelId,inboundMessageId:t.inboundMessageId}),i.unregister(_),!1;const P=()=>{T(e.abortSignal?.reason).catch(e=>{C(e instanceof Error?e:new Error(String(e)))})};e.abortSignal?.addEventListener("abort",P,{once:!0}),e.abortSignal?.aborted&&P();const Q=new SerializedCallbackQueue,j=createImReplySink({stream:F,persistObservedToolResults:void 0!==e.retainedStream,htmlDelivery:e.htmlDelivery,abortSignal:f.signal,callbackQueue:Q,...e.onNativeRunCompletion?{onCallbackFailure:e=>Q.fail(e)}:{},logger:a,requestId:m,startedAtMs:I,runId:p,channelId:t.channelId,inboundMessageId:t.inboundMessageId,origin:g,sessionKey:R,onAgentRunStart:n=>{l.bindRun(w,n),e.onNativeAgentRunStart?.(n),void 0!==U&&logChatLatency(a,"claw.runtime.pre_run_completed",m,U)}}),G=interceptAgentRuntimeFailures(j,"im_runtime",e=>a.warn?.(`[im] runtime failure reply suppressed requestId=${m} channel=${t.channelId} in_reply_to=${t.inboundMessageId} error_code=${e.errorCode}`)),Y=G.sink;let x=!1,B=!1;const V=async n=>{e.onNativeRunCompletion||Y.dispatcher.markComplete(n);try{e.onNativeRunCompletion&&Y.dispatcher.markComplete(n),await Y.dispatcher.waitForIdle()}catch(n){if(e.onNativeRunCompletion)throw Q.fail(n),n}},z=()=>{if(e.onNativeRunCompletion)return;O();const n=normalizePositiveTimeoutMs(e.abortTerminalGraceMs)??1e4;k=setTimeout(()=>{k=void 0,(async()=>{if(F.isTerminated)return;const e=f.signal.reason??$,n="string"==typeof e&&e.startsWith("chat_cancel");if(await V("failure"),!F.isTerminated)try{n?await F.cancel({stopReason:"user_stop"}):await failMessageStream(F,{body:SESSION_RECOVERY_REQUIRED_MESSAGE,stopReason:"error"}),a.warn?.(`[im] abort settlement watchdog terminalized reply requestId=${m} channel=${t.channelId} in_reply_to=${t.inboundMessageId} user_cancel=${n}`)}catch(e){a.warn?.(`[im] abort settlement watchdog terminal write failed requestId=${m} channel=${t.channelId}: ${formatErrMessage(e)}`)}})()},n)};f.signal.aborted?z():f.signal.addEventListener("abort",z,{once:!0});const W=()=>{if(!f.signal.aborted)return;const e=f.signal.reason;throw e instanceof Error?e:new Error(String(e??"runtime dispatch aborted"))};let J,H,X,Z,ee,ne,te,re,ae;const ie=async e=>{if(!w)throw new Error("session_mutation_not_acquired");const n=await l.converge(w,e);return n.ok?(J=n.permit,!0):(a.error?.(`[im] session mutation recovery required requestId=${m} sessionKey=${R} reason=${n.recovery.reason}`),H={code:n.recovery.code,reason:n.recovery.reason,sessionKey:n.recovery.sessionKey,operationId:n.recovery.operationId,surface:"message_service",...n.recovery.terminalKind?{terminalKind:n.recovery.terminalKind}:{}},!1)};try{if(W(),e.onNativeRunCompletion&&!e.nativeActiveRunRegistration)throw new Error("native MS Start registration required");const d=l.tryStartInteractive({sessionKey:R,label:"im_chat_message"},"message_service");if(!d.ok){try{await failMessageStream(F,{body:SESSION_BUSY_PUBLIC_MESSAGE,stopReason:"error"})}catch(e){a.warn?.(`[im] session busy terminal write failed channel=${t.channelId} in_reply_to=${t.inboundMessageId}: ${formatErrMessage(e)}`)}return a.warn?.(`[im] session write gate busy sessionKey=${R} channel=${t.channelId} in_reply_to=${t.inboundMessageId} active_operation=${d.activeOperationId??"unknown"}`),e.onLifecycleEvent?.({type:"dispatch_error",requestId:m,channelId:t.channelId,inboundMessageId:t.inboundMessageId,error:"session_busy",cause:"runtime_error"}),!1}if(w=d.token,logChatLatency(a,"claw.session_gate.acquired",m,I),l.bindRun(w,p),Z=o.register(R,j),ee=s.register(R,j),ne=e.approvalStreams?.bindSession(R,j.approvalParts),te=e.questionStreams?.bindSession(R,j.questionParts,{conversationId:t.channelId,replyMessageId:F.id}),D=()=>{ne?.(),te?.()},re=c.register(p,{requestId:m,startedAtMs:I,logger:a}),u){const n=l.snapshot(R);X=e.onNativeRunCompletion?{...e.nativeActiveRunRegistration}:{runtimeConversationUid:t.channelId,runtimeSessionKey:t.channelId,historyGeneration:u.historyGeneration(t.channelId),targetMessageId:t.inboundMessageId,runtimeRunId:p,activeRunRegistrationId:`im:${n?.operationId??p}`},u.activeRuns.register(X,async()=>{const e=`chat_cancel:activeRun:${X.activeRunRegistrationId}`,n=await i.abort(_,e);return n.ok||"already_aborted"===n.reason?"accepted":"terminal_committing"===n.reason||"not_found"===n.reason?"already_terminal":"recovery_required"})}W(),a.info?.(`[im] runtime dispatching requestId=${m} channel=${t.channelId} in_reply_to=${t.inboundMessageId} origin=${g}`),e.onLifecycleEvent?.({type:"dispatch_start",requestId:m,channelId:t.channelId,inboundMessageId:t.inboundMessageId}),e.onNativeRunCompletion&&e.assertNativeTarget&&(await e.assertNativeTarget(),W()),e.onNativeRunCompletion&&e.prepareNativeModel&&(await e.prepareNativeModel(w),W(),await(e.assertNativeTarget?.()),W());const f=t.modelOverrideId;"string"==typeof f&&f.length>0&&R.length>0&&e.applyTurnModel&&(await e.applyTurnModel(R,f),B=!0,W()),U=Date.now(),ae=e.enterTurn?.(R,p),logChatLatency(a,"claw.runtime.invoke",m,I);const S=setTimeout(()=>{const e=Date.now()-U-100;e>=500&&logChatLatency(a,"claw.runtime.event_loop_lag",m,U,{lag_ms:e,phase:"pre_run"})},100);S.unref?.();const $=()=>dispatchRuntimeReplyWithRoute({runtime:n,pluginConfig:e.pluginConfig,preparedPrompt:t.dispatchInput,replyRoute:h,replySinks:{im:Y},onDispatchInvoked:n=>{if(x=!0,e.onNativeRunCompletion){b=n;const t=new Promise((e,n)=>{v=e,M=n});t.catch(()=>{}),e.onNativeRunCompletion({runtimeRunId:p,token:w,hostCompletion:n,completion:t})}logChatLatency(a,"claw.runtime.dispatch_returned",m,U)}}),C=ae?ae.run($):$(),N=e.onNativeRunCompletion?await C:await Promise.race([C,q]).finally(()=>{});e.onNativeRunCompletion&&(await b,await E);const k=G.detectedFailure();if(k)throw new Error(k.diagnosticMessage);if(W(),await V("success"),e.onNativeRunCompletion&&await E,W(),void 0===Y.dispatcher.getTerminalErrorText?.()&&e.htmlDelivery?.assertComplete(),!i.claimTerminal(_,"completion")){if(W(),"cancelling"===i.phase(_))throw new Error("cancellation in progress");throw new Error("terminal outcome already claimed")}if(!await ie("completed"))return await failMessageStream(F,{body:SESSION_RECOVERY_REQUIRED_MESSAGE,stopReason:"error"}).catch(()=>{}),e.onLifecycleEvent?.({type:"dispatch_error",requestId:m,channelId:t.channelId,inboundMessageId:t.inboundMessageId,error:SESSION_RECOVERY_REQUIRED_CODE,cause:"runtime_error",...H?{recovery:H}:{}}),!1;const O=Y.dispatcher.getTerminalErrorText?.();if(void 0!==O){let n=!1;const r=classifyAgentRuntimeFailure({raw:O,source:"im_runtime",runtimeKind:"openclaw"});try{await writeRuntimeFailure(F,{body:r.publicMessage,stopReason:messageStopReasonForRuntimeFailure(r),businessErrorCode:r.businessErrorCode})}catch(e){n=!0,a.warn?.(`[im] runtime reply stream error-terminal write outcome unknown requestId=${m} channel=${t.channelId} in_reply_to=${t.inboundMessageId}: ${formatErrMessage(e)}`)}return u&&X&&u.activeRuns.markTerminal(X,"failed"),a.warn?.(`[im] runtime dispatch ended with an error terminal requestId=${m} channel=${t.channelId} in_reply_to=${t.inboundMessageId} counts=${JSON.stringify(N.counts)} terminalWriteFailed=${n} elapsed_ms=${Date.now()-I}`),e.onLifecycleEvent?.({type:"dispatch_error",requestId:m,channelId:t.channelId,inboundMessageId:t.inboundMessageId,error:"runtime_error_terminal_reply",cause:"runtime_error"}),n||await y("",O),!1}let T=!1;try{await F.finalize({stopReason:"end_turn"})}catch(e){T=!0,a.warn?.(`[im] runtime reply stream finalize outcome unknown requestId=${m} channel=${t.channelId} in_reply_to=${t.inboundMessageId}: ${formatErrMessage(e)}`)}u&&X&&u.activeRuns.markTerminal(X,"completed");const L={...N.counts,source:j.getSourceCount()};a.info?.(`[im] runtime dispatch completed requestId=${m} channel=${t.channelId} in_reply_to=${t.inboundMessageId} counts=${JSON.stringify(L)} elapsed_ms=${Date.now()-I}`),logChatLatency(a,"claw.turn.terminal",m,I,{outcome:T?"outcome_unknown":"completed",tool_count:L.tool}),e.onLifecycleEvent?.({type:"dispatch_complete",requestId:m,channelId:t.channelId,inboundMessageId:t.inboundMessageId,counts:L,...T?{terminalWriteFailed:!0}:{}});const A=Y.dispatcher.getAccumulatedBody?.()??"";T||await y(A,"");const K="string"==typeof t.dispatchInput.message?t.dispatchInput.message:"";return maybeRenameImConversationTitle({client:r,conversationId:t.channelId,question:K,answer:A,logger:a}),!0}catch(n){S=n,e.onNativeRunCompletion&&(await(b?.then(()=>{},()=>{})),await E),await V("failure"),e.onNativeRunCompletion&&await E;const r=f.signal.aborted,o=r?f.signal.reason:void 0,s="cancelling"===i.phase(_),c=o??$,l="string"==typeof c&&c.startsWith("chat_cancel");let d="runtime_error";(r||s)&&(d="abort");const p=s||l||r?"cancellation":"completion";if(!i.claimTerminal(_,p)){if(a.warn?.(`[im] terminal claim raced requestId=${m} channel=${t.channelId}; best-effort envelope terminal if still streaming`),!F.isTerminated)try{l?await F.cancel({stopReason:"user_stop"}):await failMessageStream(F,{body:formatErrMessage(n),stopReason:"error"})}catch(e){a.warn?.(`[im] raced terminal write failed requestId=${m}: ${formatErrMessage(e)}`)}throw n}const g=l?"cancelled":"error";if(u&&X&&!e.onNativeRunCompletion&&u.activeRuns.markTerminal(X,"cancelled"===g?"cancelled":"failed"),w&&!await ie(g))throw u&&X&&e.onNativeRunCompletion&&u.activeRuns.markTerminal(X,"cancelled"===g?"cancelled":"failed"),l?await F.cancel({stopReason:"user_stop"}).catch(()=>{}):await failMessageStream(F,{body:SESSION_RECOVERY_REQUIRED_MESSAGE,stopReason:"error"}).catch(()=>{}),e.onLifecycleEvent?.({type:"dispatch_error",requestId:m,channelId:t.channelId,inboundMessageId:t.inboundMessageId,error:SESSION_RECOVERY_REQUIRED_CODE,cause:d,...H?{recovery:H}:{}}),n;u&&X&&e.onNativeRunCompletion&&u.activeRuns.markTerminal(X,"cancelled"===g?"cancelled":"failed");const h=classifyAgentRuntimeFailure({raw:n,source:"im_runtime",runtimeKind:"openclaw",requestAborted:l}),R=l?h:G.detectedFailure()??h;let v=!1;try{l?await F.cancel({stopReason:"user_stop"}):await writeRuntimeFailure(F,{body:R.publicMessage,stopReason:messageStopReasonForRuntimeFailure(R),businessErrorCode:R.businessErrorCode})}catch(e){v=!0,a.warn?.(`[im] runtime stream terminal write failed requestId=${m} channel=${t.channelId}: ${formatErrMessage(e)}`)}throw a.warn?.(`[im] runtime dispatch failed requestId=${m} channel=${t.channelId} in_reply_to=${t.inboundMessageId} cause=${d} dispatchInvoked=${x} error_code=${R.errorCode}`),logChatLatency(a,"claw.turn.terminal",m,I,{outcome:d,error_code:R.errorCode}),e.onLifecycleEvent?.({type:"dispatch_error",requestId:m,channelId:t.channelId,inboundMessageId:t.inboundMessageId,error:R.errorCode,cause:d}),l||v||await y("",R.publicMessage),n}finally{if(e.onNativeRunCompletion&&(J?v?.(J):(x&&w&&l.requireRecovery(w,"terminal_not_observed"),M?.(S??new Error(SESSION_RECOVERY_REQUIRED_CODE)))),e.abortSignal?.removeEventListener("abort",P),x&&!J||ae?.close(),O(),re?.(),ee?.(),ne?.(),te?.(),Z?.(),i.unregister(_),B&&e.restoreTurnModel&&R)try{await e.restoreTurnModel(R)}catch(e){a.error?.(`[im] turn model restore failed requestId=${m} sessionKey=${R}: ${formatErrMessage(e)}`)}x?J&&l.release(J):l.releaseBeforeRuntime(w)}};
package/dist/index.js CHANGED
@@ -1 +1 @@
1
- import{harnessConnectorToolCaller}from"./connectors/harness-connector-mcp.js";import{consumeNativeOutputTool}from"./runtime/native-output-file-attempt.js";import{uploadNativeOutputFile}from"./upload/upload-file.js";import{nativeOutputToolCallGate}from"./upload/native-output-tool-hook.js";import{NativeInputFilesHttpClient}from"./runtime/native-input-files-http.js";import{fileDeliveryFinalizeDecision}from"./upload/finalize-hook.js";import{resolveConfigWithMeta,mirrorOpenClawConfig}from"./config.js";import{hasRuntimeReplyApi}from"./runtime/reply-router.js";import{PromptRunStore}from"./runtime/prompt-run-store.js";import{createMcpNativeProjectionNudge,fingerprintMcpServers}from"./acp-gateway/mcp-native-projection-nudge.js";import{createImHotPathDeferral}from"./runtime/im-hot-path-deferral.js";import{SessionWriteGate}from"./acp-gateway/session-write-gate.js";import{OpenClawSessionWriteQuiescence}from"./runtime/openclaw-session-write-quiescence.js";import{SessionMutationCoordinator}from"./runtime/session-mutation-coordinator.js";import{scheduleProcessRuntimeRestart}from"./runtime/runtime-restart-scheduler.js";import{createImInboundHandler}from"./im-inbound-handler.js";import{AgentGatewayClient,NodeRuntimeDeliveryPort,agentIdentityFromKeyPair,loadAgentKeyPair,signAgentMessage}from"@beeos-ai/cloud-harness-sdk";import{MessageServiceConversationRunBoundary}from"./runtime/message-service-conversation-run-boundary.js";import{AutomationTurnPolicy}from"./automation/turn-policy.js";import{registerAutomationTools}from"./automation/tools.js";import{extractBeeosConversationIdFromSessionKey}from"./runtime/openclaw-session-key-resolution.js";import{setOpenClawConfigSnapshot}from"./runtime/openclaw-session-model.js";import{BeeosClawMessageClientProcessOwner,createBeeosClawMessageTokenProvider}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{createNativeCloudChatComposition,nativeCloudChatAuthorityFromRuntimeAuthority}from"./runtime/native-cloud-chat-composition.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,REQUEST_TIMEOUT_MS as BEEOS_UPLOAD_REQUEST_TIMEOUT_MS,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{readDiscoveryCache,writeDiscoveryCache}from"./discovery-cache.js";import{setClaimedCloudRuntimeInstanceId,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{CanvasStateManager}from"./canvas/state.js";import{createStaticCanvasHtmlPublisher}from"./canvas/static-html.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,createRenderUiToolExecutor,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,cloudRuntimeTargetInstanceId}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,CloudRuntimeServiceTransportReadinessAdapter,RuntimeServiceTransportReadinessAdapter,createRuntimeServiceProductionCandidate}from"./runtime/runtime-service-production-candidate.js";import{fetchRuntimeLeaseAttempt}from"./runtime/message-service-runtime-authenticated-fetch.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{OpenClawApprovalBridge}from"./approval/bridge.js";import{APPROVAL_DECISION_MESSAGE_TYPE,isApprovalDocument}from"./approval/protocol.generated.js";import{isQuestionResponseDocument,OpenClawQuestionBridge}from"./question/bridge.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,agentGateway=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;let approvalLogger;const sharedApprovalBridge=new OpenClawApprovalBridge({request:(e,t)=>sharedGatewayClient?sharedGatewayClient.request(e,t):Promise.reject(new Error("Not connected to Gateway")),logger:{info:e=>approvalLogger?.info?.(e),warn:e=>approvalLogger?.warn?.(e)}}),sharedQuestionBridge=new OpenClawQuestionBridge({request:(e,t)=>sharedGatewayClient?sharedGatewayClient.request(e,t):Promise.reject(new Error("Not connected to Gateway")),logger:{info:e=>approvalLogger?.info?.(e),warn:e=>approvalLogger?.warn?.(e)}}),sharedModelCallObservationRouter=new ModelCallObservationRouter;let sharedSessionMutationCoordinator,sharedRuntimeAuthority,sharedNativeChatService,sharedNativeCloudChat,sharedWriteObs=()=>{};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 cachedRuntimeServices=[],cachedRuntimeHooks=[];let rawRegisterService,wrappedRegisterService,rawOn,wrappedOn;function cacheRuntimeRegistrations(e){const t=e.registerService===wrappedRegisterService?rawRegisterService:e.registerService;if(rawRegisterService=t,wrappedRegisterService=r=>{cachedRuntimeServices.push(r),t.call(e,r)},e.registerService=wrappedRegisterService,cachedRuntimeServices.length=0,cachedRuntimeHooks.length=0,e.on){const t=e.on===wrappedOn?rawOn:e.on;rawOn=t,wrappedOn=(...r)=>{cachedRuntimeHooks.push(r),t.call(e,...r)},e.on=wrappedOn}}function replayRuntimeRegistrations(e){const t=e.registerService===wrappedRegisterService?rawRegisterService:e.registerService,r=e.on===wrappedOn?rawOn:e.on;for(const t of cachedRuntimeHooks)r.call(e,...t);for(const r of cachedRuntimeServices)t.call(e,r)}const ensureRegisterToolCache=e=>{const t=e.registerTool===wrappedRegisterTool?rawRegisterTool:e.registerTool;rawRegisterTool=t.bind(e),wrappedRegisterTool=e=>(cachedCapabilityTools.push(e),rawRegisterTool(e)),e.registerTool=wrappedRegisterTool};function agentKeyPairToIdentity(e){return{publicKeyBase64:()=>Buffer.from(e.publicKey).toString("base64"),sign:async t=>signAgentMessage(t,e.privateKey)}}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),a=r instanceof Error?r.stack:void 0;e.logger.error?.(`[beeos-claw] unhandledRejection: ${o}${a?`\n${a}`:""}`),t({component:"plugin",domain:"fatal",name:"unhandled_rejection",severity:"error",summary:o,error:{code:"unhandled_rejection",message:o,...a?{stack:a}:{}}})}),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,a=buildThirdPartyChannelSet(t);a.size>0&&e.logger.info?.(`[beeos-claw] Third-party channels detected: ${[...a].join(", ")}`);const n=o.log.enabled?createObsEventWriter({dir:o.log.dir,redaction:{gatewayToken:o.gateway.token},logger:e.logger}):()=>{};n({component:"plugin",domain:"lifecycle",name:"lifecycle.startup",severity:"info",payload:{pluginVersion:PLUGIN_VERSION,pid:process.pid,nodeVersion:process.version,cwd:process.cwd()}}),n({component:"plugin",domain:"config",name:"config.resolved",severity:"info",payload:{config:o,sources:r.sources,openclawConfigPath:r.openclawConfigPath}});for(const t of r.validation)n({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),agentGateway=o.agentGatewayUrl?new AgentGatewayClient({baseUrl:o.agentGatewayUrl,identity:agentIdentityFromKeyPair(agentKeys),onRetry:({method:t,path:r,attempt:o,delayMs:a,error:n})=>e.logger.warn?.(`[beeos-claw] Agent Gateway ${t} ${r} retry ${o} in ${a}ms: ${n instanceof Error?n.message:String(n)}`)}):null;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 agentGateway&&(setUploadDeps({gateway:agentGateway,logger:e.logger}),registerLocalFileExtractor(browserScreenshotExtractor),registerLocalFileExtractor(deliverableFileExtractor),e.logger.info?.("[beeos-claw] Upload deps + local file extractors registered")),lastOpenclawConfig=t,sharedWriteObs=n,setOpenClawConfigSnapshot(t),{cfg:o,writeObs:n,thirdPartyChannels:a,openclawConfig:t}}function registerUploadTools(e,t,r){const{cfg:o,writeObs:a}=t,n=Boolean(pluginCloudRuntimeTarget(e));if(n){if(!e.on)throw new Error("native output Files Host hook unavailable");e.on("before_tool_call",nativeOutputToolCallGate)}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,i){const s=n?consumeNativeOutputTool(t):void 0,l=await executeUpload(r,agentGateway,i,s?(e,t)=>uploadNativeOutputFile(e,o.agentGatewayUrl,s,t,i,BEEOS_UPLOAD_REQUEST_TIMEOUT_MS):void 0,e.logger);return e.logger.debug?.(`[beeos_upload_file] toolCallId=${t} ok=${l.ok}`),a({component:"upload-tool",domain:"tool",name:l.ok?"upload.success":"upload.failure",severity:l.ok?"info":"warn",payload:{toolCallId:t,ok:l.ok,fileCount:l.files?.length??0,error:l.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})(l)}}),e.registerTool({name:BEEOS_DESKTOP_SCREENSHOT_TOOL_NAME,description:BEEOS_DESKTOP_SCREENSHOT_TOOL_DESCRIPTION,parameters:BEEOS_DESKTOP_SCREENSHOT_TOOL_SCHEMA,async execute(t,r,o){e.logger.debug?.(`[beeos_desktop_screenshot] toolCallId=${t} params=${JSON.stringify(r)}`);const n=await executeDesktopScreenshot(r,agentGateway,o);return e.logger.debug?.(`[beeos_desktop_screenshot] toolCallId=${t} ok=${n.ok}`),a({component:"desktop-screenshot-tool",domain:"tool",name:n.ok?"desktop_screenshot.success":"desktop_screenshot.failure",severity:n.ok?"info":"warn",payload:{toolCallId:t,ok:n.ok,path:n.path,fileCount:n.files?.length??0,error:n.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})(n)}}),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,o){e.logger.debug?.(`[beeos_list_files] toolCallId=${t} params=${JSON.stringify(r)}`);const n=await executeListFiles(r,agentGateway,o);return a({component:"file-list-tool",domain:"tool",name:n.ok?"list_files.success":"list_files.failure",severity:n.ok?"info":"warn",payload:{toolCallId:t,ok:n.ok,fileCount:n.files?.length??0,error:n.error}}),n}}),e.registerTool({name:BEEOS_FETCH_FILE_TOOL_NAME,description:BEEOS_FETCH_FILE_TOOL_DESCRIPTION,parameters:BEEOS_FETCH_FILE_TOOL_SCHEMA,async execute(t,r,o){e.logger.debug?.(`[beeos_fetch_file] toolCallId=${t} params=${JSON.stringify(r)}`);const n=await executeFetchFile(r,agentGateway,{signal:o,resolvePath:e.resolvePath});return a({component:"file-fetch-tool",domain:"tool",name:n.ok?"fetch_file.success":"fetch_file.failure",severity:n.ok?"info":"warn",payload:{toolCallId:t,ok:n.ok,path:n.path,size:n.size,error:n.error}}),n}})}function registerServices(e,t,r){const{cfg:o,writeObs:a,thirdPartyChannels:n}=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("before_tool_call",(e,t)=>{"ask_user"===(e.toolName||t.toolName)&&sharedQuestionBridge.observeAskUser({sessionKey:t.sessionKey,runId:t.runId??e.runId,toolCallId:t.toolCallId??e.toolCallId})}),e.on("after_tool_call",(t,r)=>{const o=r.sessionKey,a=t.toolCallId??r.toolCallId,n=t.toolName||r.toolName;if(!o||!a||!n)return void e.logger.debug?.(`[after_tool_call] ignored structured result without correlation sessionKey=${o??"missing"} toolCallId=${a??"missing"}`);observeToolCallForDelivery(t,o);sharedToolResultObservationRouter.route(o,{toolCallId:a,toolName:n,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?Buffer.from(agentKeys.publicKey).toString("base64"):void 0,s=join(process.env.OPENCLAW_HOME||join(homedir(),".openclaw"),"beeos"),l=sharedInboundExecutionJournal??=new FileInboundExecutionJournal(join(s,"inbound-execution-journal.v1.jsonl"));sharedMcpProjection??=new OpenClawNativeMcpProjection,!sharedMcpCredentialVault&&agentKeys&&i&&(sharedMcpCredentialVault=new LocalEncryptedCredentialReferenceVault(join(s,"mcp-credential-vault.json"),i,agentKeys.privateKey));const c=pluginCloudRuntimeTarget(e);if(setClaimedCloudRuntimeInstanceId(c),c&&(!agentKeys||!agentGateway))throw new Error("Cloud runtime authority requires an agent identity and gateway transport");const u=sharedRuntimeAuthority?.cloudTargetInstanceId;if(u&&c&&u!==c)throw new Error("Cloud runtime authority target changed during process lifetime");if(sharedRuntimeAuthority&&!u&&c&&sharedRuntimeAuthority.bindCloudTarget(c),!sharedRuntimeAuthority&&agentKeys&&agentGateway&&(sharedRuntimeAuthority=new RuntimeProductionAuthority({stateDirectory:s,gateway:agentGateway,keys:agentKeys,targetInstanceId:c,logger:e.logger,getMSConnectionStatus:e=>sharedMSConnectionStatus.get(e)})),sharedGatewayClient||(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}}),sharedGatewayClient.on("gateway-event",(e,t)=>{sharedApprovalBridge.onGatewayEvent(e,t),sharedQuestionBridge.onGatewayEvent(e,t)})),approvalLogger??=e.logger,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()&&agentGateway?{automationWake:!0}:{}},!sharedAgentProjection&&sharedRuntimeAuthority&&agentGateway){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({gateway:agentGateway,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 g=sharedRuntimeOwner;sharedSessionMutationCoordinator??=new SessionMutationCoordinator(sharedSessionWriteGate,new OpenClawSessionWriteQuiescence(e.logger,{agentId:o.gateway.agentId||"main"}),e.logger,{onRecoveryRequired:t=>{scheduleProcessRuntimeRestart(t,e.logger)}});const d=sharedSessionMutationCoordinator;if(!sharedAcpServer){const t=sharedRuntimeAuthority&&agentGateway?new AgentGatewayTemplateOriginReporter({gateway:agentGateway}):void 0;sharedAcpServer=new ACPServer(o,e.logger,d,a,agentGateway??void 0,i,n,sharedTerminalManager,sharedRuntimeAuthority,void 0,sharedMcpProjection,sharedMcpCredentialVault,{gateway:g.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:a})),runExclusive:(t,r,o)=>sharedImHotPathDeferral.runExclusive(t,r,e.logger,o)})}const p=sharedAcpServer,m=()=>sharedAgentMappings.snapshot()?.values().next().value;r.current=p,sharedMcpNativeProjectionNudge.request({registrationMode:e.registrationMode,fingerprint:fingerprintMcpServers(t.openclawConfig),logger:e.logger});const h=sharedPromptRunStore,{enabled:y,principalId:v,a2aEnabled:f}=o.messageBus;let w;!c&&o.agentGatewayUrl&&agentKeys&&(w=createBeeosClawMessageTokenProvider({agentGatewayUrl:o.agentGatewayUrl,identity:agentKeyPairToIdentity(agentKeys)}));const C=process.env.MESSAGE_RUNTIME_DELIVERY_KEY?.trim(),R=v||process.env.BEEOS_PRINCIPAL_ID||"",b=w?sharedMessageClientOwner.configure({tokenProvider:w,identityId:R,...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,S=c&&o.messageBus.serviceUrl&&sharedRuntimeAuthority?new NodeRuntimeDeliveryPort({serviceOrigin:async()=>o.messageBus.serviceUrl},{currentLease:()=>{const e=sharedRuntimeAuthority?.authorityLease,t=sharedRuntimeAuthority?.currentConsumerBinding();return e&&t&&e.instanceId===c?{...e,journalStoreId:t.journalStoreId,journalGeneration:t.journalGeneration}:null},subscribeAuthority:e=>sharedRuntimeAuthority.subscribeAuthority(e)}):void 0;attachNativeCloudChat(e,o);const _=sharedRuntimeAuthority,O=o.messageBus.serviceUrl;b&&_&&O&&C?p.setCronConversationDelivery({deliver:async e=>{const t=_.authorityLease;if(!t)throw new Error("current runtime authority lease unavailable for cron delivery");const r=new MessageServiceCronDeliveryHttpAuthority({messageServiceUrl:O,scopedDeliveryKey:C,fetchAuthorized:(e,t)=>fetchRuntimeLeaseAttempt(e,t??{})});return await r.deliver({...e,runtimeLeaseCredential:t.runtimeLeaseCredential})}}):p.setCronConversationDelivery(void 0);const E=sharedRuntimeAuthority?.journal??new FileRuntimeCommandJournal(join(s,"runtime-command-journal.json"),void 0,void 0,{onMaintenanceError:t=>e.logger.error?.(`[runtime-journal] event=checkpoint_failed trigger=threshold error=${t instanceof Error?t.message:String(t)}`)}),A=g.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 a=await createRuntimeServiceProductionCandidate({...agentGateway?{authorizationAdapter:new AgentGatewayGrantJwksReadinessAdapter({gateway:agentGateway})}:{},onGrantJwksWarm:t=>{t.ready?e.logger.info?.("[beeos-claw] Gateway grant keyset loaded"):e.logger.warn?.(`[beeos-claw] Gateway grant keyset not ready yet (attempt ${t.attempt}): ${t.error instanceof Error?t.error.message:String(t.error)}`)},...b&&agentGateway&&C?{transportAdapter:new RuntimeServiceTransportReadinessAdapter({gateway:agentGateway,messageService:b.nodeTransport,messageClient:b.client,configuredServiceUrl:o.messageBus.serviceUrl,scopedDeliveryKey:C})}:{},...S&&agentGateway&&o.messageBus.serviceUrl?{cloudTransportAdapter:new CloudRuntimeServiceTransportReadinessAdapter({gateway:agentGateway,configuredServiceUrl:o.messageBus.serviceUrl,runtimeDelivery:S,...sharedNativeCloudChat?{sessionNewConversation:sharedNativeCloudChat.sessionNewConversation,nativePrompt:{http:sharedNativeCloudChat.http,inputFiles:new NativeInputFilesHttpClient(o.agentGatewayUrl,()=>sharedRuntimeAuthority.authorityLease,(e,t)=>fetch(e,t)),runBoundary:new MessageServiceConversationRunBoundary({transport:{start:e=>S.startConversationRun(e),finish:e=>S.finishConversationRun(e)}}),host:{resolveNativePromptSessionKey:(e,t)=>p.resolveNativePromptSessionKey(e,t),prepareNativePromptModel:(e,t,r)=>p.prepareNativePromptModel(e,t,r),runtime:e.runtime,pluginConfig:o,logger:e.logger,promptRunStore:h,assistantMessageCommitRouter:sharedAssistantMessageCommitRouter,toolResultObservationRouter:sharedToolResultObservationRouter,approvalStreams:sharedApprovalBridge,questionStreams:sharedQuestionBridge,modelCallObservationRouter:sharedModelCallObservationRouter,sessionMutationCoordinator:d,abortTransport:{chatAbort:(e,t)=>p.chatAbort(e,t)},activeRunAuthority:p.getActiveRunAuthority(),waitIfExclusive:(t,r)=>sharedImHotPathDeferral.waitIfExclusive(t,e.logger,r).then(()=>{})}}}:{}})}:{},commandJournal:E,poisonJournal:new FileRuntimePoisonDeliveryJournal(join(s,"runtime-poison-deliveries.json")),onConsumerDeliveryError:t=>{const r=null!==t&&"object"==typeof t?t:{},o=e=>"string"==typeof e&&e.length>0&&e.length<=256&&!/[\u0000-\u001f\u007f]/.test(e)?JSON.stringify(e):JSON.stringify("unavailable"),a="string"==typeof r.code&&/^[A-Za-z0-9_.:-]{1,64}$/.test(r.code)?r.code:"unavailable",n="number"==typeof r.status&&Number.isInteger(r.status)&&r.status>=100&&r.status<=599?String(r.status):"unavailable";e.logger.error?.(`[runtime-service] delivery worker failed before acknowledgement; delivery retained for retry: operation_id=${o(r.operationId)} delivery_id=${o(r.deliveryId)} code=${a} status=${n}`)},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"===a.status?a.serviceMethods:[]),sharedRuntimeAuthority?.setRuntimeResourceBindingContractRevision("ready"===a.status?a.resourceBindingContractRevision:void 0),p.setRuntimeRpcConsumerFactory("ready"===a.status?a.factory:void 0),"unavailable"===a.status?e.logger.warn?.("[runtime-service] capability fenced: "+a.blockers.map(e=>e.id).join(",")):a.capabilityBlockers.length&&e.logger.warn?.("[runtime-service] method capability fenced: "+a.capabilityBlockers.map(e=>e.method).join(",")),!o.bridge.url&&agentGateway){const t=Date.now(),r=await agentGateway.bridge.config();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 g.acquire(A);try{await p.start(o.bridge.url||void 0)}catch(e){throw await g.release(A),e}e.logger.info?.(`[beeos-claw] ACP Server started (total service start: ${Date.now()-t}ms)`)},async stop(){try{await g.release(A)}finally{await p.stop()}await E.checkpoint(),e.logger.info?.("[runtime-service] command journal checkpoint completed"),e.logger.info?.("[beeos-claw] ACP Server stopped")}});if(y&&void 0!==w&&w){const t=sharedMessageClientOwner.createRegistrationToken(),r=async(e,t)=>{const r=await w(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}},o=(e,t,r)=>({getLocalAgentId:()=>t,resolveLocalAgentId:e=>e===r?t:void 0,getDefaultPlatformAgent:()=>({instanceId:e.principalId}),ensureImInboundSession:(e,t,r,o)=>{p.ensureImInboundSession(e,t,r,o)},chatAbort:(e,t)=>p.chatAbort(e,t)}),n=sharedRuntimeAuthority?new RuntimeAuthorityMessageBusLifecycle({authority:sharedRuntimeAuthority,logger:e.logger,bus:{connect:async n=>{await sharedMessageClientOwner.acquire(t,async t=>{const i=await r(n.instanceId,n.runtimeEpoch),s=t.client,c={leaseId:n.leaseId,runtimeEpoch:n.runtimeEpoch};sharedMSConnectionStatus.bind(c);const u=new Map,g=new MessageProjectionInboundConsumer({client:s,bindings:()=>{const t=sharedRuntimeAuthority?.authorityLease;if(!t||t.instanceId!==i.principalId||t.runtimeEpoch!==i.runtimeEpoch)return[];const r=sharedAgentMappings.snapshot();return r?[...r.entries()].flatMap(([r,n])=>{if(!n.id||n.instanceId!==i.principalId)return[];let l=u.get(n.id);return l&&l.localAgentId===r||(l={localAgentId:r,inbound:createImInboundHandler({automationTurnPolicy:sharedAutomationTurnPolicy,runtime:e.runtime,pluginConfig:e.pluginConfig,client:s,localServer:o(i,r,n.id),promptRunStore:h,waitIfExclusive:(t,r)=>sharedImHotPathDeferral.waitIfExclusive(t,e.logger,r).then(()=>{}),assistantMessageCommitRouter:sharedAssistantMessageCommitRouter,toolResultObservationRouter:sharedToolResultObservationRouter,approvalStreams:sharedApprovalBridge,questionStreams:sharedQuestionBridge,modelCallObservationRouter:sharedModelCallObservationRouter,sessionMutationCoordinator:d,logger:e.logger,writeObs:a,principalIdOverride:i.principalId,activeRunAuthority:p.getActiveRunAuthority(),applyTurnModel:(e,t)=>p.applyImTurnModel(e,t),restoreTurnModel:e=>p.restoreImTurnModel(e),...agentGateway?{completeA2ATask:(t,r,o)=>completeA2ATask({gateway:agentGateway,logger:e.logger},t,r,o)}:{}})},u.set(n.id,l)),[{principalId:i.principalId,targetAgentId:n.id,runtimeEpoch:t.runtimeEpoch,inbound:l.inbound}]}):[]},executionJournal:l,logger:e.logger}),m=sharedAgentMappings.subscribe(()=>{sharedActiveMessageConsumer===g&&g.refresh()}),y=sharedRuntimeAuthority?.subscribeAuthority(()=>{sharedActiveMessageConsumer===g&&g.refresh()})??(()=>{}),v=new MessageClientTerminalStateRecovery({logger:e.logger,isCurrent:()=>{if(sharedActiveMessageConsumer!==g)return!1;const e=sharedRuntimeAuthority?.authorityLease;return null!=e&&e.instanceId===i.principalId&&e.runtimeEpoch===i.runtimeEpoch&&e.leaseId===n.leaseId&&e.handlerIdentity===n.handlerIdentity},reconnect:async()=>{if("failed"===s.getSnapshot().connection){await s.connect();try{await p.rebindRuntimeDelivery()}catch(t){e.logger.warn?.(`[beeos-claw] runtime Service delivery rebind failed after MessageClient recovery: ${t instanceof Error?t.message:String(t)}`)}}}});let f=s.getSnapshot().connection;sharedMSConnectionStatus.setIfCurrent(c,f);const w=s.subscribe(()=>{const t=s.getSnapshot();t.connection!==f&&(f=t.connection,sharedMSConnectionStatus.setIfCurrent(c,t.connection),"connected"===t.connection?(v.notifyConnected(),e.logger.info?.(`[beeos-claw] connection transport=message_service_realtime state=connected localAgentId=${p.getLocalAgentId()} principalId=${i.principalId}`)):"failed"===t.connection?(e.logger.warn?.(`[beeos-claw] connection transport=message_service_realtime state=failed principalId=${i.principalId} recoveryError=${t.recoveryError??"unknown"}`),v.notifyFailed()):"disconnected"!==t.connection&&"reconnecting"!==t.connection||e.logger.warn?.(`[beeos-claw] connection transport=message_service_realtime state=${t.connection} principalId=${i.principalId}; SDK owns reconnect/recovery`))});sharedActiveMessageConsumer=g,g.start();try{await s.connect()}catch(e){throw sharedActiveMessageConsumer===g&&(sharedActiveMessageConsumer=void 0),sharedMSConnectionStatus.resetIfCurrent(c),v.dispose(),await g.stop(),m(),y(),w(),await s.disconnect().catch(()=>{}),e}try{await p.rebindRuntimeDelivery()}catch(t){e.logger.warn?.(`[beeos-claw] runtime Service delivery rebind failed after MessageClient connect: ${t instanceof Error?t.message:String(t)}`)}return async()=>{sharedActiveMessageConsumer===g&&(sharedActiveMessageConsumer=void 0),sharedMSConnectionStatus.resetIfCurrent(c),v.dispose(),await g.stop(),y(),w(),await s.disconnect(),e.logger.info?.("[beeos-claw] MessageClient disconnected")}})},disconnect:async()=>{await sharedMessageClientOwner.release(t)}}}):void 0;e.registerService({id:"beeos-message-bus",async start(){n?n.start():e.logger.warn?.("[beeos-claw] Message bus requires runtime authority; waiting is unavailable without Agent Gateway runtime identity")},async stop(){await(n?.stop())}})}else y?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=${y})`);if(o.messageBus.enabled&&c&&!sharedNativeCloudChat&&e.logger.warn?.("[beeos-claw] Cloud message bus enabled but native composition unavailable (messageBus.serviceUrl or runtime authority missing) — disabled"),!1!==f&&agentGateway){const t=e=>p.getGatewaySessionKeyForToolCall(e),r=e=>p.getMentionsForToolCall(e),o=(e,t)=>{p.broadcastNotification(e,t)},a=new TaskHandleRegistry;registerA2ATools(e,{gateway:agentGateway,logger:e.logger},a,t,r,o,()=>m()?.instanceId),e.logger.info?.("[beeos-claw] A2A tools registered (beeos_discover_agents, beeos_call_agent) via REST")}if(agentGateway&&(registerAutomationTools(e,{gateway:agentGateway,callConnectorTool:harnessConnectorToolCaller(),logger:e.logger,getDefaultAgent:()=>{const e=m();return e?{id:e.id,instanceId:e.instanceId}:void 0},getCurrentConversationId:e=>{const t=p.getGatewaySessionKeyForToolCall(e);return t?extractBeeosConversationIdFromSessionKey(t):void 0}}),e.logger.info?.("[beeos-claw] Automation tools registered (create/create_webhook/capabilities/preflight/list/get/run/runs/get_run)")),o.canvasEnabled){sharedCanvasState||(sharedCanvasState=new CanvasStateManager);const t=sharedCanvasState;if(agentGateway&&agentKeys){t.setLogger(e.logger);const r=()=>agentGateway.canvas.token(),a=a=>{const n=new CanvasRelayClient({canvasRelayUrl:o.canvasRelayUrl,agentKeys:agentKeys,agentId:a.id,displayName:a.displayName,avatarUrl:a.avatarUrl,tokenProvider:r,onConnected:()=>p.flushPendingCanvasLinks(),onReconnect:()=>p.onRelayReconnected(),logger:e.logger});t.setRelayClient(n),t.setAgentId(a.id),p.wireRelayActionHandler(),n.connect(),e.logger.info?.(`[beeos-claw] Canvas Relay multiplexed client connected (agentId=${a.id}, name=${a.name??"none"})`)};e.registerService(createCanvasRelayService({getAgent:m,onceAgentReady:e=>sharedAgentMappings.onceReady(e),initRelay:a,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=>a(e),getActiveSessionId:e=>p.getSessionIdForToolCall(e),getPlatformAgentId:e=>p.getPlatformAgentIdForToolCall(e),getGatewaySessionKey:e=>p.getGatewaySessionKeyForToolCall(e),waitForAgentSync:()=>sharedAgentMappings.waitForSynchronization(),publishStaticHtml:createStaticCanvasHtmlPublisher(pluginCloudRuntimeTarget(e)?o.agentGatewayUrl:void 0)},i=new FormSchemaRegistry,s=buildCronSchemas(n);i.register(s.create),i.register(s.update);const l=new ResourceDescriptorRegistry;l.register(CRON_RESOURCE);const c=e=>async(t,r,o)=>(p.preMapToolCallSession(t),e(t,r));e.registerTool({name:RENDER_UI_TOOL_NAME,description:RENDER_UI_TOOL_DESCRIPTION,parameters:RENDER_UI_TOOL_SCHEMA,execute:c(createRenderUiToolExecutor(r))}),e.registerTool({name:CLOSE_UI_TOOL_NAME,description:CLOSE_UI_TOOL_DESCRIPTION,parameters:CLOSE_UI_TOOL_SCHEMA,execute:c(createCloseUiHandler(r))}),p.setCanvasState(t,{schemaRegistry:i,descriptorRegistry:l});const u=p.getFormBuilder();u&&e.registerTool({name:RENDER_FORM_TOOL_NAME,description:buildRenderFormDescription(i),parameters:RENDER_FORM_TOOL_SCHEMA,execute:c(createRenderFormHandler({registry:i,builder:u,logger:e.logger}))});const g=p.getResourceRenderer();if(g){generateActionTools(l,e,async(e,t)=>p.gatewayRequest(e,t),g,e.logger)}const d=buildToolRoutingHint(BEEOS_FILE_RESOURCE_ROUTING_RULE,BEEOS_DESKTOP_SCREENSHOT_TOOL_NAME),h=new Set([RENDER_UI_TOOL_NAME,CLOSE_UI_TOOL_NAME,RENDER_FORM_TOOL_NAME]),y=256,v=new Map;if("function"==typeof e.on){try{e.on("before_prompt_build",(e,t)=>{if(t?.sessionKey){if(v.size>=y&&!v.has(t.sessionKey)){const e=v.keys().next().value;void 0!==e&&v.delete(e)}v.set(t.sessionKey,t.channelId),rememberDeliveryWorkspace(t.sessionKey,t.workspaceDir)}const r=isThirdPartyChannel(n,{sessionKey:t?.sessionKey,channelId:t?.channelId});return t?.sessionKey||r?{appendSystemContext:buildAppendSystemContext({thirdParty:r,canvasSystemPrompt:`${CANVAS_PRESENTATION_GATE}\n\n${CANVAS_SYSTEM_PROMPT}`,toolRoutingHint:d})}:{appendSystemContext:d}}),e.logger.debug?.("[beeos-claw] Canvas system prompt hook registered: before_prompt_build")}catch{e.logger.debug?.("[beeos-claw] before_prompt_build hook not supported")}try{e.on("before_tool_call",(e,t)=>{if(t?.sessionKey&&p.setPendingToolCallSessionKey(t.sessionKey),!h.has(e.toolName))return;const r=t?.sessionKey?v.get(t.sessionKey):void 0,o=isThirdPartyChannel(n,{sessionKey:t?.sessionKey,channelId:r}),a=canvasToolBlockReason({thirdParty:o,hasSession:Boolean(t?.sessionKey)});return a?{block:!0,blockReason:a}: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:a});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}`)}}function pluginCloudRuntimeTarget(e){const t=e.pluginConfig?.cloudRuntimeInstanceId;return cloudRuntimeTargetInstanceId("string"==typeof t?t:void 0)}function attachNativeCloudChat(e,t){if(sharedNativeChatService)return e.registerService(sharedNativeChatService),sharedNativeCloudChat;const r=sharedRuntimeAuthority,o=t.messageBus.serviceUrl,a=sharedAcpServer,n=sharedSessionMutationCoordinator;if(!(r&&r.cloudTargetInstanceId&&o&&a&&n))return sharedNativeCloudChat;const i=sharedNativeCloudChat??createNativeCloudChatComposition({authority:nativeCloudChatAuthorityFromRuntimeAuthority(r),serviceUrl:o,logger:e.logger});if(sharedNativeCloudChat=i,!t.messageBus.enabled)return i;const s=i,l=r;let c;const u=new Map,g=new RuntimeAuthorityMessageBusLifecycle({authority:l,logger:e.logger,bus:{connect:async t=>{c?.(),u.clear(),c=s.adapter.listen({eventTypes:["message.created"]},r=>{if(r.type===APPROVAL_DECISION_MESSAGE_TYPE&&r.senderId?.startsWith("user:")&&isApprovalDocument(r.content))return void sharedApprovalBridge.onDecision(r.content);if("user.continue"===r.type&&isQuestionResponseDocument(r.content))return void sharedQuestionBridge.onResponse(r);const o=(r.content??{}).metadata??{},i="string"==typeof o.target_agent_id?o.target_agent_id:"",l=i?((t,r)=>{const o=sharedAgentMappings.snapshot();if(o)for(const[i,l]of o.entries()){if(l.id!==r)continue;if(l.instanceId!==t.instanceId)continue;const o=`${i}\0${l.id}`;let c=u.get(o);return c||(c={localAgentId:i,inbound:createImInboundHandler({runtime:e.runtime,pluginConfig:e.pluginConfig,client:s.adapter,localServer:{getLocalAgentId:()=>i,resolveLocalAgentId:e=>e===l.id?i:void 0,getDefaultPlatformAgent:()=>({instanceId:t.instanceId}),ensureImInboundSession:(e,t,r,o)=>{a.ensureImInboundSession(e,t,r,o)},chatAbort:(e,t)=>a.chatAbort(e,t)},promptRunStore:sharedPromptRunStore,waitIfExclusive:(t,r)=>sharedImHotPathDeferral.waitIfExclusive(t,e.logger,r).then(()=>{}),assistantMessageCommitRouter:sharedAssistantMessageCommitRouter,toolResultObservationRouter:sharedToolResultObservationRouter,approvalStreams:sharedApprovalBridge,questionStreams:sharedQuestionBridge,modelCallObservationRouter:sharedModelCallObservationRouter,sessionMutationCoordinator:n,logger:e.logger,writeObs:sharedWriteObs,principalIdOverride:t.instanceId,activeRunAuthority:a.getActiveRunAuthority(),applyTurnModel:(e,t)=>a.applyImTurnModel(e,t),restoreTurnModel:e=>a.restoreImTurnModel(e),...agentGateway?{completeA2ATask:(t,r,o)=>completeA2ATask({gateway:agentGateway,logger:e.logger},t,r,o)}:{}})},u.set(o,c)),c}})(t,i):void 0;if(!l)return void e.logger.warn?.(`[beeos-claw] native cloud chat dropped unresolvable target_agent_id=${i||"missing"} message_id=${r.id}`);("chat_cancel"===r.type?l.inbound.onChatCancel(r):l.inbound.onChatMessage(r)).catch(t=>{e.logger.error?.(`[beeos-claw] native cloud chat dispatch failed message_id=${r.id}: ${t instanceof Error?t.message:String(t)}`)})});try{await s.adapter.connect()}catch(e){throw c?.(),c=void 0,e}},disconnect:async()=>{c?.(),c=void 0,u.clear(),await s.adapter.disconnect()}}});return sharedNativeChatService={id:"beeos-native-cloud-chat",async start(){g.start()},async stop(){await g.stop()}},e.registerService(sharedNativeChatService),e.logger.info?.("[beeos-claw] native Cloud personal-chat composition registered"),i}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=pluginCloudRuntimeTarget(e),r=void 0!==t&&t!==sharedRuntimeAuthority?.cloudTargetInstanceId,o=planPluginRegistration(e.registrationMode,_fullRuntimeRegistered,r);if("skip"!==o){_initializing=!0;try{if(!hasRuntimeReplyApi(e.runtime))throw e.logger.error?.("[beeos-claw] FATAL: OpenClaw runtime.channel.reply.dispatchReplyFromConfig not exposed. Upgrade OpenClaw or revert beeos-claw."),new Error("openclaw_runtime_reply_api_unavailable");const t=(t,r)=>{registerUploadTools(e,t,r),registerArouterLatencyRouting(e),registerArouterMediaProviders(e)};if("capabilities"===o&&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 r={current:sharedAcpServer??null};return t(sharedPluginContext,r),registerServices(e,sharedPluginContext,r),void e.logger.info?.(`[beeos-claw] capability re-register mode=${e.registrationMode} capabilities_only`)}const r=initializePlugin(e);if(!r)return;const a={current:null};installUnhandledRejectionObserver(e,r.writeObs,a),ensureRegisterToolCache(e),cacheRuntimeRegistrations(e),cachedCapabilityTools.length=0,t(r,a),agentGateway&&(e.on?e.on("before_agent_finalize",fileDeliveryFinalizeDecision):e.logger.warn?.("[file-delivery] OpenClaw plugin hooks unavailable; finalization check disabled")),registerServices(e,r,a),sharedPluginContext=r,marksFullRuntimeRegistered(e.registrationMode,o)&&(_fullRuntimeRegistered=!0),_pluginRegisteredAt=Date.now(),e.logger.info?.("[beeos-claw] Plugin registered successfully")}finally{_initializing=!1}}else if(!e.registrationMode||"full"===e.registrationMode){const t=resolveCapabilityToolReplay({currentRegisterTool:e.registerTool,wrappedRegisterTool:wrappedRegisterTool,originalRegisterTool:rawRegisterTool});replayCachedPluginTools(r=>t.call(e,r),cachedCapabilityTools),replayRuntimeRegistrations(e),registerArouterLatencyRouting(e),registerArouterMediaProviders(e)}}};export default plugin;
1
+ import{harnessConnectorToolCaller}from"./connectors/harness-connector-mcp.js";import{consumeNativeOutputTool,nativeHtmlDeliveryForRun}from"./runtime/native-output-file-attempt.js";import{uploadNativeOutputFile}from"./upload/upload-file.js";import{nativeOutputToolCallGate}from"./upload/native-output-tool-hook.js";import{NativeInputFilesHttpClient}from"./runtime/native-input-files-http.js";import{fileDeliveryFinalizeDecision}from"./upload/finalize-hook.js";import{resolveConfigWithMeta,mirrorOpenClawConfig}from"./config.js";import{hasRuntimeReplyApi}from"./runtime/reply-router.js";import{PromptRunStore}from"./runtime/prompt-run-store.js";import{createMcpNativeProjectionNudge,fingerprintMcpServers}from"./acp-gateway/mcp-native-projection-nudge.js";import{createImHotPathDeferral}from"./runtime/im-hot-path-deferral.js";import{SessionWriteGate}from"./acp-gateway/session-write-gate.js";import{OpenClawSessionWriteQuiescence}from"./runtime/openclaw-session-write-quiescence.js";import{SessionMutationCoordinator}from"./runtime/session-mutation-coordinator.js";import{scheduleProcessRuntimeRestart}from"./runtime/runtime-restart-scheduler.js";import{createImInboundHandler}from"./im-inbound-handler.js";import{AgentGatewayClient,NodeRuntimeDeliveryPort,agentIdentityFromKeyPair,loadAgentKeyPair,signAgentMessage}from"@beeos-ai/cloud-harness-sdk";import{MessageServiceConversationRunBoundary}from"./runtime/message-service-conversation-run-boundary.js";import{AutomationTurnPolicy}from"./automation/turn-policy.js";import{registerAutomationTools}from"./automation/tools.js";import{extractBeeosConversationIdFromSessionKey}from"./runtime/openclaw-session-key-resolution.js";import{setOpenClawConfigSnapshot}from"./runtime/openclaw-session-model.js";import{BeeosClawMessageClientProcessOwner,createBeeosClawMessageTokenProvider}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{createNativeCloudChatComposition,nativeCloudChatAuthorityFromRuntimeAuthority}from"./runtime/native-cloud-chat-composition.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,REQUEST_TIMEOUT_MS as BEEOS_UPLOAD_REQUEST_TIMEOUT_MS,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{readDiscoveryCache,writeDiscoveryCache}from"./discovery-cache.js";import{setClaimedCloudRuntimeInstanceId,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{CanvasStateManager}from"./canvas/state.js";import{createStaticCanvasHtmlPublisher}from"./canvas/static-html.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,createRenderUiToolExecutor,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,cloudRuntimeTargetInstanceId}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,CloudRuntimeServiceTransportReadinessAdapter,RuntimeServiceTransportReadinessAdapter,createRuntimeServiceProductionCandidate}from"./runtime/runtime-service-production-candidate.js";import{fetchRuntimeLeaseAttempt}from"./runtime/message-service-runtime-authenticated-fetch.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{OpenClawApprovalBridge}from"./approval/bridge.js";import{APPROVAL_DECISION_MESSAGE_TYPE,isApprovalDocument}from"./approval/protocol.generated.js";import{isQuestionResponseDocument,OpenClawQuestionBridge}from"./question/bridge.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,agentGateway=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;let approvalLogger;const sharedApprovalBridge=new OpenClawApprovalBridge({request:(e,t)=>sharedGatewayClient?sharedGatewayClient.request(e,t):Promise.reject(new Error("Not connected to Gateway")),logger:{info:e=>approvalLogger?.info?.(e),warn:e=>approvalLogger?.warn?.(e)}}),sharedQuestionBridge=new OpenClawQuestionBridge({request:(e,t)=>sharedGatewayClient?sharedGatewayClient.request(e,t):Promise.reject(new Error("Not connected to Gateway")),logger:{info:e=>approvalLogger?.info?.(e),warn:e=>approvalLogger?.warn?.(e)}}),sharedModelCallObservationRouter=new ModelCallObservationRouter;let sharedSessionMutationCoordinator,sharedRuntimeAuthority,sharedNativeChatService,sharedNativeCloudChat,sharedWriteObs=()=>{};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 cachedRuntimeServices=[],cachedRuntimeHooks=[];let rawRegisterService,wrappedRegisterService,rawOn,wrappedOn;function cacheRuntimeRegistrations(e){const t=e.registerService===wrappedRegisterService?rawRegisterService:e.registerService;if(rawRegisterService=t,wrappedRegisterService=r=>{cachedRuntimeServices.push(r),t.call(e,r)},e.registerService=wrappedRegisterService,cachedRuntimeServices.length=0,cachedRuntimeHooks.length=0,e.on){const t=e.on===wrappedOn?rawOn:e.on;rawOn=t,wrappedOn=(...r)=>{cachedRuntimeHooks.push(r),t.call(e,...r)},e.on=wrappedOn}}function replayRuntimeRegistrations(e){const t=e.registerService===wrappedRegisterService?rawRegisterService:e.registerService,r=e.on===wrappedOn?rawOn:e.on;for(const t of cachedRuntimeHooks)r.call(e,...t);for(const r of cachedRuntimeServices)t.call(e,r)}const ensureRegisterToolCache=e=>{const t=e.registerTool===wrappedRegisterTool?rawRegisterTool:e.registerTool;rawRegisterTool=t.bind(e),wrappedRegisterTool=e=>(cachedCapabilityTools.push(e),rawRegisterTool(e)),e.registerTool=wrappedRegisterTool};function agentKeyPairToIdentity(e){return{publicKeyBase64:()=>Buffer.from(e.publicKey).toString("base64"),sign:async t=>signAgentMessage(t,e.privateKey)}}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),a=r instanceof Error?r.stack:void 0;e.logger.error?.(`[beeos-claw] unhandledRejection: ${o}${a?`\n${a}`:""}`),t({component:"plugin",domain:"fatal",name:"unhandled_rejection",severity:"error",summary:o,error:{code:"unhandled_rejection",message:o,...a?{stack:a}:{}}})}),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,a=buildThirdPartyChannelSet(t);a.size>0&&e.logger.info?.(`[beeos-claw] Third-party channels detected: ${[...a].join(", ")}`);const n=o.log.enabled?createObsEventWriter({dir:o.log.dir,redaction:{gatewayToken:o.gateway.token},logger:e.logger}):()=>{};n({component:"plugin",domain:"lifecycle",name:"lifecycle.startup",severity:"info",payload:{pluginVersion:PLUGIN_VERSION,pid:process.pid,nodeVersion:process.version,cwd:process.cwd()}}),n({component:"plugin",domain:"config",name:"config.resolved",severity:"info",payload:{config:o,sources:r.sources,openclawConfigPath:r.openclawConfigPath}});for(const t of r.validation)n({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),agentGateway=o.agentGatewayUrl?new AgentGatewayClient({baseUrl:o.agentGatewayUrl,identity:agentIdentityFromKeyPair(agentKeys),onRetry:({method:t,path:r,attempt:o,delayMs:a,error:n})=>e.logger.warn?.(`[beeos-claw] Agent Gateway ${t} ${r} retry ${o} in ${a}ms: ${n instanceof Error?n.message:String(n)}`)}):null;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 agentGateway&&(setUploadDeps({gateway:agentGateway,logger:e.logger}),registerLocalFileExtractor(browserScreenshotExtractor),registerLocalFileExtractor(deliverableFileExtractor),e.logger.info?.("[beeos-claw] Upload deps + local file extractors registered")),lastOpenclawConfig=t,sharedWriteObs=n,setOpenClawConfigSnapshot(t),{cfg:o,writeObs:n,thirdPartyChannels:a,openclawConfig:t}}function registerUploadTools(e,t,r){const{cfg:o,writeObs:a}=t,n=Boolean(pluginCloudRuntimeTarget(e));if(n){if(!e.on)throw new Error("native output Files Host hook unavailable");e.on("before_tool_call",nativeOutputToolCallGate)}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,i){const s=n?consumeNativeOutputTool(t):void 0,l=await executeUpload(r,agentGateway,i,s?(e,t)=>uploadNativeOutputFile(e,o.agentGatewayUrl,s,t,i,BEEOS_UPLOAD_REQUEST_TIMEOUT_MS):void 0,e.logger);return e.logger.debug?.(`[beeos_upload_file] toolCallId=${t} ok=${l.ok}`),a({component:"upload-tool",domain:"tool",name:l.ok?"upload.success":"upload.failure",severity:l.ok?"info":"warn",payload:{toolCallId:t,ok:l.ok,fileCount:l.files?.length??0,error:l.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})(l)}}),e.registerTool({name:BEEOS_DESKTOP_SCREENSHOT_TOOL_NAME,description:BEEOS_DESKTOP_SCREENSHOT_TOOL_DESCRIPTION,parameters:BEEOS_DESKTOP_SCREENSHOT_TOOL_SCHEMA,async execute(t,r,o){e.logger.debug?.(`[beeos_desktop_screenshot] toolCallId=${t} params=${JSON.stringify(r)}`);const n=await executeDesktopScreenshot(r,agentGateway,o);return e.logger.debug?.(`[beeos_desktop_screenshot] toolCallId=${t} ok=${n.ok}`),a({component:"desktop-screenshot-tool",domain:"tool",name:n.ok?"desktop_screenshot.success":"desktop_screenshot.failure",severity:n.ok?"info":"warn",payload:{toolCallId:t,ok:n.ok,path:n.path,fileCount:n.files?.length??0,error:n.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})(n)}}),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,o){e.logger.debug?.(`[beeos_list_files] toolCallId=${t} params=${JSON.stringify(r)}`);const n=await executeListFiles(r,agentGateway,o);return a({component:"file-list-tool",domain:"tool",name:n.ok?"list_files.success":"list_files.failure",severity:n.ok?"info":"warn",payload:{toolCallId:t,ok:n.ok,fileCount:n.files?.length??0,error:n.error}}),n}}),e.registerTool({name:BEEOS_FETCH_FILE_TOOL_NAME,description:BEEOS_FETCH_FILE_TOOL_DESCRIPTION,parameters:BEEOS_FETCH_FILE_TOOL_SCHEMA,async execute(t,r,o){e.logger.debug?.(`[beeos_fetch_file] toolCallId=${t} params=${JSON.stringify(r)}`);const n=await executeFetchFile(r,agentGateway,{signal:o,resolvePath:e.resolvePath});return a({component:"file-fetch-tool",domain:"tool",name:n.ok?"fetch_file.success":"fetch_file.failure",severity:n.ok?"info":"warn",payload:{toolCallId:t,ok:n.ok,path:n.path,size:n.size,error:n.error}}),n}})}function registerServices(e,t,r){const{cfg:o,writeObs:a,thirdPartyChannels:n}=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("before_tool_call",(e,t)=>{"ask_user"===(e.toolName||t.toolName)&&sharedQuestionBridge.observeAskUser({sessionKey:t.sessionKey,runId:t.runId??e.runId,toolCallId:t.toolCallId??e.toolCallId})}),e.on("after_tool_call",(t,r)=>{const o=r.sessionKey,a=t.toolCallId??r.toolCallId,n=t.toolName||r.toolName;if(t.runId&&r.runId&&t.runId!==r.runId||nativeHtmlDeliveryForRun(t.runId??r.runId)?.observe(n,t.params,t.result,Boolean(t.error)),!o||!a||!n)return void e.logger.debug?.(`[after_tool_call] ignored structured result without correlation sessionKey=${o??"missing"} toolCallId=${a??"missing"}`);observeToolCallForDelivery(t,o);sharedToolResultObservationRouter.route(o,{toolCallId:a,toolName:n,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?Buffer.from(agentKeys.publicKey).toString("base64"):void 0,s=join(process.env.OPENCLAW_HOME||join(homedir(),".openclaw"),"beeos"),l=sharedInboundExecutionJournal??=new FileInboundExecutionJournal(join(s,"inbound-execution-journal.v1.jsonl"));sharedMcpProjection??=new OpenClawNativeMcpProjection,!sharedMcpCredentialVault&&agentKeys&&i&&(sharedMcpCredentialVault=new LocalEncryptedCredentialReferenceVault(join(s,"mcp-credential-vault.json"),i,agentKeys.privateKey));const c=pluginCloudRuntimeTarget(e);if(setClaimedCloudRuntimeInstanceId(c),c&&(!agentKeys||!agentGateway))throw new Error("Cloud runtime authority requires an agent identity and gateway transport");const u=sharedRuntimeAuthority?.cloudTargetInstanceId;if(u&&c&&u!==c)throw new Error("Cloud runtime authority target changed during process lifetime");if(sharedRuntimeAuthority&&!u&&c&&sharedRuntimeAuthority.bindCloudTarget(c),!sharedRuntimeAuthority&&agentKeys&&agentGateway&&(sharedRuntimeAuthority=new RuntimeProductionAuthority({stateDirectory:s,gateway:agentGateway,keys:agentKeys,targetInstanceId:c,logger:e.logger,getMSConnectionStatus:e=>sharedMSConnectionStatus.get(e)})),sharedGatewayClient||(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}}),sharedGatewayClient.on("gateway-event",(e,t)=>{sharedApprovalBridge.onGatewayEvent(e,t),sharedQuestionBridge.onGatewayEvent(e,t)})),approvalLogger??=e.logger,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()&&agentGateway?{automationWake:!0}:{}},!sharedAgentProjection&&sharedRuntimeAuthority&&agentGateway){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({gateway:agentGateway,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 g=sharedSessionMutationCoordinator;if(!sharedAcpServer){const t=sharedRuntimeAuthority&&agentGateway?new AgentGatewayTemplateOriginReporter({gateway:agentGateway}):void 0;sharedAcpServer=new ACPServer(o,e.logger,g,a,agentGateway??void 0,i,n,sharedTerminalManager,sharedRuntimeAuthority,void 0,sharedMcpProjection,sharedMcpCredentialVault,{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:a})),runExclusive:(t,r,o)=>sharedImHotPathDeferral.runExclusive(t,r,e.logger,o)})}const p=sharedAcpServer,m=()=>sharedAgentMappings.snapshot()?.values().next().value;r.current=p,sharedMcpNativeProjectionNudge.request({registrationMode:e.registrationMode,fingerprint:fingerprintMcpServers(t.openclawConfig),logger:e.logger});const h=sharedPromptRunStore,{enabled:y,principalId:v,a2aEnabled:f}=o.messageBus;let w;!c&&o.agentGatewayUrl&&agentKeys&&(w=createBeeosClawMessageTokenProvider({agentGatewayUrl:o.agentGatewayUrl,identity:agentKeyPairToIdentity(agentKeys)}));const C=process.env.MESSAGE_RUNTIME_DELIVERY_KEY?.trim(),R=v||process.env.BEEOS_PRINCIPAL_ID||"",b=w?sharedMessageClientOwner.configure({tokenProvider:w,identityId:R,...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,S=c&&o.messageBus.serviceUrl&&sharedRuntimeAuthority?new NodeRuntimeDeliveryPort({serviceOrigin:async()=>o.messageBus.serviceUrl},{currentLease:()=>{const e=sharedRuntimeAuthority?.authorityLease,t=sharedRuntimeAuthority?.currentConsumerBinding();return e&&t&&e.instanceId===c?{...e,journalStoreId:t.journalStoreId,journalGeneration:t.journalGeneration}:null},subscribeAuthority:e=>sharedRuntimeAuthority.subscribeAuthority(e)}):void 0;attachNativeCloudChat(e,o);const _=sharedRuntimeAuthority,O=o.messageBus.serviceUrl;b&&_&&O&&C?p.setCronConversationDelivery({deliver:async e=>{const t=_.authorityLease;if(!t)throw new Error("current runtime authority lease unavailable for cron delivery");const r=new MessageServiceCronDeliveryHttpAuthority({messageServiceUrl:O,scopedDeliveryKey:C,fetchAuthorized:(e,t)=>fetchRuntimeLeaseAttempt(e,t??{})});return await r.deliver({...e,runtimeLeaseCredential:t.runtimeLeaseCredential})}}):p.setCronConversationDelivery(void 0);const E=sharedRuntimeAuthority?.journal??new FileRuntimeCommandJournal(join(s,"runtime-command-journal.json"),void 0,void 0,{onMaintenanceError:t=>e.logger.error?.(`[runtime-journal] event=checkpoint_failed trigger=threshold error=${t instanceof Error?t.message:String(t)}`)}),A=d.createRegistrationToken();e.registerService({id:"beeos-acp",async start(){const t=Date.now(),r=_pluginRegisteredAt?t-_pluginRegisteredAt:-1;e.logger.info?.(`[beeos-claw] beeos-acp service start() begin, sincePluginRegister=${r}ms, bridge.url=${o.bridge.url||"(empty)"}`);const a=await createRuntimeServiceProductionCandidate({...agentGateway?{authorizationAdapter:new AgentGatewayGrantJwksReadinessAdapter({gateway:agentGateway})}:{},onGrantJwksWarm:t=>{t.ready?e.logger.info?.("[beeos-claw] Gateway grant keyset loaded"):e.logger.warn?.(`[beeos-claw] Gateway grant keyset not ready yet (attempt ${t.attempt}): ${t.error instanceof Error?t.error.message:String(t.error)}`)},...b&&agentGateway&&C?{transportAdapter:new RuntimeServiceTransportReadinessAdapter({gateway:agentGateway,messageService:b.nodeTransport,messageClient:b.client,configuredServiceUrl:o.messageBus.serviceUrl,scopedDeliveryKey:C})}:{},...S&&agentGateway&&o.messageBus.serviceUrl?{cloudTransportAdapter:new CloudRuntimeServiceTransportReadinessAdapter({gateway:agentGateway,configuredServiceUrl:o.messageBus.serviceUrl,runtimeDelivery:S,...sharedNativeCloudChat?{sessionNewConversation:sharedNativeCloudChat.sessionNewConversation,nativePrompt:{http:sharedNativeCloudChat.http,inputFiles:new NativeInputFilesHttpClient(o.agentGatewayUrl,()=>sharedRuntimeAuthority.authorityLease,(e,t)=>fetch(e,t)),runBoundary:new MessageServiceConversationRunBoundary({transport:{start:e=>S.startConversationRun(e),finish:e=>S.finishConversationRun(e)}}),host:{htmlDeliveryRequired:o.canvasEnabled,resolveNativePromptSessionKey:(e,t)=>p.resolveNativePromptSessionKey(e,t),prepareNativePromptModel:(e,t,r)=>p.prepareNativePromptModel(e,t,r),runtime:e.runtime,pluginConfig:o,logger:e.logger,promptRunStore:h,assistantMessageCommitRouter:sharedAssistantMessageCommitRouter,toolResultObservationRouter:sharedToolResultObservationRouter,approvalStreams:sharedApprovalBridge,questionStreams:sharedQuestionBridge,modelCallObservationRouter:sharedModelCallObservationRouter,sessionMutationCoordinator:g,abortTransport:{chatAbort:(e,t)=>p.chatAbort(e,t)},activeRunAuthority:p.getActiveRunAuthority(),waitIfExclusive:(t,r)=>sharedImHotPathDeferral.waitIfExclusive(t,e.logger,r).then(()=>{})}}}:{}})}:{},commandJournal:E,poisonJournal:new FileRuntimePoisonDeliveryJournal(join(s,"runtime-poison-deliveries.json")),onConsumerDeliveryError:t=>{const r=null!==t&&"object"==typeof t?t:{},o=e=>"string"==typeof e&&e.length>0&&e.length<=256&&!/[\u0000-\u001f\u007f]/.test(e)?JSON.stringify(e):JSON.stringify("unavailable"),a="string"==typeof r.code&&/^[A-Za-z0-9_.:-]{1,64}$/.test(r.code)?r.code:"unavailable",n="number"==typeof r.status&&Number.isInteger(r.status)&&r.status>=100&&r.status<=599?String(r.status):"unavailable";e.logger.error?.(`[runtime-service] delivery worker failed before acknowledgement; delivery retained for retry: operation_id=${o(r.operationId)} delivery_id=${o(r.deliveryId)} code=${a} status=${n}`)},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"===a.status?a.serviceMethods:[]),sharedRuntimeAuthority?.setRuntimeResourceBindingContractRevision("ready"===a.status?a.resourceBindingContractRevision:void 0),p.setRuntimeRpcConsumerFactory("ready"===a.status?a.factory:void 0),"unavailable"===a.status?e.logger.warn?.("[runtime-service] capability fenced: "+a.blockers.map(e=>e.id).join(",")):a.capabilityBlockers.length&&e.logger.warn?.("[runtime-service] method capability fenced: "+a.capabilityBlockers.map(e=>e.method).join(",")),!o.bridge.url&&agentGateway){const t=Date.now(),r=await agentGateway.bridge.config();if(e.logger.info?.(`[beeos-claw] bridge discovery took ${Date.now()-t}ms result=${r?.url??"null"}`),r)o.bridge.url=r.url,writeDiscoveryCache({bridge:{url:r.url,discoveredAt:Date.now()}}),e.logger.info?.(`[beeos-claw] Bridge URL discovered from Agent Gateway: ${r.url}`);else{const t=readDiscoveryCache();t?.bridge?.url?(o.bridge.url=t.bridge.url,e.logger.info?.(`[beeos-claw] Bridge URL from cache (GW unreachable): ${t.bridge.url}`)):e.logger.debug?.("[beeos-claw] Bridge URL not available (no GW, no cache) — bridge disabled")}}await d.acquire(A);try{await p.start(o.bridge.url||void 0)}catch(e){throw await d.release(A),e}e.logger.info?.(`[beeos-claw] ACP Server started (total service start: ${Date.now()-t}ms)`)},async stop(){try{await d.release(A)}finally{await p.stop()}await E.checkpoint(),e.logger.info?.("[runtime-service] command journal checkpoint completed"),e.logger.info?.("[beeos-claw] ACP Server stopped")}});if(y&&void 0!==w&&w){const t=sharedMessageClientOwner.createRegistrationToken(),r=async(e,t)=>{const r=await w(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}},o=(e,t,r)=>({getLocalAgentId:()=>t,resolveLocalAgentId:e=>e===r?t:void 0,getDefaultPlatformAgent:()=>({instanceId:e.principalId}),ensureImInboundSession:(e,t,r,o)=>{p.ensureImInboundSession(e,t,r,o)},chatAbort:(e,t)=>p.chatAbort(e,t)}),n=sharedRuntimeAuthority?new RuntimeAuthorityMessageBusLifecycle({authority:sharedRuntimeAuthority,logger:e.logger,bus:{connect:async n=>{await sharedMessageClientOwner.acquire(t,async t=>{const i=await r(n.instanceId,n.runtimeEpoch),s=t.client,c={leaseId:n.leaseId,runtimeEpoch:n.runtimeEpoch};sharedMSConnectionStatus.bind(c);const u=new Map,d=new MessageProjectionInboundConsumer({client:s,bindings:()=>{const t=sharedRuntimeAuthority?.authorityLease;if(!t||t.instanceId!==i.principalId||t.runtimeEpoch!==i.runtimeEpoch)return[];const r=sharedAgentMappings.snapshot();return r?[...r.entries()].flatMap(([r,n])=>{if(!n.id||n.instanceId!==i.principalId)return[];let l=u.get(n.id);return l&&l.localAgentId===r||(l={localAgentId:r,inbound:createImInboundHandler({automationTurnPolicy:sharedAutomationTurnPolicy,runtime:e.runtime,pluginConfig:e.pluginConfig,client:s,localServer:o(i,r,n.id),promptRunStore:h,waitIfExclusive:(t,r)=>sharedImHotPathDeferral.waitIfExclusive(t,e.logger,r).then(()=>{}),assistantMessageCommitRouter:sharedAssistantMessageCommitRouter,toolResultObservationRouter:sharedToolResultObservationRouter,approvalStreams:sharedApprovalBridge,questionStreams:sharedQuestionBridge,modelCallObservationRouter:sharedModelCallObservationRouter,sessionMutationCoordinator:g,logger:e.logger,writeObs:a,principalIdOverride:i.principalId,activeRunAuthority:p.getActiveRunAuthority(),applyTurnModel:(e,t)=>p.applyImTurnModel(e,t),restoreTurnModel:e=>p.restoreImTurnModel(e),...agentGateway?{completeA2ATask:(t,r,o)=>completeA2ATask({gateway:agentGateway,logger:e.logger},t,r,o)}:{}})},u.set(n.id,l)),[{principalId:i.principalId,targetAgentId:n.id,runtimeEpoch:t.runtimeEpoch,inbound:l.inbound}]}):[]},executionJournal:l,logger:e.logger}),m=sharedAgentMappings.subscribe(()=>{sharedActiveMessageConsumer===d&&d.refresh()}),y=sharedRuntimeAuthority?.subscribeAuthority(()=>{sharedActiveMessageConsumer===d&&d.refresh()})??(()=>{}),v=new MessageClientTerminalStateRecovery({logger:e.logger,isCurrent:()=>{if(sharedActiveMessageConsumer!==d)return!1;const e=sharedRuntimeAuthority?.authorityLease;return null!=e&&e.instanceId===i.principalId&&e.runtimeEpoch===i.runtimeEpoch&&e.leaseId===n.leaseId&&e.handlerIdentity===n.handlerIdentity},reconnect:async()=>{if("failed"===s.getSnapshot().connection){await s.connect();try{await p.rebindRuntimeDelivery()}catch(t){e.logger.warn?.(`[beeos-claw] runtime Service delivery rebind failed after MessageClient recovery: ${t instanceof Error?t.message:String(t)}`)}}}});let f=s.getSnapshot().connection;sharedMSConnectionStatus.setIfCurrent(c,f);const w=s.subscribe(()=>{const t=s.getSnapshot();t.connection!==f&&(f=t.connection,sharedMSConnectionStatus.setIfCurrent(c,t.connection),"connected"===t.connection?(v.notifyConnected(),e.logger.info?.(`[beeos-claw] connection transport=message_service_realtime state=connected localAgentId=${p.getLocalAgentId()} principalId=${i.principalId}`)):"failed"===t.connection?(e.logger.warn?.(`[beeos-claw] connection transport=message_service_realtime state=failed principalId=${i.principalId} recoveryError=${t.recoveryError??"unknown"}`),v.notifyFailed()):"disconnected"!==t.connection&&"reconnecting"!==t.connection||e.logger.warn?.(`[beeos-claw] connection transport=message_service_realtime state=${t.connection} principalId=${i.principalId}; SDK owns reconnect/recovery`))});sharedActiveMessageConsumer=d,d.start();try{await s.connect()}catch(e){throw sharedActiveMessageConsumer===d&&(sharedActiveMessageConsumer=void 0),sharedMSConnectionStatus.resetIfCurrent(c),v.dispose(),await d.stop(),m(),y(),w(),await s.disconnect().catch(()=>{}),e}try{await p.rebindRuntimeDelivery()}catch(t){e.logger.warn?.(`[beeos-claw] runtime Service delivery rebind failed after MessageClient connect: ${t instanceof Error?t.message:String(t)}`)}return async()=>{sharedActiveMessageConsumer===d&&(sharedActiveMessageConsumer=void 0),sharedMSConnectionStatus.resetIfCurrent(c),v.dispose(),await d.stop(),y(),w(),await s.disconnect(),e.logger.info?.("[beeos-claw] MessageClient disconnected")}})},disconnect:async()=>{await sharedMessageClientOwner.release(t)}}}):void 0;e.registerService({id:"beeos-message-bus",async start(){n?n.start():e.logger.warn?.("[beeos-claw] Message bus requires runtime authority; waiting is unavailable without Agent Gateway runtime identity")},async stop(){await(n?.stop())}})}else y?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=${y})`);if(o.messageBus.enabled&&c&&!sharedNativeCloudChat&&e.logger.warn?.("[beeos-claw] Cloud message bus enabled but native composition unavailable (messageBus.serviceUrl or runtime authority missing) — disabled"),!1!==f&&agentGateway){const t=e=>p.getGatewaySessionKeyForToolCall(e),r=e=>p.getMentionsForToolCall(e),o=(e,t)=>{p.broadcastNotification(e,t)},a=new TaskHandleRegistry;registerA2ATools(e,{gateway:agentGateway,logger:e.logger},a,t,r,o,()=>m()?.instanceId),e.logger.info?.("[beeos-claw] A2A tools registered (beeos_discover_agents, beeos_call_agent) via REST")}if(agentGateway&&(registerAutomationTools(e,{gateway:agentGateway,callConnectorTool:harnessConnectorToolCaller(),logger:e.logger,getDefaultAgent:()=>{const e=m();return e?{id:e.id,instanceId:e.instanceId}:void 0},getCurrentConversationId:e=>{const t=p.getGatewaySessionKeyForToolCall(e);return t?extractBeeosConversationIdFromSessionKey(t):void 0}}),e.logger.info?.("[beeos-claw] Automation tools registered (create/create_webhook/capabilities/preflight/list/get/run/runs/get_run)")),o.canvasEnabled){sharedCanvasState||(sharedCanvasState=new CanvasStateManager);const t=sharedCanvasState;if(agentGateway&&agentKeys){t.setLogger(e.logger);const r=()=>agentGateway.canvas.token(),a=a=>{const n=new CanvasRelayClient({canvasRelayUrl:o.canvasRelayUrl,agentKeys:agentKeys,agentId:a.id,displayName:a.displayName,avatarUrl:a.avatarUrl,tokenProvider:r,onConnected:()=>p.flushPendingCanvasLinks(),onReconnect:()=>p.onRelayReconnected(),logger:e.logger});t.setRelayClient(n),t.setAgentId(a.id),p.wireRelayActionHandler(),n.connect(),e.logger.info?.(`[beeos-claw] Canvas Relay multiplexed client connected (agentId=${a.id}, name=${a.name??"none"})`)};e.registerService(createCanvasRelayService({getAgent:m,onceAgentReady:e=>sharedAgentMappings.onceReady(e),initRelay:a,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=>a(e),getActiveSessionId:e=>p.getSessionIdForToolCall(e),getPlatformAgentId:e=>p.getPlatformAgentIdForToolCall(e),getGatewaySessionKey:e=>p.getGatewaySessionKeyForToolCall(e),waitForAgentSync:()=>sharedAgentMappings.waitForSynchronization(),publishStaticHtml:createStaticCanvasHtmlPublisher(pluginCloudRuntimeTarget(e)?o.agentGatewayUrl:void 0)},i=new FormSchemaRegistry,s=buildCronSchemas(n);i.register(s.create),i.register(s.update);const l=new ResourceDescriptorRegistry;l.register(CRON_RESOURCE);const c=e=>async(t,r,o)=>(p.preMapToolCallSession(t),e(t,r));e.registerTool({name:RENDER_UI_TOOL_NAME,description:RENDER_UI_TOOL_DESCRIPTION,parameters:RENDER_UI_TOOL_SCHEMA,execute:c(createRenderUiToolExecutor(r))}),e.registerTool({name:CLOSE_UI_TOOL_NAME,description:CLOSE_UI_TOOL_DESCRIPTION,parameters:CLOSE_UI_TOOL_SCHEMA,execute:c(createCloseUiHandler(r))}),p.setCanvasState(t,{schemaRegistry:i,descriptorRegistry:l});const u=p.getFormBuilder();u&&e.registerTool({name:RENDER_FORM_TOOL_NAME,description:buildRenderFormDescription(i),parameters:RENDER_FORM_TOOL_SCHEMA,execute:c(createRenderFormHandler({registry:i,builder:u,logger:e.logger}))});const d=p.getResourceRenderer();if(d){generateActionTools(l,e,async(e,t)=>p.gatewayRequest(e,t),d,e.logger)}const g=buildToolRoutingHint(BEEOS_FILE_RESOURCE_ROUTING_RULE,BEEOS_DESKTOP_SCREENSHOT_TOOL_NAME),h=new Set([RENDER_UI_TOOL_NAME,CLOSE_UI_TOOL_NAME,RENDER_FORM_TOOL_NAME]),y=256,v=new Map;if("function"==typeof e.on){try{e.on("before_prompt_build",(e,t)=>{if(t?.sessionKey){if(v.size>=y&&!v.has(t.sessionKey)){const e=v.keys().next().value;void 0!==e&&v.delete(e)}v.set(t.sessionKey,t.channelId),rememberDeliveryWorkspace(t.sessionKey,t.workspaceDir)}const r=isThirdPartyChannel(n,{sessionKey:t?.sessionKey,channelId:t?.channelId});return t?.sessionKey||r?{appendSystemContext:buildAppendSystemContext({thirdParty:r,canvasSystemPrompt:`${CANVAS_PRESENTATION_GATE}\n\n${CANVAS_SYSTEM_PROMPT}`,toolRoutingHint:g})}:{appendSystemContext:g}}),e.logger.debug?.("[beeos-claw] Canvas system prompt hook registered: before_prompt_build")}catch{e.logger.debug?.("[beeos-claw] before_prompt_build hook not supported")}try{e.on("before_tool_call",(e,t)=>{if(t?.sessionKey&&p.setPendingToolCallSessionKey(t.sessionKey),!h.has(e.toolName))return;const r=t?.sessionKey?v.get(t.sessionKey):void 0,o=isThirdPartyChannel(n,{sessionKey:t?.sessionKey,channelId:r}),a=canvasToolBlockReason({thirdParty:o,hasSession:Boolean(t?.sessionKey)});return a?{block:!0,blockReason:a}: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:a});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}`)}}function pluginCloudRuntimeTarget(e){const t=e.pluginConfig?.cloudRuntimeInstanceId;return cloudRuntimeTargetInstanceId("string"==typeof t?t:void 0)}function attachNativeCloudChat(e,t){if(sharedNativeChatService)return e.registerService(sharedNativeChatService),sharedNativeCloudChat;const r=sharedRuntimeAuthority,o=t.messageBus.serviceUrl,a=sharedAcpServer,n=sharedSessionMutationCoordinator;if(!(r&&r.cloudTargetInstanceId&&o&&a&&n))return sharedNativeCloudChat;const i=sharedNativeCloudChat??createNativeCloudChatComposition({authority:nativeCloudChatAuthorityFromRuntimeAuthority(r),serviceUrl:o,logger:e.logger});if(sharedNativeCloudChat=i,!t.messageBus.enabled)return i;const s=i,l=r;let c;const u=new Map,d=new RuntimeAuthorityMessageBusLifecycle({authority:l,logger:e.logger,bus:{connect:async t=>{c?.(),u.clear(),c=s.adapter.listen({eventTypes:["message.created"]},r=>{if(r.type===APPROVAL_DECISION_MESSAGE_TYPE&&r.senderId?.startsWith("user:")&&isApprovalDocument(r.content))return void sharedApprovalBridge.onDecision(r.content);if("user.continue"===r.type&&isQuestionResponseDocument(r.content))return void sharedQuestionBridge.onResponse(r);const o=(r.content??{}).metadata??{},i="string"==typeof o.target_agent_id?o.target_agent_id:"",l=i?((t,r)=>{const o=sharedAgentMappings.snapshot();if(o)for(const[i,l]of o.entries()){if(l.id!==r)continue;if(l.instanceId!==t.instanceId)continue;const o=`${i}\0${l.id}`;let c=u.get(o);return c||(c={localAgentId:i,inbound:createImInboundHandler({runtime:e.runtime,pluginConfig:e.pluginConfig,client:s.adapter,localServer:{getLocalAgentId:()=>i,resolveLocalAgentId:e=>e===l.id?i:void 0,getDefaultPlatformAgent:()=>({instanceId:t.instanceId}),ensureImInboundSession:(e,t,r,o)=>{a.ensureImInboundSession(e,t,r,o)},chatAbort:(e,t)=>a.chatAbort(e,t)},promptRunStore:sharedPromptRunStore,waitIfExclusive:(t,r)=>sharedImHotPathDeferral.waitIfExclusive(t,e.logger,r).then(()=>{}),assistantMessageCommitRouter:sharedAssistantMessageCommitRouter,toolResultObservationRouter:sharedToolResultObservationRouter,approvalStreams:sharedApprovalBridge,questionStreams:sharedQuestionBridge,modelCallObservationRouter:sharedModelCallObservationRouter,sessionMutationCoordinator:n,logger:e.logger,writeObs:sharedWriteObs,principalIdOverride:t.instanceId,activeRunAuthority:a.getActiveRunAuthority(),applyTurnModel:(e,t)=>a.applyImTurnModel(e,t),restoreTurnModel:e=>a.restoreImTurnModel(e),...agentGateway?{completeA2ATask:(t,r,o)=>completeA2ATask({gateway:agentGateway,logger:e.logger},t,r,o)}:{}})},u.set(o,c)),c}})(t,i):void 0;if(!l)return void e.logger.warn?.(`[beeos-claw] native cloud chat dropped unresolvable target_agent_id=${i||"missing"} message_id=${r.id}`);("chat_cancel"===r.type?l.inbound.onChatCancel(r):l.inbound.onChatMessage(r)).catch(t=>{e.logger.error?.(`[beeos-claw] native cloud chat dispatch failed message_id=${r.id}: ${t instanceof Error?t.message:String(t)}`)})});try{await s.adapter.connect()}catch(e){throw c?.(),c=void 0,e}},disconnect:async()=>{c?.(),c=void 0,u.clear(),await s.adapter.disconnect()}}});return sharedNativeChatService={id:"beeos-native-cloud-chat",async start(){d.start()},async stop(){await d.stop()}},e.registerService(sharedNativeChatService),e.logger.info?.("[beeos-claw] native Cloud personal-chat composition registered"),i}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=pluginCloudRuntimeTarget(e),r=void 0!==t&&t!==sharedRuntimeAuthority?.cloudTargetInstanceId,o=planPluginRegistration(e.registrationMode,_fullRuntimeRegistered,r);if("skip"!==o){_initializing=!0;try{if(!hasRuntimeReplyApi(e.runtime))throw e.logger.error?.("[beeos-claw] FATAL: OpenClaw runtime.channel.reply.dispatchReplyFromConfig not exposed. Upgrade OpenClaw or revert beeos-claw."),new Error("openclaw_runtime_reply_api_unavailable");const t=(t,r)=>{registerUploadTools(e,t,r),registerArouterLatencyRouting(e),registerArouterMediaProviders(e)};if("capabilities"===o&&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 r={current:sharedAcpServer??null};return t(sharedPluginContext,r),registerServices(e,sharedPluginContext,r),void e.logger.info?.(`[beeos-claw] capability re-register mode=${e.registrationMode} capabilities_only`)}const r=initializePlugin(e);if(!r)return;const a={current:null};installUnhandledRejectionObserver(e,r.writeObs,a),ensureRegisterToolCache(e),cacheRuntimeRegistrations(e),cachedCapabilityTools.length=0,t(r,a),agentGateway&&(e.on?e.on("before_agent_finalize",async(e,t)=>{if(await fileDeliveryFinalizeDecision(e),!e.runId||!t?.runId||e.runId===t.runId)return nativeHtmlDeliveryForRun(e.runId??t?.runId)?.beforeFinalize(e)}):e.logger.warn?.("[file-delivery] OpenClaw plugin hooks unavailable; finalization check disabled")),registerServices(e,r,a),sharedPluginContext=r,marksFullRuntimeRegistered(e.registrationMode,o)&&(_fullRuntimeRegistered=!0),_pluginRegisteredAt=Date.now(),e.logger.info?.("[beeos-claw] Plugin registered successfully")}finally{_initializing=!1}}else if(!e.registrationMode||"full"===e.registrationMode){const t=resolveCapabilityToolReplay({currentRegisterTool:e.registerTool,wrappedRegisterTool:wrappedRegisterTool,originalRegisterTool:rawRegisterTool});replayCachedPluginTools(r=>t.call(e,r),cachedCapabilityTools),replayRuntimeRegistrations(e),registerArouterLatencyRouting(e),registerArouterMediaProviders(e)}}};export default plugin;
@@ -1 +1 @@
1
- import{toMessagePart}from"../message-client-types.js";import{isUploadArtifactToolName,resolveToolArtifactLinks}from"../upload/tool-artifact-enrichment.js";import{sessionWorkspaceDir,takeFinalizeAttachments}from"../upload/delivery-ledger.js";import{resolveRuntimeToolArguments,resolveRuntimeToolCallId,resolveRuntimeToolName}from"./tool-envelope-metadata.js";import{extractSearchSources,mergeSearchSource,searchSourceProvider}from"./search-source-extractor.js";import{isRenderUiToolName,staticHtmlProjectionOf,staticHtmlResultOf}from"../canvas/static-html-result.js";import{logChatLatency}from"../chat-latency.js";const STREAMING_SNAPSHOT_PUBLISH_INTERVAL_MS=200,isNonEmptyString=e=>"string"==typeof e&&e.length>0,isPlainRecord=e=>"object"==typeof e&&null!==e&&!Array.isArray(e);function pickRawResultFromPayload(e){if(void 0!==e.rawResult)return e.rawResult;if(void 0!==e.result)return e.result;if(void 0!==e.details)return e.details;if(void 0!==e.toolResult)return e.toolResult;if(isPlainRecord(e.channelData)){const t=e.channelData;if(void 0!==t.rawResult)return t.rawResult;if(void 0!==t.result)return t.result;if(void 0!==t.details)return t.details}}function asToolParams(e){if(e&&"object"==typeof e&&!Array.isArray(e))return e}const summarizeObservedToolError=e=>{if(null==e)return;if("string"==typeof e)return e.slice(0,180);if("object"!=typeof e)return String(e).slice(0,180);const t=e;for(const e of["error","message","blockReason","details","text"]){const r=t[e];if("string"==typeof r&&r.trim())return r.slice(0,180);if(isPlainRecord(r)&&"string"==typeof r.message&&r.message.trim())return r.message.slice(0,180)}try{const t=JSON.stringify(e);return t?t.slice(0,180):void 0}catch{return}},observedToolResultText=e=>{if(!isPlainRecord(e)||!Array.isArray(e.content))return;const t=e.content.filter(e=>isPlainRecord(e)&&"text"===e.type&&"string"==typeof e.text).map(e=>e.text.trim()).filter(Boolean);return t.length>0?t.join("\n"):void 0};export function createImReplySink(e){const t=e.startedAtMs??Date.now();let r=!1;const s=new Map,o=new Set,a={text:""};let n="",i="",l=!1,d=!1;const u=new Set;let m;const c={receivedSnapshotCount:0,streamingPublishCount:0,terminalPublishCount:0},g=[];let p;const f=new Map,h=[],b=new Map,v=new Map;let y,_=0,I=1;const S=(e,t={})=>({schema_version:1,seq:e,...t}),x=new Set,R=new Set,w=t=>{t!==a.text&&(t.startsWith(a.text)?e.stream.appendBody(t.slice(a.text.length)):e.stream.setBody(t),a.text=t)},P=e=>{void 0===e.publishTimer&&(e.publishTimer=setTimeout(()=>{e.publishTimer=void 0,te("body_preview_flush",()=>{m===e&&(e=>{a.text!==e.targetBody&&(w(e.targetBody),e.streamingPublishCount+=1)})(e)})},200))},k=e=>{const t=m;t?(void 0!==t.publishTimer&&(clearTimeout(t.publishTimer),t.publishTimer=void 0),t.targetBody=e,a.text!==e&&(w(e),t.terminalPublishCount+=1),c.receivedSnapshotCount+=t.receivedSnapshotCount,c.streamingPublishCount+=t.streamingPublishCount,c.terminalPublishCount+=t.terminalPublishCount,m=void 0):w(e)},q=t=>{if(void 0!==t.partIndex)return;const r=_,s=I;e.stream.addPart(toMessagePart({type:"text",text:t.text,state:"done",_seq:s,data:S(s)})),t.partIndex=r,_+=1,I+=1},T=()=>{if(!d){d=!0;for(const e of g)q(e)}},$=e=>{const t={text:e};g.push(t),d&&q(t)},E=(t,r)=>{for(const s of t){const t=v.get(s.url);if(t){const r=mergeSearchSource(t,s);if(r.title===t.title&&r.snippet===t.snippet)continue;const o={...r,partIndex:t.partIndex,displaySeq:t.displaySeq,toolUseId:t.toolUseId};e.stream.replacePart(t.partIndex,toMessagePart({type:"source",url:o.url,...o.title?{title:o.title}:{},...o.snippet?{snippet:o.snippet}:{},tool_use_id:o.toolUseId,_seq:o.displaySeq,data:S(o.displaySeq)})),v.set(s.url,o);continue}const o={...s,partIndex:_,displaySeq:I,toolUseId:r};e.stream.addPart(toMessagePart({type:"source",url:s.url,...s.title?{title:s.title}:{},...s.snippet?{snippet:s.snippet}:{},tool_use_id:r,_seq:o.displaySeq,data:S(o.displaySeq)})),v.set(s.url,o),_+=1,I+=1,Y.source+=1}},C=(e,t,r,s)=>toMessagePart({type:"thinking",text:t,state:r,_seq:e.displaySeq,_started_at_ms:e.startedAtMs,...void 0!==s?{_duration_ms:s}:{},data:S(e.displaySeq,{started_at_ms:e.startedAtMs,...void 0!==s?{duration_ms:s}:{}})}),M=t=>{T();const r={partIndex:_,displaySeq:I,text:t,publishedText:t,receivedSnapshotCount:1,streamingPublishCount:1,startedAtMs:Date.now(),startedAtMonotonicMs:performance.now()};return e.stream.addPart(C(r,t,"streaming")),_+=1,I+=1,p=r,r},A=t=>{void 0===t.publishTimer&&(t.publishTimer=setTimeout(()=>{t.publishTimer=void 0,te("reasoning_snapshot_flush",()=>{p===t&&(t=>{t.publishedText!==t.text&&(e.stream.replacePart(t.partIndex,C(t,t.text,"streaming")),t.publishedText=t.text,t.streamingPublishCount+=1)})(t)})},200))},O=()=>{const r=p;if(!r)return;void 0!==r.publishTimer&&(clearTimeout(r.publishTimer),r.publishTimer=void 0);const s=Math.max(0,Math.round(performance.now()-r.startedAtMonotonicMs));e.stream.replacePart(r.partIndex,C(r,r.text,"done",s)),p=void 0,logChatLatency(e.logger,"claw.reasoning.settled",e.requestId,t,{reasoning_duration_ms:s,reasoning_chars:r.text.length,reasoning_snapshot_count:r.receivedSnapshotCount,reasoning_streaming_publish_count:r.streamingPublishCount,reasoning_coalesced_snapshot_count:Math.max(0,r.receivedSnapshotCount-r.streamingPublishCount),reasoning_publish_interval_ms:200})},F=e=>{const t=h.indexOf(e);t>=0&&h.splice(t,1)},N=e=>null!=e&&("object"!=typeof e||(Array.isArray(e)?e.length>0:Object.keys(e).length>0)),U=e=>{let t=b.get(e);return t||(t={},b.set(e,t)),t},j=(t,r,s)=>{const o="tool"===t.name&&"tool"!==r?r:t.name,a=!N(t.arguments)&&N(s)?s:t.arguments;o===t.name&&a===t.arguments||(e.stream.replacePart(t.partIndex,toMessagePart({type:"tool_use",id:t.id,name:t.htmlPresentation?.name??o,arguments:t.htmlPresentation?.arguments??a,state:t.state,_seq:t.displaySeq,data:S(t.displaySeq)})),t.name=o,t.arguments=a)},L=(t,r,s,o)=>{const a=f.get(t);if(a)return j(a,r,s),o&&!h.includes(t)&&"streaming"===a.state&&h.push(t),a;T();const n=b.get(t),i="tool"!==r?r:n?.name&&"tool"!==n.name?n.name:r,l=N(s)?s:n?.arguments??s,d=((t,r,s)=>{const o=_,a=I;return e.stream.addPart(toMessagePart({type:"tool_use",id:t,name:r,arguments:s,state:"streaming",_seq:a,data:S(a)})),_+=1,I+=1,{partIndex:o,displaySeq:a}})(t,i,l),u={id:t,name:i,arguments:l,partIndex:d.partIndex,displaySeq:d.displaySeq,state:"streaming",itemEventObserved:!0===n?.itemEventObserved,toolStartObserved:!0===n?.toolStartObserved};return f.set(t,u),(o||u.toolStartObserved||u.itemEventObserved)&&(h.includes(t)||"streaming"!==u.state||h.push(t)),n&&Q(u,n),b.delete(t),u},B=(e,t)=>{const r=(e=>f.get(e))(e);if(r)return void 0===t.name&&void 0===t.arguments||j(r,t.name??r.name,t.arguments??r.arguments),t.toolStartObserved&&(r.toolStartObserved=!0),t.itemEventObserved&&(r.itemEventObserved=!0),(t.pending||t.toolStartObserved||t.itemEventObserved)&&!h.includes(r.id)&&"streaming"===r.state&&h.push(r.id),r;const s=U(e);void 0!==t.name&&(s.name=t.name),void 0!==t.arguments&&(s.arguments=t.arguments),t.toolStartObserved&&(s.toolStartObserved=!0),t.itemEventObserved&&(s.itemEventObserved=!0)},H=t=>{"done"!==t.state&&(e.stream.replacePart(t.partIndex,toMessagePart({type:"tool_use",id:t.id,name:t.htmlPresentation?.name??t.name,arguments:t.htmlPresentation?.arguments??t.arguments,state:"done",_seq:t.displaySeq,data:S(t.displaySeq)})),t.state="done")},z=e=>{if(null===e||"object"!=typeof e)return JSON.stringify(e)??String(e);if(Array.isArray(e))return`[${e.map(z).join(",")}]`;return`{${Object.entries(e).sort(([e],[t])=>e.localeCompare(t)).map(([e,t])=>`${JSON.stringify(e)}:${z(t)}`).join(",")}}`},D=(t,r,s)=>{const o=`${s?"1":"0"}:${z(r)}`;if(t.progressSignature===o)return;const a=toMessagePart({type:"custom",kind:"tool_progress",data:{tool_use_id:t.id,content:r,...s?{is_error:!0}:{}}});if(void 0===t.progressPartIndex){const r=_;return e.stream.addPart(a),t.progressPartIndex=r,t.progressSignature=o,void(_+=1)}e.stream.replacePart(t.progressPartIndex,a),t.progressSignature=o},Q=(e,t)=>{if(t.progress?.length)for(const r of t.progress)D(e,r.content,r.isError);t.observedResult&&(D(e,t.observedResult.content,t.observedResult.isError),e.observedResult=t.observedResult,"tool_call"!==e.name&&!isRenderUiToolName(e.name)||t.observedResult.isError||V(e,{text:t.observedResult.content,isError:t.observedResult.isError,rawResult:t.observedResult.rawResult})),(t.progress?.length||t.observedResult)&&(H(e),F(e.id)),!t.observedResult&&t.htmlPayload&&V(e,t.htmlPayload),t.sources?.length&&E(t.sources,e.id)},W=e=>[...new Set([e.mediaUrl,...e.mediaUrls??[]].filter(e=>isNonEmptyString(e)).map(e=>e.trim()).filter(Boolean))],J=(e,t=[])=>{const r=[...new Set([...W(e),...t])];return isNonEmptyString(e.text)&&r.length>0?{text:e.text,media_urls:r}:isNonEmptyString(e.text)?e.text:1===r.length?r[0]:r.length>1?{media_urls:r}:!0===e.isError?"Tool execution failed.":""},K=t=>{for(const r of t)e.stream.addPart(toMessagePart({type:"resource_link",uri:r.uri,name:r.name,...r.mimeType?{mimeType:r.mimeType}:{}})),_+=1},G=t=>{x.add(t);const r=()=>{x.delete(t)};e.onCallbackFailure?t.then(r,r):t.finally(r)},V=(t,r)=>{const s=t.observedResult,o=s?.isError??!0===r.isError,a=s?.rawResult??pickRawResultFromPayload(r),n=o?void 0:staticHtmlProjectionOf(t.name,t.arguments,a);if(t.durableUploadFilesEmitted&&(!n||t.staticHtmlResultEmitted))return;e.logger.info?.(`[im-sink] schedule tool artifact enrichment requestId=${e.requestId} toolUseId=${t.id} name=${t.name}`);const i=(async()=>{let s=[];try{s=await resolveToolArtifactLinks({toolCallId:t.id,toolName:t.name,rawResult:a,rawText:"string"==typeof r.text?r.text:void 0,params:asToolParams(t.arguments),cwd:sessionWorkspaceDir(e.sessionKey),payloadMediaUrls:W(r)})}catch(r){const s=r instanceof Error?r.message:String(r);if(e.logger.warn?.(`[im-sink] resolveToolArtifactLinks failed requestId=${e.requestId} toolUseId=${t.id} error=${s}`),e.onCallbackFailure?.(r),e.onCallbackFailure)throw r;return}if(n&&!s.some(e=>e.uri===n.result.artifact.uri)){const e=n.result.artifact;s.push({uri:e.uri,name:e.title,mimeType:e.mimeType})}0!==s.length?(e.logger.info?.(`[im-sink] tool artifact enrichment resolved ${s.length} link(s) requestId=${e.requestId} toolUseId=${t.id}`),await new Promise((o,a)=>{const i=e.callbackQueue.enqueueSync(()=>{try{if(e.abortSignal.aborted||e.stream.isTerminated)return;const o=s.map(e=>e.uri);t.artifactMediaUrls=o;const a=t.observedResult;D(t,J(a?{text:a.content,isError:a.isError}:r,o),a?.isError??!0===r.isError),((t,r,s,o)=>{const a=r.length>0&&!t.durableUploadFilesEmitted,n=!s&&void 0!==o&&!t.staticHtmlResultEmitted;(a||n)&&(n&&(t.htmlPresentation={name:"render_ui",arguments:o.arguments},e.stream.replacePart(t.partIndex,toMessagePart({type:"tool_use",id:t.id,...t.htmlPresentation,state:t.state,_seq:t.displaySeq,data:S(t.displaySeq)}))),e.stream.appendToolResult(t.id,[...n?[{type:"text",text:JSON.stringify(o.result)}]:[],...a?r.map(e=>({type:"resource_link",uri:e.uri,name:e.name,...e.mimeType?{mimeType:e.mimeType}:{}})):[]],s),_+=1,n&&(t.staticHtmlResultEmitted=!0),a&&(t.durableUploadFilesEmitted=!0,K(r)))})(t,s,a?.isError??!0===r.isError,n)}catch(r){const s=r instanceof Error?r.message:String(r);e.logger.warn?.(`[im-sink] tool artifact enrichment apply failed requestId=${e.requestId} toolUseId=${t.id} error=${s}`),e.onCallbackFailure?.(r),e.onCallbackFailure&&a(r)}finally{o()}});!1===i&&e.onCallbackFailure&&e.callbackQueue.waitForIdle().then(o,a)})):e.logger.debug?.(`[im-sink] tool artifact enrichment found no links requestId=${e.requestId} toolUseId=${t.id} name=${t.name}`)})().catch(r=>{const s=r instanceof Error?r.message:String(r);if(e.logger.warn?.(`[im-sink] tool artifact enrichment failed requestId=${e.requestId} toolUseId=${t.id} error=${s}`),e.onCallbackFailure?.(r),e.onCallbackFailure)throw r});G(i)},X=(e,t,r)=>B(e,{name:t,arguments:r}),Y={tool:0,block:0,final:0,source:0};let Z,ee=!1;const te=(t,r)=>{e.callbackQueue.enqueueSync(()=>{if((t=>e.abortSignal.aborted?(e.logger.debug?.(`[im-sink] ${t} ignored after abort requestId=${e.requestId} channel=${e.channelId} in_reply_to=${e.inboundMessageId}`),!1):e.stream.isTerminated?(e.logger.debug?.(`[im-sink] ${t} ignored after stream_terminated requestId=${e.requestId} channel=${e.channelId} in_reply_to=${e.inboundMessageId}`),!1):!ee||(e.logger.debug?.(`[im-sink] ${t} ignored after mark_complete requestId=${e.requestId} channel=${e.channelId} in_reply_to=${e.inboundMessageId}`),!1))(t))try{r()}catch(r){const s=r instanceof Error?r.message:String(r);e.logger.warn?.(`[im-sink] ${t} mapping failed requestId=${e.requestId} channel=${e.channelId} in_reply_to=${e.inboundMessageId} error=${s}`),e.onCallbackFailure?.(r)}})},re={sendToolResult:t=>(te("tool_progress",()=>{const r=resolveRuntimeToolCallId(t),s=resolveRuntimeToolName(t),o=resolveRuntimeToolArguments(t),a=J(t),n=!0===t.isError;let i;if(r){if(i=X(r,s,o),!i){const e=U(r);if(s&&(e.name=s),N(o)&&(e.arguments=o),e.progress=[...e.progress??[],{content:a,isError:n}],"tool_call"===s||isRenderUiToolName(s??"")){const r=pickRawResultFromPayload(t);(n||void 0!==r)&&(e.htmlPayload=!n&&staticHtmlResultOf(r)?t:void 0)}return void(Y.tool+=1)}i&&(Y.tool+=1,i.observedResult||D(i,a,n),H(i),F(i.id),V(i,t))}else e.logger.debug?.(`[im-sink] unattributed tool progress ignored requestId=${e.requestId} channel=${e.channelId} tool=${s??"unknown"} pending=${h.length}`)}),!0),sendBlockReply:e=>(te("block_reply",()=>{Y.block+=1,isNonEmptyString(e.text)&&(Z=!0!==e.isError?void 0:e.text)}),!0),sendFinalReply:t=>(te("final_reply",()=>{var r;(Y.final+=1,!0!==t.isError)?((t=>{const r=W(t).filter(e=>!R.has(e));if(0===r.length)return;for(const e of r)R.add(e);const s=(async()=>{let t=[];try{t=await resolveToolArtifactLinks({toolCallId:`final:${e.runId??e.requestId}`,toolName:"final_reply_media",payloadMediaUrls:r})}catch(t){for(const e of r)R.delete(e);if(e.logger.warn?.(`[im-sink] final media enrichment failed requestId=${e.requestId} error=${t instanceof Error?t.message:String(t)}`),e.onCallbackFailure?.(t),e.onCallbackFailure)throw t;return}if(0!==t.length)await new Promise((r,s)=>{!1===e.callbackQueue.enqueueSync(()=>{try{if(e.abortSignal.aborted||e.stream.isTerminated)return;K(t)}catch(t){e.logger.warn?.(`[im-sink] final media enrichment apply failed requestId=${e.requestId} error=${t instanceof Error?t.message:String(t)}`),e.onCallbackFailure?.(t),e.onCallbackFailure&&s(t)}finally{r()}})&&e.onCallbackFailure&&e.callbackQueue.waitForIdle().then(r,s)});else for(const e of r)R.delete(e)})();G(s)})(t),isNonEmptyString(t.text)&&(Z=void 0,l||(r=t.text,i="",n+=r,k(n),$(r),l=!0))):Z=t.text}),!0),waitForIdle:async()=>{for(let t=0;(e.onCallbackFailure||t<16)&&(await e.callbackQueue.waitForIdle(),0!==x.size);t++)e.onCallbackFailure?await Promise.all([...x]):await Promise.allSettled([...x]);await e.callbackQueue.waitForIdle();const t=takeFinalizeAttachments(e.sessionKey);t.length>0&&!e.abortSignal.aborted&&!e.stream.isTerminated&&K(t)},getQueuedCounts:()=>({tool:Y.tool,block:Y.block,final:Y.final}),getAccumulatedBody:()=>a.text,getTerminalErrorText:()=>Z,markComplete:r=>{O(),b.size>0&&e.logger.debug?.(`[im-sink] dropping uncommitted lifecycle tool signals requestId=${e.requestId} buffered=${b.size}`),i="",k(n),c.receivedSnapshotCount>0&&logChatLatency(e.logger,"claw.text_preview.settled",e.requestId,t,{text_preview_snapshot_count:c.receivedSnapshotCount,text_preview_streaming_publish_count:c.streamingPublishCount,text_preview_terminal_publish_count:c.terminalPublishCount,text_preview_coalesced_snapshot_count:Math.max(0,c.receivedSnapshotCount-c.streamingPublishCount-c.terminalPublishCount),text_preview_publish_interval_ms:200});for(const t of f.values())try{"success"===r&&isUploadArtifactToolName(t.name)&&!t.durableUploadFilesEmitted&&V(t,{toolCallId:t.id,toolName:t.name}),"success"===r&&H(t),F(t.id)}catch(r){const s=r instanceof Error?r.message:String(r);e.logger.warn?.(`[im-sink] tool finalization failed requestId=${e.requestId} toolUseId=${t.id} error=${s}`)}b.clear(),ee=!0}},se={...e.runId?{runId:e.runId}:{},abortSignal:e.abortSignal,disableBlockStreaming:!0,onAgentRunStart:r=>{e.onAgentRunStart?.(r),logChatLatency(e.logger,"claw.runtime.run_started",e.requestId,t),e.logger.info?.(`[im-sink] run_start requestId=${e.requestId} runId=${r} channel=${e.channelId} in_reply_to=${e.inboundMessageId} origin=${e.origin??"im"}`)},onPartialReply:s=>{te("partial",()=>{l||"string"==typeof s.text&&(!r&&s.text.length>0&&(r=!0,logChatLatency(e.logger,"claw.model.first_delta",e.requestId,t)),i=s.text,(e=>{const t=m;if(!t){if(a.text===e)return;return w(e),void(m={targetBody:e,receivedSnapshotCount:1,streamingPublishCount:1,terminalPublishCount:0})}t.targetBody!==e&&(t.targetBody=e,t.receivedSnapshotCount+=1,P(t))})(n+i))})},onReasoningStream:e=>{te("reasoning",()=>{if(!isNonEmptyString(e.text))return;const t=e.text,r=p??M(t);t!==r.text&&(r.text.startsWith(t)||(t.startsWith(r.text)?(r.text=t,r.receivedSnapshotCount+=1,A(r)):(O(),M(t))))})},onReasoningEnd:()=>{te("reasoning_end",()=>{O()})},onAssistantMessageStart:()=>{te("assistant_message_start",()=>{i="",l=!1,k(n),e.logger.debug?.(`[im-sink] assistant_message_start requestId=${e.requestId} channel=${e.channelId} in_reply_to=${e.inboundMessageId}`)})},onToolStart:r=>{te("tool_start",()=>{const o="string"==typeof r.phase?r.phase:"start",a=resolveRuntimeToolName(r)??"tool",n=resolveRuntimeToolCallId(r),i=resolveRuntimeToolArguments(r);"update"!==o?(n&&s.set(n,Date.now()),logChatLatency(e.logger,"claw.tool.start",e.requestId,t,{tool:a}),n&&B(n,{name:a,arguments:i,toolStartObserved:!0,pending:!0})):n&&B(n,{name:a,arguments:i,pending:!0})})},onItemEvent:e=>{te("item_event",()=>{const t="string"==typeof e.kind?e.kind.toLowerCase():"";if(t&&"tool"!==t)return;"string"==typeof e.phase&&e.phase.toLowerCase();const r=resolveRuntimeToolCallId(e)??("string"==typeof e.itemId&&e.itemId.trim()?e.itemId.trim():void 0),s=resolveRuntimeToolName(e)??("string"==typeof e.title&&e.title.trim()?e.title.trim():void 0);r&&s&&B(r,{name:s,itemEventObserved:!0,pending:!0})})}},oe={add(t){if(e.stream.isTerminated)return;const r=_;return e.stream.addPart(t),_+=1,r}},ae={add:e=>oe.add(e),replace:(t,r)=>!e.stream.isTerminated&&(e.stream.replacePart(t,r),!0)};return{dispatcher:re,replyOptions:se,commitAssistantMessage:t=>{te("assistant_message_commit",()=>{if(u.has(t.key))return;u.add(t.key),l=!0,Z=void 0,i="";const r=t.segments.filter(e=>"text"===e.type).map(e=>e.text).join("");n+=r,k(n);for(const e of t.segments)"text"!==e.type?L(e.id,e.name,e.arguments,!0):$(e.text);if(void 0!==t.executedModel||void 0!==y){const r=toMessagePart({type:"custom",kind:"execution_model",data:{executed_model:t.executedModel??""}});void 0===y?(e.stream.addPart(r),y=_++):e.stream.replacePart(y,r)}})},observeToolResult:r=>{te("tool_result_observation",()=>{const a=r.toolCallId.trim(),n=r.toolName.trim();if(!a||!n)return;const i=!o.has(a);if(i){o.add(a);const i=s.get(a);logChatLatency(e.logger,"claw.tool.result",e.requestId,t,{tool:n,tool_elapsed_ms:void 0===i?void 0:Math.max(0,Date.now()-i),outcome:r.isError?"error":"success",...r.isError?{error:summarizeObservedToolError(r.rawResult)??"error"}:{}})}if(e.persistObservedToolResults&&i){const e=observedToolResultText(r.rawResult)??("tool_call"===n||isRenderUiToolName(n)?"":void 0);if(void 0!==e){const t=X(a,n);if(t)D(t,J({text:e,isError:r.isError},t.artifactMediaUrls),r.isError),t.observedResult={content:e,isError:r.isError,rawResult:r.rawResult},H(t),F(t.id),"tool_call"!==t.name&&!isRenderUiToolName(t.name)||r.isError||V(t,{text:e,isError:r.isError,rawResult:r.rawResult});else{const t=U(a);t.name??=n,t.observedResult={content:e,isError:r.isError,rawResult:r.rawResult}}}}const l=searchSourceProvider(r.rawResult)??"unknown";let d=0;try{const t=extractSearchSources({toolName:n,rawResult:r.rawResult,isError:r.isError});if(d=t.length,0===d)return;const s=X(a,n);if(s)E(t,s.id);else{const e=U(a);e.name??=n,e.sources=((e,t)=>{if(!e?.length&&!t?.length)return;const r=[],s=new Map;for(const o of[...e??[],...t??[]]){const e=s.get(o.url);void 0===e?(s.set(o.url,r.length),r.push(o)):r[e]=mergeSearchSource(r[e],o)}return r})(e.sources,t)}e.logger.debug?.(`[im-sink] search source projection requestId=${e.requestId} tool=${n} provider=${l} source_count=${d}`)}catch{e.logger.warn?.(`[im-sink] search source projection ignored requestId=${e.requestId} tool=${n} provider=${l} source_count=${d}`)}})},getSourceCount:()=>Y.source,approvalParts:oe,questionParts:ae}}
1
+ import{toMessagePart}from"../message-client-types.js";import{isUploadArtifactToolName,resolveToolArtifactLinks}from"../upload/tool-artifact-enrichment.js";import{sessionWorkspaceDir,takeFinalizeAttachments}from"../upload/delivery-ledger.js";import{resolveRuntimeToolArguments,resolveRuntimeToolCallId,resolveRuntimeToolName}from"./tool-envelope-metadata.js";import{extractSearchSources,mergeSearchSource,searchSourceProvider}from"./search-source-extractor.js";import{isRenderUiToolName,isStaticHtmlInvocation,staticHtmlProjectionOf,staticHtmlResultOf}from"../canvas/static-html-result.js";import{logChatLatency}from"../chat-latency.js";const STREAMING_SNAPSHOT_PUBLISH_INTERVAL_MS=200,isNonEmptyString=e=>"string"==typeof e&&e.length>0,isPlainRecord=e=>"object"==typeof e&&null!==e&&!Array.isArray(e);function pickRawResultFromPayload(e){if(void 0!==e.rawResult)return e.rawResult;if(void 0!==e.result)return e.result;if(void 0!==e.details)return e.details;if(void 0!==e.toolResult)return e.toolResult;if(isPlainRecord(e.channelData)){const t=e.channelData;if(void 0!==t.rawResult)return t.rawResult;if(void 0!==t.result)return t.result;if(void 0!==t.details)return t.details}}function asToolParams(e){if(e&&"object"==typeof e&&!Array.isArray(e))return e}const summarizeObservedToolError=e=>{if(null==e)return;if("string"==typeof e)return e.slice(0,180);if("object"!=typeof e)return String(e).slice(0,180);const t=e;for(const e of["error","message","blockReason","details","text"]){const r=t[e];if("string"==typeof r&&r.trim())return r.slice(0,180);if(isPlainRecord(r)&&"string"==typeof r.message&&r.message.trim())return r.message.slice(0,180)}try{const t=JSON.stringify(e);return t?t.slice(0,180):void 0}catch{return}},observedToolResultText=e=>{if(!isPlainRecord(e)||!Array.isArray(e.content))return;const t=e.content.filter(e=>isPlainRecord(e)&&"text"===e.type&&"string"==typeof e.text).map(e=>e.text.trim()).filter(Boolean);return t.length>0?t.join("\n"):void 0};export function createImReplySink(e){const t=e.startedAtMs??Date.now();let r=!1;const s=new Map,o=new Set,a={text:""};let n="",i="",l=!1,d=!1;const u=new Set;let m;const c={receivedSnapshotCount:0,streamingPublishCount:0,terminalPublishCount:0},g=[];let p;const f=new Map,h=[],v=new Map,b=new Map;let y,_=0,I=1;const S=(e,t={})=>({schema_version:1,seq:e,...t}),x=new Set,R=new Set,w=t=>{t!==a.text&&(t.startsWith(a.text)?e.stream.appendBody(t.slice(a.text.length)):e.stream.setBody(t),a.text=t)},P=e=>{void 0===e.publishTimer&&(e.publishTimer=setTimeout(()=>{e.publishTimer=void 0,te("body_preview_flush",()=>{m===e&&(e=>{a.text!==e.targetBody&&(w(e.targetBody),e.streamingPublishCount+=1)})(e)})},200))},k=e=>{const t=m;t?(void 0!==t.publishTimer&&(clearTimeout(t.publishTimer),t.publishTimer=void 0),t.targetBody=e,a.text!==e&&(w(e),t.terminalPublishCount+=1),c.receivedSnapshotCount+=t.receivedSnapshotCount,c.streamingPublishCount+=t.streamingPublishCount,c.terminalPublishCount+=t.terminalPublishCount,m=void 0):w(e)},q=t=>{if(void 0!==t.partIndex)return;const r=_,s=I;e.stream.addPart(toMessagePart({type:"text",text:t.text,state:"done",_seq:s,data:S(s)})),t.partIndex=r,_+=1,I+=1},T=()=>{if(!d){d=!0;for(const e of g)q(e)}},$=e=>{const t={text:e};g.push(t),d&&q(t)},E=(t,r)=>{for(const s of t){const t=b.get(s.url);if(t){const r=mergeSearchSource(t,s);if(r.title===t.title&&r.snippet===t.snippet)continue;const o={...r,partIndex:t.partIndex,displaySeq:t.displaySeq,toolUseId:t.toolUseId};e.stream.replacePart(t.partIndex,toMessagePart({type:"source",url:o.url,...o.title?{title:o.title}:{},...o.snippet?{snippet:o.snippet}:{},tool_use_id:o.toolUseId,_seq:o.displaySeq,data:S(o.displaySeq)})),b.set(s.url,o);continue}const o={...s,partIndex:_,displaySeq:I,toolUseId:r};e.stream.addPart(toMessagePart({type:"source",url:s.url,...s.title?{title:s.title}:{},...s.snippet?{snippet:s.snippet}:{},tool_use_id:r,_seq:o.displaySeq,data:S(o.displaySeq)})),b.set(s.url,o),_+=1,I+=1,Y.source+=1}},C=(e,t,r,s)=>toMessagePart({type:"thinking",text:t,state:r,_seq:e.displaySeq,_started_at_ms:e.startedAtMs,...void 0!==s?{_duration_ms:s}:{},data:S(e.displaySeq,{started_at_ms:e.startedAtMs,...void 0!==s?{duration_ms:s}:{}})}),M=t=>{T();const r={partIndex:_,displaySeq:I,text:t,publishedText:t,receivedSnapshotCount:1,streamingPublishCount:1,startedAtMs:Date.now(),startedAtMonotonicMs:performance.now()};return e.stream.addPart(C(r,t,"streaming")),_+=1,I+=1,p=r,r},A=t=>{void 0===t.publishTimer&&(t.publishTimer=setTimeout(()=>{t.publishTimer=void 0,te("reasoning_snapshot_flush",()=>{p===t&&(t=>{t.publishedText!==t.text&&(e.stream.replacePart(t.partIndex,C(t,t.text,"streaming")),t.publishedText=t.text,t.streamingPublishCount+=1)})(t)})},200))},O=()=>{const r=p;if(!r)return;void 0!==r.publishTimer&&(clearTimeout(r.publishTimer),r.publishTimer=void 0);const s=Math.max(0,Math.round(performance.now()-r.startedAtMonotonicMs));e.stream.replacePart(r.partIndex,C(r,r.text,"done",s)),p=void 0,logChatLatency(e.logger,"claw.reasoning.settled",e.requestId,t,{reasoning_duration_ms:s,reasoning_chars:r.text.length,reasoning_snapshot_count:r.receivedSnapshotCount,reasoning_streaming_publish_count:r.streamingPublishCount,reasoning_coalesced_snapshot_count:Math.max(0,r.receivedSnapshotCount-r.streamingPublishCount),reasoning_publish_interval_ms:200})},U=e=>{const t=h.indexOf(e);t>=0&&h.splice(t,1)},F=e=>null!=e&&("object"!=typeof e||(Array.isArray(e)?e.length>0:Object.keys(e).length>0)),N=e=>{let t=v.get(e);return t||(t={},v.set(e,t)),t},j=(t,r,s)=>{const o="tool"===t.name&&"tool"!==r?r:t.name,a=!F(t.arguments)&&F(s)?s:t.arguments;o===t.name&&a===t.arguments||(e.stream.replacePart(t.partIndex,toMessagePart({type:"tool_use",id:t.id,name:t.htmlPresentation?.name??o,arguments:t.htmlPresentation?.arguments??a,state:t.state,_seq:t.displaySeq,data:S(t.displaySeq)})),t.name=o,t.arguments=a)},L=(t,r,s,o)=>{const a=f.get(t);if(a)return j(a,r,s),o&&!h.includes(t)&&"streaming"===a.state&&h.push(t),a;T();const n=v.get(t),i="tool"!==r?r:n?.name&&"tool"!==n.name?n.name:r,l=F(s)?s:n?.arguments??s,d=((t,r,s)=>{const o=_,a=I;return e.stream.addPart(toMessagePart({type:"tool_use",id:t,name:r,arguments:s,state:"streaming",_seq:a,data:S(a)})),_+=1,I+=1,{partIndex:o,displaySeq:a}})(t,i,l),u={id:t,name:i,arguments:l,partIndex:d.partIndex,displaySeq:d.displaySeq,state:"streaming",itemEventObserved:!0===n?.itemEventObserved,toolStartObserved:!0===n?.toolStartObserved};return f.set(t,u),(o||u.toolStartObserved||u.itemEventObserved)&&(h.includes(t)||"streaming"!==u.state||h.push(t)),n&&Q(u,n),v.delete(t),u},B=(e,t)=>{const r=(e=>f.get(e))(e);if(r)return void 0===t.name&&void 0===t.arguments||j(r,t.name??r.name,t.arguments??r.arguments),t.toolStartObserved&&(r.toolStartObserved=!0),t.itemEventObserved&&(r.itemEventObserved=!0),(t.pending||t.toolStartObserved||t.itemEventObserved)&&!h.includes(r.id)&&"streaming"===r.state&&h.push(r.id),r;const s=N(e);void 0!==t.name&&(s.name=t.name),void 0!==t.arguments&&(s.arguments=t.arguments),t.toolStartObserved&&(s.toolStartObserved=!0),t.itemEventObserved&&(s.itemEventObserved=!0)},D=t=>{"done"!==t.state&&(e.stream.replacePart(t.partIndex,toMessagePart({type:"tool_use",id:t.id,name:t.htmlPresentation?.name??t.name,arguments:t.htmlPresentation?.arguments??t.arguments,state:"done",_seq:t.displaySeq,data:S(t.displaySeq)})),t.state="done")},H=e=>{if(null===e||"object"!=typeof e)return JSON.stringify(e)??String(e);if(Array.isArray(e))return`[${e.map(H).join(",")}]`;return`{${Object.entries(e).sort(([e],[t])=>e.localeCompare(t)).map(([e,t])=>`${JSON.stringify(e)}:${H(t)}`).join(",")}}`},z=(t,r,s)=>{const o=`${s?"1":"0"}:${H(r)}`;if(t.progressSignature===o)return;const a=toMessagePart({type:"custom",kind:"tool_progress",data:{tool_use_id:t.id,content:r,...s?{is_error:!0}:{}}});if(void 0===t.progressPartIndex){const r=_;return e.stream.addPart(a),t.progressPartIndex=r,t.progressSignature=o,void(_+=1)}e.stream.replacePart(t.progressPartIndex,a),t.progressSignature=o},Q=(e,t)=>{if(t.progress?.length)for(const r of t.progress)z(e,r.content,r.isError);t.observedResult&&(z(e,t.observedResult.content,t.observedResult.isError),e.observedResult=t.observedResult,"tool_call"!==e.name&&!isRenderUiToolName(e.name)||t.observedResult.isError||V(e,{text:t.observedResult.content,isError:t.observedResult.isError,rawResult:t.observedResult.rawResult})),(t.progress?.length||t.observedResult)&&(D(e),U(e.id)),!t.observedResult&&t.htmlPayload&&V(e,t.htmlPayload),t.sources?.length&&E(t.sources,e.id)},W=e=>[...new Set([e.mediaUrl,...e.mediaUrls??[]].filter(e=>isNonEmptyString(e)).map(e=>e.trim()).filter(Boolean))],J=(e,t=[])=>{const r=[...new Set([...W(e),...t])];return isNonEmptyString(e.text)&&r.length>0?{text:e.text,media_urls:r}:isNonEmptyString(e.text)?e.text:1===r.length?r[0]:r.length>1?{media_urls:r}:!0===e.isError?"Tool execution failed.":""},K=t=>{for(const r of t)e.stream.addPart(toMessagePart({type:"resource_link",uri:r.uri,name:r.name,...r.mimeType?{mimeType:r.mimeType}:{}})),_+=1},G=t=>{x.add(t);const r=()=>{x.delete(t)};e.onCallbackFailure?t.then(r,r):t.finally(r)},V=(t,r)=>{const s=t.observedResult,o=s?.isError??!0===r.isError,a=s?.rawResult??pickRawResultFromPayload(r),n=o?void 0:staticHtmlProjectionOf(t.name,t.arguments,a);if(!n&&isStaticHtmlInvocation(t.name,t.arguments))return;if(n&&e.htmlDelivery?.publishedFileId&&e.htmlDelivery.publishedFileId!==n.result.artifact.fileId)return;if(t.durableUploadFilesEmitted&&(!n||t.staticHtmlResultEmitted))return;e.logger.info?.(`[im-sink] schedule tool artifact enrichment requestId=${e.requestId} toolUseId=${t.id} name=${t.name}`);const i=(async()=>{let s=[];try{s=await resolveToolArtifactLinks({toolCallId:t.id,toolName:t.name,rawResult:a,rawText:"string"==typeof r.text?r.text:void 0,params:asToolParams(t.arguments),cwd:sessionWorkspaceDir(e.sessionKey),payloadMediaUrls:W(r)})}catch(r){const s=r instanceof Error?r.message:String(r);if(e.logger.warn?.(`[im-sink] resolveToolArtifactLinks failed requestId=${e.requestId} toolUseId=${t.id} error=${s}`),e.onCallbackFailure?.(r),e.onCallbackFailure)throw r;return}n&&(s=s.filter(e=>e.uri!==n.result.artifact.uri)),0!==s.length||n?(e.logger.info?.(`[im-sink] tool artifact enrichment resolved ${s.length} link(s) requestId=${e.requestId} toolUseId=${t.id}`),await new Promise((o,a)=>{const i=e.callbackQueue.enqueueSync(()=>{try{if(e.abortSignal.aborted||e.stream.isTerminated)return;const o=s.map(e=>e.uri);t.artifactMediaUrls=o;const a=t.observedResult;z(t,J(a?{text:a.content,isError:a.isError}:n?{...r,mediaUrl:r.mediaUrl===n.result.artifact.uri?void 0:r.mediaUrl,mediaUrls:r.mediaUrls?.filter(e=>e!==n.result.artifact.uri)}:r,o),a?.isError??!0===r.isError),((t,r,s,o)=>{const a=r.length>0&&!t.durableUploadFilesEmitted,n=!s&&void 0!==o&&!t.staticHtmlResultEmitted;(a||n)&&(n&&(t.htmlPresentation={name:"render_ui",arguments:o.arguments},e.stream.replacePart(t.partIndex,toMessagePart({type:"tool_use",id:t.id,...t.htmlPresentation,state:t.state,_seq:t.displaySeq,data:S(t.displaySeq)}))),e.stream.appendToolResult(t.id,[...n?[{type:"text",text:JSON.stringify(o.result)}]:[],...a?r.map(e=>({type:"resource_link",uri:e.uri,name:e.name,...e.mimeType?{mimeType:e.mimeType}:{}})):[]],s),_+=1,n&&(t.staticHtmlResultEmitted=!0,e.htmlDelivery?.recordReply(o.result)),a&&(t.durableUploadFilesEmitted=!0,K(r)))})(t,s,a?.isError??!0===r.isError,n)}catch(r){const s=r instanceof Error?r.message:String(r);e.logger.warn?.(`[im-sink] tool artifact enrichment apply failed requestId=${e.requestId} toolUseId=${t.id} error=${s}`),e.onCallbackFailure?.(r),e.onCallbackFailure&&a(r)}finally{o()}});!1===i&&e.onCallbackFailure&&e.callbackQueue.waitForIdle().then(o,a)})):e.logger.debug?.(`[im-sink] tool artifact enrichment found no links requestId=${e.requestId} toolUseId=${t.id} name=${t.name}`)})().catch(r=>{const s=r instanceof Error?r.message:String(r);if(e.logger.warn?.(`[im-sink] tool artifact enrichment failed requestId=${e.requestId} toolUseId=${t.id} error=${s}`),e.onCallbackFailure?.(r),e.onCallbackFailure)throw r});G(i)},X=(e,t,r)=>B(e,{name:t,arguments:r}),Y={tool:0,block:0,final:0,source:0};let Z,ee=!1;const te=(t,r)=>{e.callbackQueue.enqueueSync(()=>{if((t=>e.abortSignal.aborted?(e.logger.debug?.(`[im-sink] ${t} ignored after abort requestId=${e.requestId} channel=${e.channelId} in_reply_to=${e.inboundMessageId}`),!1):e.stream.isTerminated?(e.logger.debug?.(`[im-sink] ${t} ignored after stream_terminated requestId=${e.requestId} channel=${e.channelId} in_reply_to=${e.inboundMessageId}`),!1):!ee||(e.logger.debug?.(`[im-sink] ${t} ignored after mark_complete requestId=${e.requestId} channel=${e.channelId} in_reply_to=${e.inboundMessageId}`),!1))(t))try{r()}catch(r){const s=r instanceof Error?r.message:String(r);e.logger.warn?.(`[im-sink] ${t} mapping failed requestId=${e.requestId} channel=${e.channelId} in_reply_to=${e.inboundMessageId} error=${s}`),e.onCallbackFailure?.(r)}})},re={sendToolResult:t=>(te("tool_progress",()=>{const r=resolveRuntimeToolCallId(t),s=resolveRuntimeToolName(t),o=resolveRuntimeToolArguments(t),a=J(t),n=!0===t.isError;let i;if(r){if(i=X(r,s,o),!i){const e=N(r);if(s&&(e.name=s),F(o)&&(e.arguments=o),e.progress=[...e.progress??[],{content:a,isError:n}],"tool_call"===s||isRenderUiToolName(s??"")){const r=pickRawResultFromPayload(t);(n||void 0!==r)&&(e.htmlPayload=!n&&staticHtmlResultOf(r)?t:void 0)}return void(Y.tool+=1)}i&&(Y.tool+=1,i.observedResult||z(i,a,n),D(i),U(i.id),V(i,t))}else e.logger.debug?.(`[im-sink] unattributed tool progress ignored requestId=${e.requestId} channel=${e.channelId} tool=${s??"unknown"} pending=${h.length}`)}),!0),sendBlockReply:e=>(te("block_reply",()=>{Y.block+=1,isNonEmptyString(e.text)&&(Z=!0!==e.isError?void 0:e.text)}),!0),sendFinalReply:t=>(te("final_reply",()=>{var r;(Y.final+=1,!0!==t.isError)?((t=>{const r=W(t).filter(e=>!R.has(e));if(0===r.length)return;for(const e of r)R.add(e);const s=(async()=>{let t=[];try{t=await resolveToolArtifactLinks({toolCallId:`final:${e.runId??e.requestId}`,toolName:"final_reply_media",payloadMediaUrls:r})}catch(t){for(const e of r)R.delete(e);if(e.logger.warn?.(`[im-sink] final media enrichment failed requestId=${e.requestId} error=${t instanceof Error?t.message:String(t)}`),e.onCallbackFailure?.(t),e.onCallbackFailure)throw t;return}if(0!==t.length)await new Promise((r,s)=>{!1===e.callbackQueue.enqueueSync(()=>{try{if(e.abortSignal.aborted||e.stream.isTerminated)return;K(t)}catch(t){e.logger.warn?.(`[im-sink] final media enrichment apply failed requestId=${e.requestId} error=${t instanceof Error?t.message:String(t)}`),e.onCallbackFailure?.(t),e.onCallbackFailure&&s(t)}finally{r()}})&&e.onCallbackFailure&&e.callbackQueue.waitForIdle().then(r,s)});else for(const e of r)R.delete(e)})();G(s)})(t),isNonEmptyString(t.text)&&(Z=void 0,l||(r=t.text,i="",n+=r,k(n),$(r),l=!0))):Z=t.text}),!0),waitForIdle:async()=>{for(let t=0;(e.onCallbackFailure||t<16)&&(await e.callbackQueue.waitForIdle(),0!==x.size);t++)e.onCallbackFailure?await Promise.all([...x]):await Promise.allSettled([...x]);await e.callbackQueue.waitForIdle();const t=takeFinalizeAttachments(e.sessionKey);t.length>0&&!e.abortSignal.aborted&&!e.stream.isTerminated&&K(t)},getQueuedCounts:()=>({tool:Y.tool,block:Y.block,final:Y.final}),getAccumulatedBody:()=>a.text,getTerminalErrorText:()=>Z,markComplete:r=>{O(),v.size>0&&e.logger.debug?.(`[im-sink] dropping uncommitted lifecycle tool signals requestId=${e.requestId} buffered=${v.size}`),i="",k(n),c.receivedSnapshotCount>0&&logChatLatency(e.logger,"claw.text_preview.settled",e.requestId,t,{text_preview_snapshot_count:c.receivedSnapshotCount,text_preview_streaming_publish_count:c.streamingPublishCount,text_preview_terminal_publish_count:c.terminalPublishCount,text_preview_coalesced_snapshot_count:Math.max(0,c.receivedSnapshotCount-c.streamingPublishCount-c.terminalPublishCount),text_preview_publish_interval_ms:200});for(const t of f.values())try{"success"===r&&isUploadArtifactToolName(t.name)&&!t.durableUploadFilesEmitted&&V(t,{toolCallId:t.id,toolName:t.name}),"success"===r&&D(t),U(t.id)}catch(r){const s=r instanceof Error?r.message:String(r);e.logger.warn?.(`[im-sink] tool finalization failed requestId=${e.requestId} toolUseId=${t.id} error=${s}`)}v.clear(),ee=!0}},se={...e.runId?{runId:e.runId}:{},abortSignal:e.abortSignal,disableBlockStreaming:!0,onAgentRunStart:r=>{e.onAgentRunStart?.(r),logChatLatency(e.logger,"claw.runtime.run_started",e.requestId,t),e.logger.info?.(`[im-sink] run_start requestId=${e.requestId} runId=${r} channel=${e.channelId} in_reply_to=${e.inboundMessageId} origin=${e.origin??"im"}`)},onPartialReply:s=>{te("partial",()=>{l||"string"==typeof s.text&&(!r&&s.text.length>0&&(r=!0,logChatLatency(e.logger,"claw.model.first_delta",e.requestId,t)),i=s.text,(e=>{const t=m;if(!t){if(a.text===e)return;return w(e),void(m={targetBody:e,receivedSnapshotCount:1,streamingPublishCount:1,terminalPublishCount:0})}t.targetBody!==e&&(t.targetBody=e,t.receivedSnapshotCount+=1,P(t))})(n+i))})},onReasoningStream:e=>{te("reasoning",()=>{if(!isNonEmptyString(e.text))return;const t=e.text,r=p??M(t);t!==r.text&&(r.text.startsWith(t)||(t.startsWith(r.text)?(r.text=t,r.receivedSnapshotCount+=1,A(r)):(O(),M(t))))})},onReasoningEnd:()=>{te("reasoning_end",()=>{O()})},onAssistantMessageStart:()=>{te("assistant_message_start",()=>{i="",l=!1,k(n),e.logger.debug?.(`[im-sink] assistant_message_start requestId=${e.requestId} channel=${e.channelId} in_reply_to=${e.inboundMessageId}`)})},onToolStart:r=>{te("tool_start",()=>{const o="string"==typeof r.phase?r.phase:"start",a=resolveRuntimeToolName(r)??"tool",n=resolveRuntimeToolCallId(r),i=resolveRuntimeToolArguments(r);"update"!==o?(n&&s.set(n,Date.now()),logChatLatency(e.logger,"claw.tool.start",e.requestId,t,{tool:a}),n&&B(n,{name:a,arguments:i,toolStartObserved:!0,pending:!0})):n&&B(n,{name:a,arguments:i,pending:!0})})},onItemEvent:e=>{te("item_event",()=>{const t="string"==typeof e.kind?e.kind.toLowerCase():"";if(t&&"tool"!==t)return;"string"==typeof e.phase&&e.phase.toLowerCase();const r=resolveRuntimeToolCallId(e)??("string"==typeof e.itemId&&e.itemId.trim()?e.itemId.trim():void 0),s=resolveRuntimeToolName(e)??("string"==typeof e.title&&e.title.trim()?e.title.trim():void 0);r&&s&&B(r,{name:s,itemEventObserved:!0,pending:!0})})}},oe={add(t){if(e.stream.isTerminated)return;const r=_;return e.stream.addPart(t),_+=1,r}},ae={add:e=>oe.add(e),replace:(t,r)=>!e.stream.isTerminated&&(e.stream.replacePart(t,r),!0)};return{dispatcher:re,replyOptions:se,commitAssistantMessage:t=>{te("assistant_message_commit",()=>{if(u.has(t.key))return;u.add(t.key),l=!0,Z=void 0,i="";const r=t.segments.filter(e=>"text"===e.type).map(e=>e.text).join("");n+=r,k(n);for(const e of t.segments)"text"!==e.type?L(e.id,e.name,e.arguments,!0):$(e.text);if(void 0!==t.executedModel||void 0!==y){const r=toMessagePart({type:"custom",kind:"execution_model",data:{executed_model:t.executedModel??""}});void 0===y?(e.stream.addPart(r),y=_++):e.stream.replacePart(y,r)}})},observeToolResult:r=>{te("tool_result_observation",()=>{const a=r.toolCallId.trim(),n=r.toolName.trim();if(!a||!n)return;const i=!o.has(a);if(i){o.add(a);const i=s.get(a);logChatLatency(e.logger,"claw.tool.result",e.requestId,t,{tool:n,tool_elapsed_ms:void 0===i?void 0:Math.max(0,Date.now()-i),outcome:r.isError?"error":"success",...r.isError?{error:summarizeObservedToolError(r.rawResult)??"error"}:{}})}if(e.persistObservedToolResults&&i){const e=observedToolResultText(r.rawResult)??("tool_call"===n||isRenderUiToolName(n)?"":void 0);if(void 0!==e){const t=X(a,n);if(t)z(t,J({text:e,isError:r.isError},t.artifactMediaUrls),r.isError),t.observedResult={content:e,isError:r.isError,rawResult:r.rawResult},D(t),U(t.id),"tool_call"!==t.name&&!isRenderUiToolName(t.name)||r.isError||V(t,{text:e,isError:r.isError,rawResult:r.rawResult});else{const t=N(a);t.name??=n,t.observedResult={content:e,isError:r.isError,rawResult:r.rawResult}}}}const l=searchSourceProvider(r.rawResult)??"unknown";let d=0;try{const t=extractSearchSources({toolName:n,rawResult:r.rawResult,isError:r.isError});if(d=t.length,0===d)return;const s=X(a,n);if(s)E(t,s.id);else{const e=N(a);e.name??=n,e.sources=((e,t)=>{if(!e?.length&&!t?.length)return;const r=[],s=new Map;for(const o of[...e??[],...t??[]]){const e=s.get(o.url);void 0===e?(s.set(o.url,r.length),r.push(o)):r[e]=mergeSearchSource(r[e],o)}return r})(e.sources,t)}e.logger.debug?.(`[im-sink] search source projection requestId=${e.requestId} tool=${n} provider=${l} source_count=${d}`)}catch{e.logger.warn?.(`[im-sink] search source projection ignored requestId=${e.requestId} tool=${n} provider=${l} source_count=${d}`)}})},getSourceCount:()=>Y.source,approvalParts:oe,questionParts:ae}}
@@ -1 +1 @@
1
- import{createNativeOutputFileAttempt}from"./native-output-file-attempt.js";import{downloadNativeInputFile}from"./native-input-files.js";import{bindExistingRuntimeChatReply}from"@beeos-ai/cloud-harness-sdk";import{RUNTIME_HASH_DOMAINS}from"@beeos-ai/beeos-types/runtime";import{dispatchImInboundPrompt}from"../im-runtime-dispatch.js";import{prepareNativePromptInput}from"./native-prompt-preflight.js";import{verifyNativePromptAuthority}from"./native-prompt-authority.js";import{RuntimeCommandError}from"./runtime-command-dispatcher.js";import{runtimeCommandResultHash}from"./runtime-command-journal.js";import{runtimeMessageBase,runtimeMessageHash,semanticEqual}from"./runtime-message-channel-contract.js";import{openClawSessionModePatch}from"./runtime-session-control-service.js";export const nativePromptCancellationReason="chat_cancel:runtime_operation_cancel_requested";export const nativePromptExecutionDeadlineReason="deadline exceeded: the Cloud turn execution budget is about to expire";const nativePromptWrapUpMarginMs=3e4;export class NativeCloudPromptBridge{options;constructor(e){if(this.options=e,"function"!=typeof e.host.resolveNativePromptSessionKey)throw new RuntimeCommandError("INVALID_PARAMS","native Core target resolver required");if("function"!=typeof e.host.prepareNativePromptModel)throw new RuntimeCommandError("INVALID_PARAMS","native Core model preparation required");if(!e.runBoundary)throw new RuntimeCommandError("OUTCOME_UNKNOWN","native MS run boundary required")}async execute(e){const{options:t}=this;if(await t.verifier.refresh(),!e.delivery.executionGrant)throw new RuntimeCommandError("INVALID_PARAMS","native grant missing");const a=e.delivery.message.payload,n="string"==typeof a.operationId?await t.journal.get(a.operationId):null,i=!!n&&"session.prompt"===n.command&&["started","effect_committed","projection_committed","failed"].includes(n.phase)&&n.paramsHash===a.paramsHash&&n.messageBinding?.requestMessageId===a.requestMessageId&&n.messageBinding?.requestHash===a.requestHash,r=verifyNativePromptAuthority({request:e.delivery.message.payload,decoded:t.verifier.verifyAndDecodeGrant(e.delivery.executionGrant),lease:e.lease,lineage:t.getLineage(),allowExpiredReadOnly:i}),{params:o}=r,s=r.request;try{const a=e.delivery.message;if("runtime_rpc_request"!==a.type||"completed"!==a.state||a.message_id!==s.requestMessageId||a.channel_id!==s.operationId||void 0!==a.in_reply_to)throw new RuntimeCommandError("INVALID_PARAMS","native envelope binding invalid");let n;void 0!==o.mode&&openClawSessionModePatch(o.mode);try{if(void 0!==o.mentions)throw new TypeError("native host per-prompt optional binding not implemented");n=prepareNativePromptInput(o.parts)}catch(e){throw new RuntimeCommandError(i?"OUTCOME_UNKNOWN":"INVALID_PARAMS","native host part unsupported",void 0,{cause:e})}const d=n.message;if(!t.host.activeRunAuthority)throw new RuntimeCommandError("EXECUTION_FAILED","native existing active-run authority unavailable");const m=()=>{const a=t.getLease(),n=t.getLineage();if(!a||a.instanceId!==e.lease.instanceId||a.handlerIdentity!==e.lease.handlerIdentity||a.runtimeEpoch!==o.runtimeLease.runtimeEpoch||a.leaseId!==o.runtimeLease.leaseId||n.journalStoreId!==o.runtimeLease.journalStoreId||n.journalGeneration!==o.runtimeLease.journalGeneration||Date.parse(a.leaseExpiresAt)<=Date.now())throw new RuntimeCommandError("INSTANCE_EPOCH_MISMATCH","native prompt attempt fenced")};m();const u=await t.channel.history({operationId:s.operationId,lease:e.lease,currentNativePrompt:r});if(m(),"found"!==u.status||u.snapshot.requestHash!==s.requestHash||u.snapshot.requestMessageId!==s.requestMessageId||!semanticEqual(u.snapshot.request.payload,s))throw new RuntimeCommandError("INVALID_PARAMS","native retained request mismatch");let l,p=await t.journal.get(s.operationId);if(m(),p&&("session.prompt"!==p.command||p.paramsHash!==s.paramsHash||p.runtimeEpoch!==o.runtimeLease.runtimeEpoch||p.messageBinding?.requestMessageId!==s.requestMessageId||p.messageBinding.requestHash!==s.requestHash))throw new RuntimeCommandError("OUTCOME_UNKNOWN","native existing journal binding mismatch");if(!t.http.getMessage)throw new RuntimeCommandError("OUTCOME_UNKNOWN","native task snapshot unavailable");try{l=await t.http.getMessage(t.getLease(),{conversationId:o.conversationId,messageId:o.taskRequestMessageId})}catch(e){throw new RuntimeCommandError("OUTCOME_UNKNOWN","native original task snapshot unavailable",void 0,{cause:e})}if(m(),l.messageId!==o.taskRequestMessageId||l.conversationId!==o.conversationId||"chat_message"!==l.type||l.body!==d)throw new RuntimeCommandError("OUTCOME_UNKNOWN","native original task snapshot mismatch");const c=async()=>{if(!t.http.getMessage)throw new RuntimeCommandError("OUTCOME_UNKNOWN","native single snapshot unavailable");m();const e=await t.http.getMessage(t.getLease(),{conversationId:o.conversationId,messageId:o.replyMessageId});return m(),this.assertReply(e,r),e};if(u.snapshot.started||p&&"prepared"!==p.phase){const e=u.snapshot.runBoundary?.finishedReceipt,a=u.snapshot.final;if(!(p&&["started","effect_committed","projection_committed","failed"].includes(p.phase)&&e&&"finished"===e.state&&e.activeRunRegistrationId===o.taskId&&a))throw new RuntimeCommandError("OUTCOME_UNKNOWN","native durable MS finish audit or original result unavailable");const n=await c();if("agent_reply"!==n.type||n.state!==e.replyState||n.stopReason!==e.replyStopReason||n.updatedAt!==e.replyUpdatedAt)throw new RuntimeCommandError("OUTCOME_UNKNOWN","native original terminal does not match finished audit");const i=this.terminal(n,r);if(a.payload.outcome!==i.outcome||"final_message"!==u.snapshot.terminalSource)throw new RuntimeCommandError("OUTCOME_UNKNOWN","native original final outcome mismatch");const d=a.payload.response;if("2.0"!==d.jsonrpc||d.id!==s.request.id)throw new RuntimeCommandError("OUTCOME_UNKNOWN","native original final RPC binding mismatch");const l=runtimeCommandResultHash(i.result);if(p.resultReceipt&&p.resultReceipt.resultHash!==l||void 0!==p.result&&!semanticEqual(p.result,i.result))throw new RuntimeCommandError("OUTCOME_UNKNOWN","native original result conflicts with local receipt");if(void 0===d.result)throw new RuntimeCommandError("OUTCOME_UNKNOWN","native original canonical result evidence unavailable");{const e=u.snapshot.effectCommitted?.payload,t=runtimeMessageHash(RUNTIME_HASH_DOMAINS.coreResult,{method:s.method,result:d.result});if("succeeded"!==i.outcome||void 0!==d.error||!semanticEqual(d.result,i.result)||!e||e.effectRecord.coreResultHash!==t||e.effectRecord.coreResultRef!==`sha256:${t}`||a.payload.effectRecordHash!==e.effectRecordHash||a.payload.serviceResultHash!==runtimeMessageHash(RUNTIME_HASH_DOMAINS.serviceResult,{method:s.method,result:d.result}))throw new RuntimeCommandError("OUTCOME_UNKNOWN","native original final result hash evidence mismatch")}const h=d.result;return m(),"started"===p.phase&&(p=await t.journal.transition({operationId:s.operationId,expectedRevision:p.revision,phase:"succeeded"===i.outcome?"effect_committed":"failed",result:h}),m()),{request:s,outcome:i.outcome,result:h,replayedFinal:a}}if(n.files.length&&!t.inputFiles)throw new RuntimeCommandError("EXECUTION_FAILED","native authorized input file resolver unavailable");if(u.snapshot.terminal||e.signal.aborted||u.snapshot.cancelRequested||await t.journal.getCancellation(s.operationId))throw new RuntimeCommandError("OPERATION_CANCELLED","native prompt cancelled before effect");const h=await t.host.resolveNativePromptSessionKey(r,m);if(m(),p=await t.journal.acceptMessageRequest({operationId:s.operationId,command:"session.prompt",paramsHash:s.paramsHash,runtimeEpoch:o.runtimeLease.runtimeEpoch,requestMessageId:s.requestMessageId,requestHash:s.requestHash}),m(),"prepared"!==p.phase)throw new RuntimeCommandError("OUTCOME_UNKNOWN","native task already started");let v;const g=await bindExistingRuntimeChatReply({authority:{currentLease:t.getLease},http:t.http,binding:{conversationId:o.conversationId,messageId:o.replyMessageId,taskRequestMessageId:o.taskRequestMessageId,assertAttempt:m,assertPreparedClear:e=>{if(m(),!v||e.activeRunRegistrationId!==v||e.historyGeneration!==r.historyGeneration||e.runtimeEpoch!==o.runtimeLease.runtimeEpoch)throw new RuntimeCommandError("OUTCOME_UNKNOWN","native prepared clear does not freeze this registered attempt")},mutationKey:e=>`native-reply:${s.operationId}:${o.runtimeLease.journalStoreId}:${o.runtimeLease.journalGeneration}:${e}`}});if(m(),e.signal.aborted||await t.journal.getCancellation(s.operationId))throw new RuntimeCommandError("OPERATION_CANCELLED","native prompt cancelled before start");await t.channel.append({operationId:s.operationId,lease:t.getLease(),currentNativePrompt:r,executionGrant:e.delivery.executionGrant,type:"runtime_operation_started",payload:{...runtimeMessageBase(s),executionGrantHash:s.executionGrantHash,handlerIdentity:e.lease.handlerIdentity,executionMode:"start",claimedAt:p.updatedAt}}),m(),p=await t.journal.transition({operationId:s.operationId,expectedRevision:p.revision,phase:"started"}),m();const I=()=>{m();const e=t.getLease();return{leaseId:e.leaseId,handlerIdentity:e.handlerIdentity,runtimeEpoch:e.runtimeEpoch,runtimeLeaseCredential:e.runtimeLeaseCredential}},f={operationId:s.operationId,requestMessageId:s.requestMessageId,conversationId:r.command.context.conversation.runtimeConversationUid,historyGeneration:r.historyGeneration,runtimeRunId:o.runtimeRunId},R=await t.runBoundary.start({...f,lease:I()});if(m(),"registered"!==R.status||R.historyGeneration!==r.historyGeneration||R.activeRunRegistrationId!==o.taskId)throw new RuntimeCommandError("OUTCOME_UNKNOWN","native MS run start did not register the original task");v=R.activeRunRegistrationId;const N=new AbortController,w=()=>N.abort(e.signal.reason);if(e.signal.addEventListener("abort",w,{once:!0}),e.signal.aborted&&w(),e.setCancel(e=>N.abort(e)),await t.journal.getCancellation(s.operationId)&&N.abort(nativePromptCancellationReason),m(),N.signal.aborted){if(e.signal.removeEventListener("abort",w),N.signal.reason===nativePromptCancellationReason)throw new RuntimeCommandError("OPERATION_CANCELLED","native prompt cancelled before host invocation");throw new RuntimeCommandError("OUTCOME_UNKNOWN","native registered run stopped before host invocation")}const E=[];let y;const C=JSON.parse((await g.opened()).rawText),O=C.parts?.length??0,M=async e=>{const t=await e;return m(),this.assertReply(t,r),y=t,t},_={id:g.id,get isTerminated(){return g.isTerminated},get parts(){return E},opened:async()=>({messageId:g.id,outcome:"bound"}),appendBody:e=>g.appendBody(e),setBody:e=>g.setBody(e),addPart:e=>{E.push(e),g.addPart(e)},replacePart:(e,t)=>{E[e]=t,g.replacePart(O+e,t)},appendToolResult:(e,t,a)=>{const n={type:"tool_result",tool_use_id:e,content:t,...a?{is_error:!0}:{}};E.push(n),g.addPart(n)},finalize:e=>M(g.finalize(e)),fail:e=>M(g.fail(e)),cancel:e=>M(g.cancel(e))},b={message:d,mediaPaths:[],mediaTypes:[]};let U,A;const q=createNativeOutputFileAttempt({operationId:s.operationId,requestMessageId:s.requestMessageId,paramsHash:s.paramsHash,activeRunRegistrationId:v,getLease:()=>(m(),t.getLease()),assertAttempt:m,signal:N.signal}),T=setTimeout(()=>N.abort(nativePromptExecutionDeadlineReason),Math.max(0,Math.min(Date.parse(s.executionDeadlineAt)-3e4-Date.now(),2147483647)));T.unref?.();try{await dispatchImInboundPrompt({...t.host,runtimeRunId:o.runtimeRunId,onNativeAgentRunStart:e=>q.bindHostRun(e),nativeActiveRunRegistration:{runtimeConversationUid:f.conversationId,runtimeSessionKey:r.command.context.conversation.runtimeSessionKey,historyGeneration:R.historyGeneration,targetMessageId:o.taskRequestMessageId,runtimeRunId:o.runtimeRunId,activeRunRegistrationId:R.activeRunRegistrationId},assertNativeTarget:async()=>{if(await t.host.resolveNativePromptSessionKey(r,m)!==h)throw new RuntimeCommandError("OUTCOME_UNKNOWN","native prompt target changed before host invocation")},prepareNativeModel:async e=>{m(),await t.host.prepareNativePromptModel(r,e,m),m();for(const e of n.files){if(!t.inputFiles)throw new RuntimeCommandError("OUTCOME_UNKNOWN","native authorized input file resolver unavailable");const a=await t.inputFiles.resolve({operationId:s.operationId,requestMessageId:s.requestMessageId,paramsHash:s.paramsHash,partIndex:e.partIndex,lease:t.getLease(),signal:N.signal});m();const n=await downloadNativeInputFile({fileId:e.fileId,...a,signal:N.signal,assertAttempt:m});b.mediaPaths.push(n.path),b.mediaTypes.push(n.mimeType)}m()},onNativeRunCompletion:e=>{A=e},retainedStream:_,abortSignal:N.signal,inReplyTo:o.taskRequestMessageId,prompt:{channelId:o.conversationId,inboundMessageId:o.taskRequestMessageId,requestId:s.requestMessageId,selfPrincipalId:e.lease.instanceId,sessionKey:h,dispatchInput:b},client:{messages:{startStream(){throw new Error("native reply POST forbidden")},getEnvelope:async()=>({state:(await c()).state})},getSnapshot:()=>({connection:"connected",messages:{},conversations:{}})}})}catch(e){U=e}finally{clearTimeout(T),q.close(),e.signal.removeEventListener("abort",w)}if(!A&&U)throw U;if(!A||A.runtimeRunId!==o.runtimeRunId||A.token.sessionKey!==h)throw new RuntimeCommandError("OUTCOME_UNKNOWN","native physical host completion unavailable");let P;try{await A.completion}catch(e){P=e}if(m(),!y)throw new RuntimeCommandError("OUTCOME_UNKNOWN",void 0!==P?"native host/abort/drain did not converge":"native kernel settled without verified original reply terminal",void 0,{cause:P??U});const S=this.terminal(y,r),L=await c();g.assertTerminalSnapshot(L);const x="completed"===L.state?"completed":"cancelled"===L.state?"cancelled":"failed",H=await t.runBoundary.finish({...f,activeRunRegistrationId:v,terminalState:x,source:"runtime",lease:I()});if(m(),"finished"!==H.status&&"idempotent"!==H.status||H.activeRunRegistrationId!==v)throw new RuntimeCommandError("OUTCOME_UNKNOWN","native MS run finish was not acknowledged");return await t.journal.transition({operationId:s.operationId,expectedRevision:p.revision,phase:"succeeded"===S.outcome?"effect_committed":"failed",result:S.result}),m(),{request:s,...S}}catch(e){throw new NativePromptExecutionFailure(s,e)}}assertReply(e,t){if(e.messageId!==t.params.replyMessageId||e.conversationId!==t.params.conversationId||e.replyTo!==t.params.taskRequestMessageId)throw new RuntimeCommandError("OUTCOME_UNKNOWN","native durable reply binding mismatch")}terminal(e,t){if(!e.stopReason||!["completed","failed","refused","cancelled"].includes(e.state))throw new RuntimeCommandError("OUTCOME_UNKNOWN","native durable terminal absent");const a="cancelled"===e.state&&"user_stop"===e.stopReason?"cancelled":"completed"===e.state&&"end_turn"===e.stopReason?"succeeded":"failed";return{outcome:a,result:{runtimeRunId:t.params.runtimeRunId,stopReason:e.stopReason,requestAborted:"cancelled"===e.state&&"user_stop"===e.stopReason,..."failed"===a?{error:e.body}:{}}}}}export const NATIVE_PROMPT_UNKNOWN_REASONS=new Set(["native MS run boundary required","native existing journal binding mismatch","native task snapshot unavailable","native original task snapshot unavailable","native original task snapshot mismatch","native single snapshot unavailable","native durable MS finish audit or original result unavailable","native original terminal does not match finished audit","native original final outcome mismatch","native original final RPC binding mismatch","native original result conflicts with local receipt","native original final result hash evidence mismatch","native original canonical result evidence unavailable","native task already started","native prepared clear does not freeze this registered attempt","native MS run start did not register the original task","native registered run stopped before host invocation","native prompt target changed before host invocation","native authorized input file resolver unavailable","native physical host completion unavailable","native host/abort/drain did not converge","native kernel settled without verified original reply terminal","native MS run finish was not acknowledged","native durable reply binding mismatch","native durable terminal absent"]);export function nativePromptUnknownReason(e){return e instanceof RuntimeCommandError&&"OUTCOME_UNKNOWN"===e.code?NATIVE_PROMPT_UNKNOWN_REASONS.has(e.message)?e.message:"unlisted":"not_unknown"}export class NativePromptExecutionFailure extends Error{request;failure;constructor(e,t){super(t instanceof Error?t.message:String(t),{cause:t}),this.request=e,this.failure=t}}
1
+ import{HtmlDeliveryGuard}from"../canvas/html-delivery.js";import{createNativeOutputFileAttempt}from"./native-output-file-attempt.js";import{downloadNativeInputFile}from"./native-input-files.js";import{bindExistingRuntimeChatReply}from"@beeos-ai/cloud-harness-sdk";import{RUNTIME_HASH_DOMAINS}from"@beeos-ai/beeos-types/runtime";import{dispatchImInboundPrompt}from"../im-runtime-dispatch.js";import{prepareNativePromptInput}from"./native-prompt-preflight.js";import{verifyNativePromptAuthority}from"./native-prompt-authority.js";import{RuntimeCommandError}from"./runtime-command-dispatcher.js";import{runtimeCommandResultHash}from"./runtime-command-journal.js";import{runtimeMessageBase,runtimeMessageHash,semanticEqual}from"./runtime-message-channel-contract.js";import{openClawSessionModePatch}from"./runtime-session-control-service.js";export const nativePromptCancellationReason="chat_cancel:runtime_operation_cancel_requested";export const nativePromptExecutionDeadlineReason="deadline exceeded: the Cloud turn execution budget is about to expire";const nativePromptWrapUpMarginMs=3e4;export class NativeCloudPromptBridge{options;constructor(e){if(this.options=e,"function"!=typeof e.host.resolveNativePromptSessionKey)throw new RuntimeCommandError("INVALID_PARAMS","native Core target resolver required");if("function"!=typeof e.host.prepareNativePromptModel)throw new RuntimeCommandError("INVALID_PARAMS","native Core model preparation required");if(!e.runBoundary)throw new RuntimeCommandError("OUTCOME_UNKNOWN","native MS run boundary required")}async execute(e){const{options:t}=this;if(await t.verifier.refresh(),!e.delivery.executionGrant)throw new RuntimeCommandError("INVALID_PARAMS","native grant missing");const a=e.delivery.message.payload,n="string"==typeof a.operationId?await t.journal.get(a.operationId):null,i=!!n&&"session.prompt"===n.command&&["started","effect_committed","projection_committed","failed"].includes(n.phase)&&n.paramsHash===a.paramsHash&&n.messageBinding?.requestMessageId===a.requestMessageId&&n.messageBinding?.requestHash===a.requestHash,r=verifyNativePromptAuthority({request:e.delivery.message.payload,decoded:t.verifier.verifyAndDecodeGrant(e.delivery.executionGrant),lease:e.lease,lineage:t.getLineage(),allowExpiredReadOnly:i}),{params:o}=r,s=r.request;try{const a=e.delivery.message;if("runtime_rpc_request"!==a.type||"completed"!==a.state||a.message_id!==s.requestMessageId||a.channel_id!==s.operationId||void 0!==a.in_reply_to)throw new RuntimeCommandError("INVALID_PARAMS","native envelope binding invalid");let n;void 0!==o.mode&&openClawSessionModePatch(o.mode);try{if(void 0!==o.mentions)throw new TypeError("native host per-prompt optional binding not implemented");n=prepareNativePromptInput(o.parts)}catch(e){throw new RuntimeCommandError(i?"OUTCOME_UNKNOWN":"INVALID_PARAMS","native host part unsupported",void 0,{cause:e})}const d=n.message;if(!t.host.activeRunAuthority)throw new RuntimeCommandError("EXECUTION_FAILED","native existing active-run authority unavailable");const m=()=>{const a=t.getLease(),n=t.getLineage();if(!a||a.instanceId!==e.lease.instanceId||a.handlerIdentity!==e.lease.handlerIdentity||a.runtimeEpoch!==o.runtimeLease.runtimeEpoch||a.leaseId!==o.runtimeLease.leaseId||n.journalStoreId!==o.runtimeLease.journalStoreId||n.journalGeneration!==o.runtimeLease.journalGeneration||Date.parse(a.leaseExpiresAt)<=Date.now())throw new RuntimeCommandError("INSTANCE_EPOCH_MISMATCH","native prompt attempt fenced")};m();const l=await t.channel.history({operationId:s.operationId,lease:e.lease,currentNativePrompt:r});if(m(),"found"!==l.status||l.snapshot.requestHash!==s.requestHash||l.snapshot.requestMessageId!==s.requestMessageId||!semanticEqual(l.snapshot.request.payload,s))throw new RuntimeCommandError("INVALID_PARAMS","native retained request mismatch");let u,p=await t.journal.get(s.operationId);if(m(),p&&("session.prompt"!==p.command||p.paramsHash!==s.paramsHash||p.runtimeEpoch!==o.runtimeLease.runtimeEpoch||p.messageBinding?.requestMessageId!==s.requestMessageId||p.messageBinding.requestHash!==s.requestHash))throw new RuntimeCommandError("OUTCOME_UNKNOWN","native existing journal binding mismatch");if(!t.http.getMessage)throw new RuntimeCommandError("OUTCOME_UNKNOWN","native task snapshot unavailable");try{u=await t.http.getMessage(t.getLease(),{conversationId:o.conversationId,messageId:o.taskRequestMessageId})}catch(e){throw new RuntimeCommandError("OUTCOME_UNKNOWN","native original task snapshot unavailable",void 0,{cause:e})}if(m(),u.messageId!==o.taskRequestMessageId||u.conversationId!==o.conversationId||"chat_message"!==u.type||u.body!==d)throw new RuntimeCommandError("OUTCOME_UNKNOWN","native original task snapshot mismatch");const c=async()=>{if(!t.http.getMessage)throw new RuntimeCommandError("OUTCOME_UNKNOWN","native single snapshot unavailable");m();const e=await t.http.getMessage(t.getLease(),{conversationId:o.conversationId,messageId:o.replyMessageId});return m(),this.assertReply(e,r),e};if(l.snapshot.started||p&&"prepared"!==p.phase){const e=l.snapshot.runBoundary?.finishedReceipt,a=l.snapshot.final;if(!(p&&["started","effect_committed","projection_committed","failed"].includes(p.phase)&&e&&"finished"===e.state&&e.activeRunRegistrationId===o.taskId&&a))throw new RuntimeCommandError("OUTCOME_UNKNOWN","native durable MS finish audit or original result unavailable");const n=await c();if("agent_reply"!==n.type||n.state!==e.replyState||n.stopReason!==e.replyStopReason||n.updatedAt!==e.replyUpdatedAt)throw new RuntimeCommandError("OUTCOME_UNKNOWN","native original terminal does not match finished audit");const i=this.terminal(n,r);if(a.payload.outcome!==i.outcome||"final_message"!==l.snapshot.terminalSource)throw new RuntimeCommandError("OUTCOME_UNKNOWN","native original final outcome mismatch");const d=a.payload.response;if("2.0"!==d.jsonrpc||d.id!==s.request.id)throw new RuntimeCommandError("OUTCOME_UNKNOWN","native original final RPC binding mismatch");const u=runtimeCommandResultHash(i.result);if(p.resultReceipt&&p.resultReceipt.resultHash!==u||void 0!==p.result&&!semanticEqual(p.result,i.result))throw new RuntimeCommandError("OUTCOME_UNKNOWN","native original result conflicts with local receipt");if(void 0===d.result)throw new RuntimeCommandError("OUTCOME_UNKNOWN","native original canonical result evidence unavailable");{const e=l.snapshot.effectCommitted?.payload,t=runtimeMessageHash(RUNTIME_HASH_DOMAINS.coreResult,{method:s.method,result:d.result});if("succeeded"!==i.outcome||void 0!==d.error||!semanticEqual(d.result,i.result)||!e||e.effectRecord.coreResultHash!==t||e.effectRecord.coreResultRef!==`sha256:${t}`||a.payload.effectRecordHash!==e.effectRecordHash||a.payload.serviceResultHash!==runtimeMessageHash(RUNTIME_HASH_DOMAINS.serviceResult,{method:s.method,result:d.result}))throw new RuntimeCommandError("OUTCOME_UNKNOWN","native original final result hash evidence mismatch")}const h=d.result;return m(),"started"===p.phase&&(p=await t.journal.transition({operationId:s.operationId,expectedRevision:p.revision,phase:"succeeded"===i.outcome?"effect_committed":"failed",result:h}),m()),{request:s,outcome:i.outcome,result:h,replayedFinal:a}}if(n.files.length&&!t.inputFiles)throw new RuntimeCommandError("EXECUTION_FAILED","native authorized input file resolver unavailable");if(l.snapshot.terminal||e.signal.aborted||l.snapshot.cancelRequested||await t.journal.getCancellation(s.operationId))throw new RuntimeCommandError("OPERATION_CANCELLED","native prompt cancelled before effect");const h=await t.host.resolveNativePromptSessionKey(r,m);if(m(),p=await t.journal.acceptMessageRequest({operationId:s.operationId,command:"session.prompt",paramsHash:s.paramsHash,runtimeEpoch:o.runtimeLease.runtimeEpoch,requestMessageId:s.requestMessageId,requestHash:s.requestHash}),m(),"prepared"!==p.phase)throw new RuntimeCommandError("OUTCOME_UNKNOWN","native task already started");let v;const g=await bindExistingRuntimeChatReply({authority:{currentLease:t.getLease},http:t.http,binding:{conversationId:o.conversationId,messageId:o.replyMessageId,taskRequestMessageId:o.taskRequestMessageId,assertAttempt:m,assertPreparedClear:e=>{if(m(),!v||e.activeRunRegistrationId!==v||e.historyGeneration!==r.historyGeneration||e.runtimeEpoch!==o.runtimeLease.runtimeEpoch)throw new RuntimeCommandError("OUTCOME_UNKNOWN","native prepared clear does not freeze this registered attempt")},mutationKey:e=>`native-reply:${s.operationId}:${o.runtimeLease.journalStoreId}:${o.runtimeLease.journalGeneration}:${e}`}});if(m(),e.signal.aborted||await t.journal.getCancellation(s.operationId))throw new RuntimeCommandError("OPERATION_CANCELLED","native prompt cancelled before start");await t.channel.append({operationId:s.operationId,lease:t.getLease(),currentNativePrompt:r,executionGrant:e.delivery.executionGrant,type:"runtime_operation_started",payload:{...runtimeMessageBase(s),executionGrantHash:s.executionGrantHash,handlerIdentity:e.lease.handlerIdentity,executionMode:"start",claimedAt:p.updatedAt}}),m(),p=await t.journal.transition({operationId:s.operationId,expectedRevision:p.revision,phase:"started"}),m();const I=()=>{m();const e=t.getLease();return{leaseId:e.leaseId,handlerIdentity:e.handlerIdentity,runtimeEpoch:e.runtimeEpoch,runtimeLeaseCredential:e.runtimeLeaseCredential}},f={operationId:s.operationId,requestMessageId:s.requestMessageId,conversationId:r.command.context.conversation.runtimeConversationUid,historyGeneration:r.historyGeneration,runtimeRunId:o.runtimeRunId},R=await t.runBoundary.start({...f,lease:I()});if(m(),"registered"!==R.status||R.historyGeneration!==r.historyGeneration||R.activeRunRegistrationId!==o.taskId)throw new RuntimeCommandError("OUTCOME_UNKNOWN","native MS run start did not register the original task");v=R.activeRunRegistrationId;const N=new AbortController,w=()=>N.abort(e.signal.reason);if(e.signal.addEventListener("abort",w,{once:!0}),e.signal.aborted&&w(),e.setCancel(e=>N.abort(e)),await t.journal.getCancellation(s.operationId)&&N.abort(nativePromptCancellationReason),m(),N.signal.aborted){if(e.signal.removeEventListener("abort",w),N.signal.reason===nativePromptCancellationReason)throw new RuntimeCommandError("OPERATION_CANCELLED","native prompt cancelled before host invocation");throw new RuntimeCommandError("OUTCOME_UNKNOWN","native registered run stopped before host invocation")}const y=[];let E;const C=JSON.parse((await g.opened()).rawText),O=C.parts?.length??0,M=async e=>{const t=await e;return m(),this.assertReply(t,r),E=t,t},_={id:g.id,get isTerminated(){return g.isTerminated},get parts(){return y},opened:async()=>({messageId:g.id,outcome:"bound"}),appendBody:e=>g.appendBody(e),setBody:e=>g.setBody(e),addPart:e=>{y.push(e),g.addPart(e)},replacePart:(e,t)=>{y[e]=t,g.replacePart(O+e,t)},appendToolResult:(e,t,a)=>{const n={type:"tool_result",tool_use_id:e,content:t,...a?{is_error:!0}:{}};y.push(n),g.addPart(n)},finalize:e=>M(g.finalize(e)),fail:e=>M(g.fail(e)),cancel:e=>M(g.cancel(e))},b={message:d,mediaPaths:[],mediaTypes:[]};let U,A;const q=t.host.htmlDeliveryRequired?new HtmlDeliveryGuard:void 0,T=createNativeOutputFileAttempt({htmlDelivery:q,operationId:s.operationId,requestMessageId:s.requestMessageId,paramsHash:s.paramsHash,activeRunRegistrationId:v,getLease:()=>(m(),t.getLease()),assertAttempt:m,signal:N.signal}),P=setTimeout(()=>N.abort(nativePromptExecutionDeadlineReason),Math.max(0,Math.min(Date.parse(s.executionDeadlineAt)-3e4-Date.now(),2147483647)));P.unref?.();try{await dispatchImInboundPrompt({...t.host,htmlDelivery:q,runtimeRunId:o.runtimeRunId,onNativeAgentRunStart:e=>T.bindHostRun(e),nativeActiveRunRegistration:{runtimeConversationUid:f.conversationId,runtimeSessionKey:r.command.context.conversation.runtimeSessionKey,historyGeneration:R.historyGeneration,targetMessageId:o.taskRequestMessageId,runtimeRunId:o.runtimeRunId,activeRunRegistrationId:R.activeRunRegistrationId},assertNativeTarget:async()=>{if(await t.host.resolveNativePromptSessionKey(r,m)!==h)throw new RuntimeCommandError("OUTCOME_UNKNOWN","native prompt target changed before host invocation")},prepareNativeModel:async e=>{m(),await t.host.prepareNativePromptModel(r,e,m),m();for(const e of n.files){if(!t.inputFiles)throw new RuntimeCommandError("OUTCOME_UNKNOWN","native authorized input file resolver unavailable");const a=await t.inputFiles.resolve({operationId:s.operationId,requestMessageId:s.requestMessageId,paramsHash:s.paramsHash,partIndex:e.partIndex,lease:t.getLease(),signal:N.signal});m();const n=await downloadNativeInputFile({fileId:e.fileId,...a,signal:N.signal,assertAttempt:m});b.mediaPaths.push(n.path),b.mediaTypes.push(n.mimeType)}m()},onNativeRunCompletion:e=>{A=e},retainedStream:_,abortSignal:N.signal,inReplyTo:o.taskRequestMessageId,prompt:{channelId:o.conversationId,inboundMessageId:o.taskRequestMessageId,requestId:s.requestMessageId,selfPrincipalId:e.lease.instanceId,sessionKey:h,dispatchInput:b},client:{messages:{startStream(){throw new Error("native reply POST forbidden")},getEnvelope:async()=>({state:(await c()).state})},getSnapshot:()=>({connection:"connected",messages:{},conversations:{}})}})}catch(e){U=e}finally{clearTimeout(P),T.close(),e.signal.removeEventListener("abort",w)}if(!A&&U)throw U;if(!A||A.runtimeRunId!==o.runtimeRunId||A.token.sessionKey!==h)throw new RuntimeCommandError("OUTCOME_UNKNOWN","native physical host completion unavailable");let S;try{await A.completion}catch(e){S=e}if(m(),!E)throw new RuntimeCommandError("OUTCOME_UNKNOWN",void 0!==S?"native host/abort/drain did not converge":"native kernel settled without verified original reply terminal",void 0,{cause:S??U});const H=this.terminal(E,r),L=await c();g.assertTerminalSnapshot(L);const x="completed"===L.state?"completed":"cancelled"===L.state?"cancelled":"failed",K=await t.runBoundary.finish({...f,activeRunRegistrationId:v,terminalState:x,source:"runtime",lease:I()});if(m(),"finished"!==K.status&&"idempotent"!==K.status||K.activeRunRegistrationId!==v)throw new RuntimeCommandError("OUTCOME_UNKNOWN","native MS run finish was not acknowledged");return await t.journal.transition({operationId:s.operationId,expectedRevision:p.revision,phase:"succeeded"===H.outcome?"effect_committed":"failed",result:H.result}),m(),{request:s,...H}}catch(e){throw new NativePromptExecutionFailure(s,e)}}assertReply(e,t){if(e.messageId!==t.params.replyMessageId||e.conversationId!==t.params.conversationId||e.replyTo!==t.params.taskRequestMessageId)throw new RuntimeCommandError("OUTCOME_UNKNOWN","native durable reply binding mismatch")}terminal(e,t){if(!e.stopReason||!["completed","failed","refused","cancelled"].includes(e.state))throw new RuntimeCommandError("OUTCOME_UNKNOWN","native durable terminal absent");const a="cancelled"===e.state&&"user_stop"===e.stopReason?"cancelled":"completed"===e.state&&"end_turn"===e.stopReason?"succeeded":"failed";return{outcome:a,result:{runtimeRunId:t.params.runtimeRunId,stopReason:e.stopReason,requestAborted:"cancelled"===e.state&&"user_stop"===e.stopReason,..."failed"===a?{error:e.body}:{}}}}}export const NATIVE_PROMPT_UNKNOWN_REASONS=new Set(["native MS run boundary required","native existing journal binding mismatch","native task snapshot unavailable","native original task snapshot unavailable","native original task snapshot mismatch","native single snapshot unavailable","native durable MS finish audit or original result unavailable","native original terminal does not match finished audit","native original final outcome mismatch","native original final RPC binding mismatch","native original result conflicts with local receipt","native original final result hash evidence mismatch","native original canonical result evidence unavailable","native task already started","native prepared clear does not freeze this registered attempt","native MS run start did not register the original task","native registered run stopped before host invocation","native prompt target changed before host invocation","native authorized input file resolver unavailable","native physical host completion unavailable","native host/abort/drain did not converge","native kernel settled without verified original reply terminal","native MS run finish was not acknowledged","native durable reply binding mismatch","native durable terminal absent"]);export function nativePromptUnknownReason(e){return e instanceof RuntimeCommandError&&"OUTCOME_UNKNOWN"===e.code?NATIVE_PROMPT_UNKNOWN_REASONS.has(e.message)?e.message:"unlisted":"not_unknown"}export class NativePromptExecutionFailure extends Error{request;failure;constructor(e,t){super(t instanceof Error?t.message:String(t),{cause:t}),this.request=e,this.failure=t}}
@@ -1 +1 @@
1
- const runs=new Map,tools=new Map,consumed=new Set;export function createNativeOutputFileAttempt(t){let e,o=!0;const s={...t,assertAttempt(){if(!o)throw new Error("native output file attempt finished");t.signal.throwIfAborted(),t.assertAttempt();const e=t.getLease();if(!e.runtimeLeaseCredential||!Number.isFinite(Date.parse(e.leaseExpiresAt))||Date.parse(e.leaseExpiresAt)<=Date.now())throw new Error("native output file lease fenced")},getLease:()=>(s.assertAttempt(),t.getLease())};return{bindHostRun(t){if(s.assertAttempt(),!t||void 0!==e||runs.has(t))throw new Error("native output file Host run binding ambiguous");e=t,runs.set(t,s)},close(){o=!1,e&&runs.get(e)===s&&runs.delete(e);for(const[t,e]of tools)e.assertAttempt===s.assertAttempt&&tools.delete(t);for(const e of consumed)e.startsWith(t.operationId+"\0")&&consumed.delete(e)}}}export function prepareNativeOutputTool(t,e){if(!t||!e)throw new Error("native output file trusted Host tool binding missing");const o=runs.get(t);if(!o)throw new Error("native output file trusted run missing");o.assertAttempt();const s=o.operationId+"\0"+e;if(tools.has(e)||consumed.has(s))throw new Error("native output file tool binding ambiguous or consumed");tools.set(e,{...o,toolCallId:e})}export function consumeNativeOutputTool(t){const e=tools.get(t);if(tools.delete(t),!e)throw new Error("native output file tool capability missing");return e.assertAttempt(),consumed.add(e.operationId+"\0"+t),e}
1
+ const runs=new Map,tools=new Map,consumed=new Set;export function createNativeOutputFileAttempt(t){let e,o=!0;const s={...t,assertAttempt(){if(!o)throw new Error("native output file attempt finished");t.signal.throwIfAborted(),t.assertAttempt();const e=t.getLease();if(!e.runtimeLeaseCredential||!Number.isFinite(Date.parse(e.leaseExpiresAt))||Date.parse(e.leaseExpiresAt)<=Date.now())throw new Error("native output file lease fenced")},getLease:()=>(s.assertAttempt(),t.getLease())};return{bindHostRun(t){if(s.assertAttempt(),!t||void 0!==e||runs.has(t))throw new Error("native output file Host run binding ambiguous");e=t,runs.set(t,s)},close(){o=!1,e&&runs.get(e)===s&&runs.delete(e);for(const[t,e]of tools)e.assertAttempt===s.assertAttempt&&tools.delete(t);for(const e of consumed)e.startsWith(t.operationId+"\0")&&consumed.delete(e)}}}export function prepareNativeOutputTool(t,e){if(!t||!e)throw new Error("native output file trusted Host tool binding missing");const o=runs.get(t);if(!o)throw new Error("native output file trusted run missing");o.assertAttempt();const s=o.operationId+"\0"+e;if(tools.has(e)||consumed.has(s))throw new Error("native output file tool binding ambiguous or consumed");tools.set(e,{...o,toolCallId:e})}export function consumeNativeOutputTool(t){const e=tools.get(t);if(tools.delete(t),!e)throw new Error("native output file tool capability missing");return e.assertAttempt(),consumed.add(e.operationId+"\0"+t),e}export function nativeHtmlDeliveryForRun(t){const e=t?runs.get(t):void 0;if(e)return e.assertAttempt(),e.htmlDelivery}
@@ -1 +1 @@
1
- import{prepareNativeOutputTool}from"../runtime/native-output-file-attempt.js";import{RENDER_UI_TOOL_NAME}from"../canvas/tools.js";import{BEEOS_UPLOAD_TOOL_NAME}from"./tool.js";export function needsNativeOutputCapability(t,o){return t===BEEOS_UPLOAD_TOOL_NAME||t===RENDER_UI_TOOL_NAME&&Object.prototype.hasOwnProperty.call(o,"html")}export function nativeOutputToolCallGate(t,o){if(needsNativeOutputCapability(t.toolName,t.params)){if(t.runId&&o.runId&&t.runId!==o.runId||t.toolCallId&&o.toolCallId&&t.toolCallId!==o.toolCallId)return{block:!0,blockReason:"native output file Host correlation conflict"};try{prepareNativeOutputTool(t.runId??o.runId,t.toolCallId??o.toolCallId)}catch{return{block:!0,blockReason:"native output file trusted capability unavailable"}}}}
1
+ import{prepareNativeOutputTool,nativeHtmlDeliveryForRun}from"../runtime/native-output-file-attempt.js";import{RENDER_UI_TOOL_NAME}from"../canvas/tools.js";import{BEEOS_UPLOAD_TOOL_NAME}from"./tool.js";export function needsNativeOutputCapability(t,o){return t===BEEOS_UPLOAD_TOOL_NAME||t===RENDER_UI_TOOL_NAME&&Object.prototype.hasOwnProperty.call(o,"html")}export function nativeOutputToolCallGate(t,o){if(needsNativeOutputCapability(t.toolName,t.params)){if(t.runId&&o.runId&&t.runId!==o.runId||t.toolCallId&&o.toolCallId&&t.toolCallId!==o.toolCallId)return{block:!0,blockReason:"native output file Host correlation conflict"};try{const e=t.runId??o.runId;if(t.toolName===RENDER_UI_TOOL_NAME&&nativeHtmlDeliveryForRun(e)?.publishedFileId)return{block:!0,blockReason:"The HTML file for this reply is already published. Use that result; do not generate or upload another copy."};prepareNativeOutputTool(e,t.toolCallId??o.toolCallId)}catch{return{block:!0,blockReason:"native output file trusted capability unavailable"}}}}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "beeos-claw",
3
- "version": "0.6.80",
3
+ "version": "0.6.81",
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>",
@@ -90,5 +90,5 @@
90
90
  "brace-expansion": ">=5.0.9",
91
91
  "@beeos-ai/beeos-types": "$@beeos-ai/beeos-types"
92
92
  },
93
- "gitHead": "87d681df5019200469c840e01ed7ecbce29e6b81"
93
+ "gitHead": "6d051ff9b359f9e41597a1f664fad877f319a447"
94
94
  }