beeos-claw 0.6.66-cloud.1 → 0.6.66-cloud.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +13 -0
- package/dist/acp-gateway/bridge-core.js +1 -1
- package/dist/contracts/question/codec.js +1 -0
- package/dist/gateway-client.js +1 -1
- package/dist/im-inbound-handler.js +1 -1
- package/dist/im-runtime-dispatch.js +1 -1
- package/dist/index.js +1 -1
- package/dist/message-projection-inbound-consumer.js +1 -1
- package/dist/runtime/agent-projection-production.js +1 -1
- package/dist/runtime/core-execution-attempt-context.js +1 -1
- package/dist/runtime/im-reply-sink.js +1 -1
- package/dist/runtime/message-service-runtime-invocation-authority.js +1 -1
- package/dist/runtime/message-service-session-reasoning-http-authority.js +1 -0
- package/dist/runtime/ms-runtime-message-channel-adapter.js +1 -1
- package/dist/runtime/openclaw-native-sessions.js +1 -1
- package/dist/runtime/openclaw-question-adapter.js +1 -0
- package/dist/runtime/openclaw-runtime-capabilities.js +1 -1
- package/dist/runtime/question-wire.js +1 -0
- package/dist/runtime/runtime-command-dispatcher.js +1 -1
- package/dist/runtime/runtime-command-journal.js +1 -1
- package/dist/runtime/runtime-parity-manifest-digest.js +1 -1
- package/dist/runtime/runtime-parity-manifest.js +1 -1
- package/dist/runtime/runtime-production-authority.js +1 -1
- package/dist/runtime/runtime-reasoning.js +1 -0
- package/dist/runtime/runtime-service-contract-compatibility.js +1 -1
- package/dist/runtime/runtime-service-production-candidate.js +1 -1
- package/dist/runtime/runtime-session-control-service.js +1 -1
- package/dist/runtime/session-set-model-mapping.js +1 -1
- package/dist/terminal/runtime-terminal-outbound-registration.js +1 -1
- package/dist/terminal/runtime-terminal-service.js +1 -1
- package/dist/terminal/terminal-lease-bound-bridge-client.js +1 -1
- package/package.json +7 -6
- package/runtime-service-wire-identity.v1.json +12 -12
package/README.md
CHANGED
|
@@ -1,5 +1,18 @@
|
|
|
1
1
|
# beeos-claw
|
|
2
2
|
|
|
3
|
+
## Conversation thinking settings
|
|
4
|
+
|
|
5
|
+
The Service/ACP `session/set_reasoning` method accepts `modelId` and nullable
|
|
6
|
+
`reasoningOverrideId`. Only the native Gateway's advertised `thinkingLevels`
|
|
7
|
+
are accepted; null restores `thinkingDefault`. Model changes clear the
|
|
8
|
+
thinking override. Native session identity fencing is required, and Service
|
|
9
|
+
success waits for a durable Message Service metadata receipt.
|
|
10
|
+
|
|
11
|
+
The new Types/Message SDK prerelease pins are prepared locally, not published.
|
|
12
|
+
Release gates must still verify their registry bytes before publishing this
|
|
13
|
+
plugin. Existing evidence describes earlier candidates, not test results for
|
|
14
|
+
this change.
|
|
15
|
+
|
|
3
16
|
OpenClaw plugin that bridges the [BeeOS](https://beeos.ai) platform with the local OpenClaw Gateway. Provides ACP (Agent Communication Protocol) bridge, file transfer, terminal access, and observability.
|
|
4
17
|
|
|
5
18
|
## Install
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{randomUUID,createHash}from"node:crypto";import{mkdir,readFile,rm,stat,readdir,rename,cp}from"node:fs/promises";import path from"node:path";import{join,resolve,relative,sep}from"node:path";import{homedir}from"node:os";import AdmZip from"adm-zip";import{classifyCatalogSessionKey,prepareOpenClawToolCatalog}from"../runtime/prepare-openclaw-tool-catalog.js";import{sanitizePromptPayload}from"../message-sanitization/prompt-payload-sanitizer.js";import{previewSanitizedText,stripTransportMetadata}from"../message-sanitization/transport-metadata-sanitizer.js";import{isHeartbeatPromptText,isCronPromptText,extractCronJobId,classifyHeartbeatFilter}from"../message-sanitization/message-classifier.js";import{createCronIdentityStore,createInMemoryCronIdentityStore}from"../cron-identities.js";import{CANVAS_UPDATE_HINT,buildActiveSurfacesContext}from"../canvas/tools.js";import{parseMentionsParam,buildMentionInstruction}from"../delegation/mention-resolver.js";import{EXTERNAL_PROTOCOL_TAGS}from"./external-protocol-tags.js";import{extractPromptBlocks,toGatewayPrompt,resolveSlashCommandName as resolveSlashCmd}from"./prompt-converter.js";import{extractBeeosUserTextFromHistoryContent}from"./beeos-user-envelope.js";import{loadHistory,loadHistoryPage,groupIntoTurns,splitUserReplayBlocks}from"./history-replay.js";import{classifyHistoricalAssistantTurnCompletion}from"./history-turn-completion-policy.js";import{LocalSessionHistory}from"./local-session-history.js";import{reconcileMissingArtifacts}from"./missing-artifacts-reconciler.js";import{AcpRunOutputChannel}from"./acp-run-output-channel.js";import{buildResolutionPlan,resolveResources,applyResolutions}from"./prompt-resource-resolver.js";import{readEnrichmentCache,readUploadToolResult}from"../upload/result-cache.js";import{buildToolResultSessionContentBlocks}from"./tool-result-blocks.js";import{prefersChatFileArtifacts}from"./file-artifact-presenter.js";import{buildToolCallPayload,buildToolCallUpdatePayload}from"./tool-call-payload-builder.js";import{Errors}from"../errors/index.js";import{classifyAgentRuntimeFailure,INTERRUPTED_PUBLIC_MESSAGE}from"../runtime/agent-runtime-failure-classifier.js";import{generateTitleFromQA,isPlaceholderTitle}from"../runtime/conversation-title.js";import{SESSION_RECOVERY_REQUIRED_CODE,SESSION_RECOVERY_REQUIRED_MESSAGE}from"../runtime/session-mutation-coordinator.js";import{observeLifecyclePromise}from"../runtime/lifecycle-promise-observer.js";import{RunOutputRouter,errorRunOutput}from"../runtime/run-output-router.js";import{AgentMappingRegistry}from"../runtime/agent-mapping-registry.js";import{RuntimeSessionNewRejectedError}from"../runtime/runtime-rpc-consumer.js";import{normalizeRuntimeAgentLocalKey,OpenClawRuntimeCommandDispatcher,RuntimeCommandError}from"../runtime/runtime-command-dispatcher.js";import{FileRuntimeAgentIdentityRegistry}from"../runtime/runtime-agent-identity-registry.js";import{FileRuntimeLeasePort}from"../runtime/runtime-operation-lease.js";import{FileRuntimeCommandJournal,InMemoryRuntimeCommandJournal}from"../runtime/runtime-command-journal.js";import{OpenClawRuntimeCapabilities}from"../runtime/openclaw-runtime-capabilities.js";import{RuntimeCanvasService}from"../canvas/runtime-canvas-service.js";import{RuntimeSkillsService}from"../runtime/runtime-skills-service.js";import{RuntimeCronService,encodeBeeosChannelTag}from"../runtime/runtime-cron-service.js";export{BEEOS_CHANNEL_TAG_RE,decodeBeeosChannelId,encodeBeeosChannelTag,stripBeeosChannelTag}from"../runtime/runtime-cron-service.js";import{extractBeeosConversationIdFromSessionKey,openClawKeyMatchesConversation,openClawSessionKeysFromList,resolveLiveOpenClawSessionKey}from"../runtime/openclaw-session-key-resolution.js";import{LocalConversationHistoryBoundary,OpenClawGatewaySessionMetadataPort,OpenClawSessionMetadataService,RuntimeSessionControlService}from"../runtime/runtime-session-control-service.js";import{pageRuntimeSessions,parseRuntimeSessionListPage}from"../runtime/runtime-session-list-policy.js";import{assertTrustedCoreExecutionAttempt,createAcpCoreExecutionAttempt}from"../runtime/core-execution-attempt-context.js";import{CompositeActiveRunPort}from"../runtime/composite-active-run-port.js";import{resolveChannelFromSessionKey}from"../channel-detection.js";import{resolveTurnId,recordTurnIdForRole,recordToolCallTurnForUpdate}from"./gateway-events.js";import{emitToolResultProjectionForTurn}from"./tool-result-emitter.js";import{extractPersistedToolResultEnvelope,findPersistedToolResultEnvelopeByMessageId}from"./tool-result-transcript.js";import{projectPersistedToolResultForReplay}from"./tool-result-projection.js";import{openRunOutputRoute}from"./run-output-route.js";import{McpService}from"./mcp-service.js";import{McpDesiredStateStore,normalizeMcpPublicServerConfig}from"./mcp-desired-state-store.js";import{requireSecretFreeMcpServerName}from"./mcp-public-config-security.js";import{McpDesiredStateController}from"./mcp-desired-state-controller.js";import{CredentialReferenceRuntimeResolver}from"./mcp-credential-reference-port.js";import{ACP_PROTOCOL_VERSION}from"@beeos-ai/beeos-types/acp";import{canonicalizeJcs}from"@beeos-ai/beeos-types/runtime";import{MAX_ZIP_BYTES,parseAndValidateTemplatePackage,TemplatePackageError}from"./agent-template-package.js";const DEFAULT_AGENT_ID="main";export function normalizeAgentId(e){return normalizeRuntimeAgentLocalKey(e)}function narrowContentBlock(e){switch(e.type){case"text":return"string"==typeof e.text?{type:"text",text:e.text}:void 0;case"resource_link":return"string"==typeof e.uri?{type:"resource_link",uri:e.uri,...void 0!==e.name?{name:e.name}:{},...void 0!==e.mimeType?{mimeType:e.mimeType}:{}}:void 0;case"file":return{type:"file",...void 0!==e.text?{text:e.text}:{},...void 0!==e.uri?{uri:e.uri}:{},...void 0!==e.mimeType?{mimeType:e.mimeType}:{}};case"image":return{type:"image",...void 0!==e.uri?{uri:e.uri}:{},...void 0!==e.mimeType?{mimeType:e.mimeType}:{}};default:return}}const DEFAULT_MODE={id:"default",name:"Default",description:"Default agent mode"},USER_MESSAGE_PREFIX="User Message From BeeOS:",USER_MESSAGE_PREFIX_WITH_NEWLINE=`${USER_MESSAGE_PREFIX}\n`,SLASH_COMMAND_NAME_RE=/^[a-z0-9_-]+$/i,HISTORY_PAGE_DEFAULT_LIMIT=100,HISTORY_PAGE_MAX_LIMIT=1e3;function withUserMessagePrefix(e){return e.startsWith(USER_MESSAGE_PREFIX)?e:`${USER_MESSAGE_PREFIX_WITH_NEWLINE}${e}`}const BACKGROUND_SESSION_PATTERNS=[":cron:",":hook:","node-",...EXTERNAL_PROTOCOL_TAGS];function isBackgroundSessionKey(e){return BACKGROUND_SESSION_PATTERNS.some(t=>e.includes(t))}function isSystemDefaultSessionKey(e){const t=e.split(":");if(t.length<3||"agent"!==t[0])return!1;const s=t.slice(2).join(":");return"main"===s||s===t[1]}function cleanGatewayDerivedTitle(e){if(!e)return;let t=extractBeeosUserTextFromHistoryContent(e).text;return/^\[System\b/.test(t)?void 0:(t=t.trim(),t||void 0)}function cleanHistoryUserContent(e){return extractBeeosUserTextFromHistoryContent(e).text.trim()}function mergePersistedToolResultEnvelopes(e,t){if(e||t)return{toolName:e?.toolName||t?.toolName||"tool",...e?.toolCallId||t?.toolCallId?{toolCallId:e?.toolCallId??t?.toolCallId}:{},...e?.rawText??t?.rawText?{rawText:e?.rawText??t?.rawText}:{},...void 0!==e?.rawResult||void 0!==t?.rawResult?{rawResult:e?.rawResult??t?.rawResult}:{},...e?.timestamp||t?.timestamp?{timestamp:e?.timestamp??t?.timestamp}:{}}}function resolveStandaloneSlashCommandName(e,t){if(0===e.length)return;if(e.some(e=>"text"!==e.type))return;const s=t.trim();if(!s||s.includes("\n")||!s.startsWith("/"))return;const i=s.match(/^\/([^\s:]+)(?::|\s|$)/)?.[1]?.trim();return i&&!i.includes("/")&&SLASH_COMMAND_NAME_RE.test(i)?i.toLowerCase():void 0}class AcpRuntimeCommandResourceStore{fetchPackage;parsePackage;isValidationError;content=new Map;artifacts=new Map;mcpDefinitions=new Map;constructor(e,t,s){this.fetchPackage=e,this.parsePackage=t,this.isValidationError=s}registerContent(e){const t=`acp-content:sha256:${createHash("sha256").update(e).digest("hex")}`;return this.content.set(t,e),t}registerMcpDefinition(e,t){this.mcpDefinitions.set(JSON.stringify(e),structuredClone(t))}async resolveMcpDefinition(e){const t=this.mcpDefinitions.get(JSON.stringify(e));if(!t)throw new Error("ACP MCP definition was not verified for this request");return structuredClone(t)}async registerArtifact(e,t){try{const s=await this.fetchPackage(e);if(!s.ok)throw new TemplatePackageError(`failed to download package: HTTP ${s.status}`);const i=Buffer.from(await s.arrayBuffer());if(i.length>MAX_ZIP_BYTES)throw new TemplatePackageError(`package too large: ${i.length} bytes (max ${MAX_ZIP_BYTES})`);const n=createHash("sha256").update(i).digest("hex");if(t?.trim()&&t.trim().toLowerCase()!==n)throw new TemplatePackageError("package sha256 mismatch");const r=this.parsePackage(i),o=`acp-artifact:sha256:${n}`;return this.artifacts.set(o,r),{artifactRef:o,version:r.manifest?.version??"0.0.0",sha256:n,sizeBytes:String(i.length),source:"acp-verified"}}catch(e){if(e instanceof TemplatePackageError||this.isValidationError(e))throw new RuntimeCommandError("INVALID_PARAMS",`Invalid template package: ${e instanceof Error?e.message:String(e)}`);throw new RuntimeCommandError("EXECUTION_FAILED",`Apply template failed: ${e instanceof Error?e.message:String(e)}`)}}registerParsedArtifact(e){const t=createHash("sha256");for(const[s,i]of[...e.workspaceFiles].sort(([e],[t])=>e.localeCompare(t)))t.update(s).update("\0").update(i).update("\0");const s=t.digest("hex"),i=`acp-artifact:sha256:${s}`;this.artifacts.set(i,e);const n=[...e.workspaceFiles.values()].reduce((e,t)=>e+t.byteLength,0);return{artifactRef:i,version:e.manifest?.version??"0.0.0",sha256:s,sizeBytes:String(n),source:"acp-verified"}}async readContent(e){const t=this.content.get(e);if(void 0===t)throw new Error(`unknown contentRef: ${e}`);return t}async readVerifiedArtifact(e){const t=this.artifacts.get(e.artifactRef);if(!t)throw new Error(`unknown verified artifact: ${e.artifactRef}`);return t}}export const lateBoundRuntimeAgentProjectionPort=e=>({prepareCommittedMutation:async t=>{const s=e();if(!s)throw new Error("agent projection production is unavailable");return await s.prepareCommittedMutation(t)},applyCommittedMutation:async t=>{const s=e();if(!s)throw new Error("agent projection production is unavailable");return await s.applyCommittedMutation(t)},compactCommittedMutation:async t=>{const s=e();if(!s)throw new Error("agent projection production is unavailable");await s.compactCommittedMutation(t)}});function deriveSessionId(e,t){const s=createHash("sha256").update(`${t}:${e}`).digest("hex").substring(0,32);return`session:${s.slice(0,8)}-${s.slice(8,12)}-${s.slice(12,16)}-${s.slice(16,20)}-${s.slice(20,32)}`}function parseAgentIdFromKey(e){const t=e.split(":");return(t.length>=3&&"agent"===t[0]?t[1]:"")||"main"}function createSessionObject(e){const t=parseAgentIdFromKey(e.gatewayKey);return{sessionId:deriveSessionId(e.gatewayKey,e.instanceId),gatewaySessionKey:e.gatewayKey,agentId:t,platformAgentId:e.resolvePlatformAgentId?.(t),createdAt:Date.now(),chatReplayDedupKeys:new Set,cwd:e.cwd,title:e.title,channelType:resolveChannelFromSessionKey(e.gatewayKey,e.thirdPartyChannels)}}class HistoryUpdateRecorder{transport;updates=[];constructor(e){this.transport=e}sendSessionUpdate(e,t,s){const i=this.transport.materializeSessionUpdate(t,s);return null!==i&&this.updates.push(i),!0}}export class AcpGatewayBridgeCore{state;transport;config;logger;writeObs;getGateway;titles;thirdPartyChannels;beeosFileResolver;agentFetch;agentGatewayUrl;triggerAgentSync;sessionMutationCoordinator;runtimeCommands;runtimeAgentIdentities;runtimeLeases;runtimeCommandResources;localHistoryBoundary;activeRuns;markAgentProjectionDirty;projectionObservationTimeoutMs;cronChannelCache=new Map;static CRON_CHANNEL_CACHE_MAX=256;localSessionHistory;mcp;catalogPrepareInFlight=new Set;runtimeTerminalService;runOutputRouter;agentMappings;initialized=!1;get agentMapping(){return this.agentMappings.snapshot()}cronIsolatedSessionKeys=new Set;cronIdentities;cronReady;sessionPreloadInFlight=null;sessionPreloadInFlightGeneration=null;sessionPreloadReady=Promise.resolve();resolveSessionPreloadReady=null;rejectSessionPreloadReady=null;sessionPreloadGeneration=0;sessionPreloadBarrierActive=!1;sessionPreloadFailed=!1;async setAgentMapping(e){await this.agentMappings.replace(e)}async applyAgentMapping(e){for(const t of this.state.sessions.values())!t.platformAgentId&&t.agentId&&(t.platformAgentId=e.get(t.agentId)?.id);this.sessionPreloadFailed&&this.beginGatewaySessionPreload(),this.sessionPreloadBarrierActive&&await this.preloadSessionsFromGateway()}beginGatewaySessionPreload(){this.sessionPreloadBarrierActive&&this.sessionPreloadReady.catch(()=>{}),this.sessionPreloadGeneration+=1,this.sessionPreloadBarrierActive=!0,this.sessionPreloadFailed=!1,this.sessionPreloadReady=new Promise((e,t)=>{this.resolveSessionPreloadReady=e,this.rejectSessionPreloadReady=t}),this.sessionPreloadReady.catch(()=>{})}failGatewaySessionPreload(){this.sessionPreloadBarrierActive&&(this.sessionPreloadBarrierActive=!1,this.sessionPreloadFailed=!0,this.rejectSessionPreloadReady?.(new RuntimeCommandError("GATEWAY_NOT_CONNECTED","Gateway disconnected before session hydration completed")),this.resolveSessionPreloadReady=null,this.rejectSessionPreloadReady=null)}async waitForGatewaySessionPreload(){await this.sessionPreloadReady}onceAgentMappingReady(e){this.agentMappings.onceReady(e)}onAgentMappingUpdated(e){const t=this.agentMappings.snapshot();t&&t.size>0&&e(new Map(t)),this.agentMappings.subscribe(t=>e(new Map(t)))}markAgentSyncDone(){this.agentMappings.markSynchronized()}waitForAgentMapping(e=15e3){return this.agentMappings.waitForSynchronization(e)}handleTerminalRequest=null;handleTerminalNotification=null;onRunComplete=null;onRunFailed=null;onRunEnded=null;handleSlashCommand=null;onPromptStart=null;instanceId;getAuthoritativeInstanceId;mcpCredentialReferences;constructor(e){this.state=e.state,this.transport=e.transport,this.config=e.config,this.logger=e.logger,this.writeObs=e.writeObs,this.getGateway=e.getGateway,this.titles=e.titles,this.instanceId=e.instanceId||"unknown",this.getAuthoritativeInstanceId=e.getAuthoritativeInstanceId,this.mcpCredentialReferences=e.mcpCredentialReferences,this.thirdPartyChannels=e.thirdPartyChannels??new Set,this.beeosFileResolver=e.beeosFileResolver,this.agentFetch=e.agentFetch,this.agentGatewayUrl=e.agentGatewayUrl,this.triggerAgentSync=e.triggerAgentSync,this.markAgentProjectionDirty=e.markAgentProjectionDirty,this.projectionObservationTimeoutMs=e.projectionObservationTimeoutMs??5e3,this.sessionMutationCoordinator=e.sessionMutationCoordinator,this.agentMappings=e.agentMappings??new AgentMappingRegistry,this.agentMappings.subscribe(e=>this.applyAgentMapping(e)),this.runOutputRouter=e.runOutputRouter??new RunOutputRouter([new AcpRunOutputChannel({state:this.state,transport:this.transport})],{lifecycle:{terminal:(e,t)=>this.commitRunOutputTerminal(e,t)}}),this.runtimeAgentIdentities=e.runtimeAgentIdentities??new FileRuntimeAgentIdentityRegistry(join(process.env.OPENCLAW_HOME||join(homedir(),".openclaw"),"beeos","runtime-agent-identities.json")),this.runtimeLeases=e.runtimeLeases??new FileRuntimeLeasePort(join(process.env.OPENCLAW_HOME||join(homedir(),".openclaw"),"beeos","runtime-operation-leases.json")),this.runtimeCommandResources=e.runtimeCommandResources??new AcpRuntimeCommandResourceStore(e.agentFetch??((e,t)=>fetch(e,t)),e.runtimeTemplatePackageParser?.parse??parseAndValidateTemplatePackage,e.runtimeTemplatePackageParser?.isValidationError??(e=>e instanceof TemplatePackageError));const t=e.runtimeCommandJournal??(e.runtimeLeases||process.env.VITEST?new InMemoryRuntimeCommandJournal:new FileRuntimeCommandJournal(join(process.env.OPENCLAW_HOME||join(homedir(),".openclaw"),"beeos","runtime-command-journal.json"),void 0,void 0,{onMaintenanceError:e=>this.logger.error?.(`[runtime-journal] event=checkpoint_failed trigger=threshold error=${e instanceof Error?e.message:String(e)}`)}));this.localSessionHistory=new LocalSessionHistory({logger:this.logger}),this.cronIdentities=e.cronIdentities??(process.env.VITEST?createInMemoryCronIdentityStore():createCronIdentityStore(this.logger)),this.cronReady=this.cronIdentities.initialize(),this.cronReady.catch(()=>{});const s=e.mcpCredentialReferences?new CredentialReferenceRuntimeResolver(e.mcpCredentialReferences):void 0,i=e.mcpProjection?new McpDesiredStateController(new McpDesiredStateStore(e.mcpDesiredStatePath??join(process.env.OPENCLAW_HOME||join(homedir(),".openclaw"),"beeos","mcp-desired-state.json")),e.mcpProjection,s,e.mcpCredentialReferences,t,e.runtimeReceiptHistory,this.logger):void 0;this.mcp=new McpService({transport:this.transport,logger:this.logger,state:this.state,...i?{desiredState:i}:{}}),this.localHistoryBoundary=new LocalConversationHistoryBoundary(e=>{const t=this.state.sessions.get(e);t&&(t.chatReplayDedupKeys.clear(),this.onSessionCleared?.(t.sessionId,t.gatewaySessionKey))}),this.activeRuns=e.activeRuns??new CompositeActiveRunPort;const n=new RuntimeCronService({getGateway:e.getGateway,identities:this.cronIdentities,channels:{allocate:async(e,t)=>await this.allocateCronChannel(e,t),lookup:e=>this.cronChannelCache.get(e),remember:(e,t)=>this.setCronChannelCache(e,t),forget:e=>{this.cronChannelCache.delete(e)}}}),r=new RuntimeSessionControlService({convergeRun:async({runtimeSessionKey:e})=>{const t=this.state.sessionsByGatewayKey.get(e),s=(new Date).toISOString();if(!t||t.promptDone&&void 0===t.pendingPromptId&&void 0===t.currentRunId)return{state:"no_active_run",quiescence:{state:"verified",observedAt:s}};const i=this.getGateway();if(!i?.isConnected)return{state:"recovery_required",reason:"gateway_disconnected"};if(!t.writeGateToken||!t.currentRunId)return{state:"recovery_required",reason:"run_conflict",runtimeRunId:t.currentRunId??"unknown"};const n=t.currentRunId,r=await this.sessionMutationCoordinator.requestCancel(t.writeGateToken,i);if(!r.ok)return{state:"recovery_required",reason:"terminal_not_observed",runtimeRunId:n};const o=await r.terminalObservation;return o.ok?{state:"terminal_observed",runtimeRunId:n,abortObservation:r.acknowledged?{state:"target_acknowledged",aborted:!0,runIds:[n],targetRunAcknowledged:!0}:{state:"already_stopped",aborted:!1,runIds:[]},terminalState:"cancelled",quiescence:{state:"verified",observedAt:(new Date).toISOString()}}:{state:"recovery_required",reason:"quiescence_failed"===o.recovery.reason?"quiescence_failed":"quiescence_timeout"===o.recovery.reason?"quiescence_timeout":"terminal_not_observed",runtimeRunId:n}},reset:async e=>{const t=this.getGateway();if(!t?.isConnected)throw new RuntimeCommandError("GATEWAY_NOT_CONNECTED","Gateway not connected");await t.sessionsReset(e)}},new OpenClawSessionMetadataService(new OpenClawGatewaySessionMetadataPort(e.getGateway,(e,t)=>{const s=this.state.sessionsByGatewayKey.get(e);if(!s)throw new RuntimeCommandError("INVALID_PARAMS",`setModel projection requires a live local session for key=${e}`);void 0===t.modelOverrideId?delete s.modelOverrideId:s.modelOverrideId=t.modelOverrideId,s.effectiveModelId=t.effectiveModelId,void 0===t.modelProvider?delete s.modelProvider:s.modelProvider=t.modelProvider},{resolveLiveKey:e=>resolveLiveOpenClawSessionKey(e,this.state.sessions.values()),waitForHydration:async()=>await this.waitForGatewaySessionPreload(),reconcileMissingLiveKey:async(e,t)=>await this.reconcileMissingGatewaySession(e,t),materializeMissingModelKey:async(e,t)=>await this.materializeMissingModelSessionKey(e,t),registerMaterializedModelKey:e=>this.registerMaterializedModelSession(e)},async t=>{if(!e.declareModel)throw new Error("OpenClaw model configuration runtime is unavailable");return await e.declareModel(t)})),this.activeRuns),o=e.getAgentProjection?lateBoundRuntimeAgentProjectionPort(e.getAgentProjection):void 0,a=e.runtimeCommandDispatcher??new OpenClawRuntimeCommandDispatcher({declareModel:e.declareModel,getGateway:e.getGateway,resources:this.runtimeCommandResources,identities:this.runtimeAgentIdentities,leases:this.runtimeLeases,journal:t,logger:e.logger,...o?{projection:o}:{},...e.templateOriginReporter?{templateOriginReporter:e.templateOriginReporter}:{},...e.runExclusive?{runExclusive:e.runExclusive}:{}});this.runtimeCommands=a,a instanceof OpenClawRuntimeCommandDispatcher&&a.setCapabilities(new OpenClawRuntimeCapabilities({getGateway:e.getGateway,identities:this.runtimeAgentIdentities,sessions:r,skills:new RuntimeSkillsService({getGateway:e.getGateway,resources:this.runtimeCommandResources,getOpenclawHome:()=>this.getOpenclawHome()}),mcp:this.mcp,terminal:{execute:async(e,t)=>{if(!this.runtimeTerminalService)throw new RuntimeCommandError("INVALID_PARAMS","terminal canonical delegate is not configured");return await this.runtimeTerminalService.execute(e,t)}},canvas:new RuntimeCanvasService({apply:async(e,t)=>{await this.applyCanonicalCanvasControl(e,t)}}),cron:n})),this.transport.setSessionUpdateObserver((e,t,s)=>{const i=s?.turnId;if(!i)return;const n=this.state.sessions.get(e);n&&(recordTurnIdForRole(n,t,i),recordToolCallTurnForUpdate(n,t,i))})}setRuntimeTerminalService(e){this.runtimeTerminalService=e}setRuntimeReceiptHistoryPort(e){this.mcp.setRuntimeReceiptHistoryPort(e)}getRuntimeCommandDispatcher(){return this.runtimeCommands}async startMcp(){await this.mcp.start()}async stopMcp(){await this.mcp.stop()}nudgeMcpNativeProjection(){this.mcp.nudgeNativeProjection()}observeActiveRun(e){const t=e.currentRunId,s=e.writeGateToken,i=this.sessionMutationCoordinator.snapshot(e.gatewaySessionKey),n=void 0===e.pendingPromptId?e.gatewayRequestId??t:String(e.pendingPromptId);if(!t||!i||!n)return;const r={runtimeConversationUid:e.sessionId,runtimeSessionKey:e.gatewaySessionKey,historyGeneration:this.localHistoryBoundary.currentGeneration(e.sessionId),targetMessageId:n,runtimeRunId:t,activeRunRegistrationId:`acp:${i.operationId}`};return this.activeRuns.register(r,async()=>{const n=this.getGateway();if(!n?.isConnected||e.currentRunId!==t)return"recovery_required";const r=s?.operationId===i.operationId?await this.sessionMutationCoordinator.requestCancel(s,n):await this.sessionMutationCoordinator.requestCancelForSession(e.gatewaySessionKey,n);return r.ok?"accepted":!("reason"in r)||"terminal_already_observed"!==r.reason&&"no_active_mutation"!==r.reason?"recovery_required":"already_terminal"}),e.activeRunBinding=r,r}getActiveRunAuthority(){return{activeRuns:this.activeRuns,historyGeneration:e=>this.localHistoryBoundary.currentGeneration(e)}}resolvePlatformAgentId(e){return this.agentMapping?.get(e)?.id}resolveLocalAgentId(e){if(this.agentMapping&&e)for(const[t,s]of this.agentMapping)if(s.id===e)return t}getDefaultAgentId(){return this.agentMapping&&this.agentMapping.size>0?this.agentMapping.keys().next().value??"main":"main"}registerSession(e,t={}){const{ensureVerbose:s=!0}=t,i=(this.state.sessions.has(e.sessionId),this.state.sessionsByGatewayKey.get(e.gatewaySessionKey));!0===i?.canvasActive&&(e.canvasActive=!0),this.state.sessions.set(e.sessionId,e),this.state.sessionsByGatewayKey.set(e.gatewaySessionKey,e),s&&this.ensureVerboseFull(e),this.subscribeSessionMessages(e)}async ensureVerboseFull(e){if(e.verbosePatched)return;const t=this.getGateway();if(t?.isConnected){e.verbosePatched=!0;try{await t.sessionsPatch(e.gatewaySessionKey,{verboseLevel:"full"}),this.scheduleSessionToolCatalog(e.agentId,e.gatewaySessionKey)}catch(t){e.verbosePatched=!1,this.logger.debug?.(`[bridge-core] ensureVerboseFull failed: ${t instanceof Error?t.message:t}`)}}}ensureSessionForKey(e){const t=this.state.findSessionByKey(e);if(t)return t;const s=createSessionObject({gatewayKey:e,instanceId:this.instanceId,resolvePlatformAgentId:e=>this.resolvePlatformAgentId(e),thirdPartyChannels:this.thirdPartyChannels});return this.registerSession(s),this.logger.debug?.(`[bridge-core] ensureSessionForKey: lazily created session sid=${s.sessionId} key=${e}`),this.onSessionLoaded?.(s.sessionId,s.gatewaySessionKey,s.platformAgentId),s}async preloadSessionsFromGateway(){const e=this.sessionPreloadGeneration;if(this.sessionPreloadInFlight&&this.sessionPreloadInFlightGeneration===e)return await this.sessionPreloadInFlight;const t=this.preloadGatewaySessionIndex(e);this.sessionPreloadInFlight=t,this.sessionPreloadInFlightGeneration=e;try{await t,e===this.sessionPreloadGeneration&&this.sessionPreloadBarrierActive&&(this.sessionPreloadBarrierActive=!1,this.resolveSessionPreloadReady?.(),this.resolveSessionPreloadReady=null,this.rejectSessionPreloadReady=null)}catch(t){throw e===this.sessionPreloadGeneration&&this.sessionPreloadBarrierActive&&(this.sessionPreloadBarrierActive=!1,this.sessionPreloadFailed=!0,this.rejectSessionPreloadReady?.(t instanceof Error?t:new Error("session preload failed")),this.resolveSessionPreloadReady=null,this.rejectSessionPreloadReady=null),t}finally{this.sessionPreloadInFlight===t&&(this.sessionPreloadInFlight=null,this.sessionPreloadInFlightGeneration=null)}}async preloadGatewaySessionIndex(e){const t=this.getGateway();if(this.logSessionAuthority("session_preload_start",{generation:e,reason:t?.isConnected?"gateway_connected":"gateway_not_connected"}),!t?.isConnected)throw this.logSessionAuthority("session_preload_failure",{generation:e,reason:"gateway_not_connected"}),new RuntimeCommandError("GATEWAY_NOT_CONNECTED","Gateway not connected for session hydration");try{const s=await t.sessionsList(),i=this.mergeGatewaySessions(s);this.logSessionAuthority("session_preload_success",{generation:e,loaded:i,reason:"authoritative_sessions_list"})}catch(t){throw this.logSessionAuthority("session_preload_failure",{generation:e,reason:t instanceof RuntimeCommandError?t.code:"sessions_list_failed"}),t}}mergeGatewaySessions(e,t){if(!e||"object"!=typeof e)return 0;const s=e.sessions;if(!Array.isArray(s))return 0;let i=0;for(const e of s){if(!e||"object"!=typeof e||Array.isArray(e))continue;const s=e,n=s.sessionKey??s.key??s.id??s.name;if("string"!=typeof n||!n.trim()||n.trim()!==n||t&&!t(n)||this.state.findSessionByKey(n))continue;const r=s.label||s.derivedTitle||s.title||void 0,o=r&&!isPlaceholderTitle(r)?r:void 0,a=createSessionObject({gatewayKey:n,instanceId:this.instanceId,resolvePlatformAgentId:e=>this.resolvePlatformAgentId(e),thirdPartyChannels:this.thirdPartyChannels,title:o});this.registerSession(a,{ensureVerbose:!1}),i+=1}return i}async reconcileMissingGatewaySession(e,t){await this.assertSessionReconcileFence(t);const s=this.getGateway();if(!s?.isConnected)throw this.logSessionAuthority("session_reconcile_decision",{reason:"gateway_not_connected",runtimeSessionKeyHash:this.sessionKeyHash(e)}),new RuntimeCommandError("GATEWAY_NOT_CONNECTED","Gateway not connected for session reconciliation");const i=await s.sessionsList();await this.assertSessionReconcileFence(t);const n=!e.startsWith("agent:"),r=[...openClawSessionKeysFromList(i)].filter(t=>t===e?!n:openClawKeyMatchesConversation(t,e));if(this.logSessionAuthority("session_reconcile_decision",{reason:r.length?"exact_candidates":"no_exact_candidate",runtimeSessionKeyHash:this.sessionKeyHash(e),candidateHashes:r.map(e=>this.sessionKeyHash(e))}),!r.length)return;const o=new Set(r),a=this.mergeGatewaySessions(i,e=>o.has(e));this.logSessionAuthority("session_reconcile_decision",{reason:"reconciled_exact_candidates",runtimeSessionKeyHash:this.sessionKeyHash(e),loaded:a,candidateHashes:r.map(e=>this.sessionKeyHash(e))})}async materializeMissingModelSessionKey(e,t){await this.assertSessionReconcileFence(t);const s=t.attempt,i=s.verifiedMessageGrant?.claims,n=i?.command;if("service"!==s.origin||!i||"conversation"!==i.scope||"session/set_model"!==i.method||"session.setModel"!==n?.name||"conversation"!==n.context.scope||i.runtimeSessionKey!==e||i.runtimeConversationUid!==e||n.context.conversation.runtimeSessionKey!==e||n.context.conversation.runtimeConversationUid!==i.runtimeConversationUid)throw this.logSessionAuthority("model_intent_materialization_rejected",{reason:"verified_service_target_required",runtimeSessionKeyHash:this.sessionKeyHash(e)}),new RuntimeCommandError("INVALID_PARAMS","model-intent session materialization requires an exact verified Service target");if(!/^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(e))throw new RuntimeCommandError("INVALID_PARAMS","model-intent session materialization requires a canonical conversation UUID");let r;try{if(!this.runtimeAgentIdentities.resolveActiveLocalKey)throw new Error("identity resolver unavailable");r=await this.runtimeAgentIdentities.resolveActiveLocalKey(i.runtimeAgentUid)}catch{throw new RuntimeCommandError("INVALID_PARAMS","verified model-intent runtime agent is not active")}if(this.resolveLocalAgentId(i.platformAgentId)!==r)throw new RuntimeCommandError("INVALID_PARAMS","verified model-intent agent does not match the active platform projection");const o=`agent:${r}:beeos-conversation:ctx-${e}`,a=this.getGateway();if(!a?.isConnected)throw new RuntimeCommandError("GATEWAY_NOT_CONNECTED","Gateway not connected for model-intent session materialization");const l=await a.sessionsList();await this.assertSessionReconcileFence(t);const d=[...openClawSessionKeysFromList(l)].filter(t=>openClawKeyMatchesConversation(t,e));if(d.length){if(1===d.length&&d[0]===o)return this.mergeGatewaySessions(l,e=>e===o),o;throw new RuntimeCommandError("INVALID_PARAMS","verified model-intent target conflicts with an existing OpenClaw session")}return this.logSessionAuthority("model_intent_materialization_allowed",{runtimeSessionKeyHash:this.sessionKeyHash(e),openClawSessionKeyHash:this.sessionKeyHash(o)}),o}registerMaterializedModelSession(e){this.state.findSessionByKey(e)||(this.registerSession(createSessionObject({gatewayKey:e,instanceId:this.instanceId,resolvePlatformAgentId:e=>this.resolvePlatformAgentId(e),thirdPartyChannels:this.thirdPartyChannels}),{ensureVerbose:!1}),this.logSessionAuthority("model_intent_materialization_committed",{openClawSessionKeyHash:this.sessionKeyHash(e)}))}async assertSessionReconcileFence(e){if(e.lease.operationId!==e.attempt.operationId||e.lease.runtimeEpoch!==e.attempt.runtimeEpoch||e.attempt.abortSignal?.aborted)throw this.logSessionAuthority("session_reconcile_decision",{reason:"operation_fence_mismatch"}),new RuntimeCommandError("INVALID_PARAMS","session reconciliation is fenced");try{await this.runtimeLeases.renew(e.lease)}catch{throw this.logSessionAuthority("session_reconcile_decision",{reason:"lease_epoch_mismatch"}),new RuntimeCommandError("INVALID_PARAMS","session reconciliation is fenced")}if(e.attempt.abortSignal?.aborted)throw this.logSessionAuthority("session_reconcile_decision",{reason:"operation_aborted"}),new RuntimeCommandError("INVALID_PARAMS","session reconciliation is fenced")}sessionKeyHash(e){return createHash("sha256").update(e).digest("hex").slice(0,16)}logSessionAuthority(e,t){this.logger.info?.(`[bridge-core] ${JSON.stringify({event:e,...t})}`)}async handleRequest(e){this.writeObs({component:"bridge-core",domain:"forward",name:`rpc.request.${e.method}`,severity:"debug",payload:{id:e.id,method:e.method}});const t=e;try{switch(t.method){case"initialize":await this.handleInitialize(t);break;case"ping":this.transport.sendResult(t.id,{pong:!0});break;case"session/new":await this.handleSessionNew(t);break;case"session/load":await this.handleSessionLoad(t);break;case"session/list":await this.handleSessionList(t);break;case"session/rename":await this.handleSessionRename(t);break;case"session/delete":await this.handleSessionDelete(t);break;case"session/clear":await this.handleSessionClear(t);break;case"session/prompt":await this.handleSessionPrompt(t);break;case"session/set_mode":await this.handleSessionSetMode(t);break;case"session/set_model":await this.handleSessionSetModel(t);break;case"agent/create":await this.handleAgentCreate(t);break;case"agent/update":await this.handleAgentUpdate(t);break;case"agent/delete":await this.handleAgentDelete(t);break;case"agent/applyTemplate":await this.handleApplyTemplate(t);break;case"skills/list":await this.handleSkillsList(t);break;case"skills/install":await this.handleSkillsInstall(t);break;case"skills/uninstall":await this.handleSkillsUninstall(t);break;case"skills/update":await this.handleSkillsUpdate(t);break;case"models/list":await this.handleModelsList(t);break;case"session/cancel":await this.handleSessionCancel(t.id,t.params);break;case"cron/list":case"cron/status":case"cron/add":case"cron/update":case"cron/remove":case"cron/run":case"cron/runs":await this.handleCronRequest(t);break;default:"agent/list"===e.method?await this.handleAgentList(e):"_beeos/session/history"===e.method?await this.handleSessionHistoryPage(e):"mcp/list"===e.method?await this.handleMcpList(e):"mcp/set"===e.method?await this.handleMcpSet(e):"mcp/unset"===e.method?await this.handleMcpUnset(e):e.method.startsWith("terminal/")&&this.handleTerminalRequest?await this.handleTerminalRequest(e):this.transport.sendAcpError(e.id,Errors.MethodNotFound,{message:`Method not found: ${e.method}`})}}catch(t){this.transport.sendAcpError(e.id,Errors.InternalError,{message:t instanceof Error?t.message:"Internal error"})}}handleCanvasNotification=null;getCanvasState=null;onSessionLoaded=null;onSkillInstalled=null;onSessionDeleted=null;onSessionCleared=null;handleNotification(e){switch(e.method){case"session/cancel":this.observeLifecycle(this.handleSessionCancel(void 0,e.params),"session_cancel_notification");break;case"canvas/toggle":case"canvas/clear":case"canvas/reference":case"canvas/dimensions":this.observeLifecycle(this.dispatchLegacyCanvasNotification(e.method,e.params),`canvas_notification_${e.method.replace("/","_")}`);break;default:e.method.startsWith("terminal/")&&this.handleTerminalNotification?this.handleTerminalNotification(e.method,e.params):this.logger.debug?.(`[bridge-core] Unknown notification: ${e.method}`)}}async handleInitialize(e){await this.cronReady,this.initialized=!0;const t=this.transport.forwardControl;!1===e.params?.forwardThinking&&(t.forwardThinking=!1),!1===e.params?.forwardToolCalls&&(t.forwardToolCalls=!1),this.transport.sendResult(e.id,{protocolVersion:ACP_PROTOCOL_VERSION,agentCapabilities:{loadSession:!0,skills:!0,multipleSessions:!0,sessionCapabilities:{"web-ssh":!!this.config.bridge.shell.enabled,canvas:!!this.config.canvasEnabled},promptCapabilities:{image:!0,audio:!1,embeddedContext:!0},mcp:{}},agentInfo:{name:"beeos-claw",title:"BeeOS Agent Bridge",version:"0.1.0"},_meta:{},authMethods:[]})}async createRuntimeSession(e,t){const s=e.operationId.trim(),i=e.platformAgentId.trim();if(!s)throw new Error("session/new operationId is required");if(assertTrustedCoreExecutionAttempt(t,{operationId:s,runtimeEpoch:t.runtimeEpoch}),"service"!==t.origin)throw new Error("session/new runtime allocation requires Service authority");const n=this.resolveLocalAgentId(i);if(!i||!n)throw new RuntimeSessionNewRejectedError("session/new target agent is unknown");const r=`agent:${n}:service:${s}`,o=this.allocateSession({agentId:n,gatewaySessionKey:r,cwd:e.cwd??".",...void 0===e.title?{}:{title:e.title}});return await this.prepareSessionToolCatalog(n,r),{sessionId:o.sessionId,gatewaySessionKey:r,agentId:n,platformAgentId:i}}async handleSessionNew(e){const t="string"==typeof e.params?.agentId&&e.params.agentId||this.config.gateway.agentId||this.getDefaultAgentId(),s=`agent:${t}:${randomUUID()}`,i="string"==typeof e.params?.cwd?e.params.cwd:".",n=this.allocateSession({agentId:t,gatewaySessionKey:s,cwd:i});await this.prepareSessionToolCatalog(t,s),this.transport.sendResult(e.id,{sessionId:n.sessionId,modes:{availableModes:[{...DEFAULT_MODE}],currentModeId:DEFAULT_MODE.id},_meta:{sessionKey:s,agentId:n.agentId,platformAgentId:n.platformAgentId??null}}),this.fetchHistoryInBackground(s)}allocateSession(e){const t=this.state.findSessionByKey(e.gatewaySessionKey);if(t)return t;const s=createSessionObject({gatewayKey:e.gatewaySessionKey,instanceId:this.instanceId,resolvePlatformAgentId:e=>this.resolvePlatformAgentId(e),thirdPartyChannels:this.thirdPartyChannels,...void 0===e.cwd?{}:{cwd:e.cwd},...void 0===e.title?{}:{title:e.title}});if(s.agentId!==e.agentId)throw new Error("session/new gateway key agent mismatch");return this.registerSession(s,{ensureVerbose:!1}),s}prepareWorkspaceCatalog(){this.scheduleCatalogPrepare({agentId:this.getDefaultAgentId(),mode:"catalog",keyKind:"boot"})}scheduleSessionToolCatalog(e,t){const s=classifyCatalogSessionKey(t);this.scheduleCatalogPrepare({agentId:e,sessionKey:t,mode:"im_conversation"===s?"effective":"both",keyKind:s})}scheduleCatalogPrepare(e){this.runCatalogPrepare(e)}async prepareSessionToolCatalog(e,t){await this.runCatalogPrepare({agentId:e,sessionKey:t,mode:"both",keyKind:classifyCatalogSessionKey(t)})}async runCatalogPrepare(e){const t=this.getGateway();if(!t?.isConnected||"function"!=typeof t.request)return;const s=`${e.mode}:${e.agentId}:${e.sessionKey??""}`;if(!this.catalogPrepareInFlight.has(s)){this.catalogPrepareInFlight.add(s);try{await prepareOpenClawToolCatalog({request:(e,s)=>t.request(e,s),agentId:e.agentId,...void 0===e.sessionKey?{}:{sessionKey:e.sessionKey},mode:e.mode,keyKind:e.keyKind,logger:this.logger})}finally{this.catalogPrepareInFlight.delete(s)}}}fetchHistoryInBackground(e){const t=this.getGateway();t?.isConnected&&loadHistory(t,e,50).catch(e=>{this.logger.debug?.(`[bridge-core] Background history fetch failed: ${e instanceof Error?e.message:e}`)})}async subscribeSessionMessages(e){if(e.messageSubscriptionActive)return;const t=this.getGateway();if(t?.isConnected)try{await t.sessionsMessagesSubscribe(e.gatewaySessionKey),e.messageSubscriptionActive=!0,this.logger.debug?.(`[bridge-core] session.message subscribed sessionKey=${e.gatewaySessionKey}`)}catch(t){this.logger.debug?.(`[bridge-core] session.message subscribe failed key=${e.gatewaySessionKey}: ${t instanceof Error?t.message:t}`)}}async resubscribeAllSessionMessages(){for(const e of this.state.sessions.values())e.messageSubscriptionActive=!1,await this.subscribeSessionMessages(e)}handleSessionMessage(e){const t=e?.sessionKey,s=e?.messageId;if(!t||!s)return;const i=this.state.sessionsByGatewayKey.get(t);if(!i)return;const n=e.message?.role;if("toolResult"===n||"tool"===n)return void this.handlePersistedToolResultMessage(i,e).catch(e=>{this.logger.warn?.(`[bridge-core] persisted toolResult handling failed sessionKey=${t} messageId=${s}: ${e instanceof Error?e.message:String(e)}`)});const r="user"===n?"user":"system"===n?"system":"assistant",o=i.lastTurnIdByRole?.[r];if(!o)return void this.logger.debug?.(`[bridge-core] session.message skipped: no lastTurnId role=${r} sessionKey=${t} messageId=${s}`);const a=e.message?.timestamp,l="number"==typeof a&&a>0?new Date(a).toISOString():void 0,d={turnId:o};"number"==typeof e.messageSeq&&(d.seq=e.messageSeq),this.transport.sendSessionUpdate(i.sessionId,{sessionUpdate:"message_id_bind",role:r,messageId:s,...l?{timestamp:l}:{}},d),i.lastTurnIdByRole&&(i.lastTurnIdByRole[r]=void 0),"assistant"===r&&reconcileMissingArtifacts({localHistory:this.localSessionHistory,runOutputRouter:this.runOutputRouter,config:this.config,logger:this.logger,writeObs:this.writeObs,getCanvasState:()=>this.getCanvasState?.()??null,isSessionAlive:e=>this.state.sessions.has(e)},{session:i,turnId:o,messageId:s,runId:o})}async handlePersistedToolResultMessage(e,t){const s=t.messageId;if(!s)return;const i=extractPersistedToolResultEnvelope(t.message),n=this.extractPersistedToolResultFromHistory(e,s),r=mergePersistedToolResultEnvelopes(n,i);if(!r?.toolCallId)return void this.writeObs({component:"bridge-core",domain:"tool_result",name:"tool_result.skipped_no_tool_call_id",severity:"warn",payload:{sessionId:e.sessionId,messageId:s}});n&&this.writeObs({component:"bridge-core",domain:"tool_result",name:"tool_result.hydrated_from_history",severity:"debug",payload:{sessionId:e.sessionId,messageId:s,toolCallId:r.toolCallId,toolName:r.toolName}});const o=e.toolCallTurns?.get(r.toolCallId),a=o?.turnId??resolveTurnId(e);if(!a)return void this.writeObs({component:"bridge-core",domain:"tool_result",name:"tool_result.skipped_no_turn",severity:"warn",payload:{sessionId:e.sessionId,messageId:t.messageId,toolCallId:r.toolCallId,toolName:r.toolName}});const l=projectPersistedToolResultForReplay({toolCallId:r.toolCallId,toolName:r.toolName||o?.toolName||"tool",...r.rawText?{rawText:r.rawText}:{},...r.rawResult?{rawResult:r.rawResult}:{},...r.timestamp?{timestamp:r.timestamp}:{}});await emitToolResultProjectionForTurn({session:e,projection:l,toolCallId:r.toolCallId,turnId:a,config:this.config,logger:this.logger,writeObs:this.writeObs,getCanvasState:()=>this.getCanvasState?.()??null,sendUpdate:s=>(a&&void 0===s._turnId&&(s._turnId=a),this.transport.sendSessionUpdate(e.sessionId,s,{turnId:a,..."number"==typeof t.messageSeq?{seq:t.messageSeq}:{}}))})}extractPersistedToolResultFromHistory(e,t){if(!e.agentId)return;const s=this.localSessionHistory.readEntries({agentId:e.agentId,sessionKey:e.gatewaySessionKey,sessionId:e.sessionId});return findPersistedToolResultEnvelopeByMessageId(s,t)}async recoverSessionFromGateway(e){const t=this.getGateway();if(t?.isConnected)try{const s=await t.sessionsList();if(!s||"object"!=typeof s)return;const i=s.sessions;if(!Array.isArray(i))return;for(const t of i){const s=t,i=s.sessionKey??s.key??s.id??s.name;if(i&&"string"==typeof i&&deriveSessionId(i,this.instanceId)===e){const e=s.label||s.derivedTitle||s.title||void 0,t=e&&!isPlaceholderTitle(e)?e:void 0,n=createSessionObject({gatewayKey:i,instanceId:this.instanceId,resolvePlatformAgentId:e=>this.resolvePlatformAgentId(e),thirdPartyChannels:this.thirdPartyChannels,title:t});return this.registerSession(n),n}}}catch(e){this.logger.debug?.("[bridge-core] recoverSessionFromGateway failed:",e)}}async resolveOrCreateSession(e){let t=this.state.sessions.get(e);if(!t&&e.startsWith("agent:")&&(t=this.state.findSessionByKey(e)),t||e.startsWith("agent:")||(t=await this.recoverSessionFromGateway(e)),t)return{session:t,created:!1};const s=e.startsWith("agent:"),i=this.config.gateway.agentId||this.getDefaultAgentId();return t=createSessionObject({gatewayKey:s?e:`agent:${i}:${e}`,instanceId:this.instanceId,resolvePlatformAgentId:e=>this.resolvePlatformAgentId(e),thirdPartyChannels:this.thirdPartyChannels}),this.registerSession(t),{session:t,created:!0}}async handleSessionLoad(e){const t=e.params?.sessionId;if(!t)return void this.transport.sendAcpError(e.id,Errors.MissingParam,{param:"sessionId"});const{session:s}=await this.resolveOrCreateSession(t),i=s.sessionId;this.state.lastActiveSessionId=i,s.chatReplayDedupKeys.clear(),s.sessionLoadInProgress=!0;try{const e=await this.emitHistoryTurns(s);this.emitCurrentStreamSnapshot(s,e)}finally{s.sessionLoadInProgress=!1}this.transport.sendResult(e.id,{sessionId:i}),this.onSessionLoaded?.(i,s.gatewaySessionKey,s.platformAgentId),this.pushSkillsList().catch(e=>{this.logger.warn?.(`[bridge-core] pushSkillsList failed: ${e instanceof Error?e.message:e}`)})}async emitHistoryTurns(e){const t=new Set,s=e.sessionId;try{const i=this.getGateway(),n=e.assistantMq&&!e.assistantMq.closed,r=n?null:i,o=await loadHistory(r,e.gatewaySessionKey,100,{localHistory:this.localSessionHistory,agentId:e.agentId,sessionId:e.sessionId,logger:this.logger,emitObs:e=>{this.writeObs({component:"history-replay",domain:"lifecycle",name:e.name,severity:e.severity,payload:e.payload})}}),a=groupIntoTurns(o),l=a.filter(e=>"user"===e.role).length,d=a.filter(e=>"assistant"===e.role).length,c=a.filter(e=>e.toolCalls?.length).length,m=a.reduce((e,t)=>e+(t.toolCalls?.length??0),0);this.logger.debug?.(`[bridge-core] handleSessionLoad sid=${s} messages=${o.length} turns=${a.length} userTurns=${l} asstTurns=${d} turnsWithToolCalls=${c} totalToolCalls=${m}`),l>0&&e.pendingUserTurnUpdates&&e.pendingUserTurnUpdates.length>0&&(e.pendingUserTurnUpdates=[]);const u=t=>`history:${e.gatewaySessionKey}:${t}`;let p=-1;if(n)for(let e=a.length-1;e>=0;e--)if("assistant"===a[e].role){p=e;break}const{replayedToolCallIds:h,streamCompleteCount:g}=await this.replayHistoryTurnsToSink(e,a,this.transport,{mqActive:Boolean(n),lastAsstIdx:p,turnIdFor:(e,t)=>u(t)});for(const e of h)t.add(e);this.logger.debug?.(`[bridge-core] emitHistoryTurns DONE sid=${s} historyStreamCompleteEmitted=${g} replayedToolCallIds=${t.size}`)}catch(e){this.logger.warn?.("[bridge-core] Failed to load session history:",e)}return t}async replayHistoryTurnsToSink(e,t,s,i){const n=e.sessionId,r=new Set;let o=0;for(let a=0;a<t.length;a++){const l=t[a],d=i.turnIdFor(l,a);if(i.mqActive&&a===i.lastAsstIdx){const e=classifyHistoricalAssistantTurnCompletion(l,{mqActive:i.mqActive,isSkippedForLiveMq:!0});this.logger.debug?.(`[bridge-core] SKIP history_turn[${a}] assistant — ${"live"===e.kind?e.reason:"mq active"}, deferring to emitCurrentStreamSnapshot`);continue}if("user"===l.role&&"internal_system"===l.provenance?.kind){this.logger.debug?.(`[bridge-core] SKIP history_turn[${a}] internal_system user message`);continue}if("user"===l.role&&/^\s*system\s*\(untrusted[^)]*\)/i.test(l.content)){this.logger.debug?.(`[bridge-core] SKIP history_turn[${a}] system-injected user message`);continue}if("user"===l.role&&isHeartbeatPromptText(l.content)){this.logger.info?.(`[bridge-core] SKIP history_turn[${a}] heartbeat cron prompt sessionId=${e.sessionId} contentLen=${l.content.length} preview="${previewSanitizedText(l.content)}"`);continue}if("user"===l.role&&isCronPromptText(l.content)){this.logger.info?.(`[bridge-core] SKIP history_turn[${a}] cron-triggered user message sessionId=${e.sessionId} contentLen=${l.content.length} preview="${previewSanitizedText(l.content)}"`);continue}if("assistant"===l.role){const t=classifyHeartbeatFilter(l.content);if(t.filtered){this.logger.info?.(`[bridge-core] SKIP history_turn[${a}] heartbeat assistant reply reason=${t.reason} sessionId=${e.sessionId} contentLen=${l.content.length} preview="${previewSanitizedText(l.content)}"`);continue}}const c="assistant"===l.role?classifyHistoricalAssistantTurnCompletion(l,{mqActive:i.mqActive,isSkippedForLiveMq:!1}):void 0;if("error"===c?.kind||"interrupted"===c?.kind&&c.failure)s.sendSessionUpdate(n,{sessionUpdate:"agent_message_chunk",content:{type:"text",text:""},...l.timestamp?{timestamp:l.timestamp}:{}},{turnId:d,...void 0!==l._seq?{seq:l._seq}:{}}),s.sendSessionUpdate(n,{sessionUpdate:"stream_complete",stopReason:c.stopReason,error:c.message,errorCode:c.errorCode},{turnId:d}),this.logger.info?.(`[bridge-core] history_runtime_failure_terminalized session_id=${n} turn_id=${d} error_code=${c.errorCode} stop_reason=${c.stopReason} source=history_replay`),o+=1;else if("system"!==l.role)if(this.logger.debug?.(`[bridge-core] SENDING history_turn[${a}] role=${l.role} contentLen=${l.content.length} content=${JSON.stringify(l.content.slice(0,80))} tcCount=${l.toolCalls?.length??0}`),"user"===l.role)this.replayUserTurnAsChunks(s,n,l.content,l.metadata,l.timestamp,d,l._seq);else{if(await this.replayAssistantTurnAsStream(s,n,l,d),l.toolCalls)for(const e of l.toolCalls)e.id&&r.add(e.id);const e=c??classifyHistoricalAssistantTurnCompletion(l,{mqActive:i.mqActive,isSkippedForLiveMq:!1});if("interrupted"===e.kind){const t=new Set(e.incompleteToolCallIds);for(const i of l.toolCalls??[])t.has(i.id)&&s.sendSessionUpdate(n,buildToolCallUpdatePayload({toolCallId:i.id,toolName:i.name,outputText:e.message,kind:i.kind,status:"failed",timestamp:l.timestamp}),{turnId:d});this.logger.info?.(`[bridge-core] history_orphan_turn_terminalized session_id=${n} turn_id=${d} tool_call_ids=${e.incompleteToolCallIds.join(",")} stop_reason=${e.stopReason} source=history_replay`),s.sendSessionUpdate(n,{sessionUpdate:"stream_complete",stopReason:e.stopReason,error:e.message,errorCode:e.errorCode},{turnId:d}),o+=1;continue}if("completed"!==e.kind)continue;s.sendSessionUpdate(n,{sessionUpdate:"stream_complete",stopReason:e.stopReason},{turnId:d}),o+=1}else this.logger.debug?.(`[bridge-core] SENDING system_message[${a}] contentLen=${l.content.length}`),s.sendSessionUpdate(n,{sessionUpdate:"system_message_chunk",content:{type:"text",text:l.content},metadata:l.metadata,...l.timestamp?{timestamp:l.timestamp}:{}},{turnId:d,...void 0!==l._seq?{seq:l._seq}:{}})}return{replayedToolCallIds:r,streamCompleteCount:o}}async handleSessionHistoryPage(e){const t=e.params??{},s="string"==typeof t.sessionId?t.sessionId:"";if(!s)return void this.transport.sendAcpError(e.id,Errors.MissingParam,{param:"sessionId"});const i="string"==typeof t.cursor&&t.cursor.length>0?t.cursor:void 0,n="number"==typeof t.limit&&Number.isFinite(t.limit)&&t.limit>0?Math.min(1e3,Math.floor(t.limit)):100,{session:r}=await this.resolveOrCreateSession(s),o=this.getGateway(),a=await loadHistoryPage(o,r.gatewaySessionKey,{...i?{cursor:i}:{},limit:n,logger:this.logger}),l=groupIntoTurns(a.messages),d=new HistoryUpdateRecorder(this.transport);await this.replayHistoryTurnsToSink(r,l,d,{mqActive:!1,lastAsstIdx:-1,turnIdFor:(e,t)=>void 0!==e._seq?`history:${r.gatewaySessionKey}:seq:${e._seq}`:`history:${r.gatewaySessionKey}:seq:none:${i??"head"}:${t}`}),this.logger.debug?.(`[bridge-core] _beeos/session/history sid=${r.sessionId} cursor=${i??"<head>"} limit=${n} turns=${l.length} updates=${d.updates.length} hasMore=${a.hasMore} nextCursor=${a.nextCursor??"<none>"}`),this.transport.sendResult(e.id,{updates:d.updates,...a.nextCursor?{nextCursor:a.nextCursor}:{},hasMore:a.hasMore})}emitCurrentStreamSnapshot(e,t){const s=e.pendingUserTurnUpdates;if(s&&s.length>0)for(const t of s)this.transport.sendSessionUpdate(e.sessionId,t);const i=e.assistantMq;if(i&&!i.closed){const s=i.peek(0);let n=0;if(t&&t.size>0)for(let e=0;e<s.length;e++){const i=s[e],r=i.sessionUpdate,o=i.toolCallId;if("tool_call"!==r&&"tool_call_update"!==r||!o||!t.has(o)){if("agent_message_chunk"===r){if(""===(i.content?.text??"").trim()&&e<s.length-1){const i=s[e+1];if(("tool_call"===i.sessionUpdate||"tool_call_update"===i.sessionUpdate)&&i.toolCallId&&t.has(i.toolCallId)){n=e+1;continue}}break}break}n=e+1}const r=resolveTurnId(e),o=r?{turnId:r}:void 0;for(let t=n;t<s.length;t++)this.transport.sendSessionUpdate(e.sessionId,s[t],o)}}async handleSessionList(e){let t;try{t=parseRuntimeSessionListPage(e.params)}catch(t){return void this.transport.sendAcpError(e.id,Errors.InvalidParams,{message:t instanceof Error?t.message:"Invalid session/list params"})}const s=[];for(const[,e]of this.state.sessions)isBackgroundSessionKey(e.gatewaySessionKey)||this.cronIsolatedSessionKeys.has(e.gatewaySessionKey)||!t.includeDeleted&&this.state.deletedGatewaySessionKeys.has(e.gatewaySessionKey)||s.push({sessionId:e.sessionId,title:e.title||"New chat",cwd:e.cwd,updatedAt:e.lastStreamActivityTs??e.createdAt,agentId:e.agentId,platformAgentId:e.platformAgentId,deletable:!isSystemDefaultSessionKey(e.gatewaySessionKey)});const i=this.getGateway();if(i?.isConnected)try{const e=await i.sessionsList();if(e&&"object"==typeof e&&Array.isArray(e.sessions)){const i=e.sessions;for(const e of i){const i=e,n=i.sessionKey??i.key??i.id??i.name;if(!n||"string"!=typeof n)continue;if(isBackgroundSessionKey(n))continue;if(this.cronIsolatedSessionKeys.has(n))continue;const r=deriveSessionId(n,this.instanceId),o=s.find(e=>e.sessionId===n||e.sessionId===r||this.state.sessions.get(e.sessionId)?.gatewaySessionKey===n);if(!0===i.deleted){if(this.state.deletedGatewaySessionKeys.add(n),!t.includeDeleted)continue}else this.state.deletedGatewaySessionKeys.delete(n);const a=i.label||cleanGatewayDerivedTitle(i.derivedTitle)||i.title||void 0,l=a&&!isPlaceholderTitle(a)?a:void 0,d=i.model,c=i.modelProvider;if(o){if(l&&isPlaceholderTitle(o.title)){o.title=l;const e=this.state.sessions.get(o.sessionId);e&&isPlaceholderTitle(e.title)&&(e.title=l)}const e=i.updatedAt;e&&(o.updatedAt=e),d&&(o.model=d),c&&(o.modelProvider=c)}else{const e=createSessionObject({gatewayKey:n,instanceId:this.instanceId,resolvePlatformAgentId:e=>this.resolvePlatformAgentId(e),thirdPartyChannels:this.thirdPartyChannels,cwd:i.cwd,title:l});this.registerSession(e,{ensureVerbose:!1}),s.push({sessionId:e.sessionId,title:l||"",cwd:i.cwd,updatedAt:i.updatedAt,agentId:e.agentId,platformAgentId:e.platformAgentId,deletable:!isSystemDefaultSessionKey(n),model:d,modelProvider:c})}}}}catch(e){this.logger.debug?.("[bridge-core] sessions.list enrichment failed:",e)}const n=t.includeDeleted?s:s.filter(e=>{const t=this.state.sessions.get(e.sessionId)?.gatewaySessionKey;return!t||!this.state.deletedGatewaySessionKeys.has(t)});n.sort((e,t)=>(t.updatedAt??0)-(e.updatedAt??0)||e.sessionId.localeCompare(t.sessionId)),this.transport.sendResult(e.id,{sessions:pageRuntimeSessions(n,t)})}async handleSessionRename(e){const t=e.params?.sessionId,s=e.params?.title;if(!t||!s)return void this.transport.sendAcpError(e.id,Errors.MissingParam,{param:"sessionId, title"});const i=s.slice(0,64),n=this.state.sessions.get(t);if(n)try{const s=await this.dispatchLegacyConversationCapability("session.rename",n,{title:i});n.title=s.title,n.titleSource="manual",this.transport.sendResult(e.id,{sessionId:t,title:s.title})}catch(s){this.logger.warn?.(`[bridge-core] ACP rename projection deferred: ${s instanceof Error?s.message:String(s)}`),n.title=i,n.titleSource="manual",this.transport.sendResult(e.id,{sessionId:t,title:i})}else this.transport.sendAcpError(e.id,Errors.SessionNotFound)}generateTitleFromQA(e,t,s,i){const n=this.titles;if(!n)return void this.logger.warn?.("Conversation title runtime unavailable");const r=e.channelId??extractBeeosConversationIdFromSessionKey(e.gatewaySessionKey);r?(async()=>{const o=await generateTitleFromQA(s,i,{provider:await n.resolveProvider()}),a=await n.conversations.update({conversationId:r,title:o,titleSource:"generated"});e.title=a.title,e.titleSource=a.titleSource,this.transport.sendSessionUpdate(t,{sessionUpdate:"title_update",title:a.title})})().catch(e=>this.logger.warn?.(`Conversation title generation failed: ${e instanceof Error?e.message:"unknown error"}`)):this.logger.warn?.("Conversation title cannot resolve Cloud conversation ID")}async handleSessionDelete(e){const t=e.params?.sessionId;if(!t)return void this.transport.sendAcpError(e.id,Errors.MissingParam,{param:"sessionId"});const s=this.state.sessions.get(t);if(!s)return this.logger.info?.(`[bridge-core] session/delete sessionId=${t} NOT FOUND in local state`),void this.transport.sendAcpError(e.id,Errors.SessionNotFound);const i=s.gatewaySessionKey,n=this.sessionMutationCoordinator.tryStartExclusive({sessionKey:i,label:"session_delete"},"acp");if(!n.ok)return void this.sendSessionBusy(s,e.id,"active_session_mutation",n.queueDepth);const r=n.token;try{if(this.hasActiveRun(s)){const t=this.sessionMutationCoordinator.gateSnapshot(i);return void this.sendSessionBusy(s,e.id,"active_session_mutation",t.queueDepth)}this.logger.info?.(`[bridge-core] session/delete sessionId=${t} gatewayKey=${i} gwConnected=${!!this.getGateway()?.isConnected}`);let n=!1;const r=this.getGateway();if(r?.isConnected&&i)try{await r.sessionsDelete(i),n=!0,this.logger.info?.(`[bridge-core] Gateway sessions.delete SUCCESS for key=${i}`)}catch(t){const s=t instanceof Error?t.message:String(t);if(this.logger.info?.(`[bridge-core] Gateway sessions.delete FAILED for key=${i}: ${s}`),!s.includes("Cannot delete the main session"))return void this.transport.sendResult(e.id,{deleted:!1});try{await r.sessionsReset(i),n=!0,this.logger.info?.(`[bridge-core] Gateway sessions.reset SUCCESS for key=${i}`)}catch(t){return this.logger.warn?.(`[bridge-core] Gateway sessions.reset also failed for key=${i}: ${t instanceof Error?t.message:t}`),void this.transport.sendResult(e.id,{deleted:!1})}}else i&&this.logger.warn?.(`[bridge-core] Gateway not connected, proceeding with local-only delete for key=${i}`);this.state.clearStreamIdleTimer(s),s.assistantMq?.close(),s.assistantMq=void 0,s.currentRunId&&this.state.promptsByRunId.delete(s.currentRunId);const o=new Set;for(const[e,s]of this.state.toolCallToSessionMap)s===t&&(this.state.toolCallToSessionMap.delete(e),o.add(e));for(const[e,t]of this.state.toolCallIdMap)o.has(t)&&this.state.toolCallIdMap.delete(e);this.state.sessions.delete(t),this.state.sessionsByGatewayKey.delete(i),this.state.deletedGatewaySessionKeys.add(i),this.transport.resetSessionUpdateCount(t),n&&this.onSessionDeleted?.(i),this.logger.info?.(`[bridge-core] session/delete DONE sessionId=${t} gatewayKey=${i} deleted=true`),this.transport.sendResult(e.id,{deleted:!0})}finally{this.sessionMutationCoordinator.releaseBeforeRuntime(r)}}async handleSessionClear(e){this.logger.info?.(`[bridge-core] handleSessionClear called, params=${JSON.stringify(e.params)}`);const t=e.params?.sessionId;if(!t)return this.logger.warn?.("[bridge-core] session/clear missing sessionId"),void this.transport.sendAcpError(e.id,Errors.MissingParam,{param:"sessionId"});const s=this.state.sessions.get(t);if(!s)return this.logger.warn?.(`[bridge-core] session/clear sessionId=${t} not found in state`),void this.transport.sendAcpError(e.id,Errors.SessionNotFound);this.logger.info?.(`[bridge-core] session/clear sessionId=${t} gatewayKey=${s.gatewaySessionKey}`);const i=this.getGateway(),n=i?.isConnected;if(!s.promptDone&&(void 0!==s.pendingPromptId||void 0!==s.currentRunId)){this.logger.info?.(`[bridge-core] session/clear: cancelling active run sid=${t} runId=${s.currentRunId}`);const r=s.writeGateToken;let o=!1;if(n&&r){const e=await this.sessionMutationCoordinator.requestCancel(r,i);o="recovery_required"===await this.handleCancellationResult(s,e)}else n&&await i.chatAbort(s.gatewaySessionKey,s.currentRunId);return void this.transport.sendError(e.id,Errors.AgentError.rpcCode,o?SESSION_RECOVERY_REQUIRED_MESSAGE:"Cancellation is still converging",o?{code:SESSION_RECOVERY_REQUIRED_CODE,stopReason:"interrupted"}:{code:"session_cancellation_pending",stopReason:"cancelled"})}try{await this.dispatchLegacyConversationCapability("session.clear",s,{}),this.transport.sendResult(e.id,{cleared:!0}),this.logger.info?.("[bridge-core] session/clear DONE, sent cleared=true")}catch(t){this.sendRuntimeCommandError(e.id,t)}}async handleSessionSetMode(e){const t=e.params?.sessionId,s=e.params?.mode,i=t?this.state.sessions.get(t):void 0;if(t&&s)if(i)try{const t=await this.dispatchLegacyConversationCapability("session.setMode",i,{mode:s});this.transport.sendResult(e.id,t)}catch(t){this.sendRuntimeCommandError(e.id,t)}else this.transport.sendAcpError(e.id,Errors.SessionNotFound);else this.transport.sendAcpError(e.id,Errors.MissingParam,{param:"sessionId, mode"})}async handleSessionSetModel(e){const t=e.params,s=null===t||"object"!=typeof t||Array.isArray(t)?void 0:t,i="string"==typeof s?.sessionId?s.sessionId:void 0;if(!i||!s||!Object.prototype.hasOwnProperty.call(s,"model"))return void this.transport.sendAcpError(e.id,Errors.MissingParam,{param:"sessionId, model"});const n=s.model;if(null!==n&&("string"!=typeof n||!n.trim()))return void this.transport.sendAcpError(e.id,Errors.InvalidParams,{message:"model must be a non-empty string or null"});const r=i?this.state.sessions.get(i):void 0;if(r)try{const t=await this.dispatchLegacyConversationCapability("session.setModel",r,{modelOverrideId:n});this.transport.sendResult(e.id,{model:t.effectiveModelId,modelProvider:t.modelProvider??null})}catch(t){this.sendRuntimeCommandError(e.id,t)}else this.transport.sendAcpError(e.id,Errors.SessionNotFound)}sendRuntimeCommandError(e,t){if(t instanceof RuntimeCommandError)switch(t.code){case"MISSING_PARAM":return void this.transport.sendAcpError(e,Errors.MissingParam,{...t.param?{param:t.param}:{}});case"INVALID_PARAMS":return void this.transport.sendAcpError(e,Errors.InvalidParams,{message:t.message});case"GATEWAY_NOT_CONNECTED":return void this.transport.sendAcpError(e,Errors.GatewayNotConnected);case"EXECUTION_FAILED":case"AGENT_DEFAULT_MODEL_NOT_CONFIGURED":case"OUTCOME_UNKNOWN":case"INSTANCE_EPOCH_MISMATCH":case"COMMAND_EXPIRED":case"IDEMPOTENCY_CONFLICT":case"UNAUTHORIZED_TARGET":return void this.transport.sendAcpError(e,Errors.InternalError,{message:t.message})}else this.transport.sendAcpError(e,Errors.InternalError,{message:t instanceof Error?t.message:"Internal error"})}createOperationId(){const e=randomUUID();return`${e.slice(0,14)}7${e.slice(15)}`}canonicalContextBase(e,t=this.createOperationId(),s="0"){const i=Date.now(),n=createHash("sha256").update(JSON.stringify(e)).digest("hex");return{operationId:t,runtimeInstanceUid:this.instanceId,dispatchEpoch:s,paramsHash:n,idempotencyKeyHash:createHash("sha256").update(t).digest("hex"),claimDeadline:new Date(i+3e4).toISOString(),executionDeadline:new Date(i+3e5).toISOString()}}acpExecutionAttempt(e){return createAcpCoreExecutionAttempt({operationId:e.context.operationId,runtimeEpoch:e.context.dispatchEpoch,resources:this.runtimeCommandResources,historyBoundary:this.localHistoryBoundary})}instanceCommand(e,t,s){return{name:e,params:t,context:{...this.canonicalContextBase(t,s.operationId,s.runtimeEpoch),scope:"instance"}}}async agentCommand(e,t,s,i){return{name:e,params:s,context:{...this.canonicalContextBase(s,i.operationId,i.runtimeEpoch),scope:"agent",agent:{runtimeLocalKey:t,runtimeAgentUid:await this.runtimeAgentIdentities.resolveOrCreate(t)}}}}async dispatchLegacyAgentCapability(e,t,s){const i=normalizeRuntimeAgentLocalKey(t),n=await this.runtimeLeases.claim(this.createOperationId());let r=!1;try{const t={name:e,params:s,context:{...this.canonicalContextBase(s,n.operationId,n.runtimeEpoch),scope:"agent",agent:{runtimeLocalKey:i,runtimeAgentUid:await this.runtimeAgentIdentities.resolveOrCreate(i)}}},o=this.runtimeCommands.dispatch(t,this.acpExecutionAttempt(t));return r=!0,await o.completion}finally{r||await this.runtimeLeases.release(n)}}async dispatchLegacyInstanceCapability(e,t){const s=await this.runtimeLeases.claim(this.createOperationId());let i=!1;try{const n={name:e,params:t,context:{...this.canonicalContextBase(t,s.operationId,s.runtimeEpoch),scope:"instance"}},r=this.runtimeCommands.dispatch(n,this.acpExecutionAttempt(n));return i=!0,await r.completion}finally{i||await this.runtimeLeases.release(s)}}async handleMcpList(e){const t=await this.dispatchLegacyInstanceCapability("mcp.list",{});this.transport.sendResult(e.id,{servers:Object.fromEntries(t.servers.map(e=>[e.name,{transport:e.transport,...e.url?{url:e.url}:{},...e.command?{command:e.command}:{},...e.args?{args:[...e.args]}:{},...e.cwd?{cwd:e.cwd}:{},...e.connectionTimeoutMs?{connectionTimeoutMs:e.connectionTimeoutMs}:{}}]))})}async handleMcpSet(e){const t=e.params&&"object"==typeof e.params?e.params:{},s="string"==typeof t.name?t.name:"";let i;try{if(requireSecretFreeMcpServerName(s),!t.server||"object"!=typeof t.server||Array.isArray(t.server))throw new Error("mcp/set requires name and server");const e=t.server,n=extractMcpCredentialMaterial(e),r={...e};delete r.headers,delete r.env;const o=e.transport??("string"==typeof e.command?"stdio":"string"==typeof e.url?"streamable-http":void 0),a=normalizeMcpPublicServerConfig({...r,transport:o}),l=createHash("sha256").update(canonicalizeJcs(a)).digest("hex");i={definition:{definitionRef:`acp-mcp:sha256:${l}`,version:"local-v1",sha256:l,...n?{credentialRef:await requireCredentialReferencePort(this.mcpCredentialReferences).put({serverName:s,material:n})}:{}},config:structuredClone(a)}}catch(t){return void this.transport.sendAcpError(e.id,Errors.InvalidParams,{message:t instanceof Error?t.message:"Invalid MCP server config"})}this.runtimeCommandResources.registerMcpDefinition?.(i.definition,i.config);try{await this.dispatchLegacyInstanceCapability("mcp.set",{name:s,definition:i.definition})}catch(e){if(i.definition.credentialRef){!await this.mcp.removeCredentialIfUnreferenced(i.definition.credentialRef)&&this.mcpCredentialReferences?.owns(i.definition.credentialRef)&&await this.mcpCredentialReferences.remove(i.definition.credentialRef)}throw e}this.transport.sendResult(e.id,{ok:!0,restarting:!1})}async handleMcpUnset(e){const t=e.params&&"object"==typeof e.params?e.params:{};let s;try{s=requireSecretFreeMcpServerName(t.name)}catch{return void this.transport.sendAcpError(e.id,Errors.InvalidParams,{message:"mcp/unset requires name"})}await this.dispatchLegacyInstanceCapability("mcp.unset",{name:s}),this.transport.sendResult(e.id,{ok:!0,restarting:!1})}async dispatchLegacyConversationCapability(e,t,s){const i=await this.runtimeLeases.claim(this.createOperationId());let n=!1;try{const r={name:e,params:s,context:{...this.canonicalContextBase(s,i.operationId,i.runtimeEpoch),scope:"conversation",conversation:{runtimeConversationUid:t.sessionId,runtimeSessionKey:t.gatewaySessionKey}}},o=this.runtimeCommands.dispatch(r,this.acpExecutionAttempt(r));return n=!0,await o.completion}finally{n||await this.runtimeLeases.release(i)}}async dispatchLegacyTerminalCapability(e,t,s,i){const n=await this.runtimeLeases.claim(this.createOperationId());let r=!1;try{const o={name:e,params:i,context:{...this.canonicalContextBase(i,n.operationId,n.runtimeEpoch),scope:"terminal",terminal:{terminalId:t,clientId:s}}},a=this.runtimeCommands.dispatch(o,this.acpExecutionAttempt(o));return r=!0,await a.completion}finally{r||await this.runtimeLeases.release(n)}}async dispatchLegacyCanvasNotification(e,t){const s=t&&"object"==typeof t?t:{},i=e.replace("/","."),n="canvas.toggle"===i?{visible:!0===s.visible||!0===s.enabled}:"canvas.reference"===i?{surfaceId:"string"==typeof s.surfaceId?s.surfaceId:""}:"canvas.dimensions"===i?{width:Number(s.width),height:Number(s.height),..."number"==typeof s.devicePixelRatio?{devicePixelRatio:s.devicePixelRatio}:{}}:{},r="string"==typeof s.sessionId?s.sessionId:"acp",o="string"==typeof s.canvasId?s.canvasId:r,a=await this.runtimeLeases.claim(this.createOperationId());let l=!1;try{const e={name:i,params:n,context:{...this.canonicalContextBase(n,a.operationId,a.runtimeEpoch),scope:"canvas",canvas:{canvasId:o,sessionId:r}}},t=this.runtimeCommands.dispatch(e,this.acpExecutionAttempt(e));l=!0,await t.completion}finally{l||await this.runtimeLeases.release(a)}}async applyCanonicalCanvasControl(e,t){if("canvas/reference"===e){const e=this.state.resolveSessionFromParams(t);if(!e)throw new RuntimeCommandError("INVALID_PARAMS","canvas session not found");const s="string"==typeof t.surfaceId?t.surfaceId:void 0;return void(e.referencedSurfaceId=s||void 0)}if(!this.handleCanvasNotification)throw new RuntimeCommandError("INVALID_PARAMS","canvas control delegate is not configured");await this.handleCanvasNotification(e,t)}async dispatchLegacyCronCapability(e,t){const s=normalizeRuntimeAgentLocalKey(this.config.gateway.agentId),i=await this.runtimeLeases.claim(this.createOperationId());let n=!1;try{const r="cron.add"===e?{...t,runtimeCronJobUid:`runtime-cron:${i.operationId}`}:"cron.run"===e?{...t,runToken:`runtime-run:${i.operationId}`}:t,o={name:e,params:r,context:{...this.canonicalContextBase(r,i.operationId,i.runtimeEpoch),scope:"agent",agent:{runtimeLocalKey:s,runtimeAgentUid:await this.runtimeAgentIdentities.resolveOrCreate(s)}}},a=this.runtimeCommands.dispatch(o,this.acpExecutionAttempt(o));return n=!0,await a.completion}finally{n||await this.runtimeLeases.release(i)}}validateConnectedGateway(){if(!this.getGateway()?.isConnected)throw new RuntimeCommandError("GATEWAY_NOT_CONNECTED","Gateway not connected")}async finishClaimedLegacyAgentMutation(e,t){let s=!1;try{const e=await t(),i=this.runtimeCommands.dispatch(e,this.acpExecutionAttempt(e));s=!0;const n=await i.completion;return queueMicrotask(()=>{this.observeAgentProjection()}),n}finally{s||await this.runtimeLeases.release(e)}}async observeAgentProjection(){await(async(e,t)=>{if(!t)return;let s;try{await Promise.race([Promise.resolve().then(t),new Promise((t,i)=>{s=setTimeout(()=>i(new Error(`${e} timed out`)),this.projectionObservationTimeoutMs)})])}catch(t){this.logger.warn?.(`[acp] ${e} failed: ${t instanceof Error?t.message:String(t)}`)}finally{s&&clearTimeout(s)}})("agent projection sync",this.triggerAgentSync)}async handleAgentCreate(e){try{const t=e.params?.name?.trim()??"";if(!t)throw new RuntimeCommandError("INVALID_PARAMS","Missing agent name");this.validateConnectedGateway();const s=this.createOperationId(),i=await this.runtimeLeases.claim(s),n=await this.finishClaimedLegacyAgentMutation(i,async()=>{const s=await this.runtimeAgentIdentities.allocateCreate(i,"agent.create"),n=Object.entries(e.params?.files??{}).filter(e=>"string"==typeof e[1]).map(([e,t])=>({name:e,contentRef:this.runtimeCommandResources.registerContent(t)}));return this.instanceCommand("agent.create",{runtimeAgentUid:s.runtimeAgentUid,name:t,...e.params?.identity?{identity:e.params.identity}:{},...e.params?.model?{defaultModelId:e.params.model}:{},...n.length?{bootstrapFiles:n}:{}},i)});this.transport.sendResult(e.id,{agentId:n.runtimeLocalKey,platformAgentId:this.resolvePlatformAgentId(n.runtimeLocalKey)??"",workspace:n.workspacePath})}catch(t){this.sendRuntimeCommandError(e.id,t)}}async handleApplyTemplate(e){try{const t=e.params?.name?.trim()??"";if(!t)throw new RuntimeCommandError("INVALID_PARAMS","Missing agent name");const s=e.params?.packageUrl?.trim()??"";if(!s)throw new RuntimeCommandError("MISSING_PARAM","Missing packageUrl","packageUrl");this.validateConnectedGateway();const i=await this.runtimeCommandResources.registerArtifact(s,e.params?.sha256),n=this.createOperationId(),r=await this.runtimeLeases.claim(n),o=await this.finishClaimedLegacyAgentMutation(r,async()=>{const s=await this.runtimeAgentIdentities.allocateCreate(r,"agent.applyTemplate");return this.instanceCommand("agent.applyTemplate",{runtimeAgentUid:s.runtimeAgentUid,name:t,artifact:i,...e.params?.identity?{identity:e.params.identity}:{},...e.params?.model?{defaultModelId:e.params.model}:{}},r)});this.transport.sendResult(e.id,{agentId:o.runtimeLocalKey,platformAgentId:this.resolvePlatformAgentId(o.runtimeLocalKey)??"",workspace:o.workspacePath})}catch(t){this.sendRuntimeCommandError(e.id,t)}}async handleAgentUpdate(e){try{const t=e.params?.agentId?.trim()??"";if(!t)throw new RuntimeCommandError("MISSING_PARAM","Missing agentId","agentId");const s={...void 0!==e.params?.name?{name:e.params.name}:{},...void 0!==e.params?.model?{defaultModelId:e.params.model}:{},...void 0!==e.params?.identity?{identity:e.params.identity}:{}};if(0===Object.keys(s).length)throw new RuntimeCommandError("INVALID_PARAMS","No updatable fields provided (name/model/identity)");this.validateConnectedGateway();const i=await this.runtimeLeases.claim(this.createOperationId()),n=await this.finishClaimedLegacyAgentMutation(i,()=>this.agentCommand("agent.update",t,s,i));this.transport.sendResult(e.id,{agentId:n.runtimeLocalKey})}catch(t){this.sendRuntimeCommandError(e.id,t)}}async handleAgentDelete(e){try{const t=e.params?.agentId?.trim()??"";if(!t)throw new RuntimeCommandError("MISSING_PARAM","Missing agentId","agentId");if("main"===normalizeRuntimeAgentLocalKey(t))throw new RuntimeCommandError("INVALID_PARAMS",'"main" cannot be deleted; delete the instance instead');this.validateConnectedGateway();const s=await this.runtimeLeases.claim(this.createOperationId()),i=await this.finishClaimedLegacyAgentMutation(s,()=>this.agentCommand("agent.delete",t,void 0===e.params?.deleteFiles?{}:{deleteFiles:e.params.deleteFiles},s));this.transport.sendResult(e.id,{agentId:i.runtimeLocalKey,...void 0===i.removedBindings?{}:{removedBindings:i.removedBindings}})}catch(t){this.sendRuntimeCommandError(e.id,t)}}async handleAgentList(e){try{const t=await this.dispatchLegacyInstanceCapability("agent.listProjection",{}),s=t.agents.map(e=>({agentId:e.runtimeLocalKey,name:e.name,...e.emoji?{emoji:e.emoji}:{},...e.avatarUrl?{avatarUrl:e.avatarUrl}:{}})),i={...t.defaultRuntimeLocalKey?{defaultId:t.defaultRuntimeLocalKey}:{},agents:s};this.transport.sendResult(e.id,i)}catch(t){this.sendRuntimeCommandError(e.id,t)}}async handleModelsList(e){try{this.transport.sendResult(e.id,await this.dispatchLegacyInstanceCapability("models.list",{}))}catch(t){this.sendRuntimeCommandError(e.id,t)}}async handleSkillsList(e){try{const t=await this.dispatchLegacyAgentCapability("skills.list",e.params?.agentId,{});this.transport.sendResult(e.id,{skills:t.skills.map(e=>({name:e.name,description:e.description??"",...e.emoji?{emoji:e.emoji}:{},...e.always?{always:!0}:{},...e.bundled?{bundled:!0}:{},...e.enabled?{}:{disabled:!0},skillKey:e.skillKey,...e.iconUrl?{iconUrl:e.iconUrl}:{}}))})}catch(t){this.sendRuntimeCommandError(e.id,t)}}async fetchSimplifiedSkills(e){const t=this.getGateway();if(!t?.isConnected)return[];try{const s=await t.skillsStatus(e);if(!s?.skills)return[];const i=s.skills.filter(e=>!1!==e.eligible||!0===e.disabled).sort((e,t)=>e.always&&!t.always?-1:!e.always&&t.always?1:e.name.localeCompare(t.name)).map(e=>({name:e.name,description:e.description,emoji:e.emoji,always:e.always||void 0,bundled:e.bundled||void 0,disabled:e.disabled||void 0,skillKey:e.skillKey||void 0,iconUrl:void 0})),n=await this.getSkillsDir(e);for(const e of i){const t=e.skillKey??e.name;try{const s=await readFile(join(n,t,".beeos-meta.json"),"utf8"),i=JSON.parse(s);i?.iconUrl&&(e.iconUrl=i.iconUrl)}catch{}}return i}catch(e){return this.logger.warn?.(`[bridge-core] Failed to fetch skills: ${e instanceof Error?e.message:e}`),[]}}async pushSkillsList(e){const t=await this.fetchSimplifiedSkills(e);this.transport.sendNotification("session/update",{sessionId:"_system",update:{sessionUpdate:"available_commands_update",commands:t}}),this.writeObs({component:"bridge-core",domain:"lifecycle",name:"skills.pushed",severity:"debug",payload:{count:t.length}})}getOpenclawHome(){return process.env.OPENCLAW_HOME||join(homedir(),".openclaw")}async getSkillsDir(e){const t=this.getOpenclawHome();if(!e||"main"===e)return join(t,"skills");const s=await this.resolveAgentWorkspaceDir(e);return join(s??join(t,`workspace-${e}`),"skills")}async resolveAgentWorkspaceDir(e){const t=this.getGateway();if(!t?.isConnected)return null;try{const s=await t.request("agents.list",{}),i=normalizeAgentId(e),n=s?.agents?.find(e=>normalizeAgentId(e.id)===i);return n?.workspace??null}catch(t){return this.logger.warn?.(`[bridge-core] resolveAgentWorkspaceDir("${e}") failed: ${t instanceof Error?t.message:t}`),null}}async handleSkillsInstall(e){const t=e.params,s=t?.zipUrl,i=t?.files,n=t?.agentId;let r;try{r=validateSkillSlug(t?.slug||t?.name||"")}catch{return void this.transport.sendAcpError(e.id,Errors.InvalidParams,{message:"Invalid or missing skill slug"})}if(i?.length||s)try{let o;if(s){o=parseSkillZip(await fetchWithRetry(s,{maxRetries:4,logger:this.logger}))}else{if(!i?.length)throw new RuntimeCommandError("INVALID_PARAMS","Missing skill files or zipUrl");o=new Map;for(const e of i){const t=normalizeSkillArtifactPath(e.path),s=await fetchWithRetry(e.downloadUrl,{maxRetries:4,logger:this.logger});o.set(t,new Uint8Array(s))}}if(t?.iconUrl&&o.set(".beeos-meta.json",(new TextEncoder).encode(JSON.stringify({iconUrl:t.iconUrl}))),!this.runtimeCommandResources.registerParsedArtifact)throw new RuntimeCommandError("EXECUTION_FAILED","ACP skill artifact ingress is not configured");const a=this.runtimeCommandResources.registerParsedArtifact({manifest:{slug:r,version:"0.0.0"},workspaceFiles:o});await this.dispatchLegacyAgentCapability("skills.install",n,{artifact:a}),this.transport.sendResult(e.id,{success:!0}),this.onSkillInstalled?.(r,resolveSkillInstallDir(await this.getSkillsDir(n),r)),await this.pushSkillsList(n)}catch(t){this.logger.error?.(`[bridge-core] Failed to install skill "${r}": ${t instanceof Error?t.message:t}`),this.transport.sendAcpError(e.id,Errors.InternalError,{message:`Install failed: ${t instanceof Error?t.message:String(t)}`})}else this.transport.sendAcpError(e.id,Errors.InvalidParams,{message:"Missing skill files or zipUrl"})}async handleSkillsUninstall(e){const t=e.params,s=t?.agentId;let i;try{i=validateSkillSlug(t?.slug||t?.name||"")}catch{return void this.transport.sendAcpError(e.id,Errors.InvalidParams,{message:"Invalid or missing skill name"})}try{await this.dispatchLegacyAgentCapability("skills.uninstall",s,{skillKey:i}),this.transport.sendResult(e.id,{success:!0}),await this.pushSkillsList(s)}catch(t){this.logger.error?.(`[bridge-core] Failed to uninstall skill "${i}": ${t instanceof Error?t.message:t}`),this.transport.sendAcpError(e.id,Errors.InternalError,{message:`Uninstall failed: ${t instanceof Error?t.message:String(t)}`})}}async handleSkillsUpdate(e){const t=e.params,s=t?.skillKey,i=t?.enabled,n=t?.agentId;if(s&&"boolean"==typeof i)try{await this.dispatchLegacyAgentCapability("skills.setEnabled",n,{skillKey:s,enabled:i}),this.logger.info?.(`[bridge-core] Updated skill "${s}" enabled=${i}${n?` for agent "${n}"`:""}`),this.transport.sendResult(e.id,{ok:!0,skillKey:s,enabled:i}),await this.pushSkillsList(n)}catch(t){this.logger.error?.(`[bridge-core] Failed to update skill "${s}": ${t instanceof Error?t.message:t}`),this.transport.sendAcpError(e.id,Errors.InternalError,{message:`Update failed: ${t instanceof Error?t.message:String(t)}`})}else this.transport.sendAcpError(e.id,Errors.InvalidParams,{message:"Missing skillKey or enabled"})}async handleSessionPrompt(e){const t=e.params?.sessionId,s=e.params?.prompt;if(!t||!s)return void this.transport.sendAcpError(e.id,Errors.MissingParam,{param:"sessionId, prompt"});const{session:i,created:n}=await this.resolveOrCreateSession(t);n&&this.onSessionLoaded?.(i.sessionId,i.gatewaySessionKey,i.platformAgentId);const r=i.sessionId;this.state.lastActiveSessionId=r,this.onPromptStart?.();const o=this.getGateway();if(!o?.isConnected)return void this.transport.sendAcpError(e.id,Errors.GatewayNotConnected);if(this.hasActiveRun(i)){const t=this.sessionMutationCoordinator.gateSnapshot(i.gatewaySessionKey);return void this.sendSessionBusy(i,e.id,"active_run",t.queueDepth)}try{await this.ensureVerboseFull(i)}catch(t){return this.releaseSessionWriteGate(i),void this.transport.sendAcpError(e.id,Errors.InternalError,{message:`Failed to prepare agent context: ${t instanceof Error?t.message:String(t)}`})}let a=extractPromptBlocks(sanitizePromptPayload(s));const l=a.filter(e=>"text"===e.type&&"text"in e&&"string"==typeof e.text).map(e=>e.text).join("\n");if(i.fileArtifactDisplayMode=prefersChatFileArtifacts(l)?"chat":"auto","manual"!==i.titleSource&&"generated"!==i.titleSource&&!i.firstTitleQuestion){const e=a.find(e=>"text"===e.type&&e.text);e&&"text"in e&&e.text&&(i.firstTitleQuestion=stripTransportMetadata(e.text).trim())}const d=buildResolutionPlan(a);if(d.refs.length>0)try{const e=await resolveResources(d,this.writeObs);a=applyResolutions(a,e)}catch(e){this.logger.warn?.(`[bridge-core] Resource pre-resolution failed: ${e instanceof Error?e.message:e}`)}let c;if(this.beeosFileResolver){const e=this.beeosFileResolver.buildResolutionPlan(a);if(e.refs.length>0)try{c=await this.beeosFileResolver.resolveMetadataForPrompt(e)}catch(e){this.logger.warn?.(`[bridge-core] BeeOS file resolution failed: ${e instanceof Error?e.message:e}`)}}const m=toGatewayPrompt(a,{beeosFileResolutionsByUri:c});if("error"in m)return this.releaseSessionWriteGate(i),void this.transport.sendAcpError(e.id,Errors.PromptConvertFailed,{message:m.error});const{message:u,attachments:p}=m,h=resolveStandaloneSlashCommandName(a,u)??resolveSlashCmd(a,u),g="string"==typeof h;if(h&&this.handleSlashCommand){const t=this;if(await(async()=>{try{return await t.handleSlashCommand(h)}catch(e){return t.logger.warn?.(`[bridge-core] /${h} handler failed: ${e instanceof Error?e.message:e}`),!1}})())return this.emitPromptAsUserSessionUpdates(i,a,resolveTurnId(i)),this.transport.sendSessionUpdate(r,{sessionUpdate:"agent_message_chunk",content:{type:"text",text:`Loading ${h}...`},timestamp:(new Date).toISOString()},{turnId:resolveTurnId(i)}),this.transport.sendResult(e.id,{stopReason:"end_turn"}),void this.releaseSessionWriteGate(i)}const y="chat.send";let f=g?u:withUserMessagePrefix(u);if(this.config.canvasEnabled&&!g&&!0===i.canvasActive&&!i.channelType){const e=this.getCanvasState?.();if(e){const t=e.resolveCanvasForSession(i.gatewaySessionKey);f=`${buildActiveSurfacesContext(e,t)??"[Canvas] Canvas panel is available. Use render_ui for structured/visual content."}\n\n${f}`}const t=i.referencedSurfaceId;t&&(f=`${CANVAS_UPDATE_HINT}\n[IMPORTANT: The user selected canvas surface "${t}" for editing. You MUST call render_ui with id="${t}" to UPDATE this existing surface. Do NOT create a new surface with a different id.]\n${f}`,i.referencedSurfaceId=void 0)}if(!g){const t=parseMentionsParam(e.params?.mentions);if(t.length>0){i.activeMentions=t;f=`${buildMentionInstruction(t)}\n${f}`,this.logger.debug?.(`[bridge-core] delegation mention instruction injected for ${t.length} agent(s)`)}else i.activeMentions=void 0}if(g&&this.logger.info?.(`[bridge-core] slash command passthrough sessionId=${r} command=/${h}`),this.hasActiveRun(i)){const t=this.sessionMutationCoordinator.gateSnapshot(i.gatewaySessionKey);return void this.sendSessionBusy(i,e.id,"active_run_after_prepare",t.queueDepth)}const w=this.sessionMutationCoordinator.tryStartInteractive({sessionKey:i.gatewaySessionKey,label:"session/prompt"},"acp");if(!w.ok)return void this.sendSessionBusy(i,e.id,w.reason,w.queueDepth);i.writeGateToken=w.token,this.state.resetForNewRun(i,r,this.transport),i.pendingPromptId=e.id,i.gatewayMethod=y;const I=resolveTurnId(i);this.emitPromptAsUserSessionUpdates(i,a,I),this.transport.sendSessionUpdate(r,{sessionUpdate:"agent_message_chunk",content:{type:"text",text:""},timestamp:(new Date).toISOString()},I?{turnId:I}:void 0);const S=extractCronJobId(u);S&&(i.cronJobId=S);const v=p.length>0?p.map(e=>({type:e.type,content:e.content,...e.mimeType?{mimeType:e.mimeType}:{},...e.fileName?{fileName:e.fileName}:{}})):void 0,C=`req_${randomUUID().replace(/-/g,"")}`,R={sessionKey:i.gatewaySessionKey,message:f,deliver:!1,idempotencyKey:randomUUID()};v?.length&&(R.attachments=v);if(!o.sendFrame({type:"req",id:C,method:y,params:R}))return i.pendingPromptId=void 0,i.assistantMq?.close(),i.assistantMq=void 0,i.pendingUserTurnUpdates=void 0,this.releaseSessionWriteGate(i),void this.transport.sendAcpError(e.id,Errors.SendFailed,{message:"failed to send prompt to gateway"});i.gatewayRequestId=C,this.state.promptsByGatewayRequestId.set(C,i)}replayUserTurnAsChunks(e,t,s,i,n,r,o){const a=splitUserReplayBlocks(s),l=r||void 0!==o?{...r?{turnId:r}:{},...void 0!==o?{seq:o}:{}}:void 0;for(const s of a){let r;if("text"===s.type){if(!s.text)continue;r={type:"text",text:s.text}}else r="resource_link"===s.type?{type:"resource_link",uri:s.uri??"",...s.fileName?{name:s.fileName}:{},...s.mimeType?{mimeType:s.mimeType}:{}}:{type:s.type,...s.mimeType?{mimeType:s.mimeType}:{},...s.fileName?{fileName:s.fileName}:{}};e.sendSessionUpdate(t,{sessionUpdate:"user_message_chunk",content:r,...i?{metadata:i}:{},...n?{timestamp:n}:{}},l)}}async replayAssistantTurnAsStream(e,t,s,i){const n=e=>{if(i||void 0!==e)return{...i?{turnId:i}:{},...void 0!==e?{seq:e}:{}}},r=new Set,o=(s,i,o)=>{if(!s?.length)return;const a=n(o);for(const n of s)n.uri&&!r.has(n.uri)&&(r.add(n.uri),e.sendSessionUpdate(t,{sessionUpdate:"agent_message_chunk",content:{type:"resource_link",uri:n.uri,name:n.name??"file",...n.mimeType?{mimeType:n.mimeType}:{}},...i?{timestamp:i}:{}},a))},a=n(s._seq);if(!s.sourceMessages?.length){const i={sessionUpdate:"history_turn",role:s.role,content:s.content,reasoning:s.reasoning,toolCalls:s.toolCalls,...s.timestamp?{timestamp:s.timestamp}:{}};return s.metadata&&(i.metadata=s.metadata),e.sendSessionUpdate(t,i,a),void o(s.files,s.timestamp,s._seq)}for(const i of s.sourceMessages){const r=i.updateType??"agent_message_chunk",a=n(i._seq);switch(r){case"agent_message_chunk":e.sendSessionUpdate(t,{sessionUpdate:"agent_message_chunk",content:{type:"text",text:i.content},...i.timestamp?{timestamp:i.timestamp}:{},...i.metadata?{metadata:i.metadata}:{}},a);break;case"agent_thought_chunk":e.sendSessionUpdate(t,{sessionUpdate:"agent_thought_chunk",content:{type:"text",text:i.content},...i.timestamp?{timestamp:i.timestamp}:{}},a);break;case"tool_call":{const n=i.toolCallId??"",r=s.toolCalls?.find(e=>e.id===n),o=i.toolName??"tool",l=i.content??"";let d={};if(l)try{d=JSON.parse(l)}catch{}e.sendSessionUpdate(t,buildToolCallPayload({toolCallId:n,toolName:o,args:d,kind:r?.kind,status:r?.status??"in_progress",timestamp:i.timestamp}),a);break}case"tool_call_update":{const n=i.toolCallId??"",r=s.toolCalls?.find(e=>e.id===n),l=i.toolName??r?.name??"tool",d=i.content??r?.output??"",c=await this.resolveReplayResourceLinks(n,l,i.files);e.sendSessionUpdate(t,buildToolCallUpdatePayload({toolCallId:n,toolName:l,outputText:d,kind:r?.kind,status:r?.status??"completed",resourceLinks:c,timestamp:i.timestamp}),a),o(c,i.timestamp,i._seq);break}}}o(s.files,s.timestamp,s._seq)}async resolveReplayResourceLinks(e,t,s){let i=s?.map(e=>({uri:e.uri,name:e.name??"file",...e.mimeType?{mimeType:e.mimeType}:{}}));if(i?.length)return i;if(i=await readEnrichmentCache(e).catch(()=>{}),i?.length)return i;if("beeos_upload_file"!==t)return;const n=await readUploadToolResult(e).catch(()=>{});if(!n)return;const r=buildToolResultSessionContentBlocks(t,n).filter(e=>"resource_link"===e.content?.type&&e.content.uri).map(e=>({uri:e.content.uri,name:e.content.name??"file",...e.content.mimeType?{mimeType:e.content.mimeType}:{}}));return r.length>0?r:void 0}emitPromptAsUserSessionUpdates(e,t,s){const i=(new Date).toISOString(),n=s?{turnId:s}:void 0;for(const r of t){const t=this.toUserSessionUpdateContent(r);if(!t)continue;const o={sessionUpdate:"user_message_chunk",content:t,timestamp:i};s&&void 0===o._turnId&&(o._turnId=s),this.transport.sendSessionUpdate(e.sessionId,o,n),e.pendingUserTurnUpdates?.push(o)}}toUserSessionUpdateContent(e){if("text"===e.type){if(!e.text)return;return{type:"text",text:e.text}}if("image"===e.type){const t=e.mimeType??e.mime_type??"application/octet-stream",s=e.fileName??e.file_name??e.name;if(!e.data&&!e.uri)return;return{type:"image",...e.data?{data:e.data}:{},...e.uri?{uri:e.uri}:{},...t?{mimeType:t}:{},...s?{fileName:s}:{}}}if("resource_link"===e.type){if(!e.uri)return;return{type:"resource_link",uri:e.uri,...e.title?{title:e.title}:{},...e.name?{name:e.name}:{},...e.mimeType??e.mime_type?{mimeType:e.mimeType??e.mime_type}:{}}}if("file"===e.type){const t=e.mimeType??e.mime_type??"application/octet-stream",s=e.fileName??e.file_name??e.filename??e.name;if(!e.data&&!e.text&&!e.uri)return;return{type:"file",...e.data?{data:e.data}:{},...e.text?{text:e.text}:{},...e.uri?{uri:e.uri}:{},...t?{mimeType:t}:{},...s?{fileName:s}:{}}}if("resource"===e.type){const t=e.resource??{},s=t.uri??e.uri,i=t.mimeType??t.mime_type??e.mimeType??e.mime_type,n=t.text??e.text,r=t.data??e.data,o=t.fileName??t.file_name??t.filename??t.name??e.fileName??e.file_name??e.filename??e.name;if(!s&&!n&&!r)return;return{type:"resource",resource:{...s?{uri:s}:{},...i?{mimeType:i}:{},...n?{text:n}:{},...r?{data:r}:{},...o?{fileName:o}:{}}}}}async handleSessionCancel(e,t){const s=t&&"object"==typeof t&&"sessionId"in t?t.sessionId:void 0;if(!s)return void(void 0!==e&&this.transport.sendAcpError(e,Errors.MissingParam,{param:"sessionId"}));const i=this.state.sessions.get(s);if(!i)return void(void 0!==e&&this.transport.sendAcpError(e,Errors.SessionNotFound));const n=i.activeRunBinding??this.observeActiveRun(i);if(!n){const t=this.sessionMutationCoordinator.snapshot(i.gatewaySessionKey);if("exclusive"===t?.attachmentPolicy&&void 0!==e){const t=this.sessionMutationCoordinator.gateSnapshot(i.gatewaySessionKey);return void this.sendSessionBusy(i,e,"exclusive_session_mutation",t.queueDepth)}return void(void 0!==e&&this.transport.sendResult(e,{}))}const r=await this.runtimeLeases.claim(this.createOperationId());let o=!1;try{const s=t&&"object"==typeof t&&"reason"in t&&"string"==typeof t.reason?t.reason:void 0,i={targetMessageId:n.targetMessageId,...s?{reason:s}:{}},a={name:"session.cancel",params:i,context:{...this.canonicalContextBase(i,r.operationId,r.runtimeEpoch),scope:"conversation",historyGeneration:n.historyGeneration,conversation:{runtimeConversationUid:n.runtimeConversationUid,runtimeSessionKey:n.runtimeSessionKey}}},l=this.runtimeCommands.dispatch(a,this.acpExecutionAttempt(a));o=!0,await l.completion,void 0!==e&&this.transport.sendResult(e,{})}catch(t){void 0!==e&&this.sendRuntimeCommandError(e,t)}finally{o||await this.runtimeLeases.release(r)}}hasActiveRun(e){return!e.promptDone&&(void 0!==e.pendingPromptId||void 0!==e.currentRunId||void 0!==e.gatewayRequestId)}observeLifecycle(e,t,s){observeLifecyclePromise(e,this.logger,{component:"bridge-core",action:t,...s?{sessionId:s.sessionId,gatewaySessionKey:s.gatewaySessionKey}:{}})}async handleCancellationResult(e,t){return t.ok?(this.observeLifecycle(t.terminalObservation.then(t=>{if(!t.ok)return this.publishCancellationRecovery(e)}),"cancel_terminal_observation",e),"accepted"):"recovery"in t?(await this.publishCancellationRecovery(e),"recovery_required"):"terminal_already_observed"}async publishCancellationRecovery(e){await this.terminateRunOutput(e,{kind:"failed",mode:"interrupted",message:SESSION_RECOVERY_REQUIRED_MESSAGE,errorCode:SESSION_RECOVERY_REQUIRED_CODE})}releaseSessionWriteGate(e){if(!e.writeGateToken)return;const t=e.writeGateToken;e.writeGateToken=void 0,this.sessionMutationCoordinator.releaseBeforeRuntime(t)}logRuntimeFailure(e,t){this.logger.warn?.(`[bridge-core] runtime_failure_classified session_id=${e.sessionId} gateway_session_key=${e.gatewaySessionKey} run_id=${e.currentRunId??""} gateway_request_id=${e.gatewayRequestId??""} error_code=${t.errorCode} stop_reason=${t.stopReason} source=${t.source} runtime=${t.runtimeKind}`)}async commitRunOutputTerminal(e,t){const s=this.state.sessions.get(e.sessionId);if(!s||s.gatewaySessionKey!==e.gatewaySessionKey)throw new Error(`run output lifecycle session unavailable: ${e.sessionId}`);await this.commitRunOutputTerminalForSession(s,t)}async commitRunOutputTerminalForSession(e,t){if("complete"!==t.kind&&"cancelled"!==t.kind)return"interrupted"===t.mode?(t.classification&&this.logRuntimeFailure(e,t.classification),void await this.endRun(e,{kind:"interrupted",stopReason:t.classification?.stopReason??"interrupted",message:t.message,errorCode:t.errorCode})):void await this.endRun(e,{kind:"error",code:t.code,message:t.message});await this.endRun(e,{kind:"complete",stopReason:t.stopReason})}async terminateRunOutput(e,t){const s=resolveTurnId(e);s&&this.runOutputRouter.hasRoute(s)?await this.runOutputRouter.terminal(s,t):await this.commitRunOutputTerminalForSession(e,t)}sendSessionBusy(e,t,s,i){this.logger.warn?.(`[bridge-core] session_write_gate_busy session_id=${e.sessionId} gateway_session_key=${e.gatewaySessionKey} reason=${s} queue_depth=${i}`),this.transport.sendError(t,Errors.AgentError.rpcCode,"Agent is busy",{code:"session_busy",reason:s,queueDepth:i}),this.transport.sendSessionUpdate(e.sessionId,{sessionUpdate:"stream_complete",stopReason:"interrupted",error:INTERRUPTED_PUBLIC_MESSAGE,errorCode:"session_busy"},{turnId:`req:${String(t)}`})}async endRun(e,t){if(e.promptDone||e.mutationTerminalCommitting)return this.logger.debug?.(`[bridge-core] endRun skipped (promptDone=true) sessionId=${e.sessionId} outcome=${t.kind}`),!1;if(void 0===e.pendingPromptId&&void 0===e.currentRunId&&void 0===e.gatewayRequestId)return this.logger.debug?.(`[bridge-core] endRun skipped (no pendingPromptId and no currentRunId) sessionId=${e.sessionId} outcome=${t.kind}`),!1;e.mutationTerminalCommitting=!0,this.state.clearStreamIdleTimer(e);const s=e.writeGateToken,i=e.activeRunBinding;let n,r,o,a,l=t;if(s){const e="complete"===l.kind?"cancelled"===l.stopReason?"cancelled":"completed":"interrupted"===l.kind&&"timeout"===l.stopReason?"timeout":"error",t=await this.sessionMutationCoordinator.converge(s,e);t.ok?n=t.permit:l={kind:"interrupted",stopReason:"interrupted",message:SESSION_RECOVERY_REQUIRED_MESSAGE,errorCode:SESSION_RECOVERY_REQUIRED_CODE}}try{const t=resolveTurnId(e),s=t?{turnId:t}:void 0;e.promptDone=!0;const i=e.currentRunId;if("complete"===l.kind){if(i&&this.onRunComplete){const t=e.agentAssistantTextSoFar??e.chatAssistantTextSoFar??"";this.onRunComplete(i,t,l.stopReason)}if(void 0!==e.pendingPromptId&&this.transport.sendResult(e.pendingPromptId,{stopReason:l.stopReason}),this.transport.sendSessionUpdate(e.sessionId,{sessionUpdate:"stream_complete",stopReason:l.stopReason},s),e.firstTitleQuestion&&"manual"!==e.titleSource&&"generated"!==e.titleSource&&"cancelled"!==l.stopReason){const t=e.firstTitleQuestion;e.firstTitleQuestion=void 0,this.generateTitleFromQA(e,e.sessionId,t,e.agentAssistantTextSoFar??e.chatAssistantTextSoFar??"")}}else"interrupted"===l.kind?(i&&this.onRunFailed&&this.onRunFailed(i,l.message),void 0!==e.pendingPromptId&&this.transport.sendError(e.pendingPromptId,Errors.AgentError.rpcCode,l.message,{code:l.errorCode??"interrupted",stopReason:l.stopReason}),this.transport.sendSessionUpdate(e.sessionId,{sessionUpdate:"stream_complete",stopReason:l.stopReason,error:l.message,...l.errorCode?{errorCode:l.errorCode}:{}},s)):(i&&this.onRunFailed&&this.onRunFailed(i,l.message),void 0!==e.pendingPromptId&&this.transport.sendError(e.pendingPromptId,l.code,l.message),this.transport.sendSessionUpdate(e.sessionId,{sessionUpdate:"stream_complete",stopReason:"error",error:l.message},s))}catch(e){r=e}try{this.cleanupPrompt(e)}catch(e){o=e}finally{e.writeGateToken=void 0,e.activeRunBinding=void 0,n&&this.sessionMutationCoordinator.release(n)}if(i){const e="complete"===l.kind?"cancelled"===l.stopReason?"cancelled":"completed":"interrupted"===l.kind&&"cancelled"===l.stopReason?"cancelled":"failed";this.activeRuns.markTerminal(i,e)}try{this.onRunEnded?.(e,l)}catch(e){a=e}if(r)throw r;if(o)throw o;if(a)throw a;return!0}adoptBackgroundMutation(e,t){const s=this.sessionMutationCoordinator.attachOrStartExternal({sessionKey:e.gatewaySessionKey,label:"background_run"},"acp",t);return s.ok?("owner"===s.ownership&&(e.writeGateToken=s.token),s):(this.logger.error?.(`[bridge-core] background mutation rejected sessionKey=${e.gatewaySessionKey} runId=${t} reason=${s.reason}`),s)}cleanupPrompt(e){this.state.clearStreamIdleTimer(e);const t=e.currentRunId;if(t){this.state.promptsByRunId.delete(t),this.state.runIdToCronJobId.delete(t),this.state.markRunCompleted(t);const e=`tc_${t}_`;for(const[t,s]of this.state.toolCallIdMap)s.startsWith(e)&&this.state.toolCallIdMap.delete(t)}e.gatewayRequestId&&this.state.promptsByGatewayRequestId.delete(e.gatewayRequestId),e.pendingPromptId=void 0,e.promptDone=void 0,e.currentRunId=void 0,e.gatewayRequestId=void 0,e.assistantStreamStartedAt=void 0,e.gatewayMethod=void 0,e.mutationTerminalCommitting=void 0,e.assistantMq?.close(),e.assistantMq=void 0,e.pendingUserTurnUpdates=void 0}async completePrompt(e,t){"cancelled"!==t?await this.terminateRunOutput(e,{kind:"complete",stopReason:t}):await this.terminateRunOutput(e,{kind:"cancelled",stopReason:"cancelled"})}async interruptPrompt(e,t){await this.terminateRunOutput(e,{kind:"failed",mode:"interrupted",message:t.publicMessage,errorCode:t.errorCode,classification:t})}async failPrompt(e,t,s,i){await this.terminateRunOutput(e,errorRunOutput(t,s,i))}cleanupAllPendingPrompts(){for(const e of this.state.sessions.values())e.writeGateToken?(this.sessionMutationCoordinator.requireRecovery(e.writeGateToken,"gateway_disconnected","disconnect"),this.observeLifecycle(this.terminateRunOutput(e,{kind:"failed",mode:"interrupted",message:SESSION_RECOVERY_REQUIRED_MESSAGE,errorCode:SESSION_RECOVERY_REQUIRED_CODE}),"gateway_disconnect_recovery",e)):this.observeLifecycle(this.interruptPrompt(e,classifyAgentRuntimeFailure({raw:Errors.GatewayDisconnected.message,source:"live_gateway",runtimeKind:"openclaw"})),"gateway_disconnect_interrupt",e)}handleGatewayResponse(e){const t=e.id;if(!t)return;const s=this.state.promptsByGatewayRequestId.get(t);if(!s||s.promptDone)return;if(!e.ok){const i=e.error,n=i?.message??"gateway returned error";this.logger.warn?.(`[bridge-core] handleGatewayResponse: gateway rejected prompt gatewayRequestId=${t} error=${n}`);const r=classifyAgentRuntimeFailure({raw:n,source:"live_gateway",runtimeKind:"openclaw"});return void("interrupted"===r.kind?this.observeLifecycle(this.interruptPrompt(s,r),"gateway_response_interrupt",s):(this.logRuntimeFailure(s,r),this.observeLifecycle(this.failPrompt(s,Errors.SendFailed.rpcCode,r.publicMessage,r),"gateway_response_failure",s)))}const i=e.payload,n=i?.runId;if(n)s.currentRunId=n,this.sessionMutationCoordinator.bindRun(s.writeGateToken,n),this.observeActiveRun(s),s.assistantStreamStartedAt=Date.now(),this.state.promptsByRunId.set(n,s),openRunOutputRoute(this.runOutputRouter,s,n,["acp"]),this.logger.debug?.(`[bridge-core] handleGatewayResponse: bound runId=${n} gatewayRequestId=${t} sessionId=${s.sessionId}`);else{const e=resolveTurnId(s);e&&openRunOutputRoute(this.runOutputRouter,s,e,["acp"]),this.logger.debug?.(`[bridge-core] handleGatewayResponse: no runId in payload gatewayRequestId=${t} sessionId=${s.sessionId}`)}}async handleCronRequest(e){const t=this.getGateway();if(!t?.isConnected)return void this.transport.sendAcpError(e.id,Errors.GatewayNotConnected);const s=e.method.replace("/","."),i={...e.params??{}};["cron.add","cron.update","cron.remove","cron.run","cron.runs"].includes(s)&&(i.__beeosCronIngress="acp"),"cron.update"===s&&(i.patch=i.patch&&"object"==typeof i.patch?{...i.patch}:{});try{const t=await this.dispatchLegacyCronCapability(s,i),n=await this.mapCronResultForAcp(s,t);this.transport.sendResult(e.id,n??{}),"cron.add"!==s&&"cron.update"!==s&&"cron.remove"!==s||this.refreshCronIsolatedSessionKeys()}catch(t){const i=t instanceof Error?t.message:"cron request failed";this.logger.warn?.(`[bridge-core] ${s} failed: ${i}`),this.transport.sendAcpError(e.id,Errors.SendFailed,{message:i})}}async mapCronResultForAcp(e,t){if(!t||"object"!=typeof t)return t;const s=t;await this.cronIdentities.snapshot();const i=e=>{if(!e||"object"!=typeof e)return e;const t=e,s=t.id;if("string"!=typeof s)return e;const i=this.cronIdentities.localId(s)??s,n=this.cronChannelCache.get(i),r=n?encodeBeeosChannelTag("string"==typeof t.description?t.description:void 0,n):t.description;return{...t,id:i,...void 0===r?{}:{description:r}}};if(Array.isArray(s.jobs))return{...s,jobs:s.jobs.map(i)};if(s.jobs&&"object"==typeof s.jobs){const e=s.jobs,t={};for(const[s,n]of Object.entries(e))t[s]=i(n);return{...s,jobs:t}}return Array.isArray(s.runs)?{...s,runs:s.runs.map(e=>{if(!e||"object"!=typeof e)return e;const t=e,s="string"==typeof t.jobId?this.cronIdentities.localId(t.jobId)??t.jobId:t.jobId;return{...t,jobId:s}})}:"cron.add"===e||"cron.update"===e?i(t):t}async allocateCronChannel(e,t){if(!this.agentFetch||!this.agentGatewayUrl)return void this.logger.debug?.("[bridge-core] agentFetch/agentGatewayUrl unavailable, skip channel alloc");const s=this.getAuthoritativeInstanceId?.()?.trim();if(!s?.startsWith("inst_"))return void this.logger.warn?.("[bridge-core] no authoritative inst_* instance id resolved yet, skip channel alloc");const i=this.resolvePlatformAgentId(t)?.trim();if(!i)return void this.logger.warn?.(`[bridge-core] no platform agent id for runtimeLocalKey=${t}, skip channel alloc`);const n={source:"cron",job_name:e,protocol:"beeos-conversation",target_agent_id:i,title:e};try{const e=`${this.agentGatewayUrl}/api/v1/channels`,t=await this.agentFetch(e,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({participants:[s,"service:cron"],metadata:n})});if(!t.ok)return void this.logger.warn?.(`[bridge-core] channel alloc failed: ${t.status}`);return(await t.json()).id}catch(e){return void this.logger.warn?.(`[bridge-core] channel alloc error: ${e instanceof Error?e.message:e}`)}}setCronChannelCache(e,t){if(this.cronChannelCache.size>=AcpGatewayBridgeCore.CRON_CHANNEL_CACHE_MAX){const e=this.cronChannelCache.keys().next().value;void 0!==e&&this.cronChannelCache.delete(e)}this.cronChannelCache.set(e,t)}async refreshCronIsolatedSessionKeys(){const e=this.getGateway();if(e?.isConnected)try{const t=await e.request("cron.list",{}),s=t?.jobs;if(!Array.isArray(s))return;const i=new Set,n=[];for(const e of s){if(!e||"object"!=typeof e)continue;const t=e;if("string"==typeof t.id&&n.push(t.id),"isolated"!==t.sessionTarget)continue;const s=t.sessionKey;"string"==typeof s&&s.length>0&&i.add(s)}this.cronIsolatedSessionKeys=i,await this.cronIdentities.reconcile(n)}catch(e){this.logger.debug?.(`[bridge-core] refreshCronIsolatedSessionKeys failed: ${e instanceof Error?e.message:e}`)}}}async function fetchWithRetry(e,t={}){const s=t.maxRetries??3;let i;for(let n=0;n<=s;n++){const r=await fetch(e,{signal:AbortSignal.timeout(12e4),redirect:"follow"});if(r.ok)return Buffer.from(await r.arrayBuffer());if(429===r.status||r.status>=500){const o=Number(r.headers.get("retry-after"))||0,a=1e3*Math.max(o,2**n);if(i=new Error(`HTTP ${r.status} from ${e}`),n<s){t.logger?.info?.(`[bridge-core] Download ${r.status}, retry ${n+1}/${s} in ${(a/1e3).toFixed(0)}s`),await new Promise(e=>setTimeout(e,a));continue}}throw new Error(`Failed to download ${e}: HTTP ${r.status}`)}throw i??new Error(`Download failed after retries: ${e}`)}const SKILL_ROOT_MARKERS=["SKILL.md","skill.md","SKILL.MD"];function normalizeSkillArtifactPath(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 parseSkillZip(e){if(e.byteLength>MAX_ZIP_BYTES)throw new RuntimeCommandError("INVALID_PARAMS","Skill archive too large");const t=new AdmZip(e).getEntries().filter(e=>!e.isDirectory).map(e=>({name:normalizeSkillArtifactPath(e.entryName),bytes:e.getData()})).filter(e=>!e.name.split("/").includes("__MACOSX"));if(0===t.length)throw new RuntimeCommandError("INVALID_PARAMS","Skill archive is empty");const s=1===new Set(t.map(e=>e.name.split("/")[0])).size&&t.every(e=>e.name.includes("/")),i=new Map;for(const e of t){const t=s?e.name.slice(e.name.indexOf("/")+1):e.name;if(!t||i.has(t))throw new RuntimeCommandError("INVALID_PARAMS",`Duplicate skill path: ${t}`);i.set(t,e.bytes)}return i}async function resolveSkillRoot(e){for(const t of SKILL_ROOT_MARKERS)try{return await stat(join(e,t)),e}catch{}const t=(await readdir(e,{withFileTypes:!0})).filter(e=>e.isDirectory()&&!e.name.startsWith(".")&&"__MACOSX"!==e.name);if(1===t.length){const s=join(e,t[0].name);for(const e of SKILL_ROOT_MARKERS)try{return await stat(join(s,e)),s}catch{}return s}return e}async function extractZipToDir(e,t){const s=new AdmZip(e),i=join(t,".beeos-extract-tmp-"+Date.now());try{await mkdir(i,{recursive:!0}),s.extractAllTo(i,!0);const e=await resolveSkillRoot(i),n=await readdir(e,{withFileTypes:!0});let r=0;for(const s of n){if(s.name.startsWith(".beeos-extract-tmp")||"__MACOSX"===s.name)continue;const i=join(e,s.name),n=join(t,s.name);await rename(i,n).catch(async()=>{await cp(i,n,{recursive:!0})}),r++}if(0===r)throw new Error("Zip archive contained no extractable files")}finally{await rm(i,{recursive:!0,force:!0}).catch(()=>{})}}const VALID_SLUG_PATTERN=/^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/i,NON_ASCII_PATTERN=/[^\x00-\x7F]/;function validateSkillSlug(e){const t=e.trim();if(!t||t.includes("/")||t.includes("\\")||t.includes(".."))throw new Error(`Invalid skill slug: ${e}`);if(NON_ASCII_PATTERN.test(t)||!VALID_SLUG_PATTERN.test(t))throw new Error(`Invalid skill slug: ${e}`);return t}function safeDirName(e){const t=e.trim();return t?t.replaceAll("/","__").replaceAll("\\","__"):t}function extractMcpCredentialMaterial(e){const t=mcpStringMap(e.headers,"MCP headers"),s=mcpStringMap(e.env,"MCP environment");if(!(t&&0!==Object.keys(t).length||s&&0!==Object.keys(s).length))return;const i=e.transport??("string"==typeof e.command?"stdio":"string"==typeof e.url?"streamable-http":void 0);if("stdio"===i&&t&&Object.keys(t).length)throw new Error("MCP stdio config cannot include headers");if("stdio"!==i&&s&&Object.keys(s).length)throw new Error("MCP HTTP config cannot include env");return{...t&&Object.keys(t).length?{headers:t}:{},...s&&Object.keys(s).length?{env:s}:{}}}function mcpStringMap(e,t){if(void 0!==e){if(!e||"object"!=typeof e||Array.isArray(e)||Object.entries(e).some(([e,t])=>!e||/[\u0000-\u001f\u007f]/.test(e)||"string"!=typeof t))throw new Error(`${t} invalid`);return{...e}}}function requireCredentialReferencePort(e){if(!e)throw new Error("MCP credential reference vault is unavailable");return e}function resolveSkillInstallDir(e,t){const s=safeDirName(t),i=join(e,s),n=resolve(e),r=resolve(i),o=relative(n,r);if(!o||".."===o||o.startsWith(`..${sep}`)||path.isAbsolute(o))throw new Error("Invalid skill target path");return i}
|
|
1
|
+
import{randomUUID,createHash}from"node:crypto";import{mkdir,readFile,rm,stat,readdir,rename,cp}from"node:fs/promises";import path from"node:path";import{join,resolve,relative,sep}from"node:path";import{homedir}from"node:os";import AdmZip from"adm-zip";import{classifyCatalogSessionKey,prepareOpenClawToolCatalog}from"../runtime/prepare-openclaw-tool-catalog.js";import{sanitizePromptPayload}from"../message-sanitization/prompt-payload-sanitizer.js";import{previewSanitizedText,stripTransportMetadata}from"../message-sanitization/transport-metadata-sanitizer.js";import{isHeartbeatPromptText,isCronPromptText,extractCronJobId,classifyHeartbeatFilter}from"../message-sanitization/message-classifier.js";import{createCronIdentityStore,createInMemoryCronIdentityStore}from"../cron-identities.js";import{CANVAS_UPDATE_HINT,buildActiveSurfacesContext}from"../canvas/tools.js";import{parseMentionsParam,buildMentionInstruction}from"../delegation/mention-resolver.js";import{EXTERNAL_PROTOCOL_TAGS}from"./external-protocol-tags.js";import{extractPromptBlocks,toGatewayPrompt,resolveSlashCommandName as resolveSlashCmd}from"./prompt-converter.js";import{extractBeeosUserTextFromHistoryContent}from"./beeos-user-envelope.js";import{loadHistory,loadHistoryPage,groupIntoTurns,splitUserReplayBlocks}from"./history-replay.js";import{classifyHistoricalAssistantTurnCompletion}from"./history-turn-completion-policy.js";import{LocalSessionHistory}from"./local-session-history.js";import{reconcileMissingArtifacts}from"./missing-artifacts-reconciler.js";import{AcpRunOutputChannel}from"./acp-run-output-channel.js";import{buildResolutionPlan,resolveResources,applyResolutions}from"./prompt-resource-resolver.js";import{readEnrichmentCache,readUploadToolResult}from"../upload/result-cache.js";import{buildToolResultSessionContentBlocks}from"./tool-result-blocks.js";import{prefersChatFileArtifacts}from"./file-artifact-presenter.js";import{buildToolCallPayload,buildToolCallUpdatePayload}from"./tool-call-payload-builder.js";import{Errors}from"../errors/index.js";import{classifyAgentRuntimeFailure,INTERRUPTED_PUBLIC_MESSAGE}from"../runtime/agent-runtime-failure-classifier.js";import{generateTitleFromQA,isPlaceholderTitle}from"../runtime/conversation-title.js";import{SESSION_RECOVERY_REQUIRED_CODE,SESSION_RECOVERY_REQUIRED_MESSAGE}from"../runtime/session-mutation-coordinator.js";import{observeLifecyclePromise}from"../runtime/lifecycle-promise-observer.js";import{RunOutputRouter,errorRunOutput}from"../runtime/run-output-router.js";import{AgentMappingRegistry}from"../runtime/agent-mapping-registry.js";import{RuntimeSessionNewRejectedError}from"../runtime/runtime-rpc-consumer.js";import{normalizeRuntimeAgentLocalKey,OpenClawRuntimeCommandDispatcher,RuntimeCommandError}from"../runtime/runtime-command-dispatcher.js";import{FileRuntimeAgentIdentityRegistry}from"../runtime/runtime-agent-identity-registry.js";import{FileRuntimeLeasePort}from"../runtime/runtime-operation-lease.js";import{FileRuntimeCommandJournal,InMemoryRuntimeCommandJournal}from"../runtime/runtime-command-journal.js";import{OpenClawRuntimeCapabilities}from"../runtime/openclaw-runtime-capabilities.js";import{RuntimeCanvasService}from"../canvas/runtime-canvas-service.js";import{RuntimeSkillsService}from"../runtime/runtime-skills-service.js";import{RuntimeCronService,encodeBeeosChannelTag}from"../runtime/runtime-cron-service.js";export{BEEOS_CHANNEL_TAG_RE,decodeBeeosChannelId,encodeBeeosChannelTag,stripBeeosChannelTag}from"../runtime/runtime-cron-service.js";import{extractBeeosConversationIdFromSessionKey,openClawKeyMatchesConversation,openClawSessionKeysFromList,resolveLiveOpenClawSessionKey}from"../runtime/openclaw-session-key-resolution.js";import{LocalConversationHistoryBoundary,OpenClawGatewaySessionMetadataPort,OpenClawSessionMetadataService,RuntimeSessionControlService}from"../runtime/runtime-session-control-service.js";import{pageRuntimeSessions,parseRuntimeSessionListPage}from"../runtime/runtime-session-list-policy.js";import{assertTrustedCoreExecutionAttempt,createAcpCoreExecutionAttempt}from"../runtime/core-execution-attempt-context.js";import{CompositeActiveRunPort}from"../runtime/composite-active-run-port.js";import{resolveChannelFromSessionKey}from"../channel-detection.js";import{resolveTurnId,recordTurnIdForRole,recordToolCallTurnForUpdate}from"./gateway-events.js";import{emitToolResultProjectionForTurn}from"./tool-result-emitter.js";import{extractPersistedToolResultEnvelope,findPersistedToolResultEnvelopeByMessageId}from"./tool-result-transcript.js";import{projectPersistedToolResultForReplay}from"./tool-result-projection.js";import{openRunOutputRoute}from"./run-output-route.js";import{McpService}from"./mcp-service.js";import{McpDesiredStateStore,normalizeMcpPublicServerConfig}from"./mcp-desired-state-store.js";import{requireSecretFreeMcpServerName}from"./mcp-public-config-security.js";import{McpDesiredStateController}from"./mcp-desired-state-controller.js";import{CredentialReferenceRuntimeResolver}from"./mcp-credential-reference-port.js";import{ACP_PROTOCOL_VERSION}from"@beeos-ai/beeos-types/acp";import{canonicalizeJcs}from"@beeos-ai/beeos-types/runtime";import{MAX_ZIP_BYTES,parseAndValidateTemplatePackage,TemplatePackageError}from"./agent-template-package.js";const DEFAULT_AGENT_ID="main";export function normalizeAgentId(e){return normalizeRuntimeAgentLocalKey(e)}function narrowContentBlock(e){switch(e.type){case"text":return"string"==typeof e.text?{type:"text",text:e.text}:void 0;case"resource_link":return"string"==typeof e.uri?{type:"resource_link",uri:e.uri,...void 0!==e.name?{name:e.name}:{},...void 0!==e.mimeType?{mimeType:e.mimeType}:{}}:void 0;case"file":return{type:"file",...void 0!==e.text?{text:e.text}:{},...void 0!==e.uri?{uri:e.uri}:{},...void 0!==e.mimeType?{mimeType:e.mimeType}:{}};case"image":return{type:"image",...void 0!==e.uri?{uri:e.uri}:{},...void 0!==e.mimeType?{mimeType:e.mimeType}:{}};default:return}}const DEFAULT_MODE={id:"default",name:"Default",description:"Default agent mode"},USER_MESSAGE_PREFIX="User Message From BeeOS:",USER_MESSAGE_PREFIX_WITH_NEWLINE=`${USER_MESSAGE_PREFIX}\n`,SLASH_COMMAND_NAME_RE=/^[a-z0-9_-]+$/i,HISTORY_PAGE_DEFAULT_LIMIT=100,HISTORY_PAGE_MAX_LIMIT=1e3;function withUserMessagePrefix(e){return e.startsWith(USER_MESSAGE_PREFIX)?e:`${USER_MESSAGE_PREFIX_WITH_NEWLINE}${e}`}const BACKGROUND_SESSION_PATTERNS=[":cron:",":hook:","node-",...EXTERNAL_PROTOCOL_TAGS];function isBackgroundSessionKey(e){return BACKGROUND_SESSION_PATTERNS.some(t=>e.includes(t))}function isSystemDefaultSessionKey(e){const t=e.split(":");if(t.length<3||"agent"!==t[0])return!1;const s=t.slice(2).join(":");return"main"===s||s===t[1]}function cleanGatewayDerivedTitle(e){if(!e)return;let t=extractBeeosUserTextFromHistoryContent(e).text;return/^\[System\b/.test(t)?void 0:(t=t.trim(),t||void 0)}function cleanHistoryUserContent(e){return extractBeeosUserTextFromHistoryContent(e).text.trim()}function mergePersistedToolResultEnvelopes(e,t){if(e||t)return{toolName:e?.toolName||t?.toolName||"tool",...e?.toolCallId||t?.toolCallId?{toolCallId:e?.toolCallId??t?.toolCallId}:{},...e?.rawText??t?.rawText?{rawText:e?.rawText??t?.rawText}:{},...void 0!==e?.rawResult||void 0!==t?.rawResult?{rawResult:e?.rawResult??t?.rawResult}:{},...e?.timestamp||t?.timestamp?{timestamp:e?.timestamp??t?.timestamp}:{}}}function resolveStandaloneSlashCommandName(e,t){if(0===e.length)return;if(e.some(e=>"text"!==e.type))return;const s=t.trim();if(!s||s.includes("\n")||!s.startsWith("/"))return;const i=s.match(/^\/([^\s:]+)(?::|\s|$)/)?.[1]?.trim();return i&&!i.includes("/")&&SLASH_COMMAND_NAME_RE.test(i)?i.toLowerCase():void 0}class AcpRuntimeCommandResourceStore{fetchPackage;parsePackage;isValidationError;content=new Map;artifacts=new Map;mcpDefinitions=new Map;constructor(e,t,s){this.fetchPackage=e,this.parsePackage=t,this.isValidationError=s}registerContent(e){const t=`acp-content:sha256:${createHash("sha256").update(e).digest("hex")}`;return this.content.set(t,e),t}registerMcpDefinition(e,t){this.mcpDefinitions.set(JSON.stringify(e),structuredClone(t))}async resolveMcpDefinition(e){const t=this.mcpDefinitions.get(JSON.stringify(e));if(!t)throw new Error("ACP MCP definition was not verified for this request");return structuredClone(t)}async registerArtifact(e,t){try{const s=await this.fetchPackage(e);if(!s.ok)throw new TemplatePackageError(`failed to download package: HTTP ${s.status}`);const i=Buffer.from(await s.arrayBuffer());if(i.length>MAX_ZIP_BYTES)throw new TemplatePackageError(`package too large: ${i.length} bytes (max ${MAX_ZIP_BYTES})`);const n=createHash("sha256").update(i).digest("hex");if(t?.trim()&&t.trim().toLowerCase()!==n)throw new TemplatePackageError("package sha256 mismatch");const r=this.parsePackage(i),o=`acp-artifact:sha256:${n}`;return this.artifacts.set(o,r),{artifactRef:o,version:r.manifest?.version??"0.0.0",sha256:n,sizeBytes:String(i.length),source:"acp-verified"}}catch(e){if(e instanceof TemplatePackageError||this.isValidationError(e))throw new RuntimeCommandError("INVALID_PARAMS",`Invalid template package: ${e instanceof Error?e.message:String(e)}`);throw new RuntimeCommandError("EXECUTION_FAILED",`Apply template failed: ${e instanceof Error?e.message:String(e)}`)}}registerParsedArtifact(e){const t=createHash("sha256");for(const[s,i]of[...e.workspaceFiles].sort(([e],[t])=>e.localeCompare(t)))t.update(s).update("\0").update(i).update("\0");const s=t.digest("hex"),i=`acp-artifact:sha256:${s}`;this.artifacts.set(i,e);const n=[...e.workspaceFiles.values()].reduce((e,t)=>e+t.byteLength,0);return{artifactRef:i,version:e.manifest?.version??"0.0.0",sha256:s,sizeBytes:String(n),source:"acp-verified"}}async readContent(e){const t=this.content.get(e);if(void 0===t)throw new Error(`unknown contentRef: ${e}`);return t}async readVerifiedArtifact(e){const t=this.artifacts.get(e.artifactRef);if(!t)throw new Error(`unknown verified artifact: ${e.artifactRef}`);return t}}export const lateBoundRuntimeAgentProjectionPort=e=>({prepareCommittedMutation:async t=>{const s=e();if(!s)throw new Error("agent projection production is unavailable");return await s.prepareCommittedMutation(t)},applyCommittedMutation:async t=>{const s=e();if(!s)throw new Error("agent projection production is unavailable");return await s.applyCommittedMutation(t)},compactCommittedMutation:async t=>{const s=e();if(!s)throw new Error("agent projection production is unavailable");await s.compactCommittedMutation(t)}});function deriveSessionId(e,t){const s=createHash("sha256").update(`${t}:${e}`).digest("hex").substring(0,32);return`session:${s.slice(0,8)}-${s.slice(8,12)}-${s.slice(12,16)}-${s.slice(16,20)}-${s.slice(20,32)}`}function parseAgentIdFromKey(e){const t=e.split(":");return(t.length>=3&&"agent"===t[0]?t[1]:"")||"main"}function createSessionObject(e){const t=parseAgentIdFromKey(e.gatewayKey);return{sessionId:deriveSessionId(e.gatewayKey,e.instanceId),gatewaySessionKey:e.gatewayKey,agentId:t,platformAgentId:e.resolvePlatformAgentId?.(t),createdAt:Date.now(),chatReplayDedupKeys:new Set,cwd:e.cwd,title:e.title,channelType:resolveChannelFromSessionKey(e.gatewayKey,e.thirdPartyChannels)}}class HistoryUpdateRecorder{transport;updates=[];constructor(e){this.transport=e}sendSessionUpdate(e,t,s){const i=this.transport.materializeSessionUpdate(t,s);return null!==i&&this.updates.push(i),!0}}export class AcpGatewayBridgeCore{state;transport;config;logger;writeObs;getGateway;titles;thirdPartyChannels;beeosFileResolver;agentFetch;agentGatewayUrl;triggerAgentSync;sessionMutationCoordinator;runtimeCommands;runtimeAgentIdentities;runtimeLeases;runtimeCommandResources;localHistoryBoundary;activeRuns;markAgentProjectionDirty;projectionObservationTimeoutMs;cronChannelCache=new Map;static CRON_CHANNEL_CACHE_MAX=256;localSessionHistory;mcp;catalogPrepareInFlight=new Set;runtimeTerminalService;runOutputRouter;agentMappings;initialized=!1;get agentMapping(){return this.agentMappings.snapshot()}cronIsolatedSessionKeys=new Set;cronIdentities;cronReady;sessionPreloadInFlight=null;sessionPreloadInFlightGeneration=null;sessionPreloadReady=Promise.resolve();resolveSessionPreloadReady=null;rejectSessionPreloadReady=null;sessionPreloadGeneration=0;sessionPreloadBarrierActive=!1;sessionPreloadFailed=!1;async setAgentMapping(e){await this.agentMappings.replace(e)}async applyAgentMapping(e){for(const t of this.state.sessions.values())!t.platformAgentId&&t.agentId&&(t.platformAgentId=e.get(t.agentId)?.id);this.sessionPreloadFailed&&this.beginGatewaySessionPreload(),this.sessionPreloadBarrierActive&&await this.preloadSessionsFromGateway()}beginGatewaySessionPreload(){this.sessionPreloadBarrierActive&&this.sessionPreloadReady.catch(()=>{}),this.sessionPreloadGeneration+=1,this.sessionPreloadBarrierActive=!0,this.sessionPreloadFailed=!1,this.sessionPreloadReady=new Promise((e,t)=>{this.resolveSessionPreloadReady=e,this.rejectSessionPreloadReady=t}),this.sessionPreloadReady.catch(()=>{})}failGatewaySessionPreload(){this.sessionPreloadBarrierActive&&(this.sessionPreloadBarrierActive=!1,this.sessionPreloadFailed=!0,this.rejectSessionPreloadReady?.(new RuntimeCommandError("GATEWAY_NOT_CONNECTED","Gateway disconnected before session hydration completed")),this.resolveSessionPreloadReady=null,this.rejectSessionPreloadReady=null)}async waitForGatewaySessionPreload(){await this.sessionPreloadReady}onceAgentMappingReady(e){this.agentMappings.onceReady(e)}onAgentMappingUpdated(e){const t=this.agentMappings.snapshot();t&&t.size>0&&e(new Map(t)),this.agentMappings.subscribe(t=>e(new Map(t)))}markAgentSyncDone(){this.agentMappings.markSynchronized()}waitForAgentMapping(e=15e3){return this.agentMappings.waitForSynchronization(e)}handleTerminalRequest=null;handleTerminalNotification=null;onRunComplete=null;onRunFailed=null;onRunEnded=null;handleSlashCommand=null;onPromptStart=null;instanceId;getAuthoritativeInstanceId;mcpCredentialReferences;constructor(e){this.state=e.state,this.transport=e.transport,this.config=e.config,this.logger=e.logger,this.writeObs=e.writeObs,this.getGateway=e.getGateway,this.titles=e.titles,this.instanceId=e.instanceId||"unknown",this.getAuthoritativeInstanceId=e.getAuthoritativeInstanceId,this.mcpCredentialReferences=e.mcpCredentialReferences,this.thirdPartyChannels=e.thirdPartyChannels??new Set,this.beeosFileResolver=e.beeosFileResolver,this.agentFetch=e.agentFetch,this.agentGatewayUrl=e.agentGatewayUrl,this.triggerAgentSync=e.triggerAgentSync,this.markAgentProjectionDirty=e.markAgentProjectionDirty,this.projectionObservationTimeoutMs=e.projectionObservationTimeoutMs??5e3,this.sessionMutationCoordinator=e.sessionMutationCoordinator,this.agentMappings=e.agentMappings??new AgentMappingRegistry,this.agentMappings.subscribe(e=>this.applyAgentMapping(e)),this.runOutputRouter=e.runOutputRouter??new RunOutputRouter([new AcpRunOutputChannel({state:this.state,transport:this.transport})],{lifecycle:{terminal:(e,t)=>this.commitRunOutputTerminal(e,t)}}),this.runtimeAgentIdentities=e.runtimeAgentIdentities??new FileRuntimeAgentIdentityRegistry(join(process.env.OPENCLAW_HOME||join(homedir(),".openclaw"),"beeos","runtime-agent-identities.json")),this.runtimeLeases=e.runtimeLeases??new FileRuntimeLeasePort(join(process.env.OPENCLAW_HOME||join(homedir(),".openclaw"),"beeos","runtime-operation-leases.json")),this.runtimeCommandResources=e.runtimeCommandResources??new AcpRuntimeCommandResourceStore(e.agentFetch??((e,t)=>fetch(e,t)),e.runtimeTemplatePackageParser?.parse??parseAndValidateTemplatePackage,e.runtimeTemplatePackageParser?.isValidationError??(e=>e instanceof TemplatePackageError));const t=e.runtimeCommandJournal??(e.runtimeLeases||process.env.VITEST?new InMemoryRuntimeCommandJournal:new FileRuntimeCommandJournal(join(process.env.OPENCLAW_HOME||join(homedir(),".openclaw"),"beeos","runtime-command-journal.json"),void 0,void 0,{onMaintenanceError:e=>this.logger.error?.(`[runtime-journal] event=checkpoint_failed trigger=threshold error=${e instanceof Error?e.message:String(e)}`)}));this.localSessionHistory=new LocalSessionHistory({logger:this.logger}),this.cronIdentities=e.cronIdentities??(process.env.VITEST?createInMemoryCronIdentityStore():createCronIdentityStore(this.logger)),this.cronReady=this.cronIdentities.initialize(),this.cronReady.catch(()=>{});const s=e.mcpCredentialReferences?new CredentialReferenceRuntimeResolver(e.mcpCredentialReferences):void 0,i=e.mcpProjection?new McpDesiredStateController(new McpDesiredStateStore(e.mcpDesiredStatePath??join(process.env.OPENCLAW_HOME||join(homedir(),".openclaw"),"beeos","mcp-desired-state.json")),e.mcpProjection,s,e.mcpCredentialReferences,t,e.runtimeReceiptHistory,this.logger):void 0;this.mcp=new McpService({transport:this.transport,logger:this.logger,state:this.state,...i?{desiredState:i}:{}}),this.localHistoryBoundary=new LocalConversationHistoryBoundary(e=>{const t=this.state.sessions.get(e);t&&(t.chatReplayDedupKeys.clear(),this.onSessionCleared?.(t.sessionId,t.gatewaySessionKey))}),this.activeRuns=e.activeRuns??new CompositeActiveRunPort;const n=new RuntimeCronService({getGateway:e.getGateway,identities:this.cronIdentities,channels:{allocate:async(e,t)=>await this.allocateCronChannel(e,t),lookup:e=>this.cronChannelCache.get(e),remember:(e,t)=>this.setCronChannelCache(e,t),forget:e=>{this.cronChannelCache.delete(e)}}}),r=new RuntimeSessionControlService({convergeRun:async({runtimeSessionKey:e})=>{const t=this.state.sessionsByGatewayKey.get(e),s=(new Date).toISOString();if(!t||t.promptDone&&void 0===t.pendingPromptId&&void 0===t.currentRunId)return{state:"no_active_run",quiescence:{state:"verified",observedAt:s}};const i=this.getGateway();if(!i?.isConnected)return{state:"recovery_required",reason:"gateway_disconnected"};if(!t.writeGateToken||!t.currentRunId)return{state:"recovery_required",reason:"run_conflict",runtimeRunId:t.currentRunId??"unknown"};const n=t.currentRunId,r=await this.sessionMutationCoordinator.requestCancel(t.writeGateToken,i);if(!r.ok)return{state:"recovery_required",reason:"terminal_not_observed",runtimeRunId:n};const o=await r.terminalObservation;return o.ok?{state:"terminal_observed",runtimeRunId:n,abortObservation:r.acknowledged?{state:"target_acknowledged",aborted:!0,runIds:[n],targetRunAcknowledged:!0}:{state:"already_stopped",aborted:!1,runIds:[]},terminalState:"cancelled",quiescence:{state:"verified",observedAt:(new Date).toISOString()}}:{state:"recovery_required",reason:"quiescence_failed"===o.recovery.reason?"quiescence_failed":"quiescence_timeout"===o.recovery.reason?"quiescence_timeout":"terminal_not_observed",runtimeRunId:n}},reset:async e=>{const t=this.getGateway();if(!t?.isConnected)throw new RuntimeCommandError("GATEWAY_NOT_CONNECTED","Gateway not connected");await t.sessionsReset(e)}},new OpenClawSessionMetadataService(new OpenClawGatewaySessionMetadataPort(e.getGateway,(e,t)=>{const s=this.state.sessionsByGatewayKey.get(e);if(!s)throw new RuntimeCommandError("INVALID_PARAMS",`setModel projection requires a live local session for key=${e}`);void 0===t.modelOverrideId?delete s.modelOverrideId:s.modelOverrideId=t.modelOverrideId,s.effectiveModelId=t.effectiveModelId,void 0===t.modelProvider?delete s.modelProvider:s.modelProvider=t.modelProvider},{resolveLiveKey:e=>resolveLiveOpenClawSessionKey(e,this.state.sessions.values()),waitForHydration:async()=>await this.waitForGatewaySessionPreload(),reconcileMissingLiveKey:async(e,t)=>await this.reconcileMissingGatewaySession(e,t),materializeMissingModelKey:async(e,t)=>await this.materializeMissingModelSessionKey(e,t),registerMaterializedModelKey:e=>this.registerMaterializedModelSession(e)},async t=>{if(!e.declareModel)throw new Error("OpenClaw model configuration runtime is unavailable");return await e.declareModel(t)})),this.activeRuns),o=e.getAgentProjection?lateBoundRuntimeAgentProjectionPort(e.getAgentProjection):void 0,a=e.runtimeCommandDispatcher??new OpenClawRuntimeCommandDispatcher({declareModel:e.declareModel,getGateway:e.getGateway,resources:this.runtimeCommandResources,identities:this.runtimeAgentIdentities,leases:this.runtimeLeases,journal:t,logger:e.logger,...o?{projection:o}:{},...e.templateOriginReporter?{templateOriginReporter:e.templateOriginReporter}:{},...e.runExclusive?{runExclusive:e.runExclusive}:{}});this.runtimeCommands=a,a instanceof OpenClawRuntimeCommandDispatcher&&a.setCapabilities(new OpenClawRuntimeCapabilities({getGateway:e.getGateway,identities:this.runtimeAgentIdentities,sessions:r,skills:new RuntimeSkillsService({getGateway:e.getGateway,resources:this.runtimeCommandResources,getOpenclawHome:()=>this.getOpenclawHome()}),mcp:this.mcp,terminal:{execute:async(e,t)=>{if(!this.runtimeTerminalService)throw new RuntimeCommandError("INVALID_PARAMS","terminal canonical delegate is not configured");return await this.runtimeTerminalService.execute(e,t)}},canvas:new RuntimeCanvasService({apply:async(e,t)=>{await this.applyCanonicalCanvasControl(e,t)}}),cron:n})),this.transport.setSessionUpdateObserver((e,t,s)=>{const i=s?.turnId;if(!i)return;const n=this.state.sessions.get(e);n&&(recordTurnIdForRole(n,t,i),recordToolCallTurnForUpdate(n,t,i))})}setRuntimeTerminalService(e){this.runtimeTerminalService=e}setRuntimeReceiptHistoryPort(e){this.mcp.setRuntimeReceiptHistoryPort(e)}getRuntimeCommandDispatcher(){return this.runtimeCommands}async startMcp(){await this.mcp.start()}async stopMcp(){await this.mcp.stop()}nudgeMcpNativeProjection(){this.mcp.nudgeNativeProjection()}observeActiveRun(e){const t=e.currentRunId,s=e.writeGateToken,i=this.sessionMutationCoordinator.snapshot(e.gatewaySessionKey),n=void 0===e.pendingPromptId?e.gatewayRequestId??t:String(e.pendingPromptId);if(!t||!i||!n)return;const r={runtimeConversationUid:e.sessionId,runtimeSessionKey:e.gatewaySessionKey,historyGeneration:this.localHistoryBoundary.currentGeneration(e.sessionId),targetMessageId:n,runtimeRunId:t,activeRunRegistrationId:`acp:${i.operationId}`};return this.activeRuns.register(r,async()=>{const n=this.getGateway();if(!n?.isConnected||e.currentRunId!==t)return"recovery_required";const r=s?.operationId===i.operationId?await this.sessionMutationCoordinator.requestCancel(s,n):await this.sessionMutationCoordinator.requestCancelForSession(e.gatewaySessionKey,n);return r.ok?"accepted":!("reason"in r)||"terminal_already_observed"!==r.reason&&"no_active_mutation"!==r.reason?"recovery_required":"already_terminal"}),e.activeRunBinding=r,r}getActiveRunAuthority(){return{activeRuns:this.activeRuns,historyGeneration:e=>this.localHistoryBoundary.currentGeneration(e)}}resolvePlatformAgentId(e){return this.agentMapping?.get(e)?.id}resolveLocalAgentId(e){if(this.agentMapping&&e)for(const[t,s]of this.agentMapping)if(s.id===e)return t}getDefaultAgentId(){return this.agentMapping&&this.agentMapping.size>0?this.agentMapping.keys().next().value??"main":"main"}registerSession(e,t={}){const{ensureVerbose:s=!0}=t,i=(this.state.sessions.has(e.sessionId),this.state.sessionsByGatewayKey.get(e.gatewaySessionKey));!0===i?.canvasActive&&(e.canvasActive=!0),this.state.sessions.set(e.sessionId,e),this.state.sessionsByGatewayKey.set(e.gatewaySessionKey,e),s&&this.ensureVerboseFull(e),this.subscribeSessionMessages(e)}async ensureVerboseFull(e){if(e.verbosePatched)return;const t=this.getGateway();if(t?.isConnected){e.verbosePatched=!0;try{await t.sessionsPatch(e.gatewaySessionKey,{verboseLevel:"full"}),this.scheduleSessionToolCatalog(e.agentId,e.gatewaySessionKey)}catch(t){e.verbosePatched=!1,this.logger.debug?.(`[bridge-core] ensureVerboseFull failed: ${t instanceof Error?t.message:t}`)}}}ensureSessionForKey(e){const t=this.state.findSessionByKey(e);if(t)return t;const s=createSessionObject({gatewayKey:e,instanceId:this.instanceId,resolvePlatformAgentId:e=>this.resolvePlatformAgentId(e),thirdPartyChannels:this.thirdPartyChannels});return this.registerSession(s),this.logger.debug?.(`[bridge-core] ensureSessionForKey: lazily created session sid=${s.sessionId} key=${e}`),this.onSessionLoaded?.(s.sessionId,s.gatewaySessionKey,s.platformAgentId),s}async preloadSessionsFromGateway(){const e=this.sessionPreloadGeneration;if(this.sessionPreloadInFlight&&this.sessionPreloadInFlightGeneration===e)return await this.sessionPreloadInFlight;const t=this.preloadGatewaySessionIndex(e);this.sessionPreloadInFlight=t,this.sessionPreloadInFlightGeneration=e;try{await t,e===this.sessionPreloadGeneration&&this.sessionPreloadBarrierActive&&(this.sessionPreloadBarrierActive=!1,this.resolveSessionPreloadReady?.(),this.resolveSessionPreloadReady=null,this.rejectSessionPreloadReady=null)}catch(t){throw e===this.sessionPreloadGeneration&&this.sessionPreloadBarrierActive&&(this.sessionPreloadBarrierActive=!1,this.sessionPreloadFailed=!0,this.rejectSessionPreloadReady?.(t instanceof Error?t:new Error("session preload failed")),this.resolveSessionPreloadReady=null,this.rejectSessionPreloadReady=null),t}finally{this.sessionPreloadInFlight===t&&(this.sessionPreloadInFlight=null,this.sessionPreloadInFlightGeneration=null)}}async preloadGatewaySessionIndex(e){const t=this.getGateway();if(this.logSessionAuthority("session_preload_start",{generation:e,reason:t?.isConnected?"gateway_connected":"gateway_not_connected"}),!t?.isConnected)throw this.logSessionAuthority("session_preload_failure",{generation:e,reason:"gateway_not_connected"}),new RuntimeCommandError("GATEWAY_NOT_CONNECTED","Gateway not connected for session hydration");try{const s=await t.sessionsList(),i=this.mergeGatewaySessions(s);this.logSessionAuthority("session_preload_success",{generation:e,loaded:i,reason:"authoritative_sessions_list"})}catch(t){throw this.logSessionAuthority("session_preload_failure",{generation:e,reason:t instanceof RuntimeCommandError?t.code:"sessions_list_failed"}),t}}mergeGatewaySessions(e,t){if(!e||"object"!=typeof e)return 0;const s=e.sessions;if(!Array.isArray(s))return 0;let i=0;for(const e of s){if(!e||"object"!=typeof e||Array.isArray(e))continue;const s=e,n=s.sessionKey??s.key??s.id??s.name;if("string"!=typeof n||!n.trim()||n.trim()!==n||t&&!t(n)||this.state.findSessionByKey(n))continue;const r=s.label||s.derivedTitle||s.title||void 0,o=r&&!isPlaceholderTitle(r)?r:void 0,a=createSessionObject({gatewayKey:n,instanceId:this.instanceId,resolvePlatformAgentId:e=>this.resolvePlatformAgentId(e),thirdPartyChannels:this.thirdPartyChannels,title:o});this.registerSession(a,{ensureVerbose:!1}),i+=1}return i}async reconcileMissingGatewaySession(e,t){await this.assertSessionReconcileFence(t);const s=this.getGateway();if(!s?.isConnected)throw this.logSessionAuthority("session_reconcile_decision",{reason:"gateway_not_connected",runtimeSessionKeyHash:this.sessionKeyHash(e)}),new RuntimeCommandError("GATEWAY_NOT_CONNECTED","Gateway not connected for session reconciliation");const i=await s.sessionsList();await this.assertSessionReconcileFence(t);const n=!e.startsWith("agent:"),r=[...openClawSessionKeysFromList(i)].filter(t=>t===e?!n:openClawKeyMatchesConversation(t,e));if(this.logSessionAuthority("session_reconcile_decision",{reason:r.length?"exact_candidates":"no_exact_candidate",runtimeSessionKeyHash:this.sessionKeyHash(e),candidateHashes:r.map(e=>this.sessionKeyHash(e))}),!r.length)return;const o=new Set(r),a=this.mergeGatewaySessions(i,e=>o.has(e));this.logSessionAuthority("session_reconcile_decision",{reason:"reconciled_exact_candidates",runtimeSessionKeyHash:this.sessionKeyHash(e),loaded:a,candidateHashes:r.map(e=>this.sessionKeyHash(e))})}async materializeMissingModelSessionKey(e,t){await this.assertSessionReconcileFence(t);const s=t.attempt,i=s.verifiedMessageGrant?.claims,n=i?.command;if("service"!==s.origin||!i||"conversation"!==i.scope||!("session/set_model"===i.method&&"session.setModel"===n?.name||"session/set_reasoning"===i.method&&"session.setReasoning"===n?.name)||"conversation"!==n.context.scope||i.runtimeSessionKey!==e||i.runtimeConversationUid!==e||n.context.conversation.runtimeSessionKey!==e||n.context.conversation.runtimeConversationUid!==i.runtimeConversationUid)throw this.logSessionAuthority("model_intent_materialization_rejected",{reason:"verified_service_target_required",runtimeSessionKeyHash:this.sessionKeyHash(e)}),new RuntimeCommandError("INVALID_PARAMS","model-intent session materialization requires an exact verified Service target");if(!/^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(e))throw new RuntimeCommandError("INVALID_PARAMS","model-intent session materialization requires a canonical conversation UUID");let r;try{if(!this.runtimeAgentIdentities.resolveActiveLocalKey)throw new Error("identity resolver unavailable");r=await this.runtimeAgentIdentities.resolveActiveLocalKey(i.runtimeAgentUid)}catch{throw new RuntimeCommandError("INVALID_PARAMS","verified model-intent runtime agent is not active")}if(this.resolveLocalAgentId(i.platformAgentId)!==r)throw new RuntimeCommandError("INVALID_PARAMS","verified model-intent agent does not match the active platform projection");const o=`agent:${r}:beeos-conversation:ctx-${e}`,a=this.getGateway();if(!a?.isConnected)throw new RuntimeCommandError("GATEWAY_NOT_CONNECTED","Gateway not connected for model-intent session materialization");const l=await a.sessionsList();await this.assertSessionReconcileFence(t);const d=[...openClawSessionKeysFromList(l)].filter(t=>openClawKeyMatchesConversation(t,e));if(d.length){if(1===d.length&&d[0]===o)return this.mergeGatewaySessions(l,e=>e===o),o;throw new RuntimeCommandError("INVALID_PARAMS","verified model-intent target conflicts with an existing OpenClaw session")}return this.logSessionAuthority("model_intent_materialization_allowed",{runtimeSessionKeyHash:this.sessionKeyHash(e),openClawSessionKeyHash:this.sessionKeyHash(o)}),o}registerMaterializedModelSession(e){this.state.findSessionByKey(e)||(this.registerSession(createSessionObject({gatewayKey:e,instanceId:this.instanceId,resolvePlatformAgentId:e=>this.resolvePlatformAgentId(e),thirdPartyChannels:this.thirdPartyChannels}),{ensureVerbose:!1}),this.logSessionAuthority("model_intent_materialization_committed",{openClawSessionKeyHash:this.sessionKeyHash(e)}))}async assertSessionReconcileFence(e){if(e.lease.operationId!==e.attempt.operationId||e.lease.runtimeEpoch!==e.attempt.runtimeEpoch||e.attempt.abortSignal?.aborted)throw this.logSessionAuthority("session_reconcile_decision",{reason:"operation_fence_mismatch"}),new RuntimeCommandError("INVALID_PARAMS","session reconciliation is fenced");try{await this.runtimeLeases.renew(e.lease)}catch{throw this.logSessionAuthority("session_reconcile_decision",{reason:"lease_epoch_mismatch"}),new RuntimeCommandError("INVALID_PARAMS","session reconciliation is fenced")}if(e.attempt.abortSignal?.aborted)throw this.logSessionAuthority("session_reconcile_decision",{reason:"operation_aborted"}),new RuntimeCommandError("INVALID_PARAMS","session reconciliation is fenced")}sessionKeyHash(e){return createHash("sha256").update(e).digest("hex").slice(0,16)}logSessionAuthority(e,t){this.logger.info?.(`[bridge-core] ${JSON.stringify({event:e,...t})}`)}async handleRequest(e){this.writeObs({component:"bridge-core",domain:"forward",name:`rpc.request.${e.method}`,severity:"debug",payload:{id:e.id,method:e.method}});const t=e;try{switch(t.method){case"initialize":await this.handleInitialize(t);break;case"ping":this.transport.sendResult(t.id,{pong:!0});break;case"session/new":await this.handleSessionNew(t);break;case"session/load":await this.handleSessionLoad(t);break;case"session/list":await this.handleSessionList(t);break;case"session/rename":await this.handleSessionRename(t);break;case"session/delete":await this.handleSessionDelete(t);break;case"session/clear":await this.handleSessionClear(t);break;case"session/prompt":await this.handleSessionPrompt(t);break;case"session/set_mode":await this.handleSessionSetMode(t);break;case"session/set_model":await this.handleSessionSetModel(t);break;case"session/set_reasoning":await this.handleSessionSetReasoning(t);break;case"agent/create":await this.handleAgentCreate(t);break;case"agent/update":await this.handleAgentUpdate(t);break;case"agent/delete":await this.handleAgentDelete(t);break;case"agent/applyTemplate":await this.handleApplyTemplate(t);break;case"skills/list":await this.handleSkillsList(t);break;case"skills/install":await this.handleSkillsInstall(t);break;case"skills/uninstall":await this.handleSkillsUninstall(t);break;case"skills/update":await this.handleSkillsUpdate(t);break;case"models/list":await this.handleModelsList(t);break;case"session/cancel":await this.handleSessionCancel(t.id,t.params);break;case"cron/list":case"cron/status":case"cron/add":case"cron/update":case"cron/remove":case"cron/run":case"cron/runs":await this.handleCronRequest(t);break;default:"agent/list"===e.method?await this.handleAgentList(e):"_beeos/session/history"===e.method?await this.handleSessionHistoryPage(e):"mcp/list"===e.method?await this.handleMcpList(e):"mcp/set"===e.method?await this.handleMcpSet(e):"mcp/unset"===e.method?await this.handleMcpUnset(e):e.method.startsWith("terminal/")&&this.handleTerminalRequest?await this.handleTerminalRequest(e):this.transport.sendAcpError(e.id,Errors.MethodNotFound,{message:`Method not found: ${e.method}`})}}catch(t){this.transport.sendAcpError(e.id,Errors.InternalError,{message:t instanceof Error?t.message:"Internal error"})}}handleCanvasNotification=null;getCanvasState=null;onSessionLoaded=null;onSkillInstalled=null;onSessionDeleted=null;onSessionCleared=null;handleNotification(e){switch(e.method){case"session/cancel":this.observeLifecycle(this.handleSessionCancel(void 0,e.params),"session_cancel_notification");break;case"canvas/toggle":case"canvas/clear":case"canvas/reference":case"canvas/dimensions":this.observeLifecycle(this.dispatchLegacyCanvasNotification(e.method,e.params),`canvas_notification_${e.method.replace("/","_")}`);break;default:e.method.startsWith("terminal/")&&this.handleTerminalNotification?this.handleTerminalNotification(e.method,e.params):this.logger.debug?.(`[bridge-core] Unknown notification: ${e.method}`)}}async handleInitialize(e){await this.cronReady,this.initialized=!0;const t=this.transport.forwardControl;!1===e.params?.forwardThinking&&(t.forwardThinking=!1),!1===e.params?.forwardToolCalls&&(t.forwardToolCalls=!1),this.transport.sendResult(e.id,{protocolVersion:ACP_PROTOCOL_VERSION,agentCapabilities:{loadSession:!0,skills:!0,multipleSessions:!0,sessionCapabilities:{"web-ssh":!!this.config.bridge.shell.enabled,canvas:!!this.config.canvasEnabled},promptCapabilities:{image:!0,audio:!1,embeddedContext:!0},mcp:{}},agentInfo:{name:"beeos-claw",title:"BeeOS Agent Bridge",version:"0.1.0"},_meta:{},authMethods:[]})}async createRuntimeSession(e,t){const s=e.operationId.trim(),i=e.platformAgentId.trim();if(!s)throw new Error("session/new operationId is required");if(assertTrustedCoreExecutionAttempt(t,{operationId:s,runtimeEpoch:t.runtimeEpoch}),"service"!==t.origin)throw new Error("session/new runtime allocation requires Service authority");const n=this.resolveLocalAgentId(i);if(!i||!n)throw new RuntimeSessionNewRejectedError("session/new target agent is unknown");const r=`agent:${n}:service:${s}`,o=this.allocateSession({agentId:n,gatewaySessionKey:r,cwd:e.cwd??".",...void 0===e.title?{}:{title:e.title}});return await this.prepareSessionToolCatalog(n,r),{sessionId:o.sessionId,gatewaySessionKey:r,agentId:n,platformAgentId:i}}async handleSessionNew(e){const t="string"==typeof e.params?.agentId&&e.params.agentId||this.config.gateway.agentId||this.getDefaultAgentId(),s=`agent:${t}:${randomUUID()}`,i="string"==typeof e.params?.cwd?e.params.cwd:".",n=this.allocateSession({agentId:t,gatewaySessionKey:s,cwd:i});await this.prepareSessionToolCatalog(t,s),this.transport.sendResult(e.id,{sessionId:n.sessionId,modes:{availableModes:[{...DEFAULT_MODE}],currentModeId:DEFAULT_MODE.id},_meta:{sessionKey:s,agentId:n.agentId,platformAgentId:n.platformAgentId??null}}),this.fetchHistoryInBackground(s)}allocateSession(e){const t=this.state.findSessionByKey(e.gatewaySessionKey);if(t)return t;const s=createSessionObject({gatewayKey:e.gatewaySessionKey,instanceId:this.instanceId,resolvePlatformAgentId:e=>this.resolvePlatformAgentId(e),thirdPartyChannels:this.thirdPartyChannels,...void 0===e.cwd?{}:{cwd:e.cwd},...void 0===e.title?{}:{title:e.title}});if(s.agentId!==e.agentId)throw new Error("session/new gateway key agent mismatch");return this.registerSession(s,{ensureVerbose:!1}),s}prepareWorkspaceCatalog(){this.scheduleCatalogPrepare({agentId:this.getDefaultAgentId(),mode:"catalog",keyKind:"boot"})}scheduleSessionToolCatalog(e,t){const s=classifyCatalogSessionKey(t);this.scheduleCatalogPrepare({agentId:e,sessionKey:t,mode:"im_conversation"===s?"effective":"both",keyKind:s})}scheduleCatalogPrepare(e){this.runCatalogPrepare(e)}async prepareSessionToolCatalog(e,t){await this.runCatalogPrepare({agentId:e,sessionKey:t,mode:"both",keyKind:classifyCatalogSessionKey(t)})}async runCatalogPrepare(e){const t=this.getGateway();if(!t?.isConnected||"function"!=typeof t.request)return;const s=`${e.mode}:${e.agentId}:${e.sessionKey??""}`;if(!this.catalogPrepareInFlight.has(s)){this.catalogPrepareInFlight.add(s);try{await prepareOpenClawToolCatalog({request:(e,s)=>t.request(e,s),agentId:e.agentId,...void 0===e.sessionKey?{}:{sessionKey:e.sessionKey},mode:e.mode,keyKind:e.keyKind,logger:this.logger})}finally{this.catalogPrepareInFlight.delete(s)}}}fetchHistoryInBackground(e){const t=this.getGateway();t?.isConnected&&loadHistory(t,e,50).catch(e=>{this.logger.debug?.(`[bridge-core] Background history fetch failed: ${e instanceof Error?e.message:e}`)})}async subscribeSessionMessages(e){if(e.messageSubscriptionActive)return;const t=this.getGateway();if(t?.isConnected)try{await t.sessionsMessagesSubscribe(e.gatewaySessionKey),e.messageSubscriptionActive=!0,this.logger.debug?.(`[bridge-core] session.message subscribed sessionKey=${e.gatewaySessionKey}`)}catch(t){this.logger.debug?.(`[bridge-core] session.message subscribe failed key=${e.gatewaySessionKey}: ${t instanceof Error?t.message:t}`)}}async resubscribeAllSessionMessages(){for(const e of this.state.sessions.values())e.messageSubscriptionActive=!1,await this.subscribeSessionMessages(e)}handleSessionMessage(e){const t=e?.sessionKey,s=e?.messageId;if(!t||!s)return;const i=this.state.sessionsByGatewayKey.get(t);if(!i)return;const n=e.message?.role;if("toolResult"===n||"tool"===n)return void this.handlePersistedToolResultMessage(i,e).catch(e=>{this.logger.warn?.(`[bridge-core] persisted toolResult handling failed sessionKey=${t} messageId=${s}: ${e instanceof Error?e.message:String(e)}`)});const r="user"===n?"user":"system"===n?"system":"assistant",o=i.lastTurnIdByRole?.[r];if(!o)return void this.logger.debug?.(`[bridge-core] session.message skipped: no lastTurnId role=${r} sessionKey=${t} messageId=${s}`);const a=e.message?.timestamp,l="number"==typeof a&&a>0?new Date(a).toISOString():void 0,d={turnId:o};"number"==typeof e.messageSeq&&(d.seq=e.messageSeq),this.transport.sendSessionUpdate(i.sessionId,{sessionUpdate:"message_id_bind",role:r,messageId:s,...l?{timestamp:l}:{}},d),i.lastTurnIdByRole&&(i.lastTurnIdByRole[r]=void 0),"assistant"===r&&reconcileMissingArtifacts({localHistory:this.localSessionHistory,runOutputRouter:this.runOutputRouter,config:this.config,logger:this.logger,writeObs:this.writeObs,getCanvasState:()=>this.getCanvasState?.()??null,isSessionAlive:e=>this.state.sessions.has(e)},{session:i,turnId:o,messageId:s,runId:o})}async handlePersistedToolResultMessage(e,t){const s=t.messageId;if(!s)return;const i=extractPersistedToolResultEnvelope(t.message),n=this.extractPersistedToolResultFromHistory(e,s),r=mergePersistedToolResultEnvelopes(n,i);if(!r?.toolCallId)return void this.writeObs({component:"bridge-core",domain:"tool_result",name:"tool_result.skipped_no_tool_call_id",severity:"warn",payload:{sessionId:e.sessionId,messageId:s}});n&&this.writeObs({component:"bridge-core",domain:"tool_result",name:"tool_result.hydrated_from_history",severity:"debug",payload:{sessionId:e.sessionId,messageId:s,toolCallId:r.toolCallId,toolName:r.toolName}});const o=e.toolCallTurns?.get(r.toolCallId),a=o?.turnId??resolveTurnId(e);if(!a)return void this.writeObs({component:"bridge-core",domain:"tool_result",name:"tool_result.skipped_no_turn",severity:"warn",payload:{sessionId:e.sessionId,messageId:t.messageId,toolCallId:r.toolCallId,toolName:r.toolName}});const l=projectPersistedToolResultForReplay({toolCallId:r.toolCallId,toolName:r.toolName||o?.toolName||"tool",...r.rawText?{rawText:r.rawText}:{},...r.rawResult?{rawResult:r.rawResult}:{},...r.timestamp?{timestamp:r.timestamp}:{}});await emitToolResultProjectionForTurn({session:e,projection:l,toolCallId:r.toolCallId,turnId:a,config:this.config,logger:this.logger,writeObs:this.writeObs,getCanvasState:()=>this.getCanvasState?.()??null,sendUpdate:s=>(a&&void 0===s._turnId&&(s._turnId=a),this.transport.sendSessionUpdate(e.sessionId,s,{turnId:a,..."number"==typeof t.messageSeq?{seq:t.messageSeq}:{}}))})}extractPersistedToolResultFromHistory(e,t){if(!e.agentId)return;const s=this.localSessionHistory.readEntries({agentId:e.agentId,sessionKey:e.gatewaySessionKey,sessionId:e.sessionId});return findPersistedToolResultEnvelopeByMessageId(s,t)}async recoverSessionFromGateway(e){const t=this.getGateway();if(t?.isConnected)try{const s=await t.sessionsList();if(!s||"object"!=typeof s)return;const i=s.sessions;if(!Array.isArray(i))return;for(const t of i){const s=t,i=s.sessionKey??s.key??s.id??s.name;if(i&&"string"==typeof i&&deriveSessionId(i,this.instanceId)===e){const e=s.label||s.derivedTitle||s.title||void 0,t=e&&!isPlaceholderTitle(e)?e:void 0,n=createSessionObject({gatewayKey:i,instanceId:this.instanceId,resolvePlatformAgentId:e=>this.resolvePlatformAgentId(e),thirdPartyChannels:this.thirdPartyChannels,title:t});return this.registerSession(n),n}}}catch(e){this.logger.debug?.("[bridge-core] recoverSessionFromGateway failed:",e)}}async resolveOrCreateSession(e){let t=this.state.sessions.get(e);if(!t&&e.startsWith("agent:")&&(t=this.state.findSessionByKey(e)),t||e.startsWith("agent:")||(t=await this.recoverSessionFromGateway(e)),t)return{session:t,created:!1};const s=e.startsWith("agent:"),i=this.config.gateway.agentId||this.getDefaultAgentId();return t=createSessionObject({gatewayKey:s?e:`agent:${i}:${e}`,instanceId:this.instanceId,resolvePlatformAgentId:e=>this.resolvePlatformAgentId(e),thirdPartyChannels:this.thirdPartyChannels}),this.registerSession(t),{session:t,created:!0}}async handleSessionLoad(e){const t=e.params?.sessionId;if(!t)return void this.transport.sendAcpError(e.id,Errors.MissingParam,{param:"sessionId"});const{session:s}=await this.resolveOrCreateSession(t),i=s.sessionId;this.state.lastActiveSessionId=i,s.chatReplayDedupKeys.clear(),s.sessionLoadInProgress=!0;try{const e=await this.emitHistoryTurns(s);this.emitCurrentStreamSnapshot(s,e)}finally{s.sessionLoadInProgress=!1}this.transport.sendResult(e.id,{sessionId:i}),this.onSessionLoaded?.(i,s.gatewaySessionKey,s.platformAgentId),this.pushSkillsList().catch(e=>{this.logger.warn?.(`[bridge-core] pushSkillsList failed: ${e instanceof Error?e.message:e}`)})}async emitHistoryTurns(e){const t=new Set,s=e.sessionId;try{const i=this.getGateway(),n=e.assistantMq&&!e.assistantMq.closed,r=n?null:i,o=await loadHistory(r,e.gatewaySessionKey,100,{localHistory:this.localSessionHistory,agentId:e.agentId,sessionId:e.sessionId,logger:this.logger,emitObs:e=>{this.writeObs({component:"history-replay",domain:"lifecycle",name:e.name,severity:e.severity,payload:e.payload})}}),a=groupIntoTurns(o),l=a.filter(e=>"user"===e.role).length,d=a.filter(e=>"assistant"===e.role).length,c=a.filter(e=>e.toolCalls?.length).length,m=a.reduce((e,t)=>e+(t.toolCalls?.length??0),0);this.logger.debug?.(`[bridge-core] handleSessionLoad sid=${s} messages=${o.length} turns=${a.length} userTurns=${l} asstTurns=${d} turnsWithToolCalls=${c} totalToolCalls=${m}`),l>0&&e.pendingUserTurnUpdates&&e.pendingUserTurnUpdates.length>0&&(e.pendingUserTurnUpdates=[]);const u=t=>`history:${e.gatewaySessionKey}:${t}`;let p=-1;if(n)for(let e=a.length-1;e>=0;e--)if("assistant"===a[e].role){p=e;break}const{replayedToolCallIds:h,streamCompleteCount:g}=await this.replayHistoryTurnsToSink(e,a,this.transport,{mqActive:Boolean(n),lastAsstIdx:p,turnIdFor:(e,t)=>u(t)});for(const e of h)t.add(e);this.logger.debug?.(`[bridge-core] emitHistoryTurns DONE sid=${s} historyStreamCompleteEmitted=${g} replayedToolCallIds=${t.size}`)}catch(e){this.logger.warn?.("[bridge-core] Failed to load session history:",e)}return t}async replayHistoryTurnsToSink(e,t,s,i){const n=e.sessionId,r=new Set;let o=0;for(let a=0;a<t.length;a++){const l=t[a],d=i.turnIdFor(l,a);if(i.mqActive&&a===i.lastAsstIdx){const e=classifyHistoricalAssistantTurnCompletion(l,{mqActive:i.mqActive,isSkippedForLiveMq:!0});this.logger.debug?.(`[bridge-core] SKIP history_turn[${a}] assistant — ${"live"===e.kind?e.reason:"mq active"}, deferring to emitCurrentStreamSnapshot`);continue}if("user"===l.role&&"internal_system"===l.provenance?.kind){this.logger.debug?.(`[bridge-core] SKIP history_turn[${a}] internal_system user message`);continue}if("user"===l.role&&/^\s*system\s*\(untrusted[^)]*\)/i.test(l.content)){this.logger.debug?.(`[bridge-core] SKIP history_turn[${a}] system-injected user message`);continue}if("user"===l.role&&isHeartbeatPromptText(l.content)){this.logger.info?.(`[bridge-core] SKIP history_turn[${a}] heartbeat cron prompt sessionId=${e.sessionId} contentLen=${l.content.length} preview="${previewSanitizedText(l.content)}"`);continue}if("user"===l.role&&isCronPromptText(l.content)){this.logger.info?.(`[bridge-core] SKIP history_turn[${a}] cron-triggered user message sessionId=${e.sessionId} contentLen=${l.content.length} preview="${previewSanitizedText(l.content)}"`);continue}if("assistant"===l.role){const t=classifyHeartbeatFilter(l.content);if(t.filtered){this.logger.info?.(`[bridge-core] SKIP history_turn[${a}] heartbeat assistant reply reason=${t.reason} sessionId=${e.sessionId} contentLen=${l.content.length} preview="${previewSanitizedText(l.content)}"`);continue}}const c="assistant"===l.role?classifyHistoricalAssistantTurnCompletion(l,{mqActive:i.mqActive,isSkippedForLiveMq:!1}):void 0;if("error"===c?.kind||"interrupted"===c?.kind&&c.failure)s.sendSessionUpdate(n,{sessionUpdate:"agent_message_chunk",content:{type:"text",text:""},...l.timestamp?{timestamp:l.timestamp}:{}},{turnId:d,...void 0!==l._seq?{seq:l._seq}:{}}),s.sendSessionUpdate(n,{sessionUpdate:"stream_complete",stopReason:c.stopReason,error:c.message,errorCode:c.errorCode},{turnId:d}),this.logger.info?.(`[bridge-core] history_runtime_failure_terminalized session_id=${n} turn_id=${d} error_code=${c.errorCode} stop_reason=${c.stopReason} source=history_replay`),o+=1;else if("system"!==l.role)if(this.logger.debug?.(`[bridge-core] SENDING history_turn[${a}] role=${l.role} contentLen=${l.content.length} content=${JSON.stringify(l.content.slice(0,80))} tcCount=${l.toolCalls?.length??0}`),"user"===l.role)this.replayUserTurnAsChunks(s,n,l.content,l.metadata,l.timestamp,d,l._seq);else{if(await this.replayAssistantTurnAsStream(s,n,l,d),l.toolCalls)for(const e of l.toolCalls)e.id&&r.add(e.id);const e=c??classifyHistoricalAssistantTurnCompletion(l,{mqActive:i.mqActive,isSkippedForLiveMq:!1});if("interrupted"===e.kind){const t=new Set(e.incompleteToolCallIds);for(const i of l.toolCalls??[])t.has(i.id)&&s.sendSessionUpdate(n,buildToolCallUpdatePayload({toolCallId:i.id,toolName:i.name,outputText:e.message,kind:i.kind,status:"failed",timestamp:l.timestamp}),{turnId:d});this.logger.info?.(`[bridge-core] history_orphan_turn_terminalized session_id=${n} turn_id=${d} tool_call_ids=${e.incompleteToolCallIds.join(",")} stop_reason=${e.stopReason} source=history_replay`),s.sendSessionUpdate(n,{sessionUpdate:"stream_complete",stopReason:e.stopReason,error:e.message,errorCode:e.errorCode},{turnId:d}),o+=1;continue}if("completed"!==e.kind)continue;s.sendSessionUpdate(n,{sessionUpdate:"stream_complete",stopReason:e.stopReason},{turnId:d}),o+=1}else this.logger.debug?.(`[bridge-core] SENDING system_message[${a}] contentLen=${l.content.length}`),s.sendSessionUpdate(n,{sessionUpdate:"system_message_chunk",content:{type:"text",text:l.content},metadata:l.metadata,...l.timestamp?{timestamp:l.timestamp}:{}},{turnId:d,...void 0!==l._seq?{seq:l._seq}:{}})}return{replayedToolCallIds:r,streamCompleteCount:o}}async handleSessionHistoryPage(e){const t=e.params??{},s="string"==typeof t.sessionId?t.sessionId:"";if(!s)return void this.transport.sendAcpError(e.id,Errors.MissingParam,{param:"sessionId"});const i="string"==typeof t.cursor&&t.cursor.length>0?t.cursor:void 0,n="number"==typeof t.limit&&Number.isFinite(t.limit)&&t.limit>0?Math.min(1e3,Math.floor(t.limit)):100,{session:r}=await this.resolveOrCreateSession(s),o=this.getGateway(),a=await loadHistoryPage(o,r.gatewaySessionKey,{...i?{cursor:i}:{},limit:n,logger:this.logger}),l=groupIntoTurns(a.messages),d=new HistoryUpdateRecorder(this.transport);await this.replayHistoryTurnsToSink(r,l,d,{mqActive:!1,lastAsstIdx:-1,turnIdFor:(e,t)=>void 0!==e._seq?`history:${r.gatewaySessionKey}:seq:${e._seq}`:`history:${r.gatewaySessionKey}:seq:none:${i??"head"}:${t}`}),this.logger.debug?.(`[bridge-core] _beeos/session/history sid=${r.sessionId} cursor=${i??"<head>"} limit=${n} turns=${l.length} updates=${d.updates.length} hasMore=${a.hasMore} nextCursor=${a.nextCursor??"<none>"}`),this.transport.sendResult(e.id,{updates:d.updates,...a.nextCursor?{nextCursor:a.nextCursor}:{},hasMore:a.hasMore})}emitCurrentStreamSnapshot(e,t){const s=e.pendingUserTurnUpdates;if(s&&s.length>0)for(const t of s)this.transport.sendSessionUpdate(e.sessionId,t);const i=e.assistantMq;if(i&&!i.closed){const s=i.peek(0);let n=0;if(t&&t.size>0)for(let e=0;e<s.length;e++){const i=s[e],r=i.sessionUpdate,o=i.toolCallId;if("tool_call"!==r&&"tool_call_update"!==r||!o||!t.has(o)){if("agent_message_chunk"===r){if(""===(i.content?.text??"").trim()&&e<s.length-1){const i=s[e+1];if(("tool_call"===i.sessionUpdate||"tool_call_update"===i.sessionUpdate)&&i.toolCallId&&t.has(i.toolCallId)){n=e+1;continue}}break}break}n=e+1}const r=resolveTurnId(e),o=r?{turnId:r}:void 0;for(let t=n;t<s.length;t++)this.transport.sendSessionUpdate(e.sessionId,s[t],o)}}async handleSessionList(e){let t;try{t=parseRuntimeSessionListPage(e.params)}catch(t){return void this.transport.sendAcpError(e.id,Errors.InvalidParams,{message:t instanceof Error?t.message:"Invalid session/list params"})}const s=[];for(const[,e]of this.state.sessions)isBackgroundSessionKey(e.gatewaySessionKey)||this.cronIsolatedSessionKeys.has(e.gatewaySessionKey)||!t.includeDeleted&&this.state.deletedGatewaySessionKeys.has(e.gatewaySessionKey)||s.push({sessionId:e.sessionId,title:e.title||"New chat",cwd:e.cwd,updatedAt:e.lastStreamActivityTs??e.createdAt,agentId:e.agentId,platformAgentId:e.platformAgentId,deletable:!isSystemDefaultSessionKey(e.gatewaySessionKey)});const i=this.getGateway();if(i?.isConnected)try{const e=await i.sessionsList();if(e&&"object"==typeof e&&Array.isArray(e.sessions)){const i=e.sessions;for(const e of i){const i=e,n=i.sessionKey??i.key??i.id??i.name;if(!n||"string"!=typeof n)continue;if(isBackgroundSessionKey(n))continue;if(this.cronIsolatedSessionKeys.has(n))continue;const r=deriveSessionId(n,this.instanceId),o=s.find(e=>e.sessionId===n||e.sessionId===r||this.state.sessions.get(e.sessionId)?.gatewaySessionKey===n);if(!0===i.deleted){if(this.state.deletedGatewaySessionKeys.add(n),!t.includeDeleted)continue}else this.state.deletedGatewaySessionKeys.delete(n);const a=i.label||cleanGatewayDerivedTitle(i.derivedTitle)||i.title||void 0,l=a&&!isPlaceholderTitle(a)?a:void 0,d=i.model,c=i.modelProvider;if(o){if(l&&isPlaceholderTitle(o.title)){o.title=l;const e=this.state.sessions.get(o.sessionId);e&&isPlaceholderTitle(e.title)&&(e.title=l)}const e=i.updatedAt;e&&(o.updatedAt=e),d&&(o.model=d),c&&(o.modelProvider=c)}else{const e=createSessionObject({gatewayKey:n,instanceId:this.instanceId,resolvePlatformAgentId:e=>this.resolvePlatformAgentId(e),thirdPartyChannels:this.thirdPartyChannels,cwd:i.cwd,title:l});this.registerSession(e,{ensureVerbose:!1}),s.push({sessionId:e.sessionId,title:l||"",cwd:i.cwd,updatedAt:i.updatedAt,agentId:e.agentId,platformAgentId:e.platformAgentId,deletable:!isSystemDefaultSessionKey(n),model:d,modelProvider:c})}}}}catch(e){this.logger.debug?.("[bridge-core] sessions.list enrichment failed:",e)}const n=t.includeDeleted?s:s.filter(e=>{const t=this.state.sessions.get(e.sessionId)?.gatewaySessionKey;return!t||!this.state.deletedGatewaySessionKeys.has(t)});n.sort((e,t)=>(t.updatedAt??0)-(e.updatedAt??0)||e.sessionId.localeCompare(t.sessionId)),this.transport.sendResult(e.id,{sessions:pageRuntimeSessions(n,t)})}async handleSessionRename(e){const t=e.params?.sessionId,s=e.params?.title;if(!t||!s)return void this.transport.sendAcpError(e.id,Errors.MissingParam,{param:"sessionId, title"});const i=s.slice(0,64),n=this.state.sessions.get(t);if(n)try{const s=await this.dispatchLegacyConversationCapability("session.rename",n,{title:i});n.title=s.title,n.titleSource="manual",this.transport.sendResult(e.id,{sessionId:t,title:s.title})}catch(s){this.logger.warn?.(`[bridge-core] ACP rename projection deferred: ${s instanceof Error?s.message:String(s)}`),n.title=i,n.titleSource="manual",this.transport.sendResult(e.id,{sessionId:t,title:i})}else this.transport.sendAcpError(e.id,Errors.SessionNotFound)}generateTitleFromQA(e,t,s,i){const n=this.titles;if(!n)return void this.logger.warn?.("Conversation title runtime unavailable");const r=e.channelId??extractBeeosConversationIdFromSessionKey(e.gatewaySessionKey);r?(async()=>{const o=await generateTitleFromQA(s,i,{provider:await n.resolveProvider()}),a=await n.conversations.update({conversationId:r,title:o,titleSource:"generated"});e.title=a.title,e.titleSource=a.titleSource,this.transport.sendSessionUpdate(t,{sessionUpdate:"title_update",title:a.title})})().catch(e=>this.logger.warn?.(`Conversation title generation failed: ${e instanceof Error?e.message:"unknown error"}`)):this.logger.warn?.("Conversation title cannot resolve Cloud conversation ID")}async handleSessionDelete(e){const t=e.params?.sessionId;if(!t)return void this.transport.sendAcpError(e.id,Errors.MissingParam,{param:"sessionId"});const s=this.state.sessions.get(t);if(!s)return this.logger.info?.(`[bridge-core] session/delete sessionId=${t} NOT FOUND in local state`),void this.transport.sendAcpError(e.id,Errors.SessionNotFound);const i=s.gatewaySessionKey,n=this.sessionMutationCoordinator.tryStartExclusive({sessionKey:i,label:"session_delete"},"acp");if(!n.ok)return void this.sendSessionBusy(s,e.id,"active_session_mutation",n.queueDepth);const r=n.token;try{if(this.hasActiveRun(s)){const t=this.sessionMutationCoordinator.gateSnapshot(i);return void this.sendSessionBusy(s,e.id,"active_session_mutation",t.queueDepth)}this.logger.info?.(`[bridge-core] session/delete sessionId=${t} gatewayKey=${i} gwConnected=${!!this.getGateway()?.isConnected}`);let n=!1;const r=this.getGateway();if(r?.isConnected&&i)try{await r.sessionsDelete(i),n=!0,this.logger.info?.(`[bridge-core] Gateway sessions.delete SUCCESS for key=${i}`)}catch(t){const s=t instanceof Error?t.message:String(t);if(this.logger.info?.(`[bridge-core] Gateway sessions.delete FAILED for key=${i}: ${s}`),!s.includes("Cannot delete the main session"))return void this.transport.sendResult(e.id,{deleted:!1});try{await r.sessionsReset(i),n=!0,this.logger.info?.(`[bridge-core] Gateway sessions.reset SUCCESS for key=${i}`)}catch(t){return this.logger.warn?.(`[bridge-core] Gateway sessions.reset also failed for key=${i}: ${t instanceof Error?t.message:t}`),void this.transport.sendResult(e.id,{deleted:!1})}}else i&&this.logger.warn?.(`[bridge-core] Gateway not connected, proceeding with local-only delete for key=${i}`);this.state.clearStreamIdleTimer(s),s.assistantMq?.close(),s.assistantMq=void 0,s.currentRunId&&this.state.promptsByRunId.delete(s.currentRunId);const o=new Set;for(const[e,s]of this.state.toolCallToSessionMap)s===t&&(this.state.toolCallToSessionMap.delete(e),o.add(e));for(const[e,t]of this.state.toolCallIdMap)o.has(t)&&this.state.toolCallIdMap.delete(e);this.state.sessions.delete(t),this.state.sessionsByGatewayKey.delete(i),this.state.deletedGatewaySessionKeys.add(i),this.transport.resetSessionUpdateCount(t),n&&this.onSessionDeleted?.(i),this.logger.info?.(`[bridge-core] session/delete DONE sessionId=${t} gatewayKey=${i} deleted=true`),this.transport.sendResult(e.id,{deleted:!0})}finally{this.sessionMutationCoordinator.releaseBeforeRuntime(r)}}async handleSessionClear(e){this.logger.info?.(`[bridge-core] handleSessionClear called, params=${JSON.stringify(e.params)}`);const t=e.params?.sessionId;if(!t)return this.logger.warn?.("[bridge-core] session/clear missing sessionId"),void this.transport.sendAcpError(e.id,Errors.MissingParam,{param:"sessionId"});const s=this.state.sessions.get(t);if(!s)return this.logger.warn?.(`[bridge-core] session/clear sessionId=${t} not found in state`),void this.transport.sendAcpError(e.id,Errors.SessionNotFound);this.logger.info?.(`[bridge-core] session/clear sessionId=${t} gatewayKey=${s.gatewaySessionKey}`);const i=this.getGateway(),n=i?.isConnected;if(!s.promptDone&&(void 0!==s.pendingPromptId||void 0!==s.currentRunId)){this.logger.info?.(`[bridge-core] session/clear: cancelling active run sid=${t} runId=${s.currentRunId}`);const r=s.writeGateToken;let o=!1;if(n&&r){const e=await this.sessionMutationCoordinator.requestCancel(r,i);o="recovery_required"===await this.handleCancellationResult(s,e)}else n&&await i.chatAbort(s.gatewaySessionKey,s.currentRunId);return void this.transport.sendError(e.id,Errors.AgentError.rpcCode,o?SESSION_RECOVERY_REQUIRED_MESSAGE:"Cancellation is still converging",o?{code:SESSION_RECOVERY_REQUIRED_CODE,stopReason:"interrupted"}:{code:"session_cancellation_pending",stopReason:"cancelled"})}try{await this.dispatchLegacyConversationCapability("session.clear",s,{}),this.transport.sendResult(e.id,{cleared:!0}),this.logger.info?.("[bridge-core] session/clear DONE, sent cleared=true")}catch(t){this.sendRuntimeCommandError(e.id,t)}}async handleSessionSetMode(e){const t=e.params?.sessionId,s=e.params?.mode,i=t?this.state.sessions.get(t):void 0;if(t&&s)if(i)try{const t=await this.dispatchLegacyConversationCapability("session.setMode",i,{mode:s});this.transport.sendResult(e.id,t)}catch(t){this.sendRuntimeCommandError(e.id,t)}else this.transport.sendAcpError(e.id,Errors.SessionNotFound);else this.transport.sendAcpError(e.id,Errors.MissingParam,{param:"sessionId, mode"})}async handleSessionSetReasoning(e){const t=e.params;if(!t||"string"!=typeof t.sessionId||"string"!=typeof t.modelId||!Object.prototype.hasOwnProperty.call(t,"reasoningOverrideId")||null!==t.reasoningOverrideId&&"string"!=typeof t.reasoningOverrideId)return void this.transport.sendAcpError(e.id,Errors.InvalidParams);const s=this.state.sessions.get(t.sessionId);if(s)try{const i=await this.dispatchLegacyConversationCapability("session.setReasoning",s,{modelId:t.modelId,reasoningOverrideId:t.reasoningOverrideId});this.transport.sendResult(e.id,i)}catch(t){this.sendRuntimeCommandError(e.id,t)}else this.transport.sendAcpError(e.id,Errors.SessionNotFound)}async handleSessionSetModel(e){const t=e.params,s=null===t||"object"!=typeof t||Array.isArray(t)?void 0:t,i="string"==typeof s?.sessionId?s.sessionId:void 0;if(!i||!s||!Object.prototype.hasOwnProperty.call(s,"model"))return void this.transport.sendAcpError(e.id,Errors.MissingParam,{param:"sessionId, model"});const n=s.model;if(null!==n&&("string"!=typeof n||!n.trim()))return void this.transport.sendAcpError(e.id,Errors.InvalidParams,{message:"model must be a non-empty string or null"});const r=i?this.state.sessions.get(i):void 0;if(r)try{const t=await this.dispatchLegacyConversationCapability("session.setModel",r,{modelOverrideId:n});this.transport.sendResult(e.id,{model:t.effectiveModelId,modelProvider:t.modelProvider??null})}catch(t){this.sendRuntimeCommandError(e.id,t)}else this.transport.sendAcpError(e.id,Errors.SessionNotFound)}sendRuntimeCommandError(e,t){if(t instanceof RuntimeCommandError)switch(t.code){case"MISSING_PARAM":return void this.transport.sendAcpError(e,Errors.MissingParam,{...t.param?{param:t.param}:{}});case"INVALID_PARAMS":return void this.transport.sendAcpError(e,Errors.InvalidParams,{message:t.message});case"GATEWAY_NOT_CONNECTED":return void this.transport.sendAcpError(e,Errors.GatewayNotConnected);case"EXECUTION_FAILED":case"AGENT_DEFAULT_MODEL_NOT_CONFIGURED":case"OUTCOME_UNKNOWN":case"INSTANCE_EPOCH_MISMATCH":case"COMMAND_EXPIRED":case"IDEMPOTENCY_CONFLICT":case"UNAUTHORIZED_TARGET":return void this.transport.sendAcpError(e,Errors.InternalError,{message:t.message})}else this.transport.sendAcpError(e,Errors.InternalError,{message:t instanceof Error?t.message:"Internal error"})}createOperationId(){const e=randomUUID();return`${e.slice(0,14)}7${e.slice(15)}`}canonicalContextBase(e,t=this.createOperationId(),s="0"){const i=Date.now(),n=createHash("sha256").update(JSON.stringify(e)).digest("hex");return{operationId:t,runtimeInstanceUid:this.instanceId,dispatchEpoch:s,paramsHash:n,idempotencyKeyHash:createHash("sha256").update(t).digest("hex"),claimDeadline:new Date(i+3e4).toISOString(),executionDeadline:new Date(i+3e5).toISOString()}}acpExecutionAttempt(e){return createAcpCoreExecutionAttempt({operationId:e.context.operationId,runtimeEpoch:e.context.dispatchEpoch,resources:this.runtimeCommandResources,historyBoundary:this.localHistoryBoundary})}instanceCommand(e,t,s){return{name:e,params:t,context:{...this.canonicalContextBase(t,s.operationId,s.runtimeEpoch),scope:"instance"}}}async agentCommand(e,t,s,i){return{name:e,params:s,context:{...this.canonicalContextBase(s,i.operationId,i.runtimeEpoch),scope:"agent",agent:{runtimeLocalKey:t,runtimeAgentUid:await this.runtimeAgentIdentities.resolveOrCreate(t)}}}}async dispatchLegacyAgentCapability(e,t,s){const i=normalizeRuntimeAgentLocalKey(t),n=await this.runtimeLeases.claim(this.createOperationId());let r=!1;try{const t={name:e,params:s,context:{...this.canonicalContextBase(s,n.operationId,n.runtimeEpoch),scope:"agent",agent:{runtimeLocalKey:i,runtimeAgentUid:await this.runtimeAgentIdentities.resolveOrCreate(i)}}},o=this.runtimeCommands.dispatch(t,this.acpExecutionAttempt(t));return r=!0,await o.completion}finally{r||await this.runtimeLeases.release(n)}}async dispatchLegacyInstanceCapability(e,t){const s=await this.runtimeLeases.claim(this.createOperationId());let i=!1;try{const n={name:e,params:t,context:{...this.canonicalContextBase(t,s.operationId,s.runtimeEpoch),scope:"instance"}},r=this.runtimeCommands.dispatch(n,this.acpExecutionAttempt(n));return i=!0,await r.completion}finally{i||await this.runtimeLeases.release(s)}}async handleMcpList(e){const t=await this.dispatchLegacyInstanceCapability("mcp.list",{});this.transport.sendResult(e.id,{servers:Object.fromEntries(t.servers.map(e=>[e.name,{transport:e.transport,...e.url?{url:e.url}:{},...e.command?{command:e.command}:{},...e.args?{args:[...e.args]}:{},...e.cwd?{cwd:e.cwd}:{},...e.connectionTimeoutMs?{connectionTimeoutMs:e.connectionTimeoutMs}:{}}]))})}async handleMcpSet(e){const t=e.params&&"object"==typeof e.params?e.params:{},s="string"==typeof t.name?t.name:"";let i;try{if(requireSecretFreeMcpServerName(s),!t.server||"object"!=typeof t.server||Array.isArray(t.server))throw new Error("mcp/set requires name and server");const e=t.server,n=extractMcpCredentialMaterial(e),r={...e};delete r.headers,delete r.env;const o=e.transport??("string"==typeof e.command?"stdio":"string"==typeof e.url?"streamable-http":void 0),a=normalizeMcpPublicServerConfig({...r,transport:o}),l=createHash("sha256").update(canonicalizeJcs(a)).digest("hex");i={definition:{definitionRef:`acp-mcp:sha256:${l}`,version:"local-v1",sha256:l,...n?{credentialRef:await requireCredentialReferencePort(this.mcpCredentialReferences).put({serverName:s,material:n})}:{}},config:structuredClone(a)}}catch(t){return void this.transport.sendAcpError(e.id,Errors.InvalidParams,{message:t instanceof Error?t.message:"Invalid MCP server config"})}this.runtimeCommandResources.registerMcpDefinition?.(i.definition,i.config);try{await this.dispatchLegacyInstanceCapability("mcp.set",{name:s,definition:i.definition})}catch(e){if(i.definition.credentialRef){!await this.mcp.removeCredentialIfUnreferenced(i.definition.credentialRef)&&this.mcpCredentialReferences?.owns(i.definition.credentialRef)&&await this.mcpCredentialReferences.remove(i.definition.credentialRef)}throw e}this.transport.sendResult(e.id,{ok:!0,restarting:!1})}async handleMcpUnset(e){const t=e.params&&"object"==typeof e.params?e.params:{};let s;try{s=requireSecretFreeMcpServerName(t.name)}catch{return void this.transport.sendAcpError(e.id,Errors.InvalidParams,{message:"mcp/unset requires name"})}await this.dispatchLegacyInstanceCapability("mcp.unset",{name:s}),this.transport.sendResult(e.id,{ok:!0,restarting:!1})}async dispatchLegacyConversationCapability(e,t,s){const i=await this.runtimeLeases.claim(this.createOperationId());let n=!1;try{const r={name:e,params:s,context:{...this.canonicalContextBase(s,i.operationId,i.runtimeEpoch),scope:"conversation",conversation:{runtimeConversationUid:t.sessionId,runtimeSessionKey:t.gatewaySessionKey}}},o=this.runtimeCommands.dispatch(r,this.acpExecutionAttempt(r));return n=!0,await o.completion}finally{n||await this.runtimeLeases.release(i)}}async dispatchLegacyTerminalCapability(e,t,s,i){const n=await this.runtimeLeases.claim(this.createOperationId());let r=!1;try{const o={name:e,params:i,context:{...this.canonicalContextBase(i,n.operationId,n.runtimeEpoch),scope:"terminal",terminal:{terminalId:t,clientId:s}}},a=this.runtimeCommands.dispatch(o,this.acpExecutionAttempt(o));return r=!0,await a.completion}finally{r||await this.runtimeLeases.release(n)}}async dispatchLegacyCanvasNotification(e,t){const s=t&&"object"==typeof t?t:{},i=e.replace("/","."),n="canvas.toggle"===i?{visible:!0===s.visible||!0===s.enabled}:"canvas.reference"===i?{surfaceId:"string"==typeof s.surfaceId?s.surfaceId:""}:"canvas.dimensions"===i?{width:Number(s.width),height:Number(s.height),..."number"==typeof s.devicePixelRatio?{devicePixelRatio:s.devicePixelRatio}:{}}:{},r="string"==typeof s.sessionId?s.sessionId:"acp",o="string"==typeof s.canvasId?s.canvasId:r,a=await this.runtimeLeases.claim(this.createOperationId());let l=!1;try{const e={name:i,params:n,context:{...this.canonicalContextBase(n,a.operationId,a.runtimeEpoch),scope:"canvas",canvas:{canvasId:o,sessionId:r}}},t=this.runtimeCommands.dispatch(e,this.acpExecutionAttempt(e));l=!0,await t.completion}finally{l||await this.runtimeLeases.release(a)}}async applyCanonicalCanvasControl(e,t){if("canvas/reference"===e){const e=this.state.resolveSessionFromParams(t);if(!e)throw new RuntimeCommandError("INVALID_PARAMS","canvas session not found");const s="string"==typeof t.surfaceId?t.surfaceId:void 0;return void(e.referencedSurfaceId=s||void 0)}if(!this.handleCanvasNotification)throw new RuntimeCommandError("INVALID_PARAMS","canvas control delegate is not configured");await this.handleCanvasNotification(e,t)}async dispatchLegacyCronCapability(e,t){const s=normalizeRuntimeAgentLocalKey(this.config.gateway.agentId),i=await this.runtimeLeases.claim(this.createOperationId());let n=!1;try{const r="cron.add"===e?{...t,runtimeCronJobUid:`runtime-cron:${i.operationId}`}:"cron.run"===e?{...t,runToken:`runtime-run:${i.operationId}`}:t,o={name:e,params:r,context:{...this.canonicalContextBase(r,i.operationId,i.runtimeEpoch),scope:"agent",agent:{runtimeLocalKey:s,runtimeAgentUid:await this.runtimeAgentIdentities.resolveOrCreate(s)}}},a=this.runtimeCommands.dispatch(o,this.acpExecutionAttempt(o));return n=!0,await a.completion}finally{n||await this.runtimeLeases.release(i)}}validateConnectedGateway(){if(!this.getGateway()?.isConnected)throw new RuntimeCommandError("GATEWAY_NOT_CONNECTED","Gateway not connected")}async finishClaimedLegacyAgentMutation(e,t){let s=!1;try{const e=await t(),i=this.runtimeCommands.dispatch(e,this.acpExecutionAttempt(e));s=!0;const n=await i.completion;return queueMicrotask(()=>{this.observeAgentProjection()}),n}finally{s||await this.runtimeLeases.release(e)}}async observeAgentProjection(){await(async(e,t)=>{if(!t)return;let s;try{await Promise.race([Promise.resolve().then(t),new Promise((t,i)=>{s=setTimeout(()=>i(new Error(`${e} timed out`)),this.projectionObservationTimeoutMs)})])}catch(t){this.logger.warn?.(`[acp] ${e} failed: ${t instanceof Error?t.message:String(t)}`)}finally{s&&clearTimeout(s)}})("agent projection sync",this.triggerAgentSync)}async handleAgentCreate(e){try{const t=e.params?.name?.trim()??"";if(!t)throw new RuntimeCommandError("INVALID_PARAMS","Missing agent name");this.validateConnectedGateway();const s=this.createOperationId(),i=await this.runtimeLeases.claim(s),n=await this.finishClaimedLegacyAgentMutation(i,async()=>{const s=await this.runtimeAgentIdentities.allocateCreate(i,"agent.create"),n=Object.entries(e.params?.files??{}).filter(e=>"string"==typeof e[1]).map(([e,t])=>({name:e,contentRef:this.runtimeCommandResources.registerContent(t)}));return this.instanceCommand("agent.create",{runtimeAgentUid:s.runtimeAgentUid,name:t,...e.params?.identity?{identity:e.params.identity}:{},...e.params?.model?{defaultModelId:e.params.model}:{},...n.length?{bootstrapFiles:n}:{}},i)});this.transport.sendResult(e.id,{agentId:n.runtimeLocalKey,platformAgentId:this.resolvePlatformAgentId(n.runtimeLocalKey)??"",workspace:n.workspacePath})}catch(t){this.sendRuntimeCommandError(e.id,t)}}async handleApplyTemplate(e){try{const t=e.params?.name?.trim()??"";if(!t)throw new RuntimeCommandError("INVALID_PARAMS","Missing agent name");const s=e.params?.packageUrl?.trim()??"";if(!s)throw new RuntimeCommandError("MISSING_PARAM","Missing packageUrl","packageUrl");this.validateConnectedGateway();const i=await this.runtimeCommandResources.registerArtifact(s,e.params?.sha256),n=this.createOperationId(),r=await this.runtimeLeases.claim(n),o=await this.finishClaimedLegacyAgentMutation(r,async()=>{const s=await this.runtimeAgentIdentities.allocateCreate(r,"agent.applyTemplate");return this.instanceCommand("agent.applyTemplate",{runtimeAgentUid:s.runtimeAgentUid,name:t,artifact:i,...e.params?.identity?{identity:e.params.identity}:{},...e.params?.model?{defaultModelId:e.params.model}:{}},r)});this.transport.sendResult(e.id,{agentId:o.runtimeLocalKey,platformAgentId:this.resolvePlatformAgentId(o.runtimeLocalKey)??"",workspace:o.workspacePath})}catch(t){this.sendRuntimeCommandError(e.id,t)}}async handleAgentUpdate(e){try{const t=e.params?.agentId?.trim()??"";if(!t)throw new RuntimeCommandError("MISSING_PARAM","Missing agentId","agentId");const s={...void 0!==e.params?.name?{name:e.params.name}:{},...void 0!==e.params?.model?{defaultModelId:e.params.model}:{},...void 0!==e.params?.identity?{identity:e.params.identity}:{}};if(0===Object.keys(s).length)throw new RuntimeCommandError("INVALID_PARAMS","No updatable fields provided (name/model/identity)");this.validateConnectedGateway();const i=await this.runtimeLeases.claim(this.createOperationId()),n=await this.finishClaimedLegacyAgentMutation(i,()=>this.agentCommand("agent.update",t,s,i));this.transport.sendResult(e.id,{agentId:n.runtimeLocalKey})}catch(t){this.sendRuntimeCommandError(e.id,t)}}async handleAgentDelete(e){try{const t=e.params?.agentId?.trim()??"";if(!t)throw new RuntimeCommandError("MISSING_PARAM","Missing agentId","agentId");if("main"===normalizeRuntimeAgentLocalKey(t))throw new RuntimeCommandError("INVALID_PARAMS",'"main" cannot be deleted; delete the instance instead');this.validateConnectedGateway();const s=await this.runtimeLeases.claim(this.createOperationId()),i=await this.finishClaimedLegacyAgentMutation(s,()=>this.agentCommand("agent.delete",t,void 0===e.params?.deleteFiles?{}:{deleteFiles:e.params.deleteFiles},s));this.transport.sendResult(e.id,{agentId:i.runtimeLocalKey,...void 0===i.removedBindings?{}:{removedBindings:i.removedBindings}})}catch(t){this.sendRuntimeCommandError(e.id,t)}}async handleAgentList(e){try{const t=await this.dispatchLegacyInstanceCapability("agent.listProjection",{}),s=t.agents.map(e=>({agentId:e.runtimeLocalKey,name:e.name,...e.emoji?{emoji:e.emoji}:{},...e.avatarUrl?{avatarUrl:e.avatarUrl}:{}})),i={...t.defaultRuntimeLocalKey?{defaultId:t.defaultRuntimeLocalKey}:{},agents:s};this.transport.sendResult(e.id,i)}catch(t){this.sendRuntimeCommandError(e.id,t)}}async handleModelsList(e){try{this.transport.sendResult(e.id,await this.dispatchLegacyInstanceCapability("models.list",{}))}catch(t){this.sendRuntimeCommandError(e.id,t)}}async handleSkillsList(e){try{const t=await this.dispatchLegacyAgentCapability("skills.list",e.params?.agentId,{});this.transport.sendResult(e.id,{skills:t.skills.map(e=>({name:e.name,description:e.description??"",...e.emoji?{emoji:e.emoji}:{},...e.always?{always:!0}:{},...e.bundled?{bundled:!0}:{},...e.enabled?{}:{disabled:!0},skillKey:e.skillKey,...e.iconUrl?{iconUrl:e.iconUrl}:{}}))})}catch(t){this.sendRuntimeCommandError(e.id,t)}}async fetchSimplifiedSkills(e){const t=this.getGateway();if(!t?.isConnected)return[];try{const s=await t.skillsStatus(e);if(!s?.skills)return[];const i=s.skills.filter(e=>!1!==e.eligible||!0===e.disabled).sort((e,t)=>e.always&&!t.always?-1:!e.always&&t.always?1:e.name.localeCompare(t.name)).map(e=>({name:e.name,description:e.description,emoji:e.emoji,always:e.always||void 0,bundled:e.bundled||void 0,disabled:e.disabled||void 0,skillKey:e.skillKey||void 0,iconUrl:void 0})),n=await this.getSkillsDir(e);for(const e of i){const t=e.skillKey??e.name;try{const s=await readFile(join(n,t,".beeos-meta.json"),"utf8"),i=JSON.parse(s);i?.iconUrl&&(e.iconUrl=i.iconUrl)}catch{}}return i}catch(e){return this.logger.warn?.(`[bridge-core] Failed to fetch skills: ${e instanceof Error?e.message:e}`),[]}}async pushSkillsList(e){const t=await this.fetchSimplifiedSkills(e);this.transport.sendNotification("session/update",{sessionId:"_system",update:{sessionUpdate:"available_commands_update",commands:t}}),this.writeObs({component:"bridge-core",domain:"lifecycle",name:"skills.pushed",severity:"debug",payload:{count:t.length}})}getOpenclawHome(){return process.env.OPENCLAW_HOME||join(homedir(),".openclaw")}async getSkillsDir(e){const t=this.getOpenclawHome();if(!e||"main"===e)return join(t,"skills");const s=await this.resolveAgentWorkspaceDir(e);return join(s??join(t,`workspace-${e}`),"skills")}async resolveAgentWorkspaceDir(e){const t=this.getGateway();if(!t?.isConnected)return null;try{const s=await t.request("agents.list",{}),i=normalizeAgentId(e),n=s?.agents?.find(e=>normalizeAgentId(e.id)===i);return n?.workspace??null}catch(t){return this.logger.warn?.(`[bridge-core] resolveAgentWorkspaceDir("${e}") failed: ${t instanceof Error?t.message:t}`),null}}async handleSkillsInstall(e){const t=e.params,s=t?.zipUrl,i=t?.files,n=t?.agentId;let r;try{r=validateSkillSlug(t?.slug||t?.name||"")}catch{return void this.transport.sendAcpError(e.id,Errors.InvalidParams,{message:"Invalid or missing skill slug"})}if(i?.length||s)try{let o;if(s){o=parseSkillZip(await fetchWithRetry(s,{maxRetries:4,logger:this.logger}))}else{if(!i?.length)throw new RuntimeCommandError("INVALID_PARAMS","Missing skill files or zipUrl");o=new Map;for(const e of i){const t=normalizeSkillArtifactPath(e.path),s=await fetchWithRetry(e.downloadUrl,{maxRetries:4,logger:this.logger});o.set(t,new Uint8Array(s))}}if(t?.iconUrl&&o.set(".beeos-meta.json",(new TextEncoder).encode(JSON.stringify({iconUrl:t.iconUrl}))),!this.runtimeCommandResources.registerParsedArtifact)throw new RuntimeCommandError("EXECUTION_FAILED","ACP skill artifact ingress is not configured");const a=this.runtimeCommandResources.registerParsedArtifact({manifest:{slug:r,version:"0.0.0"},workspaceFiles:o});await this.dispatchLegacyAgentCapability("skills.install",n,{artifact:a}),this.transport.sendResult(e.id,{success:!0}),this.onSkillInstalled?.(r,resolveSkillInstallDir(await this.getSkillsDir(n),r)),await this.pushSkillsList(n)}catch(t){this.logger.error?.(`[bridge-core] Failed to install skill "${r}": ${t instanceof Error?t.message:t}`),this.transport.sendAcpError(e.id,Errors.InternalError,{message:`Install failed: ${t instanceof Error?t.message:String(t)}`})}else this.transport.sendAcpError(e.id,Errors.InvalidParams,{message:"Missing skill files or zipUrl"})}async handleSkillsUninstall(e){const t=e.params,s=t?.agentId;let i;try{i=validateSkillSlug(t?.slug||t?.name||"")}catch{return void this.transport.sendAcpError(e.id,Errors.InvalidParams,{message:"Invalid or missing skill name"})}try{await this.dispatchLegacyAgentCapability("skills.uninstall",s,{skillKey:i}),this.transport.sendResult(e.id,{success:!0}),await this.pushSkillsList(s)}catch(t){this.logger.error?.(`[bridge-core] Failed to uninstall skill "${i}": ${t instanceof Error?t.message:t}`),this.transport.sendAcpError(e.id,Errors.InternalError,{message:`Uninstall failed: ${t instanceof Error?t.message:String(t)}`})}}async handleSkillsUpdate(e){const t=e.params,s=t?.skillKey,i=t?.enabled,n=t?.agentId;if(s&&"boolean"==typeof i)try{await this.dispatchLegacyAgentCapability("skills.setEnabled",n,{skillKey:s,enabled:i}),this.logger.info?.(`[bridge-core] Updated skill "${s}" enabled=${i}${n?` for agent "${n}"`:""}`),this.transport.sendResult(e.id,{ok:!0,skillKey:s,enabled:i}),await this.pushSkillsList(n)}catch(t){this.logger.error?.(`[bridge-core] Failed to update skill "${s}": ${t instanceof Error?t.message:t}`),this.transport.sendAcpError(e.id,Errors.InternalError,{message:`Update failed: ${t instanceof Error?t.message:String(t)}`})}else this.transport.sendAcpError(e.id,Errors.InvalidParams,{message:"Missing skillKey or enabled"})}async handleSessionPrompt(e){const t=e.params?.sessionId,s=e.params?.prompt;if(!t||!s)return void this.transport.sendAcpError(e.id,Errors.MissingParam,{param:"sessionId, prompt"});const{session:i,created:n}=await this.resolveOrCreateSession(t);n&&this.onSessionLoaded?.(i.sessionId,i.gatewaySessionKey,i.platformAgentId);const r=i.sessionId;this.state.lastActiveSessionId=r,this.onPromptStart?.();const o=this.getGateway();if(!o?.isConnected)return void this.transport.sendAcpError(e.id,Errors.GatewayNotConnected);if(this.hasActiveRun(i)){const t=this.sessionMutationCoordinator.gateSnapshot(i.gatewaySessionKey);return void this.sendSessionBusy(i,e.id,"active_run",t.queueDepth)}try{await this.ensureVerboseFull(i)}catch(t){return this.releaseSessionWriteGate(i),void this.transport.sendAcpError(e.id,Errors.InternalError,{message:`Failed to prepare agent context: ${t instanceof Error?t.message:String(t)}`})}let a=extractPromptBlocks(sanitizePromptPayload(s));const l=a.filter(e=>"text"===e.type&&"text"in e&&"string"==typeof e.text).map(e=>e.text).join("\n");if(i.fileArtifactDisplayMode=prefersChatFileArtifacts(l)?"chat":"auto","manual"!==i.titleSource&&"generated"!==i.titleSource&&!i.firstTitleQuestion){const e=a.find(e=>"text"===e.type&&e.text);e&&"text"in e&&e.text&&(i.firstTitleQuestion=stripTransportMetadata(e.text).trim())}const d=buildResolutionPlan(a);if(d.refs.length>0)try{const e=await resolveResources(d,this.writeObs);a=applyResolutions(a,e)}catch(e){this.logger.warn?.(`[bridge-core] Resource pre-resolution failed: ${e instanceof Error?e.message:e}`)}let c;if(this.beeosFileResolver){const e=this.beeosFileResolver.buildResolutionPlan(a);if(e.refs.length>0)try{c=await this.beeosFileResolver.resolveMetadataForPrompt(e)}catch(e){this.logger.warn?.(`[bridge-core] BeeOS file resolution failed: ${e instanceof Error?e.message:e}`)}}const m=toGatewayPrompt(a,{beeosFileResolutionsByUri:c});if("error"in m)return this.releaseSessionWriteGate(i),void this.transport.sendAcpError(e.id,Errors.PromptConvertFailed,{message:m.error});const{message:u,attachments:p}=m,h=resolveStandaloneSlashCommandName(a,u)??resolveSlashCmd(a,u),g="string"==typeof h;if(h&&this.handleSlashCommand){const t=this;if(await(async()=>{try{return await t.handleSlashCommand(h)}catch(e){return t.logger.warn?.(`[bridge-core] /${h} handler failed: ${e instanceof Error?e.message:e}`),!1}})())return this.emitPromptAsUserSessionUpdates(i,a,resolveTurnId(i)),this.transport.sendSessionUpdate(r,{sessionUpdate:"agent_message_chunk",content:{type:"text",text:`Loading ${h}...`},timestamp:(new Date).toISOString()},{turnId:resolveTurnId(i)}),this.transport.sendResult(e.id,{stopReason:"end_turn"}),void this.releaseSessionWriteGate(i)}const y="chat.send";let f=g?u:withUserMessagePrefix(u);if(this.config.canvasEnabled&&!g&&!0===i.canvasActive&&!i.channelType){const e=this.getCanvasState?.();if(e){const t=e.resolveCanvasForSession(i.gatewaySessionKey);f=`${buildActiveSurfacesContext(e,t)??"[Canvas] Canvas panel is available. Use render_ui for structured/visual content."}\n\n${f}`}const t=i.referencedSurfaceId;t&&(f=`${CANVAS_UPDATE_HINT}\n[IMPORTANT: The user selected canvas surface "${t}" for editing. You MUST call render_ui with id="${t}" to UPDATE this existing surface. Do NOT create a new surface with a different id.]\n${f}`,i.referencedSurfaceId=void 0)}if(!g){const t=parseMentionsParam(e.params?.mentions);if(t.length>0){i.activeMentions=t;f=`${buildMentionInstruction(t)}\n${f}`,this.logger.debug?.(`[bridge-core] delegation mention instruction injected for ${t.length} agent(s)`)}else i.activeMentions=void 0}if(g&&this.logger.info?.(`[bridge-core] slash command passthrough sessionId=${r} command=/${h}`),this.hasActiveRun(i)){const t=this.sessionMutationCoordinator.gateSnapshot(i.gatewaySessionKey);return void this.sendSessionBusy(i,e.id,"active_run_after_prepare",t.queueDepth)}const w=this.sessionMutationCoordinator.tryStartInteractive({sessionKey:i.gatewaySessionKey,label:"session/prompt"},"acp");if(!w.ok)return void this.sendSessionBusy(i,e.id,w.reason,w.queueDepth);i.writeGateToken=w.token,this.state.resetForNewRun(i,r,this.transport),i.pendingPromptId=e.id,i.gatewayMethod=y;const I=resolveTurnId(i);this.emitPromptAsUserSessionUpdates(i,a,I),this.transport.sendSessionUpdate(r,{sessionUpdate:"agent_message_chunk",content:{type:"text",text:""},timestamp:(new Date).toISOString()},I?{turnId:I}:void 0);const S=extractCronJobId(u);S&&(i.cronJobId=S);const v=p.length>0?p.map(e=>({type:e.type,content:e.content,...e.mimeType?{mimeType:e.mimeType}:{},...e.fileName?{fileName:e.fileName}:{}})):void 0,C=`req_${randomUUID().replace(/-/g,"")}`,R={sessionKey:i.gatewaySessionKey,message:f,deliver:!1,idempotencyKey:randomUUID()};v?.length&&(R.attachments=v);if(!o.sendFrame({type:"req",id:C,method:y,params:R}))return i.pendingPromptId=void 0,i.assistantMq?.close(),i.assistantMq=void 0,i.pendingUserTurnUpdates=void 0,this.releaseSessionWriteGate(i),void this.transport.sendAcpError(e.id,Errors.SendFailed,{message:"failed to send prompt to gateway"});i.gatewayRequestId=C,this.state.promptsByGatewayRequestId.set(C,i)}replayUserTurnAsChunks(e,t,s,i,n,r,o){const a=splitUserReplayBlocks(s),l=r||void 0!==o?{...r?{turnId:r}:{},...void 0!==o?{seq:o}:{}}:void 0;for(const s of a){let r;if("text"===s.type){if(!s.text)continue;r={type:"text",text:s.text}}else r="resource_link"===s.type?{type:"resource_link",uri:s.uri??"",...s.fileName?{name:s.fileName}:{},...s.mimeType?{mimeType:s.mimeType}:{}}:{type:s.type,...s.mimeType?{mimeType:s.mimeType}:{},...s.fileName?{fileName:s.fileName}:{}};e.sendSessionUpdate(t,{sessionUpdate:"user_message_chunk",content:r,...i?{metadata:i}:{},...n?{timestamp:n}:{}},l)}}async replayAssistantTurnAsStream(e,t,s,i){const n=e=>{if(i||void 0!==e)return{...i?{turnId:i}:{},...void 0!==e?{seq:e}:{}}},r=new Set,o=(s,i,o)=>{if(!s?.length)return;const a=n(o);for(const n of s)n.uri&&!r.has(n.uri)&&(r.add(n.uri),e.sendSessionUpdate(t,{sessionUpdate:"agent_message_chunk",content:{type:"resource_link",uri:n.uri,name:n.name??"file",...n.mimeType?{mimeType:n.mimeType}:{}},...i?{timestamp:i}:{}},a))},a=n(s._seq);if(!s.sourceMessages?.length){const i={sessionUpdate:"history_turn",role:s.role,content:s.content,reasoning:s.reasoning,toolCalls:s.toolCalls,...s.timestamp?{timestamp:s.timestamp}:{}};return s.metadata&&(i.metadata=s.metadata),e.sendSessionUpdate(t,i,a),void o(s.files,s.timestamp,s._seq)}for(const i of s.sourceMessages){const r=i.updateType??"agent_message_chunk",a=n(i._seq);switch(r){case"agent_message_chunk":e.sendSessionUpdate(t,{sessionUpdate:"agent_message_chunk",content:{type:"text",text:i.content},...i.timestamp?{timestamp:i.timestamp}:{},...i.metadata?{metadata:i.metadata}:{}},a);break;case"agent_thought_chunk":e.sendSessionUpdate(t,{sessionUpdate:"agent_thought_chunk",content:{type:"text",text:i.content},...i.timestamp?{timestamp:i.timestamp}:{}},a);break;case"tool_call":{const n=i.toolCallId??"",r=s.toolCalls?.find(e=>e.id===n),o=i.toolName??"tool",l=i.content??"";let d={};if(l)try{d=JSON.parse(l)}catch{}e.sendSessionUpdate(t,buildToolCallPayload({toolCallId:n,toolName:o,args:d,kind:r?.kind,status:r?.status??"in_progress",timestamp:i.timestamp}),a);break}case"tool_call_update":{const n=i.toolCallId??"",r=s.toolCalls?.find(e=>e.id===n),l=i.toolName??r?.name??"tool",d=i.content??r?.output??"",c=await this.resolveReplayResourceLinks(n,l,i.files);e.sendSessionUpdate(t,buildToolCallUpdatePayload({toolCallId:n,toolName:l,outputText:d,kind:r?.kind,status:r?.status??"completed",resourceLinks:c,timestamp:i.timestamp}),a),o(c,i.timestamp,i._seq);break}}}o(s.files,s.timestamp,s._seq)}async resolveReplayResourceLinks(e,t,s){let i=s?.map(e=>({uri:e.uri,name:e.name??"file",...e.mimeType?{mimeType:e.mimeType}:{}}));if(i?.length)return i;if(i=await readEnrichmentCache(e).catch(()=>{}),i?.length)return i;if("beeos_upload_file"!==t)return;const n=await readUploadToolResult(e).catch(()=>{});if(!n)return;const r=buildToolResultSessionContentBlocks(t,n).filter(e=>"resource_link"===e.content?.type&&e.content.uri).map(e=>({uri:e.content.uri,name:e.content.name??"file",...e.content.mimeType?{mimeType:e.content.mimeType}:{}}));return r.length>0?r:void 0}emitPromptAsUserSessionUpdates(e,t,s){const i=(new Date).toISOString(),n=s?{turnId:s}:void 0;for(const r of t){const t=this.toUserSessionUpdateContent(r);if(!t)continue;const o={sessionUpdate:"user_message_chunk",content:t,timestamp:i};s&&void 0===o._turnId&&(o._turnId=s),this.transport.sendSessionUpdate(e.sessionId,o,n),e.pendingUserTurnUpdates?.push(o)}}toUserSessionUpdateContent(e){if("text"===e.type){if(!e.text)return;return{type:"text",text:e.text}}if("image"===e.type){const t=e.mimeType??e.mime_type??"application/octet-stream",s=e.fileName??e.file_name??e.name;if(!e.data&&!e.uri)return;return{type:"image",...e.data?{data:e.data}:{},...e.uri?{uri:e.uri}:{},...t?{mimeType:t}:{},...s?{fileName:s}:{}}}if("resource_link"===e.type){if(!e.uri)return;return{type:"resource_link",uri:e.uri,...e.title?{title:e.title}:{},...e.name?{name:e.name}:{},...e.mimeType??e.mime_type?{mimeType:e.mimeType??e.mime_type}:{}}}if("file"===e.type){const t=e.mimeType??e.mime_type??"application/octet-stream",s=e.fileName??e.file_name??e.filename??e.name;if(!e.data&&!e.text&&!e.uri)return;return{type:"file",...e.data?{data:e.data}:{},...e.text?{text:e.text}:{},...e.uri?{uri:e.uri}:{},...t?{mimeType:t}:{},...s?{fileName:s}:{}}}if("resource"===e.type){const t=e.resource??{},s=t.uri??e.uri,i=t.mimeType??t.mime_type??e.mimeType??e.mime_type,n=t.text??e.text,r=t.data??e.data,o=t.fileName??t.file_name??t.filename??t.name??e.fileName??e.file_name??e.filename??e.name;if(!s&&!n&&!r)return;return{type:"resource",resource:{...s?{uri:s}:{},...i?{mimeType:i}:{},...n?{text:n}:{},...r?{data:r}:{},...o?{fileName:o}:{}}}}}async handleSessionCancel(e,t){const s=t&&"object"==typeof t&&"sessionId"in t?t.sessionId:void 0;if(!s)return void(void 0!==e&&this.transport.sendAcpError(e,Errors.MissingParam,{param:"sessionId"}));const i=this.state.sessions.get(s);if(!i)return void(void 0!==e&&this.transport.sendAcpError(e,Errors.SessionNotFound));const n=i.activeRunBinding??this.observeActiveRun(i);if(!n){const t=this.sessionMutationCoordinator.snapshot(i.gatewaySessionKey);if("exclusive"===t?.attachmentPolicy&&void 0!==e){const t=this.sessionMutationCoordinator.gateSnapshot(i.gatewaySessionKey);return void this.sendSessionBusy(i,e,"exclusive_session_mutation",t.queueDepth)}return void(void 0!==e&&this.transport.sendResult(e,{}))}const r=await this.runtimeLeases.claim(this.createOperationId());let o=!1;try{const s=t&&"object"==typeof t&&"reason"in t&&"string"==typeof t.reason?t.reason:void 0,i={targetMessageId:n.targetMessageId,...s?{reason:s}:{}},a={name:"session.cancel",params:i,context:{...this.canonicalContextBase(i,r.operationId,r.runtimeEpoch),scope:"conversation",historyGeneration:n.historyGeneration,conversation:{runtimeConversationUid:n.runtimeConversationUid,runtimeSessionKey:n.runtimeSessionKey}}},l=this.runtimeCommands.dispatch(a,this.acpExecutionAttempt(a));o=!0,await l.completion,void 0!==e&&this.transport.sendResult(e,{})}catch(t){void 0!==e&&this.sendRuntimeCommandError(e,t)}finally{o||await this.runtimeLeases.release(r)}}hasActiveRun(e){return!e.promptDone&&(void 0!==e.pendingPromptId||void 0!==e.currentRunId||void 0!==e.gatewayRequestId)}observeLifecycle(e,t,s){observeLifecyclePromise(e,this.logger,{component:"bridge-core",action:t,...s?{sessionId:s.sessionId,gatewaySessionKey:s.gatewaySessionKey}:{}})}async handleCancellationResult(e,t){return t.ok?(this.observeLifecycle(t.terminalObservation.then(t=>{if(!t.ok)return this.publishCancellationRecovery(e)}),"cancel_terminal_observation",e),"accepted"):"recovery"in t?(await this.publishCancellationRecovery(e),"recovery_required"):"terminal_already_observed"}async publishCancellationRecovery(e){await this.terminateRunOutput(e,{kind:"failed",mode:"interrupted",message:SESSION_RECOVERY_REQUIRED_MESSAGE,errorCode:SESSION_RECOVERY_REQUIRED_CODE})}releaseSessionWriteGate(e){if(!e.writeGateToken)return;const t=e.writeGateToken;e.writeGateToken=void 0,this.sessionMutationCoordinator.releaseBeforeRuntime(t)}logRuntimeFailure(e,t){this.logger.warn?.(`[bridge-core] runtime_failure_classified session_id=${e.sessionId} gateway_session_key=${e.gatewaySessionKey} run_id=${e.currentRunId??""} gateway_request_id=${e.gatewayRequestId??""} error_code=${t.errorCode} stop_reason=${t.stopReason} source=${t.source} runtime=${t.runtimeKind}`)}async commitRunOutputTerminal(e,t){const s=this.state.sessions.get(e.sessionId);if(!s||s.gatewaySessionKey!==e.gatewaySessionKey)throw new Error(`run output lifecycle session unavailable: ${e.sessionId}`);await this.commitRunOutputTerminalForSession(s,t)}async commitRunOutputTerminalForSession(e,t){if("complete"!==t.kind&&"cancelled"!==t.kind)return"interrupted"===t.mode?(t.classification&&this.logRuntimeFailure(e,t.classification),void await this.endRun(e,{kind:"interrupted",stopReason:t.classification?.stopReason??"interrupted",message:t.message,errorCode:t.errorCode})):void await this.endRun(e,{kind:"error",code:t.code,message:t.message});await this.endRun(e,{kind:"complete",stopReason:t.stopReason})}async terminateRunOutput(e,t){const s=resolveTurnId(e);s&&this.runOutputRouter.hasRoute(s)?await this.runOutputRouter.terminal(s,t):await this.commitRunOutputTerminalForSession(e,t)}sendSessionBusy(e,t,s,i){this.logger.warn?.(`[bridge-core] session_write_gate_busy session_id=${e.sessionId} gateway_session_key=${e.gatewaySessionKey} reason=${s} queue_depth=${i}`),this.transport.sendError(t,Errors.AgentError.rpcCode,"Agent is busy",{code:"session_busy",reason:s,queueDepth:i}),this.transport.sendSessionUpdate(e.sessionId,{sessionUpdate:"stream_complete",stopReason:"interrupted",error:INTERRUPTED_PUBLIC_MESSAGE,errorCode:"session_busy"},{turnId:`req:${String(t)}`})}async endRun(e,t){if(e.promptDone||e.mutationTerminalCommitting)return this.logger.debug?.(`[bridge-core] endRun skipped (promptDone=true) sessionId=${e.sessionId} outcome=${t.kind}`),!1;if(void 0===e.pendingPromptId&&void 0===e.currentRunId&&void 0===e.gatewayRequestId)return this.logger.debug?.(`[bridge-core] endRun skipped (no pendingPromptId and no currentRunId) sessionId=${e.sessionId} outcome=${t.kind}`),!1;e.mutationTerminalCommitting=!0,this.state.clearStreamIdleTimer(e);const s=e.writeGateToken,i=e.activeRunBinding;let n,r,o,a,l=t;if(s){const e="complete"===l.kind?"cancelled"===l.stopReason?"cancelled":"completed":"interrupted"===l.kind&&"timeout"===l.stopReason?"timeout":"error",t=await this.sessionMutationCoordinator.converge(s,e);t.ok?n=t.permit:l={kind:"interrupted",stopReason:"interrupted",message:SESSION_RECOVERY_REQUIRED_MESSAGE,errorCode:SESSION_RECOVERY_REQUIRED_CODE}}try{const t=resolveTurnId(e),s=t?{turnId:t}:void 0;e.promptDone=!0;const i=e.currentRunId;if("complete"===l.kind){if(i&&this.onRunComplete){const t=e.agentAssistantTextSoFar??e.chatAssistantTextSoFar??"";this.onRunComplete(i,t,l.stopReason)}if(void 0!==e.pendingPromptId&&this.transport.sendResult(e.pendingPromptId,{stopReason:l.stopReason}),this.transport.sendSessionUpdate(e.sessionId,{sessionUpdate:"stream_complete",stopReason:l.stopReason},s),e.firstTitleQuestion&&"manual"!==e.titleSource&&"generated"!==e.titleSource&&"cancelled"!==l.stopReason){const t=e.firstTitleQuestion;e.firstTitleQuestion=void 0,this.generateTitleFromQA(e,e.sessionId,t,e.agentAssistantTextSoFar??e.chatAssistantTextSoFar??"")}}else"interrupted"===l.kind?(i&&this.onRunFailed&&this.onRunFailed(i,l.message),void 0!==e.pendingPromptId&&this.transport.sendError(e.pendingPromptId,Errors.AgentError.rpcCode,l.message,{code:l.errorCode??"interrupted",stopReason:l.stopReason}),this.transport.sendSessionUpdate(e.sessionId,{sessionUpdate:"stream_complete",stopReason:l.stopReason,error:l.message,...l.errorCode?{errorCode:l.errorCode}:{}},s)):(i&&this.onRunFailed&&this.onRunFailed(i,l.message),void 0!==e.pendingPromptId&&this.transport.sendError(e.pendingPromptId,l.code,l.message),this.transport.sendSessionUpdate(e.sessionId,{sessionUpdate:"stream_complete",stopReason:"error",error:l.message},s))}catch(e){r=e}try{this.cleanupPrompt(e)}catch(e){o=e}finally{e.writeGateToken=void 0,e.activeRunBinding=void 0,n&&this.sessionMutationCoordinator.release(n)}if(i){const e="complete"===l.kind?"cancelled"===l.stopReason?"cancelled":"completed":"interrupted"===l.kind&&"cancelled"===l.stopReason?"cancelled":"failed";this.activeRuns.markTerminal(i,e)}try{this.onRunEnded?.(e,l)}catch(e){a=e}if(r)throw r;if(o)throw o;if(a)throw a;return!0}adoptBackgroundMutation(e,t){const s=this.sessionMutationCoordinator.attachOrStartExternal({sessionKey:e.gatewaySessionKey,label:"background_run"},"acp",t);return s.ok?("owner"===s.ownership&&(e.writeGateToken=s.token),s):(this.logger.error?.(`[bridge-core] background mutation rejected sessionKey=${e.gatewaySessionKey} runId=${t} reason=${s.reason}`),s)}cleanupPrompt(e){this.state.clearStreamIdleTimer(e);const t=e.currentRunId;if(t){this.state.promptsByRunId.delete(t),this.state.runIdToCronJobId.delete(t),this.state.markRunCompleted(t);const e=`tc_${t}_`;for(const[t,s]of this.state.toolCallIdMap)s.startsWith(e)&&this.state.toolCallIdMap.delete(t)}e.gatewayRequestId&&this.state.promptsByGatewayRequestId.delete(e.gatewayRequestId),e.pendingPromptId=void 0,e.promptDone=void 0,e.currentRunId=void 0,e.gatewayRequestId=void 0,e.assistantStreamStartedAt=void 0,e.gatewayMethod=void 0,e.mutationTerminalCommitting=void 0,e.assistantMq?.close(),e.assistantMq=void 0,e.pendingUserTurnUpdates=void 0}async completePrompt(e,t){"cancelled"!==t?await this.terminateRunOutput(e,{kind:"complete",stopReason:t}):await this.terminateRunOutput(e,{kind:"cancelled",stopReason:"cancelled"})}async interruptPrompt(e,t){await this.terminateRunOutput(e,{kind:"failed",mode:"interrupted",message:t.publicMessage,errorCode:t.errorCode,classification:t})}async failPrompt(e,t,s,i){await this.terminateRunOutput(e,errorRunOutput(t,s,i))}cleanupAllPendingPrompts(){for(const e of this.state.sessions.values())e.writeGateToken?(this.sessionMutationCoordinator.requireRecovery(e.writeGateToken,"gateway_disconnected","disconnect"),this.observeLifecycle(this.terminateRunOutput(e,{kind:"failed",mode:"interrupted",message:SESSION_RECOVERY_REQUIRED_MESSAGE,errorCode:SESSION_RECOVERY_REQUIRED_CODE}),"gateway_disconnect_recovery",e)):this.observeLifecycle(this.interruptPrompt(e,classifyAgentRuntimeFailure({raw:Errors.GatewayDisconnected.message,source:"live_gateway",runtimeKind:"openclaw"})),"gateway_disconnect_interrupt",e)}handleGatewayResponse(e){const t=e.id;if(!t)return;const s=this.state.promptsByGatewayRequestId.get(t);if(!s||s.promptDone)return;if(!e.ok){const i=e.error,n=i?.message??"gateway returned error";this.logger.warn?.(`[bridge-core] handleGatewayResponse: gateway rejected prompt gatewayRequestId=${t} error=${n}`);const r=classifyAgentRuntimeFailure({raw:n,source:"live_gateway",runtimeKind:"openclaw"});return void("interrupted"===r.kind?this.observeLifecycle(this.interruptPrompt(s,r),"gateway_response_interrupt",s):(this.logRuntimeFailure(s,r),this.observeLifecycle(this.failPrompt(s,Errors.SendFailed.rpcCode,r.publicMessage,r),"gateway_response_failure",s)))}const i=e.payload,n=i?.runId;if(n)s.currentRunId=n,this.sessionMutationCoordinator.bindRun(s.writeGateToken,n),this.observeActiveRun(s),s.assistantStreamStartedAt=Date.now(),this.state.promptsByRunId.set(n,s),openRunOutputRoute(this.runOutputRouter,s,n,["acp"]),this.logger.debug?.(`[bridge-core] handleGatewayResponse: bound runId=${n} gatewayRequestId=${t} sessionId=${s.sessionId}`);else{const e=resolveTurnId(s);e&&openRunOutputRoute(this.runOutputRouter,s,e,["acp"]),this.logger.debug?.(`[bridge-core] handleGatewayResponse: no runId in payload gatewayRequestId=${t} sessionId=${s.sessionId}`)}}async handleCronRequest(e){const t=this.getGateway();if(!t?.isConnected)return void this.transport.sendAcpError(e.id,Errors.GatewayNotConnected);const s=e.method.replace("/","."),i={...e.params??{}};["cron.add","cron.update","cron.remove","cron.run","cron.runs"].includes(s)&&(i.__beeosCronIngress="acp"),"cron.update"===s&&(i.patch=i.patch&&"object"==typeof i.patch?{...i.patch}:{});try{const t=await this.dispatchLegacyCronCapability(s,i),n=await this.mapCronResultForAcp(s,t);this.transport.sendResult(e.id,n??{}),"cron.add"!==s&&"cron.update"!==s&&"cron.remove"!==s||this.refreshCronIsolatedSessionKeys()}catch(t){const i=t instanceof Error?t.message:"cron request failed";this.logger.warn?.(`[bridge-core] ${s} failed: ${i}`),this.transport.sendAcpError(e.id,Errors.SendFailed,{message:i})}}async mapCronResultForAcp(e,t){if(!t||"object"!=typeof t)return t;const s=t;await this.cronIdentities.snapshot();const i=e=>{if(!e||"object"!=typeof e)return e;const t=e,s=t.id;if("string"!=typeof s)return e;const i=this.cronIdentities.localId(s)??s,n=this.cronChannelCache.get(i),r=n?encodeBeeosChannelTag("string"==typeof t.description?t.description:void 0,n):t.description;return{...t,id:i,...void 0===r?{}:{description:r}}};if(Array.isArray(s.jobs))return{...s,jobs:s.jobs.map(i)};if(s.jobs&&"object"==typeof s.jobs){const e=s.jobs,t={};for(const[s,n]of Object.entries(e))t[s]=i(n);return{...s,jobs:t}}return Array.isArray(s.runs)?{...s,runs:s.runs.map(e=>{if(!e||"object"!=typeof e)return e;const t=e,s="string"==typeof t.jobId?this.cronIdentities.localId(t.jobId)??t.jobId:t.jobId;return{...t,jobId:s}})}:"cron.add"===e||"cron.update"===e?i(t):t}async allocateCronChannel(e,t){if(!this.agentFetch||!this.agentGatewayUrl)return void this.logger.debug?.("[bridge-core] agentFetch/agentGatewayUrl unavailable, skip channel alloc");const s=this.getAuthoritativeInstanceId?.()?.trim();if(!s?.startsWith("inst_"))return void this.logger.warn?.("[bridge-core] no authoritative inst_* instance id resolved yet, skip channel alloc");const i=this.resolvePlatformAgentId(t)?.trim();if(!i)return void this.logger.warn?.(`[bridge-core] no platform agent id for runtimeLocalKey=${t}, skip channel alloc`);const n={source:"cron",job_name:e,protocol:"beeos-conversation",target_agent_id:i,title:e};try{const e=`${this.agentGatewayUrl}/api/v1/channels`,t=await this.agentFetch(e,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({participants:[s,"service:cron"],metadata:n})});if(!t.ok)return void this.logger.warn?.(`[bridge-core] channel alloc failed: ${t.status}`);return(await t.json()).id}catch(e){return void this.logger.warn?.(`[bridge-core] channel alloc error: ${e instanceof Error?e.message:e}`)}}setCronChannelCache(e,t){if(this.cronChannelCache.size>=AcpGatewayBridgeCore.CRON_CHANNEL_CACHE_MAX){const e=this.cronChannelCache.keys().next().value;void 0!==e&&this.cronChannelCache.delete(e)}this.cronChannelCache.set(e,t)}async refreshCronIsolatedSessionKeys(){const e=this.getGateway();if(e?.isConnected)try{const t=await e.request("cron.list",{}),s=t?.jobs;if(!Array.isArray(s))return;const i=new Set,n=[];for(const e of s){if(!e||"object"!=typeof e)continue;const t=e;if("string"==typeof t.id&&n.push(t.id),"isolated"!==t.sessionTarget)continue;const s=t.sessionKey;"string"==typeof s&&s.length>0&&i.add(s)}this.cronIsolatedSessionKeys=i,await this.cronIdentities.reconcile(n)}catch(e){this.logger.debug?.(`[bridge-core] refreshCronIsolatedSessionKeys failed: ${e instanceof Error?e.message:e}`)}}}async function fetchWithRetry(e,t={}){const s=t.maxRetries??3;let i;for(let n=0;n<=s;n++){const r=await fetch(e,{signal:AbortSignal.timeout(12e4),redirect:"follow"});if(r.ok)return Buffer.from(await r.arrayBuffer());if(429===r.status||r.status>=500){const o=Number(r.headers.get("retry-after"))||0,a=1e3*Math.max(o,2**n);if(i=new Error(`HTTP ${r.status} from ${e}`),n<s){t.logger?.info?.(`[bridge-core] Download ${r.status}, retry ${n+1}/${s} in ${(a/1e3).toFixed(0)}s`),await new Promise(e=>setTimeout(e,a));continue}}throw new Error(`Failed to download ${e}: HTTP ${r.status}`)}throw i??new Error(`Download failed after retries: ${e}`)}const SKILL_ROOT_MARKERS=["SKILL.md","skill.md","SKILL.MD"];function normalizeSkillArtifactPath(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 parseSkillZip(e){if(e.byteLength>MAX_ZIP_BYTES)throw new RuntimeCommandError("INVALID_PARAMS","Skill archive too large");const t=new AdmZip(e).getEntries().filter(e=>!e.isDirectory).map(e=>({name:normalizeSkillArtifactPath(e.entryName),bytes:e.getData()})).filter(e=>!e.name.split("/").includes("__MACOSX"));if(0===t.length)throw new RuntimeCommandError("INVALID_PARAMS","Skill archive is empty");const s=1===new Set(t.map(e=>e.name.split("/")[0])).size&&t.every(e=>e.name.includes("/")),i=new Map;for(const e of t){const t=s?e.name.slice(e.name.indexOf("/")+1):e.name;if(!t||i.has(t))throw new RuntimeCommandError("INVALID_PARAMS",`Duplicate skill path: ${t}`);i.set(t,e.bytes)}return i}async function resolveSkillRoot(e){for(const t of SKILL_ROOT_MARKERS)try{return await stat(join(e,t)),e}catch{}const t=(await readdir(e,{withFileTypes:!0})).filter(e=>e.isDirectory()&&!e.name.startsWith(".")&&"__MACOSX"!==e.name);if(1===t.length){const s=join(e,t[0].name);for(const e of SKILL_ROOT_MARKERS)try{return await stat(join(s,e)),s}catch{}return s}return e}async function extractZipToDir(e,t){const s=new AdmZip(e),i=join(t,".beeos-extract-tmp-"+Date.now());try{await mkdir(i,{recursive:!0}),s.extractAllTo(i,!0);const e=await resolveSkillRoot(i),n=await readdir(e,{withFileTypes:!0});let r=0;for(const s of n){if(s.name.startsWith(".beeos-extract-tmp")||"__MACOSX"===s.name)continue;const i=join(e,s.name),n=join(t,s.name);await rename(i,n).catch(async()=>{await cp(i,n,{recursive:!0})}),r++}if(0===r)throw new Error("Zip archive contained no extractable files")}finally{await rm(i,{recursive:!0,force:!0}).catch(()=>{})}}const VALID_SLUG_PATTERN=/^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/i,NON_ASCII_PATTERN=/[^\x00-\x7F]/;function validateSkillSlug(e){const t=e.trim();if(!t||t.includes("/")||t.includes("\\")||t.includes(".."))throw new Error(`Invalid skill slug: ${e}`);if(NON_ASCII_PATTERN.test(t)||!VALID_SLUG_PATTERN.test(t))throw new Error(`Invalid skill slug: ${e}`);return t}function safeDirName(e){const t=e.trim();return t?t.replaceAll("/","__").replaceAll("\\","__"):t}function extractMcpCredentialMaterial(e){const t=mcpStringMap(e.headers,"MCP headers"),s=mcpStringMap(e.env,"MCP environment");if(!(t&&0!==Object.keys(t).length||s&&0!==Object.keys(s).length))return;const i=e.transport??("string"==typeof e.command?"stdio":"string"==typeof e.url?"streamable-http":void 0);if("stdio"===i&&t&&Object.keys(t).length)throw new Error("MCP stdio config cannot include headers");if("stdio"!==i&&s&&Object.keys(s).length)throw new Error("MCP HTTP config cannot include env");return{...t&&Object.keys(t).length?{headers:t}:{},...s&&Object.keys(s).length?{env:s}:{}}}function mcpStringMap(e,t){if(void 0!==e){if(!e||"object"!=typeof e||Array.isArray(e)||Object.entries(e).some(([e,t])=>!e||/[\u0000-\u001f\u007f]/.test(e)||"string"!=typeof t))throw new Error(`${t} invalid`);return{...e}}}function requireCredentialReferencePort(e){if(!e)throw new Error("MCP credential reference vault is unavailable");return e}function resolveSkillInstallDir(e,t){const s=safeDirName(t),i=join(e,s),n=resolve(e),r=resolve(i),o=relative(n,r);if(!o||".."===o||o.startsWith(`..${sep}`)||path.isAbsolute(o))throw new Error("Invalid skill target path");return i}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const QUESTION_SCHEMA="beeos.question.v1";export class QuestionError extends Error{code;constructor(e){super(e),this.code=e}}export const object=e=>"object"!=typeof e||null===e||Array.isArray(e)?void 0:e;function fail(e="INVALID_CONTENT"){throw new QuestionError(e)}const exact=(e,t,s=[])=>{(t.some(t=>!Object.hasOwn(e,t))||Object.keys(e).some(e=>!t.includes(e)&&!s.includes(e)))&&fail()},bounded=(e,t)=>"string"==typeof e&&[...e].length<=t,text=(e,t)=>bounded(e,t)&&e.trim().length>0,ascii=(e,t)=>text(e,t)&&/^[\x21-\x7e]+$/.test(e),unique=e=>new Set(e).size===e.length,record=e=>object(e)??fail();function base(e,t,s){let i;try{const t=JSON.stringify(e);if(void 0===t)return fail();i=[...t].reduce((e,t)=>{const s=t.codePointAt(0);return e+(s<128?1:s<2048?2:s<65536?3:4)},0)}catch{return fail()}i>65536&&fail("CONTENT_TOO_LARGE");const o=record(e);return o.schema_version!==QUESTION_SCHEMA&&fail("UNSUPPORTED_SCHEMA"),("question"===s?"agent.input_required"===t:"user.continue"===t)&&o.kind===s||fail("INVALID_ENVELOPE_KIND"),ascii(o.request_id,128)||fail(),o}function timestamp(e){if("string"!=typeof e||!/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?(?:Z|[+-]\d{2}:\d{2})$/.test(e))return!1;const[t,s,i,o,n,r]=e.slice(0,19).split(/[-T:]/).map(Number),a=new Date(Date.UTC(t,s,0)).getUTCDate(),c=/([+-])(\d{2}):(\d{2})$/.exec(e);return s>=1&&s<=12&&i>=1&&i<=a&&o<=23&&n<=59&&r<=59&&(!c||Number(c[2])<=23&&Number(c[3])<=59)&&Number.isFinite(Date.parse(e))}export function decodeQuestion(e,t="agent.input_required"){const s=base(e,t,"question");exact(s,["schema_version","kind","request_id","message","expires_at","questions"]),(!text(s.message,2048)||!timestamp(s.expires_at)||!Array.isArray(s.questions)||s.questions.length<1||s.questions.length>3)&&fail();const i=[];for(const e of s.questions){const t=record(e);!0!==t.is_secret&&!0!==t.isSecret||fail("SECRET_INPUT_UNSUPPORTED"),exact(t,["id","header","text","input"]),ascii(t.id,64)&&text(t.header,80)&&text(t.text,2048)||fail(),i.push(t.id);const s=record(t.input);if("text"===s.type)exact(s,["type"]);else{exact(s,["type","allow_other","options"]),(!["single_select","multi_select"].includes(String(s.type))||"boolean"!=typeof s.allow_other||!Array.isArray(s.options)||s.options.length<2||s.options.length>10)&&fail();const e=[];for(const t of s.options){const s=record(t);exact(s,["value","label"],["description"]),ascii(s.value,128)&&text(s.label,200)&&(void 0===s.description||bounded(s.description,1024))||fail(),e.push(s.value)}unique(e)||fail()}}return unique(i)||fail(),s}export function decodeResponse(e,t="user.continue"){const s=base(e,t,"question_response");if(exact(s,["schema_version","kind","request_id","outcome",..."answered"===s.outcome?["answers"]:[]],["message"]),void 0===s.message||bounded(s.message,2048)||fail(),"cancelled"===s.outcome)return s;"answered"!==s.outcome&&fail();const i=record(s.answers);(Object.keys(i).length<1||Object.keys(i).length>3)&&fail();for(const[e,t]of Object.entries(i)){ascii(e,64)||fail();const s=record(t);Object.hasOwn(s,"text")?(exact(s,["text"]),text(s.text,4096)||fail()):(exact(s,["values"]),(!Array.isArray(s.values)||s.values.length<1||s.values.length>10||!s.values.every(e=>ascii(e,128))||!unique(s.values))&&fail())}return s}export function validateResponse(e,t){if(e.request_id!==t.request_id&&fail("PENDING_NOT_FOUND"),"cancelled"!==t.outcome){Object.keys(t.answers).sort().join("\0")!==e.questions.map(e=>e.id).sort().join("\0")&&fail();for(const s of e.questions){const e=t.answers[s.id];if("text"===s.input.type)void 0===e.text&&fail();else if(void 0!==e.text)s.input.allow_other||fail();else{const t=s.input;("single_select"===t.type&&1!==e.values.length||e.values.some(e=>!t.options.some(t=>t.value===e)))&&fail()}}}}
|