beeos-claw 0.6.63-cloud.1 → 0.6.63-cloud.3

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.
Files changed (53) hide show
  1. package/dist/acp-gateway/bridge-core.js +1 -1
  2. package/dist/acp-server.js +1 -1
  3. package/dist/agent-auth.js +1 -1
  4. package/dist/canvas-orchestrator.js +1 -1
  5. package/dist/chat-envelope.js +1 -0
  6. package/dist/im-inbound-handler.js +1 -1
  7. package/dist/im-runtime-dispatch.js +1 -1
  8. package/dist/index.js +1 -1
  9. package/dist/message-client-composition.js +1 -1
  10. package/dist/message-projection-inbound-consumer.js +1 -1
  11. package/dist/runtime/callback-queue.js +1 -1
  12. package/dist/runtime/chat-handler-port.js +1 -0
  13. package/dist/runtime/conversation-title.js +1 -1
  14. package/dist/runtime/core-execution-attempt-context.js +1 -1
  15. package/dist/runtime/im-reply-sink.js +1 -1
  16. package/dist/runtime/message-service-conversation-clear-http-authority.js +1 -1
  17. package/dist/runtime/message-service-conversation-run-boundary.js +1 -1
  18. package/dist/runtime/message-service-runtime-message-channel.js +1 -1
  19. package/dist/runtime/ms-runtime-message-channel-adapter.js +1 -1
  20. package/dist/runtime/ms-runtime-message-channel-consumer-factory.js +1 -1
  21. package/dist/runtime/ms-runtime-rpc-adapter.js +1 -1
  22. package/dist/runtime/native-cloud-chat-composition.js +1 -0
  23. package/dist/runtime/native-cloud-prompt-bridge.js +1 -0
  24. package/dist/runtime/native-input-files-http.js +1 -0
  25. package/dist/runtime/native-input-files.js +1 -0
  26. package/dist/runtime/native-output-file-attempt.js +1 -0
  27. package/dist/runtime/native-prompt-authority.js +1 -0
  28. package/dist/runtime/native-prompt-preflight.js +1 -0
  29. package/dist/runtime/native-session-new-bridge.js +1 -0
  30. package/dist/runtime/reply-router.js +1 -1
  31. package/dist/runtime/runtime-error-part.js +1 -0
  32. package/dist/runtime/runtime-local-parity-matrix.js +1 -1
  33. package/dist/runtime/runtime-operation-resource-resolver.js +1 -1
  34. package/dist/runtime/runtime-parity-manifest-digest.js +1 -1
  35. package/dist/runtime/runtime-parity-manifest.js +1 -1
  36. package/dist/runtime/runtime-production-authority.js +1 -1
  37. package/dist/runtime/runtime-registration.js +1 -0
  38. package/dist/runtime/runtime-service-contract-compatibility.js +1 -1
  39. package/dist/runtime/runtime-service-production-candidate.js +1 -1
  40. package/dist/runtime/runtime-session-control-service.js +1 -1
  41. package/dist/runtime/runtime-skills-service.js +1 -1
  42. package/dist/runtime/runtime-transport-failure-policy.js +1 -0
  43. package/dist/runtime/service-runtime-attempt-ports-resolver.js +1 -1
  44. package/dist/runtime/session-new-runtime-delivery-bridge.js +1 -1
  45. package/dist/runtime/write-runtime-failure.js +1 -1
  46. package/dist/upload/tool.js +1 -1
  47. package/dist/upload/upload-file.js +1 -1
  48. package/openclaw.plugin.json +1 -15
  49. package/package.json +5 -5
  50. package/runtime-service-wire-identity.v1.json +5 -5
  51. package/dist/automation/tools.js +0 -1
  52. package/dist/automation/turn-policy.js +0 -1
  53. package/dist/automation/wake.js +0 -1
@@ -1 +1 @@
1
- export const RUNTIME_MESSAGE_CHANNEL_REVISION="beeos.runtime-message-channel.v2.0.0";export const RUNTIME_MESSAGE_CHANNEL_RAW_SHA256="dd791ce5f49b7bee1dba21a71e7b2cfbf4db7d77cb787aab5f04c7f4b0e3101b";export const RUNTIME_MESSAGE_CHANNEL_SHA256="e950f1a86bf2bd8bf472603a91111c15b920fcac4c06fd8ac70e2c3f4c3a408f";export const RUNTIME_AUTHORITY_PORTS_REVISION="beeos.runtime-authority-ports.v2.0.0";export const RUNTIME_AUTHORITY_PORTS_RAW_SHA256="a2e9565ae617bcaeb4d837fe176a3602be041913d28a3f0a3a9ec2e3dae0d7b4";export const RUNTIME_AUTHORITY_PORTS_SHA256="3bd582f1f6babcdbe953a082faf813247c960f6bbf4c0f713e7fd92e27d163f5";export const RUNTIME_SESSION_CANCEL_REVISION="beeos.runtime-session-cancel.v1.0.0";export const RUNTIME_SESSION_CANCEL_SHA256="ad32581546f3a97d62df3a6785e3ec057e7dd888b31885f46cfb43d07c966d3e";export const RUNTIME_PARITY_MANIFEST_REVISION="beeos.runtime-parity.v1.1.0";export const RUNTIME_PARITY_MANIFEST_RAW_SHA256="7028b5a0dd85ee78d7df0690d2ef13ef2118acb08a1c8f718d3e9f08da8f8404";export const RUNTIME_PARITY_MANIFEST_SHA256="d464594fe15a1657c805c8829d676a4c93aec5c43e020f3b1fae21f4c9453ca7";const manifestEntries=[{method:"session/set_mode",acpSource:"service",route:"POST /api/v1/runtime/operations",contractRevision:RUNTIME_MESSAGE_CHANNEL_REVISION,status:"ready",blockerOwner:""},{method:"agent/create",acpSource:"service",route:"POST /api/v1/agents/sync",contractRevision:RUNTIME_AUTHORITY_PORTS_REVISION,status:"ready",blockerOwner:""},{method:"agent/update",acpSource:"service",route:"POST /api/v1/agents/sync",contractRevision:RUNTIME_AUTHORITY_PORTS_REVISION,status:"ready",blockerOwner:""},{method:"agent/delete",acpSource:"service",route:"POST /api/v1/agents/sync",contractRevision:RUNTIME_AUTHORITY_PORTS_REVISION,status:"ready",blockerOwner:""},{method:"agent/applyTemplate",acpSource:"service",route:"POST /api/v1/agents/sync",contractRevision:RUNTIME_AUTHORITY_PORTS_REVISION,status:"ready",blockerOwner:""},{method:"skills/list",acpSource:"service",route:"POST /api/v1/runtime/operations",contractRevision:RUNTIME_MESSAGE_CHANNEL_REVISION,status:"ready",blockerOwner:""},{method:"skills/install",acpSource:"service",route:"POST /api/v1/runtime/operations",contractRevision:RUNTIME_MESSAGE_CHANNEL_REVISION,status:"ready",blockerOwner:""},{method:"skills/uninstall",acpSource:"service",route:"POST /api/v1/runtime/operations",contractRevision:RUNTIME_MESSAGE_CHANNEL_REVISION,status:"ready",blockerOwner:""},{method:"skills/update",acpSource:"service",route:"POST /api/v1/runtime/operations",contractRevision:RUNTIME_MESSAGE_CHANNEL_REVISION,status:"ready",blockerOwner:""},{method:"models/list",acpSource:"service",route:"POST /api/v1/runtime/operations",contractRevision:RUNTIME_MESSAGE_CHANNEL_REVISION,status:"ready",blockerOwner:""},{method:"cron/list",acpSource:"service",route:"POST /api/v1/runtime/operations",contractRevision:RUNTIME_MESSAGE_CHANNEL_REVISION,status:"ready",blockerOwner:""},{method:"cron/status",acpSource:"service",route:"POST /api/v1/runtime/operations",contractRevision:RUNTIME_MESSAGE_CHANNEL_REVISION,status:"ready",blockerOwner:""},{method:"cron/add",acpSource:"service",route:"POST /api/v1/runtime/operations",contractRevision:RUNTIME_MESSAGE_CHANNEL_REVISION,status:"ready",blockerOwner:""},{method:"cron/update",acpSource:"service",route:"POST /api/v1/runtime/operations",contractRevision:RUNTIME_MESSAGE_CHANNEL_REVISION,status:"ready",blockerOwner:""},{method:"cron/remove",acpSource:"service",route:"POST /api/v1/runtime/operations",contractRevision:RUNTIME_MESSAGE_CHANNEL_REVISION,status:"ready",blockerOwner:""},{method:"cron/run",acpSource:"service",route:"POST /api/v1/runtime/operations",contractRevision:RUNTIME_MESSAGE_CHANNEL_REVISION,status:"ready",blockerOwner:""},{method:"cron/runs",acpSource:"service",route:"POST /api/v1/runtime/operations",contractRevision:RUNTIME_MESSAGE_CHANNEL_REVISION,status:"ready",blockerOwner:""},{method:"mcp/list",acpSource:"service",route:"POST /api/v1/runtime/operations",contractRevision:RUNTIME_MESSAGE_CHANNEL_REVISION,status:"ready",blockerOwner:""},{method:"mcp/prepare",acpSource:"service",route:"POST /api/v1/runtime/operations",contractRevision:RUNTIME_MESSAGE_CHANNEL_REVISION,status:"ready",blockerOwner:""},{method:"mcp/set",acpSource:"service",route:"POST /api/v1/runtime/operations",contractRevision:RUNTIME_MESSAGE_CHANNEL_REVISION,status:"ready",blockerOwner:""},{method:"mcp/unset",acpSource:"service",route:"POST /api/v1/runtime/operations",contractRevision:RUNTIME_MESSAGE_CHANNEL_REVISION,status:"ready",blockerOwner:""},{method:"session/clear",acpSource:"conversation",route:"POST /api/v1/runtime/conversations/{conversationId}/history-boundary/{action}",contractRevision:RUNTIME_AUTHORITY_PORTS_REVISION,status:"ready",blockerOwner:""},{method:"session/set_model",acpSource:"conversation",route:"POST /api/v1/runtime/conversations/{conversationId}/metadata/model",contractRevision:RUNTIME_AUTHORITY_PORTS_REVISION,status:"ready",blockerOwner:""},{method:"session/cancel",acpSource:"conversation",route:"POST /api/v1/runtime/operations",contractRevision:RUNTIME_MESSAGE_CHANNEL_REVISION,status:"ready",blockerOwner:""}];export const RUNTIME_PARITY_MANIFEST=Object.freeze(manifestEntries.map(e=>Object.freeze(e)));export const RUNTIME_PARITY_READY_METHODS=Object.freeze(new Set(RUNTIME_PARITY_MANIFEST.filter(e=>"ready"===e.status).map(e=>e.method)));
1
+ export const RUNTIME_MESSAGE_CHANNEL_REVISION="beeos.runtime-message-channel.v2.0.0";export const RUNTIME_MESSAGE_CHANNEL_RAW_SHA256="dd791ce5f49b7bee1dba21a71e7b2cfbf4db7d77cb787aab5f04c7f4b0e3101b";export const RUNTIME_MESSAGE_CHANNEL_SHA256="e950f1a86bf2bd8bf472603a91111c15b920fcac4c06fd8ac70e2c3f4c3a408f";export const RUNTIME_AUTHORITY_PORTS_REVISION="beeos.runtime-authority-ports.v2.0.0";export const RUNTIME_AUTHORITY_PORTS_RAW_SHA256="a2e9565ae617bcaeb4d837fe176a3602be041913d28a3f0a3a9ec2e3dae0d7b4";export const RUNTIME_AUTHORITY_PORTS_SHA256="3bd582f1f6babcdbe953a082faf813247c960f6bbf4c0f713e7fd92e27d163f5";export const RUNTIME_SESSION_CANCEL_REVISION="beeos.runtime-session-cancel.v1.0.0";export const RUNTIME_SESSION_CANCEL_SHA256="ad32581546f3a97d62df3a6785e3ec057e7dd888b31885f46cfb43d07c966d3e";export const RUNTIME_PARITY_MANIFEST_REVISION="beeos.runtime-parity.v1.1.0";export const RUNTIME_PARITY_MANIFEST_RAW_SHA256="f1030e7bf2084f584d558c22b9bffde0f823d6950c696a6f0571adaffcdbcee0";export const RUNTIME_PARITY_MANIFEST_SHA256="c98ed2aa822b29530cb02badd51654f2766f1dc0ff2c53b660be6431b1f23747";const manifestEntries=[{method:"session/new",acpSource:"service",route:"POST /api/v1/runtime/conversations",contractRevision:RUNTIME_AUTHORITY_PORTS_REVISION,status:"ready",blockerOwner:""},{method:"session/set_mode",acpSource:"service",route:"POST /api/v1/runtime/operations",contractRevision:RUNTIME_MESSAGE_CHANNEL_REVISION,status:"ready",blockerOwner:""},{method:"agent/create",acpSource:"service",route:"POST /api/v1/agents/sync",contractRevision:RUNTIME_AUTHORITY_PORTS_REVISION,status:"ready",blockerOwner:""},{method:"agent/update",acpSource:"service",route:"POST /api/v1/agents/sync",contractRevision:RUNTIME_AUTHORITY_PORTS_REVISION,status:"ready",blockerOwner:""},{method:"agent/delete",acpSource:"service",route:"POST /api/v1/agents/sync",contractRevision:RUNTIME_AUTHORITY_PORTS_REVISION,status:"ready",blockerOwner:""},{method:"agent/applyTemplate",acpSource:"service",route:"POST /api/v1/agents/sync",contractRevision:RUNTIME_AUTHORITY_PORTS_REVISION,status:"ready",blockerOwner:""},{method:"skills/list",acpSource:"service",route:"POST /api/v1/runtime/operations",contractRevision:RUNTIME_MESSAGE_CHANNEL_REVISION,status:"ready",blockerOwner:""},{method:"skills/install",acpSource:"service",route:"POST /api/v1/runtime/operations",contractRevision:RUNTIME_MESSAGE_CHANNEL_REVISION,status:"ready",blockerOwner:""},{method:"skills/uninstall",acpSource:"service",route:"POST /api/v1/runtime/operations",contractRevision:RUNTIME_MESSAGE_CHANNEL_REVISION,status:"ready",blockerOwner:""},{method:"skills/update",acpSource:"service",route:"POST /api/v1/runtime/operations",contractRevision:RUNTIME_MESSAGE_CHANNEL_REVISION,status:"ready",blockerOwner:""},{method:"models/list",acpSource:"service",route:"POST /api/v1/runtime/operations",contractRevision:RUNTIME_MESSAGE_CHANNEL_REVISION,status:"ready",blockerOwner:""},{method:"cron/list",acpSource:"service",route:"POST /api/v1/runtime/operations",contractRevision:RUNTIME_MESSAGE_CHANNEL_REVISION,status:"ready",blockerOwner:""},{method:"cron/status",acpSource:"service",route:"POST /api/v1/runtime/operations",contractRevision:RUNTIME_MESSAGE_CHANNEL_REVISION,status:"ready",blockerOwner:""},{method:"cron/add",acpSource:"service",route:"POST /api/v1/runtime/operations",contractRevision:RUNTIME_MESSAGE_CHANNEL_REVISION,status:"ready",blockerOwner:""},{method:"cron/update",acpSource:"service",route:"POST /api/v1/runtime/operations",contractRevision:RUNTIME_MESSAGE_CHANNEL_REVISION,status:"ready",blockerOwner:""},{method:"cron/remove",acpSource:"service",route:"POST /api/v1/runtime/operations",contractRevision:RUNTIME_MESSAGE_CHANNEL_REVISION,status:"ready",blockerOwner:""},{method:"cron/run",acpSource:"service",route:"POST /api/v1/runtime/operations",contractRevision:RUNTIME_MESSAGE_CHANNEL_REVISION,status:"ready",blockerOwner:""},{method:"cron/runs",acpSource:"service",route:"POST /api/v1/runtime/operations",contractRevision:RUNTIME_MESSAGE_CHANNEL_REVISION,status:"ready",blockerOwner:""},{method:"mcp/list",acpSource:"service",route:"POST /api/v1/runtime/operations",contractRevision:RUNTIME_MESSAGE_CHANNEL_REVISION,status:"ready",blockerOwner:""},{method:"mcp/prepare",acpSource:"service",route:"POST /api/v1/runtime/operations",contractRevision:RUNTIME_MESSAGE_CHANNEL_REVISION,status:"ready",blockerOwner:""},{method:"mcp/set",acpSource:"service",route:"POST /api/v1/runtime/operations",contractRevision:RUNTIME_MESSAGE_CHANNEL_REVISION,status:"ready",blockerOwner:""},{method:"mcp/unset",acpSource:"service",route:"POST /api/v1/runtime/operations",contractRevision:RUNTIME_MESSAGE_CHANNEL_REVISION,status:"ready",blockerOwner:""},{method:"session/clear",acpSource:"conversation",route:"POST /api/v1/runtime/conversations/{conversationId}/history-boundary/{action}",contractRevision:RUNTIME_AUTHORITY_PORTS_REVISION,status:"ready",blockerOwner:""},{method:"session/set_model",acpSource:"conversation",route:"POST /api/v1/runtime/conversations/{conversationId}/metadata/model",contractRevision:RUNTIME_AUTHORITY_PORTS_REVISION,status:"ready",blockerOwner:""},{method:"session/cancel",acpSource:"conversation",route:"POST /api/v1/runtime/operations",contractRevision:RUNTIME_MESSAGE_CHANNEL_REVISION,status:"ready",blockerOwner:""}];export const RUNTIME_PARITY_MANIFEST=Object.freeze(manifestEntries.map(e=>Object.freeze(e)));export const RUNTIME_PARITY_READY_METHODS=Object.freeze(new Set(RUNTIME_PARITY_MANIFEST.filter(e=>"ready"===e.status).map(e=>e.method)));
@@ -1 +1 @@
1
- import{createHash,createPrivateKey,randomUUID,sign}from"node:crypto";import{join}from"node:path";import{CONVERSATION_RUNTIME_CANONICAL_COMMAND_NAMES,RUNTIME_METHOD_PARITY_MANIFEST,RUNTIME_RESOURCE_BINDING_CONTRACT_REVISION,RUNTIME_RPC_CONTRACT_REVISION}from"@beeos-ai/beeos-types/runtime";import{agentBoundFetch}from"../agent-auth.js";import{FileRuntimeCommandJournal}from"./runtime-command-journal.js";import{FileRuntimeAgentIdentityRegistry}from"./runtime-agent-identity-registry.js";import{RuntimeRegistrationIdentityStore}from"./runtime-registration-identity-store.js";import{InstanceAuthorityRuntimeRegistrationTransport,RuntimeRegistrationCoordinator,RuntimeRegistrationJournalAdvanceRequiredError}from"@beeos-ai/cloud-agent-sdk";import{verifiedRuntimeParityManifestDigest}from"./runtime-parity-manifest-digest.js";import{intersectRuntimeServiceMethodsWithBackendCapabilities}from"./runtime-service-contract-compatibility.js";export function cloudRuntimeTargetInstanceId(t){const e=t.BEEOS_CLOUD_RUNTIME_INSTANCE_ID;if(void 0!==e){if(!/^[A-Za-z0-9_-]{1,128}$/.test(e)||e!==t.BEEOS_INSTANCE_ID)throw new Error("Cloud runtime target must match the provisioned BeeOS instance identity");return e}}export class RuntimeProductionAuthority{options;journal;identities;identityStore;handlerIdentity;listeners=new Set;coordinator;unsubscribeCoordinator;starting;rolling=new Map;refreshing;stoppedCoordinators=new WeakSet;coordinatorGeneration=0;lineage;identity;preparing;runtimeConsumerLive=!1;runtimeServiceMethods=Object.freeze([]);realtimeCapabilityReady=new Map;resourceBindingContractRevision;manifestDigest=verifiedRuntimeParityManifestDigest();started=!1;lifecycleVersion=0;authorityLeasePending=!1;boundAgentFetch;constructor(t){if(this.options=t,void 0!==t.targetInstanceId&&!/^[A-Za-z0-9_-]{1,128}$/.test(t.targetInstanceId))throw new Error("Cloud runtime target instance ID is invalid");this.handlerIdentity=t.handlerIdentity??(process.env.HOSTNAME?.trim()||process.env.POD_NAME?.trim()||randomUUID()),this.journal=new FileRuntimeCommandJournal(join(t.stateDirectory,"runtime-command-journal.json"),void 0,void 0,{onMaintenanceError:e=>{const i=`[runtime-journal] event=checkpoint_failed trigger=threshold error=${e instanceof Error?e.message:String(e)}`;t.logger?.error?t.logger.error(i):t.logger?.warn?.(i)}}),this.identities=new FileRuntimeAgentIdentityRegistry(join(t.stateDirectory,"runtime-agent-identities.json")),this.boundAgentFetch=t.boundAgentFetch??((e,i)=>agentBoundFetch(e,t.keys,i)),this.identityStore=new RuntimeRegistrationIdentityStore(join(t.stateDirectory,"runtime-registration-identity.json"))}get agentKeys(){return this.options.keys}get stateDirectory(){return this.options.stateDirectory}get cloudTargetInstanceId(){return this.options.targetInstanceId}get authorityLease(){return this.coordinator?.authorityLease??null}subscribeAuthority(t){return this.listeners.add(t),()=>{this.listeners.delete(t)}}currentConsumerBinding(){return this.identity&&this.lineage?this.consumerBinding(this.identity,this.lineage):null}async prepareConsumerBinding(){return this.preparing||(this.preparing=(async()=>{const t=await this.identityStore.acquire(this.handlerIdentity,this.journal),e={journalStoreId:t.journalStoreId,journalGeneration:t.journalGeneration};return this.lineage=e,this.identity=t,this.consumerBinding(t,e)})().finally(()=>{this.preparing=void 0})),await this.preparing}setRuntimeConsumerLive(t){if(this.started)throw new Error("runtime consumer availability is immutable while registered");this.runtimeConsumerLive=t}setRuntimeServiceMethods(t){if(this.started)throw new Error("runtime Service methods are immutable while registered");this.runtimeServiceMethods=intersectRuntimeServiceMethodsWithBackendCapabilities(t)}setRuntimeRealtimeCapabilityReady(t,e){const i=this.realtimeCapabilityReady.get(t)!==e;if(this.realtimeCapabilityReady.set(t,e),!i||!this.started)return;const r=this.lifecycleVersion;this.refreshRealtimeCapabilities(r).catch(t=>this.options.logger?.warn?.(`[runtime-authority] authority lease refresh fenced (realtime capability): ${t instanceof Error?t.message:t}`))}setRuntimeResourceBindingContractRevision(t){if(this.started)throw new Error("runtime resource binding revision is immutable while registered");if(void 0!==t&&t!==RUNTIME_RESOURCE_BINDING_CONTRACT_REVISION)throw new Error("unsupported runtime resource binding revision");this.resourceBindingContractRevision=t}async start(t){if(this.starting)return await this.starting;if(this.started)return;if(t?.aborted)return;const e=this.lifecycleVersion,i=this.startOnce(e,t).finally(()=>{this.starting===i&&(this.starting=void 0)});this.starting=i,await i}stop(){this.lifecycleVersion++,this.starting=void 0,this.started=!1;const t=this.coordinator;t&&this.detachCoordinator(t),this.publish(null)}async startOnce(t,e){if(await this.prepareConsumerBinding(),t!==this.lifecycleVersion||e?.aborted)return;this.lineage;const i=this.identity;this.started=!0,await this.activate(i,t)}async activate(t,e){if(!this.isLifecycleCurrent(e))return;if(this.resourceBindingContractRevision&&!this.runtimeConsumerLive)throw new Error("resource binding revision requires a live Message Service consumer");if(!this.runtimeConsumerLive||0===this.runtimeServiceMethods.length)throw new Error(`runtime registration refused: consumerLive=${this.runtimeConsumerLive} methods=${this.runtimeServiceMethods.length}`);const i=[...this.runtimeServiceMethods],r=runtimeCapabilities(this.runtimeServiceMethods,this.realtimeCapabilityReady);this.options.logger?.info?.(`[runtime-authority] advertising ${i.length} methods (renew-safe)`);const n=++this.coordinatorGeneration,o=new RuntimeRegistrationCoordinator(new InstanceAuthorityRuntimeRegistrationTransport({agentGatewayUrl:this.options.agentGatewayUrl,registrationId:t.registrationId,agentFetch:this.options.agentFetch}),{registrationId:t.registrationId,handlerIdentity:t.handlerIdentity,contractRevision:RUNTIME_RPC_CONTRACT_REVISION,runtimeMethods:i,capabilities:r,manifestDigest:this.manifestDigest,journalStoreId:t.journalStoreId,journalGeneration:t.journalGeneration,instanceIdentityKeyId:createHash("sha256").update(this.options.keys.publicKey).digest("hex"),targetInstanceId:this.options.targetInstanceId,signIdentityProof:t=>sign(null,t,createPrivateKey({key:privateKeyDer(this.options.keys.privateKey),format:"der",type:"pkcs8"})),getMSConnectionStatus:this.options.getMSConnectionStatus},(t,i)=>{if(!this.isCoordinatorCurrent(e,n,o))return;const r=i instanceof Error?i.message:void 0!==i?String(i):"";this.options.logger?.warn?.(r?`[runtime-authority] lease lost: ${t}: ${r}`:`[runtime-authority] lease lost: ${t}`);const s=i instanceof RuntimeRegistrationJournalAdvanceRequiredError?i:void 0;("expired"===t||s)&&this.rollExpired(e,n,o,s).catch(t=>this.options.logger?.warn?.(`[runtime-authority] authority lease roll fenced (expired registration): ${t instanceof Error?t.message:t}`))});this.coordinator=o,this.unsubscribeCoordinator=o.subscribeAuthority(t=>{this.isCoordinatorCurrent(e,n,o)&&(t&&this.authorityLeasePending?(this.authorityLeasePending=!1,this.options.logger?.info?.("[runtime-authority] registration active (recovered after handoff)")):t||(this.authorityLeasePending=!0),this.publish(t))});const s=await o.start();this.isCoordinatorCurrent(e,n,o)?(this.options.logger?.info?.("[runtime-authority] registration "+(s?"active":"pending")),s||(this.authorityLeasePending=!0)):this.stopCoordinatorOnce(o)}async rollExpired(t,e,i,r){if(!this.lineage||!this.isCoordinatorCurrent(t,e,i))return;const n=this.rolling.get(e);if(n)return await n;let o=!1;const s=(async()=>{if(!this.isCoordinatorCurrent(t,e,i))return;this.detachCoordinator(i),o=!0;const n=this.identity;if(!n)return;const s=await this.identityStore.roll(n,this.journal,{authorityCurrentGeneration:r?.currentJournalGeneration,expectedJournalGeneration:r?.expectedJournalGeneration});this.isLifecycleCurrent(t)&&void 0===this.coordinator&&(this.identity=s,this.lineage={journalStoreId:s.journalStoreId,journalGeneration:s.journalGeneration},await this.activate(s,t))})().finally(()=>{this.rolling.get(e)===s&&this.rolling.delete(e),o&&this.publishDetachedWithoutLease("expired registration roll")});this.rolling.set(e,s),await s}async refreshRealtimeCapabilities(t){if(!this.isLifecycleCurrent(t))return;const e=this.refreshing;if(e)return await e;let i=!1;const r=(async()=>{if(!this.isLifecycleCurrent(t))return;const e=this.identity,r=this.coordinator;e&&r&&(r.authorityLease?(this.detachCoordinator(r),i=!0,this.isLifecycleCurrent(t)&&void 0===this.coordinator&&await this.activate(e,t)):this.options.logger?.info?.("[runtime-authority] deferring realtime capability refresh until registration is active"))})().finally(()=>{this.refreshing===r&&(this.refreshing=void 0),i&&this.publishDetachedWithoutLease("realtime capability refresh")});this.refreshing=r,await r}isLifecycleCurrent(t){return this.started&&t===this.lifecycleVersion}isCoordinatorCurrent(t,e,i){return this.isLifecycleCurrent(t)&&this.coordinatorGeneration===e&&this.coordinator===i}detachCoordinator(t){this.coordinator===t&&(this.unsubscribeCoordinator?.(),this.unsubscribeCoordinator=void 0,this.coordinator=void 0),this.stopCoordinatorOnce(t)}publishDetachedWithoutLease(t){null===this.authorityLease&&(this.authorityLeasePending=!0,this.options.logger?.warn?.(`[runtime-authority] authority lease cleared with no live successor lease (${t})`),this.publish(null))}stopCoordinatorOnce(t){this.stoppedCoordinators.has(t)||(this.stoppedCoordinators.add(t),t.stop())}publish(t){for(const e of this.listeners)try{e(t)}catch{}}consumerBinding(t,e){return Object.freeze({handlerIdentity:t.handlerIdentity,journalStoreId:e.journalStoreId,journalGeneration:e.journalGeneration})}}const ED25519_PKCS8_PREFIX=Buffer.from("302e020100300506032b657004220420","hex"),privateKeyDer=t=>32===t.length?Buffer.concat([ED25519_PKCS8_PREFIX,Buffer.from(t)]):Buffer.from(t),REALTIME_CAPABILITY_NAMES=["terminal","canvas"];function runtimeCapabilities(t,e){const i=new Set(t),r=RUNTIME_METHOD_PARITY_MANIFEST.filter(t=>t.serviceAllowed&&void 0!==runtimeInvocationMethod(t)&&i.has(runtimeInvocationMethod(t))).map(t=>t.capability),n=REALTIME_CAPABILITY_NAMES.filter(t=>!0===e.get(t));return[...new Set([...r,...n])]}function runtimeInvocationMethod(t){return"methods"===t.serviceRoute&&t.serviceMethod?t.serviceMethod:Object.entries(CONVERSATION_RUNTIME_CANONICAL_COMMAND_NAMES).find(([,e])=>e===t.canonicalCommand)?.[0]}
1
+ import{createHash,createPrivateKey,randomUUID}from"node:crypto";import{join}from"node:path";import{CONVERSATION_RUNTIME_CANONICAL_COMMAND_NAMES,RUNTIME_METHOD_PARITY_MANIFEST,RUNTIME_RESOURCE_BINDING_CONTRACT_REVISION,RUNTIME_RPC_CONTRACT_REVISION}from"@beeos-ai/beeos-types/runtime";import{agentBoundFetch}from"../agent-auth.js";import{FileRuntimeCommandJournal}from"./runtime-command-journal.js";import{FileRuntimeAgentIdentityRegistry}from"./runtime-agent-identity-registry.js";import{RuntimeRegistrationIdentityStore}from"./runtime-registration-identity-store.js";import{InstanceAuthorityRuntimeRegistrationTransport,RuntimeRegistrationCoordinator,RuntimeRegistrationJournalAdvanceRequiredError}from"./runtime-registration.js";import{verifiedRuntimeParityManifestDigest}from"./runtime-parity-manifest-digest.js";import{intersectRuntimeServiceMethodsWithBackendCapabilities}from"./runtime-service-contract-compatibility.js";export function cloudRuntimeTargetInstanceId(t){const e=t.BEEOS_CLOUD_RUNTIME_INSTANCE_ID;if(void 0!==e){if(!/^[A-Za-z0-9_-]{1,128}$/.test(e)||e!==t.BEEOS_INSTANCE_ID)throw new Error("Cloud runtime target must match the provisioned BeeOS instance identity");return e}}export class RuntimeProductionAuthority{options;journal;identities;identityStore;handlerIdentity;listeners=new Set;coordinator;unsubscribeCoordinator;starting;rolling=new Map;refreshing;stoppedCoordinators=new WeakSet;coordinatorGeneration=0;lineage;identity;preparing;runtimeConsumerLive=!1;runtimeServiceMethods=Object.freeze([]);realtimeCapabilityReady=new Map;resourceBindingContractRevision;manifestDigest=verifiedRuntimeParityManifestDigest();started=!1;lifecycleVersion=0;authorityLeasePending=!1;boundAgentFetch;constructor(t){if(this.options=t,void 0!==t.targetInstanceId&&!/^[A-Za-z0-9_-]{1,128}$/.test(t.targetInstanceId))throw new Error("Cloud runtime target instance ID is invalid");this.handlerIdentity=t.handlerIdentity??(process.env.HOSTNAME?.trim()||process.env.POD_NAME?.trim()||randomUUID()),this.journal=new FileRuntimeCommandJournal(join(t.stateDirectory,"runtime-command-journal.json"),void 0,void 0,{onMaintenanceError:e=>{const i=`[runtime-journal] event=checkpoint_failed trigger=threshold error=${e instanceof Error?e.message:String(e)}`;t.logger?.error?t.logger.error(i):t.logger?.warn?.(i)}}),this.identities=new FileRuntimeAgentIdentityRegistry(join(t.stateDirectory,"runtime-agent-identities.json")),this.boundAgentFetch=t.boundAgentFetch??((e,i)=>agentBoundFetch(e,t.keys,i)),this.identityStore=new RuntimeRegistrationIdentityStore(join(t.stateDirectory,"runtime-registration-identity.json"))}get agentKeys(){return this.options.keys}get stateDirectory(){return this.options.stateDirectory}get cloudTargetInstanceId(){return this.options.targetInstanceId}get authorityLease(){return this.coordinator?.authorityLease??null}subscribeAuthority(t){return this.listeners.add(t),()=>{this.listeners.delete(t)}}currentConsumerBinding(){return this.identity&&this.lineage?this.consumerBinding(this.identity,this.lineage):null}async prepareConsumerBinding(){return this.preparing||(this.preparing=(async()=>{const t=await this.identityStore.acquire(this.handlerIdentity,this.journal),e={journalStoreId:t.journalStoreId,journalGeneration:t.journalGeneration};return this.lineage=e,this.identity=t,this.consumerBinding(t,e)})().finally(()=>{this.preparing=void 0})),await this.preparing}setRuntimeConsumerLive(t){if(this.started)throw new Error("runtime consumer availability is immutable while registered");this.runtimeConsumerLive=t}setRuntimeServiceMethods(t){if(this.started)throw new Error("runtime Service methods are immutable while registered");this.runtimeServiceMethods=intersectRuntimeServiceMethodsWithBackendCapabilities(t)}setRuntimeRealtimeCapabilityReady(t,e){const i=this.realtimeCapabilityReady.get(t)!==e;if(this.realtimeCapabilityReady.set(t,e),!i||!this.started)return;const r=this.lifecycleVersion;this.refreshRealtimeCapabilities(r).catch(t=>this.options.logger?.warn?.(`[runtime-authority] authority lease refresh fenced (realtime capability): ${t instanceof Error?t.message:t}`))}setRuntimeResourceBindingContractRevision(t){if(this.started)throw new Error("runtime resource binding revision is immutable while registered");if(void 0!==t&&t!==RUNTIME_RESOURCE_BINDING_CONTRACT_REVISION)throw new Error("unsupported runtime resource binding revision");this.resourceBindingContractRevision=t}async start(t){if(this.starting)return await this.starting;if(this.started)return;if(t?.aborted)return;const e=this.lifecycleVersion,i=this.startOnce(e,t).finally(()=>{this.starting===i&&(this.starting=void 0)});this.starting=i,await i}stop(){this.lifecycleVersion++,this.starting=void 0,this.started=!1;const t=this.coordinator;t&&this.detachCoordinator(t),this.publish(null)}async startOnce(t,e){if(await this.prepareConsumerBinding(),t!==this.lifecycleVersion||e?.aborted)return;this.lineage;const i=this.identity;this.started=!0,await this.activate(i,t)}async activate(t,e){if(!this.isLifecycleCurrent(e))return;if(this.resourceBindingContractRevision&&!this.runtimeConsumerLive)throw new Error("resource binding revision requires a live Message Service consumer");if(!this.runtimeConsumerLive||0===this.runtimeServiceMethods.length)throw new Error(`runtime registration refused: consumerLive=${this.runtimeConsumerLive} methods=${this.runtimeServiceMethods.length}`);const i=[...this.runtimeServiceMethods],r=runtimeCapabilities(this.runtimeServiceMethods,this.realtimeCapabilityReady);this.options.logger?.info?.(`[runtime-authority] advertising ${i.length} methods (renew-safe)`);const n=++this.coordinatorGeneration,o=new RuntimeRegistrationCoordinator(new InstanceAuthorityRuntimeRegistrationTransport({agentGatewayUrl:this.options.agentGatewayUrl,registrationId:t.registrationId,agentFetch:this.options.agentFetch}),{registrationId:t.registrationId,handlerIdentity:t.handlerIdentity,contractRevision:RUNTIME_RPC_CONTRACT_REVISION,runtimeMethods:i,capabilities:r,manifestDigest:this.manifestDigest,journalStoreId:t.journalStoreId,journalGeneration:t.journalGeneration,instanceIdentityKeyId:createHash("sha256").update(this.options.keys.publicKey).digest("hex"),targetInstanceId:this.options.targetInstanceId,privateKey:createPrivateKey({key:privateKeyDer(this.options.keys.privateKey),format:"der",type:"pkcs8"}),getMSConnectionStatus:this.options.getMSConnectionStatus},(t,i)=>{if(!this.isCoordinatorCurrent(e,n,o))return;const r=i instanceof Error?i.message:void 0!==i?String(i):"";this.options.logger?.warn?.(r?`[runtime-authority] lease lost: ${t}: ${r}`:`[runtime-authority] lease lost: ${t}`);const s=i instanceof RuntimeRegistrationJournalAdvanceRequiredError?i:void 0;("expired"===t||s)&&this.rollExpired(e,n,o,s).catch(t=>this.options.logger?.warn?.(`[runtime-authority] authority lease roll fenced (expired registration): ${t instanceof Error?t.message:t}`))});this.coordinator=o,this.unsubscribeCoordinator=o.subscribeAuthority(t=>{this.isCoordinatorCurrent(e,n,o)&&(t&&this.authorityLeasePending?(this.authorityLeasePending=!1,this.options.logger?.info?.("[runtime-authority] registration active (recovered after handoff)")):t||(this.authorityLeasePending=!0),this.publish(t))});const s=await o.start();this.isCoordinatorCurrent(e,n,o)?(this.options.logger?.info?.("[runtime-authority] registration "+(s?"active":"pending")),s||(this.authorityLeasePending=!0)):this.stopCoordinatorOnce(o)}async rollExpired(t,e,i,r){if(!this.lineage||!this.isCoordinatorCurrent(t,e,i))return;const n=this.rolling.get(e);if(n)return await n;let o=!1;const s=(async()=>{if(!this.isCoordinatorCurrent(t,e,i))return;this.detachCoordinator(i),o=!0;const n=this.identity;if(!n)return;const s=await this.identityStore.roll(n,this.journal,{authorityCurrentGeneration:r?.currentJournalGeneration,expectedJournalGeneration:r?.expectedJournalGeneration});this.isLifecycleCurrent(t)&&void 0===this.coordinator&&(this.identity=s,this.lineage={journalStoreId:s.journalStoreId,journalGeneration:s.journalGeneration},await this.activate(s,t))})().finally(()=>{this.rolling.get(e)===s&&this.rolling.delete(e),o&&this.publishDetachedWithoutLease("expired registration roll")});this.rolling.set(e,s),await s}async refreshRealtimeCapabilities(t){if(!this.isLifecycleCurrent(t))return;const e=this.refreshing;if(e)return await e;let i=!1;const r=(async()=>{if(!this.isLifecycleCurrent(t))return;const e=this.identity,r=this.coordinator;e&&r&&(r.authorityLease?(this.detachCoordinator(r),i=!0,this.isLifecycleCurrent(t)&&void 0===this.coordinator&&await this.activate(e,t)):this.options.logger?.info?.("[runtime-authority] deferring realtime capability refresh until registration is active"))})().finally(()=>{this.refreshing===r&&(this.refreshing=void 0),i&&this.publishDetachedWithoutLease("realtime capability refresh")});this.refreshing=r,await r}isLifecycleCurrent(t){return this.started&&t===this.lifecycleVersion}isCoordinatorCurrent(t,e,i){return this.isLifecycleCurrent(t)&&this.coordinatorGeneration===e&&this.coordinator===i}detachCoordinator(t){this.coordinator===t&&(this.unsubscribeCoordinator?.(),this.unsubscribeCoordinator=void 0,this.coordinator=void 0),this.stopCoordinatorOnce(t)}publishDetachedWithoutLease(t){null===this.authorityLease&&(this.authorityLeasePending=!0,this.options.logger?.warn?.(`[runtime-authority] authority lease cleared with no live successor lease (${t})`),this.publish(null))}stopCoordinatorOnce(t){this.stoppedCoordinators.has(t)||(this.stoppedCoordinators.add(t),t.stop())}publish(t){for(const e of this.listeners)try{e(t)}catch{}}consumerBinding(t,e){return Object.freeze({handlerIdentity:t.handlerIdentity,journalStoreId:e.journalStoreId,journalGeneration:e.journalGeneration})}}const ED25519_PKCS8_PREFIX=Buffer.from("302e020100300506032b657004220420","hex"),privateKeyDer=t=>32===t.length?Buffer.concat([ED25519_PKCS8_PREFIX,Buffer.from(t)]):Buffer.from(t),REALTIME_CAPABILITY_NAMES=["terminal","canvas"];function runtimeCapabilities(t,e){const i=new Set(t),r=RUNTIME_METHOD_PARITY_MANIFEST.filter(t=>t.serviceAllowed&&void 0!==runtimeInvocationMethod(t)&&i.has(runtimeInvocationMethod(t))).map(t=>t.capability),n=REALTIME_CAPABILITY_NAMES.filter(t=>!0===e.get(t));return[...new Set([...r,...n])]}function runtimeInvocationMethod(t){return"methods"===t.serviceRoute&&t.serviceMethod?t.serviceMethod:Object.entries(CONVERSATION_RUNTIME_CANONICAL_COMMAND_NAMES).find(([,e])=>e===t.canonicalCommand)?.[0]}
@@ -0,0 +1 @@
1
+ import{createHash,randomBytes,sign}from"node:crypto";import{RUNTIME_INTERNAL_API_V1_ROUTE_CONTRACTS,RUNTIME_HASH_DOMAINS,RUNTIME_RPC_PROTOCOL_VERSION,runtimeHashPreimage,runtimeIdentitySignatureMessage,validateRuntimeInternalRegistrationResult}from"@beeos-ai/beeos-types/runtime";import{RuntimeTransportFailurePolicy}from"./runtime-transport-failure-policy.js";export class RuntimeRegistrationLeaseExpiredError extends Error{constructor(){super("Instance authority returned an expired active lease"),this.name="RuntimeRegistrationLeaseExpiredError"}}export class RuntimeRegistrationHandoffRequiredError extends Error{retryAfterMs;constructor(e=65e3){super("Instance authority requires waiting for the active runtime lease to expire"),this.name="RuntimeRegistrationHandoffRequiredError",this.retryAfterMs=e}}export class RuntimeRegistrationOwnerResetRequiredError extends Error{constructor(){super("Instance authority requires owner reset authorization after a journal store change"),this.name="RuntimeRegistrationOwnerResetRequiredError"}}export class RuntimeRegistrationJournalAdvanceRequiredError extends Error{currentJournalGeneration;expectedJournalGeneration;constructor(e){super("Instance authority requires journal generation to advance exactly once after lease expiry"),this.name="RuntimeRegistrationJournalAdvanceRequiredError",this.currentJournalGeneration=e?.currentJournalGeneration,this.expectedJournalGeneration=e?.expectedJournalGeneration}}export class RuntimeRegistrationCoordinator{transport;config;onLeaseLost;now;active=null;timer;expiryTimer;stopped=!1;generation=0;heartbeatTransportFailurePolicy=new RuntimeTransportFailurePolicy;authorityListeners=new Set;constructor(e,t,i,r=Date.now){this.transport=e,this.config=t,this.onLeaseLost=i,this.now=r}get lease(){return!this.active||Date.parse(this.active.leaseExpiresAt)<=this.now()?null:this.active}get canClaimMutations(){return null!==this.lease}get authorityLease(){const e=this.lease;return e?Object.freeze({...e,handlerIdentity:this.config.handlerIdentity}):null}subscribeAuthority(e){return this.authorityListeners.add(e),()=>{this.authorityListeners.delete(e)}}async start(){const e=++this.generation;return this.stopped=!1,await this.registerOnce(e)}async registerOnce(e){if(!this.isCurrent(e))return null;this.timer=void 0,this.active&&(this.clearLeaseExpiry(),this.active=null,this.notifyAuthority());const t={registrationId:this.config.registrationId,handlerIdentity:this.config.handlerIdentity,contractRevision:this.config.contractRevision,runtimeRpcProtocolVersion:RUNTIME_RPC_PROTOCOL_VERSION,runtimeMethods:this.config.runtimeMethods,capabilities:this.config.capabilities,manifestDigest:this.config.manifestDigest,journalStoreId:this.config.journalStoreId,journalGeneration:this.config.journalGeneration};let i;try{i=await this.transport.register(this.signProof("runtime.register",t))}catch(t){if(!this.isCurrent(e))return null;if(t instanceof RuntimeRegistrationLeaseExpiredError||t instanceof RuntimeRegistrationJournalAdvanceRequiredError||t instanceof RuntimeRegistrationOwnerResetRequiredError)return this.onLeaseLost(t instanceof RuntimeRegistrationLeaseExpiredError?"expired":"heartbeat_failed",t),null;this.onLeaseLost("heartbeat_failed",t);const i=t instanceof RuntimeRegistrationHandoffRequiredError?t.retryAfterMs:5e3;return this.scheduleRegistration(i,e),null}return this.isCurrent(e)?"active"!==i.status?("fenced"===i.status?this.onLeaseLost("fenced"):this.scheduleRegistration(i.retryAfterMs,e),null):void 0!==this.config.targetInstanceId&&i.instanceId!==this.config.targetInstanceId?(this.onLeaseLost("fenced",new Error("Instance authority returned a different Cloud instance")),null):(this.active={instanceId:i.instanceId,leaseId:i.leaseId,runtimeEpoch:i.runtimeEpoch,runtimeLeaseCredential:i.runtimeLeaseCredential,leaseExpiresAt:i.leaseExpiresAt},this.heartbeatTransportFailurePolicy.recordSuccess(),this.notifyAuthority(),this.scheduleLeaseExpiry(this.active,e),this.schedule(i.heartbeatIntervalMs,e),this.active):null}stop(){this.generation++,this.stopped=!0,this.timer&&clearTimeout(this.timer),this.expiryTimer&&clearTimeout(this.expiryTimer),this.timer=void 0,this.expiryTimer=void 0,this.active=null,this.notifyAuthority()}schedule(e,t){if(!this.isCurrent(t))return;const i=this.active;i&&(this.timer=setTimeout(()=>{this.sendHeartbeat(e,t,i)},e),this.timer.unref?.())}scheduleRegistration(e,t){this.isCurrent(t)&&(this.timer=setTimeout(()=>{this.registerOnce(t)},e))}async sendHeartbeat(e,t,i){if(!this.isCurrentLease(t,i))return;const r={leaseId:i.leaseId,handlerIdentity:this.config.handlerIdentity,runtimeEpoch:i.runtimeEpoch},s=this.config.getMSConnectionStatus?{...r,msConnectionStatus:this.config.getMSConnectionStatus(i)}:r;try{const r=await this.transport.heartbeat(this.signProof("runtime.heartbeat",s));if(!this.isCurrentLease(t,i))return;if("renewed"!==r.status)return this.heartbeatTransportFailurePolicy.recordSuccess(),this.active=null,this.clearLeaseExpiry(),this.notifyAuthority(),void this.onLeaseLost(r.status);this.heartbeatTransportFailurePolicy.recordSuccess(),this.active={...i,runtimeLeaseCredential:r.runtimeLeaseCredential,leaseExpiresAt:r.leaseExpiresAt},this.notifyAuthority(),this.scheduleLeaseExpiry(this.active,t),this.schedule(e,t)}catch(r){if(!this.isCurrentLease(t,i))return;if(!this.heartbeatTransportFailurePolicy.recordFailure())return void this.schedule(Math.min(e,5e3),t);this.active=null,this.clearLeaseExpiry(),this.notifyAuthority(),this.onLeaseLost("heartbeat_failed",r),this.scheduleRegistration(5e3,t)}}scheduleLeaseExpiry(e,t){this.clearLeaseExpiry();const i=`${e.leaseId}\0${e.runtimeEpoch}\0${e.leaseExpiresAt}`,r=()=>{const s=Date.parse(e.leaseExpiresAt)-this.now();if(s>0)return this.expiryTimer=setTimeout(r,Math.min(2147483647,s)),void this.expiryTimer.unref?.();const n=this.active;this.isCurrentLease(t,e)&&n&&`${n.leaseId}\0${n.runtimeEpoch}\0${n.leaseExpiresAt}`===i&&(this.active=null,this.expiryTimer=void 0,this.notifyAuthority(),this.onLeaseLost("expired"))};r()}clearLeaseExpiry(){this.expiryTimer&&clearTimeout(this.expiryTimer),this.expiryTimer=void 0}isCurrent(e){return!this.stopped&&e===this.generation}isCurrentLease(e,t){return this.isCurrent(e)&&this.active===t}notifyAuthority(){const e=this.authorityLease;for(const t of this.authorityListeners)try{t(e)}catch{}}signProof(e,t){const i=new Date(this.now()).toISOString(),r=randomBytes(18).toString("base64url"),s="runtime.register"===e?RUNTIME_HASH_DOMAINS.registration:RUNTIME_HASH_DOMAINS.heartbeat,n=createHash("sha256").update(runtimeHashPreimage(s,t)).digest("hex"),a=runtimeIdentitySignatureMessage({purpose:e,instanceIdentityKeyId:this.config.instanceIdentityKeyId,targetInstanceId:this.config.targetInstanceId,signedAt:i,nonce:r,payloadHash:n});return{...t,signaturePurpose:e,instanceIdentityKeyId:this.config.instanceIdentityKeyId,signedAt:i,nonce:r,payloadHash:n,...void 0===this.config.targetInstanceId?{}:{targetInstanceId:this.config.targetInstanceId},signature:sign(null,a,this.config.privateKey).toString("base64url")}}}export class InstanceAuthorityRuntimeRegistrationTransport{options;constructor(e){this.options=e}async register(e){if(e.registrationId!==this.options.registrationId)throw new Error("runtime registration id mismatch");const t=await this.post(RUNTIME_INTERNAL_API_V1_ROUTE_CONTRACTS.runtimeRegister.path,e);if("active"===t.status&&Number.isFinite(Date.parse(t.leaseExpiresAt))&&Date.parse(t.leaseExpiresAt)<=Date.now())throw new RuntimeRegistrationLeaseExpiredError;if(validateRuntimeInternalRegistrationResult(t)||"fenced"!==t.status&&(t.journalStoreId!==e.journalStoreId||t.journalGeneration!==e.journalGeneration)||"active"===t.status&&(!t.instanceId||!t.leaseId||!t.runtimeLeaseCredential||!Number.isInteger(t.heartbeatIntervalMs)||!Number.isFinite(Date.parse(t.leaseExpiresAt))||Date.parse(t.leaseExpiresAt)<=Date.now()))throw new Error("Instance authority returned an invalid registration binding");return t}async heartbeat(e){const t=await this.post(RUNTIME_INTERNAL_API_V1_ROUTE_CONTRACTS.runtimeHeartbeat.path.replace("{registrationId}",encodeURIComponent(this.options.registrationId)),e);if(!(t&&["renewed","fenced","expired"].includes(t.status)&&("renewed"!==t.status||t.runtimeLeaseCredential&&Number.isFinite(Date.parse(t.leaseExpiresAt)))))throw new Error("Instance authority returned an invalid heartbeat result");return t}async post(e,t){const i=new URL(e,this.options.agentGatewayUrl),r=await this.options.agentFetch(i,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify(t)});if(!r.ok){const e=await r.text().catch(()=>"");if(409===r.status&&i.pathname.endsWith("/runtime/registrations"))throw classifyRuntimeRegistrationConflict(e);throw new Error(`runtime Instance authority request failed (${r.status} ${r.statusText}) ${i.pathname}`+(e?`: ${e.slice(0,240)}`:""))}return await r.json()}}export function classifyRuntimeRegistrationConflict(e){const t=extractConflictMessage(e);if(/journal store change requires owner reset authorization/i.test(t))return new RuntimeRegistrationOwnerResetRequiredError;if(/journal generation must advance exactly once/i.test(t)){const e=t.match(/\bcurrent=(\d+)\b/i)?.[1],i=t.match(/\bexpected=(\d+)\b/i)?.[1];return new RuntimeRegistrationJournalAdvanceRequiredError({currentJournalGeneration:e&&/^[1-9][0-9]*$/.test(e)?e:void 0,expectedJournalGeneration:i&&/^[1-9][0-9]*$/.test(i)?i:void 0})}return/active runtime lease requires explicit handoff/i.test(t)||/waiting for the active runtime lease/i.test(t),new RuntimeRegistrationHandoffRequiredError}function extractConflictMessage(e){if(!e)return"";try{const t=JSON.parse(e),i=t.error?.message;if("string"==typeof i)return i;if("string"==typeof t.message)return t.message}catch{}return e}
@@ -1 +1 @@
1
- import{createHash}from"node:crypto";import{readFileSync}from"node:fs";import{RUNTIME_METHOD_PARITY_MANIFEST,RUNTIME_RPC_CONTRACT_REVISION}from"@beeos-ai/beeos-types/runtime";import{RUNTIME_AUTHORITY_PORTS_REVISION,RUNTIME_AUTHORITY_PORTS_RAW_SHA256,RUNTIME_AUTHORITY_PORTS_SHA256,RUNTIME_MESSAGE_CHANNEL_REVISION,RUNTIME_MESSAGE_CHANNEL_RAW_SHA256,RUNTIME_MESSAGE_CHANNEL_SHA256,RUNTIME_PARITY_MANIFEST,RUNTIME_PARITY_MANIFEST_RAW_SHA256,RUNTIME_PARITY_MANIFEST_REVISION,RUNTIME_PARITY_MANIFEST_SHA256,RUNTIME_PARITY_READY_METHODS,RUNTIME_SESSION_CANCEL_REVISION,RUNTIME_SESSION_CANCEL_SHA256}from"./runtime-parity-manifest.js";import{verifiedRuntimeParityManifestDigest}from"./runtime-parity-manifest-digest.js";export const RUNTIME_SERVICE_WIRE_CONTRACT_ARTIFACTS=Object.freeze([Object.freeze({revision:RUNTIME_MESSAGE_CHANNEL_REVISION,sha256:RUNTIME_MESSAGE_CHANNEL_SHA256,rawSha256:RUNTIME_MESSAGE_CHANNEL_RAW_SHA256}),Object.freeze({revision:RUNTIME_AUTHORITY_PORTS_REVISION,sha256:RUNTIME_AUTHORITY_PORTS_SHA256,rawSha256:RUNTIME_AUTHORITY_PORTS_RAW_SHA256}),Object.freeze({revision:RUNTIME_SESSION_CANCEL_REVISION,sha256:RUNTIME_SESSION_CANCEL_SHA256}),Object.freeze({revision:RUNTIME_PARITY_MANIFEST_REVISION,sha256:RUNTIME_PARITY_MANIFEST_SHA256})]);export function loadRuntimeServiceContractIdentity(){return verifyPackagedRuntimeServiceWireIdentity(),Object.freeze({contractRevision:RUNTIME_RPC_CONTRACT_REVISION,canonicalManifestDigest:verifiedRuntimeParityManifestDigest(),wireArtifacts:RUNTIME_SERVICE_WIRE_CONTRACT_ARTIFACTS})}const RUNTIME_SERVICE_WIRE_IDENTITY_RAW_SHA256="fce53f44ff965c7f1ca0aab2cc7126eaf0b0a172b2891a57d8b59c2f6db12e93";function verifyPackagedRuntimeServiceWireIdentity(){verifyRuntimeServiceWireIdentityBytes(readFileSync(new URL("../../runtime-service-wire-identity.v1.json",import.meta.url)))}export function verifyRuntimeServiceWireIdentityBytes(e){if(createHash("sha256").update(e).digest("hex")!==RUNTIME_SERVICE_WIRE_IDENTITY_RAW_SHA256)throw new Error("packaged runtime Service wire identity bytes drifted");const i=JSON.parse(Buffer.from(e).toString("utf8"));if(1!==i.schemaVersion||"RFC8785/JCS"!==i.canonicalization||"SHA-256"!==i.hashAlgorithm||"@beeos-ai/beeos-types"!==i.canonicalTypes?.package||"4.4.2-beta.2"!==i.canonicalTypes.version||i.canonicalTypes.runtimeRpcContractRevision!==RUNTIME_RPC_CONTRACT_REVISION||"2af68ff555d4a4aa3c7b19a285e48a3df2ef9b43aa618ffddabbebc6e4280162"!==i.canonicalTypes.runtimeMethodManifestRawSHA256||i.canonicalTypes.runtimeMethodManifestJcsSHA256!==verifiedRuntimeParityManifestDigest())throw new Error("packaged runtime Service canonical Types identity drifted");const t=new Map([["runtime-parity-manifest",["openapi/runtime-parity-manifest-v1.json",RUNTIME_PARITY_MANIFEST_REVISION,RUNTIME_PARITY_MANIFEST_RAW_SHA256,RUNTIME_PARITY_MANIFEST_SHA256]],["runtime-message-channel",["openapi/runtime-message-channel-v1.contract.json",RUNTIME_MESSAGE_CHANNEL_REVISION,RUNTIME_MESSAGE_CHANNEL_RAW_SHA256,RUNTIME_MESSAGE_CHANNEL_SHA256]],["runtime-authority-ports",["openapi/runtime-authority-ports-v1.contract.json",RUNTIME_AUTHORITY_PORTS_REVISION,RUNTIME_AUTHORITY_PORTS_RAW_SHA256,RUNTIME_AUTHORITY_PORTS_SHA256]]]);if(!Array.isArray(i.artifacts)||i.artifacts.length!==t.size)throw new Error("packaged runtime Service wire artifact set drifted");for(const e of i.artifacts){const i=t.get(e.name);if(!i||e.path!==i[0]||e.revision!==i[1]||e.rawSHA256!==i[2]||e.jcsSHA256!==i[3])throw new Error(`packaged runtime Service wire artifact ${e.name||"<missing>"} drifted`);t.delete(e.name)}if(0!==t.size)throw new Error("packaged runtime Service wire artifact is missing")}export function intersectRuntimeServiceMethodsWithBackendCapabilities(e){const i=RUNTIME_PARITY_READY_METHODS,t=new Set(RUNTIME_PARITY_MANIFEST.map(e=>e.method)),r=new Set;for(const e of RUNTIME_METHOD_PARITY_MANIFEST)e.serviceAllowed&&t.has(e.acpMethod)&&(e.minimumRuntimeContractRevision&&e.minimumRuntimeContractRevision!==RUNTIME_RPC_CONTRACT_REVISION||r.add(e.acpMethod));return Object.freeze([...new Set(e)].filter(e=>i.has(e)&&r.has(e)))}export function runtimeServiceContractRequirement(){return`Types ${RUNTIME_RPC_CONTRACT_REVISION}; canonical-manifest ${verifiedRuntimeParityManifestDigest()}; `+RUNTIME_SERVICE_WIRE_CONTRACT_ARTIFACTS.map(e=>`${e.revision}:${e.sha256}`).join("; ")}
1
+ import{createHash}from"node:crypto";import{readFileSync}from"node:fs";import{RUNTIME_METHOD_PARITY_MANIFEST,RUNTIME_RPC_CONTRACT_REVISION}from"@beeos-ai/beeos-types/runtime";import{RUNTIME_AUTHORITY_PORTS_REVISION,RUNTIME_AUTHORITY_PORTS_RAW_SHA256,RUNTIME_AUTHORITY_PORTS_SHA256,RUNTIME_MESSAGE_CHANNEL_REVISION,RUNTIME_MESSAGE_CHANNEL_RAW_SHA256,RUNTIME_MESSAGE_CHANNEL_SHA256,RUNTIME_PARITY_MANIFEST,RUNTIME_PARITY_MANIFEST_RAW_SHA256,RUNTIME_PARITY_MANIFEST_REVISION,RUNTIME_PARITY_MANIFEST_SHA256,RUNTIME_PARITY_READY_METHODS,RUNTIME_SESSION_CANCEL_REVISION,RUNTIME_SESSION_CANCEL_SHA256}from"./runtime-parity-manifest.js";import{verifiedRuntimeParityManifestDigest}from"./runtime-parity-manifest-digest.js";export const RUNTIME_SERVICE_WIRE_CONTRACT_ARTIFACTS=Object.freeze([Object.freeze({revision:RUNTIME_MESSAGE_CHANNEL_REVISION,sha256:RUNTIME_MESSAGE_CHANNEL_SHA256,rawSha256:RUNTIME_MESSAGE_CHANNEL_RAW_SHA256}),Object.freeze({revision:RUNTIME_AUTHORITY_PORTS_REVISION,sha256:RUNTIME_AUTHORITY_PORTS_SHA256,rawSha256:RUNTIME_AUTHORITY_PORTS_RAW_SHA256}),Object.freeze({revision:RUNTIME_SESSION_CANCEL_REVISION,sha256:RUNTIME_SESSION_CANCEL_SHA256}),Object.freeze({revision:RUNTIME_PARITY_MANIFEST_REVISION,sha256:RUNTIME_PARITY_MANIFEST_SHA256})]);export function loadRuntimeServiceContractIdentity(){return verifyPackagedRuntimeServiceWireIdentity(),Object.freeze({contractRevision:RUNTIME_RPC_CONTRACT_REVISION,canonicalManifestDigest:verifiedRuntimeParityManifestDigest(),wireArtifacts:RUNTIME_SERVICE_WIRE_CONTRACT_ARTIFACTS})}const RUNTIME_SERVICE_WIRE_IDENTITY_RAW_SHA256="edf6b139618b481051761403ce71dbb61c67b23bb319464fedf5fddaebe10b72";function verifyPackagedRuntimeServiceWireIdentity(){verifyRuntimeServiceWireIdentityBytes(readFileSync(new URL("../../runtime-service-wire-identity.v1.json",import.meta.url)))}export function verifyRuntimeServiceWireIdentityBytes(e){if(createHash("sha256").update(e).digest("hex")!==RUNTIME_SERVICE_WIRE_IDENTITY_RAW_SHA256)throw new Error("packaged runtime Service wire identity bytes drifted");const i=JSON.parse(Buffer.from(e).toString("utf8"));if(1!==i.schemaVersion||"RFC8785/JCS"!==i.canonicalization||"SHA-256"!==i.hashAlgorithm||"@beeos-ai/beeos-types"!==i.canonicalTypes?.package||"4.4.2-cloud.3"!==i.canonicalTypes.version||i.canonicalTypes.runtimeRpcContractRevision!==RUNTIME_RPC_CONTRACT_REVISION||"f75832c57a18ca614e946a3b1d194828df76d600907f378efe28633df94146fc"!==i.canonicalTypes.runtimeMethodManifestRawSHA256||i.canonicalTypes.runtimeMethodManifestJcsSHA256!==verifiedRuntimeParityManifestDigest())throw new Error("packaged runtime Service canonical Types identity drifted");const t=new Map([["runtime-parity-manifest",["openapi/runtime-parity-manifest-v1.json",RUNTIME_PARITY_MANIFEST_REVISION,RUNTIME_PARITY_MANIFEST_RAW_SHA256,RUNTIME_PARITY_MANIFEST_SHA256]],["runtime-message-channel",["openapi/runtime-message-channel-v1.contract.json",RUNTIME_MESSAGE_CHANNEL_REVISION,RUNTIME_MESSAGE_CHANNEL_RAW_SHA256,RUNTIME_MESSAGE_CHANNEL_SHA256]],["runtime-authority-ports",["openapi/runtime-authority-ports-v1.contract.json",RUNTIME_AUTHORITY_PORTS_REVISION,RUNTIME_AUTHORITY_PORTS_RAW_SHA256,RUNTIME_AUTHORITY_PORTS_SHA256]]]);if(!Array.isArray(i.artifacts)||i.artifacts.length!==t.size)throw new Error("packaged runtime Service wire artifact set drifted");for(const e of i.artifacts){const i=t.get(e.name);if(!i||e.path!==i[0]||e.revision!==i[1]||e.rawSHA256!==i[2]||e.jcsSHA256!==i[3])throw new Error(`packaged runtime Service wire artifact ${e.name||"<missing>"} drifted`);t.delete(e.name)}if(0!==t.size)throw new Error("packaged runtime Service wire artifact is missing")}export function intersectRuntimeServiceMethodsWithBackendCapabilities(e){const i=RUNTIME_PARITY_READY_METHODS,t=new Set(RUNTIME_PARITY_MANIFEST.map(e=>e.method)),r=new Set;for(const e of RUNTIME_METHOD_PARITY_MANIFEST)e.serviceAllowed&&t.has(e.acpMethod)&&(e.minimumRuntimeContractRevision&&e.minimumRuntimeContractRevision!==RUNTIME_RPC_CONTRACT_REVISION||r.add(e.acpMethod));return Object.freeze([...new Set(e)].filter(e=>i.has(e)&&r.has(e)))}export function runtimeServiceContractRequirement(){return`Types ${RUNTIME_RPC_CONTRACT_REVISION}; canonical-manifest ${verifiedRuntimeParityManifestDigest()}; `+RUNTIME_SERVICE_WIRE_CONTRACT_ARTIFACTS.map(e=>`${e.revision}:${e.sha256}`).join("; ")}
@@ -1 +1 @@
1
- import{RUNTIME_RESOURCE_BINDING_CONTRACT_REVISION}from"@beeos-ai/beeos-types/runtime";import{parseAndValidateTemplatePackage}from"../acp-gateway/agent-template-package.js";import{HttpRuntimeOperationResourceResolver,RuntimeResourceResolutionError}from"./runtime-operation-resource-resolver.js";import{MessageServiceConversationClearHttpAuthority}from"./message-service-conversation-clear-http-authority.js";import{MessageServiceConversationHistoryBoundary}from"./message-service-conversation-history-boundary.js";import{MessageServiceSessionModelHttpAuthority}from"./message-service-session-model-http-authority.js";import{MsRuntimeMessageChannelConsumerFactory}from"./ms-runtime-message-channel-consumer-factory.js";import{RuntimeJwksVerifier}from"./runtime-jwks-verifier.js";import{CloudGatewayGrantJwksVerifier,GatewayGrantJwksVerifier}from"./split-runtime-authorization-verifier.js";import{RUNTIME_MESSAGE_CHANNEL_REVISION,RUNTIME_PARITY_MANIFEST}from"./runtime-parity-manifest.js";import{intersectRuntimeServiceMethodsWithBackendCapabilities,loadRuntimeServiceContractIdentity,runtimeServiceContractRequirement}from"./runtime-service-contract-compatibility.js";const CONTRACT_ARTIFACT_BLOCKER=Object.freeze({id:"contract_artifact_mismatch",owner:"backend",requiredContract:"frozen Types manifest and Service wire artifacts"}),TRANSPORT_BLOCKER=Object.freeze({id:"message_channel_transport",owner:"backend",requiredContract:`${RUNTIME_MESSAGE_CHANNEL_REVISION} HTTP delivery/history/append authority`}),JWKS_BLOCKER=Object.freeze({id:"gateway_grant_jwks",owner:"backend",requiredContract:"Purpose-bound Gateway runtime execution-grant JWKS proxy"}),POISON_JOURNAL_BLOCKER=Object.freeze({id:"poison_journal",owner:"openclaw_runtime_client",requiredContract:"Durable bounded runtime poison-delivery journal"}),grantAdapterOptions=new WeakMap;export class AgentGatewayGrantJwksReadinessAdapter{constructor(e){grantAdapterOptions.set(this,{agentGatewayUrl:new URL(e.agentGatewayUrl).toString()})}}export class AgentGatewaySplitJwksReadinessAdapter extends AgentGatewayGrantJwksReadinessAdapter{}const transportOptions=new WeakMap;export class RuntimeServiceTransportReadinessAdapter{constructor(e){transportOptions.set(this,{messageService:e.messageService,messageClient:e.messageClient,configuredServiceUrl:e.configuredServiceUrl,scopedDeliveryKey:e.scopedDeliveryKey,agentGatewayUrl:new URL(e.agentGatewayUrl).toString(),boundAgentFetch:e.boundAgentFetch})}}const cloudTransportOptions=new WeakMap;export class CloudRuntimeServiceTransportReadinessAdapter{constructor(e){cloudTransportOptions.set(this,{...e,agentGatewayUrl:new URL(e.agentGatewayUrl).toString()})}}const CAPABILITY_BLOCKERS=Object.freeze(RUNTIME_PARITY_MANIFEST.filter(e=>"blocked"===e.status).map(e=>Object.freeze({method:e.method,owner:e.blockerOwner,requiredContract:e.contractRevision})));export async function createRuntimeServiceProductionCandidate(e){try{loadRuntimeServiceContractIdentity()}catch{return{status:"unavailable",blockers:Object.freeze([{...CONTRACT_ARTIFACT_BLOCKER,requiredContract:contractArtifactRequirement()}])}}if(!e.poisonJournal)return{status:"unavailable",blockers:Object.freeze([POISON_JOURNAL_BLOCKER])};const r=e.cloudTransportAdapter?cloudTransportOptions.get(e.cloudTransportAdapter):void 0;let t;if(e.authorizationAdapter)try{t=await probeGatewayGrantJwks(e.authorizationAdapter,Boolean(r))}catch{}if(r){if(!r.configuredServiceUrl)return{status:"unavailable",blockers:Object.freeze([TRANSPORT_BLOCKER,...t?[]:[JWKS_BLOCKER]])};if(!(t instanceof CloudGatewayGrantJwksVerifier))return{status:"unavailable",blockers:Object.freeze([JWKS_BLOCKER])};const n=intersectRuntimeServiceMethodsWithBackendCapabilities(["models/list"]);return n.includes("models/list")?{status:"ready",blockers:[],capabilityBlockers:[],serviceMethods:n,factory:new MsRuntimeMessageChannelConsumerFactory({runtimeDelivery:r.runtimeDelivery,verifier:t,ports:e.attemptPorts??runtimeMessageAttemptPorts(r,r.configuredServiceUrl),journal:e.commandJournal,allowedMethods:new Set(n),options:{poisonJournal:e.poisonJournal,requestSchemaVersion:2,...e.onConsumerDeliveryError?{onDeliveryError:e.onConsumerDeliveryError}:{},...e.onConsumerLeaseStarvation?{onLeaseStarvation:e.onConsumerLeaseStarvation}:{},...e.onConsumerRequestTerminalAccepted?{onRequestTerminalAccepted:e.onConsumerRequestTerminalAccepted}:{},...e.onConsumerRequestAcknowledged?{onRequestAcknowledged:e.onConsumerRequestAcknowledged}:{},...e.onConsumerRequestFailure?{onRequestFailure:e.onConsumerRequestFailure}:{}}})}:{status:"unavailable",blockers:Object.freeze([CONTRACT_ARTIFACT_BLOCKER])}}const n=e.transportAdapter?transportOptions.get(e.transportAdapter):void 0;if(!n?.messageClient||!n.configuredServiceUrl)return{status:"unavailable",blockers:Object.freeze([TRANSPORT_BLOCKER,...t?[]:[JWKS_BLOCKER]])};if(!t)return{status:"unavailable",blockers:Object.freeze([JWKS_BLOCKER])};const a=runtimeMethods(),o=e.attemptPorts??runtimeMessageAttemptPorts(n,n.configuredServiceUrl);return{status:"ready",blockers:[],capabilityBlockers:CAPABILITY_BLOCKERS,serviceMethods:a,factory:new MsRuntimeMessageChannelConsumerFactory({client:n.messageClient,verifier:t,ports:o,journal:e.commandJournal,allowedMethods:new Set(a),options:{poisonJournal:e.poisonJournal,...e.onConsumerDeliveryError?{onDeliveryError:e.onConsumerDeliveryError}:{},...e.onConsumerLeaseStarvation?{onLeaseStarvation:e.onConsumerLeaseStarvation}:{},...e.onConsumerRequestTerminalAccepted?{onRequestTerminalAccepted:e.onConsumerRequestTerminalAccepted}:{},...e.onConsumerRequestAcknowledged?{onRequestAcknowledged:e.onConsumerRequestAcknowledged}:{},...e.onConsumerRequestFailure?{onRequestFailure:e.onConsumerRequestFailure}:{}}}),...void 0===e.attemptPorts?{resourceBindingContractRevision:RUNTIME_RESOURCE_BINDING_CONTRACT_REVISION}:{}}}async function probeGatewayGrantJwks(e,r){const t=grantAdapterOptions.get(e);if(!t)throw new Error("untrusted Gateway grant readiness adapter");const n=t.agentGatewayUrl.replace(/\/+$/,""),a=new RuntimeJwksVerifier(`${n}/.well-known/runtime-execution-grant-jwks.json`),o=r?new CloudGatewayGrantJwksVerifier(a):new GatewayGrantJwksVerifier(a);return await o.refresh(!0),o}export function runtimeMessageAttemptPorts(e,r){return{async resolve({request:t,command:n,executionGrant:a,getLease:o,journalStoreId:s,journalGeneration:i,journalRecord:c,abortSignal:u}){const d="resourceBindings"in t?t.resourceBindings:void 0,l="resourceBindingContractRevision"in t?t.resourceBindingContractRevision:void 0,m="skills.install"===n.name||"agent.applyTemplate"===n.name?n.params.artifact:void 0,p="mcp.prepare"===n.name||"mcp.set"===n.name?n.params.definition:void 0;if((m||p)&&(l!==RUNTIME_RESOURCE_BINDING_CONTRACT_REVISION||!d))throw new RuntimeResourceResolutionError("resolution_rejected","resource-bound Service delivery requires the Types 4.1 binding carrier");const R=new HttpRuntimeOperationResourceResolver({gatewayUrl:e.agentGatewayUrl,operationId:t.operationId,requestMessageId:t.requestMessageId,instanceId:t.instanceId,paramsHash:t.paramsHash,bindings:d??[],getAuthorization:()=>{const e=o();return{executionGrant:a,runtimeLeaseCredential:e.runtimeLeaseCredential,fence:deliveryFence(t,e,s,i)}},fetchAuthorized:async(r,t)=>await e.boundAgentFetch(r,t??{}),parseSkillArtifact:e=>parseAndValidateTemplatePackage(Buffer.from(e)),signal:u});if("prepared"!==c.phase&&"started"!==c.phase||(m&&await R.readVerifiedArtifact(m),p&&await R.resolveMcpDefinition(p)),"session.setModel"===n.name){const n=e.messageService;if(!n)throw new Error("Cloud runtime cannot project a personal session model");return{resources:R,sessionModelProjection:{project:async({conversationId:e,modelOverrideId:a})=>{const s=o(),i=new MessageServiceSessionModelHttpAuthority({messageServiceUrl:r,fetchAuthorized:async(e,r)=>await n.fetchRuntimeChannel(e,{runtimeLeaseCredential:s.runtimeLeaseCredential},r)});return await i.project({conversationId:e,operationId:t.operationId,requestMessageId:t.requestMessageId,requestHash:t.requestHash,modelOverrideId:a,runtimeLeaseCredential:s.runtimeLeaseCredential})}}}}if("session.clear"!==n.name)return{resources:R};const C=e.messageService;if(!C)throw new Error("Cloud runtime cannot access personal conversation history");n.context.conversation;const v=o(),y=new MessageServiceConversationClearHttpAuthority({messageServiceUrl:r,runtimeLeaseCredential:v.runtimeLeaseCredential,fetchAuthorized:async(e,r)=>await C.fetchRuntimeChannel(e,{runtimeLeaseCredential:v.runtimeLeaseCredential},r)});return{resources:R,historyBoundary:new MessageServiceConversationHistoryBoundary({operationId:t.operationId,requestMessageId:t.requestMessageId,requestHash:t.requestHash,fence:deliveryFence(t,v,s,i),authority:y})}}}}function runtimeMethods(){return[...intersectRuntimeServiceMethodsWithBackendCapabilities(RUNTIME_PARITY_MANIFEST.filter(e=>"ready"===e.status).map(e=>e.method))]}function contractArtifactRequirement(){try{return runtimeServiceContractRequirement()}catch{return CONTRACT_ARTIFACT_BLOCKER.requiredContract}}function deliveryFence(e,r,t,n){if(r.handlerIdentity!==e.runtimeHandlerIdentity||r.runtimeEpoch!==e.dispatchEpoch)throw new Error("runtime delivery fence does not match the retained request");return{claimId:e.requestMessageId,handlerIdentity:r.handlerIdentity,runtimeEpoch:r.runtimeEpoch,fencingToken:r.runtimeEpoch,journalStoreId:t,journalGeneration:n}}
1
+ import{RUNTIME_RESOURCE_BINDING_CONTRACT_REVISION}from"@beeos-ai/beeos-types/runtime";import{TemplatePackageError,parseAndValidateTemplatePackage}from"../acp-gateway/agent-template-package.js";import{HttpRuntimeOperationResourceResolver,RuntimeResourceResolutionError}from"./runtime-operation-resource-resolver.js";import{MessageServiceConversationClearHttpAuthority}from"./message-service-conversation-clear-http-authority.js";import{MessageServiceConversationHistoryBoundary}from"./message-service-conversation-history-boundary.js";import{MessageServiceSessionModelHttpAuthority}from"./message-service-session-model-http-authority.js";import{MsRuntimeMessageChannelConsumerFactory}from"./ms-runtime-message-channel-consumer-factory.js";import{RuntimeJwksVerifier}from"./runtime-jwks-verifier.js";import{CloudGatewayGrantJwksVerifier,GatewayGrantJwksVerifier}from"./split-runtime-authorization-verifier.js";import{RUNTIME_MESSAGE_CHANNEL_REVISION,RUNTIME_PARITY_MANIFEST}from"./runtime-parity-manifest.js";import{intersectRuntimeServiceMethodsWithBackendCapabilities,loadRuntimeServiceContractIdentity,runtimeServiceContractRequirement}from"./runtime-service-contract-compatibility.js";const CONTRACT_ARTIFACT_BLOCKER=Object.freeze({id:"contract_artifact_mismatch",owner:"backend",requiredContract:"frozen Types manifest and Service wire artifacts"}),TRANSPORT_BLOCKER=Object.freeze({id:"message_channel_transport",owner:"backend",requiredContract:`${RUNTIME_MESSAGE_CHANNEL_REVISION} HTTP delivery/history/append authority`}),JWKS_BLOCKER=Object.freeze({id:"gateway_grant_jwks",owner:"backend",requiredContract:"Purpose-bound Gateway runtime execution-grant JWKS proxy"}),POISON_JOURNAL_BLOCKER=Object.freeze({id:"poison_journal",owner:"openclaw_runtime_client",requiredContract:"Durable bounded runtime poison-delivery journal"}),grantAdapterOptions=new WeakMap;export class AgentGatewayGrantJwksReadinessAdapter{constructor(e){grantAdapterOptions.set(this,{agentGatewayUrl:new URL(e.agentGatewayUrl).toString()})}}export class AgentGatewaySplitJwksReadinessAdapter extends AgentGatewayGrantJwksReadinessAdapter{}const transportOptions=new WeakMap;export class RuntimeServiceTransportReadinessAdapter{constructor(e){transportOptions.set(this,{messageService:e.messageService,messageClient:e.messageClient,configuredServiceUrl:e.configuredServiceUrl,scopedDeliveryKey:e.scopedDeliveryKey,agentGatewayUrl:new URL(e.agentGatewayUrl).toString(),boundAgentFetch:e.boundAgentFetch})}}const cloudTransportOptions=new WeakMap;export class CloudRuntimeServiceTransportReadinessAdapter{constructor(e){cloudTransportOptions.set(this,{...e,agentGatewayUrl:new URL(e.agentGatewayUrl).toString()})}}const CAPABILITY_BLOCKERS=Object.freeze(RUNTIME_PARITY_MANIFEST.filter(e=>"blocked"===e.status).map(e=>Object.freeze({method:e.method,owner:e.blockerOwner,requiredContract:e.contractRevision})));export async function createRuntimeServiceProductionCandidate(e){try{loadRuntimeServiceContractIdentity()}catch{return{status:"unavailable",blockers:Object.freeze([{...CONTRACT_ARTIFACT_BLOCKER,requiredContract:contractArtifactRequirement()}])}}if(!e.poisonJournal)return{status:"unavailable",blockers:Object.freeze([POISON_JOURNAL_BLOCKER])};const t=e.cloudTransportAdapter?cloudTransportOptions.get(e.cloudTransportAdapter):void 0;let r;if(e.authorizationAdapter)try{r=await probeGatewayGrantJwks(e.authorizationAdapter,Boolean(t))}catch{}if(t){if(!t.configuredServiceUrl)return{status:"unavailable",blockers:Object.freeze([TRANSPORT_BLOCKER,...r?[]:[JWKS_BLOCKER]])};if(!(r instanceof CloudGatewayGrantJwksVerifier))return{status:"unavailable",blockers:Object.freeze([JWKS_BLOCKER])};const n=intersectRuntimeServiceMethodsWithBackendCapabilities(["models/list","agent/create","skills/list","skills/update","skills/uninstall","skills/install"]);return n.includes("models/list")?{status:"ready",blockers:[],capabilityBlockers:[],serviceMethods:n,factory:new MsRuntimeMessageChannelConsumerFactory({runtimeDelivery:t.runtimeDelivery,...t.nativePrompt?{nativePrompt:t.nativePrompt}:{},...t.sessionNewConversation?{sessionNewConversation:t.sessionNewConversation}:{},verifier:r,ports:e.attemptPorts??runtimeMessageAttemptPorts(t,t.configuredServiceUrl),journal:e.commandJournal,allowedMethods:new Set(n),options:{poisonJournal:e.poisonJournal,requestSchemaVersion:2,...e.onConsumerDeliveryError?{onDeliveryError:e.onConsumerDeliveryError}:{},...e.onConsumerLeaseStarvation?{onLeaseStarvation:e.onConsumerLeaseStarvation}:{},...e.onConsumerRequestTerminalAccepted?{onRequestTerminalAccepted:e.onConsumerRequestTerminalAccepted}:{},...e.onConsumerRequestAcknowledged?{onRequestAcknowledged:e.onConsumerRequestAcknowledged}:{},...e.onConsumerRequestFailure?{onRequestFailure:e.onConsumerRequestFailure}:{}}})}:{status:"unavailable",blockers:Object.freeze([CONTRACT_ARTIFACT_BLOCKER])}}const n=e.transportAdapter?transportOptions.get(e.transportAdapter):void 0;if(!n?.messageClient||!n.configuredServiceUrl)return{status:"unavailable",blockers:Object.freeze([TRANSPORT_BLOCKER,...r?[]:[JWKS_BLOCKER]])};if(!r)return{status:"unavailable",blockers:Object.freeze([JWKS_BLOCKER])};const a=runtimeMethods(),o=e.attemptPorts??runtimeMessageAttemptPorts(n,n.configuredServiceUrl);return{status:"ready",blockers:[],capabilityBlockers:CAPABILITY_BLOCKERS,serviceMethods:a,factory:new MsRuntimeMessageChannelConsumerFactory({client:n.messageClient,verifier:r,ports:o,journal:e.commandJournal,allowedMethods:new Set(a),options:{poisonJournal:e.poisonJournal,...e.onConsumerDeliveryError?{onDeliveryError:e.onConsumerDeliveryError}:{},...e.onConsumerLeaseStarvation?{onLeaseStarvation:e.onConsumerLeaseStarvation}:{},...e.onConsumerRequestTerminalAccepted?{onRequestTerminalAccepted:e.onConsumerRequestTerminalAccepted}:{},...e.onConsumerRequestAcknowledged?{onRequestAcknowledged:e.onConsumerRequestAcknowledged}:{},...e.onConsumerRequestFailure?{onRequestFailure:e.onConsumerRequestFailure}:{}}}),...void 0===e.attemptPorts?{resourceBindingContractRevision:RUNTIME_RESOURCE_BINDING_CONTRACT_REVISION}:{}}}async function probeGatewayGrantJwks(e,t){const r=grantAdapterOptions.get(e);if(!r)throw new Error("untrusted Gateway grant readiness adapter");const n=r.agentGatewayUrl.replace(/\/+$/,""),a=new RuntimeJwksVerifier(`${n}/.well-known/runtime-execution-grant-jwks.json`),o=t?new CloudGatewayGrantJwksVerifier(a):new GatewayGrantJwksVerifier(a);return await o.refresh(!0),o}export function runtimeMessageAttemptPorts(e,t){return{async resolve({request:r,command:n,executionGrant:a,getLease:o,journalStoreId:s,journalGeneration:i,journalRecord:c,abortSignal:u}){const l="resourceBindings"in r?r.resourceBindings:void 0,d="resourceBindingContractRevision"in r?r.resourceBindingContractRevision:void 0,m="skills.install"===n.name||"agent.applyTemplate"===n.name?n.params.artifact:void 0,p="mcp.prepare"===n.name||"mcp.set"===n.name?n.params.definition:void 0;if((m||p)&&(d!==RUNTIME_RESOURCE_BINDING_CONTRACT_REVISION||!l))throw new RuntimeResourceResolutionError("resolution_rejected","resource-bound Service delivery requires the Types 4.1 binding carrier",void 0,`binding_contract_revision=${d??"absent"} bindings=${l?"present":"absent"}`);const R=new HttpRuntimeOperationResourceResolver({gatewayUrl:e.agentGatewayUrl,operationId:r.operationId,requestMessageId:r.requestMessageId,instanceId:r.instanceId,paramsHash:r.paramsHash,bindings:l??[],getAuthorization:()=>{const e=o();return{executionGrant:a,runtimeLeaseCredential:e.runtimeLeaseCredential,fence:deliveryFence(r,e,s,i)}},fetchAuthorized:async(t,r)=>await e.boundAgentFetch(t,r??{}),parseSkillArtifact:e=>parseSkillPackage(e),signal:u});if("prepared"!==c.phase&&"started"!==c.phase||(m&&await R.readVerifiedArtifact(m),p&&await R.resolveMcpDefinition(p)),"session.setModel"===n.name){const n=e.messageService;if(!n)throw new Error("Cloud runtime cannot project a personal session model");return{resources:R,sessionModelProjection:{project:async({conversationId:e,modelOverrideId:a})=>{const s=o(),i=new MessageServiceSessionModelHttpAuthority({messageServiceUrl:t,fetchAuthorized:async(e,t)=>await n.fetchRuntimeChannel(e,{runtimeLeaseCredential:s.runtimeLeaseCredential},t)});return await i.project({conversationId:e,operationId:r.operationId,requestMessageId:r.requestMessageId,requestHash:r.requestHash,modelOverrideId:a,runtimeLeaseCredential:s.runtimeLeaseCredential})}}}}if("session.clear"!==n.name)return{resources:R};const C=e.messageService;if(!C)throw new Error("Cloud runtime cannot access personal conversation history");n.context.conversation;const v=o(),g=new MessageServiceConversationClearHttpAuthority({messageServiceUrl:t,runtimeLeaseCredential:v.runtimeLeaseCredential,fetchAuthorized:async(e,t)=>await C.fetchRuntimeChannel(e,{runtimeLeaseCredential:v.runtimeLeaseCredential},t)});return{resources:R,historyBoundary:new MessageServiceConversationHistoryBoundary({operationId:r.operationId,requestMessageId:r.requestMessageId,requestHash:r.requestHash,fence:deliveryFence(r,v,s,i),authority:g})}}}}function runtimeMethods(){return[...intersectRuntimeServiceMethodsWithBackendCapabilities(RUNTIME_PARITY_MANIFEST.filter(e=>"ready"===e.status).map(e=>e.method))]}function contractArtifactRequirement(){try{return runtimeServiceContractRequirement()}catch{return CONTRACT_ARTIFACT_BLOCKER.requiredContract}}function parseSkillPackage(e){try{return parseAndValidateTemplatePackage(Buffer.from(e))}catch(t){if(!(t instanceof TemplatePackageError))throw t;throw new RuntimeResourceResolutionError("resolution_rejected","skill package failed validation",void 0,`package_invalid bytes=${e.byteLength} reason=${t.message}`)}}function deliveryFence(e,t,r,n){if(t.handlerIdentity!==e.runtimeHandlerIdentity||t.runtimeEpoch!==e.dispatchEpoch)throw new Error("runtime delivery fence does not match the retained request");return{claimId:e.requestMessageId,handlerIdentity:t.handlerIdentity,runtimeEpoch:t.runtimeEpoch,fencingToken:t.runtimeEpoch,journalStoreId:r,journalGeneration:n}}
@@ -1 +1 @@
1
- import{randomUUID}from"node:crypto";import{performance}from"node:perf_hooks";import{RuntimeCommandError}from"./runtime-command-dispatcher.js";import{ClearConversationUseCase}from"./conversation-clear-use-case.js";import{AROUTER_PROVIDER_ID}from"../media/arouter-config.js";import{openClawSessionKeysFromList}from"./openclaw-session-key-resolution.js";async function modelSwitchTimingSpan(e,t,i){const s=performance.now();let n="succeeded";try{return await i()}catch(e){throw n="failed",e}finally{e&&process.stderr.write(`[MODEL_SWITCH_TIMING] component=runtime operationId=${e} stage=${t} outcome=${n} durationMs=${(performance.now()-s).toFixed(1)}\n`)}}export class RuntimeSessionControlService{clearRuntime;fallback;activeRuns;constructor(e,t,i){this.clearRuntime=e,this.fallback=t,this.activeRuns=i}async execute(e,t,i){const s=e;if("session.clear"===s.name){if(!i.historyBoundary)throw new RuntimeCommandError("INVALID_PARAMS",`${i.origin} clear attempt lacks its history boundary`);const e=s.context.conversation;return await new ClearConversationUseCase(i.historyBoundary,this.clearRuntime).execute({operationId:s.context.operationId,conversationId:e.runtimeConversationUid,runtimeSessionKey:e.runtimeSessionKey,runtimeEpoch:s.context.dispatchEpoch})}if("session.cancel"===s.name){if(!this.activeRuns)throw new RuntimeCommandError("INVALID_PARAMS","session.cancel active-run authority is not configured");const e=s;return await this.activeRuns.cancelExact({runtimeConversationUid:e.context.conversation.runtimeConversationUid,runtimeSessionKey:e.context.conversation.runtimeSessionKey,historyGeneration:e.context.historyGeneration,targetMessageId:e.params.targetMessageId}),{}}if(!this.fallback)throw new Error(`session command delegate is not configured: ${s.name}`);return await this.fallback.execute(s,t,i)}async projectCommitted(e,t,i){return await(this.fallback?.projectCommitted?.(e,t,i))}}export class OpenClawSessionMetadataService{metadata;constructor(e){this.metadata="function"==typeof e?new OpenClawGatewaySessionMetadataPort(e):e}async execute(e,t,i){const s=e,n=s.context.conversation.runtimeSessionKey,o={lease:t,attempt:i};switch(s.name){case"session.load":return await this.metadata.load(n,o);case"session.rename":{const e=s.params.title.slice(0,64);return await this.metadata.rename(n,e,o)}case"session.delete":return await this.metadata.delete(n,o);case"session.setMode":{const e=s.params.mode;return await this.metadata.setMode(n,e,o)}case"session.setModel":{const e=s.params.modelOverrideId;if(null!==e&&("string"!=typeof e||0===e.length||e.trim()!==e))throw new RuntimeCommandError("INVALID_PARAMS","modelOverrideId must be a non-empty string or null");const t=null===e?await modelSwitchTimingSpan(i.operationId,"resolve_agent_default_model",async()=>await this.metadata.resolveAgentDefaultModel(n,o)):void 0;if(null===e&&!t)throw new RuntimeCommandError("AGENT_DEFAULT_MODEL_NOT_CONFIGURED","agent default model is not configured");return await modelSwitchTimingSpan(i.operationId,"metadata_set_model",async()=>await this.metadata.setModel(n,e,t??void 0,o))}}throw new RuntimeCommandError("INVALID_PARAMS","unsupported session metadata command")}async projectCommitted(e,t,i){if("session.setModel"!==e.name)return;const s=e,n=t;if(this.metadata.projectCommittedSetModel&&await this.metadata.projectCommittedSetModel(s.context.conversation.runtimeSessionKey,n),"service"===i?.origin){if(!i.sessionModelProjection)throw new Error("Message Service session model projection is unavailable");return Object.freeze({state:"pending",revision:"0"})}if(!this.metadata.projectCommittedSetModel)throw new Error("session metadata projection is unavailable")}}export class OpenClawGatewaySessionMetadataPort{getGateway;projectModel;liveSessionKeys;constructor(e,t,i){this.getGateway=e,this.projectModel=t,this.liveSessionKeys=i}async load(e,t){const i=await this.requireExistingOpenClawKey(e,t);return await this.gateway().request("sessions.resolve",{sessionKey:i}),{loaded:!0}}async rename(e,t,i){const s=await this.requireExistingOpenClawStoreKey(e,i);return await this.gateway().request("sessions.patch",{key:s,label:t}),{title:t}}async delete(e,t){const i=await this.requireExistingOpenClawStoreKey(e,t);return await this.gateway().request("sessions.delete",{key:i}),{deleted:!0}}async setMode(e,t,i){const s="verbose"===t||"detailed"===t?{verboseLevel:"full"}:"reasoning"===t||"thinking"===t?{reasoningLevel:"stream"}:"concise"===t||"default"===t?{verboseLevel:"default"}:null;if(!s)throw new RuntimeCommandError("INVALID_PARAMS",`unsupported session mode: ${t}`);const n=await this.requireExistingOpenClawStoreKey(e,i);if(!isAuthoritativeSetModeAck(await this.gateway().request("sessions.patch",{key:n,...s}),n,s))throw new RuntimeCommandError("OUTCOME_UNKNOWN","sessions.patch did not acknowledge the exact target and effective mode");return{mode:t}}async resolveAgentDefaultModel(e,t){const i=runtimeAgentId(this.liveSessionKeys?(await this.resolveSetModelKey(e,t)).key:await this.requireExistingOpenClawKey(e,t)),s=await this.gateway().request("agents.list",{}),n=s?.agents?.find(e=>e.id===i),o=n?.defaultModel??n?.model;if("string"!=typeof o||!o.trim()||o.trim()!==o)return null;const r=toPlatformModelId(o),a=r.indexOf("/");return{modelId:r,...a>0?{modelProvider:r.slice(0,a)}:{}}}async setModel(e,t,i,s){const n=null===t?null:toOpenClawSessionModel(t),o=s?.attempt.operationId;let r=await modelSwitchTimingSpan(o,"resolve_set_model_key",async()=>await this.resolveSetModelKey(e,s));const a=async()=>await modelSwitchTimingSpan(o,"openclaw_sessions_patch",async()=>await this.gateway().request("sessions.patch",{key:r.key,model:n,mustExist:!r.materialized}));let l;try{l=await a()}catch(t){if(r.materialized||!isMissingOpenClawStoreSession(t,r.key))throw t;r=await modelSwitchTimingSpan(o,"materialize_set_model_key",async()=>await this.materializeSetModelKey(e,r.key,s)),l=await a()}const{key:c,materialized:d}=r;if(!isAuthoritativeSetModelAck(l,c,n,t))throw new RuntimeCommandError("OUTCOME_UNKNOWN","sessions.patch did not acknowledge the exact existing session and effective model");const m=l.resolved.model,u=toPlatformModelId(m);if(null===t&&(!i||u!==i.modelId))throw new RuntimeCommandError("OUTCOME_UNKNOWN","sessions.patch effective model does not match the prevalidated agent default");if(null!==t&&u!==t&&m!==n)throw new RuntimeCommandError("OUTCOME_UNKNOWN","sessions.patch effective model does not match the requested override");if(d){const e=await modelSwitchTimingSpan(o,"openclaw_sessions_list_post_materialize",async()=>await this.gateway().request("sessions.list",{includeDerivedTitles:!0}));if(!openClawSessionKeysFromList(e).has(c))throw new RuntimeCommandError("OUTCOME_UNKNOWN","sessions.patch acknowledged a model-intent session that is absent from the authoritative store");if(!this.liveSessionKeys?.registerMaterializedModelKey)throw new RuntimeCommandError("OUTCOME_UNKNOWN","model-intent session registration authority is unavailable");this.liveSessionKeys.registerMaterializedModelKey(c)}return{...null===t?{}:{modelOverrideId:t},effectiveModelId:u,...l.resolved?.modelProvider?{modelProvider:l.resolved.modelProvider}:{}}}async projectCommittedSetModel(e,t){if(!this.projectModel)throw new Error("session metadata projection is unavailable");const i=await this.requireExistingOpenClawKey(e);await this.projectModel(i,t)}async requireExistingOpenClawKey(e,t){if(await(this.liveSessionKeys?.waitForHydration?.()),!this.liveSessionKeys)return e;try{return this.liveSessionKeys.resolveLiveKey(e)}catch(i){if(!t||!isMissingLiveSession(i)||!this.liveSessionKeys.reconcileMissingLiveKey)throw i;return await this.liveSessionKeys.reconcileMissingLiveKey(e,t),this.liveSessionKeys.resolveLiveKey(e)}}async requireExistingOpenClawStoreKey(e,t){const i=await this.requireExistingOpenClawKey(e,t),s=await this.gateway().request("sessions.list",{includeDerivedTitles:!0});if(!openClawSessionKeysFromList(s).has(i))throw new RuntimeCommandError("INVALID_PARAMS",`OpenClaw session key does not exist (refusing sessions.patch upsert): ${i}`);return i}async resolveSetModelKey(e,t){let i;const s="service"===t?.attempt.origin&&void 0!==t.attempt.verifiedMessageGrant;try{i=await this.requireExistingOpenClawKey(e,t)}catch(e){if(!(s&&t&&isMissingLiveSession(e)&&this.liveSessionKeys?.materializeMissingModelKey&&this.liveSessionKeys.registerMaterializedModelKey))throw e}return i?{key:i,materialized:!1}:await this.materializeSetModelKey(e,void 0,t)}async materializeSetModelKey(e,t,i){if(!("service"===i?.attempt.origin&&void 0!==i.attempt.verifiedMessageGrant&&i&&this.liveSessionKeys?.materializeMissingModelKey&&this.liveSessionKeys.registerMaterializedModelKey)){throw new RuntimeCommandError("INVALID_PARAMS",`OpenClaw session key does not exist (refusing sessions.patch upsert): ${t??e}`)}const s=await this.liveSessionKeys.materializeMissingModelKey(e,i);if(t&&s!==t)throw new RuntimeCommandError("INVALID_PARAMS","verified model-intent key conflicts with the live local session");return{key:s,materialized:!0}}gateway(){const e=this.getGateway();if(!e?.isConnected)throw new RuntimeCommandError("GATEWAY_NOT_CONNECTED","Gateway not connected");return e}}function isMissingLiveSession(e){return e instanceof RuntimeCommandError&&"INVALID_PARAMS"===e.code&&e.message.startsWith("no live local session")}function isMissingOpenClawStoreSession(e,t){return e instanceof Error&&e.message===`No session found: ${t}`}function runtimeAgentId(e){const t=e.split(":");return"agent"===t[0]&&t[1]?.trim()?t[1]:"main"}function toOpenClawSessionModel(e){return e.startsWith(`${AROUTER_PROVIDER_ID}/`)?e:`${AROUTER_PROVIDER_ID}/${e}`}function toPlatformModelId(e){const t=`${AROUTER_PROVIDER_ID}/`;return e.startsWith(t)?e.slice(t.length):e}function isAuthoritativeSetModeAck(e,t,i){if(!e||"object"!=typeof e||Array.isArray(e))return!1;const s=e;if(!0!==s.ok||s.key!==t||!s.entry||"object"!=typeof s.entry||Array.isArray(s.entry))return!1;const n=s.entry;return"verboseLevel"in i?n.verboseLevel===i.verboseLevel:n.reasoningLevel===i.reasoningLevel}function isAuthoritativeSetModelAck(e,t,i,s){if(!e||"object"!=typeof e||Array.isArray(e))return!1;const n=e;if("ok"in n&&!0!==n.ok)return!1;if("key"in n&&n.key!==t)return!1;const o=n.resolved;if(!o||"object"!=typeof o||Array.isArray(o))return!1;const r=o.model;return"string"==typeof r&&0!==r.length&&r.trim()===r&&(null!==s||null===i)}export class LocalConversationHistoryBoundary{onCommit;kind="local";intents=new Map;convergence=new Map;generations=new Map;constructor(e){this.onCommit=e}currentGeneration(e){return String(this.generations.get(e)??0n)}async prepare(e){const t=this.intents.get(e.operationId);if(t)return t;const i=(new Date).toISOString(),s=this.generations.get(e.conversationId)??0n,n={intentId:randomUUID(),operationId:e.operationId,conversationId:e.conversationId,state:"prepared",runtimeEpoch:e.runtimeEpoch,fromGeneration:String(s),toGeneration:String(s+1n),boundaryOffset:"0",version:"1",createdAt:i,updatedAt:i};return this.intents.set(e.operationId,n),n}async markResetting(e,t){return this.assertState(e,"prepared"),this.convergence.set(e.operationId,t),this.transition(e,"resetting")}async loadRunConvergence(e){const t=this.convergence.get(e.operationId);if(!t)throw new Error("local clear convergence receipt missing");return t}async markResetDone(e){return this.assertState(e,"resetting"),this.transition(e,"reset_done")}async commit(e){return this.assertState(e,"reset_done"),await(this.onCommit?.(e.conversationId)),this.generations.set(e.conversationId,BigInt(e.toGeneration)),this.transition(e,"committed")}async abortPrepared(e){this.assertState(e,"prepared"),this.transition(e,"aborted")}transition(e,t){const i={...e,state:t,version:String(BigInt(e.version)+1n),updatedAt:(new Date).toISOString()};return this.intents.set(e.operationId,i),i}assertState(e,t){const i=this.intents.get(e.operationId);if(!i||i.version!==e.version||i.state!==t)throw new Error(`local clear transition fenced: expected ${t}`)}}
1
+ import{randomUUID}from"node:crypto";import{performance}from"node:perf_hooks";import{RuntimeCommandError}from"./runtime-command-dispatcher.js";import{ClearConversationUseCase}from"./conversation-clear-use-case.js";import{AROUTER_PROVIDER_ID}from"../media/arouter-config.js";import{openClawSessionKeysFromList}from"./openclaw-session-key-resolution.js";async function modelSwitchTimingSpan(e,t,i){const s=performance.now();let n="succeeded";try{return await i()}catch(e){throw n="failed",e}finally{e&&process.stderr.write(`[MODEL_SWITCH_TIMING] component=runtime operationId=${e} stage=${t} outcome=${n} durationMs=${(performance.now()-s).toFixed(1)}\n`)}}export class RuntimeSessionControlService{clearRuntime;fallback;activeRuns;constructor(e,t,i){this.clearRuntime=e,this.fallback=t,this.activeRuns=i}async execute(e,t,i){const s=e;if("session.clear"===s.name){if(!i.historyBoundary)throw new RuntimeCommandError("INVALID_PARAMS",`${i.origin} clear attempt lacks its history boundary`);const e=s.context.conversation;return await new ClearConversationUseCase(i.historyBoundary,this.clearRuntime).execute({operationId:s.context.operationId,conversationId:e.runtimeConversationUid,runtimeSessionKey:e.runtimeSessionKey,runtimeEpoch:s.context.dispatchEpoch})}if("session.cancel"===s.name){if(!this.activeRuns)throw new RuntimeCommandError("INVALID_PARAMS","session.cancel active-run authority is not configured");const e=s;return await this.activeRuns.cancelExact({runtimeConversationUid:e.context.conversation.runtimeConversationUid,runtimeSessionKey:e.context.conversation.runtimeSessionKey,historyGeneration:e.context.historyGeneration,targetMessageId:e.params.targetMessageId}),{}}if(!this.fallback)throw new Error(`session command delegate is not configured: ${s.name}`);return await this.fallback.execute(s,t,i)}async projectCommitted(e,t,i){return await(this.fallback?.projectCommitted?.(e,t,i))}}export class OpenClawSessionMetadataService{metadata;constructor(e){this.metadata="function"==typeof e?new OpenClawGatewaySessionMetadataPort(e):e}async execute(e,t,i){const s=e,n=s.context.conversation.runtimeSessionKey,o={lease:t,attempt:i};switch(s.name){case"session.load":return await this.metadata.load(n,o);case"session.rename":{const e=s.params.title.slice(0,64);return await this.metadata.rename(n,e,o)}case"session.delete":return await this.metadata.delete(n,o);case"session.setMode":{const e=s.params.mode;return await this.metadata.setMode(n,e,o)}case"session.setModel":{const e=s.params.modelOverrideId;if(null!==e&&("string"!=typeof e||0===e.length||e.trim()!==e))throw new RuntimeCommandError("INVALID_PARAMS","modelOverrideId must be a non-empty string or null");const t=null===e?await modelSwitchTimingSpan(i.operationId,"resolve_agent_default_model",async()=>await this.metadata.resolveAgentDefaultModel(n,o)):void 0;if(null===e&&!t)throw new RuntimeCommandError("AGENT_DEFAULT_MODEL_NOT_CONFIGURED","agent default model is not configured");return await modelSwitchTimingSpan(i.operationId,"metadata_set_model",async()=>await this.metadata.setModel(n,e,t??void 0,o))}}throw new RuntimeCommandError("INVALID_PARAMS","unsupported session metadata command")}async projectCommitted(e,t,i){if("session.setModel"!==e.name)return;const s=e,n=t;if(this.metadata.projectCommittedSetModel&&await this.metadata.projectCommittedSetModel(s.context.conversation.runtimeSessionKey,n),"service"===i?.origin){if(!i.sessionModelProjection)throw new Error("Message Service session model projection is unavailable");return Object.freeze({state:"pending",revision:"0"})}if(!this.metadata.projectCommittedSetModel)throw new Error("session metadata projection is unavailable")}}export class OpenClawGatewaySessionMetadataPort{getGateway;projectModel;liveSessionKeys;modelMutations;constructor(e,t,i,s){this.getGateway=e,this.projectModel=t,this.liveSessionKeys=i,this.modelMutations=s}async load(e,t){const i=await this.requireExistingOpenClawKey(e,t);return await this.gateway().request("sessions.resolve",{sessionKey:i}),{loaded:!0}}async rename(e,t,i){const s=await this.requireExistingOpenClawStoreKey(e,i);return await this.gateway().request("sessions.patch",{key:s,label:t}),{title:t}}async delete(e,t){const i=await this.requireExistingOpenClawStoreKey(e,t);return await this.gateway().request("sessions.delete",{key:i}),{deleted:!0}}async setMode(e,t,i){const s=openClawSessionModePatch(t),n=await this.requireExistingOpenClawStoreKey(e,i);if(!isAuthoritativeSetModeAck(await this.gateway().request("sessions.patch",{key:n,...s}),n,s))throw new RuntimeCommandError("OUTCOME_UNKNOWN","sessions.patch did not acknowledge the exact target and effective mode");return{mode:t}}async resolveAgentDefaultModel(e,t){const i=runtimeAgentId(this.liveSessionKeys?(await this.resolveSetModelKey(e,t)).key:await this.requireExistingOpenClawKey(e,t)),s=await this.gateway().request("agents.list",{}),n=s?.agents?.find(e=>e.id===i),o=n?.defaultModel??n?.model;if("string"!=typeof o||!o.trim()||o.trim()!==o)return null;const a=toPlatformModelId(o),r=a.indexOf("/");return{modelId:a,...r>0?{modelProvider:a.slice(0,r)}:{}}}async setModel(e,t,i,s){const n=null===t?null:toOpenClawSessionModel(t),o=s?.attempt.operationId;let a=await modelSwitchTimingSpan(o,"resolve_set_model_key",async()=>await this.resolveSetModelKey(e,s));const r=await(this.modelMutations?.acquireInternal({sessionKey:a.key,label:"session model mutation"}));try{if(r){const t=await this.resolveSetModelKey(e,s);if(t.key!==r.sessionKey)throw new RuntimeCommandError("OUTCOME_UNKNOWN","model target changed while waiting for its physical session gate");a=t}const l=async()=>await modelSwitchTimingSpan(o,"openclaw_sessions_patch",async()=>await this.gateway().request("sessions.patch",{key:a.key,model:n,mustExist:!a.materialized}));let d;try{d=await l()}catch(t){if(a.materialized||!isMissingOpenClawStoreSession(t,a.key))throw t;a=await modelSwitchTimingSpan(o,"materialize_set_model_key",async()=>await this.materializeSetModelKey(e,a.key,s)),d=await l()}const{key:c,materialized:m}=a,u=resolveModelPatch(d,c,t),h=u.effectiveModelId;if(null===t&&(!i||h!==i.modelId))throw new RuntimeCommandError("OUTCOME_UNKNOWN","sessions.patch effective model does not match the prevalidated agent default");if(m){const e=await modelSwitchTimingSpan(o,"openclaw_sessions_list_post_materialize",async()=>await this.gateway().request("sessions.list",{includeDerivedTitles:!0}));if(!openClawSessionKeysFromList(e).has(c))throw new RuntimeCommandError("OUTCOME_UNKNOWN","sessions.patch acknowledged a model-intent session that is absent from the authoritative store");if(!this.liveSessionKeys?.registerMaterializedModelKey)throw new RuntimeCommandError("OUTCOME_UNKNOWN","model-intent session registration authority is unavailable");this.liveSessionKeys.registerMaterializedModelKey(c)}return u}finally{this.modelMutations?.releaseBeforeRuntime(r)}}async prepareNativeModel(e,t,i,s){await i();const n=void 0===t?null:toOpenClawSessionModel(t),o=void 0===s?void 0:openClawSessionModePatch(s),a=await this.gateway().request("sessions.patch",{key:e,model:n,...o});await i();const r=a;if(!0!==r?.ok||r.key!==e)throw new RuntimeCommandError("OUTCOME_UNKNOWN","native model patch did not acknowledge the exact qualified session");if(o&&!isAuthoritativeSetModeAck(a,e,o))throw new RuntimeCommandError("OUTCOME_UNKNOWN","sessions.patch did not acknowledge the exact target and effective mode");return resolveModelPatch(a,e,t??null)}async projectCommittedSetModel(e,t){if(!this.projectModel)throw new Error("session metadata projection is unavailable");const i=await this.requireExistingOpenClawKey(e);await this.projectModel(i,t)}async requireExistingOpenClawKey(e,t){if(await(this.liveSessionKeys?.waitForHydration?.()),!this.liveSessionKeys)return e;try{return this.liveSessionKeys.resolveLiveKey(e)}catch(i){if(!t||!isMissingLiveSession(i)||!this.liveSessionKeys.reconcileMissingLiveKey)throw i;return await this.liveSessionKeys.reconcileMissingLiveKey(e,t),this.liveSessionKeys.resolveLiveKey(e)}}async requireExistingOpenClawStoreKey(e,t){const i=await this.requireExistingOpenClawKey(e,t),s=await this.gateway().request("sessions.list",{includeDerivedTitles:!0});if(!openClawSessionKeysFromList(s).has(i))throw new RuntimeCommandError("INVALID_PARAMS",`OpenClaw session key does not exist (refusing sessions.patch upsert): ${i}`);return i}async resolveSetModelKey(e,t){let i;const s="service"===t?.attempt.origin&&void 0!==t.attempt.verifiedMessageGrant;try{i=await this.requireExistingOpenClawKey(e,t)}catch(e){if(!(s&&t&&isMissingLiveSession(e)&&this.liveSessionKeys?.materializeMissingModelKey&&this.liveSessionKeys.registerMaterializedModelKey))throw e}return i?{key:i,materialized:!1}:await this.materializeSetModelKey(e,void 0,t)}async materializeSetModelKey(e,t,i){if(!("service"===i?.attempt.origin&&void 0!==i.attempt.verifiedMessageGrant&&i&&this.liveSessionKeys?.materializeMissingModelKey&&this.liveSessionKeys.registerMaterializedModelKey)){throw new RuntimeCommandError("INVALID_PARAMS",`OpenClaw session key does not exist (refusing sessions.patch upsert): ${t??e}`)}const s=await this.liveSessionKeys.materializeMissingModelKey(e,i);if(t&&s!==t)throw new RuntimeCommandError("INVALID_PARAMS","verified model-intent key conflicts with the live local session");return{key:s,materialized:!0}}gateway(){const e=this.getGateway();if(!e?.isConnected)throw new RuntimeCommandError("GATEWAY_NOT_CONNECTED","Gateway not connected");return e}}function isMissingLiveSession(e){return e instanceof RuntimeCommandError&&"INVALID_PARAMS"===e.code&&e.message.startsWith("no live local session")}function isMissingOpenClawStoreSession(e,t){return e instanceof Error&&e.message===`No session found: ${t}`}function resolveModelPatch(e,t,i){const s=null===i?null:toOpenClawSessionModel(i);if(!isAuthoritativeSetModelAck(e,t,s,i))throw new RuntimeCommandError("OUTCOME_UNKNOWN","sessions.patch did not acknowledge the exact existing session and effective model");const n=e.resolved.model,o=toPlatformModelId(n);if(null!==i&&o!==i&&n!==s)throw new RuntimeCommandError("OUTCOME_UNKNOWN","sessions.patch effective model does not match the requested override");return{...null===i?{}:{modelOverrideId:i},effectiveModelId:o,...e.resolved.modelProvider?{modelProvider:e.resolved.modelProvider}:{}}}function runtimeAgentId(e){const t=e.split(":");return"agent"===t[0]&&t[1]?.trim()?t[1]:"main"}function toOpenClawSessionModel(e){return e.startsWith(`${AROUTER_PROVIDER_ID}/`)?e:`${AROUTER_PROVIDER_ID}/${e}`}function toPlatformModelId(e){const t=`${AROUTER_PROVIDER_ID}/`;return e.startsWith(t)?e.slice(t.length):e}export function openClawSessionModePatch(e){const t="verbose"===e||"detailed"===e?{verboseLevel:"full"}:"reasoning"===e||"thinking"===e?{reasoningLevel:"stream"}:"concise"===e||"default"===e?{verboseLevel:"default"}:null;if(!t)throw new RuntimeCommandError("INVALID_PARAMS",`unsupported session mode: ${e}`);return t}function isAuthoritativeSetModeAck(e,t,i){if(!e||"object"!=typeof e||Array.isArray(e))return!1;const s=e;if(!0!==s.ok||s.key!==t||!s.entry||"object"!=typeof s.entry||Array.isArray(s.entry))return!1;const n=s.entry;return"verboseLevel"in i?n.verboseLevel===i.verboseLevel:n.reasoningLevel===i.reasoningLevel}function isAuthoritativeSetModelAck(e,t,i,s){if(!e||"object"!=typeof e||Array.isArray(e))return!1;const n=e;if("ok"in n&&!0!==n.ok)return!1;if("key"in n&&n.key!==t)return!1;const o=n.resolved;if(!o||"object"!=typeof o||Array.isArray(o))return!1;const a=o.model;return"string"==typeof a&&0!==a.length&&a.trim()===a&&(null!==s||null===i)}export class LocalConversationHistoryBoundary{onCommit;kind="local";intents=new Map;convergence=new Map;generations=new Map;constructor(e){this.onCommit=e}currentGeneration(e){return String(this.generations.get(e)??0n)}async prepare(e){const t=this.intents.get(e.operationId);if(t)return t;const i=(new Date).toISOString(),s=this.generations.get(e.conversationId)??0n,n={intentId:randomUUID(),operationId:e.operationId,conversationId:e.conversationId,state:"prepared",runtimeEpoch:e.runtimeEpoch,fromGeneration:String(s),toGeneration:String(s+1n),boundaryOffset:"0",version:"1",createdAt:i,updatedAt:i};return this.intents.set(e.operationId,n),n}async markResetting(e,t){return this.assertState(e,"prepared"),this.convergence.set(e.operationId,t),this.transition(e,"resetting")}async loadRunConvergence(e){const t=this.convergence.get(e.operationId);if(!t)throw new Error("local clear convergence receipt missing");return t}async markResetDone(e){return this.assertState(e,"resetting"),this.transition(e,"reset_done")}async commit(e){return this.assertState(e,"reset_done"),await(this.onCommit?.(e.conversationId)),this.generations.set(e.conversationId,BigInt(e.toGeneration)),this.transition(e,"committed")}async abortPrepared(e){this.assertState(e,"prepared"),this.transition(e,"aborted")}transition(e,t){const i={...e,state:t,version:String(BigInt(e.version)+1n),updatedAt:(new Date).toISOString()};return this.intents.set(e.operationId,i),i}assertState(e,t){const i=this.intents.get(e.operationId);if(!i||i.version!==e.version||i.state!==t)throw new Error(`local clear transition fenced: expected ${t}`)}}
@@ -1 +1 @@
1
- import{access,mkdir,rename,rm,writeFile}from"node:fs/promises";import{homedir}from"node:os";import{dirname,join,resolve,sep}from"node:path";import{RuntimeCommandError}from"./runtime-command-dispatcher.js";export class RuntimeSkillsService{deps;constructor(e){this.deps=e}async execute(e,t,i){const a=e,r=this.requireGateway(),s=a.context.agent.runtimeLocalKey;if("skills.list"===a.name){const e=await r.request("skills.status",{agentId:s});return{skills:normalizeSkills(e?.skills)}}if("skills.setEnabled"===a.name){const e=a;return await r.request("skills.update",{skillKey:e.params.skillKey,enabled:e.params.enabled,agentId:s}),{skillKey:e.params.skillKey,enabled:e.params.enabled}}const n=await this.skillsRoot(r,s);if("skills.uninstall"===a.name){const e=validateSkillKey(a.params.skillKey);return await rm(contained(n,e),{recursive:!0,force:!0}),{removed:!0}}const o=a,l=await i.resources.readVerifiedArtifact(o.params.artifact),c=validateSkillKey(l.manifest?.slug??""),m=contained(n,c),d=contained(n,`.${c}.${o.context.operationId}.tmp`),u=contained(n,`.${c}.${o.context.operationId}.backup`);!await exists(m)&&await exists(u)&&await rename(u,m),await rm(d,{recursive:!0,force:!0}),await mkdir(d,{recursive:!0});try{for(const[e,t]of l.workspaceFiles){const i=contained(d,normalizeArtifactPath(e));await mkdir(dirname(i),{recursive:!0}),await writeFile(i,t,{mode:384})}await rm(u,{recursive:!0,force:!0}),await exists(m)&&await rename(m,u),await rename(d,m),await rm(u,{recursive:!0,force:!0})}catch(e){throw await rm(d,{recursive:!0,force:!0}).catch(()=>{}),!await exists(m)&&await exists(u)&&await rename(u,m).catch(()=>{}),e}return{installed:!0}}requireGateway(){const e=this.deps.getGateway();if(!e?.isConnected)throw new RuntimeCommandError("GATEWAY_NOT_CONNECTED","Gateway not connected");return e}async skillsRoot(e,t){const i=this.deps.getOpenclawHome?.()??process.env.OPENCLAW_HOME??join(homedir(),".openclaw");if("main"===t)return join(i,"skills");const a=await e.request("agents.list",{}),r=a?.agents?.find(e=>e.id===t)?.workspace;return join(r||join(i,`workspace-${t}`),"skills")}}async function exists(e){try{return await access(e),!0}catch{return!1}}function validateSkillKey(e){if(!/^[a-z0-9][a-z0-9_-]{0,63}$/i.test(e))throw new RuntimeCommandError("INVALID_PARAMS","invalid skill key");return e}function normalizeArtifactPath(e){const t=e.replaceAll("\\","/").replace(/^\.\//,"");if(!t||t.startsWith("/")||t.split("/").includes(".."))throw new RuntimeCommandError("INVALID_PARAMS",`invalid skill artifact path: ${e}`);return t}function contained(e,t){const i=resolve(e),a=resolve(i,t);if(a!==i&&!a.startsWith(`${i}${sep}`))throw new RuntimeCommandError("INVALID_PARAMS","skill path escapes workspace");return a}function normalizeSkills(e){return(e??[]).flatMap(e=>{if(!e||"object"!=typeof e)return[];const t=e,i="string"==typeof t.skillKey?t.skillKey:"string"==typeof t.name?t.name:"";return i?[{skillKey:i,slug:"string"==typeof t.slug?t.slug:i,name:"string"==typeof t.name?t.name:i,description:"string"==typeof t.description?t.description:"",enabled:!0!==t.disabled,..."string"==typeof t.emoji?{emoji:t.emoji}:{},..."boolean"==typeof t.bundled?{bundled:t.bundled}:{}}]:[]})}
1
+ import{access,mkdir,rename,rm,writeFile}from"node:fs/promises";import{homedir}from"node:os";import{basename,dirname,join,resolve,sep}from"node:path";import{RuntimeCommandError}from"./runtime-command-dispatcher.js";export class RuntimeSkillsService{deps;constructor(e){this.deps=e}async execute(e,t,i){const a=e,r=this.requireGateway(),s=a.context.agent.runtimeLocalKey;if("skills.list"===a.name){const e=await r.request("skills.status",{agentId:s});return{skills:normalizeSkills(e?.skills)}}if("skills.setEnabled"===a.name){const e=a;return await r.request("skills.update",{skillKey:e.params.skillKey,enabled:e.params.enabled,agentId:s}),{skillKey:e.params.skillKey,enabled:e.params.enabled}}const n=await this.skillsRoot(r,s);if("skills.uninstall"===a.name){const e=validateSkillKey(a.params.skillKey);return await rm(contained(n,e),{recursive:!0,force:!0}),{removed:!0}}const o=a,l=await i.resources.readVerifiedArtifact(o.params.artifact),c=l.manifest?.slug??"";if(!c)throw new RuntimeCommandError("INVALID_PARAMS","skill package declares no manifest slug (manifest.json with schema_version 1 and slug is required)");const m=contained(n,validateSkillKey(c)),d=dirname(m),u=contained(d,`.${basename(m)}.${o.context.operationId}.tmp`),p=contained(d,`.${basename(m)}.${o.context.operationId}.backup`);await mkdir(d,{recursive:!0}),!await exists(m)&&await exists(p)&&await rename(p,m),await rm(u,{recursive:!0,force:!0}),await mkdir(u,{recursive:!0});try{for(const[e,t]of l.workspaceFiles){const i=contained(u,normalizeArtifactPath(e));await mkdir(dirname(i),{recursive:!0}),await writeFile(i,t,{mode:384})}await rm(p,{recursive:!0,force:!0}),await exists(m)&&await rename(m,p),await rename(u,m),await rm(p,{recursive:!0,force:!0})}catch(e){throw await rm(u,{recursive:!0,force:!0}).catch(()=>{}),!await exists(m)&&await exists(p)&&await rename(p,m).catch(()=>{}),new RuntimeCommandError("EXECUTION_FAILED",`skill install write failed (files=${l.workspaceFiles.size}): ${e instanceof Error?e.message:String(e)}`,void 0,{cause:e})}return{installed:!0}}requireGateway(){const e=this.deps.getGateway();if(!e?.isConnected)throw new RuntimeCommandError("GATEWAY_NOT_CONNECTED","Gateway not connected");return e}async skillsRoot(e,t){const i=this.deps.getOpenclawHome?.()??process.env.OPENCLAW_HOME??join(homedir(),".openclaw");if("main"===t)return join(i,"skills");const a=await e.request("agents.list",{}),r=a?.agents?.find(e=>e.id===t)?.workspace;return join(r||join(i,`workspace-${t}`),"skills")}}async function exists(e){try{return await access(e),!0}catch{return!1}}function validateSkillKey(e){if(!/^(?:@?[A-Za-z0-9][A-Za-z0-9._-]{0,63}\/)?[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/.test(e))throw new RuntimeCommandError("INVALID_PARAMS","invalid skill key");return e}function normalizeArtifactPath(e){const t=e.replaceAll("\\","/").replace(/^\.\//,"");if(!t||t.startsWith("/")||t.split("/").includes(".."))throw new RuntimeCommandError("INVALID_PARAMS",`invalid skill artifact path: ${e}`);return t}function contained(e,t){const i=resolve(e),a=resolve(i,t);if(a!==i&&!a.startsWith(`${i}${sep}`))throw new RuntimeCommandError("INVALID_PARAMS","skill path escapes workspace");return a}function normalizeSkills(e){return(e??[]).flatMap(e=>{if(!e||"object"!=typeof e)return[];const t=e,i="string"==typeof t.skillKey?t.skillKey:"string"==typeof t.name?t.name:"";return i?[{skillKey:i,slug:"string"==typeof t.slug?t.slug:i,name:"string"==typeof t.name?t.name:i,description:"string"==typeof t.description?t.description:"",enabled:!0!==t.disabled,..."string"==typeof t.emoji?{emoji:t.emoji}:{},..."boolean"==typeof t.bundled?{bundled:t.bundled}:{}}]:[]})}
@@ -0,0 +1 @@
1
+ export class RuntimeTransportFailurePolicy{threshold;consecutiveFailures=0;constructor(e=3){if(this.threshold=e,!Number.isSafeInteger(e)||e<=0)throw new Error("transport failure threshold must be a positive integer")}recordSuccess(){this.consecutiveFailures=0}recordFailure(){return this.consecutiveFailures+=1,!(this.consecutiveFailures<this.threshold)&&(this.consecutiveFailures=0,!0)}}
@@ -1 +1 @@
1
- import{MessageServiceConversationHistoryBoundary}from"./message-service-conversation-history-boundary.js";export class ServiceRuntimeAttemptPortsResolver{deps;constructor(e){this.deps=e}async resolve(e,s){const r=await this.deps.resources(e,s);if("session.prompt"===e.attempt.command.name){if(!this.deps.run)throw new Error("Service session.prompt run boundary is unavailable");return{resources:r,conversationRunBoundary:await this.deps.run(e)}}if("session.clear"!==e.attempt.command.name)return{resources:r};const t=await this.deps.clear(e);return{resources:r,historyBoundary:new MessageServiceConversationHistoryBoundary({operationId:e.operationId,requestMessageId:e.requestMessageId,requestHash:t.requestHash??e.request.requestHash,...t.expectedGeneration?{expectedGeneration:t.expectedGeneration}:{},fence:e.fence,authority:t.authority,...t.resolveActiveRunRegistrationId?{resolveActiveRunRegistrationId:t.resolveActiveRunRegistrationId}:{}})}}}
1
+ import{MessageServiceConversationHistoryBoundary}from"./message-service-conversation-history-boundary.js";export class ServiceRuntimeAttemptPortsResolver{deps;constructor(e){this.deps=e}async resolve(e,r){const s=await this.deps.resources(e,r);if("session.prompt"===e.attempt.command.name){if(!this.deps.run)throw new Error("Service session.prompt run boundary is unavailable");return{resources:s,conversationRunBoundary:await this.deps.run(e)}}if("session.clear"!==e.attempt.command.name)return{resources:s};const t=await this.deps.clear(e);return{resources:s,historyBoundary:new MessageServiceConversationHistoryBoundary({operationId:e.operationId,requestMessageId:e.requestMessageId,expectedGeneration:t.expectedGeneration,fence:e.fence,authority:t.authority,...t.resolveActiveRunRegistrationId?{resolveActiveRunRegistrationId:t.resolveActiveRunRegistrationId}:{}})}}}
@@ -1 +1 @@
1
- import{RUNTIME_ERROR_DESCRIPTORS,RUNTIME_HASH_DOMAINS}from"@beeos-ai/beeos-types/runtime";import{verifyAndCreateMessageServiceCoreExecutionAttempt}from"./core-execution-attempt-context.js";import{RuntimeSessionNewRejectedError}from"./runtime-rpc-consumer.js";import{RUNTIME_MESSAGE_TYPES,runtimeMessageHash}from"./runtime-message-channel-contract.js";const SESSION_NEW_METHOD="session/new",SESSION_NEW_COMMAND="session.create";export class SessionNewRuntimeDeliveryBridge{options;workers=new Map;constructor(e){this.options=e}async acceptDelivery(e,t,s){this.currentLease(t);const r=record(e.message.payload),n=string(r?.operationId)??e.message.channel_id,o=this.workers.get(n);if(o)return await o;const i=this.handleDelivery(e,t,s).finally(()=>this.workers.delete(n));this.workers.set(n,i),await i}async handleDelivery(e,t,s){const r=parseSessionNewRequest(e);if(!r.ok)return void await this.options.poison(e,`invalid_session_new:${r.reason}`,t);const n=r.request,o=this.options.consumer(),i=async()=>{this.currentLease(t),await o.acknowledge([e.deliveryId])},a=await o.history(n.operationId);if("found"!==a.status)return void await i();const d=parseSessionNewHistory(a.snapshot);if(!d.ok||d.request.operationId!==n.operationId||d.request.requestMessageId!==n.requestMessageId||d.request.requestHash!==n.requestHash)return void await this.options.poison(e,"session_new_history_mismatch",t);const c=d.request;if(d.terminal)return void await i();if(c.instanceId!==t.instanceId||c.runtimeHandlerIdentity!==t.handlerIdentity||c.dispatchEpoch!==t.runtimeEpoch)return await this.appendFailure(c,t,"INSTANCE_EPOCH_MISMATCH"),void await i();const u=Date.now();if("deadline"===d.terminalSource||Date.parse(c.executionDeadlineAt)<=u||!d.started&&Date.parse(c.claimDeadlineAt)<=u)return await this.appendFailure(c,t,"COMMAND_EXPIRED"),void await i();if(d.cancelRequested&&!d.started)return await this.appendFailure(c,t,"OPERATION_CANCELLED"),void await i();const p=e.executionGrant;if(!p)return await this.appendFailure(c,t,d.started?"OUTCOME_UNKNOWN":"INVALID_EXECUTION_GRANT"),void await i();await this.options.verifier.refresh(),this.options.verifier.verifyAndDecodeGrant(p)||await this.options.verifier.refresh(!0);const h=new AbortController,l=()=>h.abort();s.addEventListener("abort",l,{once:!0});try{const e=await verifyAndCreateMessageServiceCoreExecutionAttempt({request:c,executionGrant:p,runtimeEpoch:t.runtimeEpoch,lease:t,verifier:this.options.verifier,allowExpiredGrant:d.started,abortSignal:h.signal,resolvePorts:async()=>({resources:noSessionNewResources})});if(!e.ok)return await this.appendFailure(c,t,d.started?"OUTCOME_UNKNOWN":"INVALID_EXECUTION_GRANT"),void await i();if(s.aborted)throw new Error("session/new delivery owner stopped");d.started?this.currentLease(t):await this.append(c,t,RUNTIME_MESSAGE_TYPES.started,{...correlation(c),executionGrantHash:c.executionGrantHash,handlerIdentity:t.handlerIdentity,executionMode:"start",claimedAt:(new Date).toISOString()},p);const r=c.request.params.agentId;let n;try{n=await this.options.createRuntimeSession({operationId:c.operationId,platformAgentId:r,...optionalStringProperty(c.request.params,"cwd"),...optionalStringProperty(c.command.params,"title")},e.context)}catch(e){if(!(e instanceof RuntimeSessionNewRejectedError))throw e;return await this.appendFailure(c,t,"INVALID_INVOCATION"),void await i()}const o=c.instanceId,a=r.startsWith("agent:")?r:`agent:${r}`;let u;try{u=await this.options.client.conversations.create({agentId:r,participants:dedupe([o,a,c.callerIdentityId]),...optionalStringProperty(c.command.params,"title"),metadata:{source:"session/new",runtimeSessionId:n.sessionId,runtimeSessionKey:n.gatewaySessionKey,runtimeInstanceId:c.instanceId,runtimeEpoch:c.dispatchEpoch,platformAgentId:r},idempotencyKey:c.operationId})}catch(e){const s=httpStatus(e);if(void 0===s||s>=500||408===s||429===s)throw e;return await this.appendFailure(c,t,"INVALID_INVOCATION"),void await i()}const l={sessionId:n.sessionId,conversationId:u.id},I=runtimeMessageHash(RUNTIME_HASH_DOMAINS.serviceResult,{method:"session/new",result:l});await this.append(c,t,RUNTIME_MESSAGE_TYPES.final,{...correlation(c),outcome:"succeeded",response:{jsonrpc:"2.0",id:c.request.id,result:l},serviceResultHash:I,completedAt:(new Date).toISOString()}),await i()}finally{h.abort(),s.removeEventListener("abort",l)}}async appendFailure(e,t,s){const r="COMMAND_EXPIRED"===s?"expired":"OPERATION_CANCELLED"===s?"cancelled":"OUTCOME_UNKNOWN"===s||"INSTANCE_EPOCH_MISMATCH"===s?"outcome_unknown":"failed",n="outcome_unknown"===r?"OUTCOME_UNKNOWN":s,o=RUNTIME_ERROR_DESCRIPTORS[n];await this.append(e,t,RUNTIME_MESSAGE_TYPES.final,{...correlation(e),outcome:r,response:{jsonrpc:"2.0",id:e.request.id,error:{code:o.jsonRpcCode,message:n,data:{code:n,operationId:e.operationId,effectState:r,retryMode:o.retryMode}}},completedAt:(new Date).toISOString()})}async append(e,t,s,r,n){this.currentLease(t);const o=this.options.consumer(),i=await o.append(e.operationId,s,r,n);if("created"===i.outcome||"duplicate"===i.outcome)return;let a=i.history;if(void 0===a){const t=await o.history(e.operationId);"found"===t.status&&(a=t.snapshot)}if(!hasCorrelatedMessage(a,s,e))throw new Error(`session/new ${s} append outcome is unknown`)}currentLease(e){const t=this.options.getLease();if(!t||t.instanceId!==e.instanceId||t.leaseId!==e.leaseId||t.runtimeEpoch!==e.runtimeEpoch||t.handlerIdentity!==e.handlerIdentity)throw new Error("session/new runtime delivery lease fence changed");if(!Number.isFinite(Date.parse(t.leaseExpiresAt))||Date.parse(t.leaseExpiresAt)<=Date.now())throw new Error("session/new runtime delivery lease expired");return t}}export function isCanonicalSessionNewDelivery(e){return"session/new"===record(e.message.payload)?.method}function parseSessionNewRequest(e){const t=e.message,s=record(t.payload),r=record(s?.request),n=record(r?.params),o=record(s?.command),i=record(o?.context),a=void 0===o?.params?void 0:record(o.params),d=[s?.operationId,s?.requestMessageId,s?.requestHash,s?.executionGrantHash,s?.instanceId,s?.runtimeHandlerIdentity,s?.dispatchEpoch,s?.callerIdentityId,s?.claimDeadlineAt,s?.executionDeadlineAt];return 1===s?.schemaVersion&&"session/new"===s?.method&&d.every(nonempty)&&"2.0"===r?.jsonrpc&&"session/new"===r.method&&("string"==typeof r.id||"number"==typeof r.id)&&n&&nonempty(n.agentId)&&"session.create"===o?.name&&i&&"instance"===i.scope&&i.operationId===s.operationId&&i.runtimeInstanceUid===s.instanceId&&i.dispatchEpoch===s.dispatchEpoch&&(void 0===o.params||a)&&t.type===RUNTIME_MESSAGE_TYPES.request&&t.channel_id===s.operationId&&t.message_id===s.requestMessageId&&Number.isFinite(Date.parse(s.claimDeadlineAt))&&Number.isFinite(Date.parse(s.executionDeadlineAt))?{ok:!0,request:s}:{ok:!1,reason:"required session/new fields do not correlate"}}function parseSessionNewHistory(e){const t=record(e),s=parseSessionNewRequest({deliveryId:"retained",redelivered:!0,idleMs:0,message:record(t?.request)});if(!s.ok||t?.operationId!==s.request.operationId||t.requestMessageId!==s.request.requestMessageId||t.requestHash!==s.request.requestHash)return{ok:!1};const r=record(t.started),n=record(r?.payload),o=void 0!==r&&n?.operationId===s.request.operationId&&n.requestMessageId===s.request.requestMessageId&&n.requestHash===s.request.requestHash&&n.dispatchEpoch===s.request.dispatchEpoch&&"session/new"===n.method;return{ok:!0,request:s.request,terminal:!0===t.terminal,terminalSource:string(t.terminalSource)??"none",cancelRequested:!0===t.cancelRequested,started:o}}function correlation(e){return{schemaVersion:1,operationId:e.operationId,requestMessageId:e.requestMessageId,requestHash:e.requestHash,dispatchEpoch:e.dispatchEpoch,method:"session/new"}}function hasCorrelatedMessage(e,t,s){const r=record(e);return(Array.isArray(r?.messages)?r.messages:[]).some(e=>{const r=record(e),n=record(r?.payload);return r?.type===t&&n?.operationId===s.operationId&&n.requestMessageId===s.requestMessageId&&n.requestHash===s.requestHash&&n.dispatchEpoch===s.dispatchEpoch&&"session/new"===n.method})}function optionalStringProperty(e,t){const s=e?.[t];return"string"==typeof s&&s.length>0?{[t]:s}:{}}function dedupe(e){return[...new Set(e.filter(e=>e.length>0))]}function httpStatus(e){const t=record(e),s=t?.status;return"number"==typeof s?s:void 0}function record(e){return null===e||"object"!=typeof e||Array.isArray(e)?void 0:e}function string(e){return"string"==typeof e&&e.length>0?e:void 0}function nonempty(e){return"string"==typeof e&&e.length>0}const noSessionNewResources=Object.freeze({async readContent(){throw new Error("session/new does not accept content resources")},async readVerifiedArtifact(){throw new Error("session/new does not accept artifact resources")}});
1
+ import{RUNTIME_ERROR_DESCRIPTORS,RUNTIME_HASH_DOMAINS,isCloudRuntimeMessageRequest}from"@beeos-ai/beeos-types/runtime";import{verifyAndCreateMessageServiceCoreExecutionAttempt}from"./core-execution-attempt-context.js";import{RuntimeSessionNewRejectedError}from"./runtime-rpc-consumer.js";import{RUNTIME_MESSAGE_TYPES,runtimeMessageHash}from"./runtime-message-channel-contract.js";const SESSION_NEW_METHOD="session/new",SESSION_NEW_COMMAND="session.create";export class SessionNewRuntimeDeliveryBridge{options;workers=new Map;constructor(e){this.options=e}async acceptDelivery(e,t,s){this.currentLease(t);const r=record(e.message.payload),n=string(r?.operationId)??e.message.channel_id,o=this.workers.get(n);if(o)return await o;const i=this.handleDelivery(e,t,s).finally(()=>this.workers.delete(n));this.workers.set(n,i),await i}async handleDelivery(e,t,s){const r=parseSessionNewRequest(e);if(!r.ok)return void await this.options.poison(e,`invalid_session_new:${r.reason}`,t);const n=r.request,o=this.options.consumer(),i=async()=>{this.currentLease(t),await o.acknowledge([e.deliveryId])},a=await o.history(n.operationId);if("found"!==a.status)return void await i();const d=parseSessionNewHistory(a.snapshot);if(!d.ok||d.request.operationId!==n.operationId||d.request.requestMessageId!==n.requestMessageId||d.request.requestHash!==n.requestHash)return void await this.options.poison(e,"session_new_history_mismatch",t);const c=d.request;if(d.terminal)return void await i();if(c.instanceId!==t.instanceId||c.runtimeHandlerIdentity!==t.handlerIdentity||c.dispatchEpoch!==t.runtimeEpoch)return await this.appendFailure(c,t,"INSTANCE_EPOCH_MISMATCH"),void await i();const u=Date.now();if("deadline"===d.terminalSource||Date.parse(c.executionDeadlineAt)<=u||!d.started&&Date.parse(c.claimDeadlineAt)<=u)return await this.appendFailure(c,t,"COMMAND_EXPIRED"),void await i();if(d.cancelRequested&&!d.started)return await this.appendFailure(c,t,"OPERATION_CANCELLED"),void await i();const p=e.executionGrant;if(!p)return await this.appendFailure(c,t,d.started?"OUTCOME_UNKNOWN":"INVALID_EXECUTION_GRANT"),void await i();await this.options.verifier.refresh(),this.options.verifier.verifyAndDecodeGrant(p)||await this.options.verifier.refresh(!0);const l=new AbortController,h=()=>l.abort();s.addEventListener("abort",h,{once:!0});try{const e=await verifyAndCreateMessageServiceCoreExecutionAttempt({request:c,executionGrant:p,runtimeEpoch:t.runtimeEpoch,lease:t,verifier:this.options.verifier,allowExpiredGrant:d.started,abortSignal:l.signal,resolvePorts:async()=>({resources:noSessionNewResources})});if(!e.ok)return await this.appendFailure(c,t,d.started?"OUTCOME_UNKNOWN":"INVALID_EXECUTION_GRANT"),void await i();if(s.aborted)throw new Error("session/new delivery owner stopped");d.started?this.currentLease(t):await this.append(c,t,RUNTIME_MESSAGE_TYPES.started,{...correlation(c),executionGrantHash:c.executionGrantHash,handlerIdentity:t.handlerIdentity,executionMode:"start",claimedAt:(new Date).toISOString()},p);const r=c.request.params.agentId;let n,o;try{n=await this.options.createRuntimeSession({operationId:c.operationId,platformAgentId:r,..."string"==typeof c.command.params?.runtimeWorkingDirectory?{cwd:c.command.params.runtimeWorkingDirectory}:{},...optionalStringProperty(c.command.params,"title")},e.context)}catch(e){if(!(e instanceof RuntimeSessionNewRejectedError))throw e;return await this.appendFailure(c,t,"INVALID_INVOCATION"),void await i()}try{o=await this.options.conversations.create({operationId:c.operationId,runtimeSessionId:n.sessionId,runtimeSessionKey:n.gatewaySessionKey,...optionalStringProperty(c.command.params,"title"),platformAgentId:r,runtimeInstanceId:c.instanceId,callerIdentityId:c.callerIdentityId,runtimeEpoch:c.dispatchEpoch})}catch(e){const s=httpStatus(e);if(void 0===s||s>=500||408===s||429===s)throw e;return await this.appendFailure(c,t,"INVALID_INVOCATION"),void await i()}const a={sessionId:n.sessionId,conversationId:o.conversationId},u=runtimeMessageHash(RUNTIME_HASH_DOMAINS.serviceResult,{method:"session/new",result:a});await this.append(c,t,RUNTIME_MESSAGE_TYPES.final,{...correlation(c),outcome:"succeeded",response:{jsonrpc:"2.0",id:c.request.id,result:a},serviceResultHash:u,completedAt:(new Date).toISOString()}),await i()}finally{l.abort(),s.removeEventListener("abort",h)}}async appendFailure(e,t,s){const r="COMMAND_EXPIRED"===s?"expired":"OPERATION_CANCELLED"===s?"cancelled":"OUTCOME_UNKNOWN"===s||"INSTANCE_EPOCH_MISMATCH"===s?"outcome_unknown":"failed",n="outcome_unknown"===r?"OUTCOME_UNKNOWN":s,o=RUNTIME_ERROR_DESCRIPTORS[n];await this.append(e,t,RUNTIME_MESSAGE_TYPES.final,{...correlation(e),outcome:r,response:{jsonrpc:"2.0",id:e.request.id,error:{code:o.jsonRpcCode,message:n,data:{code:n,operationId:e.operationId,effectState:r,retryMode:o.retryMode}}},completedAt:(new Date).toISOString()})}async append(e,t,s,r,n){this.currentLease(t);const o=this.options.consumer(),i=await o.append(e.operationId,s,r,n);if("created"===i.outcome||"duplicate"===i.outcome)return;let a=i.history;if(void 0===a){const t=await o.history(e.operationId);"found"===t.status&&(a=t.snapshot)}if(!hasCorrelatedMessage(a,s,e))throw new Error(`session/new ${s} append outcome is unknown`)}currentLease(e){const t=this.options.getLease();if(!t||t.instanceId!==e.instanceId||t.leaseId!==e.leaseId||t.runtimeEpoch!==e.runtimeEpoch||t.handlerIdentity!==e.handlerIdentity)throw new Error("session/new runtime delivery lease fence changed");if(!Number.isFinite(Date.parse(t.leaseExpiresAt))||Date.parse(t.leaseExpiresAt)<=Date.now())throw new Error("session/new runtime delivery lease expired");return t}}export function isCanonicalSessionNewDelivery(e){return"session/new"===record(e.message.payload)?.method}function parseSessionNewRequest(e){const t=e.message,s=record(t.payload),r=record(s?.request),n=record(r?.params),o=record(s?.command),i=record(o?.context),a=void 0===o?.params?void 0:record(o.params),d=s?.schemaVersion;if(2===d&&!isCloudRuntimeMessageRequest(s,runtimeMessageHash))return{ok:!1,reason:"invalid Cloud session/new request contract"};const c=[s?.operationId,s?.requestMessageId,s?.requestHash,s?.executionGrantHash,s?.instanceId,s?.runtimeHandlerIdentity,s?.dispatchEpoch,s?.claimDeadlineAt,s?.executionDeadlineAt];return!(1!==d&&2!==d||"session/new"!==s?.method||!c.every(nonempty)||1===d&&!nonempty(s.callerIdentityId)||"2.0"!==r?.jsonrpc||"session/new"!==r.method||"string"!=typeof r.id&&"number"!=typeof r.id)&&n&&nonempty(n.agentId)&&"session.create"===o?.name&&i&&"instance"===i.scope&&i.operationId===s.operationId&&i.runtimeInstanceUid===s.instanceId&&i.dispatchEpoch===s.dispatchEpoch&&(void 0===o.params||a)&&t.type===RUNTIME_MESSAGE_TYPES.request&&t.channel_id===s.operationId&&t.message_id===s.requestMessageId&&Number.isFinite(Date.parse(s.claimDeadlineAt))&&Number.isFinite(Date.parse(s.executionDeadlineAt))?{ok:!0,request:s}:{ok:!1,reason:"required session/new fields do not correlate"}}function parseSessionNewHistory(e){const t=record(e),s=parseSessionNewRequest({deliveryId:"retained",redelivered:!0,idleMs:0,message:record(t?.request)});if(!s.ok||t?.operationId!==s.request.operationId||t.requestMessageId!==s.request.requestMessageId||t.requestHash!==s.request.requestHash)return{ok:!1};const r=record(t.started),n=record(r?.payload),o=void 0!==r&&n?.operationId===s.request.operationId&&n.requestMessageId===s.request.requestMessageId&&n.requestHash===s.request.requestHash&&n.dispatchEpoch===s.request.dispatchEpoch&&"session/new"===n.method;return{ok:!0,request:s.request,terminal:!0===t.terminal,terminalSource:string(t.terminalSource)??"none",cancelRequested:!0===t.cancelRequested,started:o}}function correlation(e){return{schemaVersion:1,operationId:e.operationId,requestMessageId:e.requestMessageId,requestHash:e.requestHash,dispatchEpoch:e.dispatchEpoch,method:"session/new"}}function hasCorrelatedMessage(e,t,s){const r=record(e);return(Array.isArray(r?.messages)?r.messages:[]).some(e=>{const r=record(e),n=record(r?.payload);return r?.type===t&&n?.operationId===s.operationId&&n.requestMessageId===s.requestMessageId&&n.requestHash===s.requestHash&&n.dispatchEpoch===s.dispatchEpoch&&"session/new"===n.method})}function optionalStringProperty(e,t){const s=e?.[t];return"string"==typeof s&&s.length>0?{[t]:s}:{}}function httpStatus(e){const t=record(e),s=t?.status;return"number"==typeof s?s:void 0}function record(e){return null===e||"object"!=typeof e||Array.isArray(e)?void 0:e}function string(e){return"string"==typeof e&&e.length>0?e:void 0}function nonempty(e){return"string"==typeof e&&e.length>0}const noSessionNewResources=Object.freeze({async readContent(){throw new Error("session/new does not accept content resources")},async readVerifiedArtifact(){throw new Error("session/new does not accept artifact resources")}});
@@ -1 +1 @@
1
- import{toMessagePart}from"../message-client-types.js";import{createRuntimeErrorPart,parseRuntimeErrorPart}from"@beeos-ai/message-sdk/protocol";export async function writeRuntimeFailure(r,e){if(e.businessErrorCode){r.parts.some(r=>parseRuntimeErrorPart(r))||r.addPart(toMessagePart(createRuntimeErrorPart(e.businessErrorCode)))}r.setBody(e.body),await r.fail({stopReason:e.stopReason})}
1
+ import{toMessagePart}from"../message-client-types.js";import{createRuntimeErrorPart,parseRuntimeErrorPart}from"./runtime-error-part.js";export async function writeRuntimeFailure(r,e){if(e.businessErrorCode){r.parts.some(r=>parseRuntimeErrorPart(r))||r.addPart(toMessagePart(createRuntimeErrorPart(e.businessErrorCode)))}r.setBody(e.body),await r.fail({stopReason:e.stopReason})}
@@ -1 +1 @@
1
- import{constants}from"node:fs";import{access,stat}from"node:fs/promises";import{uploadLocalFile}from"./upload-file.js";import{isBeeosFileUri}from"../files/beeos-file-uri.js";export const BEEOS_UPLOAD_TOOL_NAME="beeos_upload_file";export const BEEOS_UPLOAD_TOOL_DESCRIPTION="Upload 1-5 local files from this machine to the user via the BeeOS platform. Accepts only local filesystem paths; never pass beeos-file:// URIs because they are already BeeOS platform resources. If the call succeeds, the files are already delivered to the user — do not use any other tool to send them again. Upload modified deliverables again even if the same path was uploaded earlier; unchanged contents may reuse an existing platform file. Any file type is accepted: text, images, documents, PDFs, archives, binaries.";export const BEEOS_UPLOAD_TOOL_SCHEMA={title:"BeeOS file upload",type:"object",description:BEEOS_UPLOAD_TOOL_DESCRIPTION,additionalProperties:!1,required:["paths"],properties:{paths:{type:"array",minItems:1,maxItems:5,description:"Local filesystem paths (1-5). Absolute or workspace-relative. Do not pass beeos-file:// URIs.",items:{type:"string",minLength:1,description:"Single local filesystem path, not a beeos-file:// URI."}}}};const MAX_PATHS=5,REQUEST_TIMEOUT_MS=12e4,MAX_CONCURRENCY=3,BEEOS_FILE_URI_UPLOAD_ERROR="beeos-file:// is already a BeeOS platform file URI, not a local path. Do not upload it; display it directly.";async function runWithConcurrency(e,t){const r=new Array(e.length);let a=0;async function o(){for(;a<e.length;){const t=a++;r[t]=await e[t]()}}const s=[];for(let r=0;r<Math.min(t,e.length);r++)s.push(o());return await Promise.all(s),r}async function validateFiles(e){return Promise.all(e.map(async e=>{try{await access(e,constants.R_OK);return(await stat(e)).isFile()?{path:e,ok:!0}:{path:e,ok:!1,error:"not a regular file"}}catch(t){const r=t.code;let a;return a="ENOENT"===r?"file not found":"ENOTDIR"===r?"path component is not a directory":"EACCES"===r?"permission denied":t instanceof Error?t.message:String(t),{path:e,ok:!1,error:a}}}))}export async function executeUpload(e,t,r,a){const o=parseParams(e);if(!o.ok)return errorResult(o.error);if(!t.agentGatewayUrl)return errorResult("agentGatewayUrl not configured — file upload requires Agent Gateway access");if(!r)return errorResult("agent key pair not available — cannot sign upload requests");const s=t.agentGatewayUrl,i=o.paths,n=(await validateFiles(i)).find(e=>!e.ok);if(n)return errorResult(`${n.path}: ${n.error}`);const l=[],p=i.map(e=>async()=>{try{return await uploadSingleFile(e,s,r,a)}catch(t){if(a?.aborted)throw t;return l.push(`${e}: ${t instanceof Error?t.message:String(t)}`),null}}),c=(await runWithConcurrency(p,3)).filter(e=>null!==e);if(0===c.length)return errorResult(`All uploads failed: ${l.join("; ")}`);const u=c.map(e=>({uri:`beeos-file://${e.fileId}`,name:e.fileName,mimeType:e.mimeType,fileId:e.fileId})),f=c.length===i.length?`Uploaded ${c.length} file(s): ${u.map(e=>e.name).join(", ")}`:`Uploaded ${c.length}/${i.length} file(s): ${u.map(e=>e.name).join(", ")}. Failed: ${l.join("; ")}`,m={ok:!0,files:u};return{...m,content:[{type:"text",text:f},...u.map(e=>({type:"resource_link",uri:e.uri,name:e.name,mimeType:e.mimeType}))],output:m,result:m,details:m,isError:!1}}async function uploadSingleFile(e,t,r,a){const o=await uploadLocalFile(e,t,r,{signal:a,timeoutMs:12e4});return{fileId:o.fileId,downloadUrl:"",fileName:o.name,mimeType:o.mimeType,size:o.size}}function parseParams(e){if(!e||"object"!=typeof e)return{ok:!1,error:"params must be an object"};const t=e;if(Object.keys(t).filter(e=>"paths"!==e).length>0)return{ok:!1,error:"only paths is allowed"};if(!Array.isArray(t.paths))return{ok:!1,error:"paths must be an array"};if(t.paths.length<1)return{ok:!1,error:"paths must contain at least 1 item"};if(t.paths.length>5)return{ok:!1,error:"paths must contain no more than 5 items"};const r=[];for(let e=0;e<t.paths.length;e++){const a=t.paths[e];if("string"!=typeof a||""===a.trim())return{ok:!1,error:`paths[${e}] must be a non-empty string`};const o=a.trim();if(isBeeosFileUri(o))return{ok:!1,error:BEEOS_FILE_URI_UPLOAD_ERROR};r.push(o)}return{ok:!0,paths:r}}function errorResult(e){const t={ok:!1,error:e};return{...t,content:[{type:"text",text:e}],output:t,result:t,details:t,isError:!0}}
1
+ import{constants}from"node:fs";import{access,stat}from"node:fs/promises";import{uploadLocalFile,NativeOutputFilesError}from"./upload-file.js";import{isBeeosFileUri}from"../files/beeos-file-uri.js";export const BEEOS_UPLOAD_TOOL_NAME="beeos_upload_file";export const BEEOS_UPLOAD_TOOL_DESCRIPTION="Upload 1-5 local files from this machine to the user via the BeeOS platform. This is the only way to deliver a file to the user: whenever you produce or modify a file the user asked for, call this tool with its path. Writing the file to disk or printing it through a shell command does not deliver it. Accepts only local filesystem paths; never pass beeos-file:// URIs because they are already BeeOS platform resources. If the call succeeds, the files are already delivered to the user — do not use any other tool to send them again. Upload modified deliverables again even if the same path was uploaded earlier; unchanged contents may reuse an existing platform file. Any file type is accepted: text, images, documents, PDFs, archives, binaries.";export const BEEOS_UPLOAD_TOOL_SCHEMA={title:"BeeOS file upload",type:"object",description:BEEOS_UPLOAD_TOOL_DESCRIPTION,additionalProperties:!1,required:["paths"],properties:{paths:{type:"array",minItems:1,maxItems:5,description:"Local filesystem paths (1-5). Absolute or workspace-relative. Do not pass beeos-file:// URIs.",items:{type:"string",minLength:1,description:"Single local filesystem path, not a beeos-file:// URI."}}}};const MAX_PATHS=5;export const REQUEST_TIMEOUT_MS=12e4;const MAX_CONCURRENCY=3,BEEOS_FILE_URI_UPLOAD_ERROR="beeos-file:// is already a BeeOS platform file URI, not a local path. Do not upload it; display it directly.";async function runWithConcurrency(e,t){const r=new Array(e.length);let a=0;async function o(){for(;a<e.length;){const t=a++;r[t]=await e[t]()}}const i=[];for(let r=0;r<Math.min(t,e.length);r++)i.push(o());return await Promise.all(i),r}async function validateFiles(e){return Promise.all(e.map(async e=>{try{await access(e,constants.R_OK);return(await stat(e)).isFile()?{path:e,ok:!0}:{path:e,ok:!1,error:"not a regular file"}}catch(t){const r=t.code;let a;return a="ENOENT"===r?"file not found":"ENOTDIR"===r?"path component is not a directory":"EACCES"===r?"permission denied":t instanceof Error?t.message:String(t),{path:e,ok:!1,error:a}}}))}export async function executeUpload(e,t,r,a,o,i){const s=parseParams(e);if(!s.ok)return errorResult(s.error);if(!t.agentGatewayUrl)return errorResult("agentGatewayUrl not configured — file upload requires Agent Gateway access");if(!r&&!o)return errorResult("agent key pair not available — cannot sign upload requests");const n=t.agentGatewayUrl,l=s.paths,c=(await validateFiles(l)).find(e=>!e.ok);if(c)return errorResult(`${c.path}: ${c.error}`);const p=[],u=l.map((e,t)=>async()=>{try{if(o){const r=await o(e,t);return{fileId:r.fileId,downloadUrl:"",fileName:r.name,mimeType:r.mimeType,size:r.size}}return await uploadSingleFile(e,n,r,a)}catch(r){if(a?.aborted)throw r;const{category:s,detail:n}=classifyUploadFailure(r,Boolean(o));return i?.warn?.(`[beeos_upload_file] upload failed index=${t} native=${Boolean(o)} category=${s} ${n}`),p.push(o?r instanceof NativeOutputFilesError?r.message:"native output Files transport or local read failed":`${e}: ${r instanceof Error?r.message:String(r)}`),null}}),f=(await runWithConcurrency(u,3)).filter(e=>null!==e);if(0===f.length)return errorResult(`All uploads failed: ${p.join("; ")}`);const m=f.map(e=>({uri:`beeos-file://${e.fileId}`,name:e.fileName,mimeType:e.mimeType,fileId:e.fileId})),d=f.length===l.length?`Uploaded ${f.length} file(s): ${m.map(e=>e.name).join(", ")}`:`Uploaded ${f.length}/${l.length} file(s): ${m.map(e=>e.name).join(", ")}. Failed: ${p.join("; ")}`,h={ok:!0,files:m};return{...h,content:[{type:"text",text:d},...m.map(e=>({type:"resource_link",uri:e.uri,name:e.name,mimeType:e.mimeType}))],output:h,result:h,details:h,isError:!1}}async function uploadSingleFile(e,t,r,a){const o=await uploadLocalFile(e,t,r,{signal:a,timeoutMs:12e4});return{fileId:o.fileId,downloadUrl:"",fileName:o.name,mimeType:o.mimeType,size:o.size}}function parseParams(e){if(!e||"object"!=typeof e)return{ok:!1,error:"params must be an object"};const t=e;if(Object.keys(t).filter(e=>"paths"!==e).length>0)return{ok:!1,error:"only paths is allowed"};if(!Array.isArray(t.paths))return{ok:!1,error:"paths must be an array"};if(t.paths.length<1)return{ok:!1,error:"paths must contain at least 1 item"};if(t.paths.length>5)return{ok:!1,error:"paths must contain no more than 5 items"};const r=[];for(let e=0;e<t.paths.length;e++){const a=t.paths[e];if("string"!=typeof a||""===a.trim())return{ok:!1,error:`paths[${e}] must be a non-empty string`};const o=a.trim();if(isBeeosFileUri(o))return{ok:!1,error:BEEOS_FILE_URI_UPLOAD_ERROR};r.push(o)}return{ok:!0,paths:r}}function classifyUploadFailure(e,t){const r=e instanceof Error?e.message:String(e),a=e instanceof Error&&e.cause instanceof Error?` cause=${e.cause.message}`:"";if(e instanceof NativeOutputFilesError)return{category:e.category,detail:`${e.detail??""} message=${r}`.trim()};if(e instanceof DOMException&&("AbortError"===e.name||"TimeoutError"===e.name))return{category:"aborted",detail:`name=${e.name} message=${r}`};if(e instanceof TypeError)return{category:"transport_rejected",detail:`message=${r}${a}`};const o=e?.code;return"string"==typeof o?{category:"local_read_failed",detail:`code=${o} message=${r}`}:{category:t?"attempt_or_lease_fenced":"unclassified",detail:`message=${r}${a}`}}function errorResult(e){const t={ok:!1,error:e};return{...t,content:[{type:"text",text:e}],output:t,result:t,details:t,isError:!0}}
@@ -1 +1 @@
1
- import{access,constants,readFile,stat}from"node:fs/promises";import{createHash}from"node:crypto";import{basename,extname,resolve}from"node:path";import{agentFetch}from"../agent-auth.js";import{noteUploadedDeliverable}from"./delivery-ledger.js";const DEFAULT_TIMEOUT_MS=3e4,UPLOAD_REUSE_TTL_MS=18e5,MAX_REUSED_UPLOADS=256,completedUploads=new Map;let _deps=null;export function setUploadDeps(e){_deps=e}export function getUploadDeps(){return _deps}export async function uploadLocalFile(e,t,a,r){await access(e,constants.R_OK);if(!(await stat(e)).isFile())throw new Error("not a regular file");const o=basename(e),s=r?.fileNameOverride??o,n=inferMimeType(s),i=await readFile(e);r?.signal?.throwIfAborted();const c=createHash("sha256").update(i).digest("hex"),p=JSON.stringify([t,Buffer.from(a.publicKey).toString("base64"),resolve(e),s,c]),l=Date.now();for(const[e,t]of completedUploads)t.expiresAt<=l&&completedUploads.delete(e);const d=completedUploads.get(p);if(d)return noteUploadedDeliverable(e),{...d.result};const m=AbortSignal.timeout(r?.timeoutMs??3e4),u=r?.signal?AbortSignal.any([r.signal,m]):m,f=`${t}/api/v1/agent/files`,g=await agentFetch(f+"/presign",a,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({fileName:s,mimeType:n}),signal:u});if(!g.ok){const e=await g.text().catch(()=>g.statusText);throw new Error(`presign ${g.status}: ${e}`)}const x=await g.json(),w=await fetch(x.uploadUrl,{method:"PUT",headers:{"Content-Type":n},body:new Blob([i],{type:n}),signal:u});if(!w.ok)throw new Error(`S3 PUT failed: ${w.status}`);const v=await agentFetch(f+"/confirm",a,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({fileId:x.fileId}),signal:u});if(!v.ok){const e=await v.text().catch(()=>v.statusText);throw new Error(`confirm ${v.status}: ${e}`)}const h=await v.json(),y={fileId:h.fileId,uri:`beeos-file://${h.fileId}`,name:h.fileName||s,mimeType:h.mimeType||n,size:h.size||i.byteLength};if(!y.fileId)throw new Error("confirm returned no fileId");return completedUploads.size>=256&&completedUploads.delete(completedUploads.keys().next().value),completedUploads.set(p,{result:y,expiresAt:Date.now()+18e5}),noteUploadedDeliverable(e),{...y}}function inferMimeType(e){switch(extname(e).toLowerCase()){case".txt":return"text/plain";case".md":return"text/markdown";case".json":return"application/json";case".html":return"text/html";case".css":return"text/css";case".js":case".mjs":case".cjs":return"text/javascript";case".ts":return"text/x.typescript";case".tsx":case".jsx":return"text/jsx";case".xml":return"application/xml";case".yaml":case".yml":return"application/yaml";case".toml":return"application/toml";case".pdf":return"application/pdf";case".doc":return"application/msword";case".docx":return"application/vnd.openxmlformats-officedocument.wordprocessingml.document";case".xls":return"application/vnd.ms-excel";case".xlsx":return"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";case".ppt":return"application/vnd.ms-powerpoint";case".pptx":return"application/vnd.openxmlformats-officedocument.presentationml.presentation";case".rtf":return"application/rtf";case".odt":return"application/vnd.oasis.opendocument.text";case".ods":return"application/vnd.oasis.opendocument.spreadsheet";case".odp":return"application/vnd.oasis.opendocument.presentation";case".png":return"image/png";case".jpg":case".jpeg":return"image/jpeg";case".gif":return"image/gif";case".webp":return"image/webp";case".svg":return"image/svg+xml";case".mp4":return"video/mp4";case".mov":return"video/quicktime";case".webm":return"video/webm";case".mkv":return"video/x-matroska";case".avi":return"video/x-msvideo";case".m4v":return"video/x-m4v";case".mp3":return"audio/mpeg";case".wav":return"audio/wav";case".flac":return"audio/flac";case".aac":return"audio/aac";case".ogg":return"audio/ogg";case".m4a":return"audio/mp4";case".csv":return"text/csv";case".zip":return"application/zip";case".tar":return"application/x-tar";case".gz":case".tgz":return"application/gzip";default:return"application/octet-stream"}}
1
+ import{cloudRuntimeTargetInstanceId}from"../runtime/runtime-production-authority.js";import{access,constants,readFile,stat}from"node:fs/promises";import{createHash}from"node:crypto";import{basename,extname,resolve}from"node:path";import{agentFetch}from"../agent-auth.js";import{noteUploadedDeliverable}from"./delivery-ledger.js";const DEFAULT_TIMEOUT_MS=3e4,UPLOAD_REUSE_TTL_MS=18e5,MAX_REUSED_UPLOADS=256,completedUploads=new Map;let _deps=null;export function setUploadDeps(e){_deps=e}export function getUploadDeps(){return _deps}export async function uploadLocalFile(e,t,r,a){if(cloudRuntimeTargetInstanceId(process.env))throw new Error("native output file trusted tool capability required");await access(e,constants.R_OK);if(!(await stat(e)).isFile())throw new Error("not a regular file");const i=basename(e),s=a?.fileNameOverride??i,o=inferMimeType(s),n=await readFile(e);a?.signal?.throwIfAborted();const p=createHash("sha256").update(n).digest("hex"),c=JSON.stringify([t,Buffer.from(r.publicKey).toString("base64"),resolve(e),s,p]),l=Date.now();for(const[e,t]of completedUploads)t.expiresAt<=l&&completedUploads.delete(e);const d=completedUploads.get(c);if(d)return noteUploadedDeliverable(e),{...d.result};const u=AbortSignal.timeout(a?.timeoutMs??3e4),m=a?.signal?AbortSignal.any([a.signal,u]):u,f=`${t}/api/v1/agent/files`,h=await agentFetch(f+"/presign",r,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({fileName:s,mimeType:o}),signal:m});if(!h.ok){const e=await h.text().catch(()=>h.statusText);throw new Error(`presign ${h.status}: ${e}`)}const g=await h.json(),w=await fetch(g.uploadUrl,{method:"PUT",headers:{"Content-Type":o},body:new Blob([n],{type:o}),signal:m});if(!w.ok)throw new Error(`S3 PUT failed: ${w.status}`);const y=await agentFetch(f+"/confirm",r,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({fileId:g.fileId}),signal:m});if(!y.ok){const e=await y.text().catch(()=>y.statusText);throw new Error(`confirm ${y.status}: ${e}`)}const v=await y.json(),_={fileId:v.fileId,uri:`beeos-file://${v.fileId}`,name:v.fileName||s,mimeType:v.mimeType||o,size:v.size||n.byteLength};if(!_.fileId)throw new Error("confirm returned no fileId");return completedUploads.size>=256&&completedUploads.delete(completedUploads.keys().next().value),completedUploads.set(c,{result:_,expiresAt:Date.now()+18e5}),noteUploadedDeliverable(e),{..._}}export class NativeOutputFilesError extends Error{category;detail;constructor(e,t,r){super(e),this.category=t,this.detail=r,this.name="NativeOutputFilesError"}}export async function uploadNativeOutputFile(e,t,r,a,i,s){r.assertAttempt(),await access(e,constants.R_OK);const o=await stat(e);if(!o.isFile()||o.size<=0||o.size>104857600)throw new Error("native output file size invalid");const n=await readFile(e);if(r.assertAttempt(),n.byteLength!==o.size)throw new Error("native output file changed while reading");const p=basename(e),c=inferMimeType(p),l=createHash("sha256").update(n).digest("hex"),d=createHash("sha256").update("beeos/native-output-file/v1\0"+r.operationId+"\0"+r.toolCallId+"\0"+a).digest("hex"),u=new URL(t);if(!["http:","https:"].includes(u.protocol)||u.username||u.password||"/"!==u.pathname||u.search||u.hash)throw new Error("native output Files origin invalid");const m=AbortSignal.any([r.signal,AbortSignal.timeout(s??3e4),...i?[i]:[]]),f={requestMessageId:r.requestMessageId,paramsHash:r.paramsHash,activeRunRegistrationId:r.activeRunRegistrationId,idempotencyKey:d},h=e=>!!e&&"object"==typeof e&&!Array.isArray(e),g=async(e,t)=>{r.assertAttempt();const a=r.getLease(),i=await fetch(new URL("/api/v1/internal/runtime/operations/"+encodeURIComponent(r.operationId)+"/output-files/"+e,u),{method:"POST",redirect:"error",signal:m,headers:{authorization:"Bearer "+a.runtimeLeaseCredential,"content-type":"application/json"},body:JSON.stringify({...f,...t})});if(r.assertAttempt(),!i.ok){let t;try{const e=await i.json();h(e)&&h(e.error)&&"string"==typeof e.error.code&&/^[A-Za-z0-9_.:-]{1,128}$/.test(e.error.code)&&(t=e.error.code)}catch(t){if(!(t instanceof SyntaxError))throw new NativeOutputFilesError("native output Files error decode failed","control_error_decode",`stage=${e} http_status=${i.status}`)}throw new NativeOutputFilesError("native output Files "+e+" failed ("+i.status+(t?","+t:"")+")","control_http_error",`stage=${e} http_status=${i.status} code=${t??"none"}`)}if(i.url&&new URL(i.url).origin!==u.origin)throw new NativeOutputFilesError("native output Files response origin changed","control_response_origin",`stage=${e}`);const s=await i.json();if(r.assertAttempt(),!h(o=s)||o.operationId!==r.operationId||o.requestMessageId!==f.requestMessageId||o.paramsHash!==f.paramsHash||o.activeRunRegistrationId!==f.activeRunRegistrationId)throw new NativeOutputFilesError("native output Files response binding invalid","control_response_binding",`stage=${e}`);var o;return s},w=await g("presign",{filename:p,content_type:c,size_bytes:n.byteLength,checksum_sha256:l}),y=w.upload,v=w.transferPolicy;if(!h(y)||!h(v)||"string"!=typeof v.allowedOrigin||"upload"!==y.direction||"PUT"!==y.http_method||"string"!=typeof y.file_id||!y.file_id||"string"!=typeof y.url||"string"!=typeof y.expires_at||!Number.isFinite(Date.parse(y.expires_at))||Date.parse(y.expires_at)<=Date.now()||y.content_type!==c||y.size_bytes!==n.byteLength||y.checksum_sha256!==l||!h(y.required_headers)||Object.values(y.required_headers).some(e=>"string"!=typeof e))throw new NativeOutputFilesError("native output Files transfer descriptor invalid","transfer_descriptor_invalid",descriptorDefect(y,v,c,n.byteLength,l));const _=new URL(y.url),x=new URL(v.allowedOrigin);if("https:"!==_.protocol||_.username||_.password||_.hash||"https:"!==x.protocol||x.username||x.password||"/"!==x.pathname||x.search||x.hash||x.origin!==v.allowedOrigin||_.origin!==x.origin)throw new NativeOutputFilesError("native output Files transfer origin invalid","transfer_origin_invalid",`target_matches_allowed=${_.origin===x.origin}`);r.assertAttempt();const b=await fetch(_,{method:"PUT",redirect:"error",signal:m,headers:y.required_headers,body:new Blob([n],{type:c})});if(r.assertAttempt(),!b.ok)throw new NativeOutputFilesError("native output Files PUT failed ("+b.status+")","put_rejected",`http_status=${b.status} bytes=${n.byteLength} headers=${Object.keys(y.required_headers).join(",")}`);let I;try{I=await g("confirm",{fileId:y.file_id,checksumSha256:l})}catch(e){if(r.assertAttempt(),m.throwIfAborted(),!(e instanceof TypeError||e instanceof SyntaxError))throw e;I=await g("confirm",{fileId:y.file_id,checksumSha256:l})}if(I.fileId!==y.file_id||I.fileName!==p||I.mimeType!==c||I.size!==n.byteLength)throw new NativeOutputFilesError("native output Files ready metadata mismatch","ready_metadata_mismatch",`fileId=${I.fileId===y.file_id} fileName=${I.fileName===p} mimeType=${I.mimeType===c} size=${I.size===n.byteLength}`);r.assertAttempt();const U={fileId:y.file_id,uri:"beeos-file://"+y.file_id,name:p,mimeType:c,size:n.byteLength};return noteUploadedDeliverable(e),U}function descriptorDefect(e,t,r,a,i){const s=e=>!!e&&"object"==typeof e&&!Array.isArray(e);if(!s(e))return"upload=absent";if(!s(t)||"string"!=typeof t.allowedOrigin)return"transferPolicy.allowedOrigin=invalid";const o=[["direction","upload"===e.direction],["http_method","PUT"===e.http_method],["file_id","string"==typeof e.file_id&&e.file_id.length>0],["url","string"==typeof e.url],["expires_at","string"==typeof e.expires_at&&Number.isFinite(Date.parse(e.expires_at))&&Date.parse(e.expires_at)>Date.now()],["content_type",e.content_type===r],["size_bytes",e.size_bytes===a],["checksum_sha256",e.checksum_sha256===i],["required_headers",s(e.required_headers)&&Object.values(e.required_headers).every(e=>"string"==typeof e)]];return`field=${o.find(([,e])=>!e)?.[0]??"none"} bytes=${a}`}function inferMimeType(e){switch(extname(e).toLowerCase()){case".txt":return"text/plain";case".md":return"text/markdown";case".json":return"application/json";case".html":return"text/html";case".css":return"text/css";case".js":case".mjs":case".cjs":return"text/javascript";case".ts":return"text/x.typescript";case".tsx":case".jsx":return"text/jsx";case".xml":return"application/xml";case".yaml":case".yml":return"application/yaml";case".toml":return"application/toml";case".pdf":return"application/pdf";case".doc":return"application/msword";case".docx":return"application/vnd.openxmlformats-officedocument.wordprocessingml.document";case".xls":return"application/vnd.ms-excel";case".xlsx":return"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";case".ppt":return"application/vnd.ms-powerpoint";case".pptx":return"application/vnd.openxmlformats-officedocument.presentationml.presentation";case".rtf":return"application/rtf";case".odt":return"application/vnd.oasis.opendocument.text";case".ods":return"application/vnd.oasis.opendocument.spreadsheet";case".odp":return"application/vnd.oasis.opendocument.presentation";case".png":return"image/png";case".jpg":case".jpeg":return"image/jpeg";case".gif":return"image/gif";case".webp":return"image/webp";case".svg":return"image/svg+xml";case".mp4":return"video/mp4";case".mov":return"video/quicktime";case".webm":return"video/webm";case".mkv":return"video/x-matroska";case".avi":return"video/x-msvideo";case".m4v":return"video/x-m4v";case".mp3":return"audio/mpeg";case".wav":return"audio/wav";case".flac":return"audio/flac";case".aac":return"audio/aac";case".ogg":return"audio/ogg";case".m4a":return"audio/mp4";case".csv":return"text/csv";case".zip":return"application/zip";case".tar":return"application/x-tar";case".gz":case".tgz":return"application/gzip";default:return"application/octet-stream"}}
@@ -18,15 +18,6 @@
18
18
  "beeos_discover_agents",
19
19
  "beeos_call_agent",
20
20
  "beeos_get_task",
21
- "beeos_automation_create",
22
- "beeos_automation_create_webhook",
23
- "beeos_automation_capabilities",
24
- "beeos_automation_preflight",
25
- "beeos_automation_list",
26
- "beeos_automation_get",
27
- "beeos_automation_run",
28
- "beeos_automation_runs",
29
- "beeos_automation_get_run",
30
21
  "cron_toggle",
31
22
  "cron_run",
32
23
  "cron_delete"
@@ -80,12 +71,7 @@
80
71
  "service": {
81
72
  "type": "string",
82
73
  "description": "Service type for Bridge registration (default: acp)",
83
- "enum": [
84
- "acp",
85
- "screen",
86
- "desktop",
87
- "shell"
88
- ]
74
+ "enum": ["acp", "screen", "desktop", "shell"]
89
75
  },
90
76
  "mode": {
91
77
  "type": "string",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "beeos-claw",
3
- "version": "0.6.63-cloud.1",
3
+ "version": "0.6.63-cloud.3",
4
4
  "description": "OpenClaw plugin that bridges the BeeOS platform with the local OpenClaw Gateway, providing file transfer, ACP bridge, and observability.",
5
5
  "license": "MIT",
6
6
  "author": "BeeOS <dev@beeos.ai>",
@@ -65,12 +65,12 @@
65
65
  "test:watch": "vitest"
66
66
  },
67
67
  "dependencies": {
68
- "@beeos-ai/beeos-types": "4.4.2-cloud.1",
68
+ "@beeos-ai/beeos-types": "4.4.2-cloud.3",
69
69
  "@beeos-ai/bridge-client": "^0.1.0",
70
- "@beeos-ai/cloud-agent-sdk": "1.0.0",
70
+ "@beeos-ai/cloud-agent-sdk": "1.0.0-cloud.3",
71
71
  "@beeos-ai/message-sdk": "2.0.16",
72
72
  "@modelcontextprotocol/sdk": "^1.29.0",
73
- "adm-zip": "0.6.1",
73
+ "adm-zip": "^0.6.0",
74
74
  "ws": "^8.18.0"
75
75
  },
76
76
  "devDependencies": {
@@ -88,5 +88,5 @@
88
88
  "fast-uri": ">=3.1.4",
89
89
  "brace-expansion": ">=5.0.9"
90
90
  },
91
- "gitHead": "5f9a6c03180e102484e28ed74ecb383a8bcaf137"
91
+ "gitHead": "bea059b6687c777b0a2866a71ed0d99980cf9303"
92
92
  }
@@ -4,18 +4,18 @@
4
4
  "hashAlgorithm": "SHA-256",
5
5
  "canonicalTypes": {
6
6
  "package": "@beeos-ai/beeos-types",
7
- "version": "4.4.2-beta.2",
7
+ "version": "4.4.2-cloud.3",
8
8
  "runtimeRpcContractRevision": "2026-07-14.3",
9
- "runtimeMethodManifestRawSHA256": "2af68ff555d4a4aa3c7b19a285e48a3df2ef9b43aa618ffddabbebc6e4280162",
10
- "runtimeMethodManifestJcsSHA256": "d5db2c5e7c78cee73e5672a3c18a8e56179b28888f6fbb660d98ec021ca9de9b"
9
+ "runtimeMethodManifestRawSHA256": "f75832c57a18ca614e946a3b1d194828df76d600907f378efe28633df94146fc",
10
+ "runtimeMethodManifestJcsSHA256": "2e58dbc13b8bef2491b95f89920945cb905085828b2fa7a4d4f621624971b186"
11
11
  },
12
12
  "artifacts": [
13
13
  {
14
14
  "name": "runtime-parity-manifest",
15
15
  "path": "openapi/runtime-parity-manifest-v1.json",
16
16
  "revision": "beeos.runtime-parity.v1.1.0",
17
- "rawSHA256": "7028b5a0dd85ee78d7df0690d2ef13ef2118acb08a1c8f718d3e9f08da8f8404",
18
- "jcsSHA256": "d464594fe15a1657c805c8829d676a4c93aec5c43e020f3b1fae21f4c9453ca7"
17
+ "rawSHA256": "f1030e7bf2084f584d558c22b9bffde0f823d6950c696a6f0571adaffcdbcee0",
18
+ "jcsSHA256": "c98ed2aa822b29530cb02badd51654f2766f1dc0ff2c53b660be6431b1f23747"
19
19
  },
20
20
  {
21
21
  "name": "runtime-message-channel",
@@ -1 +0,0 @@
1
- import{randomUUID}from"node:crypto";import{agentFetch}from"../agent-auth.js";const AUTOMATIONS_PATH="/api/v1/automations",WEBHOOK_AUTOMATIONS_PATH="/api/v1/automations/webhooks",CONNECTOR_MCP_PATH="/api/v1/agent/connectors/mcp",UUID_RE=/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;function asObject(e){return e&&"object"==typeof e&&!Array.isArray(e)?e:{}}function stringValue(e){return"string"==typeof e&&e.trim()?e.trim():void 0}function toolResult(e){return{content:JSON.stringify(e,null,2)}}function isUuid(e){return Boolean(e&&UUID_RE.test(e))}function resolveTarget(e){const t=e.getDefaultAgent?.(),r=stringValue(t?.instanceId),o=stringValue(t?.id);if(r&&o)return{instanceId:r,agentId:o}}function resolveConversationId(e,t){const r=stringValue(e.getCurrentConversationId?.(t));return isUuid(r)?r:void 0}async function request(e,t,r={}){const o=`${e.agentGatewayUrl.replace(/\/+$/,"")}${t}`;try{const t=e.agentKeys?await agentFetch(o,e.agentKeys,r):await fetch(o,r),n=await t.text();let a=n;try{a=n?JSON.parse(n):{}}catch{}if(!t.ok)return{ok:!1,status:t.status,error:`Automation request failed: HTTP ${t.status}`,details:a};const s=asObject(a);return{ok:!0,data:Object.hasOwn(s,"data")?s.data:a}}catch(e){return{ok:!1,status:0,error:e instanceof Error?e.message:String(e),details:void 0}}}function jsonRequestBody(e){return{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(e)}}function buildSchedule(e){const t=stringValue(e.kind)??"every",r={kind:t,tz:stringValue(e.tz)??"UTC"};switch(t){case"every":{r.intervalSeconds="number"==typeof e.intervalSeconds?String(Math.trunc(e.intervalSeconds)):stringValue(e.intervalSeconds)??"3600";const t=stringValue(e.anchorAt);t&&(r.anchorAt=t);break}case"at":{const t=stringValue(e.at);t&&(r.at=t);break}case"cron":{const t=stringValue(e.expression);t&&(r.expression=t);break}default:return{error:"schedule.kind must be one of at, every, or cron"}}return r}function buildSession(e,t,r){const o=stringValue(asObject(e.session).mode)??"new";if("new"!==o&&"bound"!==o)return{error:"session.mode must be new or bound"};const n={mode:o},a=resolveConversationId(t,r);if("bound"===o){if(!a)return{error:"bound Automation requires the current conversation context"};n.boundConversationId=a}return n}function createInput(e,t,r){const o=asObject(e),n=stringValue(o.name),a=stringValue(o.prompt),s=resolveTarget(t);if(!n||!a||!s)return{error:"name, prompt, and a resolvable target agent are required"};const i=buildSchedule(asObject(o.schedule));if("error"in i)return i;const u=buildSession(o,t,r);if("error"in u)return u;const c={name:n,prompt:a,target:s,schedule:i,session:u},l=stringValue(o.validFrom),d=stringValue(o.validUntil);l&&(c.validFrom=l),d&&(c.validUntil=d);const p=resolveConversationId(t,r);return p&&(c.createdInConversationId=p),c}function createWebhookInput(e,t,r){const o=asObject(e),n=stringValue(o.name),a=stringValue(o.prompt),s=(stringValue(o.source)??"").toLowerCase(),i=resolveTarget(t);if(!n||!a||!i)return{error:"name, prompt, and a resolvable target agent are required"};if("github"!==s&&"gmail"!==s)return{error:"source must be github or gmail"};const u=o.events,c=Array.isArray(u)?u.filter(e=>"string"==typeof e&&Boolean(e.trim())).map(e=>e.trim()):[],l=stringValue(o.repoOwner),d=stringValue(o.repoName),p=stringValue(o.mailbox);if(!("github"!==s||0!==c.length&&l&&d))return{error:"github webhook requires events, repoOwner, and repoName"};if("gmail"===s&&!p)return{error:"gmail webhook requires mailbox"};const m=buildSession(o,t,r);if("error"in m)return m;const g={name:n,prompt:a,target:i,session:m,source:s};"github"===s?(g.events=c,g.repoOwner=l,g.repoName=d):g.mailbox=p;const y=stringValue(o.eventFilter);y&&(g.eventFilter=y);const f=stringValue(o.validFrom),b=stringValue(o.validUntil);f&&(g.validFrom=f),b&&(g.validUntil=b);const h=resolveConversationId(t,r);return h&&(g.createdInConversationId=h),g}function unwrapConnectorMcp(e){let t=e;if("string"==typeof t)try{t=JSON.parse(t)}catch{return}if(!t||"object"!=typeof t||Array.isArray(t))return;const r=t;if(null!=r.error)return;let o=r.result??r;if(!o||"object"!=typeof o||Array.isArray(o))return;const n=o;if(n.isError)return;const a=n.structuredContent;if(a&&"object"==typeof a&&!Array.isArray(a))return a;const s=n.content;if(Array.isArray(s))for(const e of s){if(!e||"object"!=typeof e||Array.isArray(e))continue;const t=e;if("text"===t.type&&"string"==typeof t.text)try{const e=JSON.parse(t.text);if(e&&"object"==typeof e&&!Array.isArray(e))return e}catch{continue}}return n}async function runPreflight(e,t,r){const o=asObject(t),n=(stringValue(o.source)??"").toLowerCase(),a=resolveConversationId(e,r),s=resolveTarget(e),i={source:n,operation:"webhook",ready:!1,platform:{reachable:!1,authorized:!1},connector:{id:n,installed:!1,credentialStatus:"unknown"},operationSupported:"github"===n||"gmail"===n,target:{resolved:Boolean(s)},session:{canBindCurrentConversation:Boolean(a)},nextAction:"retry_or_check_connection"};if("github"!==n&&"gmail"!==n)return i.blockingReason="unsupported_source",i.nextAction="retry_or_check_connection",i;if("gmail"===n&&!stringValue(o.mailbox))return i.blockingReason="mailbox_required",i.nextAction="provide_mailbox",i;if("github"===n){const e=o.events,t=Array.isArray(e)&&e.some(e=>"string"==typeof e&&e.trim());if(!stringValue(o.repoOwner)||!stringValue(o.repoName)||!t)return i.blockingReason="github_webhook_input_required",i.nextAction="provide_repository_and_events",i}const u=await request(e,`${AUTOMATIONS_PATH}?limit=1`,{method:"GET"});if(!u.ok)return i.blockingReason="automation_gateway_unavailable",i.platform={reachable:!1,authorized:!1,status:u.status,error:u.error},i.nextAction="retry_or_check_connection",i;i.platform={reachable:!0,authorized:!0};const c={jsonrpc:"2.0",id:randomUUID(),method:"tools/call",params:{name:"list_connectors",arguments:{}}},l=await request(e,CONNECTOR_MCP_PATH,{...jsonRequestBody(c),headers:{"Content-Type":"application/json",Accept:"application/json"}});if(!l.ok)return i.blockingReason="mcp_unavailable",i.connector={id:n,installed:!1,credentialStatus:"unknown",error:l.error,status:l.status},i.nextAction="retry_or_check_connection",i;const d=unwrapConnectorMcp(l.data),p=d&&Array.isArray(d.connectors)?d.connectors:void 0;let m;if(Array.isArray(p))for(const e of p){if(!e||"object"!=typeof e||Array.isArray(e))continue;const t=e;if((stringValue(t.connectorId)??stringValue(t.connector_id)??"").toLowerCase()===n){m=t;break}}if(!m)return i.blockingReason="mcp_not_connected",i.nextAction="install_connector",i;const g=(stringValue(m.credentialStatus)??stringValue(m.credential_status)??"unknown").toLowerCase();return i.connector={id:n,installed:!0,credentialStatus:g},"active"!==g?(i.blockingReason="credential_missing",i.nextAction="authorize_connector",i):s?(i.ready=!0,i.nextAction="create_automation",i.sideEffects="gmail"===n?["create_rule","ensure_shared_gmail_watch"]:["create_rule","issue_hook","register_connector_webhook"],i):(i.blockingReason="target_not_resolved",i.nextAction="sync_openclaw_agent",i)}async function runCapabilities(e,t){const r=resolveTarget(e),o=resolveConversationId(e,t),n={platform:{reachable:!1,authorized:!1},capabilities:{createSchedule:Boolean(r),createWebhook:Boolean(r),run:!0},target:{resolved:Boolean(r)},session:{currentConversationAvailable:Boolean(o),canBindCurrentConversation:Boolean(o)}},a=await request(e,`${AUTOMATIONS_PATH}?limit=1`,{method:"GET"});return a.ok?n.platform={reachable:!0,authorized:!0}:n.platform={reachable:!1,authorized:!1,status:a.status,error:a.error},n}function requestErrorResult(e){return toolResult({error:e.error,status:e.status,details:e.details})}export function registerAutomationTools(e,t){e.registerTool({name:"beeos_automation_create",description:"Create a BeeOS platform Automation owned by the current user. The target is the current Agent and is resolved automatically. Prefer session.mode=bound for a task created in this chat.",parameters:{type:"object",properties:{name:{type:"string",description:"Human-readable automation name"},prompt:{type:"string",description:"Prompt sent to the target agent when the automation fires"},schedule:{type:"object",properties:{kind:{type:"string",enum:["at","every","cron"]},tz:{type:"string"},at:{type:"string"},intervalSeconds:{type:"integer",minimum:1},anchorAt:{type:"string"},expression:{type:"string"}}},session:{type:"object",description:"Always set session.mode. Prefer bound for tasks created in the current BeeOS chat so every run returns here. Use new if the user requests a separate conversation per run or the current chat cannot bind. The plugin fills the bound conversation ID; do not supply it.",properties:{mode:{type:"string",enum:["new","bound"]}}},validFrom:{type:"string"},validUntil:{type:"string"}},required:["name","prompt"]},async execute(e,r,o){const n=createInput(r,t,e);if("error"in n)return toolResult(n);try{const e=await request(t,AUTOMATIONS_PATH,{...jsonRequestBody(n),signal:o});return e.ok?toolResult(e.data):requestErrorResult(e)}catch(e){return t.logger?.warn?.(`[beeos_automation_create] ${e instanceof Error?e.message:String(e)}`),toolResult({error:e instanceof Error?e.message:String(e)})}}}),e.registerTool({name:"beeos_automation_create_webhook",description:"Create a BeeOS platform Webhook Automation. GitHub uses source/events/repoOwner/repoName; Gmail uses source/mailbox only (never hookId/hookUrl/repo*). Runs preflight first. Prefer session.mode=bound for a task created in this chat.",parameters:{type:"object",properties:{name:{type:"string"},prompt:{type:"string"},source:{type:"string",enum:["github","gmail"]},events:{type:"array",items:{type:"string"},minItems:1},repoOwner:{type:"string"},repoName:{type:"string"},eventFilter:{type:"string"},mailbox:{type:"string"},session:{type:"object",description:"Always set session.mode. Prefer bound for tasks created in the current BeeOS chat so every event returns here. Use new if the user requests a separate conversation per event or the current chat cannot bind. The plugin fills the bound conversation ID; do not supply it.",properties:{mode:{type:"string",enum:["new","bound"]}}},validFrom:{type:"string"},validUntil:{type:"string"}},required:["name","prompt","source"]},async execute(e,r,o){const n=createWebhookInput(r,t,e);if("error"in n)return toolResult(n);try{const a=await runPreflight(t,r,e);if(!a.ready)return toolResult({error:"automation_preflight_failed",preflight:a});const s=await request(t,WEBHOOK_AUTOMATIONS_PATH,{...jsonRequestBody(n),signal:o});return s.ok?toolResult(s.data):requestErrorResult(s)}catch(e){return t.logger?.warn?.(`[beeos_automation_create_webhook] ${e instanceof Error?e.message:String(e)}`),toolResult({error:e instanceof Error?e.message:String(e)})}}}),e.registerTool({name:"beeos_automation_capabilities",description:"Check whether the BeeOS Automation platform is reachable/authorized and whether the current chat can bind as an Automation session. Read-only.",parameters:{type:"object",properties:{}},async execute(e){try{return toolResult(await runCapabilities(t,e))}catch(e){return toolResult({error:e instanceof Error?e.message:String(e)})}}}),e.registerTool({name:"beeos_automation_preflight",description:"Read-only readiness check for creating a BeeOS Webhook Automation. Returns nextAction such as authorize_connector, install_connector, provide_mailbox, provide_repository_and_events, sync_openclaw_agent, retry_or_check_connection, or create_automation.",parameters:{type:"object",properties:{source:{type:"string",enum:["github","gmail"]},events:{type:"array",items:{type:"string"}},repoOwner:{type:"string"},repoName:{type:"string"},mailbox:{type:"string"},session:{type:"object",properties:{mode:{type:"string",enum:["new","bound"]}}}},required:["source"]},async execute(e,r){try{return toolResult(await runPreflight(t,r,e))}catch(e){return toolResult({error:e instanceof Error?e.message:String(e)})}}}),e.registerTool({name:"beeos_automation_list",description:"List BeeOS platform Automations owned by the current user.",parameters:{type:"object",properties:{status:{type:"string",enum:["enabled","paused","expired"]},limit:{type:"integer",minimum:1,maximum:100},cursor:{type:"string"}}},async execute(e,r,o){const n=asObject(r),a=new URLSearchParams;for(const e of["status","cursor"]){const t=stringValue(n[e]);t&&a.set(e,t)}"number"==typeof n.limit&&Number.isFinite(n.limit)&&a.set("limit",String(Math.trunc(n.limit)));try{const e=await request(t,`${AUTOMATIONS_PATH}?${a}`,{signal:o});return e.ok?toolResult(e.data):requestErrorResult(e)}catch(e){return toolResult({error:e instanceof Error?e.message:String(e)})}}}),e.registerTool({name:"beeos_automation_get",description:"Get one BeeOS platform Automation by its automation ID.",parameters:{type:"object",properties:{automationId:{type:"string"}},required:["automationId"]},async execute(e,r,o){const n=stringValue(asObject(r).automationId);if(!n)return toolResult({error:"automationId is required"});try{const e=await request(t,`${AUTOMATIONS_PATH}/${encodeURIComponent(n)}`,{signal:o});return e.ok?toolResult(e.data):requestErrorResult(e)}catch(e){return toolResult({error:e instanceof Error?e.message:String(e)})}}}),e.registerTool({name:"beeos_automation_run",description:"Trigger an existing BeeOS platform Automation immediately and return its Run record.",parameters:{type:"object",properties:{automationId:{type:"string"},requestKey:{type:"string",description:"Optional idempotency key; generated when omitted"},sourceRunId:{type:"string"},rerunSessionMode:{type:"string",enum:["original","new"]}},required:["automationId"]},async execute(e,r,o){const n=asObject(r),a=stringValue(n.automationId);if(!a)return toolResult({error:"automationId is required"});const s=stringValue(n.requestKey)??randomUUID(),i=stringValue(n.sourceRunId),u=stringValue(n.rerunSessionMode),c={};i&&(c.sourceRunId=i),u&&(c.rerunSessionMode=u);try{const e=await request(t,`${AUTOMATIONS_PATH}/${encodeURIComponent(a)}/runs`,{...jsonRequestBody(c),headers:{"Content-Type":"application/json","Idempotency-Key":s},signal:o});return e.ok?toolResult({requestKey:s,data:e.data}):toolResult({error:e.error,status:e.status,details:e.details,requestKey:s})}catch(e){return toolResult({error:e instanceof Error?e.message:String(e),requestKey:s})}}}),e.registerTool({name:"beeos_automation_runs",description:"List Runs for one BeeOS platform Automation.",parameters:{type:"object",properties:{automationId:{type:"string"},status:{type:"string",enum:["scheduled","dispatched","failed","skipped"]},limit:{type:"integer",minimum:1,maximum:100},cursor:{type:"string"}},required:["automationId"]},async execute(e,r,o){const n=asObject(r),a=stringValue(n.automationId);if(!a)return toolResult({error:"automationId is required"});const s=new URLSearchParams;for(const e of["status","cursor"]){const t=stringValue(n[e]);t&&s.set(e,t)}"number"==typeof n.limit&&Number.isFinite(n.limit)&&s.set("limit",String(Math.trunc(n.limit)));try{const e=await request(t,`${AUTOMATIONS_PATH}/${encodeURIComponent(a)}/runs?${s}`,{signal:o});return e.ok?toolResult(e.data):requestErrorResult(e)}catch(e){return toolResult({error:e instanceof Error?e.message:String(e)})}}}),e.registerTool({name:"beeos_automation_get_run",description:"Get one Run record for a BeeOS platform Automation.",parameters:{type:"object",properties:{automationId:{type:"string"},runId:{type:"string"}},required:["automationId","runId"]},async execute(e,r,o){const n=asObject(r),a=stringValue(n.automationId),s=stringValue(n.runId);if(!a||!s)return toolResult({error:"automationId and runId are required"});try{const e=await request(t,`${AUTOMATIONS_PATH}/${encodeURIComponent(a)}/runs/${encodeURIComponent(s)}`,{signal:o});return e.ok?toolResult(e.data):requestErrorResult(e)}catch(e){return toolResult({error:e instanceof Error?e.message:String(e)})}}})}
@@ -1 +0,0 @@
1
- import{AsyncLocalStorage}from"node:async_hooks";const forbidden=new Set(["beeos_automation_create","beeos_automation_create_webhook","beeos_automation_run"]);export const AUTOMATION_TOOL_BLOCK_REASON="Automation Wake cannot create or trigger another Automation.";export class AutomationTurnPolicy{ready=!1;turns=new Map;activeContext=new AsyncLocalStorage;enable(){this.ready=!0}disable(){this.ready=!1}isReady(){return this.ready}enter(e,t,o){if(!this.ready||!e||!t||this.turns.has(t))throw new Error("automation_turn_policy_unavailable");const n={sessionKey:e,context:o};return this.turns.set(t,n),{run:e=>this.activeContext.run(o,e),close:()=>{this.turns.get(t)===n&&this.turns.delete(t)}}}check(e,t){if(!forbidden.has(e))return;return this.activeContext.getStore()||t.runId&&this.turns.has(t.runId)||t.sessionKey&&[...this.turns.values()].some(e=>e.sessionKey===t.sessionKey)?{block:!0,blockReason:AUTOMATION_TOOL_BLOCK_REASON}:void 0}}
@@ -1 +0,0 @@
1
- const uuid=e=>"string"==typeof e&&/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/.test(e)&&"00000000-0000-0000-0000-000000000000"!==e,ownerID=e=>uuid(e)||"string"==typeof e&&/^[0-9a-f]{32}$/.test(e)&&"00000000000000000000000000000000"!==e;export function decodeWake(e){const o=e.content,n=o?.metadata;if(!n||!["beeos.kind","beeos.automation_id","beeos.run_id","beeos.session_mode"].some(e=>Object.hasOwn(n,e)))return{kind:"ordinary"};return"chat_message"===e.type&&"automation.run"===n["beeos.kind"]&&uuid(n["beeos.automation_id"])&&uuid(n["beeos.run_id"])&&e.id===n["beeos.run_id"]&&uuid(e.conversationId)&&!e.replyTo&&o?.channel_id===e.conversationId&&o.context_id===e.conversationId&&"string"==typeof o.message&&o.message.trim()&&e.body===o.message&&!Object.hasOwn(o,"session_key")&&("new"===n["beeos.session_mode"]||"bound"===n["beeos.session_mode"])&&uuid(n.target_agent_id)&&"string"==typeof n.delivery_principal&&n.delivery_principal&&"string"==typeof n.runtime_epoch&&/^[1-9][0-9]*$/.test(n.runtime_epoch)&&["beeos-conversation","openapi-conversation"].includes(String(n.protocol))&&ownerID(n.caller_owner_id)&&e.senderId===`user:${n.caller_owner_id}`?{kind:"wake",context:Object.freeze({automationId:n["beeos.automation_id"],runId:n["beeos.run_id"],sessionMode:n["beeos.session_mode"]})}:{kind:"invalid",reason:"invalid_automation_wake"}}