beeos-claw 0.6.62 → 0.6.63-cloud.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/agent-auth.js +1 -1
- package/dist/automation/tools.js +1 -0
- package/dist/automation/turn-policy.js +1 -0
- package/dist/automation/wake.js +1 -0
- package/dist/im-inbound-handler.js +1 -1
- package/dist/im-runtime-dispatch.js +1 -1
- package/dist/index.js +1 -1
- package/dist/message-projection-inbound-consumer.js +1 -1
- package/dist/runtime/core-execution-attempt-context.js +1 -1
- package/dist/runtime/message-service-conversation-clear-http-authority.js +1 -1
- package/dist/runtime/message-service-conversation-history-boundary.js +1 -1
- package/dist/runtime/ms-runtime-message-channel-adapter.js +1 -1
- package/dist/runtime/ms-runtime-message-channel-consumer-factory.js +1 -1
- package/dist/runtime/ms-runtime-rpc-adapter.js +1 -1
- package/dist/runtime/runtime-message-channel-contract.js +1 -1
- package/dist/runtime/runtime-production-authority.js +1 -1
- package/dist/runtime/runtime-service-contract-compatibility.js +1 -1
- package/dist/runtime/runtime-service-production-candidate.js +1 -1
- package/dist/runtime/service-runtime-attempt-ports-resolver.js +1 -1
- package/dist/runtime/split-runtime-authorization-verifier.js +1 -1
- package/openclaw.plugin.json +15 -1
- package/package.json +5 -4
- package/runtime-service-wire-identity.v1.json +1 -1
- package/dist/runtime/runtime-registration.js +0 -1
- package/dist/runtime/runtime-transport-failure-policy.js +0 -1
package/dist/agent-auth.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import*as crypto from"node:crypto";import*as fs from"node:fs";import*as path from"node:path";export const EMPTY_BODY_SHA256=
|
|
1
|
+
import*as crypto from"node:crypto";import*as fs from"node:fs";import*as path from"node:path";import{agentRequestAuthHeaders,authenticatedAgentFetch,bodyHashHex as sdkBodyHashHex,EMPTY_BODY_SHA256 as SDK_EMPTY_BODY_SHA256}from"@beeos-ai/cloud-agent-sdk";export const EMPTY_BODY_SHA256=SDK_EMPTY_BODY_SHA256;const ED25519_PKCS8_PREFIX=Buffer.from("302e020100300506032b657004220420","hex");function wrapPrivateKey(e){return 32===e.length?Buffer.concat([ED25519_PKCS8_PREFIX,Buffer.from(e)]):Buffer.from(e)}const ED25519_SPKI_PREFIX=Buffer.from("302a300506032b6570032100","hex");function derivePublicKey(e){const r=crypto.createPrivateKey({key:wrapPrivateKey(e),format:"der",type:"pkcs8"}),t=crypto.createPublicKey(r).export({type:"spki",format:"der"});return 44===t.length?new Uint8Array(t.subarray(12)):new Uint8Array(t)}export function loadAgentKeyPair(e){if(!fs.existsSync(e))return generateAgentKeyPair(e);const r=fs.readFileSync(e,"utf-8").trim();try{const e=JSON.parse(r);if(e.publicKey&&e.privateKey)return{publicKey:new Uint8Array(Buffer.from(e.publicKey,"base64")),privateKey:new Uint8Array(Buffer.from(e.privateKey,"base64"))}}catch{}const t=new Uint8Array(Buffer.from(r,"base64"));if(32===t.length)return{publicKey:derivePublicKey(t),privateKey:t};throw new Error(`Invalid key file format at ${e}`)}function generateAgentKeyPair(e){const r=crypto.generateKeyPairSync("ed25519",{publicKeyEncoding:{type:"spki",format:"der"},privateKeyEncoding:{type:"pkcs8",format:"der"}}),t=r.publicKey,n=r.privateKey,a=new Uint8Array(44===t.length?t.subarray(12):t),i=new Uint8Array(48===n.length?n.subarray(16):n),o=path.dirname(e);return fs.existsSync(o)||fs.mkdirSync(o,{recursive:!0}),fs.writeFileSync(e,JSON.stringify({publicKey:Buffer.from(a).toString("base64"),privateKey:Buffer.from(i).toString("base64")},null,2),{mode:384}),{publicKey:a,privateKey:i}}export function signAgentMessage(e,r){const t=crypto.createPrivateKey({key:wrapPrivateKey(r),format:"der",type:"pkcs8"});return new Uint8Array(crypto.sign(null,Buffer.from(e),t))}export function bodyHashHex(e){return sdkBodyHashHex(e)}export function agentAuthHeaders(e,r,t,n){return agentRequestAuthHeaders(e,r,{publicKey:t.publicKey,sign:e=>signAgentMessage(Buffer.from(e).toString("utf8"),t.privateKey)},n)}export function agentBoundAuthHeaders(e,r,t,n){return agentAuthHeaders(e,r,n,t)}export async function agentFetch(e,r,t){return await authenticatedAgentFetch(e,{publicKey:r.publicKey,sign:e=>signAgentMessage(Buffer.from(e).toString("utf8"),r.privateKey)},t)}export async function agentBoundFetch(e,r,t){if(""!==("string"==typeof e?new URL(e):e).search)throw new Error("body-bound agent authority routes forbid query parameters");return agentFetch(e,r,t)}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{randomUUID}from"node:crypto";import{agentFetch}from"../agent-auth.js";const AUTOMATIONS_PATH="/api/v1/automations",WEBHOOK_AUTOMATIONS_PATH="/api/v1/automations/webhooks",CONNECTOR_MCP_PATH="/api/v1/agent/connectors/mcp",UUID_RE=/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;function asObject(e){return e&&"object"==typeof e&&!Array.isArray(e)?e:{}}function stringValue(e){return"string"==typeof e&&e.trim()?e.trim():void 0}function toolResult(e){return{content:JSON.stringify(e,null,2)}}function isUuid(e){return Boolean(e&&UUID_RE.test(e))}function resolveTarget(e){const t=e.getDefaultAgent?.(),r=stringValue(t?.instanceId),o=stringValue(t?.id);if(r&&o)return{instanceId:r,agentId:o}}function resolveConversationId(e,t){const r=stringValue(e.getCurrentConversationId?.(t));return isUuid(r)?r:void 0}async function request(e,t,r={}){const o=`${e.agentGatewayUrl.replace(/\/+$/,"")}${t}`;try{const t=e.agentKeys?await agentFetch(o,e.agentKeys,r):await fetch(o,r),n=await t.text();let a=n;try{a=n?JSON.parse(n):{}}catch{}if(!t.ok)return{ok:!1,status:t.status,error:`Automation request failed: HTTP ${t.status}`,details:a};const s=asObject(a);return{ok:!0,data:Object.hasOwn(s,"data")?s.data:a}}catch(e){return{ok:!1,status:0,error:e instanceof Error?e.message:String(e),details:void 0}}}function jsonRequestBody(e){return{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(e)}}function buildSchedule(e){const t=stringValue(e.kind)??"every",r={kind:t,tz:stringValue(e.tz)??"UTC"};switch(t){case"every":{r.intervalSeconds="number"==typeof e.intervalSeconds?String(Math.trunc(e.intervalSeconds)):stringValue(e.intervalSeconds)??"3600";const t=stringValue(e.anchorAt);t&&(r.anchorAt=t);break}case"at":{const t=stringValue(e.at);t&&(r.at=t);break}case"cron":{const t=stringValue(e.expression);t&&(r.expression=t);break}default:return{error:"schedule.kind must be one of at, every, or cron"}}return r}function buildSession(e,t,r){const o=stringValue(asObject(e.session).mode)??"new";if("new"!==o&&"bound"!==o)return{error:"session.mode must be new or bound"};const n={mode:o},a=resolveConversationId(t,r);if("bound"===o){if(!a)return{error:"bound Automation requires the current conversation context"};n.boundConversationId=a}return n}function createInput(e,t,r){const o=asObject(e),n=stringValue(o.name),a=stringValue(o.prompt),s=resolveTarget(t);if(!n||!a||!s)return{error:"name, prompt, and a resolvable target agent are required"};const i=buildSchedule(asObject(o.schedule));if("error"in i)return i;const u=buildSession(o,t,r);if("error"in u)return u;const c={name:n,prompt:a,target:s,schedule:i,session:u},l=stringValue(o.validFrom),d=stringValue(o.validUntil);l&&(c.validFrom=l),d&&(c.validUntil=d);const p=resolveConversationId(t,r);return p&&(c.createdInConversationId=p),c}function createWebhookInput(e,t,r){const o=asObject(e),n=stringValue(o.name),a=stringValue(o.prompt),s=(stringValue(o.source)??"").toLowerCase(),i=resolveTarget(t);if(!n||!a||!i)return{error:"name, prompt, and a resolvable target agent are required"};if("github"!==s&&"gmail"!==s)return{error:"source must be github or gmail"};const u=o.events,c=Array.isArray(u)?u.filter(e=>"string"==typeof e&&Boolean(e.trim())).map(e=>e.trim()):[],l=stringValue(o.repoOwner),d=stringValue(o.repoName),p=stringValue(o.mailbox);if(!("github"!==s||0!==c.length&&l&&d))return{error:"github webhook requires events, repoOwner, and repoName"};if("gmail"===s&&!p)return{error:"gmail webhook requires mailbox"};const m=buildSession(o,t,r);if("error"in m)return m;const g={name:n,prompt:a,target:i,session:m,source:s};"github"===s?(g.events=c,g.repoOwner=l,g.repoName=d):g.mailbox=p;const y=stringValue(o.eventFilter);y&&(g.eventFilter=y);const f=stringValue(o.validFrom),b=stringValue(o.validUntil);f&&(g.validFrom=f),b&&(g.validUntil=b);const h=resolveConversationId(t,r);return h&&(g.createdInConversationId=h),g}function unwrapConnectorMcp(e){let t=e;if("string"==typeof t)try{t=JSON.parse(t)}catch{return}if(!t||"object"!=typeof t||Array.isArray(t))return;const r=t;if(null!=r.error)return;let o=r.result??r;if(!o||"object"!=typeof o||Array.isArray(o))return;const n=o;if(n.isError)return;const a=n.structuredContent;if(a&&"object"==typeof a&&!Array.isArray(a))return a;const s=n.content;if(Array.isArray(s))for(const e of s){if(!e||"object"!=typeof e||Array.isArray(e))continue;const t=e;if("text"===t.type&&"string"==typeof t.text)try{const e=JSON.parse(t.text);if(e&&"object"==typeof e&&!Array.isArray(e))return e}catch{continue}}return n}async function runPreflight(e,t,r){const o=asObject(t),n=(stringValue(o.source)??"").toLowerCase(),a=resolveConversationId(e,r),s=resolveTarget(e),i={source:n,operation:"webhook",ready:!1,platform:{reachable:!1,authorized:!1},connector:{id:n,installed:!1,credentialStatus:"unknown"},operationSupported:"github"===n||"gmail"===n,target:{resolved:Boolean(s)},session:{canBindCurrentConversation:Boolean(a)},nextAction:"retry_or_check_connection"};if("github"!==n&&"gmail"!==n)return i.blockingReason="unsupported_source",i.nextAction="retry_or_check_connection",i;if("gmail"===n&&!stringValue(o.mailbox))return i.blockingReason="mailbox_required",i.nextAction="provide_mailbox",i;if("github"===n){const e=o.events,t=Array.isArray(e)&&e.some(e=>"string"==typeof e&&e.trim());if(!stringValue(o.repoOwner)||!stringValue(o.repoName)||!t)return i.blockingReason="github_webhook_input_required",i.nextAction="provide_repository_and_events",i}const u=await request(e,`${AUTOMATIONS_PATH}?limit=1`,{method:"GET"});if(!u.ok)return i.blockingReason="automation_gateway_unavailable",i.platform={reachable:!1,authorized:!1,status:u.status,error:u.error},i.nextAction="retry_or_check_connection",i;i.platform={reachable:!0,authorized:!0};const c={jsonrpc:"2.0",id:randomUUID(),method:"tools/call",params:{name:"list_connectors",arguments:{}}},l=await request(e,CONNECTOR_MCP_PATH,{...jsonRequestBody(c),headers:{"Content-Type":"application/json",Accept:"application/json"}});if(!l.ok)return i.blockingReason="mcp_unavailable",i.connector={id:n,installed:!1,credentialStatus:"unknown",error:l.error,status:l.status},i.nextAction="retry_or_check_connection",i;const d=unwrapConnectorMcp(l.data),p=d&&Array.isArray(d.connectors)?d.connectors:void 0;let m;if(Array.isArray(p))for(const e of p){if(!e||"object"!=typeof e||Array.isArray(e))continue;const t=e;if((stringValue(t.connectorId)??stringValue(t.connector_id)??"").toLowerCase()===n){m=t;break}}if(!m)return i.blockingReason="mcp_not_connected",i.nextAction="install_connector",i;const g=(stringValue(m.credentialStatus)??stringValue(m.credential_status)??"unknown").toLowerCase();return i.connector={id:n,installed:!0,credentialStatus:g},"active"!==g?(i.blockingReason="credential_missing",i.nextAction="authorize_connector",i):s?(i.ready=!0,i.nextAction="create_automation",i.sideEffects="gmail"===n?["create_rule","ensure_shared_gmail_watch"]:["create_rule","issue_hook","register_connector_webhook"],i):(i.blockingReason="target_not_resolved",i.nextAction="sync_openclaw_agent",i)}async function runCapabilities(e,t){const r=resolveTarget(e),o=resolveConversationId(e,t),n={platform:{reachable:!1,authorized:!1},capabilities:{createSchedule:Boolean(r),createWebhook:Boolean(r),run:!0},target:{resolved:Boolean(r)},session:{currentConversationAvailable:Boolean(o),canBindCurrentConversation:Boolean(o)}},a=await request(e,`${AUTOMATIONS_PATH}?limit=1`,{method:"GET"});return a.ok?n.platform={reachable:!0,authorized:!0}:n.platform={reachable:!1,authorized:!1,status:a.status,error:a.error},n}function requestErrorResult(e){return toolResult({error:e.error,status:e.status,details:e.details})}export function registerAutomationTools(e,t){e.registerTool({name:"beeos_automation_create",description:"Create a BeeOS platform Automation owned by the current user. The target is the current Agent and is resolved automatically. Prefer session.mode=bound for a task created in this chat.",parameters:{type:"object",properties:{name:{type:"string",description:"Human-readable automation name"},prompt:{type:"string",description:"Prompt sent to the target agent when the automation fires"},schedule:{type:"object",properties:{kind:{type:"string",enum:["at","every","cron"]},tz:{type:"string"},at:{type:"string"},intervalSeconds:{type:"integer",minimum:1},anchorAt:{type:"string"},expression:{type:"string"}}},session:{type:"object",description:"Always set session.mode. Prefer bound for tasks created in the current BeeOS chat so every run returns here. Use new if the user requests a separate conversation per run or the current chat cannot bind. The plugin fills the bound conversation ID; do not supply it.",properties:{mode:{type:"string",enum:["new","bound"]}}},validFrom:{type:"string"},validUntil:{type:"string"}},required:["name","prompt"]},async execute(e,r,o){const n=createInput(r,t,e);if("error"in n)return toolResult(n);try{const e=await request(t,AUTOMATIONS_PATH,{...jsonRequestBody(n),signal:o});return e.ok?toolResult(e.data):requestErrorResult(e)}catch(e){return t.logger?.warn?.(`[beeos_automation_create] ${e instanceof Error?e.message:String(e)}`),toolResult({error:e instanceof Error?e.message:String(e)})}}}),e.registerTool({name:"beeos_automation_create_webhook",description:"Create a BeeOS platform Webhook Automation. GitHub uses source/events/repoOwner/repoName; Gmail uses source/mailbox only (never hookId/hookUrl/repo*). Runs preflight first. Prefer session.mode=bound for a task created in this chat.",parameters:{type:"object",properties:{name:{type:"string"},prompt:{type:"string"},source:{type:"string",enum:["github","gmail"]},events:{type:"array",items:{type:"string"},minItems:1},repoOwner:{type:"string"},repoName:{type:"string"},eventFilter:{type:"string"},mailbox:{type:"string"},session:{type:"object",description:"Always set session.mode. Prefer bound for tasks created in the current BeeOS chat so every event returns here. Use new if the user requests a separate conversation per event or the current chat cannot bind. The plugin fills the bound conversation ID; do not supply it.",properties:{mode:{type:"string",enum:["new","bound"]}}},validFrom:{type:"string"},validUntil:{type:"string"}},required:["name","prompt","source"]},async execute(e,r,o){const n=createWebhookInput(r,t,e);if("error"in n)return toolResult(n);try{const a=await runPreflight(t,r,e);if(!a.ready)return toolResult({error:"automation_preflight_failed",preflight:a});const s=await request(t,WEBHOOK_AUTOMATIONS_PATH,{...jsonRequestBody(n),signal:o});return s.ok?toolResult(s.data):requestErrorResult(s)}catch(e){return t.logger?.warn?.(`[beeos_automation_create_webhook] ${e instanceof Error?e.message:String(e)}`),toolResult({error:e instanceof Error?e.message:String(e)})}}}),e.registerTool({name:"beeos_automation_capabilities",description:"Check whether the BeeOS Automation platform is reachable/authorized and whether the current chat can bind as an Automation session. Read-only.",parameters:{type:"object",properties:{}},async execute(e){try{return toolResult(await runCapabilities(t,e))}catch(e){return toolResult({error:e instanceof Error?e.message:String(e)})}}}),e.registerTool({name:"beeos_automation_preflight",description:"Read-only readiness check for creating a BeeOS Webhook Automation. Returns nextAction such as authorize_connector, install_connector, provide_mailbox, provide_repository_and_events, sync_openclaw_agent, retry_or_check_connection, or create_automation.",parameters:{type:"object",properties:{source:{type:"string",enum:["github","gmail"]},events:{type:"array",items:{type:"string"}},repoOwner:{type:"string"},repoName:{type:"string"},mailbox:{type:"string"},session:{type:"object",properties:{mode:{type:"string",enum:["new","bound"]}}}},required:["source"]},async execute(e,r){try{return toolResult(await runPreflight(t,r,e))}catch(e){return toolResult({error:e instanceof Error?e.message:String(e)})}}}),e.registerTool({name:"beeos_automation_list",description:"List BeeOS platform Automations owned by the current user.",parameters:{type:"object",properties:{status:{type:"string",enum:["enabled","paused","expired"]},limit:{type:"integer",minimum:1,maximum:100},cursor:{type:"string"}}},async execute(e,r,o){const n=asObject(r),a=new URLSearchParams;for(const e of["status","cursor"]){const t=stringValue(n[e]);t&&a.set(e,t)}"number"==typeof n.limit&&Number.isFinite(n.limit)&&a.set("limit",String(Math.trunc(n.limit)));try{const e=await request(t,`${AUTOMATIONS_PATH}?${a}`,{signal:o});return e.ok?toolResult(e.data):requestErrorResult(e)}catch(e){return toolResult({error:e instanceof Error?e.message:String(e)})}}}),e.registerTool({name:"beeos_automation_get",description:"Get one BeeOS platform Automation by its automation ID.",parameters:{type:"object",properties:{automationId:{type:"string"}},required:["automationId"]},async execute(e,r,o){const n=stringValue(asObject(r).automationId);if(!n)return toolResult({error:"automationId is required"});try{const e=await request(t,`${AUTOMATIONS_PATH}/${encodeURIComponent(n)}`,{signal:o});return e.ok?toolResult(e.data):requestErrorResult(e)}catch(e){return toolResult({error:e instanceof Error?e.message:String(e)})}}}),e.registerTool({name:"beeos_automation_run",description:"Trigger an existing BeeOS platform Automation immediately and return its Run record.",parameters:{type:"object",properties:{automationId:{type:"string"},requestKey:{type:"string",description:"Optional idempotency key; generated when omitted"},sourceRunId:{type:"string"},rerunSessionMode:{type:"string",enum:["original","new"]}},required:["automationId"]},async execute(e,r,o){const n=asObject(r),a=stringValue(n.automationId);if(!a)return toolResult({error:"automationId is required"});const s=stringValue(n.requestKey)??randomUUID(),i=stringValue(n.sourceRunId),u=stringValue(n.rerunSessionMode),c={};i&&(c.sourceRunId=i),u&&(c.rerunSessionMode=u);try{const e=await request(t,`${AUTOMATIONS_PATH}/${encodeURIComponent(a)}/runs`,{...jsonRequestBody(c),headers:{"Content-Type":"application/json","Idempotency-Key":s},signal:o});return e.ok?toolResult({requestKey:s,data:e.data}):toolResult({error:e.error,status:e.status,details:e.details,requestKey:s})}catch(e){return toolResult({error:e instanceof Error?e.message:String(e),requestKey:s})}}}),e.registerTool({name:"beeos_automation_runs",description:"List Runs for one BeeOS platform Automation.",parameters:{type:"object",properties:{automationId:{type:"string"},status:{type:"string",enum:["scheduled","dispatched","failed","skipped"]},limit:{type:"integer",minimum:1,maximum:100},cursor:{type:"string"}},required:["automationId"]},async execute(e,r,o){const n=asObject(r),a=stringValue(n.automationId);if(!a)return toolResult({error:"automationId is required"});const s=new URLSearchParams;for(const e of["status","cursor"]){const t=stringValue(n[e]);t&&s.set(e,t)}"number"==typeof n.limit&&Number.isFinite(n.limit)&&s.set("limit",String(Math.trunc(n.limit)));try{const e=await request(t,`${AUTOMATIONS_PATH}/${encodeURIComponent(a)}/runs?${s}`,{signal:o});return e.ok?toolResult(e.data):requestErrorResult(e)}catch(e){return toolResult({error:e instanceof Error?e.message:String(e)})}}}),e.registerTool({name:"beeos_automation_get_run",description:"Get one Run record for a BeeOS platform Automation.",parameters:{type:"object",properties:{automationId:{type:"string"},runId:{type:"string"}},required:["automationId","runId"]},async execute(e,r,o){const n=asObject(r),a=stringValue(n.automationId),s=stringValue(n.runId);if(!a||!s)return toolResult({error:"automationId and runId are required"});try{const e=await request(t,`${AUTOMATIONS_PATH}/${encodeURIComponent(a)}/runs/${encodeURIComponent(s)}`,{signal:o});return e.ok?toolResult(e.data):requestErrorResult(e)}catch(e){return toolResult({error:e instanceof Error?e.message:String(e)})}}})}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{AsyncLocalStorage}from"node:async_hooks";const forbidden=new Set(["beeos_automation_create","beeos_automation_create_webhook","beeos_automation_run"]);export const AUTOMATION_TOOL_BLOCK_REASON="Automation Wake cannot create or trigger another Automation.";export class AutomationTurnPolicy{ready=!1;turns=new Map;activeContext=new AsyncLocalStorage;enable(){this.ready=!0}disable(){this.ready=!1}isReady(){return this.ready}enter(e,t,o){if(!this.ready||!e||!t||this.turns.has(t))throw new Error("automation_turn_policy_unavailable");const n={sessionKey:e,context:o};return this.turns.set(t,n),{run:e=>this.activeContext.run(o,e),close:()=>{this.turns.get(t)===n&&this.turns.delete(t)}}}check(e,t){if(!forbidden.has(e))return;return this.activeContext.getStore()||t.runId&&this.turns.has(t.runId)||t.sessionKey&&[...this.turns.values()].some(e=>e.sessionKey===t.sessionKey)?{block:!0,blockReason:AUTOMATION_TOOL_BLOCK_REASON}:void 0}}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
const uuid=e=>"string"==typeof e&&/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/.test(e)&&"00000000-0000-0000-0000-000000000000"!==e,ownerID=e=>uuid(e)||"string"==typeof e&&/^[0-9a-f]{32}$/.test(e)&&"00000000000000000000000000000000"!==e;export function decodeWake(e){const o=e.content,n=o?.metadata;if(!n||!["beeos.kind","beeos.automation_id","beeos.run_id","beeos.session_mode"].some(e=>Object.hasOwn(n,e)))return{kind:"ordinary"};return"chat_message"===e.type&&"automation.run"===n["beeos.kind"]&&uuid(n["beeos.automation_id"])&&uuid(n["beeos.run_id"])&&e.id===n["beeos.run_id"]&&uuid(e.conversationId)&&!e.replyTo&&o?.channel_id===e.conversationId&&o.context_id===e.conversationId&&"string"==typeof o.message&&o.message.trim()&&e.body===o.message&&!Object.hasOwn(o,"session_key")&&("new"===n["beeos.session_mode"]||"bound"===n["beeos.session_mode"])&&uuid(n.target_agent_id)&&"string"==typeof n.delivery_principal&&n.delivery_principal&&"string"==typeof n.runtime_epoch&&/^[1-9][0-9]*$/.test(n.runtime_epoch)&&["beeos-conversation","openapi-conversation"].includes(String(n.protocol))&&ownerID(n.caller_owner_id)&&e.senderId===`user:${n.caller_owner_id}`?{kind:"wake",context:Object.freeze({automationId:n["beeos.automation_id"],runId:n["beeos.run_id"],sessionMode:n["beeos.session_mode"]})}:{kind:"invalid",reason:"invalid_automation_wake"}}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{extractChatPrompt}from"@beeos-ai/message-sdk/chat-envelope";import{buildMentionInstruction,parseMentionsParam}from"./delegation/mention-resolver.js";import{dispatchImInboundPrompt}from"./im-runtime-dispatch.js";import{promptRunKey}from"./runtime/prompt-run-store.js";const MAX_PROTOCOL_MARKER_LEN=64,DEFAULT_ORIGIN="im",resolveOrigin=e=>"string"!=typeof e||0===e.length||e.length>64?"im":e,conversationIdFromMessage=e=>"string"==typeof e.conversationId?e.conversationId:"";export const createImInboundHandler=e=>{const{runtime:t,pluginConfig:n,client:
|
|
1
|
+
import{extractChatPrompt}from"@beeos-ai/message-sdk/chat-envelope";import{buildMentionInstruction,parseMentionsParam}from"./delegation/mention-resolver.js";import{dispatchImInboundPrompt}from"./im-runtime-dispatch.js";import{promptRunKey}from"./runtime/prompt-run-store.js";import{decodeWake}from"./automation/wake.js";const MAX_PROTOCOL_MARKER_LEN=64,DEFAULT_ORIGIN="im",resolveOrigin=e=>"string"!=typeof e||0===e.length||e.length>64?"im":e,conversationIdFromMessage=e=>"string"==typeof e.conversationId?e.conversationId:"";export const createImInboundHandler=e=>{const{runtime:t,pluginConfig:n,client:o,localServer:a,promptRunStore:s,waitIfExclusive:r,assistantMessageCommitRouter:i,toolResultObservationRouter:c,modelCallObservationRouter:d,sessionMutationCoordinator:l,logger:u,writeObs:p,principalIdOverride:g,dispatch:m=dispatchImInboundPrompt,completeA2ATask:h,activeRunAuthority:f}=e,y=async(e,r)=>{const h=e.content??{},y=inboundFenceMetadata(e),v="string"==typeof y?.target_agent_id?y.target_agent_id:"",_=v?a.resolveLocalAgentId?.(v):void 0;if(!v||!_)return void u.warn?.(`[beeos-claw] ${r.logLabel} target_agent_id=${v||"missing"} not resolvable to a local agent — drop`);const I=_,b=conversationIdFromMessage(e),$="string"==typeof e.id?e.id:"";if(!b||!$)return void u.warn?.(`[beeos-claw] ${r.logLabel} missing channelId/messageId — drop`);const w=g||a.getDefaultPlatformAgent()?.instanceId||"";if(!w)return void u.warn?.(`[beeos-claw] ${r.logLabel} dropped (no principalId resolved, channelId=${b})`);const R="string"==typeof y?.delivery_principal?y.delivery_principal:"";if(R&&R!==w)return void u.info?.(`[beeos-claw] ${r.logLabel} skipped (delivery_principal=${R}, selfPrincipalId=${w}, message_id=${$})`);if("user.auth_grant"===r.resumeKind&&!0!==h.authorized)return void u.warn?.(`[beeos-claw] ${r.logLabel} dropped (authorized grant required, channelId=${b})`);if(!conversationHasOpenPause(o,b,r.pauseType,$))return void u.warn?.(`[beeos-claw] ${r.logLabel} dropped (no unmatched ${r.pauseType}, channelId=${b})`);const K=resumeDispatchInput(r.resumeKind,h);if(!K)return void u.warn?.(`[beeos-claw] ${r.logLabel} dropped (empty resume payload, channelId=${b})`);const C=resumeSessionKey(o,b,I,y);a.ensureImInboundSession(C,b,$,parseMentionsParam(h.mentions));const M=resolveOrigin(y?.protocol);u.debug?.(`[beeos-claw] ${r.logLabel} resume (sessionKey=${C}, channelId=${b}, pause=${r.pauseType})`);const O=e=>{p({component:"plugin",domain:"im_dispatch",name:`im.dispatch.${e.type}`,severity:"dispatch_error"===e.type?"warn":"info",payload:e})};try{await m({runtime:t,pluginConfig:n,prompt:{channelId:b,inboundMessageId:$,selfPrincipalId:w,dispatchInput:K,sessionKey:C},client:o,logger:u,promptRunStore:s,assistantMessageCommitRouter:i,toolResultObservationRouter:c,modelCallObservationRouter:d,sessionMutationCoordinator:l,abortTransport:a,origin:M,onLifecycleEvent:O,activeRunAuthority:f})}catch(e){u.error?.(`[beeos-claw] ${r.logLabel} resume failed channelId=${b} in_reply_to=${$} error_code=runtime_dispatch_failed`)}};return{onChatMessage:async y=>{const v=decodeWake(y);if("invalid"===v.kind||"wake"===v.kind&&!e.automationTurnPolicy?.isReady())return void u.warn?.("[beeos-claw] Automation Wake rejected: invalid contract or unavailable turn policy");const _=y.content??{},I=_.metadata??void 0,b="string"==typeof I?.target_agent_id?I.target_agent_id:"",$=b?a.resolveLocalAgentId?.(b):void 0;if(!b||!$)return void u.warn?.(`[beeos-claw] chat_message target_agent_id=${b||"missing"} not resolvable to a local agent — drop`);const w=extractChatPrompt(y,{source:"chat_message",localAgentId:$});if(!w)return;if(!w.channelId||!w.messageId)return void u.warn?.(`[beeos-claw] chat_message missing channelId/messageId (sessionKey=${w.sessionKey}) — drop`);const R=g||a.getDefaultPlatformAgent()?.instanceId||"";if(!R)return void u.warn?.(`[beeos-claw] chat_message dropped (no principalId resolved, sessionKey=${w.sessionKey})`);const K="string"==typeof I?.delivery_principal?I.delivery_principal:"";if(K&&K!==R)return void u.info?.(`[beeos-claw] chat_message skipped (delivery_principal=${K}, selfPrincipalId=${R}, message_id=${w.messageId})`);const C=parseMentionsParam(_.mentions);a.ensureImInboundSession(w.sessionKey,w.channelId,w.messageId,C);const M=buildMentionInstruction(C),O=M?`${M}\n${w.message}`:w.message,A=resolveOrigin(I?.protocol),P=w.messageId,L="a2a"===A&&h?(e,t)=>h(P,e,t):void 0;u.debug?.(`[beeos-claw] chat_message dispatch (sessionKey=${w.sessionKey}, channelId=${w.channelId}, origin=${A})`);const T=e=>{p({component:"plugin",domain:"im_dispatch",name:`im.dispatch.${e.type}`,severity:"dispatch_error"===e.type?"warn":"info",payload:e})};try{await m({runtime:t,pluginConfig:n,prompt:{channelId:w.channelId,inboundMessageId:w.messageId,requestId:y.requestId,selfPrincipalId:R,dispatchInput:{message:O,attachments:w.files},sessionKey:w.sessionKey},client:o,logger:u,promptRunStore:s,...r?{waitIfExclusive:r}:{},assistantMessageCommitRouter:i,toolResultObservationRouter:c,modelCallObservationRouter:d,sessionMutationCoordinator:l,abortTransport:a,origin:A,onLifecycleEvent:T,...L?{onTerminalResult:L}:{},activeRunAuthority:f,..."wake"===v.kind?{enterTurn:(t,n)=>e.automationTurnPolicy.enter(t,n,v.context)}:{}})}catch(e){u.error?.(`[beeos-claw] chat_message dispatch failed channelId=${w.channelId} in_reply_to=${w.messageId} error_code=runtime_dispatch_failed`)}},onChatCancel:async e=>{const t=e.content??{},n="string"==typeof t.target_message_id?t.target_message_id:"",o="string"==typeof t.reason?t.reason:"unknown";if(!n)return void u.warn?.(`[beeos-claw] chat_cancel dropped (no target_message_id, reason=${o})`);const a=conversationIdFromMessage(e);if(!a)return void u.warn?.(`[beeos-claw] chat_cancel dropped (no conversation identity, target_message_id=${n}, reason=${o})`);const r=`chat_cancel:${o}`,i=await s.abort(promptRunKey(a,n),r);i.ok?u.info?.(`[beeos-claw] chat_cancel accepted (target_message_id=${n}, requestId=${i.requestId}, channelId=${i.channelId}, reason=${o})`):u.info?.(`[beeos-claw] chat_cancel no-op (target_message_id=${n}, reason=${o}, why=${i.reason})`)},onChatContinue:async e=>{await y(e,{logLabel:"user.continue",pauseType:"agent.input_required",resumeKind:"user.continue"})},onChatAuthGrant:async e=>{await y(e,{logLabel:"user.auth_grant",pauseType:"agent.auth_required",resumeKind:"user.auth_grant"})}}};const inboundFenceMetadata=e=>{const t=asRecord(e.content),n=asRecord(t?.metadata);if(hasFenceKeys(n))return n;const o=asRecord(e.metadata);return hasFenceKeys(o)?o:hasFenceKeys(t)?t:n},hasFenceKeys=e=>void 0!==e&&(void 0!==e.delivery_principal||void 0!==e.target_agent_id||void 0!==e.runtime_epoch),asRecord=e=>null===e||"object"!=typeof e||Array.isArray(e)?void 0:e,conversationHasOpenPause=(e,t,n,o)=>{const a="agent.auth_required"===n?"user.auth_grant":"user.continue";let s=!1;for(const r of conversationMessages(e,t))r.id!==o&&(r.type===n&&(s=!0),r.type===a&&(s=!1));return s},conversationMessages=(e,t)=>{const n="function"==typeof e.getSnapshot?e.getSnapshot():void 0,o=n?.messages;return o?Object.values(o).filter(e=>e.conversationId===t).sort(compareSnapshotOrder):[]},compareSnapshotOrder=(e,t)=>{try{const n=BigInt(e.offset)-BigInt(t.offset);if(0n!==n)return n<0n?-1:1}catch{const n=e.offset.localeCompare(t.offset);if(0!==n)return n}return e.id.localeCompare(t.id)},resumeSessionKey=(e,t,n,o)=>{for(const o of conversationMessages(e,t)){if("chat_message"!==o.type)continue;const e=extractChatPrompt(o,{source:"chat_message",localAgentId:n});if(e?.sessionKey)return e.sessionKey}return`agent:${n}:${"string"==typeof o?.protocol&&o.protocol.length>0?o.protocol:"invoke"}:ch-${t}`},resumeDispatchInput=(e,t)=>{if("user.auth_grant"===e){if(!0!==t.authorized)return;return{message:"authorized",resumeKind:e}}const n=continueAnswer(t);if(void 0!==n)return{message:n,resumeKind:e}},continueAnswer=e=>{if("answer"in e)return resumeText(e.answer);if("input"in e)return resumeText(e.input);const t={...e};return delete t.metadata,delete t.delivery_principal,delete t.target_agent_id,delete t.runtime_epoch,delete t.channel_id,0!==Object.keys(t).length?resumeText(t):void 0},resumeText=e=>{if("string"==typeof e)return e.length>0?e:void 0;if("number"==typeof e&&Number.isFinite(e))return String(e);if("boolean"==typeof e)return e?"true":"false";if(null!=e&&"object"==typeof e)try{const t=JSON.stringify(e);return t.length>0&&"{}"!==t?t:void 0}catch{return}};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{createHash,randomUUID}from"node:crypto";import{dispatchRuntimeReplyWithRoute,hasRuntimeReplyApi}from"./runtime/reply-router.js";import{createImReplySink}from"./runtime/im-reply-sink.js";import{SerializedCallbackQueue}from"./runtime/callback-queue.js";import{promptRunKey}from"./runtime/prompt-run-store.js";import{classifyAgentRuntimeFailure}from"./runtime/agent-runtime-failure-classifier.js";import{SESSION_RECOVERY_REQUIRED_CODE,SESSION_RECOVERY_REQUIRED_MESSAGE}from"./runtime/session-mutation-coordinator.js";import{interceptAgentRuntimeFailures,messageStopReasonForRuntimeFailure,SESSION_BUSY_PUBLIC_MESSAGE}from"./runtime/im-runtime-failure-policy.js";import{writeRuntimeFailure}from"./runtime/write-runtime-failure.js";import{maybeRenameImConversationTitle}from"./runtime/conversation-title.js";import{logChatLatency}from"./chat-latency.js";const normalizePositiveTimeoutMs=e=>{if("number"!=typeof e||!Number.isFinite(e))return;const n=Math.max(0,Math.trunc(e));return n>0?n:void 0},ABORT_TERMINAL_GRACE_MS=1e4,PRE_RUN_LOOP_PROBE_MS=100,PRE_RUN_LOOP_LAG_LOG_MS=500,formatErrMessage=e=>{if(e instanceof Error)return e.message;if("string"==typeof e)return e;try{return JSON.stringify(e)}catch{return String(e)}},failMessageStream=async(e,n)=>{void 0!==n.body&&e.setBody(n.body),await e.fail(void 0===n.stopReason?void 0:{stopReason:n.stopReason})};export const deterministicReplyMessageId=(e,n,t,r)=>`${e}_${createHash("sha256").update(`${n}:${t}:${r}`).digest("hex").slice(0,32)}`;export const dispatchImInboundPrompt=async e=>{const{runtime:n,prompt:t,client:r,logger:a,promptRunStore:i,assistantMessageCommitRouter:s,toolResultObservationRouter:o,modelCallObservationRouter:c,sessionMutationCoordinator:d,abortTransport:l,activeRunAuthority:u}=e;if(!hasRuntimeReplyApi(n))return a.warn?.(`[im] runtime dispatch skipped reason=reply_api_unavailable channel=${t.channelId} in_reply_to=${t.inboundMessageId}`),e.onLifecycleEvent?.({type:"skipped",reason:"reply_api_unavailable",channelId:t.channelId,inboundMessageId:t.inboundMessageId}),!1;const m=t.requestId??t.inboundMessageId,p=randomUUID(),I=Date.now();logChatLatency(a,"claw.inbound.received",m,I,{origin:e.origin??"im",request_id_source:t.requestId?"realtime_correlation":"message_id_fallback"});const g=e.origin??"im",h={protocol:"im",requestId:m,sessionId:t.sessionKey??"agent:main",sourceMessageId:t.inboundMessageId,chatId:t.channelId,origin:g},y=async(n,r)=>{if(e.onTerminalResult)try{await e.onTerminalResult(n,r)}catch(e){a.warn?.(`[im] terminal business callback failed requestId=${m} channel=${t.channelId} in_reply_to=${t.inboundMessageId}: ${formatErrMessage(e)}`)}},_=promptRunKey(t.channelId,t.inboundMessageId),R=t.sessionKey??"agent:main",f=new AbortController;let b,E,w;const M=new Promise((e,n)=>{w=n});let $;const S=()=>{void 0!==$&&(clearTimeout($),$=void 0)};if(e.waitIfExclusive)try{await e.waitIfExclusive("im_dispatch",f.signal)}catch(n){return a.warn?.(`[im] runtime dispatch aborted waiting for exclusive mutation requestId=${m} channel=${t.channelId} in_reply_to=${t.inboundMessageId}`),e.onLifecycleEvent?.({type:"dispatch_error",requestId:m,channelId:t.channelId,inboundMessageId:t.inboundMessageId,error:n instanceof Error?n.message:String(n),cause:"abort"}),!1}if(!i.register(_,{requestId:m,channelId:t.channelId,requestCancel:async e=>{if(E??=e,f.abort(e),!b)return{ok:!0};const n=await d.requestCancel(b,l);return n.ok?n.terminalObservation.then(e=>{e.ok||w(new Error(SESSION_RECOVERY_REQUIRED_CODE))}):w(new Error(SESSION_RECOVERY_REQUIRED_CODE)),{ok:n.ok}},startedAt:I,sessionKey:R}))return a.warn?.(`[im] runtime dispatch rejected duplicate inboundMessageId=${t.inboundMessageId} requestId=${m} channel=${t.channelId}`),e.onLifecycleEvent?.({type:"dispatch_error",requestId:m,channelId:t.channelId,inboundMessageId:t.inboundMessageId,error:"duplicate_inbound_message_id",cause:"abort"}),!1;const v=deterministicReplyMessageId("asst",t.channelId,t.inboundMessageId,t.selfPrincipalId),C={conversationId:t.channelId,clientMessageId:v,idempotencyKey:v,replyTo:e.inReplyTo??t.inboundMessageId,requestId:m,type:"agent_reply",content:{}},q=r.messages.startStream(C);let k,O=!1;try{const e=await q.opened();O="duplicate"===e.outcome,logChatLatency(a,"claw.reply_stream.opened",m,I,{outcome:e.outcome})}catch(n){let s="unconfirmed";try{s=(await r.messages.getEnvelope(t.channelId,v)).state}catch{}return a.warn?.(`[im] runtime reply stream open outcome unknown requestId=${m} channel=${t.channelId} in_reply_to=${t.inboundMessageId} reply_id=${v} reconciled_state=${s}: ${formatErrMessage(n)}; runtime_not_invoked`),e.onLifecycleEvent?.({type:"skipped",reason:"reply_open_outcome_unknown",channelId:t.channelId,inboundMessageId:t.inboundMessageId}),i.unregister(_),!1}if(O)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 L=new SerializedCallbackQueue,A=createImReplySink({stream:q,abortSignal:f.signal,callbackQueue:L,logger:a,requestId:m,startedAtMs:I,runId:p,channelId:t.channelId,inboundMessageId:t.inboundMessageId,origin:g,sessionKey:R,onAgentRunStart:e=>{d.bindRun(b,e),void 0!==k&&logChatLatency(a,"claw.runtime.pre_run_completed",m,k)}}),T=interceptAgentRuntimeFailures(A,"im_runtime",e=>a.warn?.(`[im] runtime failure reply suppressed requestId=${m} channel=${t.channelId} in_reply_to=${t.inboundMessageId} error_code=${e.errorCode}`)),D=T.sink;let F=!1;const U=async e=>{D.dispatcher.markComplete(e);try{await D.dispatcher.waitForIdle()}catch{}},K=()=>{S();const n=normalizePositiveTimeoutMs(e.abortTerminalGraceMs)??1e4;$=setTimeout(()=>{$=void 0,(async()=>{if(q.isTerminated)return;const e=f.signal.reason??E,n="string"==typeof e&&e.startsWith("chat_cancel");if(await U("failure"),!q.isTerminated)try{n?await q.cancel({stopReason:"user_stop"}):await failMessageStream(q,{body:SESSION_RECOVERY_REQUIRED_MESSAGE,stopReason:"error"}),a.warn?.(`[im] abort settlement watchdog terminalized reply requestId=${m} channel=${t.channelId} in_reply_to=${t.inboundMessageId} user_cancel=${n}`)}catch(e){a.warn?.(`[im] abort settlement watchdog terminal write failed requestId=${m} channel=${t.channelId}: ${formatErrMessage(e)}`)}})()},n)};f.signal.aborted?K():f.signal.addEventListener("abort",K,{once:!0});const N=()=>{if(!f.signal.aborted)return;const e=f.signal.reason;throw e instanceof Error?e:new Error(String(e??"runtime dispatch aborted"))};let P,G,Q,j,Y,x;const B=async e=>{if(!b)throw new Error("session_mutation_not_acquired");const n=await d.converge(b,e);return n.ok?(P=n.permit,!0):(a.error?.(`[im] session mutation recovery required requestId=${m} sessionKey=${R} reason=${n.recovery.reason}`),G={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{N();const l=d.tryStartInteractive({sessionKey:R,label:"im_chat_message"},"message_service");if(!l.ok){try{await failMessageStream(q,{body:SESSION_BUSY_PUBLIC_MESSAGE,stopReason:"error"})}catch(e){a.warn?.(`[im] session busy terminal write failed channel=${t.channelId} in_reply_to=${t.inboundMessageId}: ${formatErrMessage(e)}`)}return a.warn?.(`[im] session write gate busy sessionKey=${R} channel=${t.channelId} in_reply_to=${t.inboundMessageId} active_operation=${l.activeOperationId??"unknown"}`),e.onLifecycleEvent?.({type:"dispatch_error",requestId:m,channelId:t.channelId,inboundMessageId:t.inboundMessageId,error:"session_busy",cause:"runtime_error"}),!1}if(b=l.token,logChatLatency(a,"claw.session_gate.acquired",m,I),d.bindRun(b,p),j=s.register(R,A),Y=o.register(R,A),x=c.register(p,{requestId:m,startedAtMs:I,logger:a}),u){const e=d.snapshot(R);Q={runtimeConversationUid:t.channelId,runtimeSessionKey:t.channelId,historyGeneration:u.historyGeneration(t.channelId),targetMessageId:t.inboundMessageId,runtimeRunId:p,activeRunRegistrationId:`im:${e?.operationId??p}`},u.activeRuns.register(Q,async()=>{const e=`chat_cancel:activeRun:${Q.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"})}N(),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}),k=Date.now(),logChatLatency(a,"claw.runtime.invoke",m,I);const f=setTimeout(()=>{const e=Date.now()-k-100;e>=500&&logChatLatency(a,"claw.runtime.event_loop_lag",m,k,{lag_ms:e,phase:"pre_run"})},100);f.unref?.();const E=dispatchRuntimeReplyWithRoute({runtime:n,pluginConfig:e.pluginConfig,preparedPrompt:t.dispatchInput,replyRoute:h,replySinks:{im:D},onDispatchInvoked:()=>{F=!0,logChatLatency(a,"claw.runtime.dispatch_returned",m,k)}}),w=await Promise.race([E,M]).finally(()=>{}),$=T.detectedFailure();if($)throw new Error($.diagnosticMessage);if(N(),await U("success"),N(),!i.claimTerminal(_,"completion")){if(N(),"cancelling"===i.phase(_))throw new Error("cancellation in progress");throw new Error("terminal outcome already claimed")}if(!await B("completed"))return await failMessageStream(q,{body:SESSION_RECOVERY_REQUIRED_MESSAGE,stopReason:"error"}).catch(()=>{}),e.onLifecycleEvent?.({type:"dispatch_error",requestId:m,channelId:t.channelId,inboundMessageId:t.inboundMessageId,error:SESSION_RECOVERY_REQUIRED_CODE,cause:"runtime_error",...G?{recovery:G}:{}}),!1;const S=D.dispatcher.getTerminalErrorText?.();if(void 0!==S){let n=!1;const r=classifyAgentRuntimeFailure({raw:S,source:"im_runtime",runtimeKind:"openclaw"});try{await writeRuntimeFailure(q,{body:r.publicMessage,stopReason:messageStopReasonForRuntimeFailure(r),businessErrorCode:r.businessErrorCode})}catch(e){n=!0,a.warn?.(`[im] runtime reply stream error-terminal write outcome unknown requestId=${m} channel=${t.channelId} in_reply_to=${t.inboundMessageId}: ${formatErrMessage(e)}`)}return u&&Q&&u.activeRuns.markTerminal(Q,"failed"),a.warn?.(`[im] runtime dispatch ended with an error terminal requestId=${m} channel=${t.channelId} in_reply_to=${t.inboundMessageId} counts=${JSON.stringify(w.counts)} terminalWriteFailed=${n} elapsed_ms=${Date.now()-I}`),e.onLifecycleEvent?.({type:"dispatch_error",requestId:m,channelId:t.channelId,inboundMessageId:t.inboundMessageId,error:"runtime_error_terminal_reply",cause:"runtime_error"}),n||await y("",S),!1}let v=!1;try{await q.finalize({stopReason:"end_turn"})}catch(e){v=!0,a.warn?.(`[im] runtime reply stream finalize outcome unknown requestId=${m} channel=${t.channelId} in_reply_to=${t.inboundMessageId}: ${formatErrMessage(e)}`)}u&&Q&&u.activeRuns.markTerminal(Q,"completed");const C={...w.counts,source:A.getSourceCount()};a.info?.(`[im] runtime dispatch completed requestId=${m} channel=${t.channelId} in_reply_to=${t.inboundMessageId} counts=${JSON.stringify(C)} elapsed_ms=${Date.now()-I}`),logChatLatency(a,"claw.turn.terminal",m,I,{outcome:v?"outcome_unknown":"completed",tool_count:C.tool}),e.onLifecycleEvent?.({type:"dispatch_complete",requestId:m,channelId:t.channelId,inboundMessageId:t.inboundMessageId,counts:C,...v?{terminalWriteFailed:!0}:{}});const O=D.dispatcher.getAccumulatedBody?.()??"";v||await y(O,"");const L="string"==typeof t.dispatchInput.message?t.dispatchInput.message:"";return maybeRenameImConversationTitle({client:r,conversationId:t.channelId,question:L,answer:O,logger:a}),!0}catch(n){await U("failure");const r=f.signal.aborted,s=r?f.signal.reason:void 0,o="cancelling"===i.phase(_),c=s??E,d="string"==typeof c&&c.startsWith("chat_cancel");let l="runtime_error";(r||o)&&(l="abort");const p=o||d||r?"cancellation":"completion";if(!i.claimTerminal(_,p)){if(a.warn?.(`[im] terminal claim raced requestId=${m} channel=${t.channelId}; best-effort envelope terminal if still streaming`),!q.isTerminated)try{d?await q.cancel({stopReason:"user_stop"}):await failMessageStream(q,{body:formatErrMessage(n),stopReason:"error"})}catch(e){a.warn?.(`[im] raced terminal write failed requestId=${m}: ${formatErrMessage(e)}`)}throw n}const g=d?"cancelled":"error";if(u&&Q&&u.activeRuns.markTerminal(Q,"cancelled"===g?"cancelled":"failed"),b&&!await B(g))throw d?await q.cancel({stopReason:"user_stop"}).catch(()=>{}):await failMessageStream(q,{body:SESSION_RECOVERY_REQUIRED_MESSAGE,stopReason:"error"}).catch(()=>{}),e.onLifecycleEvent?.({type:"dispatch_error",requestId:m,channelId:t.channelId,inboundMessageId:t.inboundMessageId,error:SESSION_RECOVERY_REQUIRED_CODE,cause:l,...G?{recovery:G}:{}}),n;const h=classifyAgentRuntimeFailure({raw:n,source:"im_runtime",runtimeKind:"openclaw",requestAborted:d}),R=d?h:T.detectedFailure()??h;let w=!1;try{d?await q.cancel({stopReason:"user_stop"}):await writeRuntimeFailure(q,{body:R.publicMessage,stopReason:messageStopReasonForRuntimeFailure(R),businessErrorCode:R.businessErrorCode})}catch(e){w=!0,a.warn?.(`[im] runtime stream terminal write failed requestId=${m} channel=${t.channelId}: ${formatErrMessage(e)}`)}throw a.warn?.(`[im] runtime dispatch failed requestId=${m} channel=${t.channelId} in_reply_to=${t.inboundMessageId} cause=${l} dispatchInvoked=${F} error_code=${R.errorCode}`),logChatLatency(a,"claw.turn.terminal",m,I,{outcome:l,error_code:R.errorCode}),e.onLifecycleEvent?.({type:"dispatch_error",requestId:m,channelId:t.channelId,inboundMessageId:t.inboundMessageId,error:R.errorCode,cause:l}),d||w||await y("",R.publicMessage),n}finally{S(),x?.(),Y?.(),j?.(),i.unregister(_),F?P&&d.release(P):d.releaseBeforeRuntime(b)}};
|
|
1
|
+
import{createHash,randomUUID}from"node:crypto";import{dispatchRuntimeReplyWithRoute,hasRuntimeReplyApi}from"./runtime/reply-router.js";import{createImReplySink}from"./runtime/im-reply-sink.js";import{SerializedCallbackQueue}from"./runtime/callback-queue.js";import{promptRunKey}from"./runtime/prompt-run-store.js";import{classifyAgentRuntimeFailure}from"./runtime/agent-runtime-failure-classifier.js";import{SESSION_RECOVERY_REQUIRED_CODE,SESSION_RECOVERY_REQUIRED_MESSAGE}from"./runtime/session-mutation-coordinator.js";import{interceptAgentRuntimeFailures,messageStopReasonForRuntimeFailure,SESSION_BUSY_PUBLIC_MESSAGE}from"./runtime/im-runtime-failure-policy.js";import{writeRuntimeFailure}from"./runtime/write-runtime-failure.js";import{maybeRenameImConversationTitle}from"./runtime/conversation-title.js";import{logChatLatency}from"./chat-latency.js";const normalizePositiveTimeoutMs=e=>{if("number"!=typeof e||!Number.isFinite(e))return;const n=Math.max(0,Math.trunc(e));return n>0?n:void 0},ABORT_TERMINAL_GRACE_MS=1e4,PRE_RUN_LOOP_PROBE_MS=100,PRE_RUN_LOOP_LAG_LOG_MS=500,formatErrMessage=e=>{if(e instanceof Error)return e.message;if("string"==typeof e)return e;try{return JSON.stringify(e)}catch{return String(e)}},failMessageStream=async(e,n)=>{void 0!==n.body&&e.setBody(n.body),await e.fail(void 0===n.stopReason?void 0:{stopReason:n.stopReason})};export const deterministicReplyMessageId=(e,n,r,t)=>`${e}_${createHash("sha256").update(`${n}:${r}:${t}`).digest("hex").slice(0,32)}`;export const dispatchImInboundPrompt=async e=>{const{runtime:n,prompt:r,client:t,logger:a,promptRunStore:i,assistantMessageCommitRouter:s,toolResultObservationRouter:o,modelCallObservationRouter:c,sessionMutationCoordinator:d,abortTransport:l,activeRunAuthority:u}=e;if(!hasRuntimeReplyApi(n))return a.warn?.(`[im] runtime dispatch skipped reason=reply_api_unavailable channel=${r.channelId} in_reply_to=${r.inboundMessageId}`),e.onLifecycleEvent?.({type:"skipped",reason:"reply_api_unavailable",channelId:r.channelId,inboundMessageId:r.inboundMessageId}),!1;const m=r.requestId??r.inboundMessageId,p=randomUUID(),I=Date.now();logChatLatency(a,"claw.inbound.received",m,I,{origin:e.origin??"im",request_id_source:r.requestId?"realtime_correlation":"message_id_fallback"});const g=e.origin??"im",h={protocol:"im",requestId:m,sessionId:r.sessionKey??"agent:main",sourceMessageId:r.inboundMessageId,chatId:r.channelId,origin:g},y=async(n,t)=>{if(e.onTerminalResult)try{await e.onTerminalResult(n,t)}catch(e){a.warn?.(`[im] terminal business callback failed requestId=${m} channel=${r.channelId} in_reply_to=${r.inboundMessageId}: ${formatErrMessage(e)}`)}},_=promptRunKey(r.channelId,r.inboundMessageId),R=r.sessionKey??"agent:main",f=new AbortController;let b,E,w;const M=new Promise((e,n)=>{w=n});let $;const S=()=>{void 0!==$&&(clearTimeout($),$=void 0)};if(e.waitIfExclusive)try{await e.waitIfExclusive("im_dispatch",f.signal)}catch(n){return a.warn?.(`[im] runtime dispatch aborted waiting for exclusive mutation requestId=${m} channel=${r.channelId} in_reply_to=${r.inboundMessageId}`),e.onLifecycleEvent?.({type:"dispatch_error",requestId:m,channelId:r.channelId,inboundMessageId:r.inboundMessageId,error:n instanceof Error?n.message:String(n),cause:"abort"}),!1}if(!i.register(_,{requestId:m,channelId:r.channelId,requestCancel:async e=>{if(E??=e,f.abort(e),!b)return{ok:!0};const n=await d.requestCancel(b,l);return n.ok?n.terminalObservation.then(e=>{e.ok||w(new Error(SESSION_RECOVERY_REQUIRED_CODE))}):w(new Error(SESSION_RECOVERY_REQUIRED_CODE)),{ok:n.ok}},startedAt:I,sessionKey:R}))return a.warn?.(`[im] runtime dispatch rejected duplicate inboundMessageId=${r.inboundMessageId} requestId=${m} channel=${r.channelId}`),e.onLifecycleEvent?.({type:"dispatch_error",requestId:m,channelId:r.channelId,inboundMessageId:r.inboundMessageId,error:"duplicate_inbound_message_id",cause:"abort"}),!1;const v=deterministicReplyMessageId("asst",r.channelId,r.inboundMessageId,r.selfPrincipalId),C={conversationId:r.channelId,clientMessageId:v,idempotencyKey:v,replyTo:e.inReplyTo??r.inboundMessageId,requestId:m,type:"agent_reply",content:{}},q=t.messages.startStream(C);let k,O=!1;try{const e=await q.opened();O="duplicate"===e.outcome,logChatLatency(a,"claw.reply_stream.opened",m,I,{outcome:e.outcome})}catch(n){let s="unconfirmed";try{s=(await t.messages.getEnvelope(r.channelId,v)).state}catch{}return a.warn?.(`[im] runtime reply stream open outcome unknown requestId=${m} channel=${r.channelId} in_reply_to=${r.inboundMessageId} reply_id=${v} reconciled_state=${s}: ${formatErrMessage(n)}; runtime_not_invoked`),e.onLifecycleEvent?.({type:"skipped",reason:"reply_open_outcome_unknown",channelId:r.channelId,inboundMessageId:r.inboundMessageId}),i.unregister(_),!1}if(O)return a.info?.(`[im] runtime dispatch skipped existing deterministic reply requestId=${m} channel=${r.channelId} in_reply_to=${r.inboundMessageId}`),e.onLifecycleEvent?.({type:"skipped",reason:"idempotent_reply_exists",channelId:r.channelId,inboundMessageId:r.inboundMessageId}),i.unregister(_),!1;const L=new SerializedCallbackQueue,T=createImReplySink({stream:q,abortSignal:f.signal,callbackQueue:L,logger:a,requestId:m,startedAtMs:I,runId:p,channelId:r.channelId,inboundMessageId:r.inboundMessageId,origin:g,sessionKey:R,onAgentRunStart:e=>{d.bindRun(b,e),void 0!==k&&logChatLatency(a,"claw.runtime.pre_run_completed",m,k)}}),A=interceptAgentRuntimeFailures(T,"im_runtime",e=>a.warn?.(`[im] runtime failure reply suppressed requestId=${m} channel=${r.channelId} in_reply_to=${r.inboundMessageId} error_code=${e.errorCode}`)),D=A.sink;let F=!1;const U=async e=>{D.dispatcher.markComplete(e);try{await D.dispatcher.waitForIdle()}catch{}},K=()=>{S();const n=normalizePositiveTimeoutMs(e.abortTerminalGraceMs)??1e4;$=setTimeout(()=>{$=void 0,(async()=>{if(q.isTerminated)return;const e=f.signal.reason??E,n="string"==typeof e&&e.startsWith("chat_cancel");if(await U("failure"),!q.isTerminated)try{n?await q.cancel({stopReason:"user_stop"}):await failMessageStream(q,{body:SESSION_RECOVERY_REQUIRED_MESSAGE,stopReason:"error"}),a.warn?.(`[im] abort settlement watchdog terminalized reply requestId=${m} channel=${r.channelId} in_reply_to=${r.inboundMessageId} user_cancel=${n}`)}catch(e){a.warn?.(`[im] abort settlement watchdog terminal write failed requestId=${m} channel=${r.channelId}: ${formatErrMessage(e)}`)}})()},n)};f.signal.aborted?K():f.signal.addEventListener("abort",K,{once:!0});const N=()=>{if(!f.signal.aborted)return;const e=f.signal.reason;throw e instanceof Error?e:new Error(String(e??"runtime dispatch aborted"))};let P,G,Q,j,Y,x,B;const V=async e=>{if(!b)throw new Error("session_mutation_not_acquired");const n=await d.converge(b,e);return n.ok?(P=n.permit,!0):(a.error?.(`[im] session mutation recovery required requestId=${m} sessionKey=${R} reason=${n.recovery.reason}`),G={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{N();const l=d.tryStartInteractive({sessionKey:R,label:"im_chat_message"},"message_service");if(!l.ok){try{await failMessageStream(q,{body:SESSION_BUSY_PUBLIC_MESSAGE,stopReason:"error"})}catch(e){a.warn?.(`[im] session busy terminal write failed channel=${r.channelId} in_reply_to=${r.inboundMessageId}: ${formatErrMessage(e)}`)}return a.warn?.(`[im] session write gate busy sessionKey=${R} channel=${r.channelId} in_reply_to=${r.inboundMessageId} active_operation=${l.activeOperationId??"unknown"}`),e.onLifecycleEvent?.({type:"dispatch_error",requestId:m,channelId:r.channelId,inboundMessageId:r.inboundMessageId,error:"session_busy",cause:"runtime_error"}),!1}if(b=l.token,logChatLatency(a,"claw.session_gate.acquired",m,I),d.bindRun(b,p),j=s.register(R,T),Y=o.register(R,T),x=c.register(p,{requestId:m,startedAtMs:I,logger:a}),u){const e=d.snapshot(R);Q={runtimeConversationUid:r.channelId,runtimeSessionKey:r.channelId,historyGeneration:u.historyGeneration(r.channelId),targetMessageId:r.inboundMessageId,runtimeRunId:p,activeRunRegistrationId:`im:${e?.operationId??p}`},u.activeRuns.register(Q,async()=>{const e=`chat_cancel:activeRun:${Q.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"})}N(),a.info?.(`[im] runtime dispatching requestId=${m} channel=${r.channelId} in_reply_to=${r.inboundMessageId} origin=${g}`),e.onLifecycleEvent?.({type:"dispatch_start",requestId:m,channelId:r.channelId,inboundMessageId:r.inboundMessageId}),k=Date.now(),B=e.enterTurn?.(R,p),logChatLatency(a,"claw.runtime.invoke",m,I);const f=setTimeout(()=>{const e=Date.now()-k-100;e>=500&&logChatLatency(a,"claw.runtime.event_loop_lag",m,k,{lag_ms:e,phase:"pre_run"})},100);f.unref?.();const E=()=>dispatchRuntimeReplyWithRoute({runtime:n,pluginConfig:e.pluginConfig,preparedPrompt:r.dispatchInput,replyRoute:h,replySinks:{im:D},onDispatchInvoked:()=>{F=!0,logChatLatency(a,"claw.runtime.dispatch_returned",m,k)}}),w=B?B.run(E):E(),$=await Promise.race([w,M]).finally(()=>{}),S=A.detectedFailure();if(S)throw new Error(S.diagnosticMessage);if(N(),await U("success"),N(),!i.claimTerminal(_,"completion")){if(N(),"cancelling"===i.phase(_))throw new Error("cancellation in progress");throw new Error("terminal outcome already claimed")}if(!await V("completed"))return await failMessageStream(q,{body:SESSION_RECOVERY_REQUIRED_MESSAGE,stopReason:"error"}).catch(()=>{}),e.onLifecycleEvent?.({type:"dispatch_error",requestId:m,channelId:r.channelId,inboundMessageId:r.inboundMessageId,error:SESSION_RECOVERY_REQUIRED_CODE,cause:"runtime_error",...G?{recovery:G}:{}}),!1;const v=D.dispatcher.getTerminalErrorText?.();if(void 0!==v){let n=!1;const t=classifyAgentRuntimeFailure({raw:v,source:"im_runtime",runtimeKind:"openclaw"});try{await writeRuntimeFailure(q,{body:t.publicMessage,stopReason:messageStopReasonForRuntimeFailure(t),businessErrorCode:t.businessErrorCode})}catch(e){n=!0,a.warn?.(`[im] runtime reply stream error-terminal write outcome unknown requestId=${m} channel=${r.channelId} in_reply_to=${r.inboundMessageId}: ${formatErrMessage(e)}`)}return u&&Q&&u.activeRuns.markTerminal(Q,"failed"),a.warn?.(`[im] runtime dispatch ended with an error terminal requestId=${m} channel=${r.channelId} in_reply_to=${r.inboundMessageId} counts=${JSON.stringify($.counts)} terminalWriteFailed=${n} elapsed_ms=${Date.now()-I}`),e.onLifecycleEvent?.({type:"dispatch_error",requestId:m,channelId:r.channelId,inboundMessageId:r.inboundMessageId,error:"runtime_error_terminal_reply",cause:"runtime_error"}),n||await y("",v),!1}let C=!1;try{await q.finalize({stopReason:"end_turn"})}catch(e){C=!0,a.warn?.(`[im] runtime reply stream finalize outcome unknown requestId=${m} channel=${r.channelId} in_reply_to=${r.inboundMessageId}: ${formatErrMessage(e)}`)}u&&Q&&u.activeRuns.markTerminal(Q,"completed");const O={...$.counts,source:T.getSourceCount()};a.info?.(`[im] runtime dispatch completed requestId=${m} channel=${r.channelId} in_reply_to=${r.inboundMessageId} counts=${JSON.stringify(O)} elapsed_ms=${Date.now()-I}`),logChatLatency(a,"claw.turn.terminal",m,I,{outcome:C?"outcome_unknown":"completed",tool_count:O.tool}),e.onLifecycleEvent?.({type:"dispatch_complete",requestId:m,channelId:r.channelId,inboundMessageId:r.inboundMessageId,counts:O,...C?{terminalWriteFailed:!0}:{}});const L=D.dispatcher.getAccumulatedBody?.()??"";C||await y(L,"");const K="string"==typeof r.dispatchInput.message?r.dispatchInput.message:"";return maybeRenameImConversationTitle({client:t,conversationId:r.channelId,question:K,answer:L,logger:a}),!0}catch(n){await U("failure");const t=f.signal.aborted,s=t?f.signal.reason:void 0,o="cancelling"===i.phase(_),c=s??E,d="string"==typeof c&&c.startsWith("chat_cancel");let l="runtime_error";(t||o)&&(l="abort");const p=o||d||t?"cancellation":"completion";if(!i.claimTerminal(_,p)){if(a.warn?.(`[im] terminal claim raced requestId=${m} channel=${r.channelId}; best-effort envelope terminal if still streaming`),!q.isTerminated)try{d?await q.cancel({stopReason:"user_stop"}):await failMessageStream(q,{body:formatErrMessage(n),stopReason:"error"})}catch(e){a.warn?.(`[im] raced terminal write failed requestId=${m}: ${formatErrMessage(e)}`)}throw n}const g=d?"cancelled":"error";if(u&&Q&&u.activeRuns.markTerminal(Q,"cancelled"===g?"cancelled":"failed"),b&&!await V(g))throw d?await q.cancel({stopReason:"user_stop"}).catch(()=>{}):await failMessageStream(q,{body:SESSION_RECOVERY_REQUIRED_MESSAGE,stopReason:"error"}).catch(()=>{}),e.onLifecycleEvent?.({type:"dispatch_error",requestId:m,channelId:r.channelId,inboundMessageId:r.inboundMessageId,error:SESSION_RECOVERY_REQUIRED_CODE,cause:l,...G?{recovery:G}:{}}),n;const h=classifyAgentRuntimeFailure({raw:n,source:"im_runtime",runtimeKind:"openclaw",requestAborted:d}),R=d?h:A.detectedFailure()??h;let w=!1;try{d?await q.cancel({stopReason:"user_stop"}):await writeRuntimeFailure(q,{body:R.publicMessage,stopReason:messageStopReasonForRuntimeFailure(R),businessErrorCode:R.businessErrorCode})}catch(e){w=!0,a.warn?.(`[im] runtime stream terminal write failed requestId=${m} channel=${r.channelId}: ${formatErrMessage(e)}`)}throw a.warn?.(`[im] runtime dispatch failed requestId=${m} channel=${r.channelId} in_reply_to=${r.inboundMessageId} cause=${l} dispatchInvoked=${F} error_code=${R.errorCode}`),logChatLatency(a,"claw.turn.terminal",m,I,{outcome:l,error_code:R.errorCode}),e.onLifecycleEvent?.({type:"dispatch_error",requestId:m,channelId:r.channelId,inboundMessageId:r.inboundMessageId,error:R.errorCode,cause:l}),d||w||await y("",R.publicMessage),n}finally{F&&!P||B?.close(),S(),x?.(),Y?.(),j?.(),i.unregister(_),F?P&&d.release(P):d.releaseBeforeRuntime(b)}};
|
package/dist/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{fileDeliveryFinalizeDecision}from"./upload/finalize-hook.js";import{resolveConfigWithMeta,mirrorOpenClawConfig}from"./config.js";import{hasRuntimeReplyApi}from"./runtime/reply-router.js";import{PromptRunStore}from"./runtime/prompt-run-store.js";import{createMcpNativeProjectionNudge,fingerprintMcpServers}from"./acp-gateway/mcp-native-projection-nudge.js";import{createImHotPathDeferral}from"./runtime/im-hot-path-deferral.js";import{SessionWriteGate}from"./acp-gateway/session-write-gate.js";import{OpenClawSessionWriteQuiescence}from"./runtime/openclaw-session-write-quiescence.js";import{SessionMutationCoordinator}from"./runtime/session-mutation-coordinator.js";import{scheduleProcessRuntimeRestart}from"./runtime/runtime-restart-scheduler.js";import{createImInboundHandler}from"./im-inbound-handler.js";import{createTokenProvider}from"@beeos-ai/message-sdk/auth";import{BeeosClawMessageClientProcessOwner}from"./message-client-composition.js";import{MessageProjectionInboundConsumer}from"./message-projection-inbound-consumer.js";import{MessageClientTerminalStateRecovery}from"./runtime/message-client-terminal-state-recovery.js";import{RuntimeAuthorityMessageBusLifecycle}from"./runtime/runtime-authority-message-bus-lifecycle.js";import{MSConnectionStatusSnapshot}from"./runtime/ms-connection-status.js";import{FileInboundExecutionJournal}from"./inbound-execution-journal.js";import{completeA2ATask,registerA2ATools,TaskHandleRegistry}from"./a2a/tools.js";import{BEEOS_UPLOAD_TOOL_NAME,BEEOS_UPLOAD_TOOL_SCHEMA,BEEOS_UPLOAD_TOOL_DESCRIPTION,executeUpload}from"./upload/tool.js";import{BEEOS_DESKTOP_SCREENSHOT_TOOL_NAME,BEEOS_DESKTOP_SCREENSHOT_TOOL_SCHEMA,BEEOS_DESKTOP_SCREENSHOT_TOOL_DESCRIPTION,executeDesktopScreenshot}from"./upload/desktop-screenshot-tool.js";import{BEEOS_LIST_FILES_TOOL_NAME,BEEOS_LIST_FILES_TOOL_SCHEMA,BEEOS_LIST_FILES_TOOL_DESCRIPTION,executeListFiles}from"./files/list-tool.js";import{BEEOS_FETCH_FILE_TOOL_NAME,BEEOS_FETCH_FILE_TOOL_SCHEMA,BEEOS_FETCH_FILE_TOOL_DESCRIPTION,executeFetchFile}from"./files/fetch-tool.js";import{writeUploadToolResult,cleanupUploadToolResultCache}from"./upload/result-cache.js";import{handleFileStage}from"./upload/file-stage.js";import{handleStatusRequest}from"./status.js";import{ACPServer}from"./acp-server.js";import{GatewayClient}from"./gateway-client.js";import{createObsEventWriter}from"./observability.js";import{resolvePluginVersion}from"./plugin-version.js";import{createTerminalWebSocketService}from"./terminal/websocket.js";import{TerminalManager}from"./terminal/session-manager.js";import{loadAgentKeyPair,agentBoundFetch,agentFetch}from"./agent-auth.js";import{readDiscoveryCache,writeDiscoveryCache}from"./discovery-cache.js";import{discoverBridgeFromGW}from"./bridge-discovery.js";import{setUploadDeps}from"./upload/upload-file.js";import{registerLocalFileExtractor}from"./upload/local-file-extractors.js";import{browserScreenshotExtractor}from"./browser/screenshot-extractor.js";import{deliverableFileExtractor}from"./upload/deliverable-extractor.js";import{observeToolCallForDelivery,rememberDeliveryWorkspace}from"./upload/delivery-observe.js";import{BEEOS_FILE_RESOURCE_ROUTING_RULE}from"./files/beeos-file-uri.js";import{marksFullRuntimeRegistered,planPluginRegistration,replayCachedPluginTools,resolveCapabilityToolReplay}from"./plugin-registration-plan.js";import{buildAppendSystemContext,buildToolRoutingHint,canvasToolBlockReason}from"./prompt-system-context.js";import{CONNECTOR_MCP_PROXY_PATH,handleConnectorMcpRequest}from"./mcp-connector-proxy.js";import{CanvasStateManager}from"./canvas/state.js";import{CanvasRelayClient}from"./canvas/relay-client.js";import{createCanvasRelayService}from"./canvas/relay-service.js";import{RENDER_UI_TOOL_NAME,RENDER_UI_TOOL_DESCRIPTION,RENDER_UI_TOOL_SCHEMA,CLOSE_UI_TOOL_NAME,CLOSE_UI_TOOL_DESCRIPTION,CLOSE_UI_TOOL_SCHEMA,createRenderUiHandler,createCloseUiHandler,CANVAS_PRESENTATION_GATE,CANVAS_SYSTEM_PROMPT}from"./canvas/tools.js";import{FormSchemaRegistry}from"./surface/form-schema-registry.js";import{buildCronSchemas}from"./surface/cron-schemas.js";import{RENDER_FORM_TOOL_NAME,RENDER_FORM_TOOL_SCHEMA,buildRenderFormDescription,createRenderFormHandler}from"./surface/render-form-tool.js";import{ResourceDescriptorRegistry}from"./surface/resource-descriptor-registry.js";import{CRON_RESOURCE}from"./surface/cron-resource.js";import{generateActionTools}from"./surface/action-tool-generator.js";import{buildThirdPartyChannelSet,isThirdPartyChannel}from"./channel-detection.js";import{registerArouterMediaProviders}from"./media/index.js";import{homedir}from"node:os";import{join}from"node:path";import{RuntimeProductionAuthority}from"./runtime/runtime-production-authority.js";import{FileRuntimeCommandJournal}from"./runtime/runtime-command-journal.js";import{FileRuntimePoisonDeliveryJournal}from"./runtime/runtime-poison-delivery-journal.js";import{OpenClawNativeMcpProjection}from"./acp-gateway/openclaw-native-mcp-projection.js";import{LocalEncryptedCredentialReferenceVault}from"./acp-gateway/mcp-credential-reference-port.js";import{AgentGatewayGrantJwksReadinessAdapter,RuntimeServiceTransportReadinessAdapter,createRuntimeServiceProductionCandidate}from"./runtime/runtime-service-production-candidate.js";import{MessageServiceCronDeliveryHttpAuthority}from"./runtime/message-service-cron-delivery-http-authority.js";import{AgentMappingRegistry}from"./runtime/agent-mapping-registry.js";import{OpenClawRuntimeProcessOwner}from"./runtime/openclaw-runtime-process-owner.js";import{AgentGatewayHttpProjectionTransport,AgentGatewayTemplateOriginReporter,AgentProjectionProduction,OpenClawAgentProjectionSource}from"./runtime/agent-projection-production.js";import{MessageServiceRunOutputChannel}from"./runtime/message-service-run-output-channel.js";import{AssistantMessageCommitRouter}from"./runtime/assistant-message-commit-router.js";import{ToolResultObservationRouter}from"./runtime/tool-result-observation-router.js";import{ModelCallObservationRouter,registerModelCallObservationHooks}from"./runtime/model-call-observability.js";import{registerArouterLatencyRouting}from"./runtime/arouter-latency-routing.js";import{createPeerRunOutputRouterFactory}from"./runtime/run-output-composition.js";const PLUGIN_VERSION=resolvePluginVersion();let agentKeys=null,sharedCanvasState=null,sharedAcpServer=null;const sharedSessionWriteGate=new SessionWriteGate,sharedPromptRunStore=new PromptRunStore;let lastOpenclawConfig;const sharedMcpNativeProjectionNudge=createMcpNativeProjectionNudge({getActiveImRunCount:()=>sharedPromptRunStore.size(),nudge:()=>{sharedAcpServer?.nudgeMcpNativeProjection()}}),sharedImHotPathDeferral=createImHotPathDeferral({getActiveImRunCount:()=>sharedPromptRunStore.size()});sharedPromptRunStore.setOnBecameIdle(()=>{sharedMcpNativeProjectionNudge.flushIfIdle({fingerprint:fingerprintMcpServers(lastOpenclawConfig)}),sharedImHotPathDeferral.notifyIdle()});const sharedAssistantMessageCommitRouter=new AssistantMessageCommitRouter,sharedToolResultObservationRouter=new ToolResultObservationRouter,sharedModelCallObservationRouter=new ModelCallObservationRouter;let sharedSessionMutationCoordinator,sharedRuntimeAuthority;const sharedMSConnectionStatus=new MSConnectionStatusSnapshot,sharedAgentMappings=new AgentMappingRegistry;let sharedGatewayClient,sharedRuntimeOwner,sharedAgentProjection,sharedRuntimeAgentCapabilities,sharedMcpProjection,sharedMcpCredentialVault;const sharedMessageClientOwner=new BeeosClawMessageClientProcessOwner;let sharedActiveMessageConsumer,sharedInboundExecutionJournal;const sharedTerminalManager=new TerminalManager;let _initializing=!1,_pluginRegisteredAt=0,_fullRuntimeRegistered=!1;const cachedCapabilityTools=[];let rawRegisterTool,wrappedRegisterTool;const ensureRegisterToolCache=e=>{rawRegisterTool||(rawRegisterTool=e.registerTool.bind(e),wrappedRegisterTool=e=>(cachedCapabilityTools.push(e),rawRegisterTool(e)),e.registerTool=wrappedRegisterTool)},ED25519_PKCS8_PREFIX=Buffer.from("302e020100300506032b657004220420","hex");function agentKeyPairToIdentity(e){return{publicKeyBase64:()=>Buffer.from(e.publicKey).toString("base64"),sign:async r=>{const{createPrivateKey:t,sign:o}=await import("node:crypto"),n=t({key:32===e.privateKey.length?Buffer.concat([ED25519_PKCS8_PREFIX,Buffer.from(e.privateKey)]):Buffer.from(e.privateKey),format:"der",type:"pkcs8"});return new Uint8Array(o(null,Buffer.from(r),n))}}}let sharedPluginContext=null,unhandledRejectionObserverInstalled=!1;function installUnhandledRejectionObserver(e,r,t){unhandledRejectionObserverInstalled||(unhandledRejectionObserverInstalled=!0,process.on("unhandledRejection",t=>{const o=t instanceof Error?t.message:String(t),n=t instanceof Error?t.stack:void 0;e.logger.error?.(`[beeos-claw] unhandledRejection: ${o}${n?`\n${n}`:""}`),r({component:"plugin",domain:"fatal",name:"unhandled_rejection",severity:"error",summary:o,error:{code:"unhandled_rejection",message:o,...n?{stack:n}:{}}})}),process.on("uncaughtException",t=>{const o=t instanceof Error?t.message:String(t);e.logger.error?.(`[beeos-claw] uncaughtException: ${t instanceof Error?t.stack??t.message:String(t)}`),r({component:"plugin",domain:"fatal",name:"uncaught_exception",severity:"error",summary:o,error:{code:"uncaught_exception",message:o}})}))}function initializePlugin(e){let r,t;try{r=e.runtime?.config?.loadConfig?.()}catch{}try{t=resolveConfigWithMeta({pluginConfig:e.pluginConfig??{},openclawConfig:r})}catch(r){return e.logger.error?.(`[beeos-claw] Failed to resolve config: ${r instanceof Error?r.message:String(r)}`),null}const o=t.config,n=buildThirdPartyChannelSet(r);n.size>0&&e.logger.info?.(`[beeos-claw] Third-party channels detected: ${[...n].join(", ")}`);const a=o.log.enabled?createObsEventWriter({dir:o.log.dir,redaction:{gatewayToken:o.gateway.token},logger:e.logger}):()=>{};a({component:"plugin",domain:"lifecycle",name:"lifecycle.startup",severity:"info",payload:{pluginVersion:PLUGIN_VERSION,pid:process.pid,nodeVersion:process.version,cwd:process.cwd()}}),a({component:"plugin",domain:"config",name:"config.resolved",severity:"info",payload:{config:o,sources:t.sources,openclawConfigPath:t.openclawConfigPath}});for(const r of t.validation)a({component:"plugin",domain:"config",name:"config.validation",severity:r.severity,summary:r.message,error:{code:r.code,message:r.message,nextSteps:r.nextSteps}}),"error"===r.severity?e.logger.error?.(`[beeos-claw] ${r.message}`):e.logger.warn?.(`[beeos-claw] ${r.message}`);if(t.validation.some(e=>"error"===e.severity)&&e.logger.error?.("[beeos-claw] Plugin has configuration errors — some features may not work."),o.keyFile)try{agentKeys=loadAgentKeyPair(o.keyFile);const r=Buffer.from(agentKeys.publicKey).toString("base64");e.logger.info?.(`[beeos-claw] v${PLUGIN_VERSION} registering (publicKey: ${r})`)}catch(r){e.logger.warn?.(`[beeos-claw] Failed to load agent key pair: ${r}`),e.logger.info?.(`[beeos-claw] v${PLUGIN_VERSION} registering (no key pair — signed requests disabled)`)}else e.logger.info?.(`[beeos-claw] v${PLUGIN_VERSION} registering (no keyFile configured)`);return agentKeys&&o.agentGatewayUrl&&(setUploadDeps({gatewayUrl:o.agentGatewayUrl,keys:agentKeys,logger:e.logger}),registerLocalFileExtractor(browserScreenshotExtractor),registerLocalFileExtractor(deliverableFileExtractor),e.logger.info?.("[beeos-claw] Upload deps + local file extractors registered")),lastOpenclawConfig=r,{cfg:o,writeObs:a,thirdPartyChannels:n,openclawConfig:r}}function registerUploadTools(e,r,t){const{cfg:o,writeObs:n}=r;cleanupUploadToolResultCache().catch(r=>{e.logger.warn?.(`[beeos_upload_file] cache cleanup failed on startup: ${String(r)}`)}),e.registerTool({name:BEEOS_UPLOAD_TOOL_NAME,description:BEEOS_UPLOAD_TOOL_DESCRIPTION,parameters:BEEOS_UPLOAD_TOOL_SCHEMA,async execute(r,t,a){e.logger.debug?.(`[beeos_upload_file] toolCallId=${r} params=${JSON.stringify(t)}`);const i=await executeUpload(t,o,agentKeys,a);return e.logger.debug?.(`[beeos_upload_file] toolCallId=${r} ok=${i.ok}`),n({component:"upload-tool",domain:"tool",name:i.ok?"upload.success":"upload.failure",severity:i.ok?"info":"warn",payload:{toolCallId:r,ok:i.ok,fileCount:i.files?.length??0,error:i.error}}),(async t=>{try{await writeUploadToolResult(r,BEEOS_UPLOAD_TOOL_NAME,t),await cleanupUploadToolResultCache()}catch(t){e.logger.warn?.(`[beeos_upload_file] persist cache failed toolCallId=${r}: ${String(t)}`)}return t})(i)}}),e.registerTool({name:BEEOS_DESKTOP_SCREENSHOT_TOOL_NAME,description:BEEOS_DESKTOP_SCREENSHOT_TOOL_DESCRIPTION,parameters:BEEOS_DESKTOP_SCREENSHOT_TOOL_SCHEMA,async execute(r,t,a){e.logger.debug?.(`[beeos_desktop_screenshot] toolCallId=${r} params=${JSON.stringify(t)}`);const i=await executeDesktopScreenshot(t,o,agentKeys,a);return e.logger.debug?.(`[beeos_desktop_screenshot] toolCallId=${r} ok=${i.ok}`),n({component:"desktop-screenshot-tool",domain:"tool",name:i.ok?"desktop_screenshot.success":"desktop_screenshot.failure",severity:i.ok?"info":"warn",payload:{toolCallId:r,ok:i.ok,path:i.path,fileCount:i.files?.length??0,error:i.error}}),(async t=>{try{await writeUploadToolResult(r,BEEOS_DESKTOP_SCREENSHOT_TOOL_NAME,t),await cleanupUploadToolResultCache()}catch(t){e.logger.warn?.(`[beeos_desktop_screenshot] persist cache failed toolCallId=${r}: ${String(t)}`)}return t})(i)}}),e.registerHttpRoute({path:"/beeos/files/stage",auth:"gateway",async handler(e,t){await handleFileStage(e,t,r.cfg)}}),e.registerHttpRoute({path:"/beeos/status",auth:"plugin",handler(e,r){handleStatusRequest(e,r,{publicKey:agentKeys?.publicKey??null,pluginVersion:PLUGIN_VERSION})}}),e.registerTool({name:BEEOS_LIST_FILES_TOOL_NAME,description:BEEOS_LIST_FILES_TOOL_DESCRIPTION,parameters:BEEOS_LIST_FILES_TOOL_SCHEMA,async execute(r,t,a){e.logger.debug?.(`[beeos_list_files] toolCallId=${r} params=${JSON.stringify(t)}`);const i=await executeListFiles(t,o,agentKeys,a);return n({component:"file-list-tool",domain:"tool",name:i.ok?"list_files.success":"list_files.failure",severity:i.ok?"info":"warn",payload:{toolCallId:r,ok:i.ok,fileCount:i.files?.length??0,error:i.error}}),i}}),e.registerTool({name:BEEOS_FETCH_FILE_TOOL_NAME,description:BEEOS_FETCH_FILE_TOOL_DESCRIPTION,parameters:BEEOS_FETCH_FILE_TOOL_SCHEMA,async execute(r,t,a){e.logger.debug?.(`[beeos_fetch_file] toolCallId=${r} params=${JSON.stringify(t)}`);const i=await executeFetchFile(t,o,agentKeys,{signal:a,resolvePath:e.resolvePath});return n({component:"file-fetch-tool",domain:"tool",name:i.ok?"fetch_file.success":"fetch_file.failure",severity:i.ok?"info":"warn",payload:{toolCallId:r,ok:i.ok,path:i.path,size:i.size,error:i.error}}),i}})}function registerConnectorMcpProxy(e,r){e.registerHttpRoute({path:CONNECTOR_MCP_PROXY_PATH,auth:"plugin",async handler(t,o){await handleConnectorMcpRequest(t,o,{agentGatewayUrl:r.cfg.agentGatewayUrl,agentKeys:agentKeys,logger:e.logger})}})}function registerServices(e,r,t){const{cfg:o,writeObs:n,thirdPartyChannels:a}=r;if("function"!=typeof e.on){if(o.messageBus.enabled)throw new Error("openclaw_before_message_write_hook_unavailable")}else registerModelCallObservationHooks((r,t)=>e.on(r,t),sharedModelCallObservationRouter),e.on("before_message_write",(r,t)=>{const o=r.sessionKey??t.sessionKey;if(!o)return void e.logger.debug?.("[before_message_write] ignored assistant write without sessionKey");sharedAssistantMessageCommitRouter.route(o,r.message)||e.logger.debug?.(`[before_message_write] no active IM commit target sessionKey=${o} active_targets=${sharedAssistantMessageCommitRouter.size()}`)}),e.on("after_tool_call",(r,t)=>{const o=t.sessionKey,n=r.toolCallId??t.toolCallId,a=r.toolName||t.toolName;if(!o||!n||!a)return void e.logger.debug?.(`[after_tool_call] ignored structured result without correlation sessionKey=${o??"missing"} toolCallId=${n??"missing"}`);observeToolCallForDelivery(r,o);sharedToolResultObservationRouter.route(o,{toolCallId:n,toolName:a,rawResult:r.result,isError:Boolean(r.error)})||e.logger.debug?.(`[after_tool_call] no active IM observation target sessionKey=${o} active_targets=${sharedToolResultObservationRouter.size()}`)});const i=agentKeys?(e,r)=>agentFetch(e,agentKeys,r):void 0,s=agentKeys?(e,r)=>agentBoundFetch(e,agentKeys,r):void 0,l=agentKeys?Buffer.from(agentKeys.publicKey).toString("base64"):void 0,c=join(process.env.OPENCLAW_HOME||join(homedir(),".openclaw"),"beeos"),g=sharedInboundExecutionJournal??=new FileInboundExecutionJournal(join(c,"inbound-execution-journal.v1.jsonl"));if(sharedMcpProjection??=new OpenClawNativeMcpProjection,!sharedMcpCredentialVault&&agentKeys&&l&&(sharedMcpCredentialVault=new LocalEncryptedCredentialReferenceVault(join(c,"mcp-credential-vault.json"),l,agentKeys.privateKey)),!sharedRuntimeAuthority&&agentKeys&&o.agentGatewayUrl&&i&&s&&(sharedRuntimeAuthority=new RuntimeProductionAuthority({stateDirectory:c,agentGatewayUrl:o.agentGatewayUrl,agentFetch:i,boundAgentFetch:s,keys:agentKeys,logger:e.logger,getMSConnectionStatus:e=>sharedMSConnectionStatus.get(e)})),sharedGatewayClient??=new GatewayClient({url:o.gateway.url,token:o.gateway.token||"",clientId:o.gateway.clientId,openclawCanvasCompat:!!o.openclawCanvasCompat,retry:{baseMs:o.retry.baseMs,maxMs:o.retry.maxMs,maxAttempts:o.retry.maxAttempts}}),sharedRuntimeAgentCapabilities??={canvas:o.canvasEnabled,terminal:!1,webSsh:!1,fileSystem:!0,image:!0,audio:!1,mcp:!0,cron:!0,streaming:!0,pushNotifications:o.messageBus.enabled},!sharedAgentProjection&&sharedRuntimeAuthority&&i&&o.agentGatewayUrl){const r=sharedRuntimeAuthority;sharedAgentProjection=new AgentProjectionProduction({authority:r,stateFile:join(r.stateDirectory,"runtime-agent-projection.json"),journal:r.journal,identities:r.identities,source:new OpenClawAgentProjectionSource(()=>sharedGatewayClient),transport:new AgentGatewayHttpProjectionTransport({agentGatewayUrl:o.agentGatewayUrl,boundAgentFetch:r.boundAgentFetch,getRuntimeLeaseCredential:e=>{const t=r.authorityLease;if(!t||t.instanceId!==e.instanceId||t.leaseId!==e.leaseId||t.runtimeEpoch!==e.runtimeEpoch||t.handlerIdentity!==e.handlerIdentity)throw new Error("agent projection fenced by current runtime lease");return t.runtimeLeaseCredential},onAccepted:r=>{0!==r.size&&sharedAgentMappings.replace(r).catch(r=>{e.logger.warn?.("[agent-projection] mapping publication failed: "+(r instanceof Error?r.message:String(r)))})}}),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:r=>{scheduleProcessRuntimeRestart(r,e.logger)}});const u=sharedSessionMutationCoordinator;if(!sharedAcpServer){const r=sharedRuntimeAuthority&&o.agentGatewayUrl?new AgentGatewayTemplateOriginReporter({agentGatewayUrl:o.agentGatewayUrl,boundAgentFetch:sharedRuntimeAuthority.boundAgentFetch}):void 0;sharedAcpServer=new ACPServer(o,e.logger,u,n,i,l,a,sharedTerminalManager,sharedRuntimeAuthority,void 0,sharedMcpProjection,sharedMcpCredentialVault,s,{gateway:d.client,agentProjection:sharedAgentProjection,markAgentProjectionDirty:()=>sharedAgentProjection?.markDirty(),templateOriginReporter:r,agentMappings:sharedAgentMappings,agentCapabilities:sharedRuntimeAgentCapabilities,createRunOutputRouter:createPeerRunOutputRouterFactory(new MessageServiceRunOutputChannel({getClient:()=>sharedMessageClientOwner.current?.client??null,logger:e.logger,writeObs:n})),runExclusive:(r,t,o)=>sharedImHotPathDeferral.runExclusive(r,t,e.logger,o)})}const m=sharedAcpServer,p=()=>sharedAgentMappings.snapshot()?.values().next().value;t.current=m,sharedMcpNativeProjectionNudge.request({registrationMode:e.registrationMode,fingerprint:fingerprintMcpServers(r.openclawConfig),logger:e.logger});const y=sharedPromptRunStore,{enabled:h,principalId:f,a2aEnabled:w}=o.messageBus;let v;o.agentGatewayUrl&&agentKeys&&(v=createTokenProvider({agentGatewayUrl:o.agentGatewayUrl,identity:agentKeyPairToIdentity(agentKeys)}));const C=process.env.MESSAGE_RUNTIME_DELIVERY_KEY?.trim(),R=f||process.env.BEEOS_PRINCIPAL_ID||"",b=v?sharedMessageClientOwner.configure({tokenProvider:v,identityId:R,...o.messageBus.serviceUrl?{serviceUrl:o.messageBus.serviceUrl}:{},logger:e.logger,...sharedRuntimeAuthority&&C?{runtimeDelivery:{scopedDeliveryKey:C,authority:{currentLease:()=>{const e=sharedRuntimeAuthority?.authorityLease,r=sharedRuntimeAuthority?.currentConsumerBinding();return e&&r?{...e,journalStoreId:r.journalStoreId,journalGeneration:r.journalGeneration}:null}}}}:{}}):void 0,_=sharedRuntimeAuthority,S=o.messageBus.serviceUrl;if(b&&_&&S&&C){const e=b.nodeTransport;m.setCronConversationDelivery({deliver:async r=>{const t=_.authorityLease;if(!t)throw new Error("current runtime authority lease unavailable for cron delivery");const o=new MessageServiceCronDeliveryHttpAuthority({messageServiceUrl:S,fetchAuthorized:async(r,o)=>await e.fetchRuntimeChannel(r,{runtimeLeaseCredential:t.runtimeLeaseCredential},o)});return await o.deliver({...r,runtimeLeaseCredential:t.runtimeLeaseCredential})}})}else m.setCronConversationDelivery(void 0);const E=sharedRuntimeAuthority?.journal??new FileRuntimeCommandJournal(join(c,"runtime-command-journal.json"),void 0,void 0,{onMaintenanceError:r=>e.logger.error?.(`[runtime-journal] event=checkpoint_failed trigger=threshold error=${r instanceof Error?r.message:String(r)}`)}),O=d.createRegistrationToken();e.registerService({id:"beeos-acp",async start(){const r=Date.now(),t=_pluginRegisteredAt?r-_pluginRegisteredAt:-1;e.logger.info?.(`[beeos-claw] beeos-acp service start() begin, sincePluginRegister=${t}ms, bridge.url=${o.bridge.url||"(empty)"}`);const n=await createRuntimeServiceProductionCandidate({...o.agentGatewayUrl&&i?{authorizationAdapter:new AgentGatewayGrantJwksReadinessAdapter({agentGatewayUrl:o.agentGatewayUrl})}:{},...b&&o.agentGatewayUrl&&s&&C?{transportAdapter:new RuntimeServiceTransportReadinessAdapter({agentGatewayUrl:o.agentGatewayUrl,messageService:b.nodeTransport,messageClient:b.client,configuredServiceUrl:o.messageBus.serviceUrl,boundAgentFetch:s,scopedDeliveryKey:C})}:{},commandJournal:E,poisonJournal:new FileRuntimePoisonDeliveryJournal(join(c,"runtime-poison-deliveries.json")),onConsumerDeliveryError:r=>{const t=r instanceof Error?`${r.name}: ${r.message}${"status"in r?` status=${r.status}`:""}${"code"in r?` code=${r.code}`:""}\n${r.stack??""}`:String(r);e.logger.error?.(`[runtime-service] delivery worker failed before acknowledgement; delivery retained for retry: ${t}`)},onConsumerLeaseStarvation:({starvedForMs:r,recovered:t})=>{const o=Math.round(r/1e3);t?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:r=>e.logger.error?.(`[runtime-service] request failed operationId=${r.operationId} method=${r.method}: ${r.error instanceof Error?r.error.stack??r.error.message:String(r.error)}`)});if(sharedRuntimeAuthority?.setRuntimeServiceMethods("ready"===n.status?n.serviceMethods:[]),sharedRuntimeAuthority?.setRuntimeResourceBindingContractRevision("ready"===n.status?n.resourceBindingContractRevision:void 0),m.setRuntimeRpcConsumerFactory("ready"===n.status?n.factory:void 0),"unavailable"===n.status?e.logger.warn?.("[runtime-service] capability fenced: "+n.blockers.map(e=>e.id).join(",")):n.capabilityBlockers.length&&e.logger.warn?.("[runtime-service] method capability fenced: "+n.capabilityBlockers.map(e=>e.method).join(",")),!o.bridge.url&&o.agentGatewayUrl&&i){const r=Date.now(),t=await discoverBridgeFromGW(o.agentGatewayUrl,i);if(e.logger.info?.(`[beeos-claw] bridge discovery took ${Date.now()-r}ms result=${t?.url??"null"}`),t)o.bridge.url=t.url,writeDiscoveryCache({bridge:{url:t.url,discoveredAt:Date.now()}}),e.logger.info?.(`[beeos-claw] Bridge URL discovered from Agent Gateway: ${t.url}`);else{const r=readDiscoveryCache();r?.bridge?.url?(o.bridge.url=r.bridge.url,e.logger.info?.(`[beeos-claw] Bridge URL from cache (GW unreachable): ${r.bridge.url}`)):e.logger.debug?.("[beeos-claw] Bridge URL not available (no GW, no cache) — bridge disabled")}}await d.acquire(O);try{await m.start(o.bridge.url||void 0)}catch(e){throw await d.release(O),e}e.logger.info?.(`[beeos-claw] ACP Server started (total service start: ${Date.now()-r}ms)`)},async stop(){try{await d.release(O)}finally{await m.stop()}await E.checkpoint(),e.logger.info?.("[runtime-service] command journal checkpoint completed"),e.logger.info?.("[beeos-claw] ACP Server stopped")}});if(h&&void 0!==v&&v){const r=sharedMessageClientOwner.createRegistrationToken(),t=async(e,r)=>{const t=await v(e);if(!t.identityId||t.identityId!==e)throw new Error("Message SDK token identity did not match the current runtime authority");return{principalId:e,runtimeEpoch:r}},a=(e,r,t)=>({getLocalAgentId:()=>r,resolveLocalAgentId:e=>e===t?r:void 0,getDefaultPlatformAgent:()=>({instanceId:e.principalId}),ensureImInboundSession:(e,r,t,o)=>{m.ensureImInboundSession(e,r,t,o)},chatAbort:(e,r)=>m.chatAbort(e,r)}),i=sharedRuntimeAuthority?new RuntimeAuthorityMessageBusLifecycle({authority:sharedRuntimeAuthority,logger:e.logger,bus:{connect:async i=>{await sharedMessageClientOwner.acquire(r,async r=>{const s=await t(i.instanceId,i.runtimeEpoch),l=r.client,c={leaseId:i.leaseId,runtimeEpoch:i.runtimeEpoch};sharedMSConnectionStatus.bind(c);const d=new Map,p=new MessageProjectionInboundConsumer({client:l,bindings:()=>{const r=sharedRuntimeAuthority?.authorityLease;if(!r||r.instanceId!==s.principalId||r.runtimeEpoch!==s.runtimeEpoch)return[];const t=sharedAgentMappings.snapshot();return t?[...t.entries()].flatMap(([t,i])=>{if(!i.id||i.instanceId!==s.principalId)return[];let c=d.get(i.id);return c&&c.localAgentId===t||(c={localAgentId:t,inbound:createImInboundHandler({runtime:e.runtime,pluginConfig:e.pluginConfig,client:l,localServer:a(s,t,i.id),promptRunStore:y,waitIfExclusive:(r,t)=>sharedImHotPathDeferral.waitIfExclusive(r,e.logger,t).then(()=>{}),assistantMessageCommitRouter:sharedAssistantMessageCommitRouter,toolResultObservationRouter:sharedToolResultObservationRouter,modelCallObservationRouter:sharedModelCallObservationRouter,sessionMutationCoordinator:u,logger:e.logger,writeObs:n,principalIdOverride:s.principalId,activeRunAuthority:m.getActiveRunAuthority(),...o.agentGatewayUrl&&agentKeys?{completeA2ATask:(r,t,n)=>completeA2ATask({agentGatewayUrl:o.agentGatewayUrl,agentKeys:agentKeys,logger:e.logger},r,t,n)}:{}})},d.set(i.id,c)),[{principalId:s.principalId,targetAgentId:i.id,runtimeEpoch:r.runtimeEpoch,inbound:c.inbound}]}):[]},executionJournal:g,logger:e.logger}),h=sharedAgentMappings.subscribe(()=>{sharedActiveMessageConsumer===p&&p.refresh()}),f=sharedRuntimeAuthority?.subscribeAuthority(()=>{sharedActiveMessageConsumer===p&&p.refresh()})??(()=>{}),w=new MessageClientTerminalStateRecovery({logger:e.logger,isCurrent:()=>{if(sharedActiveMessageConsumer!==p)return!1;const e=sharedRuntimeAuthority?.authorityLease;return null!=e&&e.instanceId===s.principalId&&e.runtimeEpoch===s.runtimeEpoch&&e.leaseId===i.leaseId&&e.handlerIdentity===i.handlerIdentity},reconnect:async()=>{if("failed"===l.getSnapshot().connection){await l.connect();try{await m.rebindRuntimeDelivery()}catch(r){e.logger.warn?.(`[beeos-claw] runtime Service delivery rebind failed after MessageClient recovery: ${r instanceof Error?r.message:String(r)}`)}}}});let v=l.getSnapshot().connection;sharedMSConnectionStatus.setIfCurrent(c,v);const C=l.subscribe(()=>{const r=l.getSnapshot();r.connection!==v&&(v=r.connection,sharedMSConnectionStatus.setIfCurrent(c,r.connection),"connected"===r.connection?(w.notifyConnected(),e.logger.info?.(`[beeos-claw] connection transport=message_service_realtime state=connected localAgentId=${m.getLocalAgentId()} principalId=${s.principalId}`)):"failed"===r.connection?(e.logger.warn?.(`[beeos-claw] connection transport=message_service_realtime state=failed principalId=${s.principalId} recoveryError=${r.recoveryError??"unknown"}`),w.notifyFailed()):"disconnected"!==r.connection&&"reconnecting"!==r.connection||e.logger.warn?.(`[beeos-claw] connection transport=message_service_realtime state=${r.connection} principalId=${s.principalId}; SDK owns reconnect/recovery`))});sharedActiveMessageConsumer=p,p.start();try{await l.connect()}catch(e){throw sharedActiveMessageConsumer===p&&(sharedActiveMessageConsumer=void 0),sharedMSConnectionStatus.resetIfCurrent(c),w.dispose(),await p.stop(),h(),f(),C(),await l.disconnect().catch(()=>{}),e}try{await m.rebindRuntimeDelivery()}catch(r){e.logger.warn?.(`[beeos-claw] runtime Service delivery rebind failed after MessageClient connect: ${r instanceof Error?r.message:String(r)}`)}return async()=>{sharedActiveMessageConsumer===p&&(sharedActiveMessageConsumer=void 0),sharedMSConnectionStatus.resetIfCurrent(c),w.dispose(),await p.stop(),f(),C(),await l.disconnect(),e.logger.info?.("[beeos-claw] MessageClient disconnected")}})},disconnect:async()=>{await sharedMessageClientOwner.release(r)}}}):void 0;e.registerService({id:"beeos-message-bus",async start(){i?i.start():e.logger.warn?.("[beeos-claw] Message bus requires runtime authority; waiting is unavailable without Agent Gateway runtime identity")},async stop(){await(i?.stop())}})}else h?e.logger.warn?.("[beeos-claw] Message bus enabled but Agent Gateway token provider is unavailable — disabled"):e.logger.info?.(`[beeos-claw] Message bus disabled (enabled=${h})`);if(!1!==w&&o.agentGatewayUrl){const r=e=>m.getGatewaySessionKeyForToolCall(e),t=e=>m.getMentionsForToolCall(e),n=(e,r)=>{m.broadcastNotification(e,r)},a=new TaskHandleRegistry;registerA2ATools(e,{agentGatewayUrl:o.agentGatewayUrl,agentKeys:agentKeys,logger:e.logger},a,r,t,n,()=>p()?.instanceId),e.logger.info?.("[beeos-claw] A2A tools registered (beeos_discover_agents, beeos_call_agent) via REST")}if(o.canvasEnabled){sharedCanvasState||(sharedCanvasState=new CanvasStateManager);const r=sharedCanvasState;if(o.agentGatewayUrl&&agentKeys){let t;r.setLogger(e.logger);{const e=o.agentGatewayUrl,r=agentKeys;t=async()=>{const t=await agentFetch(`${e}/api/v1/canvas/token`,r,{method:"POST"});if(!t.ok){const e=await t.text().catch(()=>"");throw new Error(`Canvas token returned ${t.status}: ${e}`)}return t.json()}}const n=n=>{const a=new CanvasRelayClient({canvasRelayUrl:o.canvasRelayUrl,agentKeys:agentKeys,agentId:n.id,displayName:n.displayName,avatarUrl:n.avatarUrl,tokenProvider:t,onConnected:()=>m.flushPendingCanvasLinks(),onReconnect:()=>m.onRelayReconnected(),logger:e.logger});r.setRelayClient(a),r.setAgentId(n.id),m.wireRelayActionHandler(),a.connect(),e.logger.info?.(`[beeos-claw] Canvas Relay multiplexed client connected (agentId=${n.id}, name=${n.name??"none"})`)};e.registerService(createCanvasRelayService({getAgent:p,onceAgentReady:e=>sharedAgentMappings.onceReady(e),initRelay:n,stopRelay:()=>r.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 t={stateManager:r,logger:e.logger,writeObs:e=>n(e),getActiveSessionId:e=>m.getSessionIdForToolCall(e),getPlatformAgentId:e=>m.getPlatformAgentIdForToolCall(e),getGatewaySessionKey:e=>m.getGatewaySessionKeyForToolCall(e),waitForAgentSync:()=>sharedAgentMappings.waitForSynchronization()},i=new FormSchemaRegistry,s=buildCronSchemas(a);i.register(s.create),i.register(s.update);const l=new ResourceDescriptorRegistry;l.register(CRON_RESOURCE);const c=e=>async(r,t,o)=>(m.preMapToolCallSession(r),e(r,t));e.registerTool({name:RENDER_UI_TOOL_NAME,description:RENDER_UI_TOOL_DESCRIPTION,parameters:RENDER_UI_TOOL_SCHEMA,execute:c(createRenderUiHandler(t))}),e.registerTool({name:CLOSE_UI_TOOL_NAME,description:CLOSE_UI_TOOL_DESCRIPTION,parameters:CLOSE_UI_TOOL_SCHEMA,execute:c(createCloseUiHandler(t))}),m.setCanvasState(r,{schemaRegistry:i,descriptorRegistry:l});const g=m.getFormBuilder();g&&e.registerTool({name:RENDER_FORM_TOOL_NAME,description:buildRenderFormDescription(i),parameters:RENDER_FORM_TOOL_SCHEMA,execute:c(createRenderFormHandler({registry:i,builder:g,logger:e.logger}))});const d=m.getResourceRenderer();if(d){generateActionTools(l,e,async(e,r)=>m.gatewayRequest(e,r),d,e.logger)}const u=buildToolRoutingHint(BEEOS_FILE_RESOURCE_ROUTING_RULE,BEEOS_DESKTOP_SCREENSHOT_TOOL_NAME),y=new Set([RENDER_UI_TOOL_NAME,CLOSE_UI_TOOL_NAME,RENDER_FORM_TOOL_NAME]),h=256,f=new Map;if("function"==typeof e.on){try{e.on("before_prompt_build",(e,r)=>{if(r?.sessionKey){if(f.size>=h&&!f.has(r.sessionKey)){const e=f.keys().next().value;void 0!==e&&f.delete(e)}f.set(r.sessionKey,r.channelId),rememberDeliveryWorkspace(r.sessionKey,r.workspaceDir)}const t=isThirdPartyChannel(a,{sessionKey:r?.sessionKey,channelId:r?.channelId});return r?.sessionKey||t?{appendSystemContext:buildAppendSystemContext({thirdParty:t,canvasSystemPrompt:`${CANVAS_PRESENTATION_GATE}\n\n${CANVAS_SYSTEM_PROMPT}`,toolRoutingHint:u})}:{appendSystemContext:u}}),e.logger.debug?.("[beeos-claw] Canvas system prompt hook registered: before_prompt_build")}catch{e.logger.debug?.("[beeos-claw] before_prompt_build hook not supported")}try{e.on("before_tool_call",(e,r)=>{if(r?.sessionKey&&m.setPendingToolCallSessionKey(r.sessionKey),!y.has(e.toolName))return;const t=r?.sessionKey?f.get(r.sessionKey):void 0,o=isThirdPartyChannel(a,{sessionKey:r?.sessionKey,channelId:t}),n=canvasToolBlockReason({thirdParty:o,hasSession:Boolean(r?.sessionKey)});return n?{block:!0,blockReason:n}:void 0}),e.logger.debug?.("[beeos-claw] Canvas tool guard registered: before_tool_call")}catch{e.logger.debug?.("[beeos-claw] before_tool_call hook not supported")}}e.logger.info?.("[beeos-claw] Canvas tools registered (render_ui, close_ui, render_form + auto-action tools)")}else e.logger.info?.("[beeos-claw] Canvas disabled by config (canvasEnabled=false)");if(o.bridge.shell.enabled&&!0===o.legacyTerminalWsEnabled){const r=createTerminalWebSocketService({config:{port:o.terminalWsPort,token:agentKeys?Buffer.from(agentKeys.publicKey).toString("base64"):"",allowedOrigins:o.terminalWsAllowedOrigins},terminalMgr:sharedTerminalManager,logger:e.logger,writeObs:n});e.registerService({id:"beeos-terminal-ws",start(){r.start(),e.logger.info?.(`[beeos-claw] Terminal WS started on port ${o.terminalWsPort}`)},stop(){r.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(r.openclawConfig){const t=mirrorOpenClawConfig(r.openclawConfig);t.copied?e.logger.info?.(`[beeos-claw] mirrored openclaw config to ${t.destinationPath}`):e.logger.debug?.(`[beeos-claw] openclaw config mirror skipped: ${t.reason}`)}}const plugin={id:"beeos-claw",name:"beeos-claw",description:"BeeOS platform bridge plugin for OpenClaw. Provides file upload/download, ACP bridge, and observability.",version:PLUGIN_VERSION,register(e){if(_initializing)return;const r=planPluginRegistration(e.registrationMode,_fullRuntimeRegistered);if("skip"!==r){_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=(r,t)=>{registerUploadTools(e,r,t),registerConnectorMcpProxy(e,r),registerArouterLatencyRouting(e),registerArouterMediaProviders(e)};if("capabilities"===r&&sharedPluginContext){if(cachedCapabilityTools.length>0){const r=resolveCapabilityToolReplay({currentRegisterTool:e.registerTool,wrappedRegisterTool:wrappedRegisterTool,originalRegisterTool:rawRegisterTool}),t=replayCachedPluginTools(t=>r.call(e,t),cachedCapabilityTools);return registerArouterLatencyRouting(e),registerArouterMediaProviders(e),void e.logger.info?.(`[beeos-claw] capability re-register mode=${e.registrationMode} tools_only count=${t}`)}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 o=initializePlugin(e);if(!o)return;const n={current:null};installUnhandledRejectionObserver(e,o.writeObs,n),ensureRegisterToolCache(e),cachedCapabilityTools.length=0,t(o,n),agentKeys&&o.cfg.agentGatewayUrl&&(e.on?e.on("before_agent_finalize",fileDeliveryFinalizeDecision):e.logger.warn?.("[file-delivery] OpenClaw plugin hooks unavailable; finalization check disabled")),registerServices(e,o,n),sharedPluginContext=o,marksFullRuntimeRegistered(e.registrationMode,r)&&(_fullRuntimeRegistered=!0),_pluginRegisteredAt=Date.now(),e.logger.info?.("[beeos-claw] Plugin registered successfully")}finally{_initializing=!1}}else e.logger.info?.(`[beeos-claw] skip re-register mode=${e.registrationMode??"full"} already_registered`)}};export default plugin;
|
|
1
|
+
import{fileDeliveryFinalizeDecision}from"./upload/finalize-hook.js";import{resolveConfigWithMeta,mirrorOpenClawConfig}from"./config.js";import{hasRuntimeReplyApi}from"./runtime/reply-router.js";import{PromptRunStore}from"./runtime/prompt-run-store.js";import{createMcpNativeProjectionNudge,fingerprintMcpServers}from"./acp-gateway/mcp-native-projection-nudge.js";import{createImHotPathDeferral}from"./runtime/im-hot-path-deferral.js";import{SessionWriteGate}from"./acp-gateway/session-write-gate.js";import{OpenClawSessionWriteQuiescence}from"./runtime/openclaw-session-write-quiescence.js";import{SessionMutationCoordinator}from"./runtime/session-mutation-coordinator.js";import{scheduleProcessRuntimeRestart}from"./runtime/runtime-restart-scheduler.js";import{createImInboundHandler}from"./im-inbound-handler.js";import{AutomationTurnPolicy}from"./automation/turn-policy.js";import{registerAutomationTools}from"./automation/tools.js";import{extractBeeosConversationIdFromSessionKey}from"./runtime/openclaw-session-key-resolution.js";import{createTokenProvider}from"@beeos-ai/message-sdk/auth";import{NodeRuntimeDeliveryPort}from"@beeos-ai/cloud-agent-sdk";import{BeeosClawMessageClientProcessOwner}from"./message-client-composition.js";import{MessageProjectionInboundConsumer}from"./message-projection-inbound-consumer.js";import{MessageClientTerminalStateRecovery}from"./runtime/message-client-terminal-state-recovery.js";import{RuntimeAuthorityMessageBusLifecycle}from"./runtime/runtime-authority-message-bus-lifecycle.js";import{MSConnectionStatusSnapshot}from"./runtime/ms-connection-status.js";import{FileInboundExecutionJournal}from"./inbound-execution-journal.js";import{completeA2ATask,registerA2ATools,TaskHandleRegistry}from"./a2a/tools.js";import{BEEOS_UPLOAD_TOOL_NAME,BEEOS_UPLOAD_TOOL_SCHEMA,BEEOS_UPLOAD_TOOL_DESCRIPTION,executeUpload}from"./upload/tool.js";import{BEEOS_DESKTOP_SCREENSHOT_TOOL_NAME,BEEOS_DESKTOP_SCREENSHOT_TOOL_SCHEMA,BEEOS_DESKTOP_SCREENSHOT_TOOL_DESCRIPTION,executeDesktopScreenshot}from"./upload/desktop-screenshot-tool.js";import{BEEOS_LIST_FILES_TOOL_NAME,BEEOS_LIST_FILES_TOOL_SCHEMA,BEEOS_LIST_FILES_TOOL_DESCRIPTION,executeListFiles}from"./files/list-tool.js";import{BEEOS_FETCH_FILE_TOOL_NAME,BEEOS_FETCH_FILE_TOOL_SCHEMA,BEEOS_FETCH_FILE_TOOL_DESCRIPTION,executeFetchFile}from"./files/fetch-tool.js";import{writeUploadToolResult,cleanupUploadToolResultCache}from"./upload/result-cache.js";import{handleFileStage}from"./upload/file-stage.js";import{handleStatusRequest}from"./status.js";import{ACPServer}from"./acp-server.js";import{GatewayClient}from"./gateway-client.js";import{createObsEventWriter}from"./observability.js";import{resolvePluginVersion}from"./plugin-version.js";import{createTerminalWebSocketService}from"./terminal/websocket.js";import{TerminalManager}from"./terminal/session-manager.js";import{loadAgentKeyPair,agentBoundFetch,agentFetch}from"./agent-auth.js";import{readDiscoveryCache,writeDiscoveryCache}from"./discovery-cache.js";import{discoverBridgeFromGW}from"./bridge-discovery.js";import{setUploadDeps}from"./upload/upload-file.js";import{registerLocalFileExtractor}from"./upload/local-file-extractors.js";import{browserScreenshotExtractor}from"./browser/screenshot-extractor.js";import{deliverableFileExtractor}from"./upload/deliverable-extractor.js";import{observeToolCallForDelivery,rememberDeliveryWorkspace}from"./upload/delivery-observe.js";import{BEEOS_FILE_RESOURCE_ROUTING_RULE}from"./files/beeos-file-uri.js";import{marksFullRuntimeRegistered,planPluginRegistration,replayCachedPluginTools,resolveCapabilityToolReplay}from"./plugin-registration-plan.js";import{buildAppendSystemContext,buildToolRoutingHint,canvasToolBlockReason}from"./prompt-system-context.js";import{CONNECTOR_MCP_PROXY_PATH,handleConnectorMcpRequest}from"./mcp-connector-proxy.js";import{CanvasStateManager}from"./canvas/state.js";import{CanvasRelayClient}from"./canvas/relay-client.js";import{createCanvasRelayService}from"./canvas/relay-service.js";import{RENDER_UI_TOOL_NAME,RENDER_UI_TOOL_DESCRIPTION,RENDER_UI_TOOL_SCHEMA,CLOSE_UI_TOOL_NAME,CLOSE_UI_TOOL_DESCRIPTION,CLOSE_UI_TOOL_SCHEMA,createRenderUiHandler,createCloseUiHandler,CANVAS_PRESENTATION_GATE,CANVAS_SYSTEM_PROMPT}from"./canvas/tools.js";import{FormSchemaRegistry}from"./surface/form-schema-registry.js";import{buildCronSchemas}from"./surface/cron-schemas.js";import{RENDER_FORM_TOOL_NAME,RENDER_FORM_TOOL_SCHEMA,buildRenderFormDescription,createRenderFormHandler}from"./surface/render-form-tool.js";import{ResourceDescriptorRegistry}from"./surface/resource-descriptor-registry.js";import{CRON_RESOURCE}from"./surface/cron-resource.js";import{generateActionTools}from"./surface/action-tool-generator.js";import{buildThirdPartyChannelSet,isThirdPartyChannel}from"./channel-detection.js";import{registerArouterMediaProviders}from"./media/index.js";import{homedir}from"node:os";import{join}from"node:path";import{RuntimeProductionAuthority,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{MessageServiceCronDeliveryHttpAuthority}from"./runtime/message-service-cron-delivery-http-authority.js";import{AgentMappingRegistry}from"./runtime/agent-mapping-registry.js";import{OpenClawRuntimeProcessOwner}from"./runtime/openclaw-runtime-process-owner.js";import{AgentGatewayHttpProjectionTransport,AgentGatewayTemplateOriginReporter,AgentProjectionProduction,OpenClawAgentProjectionSource}from"./runtime/agent-projection-production.js";import{MessageServiceRunOutputChannel}from"./runtime/message-service-run-output-channel.js";import{AssistantMessageCommitRouter}from"./runtime/assistant-message-commit-router.js";import{ToolResultObservationRouter}from"./runtime/tool-result-observation-router.js";import{ModelCallObservationRouter,registerModelCallObservationHooks}from"./runtime/model-call-observability.js";import{registerArouterLatencyRouting}from"./runtime/arouter-latency-routing.js";import{createPeerRunOutputRouterFactory}from"./runtime/run-output-composition.js";const PLUGIN_VERSION=resolvePluginVersion();let agentKeys=null,sharedCanvasState=null,sharedAcpServer=null;const sharedSessionWriteGate=new SessionWriteGate,sharedPromptRunStore=new PromptRunStore,sharedAutomationTurnPolicy=new AutomationTurnPolicy;let lastOpenclawConfig;const sharedMcpNativeProjectionNudge=createMcpNativeProjectionNudge({getActiveImRunCount:()=>sharedPromptRunStore.size(),nudge:()=>{sharedAcpServer?.nudgeMcpNativeProjection()}}),sharedImHotPathDeferral=createImHotPathDeferral({getActiveImRunCount:()=>sharedPromptRunStore.size()});sharedPromptRunStore.setOnBecameIdle(()=>{sharedMcpNativeProjectionNudge.flushIfIdle({fingerprint:fingerprintMcpServers(lastOpenclawConfig)}),sharedImHotPathDeferral.notifyIdle()});const sharedAssistantMessageCommitRouter=new AssistantMessageCommitRouter,sharedToolResultObservationRouter=new ToolResultObservationRouter,sharedModelCallObservationRouter=new ModelCallObservationRouter;let sharedSessionMutationCoordinator,sharedRuntimeAuthority;const sharedMSConnectionStatus=new MSConnectionStatusSnapshot,sharedAgentMappings=new AgentMappingRegistry;let sharedGatewayClient,sharedRuntimeOwner,sharedAgentProjection,sharedRuntimeAgentCapabilities,sharedMcpProjection,sharedMcpCredentialVault;const sharedMessageClientOwner=new BeeosClawMessageClientProcessOwner;let sharedActiveMessageConsumer,sharedInboundExecutionJournal;const sharedTerminalManager=new TerminalManager;let _initializing=!1,_pluginRegisteredAt=0,_fullRuntimeRegistered=!1;const cachedCapabilityTools=[];let rawRegisterTool,wrappedRegisterTool;const ensureRegisterToolCache=e=>{rawRegisterTool||(rawRegisterTool=e.registerTool.bind(e),wrappedRegisterTool=e=>(cachedCapabilityTools.push(e),rawRegisterTool(e)),e.registerTool=wrappedRegisterTool)},ED25519_PKCS8_PREFIX=Buffer.from("302e020100300506032b657004220420","hex");function agentKeyPairToIdentity(e){return{publicKeyBase64:()=>Buffer.from(e.publicKey).toString("base64"),sign:async t=>{const{createPrivateKey:r,sign:o}=await import("node:crypto"),n=r({key:32===e.privateKey.length?Buffer.concat([ED25519_PKCS8_PREFIX,Buffer.from(e.privateKey)]):Buffer.from(e.privateKey),format:"der",type:"pkcs8"});return new Uint8Array(o(null,Buffer.from(t),n))}}}let sharedPluginContext=null,unhandledRejectionObserverInstalled=!1;function installUnhandledRejectionObserver(e,t,r){unhandledRejectionObserverInstalled||(unhandledRejectionObserverInstalled=!0,process.on("unhandledRejection",r=>{const o=r instanceof Error?r.message:String(r),n=r instanceof Error?r.stack:void 0;e.logger.error?.(`[beeos-claw] unhandledRejection: ${o}${n?`\n${n}`:""}`),t({component:"plugin",domain:"fatal",name:"unhandled_rejection",severity:"error",summary:o,error:{code:"unhandled_rejection",message:o,...n?{stack:n}:{}}})}),process.on("uncaughtException",r=>{const o=r instanceof Error?r.message:String(r);e.logger.error?.(`[beeos-claw] uncaughtException: ${r instanceof Error?r.stack??r.message:String(r)}`),t({component:"plugin",domain:"fatal",name:"uncaught_exception",severity:"error",summary:o,error:{code:"uncaught_exception",message:o}})}))}function initializePlugin(e){let t,r;try{t=e.runtime?.config?.loadConfig?.()}catch{}try{r=resolveConfigWithMeta({pluginConfig:e.pluginConfig??{},openclawConfig:t})}catch(t){return e.logger.error?.(`[beeos-claw] Failed to resolve config: ${t instanceof Error?t.message:String(t)}`),null}const o=r.config,n=buildThirdPartyChannelSet(t);n.size>0&&e.logger.info?.(`[beeos-claw] Third-party channels detected: ${[...n].join(", ")}`);const a=o.log.enabled?createObsEventWriter({dir:o.log.dir,redaction:{gatewayToken:o.gateway.token},logger:e.logger}):()=>{};a({component:"plugin",domain:"lifecycle",name:"lifecycle.startup",severity:"info",payload:{pluginVersion:PLUGIN_VERSION,pid:process.pid,nodeVersion:process.version,cwd:process.cwd()}}),a({component:"plugin",domain:"config",name:"config.resolved",severity:"info",payload:{config:o,sources:r.sources,openclawConfigPath:r.openclawConfigPath}});for(const t of r.validation)a({component:"plugin",domain:"config",name:"config.validation",severity:t.severity,summary:t.message,error:{code:t.code,message:t.message,nextSteps:t.nextSteps}}),"error"===t.severity?e.logger.error?.(`[beeos-claw] ${t.message}`):e.logger.warn?.(`[beeos-claw] ${t.message}`);if(r.validation.some(e=>"error"===e.severity)&&e.logger.error?.("[beeos-claw] Plugin has configuration errors — some features may not work."),o.keyFile)try{agentKeys=loadAgentKeyPair(o.keyFile);const t=Buffer.from(agentKeys.publicKey).toString("base64");e.logger.info?.(`[beeos-claw] v${PLUGIN_VERSION} registering (publicKey: ${t})`)}catch(t){e.logger.warn?.(`[beeos-claw] Failed to load agent key pair: ${t}`),e.logger.info?.(`[beeos-claw] v${PLUGIN_VERSION} registering (no key pair — signed requests disabled)`)}else e.logger.info?.(`[beeos-claw] v${PLUGIN_VERSION} registering (no keyFile configured)`);return agentKeys&&o.agentGatewayUrl&&(setUploadDeps({gatewayUrl:o.agentGatewayUrl,keys:agentKeys,logger:e.logger}),registerLocalFileExtractor(browserScreenshotExtractor),registerLocalFileExtractor(deliverableFileExtractor),e.logger.info?.("[beeos-claw] Upload deps + local file extractors registered")),lastOpenclawConfig=t,{cfg:o,writeObs:a,thirdPartyChannels:n,openclawConfig:t}}function registerUploadTools(e,t,r){const{cfg:o,writeObs:n}=t;cleanupUploadToolResultCache().catch(t=>{e.logger.warn?.(`[beeos_upload_file] cache cleanup failed on startup: ${String(t)}`)}),e.registerTool({name:BEEOS_UPLOAD_TOOL_NAME,description:BEEOS_UPLOAD_TOOL_DESCRIPTION,parameters:BEEOS_UPLOAD_TOOL_SCHEMA,async execute(t,r,a){e.logger.debug?.(`[beeos_upload_file] toolCallId=${t} params=${JSON.stringify(r)}`);const i=await executeUpload(r,o,agentKeys,a);return e.logger.debug?.(`[beeos_upload_file] toolCallId=${t} ok=${i.ok}`),n({component:"upload-tool",domain:"tool",name:i.ok?"upload.success":"upload.failure",severity:i.ok?"info":"warn",payload:{toolCallId:t,ok:i.ok,fileCount:i.files?.length??0,error:i.error}}),(async r=>{try{await writeUploadToolResult(t,BEEOS_UPLOAD_TOOL_NAME,r),await cleanupUploadToolResultCache()}catch(r){e.logger.warn?.(`[beeos_upload_file] persist cache failed toolCallId=${t}: ${String(r)}`)}return r})(i)}}),e.registerTool({name:BEEOS_DESKTOP_SCREENSHOT_TOOL_NAME,description:BEEOS_DESKTOP_SCREENSHOT_TOOL_DESCRIPTION,parameters:BEEOS_DESKTOP_SCREENSHOT_TOOL_SCHEMA,async execute(t,r,a){e.logger.debug?.(`[beeos_desktop_screenshot] toolCallId=${t} params=${JSON.stringify(r)}`);const i=await executeDesktopScreenshot(r,o,agentKeys,a);return e.logger.debug?.(`[beeos_desktop_screenshot] toolCallId=${t} ok=${i.ok}`),n({component:"desktop-screenshot-tool",domain:"tool",name:i.ok?"desktop_screenshot.success":"desktop_screenshot.failure",severity:i.ok?"info":"warn",payload:{toolCallId:t,ok:i.ok,path:i.path,fileCount:i.files?.length??0,error:i.error}}),(async r=>{try{await writeUploadToolResult(t,BEEOS_DESKTOP_SCREENSHOT_TOOL_NAME,r),await cleanupUploadToolResultCache()}catch(r){e.logger.warn?.(`[beeos_desktop_screenshot] persist cache failed toolCallId=${t}: ${String(r)}`)}return r})(i)}}),e.registerHttpRoute({path:"/beeos/files/stage",auth:"gateway",async handler(e,r){await handleFileStage(e,r,t.cfg)}}),e.registerHttpRoute({path:"/beeos/status",auth:"plugin",handler(e,t){handleStatusRequest(e,t,{publicKey:agentKeys?.publicKey??null,pluginVersion:PLUGIN_VERSION})}}),e.registerTool({name:BEEOS_LIST_FILES_TOOL_NAME,description:BEEOS_LIST_FILES_TOOL_DESCRIPTION,parameters:BEEOS_LIST_FILES_TOOL_SCHEMA,async execute(t,r,a){e.logger.debug?.(`[beeos_list_files] toolCallId=${t} params=${JSON.stringify(r)}`);const i=await executeListFiles(r,o,agentKeys,a);return n({component:"file-list-tool",domain:"tool",name:i.ok?"list_files.success":"list_files.failure",severity:i.ok?"info":"warn",payload:{toolCallId:t,ok:i.ok,fileCount:i.files?.length??0,error:i.error}}),i}}),e.registerTool({name:BEEOS_FETCH_FILE_TOOL_NAME,description:BEEOS_FETCH_FILE_TOOL_DESCRIPTION,parameters:BEEOS_FETCH_FILE_TOOL_SCHEMA,async execute(t,r,a){e.logger.debug?.(`[beeos_fetch_file] toolCallId=${t} params=${JSON.stringify(r)}`);const i=await executeFetchFile(r,o,agentKeys,{signal:a,resolvePath:e.resolvePath});return n({component:"file-fetch-tool",domain:"tool",name:i.ok?"fetch_file.success":"fetch_file.failure",severity:i.ok?"info":"warn",payload:{toolCallId:t,ok:i.ok,path:i.path,size:i.size,error:i.error}}),i}})}function registerConnectorMcpProxy(e,t){e.registerHttpRoute({path:CONNECTOR_MCP_PROXY_PATH,auth:"plugin",async handler(r,o){await handleConnectorMcpRequest(r,o,{agentGatewayUrl:t.cfg.agentGatewayUrl,agentKeys:agentKeys,logger:e.logger})}})}function registerServices(e,t,r){const{cfg:o,writeObs:n,thirdPartyChannels:a}=t;if(sharedAutomationTurnPolicy.disable(),"function"==typeof e.on)try{e.on("before_tool_call",(e,t)=>sharedAutomationTurnPolicy.check(e.toolName,{sessionKey:t?.sessionKey,runId:t?.runId??e.runId}),{priority:100}),sharedAutomationTurnPolicy.enable()}catch{e.logger.warn?.("[beeos-claw] Automation Wake disabled: tool guard unavailable")}if("function"!=typeof e.on){if(o.messageBus.enabled)throw new Error("openclaw_before_message_write_hook_unavailable")}else registerModelCallObservationHooks((t,r)=>e.on(t,r),sharedModelCallObservationRouter),e.on("before_message_write",(t,r)=>{const o=t.sessionKey??r.sessionKey;if(!o)return void e.logger.debug?.("[before_message_write] ignored assistant write without sessionKey");sharedAssistantMessageCommitRouter.route(o,t.message)||e.logger.debug?.(`[before_message_write] no active IM commit target sessionKey=${o} active_targets=${sharedAssistantMessageCommitRouter.size()}`)}),e.on("after_tool_call",(t,r)=>{const o=r.sessionKey,n=t.toolCallId??r.toolCallId,a=t.toolName||r.toolName;if(!o||!n||!a)return void e.logger.debug?.(`[after_tool_call] ignored structured result without correlation sessionKey=${o??"missing"} toolCallId=${n??"missing"}`);observeToolCallForDelivery(t,o);sharedToolResultObservationRouter.route(o,{toolCallId:n,toolName:a,rawResult:t.result,isError:Boolean(t.error)})||e.logger.debug?.(`[after_tool_call] no active IM observation target sessionKey=${o} active_targets=${sharedToolResultObservationRouter.size()}`)});const i=agentKeys?(e,t)=>agentFetch(e,agentKeys,t):void 0,s=agentKeys?(e,t)=>agentBoundFetch(e,agentKeys,t):void 0,l=agentKeys?Buffer.from(agentKeys.publicKey).toString("base64"):void 0,c=join(process.env.OPENCLAW_HOME||join(homedir(),".openclaw"),"beeos"),g=sharedInboundExecutionJournal??=new FileInboundExecutionJournal(join(c,"inbound-execution-journal.v1.jsonl"));sharedMcpProjection??=new OpenClawNativeMcpProjection,!sharedMcpCredentialVault&&agentKeys&&l&&(sharedMcpCredentialVault=new LocalEncryptedCredentialReferenceVault(join(c,"mcp-credential-vault.json"),l,agentKeys.privateKey));const u=cloudRuntimeTargetInstanceId(process.env);if(u&&(!agentKeys||!o.agentGatewayUrl||!i||!s))throw new Error("Cloud runtime authority requires an agent identity and gateway transport");if(sharedRuntimeAuthority&&sharedRuntimeAuthority.cloudTargetInstanceId!==u)throw new Error("Cloud runtime authority target changed during process lifetime");if(!sharedRuntimeAuthority&&agentKeys&&o.agentGatewayUrl&&i&&s&&(sharedRuntimeAuthority=new RuntimeProductionAuthority({stateDirectory:c,agentGatewayUrl:o.agentGatewayUrl,agentFetch:i,boundAgentFetch:s,keys:agentKeys,targetInstanceId:u,logger:e.logger,getMSConnectionStatus:e=>sharedMSConnectionStatus.get(e)})),sharedGatewayClient??=new GatewayClient({url:o.gateway.url,token:o.gateway.token||"",clientId:o.gateway.clientId,openclawCanvasCompat:!!o.openclawCanvasCompat,retry:{baseMs:o.retry.baseMs,maxMs:o.retry.maxMs,maxAttempts:o.retry.maxAttempts}}),sharedRuntimeAgentCapabilities??={canvas:o.canvasEnabled,terminal:!1,webSsh:!1,fileSystem:!0,image:!0,audio:!1,mcp:!0,cron:!0,streaming:!0,pushNotifications:o.messageBus.enabled,...o.messageBus.enabled&&sharedAutomationTurnPolicy.isReady()&&i?{automationWake:!0}:{}},!sharedAgentProjection&&sharedRuntimeAuthority&&i&&o.agentGatewayUrl){const t=sharedRuntimeAuthority;sharedAgentProjection=new AgentProjectionProduction({authority:t,stateFile:join(t.stateDirectory,"runtime-agent-projection.json"),journal:t.journal,identities:t.identities,source:new OpenClawAgentProjectionSource(()=>sharedGatewayClient),transport:new AgentGatewayHttpProjectionTransport({agentGatewayUrl:o.agentGatewayUrl,boundAgentFetch:t.boundAgentFetch,getRuntimeLeaseCredential:e=>{const r=t.authorityLease;if(!r||r.instanceId!==e.instanceId||r.leaseId!==e.leaseId||r.runtimeEpoch!==e.runtimeEpoch||r.handlerIdentity!==e.handlerIdentity)throw new Error("agent projection fenced by current runtime lease");return r.runtimeLeaseCredential},onAccepted:t=>{0!==t.size&&sharedAgentMappings.replace(t).catch(t=>{e.logger.warn?.("[agent-projection] mapping publication failed: "+(t instanceof Error?t.message:String(t)))})}}),capabilities:sharedRuntimeAgentCapabilities,logger:e.logger})}sharedRuntimeOwner??=new OpenClawRuntimeProcessOwner(sharedGatewayClient,sharedAgentProjection,e.logger);const d=sharedRuntimeOwner;sharedSessionMutationCoordinator??=new SessionMutationCoordinator(sharedSessionWriteGate,new OpenClawSessionWriteQuiescence(e.logger,{agentId:o.gateway.agentId||"main"}),e.logger,{onRecoveryRequired:t=>{scheduleProcessRuntimeRestart(t,e.logger)}});const m=sharedSessionMutationCoordinator;if(!sharedAcpServer){const t=sharedRuntimeAuthority&&o.agentGatewayUrl?new AgentGatewayTemplateOriginReporter({agentGatewayUrl:o.agentGatewayUrl,boundAgentFetch:sharedRuntimeAuthority.boundAgentFetch}):void 0;sharedAcpServer=new ACPServer(o,e.logger,m,n,i,l,a,sharedTerminalManager,sharedRuntimeAuthority,void 0,sharedMcpProjection,sharedMcpCredentialVault,s,{gateway:d.client,agentProjection:sharedAgentProjection,markAgentProjectionDirty:()=>sharedAgentProjection?.markDirty(),templateOriginReporter:t,agentMappings:sharedAgentMappings,agentCapabilities:sharedRuntimeAgentCapabilities,createRunOutputRouter:createPeerRunOutputRouterFactory(new MessageServiceRunOutputChannel({getClient:()=>sharedMessageClientOwner.current?.client??null,logger:e.logger,writeObs:n})),runExclusive:(t,r,o)=>sharedImHotPathDeferral.runExclusive(t,r,e.logger,o)})}const p=sharedAcpServer,y=()=>sharedAgentMappings.snapshot()?.values().next().value;r.current=p,sharedMcpNativeProjectionNudge.request({registrationMode:e.registrationMode,fingerprint:fingerprintMcpServers(t.openclawConfig),logger:e.logger});const h=sharedPromptRunStore,{enabled:f,principalId:w,a2aEnabled:v}=o.messageBus;let R;!u&&o.agentGatewayUrl&&agentKeys&&(R=createTokenProvider({agentGatewayUrl:o.agentGatewayUrl,identity:agentKeyPairToIdentity(agentKeys)}));const C=process.env.MESSAGE_RUNTIME_DELIVERY_KEY?.trim(),b=w||process.env.BEEOS_PRINCIPAL_ID||"",S=R?sharedMessageClientOwner.configure({tokenProvider:R,identityId:b,...o.messageBus.serviceUrl?{serviceUrl:o.messageBus.serviceUrl}:{},logger:e.logger,...sharedRuntimeAuthority&&C?{runtimeDelivery:{scopedDeliveryKey:C,authority:{currentLease:()=>{const e=sharedRuntimeAuthority?.authorityLease,t=sharedRuntimeAuthority?.currentConsumerBinding();return e&&t?{...e,journalStoreId:t.journalStoreId,journalGeneration:t.journalGeneration}:null}}}}:{}}):void 0,_=u&&o.messageBus.serviceUrl&&sharedRuntimeAuthority?new NodeRuntimeDeliveryPort({serviceOrigin:async()=>o.messageBus.serviceUrl},{currentLease:()=>{const e=sharedRuntimeAuthority?.authorityLease,t=sharedRuntimeAuthority?.currentConsumerBinding();return e&&t&&e.instanceId===u?{...e,journalStoreId:t.journalStoreId,journalGeneration:t.journalGeneration}:null},subscribeAuthority:e=>sharedRuntimeAuthority.subscribeAuthority(e)}):void 0,E=sharedRuntimeAuthority,O=o.messageBus.serviceUrl;if(S&&E&&O&&C){const e=S.nodeTransport;p.setCronConversationDelivery({deliver:async t=>{const r=E.authorityLease;if(!r)throw new Error("current runtime authority lease unavailable for cron delivery");const o=new MessageServiceCronDeliveryHttpAuthority({messageServiceUrl:O,fetchAuthorized:async(t,o)=>await e.fetchRuntimeChannel(t,{runtimeLeaseCredential:r.runtimeLeaseCredential},o)});return await o.deliver({...t,runtimeLeaseCredential:r.runtimeLeaseCredential})}})}else p.setCronConversationDelivery(void 0);const A=sharedRuntimeAuthority?.journal??new FileRuntimeCommandJournal(join(c,"runtime-command-journal.json"),void 0,void 0,{onMaintenanceError:t=>e.logger.error?.(`[runtime-journal] event=checkpoint_failed trigger=threshold error=${t instanceof Error?t.message:String(t)}`)}),T=d.createRegistrationToken();e.registerService({id:"beeos-acp",async start(){const t=Date.now(),r=_pluginRegisteredAt?t-_pluginRegisteredAt:-1;e.logger.info?.(`[beeos-claw] beeos-acp service start() begin, sincePluginRegister=${r}ms, bridge.url=${o.bridge.url||"(empty)"}`);const n=await createRuntimeServiceProductionCandidate({...o.agentGatewayUrl&&i?{authorizationAdapter:new AgentGatewayGrantJwksReadinessAdapter({agentGatewayUrl:o.agentGatewayUrl})}:{},...S&&o.agentGatewayUrl&&s&&C?{transportAdapter:new RuntimeServiceTransportReadinessAdapter({agentGatewayUrl:o.agentGatewayUrl,messageService:S.nodeTransport,messageClient:S.client,configuredServiceUrl:o.messageBus.serviceUrl,boundAgentFetch:s,scopedDeliveryKey:C})}:{},..._&&o.agentGatewayUrl&&s&&o.messageBus.serviceUrl?{cloudTransportAdapter:new CloudRuntimeServiceTransportReadinessAdapter({agentGatewayUrl:o.agentGatewayUrl,boundAgentFetch:s,configuredServiceUrl:o.messageBus.serviceUrl,runtimeDelivery:_})}:{},commandJournal:A,poisonJournal:new FileRuntimePoisonDeliveryJournal(join(c,"runtime-poison-deliveries.json")),onConsumerDeliveryError:t=>{const r=t instanceof Error?`${t.name}: ${t.message}${"status"in t?` status=${t.status}`:""}${"code"in t?` code=${t.code}`:""}\n${t.stack??""}`:String(t);e.logger.error?.(`[runtime-service] delivery worker failed before acknowledgement; delivery retained for retry: ${r}`)},onConsumerLeaseStarvation:({starvedForMs:t,recovered:r})=>{const o=Math.round(t/1e3);r?e.logger.info?.(`[runtime-service] runtime authority lease restored after ${o}s without delivery reads`):e.logger.warn?.(`[runtime-service] no runtime authority lease for ${o}s; delivery reads suspended, queued runtime operations (model switch, session control) will not be consumed`)},onConsumerRequestFailure:t=>e.logger.error?.(`[runtime-service] request failed operationId=${t.operationId} method=${t.method}: ${t.error instanceof Error?t.error.stack??t.error.message:String(t.error)}`)});if(sharedRuntimeAuthority?.setRuntimeServiceMethods("ready"===n.status?n.serviceMethods:[]),sharedRuntimeAuthority?.setRuntimeResourceBindingContractRevision("ready"===n.status?n.resourceBindingContractRevision:void 0),p.setRuntimeRpcConsumerFactory("ready"===n.status?n.factory:void 0),"unavailable"===n.status?e.logger.warn?.("[runtime-service] capability fenced: "+n.blockers.map(e=>e.id).join(",")):n.capabilityBlockers.length&&e.logger.warn?.("[runtime-service] method capability fenced: "+n.capabilityBlockers.map(e=>e.method).join(",")),!o.bridge.url&&o.agentGatewayUrl&&i){const t=Date.now(),r=await discoverBridgeFromGW(o.agentGatewayUrl,i);if(e.logger.info?.(`[beeos-claw] bridge discovery took ${Date.now()-t}ms result=${r?.url??"null"}`),r)o.bridge.url=r.url,writeDiscoveryCache({bridge:{url:r.url,discoveredAt:Date.now()}}),e.logger.info?.(`[beeos-claw] Bridge URL discovered from Agent Gateway: ${r.url}`);else{const t=readDiscoveryCache();t?.bridge?.url?(o.bridge.url=t.bridge.url,e.logger.info?.(`[beeos-claw] Bridge URL from cache (GW unreachable): ${t.bridge.url}`)):e.logger.debug?.("[beeos-claw] Bridge URL not available (no GW, no cache) — bridge disabled")}}await d.acquire(T);try{await p.start(o.bridge.url||void 0)}catch(e){throw await d.release(T),e}e.logger.info?.(`[beeos-claw] ACP Server started (total service start: ${Date.now()-t}ms)`)},async stop(){try{await d.release(T)}finally{await p.stop()}await A.checkpoint(),e.logger.info?.("[runtime-service] command journal checkpoint completed"),e.logger.info?.("[beeos-claw] ACP Server stopped")}});if(f&&void 0!==R&&R){const t=sharedMessageClientOwner.createRegistrationToken(),r=async(e,t)=>{const r=await R(e);if(!r.identityId||r.identityId!==e)throw new Error("Message SDK token identity did not match the current runtime authority");return{principalId:e,runtimeEpoch:t}},a=(e,t,r)=>({getLocalAgentId:()=>t,resolveLocalAgentId:e=>e===r?t:void 0,getDefaultPlatformAgent:()=>({instanceId:e.principalId}),ensureImInboundSession:(e,t,r,o)=>{p.ensureImInboundSession(e,t,r,o)},chatAbort:(e,t)=>p.chatAbort(e,t)}),i=sharedRuntimeAuthority?new RuntimeAuthorityMessageBusLifecycle({authority:sharedRuntimeAuthority,logger:e.logger,bus:{connect:async i=>{await sharedMessageClientOwner.acquire(t,async t=>{const s=await r(i.instanceId,i.runtimeEpoch),l=t.client,c={leaseId:i.leaseId,runtimeEpoch:i.runtimeEpoch};sharedMSConnectionStatus.bind(c);const u=new Map,d=new MessageProjectionInboundConsumer({client:l,bindings:()=>{const t=sharedRuntimeAuthority?.authorityLease;if(!t||t.instanceId!==s.principalId||t.runtimeEpoch!==s.runtimeEpoch)return[];const r=sharedAgentMappings.snapshot();return r?[...r.entries()].flatMap(([r,i])=>{if(!i.id||i.instanceId!==s.principalId)return[];let c=u.get(i.id);return c&&c.localAgentId===r||(c={localAgentId:r,inbound:createImInboundHandler({automationTurnPolicy:sharedAutomationTurnPolicy,runtime:e.runtime,pluginConfig:e.pluginConfig,client:l,localServer:a(s,r,i.id),promptRunStore:h,waitIfExclusive:(t,r)=>sharedImHotPathDeferral.waitIfExclusive(t,e.logger,r).then(()=>{}),assistantMessageCommitRouter:sharedAssistantMessageCommitRouter,toolResultObservationRouter:sharedToolResultObservationRouter,modelCallObservationRouter:sharedModelCallObservationRouter,sessionMutationCoordinator:m,logger:e.logger,writeObs:n,principalIdOverride:s.principalId,activeRunAuthority:p.getActiveRunAuthority(),...o.agentGatewayUrl&&agentKeys?{completeA2ATask:(t,r,n)=>completeA2ATask({agentGatewayUrl:o.agentGatewayUrl,agentKeys:agentKeys,logger:e.logger},t,r,n)}:{}})},u.set(i.id,c)),[{principalId:s.principalId,targetAgentId:i.id,runtimeEpoch:t.runtimeEpoch,inbound:c.inbound}]}):[]},executionJournal:g,logger:e.logger}),y=sharedAgentMappings.subscribe(()=>{sharedActiveMessageConsumer===d&&d.refresh()}),f=sharedRuntimeAuthority?.subscribeAuthority(()=>{sharedActiveMessageConsumer===d&&d.refresh()})??(()=>{}),w=new MessageClientTerminalStateRecovery({logger:e.logger,isCurrent:()=>{if(sharedActiveMessageConsumer!==d)return!1;const e=sharedRuntimeAuthority?.authorityLease;return null!=e&&e.instanceId===s.principalId&&e.runtimeEpoch===s.runtimeEpoch&&e.leaseId===i.leaseId&&e.handlerIdentity===i.handlerIdentity},reconnect:async()=>{if("failed"===l.getSnapshot().connection){await l.connect();try{await p.rebindRuntimeDelivery()}catch(t){e.logger.warn?.(`[beeos-claw] runtime Service delivery rebind failed after MessageClient recovery: ${t instanceof Error?t.message:String(t)}`)}}}});let v=l.getSnapshot().connection;sharedMSConnectionStatus.setIfCurrent(c,v);const R=l.subscribe(()=>{const t=l.getSnapshot();t.connection!==v&&(v=t.connection,sharedMSConnectionStatus.setIfCurrent(c,t.connection),"connected"===t.connection?(w.notifyConnected(),e.logger.info?.(`[beeos-claw] connection transport=message_service_realtime state=connected localAgentId=${p.getLocalAgentId()} principalId=${s.principalId}`)):"failed"===t.connection?(e.logger.warn?.(`[beeos-claw] connection transport=message_service_realtime state=failed principalId=${s.principalId} recoveryError=${t.recoveryError??"unknown"}`),w.notifyFailed()):"disconnected"!==t.connection&&"reconnecting"!==t.connection||e.logger.warn?.(`[beeos-claw] connection transport=message_service_realtime state=${t.connection} principalId=${s.principalId}; SDK owns reconnect/recovery`))});sharedActiveMessageConsumer=d,d.start();try{await l.connect()}catch(e){throw sharedActiveMessageConsumer===d&&(sharedActiveMessageConsumer=void 0),sharedMSConnectionStatus.resetIfCurrent(c),w.dispose(),await d.stop(),y(),f(),R(),await l.disconnect().catch(()=>{}),e}try{await p.rebindRuntimeDelivery()}catch(t){e.logger.warn?.(`[beeos-claw] runtime Service delivery rebind failed after MessageClient connect: ${t instanceof Error?t.message:String(t)}`)}return async()=>{sharedActiveMessageConsumer===d&&(sharedActiveMessageConsumer=void 0),sharedMSConnectionStatus.resetIfCurrent(c),w.dispose(),await d.stop(),f(),R(),await l.disconnect(),e.logger.info?.("[beeos-claw] MessageClient disconnected")}})},disconnect:async()=>{await sharedMessageClientOwner.release(t)}}}):void 0;e.registerService({id:"beeos-message-bus",async start(){i?i.start():e.logger.warn?.("[beeos-claw] Message bus requires runtime authority; waiting is unavailable without Agent Gateway runtime identity")},async stop(){await(i?.stop())}})}else f?e.logger.warn?.("[beeos-claw] Message bus enabled but Agent Gateway token provider is unavailable — disabled"):e.logger.info?.(`[beeos-claw] Message bus disabled (enabled=${f})`);if(!1!==v&&o.agentGatewayUrl){const t=e=>p.getGatewaySessionKeyForToolCall(e),r=e=>p.getMentionsForToolCall(e),n=(e,t)=>{p.broadcastNotification(e,t)},a=new TaskHandleRegistry;registerA2ATools(e,{agentGatewayUrl:o.agentGatewayUrl,agentKeys:agentKeys,logger:e.logger},a,t,r,n,()=>y()?.instanceId),e.logger.info?.("[beeos-claw] A2A tools registered (beeos_discover_agents, beeos_call_agent) via REST")}if(o.agentGatewayUrl&&(registerAutomationTools(e,{agentGatewayUrl:o.agentGatewayUrl,agentKeys:agentKeys??void 0,logger:e.logger,getDefaultAgent:()=>{const e=y();return e?{id:e.id,instanceId:e.instanceId}:void 0},getCurrentConversationId:e=>{const t=p.getGatewaySessionKeyForToolCall(e);return t?extractBeeosConversationIdFromSessionKey(t):void 0}}),e.logger.info?.("[beeos-claw] Automation tools registered (create/create_webhook/capabilities/preflight/list/get/run/runs/get_run)")),o.canvasEnabled){sharedCanvasState||(sharedCanvasState=new CanvasStateManager);const t=sharedCanvasState;if(o.agentGatewayUrl&&agentKeys){let r;t.setLogger(e.logger);{const e=o.agentGatewayUrl,t=agentKeys;r=async()=>{const r=await agentFetch(`${e}/api/v1/canvas/token`,t,{method:"POST"});if(!r.ok){const e=await r.text().catch(()=>"");throw new Error(`Canvas token returned ${r.status}: ${e}`)}return r.json()}}const n=n=>{const a=new CanvasRelayClient({canvasRelayUrl:o.canvasRelayUrl,agentKeys:agentKeys,agentId:n.id,displayName:n.displayName,avatarUrl:n.avatarUrl,tokenProvider:r,onConnected:()=>p.flushPendingCanvasLinks(),onReconnect:()=>p.onRelayReconnected(),logger:e.logger});t.setRelayClient(a),t.setAgentId(n.id),p.wireRelayActionHandler(),a.connect(),e.logger.info?.(`[beeos-claw] Canvas Relay multiplexed client connected (agentId=${n.id}, name=${n.name??"none"})`)};e.registerService(createCanvasRelayService({getAgent:y,onceAgentReady:e=>sharedAgentMappings.onceReady(e),initRelay:n,stopRelay:()=>t.getRelayClient()?.disconnect(),logger:e.logger})),e.logger.info?.("[beeos-claw] Canvas Relay configured")}else e.logger.warn?.("[beeos-claw] Canvas Relay unavailable: agentGatewayUrl or agentKeys missing");const r={stateManager:t,logger:e.logger,writeObs:e=>n(e),getActiveSessionId:e=>p.getSessionIdForToolCall(e),getPlatformAgentId:e=>p.getPlatformAgentIdForToolCall(e),getGatewaySessionKey:e=>p.getGatewaySessionKeyForToolCall(e),waitForAgentSync:()=>sharedAgentMappings.waitForSynchronization()},i=new FormSchemaRegistry,s=buildCronSchemas(a);i.register(s.create),i.register(s.update);const l=new ResourceDescriptorRegistry;l.register(CRON_RESOURCE);const c=e=>async(t,r,o)=>(p.preMapToolCallSession(t),e(t,r));e.registerTool({name:RENDER_UI_TOOL_NAME,description:RENDER_UI_TOOL_DESCRIPTION,parameters:RENDER_UI_TOOL_SCHEMA,execute:c(createRenderUiHandler(r))}),e.registerTool({name:CLOSE_UI_TOOL_NAME,description:CLOSE_UI_TOOL_DESCRIPTION,parameters:CLOSE_UI_TOOL_SCHEMA,execute:c(createCloseUiHandler(r))}),p.setCanvasState(t,{schemaRegistry:i,descriptorRegistry:l});const g=p.getFormBuilder();g&&e.registerTool({name:RENDER_FORM_TOOL_NAME,description:buildRenderFormDescription(i),parameters:RENDER_FORM_TOOL_SCHEMA,execute:c(createRenderFormHandler({registry:i,builder:g,logger:e.logger}))});const u=p.getResourceRenderer();if(u){generateActionTools(l,e,async(e,t)=>p.gatewayRequest(e,t),u,e.logger)}const d=buildToolRoutingHint(BEEOS_FILE_RESOURCE_ROUTING_RULE,BEEOS_DESKTOP_SCREENSHOT_TOOL_NAME),m=new Set([RENDER_UI_TOOL_NAME,CLOSE_UI_TOOL_NAME,RENDER_FORM_TOOL_NAME]),h=256,f=new Map;if("function"==typeof e.on){try{e.on("before_prompt_build",(e,t)=>{if(t?.sessionKey){if(f.size>=h&&!f.has(t.sessionKey)){const e=f.keys().next().value;void 0!==e&&f.delete(e)}f.set(t.sessionKey,t.channelId),rememberDeliveryWorkspace(t.sessionKey,t.workspaceDir)}const r=isThirdPartyChannel(a,{sessionKey:t?.sessionKey,channelId:t?.channelId});return t?.sessionKey||r?{appendSystemContext:buildAppendSystemContext({thirdParty:r,canvasSystemPrompt:`${CANVAS_PRESENTATION_GATE}\n\n${CANVAS_SYSTEM_PROMPT}`,toolRoutingHint:d})}:{appendSystemContext:d}}),e.logger.debug?.("[beeos-claw] Canvas system prompt hook registered: before_prompt_build")}catch{e.logger.debug?.("[beeos-claw] before_prompt_build hook not supported")}try{e.on("before_tool_call",(e,t)=>{if(t?.sessionKey&&p.setPendingToolCallSessionKey(t.sessionKey),!m.has(e.toolName))return;const r=t?.sessionKey?f.get(t.sessionKey):void 0,o=isThirdPartyChannel(a,{sessionKey:t?.sessionKey,channelId:r}),n=canvasToolBlockReason({thirdParty:o,hasSession:Boolean(t?.sessionKey)});return n?{block:!0,blockReason:n}:void 0}),e.logger.debug?.("[beeos-claw] Canvas tool guard registered: before_tool_call")}catch{e.logger.debug?.("[beeos-claw] before_tool_call hook not supported")}}e.logger.info?.("[beeos-claw] Canvas tools registered (render_ui, close_ui, render_form + auto-action tools)")}else e.logger.info?.("[beeos-claw] Canvas disabled by config (canvasEnabled=false)");if(o.bridge.shell.enabled&&!0===o.legacyTerminalWsEnabled){const t=createTerminalWebSocketService({config:{port:o.terminalWsPort,token:agentKeys?Buffer.from(agentKeys.publicKey).toString("base64"):"",allowedOrigins:o.terminalWsAllowedOrigins},terminalMgr:sharedTerminalManager,logger:e.logger,writeObs:n});e.registerService({id:"beeos-terminal-ws",start(){t.start(),e.logger.info?.(`[beeos-claw] Terminal WS started on port ${o.terminalWsPort}`)},stop(){t.stop(),e.logger.info?.("[beeos-claw] Terminal WS stopped")}})}else o.bridge.shell.enabled?e.logger.info?.("[beeos-claw] Legacy static-token Terminal WS disabled (default)"):e.logger.info?.("[beeos-claw] Terminal/shell disabled by config (bridge.shell.enabled=false)");if(t.openclawConfig){const r=mirrorOpenClawConfig(t.openclawConfig);r.copied?e.logger.info?.(`[beeos-claw] mirrored openclaw config to ${r.destinationPath}`):e.logger.debug?.(`[beeos-claw] openclaw config mirror skipped: ${r.reason}`)}}const plugin={id:"beeos-claw",name:"beeos-claw",description:"BeeOS platform bridge plugin for OpenClaw. Provides file upload/download, ACP bridge, and observability.",version:PLUGIN_VERSION,register(e){if(_initializing)return;const t=planPluginRegistration(e.registrationMode,_fullRuntimeRegistered);if("skip"!==t){_initializing=!0;try{if(!hasRuntimeReplyApi(e.runtime))throw e.logger.error?.("[beeos-claw] FATAL: OpenClaw runtime.channel.reply.dispatchReplyFromConfig not exposed. Upgrade OpenClaw or revert beeos-claw."),new Error("openclaw_runtime_reply_api_unavailable");const r=(t,r)=>{registerUploadTools(e,t,r),registerConnectorMcpProxy(e,t),registerArouterLatencyRouting(e),registerArouterMediaProviders(e)};if("capabilities"===t&&sharedPluginContext){if(cachedCapabilityTools.length>0){const t=resolveCapabilityToolReplay({currentRegisterTool:e.registerTool,wrappedRegisterTool:wrappedRegisterTool,originalRegisterTool:rawRegisterTool}),r=replayCachedPluginTools(r=>t.call(e,r),cachedCapabilityTools);return registerArouterLatencyRouting(e),registerArouterMediaProviders(e),void e.logger.info?.(`[beeos-claw] capability re-register mode=${e.registrationMode} tools_only count=${r}`)}const t={current:sharedAcpServer??null};return r(sharedPluginContext,t),registerServices(e,sharedPluginContext,t),void e.logger.info?.(`[beeos-claw] capability re-register mode=${e.registrationMode} capabilities_only`)}const o=initializePlugin(e);if(!o)return;const n={current:null};installUnhandledRejectionObserver(e,o.writeObs,n),ensureRegisterToolCache(e),cachedCapabilityTools.length=0,r(o,n),agentKeys&&o.cfg.agentGatewayUrl&&(e.on?e.on("before_agent_finalize",fileDeliveryFinalizeDecision):e.logger.warn?.("[file-delivery] OpenClaw plugin hooks unavailable; finalization check disabled")),registerServices(e,o,n),sharedPluginContext=o,marksFullRuntimeRegistered(e.registrationMode,t)&&(_fullRuntimeRegistered=!0),_pluginRegisteredAt=Date.now(),e.logger.info?.("[beeos-claw] Plugin registered successfully")}finally{_initializing=!1}}else e.logger.info?.(`[beeos-claw] skip re-register mode=${e.registrationMode??"full"} already_registered`)}};export default plugin;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{deterministicReplyMessageId}from"./im-runtime-dispatch.js";import{logChatLatency}from"./chat-latency.js";export class MessageProjectionInboundConsumer{delivered=new Set;cancelledTargets=new Set;inFlightMessages=new Set;inFlightCancels=new Set;eligibleMessages=new Set;transportContext=new Map;client;bindings;logger;executionJournal;maxConcurrentConversations;conversationQueue=[];queuedConversations=new Set;activeConversations=new Set;rerunConversations=new Set;laneTasks=new Set;cancelTasks=new Set;unsubscribeProjection;unsubscribeRealtime;previousConnection;activeConversationCount=0;started=!1;constructor(e){this.client=e.client,this.bindings=e.bindings,this.logger=e.logger,this.executionJournal=e.executionJournal,this.previousConnection=e.client.getSnapshot().connection,this.maxConcurrentConversations=Math.max(1,Math.floor(e.maxConcurrentConversations??16))}start(){this.started||(this.started=!0,this.unsubscribeRealtime=this.client.listen({eventTypes:["message.created"]},e=>this.admitRealtimeMessage(e)),this.previousConnection=this.client.getSnapshot().connection,this.unsubscribeProjection=this.client.subscribe(()=>{const e=this.client.getSnapshot().connection,t="connected"===e&&"connected"!==this.previousConnection;this.previousConnection=e,t&&this.recoverDurableSnapshot()}),"connected"===this.previousConnection&&this.recoverDurableSnapshot())}async stop(){this.started=!1,this.unsubscribeProjection?.(),this.unsubscribeProjection=void 0,this.unsubscribeRealtime?.(),this.unsubscribeRealtime=void 0,this.conversationQueue.length=0,this.queuedConversations.clear(),this.rerunConversations.clear(),this.eligibleMessages.clear(),this.transportContext.clear(),await Promise.allSettled([...this.laneTasks,...this.cancelTasks])}refresh(){this.recoverDurableSnapshot()}admitRealtimeMessage(e){if(!this.started||"message.created"!==e.type)return;const{instanceId:t,agentId:n,runtimeEpoch:i,conversationId:s,messageId:o}=e.scope;if(!(t&&n&&i&&s&&o))return;if(!this.bindings().find(e=>e.principalId===t&&e.targetAgentId===n&&e.runtimeEpoch===i))return;const r=e.data.message;if(r.id!==o||r.conversationId!==s)return;const a=this.client.getSnapshot().messages[o];if(!a||!isInbound(a)||a.conversationId!==s||a.senderId!==r.senderId||!isCanonicalInboundPublisher(a,e))return;const c=`${s}:${o}`,d=validRequestId(e.correlation.requestId),l=Date.now();d&&this.transportContext.set(c,{requestId:d,admittedAtMs:l,requestIdSource:"realtime_correlation"}),logChatLatency(this.logger,"claw.inbound.realtime_admitted",d??o,l,{request_id_source:d?"realtime_correlation":"message_id_fallback"}),this.eligibleMessages.add(c),this.schedule()}recoverDurableSnapshot(){if(this.started){for(const e of Object.values(this.client.getSnapshot().messages))isInbound(e)&&isRecoverableInboundPublisher(e)&&this.eligibleMessages.add(`${e.conversationId}:${e.id}`);this.schedule()}}schedule(){if(!this.started)return;const e=Object.values(this.client.getSnapshot().messages).filter(isInbound).sort(compareProjectionOrder),t=e.filter(e=>this.eligibleMessages.has(`${e.conversationId}:${e.id}`));for(const n of t)"chat_cancel"===n.type&&this.dispatchCancel(n,e);for(const e of new Set(t.filter(e=>"chat_message"===e.type).map(e=>e.conversationId)))this.enqueueConversation(e);this.pumpConversationQueue()}enqueueConversation(e){this.activeConversations.has(e)?this.rerunConversations.add(e):this.queuedConversations.has(e)||(this.queuedConversations.add(e),this.conversationQueue.push(e))}pumpConversationQueue(){for(;this.started&&this.activeConversationCount<this.maxConcurrentConversations&&this.conversationQueue.length>0;){const e=this.conversationQueue.shift();this.queuedConversations.delete(e),this.activeConversations.add(e),this.activeConversationCount+=1;const t=this.drainConversation(e).catch(t=>{this.logger.warn?.(`[beeos-claw] projected inbound lane failed conversation=${e}: ${t instanceof Error?t.message:String(t)}`)}).finally(()=>{this.laneTasks.delete(t),this.activeConversations.delete(e),this.activeConversationCount-=1,this.rerunConversations.delete(e)&&this.started&&this.enqueueConversation(e),this.pumpConversationQueue()});this.laneTasks.add(t)}}async drainConversation(e){const t=Object.values(this.client.getSnapshot().messages).filter(t=>"chat_message"===t.type&&t.conversationId===e&&this.eligibleMessages.has(`${t.conversationId}:${t.id}`)).sort(compareProjectionOrder);for(const e of t){if(!this.started)break;const t=resolveExecutionBinding(e,this.bindings());if(!t)continue;const n=`${e.conversationId}:${e.id}`;if(this.cancelledTargets.has(n)){this.delivered.add(n),this.eligibleMessages.delete(n),this.transportContext.delete(n);continue}if(this.delivered.has(n)||this.inFlightMessages.has(n))continue;const i=await this.reconcileRequest(e,t);if(!this.started)break;if("handled"===i){this.delivered.add(n),this.eligibleMessages.delete(n),this.transportContext.delete(n);continue}if("pending"===i)break;if(!sameBinding(t,resolveExecutionBinding(e,this.bindings())))break;let s;try{s=await this.executionJournal.claim(n)}catch(t){this.logger.warn?.(`[beeos-claw] inbound execution claim failed conversation=${e.conversationId} message_id=${e.id}: ${t instanceof Error?t.message:String(t)}`);break}if(!this.started)break;if("existing"===s){this.logger.warn?.(`[beeos-claw] projected inbound skip claimed conversation=${e.conversationId} message_id=${e.id}`),this.delivered.add(n),this.eligibleMessages.delete(n),this.transportContext.delete(n);continue}if(!sameBinding(t,resolveExecutionBinding(e,this.bindings())))break;this.inFlightMessages.add(n);try{const i=this.transportContext.get(n);i&&logChatLatency(this.logger,"claw.inbound.execution_claimed",i.requestId,i.admittedAtMs,{request_id_source:i.requestIdSource}),await t.inbound.onChatMessage(i?{...e,requestId:i.requestId}:e),this.delivered.add(n),this.eligibleMessages.delete(n),this.transportContext.delete(n)}catch(t){this.logger.warn?.(`[beeos-claw] projected inbound handler failed conversation=${e.conversationId} message_id=${e.id}: ${t instanceof Error?t.message:String(t)}`);break}finally{this.inFlightMessages.delete(n)}}}dispatchCancel(e,t){const n=`${e.conversationId}:${e.id}`;if(this.delivered.has(n)||this.inFlightCancels.has(n))return;const i=resolveCancelTarget(e,t,this.bindings());if(!i)return;this.cancelledTargets.add(`${i.message.conversationId}:${i.message.id}`),this.inFlightCancels.add(n);const s=i.binding.inbound.onChatCancel(e).then(()=>{this.delivered.add(n),this.eligibleMessages.delete(n),this.transportContext.delete(n)}).catch(t=>{this.logger.warn?.(`[beeos-claw] projected inbound cancel failed conversation=${e.conversationId} message_id=${e.id}: ${t instanceof Error?t.message:String(t)}`)}).finally(()=>{this.cancelTasks.delete(s),this.inFlightCancels.delete(n)});this.cancelTasks.add(s)}async reconcileRequest(e,t){const n=Object.values(this.client.getSnapshot().messages),i=n.filter(n=>isReplyFor(n,e,t.targetAgentId));if(i.some(isTerminalReply))return"handled";if(i.length>0)return"pending";if(n.some(n=>isReasoningFor(n,e,t.targetAgentId)))return"pending";const s=deterministicReplyMessageId("asst",e.conversationId,e.id,t.principalId);try{const t=await this.client.messages.getEnvelope(e.conversationId,s);return"agent_reply"!==t.type||t.conversationId!==e.conversationId||void 0!==t.replyTo&&t.replyTo.length>0&&t.replyTo!==e.id?(this.logger.warn?.(`[beeos-claw] deterministic reply reconciliation mismatch conversation=${e.conversationId} message_id=${e.id} reply_id=${s}`),"pending"):isTerminalReply(t)?"handled":"pending"}catch(t){return 404===httpStatus(t)?"execute":(this.logger.warn?.(`[beeos-claw] deterministic reply reconciliation failed conversation=${e.conversationId} message_id=${e.id}: ${t instanceof Error?t.message:String(t)}`),"pending")}}}function isInbound(e){return"chat_message"===e.type||"chat_cancel"===e.type}function isCanonicalUserPublisher(e){return"string"==typeof e.senderId&&e.senderId.startsWith("user:")&&e.senderId.length>5}function isCanonicalA2ACommand(e){const t=asRecord(asRecord(e.content)?.metadata);return"chat_message"===e.type&&"a2a"===t?.protocol&&"string"==typeof e.senderId&&e.senderId.length>0}function isCanonicalInboundPublisher(e,t){return"user"===t.actor.kind?t.actor.id===e.senderId&&isCanonicalUserPublisher(e):"service"===t.actor.kind&&"message-service"===t.actor.id&&isCanonicalA2ACommand(e)}function isRecoverableInboundPublisher(e){return isCanonicalUserPublisher(e)||isCanonicalA2ACommand(e)}function resolveExecutionBinding(e,t){if(!e.id)return;const n=asRecord(e.content),i=asRecord(n?.metadata),s=nonEmptyString(i?.delivery_principal),o=nonEmptyString(i?.target_agent_id),r=epochValue(i?.runtime_epoch);if(s&&o&&void 0!==r)for(const e of t){if(e.principalId!==s)continue;if(e.targetAgentId!==o)continue;if(epochValue(e.runtimeEpoch)===r)return e}}function epochValue(e){const t=nonEmptyString(e);if(void 0!==t&&/^\d+$/.test(t))return BigInt(t)}function sameBinding(e,t){return void 0!==t&&t.principalId===e.principalId&&t.targetAgentId===e.targetAgentId&&t.runtimeEpoch===e.runtimeEpoch&&t.inbound===e.inbound}function resolveCancelTarget(e,t,n){const i=nonEmptyString(asRecord(e.content)?.target_message_id);if(!i)return;const s=t.find(t=>"chat_message"===t.type&&t.id===i&&t.conversationId===e.conversationId);if(!s)return;const o=resolveExecutionBinding(s,n),r=resolveExecutionBinding(e,n);return o&&r&&o.principalId===r.principalId&&o.targetAgentId===r.targetAgentId&&o.runtimeEpoch===r.runtimeEpoch?{message:s,binding:o}:void 0}function isReplyFor(e,t,n){if("agent_reply"!==e.type||e.conversationId!==t.conversationId||e.replyTo!==t.id)return!1;if(e.senderId===n)return!0;if(!isTerminalReply(e))return!1;const i=nonEmptyString(asRecord(asRecord(t.content)?.metadata)?.delivery_principal);return void 0!==i&&e.senderId===i}function isReasoningFor(e,t,n){return"agent_reasoning"===e.type&&e.conversationId===t.conversationId&&e.replyTo===t.id&&e.senderId===n}function isTerminalReply(e){return"agent_reply"===e.type&&("completed"===e.state||"failed"===e.state||"refused"===e.state||"cancelled"===e.state)}function httpStatus(e){return null!==e&&"object"==typeof e&&"status"in e&&"number"==typeof e.status?e.status:void 0}function asRecord(e){return null===e||"object"!=typeof e||Array.isArray(e)?void 0:e}function nonEmptyString(e){return"string"==typeof e&&e.length>0?e:void 0}function validRequestId(e){if(!("string"!=typeof e||0===e.length||e.length>128))return/^[A-Za-z0-9._:-]+$/.test(e)?e:void 0}function compareProjectionOrder(e,t){try{const n=BigInt(e.offset)-BigInt(t.offset);if(0n!==n)return n<0n?-1:1}catch{const n=e.offset.localeCompare(t.offset);if(0!==n)return n}return e.id.localeCompare(t.id)}
|
|
1
|
+
import{deterministicReplyMessageId}from"./im-runtime-dispatch.js";import{logChatLatency}from"./chat-latency.js";export class MessageProjectionInboundConsumer{delivered=new Set;cancelledTargets=new Set;inFlightMessages=new Set;inFlightCancels=new Set;eligibleMessages=new Set;transportContext=new Map;client;bindings;logger;executionJournal;maxConcurrentConversations;conversationQueue=[];queuedConversations=new Set;activeConversations=new Set;rerunConversations=new Set;laneTasks=new Set;cancelTasks=new Set;unsubscribeProjection;unsubscribeRealtime;previousConnection;activeConversationCount=0;started=!1;constructor(e){this.client=e.client,this.bindings=e.bindings,this.logger=e.logger,this.executionJournal=e.executionJournal,this.previousConnection=e.client.getSnapshot().connection,this.maxConcurrentConversations=Math.max(1,Math.floor(e.maxConcurrentConversations??16))}start(){this.started||(this.started=!0,this.unsubscribeRealtime=this.client.listen({eventTypes:["message.created"]},e=>this.admitRealtimeMessage(e)),this.previousConnection=this.client.getSnapshot().connection,this.unsubscribeProjection=this.client.subscribe(()=>{const e=this.client.getSnapshot().connection,t="connected"===e&&"connected"!==this.previousConnection;this.previousConnection=e,t&&this.recoverDurableSnapshot()}),"connected"===this.previousConnection&&this.recoverDurableSnapshot())}async stop(){this.started=!1,this.unsubscribeProjection?.(),this.unsubscribeProjection=void 0,this.unsubscribeRealtime?.(),this.unsubscribeRealtime=void 0,this.conversationQueue.length=0,this.queuedConversations.clear(),this.rerunConversations.clear(),this.eligibleMessages.clear(),this.transportContext.clear(),await Promise.allSettled([...this.laneTasks,...this.cancelTasks])}refresh(){this.recoverDurableSnapshot()}admitRealtimeMessage(e){if(!this.started||"message.created"!==e.type)return;const{instanceId:t,agentId:n,runtimeEpoch:i,conversationId:s,messageId:o}=e.scope;if(!(t&&n&&i&&s&&o))return;if(!this.bindings().find(e=>e.principalId===t&&e.targetAgentId===n&&e.runtimeEpoch===i))return;const a=e.data.message;if(a.id!==o||a.conversationId!==s)return;const r=this.client.getSnapshot().messages[o];if(!r||!isInbound(r)||r.conversationId!==s||r.senderId!==a.senderId||!isCanonicalInboundPublisher(r,e))return;const c=`${s}:${o}`,d=validRequestId(e.correlation.requestId),l=Date.now();d&&this.transportContext.set(c,{requestId:d,admittedAtMs:l,requestIdSource:"realtime_correlation"}),logChatLatency(this.logger,"claw.inbound.realtime_admitted",d??o,l,{request_id_source:d?"realtime_correlation":"message_id_fallback"}),this.eligibleMessages.add(c),this.schedule()}recoverDurableSnapshot(){if(this.started){for(const e of Object.values(this.client.getSnapshot().messages))isInbound(e)&&isRecoverableInboundPublisher(e)&&this.eligibleMessages.add(`${e.conversationId}:${e.id}`);this.schedule()}}schedule(){if(!this.started)return;const e=Object.values(this.client.getSnapshot().messages).filter(isInbound).sort(compareProjectionOrder),t=e.filter(e=>this.eligibleMessages.has(`${e.conversationId}:${e.id}`));for(const n of t)"chat_cancel"===n.type&&this.dispatchCancel(n,e);for(const e of new Set(t.filter(isConversationLane).map(e=>e.conversationId)))this.enqueueConversation(e);this.pumpConversationQueue()}enqueueConversation(e){this.activeConversations.has(e)?this.rerunConversations.add(e):this.queuedConversations.has(e)||(this.queuedConversations.add(e),this.conversationQueue.push(e))}pumpConversationQueue(){for(;this.started&&this.activeConversationCount<this.maxConcurrentConversations&&this.conversationQueue.length>0;){const e=this.conversationQueue.shift();this.queuedConversations.delete(e),this.activeConversations.add(e),this.activeConversationCount+=1;const t=this.drainConversation(e).catch(t=>{this.logger.warn?.(`[beeos-claw] projected inbound lane failed conversation=${e}: ${t instanceof Error?t.message:String(t)}`)}).finally(()=>{this.laneTasks.delete(t),this.activeConversations.delete(e),this.activeConversationCount-=1,this.rerunConversations.delete(e)&&this.started&&this.enqueueConversation(e),this.pumpConversationQueue()});this.laneTasks.add(t)}}async drainConversation(e){const t=Object.values(this.client.getSnapshot().messages).filter(t=>isConversationLane(t)&&t.conversationId===e&&this.eligibleMessages.has(`${t.conversationId}:${t.id}`)).sort(compareProjectionOrder);for(const e of t){if(!this.started)break;const t=resolveExecutionBinding(e,this.bindings());if(!t)continue;const n=`${e.conversationId}:${e.id}`;if(this.cancelledTargets.has(n)){this.delivered.add(n),this.eligibleMessages.delete(n),this.transportContext.delete(n);continue}if(this.delivered.has(n)||this.inFlightMessages.has(n))continue;if("chat_message"===e.type){const i=await this.reconcileRequest(e,t);if(!this.started)break;if("handled"===i){this.delivered.add(n),this.eligibleMessages.delete(n),this.transportContext.delete(n);continue}if("pending"===i)break}if(!sameBinding(t,resolveExecutionBinding(e,this.bindings())))break;let i;try{i=await this.executionJournal.claim(n)}catch(t){this.logger.warn?.(`[beeos-claw] inbound execution claim failed conversation=${e.conversationId} message_id=${e.id}: ${t instanceof Error?t.message:String(t)}`);break}if(!this.started)break;if("existing"===i){this.logger.warn?.(`[beeos-claw] projected inbound skip claimed conversation=${e.conversationId} message_id=${e.id}`),this.delivered.add(n),this.eligibleMessages.delete(n),this.transportContext.delete(n);continue}if(!sameBinding(t,resolveExecutionBinding(e,this.bindings())))break;this.inFlightMessages.add(n);try{const i=this.transportContext.get(n);i&&logChatLatency(this.logger,"claw.inbound.execution_claimed",i.requestId,i.admittedAtMs,{request_id_source:i.requestIdSource}),await dispatchLaneMessage(t,i?{...e,requestId:i.requestId}:e),this.delivered.add(n),this.eligibleMessages.delete(n),this.transportContext.delete(n)}catch(t){this.logger.warn?.(`[beeos-claw] projected inbound handler failed conversation=${e.conversationId} message_id=${e.id}: ${t instanceof Error?t.message:String(t)}`);break}finally{this.inFlightMessages.delete(n)}}}dispatchCancel(e,t){const n=`${e.conversationId}:${e.id}`;if(this.delivered.has(n)||this.inFlightCancels.has(n))return;const i=resolveCancelTarget(e,t,this.bindings());if(!i)return;this.cancelledTargets.add(`${i.message.conversationId}:${i.message.id}`),this.inFlightCancels.add(n);const s=i.binding.inbound.onChatCancel(e).then(()=>{this.delivered.add(n),this.eligibleMessages.delete(n),this.transportContext.delete(n)}).catch(t=>{this.logger.warn?.(`[beeos-claw] projected inbound cancel failed conversation=${e.conversationId} message_id=${e.id}: ${t instanceof Error?t.message:String(t)}`)}).finally(()=>{this.cancelTasks.delete(s),this.inFlightCancels.delete(n)});this.cancelTasks.add(s)}async reconcileRequest(e,t){const n=Object.values(this.client.getSnapshot().messages),i=n.filter(n=>isReplyFor(n,e,t.targetAgentId));if(i.some(isTerminalReply))return"handled";if(i.length>0)return"pending";if(n.some(n=>isReasoningFor(n,e,t.targetAgentId)))return"pending";const s=deterministicReplyMessageId("asst",e.conversationId,e.id,t.principalId);try{const t=await this.client.messages.getEnvelope(e.conversationId,s);return"agent_reply"!==t.type||t.conversationId!==e.conversationId||void 0!==t.replyTo&&t.replyTo.length>0&&t.replyTo!==e.id?(this.logger.warn?.(`[beeos-claw] deterministic reply reconciliation mismatch conversation=${e.conversationId} message_id=${e.id} reply_id=${s}`),"pending"):isTerminalReply(t)?"handled":"pending"}catch(t){return 404===httpStatus(t)?"execute":(this.logger.warn?.(`[beeos-claw] deterministic reply reconciliation failed conversation=${e.conversationId} message_id=${e.id}: ${t instanceof Error?t.message:String(t)}`),"pending")}}}function isInbound(e){return"chat_message"===e.type||"chat_cancel"===e.type||"user.continue"===e.type||"user.auth_grant"===e.type}function isConversationLane(e){return"chat_message"===e.type||"user.continue"===e.type||"user.auth_grant"===e.type}async function dispatchLaneMessage(e,t){"user.continue"!==t.type?"user.auth_grant"!==t.type?await e.inbound.onChatMessage(t):await e.inbound.onChatAuthGrant(t):await e.inbound.onChatContinue(t)}function isCanonicalUserPublisher(e){return"string"==typeof e.senderId&&e.senderId.startsWith("user:")&&e.senderId.length>5}function isCanonicalA2ACommand(e){const t=asRecord(asRecord(e.content)?.metadata);return"chat_message"===e.type&&"a2a"===t?.protocol&&"string"==typeof e.senderId&&e.senderId.length>0}function isCanonicalInboundPublisher(e,t){return"user"===t.actor.kind?t.actor.id===e.senderId&&isCanonicalUserPublisher(e):"service"===t.actor.kind&&"message-service"===t.actor.id&&isCanonicalA2ACommand(e)}function isRecoverableInboundPublisher(e){return isCanonicalUserPublisher(e)||isCanonicalA2ACommand(e)}function resolveExecutionBinding(e,t){if(!e.id)return;const n=fenceMetadata(e),i=nonEmptyString(n?.delivery_principal),s=nonEmptyString(n?.target_agent_id),o=epochValue(n?.runtime_epoch);if(i&&s&&void 0!==o)for(const e of t){if(e.principalId!==i)continue;if(e.targetAgentId!==s)continue;if(epochValue(e.runtimeEpoch)===o)return e}}function epochValue(e){const t=nonEmptyString(e);if(void 0!==t&&/^\d+$/.test(t))return BigInt(t)}function sameBinding(e,t){return void 0!==t&&t.principalId===e.principalId&&t.targetAgentId===e.targetAgentId&&t.runtimeEpoch===e.runtimeEpoch&&t.inbound===e.inbound}function resolveCancelTarget(e,t,n){const i=nonEmptyString(asRecord(e.content)?.target_message_id);if(!i)return;const s=t.find(t=>"chat_message"===t.type&&t.id===i&&t.conversationId===e.conversationId);if(!s)return;const o=resolveExecutionBinding(s,n),a=resolveExecutionBinding(e,n);return o&&a&&o.principalId===a.principalId&&o.targetAgentId===a.targetAgentId&&o.runtimeEpoch===a.runtimeEpoch?{message:s,binding:o}:void 0}function isReplyFor(e,t,n){if("agent_reply"!==e.type||e.conversationId!==t.conversationId||e.replyTo!==t.id)return!1;if(e.senderId===n)return!0;if(!isTerminalReply(e))return!1;const i=nonEmptyString(asRecord(asRecord(t.content)?.metadata)?.delivery_principal);return void 0!==i&&e.senderId===i}function isReasoningFor(e,t,n){return"agent_reasoning"===e.type&&e.conversationId===t.conversationId&&e.replyTo===t.id&&e.senderId===n}function isTerminalReply(e){return"agent_reply"===e.type&&("completed"===e.state||"failed"===e.state||"refused"===e.state||"cancelled"===e.state)}function httpStatus(e){return null!==e&&"object"==typeof e&&"status"in e&&"number"==typeof e.status?e.status:void 0}function asRecord(e){return null===e||"object"!=typeof e||Array.isArray(e)?void 0:e}function fenceMetadata(e){const t=asRecord(e.content),n=asRecord(t?.metadata);if(hasFenceKeys(n))return n;const i=asRecord(e.metadata);return hasFenceKeys(i)?i:hasFenceKeys(t)?t:n}function hasFenceKeys(e){return void 0!==e&&(void 0!==e.delivery_principal||void 0!==e.target_agent_id||void 0!==e.runtime_epoch)}function nonEmptyString(e){return"string"==typeof e&&e.length>0?e:void 0}function validRequestId(e){if(!("string"!=typeof e||0===e.length||e.length>128))return/^[A-Za-z0-9._:-]+$/.test(e)?e:void 0}function compareProjectionOrder(e,t){try{const n=BigInt(e.offset)-BigInt(t.offset);if(0n!==n)return n<0n?-1:1}catch{const n=e.offset.localeCompare(t.offset);if(0!==n)return n}return e.id.localeCompare(t.id)}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{canonicalizeJcs,verifyAndDeriveCoreExecutionAttempt}from"@beeos-ai/beeos-types/runtime";const TRUSTED_CORE_ATTEMPT=Symbol("beeos.trusted-core-attempt"),VERIFIED_MESSAGE_GRANT=Symbol("beeos.verified-message-grant");export function createAcpCoreExecutionAttempt(e){if(e.historyBoundary&&"local"!==e.historyBoundary.kind)throw new Error("ACP attempt cannot use a Message Service history boundary");return Object.freeze({...e,origin:"acp",[TRUSTED_CORE_ATTEMPT]:!0})}export async function verifyAndCreateServiceCoreExecutionAttempt(e){let t;try{t=verifyAndDeriveCoreExecutionAttempt(e.authorization,e.derivationContext,e.verifier)}catch{return{ok:!1,violations:["MALFORMED_EXECUTION_AUTHORIZATION"]}}if(!t.ok)return t;await e.recordVerifiedAttempt(t.attempt);const o=await e.resolvePorts();if(o.historyBoundary&&"message-service"!==o.historyBoundary.kind)throw new Error("Service attempt cannot fall back to local conversation history");if(o.sessionModelProjection&&o.historyBoundary)throw new Error("Service attempt cannot bind clear history and session model projection together");return{ok:!0,context:Object.freeze({operationId:e.operationId,runtimeEpoch:e.runtimeEpoch,fence:e.fence,resources:o.resources,...o.historyBoundary?{historyBoundary:o.historyBoundary}:{},...o.sessionModelProjection?{sessionModelProjection:o.sessionModelProjection}:{},...o.canvasControl?{canvasControl:o.canvasControl}:{},...e.abortSignal?{abortSignal:e.abortSignal}:{},verifiedAttempt:t.attempt,origin:"service",[TRUSTED_CORE_ATTEMPT]:!0})}}export async function verifyAndCreateMessageServiceCoreExecutionAttempt(e){const t=e.verifier.verifyAndDecodeGrant(e.executionGrant);if(!t)return{ok:!1,violations:["GRANT_SIGNATURE_INVALID"]};if(t.compactHash!==e.request.executionGrantHash)return{ok:!1,violations:["GRANT_HASH_MISMATCH"]};const o=t.claims,
|
|
1
|
+
import{canonicalizeJcs,isCloudRuntimeExecutionGrant,verifyAndDeriveCoreExecutionAttempt}from"@beeos-ai/beeos-types/runtime";const TRUSTED_CORE_ATTEMPT=Symbol("beeos.trusted-core-attempt"),VERIFIED_MESSAGE_GRANT=Symbol("beeos.verified-message-grant");export function createAcpCoreExecutionAttempt(e){if(e.historyBoundary&&"local"!==e.historyBoundary.kind)throw new Error("ACP attempt cannot use a Message Service history boundary");return Object.freeze({...e,origin:"acp",[TRUSTED_CORE_ATTEMPT]:!0})}export async function verifyAndCreateServiceCoreExecutionAttempt(e){let t;try{t=verifyAndDeriveCoreExecutionAttempt(e.authorization,e.derivationContext,e.verifier)}catch{return{ok:!1,violations:["MALFORMED_EXECUTION_AUTHORIZATION"]}}if(!t.ok)return t;await e.recordVerifiedAttempt(t.attempt);const o=await e.resolvePorts();if(o.historyBoundary&&"message-service"!==o.historyBoundary.kind)throw new Error("Service attempt cannot fall back to local conversation history");if(o.sessionModelProjection&&o.historyBoundary)throw new Error("Service attempt cannot bind clear history and session model projection together");return{ok:!0,context:Object.freeze({operationId:e.operationId,runtimeEpoch:e.runtimeEpoch,fence:e.fence,resources:o.resources,...o.historyBoundary?{historyBoundary:o.historyBoundary}:{},...o.sessionModelProjection?{sessionModelProjection:o.sessionModelProjection}:{},...o.canvasControl?{canvasControl:o.canvasControl}:{},...e.abortSignal?{abortSignal:e.abortSignal}:{},verifiedAttempt:t.attempt,origin:"service",[TRUSTED_CORE_ATTEMPT]:!0})}}export async function verifyAndCreateMessageServiceCoreExecutionAttempt(e){const t=e.verifier.verifyAndDecodeGrant(e.executionGrant);if(!t)return{ok:!1,violations:["GRANT_SIGNATURE_INVALID"]};if(t.compactHash!==e.request.executionGrantHash)return{ok:!1,violations:["GRANT_HASH_MISMATCH"]};const o=e.nowEpochSeconds??Math.floor(Date.now()/1e3),n=t.claims,r=2===e.request.schemaVersion?isCloudRuntimeExecutionGrant(t.claims,e.request,{...e.lease,leaseId:e.lease.leaseId??""},o,{allowExpiredAfterRetainedStart:!0===e.allowExpiredGrant&&"models/list"===e.request.method})?[]:["CLOUD_GRANT_REQUEST_MISMATCH"]:validateMessageGrant(n,e.request,e.lease,o,!0===e.allowExpiredGrant);if(r.length)return{ok:!1,violations:r};const i=await e.resolvePorts();if(i.historyBoundary&&"message-service"!==i.historyBoundary.kind)throw new Error("Service attempt cannot fall back to local conversation history");if(i.sessionModelProjection&&i.historyBoundary)throw new Error("Service attempt cannot bind clear history and session model projection together");const s=Object.freeze({[VERIFIED_MESSAGE_GRANT]:!0,claims:Object.freeze({...n}),compactHash:t.compactHash});return{ok:!0,grant:s,context:Object.freeze({operationId:e.request.operationId,runtimeEpoch:e.runtimeEpoch,resources:i.resources,...i.historyBoundary?{historyBoundary:i.historyBoundary}:{},...i.sessionModelProjection?{sessionModelProjection:i.sessionModelProjection}:{},...i.canvasControl?{canvasControl:i.canvasControl}:{},...e.abortSignal?{abortSignal:e.abortSignal}:{},verifiedMessageGrant:s,origin:"service",[TRUSTED_CORE_ATTEMPT]:!0})}}export function assertTrustedCoreExecutionAttempt(e,t){if(!e||!0!==e[TRUSTED_CORE_ATTEMPT]||e.operationId!==t.operationId||e.runtimeEpoch!==t.runtimeEpoch)throw new Error("untrusted or mismatched Core execution attempt context");if(!("service"!==e.origin||e.verifiedMessageGrant||e.fence&&e.verifiedAttempt))throw new Error("Service Core execution attempt lacks verified authorization")}function validateMessageGrant(e,t,o,n,r){if(!e||"object"!=typeof e)return["MALFORMED_EXECUTION_GRANT"];const i="historyGeneration"in e?e.historyGeneration:void 0,s="instance"===e.scope&&"runtimeAgentUid"in e?e.runtimeAgentUid:void 0;if(!exactKeys(e,["iss","aud","purpose","sub","jti","iat","exp","operationId","requestMessageId","instanceId","handlerIdentity","dispatchEpoch","method","requestHash","paramsHash","commandHash","idempotencyKeyHash","callerUserId","callerIdentityId","scope","command","executionMode",..."instance"===e.scope?void 0!==s?["runtimeAgentUid"]:[]:"agent"===e.scope?["platformAgentId","runtimeAgentUid"]:"conversation"===e.scope?["platformConversationId","runtimeConversationUid","runtimeSessionKey","platformAgentId","runtimeAgentUid",...void 0!==i?["historyGeneration"]:[]]:[]]))return["GRANT_FIELDS_INVALID"];const a=[];"beeos-gateway"===e.iss&&"beeos-runtime-command"===e.aud&&"runtime-execution-grant"===e.purpose&&e.sub===t.runtimeHandlerIdentity&&e.handlerIdentity===t.runtimeHandlerIdentity&&"start"===e.executionMode||a.push("GRANT_IDENTITY_MISMATCH"),(!Number.isSafeInteger(e.iat)||!Number.isSafeInteger(e.exp)||"string"!=typeof e.jti||0===e.jti.length||e.iat>n||e.exp<=e.iat||!r&&e.exp<=n||e.exp-e.iat>60)&&a.push("GRANT_TIME_INVALID");if([[e.operationId,t.operationId],[e.requestMessageId,t.requestMessageId],[e.instanceId,t.instanceId],[e.dispatchEpoch,t.dispatchEpoch],[e.method,t.method],[e.requestHash,t.requestHash],[e.paramsHash,t.paramsHash],[e.commandHash,t.commandHash],[e.idempotencyKeyHash,t.idempotencyKeyHash],[e.callerUserId,t.callerUserId],[e.callerIdentityId,t.callerIdentityId],[o.instanceId,t.instanceId],[o.handlerIdentity,t.runtimeHandlerIdentity],[o.runtimeEpoch,t.dispatchEpoch]].some(([e,t])=>e!==t)&&a.push("GRANT_REQUEST_MISMATCH"),semanticEqual(e.command,t.command)||a.push("GRANT_COMMAND_MISMATCH"),targetMatches(e,t)||a.push("GRANT_TARGET_MISMATCH"),"conversation"===e.scope){const o=t.command.context,n=o?.agent;o&&"conversation"===o.scope&&n?.platformAgentId===e.platformAgentId&&n?.runtimeAgentUid===e.runtimeAgentUid||a.push("GRANT_TARGET_MISMATCH");const r=o?.historyGeneration;"session/cancel"===e.method?"string"==typeof i&&0!==i.length&&i===r||a.push("GRANT_HISTORY_GENERATION_MISMATCH"):void 0!==i&&a.push("GRANT_HISTORY_GENERATION_MISMATCH")}return a}function semanticEqual(e,t){try{return canonicalizeJcs(e)===canonicalizeJcs(t)}catch{return!1}}function targetMatches(e,t){const o=t.command.context,n=t.request.params;if(!o||o.scope!==e.scope)return!1;if("instance"===o.scope){if(!("runtimeAgentUid"in e))return!0;const o=e.runtimeAgentUid;if("string"!=typeof o||0===o.length)return!1;const n=t.command.params;return n?.runtimeAgentUid===o}if("agent"===o.scope)return o.agent.runtimeAgentUid===e.runtimeAgentUid&&null!==n&&"object"==typeof n&&!Array.isArray(n)&&n.platformAgentId===e.platformAgentId;const r=null===n||"object"!=typeof n||Array.isArray(n)?void 0:"string"==typeof n.conversationId?n.conversationId:"string"==typeof n.sessionId?n.sessionId:void 0;return o.conversation.runtimeConversationUid===e.runtimeConversationUid&&o.conversation?.runtimeSessionKey===e.runtimeSessionKey&&r===e.platformConversationId}function exactKeys(e,t){const o=Object.keys(e).sort(),n=[...t].sort();return o.length===n.length&&o.every((e,t)=>e===n[t])}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export class MessageServiceConversationClearHttpAuthority{options;constructor(e){this.options=e}async
|
|
1
|
+
import{RUNTIME_HASH_DOMAINS}from"@beeos-ai/beeos-types/runtime";import{runtimeMessageHash}from"./runtime-message-channel-contract.js";export class MessageServiceConversationClearHttpAuthority{options;states=new Map;constructor(e){this.options=e}async prepare(e){const t=e.requestHash??"",n=[...this.states.values()].find(t=>t.intent.operationId===e.operationId),s=await this.post(e.conversationId,"prepare",{operationId:e.operationId,requestMessageId:e.requestMessageId??e.fence.claimId,requestHash:t,fence:e.fence}),r=this.intentFromResult(s,{...e,requestHash:t},n?.intent),i=n?.convergence??("prepared"===r.state?void 0:noActiveRunConvergence());return this.states.set(r.intentId,{intent:r,requestHash:t,...i?{convergence:i}:{}}),this.state(r.intentId)}async markResetting(e){const t=this.requireState(e.intent),n=await this.post(e.intent.conversationId,"resetting",{operationId:e.intent.operationId,requestMessageId:e.requestMessageId,requestHash:t.requestHash,boundaryToken:e.intent.intentId,fence:e.fence}),s=this.intentFromResult(n,{operationId:e.intent.operationId,conversationId:e.intent.conversationId,requestHash:t.requestHash,fence:e.fence},e.intent);return this.states.set(s.intentId,{...t,intent:s,convergence:e.runConvergence}),this.state(s.intentId)}async markResetDone(e){return await this.transition(e,"reset_done")}async commit(e){const t=this.requireState(e.intent),n=t.convergence??noActiveRunConvergence(),s={cleared:!0,historyGeneration:e.intent.toGeneration,runConvergence:n},r=runtimeMessageHash(RUNTIME_HASH_DOMAINS.coreResult,{method:"session/clear",result:s}),i={schemaVersion:1,operationId:e.intent.operationId,method:"session/clear",coreResultRef:`sha256:${r}`,coreResultHash:r},o=runtimeMessageHash(RUNTIME_HASH_DOMAINS.effectRecord,i),a={schemaVersion:1,operationId:e.intent.operationId,requestMessageId:e.requestMessageId,requestHash:t.requestHash,dispatchEpoch:e.fence.runtimeEpoch,method:"session/clear",effectRecord:i,effectRecordHash:o,committedAt:(new Date).toISOString()},c=await this.post(e.intent.conversationId,"commit",{operationId:e.intent.operationId,requestMessageId:e.requestMessageId,requestHash:t.requestHash,boundaryToken:e.intent.intentId,effectReceipt:a,fence:e.fence}),d=this.intentFromResult(c,{operationId:e.intent.operationId,conversationId:e.intent.conversationId,requestHash:t.requestHash,fence:e.fence},e.intent);return this.states.set(d.intentId,{...t,intent:d}),this.state(d.intentId)}async abortPrepared(e){return await this.transition(e,"abort")}async get(e){if(this.states.get(e.intentId))return this.state(e.intentId);throw new Error(`Message Service clear intent ${e.intentId} is unavailable in this runtime`)}async transition(e,t){const n=this.requireState(e.intent),s=await this.post(e.intent.conversationId,t,{operationId:e.intent.operationId,requestMessageId:e.requestMessageId,requestHash:n.requestHash,boundaryToken:e.intent.intentId,fence:e.fence}),r=this.intentFromResult(s,{operationId:e.intent.operationId,conversationId:e.intent.conversationId,requestHash:n.requestHash,fence:e.fence},e.intent);return this.states.set(r.intentId,{...n,intent:r}),this.state(r.intentId)}async post(e,t,n){const s={schemaVersion:1,operationId:n.operationId,requestMessageId:n.requestMessageId,requestHash:n.requestHash,...this.options.runtimeLeaseCredential?{runtimeLeaseCredential:this.options.runtimeLeaseCredential}:{},...n.boundaryToken?{boundaryToken:n.boundaryToken}:{},...n.effectReceipt?{effectReceipt:n.effectReceipt}:{}},r=await this.options.fetchAuthorized(this.url(e,t),{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify(s)}),i=await decodeRaw(r);if(!r.ok)throw new Error(`Message Service clear authority failed (${r.status})`);if(!record(i)||i.conversationId!==e||"string"!=typeof i.status||"string"!=typeof i.boundaryToken||"string"!=typeof i.historyGeneration||"string"!=typeof i.boundaryOffset)throw new Error("Message Service returned invalid history boundary receipt");return i}url(e,t){return new URL(`/api/v1/runtime/conversations/${encodeURIComponent(e)}/history-boundary/${t}`,this.options.messageServiceUrl)}intentFromResult(e,t,n){const s=BigInt(e.historyGeneration),r=!n||n.state!==e.status;return{intentId:e.boundaryToken,operationId:t.operationId,conversationId:t.conversationId,state:e.status,runtimeEpoch:t.fence.runtimeEpoch,fromGeneration:n?.fromGeneration??`${"committed"===e.status?s-1n:s}`,toGeneration:n?.toGeneration??`${"committed"===e.status?s:s+1n}`,boundaryOffset:n?.boundaryOffset??e.boundaryOffset,boundaryMessageId:n?.boundaryMessageId??`offset:${e.boundaryOffset}`,version:n?`${BigInt(n.version)+(r?1n:0n)}`:"1",createdAt:n?.createdAt??(new Date).toISOString(),updatedAt:(new Date).toISOString()}}requireState(e){const t=this.states.get(e.intentId);if(!t)throw new Error(`Message Service clear intent ${e.intentId} is unavailable`);return t}state(e){const t=this.states.get(e);if(!t)throw new Error(`Message Service clear intent ${e} is unavailable`);return{intent:t.intent,...t.convergence?{runConvergence:t.convergence}:{}}}}function noActiveRunConvergence(){return{state:"no_active_run",quiescence:{state:"verified",observedAt:(new Date).toISOString()}}}async function decodeRaw(e){try{return await e.json()}catch{return}}function record(e){return null!==e&&"object"==typeof e&&!Array.isArray(e)}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{validateClearResetConvergence,validateConversationClearIntent,validateConversationClearIntentReuse,canonicalizeJcs}from"@beeos-ai/beeos-types/runtime";export class MessageServiceConversationHistoryBoundary{deps;kind="message-service";preparedActiveRunRegistrationId;activeRunBinding;latestIntent;constructor(e){this.deps=e}async prepare(e){this.assertAttempt(e.operationId,e.runtimeEpoch);const t=await this.deps.authority.prepare({operationId:e.operationId,conversationId:e.conversationId,expectedGeneration:this.deps.expectedGeneration,fence:this.deps.fence});return this.assertState(t,{operationId:e.operationId,conversationId:e.conversationId}),this.rememberActiveRun(t),this.latestIntent=t.intent,t.intent}async markResetting(e,t){this.assertState({intent:e},{previous:e,requiredState:"prepared"});const n="no_active_run"===t.state?void 0:this.activeRunBinding?.activeRunRegistrationId??this.preparedActiveRunRegistrationId??await(this.deps.resolveActiveRunRegistrationId?.(t.runtimeRunId));if("no_active_run"!==t.state&&!n)throw new Error("active run registration authority is required before clear resetting");const r=await this.deps.authority.markResetting({intent:e,requestMessageId:this.deps.requestMessageId,fence:this.deps.fence,runConvergence:t,...n?{activeRunRegistrationId:n}:{}});return this.assertState(r,{previous:e,requiredState:"resetting",convergence:t}),this.rememberActiveRun(r),this.latestIntent=r.intent,r.intent}async loadRunConvergence(e){const t=await this.deps.authority.get({conversationId:e.conversationId,intentId:e.intentId});if(this.assertState(t,{previous:e}),this.rememberActiveRun(t),this.latestIntent=t.intent,!t.runConvergence)throw new Error("durable clear convergence receipt is missing");return this.assertConvergence(t.intent,t.runConvergence),t.runConvergence}async markResetDone(e){const t=await this.deps.authority.markResetDone({intent:e,requestMessageId:this.deps.requestMessageId,fence:this.deps.fence});if(this.assertState(t,{previous:e,requiredState:"reset_done"}),this.rememberActiveRun(t),this.latestIntent=t.intent,!t.runConvergence)throw new Error("reset_done receipt lost run convergence");return t.intent}async commit(e){const t=await this.deps.authority.commit({intent:e,requestMessageId:this.deps.requestMessageId,fence:this.deps.fence});if(this.assertState(t,{previous:e,requiredState:"committed"}),this.rememberActiveRun(t),this.latestIntent=t.intent,!t.runConvergence)throw new Error("committed receipt lost run convergence");return t.intent}async abortPrepared(e,t){if("prepared"!==e.state)throw new Error(`clear abort forbidden from ${e.state}`);const n=await this.deps.authority.abortPrepared({intent:e,requestMessageId:this.deps.requestMessageId,fence:this.deps.fence,reason:t});this.assertState(n,{previous:e,requiredState:"aborted"}),this.latestIntent=n.intent}committedIntentId(){if("committed"!==this.latestIntent?.state)throw new Error("conversation clear has no committed Message Service receipt");return this.latestIntent.intentId}assertAttempt(e,t){if(e!==this.deps.operationId||t!==this.deps.fence.runtimeEpoch)throw new Error("conversation clear attempt/fence mismatch")}rememberActiveRun(e){const t=e.runConvergence;if(!t){if(!e.activeRunRegistrationId)return;if(this.preparedActiveRunRegistrationId&&this.preparedActiveRunRegistrationId!==e.activeRunRegistrationId)throw new Error("conversation clear active run registration changed");return void(this.preparedActiveRunRegistrationId=e.activeRunRegistrationId)}if("no_active_run"===t.state){if(e.activeRunRegistrationId||this.activeRunBinding)throw new Error("no-active-run clear receipt carried an active run registration");return}if(!e.activeRunRegistrationId)throw new Error("durable clear convergence omitted active run registration");const n={activeRunRegistrationId:e.activeRunRegistrationId,runtimeRunId:t.runtimeRunId};if(this.preparedActiveRunRegistrationId&&this.preparedActiveRunRegistrationId!==n.activeRunRegistrationId)throw new Error("conversation clear active run registration changed");if(this.activeRunBinding&&(this.activeRunBinding.activeRunRegistrationId!==n.activeRunRegistrationId||this.activeRunBinding.runtimeRunId!==n.runtimeRunId))throw new Error("conversation clear active run binding changed");this.activeRunBinding=n}assertState(e,t){const n=validateConversationClearIntent(e.intent);if(n.length)throw new Error(`invalid clear intent: ${n.join(",")}`);if(e.intent.operationId!==(t.operationId??this.deps.operationId)||t.conversationId&&e.intent.conversationId!==t.conversationId||e.intent.runtimeEpoch!==this.deps.fence.runtimeEpoch||t.requiredState&&e.intent.state!==t.requiredState)throw new Error("conversation clear authority identity/state mismatch");if(t.previous){const n=validateConversationClearIntentReuse(t.previous,e.intent);if(n.length)throw new Error(`conversation clear boundary changed: ${n.join(",")}`);if(BigInt(e.intent.version)<BigInt(t.previous.version))throw new Error("conversation clear authority version regressed");if(t.requiredState&&e.intent.state!==t.previous.state&&BigInt(e.intent.version)!==BigInt(t.previous.version)+1n)throw new Error("conversation clear authority skipped a transition version")}if(t.convergence){if(!e.runConvergence||canonicalizeJcs(e.runConvergence)!==canonicalizeJcs(t.convergence))throw new Error("conversation clear authority changed the convergence receipt");this.assertConvergence(e.intent,e.runConvergence)}}assertConvergence(e,t){const n=validateClearResetConvergence(t,e.createdAt);if(n.length)throw new Error(`invalid durable clear convergence: ${n.join(",")}`)}}
|
|
1
|
+
import{validateClearResetConvergence,validateConversationClearIntent,validateConversationClearIntentReuse,canonicalizeJcs}from"@beeos-ai/beeos-types/runtime";export class MessageServiceConversationHistoryBoundary{deps;kind="message-service";preparedActiveRunRegistrationId;activeRunBinding;latestIntent;constructor(e){this.deps=e}async prepare(e){this.assertAttempt(e.operationId,e.runtimeEpoch);const t=await this.deps.authority.prepare({operationId:e.operationId,conversationId:e.conversationId,requestHash:this.deps.requestHash,requestMessageId:this.deps.requestMessageId,...this.deps.expectedGeneration?{expectedGeneration:this.deps.expectedGeneration}:{},fence:this.deps.fence});return this.assertState(t,{operationId:e.operationId,conversationId:e.conversationId}),this.rememberActiveRun(t),this.latestIntent=t.intent,t.intent}async markResetting(e,t){this.assertState({intent:e},{previous:e,requiredState:"prepared"});const n="no_active_run"===t.state?void 0:this.activeRunBinding?.activeRunRegistrationId??this.preparedActiveRunRegistrationId??await(this.deps.resolveActiveRunRegistrationId?.(t.runtimeRunId));if("no_active_run"!==t.state&&!n)throw new Error("active run registration authority is required before clear resetting");const r=await this.deps.authority.markResetting({intent:e,requestMessageId:this.deps.requestMessageId,fence:this.deps.fence,runConvergence:t,...n?{activeRunRegistrationId:n}:{}});return this.assertState(r,{previous:e,requiredState:"resetting",convergence:t}),this.rememberActiveRun(r),this.latestIntent=r.intent,r.intent}async loadRunConvergence(e){const t=await this.deps.authority.get({conversationId:e.conversationId,intentId:e.intentId});if(this.assertState(t,{previous:e}),this.rememberActiveRun(t),this.latestIntent=t.intent,!t.runConvergence)throw new Error("durable clear convergence receipt is missing");return this.assertConvergence(t.intent,t.runConvergence),t.runConvergence}async markResetDone(e){const t=await this.deps.authority.markResetDone({intent:e,requestMessageId:this.deps.requestMessageId,fence:this.deps.fence});if(this.assertState(t,{previous:e,requiredState:"reset_done"}),this.rememberActiveRun(t),this.latestIntent=t.intent,!t.runConvergence)throw new Error("reset_done receipt lost run convergence");return t.intent}async commit(e){const t=await this.deps.authority.commit({intent:e,requestMessageId:this.deps.requestMessageId,fence:this.deps.fence});if(this.assertState(t,{previous:e,requiredState:"committed"}),this.rememberActiveRun(t),this.latestIntent=t.intent,!t.runConvergence)throw new Error("committed receipt lost run convergence");return t.intent}async abortPrepared(e,t){if("prepared"!==e.state)throw new Error(`clear abort forbidden from ${e.state}`);const n=await this.deps.authority.abortPrepared({intent:e,requestMessageId:this.deps.requestMessageId,fence:this.deps.fence,reason:t});this.assertState(n,{previous:e,requiredState:"aborted"}),this.latestIntent=n.intent}committedIntentId(){if("committed"!==this.latestIntent?.state)throw new Error("conversation clear has no committed Message Service receipt");return this.latestIntent.intentId}assertAttempt(e,t){if(e!==this.deps.operationId||t!==this.deps.fence.runtimeEpoch)throw new Error("conversation clear attempt/fence mismatch")}rememberActiveRun(e){const t=e.runConvergence;if(!t){if(!e.activeRunRegistrationId)return;if(this.preparedActiveRunRegistrationId&&this.preparedActiveRunRegistrationId!==e.activeRunRegistrationId)throw new Error("conversation clear active run registration changed");return void(this.preparedActiveRunRegistrationId=e.activeRunRegistrationId)}if("no_active_run"===t.state){if(e.activeRunRegistrationId||this.activeRunBinding)throw new Error("no-active-run clear receipt carried an active run registration");return}if(!e.activeRunRegistrationId)throw new Error("durable clear convergence omitted active run registration");const n={activeRunRegistrationId:e.activeRunRegistrationId,runtimeRunId:t.runtimeRunId};if(this.preparedActiveRunRegistrationId&&this.preparedActiveRunRegistrationId!==n.activeRunRegistrationId)throw new Error("conversation clear active run registration changed");if(this.activeRunBinding&&(this.activeRunBinding.activeRunRegistrationId!==n.activeRunRegistrationId||this.activeRunBinding.runtimeRunId!==n.runtimeRunId))throw new Error("conversation clear active run binding changed");this.activeRunBinding=n}assertState(e,t){const n=validateConversationClearIntent(e.intent);if(n.length)throw new Error(`invalid clear intent: ${n.join(",")}`);if(e.intent.operationId!==(t.operationId??this.deps.operationId)||t.conversationId&&e.intent.conversationId!==t.conversationId||e.intent.runtimeEpoch!==this.deps.fence.runtimeEpoch||t.requiredState&&e.intent.state!==t.requiredState)throw new Error("conversation clear authority identity/state mismatch");if(t.previous){const n=validateConversationClearIntentReuse(t.previous,e.intent);if(n.length)throw new Error(`conversation clear boundary changed: ${n.join(",")}`);if(BigInt(e.intent.version)<BigInt(t.previous.version))throw new Error("conversation clear authority version regressed");if(t.requiredState&&e.intent.state!==t.previous.state&&BigInt(e.intent.version)!==BigInt(t.previous.version)+1n)throw new Error("conversation clear authority skipped a transition version")}if(t.convergence){if(!e.runConvergence||canonicalizeJcs(e.runConvergence)!==canonicalizeJcs(t.convergence))throw new Error("conversation clear authority changed the convergence receipt");this.assertConvergence(e.intent,e.runConvergence)}}assertConvergence(e,t){const n=validateClearResetConvergence(t,e.createdAt);if(n.length)throw new Error(`invalid durable clear convergence: ${n.join(",")}`)}}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{RUNTIME_ERROR_DESCRIPTORS,RUNTIME_HASH_DOMAINS,RUNTIME_INVOCATION_PARITY_ENTRIES,RUNTIME_INVOCATION_RECOVERY_CLASSES,SERVICE_CANONICAL_COMMAND_NAMES,mapCanonicalResultToService}from"@beeos-ai/beeos-types/runtime";import{reduceRuntimeRecoveryPolicy}from"@beeos-ai/beeos-types/runtime/internal";import{verifyAndCreateMessageServiceCoreExecutionAttempt}from"./core-execution-attempt-context.js";import{awaitRuntimeProjection,hasEffectReceiptHandoff,RuntimeCommandError}from"./runtime-command-dispatcher.js";import{createRuntimeAppendTimingRecorder}from"./runtime-append-timing.js";import{runtimeCommandIsReplayableQuery}from"./runtime-command-journal.js";import{RUNTIME_MESSAGE_TYPES,runtimeMessageBase,runtimeMessageHash,semanticEqual,validateRuntimeCancelEnvelope,validateRuntimeRequestEnvelope}from"./runtime-message-channel-contract.js";import{RuntimeResourceResolutionError}from"./runtime-operation-resource-resolver.js";import{RuntimeTransportFailurePolicy}from"./runtime-transport-failure-policy.js";const leaseStarvationReportIntervalMs=3e4;export class MsRuntimeMessageChannelAdapter{dispatcher;channel;verifier;ports;journal;getLease;options;controller;running;externalDeliveryOwner;workers=new Map;poisonJournal;renewTransportFailurePolicy=new RuntimeTransportFailurePolicy;leaseStarvedSince;leaseStarvationReportedAt=0;receiptHistory=this;constructor(e,t,a,o,r,n,i){if(this.dispatcher=e,this.channel=t,this.verifier=a,this.ports=o,this.journal=r,this.getLease=n,this.options=i,!i.poisonJournal)throw new Error("durable poison journal is required");if(this.poisonJournal=i.poisonJournal,!Number.isSafeInteger(i.poisonDeliveryLimit??3)||(i.poisonDeliveryLimit??3)<=0)throw new Error("poison delivery limit must be a positive integer")}start(){if(this.externalDeliveryOwner)return void this.externalDeliveryOwner.start();if(this.running)return;const e=new AbortController;this.controller=e;const t=this.loop(e.signal).catch(t=>{e.signal.aborted||this.observeDeliveryError(t)}).finally(()=>{this.running===t&&(this.running=void 0)});this.running=t}async stop(e){this.controller?.abort();for(const e of this.workers.values())e.cancel?.();if(this.externalDeliveryOwner)return await this.externalDeliveryOwner.stop(e),void await Promise.allSettled([...this.workers.values()].map(e=>e.completion));this.running&&await abortable(this.running,e)}attachExternalDeliveryOwner(e){if(this.running||this.externalDeliveryOwner)throw new Error("runtime delivery owner is already attached");this.externalDeliveryOwner=e}async acceptDelivery(e,t,a){if(this.currentLease(t),e.message.type===RUNTIME_MESSAGE_TYPES.cancel)return void await this.handleCancel(e,t);const o=validateRuntimeRequestEnvelope(e.message),r=o.ok?o.envelope.payload.operationId:e.message.channel_id;if(this.workers.has(r))return;const n={deliveryId:e.deliveryId,claimedLease:t,completion:Promise.resolve()};n.completion=this.handleRequest(e,t,a,e=>{n.cancel=e}).catch(e=>this.observeDeliveryError(e)).finally(()=>{this.workers.delete(r)}),this.workers.set(r,n),await n.completion}async acceptPoisonDelivery(e,t,a){await this.handlePoison(e,t,a)}async readOperationHistory(e){const t=this.getLease();if(!t)return{status:"unavailable"};try{return await this.channel.history({operationId:e.operationId,lease:t})}catch{return{status:"unavailable"}}}async loop(e){let t;(()=>{t||(t=setInterval(()=>{this.renewActive().catch(e=>this.observeDeliveryError(e))},this.options.renewIntervalMs??2e4),t.unref?.())})();try{for(;!e.aborted;){const t=this.getLease();if(!t){this.observeLeaseStarvation(),await delay(this.options.idleDelayMs??500,e);continue}let a;this.observeLeaseRestored();try{a=await this.channel.read({maxCount:this.options.readCount??32,blockMs:this.options.blockMs??5e3,lease:t,signal:e})}catch(t){if(e.aborted)return;this.observeDeliveryError(t),await delay(this.options.idleDelayMs??500,e);continue}for(const o of a){if(e.aborted)break;if(o.message.type===RUNTIME_MESSAGE_TYPES.cancel){await this.handleCancel(o,t).catch(e=>this.observeDeliveryError(e));continue}const a=o.message.channel_id;if(this.workers.has(a))continue;const r={deliveryId:o.deliveryId,claimedLease:t,completion:Promise.resolve()};r.completion=this.handleRequest(o,t,e,e=>{r.cancel=e}).catch(e=>this.observeDeliveryError(e)).finally(()=>{this.workers.delete(a)}),this.workers.set(a,r)}}}finally{t&&clearInterval(t),await Promise.allSettled([...this.workers.values()].map(e=>e.completion))}}observeDeliveryError(e){try{this.options.onDeliveryError?.(e)}catch{}}observeLeaseStarvation(){const e=Date.now();this.leaseStarvedSince??=e;const t=e-this.leaseStarvedSince;if(!(t<3e4||this.leaseStarvationReportedAt&&e-this.leaseStarvationReportedAt<3e4)){this.leaseStarvationReportedAt=e;try{this.options.onLeaseStarvation?.({starvedForMs:t,recovered:!1})}catch{}}}observeLeaseRestored(){const e=this.leaseStarvedSince;if(void 0===e)return;this.leaseStarvedSince=void 0;const t=this.leaseStarvationReportedAt;if(this.leaseStarvationReportedAt=0,t)try{this.options.onLeaseStarvation?.({starvedForMs:Date.now()-e,recovered:!0})}catch{}}observeRequestFailure(e,t,a){try{this.options.onRequestFailure?.({operationId:e,method:t,error:a})}catch{}}async renewActive(){const e=this.getLease();if(!e)return;const t=[];for(const a of this.workers.values())sameLeaseBinding(a.claimedLease,e)?t.push(a.deliveryId):a.cancel?.();if(!t.length)return;let a;try{a=await this.channel.renew({deliveryIds:t,lease:e})}catch(e){if(this.observeDeliveryError(e),this.renewTransportFailurePolicy.recordFailure()){const e=new Set(t);for(const t of this.workers.values())e.has(t.deliveryId)&&t.cancel?.()}return}if(this.renewTransportFailurePolicy.recordSuccess(),a.notPending.length){const e=new Set(a.notPending);for(const t of this.workers.values())e.has(t.deliveryId)&&t.cancel?.()}}async handleCancel(e,t){const a=validateRuntimeCancelEnvelope(e.message);if(!a.ok)return void await this.handlePoison(e,`invalid_cancel:${a.reason}`,t);const o=a.envelope.payload,r=await this.channel.history({operationId:o.operationId,lease:t});if("found"===r.status&&r.snapshot.terminal&&"final_message"===r.snapshot.terminalSource)return void await this.channel.acknowledge({deliveryIds:[e.deliveryId],lease:t});if("not_found"===r.status||"expired"===r.status)return void await this.channel.acknowledge({deliveryIds:[e.deliveryId],lease:t});const n=r.snapshot.request.payload;n.operationId===o.operationId&&n.requestMessageId===o.requestMessageId&&n.requestHash===o.requestHash&&n.dispatchEpoch===o.dispatchEpoch&&n.method===o.method?(await this.journal.observeCancellation({operationId:o.operationId,requestMessageId:o.requestMessageId,requestHash:o.requestHash,eventId:o.eventId}),this.workers.get(o.operationId)?.cancel?.(),await this.channel.acknowledge({deliveryIds:[e.deliveryId],lease:t})):await this.handlePoison(e,"cancel_history_mismatch",t)}async handleRequest(e,t,a,o){const r=validateRuntimeRequestEnvelope(e.message);if(!r.ok)return void await this.handlePoison(e,`invalid_request:${r.reason}`,t);const n=r.envelope.payload,i=e.executionGrant,s=createRuntimeAppendTimingRecorder("runtime-op",{operationId:n.operationId,method:n.method}),c=new AbortController;o(()=>c.abort());const d=async()=>{await this.channel.acknowledge({deliveryIds:[e.deliveryId],lease:this.currentLease(t)}),s.mark("ack")},l=await this.channel.history({operationId:n.operationId,lease:t});if(s.mark("history"),"not_found"===l.status||"expired"===l.status)return void await d();if(l.snapshot.terminal&&"final_message"===l.snapshot.terminalSource)return void await d();if(this.options.allowedMethods&&!this.options.allowedMethods.has(n.method))return await this.reject(n,"unsupported_method",t),void await d();if("deadline"===l.snapshot.terminalSource||Date.parse(n.executionDeadlineAt)<=Date.now()||!l.snapshot.started&&Date.parse(n.claimDeadlineAt)<=Date.now())return await this.reject(n,"execution_deadline_exceeded",t),void await d();const p=await this.journal.getCancellation(n.operationId);s.mark("journal-cancel-read");const u=l.snapshot.cancelRequested||null!==p;if(u&&"queued"===l.snapshot.status)return await this.reject(n,"cancelled_before_dispatch",t),void await d();if(n.instanceId!==t.instanceId||n.runtimeHandlerIdentity!==t.handlerIdentity||n.dispatchEpoch!==t.runtimeEpoch)return await this.reject(n,"authority_mismatch",t),void await d();const m=l.snapshot.request.payload;if(l.snapshot.operationId!==n.operationId||l.snapshot.requestMessageId!==n.requestMessageId||l.snapshot.requestHash!==n.requestHash||!semanticEqual(m,n))return void await this.handlePoison(e,"request_history_mismatch",t);const h=l.snapshot.started?.payload,I=void 0!==h&&h.operationId===n.operationId&&h.requestMessageId===n.requestMessageId&&h.requestHash===n.requestHash&&h.executionGrantHash===n.executionGrantHash&&h.handlerIdentity===t.handlerIdentity&&h.dispatchEpoch===t.runtimeEpoch&&h.method===n.method,E=await this.journal.get(n.operationId);s.mark("journal-prior");const v=runtimeCommandIsReplayableQuery(n.command.name),f=v?ephemeralQueryAccept(n):await this.journal.acceptMessageRequest({operationId:n.operationId,command:n.command.name,paramsHash:n.paramsHash,runtimeEpoch:n.dispatchEpoch,requestMessageId:n.requestMessageId,requestHash:n.requestHash});s.mark("journal-accept");const R=I?E:f,y=v?"execute":decideMessageServiceRecovery(l.snapshot,n,R,I);if("already_terminal"===y)return void await d();if(!CORE_RECOVERY_DECISIONS.has(y))return v||await this.markOutcomeUnknownIfMutable(f),await this.channel.append({operationId:n.operationId,lease:this.currentLease(t),type:RUNTIME_MESSAGE_TYPES.final,payload:failurePayload(n,new RuntimeCommandError("OUTCOME_UNKNOWN",`Types recovery policy requires ${y}; this runtime cannot safely replay Core`),(new Date).toISOString())}),void await d();if(await this.verifier.refresh(),s.mark("verifier-refresh"),!i)return I?await this.channel.append({operationId:n.operationId,lease:this.currentLease(t),type:RUNTIME_MESSAGE_TYPES.final,payload:failurePayload(n,new RuntimeCommandError("OUTCOME_UNKNOWN","retained started proof has no live execution grant"),(new Date).toISOString())}):await this.reject(n,"invalid_execution_grant",t),void await d();this.verifier.verifyAndDecodeGrant(i)||await this.verifier.refresh(!0);const _=()=>c.abort();let w;a.addEventListener("abort",_,{once:!0});try{w=await verifyAndCreateMessageServiceCoreExecutionAttempt({request:n,executionGrant:i,runtimeEpoch:t.runtimeEpoch,lease:t,verifier:this.verifier,allowExpiredGrant:I,abortSignal:c.signal,resolvePorts:async()=>await this.ports.resolve({request:n,executionGrant:i,command:n.command,getLease:()=>this.currentLease(t),...this.journalLineage(),journalRecord:f,abortSignal:c.signal})})}catch(e){if(a.removeEventListener("abort",_),c.abort(),!(e instanceof RuntimeResourceResolutionError))throw e;if("transient"===e.kind)return;return I?(v||await this.markOutcomeUnknownIfMutable(f),await this.channel.append({operationId:n.operationId,lease:this.currentLease(t),type:RUNTIME_MESSAGE_TYPES.final,payload:failurePayload(n,new RuntimeCommandError("OUTCOME_UNKNOWN","eager resources cannot be reconstructed after retained started proof"),(new Date).toISOString())})):await this.reject(n,"integrity_mismatch"===e.kind?"resource_integrity_mismatch":"resource_resolution_rejected",t),void await d()}if(!w.ok)return a.removeEventListener("abort",_),c.abort(),I?(v||await this.markOutcomeUnknownIfMutable(f),await this.channel.append({operationId:n.operationId,lease:this.currentLease(t),type:RUNTIME_MESSAGE_TYPES.final,payload:failurePayload(n,new RuntimeCommandError("OUTCOME_UNKNOWN","execution grant cannot be revalidated after retained started proof"),(new Date).toISOString())})):await this.reject(n,"invalid_execution_grant",t),void await d();if(!u||"prepared"!==f.phase&&"started"!==f.phase||c.abort(),c.signal.aborted&&!I)return a.removeEventListener("abort",_),await this.reject(n,"cancelled_before_dispatch",t),void await d();s.mark("attempt"),I?this.currentLease(t):(await this.channel.append({operationId:n.operationId,lease:this.currentLease(t),executionGrant:i,type:RUNTIME_MESSAGE_TYPES.started,payload:{...runtimeMessageBase(n),executionMode:"start",executionGrantHash:n.executionGrantHash,handlerIdentity:t.handlerIdentity,claimedAt:f.messageBinding?.acceptedAt??f.updatedAt}}),s.mark("append-started"));const g=this.dispatcher.dispatch(n.command,w.context);g.completion.catch(()=>{}),o(()=>{c.abort(),g.cancel()}),c.signal.aborted&&g.cancel();const M=this.forwardProgress(n,g.progress,t).catch(()=>{});let S,O=!1;try{if(("mcp/set"===n.method||"mcp/unset"===n.method)&&hasEffectReceiptHandoff(g)){const e=await g.effectCommitted;if(e){if(e.operationId!==n.operationId||e.method!==n.command.name)throw new RuntimeCommandError("OUTCOME_UNKNOWN","Core MCP receipt correlation mismatch");const a=runtimeMessageHash(RUNTIME_HASH_DOMAINS.coreResult,{method:n.method,result:e.result}),o={schemaVersion:1,operationId:n.operationId,method:n.method,coreResultRef:`sha256:${a}`,coreResultHash:a};S=runtimeMessageHash(RUNTIME_HASH_DOMAINS.effectRecord,o),O=!0,await this.channel.append({operationId:n.operationId,lease:this.currentLease(t),type:RUNTIME_MESSAGE_TYPES.effectCommitted,payload:{...runtimeMessageBase(n),effectRecord:o,effectRecordHash:S,committedAt:e.committedAt}}),await g.acknowledgeEffectReceipt({operationId:n.operationId,method:n.command.name,effectRecordHash:S})}}const a=await g.completion;if(await M,s.mark("core"),v){const o=(new Date).toISOString();let r;try{r=sanitizeRuntimeServiceResult(n.method,a,w.context,void 0)}catch(e){throw new RuntimeCommandError("OUTCOME_UNKNOWN","committed Core result cannot be mapped safely",void 0,{cause:e})}const i=runtimeMessageHash(RUNTIME_HASH_DOMAINS.serviceResult,{method:n.method,result:r}),c=await this.channel.append({operationId:n.operationId,lease:this.currentLease(t),type:RUNTIME_MESSAGE_TYPES.final,payload:{...runtimeMessageBase(n),outcome:"succeeded",response:{jsonrpc:"2.0",id:n.request.id,result:r},serviceResultHash:i,completedAt:o}});return s.mark("append-final"),this.observeRequestTerminalAccepted(e.deliveryId,c.message),await d(),void this.observeRequestAcknowledged(e.deliveryId,c.message)}const o=await this.journal.get(n.operationId);if(s.mark("journal-after-core"),!o||!isCommittedCorePhase(o.phase))throw new RuntimeCommandError("OUTCOME_UNKNOWN","Core completed without a committed local journal record");const r=o.updatedAt;let i,c;if("query"!==RUNTIME_INVOCATION_RECOVERY_CLASSES[n.method]){const e=isAgentProjectionMethod(n.method),c=g.getSnapshot().projection,l=o.agentProjectionIntent??o.agentProjectionPreparationFailure;if(e&&!l)throw new RuntimeCommandError("OUTCOME_UNKNOWN","agent effect committed without an atomic projection preparation record");if(!S){const e=l?.coreResultHash??runtimeMessageHash(RUNTIME_HASH_DOMAINS.coreResult,{method:n.method,result:a}),i=o.agentProjectionIntent?.projectionRevision??(methodRequiresProjectionRevision(n.method)?c?.revision??`${Math.max(0,o.revision)}`:void 0),d={schemaVersion:1,operationId:n.operationId,method:n.method,coreResultRef:`sha256:${e}`,coreResultHash:e,...i?{projectionRevision:i}:{}};S=runtimeMessageHash(RUNTIME_HASH_DOMAINS.effectRecord,d),O=!0,await this.channel.append({operationId:n.operationId,lease:this.currentLease(t),type:RUNTIME_MESSAGE_TYPES.effectCommitted,payload:{...runtimeMessageBase(n),effectRecord:d,effectRecordHash:S,committedAt:r}}),s.mark("append-effect")}let p=e?await awaitRuntimeProjection(g):c;if("session/set_model"===n.method&&"synced"!==p?.state&&(p=await this.projectSessionModelAfterEffect(n,w.context,o,p),s.mark("set-model-projection")),e&&o.agentProjectionIntent&&(!p||p.revision!==o.agentProjectionIntent.projectionRevision))throw new RuntimeCommandError("OUTCOME_UNKNOWN","agent projection receipt revision mismatch");if("pending"===p?.state)return void await this.channel.append({operationId:n.operationId,lease:this.currentLease(t),type:RUNTIME_MESSAGE_TYPES.projectionPending,payload:{...runtimeMessageBase(n),revision:p.revision}});if("synced"===p?.state&&await this.publishProjectionSynced(n,t,{...p,state:"synced"}),"blocked"===p?.state){await this.channel.append({operationId:n.operationId,lease:this.currentLease(t),type:RUNTIME_MESSAGE_TYPES.projectionBlocked,payload:{...runtimeMessageBase(n),revision:p.revision,errorCode:"PROJECTION_BLOCKED"}});const e=RUNTIME_ERROR_DESCRIPTORS.PROJECTION_BLOCKED;return await this.channel.append({operationId:n.operationId,lease:this.currentLease(t),type:RUNTIME_MESSAGE_TYPES.final,payload:{...runtimeMessageBase(n),outcome:"projection_blocked",response:{jsonrpc:"2.0",id:n.request.id,error:{code:e.jsonRpcCode,message:"PROJECTION_BLOCKED",data:{code:"PROJECTION_BLOCKED",operationId:n.operationId,effectState:"committed",retryMode:e.retryMode}}},effectRecordHash:S,completedAt:r}}),void await d()}i=e?p?.platformAgentId:void 0}try{c=sanitizeRuntimeServiceResult(n.method,a,w.context,i)}catch(e){throw new RuntimeCommandError("OUTCOME_UNKNOWN","committed Core result cannot be mapped safely",void 0,{cause:e})}const l=runtimeMessageHash(RUNTIME_HASH_DOMAINS.serviceResult,{method:n.method,result:c});O=!0;const p=await this.channel.append({operationId:n.operationId,lease:this.currentLease(t),type:RUNTIME_MESSAGE_TYPES.final,payload:{...runtimeMessageBase(n),outcome:"succeeded",response:{jsonrpc:"2.0",id:n.request.id,result:c},serviceResultHash:l,...S?{effectRecordHash:S}:{},completedAt:r}});if(s.mark("append-final"),this.observeRequestTerminalAccepted(e.deliveryId,p.message),("mcp/set"===n.method||"mcp/unset"===n.method)&&hasEffectReceiptHandoff(g)&&"function"==typeof g.acknowledgeTerminalReceipt){if(!S)throw new RuntimeCommandError("OUTCOME_UNKNOWN","MCP terminal response lacks its committed effect hash");await g.acknowledgeTerminalReceipt({operationId:n.operationId,method:n.command.name,effectRecordHash:S,terminalMessageId:p.message.message_id})}await d(),this.observeRequestAcknowledged(e.deliveryId,p.message)}catch(e){const a=await this.journal.get(n.operationId);if(a&&isCommittedCorePhase(a.phase)&&O)throw e;const o=!a||!isCommittedCorePhase(a.phase)||e instanceof RuntimeCommandError&&"OUTCOME_UNKNOWN"===e.code?e:new RuntimeCommandError("OUTCOME_UNKNOWN","Core effect committed before terminal mapping",void 0,{cause:e});this.observeRequestFailure(n.operationId,n.method,o);const r=failurePayload(n,o,a?.updatedAt??(new Date).toISOString());await this.channel.append({operationId:n.operationId,lease:this.currentLease(t),type:RUNTIME_MESSAGE_TYPES.final,payload:r}),s.mark("append-terminal-failure"),await d()}finally{s.finish(),c.abort(),a.removeEventListener("abort",_)}}async handlePoison(e,t,a){this.currentLease(a);(await this.poisonJournal.record({delivery:e,reason:durablePoisonReason(t),limit:this.options.poisonDeliveryLimit??3})).terminal&&await this.channel.acknowledge({deliveryIds:[e.deliveryId],lease:this.currentLease(a)})}observeRequestTerminalAccepted(e,t){try{this.options.onRequestTerminalAccepted?.({deliveryId:e,message:t})}catch{}}observeRequestAcknowledged(e,t){try{this.options.onRequestAcknowledged?.({deliveryId:e,message:t})}catch{}}async forwardProgress(e,t,a){for await(const o of t)await this.channel.append({operationId:e.operationId,lease:this.currentLease(a),type:RUNTIME_MESSAGE_TYPES.progress,payload:{...runtimeMessageBase(e),phase:o.phase,progress:o}})}async reject(e,t,a){await this.channel.append({operationId:e.operationId,lease:this.currentLease(a),type:RUNTIME_MESSAGE_TYPES.final,payload:prestartFailurePayload(e,t)})}async markOutcomeUnknownIfMutable(e){"prepared"!==e.phase&&"started"!==e.phase||await this.journal.transition({operationId:e.operationId,expectedRevision:e.revision,phase:"outcome_unknown",errorCode:"OUTCOME_UNKNOWN"})}async projectSessionModelAfterEffect(e,t,a,o){const r=Object.freeze({state:"pending",revision:o?.revision??`${Math.max(0,a.revision)}`}),n=e.command;if("session.setModel"!==n.name||"conversation"!==n.context.scope)return r;const i=t.sessionModelProjection;if(!i)return r;try{await i.project({conversationId:n.context.conversation.runtimeConversationUid,modelOverrideId:n.params.modelOverrideId});const e=Object.freeze({state:"synced",revision:r.revision});try{const t=await this.journal.get(a.operationId)??a;if("projection_committed"===t.phase&&"synced"===t.projectionReceipt?.snapshot.state)return Object.freeze({state:"synced",revision:t.projectionReceipt.snapshot.revision});await this.journal.transition({operationId:t.operationId,expectedRevision:t.revision,phase:"projection_committed",projectionSnapshot:e})}catch{}return e}catch(e){return this.observeDeliveryError(e),r}}async publishProjectionSynced(e,t,a){const o=this.currentLease(t),r=await this.channel.history({operationId:e.operationId,lease:o});if("found"===r.status&&"synced"===r.snapshot.projectionState)return;const n={...runtimeMessageBase(e),revision:a.revision};("found"!==r.status||"reserved"===r.snapshot.projectionState||"pending"!==r.snapshot.projectionState&&void 0===r.snapshot.projectionPending)&&await this.channel.append({operationId:e.operationId,lease:o,type:RUNTIME_MESSAGE_TYPES.projectionPending,payload:n}),await this.channel.append({operationId:e.operationId,lease:o,type:RUNTIME_MESSAGE_TYPES.projectionSynced,payload:{...n,...a.platformAgentId?{platformAgentId:a.platformAgentId}:{}}})}journalLineage(){return this.options.getJournalLineage?.()??{journalStoreId:this.options.journalStoreId,journalGeneration:this.options.journalGeneration}}currentLease(e){const t=this.getLease();if(!t||t.instanceId!==e.instanceId||t.leaseId!==e.leaseId||t.runtimeEpoch!==e.runtimeEpoch||t.handlerIdentity!==e.handlerIdentity||!Number.isFinite(Date.parse(t.leaseExpiresAt))||Date.parse(t.leaseExpiresAt)<=Date.now())throw new RuntimeCommandError("INSTANCE_EPOCH_MISMATCH","runtime message lease changed");return t}}function ephemeralQueryAccept(e){return{operationId:e.operationId,command:e.command.name,paramsHash:e.paramsHash,runtimeEpoch:e.dispatchEpoch,phase:"prepared",revision:1,updatedAt:(new Date).toISOString(),messageBinding:{requestMessageId:e.requestMessageId,requestHash:e.requestHash,acceptedAt:(new Date).toISOString()}}}function durablePoisonReason(e){const t=e.replace(/[^a-z0-9_:.-]+/gi,"_").replace(/^_+|_+$/g,"").slice(0,256);if(!t)throw new Error("poison reason is empty");return t}function isCommittedCorePhase(e){return"effect_committed"===e||"projection_committed"===e}const CORE_RECOVERY_DECISIONS=new Set(["execute","resume","replay_effect_committed","resume_projection","publish_committed_result","publish_projected_result","publish_terminal_result"]);function decideMessageServiceRecovery(e,t,a,o){const r=RUNTIME_INVOCATION_PARITY_ENTRIES.find(e=>e.acpMethod===t.method),n=e.terminal?"terminal":"blocked"===e.projectionState?"projection_blocked":"synced"===e.projectionState?"projection_synced":"pending"===e.projectionState?"projection_pending":e.effectCommitted?"runtime_committed":o?"running":"queued",i="failed"===a?.phase||"outcome_unknown"===a?.phase?"terminal":a?.phase,s=a&&a.runtimeEpoch===t.dispatchEpoch&&(void 0===a.messageBinding||a.messageBinding.requestMessageId===t.requestMessageId&&a.messageBinding.requestHash===t.requestHash)?"continuous":"discontinuous";return reduceRuntimeRecoveryPolicy({recoveryClass:r?.recoveryClass??RUNTIME_INVOCATION_RECOVERY_CLASSES[t.method],projectionRequired:"mcp/set"===t.method||"mcp/unset"===t.method||(r?.projectionRequired??!1),messageServicePhase:n,executionBoundaryCrossed:o,lineageContinuity:s,journalState:a?"available":"unavailable",...i?{journalPhase:i}:{}})}export function sanitizeRuntimeServiceResult(e,t,a,o){if("session/clear"===e)return serviceClearResult(t,a?.historyBoundary);if("session/set_model"===e)return serviceSetModelResult(t,a);if(!(e in SERVICE_CANONICAL_COMMAND_NAMES))return t;const r="cron/status"===e&&record(t)?{enabled:t.enabled,jobCount:t.jobCount,..."number"==typeof t.nextWakeAtMs?{nextWakeAtMs:t.nextWakeAtMs}:{}}:t;return mapCanonicalResultToService(e,r,{...o?{platformAgentId:o}:{},classifySkillOrigin:e=>e.bundled?{origin:"bundled",slug:e.slug}:{origin:"legacy_runtime",slug:e.slug},resolveMcpView:e=>({name:e.name,transport:e.transport,origin:"legacy_runtime",...void 0===e.connected?{}:{connected:e.connected},...void 0===e.connectionTimeoutMs?{}:{connectionTimeoutMs:e.connectionTimeoutMs}}),sanitizeDiagnostic:e=>/^[A-Z][A-Z0-9_]{0,63}$/.test(e)?e:"RUNTIME_EXECUTION_FAILED"})}function serviceClearResult(e,t){if(!record(e)||!0!==e.cleared||"string"!=typeof e.historyGeneration||!t||"message-service"!==t.kind)throw new RuntimeCommandError("OUTCOME_UNKNOWN","committed clear result lacks its Message Service receipt");const a=t;if(!a.committedIntentId)throw new RuntimeCommandError("OUTCOME_UNKNOWN","Message Service clear receipt is unavailable");return{...e,clearIntentId:a.committedIntentId()}}function serviceSetModelResult(e,t){if(!record(e)||"string"!=typeof e.effectiveModelId||!t)throw new RuntimeCommandError("OUTCOME_UNKNOWN","committed set_model result is invalid");const a=t.verifiedMessageGrant?.claims.command;if(!a||"session.setModel"!==a.name||"conversation"!==a.context.scope)throw new RuntimeCommandError("OUTCOME_UNKNOWN","set_model Service target receipt is unavailable");const o=a.params.modelOverrideId;return{conversationId:a.context.conversation.runtimeConversationUid,...null===o?{defaultModelId:e.effectiveModelId}:{},modelOverrideId:o,effectiveModelId:e.effectiveModelId,effectState:"committed",projectionState:"synced",operationId:t.operationId}}function isAgentProjectionMethod(e){return"agent/create"===e||"agent/update"===e||"agent/delete"===e||"agent/applyTemplate"===e}function methodRequiresProjectionRevision(e){return isAgentProjectionMethod(e)||"session/set_model"===e}function record(e){return null!==e&&"object"==typeof e&&!Array.isArray(e)}function sameLeaseBinding(e,t){return e.instanceId===t.instanceId&&e.leaseId===t.leaseId&&e.handlerIdentity===t.handlerIdentity&&e.runtimeEpoch===t.runtimeEpoch}function failurePayload(e,t,a){const o=t instanceof RuntimeCommandError?t.code:"RUNTIME_EXECUTION_FAILED",r=o in RUNTIME_ERROR_DESCRIPTORS?o:"RUNTIME_EXECUTION_FAILED",n="OUTCOME_UNKNOWN"===r||"INSTANCE_EPOCH_MISMATCH"===r?"outcome_unknown":"COMMAND_EXPIRED"===r?"expired":"OPERATION_CANCELLED"===r?"cancelled":"failed",i="outcome_unknown"===n?"OUTCOME_UNKNOWN":r,s=RUNTIME_ERROR_DESCRIPTORS[i],c="outcome_unknown"===n?"outcome_unknown":n;return{...runtimeMessageBase(e),outcome:n,response:{jsonrpc:"2.0",id:e.request.id,error:{code:s.jsonRpcCode,message:i,data:{code:i,operationId:e.operationId,effectState:c,retryMode:s.retryMode}}},completedAt:a}}function prestartFailurePayload(e,t){const a="invalid_execution_grant"===t?"INVALID_EXECUTION_GRANT":"unsupported_method"===t?"METHOD_NOT_SUPPORTED":"authority_mismatch"===t?"INSTANCE_EPOCH_MISMATCH":"execution_deadline_exceeded"===t?"COMMAND_EXPIRED":"cancelled_before_dispatch"===t?"OPERATION_CANCELLED":"resource_resolution_rejected"===t?"INVALID_INVOCATION":"RUNTIME_EXECUTION_FAILED",o=RUNTIME_ERROR_DESCRIPTORS[a in RUNTIME_ERROR_DESCRIPTORS?a:"RUNTIME_EXECUTION_FAILED"],r="COMMAND_EXPIRED"===a?"expired":"OPERATION_CANCELLED"===a?"cancelled":"failed";return{...runtimeMessageBase(e),outcome:r,response:{jsonrpc:"2.0",id:e.request.id,error:{code:o.jsonRpcCode,message:a,data:{code:a,operationId:e.operationId,effectState:r,retryMode:o.retryMode}}},completedAt:(new Date).toISOString()}}function abortable(e,t){return t?t.aborted?Promise.resolve():new Promise((a,o)=>{const r=()=>a();t.addEventListener("abort",r,{once:!0}),e.then(()=>{t.removeEventListener("abort",r),a()},e=>{t.removeEventListener("abort",r),o(e)})}):e}function delay(e,t){return new Promise(a=>{if(t?.aborted)return a();const o=()=>{clearTimeout(r),a()},r=setTimeout(()=>{t?.removeEventListener("abort",o),a()},e);t?.addEventListener("abort",o,{once:!0})})}
|
|
1
|
+
import{RUNTIME_ERROR_DESCRIPTORS,RUNTIME_HASH_DOMAINS,RUNTIME_INVOCATION_PARITY_ENTRIES,RUNTIME_INVOCATION_RECOVERY_CLASSES,SERVICE_CANONICAL_COMMAND_NAMES,mapCanonicalResultToService}from"@beeos-ai/beeos-types/runtime";import{reduceRuntimeRecoveryPolicy}from"@beeos-ai/beeos-types/runtime/internal";import{verifyAndCreateMessageServiceCoreExecutionAttempt}from"./core-execution-attempt-context.js";import{awaitRuntimeProjection,hasEffectReceiptHandoff,RuntimeCommandError}from"./runtime-command-dispatcher.js";import{createRuntimeAppendTimingRecorder}from"./runtime-append-timing.js";import{runtimeCommandIsReplayableQuery}from"./runtime-command-journal.js";import{RUNTIME_MESSAGE_TYPES,runtimeMessageBase,runtimeMessageHash,semanticEqual,validateRuntimeCancelEnvelope,validateRuntimeRequestEnvelope}from"./runtime-message-channel-contract.js";import{RuntimeResourceResolutionError}from"./runtime-operation-resource-resolver.js";import{RuntimeTransportFailurePolicy}from"@beeos-ai/cloud-agent-sdk";const leaseStarvationReportIntervalMs=3e4;export class MsRuntimeMessageChannelAdapter{dispatcher;channel;verifier;ports;journal;getLease;options;controller;running;externalDeliveryOwner;workers=new Map;poisonJournal;renewTransportFailurePolicy=new RuntimeTransportFailurePolicy;leaseStarvedSince;leaseStarvationReportedAt=0;receiptHistory=this;constructor(e,t,a,o,r,n,i){if(this.dispatcher=e,this.channel=t,this.verifier=a,this.ports=o,this.journal=r,this.getLease=n,this.options=i,!i.poisonJournal)throw new Error("durable poison journal is required");if(this.poisonJournal=i.poisonJournal,!Number.isSafeInteger(i.poisonDeliveryLimit??3)||(i.poisonDeliveryLimit??3)<=0)throw new Error("poison delivery limit must be a positive integer")}start(){if(this.externalDeliveryOwner)return void this.externalDeliveryOwner.start();if(this.running)return;const e=new AbortController;this.controller=e;const t=this.loop(e.signal).catch(t=>{e.signal.aborted||this.observeDeliveryError(t)}).finally(()=>{this.running===t&&(this.running=void 0)});this.running=t}async stop(e){this.controller?.abort();for(const e of this.workers.values())e.cancel?.();if(this.externalDeliveryOwner)return await this.externalDeliveryOwner.stop(e),void await Promise.allSettled([...this.workers.values()].map(e=>e.completion));this.running&&await abortable(this.running,e)}attachExternalDeliveryOwner(e){if(this.running||this.externalDeliveryOwner)throw new Error("runtime delivery owner is already attached");this.externalDeliveryOwner=e}async acceptDelivery(e,t,a){if(this.currentLease(t),e.message.type===RUNTIME_MESSAGE_TYPES.cancel)return void await this.handleCancel(e,t);const o=validateRuntimeRequestEnvelope(e.message),r=o.ok?o.envelope.payload.operationId:e.message.channel_id;if(this.workers.has(r))return;const n={deliveryId:e.deliveryId,claimedLease:t,completion:Promise.resolve()};n.completion=this.handleRequest(e,t,a,e=>{n.cancel=e}).catch(e=>this.observeDeliveryError(e)).finally(()=>{this.workers.delete(r)}),this.workers.set(r,n),await n.completion}async acceptPoisonDelivery(e,t,a){await this.handlePoison(e,t,a)}async readOperationHistory(e){const t=this.getLease();if(!t)return{status:"unavailable"};try{return await this.channel.history({operationId:e.operationId,lease:t})}catch{return{status:"unavailable"}}}async loop(e){let t;(()=>{t||(t=setInterval(()=>{this.renewActive().catch(e=>this.observeDeliveryError(e))},this.options.renewIntervalMs??2e4),t.unref?.())})();try{for(;!e.aborted;){const t=this.getLease();if(!t){this.observeLeaseStarvation(),await delay(this.options.idleDelayMs??500,e);continue}let a;this.observeLeaseRestored();try{a=await this.channel.read({maxCount:this.options.readCount??32,blockMs:this.options.blockMs??5e3,lease:t,signal:e})}catch(t){if(e.aborted)return;this.observeDeliveryError(t),await delay(this.options.idleDelayMs??500,e);continue}for(const o of a){if(e.aborted)break;if(o.message.type===RUNTIME_MESSAGE_TYPES.cancel){await this.handleCancel(o,t).catch(e=>this.observeDeliveryError(e));continue}const a=o.message.channel_id;if(this.workers.has(a))continue;const r={deliveryId:o.deliveryId,claimedLease:t,completion:Promise.resolve()};r.completion=this.handleRequest(o,t,e,e=>{r.cancel=e}).catch(e=>this.observeDeliveryError(e)).finally(()=>{this.workers.delete(a)}),this.workers.set(a,r)}}}finally{t&&clearInterval(t),await Promise.allSettled([...this.workers.values()].map(e=>e.completion))}}observeDeliveryError(e){try{this.options.onDeliveryError?.(e)}catch{}}observeLeaseStarvation(){const e=Date.now();this.leaseStarvedSince??=e;const t=e-this.leaseStarvedSince;if(!(t<3e4||this.leaseStarvationReportedAt&&e-this.leaseStarvationReportedAt<3e4)){this.leaseStarvationReportedAt=e;try{this.options.onLeaseStarvation?.({starvedForMs:t,recovered:!1})}catch{}}}observeLeaseRestored(){const e=this.leaseStarvedSince;if(void 0===e)return;this.leaseStarvedSince=void 0;const t=this.leaseStarvationReportedAt;if(this.leaseStarvationReportedAt=0,t)try{this.options.onLeaseStarvation?.({starvedForMs:Date.now()-e,recovered:!0})}catch{}}observeRequestFailure(e,t,a){try{this.options.onRequestFailure?.({operationId:e,method:t,error:a})}catch{}}async renewActive(){const e=this.getLease();if(!e)return;const t=[];for(const a of this.workers.values())sameLeaseBinding(a.claimedLease,e)?t.push(a.deliveryId):a.cancel?.();if(!t.length)return;let a;try{a=await this.channel.renew({deliveryIds:t,lease:e})}catch(e){if(this.observeDeliveryError(e),this.renewTransportFailurePolicy.recordFailure()){const e=new Set(t);for(const t of this.workers.values())e.has(t.deliveryId)&&t.cancel?.()}return}if(this.renewTransportFailurePolicy.recordSuccess(),a.notPending.length){const e=new Set(a.notPending);for(const t of this.workers.values())e.has(t.deliveryId)&&t.cancel?.()}}async handleCancel(e,t){const a=validateRuntimeCancelEnvelope(e.message);if(!a.ok)return void await this.handlePoison(e,`invalid_cancel:${a.reason}`,t);const o=a.envelope.payload,r=await this.channel.history({operationId:o.operationId,lease:t});if("found"===r.status&&r.snapshot.terminal&&"final_message"===r.snapshot.terminalSource)return void await this.channel.acknowledge({deliveryIds:[e.deliveryId],lease:t});if("not_found"===r.status||"expired"===r.status)return void await this.channel.acknowledge({deliveryIds:[e.deliveryId],lease:t});const n=r.snapshot.request.payload;n.operationId===o.operationId&&n.requestMessageId===o.requestMessageId&&n.requestHash===o.requestHash&&n.dispatchEpoch===o.dispatchEpoch&&n.method===o.method?(await this.journal.observeCancellation({operationId:o.operationId,requestMessageId:o.requestMessageId,requestHash:o.requestHash,eventId:o.eventId}),this.workers.get(o.operationId)?.cancel?.(),await this.channel.acknowledge({deliveryIds:[e.deliveryId],lease:t})):await this.handlePoison(e,"cancel_history_mismatch",t)}async handleRequest(e,t,a,o){const r=validateRuntimeRequestEnvelope(e.message);if(!r.ok)return void await this.handlePoison(e,`invalid_request:${r.reason}`,t);if(r.envelope.payload.schemaVersion!==(this.options.requestSchemaVersion??1))return void await this.handlePoison(e,"request_realm_mismatch",t);const n=r.envelope.payload,i=e.executionGrant,s=createRuntimeAppendTimingRecorder("runtime-op",{operationId:n.operationId,method:n.method}),c=new AbortController;o(()=>c.abort());const d=async()=>{await this.channel.acknowledge({deliveryIds:[e.deliveryId],lease:this.currentLease(t)}),s.mark("ack")},l=await this.channel.history({operationId:n.operationId,lease:t});if(s.mark("history"),"not_found"===l.status||"expired"===l.status)return void await d();if(l.snapshot.terminal&&"final_message"===l.snapshot.terminalSource)return void await d();if(this.options.allowedMethods&&!this.options.allowedMethods.has(n.method))return await this.reject(n,"unsupported_method",t),void await d();if("deadline"===l.snapshot.terminalSource||Date.parse(n.executionDeadlineAt)<=Date.now()||!l.snapshot.started&&Date.parse(n.claimDeadlineAt)<=Date.now())return await this.reject(n,"execution_deadline_exceeded",t),void await d();const p=await this.journal.getCancellation(n.operationId);s.mark("journal-cancel-read");const u=l.snapshot.cancelRequested||null!==p;if(u&&"queued"===l.snapshot.status)return await this.reject(n,"cancelled_before_dispatch",t),void await d();if(n.instanceId!==t.instanceId||n.runtimeHandlerIdentity!==t.handlerIdentity||n.dispatchEpoch!==t.runtimeEpoch)return await this.reject(n,"authority_mismatch",t),void await d();const m=l.snapshot.request.payload;if(l.snapshot.operationId!==n.operationId||l.snapshot.requestMessageId!==n.requestMessageId||l.snapshot.requestHash!==n.requestHash||!semanticEqual(m,n))return void await this.handlePoison(e,"request_history_mismatch",t);const h=l.snapshot.started?.payload,I=void 0!==h&&h.operationId===n.operationId&&h.requestMessageId===n.requestMessageId&&h.requestHash===n.requestHash&&h.executionGrantHash===n.executionGrantHash&&h.handlerIdentity===t.handlerIdentity&&h.dispatchEpoch===t.runtimeEpoch&&h.method===n.method,E=await this.journal.get(n.operationId);s.mark("journal-prior");const v=runtimeCommandIsReplayableQuery(n.command.name),f=v?ephemeralQueryAccept(n):await this.journal.acceptMessageRequest({operationId:n.operationId,command:n.command.name,paramsHash:n.paramsHash,runtimeEpoch:n.dispatchEpoch,requestMessageId:n.requestMessageId,requestHash:n.requestHash});s.mark("journal-accept");const R=I?E:f,y=v?"execute":decideMessageServiceRecovery(l.snapshot,n,R,I);if("already_terminal"===y)return void await d();if(!CORE_RECOVERY_DECISIONS.has(y))return v||await this.markOutcomeUnknownIfMutable(f),await this.channel.append({operationId:n.operationId,lease:this.currentLease(t),type:RUNTIME_MESSAGE_TYPES.final,payload:failurePayload(n,new RuntimeCommandError("OUTCOME_UNKNOWN",`Types recovery policy requires ${y}; this runtime cannot safely replay Core`),(new Date).toISOString())}),void await d();if(await this.verifier.refresh(),s.mark("verifier-refresh"),!i)return I?await this.channel.append({operationId:n.operationId,lease:this.currentLease(t),type:RUNTIME_MESSAGE_TYPES.final,payload:failurePayload(n,new RuntimeCommandError("OUTCOME_UNKNOWN","retained started proof has no live execution grant"),(new Date).toISOString())}):await this.reject(n,"invalid_execution_grant",t),void await d();this.verifier.verifyAndDecodeGrant(i)||await this.verifier.refresh(!0);const _=()=>c.abort();let w;a.addEventListener("abort",_,{once:!0});try{w=await verifyAndCreateMessageServiceCoreExecutionAttempt({request:n,executionGrant:i,runtimeEpoch:t.runtimeEpoch,lease:t,verifier:this.verifier,allowExpiredGrant:I,abortSignal:c.signal,resolvePorts:async()=>await this.ports.resolve({request:n,executionGrant:i,command:n.command,getLease:()=>this.currentLease(t),...this.journalLineage(),journalRecord:f,abortSignal:c.signal})})}catch(e){if(a.removeEventListener("abort",_),c.abort(),!(e instanceof RuntimeResourceResolutionError))throw e;if("transient"===e.kind)return;return I?(v||await this.markOutcomeUnknownIfMutable(f),await this.channel.append({operationId:n.operationId,lease:this.currentLease(t),type:RUNTIME_MESSAGE_TYPES.final,payload:failurePayload(n,new RuntimeCommandError("OUTCOME_UNKNOWN","eager resources cannot be reconstructed after retained started proof"),(new Date).toISOString())})):await this.reject(n,"integrity_mismatch"===e.kind?"resource_integrity_mismatch":"resource_resolution_rejected",t),void await d()}if(!w.ok)return a.removeEventListener("abort",_),c.abort(),I?(v||await this.markOutcomeUnknownIfMutable(f),await this.channel.append({operationId:n.operationId,lease:this.currentLease(t),type:RUNTIME_MESSAGE_TYPES.final,payload:failurePayload(n,new RuntimeCommandError("OUTCOME_UNKNOWN","execution grant cannot be revalidated after retained started proof"),(new Date).toISOString())})):await this.reject(n,"invalid_execution_grant",t),void await d();if(!u||"prepared"!==f.phase&&"started"!==f.phase||c.abort(),c.signal.aborted&&!I)return a.removeEventListener("abort",_),await this.reject(n,"cancelled_before_dispatch",t),void await d();s.mark("attempt"),I?this.currentLease(t):(await this.channel.append({operationId:n.operationId,lease:this.currentLease(t),executionGrant:i,type:RUNTIME_MESSAGE_TYPES.started,payload:{...runtimeMessageBase(n),executionMode:"start",executionGrantHash:n.executionGrantHash,handlerIdentity:t.handlerIdentity,claimedAt:f.messageBinding?.acceptedAt??f.updatedAt}}),s.mark("append-started"));const g=this.dispatcher.dispatch(n.command,w.context);g.completion.catch(()=>{}),o(()=>{c.abort(),g.cancel()}),c.signal.aborted&&g.cancel();const M=this.forwardProgress(n,g.progress,t).catch(()=>{});let S,O=!1;try{if(("mcp/set"===n.method||"mcp/unset"===n.method)&&hasEffectReceiptHandoff(g)){const e=await g.effectCommitted;if(e){if(e.operationId!==n.operationId||e.method!==n.command.name)throw new RuntimeCommandError("OUTCOME_UNKNOWN","Core MCP receipt correlation mismatch");const a=runtimeMessageHash(RUNTIME_HASH_DOMAINS.coreResult,{method:n.method,result:e.result}),o={schemaVersion:1,operationId:n.operationId,method:n.method,coreResultRef:`sha256:${a}`,coreResultHash:a};S=runtimeMessageHash(RUNTIME_HASH_DOMAINS.effectRecord,o),O=!0,await this.channel.append({operationId:n.operationId,lease:this.currentLease(t),type:RUNTIME_MESSAGE_TYPES.effectCommitted,payload:{...runtimeMessageBase(n),effectRecord:o,effectRecordHash:S,committedAt:e.committedAt}}),await g.acknowledgeEffectReceipt({operationId:n.operationId,method:n.command.name,effectRecordHash:S})}}const a=await g.completion;if(await M,s.mark("core"),v){const o=(new Date).toISOString();let r;try{r=sanitizeRuntimeServiceResult(n.method,a,w.context,void 0)}catch(e){throw new RuntimeCommandError("OUTCOME_UNKNOWN","committed Core result cannot be mapped safely",void 0,{cause:e})}const i=runtimeMessageHash(RUNTIME_HASH_DOMAINS.serviceResult,{method:n.method,result:r}),c=await this.channel.append({operationId:n.operationId,lease:this.currentLease(t),type:RUNTIME_MESSAGE_TYPES.final,payload:{...runtimeMessageBase(n),outcome:"succeeded",response:{jsonrpc:"2.0",id:n.request.id,result:r},serviceResultHash:i,completedAt:o}});return s.mark("append-final"),this.observeRequestTerminalAccepted(e.deliveryId,c.message),await d(),void this.observeRequestAcknowledged(e.deliveryId,c.message)}const o=await this.journal.get(n.operationId);if(s.mark("journal-after-core"),!o||!isCommittedCorePhase(o.phase))throw new RuntimeCommandError("OUTCOME_UNKNOWN","Core completed without a committed local journal record");const r=o.updatedAt;let i,c;if("query"!==RUNTIME_INVOCATION_RECOVERY_CLASSES[n.method]){const e=isAgentProjectionMethod(n.method),c=g.getSnapshot().projection,l=o.agentProjectionIntent??o.agentProjectionPreparationFailure;if(e&&!l)throw new RuntimeCommandError("OUTCOME_UNKNOWN","agent effect committed without an atomic projection preparation record");if(!S&&"session/clear"!==n.method){const e=l?.coreResultHash??runtimeMessageHash(RUNTIME_HASH_DOMAINS.coreResult,{method:n.method,result:a}),i=o.agentProjectionIntent?.projectionRevision??(methodRequiresProjectionRevision(n.method)?c?.revision??`${Math.max(0,o.revision)}`:void 0),d={schemaVersion:1,operationId:n.operationId,method:n.method,coreResultRef:`sha256:${e}`,coreResultHash:e,...i?{projectionRevision:i}:{}};S=runtimeMessageHash(RUNTIME_HASH_DOMAINS.effectRecord,d),O=!0,await this.channel.append({operationId:n.operationId,lease:this.currentLease(t),type:RUNTIME_MESSAGE_TYPES.effectCommitted,payload:{...runtimeMessageBase(n),effectRecord:d,effectRecordHash:S,committedAt:r}}),s.mark("append-effect")}let p=e?await awaitRuntimeProjection(g):c;if("session/set_model"===n.method&&"synced"!==p?.state&&(p=await this.projectSessionModelAfterEffect(n,w.context,o,p),s.mark("set-model-projection")),e&&o.agentProjectionIntent&&(!p||p.revision!==o.agentProjectionIntent.projectionRevision))throw new RuntimeCommandError("OUTCOME_UNKNOWN","agent projection receipt revision mismatch");if("pending"===p?.state)return void await this.channel.append({operationId:n.operationId,lease:this.currentLease(t),type:RUNTIME_MESSAGE_TYPES.projectionPending,payload:{...runtimeMessageBase(n),revision:p.revision}});if("synced"===p?.state&&await this.publishProjectionSynced(n,t,{...p,state:"synced"}),"blocked"===p?.state){await this.channel.append({operationId:n.operationId,lease:this.currentLease(t),type:RUNTIME_MESSAGE_TYPES.projectionBlocked,payload:{...runtimeMessageBase(n),revision:p.revision,errorCode:"PROJECTION_BLOCKED"}});const e=RUNTIME_ERROR_DESCRIPTORS.PROJECTION_BLOCKED;return await this.channel.append({operationId:n.operationId,lease:this.currentLease(t),type:RUNTIME_MESSAGE_TYPES.final,payload:{...runtimeMessageBase(n),outcome:"projection_blocked",response:{jsonrpc:"2.0",id:n.request.id,error:{code:e.jsonRpcCode,message:"PROJECTION_BLOCKED",data:{code:"PROJECTION_BLOCKED",operationId:n.operationId,effectState:"committed",retryMode:e.retryMode}}},effectRecordHash:S,completedAt:r}}),void await d()}i=e?p?.platformAgentId:void 0}try{c=sanitizeRuntimeServiceResult(n.method,a,w.context,i)}catch(e){throw new RuntimeCommandError("OUTCOME_UNKNOWN","committed Core result cannot be mapped safely",void 0,{cause:e})}const l=runtimeMessageHash(RUNTIME_HASH_DOMAINS.serviceResult,{method:n.method,result:c});O=!0;const p=await this.channel.append({operationId:n.operationId,lease:this.currentLease(t),type:RUNTIME_MESSAGE_TYPES.final,payload:{...runtimeMessageBase(n),outcome:"succeeded",response:{jsonrpc:"2.0",id:n.request.id,result:c},serviceResultHash:l,...S?{effectRecordHash:S}:{},completedAt:r}});if(s.mark("append-final"),this.observeRequestTerminalAccepted(e.deliveryId,p.message),("mcp/set"===n.method||"mcp/unset"===n.method)&&hasEffectReceiptHandoff(g)&&"function"==typeof g.acknowledgeTerminalReceipt){if(!S)throw new RuntimeCommandError("OUTCOME_UNKNOWN","MCP terminal response lacks its committed effect hash");await g.acknowledgeTerminalReceipt({operationId:n.operationId,method:n.command.name,effectRecordHash:S,terminalMessageId:p.message.message_id})}await d(),this.observeRequestAcknowledged(e.deliveryId,p.message)}catch(e){const a=await this.journal.get(n.operationId);if(a&&isCommittedCorePhase(a.phase)&&O)throw e;const o=!a||!isCommittedCorePhase(a.phase)||e instanceof RuntimeCommandError&&"OUTCOME_UNKNOWN"===e.code?e:new RuntimeCommandError("OUTCOME_UNKNOWN","Core effect committed before terminal mapping",void 0,{cause:e});this.observeRequestFailure(n.operationId,n.method,o);const r=failurePayload(n,o,a?.updatedAt??(new Date).toISOString());await this.channel.append({operationId:n.operationId,lease:this.currentLease(t),type:RUNTIME_MESSAGE_TYPES.final,payload:r}),s.mark("append-terminal-failure"),await d()}finally{s.finish(),c.abort(),a.removeEventListener("abort",_)}}async handlePoison(e,t,a){this.currentLease(a);(await this.poisonJournal.record({delivery:e,reason:durablePoisonReason(t),limit:this.options.poisonDeliveryLimit??3})).terminal&&await this.channel.acknowledge({deliveryIds:[e.deliveryId],lease:this.currentLease(a)})}observeRequestTerminalAccepted(e,t){try{this.options.onRequestTerminalAccepted?.({deliveryId:e,message:t})}catch{}}observeRequestAcknowledged(e,t){try{this.options.onRequestAcknowledged?.({deliveryId:e,message:t})}catch{}}async forwardProgress(e,t,a){for await(const o of t)await this.channel.append({operationId:e.operationId,lease:this.currentLease(a),type:RUNTIME_MESSAGE_TYPES.progress,payload:{...runtimeMessageBase(e),phase:o.phase,progress:o}})}async reject(e,t,a){await this.channel.append({operationId:e.operationId,lease:this.currentLease(a),type:RUNTIME_MESSAGE_TYPES.final,payload:prestartFailurePayload(e,t)})}async markOutcomeUnknownIfMutable(e){"prepared"!==e.phase&&"started"!==e.phase||await this.journal.transition({operationId:e.operationId,expectedRevision:e.revision,phase:"outcome_unknown",errorCode:"OUTCOME_UNKNOWN"})}async projectSessionModelAfterEffect(e,t,a,o){const r=Object.freeze({state:"pending",revision:o?.revision??`${Math.max(0,a.revision)}`}),n=e.command;if("session.setModel"!==n.name||"conversation"!==n.context.scope)return r;const i=t.sessionModelProjection;if(!i)return r;try{await i.project({conversationId:n.context.conversation.runtimeConversationUid,modelOverrideId:n.params.modelOverrideId});const e=Object.freeze({state:"synced",revision:r.revision});try{const t=await this.journal.get(a.operationId)??a;if("projection_committed"===t.phase&&"synced"===t.projectionReceipt?.snapshot.state)return Object.freeze({state:"synced",revision:t.projectionReceipt.snapshot.revision});await this.journal.transition({operationId:t.operationId,expectedRevision:t.revision,phase:"projection_committed",projectionSnapshot:e})}catch{}return e}catch(e){return this.observeDeliveryError(e),r}}async publishProjectionSynced(e,t,a){const o=this.currentLease(t),r=await this.channel.history({operationId:e.operationId,lease:o});if("found"===r.status&&"synced"===r.snapshot.projectionState)return;const n={...runtimeMessageBase(e),revision:a.revision};("found"!==r.status||"reserved"===r.snapshot.projectionState||"pending"!==r.snapshot.projectionState&&void 0===r.snapshot.projectionPending)&&await this.channel.append({operationId:e.operationId,lease:o,type:RUNTIME_MESSAGE_TYPES.projectionPending,payload:n}),await this.channel.append({operationId:e.operationId,lease:o,type:RUNTIME_MESSAGE_TYPES.projectionSynced,payload:{...n,...a.platformAgentId?{platformAgentId:a.platformAgentId}:{}}})}journalLineage(){return this.options.getJournalLineage?.()??{journalStoreId:this.options.journalStoreId,journalGeneration:this.options.journalGeneration}}currentLease(e){const t=this.getLease();if(!t||t.instanceId!==e.instanceId||t.leaseId!==e.leaseId||t.runtimeEpoch!==e.runtimeEpoch||t.handlerIdentity!==e.handlerIdentity||!Number.isFinite(Date.parse(t.leaseExpiresAt))||Date.parse(t.leaseExpiresAt)<=Date.now())throw new RuntimeCommandError("INSTANCE_EPOCH_MISMATCH","runtime message lease changed");return t}}function ephemeralQueryAccept(e){return{operationId:e.operationId,command:e.command.name,paramsHash:e.paramsHash,runtimeEpoch:e.dispatchEpoch,phase:"prepared",revision:1,updatedAt:(new Date).toISOString(),messageBinding:{requestMessageId:e.requestMessageId,requestHash:e.requestHash,acceptedAt:(new Date).toISOString()}}}function durablePoisonReason(e){const t=e.replace(/[^a-z0-9_:.-]+/gi,"_").replace(/^_+|_+$/g,"").slice(0,256);if(!t)throw new Error("poison reason is empty");return t}function isCommittedCorePhase(e){return"effect_committed"===e||"projection_committed"===e}const CORE_RECOVERY_DECISIONS=new Set(["execute","resume","replay_effect_committed","resume_projection","publish_committed_result","publish_projected_result","publish_terminal_result"]);function decideMessageServiceRecovery(e,t,a,o){const r=RUNTIME_INVOCATION_PARITY_ENTRIES.find(e=>e.acpMethod===t.method),n=e.terminal?"terminal":"blocked"===e.projectionState?"projection_blocked":"synced"===e.projectionState?"projection_synced":"pending"===e.projectionState?"projection_pending":e.effectCommitted?"runtime_committed":o?"running":"queued",i="failed"===a?.phase||"outcome_unknown"===a?.phase?"terminal":a?.phase,s=a&&a.runtimeEpoch===t.dispatchEpoch&&(void 0===a.messageBinding||a.messageBinding.requestMessageId===t.requestMessageId&&a.messageBinding.requestHash===t.requestHash)?"continuous":"discontinuous";return reduceRuntimeRecoveryPolicy({recoveryClass:r?.recoveryClass??RUNTIME_INVOCATION_RECOVERY_CLASSES[t.method],projectionRequired:"mcp/set"===t.method||"mcp/unset"===t.method||(r?.projectionRequired??!1),messageServicePhase:n,executionBoundaryCrossed:o,lineageContinuity:s,journalState:a?"available":"unavailable",...i?{journalPhase:i}:{}})}export function sanitizeRuntimeServiceResult(e,t,a,o){if("session/clear"===e)return serviceClearResult(t,a?.historyBoundary);if("session/set_model"===e)return serviceSetModelResult(t,a);if(!(e in SERVICE_CANONICAL_COMMAND_NAMES))return t;const r="cron/status"===e&&record(t)?{enabled:t.enabled,jobCount:t.jobCount,..."number"==typeof t.nextWakeAtMs?{nextWakeAtMs:t.nextWakeAtMs}:{}}:t;return mapCanonicalResultToService(e,r,{...o?{platformAgentId:o}:{},classifySkillOrigin:e=>e.bundled?{origin:"bundled",slug:e.slug}:{origin:"legacy_runtime",slug:e.slug},resolveMcpView:e=>({name:e.name,transport:e.transport,origin:"legacy_runtime",...void 0===e.connected?{}:{connected:e.connected},...void 0===e.connectionTimeoutMs?{}:{connectionTimeoutMs:e.connectionTimeoutMs}}),sanitizeDiagnostic:e=>/^[A-Z][A-Z0-9_]{0,63}$/.test(e)?e:"RUNTIME_EXECUTION_FAILED"})}function serviceClearResult(e,t){if(!record(e)||!0!==e.cleared||"string"!=typeof e.historyGeneration||!t||"message-service"!==t.kind)throw new RuntimeCommandError("OUTCOME_UNKNOWN","committed clear result lacks its Message Service receipt");const a=t;if(!a.committedIntentId)throw new RuntimeCommandError("OUTCOME_UNKNOWN","Message Service clear receipt is unavailable");return{...e,clearIntentId:a.committedIntentId()}}function serviceSetModelResult(e,t){if(!record(e)||"string"!=typeof e.effectiveModelId||!t)throw new RuntimeCommandError("OUTCOME_UNKNOWN","committed set_model result is invalid");const a=t.verifiedMessageGrant?.claims.command;if(!a||"session.setModel"!==a.name||"conversation"!==a.context.scope)throw new RuntimeCommandError("OUTCOME_UNKNOWN","set_model Service target receipt is unavailable");const o=a.params.modelOverrideId;return{conversationId:a.context.conversation.runtimeConversationUid,...null===o?{defaultModelId:e.effectiveModelId}:{},modelOverrideId:o,effectiveModelId:e.effectiveModelId,effectState:"committed",projectionState:"synced",operationId:t.operationId}}function isAgentProjectionMethod(e){return"agent/create"===e||"agent/update"===e||"agent/delete"===e||"agent/applyTemplate"===e}function methodRequiresProjectionRevision(e){return isAgentProjectionMethod(e)||"session/set_model"===e}function record(e){return null!==e&&"object"==typeof e&&!Array.isArray(e)}function sameLeaseBinding(e,t){return e.instanceId===t.instanceId&&e.leaseId===t.leaseId&&e.handlerIdentity===t.handlerIdentity&&e.runtimeEpoch===t.runtimeEpoch}function failurePayload(e,t,a){const o=t instanceof RuntimeCommandError?t.code:"RUNTIME_EXECUTION_FAILED",r=o in RUNTIME_ERROR_DESCRIPTORS?o:"RUNTIME_EXECUTION_FAILED",n="OUTCOME_UNKNOWN"===r||"INSTANCE_EPOCH_MISMATCH"===r?"outcome_unknown":"COMMAND_EXPIRED"===r?"expired":"OPERATION_CANCELLED"===r?"cancelled":"failed",i="outcome_unknown"===n?"OUTCOME_UNKNOWN":r,s=RUNTIME_ERROR_DESCRIPTORS[i],c="outcome_unknown"===n?"outcome_unknown":n;return{...runtimeMessageBase(e),outcome:n,response:{jsonrpc:"2.0",id:e.request.id,error:{code:s.jsonRpcCode,message:i,data:{code:i,operationId:e.operationId,effectState:c,retryMode:s.retryMode}}},completedAt:a}}function prestartFailurePayload(e,t){const a="invalid_execution_grant"===t?"INVALID_EXECUTION_GRANT":"unsupported_method"===t?"METHOD_NOT_SUPPORTED":"authority_mismatch"===t?"INSTANCE_EPOCH_MISMATCH":"execution_deadline_exceeded"===t?"COMMAND_EXPIRED":"cancelled_before_dispatch"===t?"OPERATION_CANCELLED":"resource_resolution_rejected"===t?"INVALID_INVOCATION":"RUNTIME_EXECUTION_FAILED",o=RUNTIME_ERROR_DESCRIPTORS[a in RUNTIME_ERROR_DESCRIPTORS?a:"RUNTIME_EXECUTION_FAILED"],r="COMMAND_EXPIRED"===a?"expired":"OPERATION_CANCELLED"===a?"cancelled":"failed";return{...runtimeMessageBase(e),outcome:r,response:{jsonrpc:"2.0",id:e.request.id,error:{code:o.jsonRpcCode,message:a,data:{code:a,operationId:e.operationId,effectState:r,retryMode:o.retryMode}}},completedAt:(new Date).toISOString()}}function abortable(e,t){return t?t.aborted?Promise.resolve():new Promise((a,o)=>{const r=()=>a();t.addEventListener("abort",r,{once:!0}),e.then(()=>{t.removeEventListener("abort",r),a()},e=>{t.removeEventListener("abort",r),o(e)})}):e}function delay(e,t){return new Promise(a=>{if(t?.aborted)return a();const o=()=>{clearTimeout(r),a()},r=setTimeout(()=>{t?.removeEventListener("abort",o),a()},e);t?.addEventListener("abort",o,{once:!0})})}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{MsRuntimeMessageChannelAdapter}from"./ms-runtime-message-channel-adapter.js";import{decodeRuntimeAppendMessage,decodeRuntimeBrokerDelivery,decodeRuntimeOperationHistory,RuntimeMessageChannelProtocolError}from"./message-service-runtime-message-channel.js";import{isCanonicalSessionNewDelivery,SessionNewRuntimeDeliveryBridge}from"./session-new-runtime-delivery-bridge.js";export class MsRuntimeMessageChannelConsumerFactory{deps;constructor(e){this.deps=e}async create(e){let
|
|
1
|
+
import{CloudGatewayGrantJwksVerifier}from"./split-runtime-authorization-verifier.js";import{MsRuntimeMessageChannelAdapter}from"./ms-runtime-message-channel-adapter.js";import{decodeRuntimeAppendMessage,decodeRuntimeBrokerDelivery,decodeRuntimeOperationHistory,RuntimeMessageChannelProtocolError}from"./message-service-runtime-message-channel.js";import{isCanonicalSessionNewDelivery,SessionNewRuntimeDeliveryBridge}from"./session-new-runtime-delivery-bridge.js";export class MsRuntimeMessageChannelConsumerFactory{deps;constructor(e){if(this.deps=e,Boolean(e.client)===Boolean(e.runtimeDelivery))throw new Error("exactly one runtime delivery owner is required");if(e.runtimeDelivery&&!(e.verifier instanceof CloudGatewayGrantJwksVerifier))throw new Error("runtime delivery owner and grant realm differ")}async create(e){let r;const n=new SdkRuntimeMessageChannel(()=>{if(!r)throw new Error("SDK runtime delivery consumer is not attached");return r}),i=new MsRuntimeMessageChannelAdapter(e.dispatcher,n,this.deps.verifier,this.deps.ports,this.deps.journal,()=>e.getLease(),{...this.deps.options,allowedMethods:this.deps.allowedMethods,journalStoreId:e.binding.journalStoreId,journalGeneration:e.binding.journalGeneration,...e.getBinding?{getJournalLineage:()=>{const r=e.getBinding();return{journalStoreId:r.journalStoreId,journalGeneration:r.journalGeneration}}}:{}}),t=this.deps.client?this.deps.verifier:void 0,s=this.deps.client&&t?new SessionNewRuntimeDeliveryBridge({client:this.deps.client,consumer:()=>{if(!r)throw new Error("SDK runtime delivery consumer is not attached");return r},verifier:t,getLease:()=>e.getLease(),createRuntimeSession:e.createRuntimeSession,poison:async(e,r,n)=>{await i.acceptPoisonDelivery(e,r,n)}}):void 0,o=this.deps.client?.methods??this.deps.runtimeDelivery;if(!o)throw new Error("runtime delivery owner is unavailable");return r=o.consume({onDelivery:async(r,n)=>{const t=currentFencedLease(e,n.lease),o=decodeRuntimeBrokerDelivery(r);if(isCanonicalSessionNewDelivery(o))return s?void await s.acceptDelivery(o,t,n.signal):void await i.acceptPoisonDelivery(o,"cloud_session_new_unsupported",t);await i.acceptDelivery(o,t,n.signal)},onError:this.deps.options.onDeliveryError,readCount:this.deps.options.readCount,blockMs:this.deps.options.blockMs,idleDelayMs:this.deps.options.idleDelayMs,renewIntervalMs:this.deps.options.renewIntervalMs}),i.attachExternalDeliveryOwner(r),i}}class SdkRuntimeMessageChannel{consumer;constructor(e){this.consumer=e}async read(){throw new Error("MessageClient owns runtime delivery reads")}async renew(){throw new Error("MessageClient owns runtime delivery renewal")}async acknowledge(e){assertLeaseAvailable(e.lease),await this.consumer().acknowledge(e.deliveryIds)}async history(e){assertLeaseAvailable(e.lease);const r=await this.consumer().history(e.operationId);return"found"!==r.status?r:{status:"found",snapshot:decodeRuntimeOperationHistory(r.snapshot,e.operationId)}}async append(e){assertLeaseAvailable(e.lease);const r=await this.consumer().append(e.operationId,e.type,e.payload,e.executionGrant);if("created"===r.outcome||"duplicate"===r.outcome)return{disposition:"created"===r.outcome?"created":"reused",message:decodeRuntimeAppendMessage(r.message,e)};if(r.history){const n=findReconciledMessage(decodeRuntimeOperationHistory(r.history,e.operationId),e);if(n)return{disposition:"reused",message:n}}throw new RuntimeMessageChannelProtocolError("runtime append outcome is unknown after history reconcile",503,"OUTCOME_UNKNOWN")}}function currentFencedLease(e,r){const n=e.getLease(),i=e.getBinding?.()??e.binding;if(!n||n.instanceId!==r.instanceId||n.handlerIdentity!==r.handlerIdentity||n.runtimeEpoch!==r.runtimeEpoch||n.leaseId!==r.leaseId||i.journalStoreId!==r.journalStoreId||i.journalGeneration!==r.journalGeneration)throw new Error("runtime delivery lease fence changed");if(Date.parse(n.leaseExpiresAt)<=Date.now())throw new Error("runtime delivery lease expired");return n}function assertLeaseAvailable(e){if(!Number.isFinite(Date.parse(e.leaseExpiresAt))||Date.parse(e.leaseExpiresAt)<=Date.now())throw new Error("runtime delivery lease expired")}function findReconciledMessage(e,r){for(let n=e.messages.length-1;n>=0;n-=1)try{return decodeRuntimeAppendMessage(e.messages[n],r)}catch{}}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{randomUUID}from"node:crypto";import{verifyAndCreateServiceCoreExecutionAttempt}from"./core-execution-attempt-context.js";import{RuntimeCommandError}from"./runtime-command-dispatcher.js";import{RuntimeClaimAuthorityProtocolError}from"./message-service-runtime-claim-authority.js";import{sessionSetModelMappingMatches}from"./session-set-model-mapping.js";import{RuntimeTransportFailurePolicy}from"./runtime-transport-failure-policy.js";export class MsRuntimeRpcAdapter{dispatcher;authority;authorizationVerifier;attemptPorts;authorizationJournal;getLease;options;controller;running;constructor(e,t,r,a,n,o,i){this.dispatcher=e,this.authority=t,this.authorizationVerifier=r,this.attemptPorts=a,this.authorizationJournal=n,this.getLease=o,this.options=i}start(){if(this.running)return;this.controller=new AbortController;const e=this.loop(this.controller.signal).finally(()=>{this.running===e&&(this.running=void 0)});this.running=e}async stop(e){const t=this.running;if(this.controller?.abort(),!t)return;await waitForAbort(t,e)===aborted&&this.running===t&&(this.running=void 0)}journalLineage(){return this.options.getJournalLineage?.()??{journalStoreId:this.options.journalStoreId,journalGeneration:this.options.journalGeneration}}async loop(e){let t=randomUUID(),r="",a="";for(;!e.aborted;){const n=this.getLease();if(!n){await delay(this.options.pollDelayMs??1e3,e);continue}const o=this.journalLineage(),i=`${n.instanceId}\n${n.runtimeEpoch}\n${this.options.handlerIdentity}\n${o.journalStoreId}\n${o.journalGeneration}`;if(r&&r!==i&&(t=randomUUID()),r=i,a!==i)try{const t=await this.authorizationJournal.listRecoverable(n.runtimeEpoch);for(const r of t){if(e.aborted||this.getLease()?.leaseId!==n.leaseId)break;if(!await this.recoverExact({operationId:r.operationId,requestMessageId:r.requestMessageId,dispatchEpoch:r.runtimeEpoch},n,e))throw new Error("durable runtime invocation is still owned by another claim")}a=i}catch{await delay(this.options.pollDelayMs??1e3,e);continue}let s;try{const r=this.authority.claimNext({claimRequestId:t,instanceId:n.instanceId,handlerIdentity:this.options.handlerIdentity,runtimeEpoch:n.runtimeEpoch,journalStoreId:o.journalStoreId,journalGeneration:o.journalGeneration,runtimeLeaseCredential:n.runtimeLeaseCredential}),a=await waitForAbort(r,e);if(a===aborted)return;s=a}catch(t){if(e.aborted)return;if(t instanceof RuntimeClaimAuthorityProtocolError)return;await delay(this.options.pollDelayMs??1e3,e);continue}if(e.aborted)return;t=randomUUID(),"empty"!==s.status?"fenced"!==s.status?await this.handleClaimed(s,n,e,"start").catch(()=>{}):await delay(this.options.pollDelayMs??1e3,e):await delay(Math.max(s.retryAfterMs,this.options.pollDelayMs??0),e)}}async recoverExact(e,t,r){if(e.dispatchEpoch!==t.runtimeEpoch)throw new Error("recovery candidate runtime epoch mismatch");const a=this.journalLineage(),n=await this.authority.claimExact({operationId:e.operationId,requestMessageId:e.requestMessageId,handlerIdentity:this.options.handlerIdentity,dispatchEpoch:e.dispatchEpoch,runtimeEpoch:t.runtimeEpoch,journalStoreId:a.journalStoreId,journalGeneration:a.journalGeneration,runtimeLeaseCredential:t.runtimeLeaseCredential});return"terminal"===n.status?(await this.authorizationJournal.markTerminal(e.operationId).catch(()=>{}),!0):"claimed"===n.status&&(await this.handleClaimed(n,t,r,"recovery"),!0)}async handleClaimed(e,t,r,a="start"){const n={operationId:e.request.operationId,requestMessageId:e.request.messageId,dispatchEpoch:e.request.payload.command.context.dispatchEpoch},o=async()=>{await this.authorizationJournal.markTerminal(n.operationId).catch(()=>{})},i=async e=>{await this.authority.commitFailure(e),await o()},s=()=>{const r=this.getLease(),a=r?.leaseId===t.leaseId&&r.runtimeEpoch===t.runtimeEpoch?r.runtimeLeaseCredential:t.runtimeLeaseCredential;return{operationId:n.operationId,requestMessageId:n.requestMessageId,...e.fence,runtimeLeaseCredential:a}},c=async e=>{try{await i(e)}finally{await this.authority.releaseClaim({...s(),reason:"claim_rejected"}).catch(()=>{})}};if(e.cancelRequested)return await i({operationId:n.operationId,requestMessageId:n.requestMessageId,request:e.request,fence:e.fence,claimLeaseExpiresAt:e.leaseExpiresAt,runtimeLeaseCredential:t.runtimeLeaseCredential,attempt:prestartAttempt(e.request),outcome:"cancelled",code:"OPERATION_CANCELLED"}),void await this.authority.releaseClaim({...s(),reason:"cancelled"});let u;try{u=await this.authorizationJournal.prepare({operationId:n.operationId,requestMessageId:n.requestMessageId,method:e.request.payload.request.method,commandHash:e.request.payload.commandHash,runtimeEpoch:t.runtimeEpoch,journalStoreId:e.fence.journalStoreId,journalGeneration:e.fence.journalGeneration})}catch{return void await c({operationId:n.operationId,requestMessageId:n.requestMessageId,request:e.request,fence:e.fence,claimLeaseExpiresAt:e.leaseExpiresAt,runtimeLeaseCredential:t.runtimeLeaseCredential,attempt:prestartAttempt(e.request),outcome:"failed",code:"UNAUTHORIZED_TARGET"}).catch(()=>{})}const d=await this.authority.authorize({request:e.request,fence:e.fence,lease:t,journal:u});if("authorized"!==d.status)return"terminal"===d.status?(await o(),void await this.authority.releaseClaim({...s(),reason:"completed"}).catch(()=>{})):void await c({operationId:n.operationId,requestMessageId:n.requestMessageId,request:e.request,fence:e.fence,claimLeaseExpiresAt:e.leaseExpiresAt,runtimeLeaseCredential:t.runtimeLeaseCredential,attempt:prestartAttempt(e.request),outcome:"expired"===d.status?"expired":"cancelled"===d.status?"cancelled":"failed",code:"expired"===d.status?"COMMAND_EXPIRED":"cancelled"===d.status?"OPERATION_CANCELLED":"fenced"===d.status?"INSTANCE_EPOCH_MISMATCH":"UNAUTHORIZED_TARGET"});const m={operationId:n.operationId,requestMessageId:n.requestMessageId,request:e.request,fence:e.fence,claimLeaseExpiresAt:e.leaseExpiresAt,runtimeLeaseCredential:t.runtimeLeaseCredential,attempt:d.attempt},l={...m,attempt:prestartAttempt(e.request)};let h;const p=new AbortController;let E;try{assertCommandCorrelation(e.request,d.attempt.command,t.runtimeEpoch),h=await verifyAndCreateServiceCoreExecutionAttempt({operationId:n.operationId,runtimeEpoch:t.runtimeEpoch,fence:e.fence,authorization:d.attempt.authorization,derivationContext:{...d.attempt.derivationContext,localJournalRevision:u.journalRevisionBeforeAttempt,localPreparedEntryHash:u.preparedEntryHash},verifier:this.authorizationVerifier,abortSignal:p.signal,recordVerifiedAttempt:async t=>{const r=t.request.binding;await this.authorizationJournal.recordBoundary({operationId:n.operationId,expectedRevision:u.revision,preparedEntryHash:u.preparedEntryHash,boundary:{executionBoundaryId:r.executionBoundaryId,boundaryAssertionHash:r.boundaryAssertionHash,executionMode:r.executionMode,fence:e.fence,...r.handoffId?{handoffId:r.handoffId}:{}}})},resolvePorts:async()=>E??=await this.attemptPorts.resolve(m,{getRuntimeLeaseCredential:()=>this.currentRuntimeLeaseCredential(t)})})}catch{return p.abort(),void await c({...l,outcome:"failed",code:"UNAUTHORIZED_TARGET"})}if(!h.ok)return p.abort(),void await c({...l,outcome:"failed",code:"UNAUTHORIZED_TARGET"});let I=!1,C=!1;const f=new RuntimeTransportFailurePolicy;let y,w,M,v,N=!1,A=!1,R=!1,O=!1,T=!1,_=!1,g=!1;const b=new Promise(e=>{v=e}),L=e=>{g||(g=!0,v(e))};let x;const U=()=>{T=!0,p.abort(),M&&!x&&(x=M.cancel().catch(()=>({status:"not_cancellable"})))},q=()=>{I=!0,L("fenced"),U()},S=()=>q();r.addEventListener("abort",S,{once:!0});const P=()=>{const e=this.getLease();e&&e.leaseId===t.leaseId&&e.runtimeEpoch===t.runtimeEpoch||q()},H=setInterval(P,100);H.unref?.();const D=setInterval(()=>{this.authority.renewClaim(s()).then(e=>{f.recordSuccess(),"renewed"===e.status&&e.cancelRequested?(C=!0,L("cancel_requested"),U()):"renewed"!==e.status&&q()}).catch(()=>{f.recordFailure()&&q()})},this.options.claimRenewIntervalMs??2e4);D.unref?.();const j=Date.parse(d.attempt.command.context.executionDeadline)-Date.now(),G=setTimeout(()=>{_=!0,L("deadline"),U()},Math.max(0,Math.min(j,2147483647)));G.unref?.();const z=async e=>{if(!y)return;const r=this.currentConversationRunBoundaryLease(t);await this.finishConversationRunBoundary(m,r,y,e,"recovery"===a?"recovery":"runtime"),y=void 0};try{if(P(),I||r.aborted)return void await i({...m,outcome:"outcome_unknown",code:"INSTANCE_EPOCH_MISMATCH"});if(await this.authority.appendStarted(m),I||r.aborted)return void await i({...m,outcome:"outcome_unknown",code:"INSTANCE_EPOCH_MISMATCH"});if(_)return void await i({...m,outcome:"expired",code:"COMMAND_EXPIRED"});if(C)return void await i({...m,outcome:"cancelled",code:"OPERATION_CANCELLED"});const e=await this.startConversationRunBoundary(m,t,E);if(e?.rejected)return void await i({...m,outcome:"failed",code:e.code});if(y=e,I||r.aborted)throw new RuntimeCommandError("INSTANCE_EPOCH_MISMATCH","runtime authority fenced after the conversation run boundary started");if("recovery"===a&&y)throw new RuntimeCommandError("OUTCOME_UNKNOWN","session.prompt crossed its durable run boundary before recovery");if(_)throw new RuntimeCommandError("COMMAND_EXPIRED","session.prompt expired after the conversation run boundary started");if(C)throw new RuntimeCommandError("OPERATION_CANCELLED","session.prompt was cancelled after the conversation run boundary started");let n;M=this.dispatcher.dispatch(d.attempt.command,h.context),A=!0,T&&U();const s=this.forwardProgress(M.progress,m,()=>I||r.aborted||C||_).catch(e=>{n=e}),c=M.completion.then(e=>(clearTimeout(G),{status:"completed",result:e}),e=>(clearTimeout(G),{status:"failed",error:e}));let u=await Promise.race([c,b.then(e=>({status:"interrupted",reason:e}))]);if("interrupted"===u.status){const e=u.reason;U();if("cancel_requested"===(x?await Promise.race([x,delay(this.options.cancellationGraceMs??1e3).then(()=>({status:"not_cancellable"}))]):{status:"not_cancellable"}).status&&(u=await Promise.race([c,delay(this.options.cancellationGraceMs??1e3).then(()=>({status:"interrupted",reason:e}))])),"interrupted"===u.status)throw new RuntimeCommandError("fenced"===u.reason?"INSTANCE_EPOCH_MISMATCH":"OUTCOME_UNKNOWN","session.prompt execution did not converge after interruption")}if(R=!0,"failed"===u.status)throw u.error;const l=u.result;N=!0;const p="session.prompt"===d.attempt.command.name&&!0===l.requestAborted;if(w=p?"cancelled":"completed",await s,n)throw n;if(await z(w),I||r.aborted)return void await i({...m,outcome:"outcome_unknown",code:"INSTANCE_EPOCH_MISMATCH"});if(_)return void await i({...m,outcome:"outcome_unknown",code:"OUTCOME_UNKNOWN"});if(C&&!p)throw new RuntimeCommandError("OUTCOME_UNKNOWN","cancel request raced a locally completed runtime effect");await this.authority.commitResult({...m,command:d.attempt.command,result:sanitizeServiceResult(d.attempt.command.name,l)}),await o()}catch(e){let t=e;if(y&&(!A||R))try{await z(w??(C||_?"cancelled":"failed"))}catch(r){t=new RuntimeCommandError("OUTCOME_UNKNOWN",`conversation run terminal outcome is unknown: ${errorText(r)}`,void 0,{cause:e})}if(N&&!isUncertainRuntimeError(t)&&(t=new RuntimeCommandError("OUTCOME_UNKNOWN",`runtime effect completed before its terminal receipt: ${errorText(t)}`,void 0,{cause:e})),y)return void(O=!0);const r=serviceRuntimeFailureCode(t),a="OUTCOME_UNKNOWN"===r||"INSTANCE_EPOCH_MISMATCH"===r||I||A&&(C||_),n="COMMAND_EXPIRED"===r||!A&&_,o=!A&&C;await i({...m,outcome:a?"outcome_unknown":n?"expired":o?"cancelled":"failed",code:"INSTANCE_EPOCH_MISMATCH"===r||I?"INSTANCE_EPOCH_MISMATCH":a?"OUTCOME_UNKNOWN":n?"COMMAND_EXPIRED":o?"OPERATION_CANCELLED":r??"RUNTIME_EXECUTION_FAILED"})}finally{clearInterval(H),clearInterval(D),clearTimeout(G),r.removeEventListener("abort",S),p.abort(),await this.authority.releaseClaim({...s(),reason:r.aborted||O?"worker_shutdown":"completed"}).catch(()=>{})}}async startConversationRunBoundary(e,t,r){const a=e.attempt.command;if("session.prompt"!==a.name)return;const n=r?.conversationRunBoundary;if(!n)throw new RuntimeCommandError("INVALID_PARAMS","Service session.prompt lacks its conversation run boundary");const o=a.context.conversation,i=await n.boundary.start({operationId:e.operationId,requestMessageId:e.requestMessageId,conversationId:o.runtimeConversationUid,historyGeneration:n.historyGeneration,runtimeRunId:a.params.runtimeRunId,lease:exactRunBoundaryLease(t)});if("rejected"===i.status)return{rejected:!0,code:i.reason};if(i.historyGeneration!==n.historyGeneration)throw new RuntimeCommandError("OUTCOME_UNKNOWN","conversation run boundary changed history generation");return{boundary:n.boundary,historyGeneration:n.historyGeneration,activeRunRegistrationId:i.activeRunRegistrationId,runtimeRunId:a.params.runtimeRunId}}currentRuntimeLeaseCredential(e){const t=this.getLease();if(!t||t.leaseId!==e.leaseId||t.runtimeEpoch!==e.runtimeEpoch)throw new RuntimeCommandError("INSTANCE_EPOCH_MISMATCH","runtime lease changed before deferred resource redemption");return t.runtimeLeaseCredential}currentConversationRunBoundaryLease(e){const t=this.getLease();if(!t||t.leaseId!==e.leaseId||t.runtimeEpoch!==e.runtimeEpoch||t.handlerIdentity!==e.handlerIdentity)throw new RuntimeCommandError("INSTANCE_EPOCH_MISMATCH","runtime lease changed before conversation run convergence");return t}async finishConversationRunBoundary(e,t,r,a,n){const o=e.attempt.command.context;if("conversation"!==o.scope)throw new RuntimeCommandError("OUTCOME_UNKNOWN","conversation run boundary lost its target");const i=await r.boundary.finish({activeRunRegistrationId:r.activeRunRegistrationId,operationId:e.operationId,requestMessageId:e.requestMessageId,conversationId:o.conversation.runtimeConversationUid,historyGeneration:r.historyGeneration,runtimeRunId:r.runtimeRunId,terminalState:a,source:n,lease:exactRunBoundaryLease(t)});if("rejected"===i.status||i.activeRunRegistrationId!==r.activeRunRegistrationId)throw new RuntimeCommandError("OUTCOME_UNKNOWN",`conversation run finish rejected: ${"rejected"===i.status?i.reason:"registration_mismatch"}`)}async forwardProgress(e,t,r){for await(const a of e){if(r())return;await this.authority.appendProgress({...t,progress:a})}}}function exactRunBoundaryLease(e){return{leaseId:e.leaseId,handlerIdentity:e.handlerIdentity,runtimeEpoch:e.runtimeEpoch,runtimeLeaseCredential:e.runtimeLeaseCredential}}function serviceRuntimeFailureCode(e){const t=e?.code;return t&&["OUTCOME_UNKNOWN","INSTANCE_EPOCH_MISMATCH","COMMAND_EXPIRED","OPERATION_CANCELLED","IDEMPOTENCY_CONFLICT","UNAUTHORIZED_TARGET","AGENT_DEFAULT_MODEL_NOT_CONFIGURED"].includes(t)?t:void 0}function isUncertainRuntimeError(e){return e instanceof RuntimeCommandError&&("OUTCOME_UNKNOWN"===e.code||"INSTANCE_EPOCH_MISMATCH"===e.code)}function errorText(e){return e instanceof Error?e.message:String(e)}export function sanitizeServiceResult(e,t){if("cron.status"!==e||!t||"object"!=typeof t)return t;const r=t;return{enabled:r.enabled,jobCount:r.jobCount,..."number"==typeof r.nextWakeAtMs?{nextWakeAtMs:r.nextWakeAtMs}:{}}}function assertCommandCorrelation(e,t,r){if(e.operationId!==t.context.operationId||e.payload.paramsHash!==t.context.paramsHash||t.context.dispatchEpoch!==r||e.payload.command.name!==t.name||!sessionSetModelMappingMatches(e.payload.request,t))throw new Error("runtime invocation command correlation failed")}function prestartAttempt(e){return{command:e.payload.command,executionBoundaryId:"prestart",boundaryAssertionHash:e.payload.commandHash,executionMode:"start",validationContext:{},authorization:{},derivationContext:{}}}const aborted=Symbol("aborted");function waitForAbort(e,t){return t?t.aborted?(e.catch(()=>{}),Promise.resolve(aborted)):new Promise((r,a)=>{let n=!1;const o=()=>{n||(n=!0,r(aborted))};t.addEventListener("abort",o,{once:!0}),e.then(e=>{n||(n=!0,t.removeEventListener("abort",o),r(e))},e=>{n||(n=!0,t.removeEventListener("abort",o),a(e))})}):e}const delay=(e,t)=>new Promise(r=>{if(t?.aborted)return r();const a=()=>{clearTimeout(n),r()},n=setTimeout(()=>{t?.removeEventListener("abort",a),r()},e);t?.addEventListener("abort",a,{once:!0})});
|
|
1
|
+
import{randomUUID}from"node:crypto";import{verifyAndCreateServiceCoreExecutionAttempt}from"./core-execution-attempt-context.js";import{RuntimeCommandError}from"./runtime-command-dispatcher.js";import{RuntimeClaimAuthorityProtocolError}from"./message-service-runtime-claim-authority.js";import{sessionSetModelMappingMatches}from"./session-set-model-mapping.js";import{RuntimeTransportFailurePolicy}from"@beeos-ai/cloud-agent-sdk";export class MsRuntimeRpcAdapter{dispatcher;authority;authorizationVerifier;attemptPorts;authorizationJournal;getLease;options;controller;running;constructor(e,t,r,a,n,o,i){this.dispatcher=e,this.authority=t,this.authorizationVerifier=r,this.attemptPorts=a,this.authorizationJournal=n,this.getLease=o,this.options=i}start(){if(this.running)return;this.controller=new AbortController;const e=this.loop(this.controller.signal).finally(()=>{this.running===e&&(this.running=void 0)});this.running=e}async stop(e){const t=this.running;if(this.controller?.abort(),!t)return;await waitForAbort(t,e)===aborted&&this.running===t&&(this.running=void 0)}journalLineage(){return this.options.getJournalLineage?.()??{journalStoreId:this.options.journalStoreId,journalGeneration:this.options.journalGeneration}}async loop(e){let t=randomUUID(),r="",a="";for(;!e.aborted;){const n=this.getLease();if(!n){await delay(this.options.pollDelayMs??1e3,e);continue}const o=this.journalLineage(),i=`${n.instanceId}\n${n.runtimeEpoch}\n${this.options.handlerIdentity}\n${o.journalStoreId}\n${o.journalGeneration}`;if(r&&r!==i&&(t=randomUUID()),r=i,a!==i)try{const t=await this.authorizationJournal.listRecoverable(n.runtimeEpoch);for(const r of t){if(e.aborted||this.getLease()?.leaseId!==n.leaseId)break;if(!await this.recoverExact({operationId:r.operationId,requestMessageId:r.requestMessageId,dispatchEpoch:r.runtimeEpoch},n,e))throw new Error("durable runtime invocation is still owned by another claim")}a=i}catch{await delay(this.options.pollDelayMs??1e3,e);continue}let s;try{const r=this.authority.claimNext({claimRequestId:t,instanceId:n.instanceId,handlerIdentity:this.options.handlerIdentity,runtimeEpoch:n.runtimeEpoch,journalStoreId:o.journalStoreId,journalGeneration:o.journalGeneration,runtimeLeaseCredential:n.runtimeLeaseCredential}),a=await waitForAbort(r,e);if(a===aborted)return;s=a}catch(t){if(e.aborted)return;if(t instanceof RuntimeClaimAuthorityProtocolError)return;await delay(this.options.pollDelayMs??1e3,e);continue}if(e.aborted)return;t=randomUUID(),"empty"!==s.status?"fenced"!==s.status?await this.handleClaimed(s,n,e,"start").catch(()=>{}):await delay(this.options.pollDelayMs??1e3,e):await delay(Math.max(s.retryAfterMs,this.options.pollDelayMs??0),e)}}async recoverExact(e,t,r){if(e.dispatchEpoch!==t.runtimeEpoch)throw new Error("recovery candidate runtime epoch mismatch");const a=this.journalLineage(),n=await this.authority.claimExact({operationId:e.operationId,requestMessageId:e.requestMessageId,handlerIdentity:this.options.handlerIdentity,dispatchEpoch:e.dispatchEpoch,runtimeEpoch:t.runtimeEpoch,journalStoreId:a.journalStoreId,journalGeneration:a.journalGeneration,runtimeLeaseCredential:t.runtimeLeaseCredential});return"terminal"===n.status?(await this.authorizationJournal.markTerminal(e.operationId).catch(()=>{}),!0):"claimed"===n.status&&(await this.handleClaimed(n,t,r,"recovery"),!0)}async handleClaimed(e,t,r,a="start"){const n={operationId:e.request.operationId,requestMessageId:e.request.messageId,dispatchEpoch:e.request.payload.command.context.dispatchEpoch},o=async()=>{await this.authorizationJournal.markTerminal(n.operationId).catch(()=>{})},i=async e=>{await this.authority.commitFailure(e),await o()},s=()=>{const r=this.getLease(),a=r?.leaseId===t.leaseId&&r.runtimeEpoch===t.runtimeEpoch?r.runtimeLeaseCredential:t.runtimeLeaseCredential;return{operationId:n.operationId,requestMessageId:n.requestMessageId,...e.fence,runtimeLeaseCredential:a}},c=async e=>{try{await i(e)}finally{await this.authority.releaseClaim({...s(),reason:"claim_rejected"}).catch(()=>{})}};if(e.cancelRequested)return await i({operationId:n.operationId,requestMessageId:n.requestMessageId,request:e.request,fence:e.fence,claimLeaseExpiresAt:e.leaseExpiresAt,runtimeLeaseCredential:t.runtimeLeaseCredential,attempt:prestartAttempt(e.request),outcome:"cancelled",code:"OPERATION_CANCELLED"}),void await this.authority.releaseClaim({...s(),reason:"cancelled"});let u;try{u=await this.authorizationJournal.prepare({operationId:n.operationId,requestMessageId:n.requestMessageId,method:e.request.payload.request.method,commandHash:e.request.payload.commandHash,runtimeEpoch:t.runtimeEpoch,journalStoreId:e.fence.journalStoreId,journalGeneration:e.fence.journalGeneration})}catch{return void await c({operationId:n.operationId,requestMessageId:n.requestMessageId,request:e.request,fence:e.fence,claimLeaseExpiresAt:e.leaseExpiresAt,runtimeLeaseCredential:t.runtimeLeaseCredential,attempt:prestartAttempt(e.request),outcome:"failed",code:"UNAUTHORIZED_TARGET"}).catch(()=>{})}const d=await this.authority.authorize({request:e.request,fence:e.fence,lease:t,journal:u});if("authorized"!==d.status)return"terminal"===d.status?(await o(),void await this.authority.releaseClaim({...s(),reason:"completed"}).catch(()=>{})):void await c({operationId:n.operationId,requestMessageId:n.requestMessageId,request:e.request,fence:e.fence,claimLeaseExpiresAt:e.leaseExpiresAt,runtimeLeaseCredential:t.runtimeLeaseCredential,attempt:prestartAttempt(e.request),outcome:"expired"===d.status?"expired":"cancelled"===d.status?"cancelled":"failed",code:"expired"===d.status?"COMMAND_EXPIRED":"cancelled"===d.status?"OPERATION_CANCELLED":"fenced"===d.status?"INSTANCE_EPOCH_MISMATCH":"UNAUTHORIZED_TARGET"});const m={operationId:n.operationId,requestMessageId:n.requestMessageId,request:e.request,fence:e.fence,claimLeaseExpiresAt:e.leaseExpiresAt,runtimeLeaseCredential:t.runtimeLeaseCredential,attempt:d.attempt},l={...m,attempt:prestartAttempt(e.request)};let h;const p=new AbortController;let E;try{assertCommandCorrelation(e.request,d.attempt.command,t.runtimeEpoch),h=await verifyAndCreateServiceCoreExecutionAttempt({operationId:n.operationId,runtimeEpoch:t.runtimeEpoch,fence:e.fence,authorization:d.attempt.authorization,derivationContext:{...d.attempt.derivationContext,localJournalRevision:u.journalRevisionBeforeAttempt,localPreparedEntryHash:u.preparedEntryHash},verifier:this.authorizationVerifier,abortSignal:p.signal,recordVerifiedAttempt:async t=>{const r=t.request.binding;await this.authorizationJournal.recordBoundary({operationId:n.operationId,expectedRevision:u.revision,preparedEntryHash:u.preparedEntryHash,boundary:{executionBoundaryId:r.executionBoundaryId,boundaryAssertionHash:r.boundaryAssertionHash,executionMode:r.executionMode,fence:e.fence,...r.handoffId?{handoffId:r.handoffId}:{}}})},resolvePorts:async()=>E??=await this.attemptPorts.resolve(m,{getRuntimeLeaseCredential:()=>this.currentRuntimeLeaseCredential(t)})})}catch{return p.abort(),void await c({...l,outcome:"failed",code:"UNAUTHORIZED_TARGET"})}if(!h.ok)return p.abort(),void await c({...l,outcome:"failed",code:"UNAUTHORIZED_TARGET"});let I=!1,C=!1;const f=new RuntimeTransportFailurePolicy;let y,w,M,v,N=!1,A=!1,R=!1,O=!1,T=!1,_=!1,g=!1;const b=new Promise(e=>{v=e}),L=e=>{g||(g=!0,v(e))};let x;const U=()=>{T=!0,p.abort(),M&&!x&&(x=M.cancel().catch(()=>({status:"not_cancellable"})))},q=()=>{I=!0,L("fenced"),U()},S=()=>q();r.addEventListener("abort",S,{once:!0});const P=()=>{const e=this.getLease();e&&e.leaseId===t.leaseId&&e.runtimeEpoch===t.runtimeEpoch||q()},H=setInterval(P,100);H.unref?.();const D=setInterval(()=>{this.authority.renewClaim(s()).then(e=>{f.recordSuccess(),"renewed"===e.status&&e.cancelRequested?(C=!0,L("cancel_requested"),U()):"renewed"!==e.status&&q()}).catch(()=>{f.recordFailure()&&q()})},this.options.claimRenewIntervalMs??2e4);D.unref?.();const j=Date.parse(d.attempt.command.context.executionDeadline)-Date.now(),G=setTimeout(()=>{_=!0,L("deadline"),U()},Math.max(0,Math.min(j,2147483647)));G.unref?.();const z=async e=>{if(!y)return;const r=this.currentConversationRunBoundaryLease(t);await this.finishConversationRunBoundary(m,r,y,e,"recovery"===a?"recovery":"runtime"),y=void 0};try{if(P(),I||r.aborted)return void await i({...m,outcome:"outcome_unknown",code:"INSTANCE_EPOCH_MISMATCH"});if(await this.authority.appendStarted(m),I||r.aborted)return void await i({...m,outcome:"outcome_unknown",code:"INSTANCE_EPOCH_MISMATCH"});if(_)return void await i({...m,outcome:"expired",code:"COMMAND_EXPIRED"});if(C)return void await i({...m,outcome:"cancelled",code:"OPERATION_CANCELLED"});const e=await this.startConversationRunBoundary(m,t,E);if(e?.rejected)return void await i({...m,outcome:"failed",code:e.code});if(y=e,I||r.aborted)throw new RuntimeCommandError("INSTANCE_EPOCH_MISMATCH","runtime authority fenced after the conversation run boundary started");if("recovery"===a&&y)throw new RuntimeCommandError("OUTCOME_UNKNOWN","session.prompt crossed its durable run boundary before recovery");if(_)throw new RuntimeCommandError("COMMAND_EXPIRED","session.prompt expired after the conversation run boundary started");if(C)throw new RuntimeCommandError("OPERATION_CANCELLED","session.prompt was cancelled after the conversation run boundary started");let n;M=this.dispatcher.dispatch(d.attempt.command,h.context),A=!0,T&&U();const s=this.forwardProgress(M.progress,m,()=>I||r.aborted||C||_).catch(e=>{n=e}),c=M.completion.then(e=>(clearTimeout(G),{status:"completed",result:e}),e=>(clearTimeout(G),{status:"failed",error:e}));let u=await Promise.race([c,b.then(e=>({status:"interrupted",reason:e}))]);if("interrupted"===u.status){const e=u.reason;U();if("cancel_requested"===(x?await Promise.race([x,delay(this.options.cancellationGraceMs??1e3).then(()=>({status:"not_cancellable"}))]):{status:"not_cancellable"}).status&&(u=await Promise.race([c,delay(this.options.cancellationGraceMs??1e3).then(()=>({status:"interrupted",reason:e}))])),"interrupted"===u.status)throw new RuntimeCommandError("fenced"===u.reason?"INSTANCE_EPOCH_MISMATCH":"OUTCOME_UNKNOWN","session.prompt execution did not converge after interruption")}if(R=!0,"failed"===u.status)throw u.error;const l=u.result;N=!0;const p="session.prompt"===d.attempt.command.name&&!0===l.requestAborted;if(w=p?"cancelled":"completed",await s,n)throw n;if(await z(w),I||r.aborted)return void await i({...m,outcome:"outcome_unknown",code:"INSTANCE_EPOCH_MISMATCH"});if(_)return void await i({...m,outcome:"outcome_unknown",code:"OUTCOME_UNKNOWN"});if(C&&!p)throw new RuntimeCommandError("OUTCOME_UNKNOWN","cancel request raced a locally completed runtime effect");await this.authority.commitResult({...m,command:d.attempt.command,result:sanitizeServiceResult(d.attempt.command.name,l)}),await o()}catch(e){let t=e;if(y&&(!A||R))try{await z(w??(C||_?"cancelled":"failed"))}catch(r){t=new RuntimeCommandError("OUTCOME_UNKNOWN",`conversation run terminal outcome is unknown: ${errorText(r)}`,void 0,{cause:e})}if(N&&!isUncertainRuntimeError(t)&&(t=new RuntimeCommandError("OUTCOME_UNKNOWN",`runtime effect completed before its terminal receipt: ${errorText(t)}`,void 0,{cause:e})),y)return void(O=!0);const r=serviceRuntimeFailureCode(t),a="OUTCOME_UNKNOWN"===r||"INSTANCE_EPOCH_MISMATCH"===r||I||A&&(C||_),n="COMMAND_EXPIRED"===r||!A&&_,o=!A&&C;await i({...m,outcome:a?"outcome_unknown":n?"expired":o?"cancelled":"failed",code:"INSTANCE_EPOCH_MISMATCH"===r||I?"INSTANCE_EPOCH_MISMATCH":a?"OUTCOME_UNKNOWN":n?"COMMAND_EXPIRED":o?"OPERATION_CANCELLED":r??"RUNTIME_EXECUTION_FAILED"})}finally{clearInterval(H),clearInterval(D),clearTimeout(G),r.removeEventListener("abort",S),p.abort(),await this.authority.releaseClaim({...s(),reason:r.aborted||O?"worker_shutdown":"completed"}).catch(()=>{})}}async startConversationRunBoundary(e,t,r){const a=e.attempt.command;if("session.prompt"!==a.name)return;const n=r?.conversationRunBoundary;if(!n)throw new RuntimeCommandError("INVALID_PARAMS","Service session.prompt lacks its conversation run boundary");const o=a.context.conversation,i=await n.boundary.start({operationId:e.operationId,requestMessageId:e.requestMessageId,conversationId:o.runtimeConversationUid,historyGeneration:n.historyGeneration,runtimeRunId:a.params.runtimeRunId,lease:exactRunBoundaryLease(t)});if("rejected"===i.status)return{rejected:!0,code:i.reason};if(i.historyGeneration!==n.historyGeneration)throw new RuntimeCommandError("OUTCOME_UNKNOWN","conversation run boundary changed history generation");return{boundary:n.boundary,historyGeneration:n.historyGeneration,activeRunRegistrationId:i.activeRunRegistrationId,runtimeRunId:a.params.runtimeRunId}}currentRuntimeLeaseCredential(e){const t=this.getLease();if(!t||t.leaseId!==e.leaseId||t.runtimeEpoch!==e.runtimeEpoch)throw new RuntimeCommandError("INSTANCE_EPOCH_MISMATCH","runtime lease changed before deferred resource redemption");return t.runtimeLeaseCredential}currentConversationRunBoundaryLease(e){const t=this.getLease();if(!t||t.leaseId!==e.leaseId||t.runtimeEpoch!==e.runtimeEpoch||t.handlerIdentity!==e.handlerIdentity)throw new RuntimeCommandError("INSTANCE_EPOCH_MISMATCH","runtime lease changed before conversation run convergence");return t}async finishConversationRunBoundary(e,t,r,a,n){const o=e.attempt.command.context;if("conversation"!==o.scope)throw new RuntimeCommandError("OUTCOME_UNKNOWN","conversation run boundary lost its target");const i=await r.boundary.finish({activeRunRegistrationId:r.activeRunRegistrationId,operationId:e.operationId,requestMessageId:e.requestMessageId,conversationId:o.conversation.runtimeConversationUid,historyGeneration:r.historyGeneration,runtimeRunId:r.runtimeRunId,terminalState:a,source:n,lease:exactRunBoundaryLease(t)});if("rejected"===i.status||i.activeRunRegistrationId!==r.activeRunRegistrationId)throw new RuntimeCommandError("OUTCOME_UNKNOWN",`conversation run finish rejected: ${"rejected"===i.status?i.reason:"registration_mismatch"}`)}async forwardProgress(e,t,r){for await(const a of e){if(r())return;await this.authority.appendProgress({...t,progress:a})}}}function exactRunBoundaryLease(e){return{leaseId:e.leaseId,handlerIdentity:e.handlerIdentity,runtimeEpoch:e.runtimeEpoch,runtimeLeaseCredential:e.runtimeLeaseCredential}}function serviceRuntimeFailureCode(e){const t=e?.code;return t&&["OUTCOME_UNKNOWN","INSTANCE_EPOCH_MISMATCH","COMMAND_EXPIRED","OPERATION_CANCELLED","IDEMPOTENCY_CONFLICT","UNAUTHORIZED_TARGET","AGENT_DEFAULT_MODEL_NOT_CONFIGURED"].includes(t)?t:void 0}function isUncertainRuntimeError(e){return e instanceof RuntimeCommandError&&("OUTCOME_UNKNOWN"===e.code||"INSTANCE_EPOCH_MISMATCH"===e.code)}function errorText(e){return e instanceof Error?e.message:String(e)}export function sanitizeServiceResult(e,t){if("cron.status"!==e||!t||"object"!=typeof t)return t;const r=t;return{enabled:r.enabled,jobCount:r.jobCount,..."number"==typeof r.nextWakeAtMs?{nextWakeAtMs:r.nextWakeAtMs}:{}}}function assertCommandCorrelation(e,t,r){if(e.operationId!==t.context.operationId||e.payload.paramsHash!==t.context.paramsHash||t.context.dispatchEpoch!==r||e.payload.command.name!==t.name||!sessionSetModelMappingMatches(e.payload.request,t))throw new Error("runtime invocation command correlation failed")}function prestartAttempt(e){return{command:e.payload.command,executionBoundaryId:"prestart",boundaryAssertionHash:e.payload.commandHash,executionMode:"start",validationContext:{},authorization:{},derivationContext:{}}}const aborted=Symbol("aborted");function waitForAbort(e,t){return t?t.aborted?(e.catch(()=>{}),Promise.resolve(aborted)):new Promise((r,a)=>{let n=!1;const o=()=>{n||(n=!0,r(aborted))};t.addEventListener("abort",o,{once:!0}),e.then(e=>{n||(n=!0,t.removeEventListener("abort",o),r(e))},e=>{n||(n=!0,t.removeEventListener("abort",o),a(e))})}):e}const delay=(e,t)=>new Promise(r=>{if(t?.aborted)return r();const a=()=>{clearTimeout(n),r()},n=setTimeout(()=>{t?.removeEventListener("abort",a),r()},e);t?.addEventListener("abort",a,{once:!0})});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{createHash}from"node:crypto";import{CONVERSATION_RUNTIME_CANONICAL_COMMAND_NAMES,RUNTIME_HASH_DOMAINS,RUNTIME_INVOCATION_RECOVERY_CLASSES,RUNTIME_RESOURCE_BINDING_CONTRACT_REVISION,SERVICE_CANONICAL_COMMAND_NAMES,canonicalizeJcs,runtimeIdempotencyFingerprintPayload,runtimeHashPreimage,validateRuntimeRpcRequestResourceBindings}from"@beeos-ai/beeos-types/runtime";import{sessionSetModelMappingMatches}from"./session-set-model-mapping.js";export const RUNTIME_MESSAGE_TYPES=Object.freeze({request:"runtime_rpc_request",started:"runtime_operation_started",progress:"runtime_operation_progress",effectCommitted:"runtime_effect_committed",final:"runtime_rpc_response",projectionPending:"runtime_projection_pending",projectionSynced:"runtime_projection_synced",projectionBlocked:"runtime_projection_blocked",cancel:"runtime_operation_cancel_requested"});const methods=new Set(Object.keys(RUNTIME_INVOCATION_RECOVERY_CLASSES)),commandNames={...SERVICE_CANONICAL_COMMAND_NAMES,...CONVERSATION_RUNTIME_CANONICAL_COMMAND_NAMES},sha=/^[0-9a-f]{64}$/,decimal=/^(0|[1-9][0-9]*)$/;export function validateRuntimeRequestEnvelope(e){if(!record(e)||e.type!==RUNTIME_MESSAGE_TYPES.request||!record(e.payload))return{ok:!1,reason:"delivery is not a runtime request"};const t=e,n=t.payload;if(!exactKeys(t,["message_id","offset","channel_id","type","payload","publisher_id","idempotency_key","created_at","state",...void 0===t.in_reply_to?[]:["in_reply_to"]])||void 0!==t.in_reply_to||"completed"!==t.state||!nonempty(t.message_id)||!nonempty(t.channel_id)||!Number.isInteger(t.offset)||t.offset<1||!nonempty(t.publisher_id)||!nonempty(t.idempotency_key))return{ok:!1,reason:"invalid persisted envelope"};const r=void 0!==n.resourceBindingContractRevision,a=void 0!==n.resourceBindings;if(!exactKeys(n,["schemaVersion","operationId","requestMessageId","requestHash","paramsHash","commandHash","idempotencyKeyHash","idempotencyFingerprint","callerIntentHash","callerUserId","callerIdentityId","instanceId","runtimeHandlerIdentity","dispatchEpoch","method","request","command","executionGrantHash","claimDeadlineAt","executionDeadlineAt","createdAt",...r||a?["resourceBindingContractRevision","resourceBindings"]:[]]))return{ok:!1,reason:"request payload fields do not match v1.3"};if(r!==a||r&&n.resourceBindingContractRevision!==RUNTIME_RESOURCE_BINDING_CONTRACT_REVISION||a&&!Array.isArray(n.resourceBindings))return{ok:!1,reason:"resource binding carrier is invalid"};if(!(1===n.schemaVersion&&methods.has(n.method)&&decimal.test(n.dispatchEpoch)&&[n.operationId,n.requestMessageId,n.callerUserId,n.callerIdentityId,n.instanceId,n.runtimeHandlerIdentity].every(nonempty)&&[n.requestHash,n.paramsHash,n.commandHash,n.idempotencyKeyHash,n.idempotencyFingerprint,n.callerIntentHash,n.executionGrantHash].every(e=>"string"==typeof e&&sha.test(e))&&record(n.request)&&record(n.command)&&"2.0"===n.request.jsonrpc&&n.request.method===n.method&&n.command.name===commandNames[n.method]))return{ok:!1,reason:"invalid request union"};if(!sessionSetModelMappingMatches(n.request,n.command))return{ok:!1,reason:"request command mapping mismatch"};if(t.channel_id!==n.operationId||t.message_id!==n.requestMessageId||n.command.context?.operationId!==n.operationId||n.command.context?.runtimeInstanceUid!==n.instanceId||n.command.context?.dispatchEpoch!==n.dispatchEpoch||n.command.context?.paramsHash!==n.paramsHash||n.command.context?.idempotencyKeyHash!==n.idempotencyKeyHash||n.command.context?.claimDeadline!==n.claimDeadlineAt||n.command.context?.executionDeadline!==n.executionDeadlineAt)return{ok:!1,reason:"request correlation mismatch"};try{const e={callerUserId:n.callerUserId,instanceId:n.instanceId,method:n.method,params:n.request.params};if(digest(RUNTIME_HASH_DOMAINS.request,n.request)!==n.requestHash||digest(RUNTIME_HASH_DOMAINS.params,{method:n.method,params:n.request.params})!==n.paramsHash||digest(RUNTIME_HASH_DOMAINS.command,{command:n.command})!==n.commandHash||digest(RUNTIME_HASH_DOMAINS.callerIntent,e)!==n.callerIntentHash||digest(RUNTIME_HASH_DOMAINS.idempotencyFingerprint,runtimeIdempotencyFingerprintPayload(n.callerUserId,n.instanceId,n.method,n.callerIntentHash))!==n.idempotencyFingerprint)return{ok:!1,reason:"canonical hash mismatch"}}catch{return{ok:!1,reason:"canonical hash mismatch"}}if(![n.claimDeadlineAt,n.executionDeadlineAt,n.createdAt].every(e=>"string"==typeof e&&Number.isFinite(Date.parse(e))))return{ok:!1,reason:"invalid request timestamp"};if(r){let e;try{e=validateRuntimeRpcRequestResourceBindings({type:"runtime_rpc_request",operationId:n.operationId,requestMessageId:n.requestMessageId,payload:n},Math.floor(Date.now()/1e3))}catch{return{ok:!1,reason:"resource binding validation failed"}}if(e.length)return{ok:!1,reason:"resource binding validation failed"}}return{ok:!0,envelope:t}}export function validateRuntimeCancelEnvelope(e){if(!record(e)||e.type!==RUNTIME_MESSAGE_TYPES.cancel||!record(e.payload))return{ok:!1,reason:"delivery is not a runtime cancel"};const t=e,n=t.payload;return exactKeys(n,["schemaVersion","operationId","requestMessageId","requestHash","dispatchEpoch","method","eventId","requestedBy",...void 0===n.reason?[]:["reason"]])&&exactKeys(t,["message_id","offset","channel_id","type","payload","publisher_id","idempotency_key","created_at","state"])&&"completed"===t.state&&nonempty(t.message_id)&&Number.isInteger(t.offset)&&!(t.offset<1)&&nonempty(t.publisher_id)&&nonempty(t.idempotency_key)&&1===n.schemaVersion&&methods.has(n.method)&&decimal.test(n.dispatchEpoch)&&sha.test(n.requestHash)&&t.channel_id===n.operationId&&void 0===t.in_reply_to&&nonempty(n.eventId)&&nonempty(n.requestedBy)&&(void 0===n.reason||"string"==typeof n.reason)?{ok:!0,envelope:t}:{ok:!1,reason:"invalid cancel correlation"}}export function runtimeMessageBase(e){return{schemaVersion:1,operationId:e.operationId,requestMessageId:e.requestMessageId,requestHash:e.requestHash,dispatchEpoch:e.dispatchEpoch,method:e.method}}export function runtimeMessageHash(e,t){return digest(e,t)}function digest(e,t){return createHash("sha256").update(runtimeHashPreimage(e,t)).digest("hex")}function record(e){return null!==e&&"object"==typeof e&&!Array.isArray(e)}function nonempty(e){return"string"==typeof e&&e.length>0}function exactKeys(e,t){const n=Object.keys(e).sort(),r=[...t].sort();return n.length===r.length&&n.every((e,t)=>e===r[t])}export function semanticEqual(e,t){return canonicalizeJcs(e)===canonicalizeJcs(t)}
|
|
1
|
+
import{createHash}from"node:crypto";import{CONVERSATION_RUNTIME_CANONICAL_COMMAND_NAMES,RUNTIME_HASH_DOMAINS,RUNTIME_INVOCATION_RECOVERY_CLASSES,RUNTIME_RESOURCE_BINDING_CONTRACT_REVISION,SERVICE_CANONICAL_COMMAND_NAMES,canonicalizeJcs,isCloudRuntimeMessageRequest,runtimeIdempotencyFingerprintPayload,runtimeHashPreimage,validateRuntimeRpcRequestResourceBindings}from"@beeos-ai/beeos-types/runtime";import{sessionSetModelMappingMatches}from"./session-set-model-mapping.js";export const RUNTIME_MESSAGE_TYPES=Object.freeze({request:"runtime_rpc_request",started:"runtime_operation_started",progress:"runtime_operation_progress",effectCommitted:"runtime_effect_committed",final:"runtime_rpc_response",projectionPending:"runtime_projection_pending",projectionSynced:"runtime_projection_synced",projectionBlocked:"runtime_projection_blocked",cancel:"runtime_operation_cancel_requested"});const methods=new Set(Object.keys(RUNTIME_INVOCATION_RECOVERY_CLASSES)),commandNames={...SERVICE_CANONICAL_COMMAND_NAMES,...CONVERSATION_RUNTIME_CANONICAL_COMMAND_NAMES},sha=/^[0-9a-f]{64}$/,decimal=/^(0|[1-9][0-9]*)$/;export function validateRuntimeRequestEnvelope(e){if(!record(e)||e.type!==RUNTIME_MESSAGE_TYPES.request||!record(e.payload))return{ok:!1,reason:"delivery is not a runtime request"};const t=e,n=t.payload;if(!exactKeys(t,["message_id","offset","channel_id","type","payload","publisher_id","idempotency_key","created_at","state",...void 0===t.in_reply_to?[]:["in_reply_to"]])||void 0!==t.in_reply_to||"completed"!==t.state||!nonempty(t.message_id)||!nonempty(t.channel_id)||!Number.isInteger(t.offset)||t.offset<1||!nonempty(t.publisher_id)||!nonempty(t.idempotency_key))return{ok:!1,reason:"invalid persisted envelope"};if(2===n.schemaVersion)return isCloudRuntimeMessageRequest(n,digest)?t.channel_id!==n.operationId||t.message_id!==n.requestMessageId?{ok:!1,reason:"request correlation mismatch"}:{ok:!0,envelope:t}:{ok:!1,reason:"invalid Cloud runtime request"};const r=void 0!==n.resourceBindingContractRevision,a=void 0!==n.resourceBindings;if(!exactKeys(n,["schemaVersion","operationId","requestMessageId","requestHash","paramsHash","commandHash","idempotencyKeyHash","idempotencyFingerprint","callerIntentHash","callerUserId","callerIdentityId","instanceId","runtimeHandlerIdentity","dispatchEpoch","method","request","command","executionGrantHash","claimDeadlineAt","executionDeadlineAt","createdAt",...r||a?["resourceBindingContractRevision","resourceBindings"]:[]]))return{ok:!1,reason:"request payload fields do not match v1.3"};if(r!==a||r&&n.resourceBindingContractRevision!==RUNTIME_RESOURCE_BINDING_CONTRACT_REVISION||a&&!Array.isArray(n.resourceBindings))return{ok:!1,reason:"resource binding carrier is invalid"};if(!(1===n.schemaVersion&&methods.has(n.method)&&decimal.test(n.dispatchEpoch)&&[n.operationId,n.requestMessageId,n.callerUserId,n.callerIdentityId,n.instanceId,n.runtimeHandlerIdentity].every(nonempty)&&[n.requestHash,n.paramsHash,n.commandHash,n.idempotencyKeyHash,n.idempotencyFingerprint,n.callerIntentHash,n.executionGrantHash].every(e=>"string"==typeof e&&sha.test(e))&&record(n.request)&&record(n.command)&&"2.0"===n.request.jsonrpc&&n.request.method===n.method&&n.command.name===commandNames[n.method]))return{ok:!1,reason:"invalid request union"};if(!sessionSetModelMappingMatches(n.request,n.command))return{ok:!1,reason:"request command mapping mismatch"};if(t.channel_id!==n.operationId||t.message_id!==n.requestMessageId||n.command.context?.operationId!==n.operationId||n.command.context?.runtimeInstanceUid!==n.instanceId||n.command.context?.dispatchEpoch!==n.dispatchEpoch||n.command.context?.paramsHash!==n.paramsHash||n.command.context?.idempotencyKeyHash!==n.idempotencyKeyHash||n.command.context?.claimDeadline!==n.claimDeadlineAt||n.command.context?.executionDeadline!==n.executionDeadlineAt)return{ok:!1,reason:"request correlation mismatch"};try{const e={callerUserId:n.callerUserId,instanceId:n.instanceId,method:n.method,params:n.request.params};if(digest(RUNTIME_HASH_DOMAINS.request,n.request)!==n.requestHash||digest(RUNTIME_HASH_DOMAINS.params,{method:n.method,params:n.request.params})!==n.paramsHash||digest(RUNTIME_HASH_DOMAINS.command,{command:n.command})!==n.commandHash||digest(RUNTIME_HASH_DOMAINS.callerIntent,e)!==n.callerIntentHash||digest(RUNTIME_HASH_DOMAINS.idempotencyFingerprint,runtimeIdempotencyFingerprintPayload(n.callerUserId,n.instanceId,n.method,n.callerIntentHash))!==n.idempotencyFingerprint)return{ok:!1,reason:"canonical hash mismatch"}}catch{return{ok:!1,reason:"canonical hash mismatch"}}if(![n.claimDeadlineAt,n.executionDeadlineAt,n.createdAt].every(e=>"string"==typeof e&&Number.isFinite(Date.parse(e))))return{ok:!1,reason:"invalid request timestamp"};if(r){let e;try{e=validateRuntimeRpcRequestResourceBindings({type:"runtime_rpc_request",operationId:n.operationId,requestMessageId:n.requestMessageId,payload:n},Math.floor(Date.now()/1e3))}catch{return{ok:!1,reason:"resource binding validation failed"}}if(e.length)return{ok:!1,reason:"resource binding validation failed"}}return{ok:!0,envelope:t}}export function validateRuntimeCancelEnvelope(e){if(!record(e)||e.type!==RUNTIME_MESSAGE_TYPES.cancel||!record(e.payload))return{ok:!1,reason:"delivery is not a runtime cancel"};const t=e,n=t.payload;return exactKeys(n,["schemaVersion","operationId","requestMessageId","requestHash","dispatchEpoch","method","eventId","requestedBy",...void 0===n.reason?[]:["reason"]])&&exactKeys(t,["message_id","offset","channel_id","type","payload","publisher_id","idempotency_key","created_at","state"])&&"completed"===t.state&&nonempty(t.message_id)&&Number.isInteger(t.offset)&&!(t.offset<1)&&nonempty(t.publisher_id)&&nonempty(t.idempotency_key)&&1===n.schemaVersion&&methods.has(n.method)&&decimal.test(n.dispatchEpoch)&&sha.test(n.requestHash)&&t.channel_id===n.operationId&&void 0===t.in_reply_to&&nonempty(n.eventId)&&nonempty(n.requestedBy)&&(void 0===n.reason||"string"==typeof n.reason)?{ok:!0,envelope:t}:{ok:!1,reason:"invalid cancel correlation"}}export function runtimeMessageBase(e){return{schemaVersion:1,operationId:e.operationId,requestMessageId:e.requestMessageId,requestHash:e.requestHash,dispatchEpoch:e.dispatchEpoch,method:e.method}}export function runtimeMessageHash(e,t){return digest(e,t)}function digest(e,t){return createHash("sha256").update(runtimeHashPreimage(e,t)).digest("hex")}function record(e){return null!==e&&"object"==typeof e&&!Array.isArray(e)}function nonempty(e){return"string"==typeof e&&e.length>0}function exactKeys(e,t){const n=Object.keys(e).sort(),r=[...t].sort();return n.length===r.length&&n.every((e,t)=>e===r[t])}export function semanticEqual(e,t){return canonicalizeJcs(e)===canonicalizeJcs(t)}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{createHash,createPrivateKey,randomUUID}from"node:crypto";import{join}from"node:path";import{CONVERSATION_RUNTIME_CANONICAL_COMMAND_NAMES,RUNTIME_METHOD_PARITY_MANIFEST,RUNTIME_RESOURCE_BINDING_CONTRACT_REVISION,RUNTIME_RPC_CONTRACT_REVISION}from"@beeos-ai/beeos-types/runtime";import{agentBoundFetch}from"../agent-auth.js";import{FileRuntimeCommandJournal}from"./runtime-command-journal.js";import{FileRuntimeAgentIdentityRegistry}from"./runtime-agent-identity-registry.js";import{RuntimeRegistrationIdentityStore}from"./runtime-registration-identity-store.js";import{InstanceAuthorityRuntimeRegistrationTransport,RuntimeRegistrationCoordinator,RuntimeRegistrationJournalAdvanceRequiredError}from"./runtime-registration.js";import{verifiedRuntimeParityManifestDigest}from"./runtime-parity-manifest-digest.js";import{intersectRuntimeServiceMethodsWithBackendCapabilities}from"./runtime-service-contract-compatibility.js";export class RuntimeProductionAuthority{options;journal;identities;identityStore;handlerIdentity;listeners=new Set;coordinator;unsubscribeCoordinator;starting;rolling=new Map;refreshing;stoppedCoordinators=new WeakSet;coordinatorGeneration=0;lineage;identity;preparing;runtimeConsumerLive=!1;runtimeServiceMethods=Object.freeze([]);realtimeCapabilityReady=new Map;resourceBindingContractRevision;manifestDigest=verifiedRuntimeParityManifestDigest();started=!1;lifecycleVersion=0;authorityLeasePending=!1;boundAgentFetch;constructor(t){this.options=t,this.handlerIdentity=t.handlerIdentity??(process.env.HOSTNAME?.trim()||process.env.POD_NAME?.trim()||randomUUID()),this.journal=new FileRuntimeCommandJournal(join(t.stateDirectory,"runtime-command-journal.json"),void 0,void 0,{onMaintenanceError:e=>{const i=`[runtime-journal] event=checkpoint_failed trigger=threshold error=${e instanceof Error?e.message:String(e)}`;t.logger?.error?t.logger.error(i):t.logger?.warn?.(i)}}),this.identities=new FileRuntimeAgentIdentityRegistry(join(t.stateDirectory,"runtime-agent-identities.json")),this.boundAgentFetch=t.boundAgentFetch??((e,i)=>agentBoundFetch(e,t.keys,i)),this.identityStore=new RuntimeRegistrationIdentityStore(join(t.stateDirectory,"runtime-registration-identity.json"))}get agentKeys(){return this.options.keys}get stateDirectory(){return this.options.stateDirectory}get authorityLease(){return this.coordinator?.authorityLease??null}subscribeAuthority(t){return this.listeners.add(t),()=>{this.listeners.delete(t)}}currentConsumerBinding(){return this.identity&&this.lineage?this.consumerBinding(this.identity,this.lineage):null}async prepareConsumerBinding(){return this.preparing||(this.preparing=(async()=>{const t=await this.identityStore.acquire(this.handlerIdentity,this.journal),e={journalStoreId:t.journalStoreId,journalGeneration:t.journalGeneration};return this.lineage=e,this.identity=t,this.consumerBinding(t,e)})().finally(()=>{this.preparing=void 0})),await this.preparing}setRuntimeConsumerLive(t){if(this.started)throw new Error("runtime consumer availability is immutable while registered");this.runtimeConsumerLive=t}setRuntimeServiceMethods(t){if(this.started)throw new Error("runtime Service methods are immutable while registered");this.runtimeServiceMethods=intersectRuntimeServiceMethodsWithBackendCapabilities(t)}setRuntimeRealtimeCapabilityReady(t,e){const i=this.realtimeCapabilityReady.get(t)!==e;if(this.realtimeCapabilityReady.set(t,e),!i||!this.started)return;const r=this.lifecycleVersion;this.refreshRealtimeCapabilities(r).catch(t=>this.options.logger?.warn?.(`[runtime-authority] authority lease refresh fenced (realtime capability): ${t instanceof Error?t.message:t}`))}setRuntimeResourceBindingContractRevision(t){if(this.started)throw new Error("runtime resource binding revision is immutable while registered");if(void 0!==t&&t!==RUNTIME_RESOURCE_BINDING_CONTRACT_REVISION)throw new Error("unsupported runtime resource binding revision");this.resourceBindingContractRevision=t}async start(t){if(this.starting)return await this.starting;if(this.started)return;if(t?.aborted)return;const e=this.lifecycleVersion,i=this.startOnce(e,t).finally(()=>{this.starting===i&&(this.starting=void 0)});this.starting=i,await i}stop(){this.lifecycleVersion++,this.starting=void 0,this.started=!1;const t=this.coordinator;t&&this.detachCoordinator(t),this.publish(null)}async startOnce(t,e){if(await this.prepareConsumerBinding(),t!==this.lifecycleVersion||e?.aborted)return;this.lineage;const i=this.identity;this.started=!0,await this.activate(i,t)}async activate(t,e){if(!this.isLifecycleCurrent(e))return;if(this.resourceBindingContractRevision&&!this.runtimeConsumerLive)throw new Error("resource binding revision requires a live Message Service consumer");if(!this.runtimeConsumerLive||0===this.runtimeServiceMethods.length)throw new Error(`runtime registration refused: consumerLive=${this.runtimeConsumerLive} methods=${this.runtimeServiceMethods.length}`);const i=[...this.runtimeServiceMethods],r=runtimeCapabilities(this.runtimeServiceMethods,this.realtimeCapabilityReady);this.options.logger?.info?.(`[runtime-authority] advertising ${i.length} methods (renew-safe)`);const n=++this.coordinatorGeneration,o=new RuntimeRegistrationCoordinator(new InstanceAuthorityRuntimeRegistrationTransport({agentGatewayUrl:this.options.agentGatewayUrl,registrationId:t.registrationId,agentFetch:this.options.agentFetch}),{registrationId:t.registrationId,handlerIdentity:t.handlerIdentity,contractRevision:RUNTIME_RPC_CONTRACT_REVISION,runtimeMethods:i,capabilities:r,manifestDigest:this.manifestDigest,journalStoreId:t.journalStoreId,journalGeneration:t.journalGeneration,instanceIdentityKeyId:createHash("sha256").update(this.options.keys.publicKey).digest("hex"),privateKey:createPrivateKey({key:privateKeyDer(this.options.keys.privateKey),format:"der",type:"pkcs8"}),getMSConnectionStatus:this.options.getMSConnectionStatus},(t,i)=>{if(!this.isCoordinatorCurrent(e,n,o))return;const r=i instanceof Error?i.message:void 0!==i?String(i):"";this.options.logger?.warn?.(r?`[runtime-authority] lease lost: ${t}: ${r}`:`[runtime-authority] lease lost: ${t}`);const s=i instanceof RuntimeRegistrationJournalAdvanceRequiredError?i:void 0;("expired"===t||s)&&this.rollExpired(e,n,o,s).catch(t=>this.options.logger?.warn?.(`[runtime-authority] authority lease roll fenced (expired registration): ${t instanceof Error?t.message:t}`))});this.coordinator=o,this.unsubscribeCoordinator=o.subscribeAuthority(t=>{this.isCoordinatorCurrent(e,n,o)&&(t&&this.authorityLeasePending?(this.authorityLeasePending=!1,this.options.logger?.info?.("[runtime-authority] registration active (recovered after handoff)")):t||(this.authorityLeasePending=!0),this.publish(t))});const s=await o.start();this.isCoordinatorCurrent(e,n,o)?(this.options.logger?.info?.("[runtime-authority] registration "+(s?"active":"pending")),s||(this.authorityLeasePending=!0)):this.stopCoordinatorOnce(o)}async rollExpired(t,e,i,r){if(!this.lineage||!this.isCoordinatorCurrent(t,e,i))return;const n=this.rolling.get(e);if(n)return await n;let o=!1;const s=(async()=>{if(!this.isCoordinatorCurrent(t,e,i))return;this.detachCoordinator(i),o=!0;const n=this.identity;if(!n)return;const s=await this.identityStore.roll(n,this.journal,{authorityCurrentGeneration:r?.currentJournalGeneration,expectedJournalGeneration:r?.expectedJournalGeneration});this.isLifecycleCurrent(t)&&void 0===this.coordinator&&(this.identity=s,this.lineage={journalStoreId:s.journalStoreId,journalGeneration:s.journalGeneration},await this.activate(s,t))})().finally(()=>{this.rolling.get(e)===s&&this.rolling.delete(e),o&&this.publishDetachedWithoutLease("expired registration roll")});this.rolling.set(e,s),await s}async refreshRealtimeCapabilities(t){if(!this.isLifecycleCurrent(t))return;const e=this.refreshing;if(e)return await e;let i=!1;const r=(async()=>{if(!this.isLifecycleCurrent(t))return;const e=this.identity,r=this.coordinator;e&&r&&(r.authorityLease?(this.detachCoordinator(r),i=!0,this.isLifecycleCurrent(t)&&void 0===this.coordinator&&await this.activate(e,t)):this.options.logger?.info?.("[runtime-authority] deferring realtime capability refresh until registration is active"))})().finally(()=>{this.refreshing===r&&(this.refreshing=void 0),i&&this.publishDetachedWithoutLease("realtime capability refresh")});this.refreshing=r,await r}isLifecycleCurrent(t){return this.started&&t===this.lifecycleVersion}isCoordinatorCurrent(t,e,i){return this.isLifecycleCurrent(t)&&this.coordinatorGeneration===e&&this.coordinator===i}detachCoordinator(t){this.coordinator===t&&(this.unsubscribeCoordinator?.(),this.unsubscribeCoordinator=void 0,this.coordinator=void 0),this.stopCoordinatorOnce(t)}publishDetachedWithoutLease(t){null===this.authorityLease&&(this.authorityLeasePending=!0,this.options.logger?.warn?.(`[runtime-authority] authority lease cleared with no live successor lease (${t})`),this.publish(null))}stopCoordinatorOnce(t){this.stoppedCoordinators.has(t)||(this.stoppedCoordinators.add(t),t.stop())}publish(t){for(const e of this.listeners)try{e(t)}catch{}}consumerBinding(t,e){return Object.freeze({handlerIdentity:t.handlerIdentity,journalStoreId:e.journalStoreId,journalGeneration:e.journalGeneration})}}const ED25519_PKCS8_PREFIX=Buffer.from("302e020100300506032b657004220420","hex"),privateKeyDer=t=>32===t.length?Buffer.concat([ED25519_PKCS8_PREFIX,Buffer.from(t)]):Buffer.from(t),REALTIME_CAPABILITY_NAMES=["terminal","canvas"];function runtimeCapabilities(t,e){const i=new Set(t),r=RUNTIME_METHOD_PARITY_MANIFEST.filter(t=>t.serviceAllowed&&void 0!==runtimeInvocationMethod(t)&&i.has(runtimeInvocationMethod(t))).map(t=>t.capability),n=REALTIME_CAPABILITY_NAMES.filter(t=>!0===e.get(t));return[...new Set([...r,...n])]}function runtimeInvocationMethod(t){return"methods"===t.serviceRoute&&t.serviceMethod?t.serviceMethod:Object.entries(CONVERSATION_RUNTIME_CANONICAL_COMMAND_NAMES).find(([,e])=>e===t.canonicalCommand)?.[0]}
|
|
1
|
+
import{createHash,createPrivateKey,randomUUID,sign}from"node:crypto";import{join}from"node:path";import{CONVERSATION_RUNTIME_CANONICAL_COMMAND_NAMES,RUNTIME_METHOD_PARITY_MANIFEST,RUNTIME_RESOURCE_BINDING_CONTRACT_REVISION,RUNTIME_RPC_CONTRACT_REVISION}from"@beeos-ai/beeos-types/runtime";import{agentBoundFetch}from"../agent-auth.js";import{FileRuntimeCommandJournal}from"./runtime-command-journal.js";import{FileRuntimeAgentIdentityRegistry}from"./runtime-agent-identity-registry.js";import{RuntimeRegistrationIdentityStore}from"./runtime-registration-identity-store.js";import{InstanceAuthorityRuntimeRegistrationTransport,RuntimeRegistrationCoordinator,RuntimeRegistrationJournalAdvanceRequiredError}from"@beeos-ai/cloud-agent-sdk";import{verifiedRuntimeParityManifestDigest}from"./runtime-parity-manifest-digest.js";import{intersectRuntimeServiceMethodsWithBackendCapabilities}from"./runtime-service-contract-compatibility.js";export function cloudRuntimeTargetInstanceId(t){const e=t.BEEOS_CLOUD_RUNTIME_INSTANCE_ID;if(void 0!==e){if(!/^[A-Za-z0-9_-]{1,128}$/.test(e)||e!==t.BEEOS_INSTANCE_ID)throw new Error("Cloud runtime target must match the provisioned BeeOS instance identity");return e}}export class RuntimeProductionAuthority{options;journal;identities;identityStore;handlerIdentity;listeners=new Set;coordinator;unsubscribeCoordinator;starting;rolling=new Map;refreshing;stoppedCoordinators=new WeakSet;coordinatorGeneration=0;lineage;identity;preparing;runtimeConsumerLive=!1;runtimeServiceMethods=Object.freeze([]);realtimeCapabilityReady=new Map;resourceBindingContractRevision;manifestDigest=verifiedRuntimeParityManifestDigest();started=!1;lifecycleVersion=0;authorityLeasePending=!1;boundAgentFetch;constructor(t){if(this.options=t,void 0!==t.targetInstanceId&&!/^[A-Za-z0-9_-]{1,128}$/.test(t.targetInstanceId))throw new Error("Cloud runtime target instance ID is invalid");this.handlerIdentity=t.handlerIdentity??(process.env.HOSTNAME?.trim()||process.env.POD_NAME?.trim()||randomUUID()),this.journal=new FileRuntimeCommandJournal(join(t.stateDirectory,"runtime-command-journal.json"),void 0,void 0,{onMaintenanceError:e=>{const i=`[runtime-journal] event=checkpoint_failed trigger=threshold error=${e instanceof Error?e.message:String(e)}`;t.logger?.error?t.logger.error(i):t.logger?.warn?.(i)}}),this.identities=new FileRuntimeAgentIdentityRegistry(join(t.stateDirectory,"runtime-agent-identities.json")),this.boundAgentFetch=t.boundAgentFetch??((e,i)=>agentBoundFetch(e,t.keys,i)),this.identityStore=new RuntimeRegistrationIdentityStore(join(t.stateDirectory,"runtime-registration-identity.json"))}get agentKeys(){return this.options.keys}get stateDirectory(){return this.options.stateDirectory}get cloudTargetInstanceId(){return this.options.targetInstanceId}get authorityLease(){return this.coordinator?.authorityLease??null}subscribeAuthority(t){return this.listeners.add(t),()=>{this.listeners.delete(t)}}currentConsumerBinding(){return this.identity&&this.lineage?this.consumerBinding(this.identity,this.lineage):null}async prepareConsumerBinding(){return this.preparing||(this.preparing=(async()=>{const t=await this.identityStore.acquire(this.handlerIdentity,this.journal),e={journalStoreId:t.journalStoreId,journalGeneration:t.journalGeneration};return this.lineage=e,this.identity=t,this.consumerBinding(t,e)})().finally(()=>{this.preparing=void 0})),await this.preparing}setRuntimeConsumerLive(t){if(this.started)throw new Error("runtime consumer availability is immutable while registered");this.runtimeConsumerLive=t}setRuntimeServiceMethods(t){if(this.started)throw new Error("runtime Service methods are immutable while registered");this.runtimeServiceMethods=intersectRuntimeServiceMethodsWithBackendCapabilities(t)}setRuntimeRealtimeCapabilityReady(t,e){const i=this.realtimeCapabilityReady.get(t)!==e;if(this.realtimeCapabilityReady.set(t,e),!i||!this.started)return;const r=this.lifecycleVersion;this.refreshRealtimeCapabilities(r).catch(t=>this.options.logger?.warn?.(`[runtime-authority] authority lease refresh fenced (realtime capability): ${t instanceof Error?t.message:t}`))}setRuntimeResourceBindingContractRevision(t){if(this.started)throw new Error("runtime resource binding revision is immutable while registered");if(void 0!==t&&t!==RUNTIME_RESOURCE_BINDING_CONTRACT_REVISION)throw new Error("unsupported runtime resource binding revision");this.resourceBindingContractRevision=t}async start(t){if(this.starting)return await this.starting;if(this.started)return;if(t?.aborted)return;const e=this.lifecycleVersion,i=this.startOnce(e,t).finally(()=>{this.starting===i&&(this.starting=void 0)});this.starting=i,await i}stop(){this.lifecycleVersion++,this.starting=void 0,this.started=!1;const t=this.coordinator;t&&this.detachCoordinator(t),this.publish(null)}async startOnce(t,e){if(await this.prepareConsumerBinding(),t!==this.lifecycleVersion||e?.aborted)return;this.lineage;const i=this.identity;this.started=!0,await this.activate(i,t)}async activate(t,e){if(!this.isLifecycleCurrent(e))return;if(this.resourceBindingContractRevision&&!this.runtimeConsumerLive)throw new Error("resource binding revision requires a live Message Service consumer");if(!this.runtimeConsumerLive||0===this.runtimeServiceMethods.length)throw new Error(`runtime registration refused: consumerLive=${this.runtimeConsumerLive} methods=${this.runtimeServiceMethods.length}`);const i=[...this.runtimeServiceMethods],r=runtimeCapabilities(this.runtimeServiceMethods,this.realtimeCapabilityReady);this.options.logger?.info?.(`[runtime-authority] advertising ${i.length} methods (renew-safe)`);const n=++this.coordinatorGeneration,o=new RuntimeRegistrationCoordinator(new InstanceAuthorityRuntimeRegistrationTransport({agentGatewayUrl:this.options.agentGatewayUrl,registrationId:t.registrationId,agentFetch:this.options.agentFetch}),{registrationId:t.registrationId,handlerIdentity:t.handlerIdentity,contractRevision:RUNTIME_RPC_CONTRACT_REVISION,runtimeMethods:i,capabilities:r,manifestDigest:this.manifestDigest,journalStoreId:t.journalStoreId,journalGeneration:t.journalGeneration,instanceIdentityKeyId:createHash("sha256").update(this.options.keys.publicKey).digest("hex"),targetInstanceId:this.options.targetInstanceId,signIdentityProof:t=>sign(null,t,createPrivateKey({key:privateKeyDer(this.options.keys.privateKey),format:"der",type:"pkcs8"})),getMSConnectionStatus:this.options.getMSConnectionStatus},(t,i)=>{if(!this.isCoordinatorCurrent(e,n,o))return;const r=i instanceof Error?i.message:void 0!==i?String(i):"";this.options.logger?.warn?.(r?`[runtime-authority] lease lost: ${t}: ${r}`:`[runtime-authority] lease lost: ${t}`);const s=i instanceof RuntimeRegistrationJournalAdvanceRequiredError?i:void 0;("expired"===t||s)&&this.rollExpired(e,n,o,s).catch(t=>this.options.logger?.warn?.(`[runtime-authority] authority lease roll fenced (expired registration): ${t instanceof Error?t.message:t}`))});this.coordinator=o,this.unsubscribeCoordinator=o.subscribeAuthority(t=>{this.isCoordinatorCurrent(e,n,o)&&(t&&this.authorityLeasePending?(this.authorityLeasePending=!1,this.options.logger?.info?.("[runtime-authority] registration active (recovered after handoff)")):t||(this.authorityLeasePending=!0),this.publish(t))});const s=await o.start();this.isCoordinatorCurrent(e,n,o)?(this.options.logger?.info?.("[runtime-authority] registration "+(s?"active":"pending")),s||(this.authorityLeasePending=!0)):this.stopCoordinatorOnce(o)}async rollExpired(t,e,i,r){if(!this.lineage||!this.isCoordinatorCurrent(t,e,i))return;const n=this.rolling.get(e);if(n)return await n;let o=!1;const s=(async()=>{if(!this.isCoordinatorCurrent(t,e,i))return;this.detachCoordinator(i),o=!0;const n=this.identity;if(!n)return;const s=await this.identityStore.roll(n,this.journal,{authorityCurrentGeneration:r?.currentJournalGeneration,expectedJournalGeneration:r?.expectedJournalGeneration});this.isLifecycleCurrent(t)&&void 0===this.coordinator&&(this.identity=s,this.lineage={journalStoreId:s.journalStoreId,journalGeneration:s.journalGeneration},await this.activate(s,t))})().finally(()=>{this.rolling.get(e)===s&&this.rolling.delete(e),o&&this.publishDetachedWithoutLease("expired registration roll")});this.rolling.set(e,s),await s}async refreshRealtimeCapabilities(t){if(!this.isLifecycleCurrent(t))return;const e=this.refreshing;if(e)return await e;let i=!1;const r=(async()=>{if(!this.isLifecycleCurrent(t))return;const e=this.identity,r=this.coordinator;e&&r&&(r.authorityLease?(this.detachCoordinator(r),i=!0,this.isLifecycleCurrent(t)&&void 0===this.coordinator&&await this.activate(e,t)):this.options.logger?.info?.("[runtime-authority] deferring realtime capability refresh until registration is active"))})().finally(()=>{this.refreshing===r&&(this.refreshing=void 0),i&&this.publishDetachedWithoutLease("realtime capability refresh")});this.refreshing=r,await r}isLifecycleCurrent(t){return this.started&&t===this.lifecycleVersion}isCoordinatorCurrent(t,e,i){return this.isLifecycleCurrent(t)&&this.coordinatorGeneration===e&&this.coordinator===i}detachCoordinator(t){this.coordinator===t&&(this.unsubscribeCoordinator?.(),this.unsubscribeCoordinator=void 0,this.coordinator=void 0),this.stopCoordinatorOnce(t)}publishDetachedWithoutLease(t){null===this.authorityLease&&(this.authorityLeasePending=!0,this.options.logger?.warn?.(`[runtime-authority] authority lease cleared with no live successor lease (${t})`),this.publish(null))}stopCoordinatorOnce(t){this.stoppedCoordinators.has(t)||(this.stoppedCoordinators.add(t),t.stop())}publish(t){for(const e of this.listeners)try{e(t)}catch{}}consumerBinding(t,e){return Object.freeze({handlerIdentity:t.handlerIdentity,journalStoreId:e.journalStoreId,journalGeneration:e.journalGeneration})}}const ED25519_PKCS8_PREFIX=Buffer.from("302e020100300506032b657004220420","hex"),privateKeyDer=t=>32===t.length?Buffer.concat([ED25519_PKCS8_PREFIX,Buffer.from(t)]):Buffer.from(t),REALTIME_CAPABILITY_NAMES=["terminal","canvas"];function runtimeCapabilities(t,e){const i=new Set(t),r=RUNTIME_METHOD_PARITY_MANIFEST.filter(t=>t.serviceAllowed&&void 0!==runtimeInvocationMethod(t)&&i.has(runtimeInvocationMethod(t))).map(t=>t.capability),n=REALTIME_CAPABILITY_NAMES.filter(t=>!0===e.get(t));return[...new Set([...r,...n])]}function runtimeInvocationMethod(t){return"methods"===t.serviceRoute&&t.serviceMethod?t.serviceMethod:Object.entries(CONVERSATION_RUNTIME_CANONICAL_COMMAND_NAMES).find(([,e])=>e===t.canonicalCommand)?.[0]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{createHash}from"node:crypto";import{readFileSync}from"node:fs";import{RUNTIME_METHOD_PARITY_MANIFEST,RUNTIME_RPC_CONTRACT_REVISION}from"@beeos-ai/beeos-types/runtime";import{RUNTIME_AUTHORITY_PORTS_REVISION,RUNTIME_AUTHORITY_PORTS_RAW_SHA256,RUNTIME_AUTHORITY_PORTS_SHA256,RUNTIME_MESSAGE_CHANNEL_REVISION,RUNTIME_MESSAGE_CHANNEL_RAW_SHA256,RUNTIME_MESSAGE_CHANNEL_SHA256,RUNTIME_PARITY_MANIFEST,RUNTIME_PARITY_MANIFEST_RAW_SHA256,RUNTIME_PARITY_MANIFEST_REVISION,RUNTIME_PARITY_MANIFEST_SHA256,RUNTIME_PARITY_READY_METHODS,RUNTIME_SESSION_CANCEL_REVISION,RUNTIME_SESSION_CANCEL_SHA256}from"./runtime-parity-manifest.js";import{verifiedRuntimeParityManifestDigest}from"./runtime-parity-manifest-digest.js";export const RUNTIME_SERVICE_WIRE_CONTRACT_ARTIFACTS=Object.freeze([Object.freeze({revision:RUNTIME_MESSAGE_CHANNEL_REVISION,sha256:RUNTIME_MESSAGE_CHANNEL_SHA256,rawSha256:RUNTIME_MESSAGE_CHANNEL_RAW_SHA256}),Object.freeze({revision:RUNTIME_AUTHORITY_PORTS_REVISION,sha256:RUNTIME_AUTHORITY_PORTS_SHA256,rawSha256:RUNTIME_AUTHORITY_PORTS_RAW_SHA256}),Object.freeze({revision:RUNTIME_SESSION_CANCEL_REVISION,sha256:RUNTIME_SESSION_CANCEL_SHA256}),Object.freeze({revision:RUNTIME_PARITY_MANIFEST_REVISION,sha256:RUNTIME_PARITY_MANIFEST_SHA256})]);export function loadRuntimeServiceContractIdentity(){return verifyPackagedRuntimeServiceWireIdentity(),Object.freeze({contractRevision:RUNTIME_RPC_CONTRACT_REVISION,canonicalManifestDigest:verifiedRuntimeParityManifestDigest(),wireArtifacts:RUNTIME_SERVICE_WIRE_CONTRACT_ARTIFACTS})}const RUNTIME_SERVICE_WIRE_IDENTITY_RAW_SHA256="
|
|
1
|
+
import{createHash}from"node:crypto";import{readFileSync}from"node:fs";import{RUNTIME_METHOD_PARITY_MANIFEST,RUNTIME_RPC_CONTRACT_REVISION}from"@beeos-ai/beeos-types/runtime";import{RUNTIME_AUTHORITY_PORTS_REVISION,RUNTIME_AUTHORITY_PORTS_RAW_SHA256,RUNTIME_AUTHORITY_PORTS_SHA256,RUNTIME_MESSAGE_CHANNEL_REVISION,RUNTIME_MESSAGE_CHANNEL_RAW_SHA256,RUNTIME_MESSAGE_CHANNEL_SHA256,RUNTIME_PARITY_MANIFEST,RUNTIME_PARITY_MANIFEST_RAW_SHA256,RUNTIME_PARITY_MANIFEST_REVISION,RUNTIME_PARITY_MANIFEST_SHA256,RUNTIME_PARITY_READY_METHODS,RUNTIME_SESSION_CANCEL_REVISION,RUNTIME_SESSION_CANCEL_SHA256}from"./runtime-parity-manifest.js";import{verifiedRuntimeParityManifestDigest}from"./runtime-parity-manifest-digest.js";export const RUNTIME_SERVICE_WIRE_CONTRACT_ARTIFACTS=Object.freeze([Object.freeze({revision:RUNTIME_MESSAGE_CHANNEL_REVISION,sha256:RUNTIME_MESSAGE_CHANNEL_SHA256,rawSha256:RUNTIME_MESSAGE_CHANNEL_RAW_SHA256}),Object.freeze({revision:RUNTIME_AUTHORITY_PORTS_REVISION,sha256:RUNTIME_AUTHORITY_PORTS_SHA256,rawSha256:RUNTIME_AUTHORITY_PORTS_RAW_SHA256}),Object.freeze({revision:RUNTIME_SESSION_CANCEL_REVISION,sha256:RUNTIME_SESSION_CANCEL_SHA256}),Object.freeze({revision:RUNTIME_PARITY_MANIFEST_REVISION,sha256:RUNTIME_PARITY_MANIFEST_SHA256})]);export function loadRuntimeServiceContractIdentity(){return verifyPackagedRuntimeServiceWireIdentity(),Object.freeze({contractRevision:RUNTIME_RPC_CONTRACT_REVISION,canonicalManifestDigest:verifiedRuntimeParityManifestDigest(),wireArtifacts:RUNTIME_SERVICE_WIRE_CONTRACT_ARTIFACTS})}const RUNTIME_SERVICE_WIRE_IDENTITY_RAW_SHA256="fce53f44ff965c7f1ca0aab2cc7126eaf0b0a172b2891a57d8b59c2f6db12e93";function verifyPackagedRuntimeServiceWireIdentity(){verifyRuntimeServiceWireIdentityBytes(readFileSync(new URL("../../runtime-service-wire-identity.v1.json",import.meta.url)))}export function verifyRuntimeServiceWireIdentityBytes(e){if(createHash("sha256").update(e).digest("hex")!==RUNTIME_SERVICE_WIRE_IDENTITY_RAW_SHA256)throw new Error("packaged runtime Service wire identity bytes drifted");const i=JSON.parse(Buffer.from(e).toString("utf8"));if(1!==i.schemaVersion||"RFC8785/JCS"!==i.canonicalization||"SHA-256"!==i.hashAlgorithm||"@beeos-ai/beeos-types"!==i.canonicalTypes?.package||"4.4.2-beta.2"!==i.canonicalTypes.version||i.canonicalTypes.runtimeRpcContractRevision!==RUNTIME_RPC_CONTRACT_REVISION||"2af68ff555d4a4aa3c7b19a285e48a3df2ef9b43aa618ffddabbebc6e4280162"!==i.canonicalTypes.runtimeMethodManifestRawSHA256||i.canonicalTypes.runtimeMethodManifestJcsSHA256!==verifiedRuntimeParityManifestDigest())throw new Error("packaged runtime Service canonical Types identity drifted");const t=new Map([["runtime-parity-manifest",["openapi/runtime-parity-manifest-v1.json",RUNTIME_PARITY_MANIFEST_REVISION,RUNTIME_PARITY_MANIFEST_RAW_SHA256,RUNTIME_PARITY_MANIFEST_SHA256]],["runtime-message-channel",["openapi/runtime-message-channel-v1.contract.json",RUNTIME_MESSAGE_CHANNEL_REVISION,RUNTIME_MESSAGE_CHANNEL_RAW_SHA256,RUNTIME_MESSAGE_CHANNEL_SHA256]],["runtime-authority-ports",["openapi/runtime-authority-ports-v1.contract.json",RUNTIME_AUTHORITY_PORTS_REVISION,RUNTIME_AUTHORITY_PORTS_RAW_SHA256,RUNTIME_AUTHORITY_PORTS_SHA256]]]);if(!Array.isArray(i.artifacts)||i.artifacts.length!==t.size)throw new Error("packaged runtime Service wire artifact set drifted");for(const e of i.artifacts){const i=t.get(e.name);if(!i||e.path!==i[0]||e.revision!==i[1]||e.rawSHA256!==i[2]||e.jcsSHA256!==i[3])throw new Error(`packaged runtime Service wire artifact ${e.name||"<missing>"} drifted`);t.delete(e.name)}if(0!==t.size)throw new Error("packaged runtime Service wire artifact is missing")}export function intersectRuntimeServiceMethodsWithBackendCapabilities(e){const i=RUNTIME_PARITY_READY_METHODS,t=new Set(RUNTIME_PARITY_MANIFEST.map(e=>e.method)),r=new Set;for(const e of RUNTIME_METHOD_PARITY_MANIFEST)e.serviceAllowed&&t.has(e.acpMethod)&&(e.minimumRuntimeContractRevision&&e.minimumRuntimeContractRevision!==RUNTIME_RPC_CONTRACT_REVISION||r.add(e.acpMethod));return Object.freeze([...new Set(e)].filter(e=>i.has(e)&&r.has(e)))}export function runtimeServiceContractRequirement(){return`Types ${RUNTIME_RPC_CONTRACT_REVISION}; canonical-manifest ${verifiedRuntimeParityManifestDigest()}; `+RUNTIME_SERVICE_WIRE_CONTRACT_ARTIFACTS.map(e=>`${e.revision}:${e.sha256}`).join("; ")}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{RUNTIME_RESOURCE_BINDING_CONTRACT_REVISION}from"@beeos-ai/beeos-types/runtime";import{parseAndValidateTemplatePackage}from"../acp-gateway/agent-template-package.js";import{HttpRuntimeOperationResourceResolver,RuntimeResourceResolutionError}from"./runtime-operation-resource-resolver.js";import{MessageServiceConversationClearHttpAuthority}from"./message-service-conversation-clear-http-authority.js";import{MessageServiceConversationHistoryBoundary}from"./message-service-conversation-history-boundary.js";import{MessageServiceSessionModelHttpAuthority}from"./message-service-session-model-http-authority.js";import{MsRuntimeMessageChannelConsumerFactory}from"./ms-runtime-message-channel-consumer-factory.js";import{RuntimeJwksVerifier}from"./runtime-jwks-verifier.js";import{GatewayGrantJwksVerifier}from"./split-runtime-authorization-verifier.js";import{RUNTIME_MESSAGE_CHANNEL_REVISION,RUNTIME_PARITY_MANIFEST}from"./runtime-parity-manifest.js";import{intersectRuntimeServiceMethodsWithBackendCapabilities,loadRuntimeServiceContractIdentity,runtimeServiceContractRequirement}from"./runtime-service-contract-compatibility.js";const CONTRACT_ARTIFACT_BLOCKER=Object.freeze({id:"contract_artifact_mismatch",owner:"backend",requiredContract:"frozen Types manifest and Service wire artifacts"}),TRANSPORT_BLOCKER=Object.freeze({id:"message_channel_transport",owner:"backend",requiredContract:`${RUNTIME_MESSAGE_CHANNEL_REVISION} HTTP delivery/history/append authority`}),JWKS_BLOCKER=Object.freeze({id:"gateway_grant_jwks",owner:"backend",requiredContract:"Purpose-bound Gateway runtime execution-grant JWKS proxy"}),POISON_JOURNAL_BLOCKER=Object.freeze({id:"poison_journal",owner:"openclaw_runtime_client",requiredContract:"Durable bounded runtime poison-delivery journal"}),grantAdapterOptions=new WeakMap;export class AgentGatewayGrantJwksReadinessAdapter{constructor(e){grantAdapterOptions.set(this,{agentGatewayUrl:new URL(e.agentGatewayUrl).toString()})}}export class AgentGatewaySplitJwksReadinessAdapter extends AgentGatewayGrantJwksReadinessAdapter{}const transportOptions=new WeakMap;export class RuntimeServiceTransportReadinessAdapter{constructor(e){transportOptions.set(this,{messageService:e.messageService,messageClient:e.messageClient,configuredServiceUrl:e.configuredServiceUrl,scopedDeliveryKey:e.scopedDeliveryKey,agentGatewayUrl:new URL(e.agentGatewayUrl).toString(),boundAgentFetch:e.boundAgentFetch})}}const CAPABILITY_BLOCKERS=Object.freeze(RUNTIME_PARITY_MANIFEST.filter(e=>"blocked"===e.status).map(e=>Object.freeze({method:e.method,owner:e.blockerOwner,requiredContract:e.contractRevision})));export async function createRuntimeServiceProductionCandidate(e){try{loadRuntimeServiceContractIdentity()}catch{return{status:"unavailable",blockers:Object.freeze([{...CONTRACT_ARTIFACT_BLOCKER,requiredContract:contractArtifactRequirement()}])}}if(!e.poisonJournal)return{status:"unavailable",blockers:Object.freeze([POISON_JOURNAL_BLOCKER])};let t;if(e.authorizationAdapter)try{t=await probeGatewayGrantJwks(e.authorizationAdapter)}catch{}const r=e.transportAdapter?transportOptions.get(e.transportAdapter):void 0;if(!
|
|
1
|
+
import{RUNTIME_RESOURCE_BINDING_CONTRACT_REVISION}from"@beeos-ai/beeos-types/runtime";import{parseAndValidateTemplatePackage}from"../acp-gateway/agent-template-package.js";import{HttpRuntimeOperationResourceResolver,RuntimeResourceResolutionError}from"./runtime-operation-resource-resolver.js";import{MessageServiceConversationClearHttpAuthority}from"./message-service-conversation-clear-http-authority.js";import{MessageServiceConversationHistoryBoundary}from"./message-service-conversation-history-boundary.js";import{MessageServiceSessionModelHttpAuthority}from"./message-service-session-model-http-authority.js";import{MsRuntimeMessageChannelConsumerFactory}from"./ms-runtime-message-channel-consumer-factory.js";import{RuntimeJwksVerifier}from"./runtime-jwks-verifier.js";import{CloudGatewayGrantJwksVerifier,GatewayGrantJwksVerifier}from"./split-runtime-authorization-verifier.js";import{RUNTIME_MESSAGE_CHANNEL_REVISION,RUNTIME_PARITY_MANIFEST}from"./runtime-parity-manifest.js";import{intersectRuntimeServiceMethodsWithBackendCapabilities,loadRuntimeServiceContractIdentity,runtimeServiceContractRequirement}from"./runtime-service-contract-compatibility.js";const CONTRACT_ARTIFACT_BLOCKER=Object.freeze({id:"contract_artifact_mismatch",owner:"backend",requiredContract:"frozen Types manifest and Service wire artifacts"}),TRANSPORT_BLOCKER=Object.freeze({id:"message_channel_transport",owner:"backend",requiredContract:`${RUNTIME_MESSAGE_CHANNEL_REVISION} HTTP delivery/history/append authority`}),JWKS_BLOCKER=Object.freeze({id:"gateway_grant_jwks",owner:"backend",requiredContract:"Purpose-bound Gateway runtime execution-grant JWKS proxy"}),POISON_JOURNAL_BLOCKER=Object.freeze({id:"poison_journal",owner:"openclaw_runtime_client",requiredContract:"Durable bounded runtime poison-delivery journal"}),grantAdapterOptions=new WeakMap;export class AgentGatewayGrantJwksReadinessAdapter{constructor(e){grantAdapterOptions.set(this,{agentGatewayUrl:new URL(e.agentGatewayUrl).toString()})}}export class AgentGatewaySplitJwksReadinessAdapter extends AgentGatewayGrantJwksReadinessAdapter{}const transportOptions=new WeakMap;export class RuntimeServiceTransportReadinessAdapter{constructor(e){transportOptions.set(this,{messageService:e.messageService,messageClient:e.messageClient,configuredServiceUrl:e.configuredServiceUrl,scopedDeliveryKey:e.scopedDeliveryKey,agentGatewayUrl:new URL(e.agentGatewayUrl).toString(),boundAgentFetch:e.boundAgentFetch})}}const cloudTransportOptions=new WeakMap;export class CloudRuntimeServiceTransportReadinessAdapter{constructor(e){cloudTransportOptions.set(this,{...e,agentGatewayUrl:new URL(e.agentGatewayUrl).toString()})}}const CAPABILITY_BLOCKERS=Object.freeze(RUNTIME_PARITY_MANIFEST.filter(e=>"blocked"===e.status).map(e=>Object.freeze({method:e.method,owner:e.blockerOwner,requiredContract:e.contractRevision})));export async function createRuntimeServiceProductionCandidate(e){try{loadRuntimeServiceContractIdentity()}catch{return{status:"unavailable",blockers:Object.freeze([{...CONTRACT_ARTIFACT_BLOCKER,requiredContract:contractArtifactRequirement()}])}}if(!e.poisonJournal)return{status:"unavailable",blockers:Object.freeze([POISON_JOURNAL_BLOCKER])};const r=e.cloudTransportAdapter?cloudTransportOptions.get(e.cloudTransportAdapter):void 0;let t;if(e.authorizationAdapter)try{t=await probeGatewayGrantJwks(e.authorizationAdapter,Boolean(r))}catch{}if(r){if(!r.configuredServiceUrl)return{status:"unavailable",blockers:Object.freeze([TRANSPORT_BLOCKER,...t?[]:[JWKS_BLOCKER]])};if(!(t instanceof CloudGatewayGrantJwksVerifier))return{status:"unavailable",blockers:Object.freeze([JWKS_BLOCKER])};const n=intersectRuntimeServiceMethodsWithBackendCapabilities(["models/list"]);return n.includes("models/list")?{status:"ready",blockers:[],capabilityBlockers:[],serviceMethods:n,factory:new MsRuntimeMessageChannelConsumerFactory({runtimeDelivery:r.runtimeDelivery,verifier:t,ports:e.attemptPorts??runtimeMessageAttemptPorts(r,r.configuredServiceUrl),journal:e.commandJournal,allowedMethods:new Set(n),options:{poisonJournal:e.poisonJournal,requestSchemaVersion:2,...e.onConsumerDeliveryError?{onDeliveryError:e.onConsumerDeliveryError}:{},...e.onConsumerLeaseStarvation?{onLeaseStarvation:e.onConsumerLeaseStarvation}:{},...e.onConsumerRequestTerminalAccepted?{onRequestTerminalAccepted:e.onConsumerRequestTerminalAccepted}:{},...e.onConsumerRequestAcknowledged?{onRequestAcknowledged:e.onConsumerRequestAcknowledged}:{},...e.onConsumerRequestFailure?{onRequestFailure:e.onConsumerRequestFailure}:{}}})}:{status:"unavailable",blockers:Object.freeze([CONTRACT_ARTIFACT_BLOCKER])}}const n=e.transportAdapter?transportOptions.get(e.transportAdapter):void 0;if(!n?.messageClient||!n.configuredServiceUrl)return{status:"unavailable",blockers:Object.freeze([TRANSPORT_BLOCKER,...t?[]:[JWKS_BLOCKER]])};if(!t)return{status:"unavailable",blockers:Object.freeze([JWKS_BLOCKER])};const a=runtimeMethods(),o=e.attemptPorts??runtimeMessageAttemptPorts(n,n.configuredServiceUrl);return{status:"ready",blockers:[],capabilityBlockers:CAPABILITY_BLOCKERS,serviceMethods:a,factory:new MsRuntimeMessageChannelConsumerFactory({client:n.messageClient,verifier:t,ports:o,journal:e.commandJournal,allowedMethods:new Set(a),options:{poisonJournal:e.poisonJournal,...e.onConsumerDeliveryError?{onDeliveryError:e.onConsumerDeliveryError}:{},...e.onConsumerLeaseStarvation?{onLeaseStarvation:e.onConsumerLeaseStarvation}:{},...e.onConsumerRequestTerminalAccepted?{onRequestTerminalAccepted:e.onConsumerRequestTerminalAccepted}:{},...e.onConsumerRequestAcknowledged?{onRequestAcknowledged:e.onConsumerRequestAcknowledged}:{},...e.onConsumerRequestFailure?{onRequestFailure:e.onConsumerRequestFailure}:{}}}),...void 0===e.attemptPorts?{resourceBindingContractRevision:RUNTIME_RESOURCE_BINDING_CONTRACT_REVISION}:{}}}async function probeGatewayGrantJwks(e,r){const t=grantAdapterOptions.get(e);if(!t)throw new Error("untrusted Gateway grant readiness adapter");const n=t.agentGatewayUrl.replace(/\/+$/,""),a=new RuntimeJwksVerifier(`${n}/.well-known/runtime-execution-grant-jwks.json`),o=r?new CloudGatewayGrantJwksVerifier(a):new GatewayGrantJwksVerifier(a);return await o.refresh(!0),o}export function runtimeMessageAttemptPorts(e,r){return{async resolve({request:t,command:n,executionGrant:a,getLease:o,journalStoreId:s,journalGeneration:i,journalRecord:c,abortSignal:u}){const d="resourceBindings"in t?t.resourceBindings:void 0,l="resourceBindingContractRevision"in t?t.resourceBindingContractRevision:void 0,m="skills.install"===n.name||"agent.applyTemplate"===n.name?n.params.artifact:void 0,p="mcp.prepare"===n.name||"mcp.set"===n.name?n.params.definition:void 0;if((m||p)&&(l!==RUNTIME_RESOURCE_BINDING_CONTRACT_REVISION||!d))throw new RuntimeResourceResolutionError("resolution_rejected","resource-bound Service delivery requires the Types 4.1 binding carrier");const R=new HttpRuntimeOperationResourceResolver({gatewayUrl:e.agentGatewayUrl,operationId:t.operationId,requestMessageId:t.requestMessageId,instanceId:t.instanceId,paramsHash:t.paramsHash,bindings:d??[],getAuthorization:()=>{const e=o();return{executionGrant:a,runtimeLeaseCredential:e.runtimeLeaseCredential,fence:deliveryFence(t,e,s,i)}},fetchAuthorized:async(r,t)=>await e.boundAgentFetch(r,t??{}),parseSkillArtifact:e=>parseAndValidateTemplatePackage(Buffer.from(e)),signal:u});if("prepared"!==c.phase&&"started"!==c.phase||(m&&await R.readVerifiedArtifact(m),p&&await R.resolveMcpDefinition(p)),"session.setModel"===n.name){const n=e.messageService;if(!n)throw new Error("Cloud runtime cannot project a personal session model");return{resources:R,sessionModelProjection:{project:async({conversationId:e,modelOverrideId:a})=>{const s=o(),i=new MessageServiceSessionModelHttpAuthority({messageServiceUrl:r,fetchAuthorized:async(e,r)=>await n.fetchRuntimeChannel(e,{runtimeLeaseCredential:s.runtimeLeaseCredential},r)});return await i.project({conversationId:e,operationId:t.operationId,requestMessageId:t.requestMessageId,requestHash:t.requestHash,modelOverrideId:a,runtimeLeaseCredential:s.runtimeLeaseCredential})}}}}if("session.clear"!==n.name)return{resources:R};const C=e.messageService;if(!C)throw new Error("Cloud runtime cannot access personal conversation history");n.context.conversation;const v=o(),y=new MessageServiceConversationClearHttpAuthority({messageServiceUrl:r,runtimeLeaseCredential:v.runtimeLeaseCredential,fetchAuthorized:async(e,r)=>await C.fetchRuntimeChannel(e,{runtimeLeaseCredential:v.runtimeLeaseCredential},r)});return{resources:R,historyBoundary:new MessageServiceConversationHistoryBoundary({operationId:t.operationId,requestMessageId:t.requestMessageId,requestHash:t.requestHash,fence:deliveryFence(t,v,s,i),authority:y})}}}}function runtimeMethods(){return[...intersectRuntimeServiceMethodsWithBackendCapabilities(RUNTIME_PARITY_MANIFEST.filter(e=>"ready"===e.status).map(e=>e.method))]}function contractArtifactRequirement(){try{return runtimeServiceContractRequirement()}catch{return CONTRACT_ARTIFACT_BLOCKER.requiredContract}}function deliveryFence(e,r,t,n){if(r.handlerIdentity!==e.runtimeHandlerIdentity||r.runtimeEpoch!==e.dispatchEpoch)throw new Error("runtime delivery fence does not match the retained request");return{claimId:e.requestMessageId,handlerIdentity:r.handlerIdentity,runtimeEpoch:r.runtimeEpoch,fencingToken:r.runtimeEpoch,journalStoreId:t,journalGeneration:n}}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{MessageServiceConversationHistoryBoundary}from"./message-service-conversation-history-boundary.js";export class ServiceRuntimeAttemptPortsResolver{deps;constructor(e){this.deps=e}async resolve(e,
|
|
1
|
+
import{MessageServiceConversationHistoryBoundary}from"./message-service-conversation-history-boundary.js";export class ServiceRuntimeAttemptPortsResolver{deps;constructor(e){this.deps=e}async resolve(e,s){const r=await this.deps.resources(e,s);if("session.prompt"===e.attempt.command.name){if(!this.deps.run)throw new Error("Service session.prompt run boundary is unavailable");return{resources:r,conversationRunBoundary:await this.deps.run(e)}}if("session.clear"!==e.attempt.command.name)return{resources:r};const t=await this.deps.clear(e);return{resources:r,historyBoundary:new MessageServiceConversationHistoryBoundary({operationId:e.operationId,requestMessageId:e.requestMessageId,requestHash:t.requestHash??e.request.requestHash,...t.expectedGeneration?{expectedGeneration:t.expectedGeneration}:{},fence:e.fence,authority:t.authority,...t.resolveActiveRunRegistrationId?{resolveActiveRunRegistrationId:t.resolveActiveRunRegistrationId}:{}})}}}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
const GATEWAY_GRANT_PROVENANCE=Object.freeze({issuer:"beeos-gateway",audience:"beeos-runtime-command",purpose:"runtime-execution-grant",maxClaimTtlSeconds:60}),MESSAGE_BOUNDARY_PROVENANCE=Object.freeze({issuer:"message-service",audience:"beeos-runtime-execution-boundary",purpose:"runtime-execution-boundary",maxClaimTtlSeconds:60});export class GatewayGrantJwksVerifier{verifier;purpose="gateway-runtime-grant";keysetProvenance;constructor(e){this.verifier=e,this.keysetProvenance=e.keysetUrl}async refresh(e=!1){await this.verifier.refresh(e),assertResponseProvenance(this.verifier,GATEWAY_GRANT_PROVENANCE)}trustedKeyMaterials(){return this.verifier.trustedKeyMaterials()}responseProvenance(){return this.verifier.documentProvenance()}verifyAndDecodeGrant(e){const r=this.verifier.verifyAndDecodeGrant(e);return"beeos-gateway"===r?.claims.iss&&"beeos-runtime-command"===r.claims.aud&&"runtime-execution-grant"===r.claims.purpose?r:null}}export class MessageServiceBoundaryJwksVerifier{verifier;purpose="message-service-execution-boundary";keysetProvenance;constructor(e){this.verifier=e,this.keysetProvenance=e.keysetUrl}async refresh(e=!1){await this.verifier.refresh(e),assertResponseProvenance(this.verifier,MESSAGE_BOUNDARY_PROVENANCE)}trustedKeyMaterials(){return this.verifier.trustedKeyMaterials()}responseProvenance(){return this.verifier.documentProvenance()}verifyAndDecodeBoundary(e){const r=this.verifier.verifyAndDecodeBoundary(e);return"message-service"===r?.claims.iss&&"beeos-runtime-execution-boundary"===r.claims.aud?r:null}}export class SplitIssuerRuntimeAuthorizationVerifier{grant;boundary;constructor(e,r){if(this.grant=e,this.boundary=r,e.keysetProvenance===r.keysetProvenance)throw new Error("grant and boundary JWKS keysets require distinct provenance")}async refresh(e=!1){await Promise.all([this.grant.refresh(e),this.boundary.refresh(e)]);const r=this.grant.responseProvenance(),s=this.boundary.responseProvenance();if(!r||!s||r.sourceUrl===s.sourceUrl||r.etag===s.etag)throw new Error("grant and boundary JWKS responses require distinct provenance");const n=new Set(this.boundary.trustedKeyMaterials().flatMap(e=>[e.jwkThumbprint,e.spkiSha256]));if(this.grant.trustedKeyMaterials().some(e=>n.has(e.jwkThumbprint)||n.has(e.spkiSha256)))throw new Error("grant and boundary JWKS keysets reuse public-key material")}verifyAndDecodeGrant(e){return this.grant.verifyAndDecodeGrant(e)}verifyAndDecodeBoundary(e){return this.boundary.verifyAndDecodeBoundary(e)}}function assertResponseProvenance(e,r){const s=e.documentProvenance();if(!s||!s.etag||s.sourceUrl!==e.keysetUrl||s.issuer!==r.issuer||s.audience!==r.audience||s.purpose!==r.purpose||s.maxClaimTtlSeconds!==r.maxClaimTtlSeconds)throw new Error(`JWKS response provenance mismatch for ${r.purpose}`)}
|
|
1
|
+
import{CLOUD_RUNTIME_EXECUTION_GRANT_DOMAIN}from"@beeos-ai/beeos-types/runtime";const GATEWAY_GRANT_PROVENANCE=Object.freeze({issuer:"beeos-gateway",audience:"beeos-runtime-command",purpose:"runtime-execution-grant",maxClaimTtlSeconds:60}),CLOUD_GRANT_PROVENANCE=Object.freeze({issuer:CLOUD_RUNTIME_EXECUTION_GRANT_DOMAIN.issuer,audience:CLOUD_RUNTIME_EXECUTION_GRANT_DOMAIN.audience,purpose:CLOUD_RUNTIME_EXECUTION_GRANT_DOMAIN.purpose,maxClaimTtlSeconds:60}),MESSAGE_BOUNDARY_PROVENANCE=Object.freeze({issuer:"message-service",audience:"beeos-runtime-execution-boundary",purpose:"runtime-execution-boundary",maxClaimTtlSeconds:60});export class GatewayGrantJwksVerifier{verifier;purpose="gateway-runtime-grant";keysetProvenance;constructor(e){this.verifier=e,this.keysetProvenance=e.keysetUrl}async refresh(e=!1){await this.verifier.refresh(e),assertResponseProvenance(this.verifier,GATEWAY_GRANT_PROVENANCE)}trustedKeyMaterials(){return this.verifier.trustedKeyMaterials()}responseProvenance(){return this.verifier.documentProvenance()}verifyAndDecodeGrant(e){const r=this.verifier.verifyAndDecodeGrant(e);return"beeos-gateway"===r?.claims.iss&&"beeos-runtime-command"===r.claims.aud&&"runtime-execution-grant"===r.claims.purpose?r:null}}export class CloudGatewayGrantJwksVerifier{verifier;keysetProvenance;constructor(e){this.verifier=e,this.keysetProvenance=e.keysetUrl}async refresh(e=!1){await this.verifier.refresh(e),assertResponseProvenance(this.verifier,CLOUD_GRANT_PROVENANCE)}verifyAndDecodeGrant(e){const r=this.verifier.verifyAndDecodeGrant(e),s=r?.claims;return s?.iss===CLOUD_RUNTIME_EXECUTION_GRANT_DOMAIN.issuer&&s.aud===CLOUD_RUNTIME_EXECUTION_GRANT_DOMAIN.audience&&s.purpose===CLOUD_RUNTIME_EXECUTION_GRANT_DOMAIN.purpose?r:null}}export class MessageServiceBoundaryJwksVerifier{verifier;purpose="message-service-execution-boundary";keysetProvenance;constructor(e){this.verifier=e,this.keysetProvenance=e.keysetUrl}async refresh(e=!1){await this.verifier.refresh(e),assertResponseProvenance(this.verifier,MESSAGE_BOUNDARY_PROVENANCE)}trustedKeyMaterials(){return this.verifier.trustedKeyMaterials()}responseProvenance(){return this.verifier.documentProvenance()}verifyAndDecodeBoundary(e){const r=this.verifier.verifyAndDecodeBoundary(e);return"message-service"===r?.claims.iss&&"beeos-runtime-execution-boundary"===r.claims.aud?r:null}}export class SplitIssuerRuntimeAuthorizationVerifier{grant;boundary;constructor(e,r){if(this.grant=e,this.boundary=r,e.keysetProvenance===r.keysetProvenance)throw new Error("grant and boundary JWKS keysets require distinct provenance")}async refresh(e=!1){await Promise.all([this.grant.refresh(e),this.boundary.refresh(e)]);const r=this.grant.responseProvenance(),s=this.boundary.responseProvenance();if(!r||!s||r.sourceUrl===s.sourceUrl||r.etag===s.etag)throw new Error("grant and boundary JWKS responses require distinct provenance");const n=new Set(this.boundary.trustedKeyMaterials().flatMap(e=>[e.jwkThumbprint,e.spkiSha256]));if(this.grant.trustedKeyMaterials().some(e=>n.has(e.jwkThumbprint)||n.has(e.spkiSha256)))throw new Error("grant and boundary JWKS keysets reuse public-key material")}verifyAndDecodeGrant(e){return this.grant.verifyAndDecodeGrant(e)}verifyAndDecodeBoundary(e){return this.boundary.verifyAndDecodeBoundary(e)}}function assertResponseProvenance(e,r){const s=e.documentProvenance();if(!s||!s.etag||s.sourceUrl!==e.keysetUrl||s.issuer!==r.issuer||s.audience!==r.audience||s.purpose!==r.purpose||s.maxClaimTtlSeconds!==r.maxClaimTtlSeconds)throw new Error(`JWKS response provenance mismatch for ${r.purpose}`)}
|
package/openclaw.plugin.json
CHANGED
|
@@ -18,6 +18,15 @@
|
|
|
18
18
|
"beeos_discover_agents",
|
|
19
19
|
"beeos_call_agent",
|
|
20
20
|
"beeos_get_task",
|
|
21
|
+
"beeos_automation_create",
|
|
22
|
+
"beeos_automation_create_webhook",
|
|
23
|
+
"beeos_automation_capabilities",
|
|
24
|
+
"beeos_automation_preflight",
|
|
25
|
+
"beeos_automation_list",
|
|
26
|
+
"beeos_automation_get",
|
|
27
|
+
"beeos_automation_run",
|
|
28
|
+
"beeos_automation_runs",
|
|
29
|
+
"beeos_automation_get_run",
|
|
21
30
|
"cron_toggle",
|
|
22
31
|
"cron_run",
|
|
23
32
|
"cron_delete"
|
|
@@ -71,7 +80,12 @@
|
|
|
71
80
|
"service": {
|
|
72
81
|
"type": "string",
|
|
73
82
|
"description": "Service type for Bridge registration (default: acp)",
|
|
74
|
-
"enum": [
|
|
83
|
+
"enum": [
|
|
84
|
+
"acp",
|
|
85
|
+
"screen",
|
|
86
|
+
"desktop",
|
|
87
|
+
"shell"
|
|
88
|
+
]
|
|
75
89
|
},
|
|
76
90
|
"mode": {
|
|
77
91
|
"type": "string",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "beeos-claw",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.63-cloud.1",
|
|
4
4
|
"description": "OpenClaw plugin that bridges the BeeOS platform with the local OpenClaw Gateway, providing file transfer, ACP bridge, and observability.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "BeeOS <dev@beeos.ai>",
|
|
@@ -65,11 +65,12 @@
|
|
|
65
65
|
"test:watch": "vitest"
|
|
66
66
|
},
|
|
67
67
|
"dependencies": {
|
|
68
|
-
"@beeos-ai/beeos-types": "4.4.1",
|
|
68
|
+
"@beeos-ai/beeos-types": "4.4.2-cloud.1",
|
|
69
69
|
"@beeos-ai/bridge-client": "^0.1.0",
|
|
70
|
+
"@beeos-ai/cloud-agent-sdk": "1.0.0",
|
|
70
71
|
"@beeos-ai/message-sdk": "2.0.16",
|
|
71
72
|
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
72
|
-
"adm-zip": "
|
|
73
|
+
"adm-zip": "0.6.1",
|
|
73
74
|
"ws": "^8.18.0"
|
|
74
75
|
},
|
|
75
76
|
"devDependencies": {
|
|
@@ -87,5 +88,5 @@
|
|
|
87
88
|
"fast-uri": ">=3.1.4",
|
|
88
89
|
"brace-expansion": ">=5.0.9"
|
|
89
90
|
},
|
|
90
|
-
"gitHead": "
|
|
91
|
+
"gitHead": "5f9a6c03180e102484e28ed74ecb383a8bcaf137"
|
|
91
92
|
}
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
"hashAlgorithm": "SHA-256",
|
|
5
5
|
"canonicalTypes": {
|
|
6
6
|
"package": "@beeos-ai/beeos-types",
|
|
7
|
-
"version": "4.4.
|
|
7
|
+
"version": "4.4.2-beta.2",
|
|
8
8
|
"runtimeRpcContractRevision": "2026-07-14.3",
|
|
9
9
|
"runtimeMethodManifestRawSHA256": "2af68ff555d4a4aa3c7b19a285e48a3df2ef9b43aa618ffddabbebc6e4280162",
|
|
10
10
|
"runtimeMethodManifestJcsSHA256": "d5db2c5e7c78cee73e5672a3c18a8e56179b28888f6fbb660d98ec021ca9de9b"
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{createHash,randomBytes,sign}from"node:crypto";import{RUNTIME_INTERNAL_API_V1_ROUTE_CONTRACTS,RUNTIME_HASH_DOMAINS,RUNTIME_RPC_PROTOCOL_VERSION,runtimeHashPreimage,runtimeIdentitySignatureMessage,validateRuntimeInternalRegistrationResult}from"@beeos-ai/beeos-types/runtime";import{RuntimeTransportFailurePolicy}from"./runtime-transport-failure-policy.js";export class RuntimeRegistrationLeaseExpiredError extends Error{constructor(){super("Instance authority returned an expired active lease"),this.name="RuntimeRegistrationLeaseExpiredError"}}export class RuntimeRegistrationHandoffRequiredError extends Error{retryAfterMs;constructor(e=65e3){super("Instance authority requires waiting for the active runtime lease to expire"),this.name="RuntimeRegistrationHandoffRequiredError",this.retryAfterMs=e}}export class RuntimeRegistrationOwnerResetRequiredError extends Error{constructor(){super("Instance authority requires owner reset authorization after a journal store change"),this.name="RuntimeRegistrationOwnerResetRequiredError"}}export class RuntimeRegistrationJournalAdvanceRequiredError extends Error{currentJournalGeneration;expectedJournalGeneration;constructor(e){super("Instance authority requires journal generation to advance exactly once after lease expiry"),this.name="RuntimeRegistrationJournalAdvanceRequiredError",this.currentJournalGeneration=e?.currentJournalGeneration,this.expectedJournalGeneration=e?.expectedJournalGeneration}}export class RuntimeRegistrationCoordinator{transport;config;onLeaseLost;now;active=null;timer;expiryTimer;stopped=!1;generation=0;heartbeatTransportFailurePolicy=new RuntimeTransportFailurePolicy;authorityListeners=new Set;constructor(e,t,i,r=Date.now){this.transport=e,this.config=t,this.onLeaseLost=i,this.now=r}get lease(){return!this.active||Date.parse(this.active.leaseExpiresAt)<=this.now()?null:this.active}get canClaimMutations(){return null!==this.lease}get authorityLease(){const e=this.lease;return e?Object.freeze({...e,handlerIdentity:this.config.handlerIdentity}):null}subscribeAuthority(e){return this.authorityListeners.add(e),()=>{this.authorityListeners.delete(e)}}async start(){const e=++this.generation;return this.stopped=!1,await this.registerOnce(e)}async registerOnce(e){if(!this.isCurrent(e))return null;this.timer=void 0,this.active&&(this.clearLeaseExpiry(),this.active=null,this.notifyAuthority());const t={registrationId:this.config.registrationId,handlerIdentity:this.config.handlerIdentity,contractRevision:this.config.contractRevision,runtimeRpcProtocolVersion:RUNTIME_RPC_PROTOCOL_VERSION,runtimeMethods:this.config.runtimeMethods,capabilities:this.config.capabilities,manifestDigest:this.config.manifestDigest,journalStoreId:this.config.journalStoreId,journalGeneration:this.config.journalGeneration};let i;try{i=await this.transport.register(this.signProof("runtime.register",t))}catch(t){if(!this.isCurrent(e))return null;if(t instanceof RuntimeRegistrationLeaseExpiredError||t instanceof RuntimeRegistrationJournalAdvanceRequiredError||t instanceof RuntimeRegistrationOwnerResetRequiredError)return this.onLeaseLost(t instanceof RuntimeRegistrationLeaseExpiredError?"expired":"heartbeat_failed",t),null;this.onLeaseLost("heartbeat_failed",t);const i=t instanceof RuntimeRegistrationHandoffRequiredError?t.retryAfterMs:5e3;return this.scheduleRegistration(i,e),null}return this.isCurrent(e)?"active"!==i.status?("fenced"===i.status?this.onLeaseLost("fenced"):this.scheduleRegistration(i.retryAfterMs,e),null):(this.active={instanceId:i.instanceId,leaseId:i.leaseId,runtimeEpoch:i.runtimeEpoch,runtimeLeaseCredential:i.runtimeLeaseCredential,leaseExpiresAt:i.leaseExpiresAt},this.heartbeatTransportFailurePolicy.recordSuccess(),this.notifyAuthority(),this.scheduleLeaseExpiry(this.active,e),this.schedule(i.heartbeatIntervalMs,e),this.active):null}stop(){this.generation++,this.stopped=!0,this.timer&&clearTimeout(this.timer),this.expiryTimer&&clearTimeout(this.expiryTimer),this.timer=void 0,this.expiryTimer=void 0,this.active=null,this.notifyAuthority()}schedule(e,t){if(!this.isCurrent(t))return;const i=this.active;i&&(this.timer=setTimeout(()=>{this.sendHeartbeat(e,t,i)},e),this.timer.unref?.())}scheduleRegistration(e,t){this.isCurrent(t)&&(this.timer=setTimeout(()=>{this.registerOnce(t)},e))}async sendHeartbeat(e,t,i){if(!this.isCurrentLease(t,i))return;const r={leaseId:i.leaseId,handlerIdentity:this.config.handlerIdentity,runtimeEpoch:i.runtimeEpoch},s=this.config.getMSConnectionStatus?{...r,msConnectionStatus:this.config.getMSConnectionStatus(i)}:r;try{const r=await this.transport.heartbeat(this.signProof("runtime.heartbeat",s));if(!this.isCurrentLease(t,i))return;if("renewed"!==r.status)return this.heartbeatTransportFailurePolicy.recordSuccess(),this.active=null,this.clearLeaseExpiry(),this.notifyAuthority(),void this.onLeaseLost(r.status);this.heartbeatTransportFailurePolicy.recordSuccess(),this.active={...i,runtimeLeaseCredential:r.runtimeLeaseCredential,leaseExpiresAt:r.leaseExpiresAt},this.notifyAuthority(),this.scheduleLeaseExpiry(this.active,t),this.schedule(e,t)}catch(r){if(!this.isCurrentLease(t,i))return;if(!this.heartbeatTransportFailurePolicy.recordFailure())return void this.schedule(Math.min(e,5e3),t);this.active=null,this.clearLeaseExpiry(),this.notifyAuthority(),this.onLeaseLost("heartbeat_failed",r),this.scheduleRegistration(5e3,t)}}scheduleLeaseExpiry(e,t){this.clearLeaseExpiry();const i=`${e.leaseId}\0${e.runtimeEpoch}\0${e.leaseExpiresAt}`,r=()=>{const s=Date.parse(e.leaseExpiresAt)-this.now();if(s>0)return this.expiryTimer=setTimeout(r,Math.min(2147483647,s)),void this.expiryTimer.unref?.();const n=this.active;this.isCurrentLease(t,e)&&n&&`${n.leaseId}\0${n.runtimeEpoch}\0${n.leaseExpiresAt}`===i&&(this.active=null,this.expiryTimer=void 0,this.notifyAuthority(),this.onLeaseLost("expired"))};r()}clearLeaseExpiry(){this.expiryTimer&&clearTimeout(this.expiryTimer),this.expiryTimer=void 0}isCurrent(e){return!this.stopped&&e===this.generation}isCurrentLease(e,t){return this.isCurrent(e)&&this.active===t}notifyAuthority(){const e=this.authorityLease;for(const t of this.authorityListeners)try{t(e)}catch{}}signProof(e,t){const i=new Date(this.now()).toISOString(),r=randomBytes(18).toString("base64url"),s="runtime.register"===e?RUNTIME_HASH_DOMAINS.registration:RUNTIME_HASH_DOMAINS.heartbeat,n=createHash("sha256").update(runtimeHashPreimage(s,t)).digest("hex"),a=runtimeIdentitySignatureMessage({purpose:e,instanceIdentityKeyId:this.config.instanceIdentityKeyId,signedAt:i,nonce:r,payloadHash:n});return{...t,signaturePurpose:e,instanceIdentityKeyId:this.config.instanceIdentityKeyId,signedAt:i,nonce:r,payloadHash:n,signature:sign(null,a,this.config.privateKey).toString("base64url")}}}export class InstanceAuthorityRuntimeRegistrationTransport{options;constructor(e){this.options=e}async register(e){if(e.registrationId!==this.options.registrationId)throw new Error("runtime registration id mismatch");const t=await this.post(RUNTIME_INTERNAL_API_V1_ROUTE_CONTRACTS.runtimeRegister.path,e);if("active"===t.status&&Number.isFinite(Date.parse(t.leaseExpiresAt))&&Date.parse(t.leaseExpiresAt)<=Date.now())throw new RuntimeRegistrationLeaseExpiredError;if(validateRuntimeInternalRegistrationResult(t)||"fenced"!==t.status&&(t.journalStoreId!==e.journalStoreId||t.journalGeneration!==e.journalGeneration)||"active"===t.status&&(!t.instanceId||!t.leaseId||!t.runtimeLeaseCredential||!Number.isInteger(t.heartbeatIntervalMs)||!Number.isFinite(Date.parse(t.leaseExpiresAt))||Date.parse(t.leaseExpiresAt)<=Date.now()))throw new Error("Instance authority returned an invalid registration binding");return t}async heartbeat(e){const t=await this.post(RUNTIME_INTERNAL_API_V1_ROUTE_CONTRACTS.runtimeHeartbeat.path.replace("{registrationId}",encodeURIComponent(this.options.registrationId)),e);if(!(t&&["renewed","fenced","expired"].includes(t.status)&&("renewed"!==t.status||t.runtimeLeaseCredential&&Number.isFinite(Date.parse(t.leaseExpiresAt)))))throw new Error("Instance authority returned an invalid heartbeat result");return t}async post(e,t){const i=new URL(e,this.options.agentGatewayUrl),r=await this.options.agentFetch(i,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify(t)});if(!r.ok){const e=await r.text().catch(()=>"");if(409===r.status&&i.pathname.endsWith("/runtime/registrations"))throw classifyRuntimeRegistrationConflict(e);throw new Error(`runtime Instance authority request failed (${r.status} ${r.statusText}) ${i.pathname}`+(e?`: ${e.slice(0,240)}`:""))}return await r.json()}}export function classifyRuntimeRegistrationConflict(e){const t=extractConflictMessage(e);if(/journal store change requires owner reset authorization/i.test(t))return new RuntimeRegistrationOwnerResetRequiredError;if(/journal generation must advance exactly once/i.test(t)){const e=t.match(/\bcurrent=(\d+)\b/i)?.[1],i=t.match(/\bexpected=(\d+)\b/i)?.[1];return new RuntimeRegistrationJournalAdvanceRequiredError({currentJournalGeneration:e&&/^[1-9][0-9]*$/.test(e)?e:void 0,expectedJournalGeneration:i&&/^[1-9][0-9]*$/.test(i)?i:void 0})}return/active runtime lease requires explicit handoff/i.test(t)||/waiting for the active runtime lease/i.test(t),new RuntimeRegistrationHandoffRequiredError}function extractConflictMessage(e){if(!e)return"";try{const t=JSON.parse(e),i=t.error?.message;if("string"==typeof i)return i;if("string"==typeof t.message)return t.message}catch{}return e}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export class RuntimeTransportFailurePolicy{threshold;consecutiveFailures=0;constructor(e=3){if(this.threshold=e,!Number.isSafeInteger(e)||e<=0)throw new Error("transport failure threshold must be a positive integer")}recordSuccess(){this.consecutiveFailures=0}recordFailure(){return this.consecutiveFailures+=1,!(this.consecutiveFailures<this.threshold)&&(this.consecutiveFailures=0,!0)}}
|