beeos-claw 0.6.66-cloud.2 → 0.6.66-cloud.4
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/README.md +12 -13
- package/dist/a2a/tools.js +1 -1
- package/dist/acp-gateway/beeos-file-resolver.js +1 -1
- package/dist/acp-gateway/bridge-core.js +1 -1
- package/dist/acp-gateway/gateway-events.js +1 -1
- package/dist/acp-gateway/local-session-history.js +1 -1
- package/dist/acp-gateway/mcp-desired-state-controller.js +1 -1
- package/dist/acp-gateway/mcp-desired-state-store.js +1 -1
- package/dist/acp-gateway/mcp-service.js +1 -1
- package/dist/acp-server.js +1 -1
- package/dist/agent-sync.js +1 -1
- package/dist/approval/gate.js +1 -0
- package/dist/approval/journal.js +1 -0
- package/dist/approval/presentation.js +1 -0
- package/dist/approval/protocol.generated.js +1 -0
- package/dist/approval/turn-policy.js +1 -0
- package/dist/auto-reply-tokens.js +1 -1
- package/dist/automation/tools.js +1 -1
- package/dist/canvas/relay-client.js +1 -1
- package/dist/canvas/static-html.js +1 -0
- package/dist/canvas/tools.js +1 -1
- package/dist/canvas/ws-provider.js +1 -1
- package/dist/canvas-orchestrator.js +1 -1
- package/dist/chat-envelope.js +1 -0
- package/dist/connectors/managed-connector-tools.js +1 -0
- package/dist/files/fetch-tool.js +1 -1
- package/dist/files/list-tool.js +1 -1
- package/dist/gateway-client.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/mcp-connector-proxy.js +1 -1
- package/dist/media/arouter-config.js +1 -1
- package/dist/media/image-provider.js +1 -1
- package/dist/media/video-provider.js +1 -1
- package/dist/message-client-composition.js +1 -1
- package/dist/message-projection-inbound-consumer.js +1 -1
- package/dist/plugin-registration-plan.js +1 -1
- package/dist/runtime/agent-projection-production.js +1 -1
- package/dist/runtime/callback-queue.js +1 -1
- package/dist/runtime/chat-handler-port.js +1 -0
- package/dist/runtime/conversation-title.js +1 -1
- package/dist/runtime/core-execution-attempt-context.js +1 -1
- package/dist/runtime/deepseek-reasoning-stream.js +1 -1
- package/dist/runtime/im-reply-sink.js +1 -1
- package/dist/runtime/message-service-conversation-clear-http-authority.js +1 -1
- package/dist/runtime/message-service-conversation-run-boundary.js +1 -1
- package/dist/runtime/message-service-cron-delivery-http-authority.js +1 -1
- package/dist/runtime/message-service-runtime-authenticated-fetch.js +1 -1
- package/dist/runtime/message-service-runtime-claim-authority.js +1 -1
- package/dist/runtime/message-service-runtime-message-channel.js +1 -1
- package/dist/runtime/message-service-session-model-http-authority.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/native-cloud-chat-composition.js +1 -0
- package/dist/runtime/native-cloud-prompt-bridge.js +1 -0
- package/dist/runtime/native-input-files-http.js +1 -0
- package/dist/runtime/native-input-files.js +1 -0
- package/dist/runtime/native-output-file-attempt.js +1 -0
- package/dist/runtime/native-prompt-authority.js +1 -0
- package/dist/runtime/native-prompt-preflight.js +1 -0
- package/dist/runtime/native-session-new-bridge.js +1 -0
- package/dist/runtime/openclaw-runtime-capabilities.js +1 -1
- package/dist/runtime/openclaw-session-model.js +1 -0
- package/dist/runtime/openclaw-session-write-quiescence.js +1 -1
- package/dist/runtime/reply-router.js +1 -1
- package/dist/runtime/runtime-command-dispatcher.js +1 -1
- package/dist/runtime/runtime-command-journal.js +1 -1
- package/dist/runtime/runtime-error-part.js +1 -0
- package/dist/runtime/runtime-local-parity-matrix.js +1 -1
- package/dist/runtime/runtime-message-channel-contract.js +1 -1
- package/dist/runtime/runtime-operation-resource-resolver.js +1 -1
- package/dist/runtime/runtime-parity-manifest-digest.js +1 -1
- package/dist/runtime/runtime-parity-manifest.js +1 -1
- package/dist/runtime/runtime-production-authority.js +1 -1
- package/dist/runtime/runtime-service-contract-compatibility.js +1 -1
- package/dist/runtime/runtime-service-production-candidate.js +1 -1
- package/dist/runtime/runtime-session-control-service.js +1 -1
- package/dist/runtime/runtime-skills-service.js +1 -1
- package/dist/runtime/session-new-runtime-delivery-bridge.js +1 -1
- package/dist/runtime/session-set-model-mapping.js +1 -1
- package/dist/runtime/write-runtime-failure.js +1 -1
- package/dist/terminal/terminal-lease-bound-bridge-client.js +1 -1
- package/dist/upload/desktop-screenshot-tool.js +1 -1
- package/dist/upload/finalize-hook.js +1 -1
- package/dist/upload/native-output-tool-hook.js +1 -0
- package/dist/upload/tool-artifact-enrichment.js +1 -1
- package/dist/upload/tool.js +1 -1
- package/dist/upload/upload-file.js +1 -1
- package/openclaw.plugin.json +7 -2
- package/package.json +11 -11
- package/runtime-service-wire-identity.v1.json +12 -12
- package/scripts/runtime-blackbox-worker-main.mjs +5 -2
- package/dist/agent-auth.js +0 -1
- package/dist/bridge-discovery.js +0 -1
- package/dist/contracts/question/codec.js +0 -1
- package/dist/runtime/agent-gateway-runtime-attempt-authorization.js +0 -1
- package/dist/runtime/message-service-runtime-invocation-authority.js +0 -1
- package/dist/runtime/message-service-session-reasoning-http-authority.js +0 -1
- package/dist/runtime/openclaw-native-sessions.js +0 -1
- package/dist/runtime/openclaw-question-adapter.js +0 -1
- package/dist/runtime/openclaw-title-provider.js +0 -1
- package/dist/runtime/question-wire.js +0 -1
- package/dist/runtime/requested-model.js +0 -1
- package/dist/runtime/runtime-jwks-verifier.js +0 -1
- package/dist/runtime/runtime-reasoning.js +0 -1
- package/dist/runtime/runtime-registration.js +0 -1
- package/dist/runtime/runtime-transport-failure-policy.js +0 -1
- package/dist/runtime/split-runtime-authorization-verifier.js +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
import{MsRuntimeMessageChannelAdapter}from"./ms-runtime-message-channel-adapter.js";import{decodeRuntimeAppendMessage,decodeRuntimeBrokerDelivery,decodeRuntimeOperationHistory,RuntimeMessageChannelProtocolError}from"./message-service-runtime-message-channel.js";import{isCanonicalSessionNewDelivery,SessionNewRuntimeDeliveryBridge}from"./session-new-runtime-delivery-bridge.js";export class MsRuntimeMessageChannelConsumerFactory{deps;constructor(e){this.deps=e}async create(e){let
|
|
1
|
+
import{CloudGatewayGrantJwksVerifier,GatewayGrantJwksVerifier}from"@beeos-ai/cloud-harness-sdk";import{MsRuntimeMessageChannelAdapter}from"./ms-runtime-message-channel-adapter.js";import{decodeRuntimeAppendMessage,decodeRuntimeBrokerDelivery,decodeRuntimeOperationHistory,decodeNativePromptOperationHistory,RuntimeMessageChannelProtocolError}from"./message-service-runtime-message-channel.js";import{isCanonicalSessionNewDelivery,SessionNewRuntimeDeliveryBridge}from"./session-new-runtime-delivery-bridge.js";import{NativeCloudPromptBridge}from"./native-cloud-prompt-bridge.js";export class MsRuntimeMessageChannelConsumerFactory{deps;constructor(e){if(this.deps=e,Boolean(e.client)===Boolean(e.runtimeDelivery))throw new Error("exactly one runtime delivery owner is required");if(e.runtimeDelivery&&!(e.verifier instanceof CloudGatewayGrantJwksVerifier))throw new Error("runtime delivery owner and grant realm differ")}async create(e){let t;const r=new SdkRuntimeMessageChannel(()=>{if(!t)throw new Error("SDK runtime delivery consumer is not attached");return t}),n=new MsRuntimeMessageChannelAdapter(e.dispatcher,r,this.deps.verifier,this.deps.ports,this.deps.journal,()=>e.getLease(),{...this.deps.options,allowedMethods:this.deps.allowedMethods,...this.deps.nativePrompt&&this.deps.verifier instanceof CloudGatewayGrantJwksVerifier?{nativePromptBridge:new NativeCloudPromptBridge({...this.deps.nativePrompt,verifier:this.deps.verifier,channel:r,journal:this.deps.journal,getLease:()=>e.getLease(),getLineage:()=>{const t=e.getBinding?.()??e.binding;return{journalStoreId:t.journalStoreId,journalGeneration:t.journalGeneration}}})}:{},journalStoreId:e.binding.journalStoreId,journalGeneration:e.binding.journalGeneration,...e.getBinding?{getJournalLineage:()=>{const t=e.getBinding();return{journalStoreId:t.journalStoreId,journalGeneration:t.journalGeneration}}}:{}}),i=this.deps.client&&this.deps.verifier instanceof GatewayGrantJwksVerifier?legacySessionNewConversationCreator(this.deps.client):this.deps.runtimeDelivery&&this.deps.verifier instanceof CloudGatewayGrantJwksVerifier?this.deps.sessionNewConversation:void 0,s=i?new SessionNewRuntimeDeliveryBridge({conversations:i,consumer:()=>{if(!t)throw new Error("SDK runtime delivery consumer is not attached");return t},verifier:this.deps.verifier,getLease:()=>e.getLease(),createRuntimeSession:e.createRuntimeSession,poison:async(e,t,r)=>{await n.acceptPoisonDelivery(e,t,r)}}):void 0,o=this.deps.client?.methods??this.deps.runtimeDelivery;if(!o)throw new Error("runtime delivery owner is unavailable");return t=o.consume({onDelivery:async(t,r)=>{runtimeDeliveryTrace(t);let i="fence";try{const o=currentFencedLease(e,r.lease);i="decode";const a=t,d=this.deps.nativePrompt&&2===a.message?.payload?.schemaVersion&&"session/prompt"===a.message.payload.method?t:decodeRuntimeBrokerDelivery(t);if(i="dispatch",isCanonicalSessionNewDelivery(d))return s?void await s.acceptDelivery(d,o,r.signal):void await n.acceptPoisonDelivery(d,"cloud_session_new_unsupported",o);await n.acceptDelivery(d,o,r.signal)}catch(e){runtimeDeliveryError(e);throw e}},onError:this.deps.options.onDeliveryError,readCount:this.deps.options.readCount,blockMs:this.deps.options.blockMs,idleDelayMs:this.deps.options.idleDelayMs,renewIntervalMs:this.deps.options.renewIntervalMs}),n.attachExternalDeliveryOwner(t),n}}export class SdkRuntimeMessageChannel{consumer;constructor(e){this.consumer=e}async read(){throw new Error("MessageClient owns runtime delivery reads")}async renew(){throw new Error("MessageClient owns runtime delivery renewal")}async acknowledge(e){assertLeaseAvailable(e.lease),await this.consumer().acknowledge(e.deliveryIds)}async history(e){assertLeaseAvailable(e.lease);const t=await this.consumer().history(e.operationId);return"found"!==t.status?t:{status:"found",snapshot:this.decodeHistory(t.snapshot,e)}}async append(e){assertLeaseAvailable(e.lease);const t=await this.consumer().append(e.operationId,e.type,e.payload,e.executionGrant);if("created"===t.outcome||"duplicate"===t.outcome)return{disposition:"created"===t.outcome?"created":"reused",message:decodeRuntimeAppendMessage(t.message,e)};if(t.history){const r=findReconciledMessage(this.decodeHistory(t.history,e),e);if(r)return{disposition:"reused",message:r}}throw new RuntimeMessageChannelProtocolError("runtime append outcome is unknown after history reconcile",503,"OUTCOME_UNKNOWN")}decodeHistory(e,t){if(!t.currentNativePrompt)return decodeRuntimeOperationHistory(e,t.operationId);if(t.currentNativePrompt.request.operationId!==t.operationId)throw new RuntimeMessageChannelProtocolError("native history operation does not match current proof");return decodeNativePromptOperationHistory(e,t.currentNativePrompt)}}function currentFencedLease(e,t){const r=e.getLease(),n=e.getBinding?.()??e.binding;if(!r||r.instanceId!==t.instanceId||r.handlerIdentity!==t.handlerIdentity||r.runtimeEpoch!==t.runtimeEpoch||r.leaseId!==t.leaseId||n.journalStoreId!==t.journalStoreId||n.journalGeneration!==t.journalGeneration)throw new Error("runtime delivery lease fence changed");if(Date.parse(r.leaseExpiresAt)<=Date.now())throw new Error("runtime delivery lease expired");return r}function assertLeaseAvailable(e){if(!Number.isFinite(Date.parse(e.leaseExpiresAt))||Date.parse(e.leaseExpiresAt)<=Date.now())throw new Error("runtime delivery lease expired")}function legacySessionNewConversationCreator(e){return{async create(t){const r=t.platformAgentId.startsWith("agent:")?t.platformAgentId:`agent:${t.platformAgentId}`;return{conversationId:(await e.conversations.create({agentId:t.platformAgentId,participants:dedupe([t.runtimeInstanceId,r,t.callerIdentityId??""]),...t.title?{title:t.title}:{},metadata:{source:"session/new",runtimeSessionId:t.runtimeSessionId,runtimeSessionKey:t.runtimeSessionKey,runtimeInstanceId:t.runtimeInstanceId,runtimeEpoch:t.runtimeEpoch,platformAgentId:t.platformAgentId},idempotencyKey:t.operationId})).id}}}}function dedupe(e){return[...new Set(e.filter(e=>e.length>0))]}function safeRuntimeDeliveryId(e){return"string"==typeof e&&e.length>0&&e.length<=256&&!/[\u0000-\u001f\u007f]/.test(e)?JSON.stringify(e):JSON.stringify("unavailable")}function safeRuntimeDeliveryCode(e){return"string"==typeof e&&/^[A-Za-z0-9_.:-]{1,64}$/.test(e)?e:"unavailable"}function runtimeDeliveryTrace(e){const t=null!==e&&"object"==typeof e?e:{};return{channelId:safeRuntimeDeliveryId((null!==t.message&&"object"==typeof t.message?t.message:{}).channel_id),deliveryId:safeRuntimeDeliveryId(t.deliveryId)}}function runtimeDeliveryError(e){const t=null!==e&&"object"==typeof e?e:{};return{code:safeRuntimeDeliveryCode(t.code),status:"number"==typeof t.status&&Number.isInteger(t.status)&&t.status>=100&&t.status<=599?String(t.status):"unavailable"}}function findReconciledMessage(e,t){for(let r=e.messages.length-1;r>=0;r-=1)try{return decodeRuntimeAppendMessage(e.messages[r],t)}catch{}}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{randomUUID}from"node:crypto";import{verifyAndCreateServiceCoreExecutionAttempt}from"./core-execution-attempt-context.js";import{RuntimeCommandError}from"./runtime-command-dispatcher.js";import{RuntimeClaimAuthorityProtocolError}from"./message-service-runtime-claim-authority.js";import{sessionSetModelMappingMatches}from"./session-set-model-mapping.js";import{RuntimeTransportFailurePolicy}from"./runtime-transport-failure-policy.js";export class MsRuntimeRpcAdapter{dispatcher;authority;authorizationVerifier;attemptPorts;authorizationJournal;getLease;options;controller;running;constructor(e,t,r,a,n,o,i){this.dispatcher=e,this.authority=t,this.authorizationVerifier=r,this.attemptPorts=a,this.authorizationJournal=n,this.getLease=o,this.options=i}start(){if(this.running)return;this.controller=new AbortController;const e=this.loop(this.controller.signal).finally(()=>{this.running===e&&(this.running=void 0)});this.running=e}async stop(e){const t=this.running;if(this.controller?.abort(),!t)return;await waitForAbort(t,e)===aborted&&this.running===t&&(this.running=void 0)}journalLineage(){return this.options.getJournalLineage?.()??{journalStoreId:this.options.journalStoreId,journalGeneration:this.options.journalGeneration}}async loop(e){let t=randomUUID(),r="",a="";for(;!e.aborted;){const n=this.getLease();if(!n){await delay(this.options.pollDelayMs??1e3,e);continue}const o=this.journalLineage(),i=`${n.instanceId}\n${n.runtimeEpoch}\n${this.options.handlerIdentity}\n${o.journalStoreId}\n${o.journalGeneration}`;if(r&&r!==i&&(t=randomUUID()),r=i,a!==i)try{const t=await this.authorizationJournal.listRecoverable(n.runtimeEpoch);for(const r of t){if(e.aborted||this.getLease()?.leaseId!==n.leaseId)break;if(!await this.recoverExact({operationId:r.operationId,requestMessageId:r.requestMessageId,dispatchEpoch:r.runtimeEpoch},n,e))throw new Error("durable runtime invocation is still owned by another claim")}a=i}catch{await delay(this.options.pollDelayMs??1e3,e);continue}let s;try{const r=this.authority.claimNext({claimRequestId:t,instanceId:n.instanceId,handlerIdentity:this.options.handlerIdentity,runtimeEpoch:n.runtimeEpoch,journalStoreId:o.journalStoreId,journalGeneration:o.journalGeneration,runtimeLeaseCredential:n.runtimeLeaseCredential}),a=await waitForAbort(r,e);if(a===aborted)return;s=a}catch(t){if(e.aborted)return;if(t instanceof RuntimeClaimAuthorityProtocolError)return;await delay(this.options.pollDelayMs??1e3,e);continue}if(e.aborted)return;t=randomUUID(),"empty"!==s.status?"fenced"!==s.status?await this.handleClaimed(s,n,e,"start").catch(()=>{}):await delay(this.options.pollDelayMs??1e3,e):await delay(Math.max(s.retryAfterMs,this.options.pollDelayMs??0),e)}}async recoverExact(e,t,r){if(e.dispatchEpoch!==t.runtimeEpoch)throw new Error("recovery candidate runtime epoch mismatch");const a=this.journalLineage(),n=await this.authority.claimExact({operationId:e.operationId,requestMessageId:e.requestMessageId,handlerIdentity:this.options.handlerIdentity,dispatchEpoch:e.dispatchEpoch,runtimeEpoch:t.runtimeEpoch,journalStoreId:a.journalStoreId,journalGeneration:a.journalGeneration,runtimeLeaseCredential:t.runtimeLeaseCredential});return"terminal"===n.status?(await this.authorizationJournal.markTerminal(e.operationId).catch(()=>{}),!0):"claimed"===n.status&&(await this.handleClaimed(n,t,r,"recovery"),!0)}async handleClaimed(e,t,r,a="start"){const n={operationId:e.request.operationId,requestMessageId:e.request.messageId,dispatchEpoch:e.request.payload.command.context.dispatchEpoch},o=async()=>{await this.authorizationJournal.markTerminal(n.operationId).catch(()=>{})},i=async e=>{await this.authority.commitFailure(e),await o()},s=()=>{const r=this.getLease(),a=r?.leaseId===t.leaseId&&r.runtimeEpoch===t.runtimeEpoch?r.runtimeLeaseCredential:t.runtimeLeaseCredential;return{operationId:n.operationId,requestMessageId:n.requestMessageId,...e.fence,runtimeLeaseCredential:a}},c=async e=>{try{await i(e)}finally{await this.authority.releaseClaim({...s(),reason:"claim_rejected"}).catch(()=>{})}};if(e.cancelRequested)return await i({operationId:n.operationId,requestMessageId:n.requestMessageId,request:e.request,fence:e.fence,claimLeaseExpiresAt:e.leaseExpiresAt,runtimeLeaseCredential:t.runtimeLeaseCredential,attempt:prestartAttempt(e.request),outcome:"cancelled",code:"OPERATION_CANCELLED"}),void await this.authority.releaseClaim({...s(),reason:"cancelled"});let u;try{u=await this.authorizationJournal.prepare({operationId:n.operationId,requestMessageId:n.requestMessageId,method:e.request.payload.request.method,commandHash:e.request.payload.commandHash,runtimeEpoch:t.runtimeEpoch,journalStoreId:e.fence.journalStoreId,journalGeneration:e.fence.journalGeneration})}catch{return void await c({operationId:n.operationId,requestMessageId:n.requestMessageId,request:e.request,fence:e.fence,claimLeaseExpiresAt:e.leaseExpiresAt,runtimeLeaseCredential:t.runtimeLeaseCredential,attempt:prestartAttempt(e.request),outcome:"failed",code:"UNAUTHORIZED_TARGET"}).catch(()=>{})}const d=await this.authority.authorize({request:e.request,fence:e.fence,lease:t,journal:u});if("authorized"!==d.status)return"terminal"===d.status?(await o(),void await this.authority.releaseClaim({...s(),reason:"completed"}).catch(()=>{})):void await c({operationId:n.operationId,requestMessageId:n.requestMessageId,request:e.request,fence:e.fence,claimLeaseExpiresAt:e.leaseExpiresAt,runtimeLeaseCredential:t.runtimeLeaseCredential,attempt:prestartAttempt(e.request),outcome:"expired"===d.status?"expired":"cancelled"===d.status?"cancelled":"failed",code:"expired"===d.status?"COMMAND_EXPIRED":"cancelled"===d.status?"OPERATION_CANCELLED":"fenced"===d.status?"INSTANCE_EPOCH_MISMATCH":"UNAUTHORIZED_TARGET"});const m={operationId:n.operationId,requestMessageId:n.requestMessageId,request:e.request,fence:e.fence,claimLeaseExpiresAt:e.leaseExpiresAt,runtimeLeaseCredential:t.runtimeLeaseCredential,attempt:d.attempt},l={...m,attempt:prestartAttempt(e.request)};let h;const p=new AbortController;let E;try{assertCommandCorrelation(e.request,d.attempt.command,t.runtimeEpoch),h=await verifyAndCreateServiceCoreExecutionAttempt({operationId:n.operationId,runtimeEpoch:t.runtimeEpoch,fence:e.fence,authorization:d.attempt.authorization,derivationContext:{...d.attempt.derivationContext,localJournalRevision:u.journalRevisionBeforeAttempt,localPreparedEntryHash:u.preparedEntryHash},verifier:this.authorizationVerifier,abortSignal:p.signal,recordVerifiedAttempt:async t=>{const r=t.request.binding;await this.authorizationJournal.recordBoundary({operationId:n.operationId,expectedRevision:u.revision,preparedEntryHash:u.preparedEntryHash,boundary:{executionBoundaryId:r.executionBoundaryId,boundaryAssertionHash:r.boundaryAssertionHash,executionMode:r.executionMode,fence:e.fence,...r.handoffId?{handoffId:r.handoffId}:{}}})},resolvePorts:async()=>E??=await this.attemptPorts.resolve(m,{getRuntimeLeaseCredential:()=>this.currentRuntimeLeaseCredential(t)})})}catch{return p.abort(),void await c({...l,outcome:"failed",code:"UNAUTHORIZED_TARGET"})}if(!h.ok)return p.abort(),void await c({...l,outcome:"failed",code:"UNAUTHORIZED_TARGET"});let I=!1,C=!1;const f=new RuntimeTransportFailurePolicy;let y,w,M,v,N=!1,A=!1,R=!1,O=!1,T=!1,_=!1,g=!1;const b=new Promise(e=>{v=e}),L=e=>{g||(g=!0,v(e))};let x;const U=()=>{T=!0,p.abort(),M&&!x&&(x=M.cancel().catch(()=>({status:"not_cancellable"})))},q=()=>{I=!0,L("fenced"),U()},S=()=>q();r.addEventListener("abort",S,{once:!0});const P=()=>{const e=this.getLease();e&&e.leaseId===t.leaseId&&e.runtimeEpoch===t.runtimeEpoch||q()},H=setInterval(P,100);H.unref?.();const D=setInterval(()=>{this.authority.renewClaim(s()).then(e=>{f.recordSuccess(),"renewed"===e.status&&e.cancelRequested?(C=!0,L("cancel_requested"),U()):"renewed"!==e.status&&q()}).catch(()=>{f.recordFailure()&&q()})},this.options.claimRenewIntervalMs??2e4);D.unref?.();const j=Date.parse(d.attempt.command.context.executionDeadline)-Date.now(),G=setTimeout(()=>{_=!0,L("deadline"),U()},Math.max(0,Math.min(j,2147483647)));G.unref?.();const z=async e=>{if(!y)return;const r=this.currentConversationRunBoundaryLease(t);await this.finishConversationRunBoundary(m,r,y,e,"recovery"===a?"recovery":"runtime"),y=void 0};try{if(P(),I||r.aborted)return void await i({...m,outcome:"outcome_unknown",code:"INSTANCE_EPOCH_MISMATCH"});if(await this.authority.appendStarted(m),I||r.aborted)return void await i({...m,outcome:"outcome_unknown",code:"INSTANCE_EPOCH_MISMATCH"});if(_)return void await i({...m,outcome:"expired",code:"COMMAND_EXPIRED"});if(C)return void await i({...m,outcome:"cancelled",code:"OPERATION_CANCELLED"});const e=await this.startConversationRunBoundary(m,t,E);if(e?.rejected)return void await i({...m,outcome:"failed",code:e.code});if(y=e,I||r.aborted)throw new RuntimeCommandError("INSTANCE_EPOCH_MISMATCH","runtime authority fenced after the conversation run boundary started");if("recovery"===a&&y)throw new RuntimeCommandError("OUTCOME_UNKNOWN","session.prompt crossed its durable run boundary before recovery");if(_)throw new RuntimeCommandError("COMMAND_EXPIRED","session.prompt expired after the conversation run boundary started");if(C)throw new RuntimeCommandError("OPERATION_CANCELLED","session.prompt was cancelled after the conversation run boundary started");let n;M=this.dispatcher.dispatch(d.attempt.command,h.context),A=!0,T&&U();const s=this.forwardProgress(M.progress,m,()=>I||r.aborted||C||_).catch(e=>{n=e}),c=M.completion.then(e=>(clearTimeout(G),{status:"completed",result:e}),e=>(clearTimeout(G),{status:"failed",error:e}));let u=await Promise.race([c,b.then(e=>({status:"interrupted",reason:e}))]);if("interrupted"===u.status){const e=u.reason;U();if("cancel_requested"===(x?await Promise.race([x,delay(this.options.cancellationGraceMs??1e3).then(()=>({status:"not_cancellable"}))]):{status:"not_cancellable"}).status&&(u=await Promise.race([c,delay(this.options.cancellationGraceMs??1e3).then(()=>({status:"interrupted",reason:e}))])),"interrupted"===u.status)throw new RuntimeCommandError("fenced"===u.reason?"INSTANCE_EPOCH_MISMATCH":"OUTCOME_UNKNOWN","session.prompt execution did not converge after interruption")}if(R=!0,"failed"===u.status)throw u.error;const l=u.result;N=!0;const p="session.prompt"===d.attempt.command.name&&!0===l.requestAborted;if(w=p?"cancelled":"completed",await s,n)throw n;if(await z(w),I||r.aborted)return void await i({...m,outcome:"outcome_unknown",code:"INSTANCE_EPOCH_MISMATCH"});if(_)return void await i({...m,outcome:"outcome_unknown",code:"OUTCOME_UNKNOWN"});if(C&&!p)throw new RuntimeCommandError("OUTCOME_UNKNOWN","cancel request raced a locally completed runtime effect");await this.authority.commitResult({...m,command:d.attempt.command,result:sanitizeServiceResult(d.attempt.command.name,l)}),await o()}catch(e){let t=e;if(y&&(!A||R))try{await z(w??(C||_?"cancelled":"failed"))}catch(r){t=new RuntimeCommandError("OUTCOME_UNKNOWN",`conversation run terminal outcome is unknown: ${errorText(r)}`,void 0,{cause:e})}if(N&&!isUncertainRuntimeError(t)&&(t=new RuntimeCommandError("OUTCOME_UNKNOWN",`runtime effect completed before its terminal receipt: ${errorText(t)}`,void 0,{cause:e})),y)return void(O=!0);const r=serviceRuntimeFailureCode(t),a="OUTCOME_UNKNOWN"===r||"INSTANCE_EPOCH_MISMATCH"===r||I||A&&(C||_),n="COMMAND_EXPIRED"===r||!A&&_,o=!A&&C;await i({...m,outcome:a?"outcome_unknown":n?"expired":o?"cancelled":"failed",code:"INSTANCE_EPOCH_MISMATCH"===r||I?"INSTANCE_EPOCH_MISMATCH":a?"OUTCOME_UNKNOWN":n?"COMMAND_EXPIRED":o?"OPERATION_CANCELLED":r??"RUNTIME_EXECUTION_FAILED"})}finally{clearInterval(H),clearInterval(D),clearTimeout(G),r.removeEventListener("abort",S),p.abort(),await this.authority.releaseClaim({...s(),reason:r.aborted||O?"worker_shutdown":"completed"}).catch(()=>{})}}async startConversationRunBoundary(e,t,r){const a=e.attempt.command;if("session.prompt"!==a.name)return;const n=r?.conversationRunBoundary;if(!n)throw new RuntimeCommandError("INVALID_PARAMS","Service session.prompt lacks its conversation run boundary");const o=a.context.conversation,i=await n.boundary.start({operationId:e.operationId,requestMessageId:e.requestMessageId,conversationId:o.runtimeConversationUid,historyGeneration:n.historyGeneration,runtimeRunId:a.params.runtimeRunId,lease:exactRunBoundaryLease(t)});if("rejected"===i.status)return{rejected:!0,code:i.reason};if(i.historyGeneration!==n.historyGeneration)throw new RuntimeCommandError("OUTCOME_UNKNOWN","conversation run boundary changed history generation");return{boundary:n.boundary,historyGeneration:n.historyGeneration,activeRunRegistrationId:i.activeRunRegistrationId,runtimeRunId:a.params.runtimeRunId}}currentRuntimeLeaseCredential(e){const t=this.getLease();if(!t||t.leaseId!==e.leaseId||t.runtimeEpoch!==e.runtimeEpoch)throw new RuntimeCommandError("INSTANCE_EPOCH_MISMATCH","runtime lease changed before deferred resource redemption");return t.runtimeLeaseCredential}currentConversationRunBoundaryLease(e){const t=this.getLease();if(!t||t.leaseId!==e.leaseId||t.runtimeEpoch!==e.runtimeEpoch||t.handlerIdentity!==e.handlerIdentity)throw new RuntimeCommandError("INSTANCE_EPOCH_MISMATCH","runtime lease changed before conversation run convergence");return t}async finishConversationRunBoundary(e,t,r,a,n){const o=e.attempt.command.context;if("conversation"!==o.scope)throw new RuntimeCommandError("OUTCOME_UNKNOWN","conversation run boundary lost its target");const i=await r.boundary.finish({activeRunRegistrationId:r.activeRunRegistrationId,operationId:e.operationId,requestMessageId:e.requestMessageId,conversationId:o.conversation.runtimeConversationUid,historyGeneration:r.historyGeneration,runtimeRunId:r.runtimeRunId,terminalState:a,source:n,lease:exactRunBoundaryLease(t)});if("rejected"===i.status||i.activeRunRegistrationId!==r.activeRunRegistrationId)throw new RuntimeCommandError("OUTCOME_UNKNOWN",`conversation run finish rejected: ${"rejected"===i.status?i.reason:"registration_mismatch"}`)}async forwardProgress(e,t,r){for await(const a of e){if(r())return;await this.authority.appendProgress({...t,progress:a})}}}function exactRunBoundaryLease(e){return{leaseId:e.leaseId,handlerIdentity:e.handlerIdentity,runtimeEpoch:e.runtimeEpoch,runtimeLeaseCredential:e.runtimeLeaseCredential}}function serviceRuntimeFailureCode(e){const t=e?.code;return t&&["OUTCOME_UNKNOWN","INSTANCE_EPOCH_MISMATCH","COMMAND_EXPIRED","OPERATION_CANCELLED","IDEMPOTENCY_CONFLICT","UNAUTHORIZED_TARGET","AGENT_DEFAULT_MODEL_NOT_CONFIGURED"].includes(t)?t:void 0}function isUncertainRuntimeError(e){return e instanceof RuntimeCommandError&&("OUTCOME_UNKNOWN"===e.code||"INSTANCE_EPOCH_MISMATCH"===e.code)}function errorText(e){return e instanceof Error?e.message:String(e)}export function sanitizeServiceResult(e,t){if("cron.status"!==e||!t||"object"!=typeof t)return t;const r=t;return{enabled:r.enabled,jobCount:r.jobCount,..."number"==typeof r.nextWakeAtMs?{nextWakeAtMs:r.nextWakeAtMs}:{}}}function assertCommandCorrelation(e,t,r){if(e.operationId!==t.context.operationId||e.payload.paramsHash!==t.context.paramsHash||t.context.dispatchEpoch!==r||e.payload.command.name!==t.name||!sessionSetModelMappingMatches(e.payload.request,t))throw new Error("runtime invocation command correlation failed")}function prestartAttempt(e){return{command:e.payload.command,executionBoundaryId:"prestart",boundaryAssertionHash:e.payload.commandHash,executionMode:"start",validationContext:{},authorization:{},derivationContext:{}}}const aborted=Symbol("aborted");function waitForAbort(e,t){return t?t.aborted?(e.catch(()=>{}),Promise.resolve(aborted)):new Promise((r,a)=>{let n=!1;const o=()=>{n||(n=!0,r(aborted))};t.addEventListener("abort",o,{once:!0}),e.then(e=>{n||(n=!0,t.removeEventListener("abort",o),r(e))},e=>{n||(n=!0,t.removeEventListener("abort",o),a(e))})}):e}const delay=(e,t)=>new Promise(r=>{if(t?.aborted)return r();const a=()=>{clearTimeout(n),r()},n=setTimeout(()=>{t?.removeEventListener("abort",a),r()},e);t?.addEventListener("abort",a,{once:!0})});
|
|
1
|
+
import{randomUUID}from"node:crypto";import{verifyAndCreateServiceCoreExecutionAttempt}from"./core-execution-attempt-context.js";import{RuntimeCommandError}from"./runtime-command-dispatcher.js";import{RuntimeClaimAuthorityProtocolError}from"./message-service-runtime-claim-authority.js";import{sessionSetModelMappingMatches}from"./session-set-model-mapping.js";import{RuntimeTransportFailurePolicy}from"@beeos-ai/cloud-harness-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,b=!1;const g=new Promise(e=>{v=e}),L=e=>{b||(b=!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,g.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})});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{createCentrifugoRealtimePort,createRuntimeChatClient,createRuntimeChatHttpPort}from"@beeos-ai/cloud-harness-sdk";import{NativeSessionNewBridge,nativeSessionNewConversationCreator}from"./native-session-new-bridge.js";const MESSAGE_CREATED="message.created";export function nativeCloudChatAuthorityFromRuntimeAuthority(e,t=Date.now){const n=e=>{if(!e||!e.runtimeLeaseCredential)return null;const n=Date.parse(e.leaseExpiresAt);return!Number.isFinite(n)||n<=t()?null:{instanceId:e.instanceId,handlerIdentity:e.handlerIdentity,runtimeEpoch:e.runtimeEpoch,leaseId:e.leaseId,runtimeLeaseCredential:e.runtimeLeaseCredential,leaseExpiresAt:e.leaseExpiresAt}};return{currentLease:()=>n(e.authorityLease),subscribeAuthority:t=>e.subscribeAuthority(e=>t(n(e)))}}export function createNativeCloudChatComposition(e){const t=e.now??Date.now,n=e.http??createRuntimeChatHttpPort({baseUrl:e.serviceUrl,...e.fetchImpl?{fetchImpl:e.fetchImpl}:{},now:t}),s=e.realtime??createCentrifugoRealtimePort(),i=createRuntimeChatClient({authority:{currentLease:()=>e.authority.currentLease(),subscribeLease:t=>e.authority.subscribeAuthority(t)},http:n,realtime:s,now:t,...e.reconnectDelayMs?{reconnectDelayMs:e.reconnectDelayMs}:{},...e.logger?{logger:e.logger}:{}}),o=new NativeCloudChatAdapterImpl({client:i,...e.logger?{logger:e.logger}:{}});return{client:i,adapter:o,http:n,sessionNew:new NativeSessionNewBridge({client:i,...e.logger?{logger:e.logger}:{}}),sessionNewConversation:nativeSessionNewConversationCreator(i,e.logger),disconnect:async()=>{await o.disconnect(),await i.disconnect()},dispose:()=>{o.dispose(),i.dispose()}}}class NativeCloudChatAdapterImpl{deps;messages;projections=new Map;conversationProjections=new Map;storeListeners=new Set;inboundListeners=new Set;connection="disconnected";constructor(e){this.deps=e,this.messages={startStream:e=>this.startStream(e),getEnvelope:async(e,t)=>await this.getEnvelope(e,t)},this.deps.client.on("message",e=>this.admitInbound(e)),this.deps.client.on("connection",e=>{this.connection=e,this.publish()}),this.deps.client.on("error",e=>{this.deps.logger?.warn?.(`[native-cloud-chat] realtime error: ${e instanceof Error?e.message:String(e)}`)})}async connect(){try{await this.deps.client.connect()}catch(e){throw this.connection="disconnected",this.publish(),e}}async disconnect(){this.connection="disconnected",await this.deps.client.disconnect(),this.publish()}dispose(){this.storeListeners.clear(),this.inboundListeners.clear(),this.deps.client.dispose()}listen(e,t){const n=e.eventTypes;return n&&!n.includes(MESSAGE_CREATED)?()=>{}:(this.inboundListeners.add(t),()=>{this.inboundListeners.delete(t)})}subscribe(e){return this.storeListeners.add(e),()=>{this.storeListeners.delete(e)}}getSnapshot(){const e={};for(const[t,n]of this.projections)e[t]=n;const t={};for(const[e,n]of this.conversationProjections)t[e]=n;return{connection:this.connection,messages:e,conversations:t}}admitInbound(e){const t=toInboundProjection(e);this.projections.set(t.id,t),this.publish();for(const e of[...this.inboundListeners])e(t)}publish(){for(const e of[...this.storeListeners])e()}startStream(e){return new NativeHandlerReplyStream(this.deps.client.messages.startStream({conversationId:e.conversationId,id:e.clientMessageId,type:e.type,content:e.content,...e.replyTo?{replyTo:e.replyTo}:{}}))}async getEnvelope(e,t){const n=(await this.deps.client.history(e)).messages.find(e=>e.id===t);return{state:"string"==typeof n?.state?n.state:"outcome_unknown"}}}class NativeHandlerReplyStream{inner;constructor(e){this.inner=e}get id(){return this.inner.id}get isTerminated(){return this.inner.isTerminated}get parts(){return this.inner.parts}async opened(){const e=asRecord(await this.inner.opened()),t="string"==typeof e?.messageId&&e.messageId.length>0?e.messageId:this.inner.id;return"string"==typeof e?.outcome?{messageId:t,outcome:e.outcome}:{messageId:t}}appendBody(e){this.inner.appendBody(e)}setBody(e){this.inner.setBody(e)}addPart(e){this.inner.addPart(e)}replacePart(e,t){this.inner.replacePart(e,t)}appendToolResult(e,t,n){this.inner.addToolResult({type:"tool_result",tool_use_id:e,content:t,...n?{is_error:!0}:{}})}finalize(e){return this.inner.finalize(e)}fail(e){return this.inner.fail(e)}cancel(e){return this.inner.cancel(e)}}export function toInboundProjection(e){const t=asRecord(e)??{},n="string"==typeof t.body?t.body:"",s=Array.isArray(t.parts)?t.parts:void 0,i="string"==typeof t.state?t.state:void 0,o="string"==typeof t.stopReason?t.stopReason:void 0,r="string"==typeof t.createdAt?t.createdAt:void 0,a="string"==typeof t.updatedAt?t.updatedAt:r,c="string"==typeof t.offset?t.offset:"0";return{...t,id:e.id,conversationId:e.conversationId,type:e.type,content:e.content??{},...e.senderId?{senderId:e.senderId}:{},...e.replyTo?{replyTo:e.replyTo}:{},body:n,...s?{parts:s}:{},...i?{state:i}:{},...o?{stopReason:o}:{},...r?{createdAt:r}:{},...a?{updatedAt:a}:{},offset:c}}function asRecord(e){return null===e||"object"!=typeof e||Array.isArray(e)?void 0:e}export function historyToInboundProjection(e){return toInboundProjection(e)}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{createNativeOutputFileAttempt}from"./native-output-file-attempt.js";import{downloadNativeInputFile}from"./native-input-files.js";import{bindExistingRuntimeChatReply}from"@beeos-ai/cloud-harness-sdk";import{RUNTIME_HASH_DOMAINS}from"@beeos-ai/beeos-types/runtime";import{dispatchImInboundPrompt}from"../im-runtime-dispatch.js";import{prepareNativePromptInput}from"./native-prompt-preflight.js";import{verifyNativePromptAuthority}from"./native-prompt-authority.js";import{RuntimeCommandError}from"./runtime-command-dispatcher.js";import{runtimeCommandResultHash}from"./runtime-command-journal.js";import{runtimeMessageBase,runtimeMessageHash,semanticEqual}from"./runtime-message-channel-contract.js";import{openClawSessionModePatch}from"./runtime-session-control-service.js";export const nativePromptCancellationReason="chat_cancel:runtime_operation_cancel_requested";export const nativePromptExecutionDeadlineReason="deadline exceeded: the Cloud turn execution budget is about to expire";const nativePromptWrapUpMarginMs=3e4;export class NativeCloudPromptBridge{options;constructor(e){if(this.options=e,"function"!=typeof e.host.resolveNativePromptSessionKey)throw new RuntimeCommandError("INVALID_PARAMS","native Core target resolver required");if("function"!=typeof e.host.prepareNativePromptModel)throw new RuntimeCommandError("INVALID_PARAMS","native Core model preparation required");if(!e.runBoundary)throw new RuntimeCommandError("OUTCOME_UNKNOWN","native MS run boundary required")}async execute(e){const{options:t}=this;if(await t.verifier.refresh(),!e.delivery.executionGrant)throw new RuntimeCommandError("INVALID_PARAMS","native grant missing");const a=e.delivery.message.payload,n="string"==typeof a.operationId?await t.journal.get(a.operationId):null,i=!!n&&"session.prompt"===n.command&&["started","effect_committed","projection_committed","failed"].includes(n.phase)&&n.paramsHash===a.paramsHash&&n.messageBinding?.requestMessageId===a.requestMessageId&&n.messageBinding?.requestHash===a.requestHash,r=verifyNativePromptAuthority({request:e.delivery.message.payload,decoded:t.verifier.verifyAndDecodeGrant(e.delivery.executionGrant),lease:e.lease,lineage:t.getLineage(),allowExpiredReadOnly:i}),{params:o}=r,s=r.request;try{const a=e.delivery.message;if("runtime_rpc_request"!==a.type||"completed"!==a.state||a.message_id!==s.requestMessageId||a.channel_id!==s.operationId||void 0!==a.in_reply_to)throw new RuntimeCommandError("INVALID_PARAMS","native envelope binding invalid");let n;void 0!==o.mode&&openClawSessionModePatch(o.mode);try{if(void 0!==o.mentions)throw new TypeError("native host per-prompt optional binding not implemented");n=prepareNativePromptInput(o.parts)}catch(e){throw new RuntimeCommandError(i?"OUTCOME_UNKNOWN":"INVALID_PARAMS","native host part unsupported",void 0,{cause:e})}const d=n.message;if(!t.host.activeRunAuthority)throw new RuntimeCommandError("EXECUTION_FAILED","native existing active-run authority unavailable");const m=()=>{const a=t.getLease(),n=t.getLineage();if(!a||a.instanceId!==e.lease.instanceId||a.handlerIdentity!==e.lease.handlerIdentity||a.runtimeEpoch!==o.runtimeLease.runtimeEpoch||a.leaseId!==o.runtimeLease.leaseId||n.journalStoreId!==o.runtimeLease.journalStoreId||n.journalGeneration!==o.runtimeLease.journalGeneration||Date.parse(a.leaseExpiresAt)<=Date.now())throw new RuntimeCommandError("INSTANCE_EPOCH_MISMATCH","native prompt attempt fenced")};m();const u=await t.channel.history({operationId:s.operationId,lease:e.lease,currentNativePrompt:r});if(m(),"found"!==u.status||u.snapshot.requestHash!==s.requestHash||u.snapshot.requestMessageId!==s.requestMessageId||!semanticEqual(u.snapshot.request.payload,s))throw new RuntimeCommandError("INVALID_PARAMS","native retained request mismatch");let l,p=await t.journal.get(s.operationId);if(m(),p&&("session.prompt"!==p.command||p.paramsHash!==s.paramsHash||p.runtimeEpoch!==o.runtimeLease.runtimeEpoch||p.messageBinding?.requestMessageId!==s.requestMessageId||p.messageBinding.requestHash!==s.requestHash))throw new RuntimeCommandError("OUTCOME_UNKNOWN","native existing journal binding mismatch");if(!t.http.getMessage)throw new RuntimeCommandError("OUTCOME_UNKNOWN","native task snapshot unavailable");try{l=await t.http.getMessage(t.getLease(),{conversationId:o.conversationId,messageId:o.taskRequestMessageId})}catch(e){throw new RuntimeCommandError("OUTCOME_UNKNOWN","native original task snapshot unavailable",void 0,{cause:e})}if(m(),l.messageId!==o.taskRequestMessageId||l.conversationId!==o.conversationId||"chat_message"!==l.type||l.body!==d)throw new RuntimeCommandError("OUTCOME_UNKNOWN","native original task snapshot mismatch");const c=async()=>{if(!t.http.getMessage)throw new RuntimeCommandError("OUTCOME_UNKNOWN","native single snapshot unavailable");m();const e=await t.http.getMessage(t.getLease(),{conversationId:o.conversationId,messageId:o.replyMessageId});return m(),this.assertReply(e,r),e};if(u.snapshot.started||p&&"prepared"!==p.phase){const e=u.snapshot.runBoundary?.finishedReceipt,a=u.snapshot.final;if(!(p&&["started","effect_committed","projection_committed","failed"].includes(p.phase)&&e&&"finished"===e.state&&e.activeRunRegistrationId===o.taskId&&a))throw new RuntimeCommandError("OUTCOME_UNKNOWN","native durable MS finish audit or original result unavailable");const n=await c();if("agent_reply"!==n.type||n.state!==e.replyState||n.stopReason!==e.replyStopReason||n.updatedAt!==e.replyUpdatedAt)throw new RuntimeCommandError("OUTCOME_UNKNOWN","native original terminal does not match finished audit");const i=this.terminal(n,r);if(a.payload.outcome!==i.outcome||"final_message"!==u.snapshot.terminalSource)throw new RuntimeCommandError("OUTCOME_UNKNOWN","native original final outcome mismatch");const d=a.payload.response;if("2.0"!==d.jsonrpc||d.id!==s.request.id)throw new RuntimeCommandError("OUTCOME_UNKNOWN","native original final RPC binding mismatch");const l=runtimeCommandResultHash(i.result);if(p.resultReceipt&&p.resultReceipt.resultHash!==l||void 0!==p.result&&!semanticEqual(p.result,i.result))throw new RuntimeCommandError("OUTCOME_UNKNOWN","native original result conflicts with local receipt");if(void 0===d.result)throw new RuntimeCommandError("OUTCOME_UNKNOWN","native original canonical result evidence unavailable");{const e=u.snapshot.effectCommitted?.payload,t=runtimeMessageHash(RUNTIME_HASH_DOMAINS.coreResult,{method:s.method,result:d.result});if("succeeded"!==i.outcome||void 0!==d.error||!semanticEqual(d.result,i.result)||!e||e.effectRecord.coreResultHash!==t||e.effectRecord.coreResultRef!==`sha256:${t}`||a.payload.effectRecordHash!==e.effectRecordHash||a.payload.serviceResultHash!==runtimeMessageHash(RUNTIME_HASH_DOMAINS.serviceResult,{method:s.method,result:d.result}))throw new RuntimeCommandError("OUTCOME_UNKNOWN","native original final result hash evidence mismatch")}const h=d.result;return m(),"started"===p.phase&&(p=await t.journal.transition({operationId:s.operationId,expectedRevision:p.revision,phase:"succeeded"===i.outcome?"effect_committed":"failed",result:h}),m()),{request:s,outcome:i.outcome,result:h,replayedFinal:a}}if(n.files.length&&!t.inputFiles)throw new RuntimeCommandError("EXECUTION_FAILED","native authorized input file resolver unavailable");if(u.snapshot.terminal||e.signal.aborted||u.snapshot.cancelRequested||await t.journal.getCancellation(s.operationId))throw new RuntimeCommandError("OPERATION_CANCELLED","native prompt cancelled before effect");const h=await t.host.resolveNativePromptSessionKey(r,m);if(m(),p=await t.journal.acceptMessageRequest({operationId:s.operationId,command:"session.prompt",paramsHash:s.paramsHash,runtimeEpoch:o.runtimeLease.runtimeEpoch,requestMessageId:s.requestMessageId,requestHash:s.requestHash}),m(),"prepared"!==p.phase)throw new RuntimeCommandError("OUTCOME_UNKNOWN","native task already started");let v;const g=await bindExistingRuntimeChatReply({authority:{currentLease:t.getLease},http:t.http,binding:{conversationId:o.conversationId,messageId:o.replyMessageId,taskRequestMessageId:o.taskRequestMessageId,assertAttempt:m,assertPreparedClear:e=>{if(m(),!v||e.activeRunRegistrationId!==v||e.historyGeneration!==r.historyGeneration||e.runtimeEpoch!==o.runtimeLease.runtimeEpoch)throw new RuntimeCommandError("OUTCOME_UNKNOWN","native prepared clear does not freeze this registered attempt")},mutationKey:e=>`native-reply:${s.operationId}:${o.runtimeLease.journalStoreId}:${o.runtimeLease.journalGeneration}:${e}`}});if(m(),e.signal.aborted||await t.journal.getCancellation(s.operationId))throw new RuntimeCommandError("OPERATION_CANCELLED","native prompt cancelled before start");await t.channel.append({operationId:s.operationId,lease:t.getLease(),currentNativePrompt:r,executionGrant:e.delivery.executionGrant,type:"runtime_operation_started",payload:{...runtimeMessageBase(s),executionGrantHash:s.executionGrantHash,handlerIdentity:e.lease.handlerIdentity,executionMode:"start",claimedAt:p.updatedAt}}),m(),p=await t.journal.transition({operationId:s.operationId,expectedRevision:p.revision,phase:"started"}),m();const I=()=>{m();const e=t.getLease();return{leaseId:e.leaseId,handlerIdentity:e.handlerIdentity,runtimeEpoch:e.runtimeEpoch,runtimeLeaseCredential:e.runtimeLeaseCredential}},f={operationId:s.operationId,requestMessageId:s.requestMessageId,conversationId:r.command.context.conversation.runtimeConversationUid,historyGeneration:r.historyGeneration,runtimeRunId:o.runtimeRunId},R=await t.runBoundary.start({...f,lease:I()});if(m(),"registered"!==R.status||R.historyGeneration!==r.historyGeneration||R.activeRunRegistrationId!==o.taskId)throw new RuntimeCommandError("OUTCOME_UNKNOWN","native MS run start did not register the original task");v=R.activeRunRegistrationId;const N=new AbortController,w=()=>N.abort(e.signal.reason);if(e.signal.addEventListener("abort",w,{once:!0}),e.signal.aborted&&w(),e.setCancel(e=>N.abort(e)),await t.journal.getCancellation(s.operationId)&&N.abort(nativePromptCancellationReason),m(),N.signal.aborted){if(e.signal.removeEventListener("abort",w),N.signal.reason===nativePromptCancellationReason)throw new RuntimeCommandError("OPERATION_CANCELLED","native prompt cancelled before host invocation");throw new RuntimeCommandError("OUTCOME_UNKNOWN","native registered run stopped before host invocation")}const E=[];let y;const C=JSON.parse((await g.opened()).rawText),O=C.parts?.length??0,M=async e=>{const t=await e;return m(),this.assertReply(t,r),y=t,t},_={id:g.id,get isTerminated(){return g.isTerminated},get parts(){return E},opened:async()=>({messageId:g.id,outcome:"bound"}),appendBody:e=>g.appendBody(e),setBody:e=>g.setBody(e),addPart:e=>{E.push(e),g.addPart(e)},replacePart:(e,t)=>{E[e]=t,g.replacePart(O+e,t)},appendToolResult:(e,t,a)=>{const n={type:"tool_result",tool_use_id:e,content:t,...a?{is_error:!0}:{}};E.push(n),g.addPart(n)},finalize:e=>M(g.finalize(e)),fail:e=>M(g.fail(e)),cancel:e=>M(g.cancel(e))},b={message:d,mediaPaths:[],mediaTypes:[]};let U,A;const q=createNativeOutputFileAttempt({operationId:s.operationId,requestMessageId:s.requestMessageId,paramsHash:s.paramsHash,activeRunRegistrationId:v,getLease:()=>(m(),t.getLease()),assertAttempt:m,signal:N.signal}),T=setTimeout(()=>N.abort(nativePromptExecutionDeadlineReason),Math.max(0,Math.min(Date.parse(s.executionDeadlineAt)-3e4-Date.now(),2147483647)));T.unref?.();try{await dispatchImInboundPrompt({...t.host,runtimeRunId:o.runtimeRunId,onNativeAgentRunStart:e=>q.bindHostRun(e),nativeActiveRunRegistration:{runtimeConversationUid:f.conversationId,runtimeSessionKey:r.command.context.conversation.runtimeSessionKey,historyGeneration:R.historyGeneration,targetMessageId:o.taskRequestMessageId,runtimeRunId:o.runtimeRunId,activeRunRegistrationId:R.activeRunRegistrationId},assertNativeTarget:async()=>{if(await t.host.resolveNativePromptSessionKey(r,m)!==h)throw new RuntimeCommandError("OUTCOME_UNKNOWN","native prompt target changed before host invocation")},prepareNativeModel:async e=>{m(),await t.host.prepareNativePromptModel(r,e,m),m();for(const e of n.files){if(!t.inputFiles)throw new RuntimeCommandError("OUTCOME_UNKNOWN","native authorized input file resolver unavailable");const a=await t.inputFiles.resolve({operationId:s.operationId,requestMessageId:s.requestMessageId,paramsHash:s.paramsHash,partIndex:e.partIndex,lease:t.getLease(),signal:N.signal});m();const n=await downloadNativeInputFile({fileId:e.fileId,...a,signal:N.signal,assertAttempt:m});b.mediaPaths.push(n.path),b.mediaTypes.push(n.mimeType)}m()},onNativeRunCompletion:e=>{A=e},retainedStream:_,abortSignal:N.signal,inReplyTo:o.taskRequestMessageId,prompt:{channelId:o.conversationId,inboundMessageId:o.taskRequestMessageId,requestId:s.requestMessageId,selfPrincipalId:e.lease.instanceId,sessionKey:h,dispatchInput:b},client:{messages:{startStream(){throw new Error("native reply POST forbidden")},getEnvelope:async()=>({state:(await c()).state})},getSnapshot:()=>({connection:"connected",messages:{},conversations:{}})}})}catch(e){U=e}finally{clearTimeout(T),q.close(),e.signal.removeEventListener("abort",w)}if(!A&&U)throw U;if(!A||A.runtimeRunId!==o.runtimeRunId||A.token.sessionKey!==h)throw new RuntimeCommandError("OUTCOME_UNKNOWN","native physical host completion unavailable");let P;try{await A.completion}catch(e){P=e}if(m(),!y)throw new RuntimeCommandError("OUTCOME_UNKNOWN",void 0!==P?"native host/abort/drain did not converge":"native kernel settled without verified original reply terminal",void 0,{cause:P??U});const S=this.terminal(y,r),L=await c();g.assertTerminalSnapshot(L);const x="completed"===L.state?"completed":"cancelled"===L.state?"cancelled":"failed",H=await t.runBoundary.finish({...f,activeRunRegistrationId:v,terminalState:x,source:"runtime",lease:I()});if(m(),"finished"!==H.status&&"idempotent"!==H.status||H.activeRunRegistrationId!==v)throw new RuntimeCommandError("OUTCOME_UNKNOWN","native MS run finish was not acknowledged");return await t.journal.transition({operationId:s.operationId,expectedRevision:p.revision,phase:"succeeded"===S.outcome?"effect_committed":"failed",result:S.result}),m(),{request:s,...S}}catch(e){throw new NativePromptExecutionFailure(s,e)}}assertReply(e,t){if(e.messageId!==t.params.replyMessageId||e.conversationId!==t.params.conversationId||e.replyTo!==t.params.taskRequestMessageId)throw new RuntimeCommandError("OUTCOME_UNKNOWN","native durable reply binding mismatch")}terminal(e,t){if(!e.stopReason||!["completed","failed","refused","cancelled"].includes(e.state))throw new RuntimeCommandError("OUTCOME_UNKNOWN","native durable terminal absent");const a="cancelled"===e.state&&"user_stop"===e.stopReason?"cancelled":"completed"===e.state&&"end_turn"===e.stopReason?"succeeded":"failed";return{outcome:a,result:{runtimeRunId:t.params.runtimeRunId,stopReason:e.stopReason,requestAborted:"cancelled"===e.state&&"user_stop"===e.stopReason,..."failed"===a?{error:e.body}:{}}}}}export const NATIVE_PROMPT_UNKNOWN_REASONS=new Set(["native MS run boundary required","native existing journal binding mismatch","native task snapshot unavailable","native original task snapshot unavailable","native original task snapshot mismatch","native single snapshot unavailable","native durable MS finish audit or original result unavailable","native original terminal does not match finished audit","native original final outcome mismatch","native original final RPC binding mismatch","native original result conflicts with local receipt","native original final result hash evidence mismatch","native original canonical result evidence unavailable","native task already started","native prepared clear does not freeze this registered attempt","native MS run start did not register the original task","native registered run stopped before host invocation","native prompt target changed before host invocation","native authorized input file resolver unavailable","native physical host completion unavailable","native host/abort/drain did not converge","native kernel settled without verified original reply terminal","native MS run finish was not acknowledged","native durable reply binding mismatch","native durable terminal absent"]);export function nativePromptUnknownReason(e){return e instanceof RuntimeCommandError&&"OUTCOME_UNKNOWN"===e.code?NATIVE_PROMPT_UNKNOWN_REASONS.has(e.message)?e.message:"unlisted":"not_unknown"}export class NativePromptExecutionFailure extends Error{request;failure;constructor(e,t){super(t instanceof Error?t.message:String(t),{cause:t}),this.request=e,this.failure=t}}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{CLOUD_GATEWAY_RUNTIME_ROUTES}from"@beeos-ai/cloud-harness-sdk";import{runtimeLeaseHttp}from"./message-service-runtime-authenticated-fetch.js";const nativeJson=JSON,object=e=>null!==e&&"object"==typeof e&&!Array.isArray(e),integerSource=e=>{if(!nativeJson.isRawJSON(e))return null;const t=JSON.stringify(e);return/^(0|[1-9][0-9]*)$/.test(t)&&BigInt(t)<=9223372036854775807n?t:null},nonempty=e=>"string"==typeof e&&e.length>0;export class NativeInputFilesHttpError extends Error{status;code;requestId;metadata;constructor(e,t,r,n){super(`native input Files resolve failed (${e})`),this.status=e,this.code=t,this.requestId=r,this.metadata=n}}const evidenceText=e=>"string"==typeof e&&/^[A-Za-z0-9_.:-]{1,128}$/.test(e);function selectedHttpError(e,t){const r=object(t)&&object(t.error)?t.error:void 0,n={};if(r&&object(r.metadata))for(const e of["operationId","requestMessageId","paramsHash","taskId","partIndex","clearIntentId","activeRunRegistrationId","historyGeneration","runtimeEpoch"]){const t=r.metadata[e];(evidenceText(t)||nativeJson.isRawJSON(t)&&null!==integerSource(t))&&(n[e]=t)}return new NativeInputFilesHttpError(e,r&&evidenceText(r.code)?r.code:void 0,object(t)&&evidenceText(t.request_id)?t.request_id:void 0,Object.keys(n).length?n:void 0)}export class NativeInputFilesHttpClient{getLease;transport;origin;constructor(e,t,r){this.getLease=t,this.transport=r;const n=new URL(e);if(!["http:","https:"].includes(n.protocol)||n.username||n.password||"/"!==n.pathname||n.search||n.hash)throw new Error("native input Files origin invalid");this.origin=n.origin}async resolve(e){const t=()=>{const t=this.getLease();if(!t||t.instanceId!==e.lease.instanceId||t.handlerIdentity!==e.lease.handlerIdentity||t.leaseId!==e.lease.leaseId||t.runtimeEpoch!==e.lease.runtimeEpoch||!t.runtimeLeaseCredential||Date.parse(t.leaseExpiresAt)<=Date.now())throw new Error("native input Files lease fenced");return t};if(!Number.isSafeInteger(e.partIndex)||e.partIndex<0)throw new Error("native input Files part index invalid");const r=t(),n=await runtimeLeaseHttp(this.origin,(e,t)=>this.transport(e,t??{})).postJson(CLOUD_GATEWAY_RUNTIME_ROUTES.inputFilesResolve(e.operationId),{runtimeLeaseCredential:r.runtimeLeaseCredential},{requestMessageId:e.requestMessageId,paramsHash:e.paramsHash,partIndex:e.partIndex},void 0,e.signal);if(t(),n.url&&new URL(n.url).origin!==this.origin)throw new Error("native input Files response origin changed");if("function"!=typeof nativeJson.rawJSON||"function"!=typeof nativeJson.isRawJSON)throw new Error("native numeric JSON source required");const i=await n.text();let s;try{s=nativeJson.parse(i,(e,t,r)=>{if("number"!=typeof t)return t;if(!r?.source)throw new Error("native numeric JSON source required");return nativeJson.rawJSON(r.source)})}catch(e){if(!(e instanceof SyntaxError))throw e;if(!n.ok)throw new NativeInputFilesHttpError(n.status);throw new Error("native input Files response JSON invalid")}if(t(),!n.ok)throw selectedHttpError(n.status,s);if(!object(s)||s.operationId!==e.operationId||s.requestMessageId!==e.requestMessageId||s.paramsHash!==e.paramsHash||s.taskId!==e.operationId||integerSource(s.partIndex)!==`${e.partIndex}`||!object(s.file)||!object(s.download))throw new Error("native input Files response binding invalid");const o=s.file,a=s.download,d=integerSource(a.size_bytes),c=integerSource(o.size_bytes),u=integerSource(o.resource_version);if(!nonempty(o.file_id)||!nonempty(o.filename)||"ready"!==o.status||null===u||null===c||!nonempty(o.content_type)||"download"!==a.direction||a.file_id!==o.file_id||"GET"!==a.http_method||!nonempty(a.url)||!nonempty(a.expires_at)||!nonempty(a.content_type)||null===d||d!==c||a.content_type!==o.content_type||a.checksum_sha256!==o.checksum_sha256||void 0!==a.checksum_sha256&&"string"!=typeof a.checksum_sha256||!object(a.required_headers)||Object.values(a.required_headers).some(e=>"string"!=typeof e))throw new Error("native input Files descriptor invalid");const p={...a,direction:"download",file_id:o.file_id,url:a.url,http_method:"GET",required_headers:a.required_headers,expires_at:a.expires_at,content_type:a.content_type,size_bytes:d,...void 0!==a.checksum_sha256?{checksum_sha256:a.checksum_sha256}:{}};return{filename:o.filename,download:p,file:o}}}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{createWriteStream}from"node:fs";import{mkdir,mkdtemp,rm}from"node:fs/promises";import{join,basename}from"node:path";import{createHash}from"node:crypto";import{Readable,Transform}from"node:stream";import{pipeline}from"node:stream/promises";export async function downloadNativeInputFile(e){const t=e.download;if("download"!==t.direction||t.file_id!==e.fileId||"GET"!==t.http_method||!/^(0|[1-9][0-9]*)$/.test(t.size_bytes)||!t.content_type||!Number.isFinite(Date.parse(t.expires_at))||Date.parse(t.expires_at)<=Date.now())throw new Error("native input file descriptor invalid or expired");let r;try{r=new URL(t.url)}catch(e){if(!(e instanceof TypeError))throw e;throw new Error("native input file transfer URL invalid")}if("https:"!==r.protocol&&"http:"!==r.protocol||r.username||r.password)throw new Error("native input file transfer URL invalid");if(void 0!==t.checksum_sha256&&!/^[0-9a-f]{64}$/.test(t.checksum_sha256))throw new Error("native input file checksum invalid");e.assertAttempt();const i=e.signal,a=await fetch(r,{method:t.http_method,headers:t.required_headers,signal:i,redirect:"error"});if(!a.ok||!a.body)throw new Error(`native input file download failed (${a.status})`);e.assertAttempt();const o=process.env.OPENCLAW_HOME??join(process.env.HOME??"/home/node",".openclaw"),n=join(o,"workspace","uploads");await mkdir(n,{recursive:!0});const s=await mkdtemp(join(n,"native-input-")),p=basename(e.filename).replace(/[^\p{L}\p{N}._-]/gu,"_"),c=join(s,p&&"."!==p&&".."!==p?p:"attachment");let m=0n;const d=BigInt(t.size_bytes),l=createHash("sha256"),f=new Transform({transform(e,t,r){m+=BigInt(e.byteLength),m>d?r(new Error("native input file size mismatch")):(l.update(e),r(null,e))}});try{const r=a.body.getReader(),o=async function*(){try{for(;;){const e=await r.read();if(e.done)return;yield e.value}}finally{await r.cancel(),r.releaseLock()}};if(await pipeline(Readable.from(o()),f,createWriteStream(c,{flags:"wx",mode:384}),{signal:i}),e.assertAttempt(),m!==d||void 0!==t.checksum_sha256&&l.digest("hex")!==t.checksum_sha256)throw new Error("native input file bytes do not match confirmed descriptor");return{path:c,mimeType:t.content_type}}catch(e){throw await rm(s,{recursive:!0,force:!0}),e}}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
const runs=new Map,tools=new Map,consumed=new Set;export function createNativeOutputFileAttempt(t){let e,o=!0;const s={...t,assertAttempt(){if(!o)throw new Error("native output file attempt finished");t.signal.throwIfAborted(),t.assertAttempt();const e=t.getLease();if(!e.runtimeLeaseCredential||!Number.isFinite(Date.parse(e.leaseExpiresAt))||Date.parse(e.leaseExpiresAt)<=Date.now())throw new Error("native output file lease fenced")},getLease:()=>(s.assertAttempt(),t.getLease())};return{bindHostRun(t){if(s.assertAttempt(),!t||void 0!==e||runs.has(t))throw new Error("native output file Host run binding ambiguous");e=t,runs.set(t,s)},close(){o=!1,e&&runs.get(e)===s&&runs.delete(e);for(const[t,e]of tools)e.assertAttempt===s.assertAttempt&&tools.delete(t);for(const e of consumed)e.startsWith(t.operationId+"\0")&&consumed.delete(e)}}}export function prepareNativeOutputTool(t,e){if(!t||!e)throw new Error("native output file trusted Host tool binding missing");const o=runs.get(t);if(!o)throw new Error("native output file trusted run missing");o.assertAttempt();const s=o.operationId+"\0"+e;if(tools.has(e)||consumed.has(s))throw new Error("native output file tool binding ambiguous or consumed");tools.set(e,{...o,toolCallId:e})}export function consumeNativeOutputTool(t){const e=tools.get(t);if(tools.delete(t),!e)throw new Error("native output file tool capability missing");return e.assertAttempt(),consumed.add(e.operationId+"\0"+t),e}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{isCloudRuntimeSubject,RUNTIME_HASH_DOMAINS,runtimeCloudCallerIntentPayload,runtimeCloudIdempotencyFingerprintPayload}from"@beeos-ai/beeos-types/runtime";import{runtimeMessageHash,semanticEqual}from"./runtime-message-channel-contract.js";import{RuntimeCommandError}from"./runtime-command-dispatcher.js";import{isDeepStrictEqual}from"node:util";const verifiedPrompts=new WeakMap;export function retainedVerifiedNativePrompt(e){if(!e||"object"!=typeof e)return null;const t=verifiedPrompts.get(e);return t&&isDeepStrictEqual(e,t)?structuredClone(t):null}const object=e=>!!e&&"object"==typeof e&&!Array.isArray(e),nonempty=e=>"string"==typeof e&&!!e,digest=e=>"string"==typeof e&&/^[0-9a-f]{64}$/.test(e),decimal=e=>"string"==typeof e&&/^(0|[1-9][0-9]*)$/.test(e),exact=(e,t)=>Object.keys(e).sort().join("\0")===t.sort().join("\0"),fail=()=>{throw new RuntimeCommandError("INVALID_PARAMS","native prompt signed binding invalid")},validReasoning=e=>object(e)&&exact(e,["modelId","reasoningOverrideId"])&&nonempty(e.modelId)&&(null===e.reasoningOverrideId||nonempty(e.reasoningOverrideId));export function verifyNativePromptAuthority(e){const t=e.request,a=e.decoded?.claims;if(!(object(t)&&object(a)&&object(t.request)&&object(t.command)&&object(t.command.context)&&object(t.command.params)&&object(t.request.params)))return fail();const n=t.request.params,r=t.command.context,o=n.runtimeLease;if(!(exact(t,["schemaVersion","authorityRealm","cloudSubject","operationId","requestMessageId","requestHash","paramsHash","commandHash","idempotencyKeyHash","idempotencyFingerprint","callerIntentHash","instanceId","leaseId","runtimeHandlerIdentity","dispatchEpoch","executionGrantHash","claimDeadlineAt","executionDeadlineAt","createdAt","method","request","command","callerExternalUserId"])&&exact(t.request,["jsonrpc","id","method","params"])&&exact(t.command,["context","name","params"])&&exact(n,["runtimeRunId","parts","conversationId","platformAgentId","taskId","taskRequestMessageId","replyMessageId","inputFingerprint","runtimeLease",...["mentions","mode","modelOverrideId","reasoning"].filter(e=>void 0!==n[e])])&&object(o)&&exact(o,["runtimeEpoch","leaseId","journalStoreId","journalGeneration"])&&2===t.schemaVersion&&"cloud"===t.authorityRealm&&"session/prompt"===t.method&&t.request.method===t.method&&"2.0"===t.request.jsonrpc&&("string"==typeof t.request.id||"number"==typeof t.request.id)&&"session.prompt"===t.command.name&&isCloudRuntimeSubject(t.cloudSubject)&&isCloudRuntimeSubject(a.cloudSubject)&&semanticEqual(t.cloudSubject,a.cloudSubject)&&t.cloudSubject.instanceId===e.lease.instanceId&&t.instanceId===e.lease.instanceId&&t.runtimeHandlerIdentity===e.lease.handlerIdentity&&t.leaseId===e.lease.leaseId&&decimal(t.dispatchEpoch)&&decimal(o.runtimeEpoch)&&decimal(o.journalGeneration)&&o.runtimeEpoch===e.lease.runtimeEpoch&&o.leaseId===e.lease.leaseId&&o.journalStoreId===e.lineage.journalStoreId&&o.journalGeneration===e.lineage.journalGeneration&&nonempty(t.callerExternalUserId)&&nonempty(t.requestMessageId)&&t.requestMessageId!==n.taskRequestMessageId&&n.replyMessageId!==n.taskRequestMessageId&&[n.conversationId,n.platformAgentId,n.taskRequestMessageId,n.replyMessageId].every(nonempty)&&digest(n.inputFingerprint)&&n.taskId===t.operationId&&n.runtimeRunId===t.operationId&&"string"==typeof n.taskId&&/^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/.test(n.taskId)&&Array.isArray(n.parts)&&(void 0===n.mode||"string"==typeof n.mode)&&(void 0===n.modelOverrideId||"string"==typeof n.modelOverrideId)&&(void 0===n.reasoning||validReasoning(n.reasoning))&&(void 0===n.mentions||Array.isArray(n.mentions))&&"conversation"===r.scope&&r.operationId===t.operationId&&r.dispatchEpoch===t.dispatchEpoch&&r.runtimeInstanceUid===t.instanceId&&r.paramsHash===t.paramsHash&&r.idempotencyKeyHash===t.idempotencyKeyHash&&r.claimDeadline===t.claimDeadlineAt&&r.executionDeadline===t.executionDeadlineAt&&decimal(r.historyGeneration)&&object(r.conversation)&&r.conversation.runtimeConversationUid===n.conversationId&&r.conversation.runtimeSessionKey===n.conversationId&&[t.requestHash,t.paramsHash,t.commandHash,t.callerIntentHash,t.idempotencyFingerprint,t.idempotencyKeyHash,t.executionGrantHash].every(digest)))return fail();const i={runtimeRunId:n.runtimeRunId,parts:n.parts,...void 0===n.mentions?{}:{mentions:n.mentions},...void 0===n.mode?{}:{mode:n.mode},...void 0===n.modelOverrideId?{}:{modelOverrideId:n.modelOverrideId},...void 0===n.reasoning?{}:{reasoning:n.reasoning}},s=e.now??Date.now();if(![t.createdAt,t.claimDeadlineAt,t.executionDeadlineAt].every(e=>"string"==typeof e&&Number.isFinite(Date.parse(e)))||Date.parse(t.createdAt)>Date.parse(t.claimDeadlineAt)||Date.parse(t.claimDeadlineAt)>Date.parse(t.executionDeadlineAt)||!e.allowExpiredReadOnly&&Date.parse(t.claimDeadlineAt)<=s||!e.allowExpiredReadOnly&&Date.parse(t.executionDeadlineAt)<=s||!semanticEqual(i,t.command.params)||runtimeMessageHash(RUNTIME_HASH_DOMAINS.request,t.request)!==t.requestHash||runtimeMessageHash(RUNTIME_HASH_DOMAINS.params,{method:t.method,params:n})!==t.paramsHash||runtimeMessageHash(RUNTIME_HASH_DOMAINS.command,{command:t.command})!==t.commandHash||runtimeMessageHash(RUNTIME_HASH_DOMAINS.cloudCallerIntent,runtimeCloudCallerIntentPayload(t.cloudSubject,"session/prompt",n,t.callerExternalUserId))!==t.callerIntentHash||runtimeMessageHash(RUNTIME_HASH_DOMAINS.cloudIdempotencyFingerprint,runtimeCloudIdempotencyFingerprintPayload(t.cloudSubject,"session/prompt",t.callerIntentHash))!==t.idempotencyFingerprint||e.decoded?.compactHash!==t.executionGrantHash)return fail();const d=Math.floor(s/1e3);if(!exact(a,["iss","aud","purpose","sub","jti","iat","exp","authorityRealm","cloudSubject","operationId","requestMessageId","instanceId","leaseId","handlerIdentity","dispatchEpoch","requestHash","paramsHash","commandHash","idempotencyKeyHash","executionMode","method","command","scope","callerExternalUserId","platformAgentId","runtimeAgentUid","platformConversationId","historyGeneration"])||"beeos-cloud-gateway"!==a.iss||"beeos-cloud-runtime-command"!==a.aud||"cloud-runtime-execution-grant"!==a.purpose||"cloud"!==a.authorityRealm||"start"!==a.executionMode||"conversation"!==a.scope||!nonempty(a.jti)||!nonempty(a.runtimeAgentUid)||!Number.isSafeInteger(a.iat)||!Number.isSafeInteger(a.exp)||a.iat>d||!e.allowExpiredReadOnly&&a.exp<=d||a.exp<=a.iat||a.exp-a.iat>60||a.sub!==e.lease.handlerIdentity||a.handlerIdentity!==e.lease.handlerIdentity||a.platformAgentId!==n.platformAgentId||a.platformConversationId!==n.conversationId||a.historyGeneration!==r.historyGeneration||!semanticEqual(a.command,t.command)||!semanticEqual(a.cloudSubject,t.cloudSubject)||["operationId","requestMessageId","instanceId","leaseId","dispatchEpoch","requestHash","paramsHash","commandHash","idempotencyKeyHash","callerExternalUserId","method"].some(e=>a[e]!==t[e]))return fail();const m=structuredClone(t),c={historyGeneration:r.historyGeneration,runtimeAgentUid:a.runtimeAgentUid,request:m,params:m.request.params,command:m.command};return verifiedPrompts.set(c,structuredClone(c)),c}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export function prepareNativePromptInput(e){const t=[],i=[];for(const[r,n]of e.entries()){if("text"===n.type){if("string"!=typeof n.text||hasUnexpectedKey(n,["type","text"]))throw new TypeError("native text part unsupported or invalid");t.push(n.text);continue}if(!["image","audio","file","resource","resource_link"].includes(n.type)||void 0!==n.mimeType&&"string"!=typeof n.mimeType||"resource_link"!==n.type&&void 0!==n.fileName&&"string"!=typeof n.fileName||"resource_link"===n.type&&(void 0!==n.name&&"string"!=typeof n.name||void 0!==n.title&&"string"!=typeof n.title||void 0!==n.sizeBytes&&("string"!=typeof n.sizeBytes||!/^(0|[1-9][0-9]*)$/.test(n.sizeBytes))))throw new TypeError("native file part unsupported or invalid");const e="resource_link"===n.type?n.resourceRef:n.blobRef;if("string"!=typeof e||!/^beeos-file:\/\/[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/.test(e))throw new TypeError("native input part unsupported: canonical Cloud file reference required");i.push({partIndex:r,fileId:e.slice(13)})}return{message:t.join("\n"),files:i}}function hasUnexpectedKey(e,t){return Object.keys(e).some(e=>!t.includes(e))}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export function nativeSessionNewSyncRequest(n){const e=nonEmpty(n.operationId);if(!e)throw new Error("native session/new sync requires a retained operationId");const s=nonEmpty(n.runtimeSessionId);if(!s)throw new Error("native session/new sync requires a runtimeSessionId");const o=nonEmpty(n.runtimeSessionKey),t=nonEmpty(n.title);return{schemaVersion:1,operationId:e,runtimeSessionId:s,...o?{runtimeSessionKey:o}:{},...t?{title:t}:{}}}export function assertCanonicalConversationSyncResponse(n){if(!n||"string"!=typeof n.id||0===n.id.length)throw new Error("native session/new sync response is missing a canonical conversation id");return n}export class NativeSessionNewBridge{options;constructor(n){this.options=n}async sync(n){const e=nativeSessionNewSyncRequest(n);this.options.logger?.info?.(`[native-cloud-chat] session/new sync operationId=${e.operationId} runtimeSessionId=${e.runtimeSessionId}`);return{conversation:assertCanonicalConversationSyncResponse(await this.options.client.syncConversation(e))}}}export async function syncNativeSessionNew(n,e){const s=nativeSessionNewSyncRequest(e);return assertCanonicalConversationSyncResponse(await n.syncConversation(s))}export function nativeSessionNewConversationCreator(n,e){const s=new NativeSessionNewBridge({client:n,...e?{logger:e}:{}});return{create:async n=>({conversationId:(await s.sync({operationId:n.operationId,runtimeSessionId:n.runtimeSessionId,runtimeSessionKey:n.runtimeSessionKey,...n.title?{title:n.title}:{}})).conversation.id})}}function nonEmpty(n){return"string"==typeof n&&n.length>0?n:void 0}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export class OpenClawRuntimeCapabilities{deps;constructor(t){this.deps=t}async execute(t,e,
|
|
1
|
+
export class OpenClawRuntimeCapabilities{deps;constructor(t){this.deps=t}async execute(t,e,i){const s=t;if(s.name.startsWith("session."))return await this.deps.sessions.execute(s,e,i);if(s.name.startsWith("skills."))return await this.deps.skills.execute(s,e,i);if(s.name.startsWith("mcp."))return await this.deps.mcp.execute(s,e,i);if(s.name.startsWith("terminal."))return await this.deps.terminal.execute(s,e,i);if(s.name.startsWith("canvas."))return await this.deps.canvas.execute(s,e,i);if(s.name.startsWith("cron."))return await this.deps.cron.execute(s,e,i);const n=this.requireGateway();switch(s.name){case"models.list":return normalizeModels(await n.request("models.list",{}),await n.request("agents.list",{}));case"agent.listProjection":return await this.listAgents(n)}throw new Error(`canonical capability has no owner: ${s.name}`)}async cancel(t){for(const e of[this.deps.sessions,this.deps.skills,this.deps.mcp,this.deps.terminal,this.deps.canvas,this.deps.cron]){const i=await(e.cancel?.(t));if("cancel_requested"===i)return i}return"not_cancellable"}async afterCommit(t,e){const i=t.name.startsWith("session.")?this.deps.sessions:t.name.startsWith("skills.")?this.deps.skills:t.name.startsWith("mcp.")?this.deps.mcp:t.name.startsWith("terminal.")?this.deps.terminal:t.name.startsWith("canvas.")?this.deps.canvas:t.name.startsWith("cron.")?this.deps.cron:void 0;await(i?.afterCommit?.(t,e))}async projectCommitted(t,e,i){const s=t.name.startsWith("session.")?this.deps.sessions:t.name.startsWith("skills.")?this.deps.skills:t.name.startsWith("mcp.")?this.deps.mcp:t.name.startsWith("terminal.")?this.deps.terminal:t.name.startsWith("canvas.")?this.deps.canvas:t.name.startsWith("cron.")?this.deps.cron:void 0,n=s?.projectCommitted;return!!n&&(await n.call(s,t,e,i)??!0)}requireGateway(){const t=this.deps.getGateway();if(!t?.isConnected)throw new Error("Gateway not connected");return t}async listAgents(t){const e=await t.request("agents.list",{}),i=[];for(const t of e?.agents??[]){const e=t.id?.trim();e&&i.push({runtimeAgentUid:await this.deps.identities.resolveOrCreate(e),runtimeLocalKey:e,name:t.identity?.name||t.name||e,...t.identity?.emoji?{emoji:t.identity.emoji}:{},...t.identity?.avatarUrl||t.identity?.avatar?{avatarUrl:t.identity.avatarUrl||t.identity.avatar}:{}})}return{...e?.defaultId?{defaultRuntimeLocalKey:e.defaultId}:{},agents:i}}}export function reasoningOptionsByModel(t){const e=t,i=[...(e?.agents??[]).flatMap(t=>t&&"object"==typeof t?[t]:[])].sort((t,i)=>Number(t.id===e?.defaultId)-Number(i.id===e?.defaultId)),s=new Map;for(const t of i){const e=t.model,i=e&&"object"==typeof e?e.primary:void 0,n=thinkingProfile(t.thinkingLevels,t.thinkingDefault);"string"==typeof i&&i.trim()&&n&&s.set(catalogModelKey(i),n)}return s}function catalogModelKey(t){const e=t.trim();return e.startsWith("arouter/")?e.slice(8):e}function thinkingProfile(t,e){if(!Array.isArray(t))return;const i=t.flatMap(t=>{if(!t||"object"!=typeof t)return[];const e=t.id;if("string"!=typeof e||!e.trim())return[];const i=t.label;return[{id:e,..."string"==typeof i&&i?{label:i}:{}}]});if(!i.length)return;const s="string"==typeof e&&i.some(t=>t.id===e)?e:null;return{levels:i,defaultLevel:s}}function normalizeModels(t,e){const i=reasoningOptionsByModel(e);return{models:(t?.models??[]).flatMap(t=>{if(!t||"object"!=typeof t)return[];const e=t,s="string"==typeof e.id?e.id:"";if(!s)return[];const n="string"==typeof e.provider?e.provider:s.split("/")[0]||"unknown",a=Array.isArray(e.input)?e.input.filter(t=>"text"===t||"image"===t||"document"===t):void 0,r=e.pricing&&"object"==typeof e.pricing?e.pricing:void 0,o=i.get(catalogModelKey(s));return[{id:s,name:"string"==typeof e.name?e.name:s,provider:n,..."number"==typeof e.contextWindow?{contextWindow:e.contextWindow}:{},..."boolean"==typeof e.reasoning?{reasoning:e.reasoning}:{},...o?{reasoningOptions:o}:{},...a?.length?{input:a}:{},..."string"==typeof r?.prompt&&"string"==typeof r.completion?{pricing:{prompt:r.prompt,completion:r.completion}}:{},..."number"==typeof e.completionMultiplier?{completionMultiplier:e.completionMultiplier}:{}}]})}}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{AROUTER_PROVIDER_ID}from"../media/arouter-config.js";let snapshot;export function setOpenClawConfigSnapshot(t){snapshot=t}function listedModelIds(t){if(!t||!Array.isArray(t.models))return[];const o=[];for(const e of t.models){if("string"==typeof e){const t=e.trim();t&&o.push(t);continue}if(e&&"string"==typeof e.id){const t=e.id.trim();t&&o.push(t)}}return o}function primaryProvider(t){const o=t?.agents?.defaults?.model?.primary;if("string"!=typeof o)return;const e=o.indexOf("/");return e>0?o.slice(0,e):void 0}export function createSessionModelRefs(t=snapshot){const o=t?.models?.providers??{},e=Object.keys(o).filter(t=>null!=o[t]),n=new Map;for(const t of e)for(const e of listedModelIds(o[t]))n.set(e,n.has(e)?void 0:t);const r=primaryProvider(t),s=r&&e.includes(r)?r:1===e.length?e[0]:AROUTER_PROVIDER_ID,i=e.includes(AROUTER_PROVIDER_ID)?e:[...e,AROUTER_PROVIDER_ID],f=t=>i.find(o=>t.startsWith(`${o}/`));return{toOpenClaw(t){return f(t)?t:`${o=t,n.get(o)??s}/${t}`;var o},toPlatform(t){const o=f(t);return o?t.slice(o.length+1):t}}}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{existsSync,readFileSync}from"node:fs";import{isAbsolute,join}from"node:path";import{resolveOpenClawStateDir}from"./openclaw-state-dir.js";
|
|
1
|
+
import{existsSync,readFileSync}from"node:fs";import{isAbsolute,join}from"node:path";import{resolveOpenClawStateDir}from"./openclaw-state-dir.js";const DEFAULT_POLL_INTERVAL_MS=250,DEFAULT_QUIET_CONFIRMATION_MS=750;export class OpenClawSessionWriteQuiescence{logger;stateDir;defaultAgentId;pollIntervalMs;quietConfirmationMs;now;sleep;constructor(e,s={}){this.logger=e,this.stateDir=s.stateDir??resolveOpenClawStateDir(),this.defaultAgentId=s.agentId??"main",this.pollIntervalMs=s.pollIntervalMs??250,this.quietConfirmationMs=s.quietConfirmationMs??750,this.now=s.now??(()=>Date.now()),this.sleep=s.sleep??(e=>new Promise(s=>{const t=setTimeout(s,e);t.unref?.()}))}async waitForQuiescence(e,s){const t=this.now();let i,o,n;for(;this.now()-t<s;){const s=n??this.resolveLockPath(e);if(s){if(o=s,n=s,existsSync(s))i=void 0;else if(i??=this.now(),this.now()-i>=this.quietConfirmationMs)return{quiescent:!0,reason:"unlocked",lockPath:s,elapsedMs:this.now()-t};await this.sleep(this.pollIntervalMs)}else await this.sleep(this.pollIntervalMs)}return this.logger.warn?.(`[im] session writer did not become quiescent sessionKey=${e} timeout_ms=${s}`),{quiescent:!1,reason:o?"timeout":"session_unresolved",...o?{lockPath:o}:{},elapsedMs:this.now()-t}}resolveLockPath(e){const s=/^agent:([^:]+):/.exec(e)?.[1]??this.defaultAgentId,t=join(this.stateDir,"agents",s,"sessions"),i=join(t,"sessions.json");if(!existsSync(i))return;let o;try{const s=JSON.parse(readFileSync(i,"utf8"));if(!s||"object"!=typeof s)return;const t=s[e];t&&"object"==typeof t&&(o=t)}catch(s){return void this.logger.debug?.(`[im] session index read deferred sessionKey=${e} error=${s instanceof Error?s.message:String(s)}`)}if(!o)return;const n="string"==typeof o.sessionFile?o.sessionFile.trim():"",r="string"==typeof o.sessionId?o.sessionId.trim():"",l=n?isAbsolute(n)?n:join(t,n):r?join(t,`${r}.jsonl`):"";return l?`${l}.lock`:void 0}}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{enableDeepseekReasoningStream}from"./deepseek-reasoning-stream.js";const DEFAULT_ORIGINATING_CHANNEL="beeos-claw",DEFAULT_ORIGINATING_TARGET="main",DEFAULT_ORIGINATING_ACCOUNT_ID="main",isRecord=e=>"object"==typeof e&&null!==e&&!Array.isArray(e),assertKnownReplyProtocol=e=>{if("acp"!==e&&"im"!==e)throw new Error(`unsupported reply route protocol=${e}`)},normalizeMediaUrls=e=>{if(!Array.isArray(e))return;const o=e.filter(e=>"string"==typeof e&&e.length>0);return o.length>0?o:void 0};export const hasRuntimeReplyApi=e=>"function"==typeof e?.channel?.reply?.dispatchReplyFromConfig;export const buildFinalizedMsgContext=(e,o)=>{assertKnownReplyProtocol(o.protocol);const r=o.originatingChannel??"beeos-claw",
|
|
1
|
+
import{enableDeepseekReasoningStream}from"./deepseek-reasoning-stream.js";const DEFAULT_ORIGINATING_CHANNEL="beeos-claw",DEFAULT_ORIGINATING_TARGET="main",DEFAULT_ORIGINATING_ACCOUNT_ID="main",isRecord=e=>"object"==typeof e&&null!==e&&!Array.isArray(e),assertKnownReplyProtocol=e=>{if("acp"!==e&&"im"!==e)throw new Error(`unsupported reply route protocol=${e}`)},normalizeMediaUrls=e=>{if(!Array.isArray(e))return;const o=e.filter(e=>"string"==typeof e&&e.length>0);return o.length>0?o:void 0};export const hasRuntimeReplyApi=e=>"function"==typeof e?.channel?.reply?.dispatchReplyFromConfig;export const buildFinalizedMsgContext=(e,o)=>{assertKnownReplyProtocol(o.protocol);const r=o.originatingChannel??"beeos-claw",t=o.originatingTo??"main",n=o.accountId??"main",i=normalizeMediaUrls(e.attachments);return{Body:e.message,BodyForAgent:e.agentMessage,From:`${r}:${t}`,To:t,SessionKey:o.sessionId,Surface:r,Provider:r,OriginatingChannel:r,OriginatingTo:t,AccountId:n,CommandAuthorized:!0,...i?{MediaUrls:i}:{},...e.mediaPaths?.length?{MediaPaths:e.mediaPaths,MediaTypes:e.mediaTypes}:{}}};export const resolveReplySinkForRoute=(e,o)=>{assertKnownReplyProtocol(e.protocol);const r=o[e.protocol];if(!r)throw new Error(`runtime reply sink unavailable protocol=${e.protocol} requestId=${e.requestId}`);return r};const resolveRuntimeDispatchConfig=(e,o)=>{try{const o=e.config?.loadConfig?.();if(isRecord(o))return o}catch{}return o};export const dispatchRuntimeReplyWithRoute=async e=>{const o=e.runtime.channel?.reply?.dispatchReplyFromConfig;if("function"!=typeof o)throw new Error(`runtime reply API unavailable protocol=${e.replyRoute.protocol} requestId=${e.replyRoute.requestId}`);const r=resolveReplySinkForRoute(e.replyRoute,e.replySinks),t=enableDeepseekReasoningStream(resolveRuntimeDispatchConfig(e.runtime,e.pluginConfig)),n=o({ctx:buildFinalizedMsgContext(e.preparedPrompt,e.replyRoute),cfg:t,dispatcher:r.dispatcher,replyOptions:r.replyOptions});return e.onDispatchInvoked?.(n),n};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{createHash,randomUUID}from"node:crypto";import{mkdir,writeFile}from"node:fs/promises";import{homedir}from"node:os";import path,{join,relative,resolve}from"node:path";import{RUNTIME_HASH_DOMAINS,RUNTIME_METHOD_PARITY_MANIFEST,runtimeHashPreimage}from"@beeos-ai/beeos-types/runtime";import{reduceRuntimeRecoveryPolicy}from"@beeos-ai/beeos-types/runtime/internal";import{assertTrustedCoreExecutionAttempt}from"./core-execution-attempt-context.js";import{InMemoryRuntimeCommandJournal,RuntimeCommandJournalConflictError,runtimeCommandIsReplayableQuery,runtimeCommandResultHash}from"./runtime-command-journal.js";import{RuntimeProjectionTransportError}from"./agent-projection-worker.js";import{ImHotPathAbortedError}from"./im-hot-path-deferral.js";import{prepareOpenClawToolCatalog}from"./prepare-openclaw-tool-catalog.js";const DEFAULT_AGENT_ID="main",AGENT_VALID_ID_RE=/^[a-z0-9][a-z0-9_-]{0,63}$/i,AGENT_INVALID_CHARS_RE=/[^a-z0-9_-]+/g;export function normalizeRuntimeAgentLocalKey(e){const t=(e??"").trim();return t?AGENT_VALID_ID_RE.test(t)?t.toLowerCase():t.toLowerCase().replace(AGENT_INVALID_CHARS_RE,"-").replace(/^-+/,"").replace(/-+$/,"").slice(0,64)||"main":"main"}export async function awaitRuntimeProjection(e){const t=e;return await(t.awaitProjection?.()??Promise.resolve(e.getSnapshot().projection))}export function hasEffectReceiptHandoff(e){const t=e;return t.effectCommitted instanceof Promise&&"function"==typeof t.acknowledgeEffectReceipt}export class RuntimeCommandError extends Error{code;param;constructor(e,t,r,i){super(t,i),this.code=e,this.param=r,this.name="RuntimeCommandError"}}const emptyProgress=async function*(){};function deferred(){let e;return{promise:new Promise(t=>{e=t}),resolve:e}}async function waitForEffectReceipt(e,t){if(!t)return await e;if(t.aborted)throw new RuntimeCommandError("OUTCOME_UNKNOWN","MCP effect receipt was not durably published before the Service attempt ended");await new Promise((r,i)=>{const a=()=>i(new RuntimeCommandError("OUTCOME_UNKNOWN","MCP effect receipt was not durably published before the Service attempt ended"));t.addEventListener("abort",a,{once:!0}),e.then(()=>{t.removeEventListener("abort",a),r()},e=>{t.removeEventListener("abort",a),i(e)})})}export class OpenClawRuntimeCommandDispatcher{deps;logger;getOpenclawHome;createAgentId;journal;capabilities;constructor(e){this.deps=e,this.logger=e.logger??{},this.getOpenclawHome=e.getOpenclawHome??(()=>process.env.OPENCLAW_HOME||join(homedir(),".openclaw")),this.createAgentId=e.createAgentId??(()=>`agent-${randomUUID().replace(/-/g,"").slice(0,12)}`),this.journal=e.journal??new InMemoryRuntimeCommandJournal,this.capabilities=e.capabilities}setCapabilities(e){this.capabilities=e}dispatch(e,t){const r=e;assertTrustedCoreExecutionAttempt(t,{operationId:r.context.operationId,runtimeEpoch:r.context.dispatchEpoch});let i,a,n,o="running";const s=deferred(),c=deferred();let m;const d="service"===t.origin&&("mcp.set"===r.name||"mcp.unset"===r.name),p=this.executeWithLease(r,t,(e,t)=>{o="committed",m={operationId:r.context.operationId,method:r.name,record:t},s.resolve({...m,result:e,committedAt:t.updatedAt})},async()=>{d?await waitForEffectReceipt(c.promise,t.abortSignal):"acp"!==t.origin||"mcp.set"!==r.name&&"mcp.unset"!==r.name||!m?.record.mcpDesiredStateCommit||(await this.journal.releaseMcpLiveEffect({operationId:r.context.operationId,source:"acp_local"}),await this.journal.promoteMcpDesiredStateActive({operationId:r.context.operationId,source:"acp_local"}))},d,e=>{i=e},e=>{a=e}).then(e=>(o="committed",e),e=>{throw o=e instanceof RuntimeCommandError&&("OUTCOME_UNKNOWN"===e.code||"INSTANCE_EPOCH_MISMATCH"===e.code)?"outcome_unknown":"failed",s.resolve(void 0),e});return{operationId:r.context.operationId,getSnapshot:()=>({effectState:o,...i?{projection:i}:{}}),completion:p,progress:emptyProgress(),effectCommitted:s.promise,acknowledgeEffectReceipt:async e=>{if(!m||e.operationId!==m.operationId||e.method!==m.method)throw new RuntimeCommandError("OUTCOME_UNKNOWN","MCP effect receipt does not match this operation");if(!/^[a-f0-9]{64}$/.test(e.effectRecordHash))throw new RuntimeCommandError("OUTCOME_UNKNOWN","MCP effect receipt hash is invalid");await this.journal.releaseMcpLiveEffect({operationId:e.operationId,source:"service",effectRecordHash:e.effectRecordHash}),c.resolve()},acknowledgeTerminalReceipt:async e=>{if(!(d&&m&&e.operationId===m.operationId&&e.method===m.method&&/^[a-f0-9]{64}$/.test(e.effectRecordHash)&&e.terminalMessageId))throw new RuntimeCommandError("OUTCOME_UNKNOWN","MCP terminal receipt does not match this operation");await this.journal.promoteMcpDesiredStateActive({operationId:e.operationId,source:"service",effectRecordHash:e.effectRecordHash,terminalMessageId:e.terminalMessageId});const n=await this.requireReleasedRecord(e.operationId);if(void 0===n.result)throw new RuntimeCommandError("OUTCOME_UNKNOWN","active MCP desired state lost its committed result");await this.afterCommittedEffect(r,n.result,n,t,e=>{i=e},e=>{a=e})},cancel:async()=>"running"===o?{status:"cancel_requested"===await(this.capabilities?.cancel?.(r.context.operationId))?"cancel_requested":"not_cancellable"}:{status:"already_terminal"},awaitProjection:async()=>a?(n??=a(),await n):i}}async executeWithLease(e,t,r,i,a,n,o){return await this.withExclusiveLifecycle(e,t.abortSignal,()=>this.executeClaimedLifecycle(e,t,r,i,a,n,o))}async executeClaimedLifecycle(e,t,r,i,a,n,o){const s=await this.deps.leases.claim(e.context.operationId,e.context.dispatchEpoch);if(s.runtimeEpoch!==e.context.dispatchEpoch)throw await this.deps.leases.release(s),new RuntimeCommandError("INSTANCE_EPOCH_MISMATCH","runtime operation lease epoch mismatch");const c=this.deps.leases.startHeartbeat(s);try{if(runtimeCommandIsReplayableQuery(e.name))return await this.executeReplayableQuery(e,s,t,r,i);let c;await this.recoverInterruptedCreates(e.context.operationId);try{c=await this.journal.prepare({operationId:e.context.operationId,command:e.name,paramsHash:e.context.paramsHash,runtimeEpoch:s.runtimeEpoch})}catch(e){if(e instanceof RuntimeCommandJournalConflictError)throw new RuntimeCommandError("OUTCOME_UNKNOWN",e.message,void 0,{cause:e});throw e}if(("effect_committed"===c.phase||"projection_committed"===c.phase)&&void 0!==c.result){if(r(c.result,c),await i(),c=await this.requireReleasedRecord(c.operationId),void 0===c.result)throw new RuntimeCommandError("OUTCOME_UNKNOWN","committed result disappeared from command journal");return a||await this.afterCommittedEffect(e,c.result,c,t,n,o),c.result}if(("effect_committed"===c.phase||"projection_committed"===c.phase)&&"hash_only"===c.resultReceipt?.policy&&runtimeCommandIsReplayableQuery(e.name)){const a=await this.execute(e,s,t);if(runtimeCommandResultHash(a)!==c.resultReceipt.resultHash)throw new RuntimeCommandError("OUTCOME_UNKNOWN","redacted committed query result changed");return r(a,c),await i(),a}if(("effect_committed"===c.phase||"projection_committed"===c.phase)&&"hash_only"===c.resultReceipt?.policy){if(r(void 0,c),await i(),c=await this.requireReleasedRecord(c.operationId),!a){if(isRuntimeAgentMutation(e)&&(c.agentProjectionIntent||c.agentProjectionPreparationFailure)){const r=agentProjectionRecoveryResult(c.agentProjectionIntent??c.agentProjectionPreparationFailure);return await this.afterCommittedEffect(e,r,c,t,n,o),r}await this.runAfterEffect(e,void 0,c,t,n)}throw new RuntimeCommandError("OUTCOME_UNKNOWN","committed mutation result is redacted")}if("effect_committed"===c.phase||"projection_committed"===c.phase)throw new RuntimeCommandError("OUTCOME_UNKNOWN","committed result receipt is unavailable");if("outcome_unknown"===c.phase)throw new RuntimeCommandError("OUTCOME_UNKNOWN","previous execution outcome is unknown");if("started"===c.phase&&isRuntimeAgentMutation(e)){let a;try{a=await this.recoverStartedAgentMutation(e,s,t)}catch(e){throw await this.markJournalUnknown(c),e instanceof RuntimeCommandError?e:new RuntimeCommandError("OUTCOME_UNKNOWN",`agent desired-state recovery could not prove its effect: ${errorText(e)}`,void 0,{cause:e})}const m=this.deps.projection?await this.prepareAgentProjectionCommit(e,a,t):void 0,d=await this.journal.transition({operationId:c.operationId,expectedRevision:c.revision,phase:"effect_committed",result:a,...m?{agentProjection:m}:{}});return r(a,d),await i(),await this.afterCommittedEffect(e,a,d,t,n,o),this.scheduleJournalCompaction(),a}if("started"===c.phase&&"outcome_unknown"===this.recoveryDecision(e.name,c.phase))throw await this.markJournalUnknown(c),new RuntimeCommandError("OUTCOME_UNKNOWN","imperative action crossed its execution boundary");if("failed"===c.phase)throw new RuntimeCommandError("EXECUTION_FAILED","previous execution failed");"prepared"===c.phase&&(c=await this.journal.transition({operationId:c.operationId,expectedRevision:c.revision,phase:"started"}));let m=!1;try{const d=await this.execute(e,s,t);m=!0;const p=isRuntimeAgentMutation(e)&&this.deps.projection?await this.prepareAgentProjectionCommit(e,d,t):void 0;let u;try{const e=await this.requireReleasedRecord(c.operationId);u=await this.journal.transition({operationId:e.operationId,expectedRevision:e.revision,phase:"effect_committed",result:d,...p?{agentProjection:p}:{}})}catch(e){throw new RuntimeCommandError("OUTCOME_UNKNOWN",`effect completed before its journal commit: ${errorText(e)}`,void 0,{cause:e})}return r(d,u),await i(),u=await this.requireReleasedRecord(u.operationId),a||await this.afterCommittedEffect(e,d,u,t,n,o),this.scheduleJournalCompaction(),d}catch(t){if(m)throw t;const r=t instanceof RuntimeCommandError&&("OUTCOME_UNKNOWN"===t.code||"INSTANCE_EPOCH_MISMATCH"===t.code);if(r&&"clear-saga"===recoveryMetadata(e.name).recoveryClass)throw t;try{await this.journal.transition({operationId:c.operationId,expectedRevision:c.revision,phase:r?"outcome_unknown":"failed",errorCode:r?"OUTCOME_UNKNOWN":"EXECUTION_FAILED"})}catch(e){throw new RuntimeCommandError("OUTCOME_UNKNOWN",`command and journal outcome unknown: ${errorText(e)}`,void 0,{cause:t})}throw t}}finally{try{await c.stop(),await this.deps.leases.release(s)}catch(e){throw new RuntimeCommandError("OUTCOME_UNKNOWN",`runtime operation lease finalization unknown: ${errorText(e)}`,void 0,{cause:e})}}}async executeReplayableQuery(e,t,r,i,a){const n=await this.execute(e,t,r);return i(n,{operationId:e.context.operationId,command:e.name,paramsHash:e.context.paramsHash,runtimeEpoch:t.runtimeEpoch,phase:"effect_committed",revision:1,updatedAt:(new Date).toISOString(),resultReceipt:{policy:"hash_only",resultHash:runtimeCommandResultHash(n),redacted:{kind:"query_result",command:e.name}}}),await a(),await this.runPostCommit(e,n),n}scheduleJournalCompaction(){this.journal.compact().catch(e=>this.logger.error?.(`[runtime-journal] event=checkpoint_failed trigger=effect_committed error=${errorText(e)}`))}async execute(e,t,r){switch(e.name){case"agent.create":return await this.createAgent(e,t,r);case"agent.update":return await this.updateAgent(e);case"agent.delete":return await this.deleteAgent(e,t);case"agent.applyTemplate":return await this.applyTemplate(e,t,r)}if(!this.capabilities)throw new RuntimeCommandError("INVALID_PARAMS",`Unsupported runtime command: ${e.name}`);return await this.capabilities.execute(e,t,r)}async recoverStartedAgentMutation(e,t,r){if("agent.create"===e.name||"agent.applyTemplate"===e.name){let i=await this.bindCreateOperation(e,t,!1);if(i||(i=await this.findCreateRecoveryIdentity(e,!1),"allocated"===i.state&&void 0===i.createOperation?.paramsHash&&await this.bindCreateOperation(e,t,!1),i=await this.findCreateRecoveryIdentity(e)),"active"===i.state)return await this.recoverActiveCreateEffect(i);if(await this.runtimeAgentExists(i.observedRuntimeLocalKey??i.runtimeLocalKey)){const a={runtimeLocalKey:i.observedRuntimeLocalKey??i.runtimeLocalKey,workspacePath:join(this.getOpenclawHome(),`workspace-${i.runtimeLocalKey}`)};return"agent.create"===e.name?await this.completeCreatedAgent(e,t,r,a):await this.completeTemplateAgent(e,t,r,a)}return await this.execute(e,t,r)}const i=e.context.agent,a=await this.runtimeAgentExists(i.runtimeLocalKey);if("agent.delete"===e.name&&!a)return await this.deps.identities.commitDeleteTombstone(i.runtimeAgentUid,i.runtimeLocalKey,t),{runtimeAgentUid:i.runtimeAgentUid,runtimeLocalKey:i.runtimeLocalKey};if("agent.update"===e.name&&!a)throw new RuntimeCommandError("OUTCOME_UNKNOWN","update recovery target is absent");return await this.execute(e,t,r)}async runtimeAgentExists(e){const t=await this.requireGateway().request("agents.list",{});if(!Array.isArray(t?.agents))throw new RuntimeCommandError("OUTCOME_UNKNOWN","runtime agent directory query is incomplete");return t.agents.some(t=>null!==t&&"object"==typeof t&&(t.id===e||t.name===e))}async findCreateRecoveryIdentity(e,t=!0){const r={operationId:e.context.operationId,command:e.name,runtimeEpoch:e.context.dispatchEpoch,paramsHash:e.context.paramsHash,runtimeAgentUid:e.params.runtimeAgentUid},i=void 0!==this.deps.identities.findCreateOperation,a=i?await this.deps.identities.findCreateOperation(r):(await this.deps.identities.pendingAllocations()).filter(e=>e.operationId===r.operationId&&e.command===r.command&&e.runtimeEpoch===r.runtimeEpoch&&e.runtimeAgentUid===r.runtimeAgentUid);if(1!==a.length)throw new RuntimeCommandError("OUTCOME_UNKNOWN","create recovery identity binding is missing or ambiguous");const n=a[0];if(i&&(!n.createOperation||t&&n.createOperation.paramsHash!==r.paramsHash||n.createOperation.operationId!==r.operationId||n.createOperation.command!==r.command||n.createOperation.runtimeEpoch!==r.runtimeEpoch))throw new RuntimeCommandError("OUTCOME_UNKNOWN","create recovery identity hash binding conflicts");return n}async bindCreateOperation(e,t,r=!0){if(!this.deps.identities.findCreateOperation||!this.deps.identities.bindCreateOperation)return;const i={operationId:e.context.operationId,command:e.name,runtimeEpoch:e.context.dispatchEpoch,paramsHash:e.context.paramsHash,runtimeAgentUid:e.params.runtimeAgentUid};try{let a=await this.deps.identities.findCreateOperation(i);if(0===a.length){if(!r)throw new RuntimeCommandError("OUTCOME_UNKNOWN","create recovery identity binding is missing");if((await this.deps.identities.allocateCreate(t,e.name,e.params.runtimeAgentUid)).runtimeAgentUid!==e.params.runtimeAgentUid)throw new RuntimeCommandError("OUTCOME_UNKNOWN","create allocation UID does not match the canonical command");a=await this.deps.identities.findCreateOperation(i)}if(1!==a.length)throw new RuntimeCommandError("OUTCOME_UNKNOWN","create identity binding is missing or ambiguous");const n=a[0],o=await this.deps.identities.bindCreateOperation(n.runtimeAgentUid,n.runtimeLocalKey,{operationId:e.context.operationId,command:e.name,runtimeEpoch:e.context.dispatchEpoch,paramsHash:e.context.paramsHash});if(!o.createOperation||o.runtimeAgentUid!==e.params.runtimeAgentUid||o.createOperation.operationId!==i.operationId||o.createOperation.command!==i.command||o.createOperation.runtimeEpoch!==i.runtimeEpoch||o.createOperation.paramsHash!==i.paramsHash)throw new Error("create operation identity binding changed during compare-and-set");if("tombstoned"===o.state)throw new Error("create operation identity is tombstoned");return o}catch(e){if(e instanceof RuntimeCommandError)throw e;throw new RuntimeCommandError("OUTCOME_UNKNOWN",`create operation identity binding is corrupt: ${errorText(e)}`,void 0,{cause:e})}}async recoverActiveCreateEffect(e){const t=e.observedRuntimeLocalKey??e.runtimeLocalKey;if(!await this.runtimeAgentExists(t))throw new RuntimeCommandError("OUTCOME_UNKNOWN","active create identity is absent from the runtime directory");return{runtimeAgentUid:e.runtimeAgentUid,runtimeLocalKey:t,workspacePath:join(this.getOpenclawHome(),`workspace-${e.runtimeLocalKey}`)}}async markJournalUnknown(e){try{await this.journal.transition({operationId:e.operationId,expectedRevision:e.revision,phase:"outcome_unknown",errorCode:"OUTCOME_UNKNOWN"})}catch{}}async requireReleasedRecord(e){const t=await this.journal.get(e);if(!t)throw new RuntimeCommandError("OUTCOME_UNKNOWN","command journal record disappeared");return t}async afterCommittedEffect(e,t,r,i,a,n){if(!isRuntimeAgentMutation(e))return void await this.runAfterEffect(e,t,r,i,a);const o=await this.prepareAgentProjection(e,t,r,i,a);o&&(n(o),"acp"===i.origin&&o().catch(e=>this.logger.warn?.(`[runtime-core] deferred ACP agent projection failed: ${errorText(e)}`)))}async prepareAgentProjectionCommit(e,t,r){const i=agentProjectionRecoverySummary(e,t,r);try{const r=await this.deps.projection.prepareCommittedMutation({command:e,result:t,...i.expectedPlatformAgentId?{expectedPlatformAgentId:i.expectedPlatformAgentId}:{}});return Object.freeze({...r,...i})}catch(e){const t=!(e instanceof RuntimeProjectionTransportError)||e.retryable;return this.logger.warn?.(`[runtime-core] agent projection preparation ${t?"deferred":"blocked"}: ${errorText(e)}`),Object.freeze({...i,state:t?"pending":"blocked",errorKind:t?"snapshot_retryable":"snapshot_nonretryable"})}}async prepareAgentProjection(e,t,r,i,a){if("projection_committed"===r.phase)return a(r.projectionReceipt?.snapshot??Object.freeze({state:"synced",revision:`${Math.max(0,r.revision-1)}`})),r.agentProjectionIntent&&await this.compactAgentProjectionReference(r.agentProjectionIntent),void await this.runPostCommit(e,t);if("effect_committed"!==r.phase||!this.deps.projection)return;let n=r,o=n.agentProjectionIntent;const s=n.agentProjectionPreparationFailure;if(o&&"blocked"===n.agentProjectionTerminal?.state)return a(Object.freeze({state:"blocked",revision:n.agentProjectionTerminal.revision})),await this.compactAgentProjectionReference(o),void await this.runPostCommit(e,t);let c;return o&&a(Object.freeze({state:"pending",revision:o.projectionRevision})),async()=>(c??=(async()=>{if(!o){if(!s||"blocked"===s.state)return Object.freeze({state:"blocked",revision:`${Math.max(0,n.revision)}`});const t=await this.prepareAgentProjectionCommit(e,agentProjectionRecoveryResult(s),i);if(!isProjectionIntent(t))return"blocked"===t.state?Object.freeze({state:"blocked",revision:`${Math.max(0,n.revision)}`}):Object.freeze({state:"pending",revision:`${Math.max(0,n.revision)}`});o=t;try{n=await this.journal.recordAgentProjectionIntent({operationId:n.operationId,expectedRevision:n.revision,intent:o})}catch(e){return void this.logger.warn?.(`[runtime-core] agent projection reservation recovery deferred: ${errorText(e)}`)}a(Object.freeze({state:"pending",revision:o.projectionRevision}))}const r=Object.freeze({state:"pending",revision:o.projectionRevision});let c;try{c=await this.deps.projection.applyCommittedMutation(o)}catch(e){this.logger.warn?.(`[runtime-core] agent projection receipt deferred: ${errorText(e)}`),c=r}if(c.revision!==o.projectionRevision&&(c=Object.freeze({state:"blocked",revision:o.projectionRevision})),"synced"===c.state)try{n=await this.journal.transition({operationId:n.operationId,expectedRevision:n.revision,phase:"projection_committed",projectionSnapshot:c}),await this.compactAgentProjectionReference(o)}catch(e){this.logger.warn?.(`[runtime-core] projection phase commit deferred: ${errorText(e)}`),c=Object.freeze({state:"pending",revision:o.projectionRevision})}if("blocked"===c.state)try{n=await this.journal.recordAgentProjectionBlocked({operationId:n.operationId,expectedRevision:n.revision,revision:o.projectionRevision}),await this.compactAgentProjectionReference(o)}catch(e){this.logger.warn?.(`[runtime-core] blocked projection terminal commit deferred: ${errorText(e)}`),c=r}return"synced"===c.state&&c.platformAgentId&&"projection_committed"===n.phase&&this.reportTemplateOriginBestEffort(e,c.platformAgentId),a(c),await this.runPostCommit(e,t),c})(),await c)}async compactAgentProjectionReference(e){if(this.deps.projection)try{await this.deps.projection.compactCommittedMutation(e)}catch(e){this.logger.warn?.(`[runtime-core] projection reference compaction deferred: ${errorText(e)}`)}}reportTemplateOriginBestEffort(e,t){if("agent.applyTemplate"!==e.name||!this.deps.templateOriginReporter)return;const r=e.params.templateId;r&&this.deps.templateOriginReporter.report({platformAgentId:t,templateId:r}).catch(e=>{this.logger.warn?.(`[runtime-core] active template origin report failed, client fallback remains: ${errorText(e)}`)})}async runAfterEffect(e,t,r,i,a){if("projection_committed"===r.phase)return a(r.projectionReceipt?.snapshot??Object.freeze({state:"synced",revision:`${Math.max(0,r.revision-1)}`})),void await this.runPostCommit(e,t);const n="effect_committed"===r.phase&&await this.runProjection(e,t,i),o=!0===n?Object.freeze({state:"synced",revision:`${r.revision}`}):n&&"object"==typeof n?Object.freeze({...n,revision:`${r.revision}`}):"session.setModel"===e.name||"session.setReasoning"===e.name?Object.freeze({state:"pending",revision:`${r.revision}`}):void 0;if("synced"===o?.state)try{const e=o;await this.journal.transition({operationId:r.operationId,expectedRevision:r.revision,phase:"projection_committed",projectionSnapshot:e}),a(o)}catch(e){return this.logger.warn?.(`[runtime-core] projection phase commit deferred: ${errorText(e)}`),void a(Object.freeze({revision:o.revision,state:"pending"}))}else o&&a(o);await this.runPostCommit(e,t)}async runPostCommit(e,t){if(this.capabilities?.afterCommit)try{await this.capabilities.afterCommit(e,t)}catch(e){this.logger.warn?.(`[runtime-core] post-commit action failed: ${errorText(e)}`)}}async runProjection(e,t,r){if(!recoveryMetadata(e.name).projectionRequired)return!1;try{return!isRuntimeAgentMutation(e)&&(await(this.capabilities?.projectCommitted?.(e,t,r))??!1)}catch(e){return this.logger.warn?.(`[runtime-core] agent projection deferred: ${errorText(e)}`),!1}}recoveryDecision(e,t){const r=recoveryMetadata(e);return reduceRuntimeRecoveryPolicy({...r,messageServicePhase:"queued",executionBoundaryCrossed:"prepared"!==t,lineageContinuity:"continuous",journalState:"available",journalPhase:t})}requireGateway(){const e=this.deps.getGateway();if(!e?.isConnected)throw new RuntimeCommandError("GATEWAY_NOT_CONNECTED","Gateway not connected");return e}exclusiveLifecycleReason(e){switch(e.name){case"agent.applyTemplate":return"applyTemplate";case"agent.create":return"createAgent";case"agent.delete":return"deleteAgent";case"agent.update":return"updateAgent";default:return}}async withExclusiveLifecycle(e,t,r){const i=this.exclusiveLifecycleReason(e);if(!i)return await r();if(this.deps.runExclusive)try{return await this.deps.runExclusive(i,r,t)}catch(e){if(e instanceof ImHotPathAbortedError)throw new RuntimeCommandError("INVALID_PARAMS",`${i} aborted while waiting for in-flight chat`);throw e}return await(this.deps.waitForChatIdle?.(i,t)),await r()}async createAgentShell(e,t,r,i,a,n){const o=this.requireGateway(),s=(await this.deps.identities.pendingAllocations()).find(t=>t.runtimeAgentUid===e),c=n??s?.runtimeLocalKey??this.createAgentId(),m=join(this.getOpenclawHome(),`workspace-${c}`);let d;try{if(a&&!this.deps.declareModel)throw new Error("OpenClaw model configuration runtime is unavailable");const e=a?await this.deps.declareModel(a):void 0;d=await o.request("agents.create",{name:c,workspace:m,...e?{model:e}:{},...i?.emoji?{emoji:i.emoji}:{},...i?.avatar?{avatar:i.avatar}:{}})}catch(r){if(isDeterministicAgentsCreateParamsRejection(r)&&(void 0!==n||void 0!==s))try{await this.deps.identities.tombstone(e,c,t)}catch(e){this.logger.warn?.(`[runtime-core] rejected-create allocation cleanup deferred: ${errorText(e)}`)}throw r}const p=d?.agentId??c,u=d?.workspace??m;if(await this.deps.identities.observeEffectKey(e,c,p,t),p!==c)throw new RuntimeCommandError("OUTCOME_UNKNOWN",`Create agent outcome unknown: gateway changed reserved local key to ${p}`);try{await o.request("agents.update",{agentId:p,name:r})}catch(e){this.logger.warn?.(`[runtime-core] display-name update failed agent=${p}: ${errorText(e)}`)}return{runtimeLocalKey:p,workspacePath:u}}async createAgent(e,t,r){const i=e.params.name.trim();if(!i)throw new RuntimeCommandError("INVALID_PARAMS","Missing agent name");try{const a=await this.bindCreateOperation(e,t);if("active"===a?.state)return await this.recoverActiveCreateEffect(a);const n=await this.createAgentShell(e.params.runtimeAgentUid,t,i,e.params.identity,e.params.defaultModelId,a?.runtimeLocalKey);return await this.completeCreatedAgent(e,t,r,n)}catch(e){throw this.executionError("Create agent",e)}}async updateAgent(e){const{runtimeAgentUid:t,runtimeLocalKey:r}=e.context.agent;if(!r.trim())throw new RuntimeCommandError("MISSING_PARAM","Missing agentId","agentId");const i=e.params,a=i.name?.trim(),n=i.defaultModelId,o=i.identity?.emoji?.trim(),s=i.identity?.avatar?.trim();if(!(a||n||o||s||void 0!==i.description||void 0!==i.version||void 0!==i.capabilities||void 0!==i.skills||void 0!==i.avatarUrl||void 0!==i.homepage||void 0!==i.status||void 0!==i.identity?.vibe))throw new RuntimeCommandError("INVALID_PARAMS","No updatable fields provided (name/model/identity)");try{if(await this.deps.identities.requireActive(t,r),n&&!this.deps.declareModel)throw new Error("OpenClaw model configuration runtime is unavailable");const e=n?await this.deps.declareModel(n):void 0;return await this.requireGateway().request("agents.update",{agentId:r,...a?{name:a}:{},...e?{model:e}:{},...o?{emoji:o}:{},...s?{avatar:s}:{},...i.identity?.vibe?{vibe:i.identity.vibe}:{},...void 0!==i.description?{description:i.description}:{},...void 0!==i.version?{version:i.version}:{},...void 0!==i.capabilities?{capabilities:i.capabilities}:{},...void 0!==i.skills?{skills:i.skills}:{},...void 0!==i.avatarUrl?{avatarUrl:i.avatarUrl}:{},...void 0!==i.homepage?{homepage:i.homepage}:{},...void 0!==i.status?{status:i.status}:{}}),{runtimeAgentUid:t,runtimeLocalKey:r}}catch(e){throw this.executionError("Update agent",e)}}async deleteAgent(e,t){const{runtimeAgentUid:r,runtimeLocalKey:i}=e.context.agent;if(!i.trim())throw new RuntimeCommandError("MISSING_PARAM","Missing agentId","agentId");if("main"===normalizeRuntimeAgentLocalKey(i))throw new RuntimeCommandError("INVALID_PARAMS",'"main" cannot be deleted; delete the instance instead');try{if("tombstoned"===await this.deps.identities.requireDeleteTarget(r,i))return{runtimeAgentUid:r,runtimeLocalKey:i};if(!await this.runtimeAgentExists(i))return await this.deps.identities.commitDeleteTombstone(r,i,t),{runtimeAgentUid:r,runtimeLocalKey:i};const a=requireCommittedOpenClawDeleteAck(await this.requireGateway().request("agents.delete",{agentId:i,deleteFiles:e.params.deleteFiles??!0}),i);try{await this.deps.identities.commitDeleteTombstone(r,i,t)}catch(e){throw new RuntimeCommandError("OUTCOME_UNKNOWN",`Delete agent outcome unknown: identity tombstone failed: ${errorText(e)}`,void 0,{cause:e})}return{runtimeAgentUid:r,runtimeLocalKey:i,...void 0===a.removedBindings?{}:{removedBindings:a.removedBindings}}}catch(e){throw this.executionError("Delete agent",e)}}async applyTemplate(e,t,r){const i=e.params.name.trim();if(!i)throw new RuntimeCommandError("INVALID_PARAMS","Missing agent name");this.requireGateway();try{const a=await this.bindCreateOperation(e,t);if("active"===a?.state)return await this.recoverActiveCreateEffect(a);const n=await r.resources.readVerifiedArtifact(e.params.artifact),o=await this.createAgentShell(e.params.runtimeAgentUid,t,i,e.params.identity,e.params.defaultModelId,a?.runtimeLocalKey);return await this.completeTemplateAgent(e,t,r,o,n)}catch(e){throw this.executionError("Apply template",e)}}async completeCreatedAgent(e,t,r,i){for(const t of e.params.bootstrapFiles??[]){if("IDENTITY.md"===t.name){this.logger.warn?.("[runtime-core] agent.create ignoring IDENTITY.md; identity is structured");continue}const e=await r.resources.readContent(t.contentRef);try{await this.requireGateway().request("agents.files.set",{agentId:i.runtimeLocalKey,name:t.name,content:e})}catch(e){this.logger.warn?.(`[runtime-core] bootstrap write failed file=${t.name}: ${errorText(e)}`)}}try{await this.deps.identities.activate(e.params.runtimeAgentUid,i.runtimeLocalKey,t)}catch(r){throw await this.compensateCreatedAgent(e.params.runtimeAgentUid,i.runtimeLocalKey,t,r,"Create agent"),new RuntimeCommandError("EXECUTION_FAILED",`Create agent failed after compensation: ${errorText(r)}`)}return await this.prepareAgentToolCatalog(i.runtimeLocalKey),{runtimeAgentUid:e.params.runtimeAgentUid,runtimeLocalKey:i.runtimeLocalKey,workspacePath:i.workspacePath}}async completeTemplateAgent(e,t,r,i,a){const n=a??await r.resources.readVerifiedArtifact(e.params.artifact);try{for(const[e,t]of n.workspaceFiles){const r=resolve(i.workspacePath,e),a=relative(i.workspacePath,r);if(""===a||a.startsWith("..")||path.isAbsolute(a))throw new Error(`refusing to write outside workspace: ${e}`);await(this.deps.mkdirWorkspace??mkdir)(path.dirname(r),{recursive:!0}),await(this.deps.writeWorkspaceFile??writeFile)(r,t)}await this.deps.identities.activate(e.params.runtimeAgentUid,i.runtimeLocalKey,t)}catch(r){throw await this.compensateCreatedAgent(e.params.runtimeAgentUid,i.runtimeLocalKey,t,r,"Apply template"),new RuntimeCommandError("EXECUTION_FAILED",`Apply template failed after compensation: ${errorText(r)}`,void 0,{cause:r})}return await this.prepareAgentToolCatalog(i.runtimeLocalKey),{runtimeAgentUid:e.params.runtimeAgentUid,runtimeLocalKey:i.runtimeLocalKey,workspacePath:i.workspacePath}}async prepareAgentToolCatalog(e){await prepareOpenClawToolCatalog({request:(e,t)=>this.requireGateway().request(e,t),agentId:e,mode:"catalog",keyKind:"boot",logger:this.logger})}executionError(e,t){return t instanceof RuntimeCommandError?t:(this.logger.error?.(`[runtime-core] ${e.toLowerCase()} failed: ${errorText(t)}`),new RuntimeCommandError("EXECUTION_FAILED",`${e} failed: ${errorText(t)}`,void 0,{cause:t}))}async compensateCreatedAgent(e,t,r,i,a){try{const i=this.requireGateway();requireCommittedOpenClawDeleteAck(await i.request("agents.delete",{agentId:t,deleteFiles:!0}),t),await this.deps.identities.tombstone(e,t,r)}catch(e){throw new RuntimeCommandError("OUTCOME_UNKNOWN",`${a} outcome unknown after ${errorText(i)}; compensation unproven: ${errorText(e)}`,void 0,{cause:e})}}async recoverInterruptedCreates(e){const t=await this.deps.identities.pendingAllocations();for(const r of t){if(r.operationId===e)continue;const t=await this.deps.leases.claimExpiredForRecovery(r.operationId);if(!t)continue;const i=this.deps.leases.startHeartbeat(t);try{await this.deps.identities.adoptRecoveryLease(r.runtimeAgentUid,r.runtimeLocalKey,t);const e=r.observedRuntimeLocalKey;if(!e){const e=r.createOperation?.operationId??r.operationId,i=await this.journal.get(e);if("failed"===i?.phase&&i.command===r.command&&!await this.runtimeAgentExists(r.runtimeLocalKey)){await this.deps.identities.tombstone(r.runtimeAgentUid,r.runtimeLocalKey,t);continue}throw new Error("no persisted gateway effect key; absence cannot be proven")}requireCommittedOpenClawDeleteAck(await this.requireGateway().request("agents.delete",{agentId:e,deleteFiles:!0}),e),await this.deps.identities.tombstone(r.runtimeAgentUid,r.runtimeLocalKey,t)}catch(e){throw new RuntimeCommandError("OUTCOME_UNKNOWN",`Interrupted ${r.command} recovery outcome unknown for operation ${r.operationId}: ${errorText(e)}`,void 0,{cause:e})}finally{let e;try{await i.stop()}catch(t){e=t}try{await this.deps.leases.release(t)}catch(t){e??=t}if(e)throw new RuntimeCommandError("OUTCOME_UNKNOWN",`Interrupted recovery lease finalization unknown: ${errorText(e)}`,void 0,{cause:e})}}}}function isRuntimeAgentMutation(e){return"agent.create"===e.name||"agent.update"===e.name||"agent.delete"===e.name||"agent.applyTemplate"===e.name}function servicePlatformAgentId(e){const t=e.verifiedMessageGrant?.claims;return"agent"===t?.scope?t.platformAgentId:void 0}function agentCoreResultHash(e,t){return createHash("sha256").update(runtimeHashPreimage(RUNTIME_HASH_DOMAINS.coreResult,{method:e.name.replace(".","/"),result:t})).digest("hex")}function agentProjectionRecoverySummary(e,t,r){const i="agent.update"===e.name||"agent.delete"===e.name?e.context.agent:createProjectionTarget(e,t),a=t;return Object.freeze({schemaVersion:1,operationId:e.context.operationId,method:e.name,runtimeEpoch:e.context.dispatchEpoch,runtimeAgentUid:i.runtimeAgentUid,runtimeLocalKey:i.runtimeLocalKey,coreResultHash:agentCoreResultHash(e,t),...servicePlatformAgentId(r)?{expectedPlatformAgentId:servicePlatformAgentId(r)}:{},..."agent.delete"===e.name&&Number.isSafeInteger(a?.removedBindings)?{removedBindings:a.removedBindings}:{}})}function createProjectionTarget(e,t){const r=t;if(!r||r.runtimeAgentUid!==e.params.runtimeAgentUid||"string"!=typeof r.runtimeLocalKey||!r.runtimeLocalKey)throw new RuntimeCommandError("OUTCOME_UNKNOWN","committed agent effect lacks a recoverable local identity");return{runtimeAgentUid:r.runtimeAgentUid,runtimeLocalKey:r.runtimeLocalKey}}function agentProjectionRecoveryResult(e){return Object.freeze({runtimeAgentUid:e.runtimeAgentUid,runtimeLocalKey:e.runtimeLocalKey,...void 0===e.removedBindings?{}:{removedBindings:e.removedBindings}})}function isProjectionIntent(e){return"projectionRevision"in e}function recoveryMetadata(e){const t=RUNTIME_METHOD_PARITY_MANIFEST.filter(t=>t.serviceAllowed&&t.canonicalCommand===e),r=t[0]?.recoveryClass;return r?{recoveryClass:r,projectionRequired:"mcp.set"===e||"mcp.unset"===e||t.some(e=>!0===e.projectionRequired)}:{recoveryClass:"imperative-ambiguous",projectionRequired:!1}}export function isDeterministicAgentsCreateParamsRejection(e){return e instanceof Error&&/^invalid agents\.create params\b/.test(e.message)}export function requireCommittedOpenClawDeleteAck(e,t){if(!e||"object"!=typeof e)throw new Error("gateway delete lacks the committed target acknowledgement");const r=e,i=Object.keys(r).every(e=>"ok"===e||"agentId"===e||"removedBindings"===e),a=void 0===r.removedBindings||Number.isSafeInteger(r.removedBindings)&&r.removedBindings>=0;if(!i||!0!==r.ok||r.agentId!==t||!a)throw new Error("gateway delete lacks the committed target acknowledgement");return{ok:!0,agentId:t,...void 0===r.removedBindings?{}:{removedBindings:r.removedBindings}}}function errorText(e){return e instanceof Error?e.message:String(e)}
|
|
1
|
+
import{createHash,randomUUID}from"node:crypto";import{mkdir,writeFile}from"node:fs/promises";import{homedir}from"node:os";import path,{join,relative,resolve}from"node:path";import{RUNTIME_HASH_DOMAINS,RUNTIME_METHOD_PARITY_MANIFEST,runtimeHashPreimage}from"@beeos-ai/beeos-types/runtime";import{reduceRuntimeRecoveryPolicy}from"@beeos-ai/beeos-types/runtime/internal";import{assertTrustedCoreExecutionAttempt}from"./core-execution-attempt-context.js";import{InMemoryRuntimeCommandJournal,RuntimeCommandJournalConflictError,runtimeCommandIsReplayableQuery,runtimeCommandResultHash}from"./runtime-command-journal.js";import{RuntimeProjectionTransportError}from"./agent-projection-worker.js";import{ImHotPathAbortedError}from"./im-hot-path-deferral.js";import{prepareOpenClawToolCatalog}from"./prepare-openclaw-tool-catalog.js";const DEFAULT_AGENT_ID="main",AGENT_VALID_ID_RE=/^[a-z0-9][a-z0-9_-]{0,63}$/i,AGENT_INVALID_CHARS_RE=/[^a-z0-9_-]+/g;export function normalizeRuntimeAgentLocalKey(e){const t=(e??"").trim();return t?AGENT_VALID_ID_RE.test(t)?t.toLowerCase():t.toLowerCase().replace(AGENT_INVALID_CHARS_RE,"-").replace(/^-+/,"").replace(/-+$/,"").slice(0,64)||"main":"main"}export async function awaitRuntimeProjection(e){const t=e;return await(t.awaitProjection?.()??Promise.resolve(e.getSnapshot().projection))}export function hasEffectReceiptHandoff(e){const t=e;return t.effectCommitted instanceof Promise&&"function"==typeof t.acknowledgeEffectReceipt}export class RuntimeCommandError extends Error{code;param;constructor(e,t,r,i){super(t,i),this.code=e,this.param=r,this.name="RuntimeCommandError"}}const emptyProgress=async function*(){};function deferred(){let e;return{promise:new Promise(t=>{e=t}),resolve:e}}async function waitForEffectReceipt(e,t){if(!t)return await e;if(t.aborted)throw new RuntimeCommandError("OUTCOME_UNKNOWN","MCP effect receipt was not durably published before the Service attempt ended");await new Promise((r,i)=>{const a=()=>i(new RuntimeCommandError("OUTCOME_UNKNOWN","MCP effect receipt was not durably published before the Service attempt ended"));t.addEventListener("abort",a,{once:!0}),e.then(()=>{t.removeEventListener("abort",a),r()},e=>{t.removeEventListener("abort",a),i(e)})})}export class OpenClawRuntimeCommandDispatcher{deps;logger;getOpenclawHome;createAgentId;journal;capabilities;constructor(e){this.deps=e,this.logger=e.logger??{},this.getOpenclawHome=e.getOpenclawHome??(()=>process.env.OPENCLAW_HOME||join(homedir(),".openclaw")),this.createAgentId=e.createAgentId??(()=>`agent-${randomUUID().replace(/-/g,"").slice(0,12)}`),this.journal=e.journal??new InMemoryRuntimeCommandJournal,this.capabilities=e.capabilities}setCapabilities(e){this.capabilities=e}dispatch(e,t){const r=e;assertTrustedCoreExecutionAttempt(t,{operationId:r.context.operationId,runtimeEpoch:r.context.dispatchEpoch});let i,a,n,o="running";const s=deferred(),c=deferred();let m;const d="service"===t.origin&&("mcp.set"===r.name||"mcp.unset"===r.name),p=this.executeWithLease(r,t,(e,t)=>{o="committed",m={operationId:r.context.operationId,method:r.name,record:t},s.resolve({...m,result:e,committedAt:t.updatedAt})},async()=>{d?await waitForEffectReceipt(c.promise,t.abortSignal):"acp"!==t.origin||"mcp.set"!==r.name&&"mcp.unset"!==r.name||!m?.record.mcpDesiredStateCommit||(await this.journal.releaseMcpLiveEffect({operationId:r.context.operationId,source:"acp_local"}),await this.journal.promoteMcpDesiredStateActive({operationId:r.context.operationId,source:"acp_local"}))},d,e=>{i=e},e=>{a=e}).then(e=>(o="committed",e),e=>{throw o=e instanceof RuntimeCommandError&&("OUTCOME_UNKNOWN"===e.code||"INSTANCE_EPOCH_MISMATCH"===e.code)?"outcome_unknown":"failed",s.resolve(void 0),e});return{operationId:r.context.operationId,getSnapshot:()=>({effectState:o,...i?{projection:i}:{}}),completion:p,progress:emptyProgress(),effectCommitted:s.promise,acknowledgeEffectReceipt:async e=>{if(!m||e.operationId!==m.operationId||e.method!==m.method)throw new RuntimeCommandError("OUTCOME_UNKNOWN","MCP effect receipt does not match this operation");if(!/^[a-f0-9]{64}$/.test(e.effectRecordHash))throw new RuntimeCommandError("OUTCOME_UNKNOWN","MCP effect receipt hash is invalid");await this.journal.releaseMcpLiveEffect({operationId:e.operationId,source:"service",effectRecordHash:e.effectRecordHash}),c.resolve()},acknowledgeTerminalReceipt:async e=>{if(!(d&&m&&e.operationId===m.operationId&&e.method===m.method&&/^[a-f0-9]{64}$/.test(e.effectRecordHash)&&e.terminalMessageId))throw new RuntimeCommandError("OUTCOME_UNKNOWN","MCP terminal receipt does not match this operation");await this.journal.promoteMcpDesiredStateActive({operationId:e.operationId,source:"service",effectRecordHash:e.effectRecordHash,terminalMessageId:e.terminalMessageId});const n=await this.requireReleasedRecord(e.operationId);if(void 0===n.result)throw new RuntimeCommandError("OUTCOME_UNKNOWN","active MCP desired state lost its committed result");await this.afterCommittedEffect(r,n.result,n,t,e=>{i=e},e=>{a=e})},cancel:async()=>"running"===o?{status:"cancel_requested"===await(this.capabilities?.cancel?.(r.context.operationId))?"cancel_requested":"not_cancellable"}:{status:"already_terminal"},awaitProjection:async()=>a?(n??=a(),await n):i}}async executeWithLease(e,t,r,i,a,n,o){return await this.withExclusiveLifecycle(e,t.abortSignal,()=>this.executeClaimedLifecycle(e,t,r,i,a,n,o))}async executeClaimedLifecycle(e,t,r,i,a,n,o){const s=await this.deps.leases.claim(e.context.operationId,e.context.dispatchEpoch);if(s.runtimeEpoch!==e.context.dispatchEpoch)throw await this.deps.leases.release(s),new RuntimeCommandError("INSTANCE_EPOCH_MISMATCH","runtime operation lease epoch mismatch");const c=this.deps.leases.startHeartbeat(s);try{if(runtimeCommandIsReplayableQuery(e.name))return await this.executeReplayableQuery(e,s,t,r,i);let c;await this.recoverInterruptedCreates(e.context.operationId);try{c=await this.journal.prepare({operationId:e.context.operationId,command:e.name,paramsHash:e.context.paramsHash,runtimeEpoch:s.runtimeEpoch})}catch(e){if(e instanceof RuntimeCommandJournalConflictError)throw new RuntimeCommandError("OUTCOME_UNKNOWN",e.message,void 0,{cause:e});throw e}if(("effect_committed"===c.phase||"projection_committed"===c.phase)&&void 0!==c.result){if(r(c.result,c),await i(),c=await this.requireReleasedRecord(c.operationId),void 0===c.result)throw new RuntimeCommandError("OUTCOME_UNKNOWN","committed result disappeared from command journal");return a||await this.afterCommittedEffect(e,c.result,c,t,n,o),c.result}if(("effect_committed"===c.phase||"projection_committed"===c.phase)&&"hash_only"===c.resultReceipt?.policy&&runtimeCommandIsReplayableQuery(e.name)){const a=await this.execute(e,s,t);if(runtimeCommandResultHash(a)!==c.resultReceipt.resultHash)throw new RuntimeCommandError("OUTCOME_UNKNOWN","redacted committed query result changed");return r(a,c),await i(),a}if(("effect_committed"===c.phase||"projection_committed"===c.phase)&&"hash_only"===c.resultReceipt?.policy){if(r(void 0,c),await i(),c=await this.requireReleasedRecord(c.operationId),!a){if(isRuntimeAgentMutation(e)&&(c.agentProjectionIntent||c.agentProjectionPreparationFailure)){const r=agentProjectionRecoveryResult(c.agentProjectionIntent??c.agentProjectionPreparationFailure);return await this.afterCommittedEffect(e,r,c,t,n,o),r}await this.runAfterEffect(e,void 0,c,t,n)}throw new RuntimeCommandError("OUTCOME_UNKNOWN","committed mutation result is redacted")}if("effect_committed"===c.phase||"projection_committed"===c.phase)throw new RuntimeCommandError("OUTCOME_UNKNOWN","committed result receipt is unavailable");if("outcome_unknown"===c.phase)throw new RuntimeCommandError("OUTCOME_UNKNOWN","previous execution outcome is unknown");if("started"===c.phase&&isRuntimeAgentMutation(e)){let a;try{a=await this.recoverStartedAgentMutation(e,s,t)}catch(e){throw await this.markJournalUnknown(c),e instanceof RuntimeCommandError?e:new RuntimeCommandError("OUTCOME_UNKNOWN",`agent desired-state recovery could not prove its effect: ${errorText(e)}`,void 0,{cause:e})}const m=this.deps.projection?await this.prepareAgentProjectionCommit(e,a,t):void 0,d=await this.journal.transition({operationId:c.operationId,expectedRevision:c.revision,phase:"effect_committed",result:a,...m?{agentProjection:m}:{}});return r(a,d),await i(),await this.afterCommittedEffect(e,a,d,t,n,o),this.scheduleJournalCompaction(),a}if("started"===c.phase&&"outcome_unknown"===this.recoveryDecision(e.name,c.phase))throw await this.markJournalUnknown(c),new RuntimeCommandError("OUTCOME_UNKNOWN","imperative action crossed its execution boundary");if("failed"===c.phase)throw new RuntimeCommandError("EXECUTION_FAILED","previous execution failed");"prepared"===c.phase&&(c=await this.journal.transition({operationId:c.operationId,expectedRevision:c.revision,phase:"started"}));let m=!1;try{const d=await this.execute(e,s,t);m=!0;const p=isRuntimeAgentMutation(e)&&this.deps.projection?await this.prepareAgentProjectionCommit(e,d,t):void 0;let u;try{const e=await this.requireReleasedRecord(c.operationId);u=await this.journal.transition({operationId:e.operationId,expectedRevision:e.revision,phase:"effect_committed",result:d,...p?{agentProjection:p}:{}})}catch(e){throw new RuntimeCommandError("OUTCOME_UNKNOWN",`effect completed before its journal commit: ${errorText(e)}`,void 0,{cause:e})}return r(d,u),await i(),u=await this.requireReleasedRecord(u.operationId),a||await this.afterCommittedEffect(e,d,u,t,n,o),this.scheduleJournalCompaction(),d}catch(t){if(m)throw t;const r=t instanceof RuntimeCommandError&&("OUTCOME_UNKNOWN"===t.code||"INSTANCE_EPOCH_MISMATCH"===t.code);if(r&&"clear-saga"===recoveryMetadata(e.name).recoveryClass)throw t;try{await this.journal.transition({operationId:c.operationId,expectedRevision:c.revision,phase:r?"outcome_unknown":"failed",errorCode:r?"OUTCOME_UNKNOWN":"EXECUTION_FAILED"})}catch(e){throw new RuntimeCommandError("OUTCOME_UNKNOWN",`command and journal outcome unknown: ${errorText(e)}`,void 0,{cause:t})}throw t}}finally{try{await c.stop(),await this.deps.leases.release(s)}catch(e){throw new RuntimeCommandError("OUTCOME_UNKNOWN",`runtime operation lease finalization unknown: ${errorText(e)}`,void 0,{cause:e})}}}async executeReplayableQuery(e,t,r,i,a){const n=await this.execute(e,t,r);return i(n,{operationId:e.context.operationId,command:e.name,paramsHash:e.context.paramsHash,runtimeEpoch:t.runtimeEpoch,phase:"effect_committed",revision:1,updatedAt:(new Date).toISOString(),resultReceipt:{policy:"hash_only",resultHash:runtimeCommandResultHash(n),redacted:{kind:"query_result",command:e.name}}}),await a(),await this.runPostCommit(e,n),n}scheduleJournalCompaction(){this.journal.compact().catch(e=>this.logger.error?.(`[runtime-journal] event=checkpoint_failed trigger=effect_committed error=${errorText(e)}`))}async execute(e,t,r){switch(e.name){case"agent.create":return await this.createAgent(e,t,r);case"agent.update":return await this.updateAgent(e);case"agent.delete":return await this.deleteAgent(e,t);case"agent.applyTemplate":return await this.applyTemplate(e,t,r)}if(!this.capabilities)throw new RuntimeCommandError("INVALID_PARAMS",`Unsupported runtime command: ${e.name}`);return await this.capabilities.execute(e,t,r)}async recoverStartedAgentMutation(e,t,r){if("agent.create"===e.name||"agent.applyTemplate"===e.name){let i=await this.bindCreateOperation(e,t,!1);if(i||(i=await this.findCreateRecoveryIdentity(e,!1),"allocated"===i.state&&void 0===i.createOperation?.paramsHash&&await this.bindCreateOperation(e,t,!1),i=await this.findCreateRecoveryIdentity(e)),"active"===i.state)return await this.recoverActiveCreateEffect(i);if(await this.runtimeAgentExists(i.observedRuntimeLocalKey??i.runtimeLocalKey)){const a={runtimeLocalKey:i.observedRuntimeLocalKey??i.runtimeLocalKey,workspacePath:join(this.getOpenclawHome(),`workspace-${i.runtimeLocalKey}`)};return"agent.create"===e.name?await this.completeCreatedAgent(e,t,r,a):await this.completeTemplateAgent(e,t,r,a)}return await this.execute(e,t,r)}const i=e.context.agent,a=await this.runtimeAgentExists(i.runtimeLocalKey);if("agent.delete"===e.name&&!a)return await this.deps.identities.commitDeleteTombstone(i.runtimeAgentUid,i.runtimeLocalKey,t),{runtimeAgentUid:i.runtimeAgentUid,runtimeLocalKey:i.runtimeLocalKey};if("agent.update"===e.name&&!a)throw new RuntimeCommandError("OUTCOME_UNKNOWN","update recovery target is absent");return await this.execute(e,t,r)}async runtimeAgentExists(e){const t=await this.requireGateway().request("agents.list",{});if(!Array.isArray(t?.agents))throw new RuntimeCommandError("OUTCOME_UNKNOWN","runtime agent directory query is incomplete");return t.agents.some(t=>null!==t&&"object"==typeof t&&(t.id===e||t.name===e))}async findCreateRecoveryIdentity(e,t=!0){const r={operationId:e.context.operationId,command:e.name,runtimeEpoch:e.context.dispatchEpoch,paramsHash:e.context.paramsHash,runtimeAgentUid:e.params.runtimeAgentUid},i=void 0!==this.deps.identities.findCreateOperation,a=i?await this.deps.identities.findCreateOperation(r):(await this.deps.identities.pendingAllocations()).filter(e=>e.operationId===r.operationId&&e.command===r.command&&e.runtimeEpoch===r.runtimeEpoch&&e.runtimeAgentUid===r.runtimeAgentUid);if(1!==a.length)throw new RuntimeCommandError("OUTCOME_UNKNOWN","create recovery identity binding is missing or ambiguous");const n=a[0];if(i&&(!n.createOperation||t&&n.createOperation.paramsHash!==r.paramsHash||n.createOperation.operationId!==r.operationId||n.createOperation.command!==r.command||n.createOperation.runtimeEpoch!==r.runtimeEpoch))throw new RuntimeCommandError("OUTCOME_UNKNOWN","create recovery identity hash binding conflicts");return n}async bindCreateOperation(e,t,r=!0){if(!this.deps.identities.findCreateOperation||!this.deps.identities.bindCreateOperation)return;const i={operationId:e.context.operationId,command:e.name,runtimeEpoch:e.context.dispatchEpoch,paramsHash:e.context.paramsHash,runtimeAgentUid:e.params.runtimeAgentUid};try{let a=await this.deps.identities.findCreateOperation(i);if(0===a.length){if(!r)throw new RuntimeCommandError("OUTCOME_UNKNOWN","create recovery identity binding is missing");if((await this.deps.identities.allocateCreate(t,e.name,e.params.runtimeAgentUid)).runtimeAgentUid!==e.params.runtimeAgentUid)throw new RuntimeCommandError("OUTCOME_UNKNOWN","create allocation UID does not match the canonical command");a=await this.deps.identities.findCreateOperation(i)}if(1!==a.length)throw new RuntimeCommandError("OUTCOME_UNKNOWN","create identity binding is missing or ambiguous");const n=a[0],o=await this.deps.identities.bindCreateOperation(n.runtimeAgentUid,n.runtimeLocalKey,{operationId:e.context.operationId,command:e.name,runtimeEpoch:e.context.dispatchEpoch,paramsHash:e.context.paramsHash});if(!o.createOperation||o.runtimeAgentUid!==e.params.runtimeAgentUid||o.createOperation.operationId!==i.operationId||o.createOperation.command!==i.command||o.createOperation.runtimeEpoch!==i.runtimeEpoch||o.createOperation.paramsHash!==i.paramsHash)throw new Error("create operation identity binding changed during compare-and-set");if("tombstoned"===o.state)throw new Error("create operation identity is tombstoned");return o}catch(e){if(e instanceof RuntimeCommandError)throw e;throw new RuntimeCommandError("OUTCOME_UNKNOWN",`create operation identity binding is corrupt: ${errorText(e)}`,void 0,{cause:e})}}async recoverActiveCreateEffect(e){const t=e.observedRuntimeLocalKey??e.runtimeLocalKey;if(!await this.runtimeAgentExists(t))throw new RuntimeCommandError("OUTCOME_UNKNOWN","active create identity is absent from the runtime directory");return{runtimeAgentUid:e.runtimeAgentUid,runtimeLocalKey:t,workspacePath:join(this.getOpenclawHome(),`workspace-${e.runtimeLocalKey}`)}}async markJournalUnknown(e){try{await this.journal.transition({operationId:e.operationId,expectedRevision:e.revision,phase:"outcome_unknown",errorCode:"OUTCOME_UNKNOWN"})}catch{}}async requireReleasedRecord(e){const t=await this.journal.get(e);if(!t)throw new RuntimeCommandError("OUTCOME_UNKNOWN","command journal record disappeared");return t}async afterCommittedEffect(e,t,r,i,a,n){if(!isRuntimeAgentMutation(e))return void await this.runAfterEffect(e,t,r,i,a);const o=await this.prepareAgentProjection(e,t,r,i,a);o&&(n(o),"acp"===i.origin&&o().catch(e=>this.logger.warn?.(`[runtime-core] deferred ACP agent projection failed: ${errorText(e)}`)))}async prepareAgentProjectionCommit(e,t,r){const i=agentProjectionRecoverySummary(e,t,r);try{const r=await this.deps.projection.prepareCommittedMutation({command:e,result:t,...i.expectedPlatformAgentId?{expectedPlatformAgentId:i.expectedPlatformAgentId}:{}});return Object.freeze({...r,...i})}catch(e){const t=!(e instanceof RuntimeProjectionTransportError)||e.retryable;return this.logger.warn?.(`[runtime-core] agent projection preparation ${t?"deferred":"blocked"}: ${errorText(e)}`),Object.freeze({...i,state:t?"pending":"blocked",errorKind:t?"snapshot_retryable":"snapshot_nonretryable"})}}async prepareAgentProjection(e,t,r,i,a){if("projection_committed"===r.phase)return a(r.projectionReceipt?.snapshot??Object.freeze({state:"synced",revision:`${Math.max(0,r.revision-1)}`})),r.agentProjectionIntent&&await this.compactAgentProjectionReference(r.agentProjectionIntent),void await this.runPostCommit(e,t);if("effect_committed"!==r.phase||!this.deps.projection)return;let n=r,o=n.agentProjectionIntent;const s=n.agentProjectionPreparationFailure;if(o&&"blocked"===n.agentProjectionTerminal?.state)return a(Object.freeze({state:"blocked",revision:n.agentProjectionTerminal.revision})),await this.compactAgentProjectionReference(o),void await this.runPostCommit(e,t);let c;return o&&a(Object.freeze({state:"pending",revision:o.projectionRevision})),async()=>(c??=(async()=>{if(!o){if(!s||"blocked"===s.state)return Object.freeze({state:"blocked",revision:`${Math.max(0,n.revision)}`});const t=await this.prepareAgentProjectionCommit(e,agentProjectionRecoveryResult(s),i);if(!isProjectionIntent(t))return"blocked"===t.state?Object.freeze({state:"blocked",revision:`${Math.max(0,n.revision)}`}):Object.freeze({state:"pending",revision:`${Math.max(0,n.revision)}`});o=t;try{n=await this.journal.recordAgentProjectionIntent({operationId:n.operationId,expectedRevision:n.revision,intent:o})}catch(e){return void this.logger.warn?.(`[runtime-core] agent projection reservation recovery deferred: ${errorText(e)}`)}a(Object.freeze({state:"pending",revision:o.projectionRevision}))}const r=Object.freeze({state:"pending",revision:o.projectionRevision});let c;try{c=await this.deps.projection.applyCommittedMutation(o)}catch(e){this.logger.warn?.(`[runtime-core] agent projection receipt deferred: ${errorText(e)}`),c=r}if(c.revision!==o.projectionRevision&&(c=Object.freeze({state:"blocked",revision:o.projectionRevision})),"synced"===c.state)try{n=await this.journal.transition({operationId:n.operationId,expectedRevision:n.revision,phase:"projection_committed",projectionSnapshot:c}),await this.compactAgentProjectionReference(o)}catch(e){this.logger.warn?.(`[runtime-core] projection phase commit deferred: ${errorText(e)}`),c=Object.freeze({state:"pending",revision:o.projectionRevision})}if("blocked"===c.state)try{n=await this.journal.recordAgentProjectionBlocked({operationId:n.operationId,expectedRevision:n.revision,revision:o.projectionRevision}),await this.compactAgentProjectionReference(o)}catch(e){this.logger.warn?.(`[runtime-core] blocked projection terminal commit deferred: ${errorText(e)}`),c=r}return"synced"===c.state&&c.platformAgentId&&"projection_committed"===n.phase&&this.reportTemplateOriginBestEffort(e,c.platformAgentId),a(c),await this.runPostCommit(e,t),c})(),await c)}async compactAgentProjectionReference(e){if(this.deps.projection)try{await this.deps.projection.compactCommittedMutation(e)}catch(e){this.logger.warn?.(`[runtime-core] projection reference compaction deferred: ${errorText(e)}`)}}reportTemplateOriginBestEffort(e,t){if("agent.applyTemplate"!==e.name||!this.deps.templateOriginReporter)return;const r=e.params.templateId;r&&this.deps.templateOriginReporter.report({platformAgentId:t,templateId:r}).catch(e=>{this.logger.warn?.(`[runtime-core] active template origin report failed, client fallback remains: ${errorText(e)}`)})}async runAfterEffect(e,t,r,i,a){if("projection_committed"===r.phase)return a(r.projectionReceipt?.snapshot??Object.freeze({state:"synced",revision:`${Math.max(0,r.revision-1)}`})),void await this.runPostCommit(e,t);const n="effect_committed"===r.phase&&await this.runProjection(e,t,i),o=!0===n?Object.freeze({state:"synced",revision:`${r.revision}`}):n&&"object"==typeof n?Object.freeze({...n,revision:`${r.revision}`}):"session.setModel"===e.name?Object.freeze({state:"pending",revision:`${r.revision}`}):void 0;if("synced"===o?.state)try{const e=o;await this.journal.transition({operationId:r.operationId,expectedRevision:r.revision,phase:"projection_committed",projectionSnapshot:e}),a(o)}catch(e){return this.logger.warn?.(`[runtime-core] projection phase commit deferred: ${errorText(e)}`),void a(Object.freeze({revision:o.revision,state:"pending"}))}else o&&a(o);await this.runPostCommit(e,t)}async runPostCommit(e,t){if(this.capabilities?.afterCommit)try{await this.capabilities.afterCommit(e,t)}catch(e){this.logger.warn?.(`[runtime-core] post-commit action failed: ${errorText(e)}`)}}async runProjection(e,t,r){if(!recoveryMetadata(e.name).projectionRequired)return!1;try{return!isRuntimeAgentMutation(e)&&(await(this.capabilities?.projectCommitted?.(e,t,r))??!1)}catch(e){return this.logger.warn?.(`[runtime-core] agent projection deferred: ${errorText(e)}`),!1}}recoveryDecision(e,t){const r=recoveryMetadata(e);return reduceRuntimeRecoveryPolicy({...r,messageServicePhase:"queued",executionBoundaryCrossed:"prepared"!==t,lineageContinuity:"continuous",journalState:"available",journalPhase:t})}requireGateway(){const e=this.deps.getGateway();if(!e?.isConnected)throw new RuntimeCommandError("GATEWAY_NOT_CONNECTED","Gateway not connected");return e}exclusiveLifecycleReason(e){switch(e.name){case"agent.applyTemplate":return"applyTemplate";case"agent.create":return"createAgent";case"agent.delete":return"deleteAgent";case"agent.update":return"updateAgent";default:return}}async withExclusiveLifecycle(e,t,r){const i=this.exclusiveLifecycleReason(e);if(!i)return await r();if(this.deps.runExclusive)try{return await this.deps.runExclusive(i,r,t)}catch(e){if(e instanceof ImHotPathAbortedError)throw new RuntimeCommandError("INVALID_PARAMS",`${i} aborted while waiting for in-flight chat`);throw e}return await(this.deps.waitForChatIdle?.(i,t)),await r()}async createAgentShell(e,t,r,i,a,n){const o=this.requireGateway(),s=(await this.deps.identities.pendingAllocations()).find(t=>t.runtimeAgentUid===e),c=n??s?.runtimeLocalKey??this.createAgentId(),m=join(this.getOpenclawHome(),`workspace-${c}`);let d;try{d=await o.request("agents.create",{name:c,workspace:m,...a?{model:a}:{},...i?.emoji?{emoji:i.emoji}:{},...i?.avatar?{avatar:i.avatar}:{}})}catch(r){if(isDeterministicAgentsCreateParamsRejection(r)&&(void 0!==n||void 0!==s))try{await this.deps.identities.tombstone(e,c,t)}catch(e){this.logger.warn?.(`[runtime-core] rejected-create allocation cleanup deferred: ${errorText(e)}`)}throw r}const p=d?.agentId??c,u=d?.workspace??m;if(await this.deps.identities.observeEffectKey(e,c,p,t),p!==c)throw new RuntimeCommandError("OUTCOME_UNKNOWN",`Create agent outcome unknown: gateway changed reserved local key to ${p}`);try{await o.request("agents.update",{agentId:p,name:r})}catch(e){this.logger.warn?.(`[runtime-core] display-name update failed agent=${p}: ${errorText(e)}`)}return{runtimeLocalKey:p,workspacePath:u}}async createAgent(e,t,r){const i=e.params.name.trim();if(!i)throw new RuntimeCommandError("INVALID_PARAMS","Missing agent name");try{const a=await this.bindCreateOperation(e,t);if("active"===a?.state)return await this.recoverActiveCreateEffect(a);const n=await this.createAgentShell(e.params.runtimeAgentUid,t,i,e.params.identity,e.params.defaultModelId,a?.runtimeLocalKey);return await this.completeCreatedAgent(e,t,r,n)}catch(e){throw this.executionError("Create agent",e)}}async updateAgent(e){const{runtimeAgentUid:t,runtimeLocalKey:r}=e.context.agent;if(!r.trim())throw new RuntimeCommandError("MISSING_PARAM","Missing agentId","agentId");const i=e.params,a=i.name?.trim(),n=i.defaultModelId?.trim(),o=i.identity?.emoji?.trim(),s=i.identity?.avatar?.trim();if(!(a||n||o||s||void 0!==i.description||void 0!==i.version||void 0!==i.capabilities||void 0!==i.skills||void 0!==i.avatarUrl||void 0!==i.homepage||void 0!==i.status||void 0!==i.identity?.vibe))throw new RuntimeCommandError("INVALID_PARAMS","No updatable fields provided (name/model/identity)");try{return await this.deps.identities.requireActive(t,r),await this.requireGateway().request("agents.update",{agentId:r,...a?{name:a}:{},...n?{model:n}:{},...o?{emoji:o}:{},...s?{avatar:s}:{},...i.identity?.vibe?{vibe:i.identity.vibe}:{},...void 0!==i.description?{description:i.description}:{},...void 0!==i.version?{version:i.version}:{},...void 0!==i.capabilities?{capabilities:i.capabilities}:{},...void 0!==i.skills?{skills:i.skills}:{},...void 0!==i.avatarUrl?{avatarUrl:i.avatarUrl}:{},...void 0!==i.homepage?{homepage:i.homepage}:{},...void 0!==i.status?{status:i.status}:{}}),{runtimeAgentUid:t,runtimeLocalKey:r}}catch(e){throw this.executionError("Update agent",e)}}async deleteAgent(e,t){const{runtimeAgentUid:r,runtimeLocalKey:i}=e.context.agent;if(!i.trim())throw new RuntimeCommandError("MISSING_PARAM","Missing agentId","agentId");if("main"===normalizeRuntimeAgentLocalKey(i))throw new RuntimeCommandError("INVALID_PARAMS",'"main" cannot be deleted; delete the instance instead');try{if("tombstoned"===await this.deps.identities.requireDeleteTarget(r,i))return{runtimeAgentUid:r,runtimeLocalKey:i};if(!await this.runtimeAgentExists(i))return await this.deps.identities.commitDeleteTombstone(r,i,t),{runtimeAgentUid:r,runtimeLocalKey:i};const a=requireCommittedOpenClawDeleteAck(await this.requireGateway().request("agents.delete",{agentId:i,deleteFiles:e.params.deleteFiles??!0}),i);try{await this.deps.identities.commitDeleteTombstone(r,i,t)}catch(e){throw new RuntimeCommandError("OUTCOME_UNKNOWN",`Delete agent outcome unknown: identity tombstone failed: ${errorText(e)}`,void 0,{cause:e})}return{runtimeAgentUid:r,runtimeLocalKey:i,...void 0===a.removedBindings?{}:{removedBindings:a.removedBindings}}}catch(e){throw this.executionError("Delete agent",e)}}async applyTemplate(e,t,r){const i=e.params.name.trim();if(!i)throw new RuntimeCommandError("INVALID_PARAMS","Missing agent name");this.requireGateway();try{const a=await this.bindCreateOperation(e,t);if("active"===a?.state)return await this.recoverActiveCreateEffect(a);const n=await r.resources.readVerifiedArtifact(e.params.artifact),o=await this.createAgentShell(e.params.runtimeAgentUid,t,i,e.params.identity,e.params.defaultModelId,a?.runtimeLocalKey);return await this.completeTemplateAgent(e,t,r,o,n)}catch(e){throw this.executionError("Apply template",e)}}async completeCreatedAgent(e,t,r,i){for(const t of e.params.bootstrapFiles??[]){if("IDENTITY.md"===t.name){this.logger.warn?.("[runtime-core] agent.create ignoring IDENTITY.md; identity is structured");continue}const e=await r.resources.readContent(t.contentRef);try{await this.requireGateway().request("agents.files.set",{agentId:i.runtimeLocalKey,name:t.name,content:e})}catch(e){this.logger.warn?.(`[runtime-core] bootstrap write failed file=${t.name}: ${errorText(e)}`)}}try{await this.deps.identities.activate(e.params.runtimeAgentUid,i.runtimeLocalKey,t)}catch(r){throw await this.compensateCreatedAgent(e.params.runtimeAgentUid,i.runtimeLocalKey,t,r,"Create agent"),new RuntimeCommandError("EXECUTION_FAILED",`Create agent failed after compensation: ${errorText(r)}`)}return await this.prepareAgentToolCatalog(i.runtimeLocalKey),{runtimeAgentUid:e.params.runtimeAgentUid,runtimeLocalKey:i.runtimeLocalKey,workspacePath:i.workspacePath}}async completeTemplateAgent(e,t,r,i,a){const n=a??await r.resources.readVerifiedArtifact(e.params.artifact);try{for(const[e,t]of n.workspaceFiles){const r=resolve(i.workspacePath,e),a=relative(i.workspacePath,r);if(""===a||a.startsWith("..")||path.isAbsolute(a))throw new Error(`refusing to write outside workspace: ${e}`);await(this.deps.mkdirWorkspace??mkdir)(path.dirname(r),{recursive:!0}),await(this.deps.writeWorkspaceFile??writeFile)(r,t)}await this.deps.identities.activate(e.params.runtimeAgentUid,i.runtimeLocalKey,t)}catch(r){throw await this.compensateCreatedAgent(e.params.runtimeAgentUid,i.runtimeLocalKey,t,r,"Apply template"),new RuntimeCommandError("EXECUTION_FAILED",`Apply template failed after compensation: ${errorText(r)}`,void 0,{cause:r})}return await this.prepareAgentToolCatalog(i.runtimeLocalKey),{runtimeAgentUid:e.params.runtimeAgentUid,runtimeLocalKey:i.runtimeLocalKey,workspacePath:i.workspacePath}}async prepareAgentToolCatalog(e){await prepareOpenClawToolCatalog({request:(e,t)=>this.requireGateway().request(e,t),agentId:e,mode:"catalog",keyKind:"boot",logger:this.logger})}executionError(e,t){return t instanceof RuntimeCommandError?t:(this.logger.error?.(`[runtime-core] ${e.toLowerCase()} failed: ${errorText(t)}`),new RuntimeCommandError("EXECUTION_FAILED",`${e} failed: ${errorText(t)}`,void 0,{cause:t}))}async compensateCreatedAgent(e,t,r,i,a){try{const i=this.requireGateway();requireCommittedOpenClawDeleteAck(await i.request("agents.delete",{agentId:t,deleteFiles:!0}),t),await this.deps.identities.tombstone(e,t,r)}catch(e){throw new RuntimeCommandError("OUTCOME_UNKNOWN",`${a} outcome unknown after ${errorText(i)}; compensation unproven: ${errorText(e)}`,void 0,{cause:e})}}async recoverInterruptedCreates(e){const t=await this.deps.identities.pendingAllocations();for(const r of t){if(r.operationId===e)continue;const t=await this.deps.leases.claimExpiredForRecovery(r.operationId);if(!t)continue;const i=this.deps.leases.startHeartbeat(t);try{await this.deps.identities.adoptRecoveryLease(r.runtimeAgentUid,r.runtimeLocalKey,t);const e=r.observedRuntimeLocalKey;if(!e){const e=r.createOperation?.operationId??r.operationId,i=await this.journal.get(e);if("failed"===i?.phase&&i.command===r.command&&!await this.runtimeAgentExists(r.runtimeLocalKey)){await this.deps.identities.tombstone(r.runtimeAgentUid,r.runtimeLocalKey,t);continue}throw new Error("no persisted gateway effect key; absence cannot be proven")}requireCommittedOpenClawDeleteAck(await this.requireGateway().request("agents.delete",{agentId:e,deleteFiles:!0}),e),await this.deps.identities.tombstone(r.runtimeAgentUid,r.runtimeLocalKey,t)}catch(e){throw new RuntimeCommandError("OUTCOME_UNKNOWN",`Interrupted ${r.command} recovery outcome unknown for operation ${r.operationId}: ${errorText(e)}`,void 0,{cause:e})}finally{let e;try{await i.stop()}catch(t){e=t}try{await this.deps.leases.release(t)}catch(t){e??=t}if(e)throw new RuntimeCommandError("OUTCOME_UNKNOWN",`Interrupted recovery lease finalization unknown: ${errorText(e)}`,void 0,{cause:e})}}}}function isRuntimeAgentMutation(e){return"agent.create"===e.name||"agent.update"===e.name||"agent.delete"===e.name||"agent.applyTemplate"===e.name}function servicePlatformAgentId(e){const t=e.verifiedMessageGrant?.claims;return"agent"===t?.scope?t.platformAgentId:void 0}function agentCoreResultHash(e,t){return createHash("sha256").update(runtimeHashPreimage(RUNTIME_HASH_DOMAINS.coreResult,{method:e.name.replace(".","/"),result:t})).digest("hex")}function agentProjectionRecoverySummary(e,t,r){const i="agent.update"===e.name||"agent.delete"===e.name?e.context.agent:createProjectionTarget(e,t),a=t;return Object.freeze({schemaVersion:1,operationId:e.context.operationId,method:e.name,runtimeEpoch:e.context.dispatchEpoch,runtimeAgentUid:i.runtimeAgentUid,runtimeLocalKey:i.runtimeLocalKey,coreResultHash:agentCoreResultHash(e,t),...servicePlatformAgentId(r)?{expectedPlatformAgentId:servicePlatformAgentId(r)}:{},..."agent.delete"===e.name&&Number.isSafeInteger(a?.removedBindings)?{removedBindings:a.removedBindings}:{}})}function createProjectionTarget(e,t){const r=t;if(!r||r.runtimeAgentUid!==e.params.runtimeAgentUid||"string"!=typeof r.runtimeLocalKey||!r.runtimeLocalKey)throw new RuntimeCommandError("OUTCOME_UNKNOWN","committed agent effect lacks a recoverable local identity");return{runtimeAgentUid:r.runtimeAgentUid,runtimeLocalKey:r.runtimeLocalKey}}function agentProjectionRecoveryResult(e){return Object.freeze({runtimeAgentUid:e.runtimeAgentUid,runtimeLocalKey:e.runtimeLocalKey,...void 0===e.removedBindings?{}:{removedBindings:e.removedBindings}})}function isProjectionIntent(e){return"projectionRevision"in e}function recoveryMetadata(e){const t=RUNTIME_METHOD_PARITY_MANIFEST.filter(t=>t.serviceAllowed&&t.canonicalCommand===e),r=t[0]?.recoveryClass;return r?{recoveryClass:r,projectionRequired:"mcp.set"===e||"mcp.unset"===e||t.some(e=>!0===e.projectionRequired)}:{recoveryClass:"imperative-ambiguous",projectionRequired:!1}}export function isDeterministicAgentsCreateParamsRejection(e){return e instanceof Error&&/^invalid agents\.create params\b/.test(e.message)}export function requireCommittedOpenClawDeleteAck(e,t){if(!e||"object"!=typeof e)throw new Error("gateway delete lacks the committed target acknowledgement");const r=e,i=Object.keys(r).every(e=>"ok"===e||"agentId"===e||"removedBindings"===e),a=void 0===r.removedBindings||Number.isSafeInteger(r.removedBindings)&&r.removedBindings>=0;if(!i||!0!==r.ok||r.agentId!==t||!a)throw new Error("gateway delete lacks the committed target acknowledgement");return{ok:!0,agentId:t,...void 0===r.removedBindings?{}:{removedBindings:r.removedBindings}}}function errorText(e){return e instanceof Error?e.message:String(e)}
|