beeos-claw 0.6.63-cloud.0 → 0.6.63-cloud.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/acp-server.js +1 -1
- 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/config.js +1 -1
- 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-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/dist/runtime/runtime-registration.js +0 -1
- package/dist/runtime/runtime-transport-failure-policy.js +0 -1
|
@@ -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 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,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{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.63-cloud.
|
|
3
|
+
"version": "0.6.63-cloud.2",
|
|
4
4
|
"description": "OpenClaw plugin that bridges the BeeOS platform with the local OpenClaw Gateway, providing file transfer, ACP bridge, and observability.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "BeeOS <dev@beeos.ai>",
|
|
@@ -65,11 +65,12 @@
|
|
|
65
65
|
"test:watch": "vitest"
|
|
66
66
|
},
|
|
67
67
|
"dependencies": {
|
|
68
|
-
"@beeos-ai/beeos-types": "4.4.2-
|
|
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": "b2cfd11195d56c84b8f128394104face88bf6eac"
|
|
91
92
|
}
|
|
@@ -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):void 0!==this.config.targetInstanceId&&i.instanceId!==this.config.targetInstanceId?(this.onLeaseLost("fenced",new Error("Instance authority returned a different Cloud instance")),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,targetInstanceId:this.config.targetInstanceId,signedAt:i,nonce:r,payloadHash:n});return{...t,signaturePurpose:e,instanceIdentityKeyId:this.config.instanceIdentityKeyId,signedAt:i,nonce:r,payloadHash:n,...void 0===this.config.targetInstanceId?{}:{targetInstanceId:this.config.targetInstanceId},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)}}
|