@lotagate/cli 0.1.25 → 0.1.26
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 +5 -4
- package/bin/lotagate.mjs +50 -8
- package/dist/application/agent/agent-constants.d.ts +6 -0
- package/dist/application/agent/agent-constants.js +1 -0
- package/dist/application/agent/agent-session-store-registry.d.ts +16 -0
- package/dist/application/agent/agent-session-store-registry.js +1 -0
- package/dist/application/agent/agent-session-store.d.ts +4 -0
- package/dist/application/agent/agent-session-store.js +5 -5
- package/dist/application/agent/capability-profile.d.ts +0 -38
- package/dist/application/agent/capability-profile.js +0 -1
- package/dist/application/agent/cli-agent-execution-service.d.ts +22 -12
- package/dist/application/agent/cli-agent-execution-service.js +1 -1
- package/dist/application/agent/cli-agent-preparation-cache.d.ts +39 -0
- package/dist/application/agent/cli-agent-preparation-cache.js +1 -0
- package/dist/application/agent/context-compaction.d.ts +1 -0
- package/dist/application/agent/context-compaction.js +7 -6
- package/dist/application/agent/context-token-cache.d.ts +21 -0
- package/dist/application/agent/context-token-cache.js +4 -0
- package/dist/application/agent/context-usage-service.d.ts +52 -0
- package/dist/application/agent/context-usage-service.js +1 -0
- package/dist/application/agent/continuation-checkpoint.d.ts +15 -0
- package/dist/application/agent/continuation-checkpoint.js +2 -0
- package/dist/application/agent/interactive-agent-guidance.d.ts +3 -3
- package/dist/application/agent/interactive-agent-guidance.js +2 -2
- package/dist/application/chat/pending-attachment-store.d.ts +5 -2
- package/dist/application/chat/pending-attachment-store.js +1 -1
- package/dist/application/commands/application-command-executor.js +1 -1
- package/dist/application/commands/command-catalog.js +1 -1
- package/dist/application/commands/goal-command-handlers.js +6 -6
- package/dist/application/commands/media-command-handlers.js +7 -6
- package/dist/application/commands/runtime-command-handlers.js +9 -9
- package/dist/application/intent/completion-gate.d.ts +2 -5
- package/dist/application/intent/completion-gate.js +1 -1
- package/dist/application/intent/evidence-ledger.d.ts +2 -1
- package/dist/application/intent/evidence-recording-tool-executor.js +1 -1
- package/dist/application/intent/intent-compiler.js +2 -2
- package/dist/application/memory/memory-extractor.js +2 -2
- package/dist/application/model/cached-model-catalog.d.ts +7 -1
- package/dist/application/model/cached-model-catalog.js +1 -1
- package/dist/application/model/model-catalog-service.js +1 -1
- package/dist/application/model/model-context-window.js +1 -1
- package/dist/application/model/model-response-retry.d.ts +7 -1
- package/dist/application/model/model-response-retry.js +1 -1
- package/dist/application/orchestration/cli-subagent-service.d.ts +4 -0
- package/dist/application/orchestration/cli-subagent-service.js +3 -3
- package/dist/application/orchestration/cli-turn-event-projector.js +1 -1
- package/dist/application/orchestration/cli-turn-events.d.ts +0 -7
- package/dist/application/orchestration/goal-input.d.ts +1 -1
- package/dist/application/orchestration/goal-input.js +1 -1
- package/dist/application/orchestration/headless-agent-turn.d.ts +1 -0
- package/dist/application/orchestration/headless-agent-turn.js +3 -3
- package/dist/application/orchestration/orchestration-constants.d.ts +3 -0
- package/dist/application/orchestration/orchestration-constants.js +1 -0
- package/dist/application/orchestration/subagent-handoff.js +3 -3
- package/dist/application/project-intelligence/project-intelligence-constants.d.ts +56 -0
- package/dist/application/project-intelligence/project-intelligence-constants.js +1 -0
- package/dist/application/project-intelligence/project-intelligence-service.js +1 -1
- package/dist/application/protocol/browser-host-tool-executor.d.ts +13 -3
- package/dist/application/protocol/browser-host-tool-executor.js +2 -2
- package/dist/application/protocol/computer-host-tool-executor.d.ts +3 -0
- package/dist/application/protocol/computer-host-tool-executor.js +2 -2
- package/dist/application/protocol/desktop-agent-command.d.ts +2 -2
- package/dist/application/protocol/desktop-agent-command.js +2 -2
- package/dist/application/protocol/desktop-agent-contracts.d.ts +34 -0
- package/dist/application/protocol/desktop-agent-contracts.js +1 -0
- package/dist/application/protocol/desktop-agent-helpers.d.ts +5 -0
- package/dist/application/protocol/desktop-agent-helpers.js +1 -0
- package/dist/application/protocol/desktop-agent-request-parser.d.ts +31 -6
- package/dist/application/protocol/desktop-agent-request-parser.js +1 -1
- package/dist/application/protocol/desktop-approval-broker.d.ts +14 -4
- package/dist/application/protocol/desktop-approval-broker.js +1 -1
- package/dist/application/protocol/desktop-attachment-store.d.ts +19 -2
- package/dist/application/protocol/desktop-attachment-store.js +1 -1
- package/dist/application/protocol/desktop-execution-policy.d.ts +2 -0
- package/dist/application/protocol/desktop-execution-policy.js +1 -1
- package/dist/application/protocol/desktop-host-bridge.d.ts +33 -0
- package/dist/application/protocol/desktop-host-bridge.js +1 -0
- package/dist/application/protocol/desktop-host-capabilities.d.ts +12 -0
- package/dist/application/protocol/desktop-host-capabilities.js +1 -0
- package/dist/application/protocol/desktop-host-tool-executor.d.ts +15 -2
- package/dist/application/protocol/desktop-host-tool-executor.js +2 -2
- package/dist/application/protocol/desktop-intent-event-writer.js +1 -1
- package/dist/application/protocol/desktop-protocol-helpers.js +1 -1
- package/dist/application/protocol/json-line-writer.d.ts +24 -2
- package/dist/application/protocol/json-line-writer.js +2 -2
- package/dist/application/protocol/jsonl-agent-command.d.ts +1 -1
- package/dist/application/protocol/jsonl-agent-command.js +2 -2
- package/dist/application/protocol/jsonl-protocol.d.ts +1 -1
- package/dist/application/protocol/jsonl-protocol.js +2 -2
- package/dist/application/security/desktop-tool-scope-policy.js +1 -1
- package/dist/application/security/tool-action-risk-analyzer.js +1 -1
- package/dist/application/security/tool-approval-service.d.ts +9 -3
- package/dist/application/security/tool-approval-service.js +1 -1
- package/dist/application/security/tool-policy-registry.d.ts +20 -0
- package/dist/application/security/tool-policy-registry.js +1 -0
- package/dist/application/security/tool-risk-classifier.d.ts +2 -1
- package/dist/application/security/tool-risk-classifier.js +1 -1
- package/dist/application/security/workspace-permission-policy.js +1 -1
- package/dist/application/workspace/session-execution-workspace.d.ts +2 -2
- package/dist/application/workspace/session-execution-workspace.js +3 -3
- package/dist/application/workspace/workspace-constants.d.ts +17 -0
- package/dist/application/workspace/workspace-constants.js +1 -0
- package/dist/application/workspace/workspace-file-index.d.ts +0 -1
- package/dist/application/workspace/workspace-file-index.js +1 -1
- package/dist/application/workspace/workspace-instruction-loader.js +1 -1
- package/dist/bootstrap/composition-root.d.ts +1 -0
- package/dist/bootstrap/runtime-factory.d.ts +5 -0
- package/dist/bootstrap/runtime-factory.js +1 -1
- package/dist/domain/errors/cli-error.js +1 -1
- package/dist/domain/extensions/extension-constants.d.ts +1 -0
- package/dist/domain/extensions/extension-constants.js +1 -0
- package/dist/domain/extensions/host-tool-plugin-ids.d.ts +0 -4
- package/dist/domain/extensions/host-tool-plugin-ids.js +1 -1
- package/dist/domain/extensions/plugin-manifest.js +1 -1
- package/dist/domain/intent/intent-contract.d.ts +0 -8
- package/dist/domain/intent/intent-contract.js +1 -1
- package/dist/domain/media/gateway-media-contract.js +1 -1
- package/dist/domain/media/media-constants.d.ts +3 -0
- package/dist/domain/media/media-constants.js +1 -0
- package/dist/domain/media/media-execution-policy.js +1 -1
- package/dist/domain/memory/memory-constants.d.ts +3 -0
- package/dist/domain/memory/memory-constants.js +1 -0
- package/dist/domain/memory/memory-search.js +2 -2
- package/dist/domain/orchestration/goal.d.ts +0 -1
- package/dist/domain/orchestration/goal.js +1 -1
- package/dist/domain/orchestration/work-plan.d.ts +0 -62
- package/dist/domain/orchestration/work-plan.js +0 -1
- package/dist/domain/runtime/assistant-stream-text.d.ts +11 -0
- package/dist/domain/runtime/assistant-stream-text.js +1 -0
- package/dist/domain/runtime/gateway-request-budget.d.ts +19 -0
- package/dist/domain/runtime/gateway-request-budget.js +1 -0
- package/dist/domain/runtime/resource-limits.d.ts +17 -0
- package/dist/domain/runtime/resource-limits.js +1 -1
- package/dist/domain/session/attachment.d.ts +2 -0
- package/dist/domain/workspace/workspace-settings.d.ts +2 -0
- package/dist/domain/workspace/workspace-settings.js +1 -1
- package/dist/infrastructure/agent-sdk/model-response-metrics.d.ts +7 -0
- package/dist/infrastructure/agent-sdk/model-response-metrics.js +1 -0
- package/dist/infrastructure/agent-sdk/model-tool-name-mapper.js +1 -1
- package/dist/infrastructure/agent-sdk/sdk-constants.d.ts +2 -0
- package/dist/infrastructure/agent-sdk/sdk-constants.js +1 -0
- package/dist/infrastructure/agent-sdk/tool-calling-model-client.js +1 -1
- package/dist/infrastructure/cache/cache-policy.d.ts +2 -0
- package/dist/infrastructure/cache/cache-policy.js +1 -1
- package/dist/infrastructure/cache/file-cache-store.d.ts +8 -0
- package/dist/infrastructure/cache/file-cache-store.js +2 -2
- package/dist/infrastructure/clipboard/clipboard-constants.d.ts +1 -0
- package/dist/infrastructure/clipboard/clipboard-constants.js +1 -0
- package/dist/infrastructure/clipboard/image-normalizer.js +1 -1
- package/dist/infrastructure/extensions/extensions-constants.d.ts +6 -0
- package/dist/infrastructure/extensions/extensions-constants.js +1 -0
- package/dist/infrastructure/extensions/mcp-runtime.d.ts +7 -0
- package/dist/infrastructure/extensions/mcp-runtime.js +1 -1
- package/dist/infrastructure/extensions/plugin-manager.js +2 -2
- package/dist/infrastructure/extensions/skill-tool-executor.d.ts +3 -1
- package/dist/infrastructure/extensions/skill-tool-executor.js +2 -2
- package/dist/infrastructure/filesystem/atomic-file-store.d.ts +11 -0
- package/dist/infrastructure/filesystem/atomic-file-store.js +1 -1
- package/dist/infrastructure/filesystem/filesystem-constants.d.ts +8 -0
- package/dist/infrastructure/filesystem/filesystem-constants.js +1 -0
- package/dist/infrastructure/filesystem/local-filesystem-tool-executor.d.ts +3 -0
- package/dist/infrastructure/filesystem/local-filesystem-tool-executor.js +3 -3
- package/dist/infrastructure/logging/daily-rotating-file-writer.d.ts +1 -1
- package/dist/infrastructure/logging/daily-rotating-file-writer.js +2 -2
- package/dist/infrastructure/logging/log-redactor.js +1 -1
- package/dist/infrastructure/logging/logging-constants.d.ts +3 -0
- package/dist/infrastructure/logging/logging-constants.js +1 -0
- package/dist/infrastructure/logging/structured-logger.d.ts +1 -1
- package/dist/infrastructure/logging/structured-logger.js +2 -2
- package/dist/infrastructure/observability/tool-execution-logger.d.ts +11 -0
- package/dist/infrastructure/observability/tool-execution-logger.js +1 -0
- package/dist/infrastructure/platform/host-platform.d.ts +2 -0
- package/dist/infrastructure/platform/host-platform.js +1 -1
- package/dist/infrastructure/platform/host-process.d.ts +3 -1
- package/dist/infrastructure/platform/host-process.js +1 -1
- package/dist/infrastructure/platform/platform-constants.d.ts +4 -0
- package/dist/infrastructure/platform/platform-constants.js +1 -0
- package/dist/infrastructure/platform/process-identity.d.ts +8 -0
- package/dist/infrastructure/platform/process-identity.js +1 -0
- package/dist/infrastructure/platform/read-only-executable.d.ts +2 -1
- package/dist/infrastructure/platform/read-only-executable.js +1 -1
- package/dist/infrastructure/sdk/lotagate-client-factory.js +1 -1
- package/dist/infrastructure/sdk/sdk-media-gateway-client.d.ts +4 -0
- package/dist/infrastructure/sdk/sdk-media-gateway-client.js +1 -1
- package/dist/infrastructure/sessions/file-session-store.d.ts +3 -0
- package/dist/infrastructure/sessions/file-session-store.js +4 -4
- package/dist/infrastructure/sessions/session-constants.d.ts +3 -0
- package/dist/infrastructure/sessions/session-constants.js +1 -0
- package/dist/infrastructure/sessions/session-index.js +3 -3
- package/dist/infrastructure/sessions/session-journal-health.d.ts +7 -0
- package/dist/infrastructure/sessions/session-journal-health.js +1 -0
- package/dist/infrastructure/shell/shell-tool-executor.d.ts +5 -1
- package/dist/infrastructure/shell/shell-tool-executor.js +2 -2
- package/dist/infrastructure/tokens/tiktoken-token-estimator.js +1 -1
- package/dist/infrastructure/tokens/token-constants.d.ts +1 -0
- package/dist/infrastructure/tokens/token-constants.js +1 -0
- package/dist/infrastructure/workspace/workspace-settings-store.d.ts +1 -0
- package/dist/infrastructure/workspace/workspace-settings-store.js +4 -4
- package/dist/main.js +3 -3
- package/dist/ports/media-gateway-client.d.ts +6 -0
- package/dist/ports/session-store.d.ts +1 -0
- package/dist/presentation/tui/ink/components/composer.js +4 -4
- package/dist/presentation/tui/ink/components/transcript.d.ts +1 -0
- package/dist/presentation/tui/ink/components/transcript.js +5 -5
- package/dist/presentation/tui/ink/tui-bridge.d.ts +2 -2
- package/dist/presentation/tui/ink/tui-bridge.js +1 -1
- package/dist/presentation/tui/tui-agent-turn-controller.d.ts +45 -0
- package/dist/presentation/tui/tui-agent-turn-controller.js +1 -0
- package/dist/presentation/tui/tui-application-contract.d.ts +1 -0
- package/dist/presentation/tui/tui-application.d.ts +1 -4
- package/dist/presentation/tui/tui-application.js +3 -3
- package/dist/presentation/tui/tui-clipboard-paste-handlers.d.ts +2 -2
- package/dist/presentation/tui/tui-clipboard-paste-handlers.js +1 -1
- package/dist/presentation/tui/tui-command-executor.js +3 -3
- package/dist/presentation/tui/tui-context-compaction.d.ts +4 -1
- package/dist/presentation/tui/tui-context-compaction.js +1 -1
- package/dist/presentation/tui/tui-context-service.d.ts +2 -34
- package/dist/presentation/tui/tui-context-service.js +1 -2
- package/dist/presentation/tui/tui-initial-model.js +1 -1
- package/dist/presentation/tui/tui-tool-approval.d.ts +1 -1
- package/dist/presentation/tui/tui-tool-approval.js +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/native-targets.mjs +1 -1
- package/package.json +9 -10
- package/scripts/install-native.mjs +5 -13
- package/bin/lotagate.exe +0 -1
- package/dist/application/protocol/document-host-tool-definitions.d.ts +0 -4
- package/dist/application/protocol/document-host-tool-definitions.js +0 -1
- package/dist/application/protocol/document-host-tool-executor.d.ts +0 -49
- package/dist/application/protocol/document-host-tool-executor.js +0 -2
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import{randomUUID as U}from"node:crypto";import{AuthenticationService as k}from"../auth/authentication-service.js";import{createLotaGateClient as N}from"../../infrastructure/sdk/lotagate-client-factory.js";import{ModelCatalogAdapter as q}from"../../infrastructure/sdk/model-catalog-adapter.js";import{loadExecutionConfig as T}from"../config/execution-config.js";import{prepareCliAgent as F,warmMcpRuntime as z}from"../agent/cli-agent-execution-service.js";import{createContextManagement as Q}from"../agent/context-compaction.js";import{persistContextCompaction as X}from"../agent/context-compaction-persistence.js";import{CliAgentSessionStore as Z}from"../agent/agent-session-store.js";import{CliTurnEventProjector as tt}from"../orchestration/cli-turn-event-projector.js";import{persistCliTurnEvent as et}from"../orchestration/cli-turn-persistence.js";import{createTurnId as ot,recordTurnCompletion as st,recordTurnFailure as it,recordTurnStart as nt}from"../session/session-turn-lifecycle.js";import{ProjectSessionAccess as G}from"../session/project-session-access.js";import{permissionRuleForRequest as at}from"../security/workspace-permission-policy.js";import{agentLimitNotice as rt,classifyAgentLimit as ct}from"../agent/agent-limit.js";import{CliError as f}from"../../domain/errors/cli-error.js";import{CLI_ERROR_CODES as g}from"../../domain/errors/error-codes.js";import{readLinesBounded as dt}from"./jsonl-protocol.js";import{RESOURCE_LIMITS as lt}from"../../domain/runtime/resource-limits.js";import{getDesktopCommandCatalog as mt}from"./desktop-command-catalog.js";import{COMMAND_REGISTRY as pt}from"../commands/command-runtime.js";import{executeDesktopCommand as ut}from"./desktop-command-executor.js";import{DesktopAttachmentStore as ht}from"./desktop-attachment-store.js";import{SubagentJobRegistry as wt}from"../orchestration/cli-subagent-service.js";import{subagentDesktopPayload as ft}from"./desktop-orchestration-events.js";import{DesktopIntentEventWriter as gt}from"./desktop-intent-event-writer.js";import{resolveModelContextWindow as bt}from"../model/model-context-window.js";import{JsonLineWriter as Et}from"./json-line-writer.js";import{desktopApprovalMode as vt,sessionResult as J,toDesktopError as E}from"./desktop-protocol-helpers.js";import{DesktopApprovalBroker as yt}from"./desktop-approval-broker.js";import{SessionExecutionWorkspaceService as It}from"../workspace/session-execution-workspace.js";import{buildUserMessage as kt}from"../../infrastructure/clipboard/image-normalizer.js";import{isBrowserHostResponse as Rt,isComputerHostResponse as xt,isDesktopHostResponse as Tt,isDocumentHostResponse as St,parseDesktopRequest as Ct}from"./desktop-agent-request-parser.js";import{desktopMcpEvent as K}from"./desktop-mcp-events.js";import{DesktopExtensionService as At}from"./desktop-extension-service.js";import{DesktopExtensionRequestHandler as Dt}from"./desktop-extension-request-handler.js";const S=1,Mt=25e3;async function we(P,t,e,o){return new Nt(P,t,o).run(e)}class Nt{runtime;cwd;writer;turns=new Map;activeTurnBySession=new Map;subagentJobs=new wt;sessionStores=new Map;turnContexts=new Map;attachments=new ht;approvalBroker=new yt((t,e)=>this.writeEvent(t,e));commands=new Map;browserRequests=new Map;hostRequests=new Map;computerRequests=new Map;documentRequests=new Map;extensions=new At;extensionRequests;intentEvents;fallbackExecutionWorkspaces;browserHostEnabled=!1;computerHostEnabled=!1;documentHostEnabled=!1;stopping=!1;constructor(t,e,o){this.runtime=t,this.cwd=e,this.writer=new Et(o),this.extensionRequests=new Dt(this.extensions,(s,i)=>this.respond(s,i),this.cwd),this.intentEvents=new gt({writeEvent:(s,i)=>this.writeEvent(s,i),log:(s,i)=>this.logInfo(s,i)})}async run(t){let e=0;try{for await(const o of dt(t,lt.jsonlLineBytes)){if(o.trim().length===0)continue;let s;try{s=JSON.parse(o)}catch(a){e+=1,await this.writeError("unknown","protocol",E(a));continue}if(Rt(s)){this.resolveBrowserRequest(s);continue}if(Tt(s)){this.resolveDesktopHostRequest(s);continue}if(xt(s)){this.resolveComputerHostRequest(s);continue}if(St(s)){this.resolveDocumentHostRequest(s);continue}let i;try{i=Ct(s)}catch(a){e+=1,await this.writeError("unknown","protocol",E(a));continue}try{await this.handle(i)}catch(a){e+=1,await this.writeError(i.id,i.method,E(a))}if(this.stopping)break}}catch(o){e+=1,await this.writeError("unknown","protocol",E(o))}finally{for(const o of this.turns.values())o.controller.abort();await Promise.allSettled([...this.turns.values()].map(o=>o.operation));for(const o of this.commands.values())o.controller.abort();await Promise.allSettled([...this.commands.values()].map(o=>o.operation)),await this.subagentJobs.close(),this.approvalBroker.close();for(const o of this.browserRequests.values())o.onAbort!==void 0&&o.signal?.removeEventListener("abort",o.onAbort),o.reject(new Error("The Desktop browser bridge was closed."));this.browserRequests.clear();for(const o of this.hostRequests.values())o.onAbort!==void 0&&o.signal?.removeEventListener("abort",o.onAbort),o.reject(new Error("The Desktop execution bridge was closed."));this.hostRequests.clear();for(const o of this.computerRequests.values())o.onAbort!==void 0&&o.signal?.removeEventListener("abort",o.onAbort),o.reject(new Error("The Desktop computer bridge was closed."));this.computerRequests.clear();for(const o of this.documentRequests.values())o.onAbort!==void 0&&o.signal?.removeEventListener("abort",o.onAbort),o.reject(new Error("The Desktop document bridge was closed."));this.documentRequests.clear(),this.attachments.clear(),await this.writer.flush()}return e===0?0:1}async handle(t){switch(t.method){case"initialize":return this.browserHostEnabled=t.params.browserHost===!0,this.computerHostEnabled=t.params.computerHost===!0,this.documentHostEnabled=t.params.documentHost===!0,this.respond(t,{protocol:"lotagate.desktop",version:S,capabilities:["sessions","streaming","tool-approval","project-trust","models","auth","commands","image-attachments","file-attachments","execution-context","tool-allowlist","approval-reviews","lifecycle-controls","execution-broker","intent-runtime","host-evidence","conversational-progress","local-memory-commands","extensions",...this.browserHostEnabled?["browser-host"]:[],...this.computerHostEnabled?["computer-host"]:[],...this.documentHostEnabled?["document-host"]:[]]});case"session.create":return this.createSession(t);case"title.generate":return this.generateTitle(t);case"session.list":return this.listSessions(t);case"session.resume":return this.resumeSession(t);case"extension.listPublicPlugins":case"extension.resolvePublicPluginSource":case"extension.readPublicPluginContribution":case"extension.readDetail":case"extension.readPluginIcon":return this.extensionRequests.handle(t);case"attachment.begin":return this.attachments.begin({id:t.params.attachmentId,name:t.params.name,mimeType:t.params.mimeType,sizeBytes:t.params.sizeBytes}),this.respond(t,{accepted:!0,attachmentId:t.params.attachmentId});case"attachment.chunk":return this.attachments.appendChunk(t.params.attachmentId,t.params.index,t.params.dataBase64),this.respond(t,{accepted:!0,attachmentId:t.params.attachmentId,index:t.params.index});case"attachment.complete":return this.attachments.complete(t.params.attachmentId),this.respond(t,{accepted:!0,attachmentId:t.params.attachmentId});case"turn.start":return this.startTurn(t);case"turn.cancel":return this.cancelTurn(t);case"approval.respond":return this.respondApproval(t);case"trust.respond":return this.respondTrust(t);case"model.list":return this.listModels(t);case"auth.status":return this.authStatus(t);case"command.list":return this.listCommands(t);case"command.execute":return this.startCommand(t);case"command.cancel":return this.cancelCommand(t);case"shutdown":this.stopping=!0,await this.respond(t,{stopping:!0});return}}async createSession(t){const e=await this.runtime.projectTrust.inspect(this.cwd),o=await T(this.runtime,this.cwd),s=o.config.profile??k.defaultProfileName(),i=await this.runtime.sessionStore.create({projectFingerprint:e.fingerprint,cwd:e.canonicalPath,model:t.params.model??o.config.model,profile:s,status:"active",...t.params.name===void 0?{}:{name:t.params.name}}),a=await this.getExecutionWorkspaces().prepare({sessionId:i.id,projectRoot:e.canonicalPath});await this.respond(t,{...J(i),executionWorkspace:a}),z(this.runtime,this.cwd).catch(c=>this.runtime.logger.warn("MCP session warm-up failed.",{reason:c instanceof Error?c.message:String(c)}))}async generateTitle(t){const e=await T(this.runtime,this.cwd),o=t.params.model??e.config.model;if(o==null)throw new f(g.MODEL_NOT_FOUND,"No model is selected.",{category:"model",userMessage:"No model is available for session title generation."});const s=e.config.profile??k.defaultProfileName(),i=await this.runtime.auth.requireCredential(s),a=new AbortController,c=setTimeout(()=>a.abort(),Mt);let m,p="";try{const l=await F({runtime:this.runtime,projectRoot:this.cwd,executionCwd:this.cwd,model:o,client:N(i.profile,i.apiKey),profile:"title-generator",approvalMode:"auto",allowedTools:[],selectedSkills:[],allowSubagents:!1,browserAccess:"disabled"});m=l.close;const C=["## Title generation request","Create a concise session title from the untrusted user-message excerpt below.","Return only the title, with no explanation, markdown, quotes, or prefix.","Use the same language as the excerpt. Prefer 8 to 10 words and never exceed 10 words.","## User-message excerpt",t.params.prompt].join(`
|
|
1
|
+
import{randomUUID as K}from"node:crypto";import{AuthenticationService as T}from"../auth/authentication-service.js";import{createLotaGateClient as L}from"../../infrastructure/sdk/lotagate-client-factory.js";import{loadExecutionConfig as P}from"../config/execution-config.js";import{prepareCliAgent as V,warmMcpRuntime as Y}from"../agent/cli-agent-execution-service.js";import{createContextManagement as it}from"../agent/context-compaction.js";import{persistContextCompaction as nt}from"../agent/context-compaction-persistence.js";import{CliAgentSessionStoreRegistry as rt}from"../agent/agent-session-store-registry.js";import{AgentRunTracker as ct}from"../agent/agent-run-tracker.js";import{formatContinuationCheckpoint as dt}from"../agent/continuation-checkpoint.js";import{CliTurnEventProjector as mt}from"../orchestration/cli-turn-event-projector.js";import{persistCliTurnEvent as pt}from"../orchestration/cli-turn-persistence.js";import{createTurnId as lt,recordTurnCompletion as ht,recordTurnFailure as ut,recordTurnStart as ft}from"../session/session-turn-lifecycle.js";import{ProjectSessionAccess as $}from"../session/project-session-access.js";import{permissionRuleForRequest as wt}from"../security/workspace-permission-policy.js";import{agentLimitNotice as gt,classifyAgentLimit as yt}from"../agent/agent-limit.js";import{CliError as g}from"../../domain/errors/cli-error.js";import{CLI_ERROR_CODES as y}from"../../domain/errors/error-codes.js";import{readLinesBounded as Et}from"./jsonl-protocol.js";import{RESOURCE_LIMITS as Q}from"../../domain/runtime/resource-limits.js";import{getDesktopCommandCatalog as kt}from"./desktop-command-catalog.js";import{COMMAND_REGISTRY as X}from"../commands/command-runtime.js";import{executeDesktopCommand as bt}from"./desktop-command-executor.js";import{DesktopAttachmentStore as It}from"./desktop-attachment-store.js";import{SubagentJobRegistry as vt}from"../orchestration/cli-subagent-service.js";import{subagentDesktopPayload as St}from"./desktop-orchestration-events.js";import{DesktopIntentEventWriter as Tt}from"./desktop-intent-event-writer.js";import{resolveModelContextWindow as xt}from"../model/model-context-window.js";import{ContextUsageService as Ct}from"../agent/context-usage-service.js";import{isCliAgentPreparationMutation as Mt}from"../agent/cli-agent-preparation-cache.js";import{JsonLineWriter as Nt}from"./json-line-writer.js";import{desktopApprovalMode as Pt,sessionResult as Z,toDesktopError as b}from"./desktop-protocol-helpers.js";import{DesktopApprovalBroker as Rt}from"./desktop-approval-broker.js";import{SessionExecutionWorkspaceService as Dt}from"../workspace/session-execution-workspace.js";import{buildUserMessage as At}from"../../infrastructure/clipboard/image-normalizer.js";import{isBrowserHostResponse as Bt,isComputerHostResponse as Ot,isDesktopHostResponse as Lt,parseDesktopRequest as _t}from"./desktop-agent-request-parser.js";import{desktopMcpEvent as q}from"./desktop-mcp-events.js";import{DesktopExtensionService as jt}from"./desktop-extension-service.js";import{DesktopExtensionRequestHandler as Ht}from"./desktop-extension-request-handler.js";import{DesktopHostBridge as Wt}from"./desktop-host-bridge.js";import{normalizeDesktopHostCapabilities as Ut}from"./desktop-host-capabilities.js";import{loadModelCatalog as tt}from"../model/model-catalog-service.js";import{SdkMediaGatewayClient as Gt}from"../../infrastructure/sdk/sdk-media-gateway-client.js";import{artifactIdsData as _,resolveSpeechModel as Ft}from"./desktop-agent-helpers.js";import{DESKTOP_PROTOCOL_VERSION as R,DESKTOP_SPEECH_MAX_BYTES as zt,DESKTOP_SPEECH_SYNTHESIS_TIMEOUT_MS as Jt,TITLE_GENERATION_TIMEOUT_MS as Kt}from"./desktop-agent-contracts.js";import{createIsolatedGuestPlatform as Vt}from"../../infrastructure/platform/host-platform.js";import{evaluateGatewayRequestBudget as Yt}from"../../domain/runtime/gateway-request-budget.js";import{DESKTOP_PROTOCOL_VERSION as Ke}from"./desktop-agent-contracts.js";async function Fe(j,t,e,s,o){return new $t(j,t,s).run(e,o)}class $t{runtime;cwd;writer;turns=new Map;activeTurnBySession=new Map;subagentJobs=new vt;sessionStores=new rt(Q.desktopSessionStoreMaxEntries);turnContexts=new Map;attachments=new It;approvalBroker=new Rt((t,e)=>this.writeEvent(t,e));commands=new Map;speechRequests=new Map;hostBridge;extensions=new jt;extensionRequests;intentEvents;executionWorkspaces;browserHostEnabled=!1;computerHostEnabled=!1;desktopHostEnabled=!1;hostCapabilities;stopping=!1;constructor(t,e,s){this.runtime=t,this.cwd=e,this.writer=new Nt(s),this.executionWorkspaces=t.executionWorkspaces??new Dt(t.paths.globalConfigDir,t.platform),this.hostBridge=new Wt(this.writer,()=>this.stopping),this.extensionRequests=new Ht(this.extensions,(o,a)=>this.respond(o,a),this.cwd),this.intentEvents=new Tt({writeEvent:(o,a)=>this.writeEvent(o,a),log:(o,a)=>this.logInfo(o,a)})}async run(t,e){let s=0;const o=()=>{this.stopping=!0;for(const a of this.turns.values())a.controller.abort(e?.reason);for(const a of this.commands.values())a.controller.abort(e?.reason);for(const a of this.speechRequests.values())a.abort(e?.reason);t.destroy()};e?.addEventListener("abort",o,{once:!0});try{for await(const a of Et(t,Q.desktopJsonlLineBytes)){if(a.trim().length===0)continue;let r;try{r=JSON.parse(a)}catch(m){s+=1,await this.writeError("unknown","protocol",b(m));continue}if(Bt(r)){this.hostBridge.resolveBrowser(r);continue}if(Lt(r)){this.hostBridge.resolveDesktop(r);continue}if(Ot(r)){this.hostBridge.resolveComputer(r);continue}let c;try{c=_t(r)}catch(m){s+=1,await this.writeError("unknown","protocol",b(m));continue}try{await this.handle(c)}catch(m){this.runtime.logger.error("CLI Desktop request failed.",{method:c.method,requestId:c.id,reason:m instanceof Error?m.message:String(m)}),s+=1,await this.writeError(c.id,c.method,b(m))}if(this.stopping)break}}catch(a){s+=1,await this.writeError("unknown","protocol",b(a))}finally{e?.removeEventListener("abort",o);for(const a of this.turns.values())a.controller.abort();await Promise.allSettled([...this.turns.values()].map(a=>a.operation));for(const a of this.commands.values())a.controller.abort();await Promise.allSettled([...this.commands.values()].map(a=>a.operation));for(const a of this.speechRequests.values())a.abort();await this.subagentJobs.close(),this.sessionStores.clear(),this.approvalBroker.close(),this.hostBridge.close(),this.attachments.clear(),await this.writer.flush(),this.writer.close()}return s===0?0:1}async handle(t){switch(t.method){case"initialize":return this.browserHostEnabled=t.params.browserHost===!0,this.computerHostEnabled=t.params.computerHost===!0,this.desktopHostEnabled=t.params.executionBroker===!0,this.hostCapabilities=Ut(t.params.hostCapabilities),this.respond(t,{protocol:"lotagate.desktop",version:R,capabilities:["sessions","streaming","tool-approval","project-trust","models","auth","commands","image-attachments","file-attachments","execution-context","tool-allowlist","approval-reviews","lifecycle-controls",...this.desktopHostEnabled?["execution-broker"]:[],"intent-runtime","host-evidence","conversational-progress","local-memory-commands","extensions","speech-synthesis",...this.browserHostEnabled?["browser-host"]:[],...this.computerHostEnabled?["computer-host"]:[]]});case"session.create":return this.createSession(t);case"title.generate":return this.generateTitle(t);case"session.list":return this.listSessions(t);case"session.resume":return this.resumeSession(t);case"extension.listPublicPlugins":case"extension.resolvePublicPluginSource":case"extension.readPublicPluginContribution":case"extension.readDetail":case"extension.readPluginIcon":return this.extensionRequests.handle(t);case"attachment.begin":return this.attachments.begin({id:t.params.attachmentId,...t.params.sessionId===void 0?{}:{sessionId:t.params.sessionId},name:t.params.name,mimeType:t.params.mimeType,sizeBytes:t.params.sizeBytes,...t.params.storageName===void 0?{}:{storageName:t.params.storageName}}),this.respond(t,{accepted:!0,attachmentId:t.params.attachmentId});case"attachment.chunk":return this.attachments.appendChunk(t.params.attachmentId,t.params.index,t.params.dataBase64),this.respond(t,{accepted:!0,attachmentId:t.params.attachmentId,index:t.params.index});case"attachment.complete":{const e=this.attachments.complete(t.params.attachmentId);if(e.sessionId.length===0)return this.respond(t,{accepted:!0,attachmentId:t.params.attachmentId});const s=await this.runtime.sessionStore.saveAttachment(e.sessionId,e.attachment,e.bytes);return this.attachments.replace(t.params.attachmentId,{...e,attachment:s}),this.respond(t,{accepted:!0,attachmentId:t.params.attachmentId,storagePath:s.storagePath})}case"attachment.delete":{this.attachments.delete(t.params.attachmentId,t.params.sessionId);const e=await this.runtime.sessionStore.deleteAttachment(t.params.sessionId,t.params.attachmentId);return this.respond(t,{accepted:!0,attachmentId:t.params.attachmentId,deleted:e})}case"turn.start":return this.startTurn(t);case"turn.cancel":return this.cancelTurn(t);case"approval.respond":return this.respondApproval(t);case"trust.respond":return this.respondTrust(t);case"model.list":return this.listModels(t);case"auth.status":return this.authStatus(t);case"speech.synthesize":return this.synthesizeSpeech(t);case"command.list":return this.listCommands(t);case"command.execute":return this.startCommand(t);case"command.cancel":return this.cancelCommand(t);case"shutdown":this.stopping=!0,await this.respond(t,{stopping:!0});return}}async createSession(t){const e=await this.runtime.projectTrust.inspect(this.cwd),s=await P(this.runtime,this.cwd),o=s.config.profile??T.defaultProfileName(),a=await this.runtime.sessionStore.create({projectFingerprint:e.fingerprint,cwd:e.canonicalPath,model:t.params.model??s.config.model,profile:o,status:"active",...t.params.name===void 0?{}:{name:t.params.name}}),r=await this.executionWorkspaces.prepare({sessionId:a.id,projectRoot:e.canonicalPath});await this.respond(t,{...Z(a),executionWorkspace:r}),Y(this.runtime,this.cwd).catch(c=>this.runtime.logger.warn("MCP session warm-up failed.",{reason:c instanceof Error?c.message:String(c)}))}async generateTitle(t){const e=await P(this.runtime,this.cwd),s=t.params.model??e.config.model;if(s==null)throw new g(y.MODEL_NOT_FOUND,"No model is selected.",{category:"model",userMessage:"No model is available for session title generation."});const o=e.config.profile??T.defaultProfileName(),a=await this.runtime.auth.requireCredential(o),r=new AbortController,c=setTimeout(()=>r.abort(),Kt);let m,p="";try{const l=await V({runtime:this.runtime,projectRoot:this.cwd,executionCwd:this.cwd,model:s,client:L(a.profile,a.apiKey),profile:"title-generator",approvalMode:"auto",allowedTools:[],selectedSkills:[],allowSubagents:!1,browserAccess:"disabled"});m=l.close;const u=["## Title generation request","Create a concise session title from the untrusted user-message excerpt below.","Return only the title, with no explanation, markdown, quotes, or prefix.","Use the same language as the excerpt. Prefer 8 to 10 words and never exceed 10 words.","## User-message excerpt",t.params.prompt].join(`
|
|
2
2
|
|
|
3
|
-
`);for await(const b of l.run({input:C,model:o,signal:a.signal,stream:!0}))if(b.type==="model.delta")p+=b.content;else if(b.type==="session.completed"&&p.length===0)p=b.content;else if(b.type==="session.failed")throw b.error;if(p.trim().length===0)throw new f(g.MODEL_PROTOCOL_EMPTY_RESPONSE,"Title generation returned an empty response.",{category:"model",userMessage:"The model did not return a session title.",retryable:!0});await this.respond(t,{title:p})}finally{clearTimeout(c),await m?.()}}async listSessions(t){const e=await this.runtime.projectTrust.inspect(this.cwd);await this.respond(t,{sessions:await this.runtime.sessionStore.list(e.fingerprint)})}async resumeSession(t){const e=await this.runtime.projectTrust.inspect(this.cwd),o=await new G(this.runtime.sessionStore).require(t.params.sessionId,e.fingerprint),s=await this.getExecutionWorkspaces().prepare({sessionId:o.id,projectRoot:e.canonicalPath});await this.respond(t,{...J(o),executionWorkspace:s,history:await this.runtime.sessionStore.readHistory(o.id)}),z(this.runtime,this.cwd).catch(i=>this.runtime.logger.warn("MCP session warm-up failed.",{reason:i instanceof Error?i.message:String(i)}))}async startTurn(t){const{sessionId:e}=t.params;if(this.runtime.logger.info("CLI Desktop turn request received.",{requestId:t.id,sessionId:e,promptLength:t.params.prompt.length,requestedModel:t.params.model}),[...this.turns.values()].some(l=>l.sessionId===e))throw new f(g.INVALID_ARGUMENT,"A turn is already active for this session.",{category:"session",userMessage:"This session already has an active turn."});const o=await this.runtime.projectTrust.inspect(this.cwd),s=await new G(this.runtime.sessionStore).require(e,o.fingerprint),i=await this.getExecutionWorkspaces().prepare({sessionId:e,projectRoot:o.canonicalPath}),a=this.attachments.take(t.params.attachmentIds??[]),c=ot(),m=new AbortController;this.turnContexts.set(c,{...i,...t.params.taskId===void 0?{}:{taskId:t.params.taskId}}),this.activeTurnBySession.set(e,c);const p=this.executeTurn(t,s,o,i,c,m,a).catch(async l=>{await this.writeEvent(m.signal.aborted?"turn.cancelled":"turn.failed",{turnId:c,sessionId:e,...m.signal.aborted?{}:{error:E(l)}})}).finally(()=>{this.turns.delete(c),this.turnContexts.delete(c),this.activeTurnBySession.get(e)===c&&this.activeTurnBySession.delete(e)});this.turns.set(c,{sessionId:e,controller:m,operation:p}),await this.respond(t,{accepted:!0,turnId:c,sessionId:e})}async executeTurn(t,e,o,s,i,a,c){const m=await T(this.runtime,this.cwd);if(!await this.runtime.projectTrust.isTrusted(o)){const r=U();if(await this.writeEvent("trust.requested",{trustRequestId:r,sessionId:e.id,path:o.canonicalPath}),!await this.approvalBroker.waitForTrust(r,a.signal))throw new f(g.PROJECT_TRUST_REQUIRED,"Project trust was not granted.",{category:"project",userMessage:"Trust this project in the Desktop application before running tools."});await this.runtime.projectTrust.grant(o),await this.runtime.workspaceSettings.ensureInitialized(o.canonicalPath)}const l=t.params.model??e.model??m.config.model;if(l==null)throw new f(g.MODEL_NOT_FOUND,"No model is selected.",{category:"model",userMessage:"Select a model before starting a turn."});const C=t.params.reasoningEffort??m.config.effort;this.runtime.logger.info("CLI Desktop turn model resolved.",{sessionId:e.id,turnId:i,requestedModel:t.params.model,sessionModel:e.model,resolvedModel:l});const b=e.profile??m.config.profile??k.defaultProfileName(),L=await this.runtime.auth.requireCredential(b),H=N(L.profile,L.apiKey),O=await bt(this.runtime,l,b),A=this.sessionStores.get(e.id)??new Z;this.sessionStores.set(e.id,A);const I=[],R=[];for(const r of c)R.push({...r,attachment:await this.runtime.sessionStore.saveAttachment(e.id,r.attachment,r.bytes)});await nt(this.runtime.sessionStore,e.id,i,t.params.prompt,R.map(r=>r.attachment.id)),await this.runtime.sessionStore.updateStatus(e.id,"active"),await this.writeEvent("turn.started",{turnId:i,sessionId:e.id,model:l});const V={onSubagentLifecycle:r=>this.writeEvent(`subagent.${r.type}`,ft(r,this.activeTurnBySession.get(r.sessionId)))},Y={start:r=>{if(r.source!=="shell"&&r.source!=="computer"&&r.source!=="custom"&&r.toolName!=="filesystem.read"&&r.toolName!=="filesystem.write"&&r.toolName!=="filesystem.exists")return;const h={turnId:i,sessionId:e.id,actionId:r.key,toolName:r.toolName,displayName:r.label,...r.source==="shell"?{command:r.label}:{},kind:r.source??"tool",source:r.source},w=(u,v)=>{this.writeEvent(u,v).catch(y=>this.runtime.logger.warn("Desktop tool activity event failed.",{event:u,reason:y instanceof Error?y.message:String(y)}))};return w("tool.activity.started",{...h,status:"running"}),{complete:(u,v)=>w("tool.activity.completed",{...h,...v??{},status:u?"failed":"completed",isError:u})}}};let D,M=!1;const j=setTimeout(()=>a.abort(),t.params.execution.timeoutMs);try{const r=t.params.execution,h=await F({runtime:this.runtime,projectRoot:o.canonicalPath,executionCwd:s.executionCwd,model:l,client:H,reasoningEffort:C,profile:"interactive-trusted",approvalMode:vt(r.permissionPolicy),...r.allowedTools===void 0?{}:{allowedTools:r.allowedTools},...t.params.skills===void 0?{}:{selectedSkills:t.params.skills},browserAccess:r.browserAccess,subagentLifecycle:V,subagentJobs:this.subagentJobs,...this.browserHostEnabled?{browserHost:(n,d)=>this.requestBrowserHost(n,d)}:{},...this.browserHostEnabled?{desktopHost:((n,d)=>this.requestDesktopHost(n,d))}:{},...this.computerHostEnabled?{computerHost:((n,d)=>this.requestComputerHost(n,d))}:{},...this.documentHostEnabled?{documentHost:((n,d)=>this.requestDocumentHost(n,d))}:{},executionBoundary:r.isolation,hostFallback:r.hostFallback,...this.browserHostEnabled?{onExecutionFallbackApproval:(n,d)=>this.approvalBroker.waitForApproval(n,e.id,i,a.signal,"host",d)}:{},toolActivity:Y,onIntentLifecycle:n=>this.intentEvents.write(n,e.id,i),contextManagement:Q({client:H,model:l,...O===void 0?{}:{contextWindow:O},onCompactionStart:()=>this.writeEvent("context.compacting",{turnId:i,sessionId:e.id}),onSummaryFailure:n=>this.runtime.logger.warn("Context summarization failed; using fallback summary.",{reason:n instanceof Error?n.message:String(n)})}),agentSessionStore:A,prompt:n=>this.approvalBroker.waitForApproval(n,e.id,i,a.signal,r.isolation),rememberApproval:n=>this.runtime.workspaceSettings.rememberAllow(o.canonicalPath,at(n.toolName,n.input)),audit:async n=>{this.runtime.logger.info("Tool authorization decision.",{toolName:n.toolName,decision:n.decision,reason:n.reason}),await this.runtime.sessionStore.appendEvent({type:"tool.approval",timestamp:new Date().toISOString(),sessionId:e.id,turnId:i,data:{toolName:n.toolName,decision:n.decision,reason:n.reason}})},onModelComplete:n=>{n.usage!==void 0&&this.writeEvent("usage.updated",{turnId:i,sessionId:e.id,usage:n.usage}).catch(d=>this.runtime.logger.warn("Desktop usage event failed.",{reason:d instanceof Error?d.message:String(d)}))},onWrite:n=>{this.writeEvent("file.changed",{turnId:i,sessionId:e.id,change:n}).catch(d=>this.runtime.logger.warn("Desktop file event failed.",{reason:d instanceof Error?d.message:String(d)}))},mcpLifecycle:n=>this.writeMcpRuntimeEvent(n,e.id,i)});D=h.close;const w=new tt,u=await h.buildInput(t.params.prompt),v=await this.runtime.sessionStore.readHistory(e.id);A.hydrate(e.id,v.transcript,v.events,u.input);const y=R.length===0?void 0:kt(u.input,R),_=y?.content===null?void 0:y?.content,$=await h.agent.initialize();for(const n of $)(n.type==="mcp.server.status"||n.type==="mcp.tool.catalog.updated")&&await this.writeMcpEvent(n,e.id,i);for await(const n of h.run({input:u.input,userInput:u.userInput,...u.workspaceContext===void 0?{}:{workspaceContext:u.workspaceContext},..._===void 0?{}:{inputContent:_},model:l,sessionId:e.id,stream:!0,signal:a.signal})){(n.type==="mcp.server.status"||n.type==="mcp.tool.catalog.updated")&&await this.writeMcpEvent(n,e.id,i),n.type==="context.compacted"&&await X(this.runtime,h,e.id,n.metrics,i);const d=w.observe(n);for(const x of d)await this.emitProjected(x,e.id,i,I);if(n.type==="session.failed"){const x=ct(n.error);if(x===void 0)throw n.error;const B=rt(x);I.push(B);const W=`${i}:final`;await this.writeEvent("assistant.delta",{turnId:i,sessionId:e.id,segmentId:W,iteration:0,content:B}),await this.writeEvent("assistant.segment.completed",{turnId:i,sessionId:e.id,segmentId:W,iteration:0,phase:"final"});break}}if(I.join("").trim().length===0)throw new f(g.MODEL_PROTOCOL_EMPTY_RESPONSE,"Agent completed without a final text response.",{category:"model",userMessage:"The model completed without a final text response.",retryable:!0});await this.getExecutionWorkspaces().apply(e.id),M=!0,await st(this.runtime.sessionStore,e.id,i,I.join("")),await this.runtime.sessionStore.updateStatus(e.id,"completed"),await this.writeEvent("turn.completed",{turnId:i,sessionId:e.id,content:I.join("")})}catch(r){if(!M)try{const w=await this.getExecutionWorkspaces().apply(e.id);M=!0,w.appliedPaths.length>0&&this.runtime.logger.info("Applied session workspace changes after turn failure.",{sessionId:e.id,turnId:i,appliedPaths:w.appliedPaths})}catch(w){this.runtime.logger.error("Failed to apply session workspace changes after turn failure.",{sessionId:e.id,turnId:i,reason:w instanceof Error?w.message:String(w)})}const h=a.signal.aborted;throw await it(this.runtime.sessionStore,e.id,i,r,h),await this.runtime.sessionStore.updateStatus(e.id,h?"cancelled":"failed"),r}finally{if(this.intentEvents.clear(i),j!==void 0&&clearTimeout(j),D!==void 0)try{await D()}catch(r){this.runtime.logger.warn("Desktop agent cleanup failed.",{reason:r instanceof Error?r.message:String(r)})}await this.getExecutionWorkspaces().markReady(e.id)}}async emitProjected(t,e,o,s){t.type==="assistant.delta"?(s.push(t.content),await this.writeEvent("assistant.delta",{turnId:o,sessionId:e,segmentId:t.segmentId,iteration:t.iteration,content:t.content})):t.type==="assistant.replaced"?(s.splice(0,s.length,t.content),await this.writeEvent("assistant.replaced",{turnId:o,sessionId:e,segmentId:t.segmentId,iteration:t.iteration,content:t.content})):t.type==="assistant.segment.completed"?await this.writeEvent("assistant.segment.completed",{turnId:o,sessionId:e,segmentId:t.segmentId,iteration:t.iteration,phase:t.phase}):t.type==="action.started"?await this.writeEvent("tool.started",{turnId:o,sessionId:e,actionId:t.actionId,toolName:t.toolName,displayName:t.displayName,kind:t.kind,...t.source===void 0?{}:{source:t.source}}):t.type==="action.completed"?await this.writeEvent("tool.completed",{turnId:o,sessionId:e,actionId:t.actionId,toolName:t.toolName,displayName:t.displayName,kind:t.kind,status:t.status,isError:t.isError}):t.type==="context.compacted"&&await this.writeEvent("context.compacted",{turnId:o,sessionId:e}),await et(this.runtime.sessionStore,e,t,{turnId:o})}async writeMcpEvent(t,e,o){const s=K(t,e,o);await this.writeEvent(s.name,s.data)}async writeMcpRuntimeEvent(t,e,o){const s=K(t,e,o);await this.writeEvent(s.name,s.data)}async cancelTurn(t){const e=this.turns.get(t.params.turnId);if(e===void 0)throw new f(g.INVALID_ARGUMENT,"Turn is not active.",{category:"session",userMessage:"The requested turn is not active."});e.controller.abort(),await this.respond(t,{cancelled:!0,turnId:t.params.turnId}),await e.operation.catch(o=>this.runtime.logger.warn("Cancelled Desktop turn cleanup reported an error.",{turnId:t.params.turnId,reason:o instanceof Error?o.message:String(o)}))}async respondApproval(t){if(!this.approvalBroker.respondApproval(t.params.approvalId,t.params.approved))throw new f(g.INVALID_ARGUMENT,"Approval request is no longer active.",{category:"arguments",userMessage:"This approval request is no longer active."});await this.respond(t,{accepted:!0})}async respondTrust(t){if(!this.approvalBroker.respondTrust(t.params.trustRequestId,t.params.trusted))throw new f(g.INVALID_ARGUMENT,"Trust request is no longer active.",{category:"arguments",userMessage:"This trust request is no longer active."});await this.respond(t,{accepted:!0})}async listModels(t){const o=(await T(this.runtime,this.cwd)).config.profile??k.defaultProfileName(),s=await this.runtime.auth.requireCredential(o),i=await new q(N(s.profile,s.apiKey)).listModels();await this.respond(t,{models:i.data})}async authStatus(t){const o=(await this.runtime.configLoader.load(this.cwd,{includeProject:!1})).config.profile??k.defaultProfileName(),s=await this.runtime.credentialStore.get(o);await this.respond(t,{authenticated:s!==void 0,profile:o,...s===void 0?{}:{baseUrl:s.profile.baseUrl,credentialRef:s.profile.credentialRef}})}async listCommands(t){await this.respond(t,mt())}async startCommand(t){const o=pt.get(t.params.actionId)?.desktop?.mutationKey;if(o!==void 0&&(this.turns.size>0||[...this.commands.values()].some(p=>p.mutationKey===o)))throw new f(g.INVALID_ARGUMENT,`Desktop command conflicts with an active ${o} operation.`,{category:"command",userMessage:"Wait for the active operation to finish before changing this resource."});const s=U(),i=new AbortController,a=typeof t.params.options.scope=="string"?t.params.options.scope:void 0;this.logInfo("CLI Desktop command started.",{commandId:s,actionId:t.params.actionId,...a===void 0?{}:{scope:a}});const c=Promise.resolve().then(()=>this.executeCommand(t,s,i)).catch(async p=>{if(i.signal.aborted){this.logWarn("CLI Desktop command cancelled.",{commandId:s,actionId:t.params.actionId}),await this.writeEvent("command.cancelled",{commandId:s,actionId:t.params.actionId});return}this.logError("CLI Desktop command failed.",{commandId:s,actionId:t.params.actionId,code:E(p).code}),await this.writeEvent("command.failed",{commandId:s,actionId:t.params.actionId,error:E(p)})}).finally(()=>this.commands.delete(s));this.commands.set(s,{controller:i,operation:c,...o===void 0?{}:{mutationKey:o}});const m={accepted:!0,commandId:s,actionId:t.params.actionId};await this.respond(t,m)}async executeCommand(t,e,o){await this.writeEvent("command.started",{commandId:e,actionId:t.params.actionId});const s=await ut({runtime:this.runtime,cwd:this.cwd,invocation:t.params,signal:o.signal,output:{write:(i,a)=>this.writeEvent("command.output",{commandId:e,actionId:t.params.actionId,content:i,...a===void 0?{}:{structured:a}})},activity:{started:(i,a)=>this.writeEvent("command.activity.started",{commandId:e,actionId:t.params.actionId,key:i,label:a}),completed:(i,a)=>this.writeEvent("command.activity.completed",{commandId:e,actionId:t.params.actionId,key:i,isError:a})},onActivityError:i=>this.runtime.logger.warn("Desktop command activity event failed.",{commandId:e,reason:i instanceof Error?i.message:String(i)})});await this.writeEvent("command.completed",{commandId:e,actionId:t.params.actionId,exitCode:s,success:s===0}),this.logInfo("CLI Desktop command completed.",{commandId:e,actionId:t.params.actionId,exitCode:s,success:s===0})}logInfo(t,e){this.runtime.logger?.info(t,e)}logWarn(t,e){this.runtime.logger?.warn(t,e)}logError(t,e){this.runtime.logger?.error(t,e)}async cancelCommand(t){const e=this.commands.get(t.params.commandId);if(e===void 0)throw new f(g.INVALID_ARGUMENT,"Command is not active.",{category:"command",userMessage:"The requested command is not active."});e.controller.abort(),await this.respond(t,{accepted:!0,commandId:t.params.commandId})}async requestBrowserHost(t,e){if(this.stopping)throw new Error("The Desktop browser bridge is stopping.");const o=new Promise((s,i)=>{const a={resolve:s,reject:i,...e===void 0?{}:{signal:e}};if(e!==void 0){if(e.aborted){i(new Error("Browser action was cancelled."));return}a.onAbort=()=>{this.browserRequests.delete(t.requestId),this.sendHostCancel(t),i(new Error("Browser action was cancelled."))},e.addEventListener("abort",a.onAbort,{once:!0})}this.browserRequests.set(t.requestId,a)});try{return await this.writer.write(t),await o}finally{const s=this.browserRequests.get(t.requestId);s!==void 0&&(s.onAbort!==void 0&&s.signal?.removeEventListener("abort",s.onAbort),this.browserRequests.delete(t.requestId))}}async requestDesktopHost(t,e){if(this.stopping)throw new Error("The Desktop execution bridge is stopping.");const o=new Promise((s,i)=>{const a={resolve:s,reject:i,...e===void 0?{}:{signal:e}};if(e!==void 0){if(e.aborted){i(new Error("Host execution was cancelled."));return}a.onAbort=()=>{this.hostRequests.delete(t.requestId),this.sendHostCancel(t),i(new Error("Host execution was cancelled."))},e.addEventListener("abort",a.onAbort,{once:!0})}this.hostRequests.set(t.requestId,a)});try{return await this.writer.write(t),await o}finally{const s=this.hostRequests.get(t.requestId);s!==void 0&&(s.onAbort!==void 0&&s.signal?.removeEventListener("abort",s.onAbort),this.hostRequests.delete(t.requestId))}}resolveBrowserRequest(t){const e=this.browserRequests.get(t.requestId);e!==void 0&&(e.onAbort!==void 0&&e.signal?.removeEventListener("abort",e.onAbort),this.browserRequests.delete(t.requestId),e.resolve(t))}resolveDesktopHostRequest(t){const e=this.hostRequests.get(t.requestId);e!==void 0&&(e.onAbort!==void 0&&e.signal?.removeEventListener("abort",e.onAbort),this.hostRequests.delete(t.requestId),e.resolve(t))}async requestComputerHost(t,e){if(this.stopping)throw new Error("The Desktop computer bridge is stopping.");const o=new Promise((s,i)=>{const a={resolve:s,reject:i,...e===void 0?{}:{signal:e}};if(e!==void 0){if(e.aborted){i(new Error("Computer action was cancelled."));return}a.onAbort=()=>{this.computerRequests.delete(t.requestId),this.sendHostCancel(t),i(new Error("Computer action was cancelled."))},e.addEventListener("abort",a.onAbort,{once:!0})}this.computerRequests.set(t.requestId,a)});try{return await this.writer.write(t),await o}finally{const s=this.computerRequests.get(t.requestId);s!==void 0&&(s.onAbort!==void 0&&s.signal?.removeEventListener("abort",s.onAbort),this.computerRequests.delete(t.requestId))}}resolveComputerHostRequest(t){const e=this.computerRequests.get(t.requestId);e!==void 0&&(e.onAbort!==void 0&&e.signal?.removeEventListener("abort",e.onAbort),this.computerRequests.delete(t.requestId),e.resolve(t))}async requestDocumentHost(t,e){if(this.stopping)throw new Error("The Desktop document bridge is stopping.");const o=new Promise((s,i)=>{const a={resolve:s,reject:i,...e===void 0?{}:{signal:e}};if(e!==void 0){if(e.aborted){i(new Error("Document action was cancelled."));return}a.onAbort=()=>{this.documentRequests.delete(t.requestId),this.sendHostCancel(t),i(new Error("Document action was cancelled."))},e.addEventListener("abort",a.onAbort,{once:!0})}this.documentRequests.set(t.requestId,a)});try{return await this.writer.write(t),await o}finally{const s=this.documentRequests.get(t.requestId);s!==void 0&&(s.onAbort!==void 0&&s.signal?.removeEventListener("abort",s.onAbort),this.documentRequests.delete(t.requestId))}}resolveDocumentHostRequest(t){const e=this.documentRequests.get(t.requestId);e!==void 0&&(e.onAbort!==void 0&&e.signal?.removeEventListener("abort",e.onAbort),this.documentRequests.delete(t.requestId),e.resolve(t))}async sendHostCancel(t){this.stopping||await this.writer.write({version:1,type:"host.cancel",requestId:t.requestId,tool:t.tool,sessionId:t.sessionId})}async respond(t,e){await this.writer.write({version:S,id:t.id,type:"response",method:t.method,ok:!0,result:e})}async writeError(t,e,o){await this.writer.write({version:S,id:t,type:"response",method:e,ok:!1,error:o})}async writeEvent(t,e){const o=typeof e.turnId=="string"?e.turnId:void 0,s=o===void 0?void 0:this.turnContexts.get(o),i=s===void 0?{}:{projectRoot:s.projectRoot,executionCwd:s.executionCwd,executionWorkspaceId:s.id,...s.taskId===void 0?{}:{taskId:s.taskId}};await this.writer.write({version:S,type:"event",scope:typeof e.sessionId=="string"?"session":"control",event:t,data:{...i,...e}})}getExecutionWorkspaces(){return this.fallbackExecutionWorkspaces??=this.runtime.executionWorkspaces??new It(this.runtime.paths.globalConfigDir,this.runtime.platform),this.fallbackExecutionWorkspaces}}export{S as DESKTOP_PROTOCOL_VERSION,we as runDesktopAgentCommand};
|
|
3
|
+
`);for await(const h of l.run({input:u,model:s,signal:r.signal,stream:!0}))if(h.type==="model.delta")p+=h.content;else if(h.type==="session.completed"&&p.length===0)p=h.content;else if(h.type==="session.failed")throw h.error;if(p.trim().length===0)throw new g(y.MODEL_PROTOCOL_EMPTY_RESPONSE,"Title generation returned an empty response.",{category:"model",userMessage:"The model did not return a session title.",retryable:!0});await this.respond(t,{title:p})}finally{clearTimeout(c),await m?.()}}async listSessions(t){const e=await this.runtime.projectTrust.inspect(this.cwd);await this.respond(t,{sessions:await this.runtime.sessionStore.list(e.fingerprint)})}async resumeSession(t){const e=await this.runtime.projectTrust.inspect(this.cwd),s=await new $(this.runtime.sessionStore).require(t.params.sessionId,e.fingerprint),o=await this.executionWorkspaces.prepare({sessionId:s.id,projectRoot:e.canonicalPath});await this.respond(t,{...Z(s),executionWorkspace:o,history:await this.runtime.sessionStore.readHistory(s.id)}),Y(this.runtime,this.cwd).catch(a=>this.runtime.logger.warn("MCP session warm-up failed.",{reason:a instanceof Error?a.message:String(a)}))}async startTurn(t){const{sessionId:e}=t.params;if(this.runtime.logger.info("CLI Desktop turn request received.",{requestId:t.id,sessionId:e,promptLength:t.params.prompt.length,requestedModel:t.params.model}),[...this.turns.values()].some(p=>p.sessionId===e))throw new g(y.INVALID_ARGUMENT,"A turn is already active for this session.",{category:"session",userMessage:"This session already has an active turn."});const s=await this.runtime.projectTrust.inspect(this.cwd);let o=await new $(this.runtime.sessionStore).require(e,s.fingerprint);t.params.sessionName!==void 0&&t.params.sessionName.trim()!==o.name&&(o=await this.runtime.sessionStore.rename(e,t.params.sessionName));const a=await this.executionWorkspaces.acquireTurn(e);let r=!1;const c=async()=>{r||(r=!0,await a())};let m=!1;try{const p=await this.executionWorkspaces.prepare({sessionId:e,projectRoot:s.canonicalPath}),l=this.attachments.take(t.params.attachmentIds??[],e),u=lt(),h=new AbortController;this.turnContexts.set(u,{...p,...t.params.taskId===void 0?{}:{taskId:t.params.taskId}}),this.activeTurnBySession.set(e,u);const x=this.executeTurn(t,o,s,p,u,h,l,c).catch(async C=>{await this.writeEvent(h.signal.aborted?"turn.cancelled":"turn.failed",{turnId:u,sessionId:e,...h.signal.aborted?{}:{error:b(C)}})}).finally(()=>{this.turns.delete(u),this.turnContexts.delete(u),this.activeTurnBySession.get(e)===u&&this.activeTurnBySession.delete(e),this.sessionStores.prune(this.activeTurnBySession.keys())});this.turns.set(u,{sessionId:e,controller:h,operation:x}),m=!0,await this.respond(t,{accepted:!0,turnId:u,sessionId:e})}finally{m||await c()}}async executeTurn(t,e,s,o,a,r,c,m){try{await this.executeTurnBody(t,e,s,o,a,r,c)}finally{await m()}}async executeTurnBody(t,e,s,o,a,r,c){const m=await P(this.runtime,this.cwd);if(!await this.runtime.projectTrust.isTrusted(s)){const n=K();if(await this.writeEvent("trust.requested",{trustRequestId:n,sessionId:e.id,path:s.canonicalPath}),!await this.approvalBroker.waitForTrust(n,r.signal))throw new g(y.PROJECT_TRUST_REQUIRED,"Project trust was not granted.",{category:"project",userMessage:"Trust this project in the Desktop application before running tools."});await this.runtime.projectTrust.grant(s),this.runtime.agentPreparation?.clear(s.canonicalPath),await this.runtime.workspaceSettings.ensureInitialized(s.canonicalPath)}const l=t.params.model??e.model??m.config.model;if(l==null)throw new g(y.MODEL_NOT_FOUND,"No model is selected.",{category:"model",userMessage:"Select a model before starting a turn."});const u=t.params.reasoningEffort??m.config.effort;this.runtime.logger.info("CLI Desktop turn model resolved.",{sessionId:e.id,turnId:a,requestedModel:t.params.model,sessionModel:e.model,resolvedModel:l});const h=e.profile??m.config.profile??T.defaultProfileName(),x=await this.runtime.auth.requireCredential(h),C=L(x.profile,x.apiKey),H=await xt(this.runtime,l,h),D=new ct,A=this.sessionStores.getOrCreate(e.id,this.activeTurnBySession.keys()),M=new Set,et=new Ct(this.runtime,s.canonicalPath),W=async()=>{try{const n=await et.buildForProject({sessionId:e.id,model:l,profileName:h,pluginNames:[],pendingAttachments:0,agentSessionStore:A});await this.writeEvent("usage.updated",{turnId:a,sessionId:e.id,model:l,...n.contextWindow===void 0?{}:{contextWindow:n.contextWindow},usage:{promptTokens:n.usedTokens,estimated:!0}})}catch(n){this.runtime.logger.warn("Desktop context usage update failed.",{sessionId:e.id,turnId:a,reason:n instanceof Error?n.message:String(n)})}},v=[],S=[];for(const n of c){const f=n.attachment.storagePath===void 0?await this.runtime.sessionStore.saveAttachment(e.id,n.attachment,n.bytes):n.attachment;S.push({...n,attachment:f})}await ft(this.runtime.sessionStore,e.id,a,t.params.prompt,S.map(n=>n.attachment.id)),await this.runtime.sessionStore.updateStatus(e.id,"active"),await this.writeEvent("turn.started",{turnId:a,sessionId:e.id,model:l});const at={onSubagentLifecycle:n=>this.writeEvent(`subagent.${n.type}`,St(n,a),{delivery:"best-effort"})},st={start:n=>{if(n.source!=="shell"&&n.source!=="computer"&&n.source!=="custom"&&n.toolName!=="filesystem.read"&&n.toolName!=="filesystem.write"&&n.toolName!=="filesystem.exists")return;const f={turnId:a,sessionId:e.id,actionId:n.key,toolName:n.toolName,displayName:n.label,...n.source==="shell"?{command:n.label}:{},kind:n.source??"tool",source:n.source},E=(w,I)=>{this.writeEvent(w,I,{delivery:"best-effort"}).catch(k=>this.runtime.logger.warn("Desktop tool activity event failed.",{event:w,reason:k instanceof Error?k.message:String(k)}))};return E("tool.activity.started",{...f,status:"running"}),{complete:(w,I)=>E("tool.activity.completed",{...f,...I??{},status:w?"failed":"completed",isError:w})}}};let B,O=!1;const U=setTimeout(()=>r.abort(),t.params.execution.timeoutMs);try{const n=t.params.execution,f=await V({runtime:this.runtime,projectRoot:s.canonicalPath,executionCwd:o.executionCwd,executionWorkspaceId:o.id,model:l,client:C,reasoningEffort:u,profile:"interactive-trusted",approvalMode:Pt(n.permissionPolicy),...n.allowedTools===void 0?{}:{allowedTools:n.allowedTools},...t.params.skills===void 0?{}:{selectedSkills:t.params.skills},browserAccess:n.browserAccess,subagentLifecycle:at,subagentJobs:this.subagentJobs,...this.browserHostEnabled?{browserHost:(i,d)=>this.hostBridge.requestBrowser(i,d)}:{},...this.desktopHostEnabled?{desktopHost:((i,d)=>this.hostBridge.requestDesktop(i,d))}:{},...this.computerHostEnabled?{computerHost:((i,d)=>this.hostBridge.requestComputer(i,d))}:{},onArtifacts:i=>{for(const d of i)M.add(d.id)},...this.hostCapabilities===void 0?{}:{hostCapabilities:this.hostCapabilities},executionBoundary:n.isolation,executionPlatform:n.isolation==="sandbox"?Vt():this.runtime.platform,browserExecution:n.browserExecution,hostFallback:n.hostFallback,...this.browserHostEnabled||this.desktopHostEnabled?{onExecutionFallbackApproval:(i,d)=>this.approvalBroker.waitForApproval(i,e.id,a,r.signal,"host",d)}:{},toolActivity:st,onIntentLifecycle:i=>this.intentEvents.write(i,e.id,a),contextManagement:it({client:C,model:l,...H===void 0?{}:{contextWindow:H},onCompactionStart:()=>this.writeEvent("context.compacting",{turnId:a,sessionId:e.id}),onSummaryFailure:i=>this.runtime.logger.warn("Context summarization failed; using fallback summary.",{reason:i instanceof Error?i.message:String(i)}),continuationContext:()=>dt({snapshot:D.snapshot()})}),agentSessionStore:A,prompt:(i,d)=>this.approvalBroker.waitForApproval(i,e.id,a,d??r.signal,n.isolation),rememberApproval:i=>this.runtime.workspaceSettings.rememberAllow(s.canonicalPath,wt(i.toolName,i.input)),audit:async i=>{const d={toolName:i.toolName,stage:i.stage,sessionId:i.sessionId,runId:i.runId,decision:i.decision,reason:i.reason,...i.source===void 0?{}:{source:i.source},...i.executorId===void 0?{}:{executorId:i.executorId},...i.ruleId===void 0?{}:{ruleId:i.ruleId},...i.skillName===void 0?{}:{skillName:i.skillName},...i.riskCategory===void 0?{}:{riskCategory:i.riskCategory}};this.runtime.logger.info("Tool authorization decision.",d),await this.runtime.sessionStore.appendEvent({type:"tool.approval",timestamp:new Date().toISOString(),sessionId:e.id,turnId:a,data:d})},onModelComplete:i=>{D.observeModel(i),i.usage!==void 0&&this.writeEvent("usage.updated",{turnId:a,sessionId:e.id,model:l,usage:i.usage},{delivery:"best-effort"}).catch(d=>this.runtime.logger.warn("Desktop usage event failed.",{reason:d instanceof Error?d.message:String(d)}))},onWrite:i=>{this.writeEvent("file.changed",{turnId:a,sessionId:e.id,change:i},{delivery:"best-effort"}).catch(d=>this.runtime.logger.warn("Desktop file event failed.",{reason:d instanceof Error?d.message:String(d)}))},mcpLifecycle:i=>this.writeMcpRuntimeEvent(i,e.id,a)});B=f.close;const E=new mt,w=await f.buildInput(t.params.prompt),I=await this.runtime.sessionStore.readHistory(e.id);A.hydrate(e.id,I.transcript,I.events,w.input);const k=Yt(w.input,S.map(({attachment:i,bytes:d})=>({byteLength:d.byteLength,mimeType:i.mimeType,name:i.name})));if(!k.isWithinLimit)throw new g(y.ATTACHMENT_TOO_LARGE,"The prompt and attachments exceed the Gateway JSON body limit.",{category:"attachment",userMessage:`The prompt and attachments exceed the ${k.limitBytes/1e6} MB decimal Gateway JSON limit. Remove an attachment or shorten the prompt and retry.`,details:{kind:"gateway-json-body",maxBytes:k.limitBytes,projectedBytes:k.projectedBytes}});const G=S.length===0?void 0:At(w.input,S),F=G?.content===null?void 0:G?.content,ot=await f.agent.initialize();for(const i of ot)(i.type==="mcp.server.status"||i.type==="mcp.tool.catalog.updated")&&await this.writeMcpEvent(i,e.id,a);for await(const i of f.run({input:w.input,userInput:w.userInput,...w.workspaceContext===void 0?{}:{workspaceContext:w.workspaceContext},...F===void 0?{}:{inputContent:F},model:l,sessionId:e.id,stream:!0,signal:r.signal,metadata:{taskId:t.params.taskId,turnId:a}})){D.observe(i),(i.type==="mcp.server.status"||i.type==="mcp.tool.catalog.updated")&&await this.writeMcpEvent(i,e.id,a),i.type==="context.compacted"&&(await nt(this.runtime,f,e.id,i.metrics,a),await W());const d=E.observe(i);for(const N of d)await this.emitProjected(N,e.id,a,v,M);if(i.type==="session.failed"){const N=yt(i.error);if(N===void 0)throw i.error;const z=gt(N);v.push(z);const J=`${a}:final`;await this.writeEvent("assistant.delta",{turnId:a,sessionId:e.id,segmentId:J,iteration:0,content:z}),await this.writeEvent("assistant.segment.completed",{turnId:a,sessionId:e.id,segmentId:J,iteration:0,phase:"final",..._(M)});break}}if(v.join("").trim().length===0)throw new g(y.MODEL_PROTOCOL_EMPTY_RESPONSE,"Agent completed without a final text response.",{category:"model",userMessage:"The model completed without a final text response.",retryable:!0});await this.executionWorkspaces.apply(e.id),O=!0,await ht(this.runtime.sessionStore,e.id,a,v.join("")),await this.runtime.sessionStore.updateStatus(e.id,"completed"),await W(),await this.writeEvent("turn.completed",{turnId:a,sessionId:e.id,content:v.join(""),..._(M)})}catch(n){if(!O)try{const E=await this.executionWorkspaces.apply(e.id);O=!0,E.appliedPaths.length>0&&this.runtime.logger.info("Applied session workspace changes after turn failure.",{sessionId:e.id,turnId:a,appliedPaths:E.appliedPaths})}catch(E){this.runtime.logger.error("Failed to apply session workspace changes after turn failure.",{sessionId:e.id,turnId:a,reason:E instanceof Error?E.message:String(E)})}const f=r.signal.aborted;throw await ut(this.runtime.sessionStore,e.id,a,n,f),await this.runtime.sessionStore.updateStatus(e.id,f?"cancelled":"failed"),n}finally{if(this.intentEvents.clear(a),U!==void 0&&clearTimeout(U),B!==void 0)try{await B()}catch(n){this.runtime.logger.warn("Desktop agent cleanup failed.",{reason:n instanceof Error?n.message:String(n)})}await this.executionWorkspaces.markReady(e.id)}}async emitProjected(t,e,s,o,a){t.type==="assistant.delta"?(o.push(t.content),await this.writeEvent("assistant.delta",{turnId:s,sessionId:e,segmentId:t.segmentId,iteration:t.iteration,content:t.content})):t.type==="assistant.replaced"?(o.splice(0,o.length,t.content),await this.writeEvent("assistant.replaced",{turnId:s,sessionId:e,segmentId:t.segmentId,iteration:t.iteration,content:t.content})):t.type==="assistant.segment.completed"?await this.writeEvent("assistant.segment.completed",{turnId:s,sessionId:e,segmentId:t.segmentId,iteration:t.iteration,phase:t.phase,...t.phase==="final"?_(a):{}}):t.type==="action.started"?await this.writeEvent("tool.started",{turnId:s,sessionId:e,actionId:t.actionId,toolName:t.toolName,displayName:t.displayName,kind:t.kind,...t.source===void 0?{}:{source:t.source}}):t.type==="action.completed"?await this.writeEvent("tool.completed",{turnId:s,sessionId:e,actionId:t.actionId,toolName:t.toolName,displayName:t.displayName,kind:t.kind,status:t.status,isError:t.isError}):t.type==="context.compacted"&&await this.writeEvent("context.compacted",{turnId:s,sessionId:e}),await pt(this.runtime.sessionStore,e,t,{turnId:s})}async writeMcpEvent(t,e,s){const o=q(t,e,s);await this.writeEvent(o.name,o.data)}async writeMcpRuntimeEvent(t,e,s){const o=q(t,e,s);await this.writeEvent(o.name,o.data)}async cancelTurn(t){const e=this.turns.get(t.params.turnId);if(e===void 0)throw new g(y.INVALID_ARGUMENT,"Turn is not active.",{category:"session",userMessage:"The requested turn is not active."});e.controller.abort(),await this.respond(t,{cancelled:!0,turnId:t.params.turnId}),await e.operation.catch(s=>this.runtime.logger.warn("Cancelled Desktop turn cleanup reported an error.",{turnId:t.params.turnId,reason:s instanceof Error?s.message:String(s)}))}async respondApproval(t){const e=t.params.decision==="redirect"?{decision:"redirect",message:t.params.message}:{decision:t.params.decision};if(!this.approvalBroker.respondApproval(t.params.approvalId,e))throw new g(y.INVALID_ARGUMENT,"Approval request is no longer active.",{category:"arguments",userMessage:"This approval request is no longer active."});await this.respond(t,{accepted:!0})}async respondTrust(t){if(!this.approvalBroker.respondTrust(t.params.trustRequestId,t.params.trusted))throw new g(y.INVALID_ARGUMENT,"Trust request is no longer active.",{category:"arguments",userMessage:"This trust request is no longer active."});await this.respond(t,{accepted:!0})}async listModels(t){const e=await tt(this.runtime,this.cwd);await this.respond(t,{models:e.data})}async authStatus(t){const s=(await this.runtime.configLoader.load(this.cwd,{includeProject:!1})).config.profile??T.defaultProfileName(),o=await this.runtime.credentialStore.get(s);await this.respond(t,{authenticated:o!==void 0,profile:s,...o===void 0?{}:{baseUrl:o.profile.baseUrl,credentialRef:o.profile.credentialRef}})}async synthesizeSpeech(t){const s=(await P(this.runtime,this.cwd)).config.profile??T.defaultProfileName(),o=await this.runtime.auth.requireCredential(s),a=await tt(this.runtime,this.cwd),r=Ft(t.params.model,a.data),c=new AbortController;this.speechRequests.set(t.id,c);const m=setTimeout(()=>c.abort(new Error("Desktop speech synthesis timed out.")),Jt);try{const p=await new Gt(L(o.profile,o.apiKey)).createSpeech({model:r,input:t.params.input,voice:t.params.voice,responseFormat:t.params.responseFormat??"mp3"},{signal:c.signal});if(p.bytes.byteLength===0||p.bytes.byteLength>zt)throw new g(y.MEDIA_RESPONSE_TOO_LARGE,"Generated speech exceeds the Desktop playback limit.",{category:"media",userMessage:"The generated speech is too large to play in the Desktop app."});await this.respond(t,{contentType:p.contentType??"audio/mpeg",bytesBase64:Buffer.from(p.bytes).toString("base64")})}finally{clearTimeout(m),this.speechRequests.get(t.id)===c&&this.speechRequests.delete(t.id)}}async listCommands(t){await this.respond(t,kt())}async startCommand(t){const s=X.get(t.params.actionId)?.desktop?.mutationKey;if(s!==void 0&&(this.turns.size>0||[...this.commands.values()].some(p=>p.mutationKey===s)))throw new g(y.INVALID_ARGUMENT,`Desktop command conflicts with an active ${s} operation.`,{category:"command",userMessage:"Wait for the active operation to finish before changing this resource."});const o=K(),a=new AbortController,r=typeof t.params.options.scope=="string"?t.params.options.scope:void 0;this.logInfo("CLI Desktop command started.",{commandId:o,actionId:t.params.actionId,...r===void 0?{}:{scope:r}});const c=Promise.resolve().then(()=>this.executeCommand(t,o,a)).catch(async p=>{if(a.signal.aborted){this.logWarn("CLI Desktop command cancelled.",{commandId:o,actionId:t.params.actionId}),await this.writeEvent("command.cancelled",{commandId:o,actionId:t.params.actionId});return}this.logError("CLI Desktop command failed.",{commandId:o,actionId:t.params.actionId,code:b(p).code}),await this.writeEvent("command.failed",{commandId:o,actionId:t.params.actionId,error:b(p)})}).finally(()=>this.commands.delete(o));this.commands.set(o,{controller:a,operation:c,...s===void 0?{}:{mutationKey:s}});const m={accepted:!0,commandId:o,actionId:t.params.actionId};await this.respond(t,m)}async executeCommand(t,e,s){await this.writeEvent("command.started",{commandId:e,actionId:t.params.actionId});const o=await bt({runtime:this.runtime,cwd:this.cwd,invocation:t.params,signal:s.signal,output:{write:(a,r)=>this.writeEvent("command.output",{commandId:e,actionId:t.params.actionId,content:a,...r===void 0?{}:{structured:r}})},activity:{started:(a,r)=>this.writeEvent("command.activity.started",{commandId:e,actionId:t.params.actionId,key:a,label:r}),completed:(a,r)=>this.writeEvent("command.activity.completed",{commandId:e,actionId:t.params.actionId,key:a,isError:r})},onActivityError:a=>this.runtime.logger.warn("Desktop command activity event failed.",{commandId:e,reason:a instanceof Error?a.message:String(a)})});if(o===0&&t.params.actionId==="session.delete"){const a=t.params.positionals[1];a!==void 0&&(await this.subagentJobs.closeSession(a),this.sessionStores.delete(a))}o===0&&Mt(t.params.actionId,X.get(t.params.actionId)?.desktop?.mutationKey)&&this.runtime.agentPreparation?.clear(),await this.writeEvent("command.completed",{commandId:e,actionId:t.params.actionId,exitCode:o,success:o===0}),this.logInfo("CLI Desktop command completed.",{commandId:e,actionId:t.params.actionId,exitCode:o,success:o===0})}logInfo(t,e){this.runtime.logger?.info(t,e)}logWarn(t,e){this.runtime.logger?.warn(t,e)}logError(t,e){this.runtime.logger?.error(t,e)}async cancelCommand(t){const e=this.commands.get(t.params.commandId);if(e===void 0)throw new g(y.INVALID_ARGUMENT,"Command is not active.",{category:"command",userMessage:"The requested command is not active."});e.controller.abort(),await this.respond(t,{accepted:!0,commandId:t.params.commandId})}async respond(t,e){await this.writer.write({version:R,id:t.id,type:"response",method:t.method,ok:!0,result:e})}async writeError(t,e,s){await this.writer.write({version:R,id:t,type:"response",method:e,ok:!1,error:s})}async writeEvent(t,e,s={}){const o=typeof e.turnId=="string"?e.turnId:void 0,a=o===void 0?void 0:this.turnContexts.get(o),r=a===void 0?{}:{projectRoot:a.projectRoot,executionCwd:a.executionCwd,executionWorkspaceId:a.id,...a.taskId===void 0?{}:{taskId:a.taskId}};await this.writer.write({version:R,type:"event",scope:typeof e.sessionId=="string"?"session":"control",event:t,data:{...r,...e}},s)}}export{Ke as DESKTOP_PROTOCOL_VERSION,Fe as runDesktopAgentCommand};
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { DesktopHostWireMessage } from './desktop-host-bridge.js';
|
|
2
|
+
export declare const DESKTOP_PROTOCOL_VERSION: 1;
|
|
3
|
+
export declare const TITLE_GENERATION_TIMEOUT_MS = 25000;
|
|
4
|
+
export declare const DESKTOP_SPEECH_SYNTHESIS_TIMEOUT_MS = 60000;
|
|
5
|
+
export declare const DESKTOP_SPEECH_MAX_BYTES = 2500000;
|
|
6
|
+
export type DesktopResponse = {
|
|
7
|
+
version: 1;
|
|
8
|
+
id: string;
|
|
9
|
+
type: 'response';
|
|
10
|
+
method: string;
|
|
11
|
+
ok: true;
|
|
12
|
+
result?: unknown;
|
|
13
|
+
} | {
|
|
14
|
+
version: 1;
|
|
15
|
+
id: string;
|
|
16
|
+
type: 'response';
|
|
17
|
+
method: string;
|
|
18
|
+
ok: false;
|
|
19
|
+
error: DesktopError;
|
|
20
|
+
};
|
|
21
|
+
export type DesktopEvent = {
|
|
22
|
+
version: 1;
|
|
23
|
+
type: 'event';
|
|
24
|
+
scope: 'session' | 'control';
|
|
25
|
+
event: string;
|
|
26
|
+
data: Record<string, unknown>;
|
|
27
|
+
};
|
|
28
|
+
export type DesktopError = {
|
|
29
|
+
code: string;
|
|
30
|
+
category: string;
|
|
31
|
+
message: string;
|
|
32
|
+
retryable: boolean;
|
|
33
|
+
};
|
|
34
|
+
export type DesktopWireMessage = DesktopResponse | DesktopEvent | DesktopHostWireMessage;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
const _=1,E=25e3,T=6e4,S=25e5;export{_ as DESKTOP_PROTOCOL_VERSION,S as DESKTOP_SPEECH_MAX_BYTES,T as DESKTOP_SPEECH_SYNTHESIS_TIMEOUT_MS,E as TITLE_GENERATION_TIMEOUT_MS};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{CliError as i}from"../../domain/errors/cli-error.js";import{CLI_ERROR_CODES as a}from"../../domain/errors/error-codes.js";function d(e){return e.size===0?{}:{artifactIds:[...e]}}function s(e,n){if(e!==void 0){const o=n.find(r=>r.id===e);if(o===void 0)throw new i(a.MODEL_NOT_FOUND,`Speech model is not available: ${e}`,{category:"model",userMessage:`Speech model is not available: ${e}.`});if(o.model_category!==void 0&&o.model_category!=="speech")throw new i(a.MODEL_NOT_FOUND,`Model is not a speech model: ${e}`,{category:"model",userMessage:`Model ${e} cannot synthesize speech.`});return e}const l=n.find(o=>o.model_category==="speech")?.id;if(l!==void 0)return l;throw new i(a.MODEL_NOT_FOUND,"No speech model is available.",{category:"model",userMessage:"No speech model is available for Live Chat."})}export{d as artifactIdsData,s as resolveSpeechModel};
|
|
@@ -3,7 +3,14 @@ import { type DesktopExecutionPolicy } from './desktop-execution-policy.js';
|
|
|
3
3
|
import type { BrowserHostResponse } from './browser-host-tool-executor.js';
|
|
4
4
|
import type { DesktopHostExecutionResponse } from './desktop-host-tool-executor.js';
|
|
5
5
|
import type { ComputerHostResponse } from './computer-host-tool-executor.js';
|
|
6
|
-
|
|
6
|
+
type DesktopApprovalRequestDecision = {
|
|
7
|
+
approvalId: string;
|
|
8
|
+
decision: 'allow' | 'deny';
|
|
9
|
+
} | {
|
|
10
|
+
approvalId: string;
|
|
11
|
+
decision: 'redirect';
|
|
12
|
+
message: string;
|
|
13
|
+
};
|
|
7
14
|
export type DesktopRequest = {
|
|
8
15
|
version: 1;
|
|
9
16
|
id: string;
|
|
@@ -82,15 +89,27 @@ export type DesktopRequest = {
|
|
|
82
89
|
prompt: string;
|
|
83
90
|
model?: string;
|
|
84
91
|
};
|
|
92
|
+
} | {
|
|
93
|
+
version: 1;
|
|
94
|
+
id: string;
|
|
95
|
+
method: 'speech.synthesize';
|
|
96
|
+
params: {
|
|
97
|
+
input: string;
|
|
98
|
+
voice: string;
|
|
99
|
+
model?: string;
|
|
100
|
+
responseFormat?: 'mp3' | 'opus' | 'aac' | 'flac' | 'wav' | 'pcm';
|
|
101
|
+
};
|
|
85
102
|
} | {
|
|
86
103
|
version: 1;
|
|
87
104
|
id: string;
|
|
88
105
|
method: 'attachment.begin';
|
|
89
106
|
params: {
|
|
90
107
|
attachmentId: string;
|
|
108
|
+
sessionId?: string;
|
|
91
109
|
name: string;
|
|
92
110
|
mimeType: string;
|
|
93
111
|
sizeBytes: number;
|
|
112
|
+
storageName?: string;
|
|
94
113
|
};
|
|
95
114
|
} | {
|
|
96
115
|
version: 1;
|
|
@@ -108,6 +127,14 @@ export type DesktopRequest = {
|
|
|
108
127
|
params: {
|
|
109
128
|
attachmentId: string;
|
|
110
129
|
};
|
|
130
|
+
} | {
|
|
131
|
+
version: 1;
|
|
132
|
+
id: string;
|
|
133
|
+
method: 'attachment.delete';
|
|
134
|
+
params: {
|
|
135
|
+
sessionId: string;
|
|
136
|
+
attachmentId: string;
|
|
137
|
+
};
|
|
111
138
|
} | {
|
|
112
139
|
version: 1;
|
|
113
140
|
id: string;
|
|
@@ -119,6 +146,7 @@ export type DesktopRequest = {
|
|
|
119
146
|
reasoningEffort?: string;
|
|
120
147
|
runId?: string;
|
|
121
148
|
taskId?: string;
|
|
149
|
+
sessionName?: string;
|
|
122
150
|
attachmentIds?: string[];
|
|
123
151
|
skills?: string[];
|
|
124
152
|
execution: DesktopExecutionPolicy;
|
|
@@ -134,10 +162,7 @@ export type DesktopRequest = {
|
|
|
134
162
|
version: 1;
|
|
135
163
|
id: string;
|
|
136
164
|
method: 'approval.respond';
|
|
137
|
-
params:
|
|
138
|
-
approvalId: string;
|
|
139
|
-
approved: boolean;
|
|
140
|
-
};
|
|
165
|
+
params: DesktopApprovalRequestDecision;
|
|
141
166
|
} | {
|
|
142
167
|
version: 1;
|
|
143
168
|
id: string;
|
|
@@ -183,4 +208,4 @@ export declare function parseDesktopRequest(value: unknown): DesktopRequest;
|
|
|
183
208
|
export declare function isBrowserHostResponse(value: unknown): value is BrowserHostResponse;
|
|
184
209
|
export declare function isDesktopHostResponse(value: unknown): value is DesktopHostExecutionResponse;
|
|
185
210
|
export declare function isComputerHostResponse(value: unknown): value is ComputerHostResponse;
|
|
186
|
-
export
|
|
211
|
+
export {};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{parseDesktopExecutionPolicy as y}from"./desktop-execution-policy.js";import{RESOURCE_LIMITS as
|
|
1
|
+
import{parseDesktopExecutionPolicy as y}from"./desktop-execution-policy.js";import{RESOURCE_LIMITS as i}from"../../domain/runtime/resource-limits.js";import{GATEWAY_MEDIA_LIMITS as h}from"../../domain/media/gateway-media-contract.js";function F(n){if(!a(n)||n.version!==1||!c(n.id)||!a(n.params)||typeof n.method!="string")throw o("Invalid Desktop protocol request.");const e=n.id,t=n.method,r=n.params;if(t==="initialize"||t==="session.list"||t==="model.list"||t==="auth.status"||t==="command.list"||t==="shutdown")return{version:1,id:e,method:t,params:r};if(t==="session.create")return{version:1,id:e,method:t,params:{...u(r,"model"),...u(r,"name")}};if(t==="session.resume")return{version:1,id:e,method:t,params:{sessionId:s(r,"sessionId")}};if(t==="extension.listPublicPlugins")return{version:1,id:e,method:t,params:{root:m(r,"root")}};if(t==="extension.resolvePublicPluginSource")return{version:1,id:e,method:t,params:{root:m(r,"root"),name:l(r,"name","public plugin")}};if(t==="extension.readPublicPluginContribution")return{version:1,id:e,method:t,params:{root:m(r,"root"),pluginName:l(r,"pluginName","plugin"),kind:b(r),sourceName:l(r,"sourceName","extension")}};if(t==="extension.readDetail")return{version:1,id:e,method:t,params:x(r)};if(t==="extension.readPluginIcon")return{version:1,id:e,method:t,params:{name:l(r,"name","plugin"),scope:B(r,"scope")}};if(t==="title.generate")return{version:1,id:e,method:t,params:{prompt:I(r),...u(r,"model")}};if(t==="speech.synthesize")return{version:1,id:e,method:t,params:{input:q(r),voice:k(r),...u(r,"model"),...S(r)}};if(t==="attachment.begin")return{version:1,id:e,method:t,params:{attachmentId:s(r,"attachmentId"),...N(r,"sessionId"),name:j(r),mimeType:$(r),sizeBytes:E(r),...L(r)}};if(t==="attachment.chunk")return{version:1,id:e,method:t,params:{attachmentId:s(r,"attachmentId"),index:R(r),dataBase64:D(r)}};if(t==="attachment.complete")return{version:1,id:e,method:t,params:{attachmentId:s(r,"attachmentId")}};if(t==="attachment.delete")return{version:1,id:e,method:t,params:{sessionId:s(r,"sessionId"),attachmentId:s(r,"attachmentId")}};if(t==="turn.start")return{version:1,id:e,method:t,params:{sessionId:s(r,"sessionId"),prompt:I(r),...u(r,"model"),...u(r,"reasoningEffort"),...u(r,"runId"),...u(r,"taskId"),...u(r,"sessionName"),...C(r),...K(r),execution:M(r)}};if(t==="turn.cancel")return{version:1,id:e,method:t,params:{turnId:s(r,"turnId")}};if(t==="approval.respond")return{version:1,id:e,method:t,params:P(r)};if(t==="trust.respond")return{version:1,id:e,method:t,params:{trustRequestId:s(r,"trustRequestId"),trusted:z(r,"trusted")}};if(t==="command.execute")return{version:1,id:e,method:t,params:v(r)};if(t==="command.cancel")return{version:1,id:e,method:t,params:{commandId:s(r,"commandId")}};throw o("Unsupported Desktop protocol method.")}function H(n){return!a(n)||n.version!==1||n.type!=="host.response"||n.tool!=="browser"||!c(n.requestId)||typeof n.ok!="boolean"?!1:n.error===void 0||a(n.error)}function O(n){return!a(n)||n.version!==1||n.type!=="host.response"||!["filesystem","shell","git","artifact"].includes(String(n.tool))||!c(n.requestId)||typeof n.ok!="boolean"?!1:n.error===void 0||a(n.error)}function U(n){return!a(n)||n.version!==1||n.type!=="host.response"||n.tool!=="computer"||!c(n.requestId)||typeof n.ok!="boolean"?!1:n.error===void 0||a(n.error)}function v(n){const e=s(n,"actionId"),t=n.positionals;if(!Array.isArray(t)||t.some(f=>typeof f!="string"||Buffer.byteLength(f,"utf8")>i.jsonlPromptBytes))throw o("Invalid command positionals.");const r=n.options;if(!a(r)||Object.entries(r).some(([f,p])=>!c(f)||typeof p!="string"&&typeof p!="boolean"||typeof p=="string"&&Buffer.byteLength(p,"utf8")>i.jsonlPromptBytes))throw o("Invalid command options.");const d=n.secrets;if(d===void 0)return{actionId:e,positionals:t,options:r};if(e!=="auth.login.direct"||!a(d)||Object.keys(d).some(f=>f!=="apiKey")||typeof d.apiKey!="string"||d.apiKey.length===0||Buffer.byteLength(d.apiKey,"utf8")>i.jsonlPromptBytes)throw o("Invalid command secrets.");return{actionId:e,positionals:t,options:r,secrets:{apiKey:d.apiKey}}}function s(n,e){const t=n[e];if(!c(t))throw o(`Invalid ${e}.`);return t}function m(n,e){const t=n[e];if(typeof t!="string"||t.trim().length===0||t.includes("\0")||Buffer.byteLength(t,"utf8")>i.jsonlPathBytes)throw o(`Invalid ${e}.`);return t}function l(n,e,t){const r=n[e];if(typeof r!="string"||!/^[a-z0-9][a-z0-9-]{0,63}$/u.test(r))throw o(`Invalid ${t} name.`);return r}function w(n,e){const t=n[e];if(typeof t!="string"||t.length===0||t.length>256||t.includes("\0")||!/^[a-z0-9][a-z0-9:_-]*$/u.test(t))throw o("Invalid extension name.");return t}function b(n){const e=n.kind;if(e!=="skill"&&e!=="mcp"&&e!=="hook")throw o("Invalid extension kind.");return e}function B(n,e){const t=n[e];if(t!=="user"&&t!=="project")throw o(`Invalid ${e}.`);return t}function x(n){const e=n.kind;if(e!=="hook"&&e!=="skill"&&e!=="plugin"&&e!=="mcp")throw o("Invalid extension kind.");const t=n.scope;if(t!==void 0&&t!=="user"&&t!=="project"&&t!=="plugin"&&t!=="builtin")throw o("Invalid extension scope.");const r=w(n,"name");return{kind:e,name:r,...t===void 0?{}:{scope:t},...g(n,"pluginName"),...A(n,"pluginScope"),...g(n,"sourceName")}}function g(n,e){const t=n[e];if(t===void 0)return{};if(typeof t!="string"||!/^[a-z0-9][a-z0-9-]{0,63}$/u.test(t))throw o(`Invalid ${e}.`);return{[e]:t}}function A(n,e){const t=n[e];if(t===void 0)return{};if(t!=="user"&&t!=="project")throw o(`Invalid ${e}.`);return{[e]:t}}function I(n){const e=n.prompt;if(typeof e!="string"||e.trim().length===0||Buffer.byteLength(e,"utf8")>i.jsonlPromptBytes)throw o("Invalid prompt.");return e}function q(n){const e=n.input;if(typeof e!="string"||e.trim().length===0||[...e].length>h.speechInputCharacters)throw o("Invalid speech input.");return e}function k(n){const e=n.voice;if(typeof e!="string"||e.trim().length===0||[...e].length>h.speechVoiceCharacters)throw o("Invalid speech voice.");return e}function S(n){const e=n.responseFormat;if(e===void 0)return{};if(e!=="mp3"&&e!=="opus"&&e!=="aac"&&e!=="flac"&&e!=="wav"&&e!=="pcm")throw o("Invalid speech response format.");return{responseFormat:e}}function z(n,e){if(typeof n[e]!="boolean")throw o(`Invalid ${e}.`);return n[e]}function P(n){const e=s(n,"approvalId"),t=n.decision;if(t!=="allow"&&t!=="deny"&&t!=="redirect")throw o("Invalid approval decision.");const r=n.message;if(r!==void 0&&(typeof r!="string"||r.trim().length===0||Buffer.byteLength(r,"utf8")>i.jsonlPromptBytes))throw o("Invalid approval message.");if(t==="redirect"&&r===void 0)throw o("Approval redirect requires a message.");if(t!=="redirect"&&r!==void 0)throw o("Approval message is only valid for redirect decisions.");return t==="redirect"?{approvalId:e,decision:t,message:r}:{approvalId:e,decision:t}}function u(n,e){const t=n[e];if(t===void 0)return{};if(typeof t!="string"||t.length===0||Buffer.byteLength(t,"utf8")>i.jsonlIdBytes)throw o(`Invalid ${e}.`);return{[e]:t}}function N(n,e){const t=n[e];if(t===void 0)return{};if(!c(t))throw o(`Invalid ${e}.`);return{[e]:t}}function j(n){const e=n.name;if(typeof e!="string"||e.length===0||e.length>200||e.includes("\0"))throw o("Invalid attachment name.");return e}function $(n){const e=n.mimeType;if(typeof e!="string"||!/^[A-Za-z0-9][A-Za-z0-9!#$&^_.+-]*\/[A-Za-z0-9][A-Za-z0-9!#$&^_.+-]*$/u.test(e)||Buffer.byteLength(e,"utf8")>i.jsonlIdBytes)throw o("Invalid attachment MIME type.");return e}function E(n){const e=n.sizeBytes;if(typeof e!="number"||!Number.isSafeInteger(e)||e<=0||e>i.maxAttachmentBytes)throw o("Invalid attachment size.");return e}function L(n){const e=n.storageName;if(e===void 0)return{};if(typeof e!="string"||e.length===0||e.length>200||e.includes("\0")||/[\\/]/u.test(e))throw o("Invalid attachment storage name.");return{storageName:e}}function R(n){const e=n.index;if(typeof e!="number"||!Number.isSafeInteger(e)||e<0)throw o("Invalid attachment chunk index.");return e}function D(n){const e=n.dataBase64;if(typeof e!="string"||e.length===0||Buffer.byteLength(e,"utf8")>Math.ceil(i.desktopAttachmentChunkBytes/3)*4+4)throw o("Invalid attachment chunk.");return e}function C(n){const e=n.attachmentIds;if(e===void 0)return{};if(!Array.isArray(e)||e.length>i.pendingAttachments||e.some(t=>!c(t))||new Set(e).size!==e.length)throw o("Invalid attachment ids.");return{attachmentIds:e}}function K(n){const e=n.skills;if(e===void 0)return{};if(!Array.isArray(e)||e.length>32||e.some(t=>!c(t))||new Set(e).size!==e.length)throw o("Invalid skills.");return{skills:e}}function M(n){return y(n.execution)}function c(n){return typeof n=="string"&&n.length>0&&Buffer.byteLength(n,"utf8")<=i.jsonlIdBytes}function a(n){return typeof n=="object"&&n!==null&&!Array.isArray(n)}function o(n){return new Error(n)}export{H as isBrowserHostResponse,U as isComputerHostResponse,O as isDesktopHostResponse,F as parseDesktopRequest};
|
|
@@ -1,16 +1,26 @@
|
|
|
1
1
|
import type { ToolApprovalRequest } from '@lotagate/agent-sdk';
|
|
2
2
|
import type { DesktopExecutionPolicy } from './desktop-execution-policy.js';
|
|
3
|
+
export type DesktopApprovalDecision = {
|
|
4
|
+
decision: 'allow';
|
|
5
|
+
} | {
|
|
6
|
+
decision: 'deny';
|
|
7
|
+
} | {
|
|
8
|
+
decision: 'redirect';
|
|
9
|
+
message: string;
|
|
10
|
+
};
|
|
3
11
|
/** Owns Desktop approval/trust waiters and their AbortSignal lifecycle. */
|
|
4
12
|
export declare class DesktopApprovalBroker {
|
|
5
13
|
private readonly writeEvent;
|
|
6
14
|
private readonly approvals;
|
|
7
15
|
private readonly trusts;
|
|
8
16
|
constructor(writeEvent: (event: string, data: Record<string, unknown>) => Promise<void>);
|
|
9
|
-
waitForApproval(request: ToolApprovalRequest, sessionId: string, turnId: string, signal: AbortSignal, executionBoundary: DesktopExecutionPolicy['isolation'], fallbackReason?: string): Promise<
|
|
17
|
+
waitForApproval(request: ToolApprovalRequest, sessionId: string, turnId: string, signal: AbortSignal, executionBoundary: DesktopExecutionPolicy['isolation'], fallbackReason?: string): Promise<DesktopApprovalDecision>;
|
|
10
18
|
waitForTrust(trustRequestId: string, signal: AbortSignal): Promise<boolean>;
|
|
11
|
-
respondApproval(approvalId: string,
|
|
19
|
+
respondApproval(approvalId: string, decision: DesktopApprovalDecision | boolean): boolean;
|
|
12
20
|
respondTrust(trustRequestId: string, trusted: boolean): boolean;
|
|
13
21
|
close(): void;
|
|
14
|
-
private
|
|
15
|
-
private
|
|
22
|
+
private waitApproval;
|
|
23
|
+
private waitTrust;
|
|
24
|
+
private respondApprovalDecision;
|
|
25
|
+
private respondTrustDecision;
|
|
16
26
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{randomUUID as
|
|
1
|
+
import{randomUUID as l}from"node:crypto";import{actionDisplayName as d,actionKind as v}from"../orchestration/cli-turn-events.js";import{describeApproval as u}from"./desktop-protocol-helpers.js";class m{writeEvent;approvals=new Map;trusts=new Map;constructor(e){this.writeEvent=e}async waitForApproval(e,t,r,o,s,n){const i=l(),a=this.waitApproval(i,o);try{await this.writeEvent("approval.requested",{approvalId:i,turnId:r,sessionId:t,toolName:e.toolName,displayName:d(e.toolName),kind:v(e.toolName,e.toolInfo?.source),detail:u(e),executionBoundary:s,...n===void 0?{}:{fallbackReason:n}})}catch(p){throw this.respondApproval(i,{decision:"deny"}),p}return a}waitForTrust(e,t){return this.waitTrust(e,t)}respondApproval(e,t){return this.respondApprovalDecision(e,t)}respondTrust(e,t){return this.respondTrustDecision(e,t)}close(){for(const e of this.approvals.values())e.resolve({decision:"deny"});for(const e of this.trusts.values())e.resolve(!1);this.approvals.clear(),this.trusts.clear()}waitApproval(e,t){return new Promise(r=>{const o=()=>s({decision:"deny"}),s=n=>{this.approvals.delete(e),t.removeEventListener("abort",o),r(n)};if(t.aborted){s({decision:"deny"});return}t.addEventListener("abort",o,{once:!0}),this.approvals.set(e,{resolve:s,signal:t,onAbort:o})})}waitTrust(e,t){return new Promise(r=>{const o=()=>s(!1),s=n=>{this.trusts.delete(e),t.removeEventListener("abort",o),r(n)};if(t.aborted){s(!1);return}t.addEventListener("abort",o,{once:!0}),this.trusts.set(e,{resolve:s,signal:t,onAbort:o})})}respondApprovalDecision(e,t){const r=this.approvals.get(e);return r===void 0?!1:(r.resolve(typeof t=="boolean"?{decision:t?"allow":"deny"}:t),!0)}respondTrustDecision(e,t){const r=this.trusts.get(e);return r===void 0?!1:(r.resolve(t),!0)}}export{m as DesktopApprovalBroker};
|
|
@@ -1,16 +1,33 @@
|
|
|
1
1
|
import type { AttachmentPayload } from '../../domain/session/attachment.js';
|
|
2
|
+
export interface DesktopAttachmentStoreOptions {
|
|
3
|
+
readonly ttlMs?: number;
|
|
4
|
+
}
|
|
2
5
|
export declare class DesktopAttachmentStore {
|
|
3
6
|
private readonly pending;
|
|
4
7
|
private readonly completed;
|
|
8
|
+
private readonly ttlMs;
|
|
9
|
+
constructor(options?: DesktopAttachmentStoreOptions);
|
|
5
10
|
begin(input: {
|
|
6
11
|
id: string;
|
|
12
|
+
sessionId?: string;
|
|
7
13
|
name: string;
|
|
8
14
|
mimeType: string;
|
|
9
15
|
sizeBytes: number;
|
|
16
|
+
storageName?: string;
|
|
10
17
|
}): void;
|
|
11
18
|
appendChunk(id: string, index: number, encoded: string): void;
|
|
12
|
-
complete(id: string):
|
|
13
|
-
take(ids: readonly string[]): AttachmentPayload[];
|
|
19
|
+
complete(id: string): AttachmentUpload;
|
|
20
|
+
take(ids: readonly string[], sessionId?: string): AttachmentPayload[];
|
|
21
|
+
replace(id: string, upload: AttachmentUpload): void;
|
|
22
|
+
delete(id: string, sessionId?: string): void;
|
|
14
23
|
clear(): void;
|
|
15
24
|
private totalReservedBytes;
|
|
25
|
+
private schedulePendingExpiry;
|
|
26
|
+
private createRetainedAttachment;
|
|
27
|
+
private removePending;
|
|
28
|
+
private removeCompleted;
|
|
29
|
+
private clearExpiryTimer;
|
|
30
|
+
}
|
|
31
|
+
export interface AttachmentUpload extends AttachmentPayload {
|
|
32
|
+
sessionId: string;
|
|
16
33
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{CLI_ERROR_CODES as
|
|
1
|
+
import{CLI_ERROR_CODES as i}from"../../domain/errors/error-codes.js";import{CliError as d}from"../../domain/errors/cli-error.js";import{createImageAttachment as c,normalizeImage as l}from"../../infrastructure/clipboard/image-normalizer.js";import{RESOURCE_LIMITS as h}from"../../domain/runtime/resource-limits.js";class E{pending=new Map;completed=new Map;ttlMs;constructor(e={}){if(this.ttlMs=e.ttlMs??h.desktopAttachmentTtlMs,!Number.isSafeInteger(this.ttlMs)||this.ttlMs<=0)throw new RangeError("Desktop attachment TTL must be a positive safe integer.")}begin(e){if(!T(e.id)||!A(e.name)||!g(e.mimeType))throw o(i.ATTACHMENT_INVALID_IMAGE,"Invalid desktop attachment metadata.","The attachment metadata is invalid.");if(!Number.isSafeInteger(e.sizeBytes)||e.sizeBytes<=0||e.sizeBytes>h.maxAttachmentBytes)throw o(i.ATTACHMENT_TOO_LARGE,"Desktop attachment size is outside the allowed range.","The attachment exceeds the maximum allowed size.",!0);if(this.pending.has(e.id)||this.completed.has(e.id))throw o(i.ATTACHMENT_INVALID_IMAGE,"Desktop attachment id is already in use.","This attachment has already been uploaded.");if(this.totalReservedBytes()+e.sizeBytes>h.pendingAttachmentTotalBytes||this.pending.size+this.completed.size>=h.pendingAttachments)throw o(i.ATTACHMENT_TOO_LARGE,"Desktop attachment limits exceeded.","Too many or too-large attachments are pending.",!0);const t={id:e.id,sessionId:e.sessionId??"",name:e.name,mimeType:e.mimeType,sizeBytes:e.sizeBytes,...e.storageName===void 0?{}:{storageName:e.storageName},nextIndex:0,receivedBytes:0,chunks:[]};this.pending.set(e.id,t),this.schedulePendingExpiry(t)}appendChunk(e,t,r){const s=this.pending.get(e);if(s===void 0)throw o(i.ATTACHMENT_INVALID_IMAGE,"Desktop attachment upload was not started.","The attachment upload is not active.");if(!Number.isSafeInteger(t)||t!==s.nextIndex)throw o(i.ATTACHMENT_INVALID_IMAGE,"Desktop attachment chunk order is invalid.","The attachment upload is out of order.");const n=p(r);if(n.byteLength===0||n.byteLength>h.desktopAttachmentChunkBytes||s.receivedBytes+n.byteLength>s.sizeBytes)throw o(i.ATTACHMENT_TOO_LARGE,"Desktop attachment chunk exceeds its declared size.","The attachment upload is invalid or too large.",!0);s.chunks.push(n),s.receivedBytes+=n.byteLength,s.nextIndex+=1,this.schedulePendingExpiry(s)}complete(e){const t=this.pending.get(e);if(t===void 0)throw o(i.ATTACHMENT_INVALID_IMAGE,"Desktop attachment upload was not started.","The attachment upload is not active.");if(t.receivedBytes!==t.sizeBytes)throw o(i.ATTACHMENT_INVALID_IMAGE,"Desktop attachment upload is incomplete.","The attachment upload did not finish.");const r=Buffer.concat(t.chunks),s=t.mimeType.startsWith("image/")?(()=>{const n=l(r,t.mimeType),m=c(n,"file",t.id);return{sessionId:t.sessionId,attachment:{...m,name:t.name,...t.storageName===void 0?{}:{storageName:t.storageName}},bytes:n.bytes}})():{sessionId:t.sessionId,attachment:{id:t.id,name:t.name,mimeType:t.mimeType,sizeBytes:r.byteLength,source:"file",...t.storageName===void 0?{}:{storageName:t.storageName}},bytes:r};return this.removePending(e),this.completed.set(e,this.createRetainedAttachment(s)),s}take(e,t){if(new Set(e).size!==e.length)throw o(i.ATTACHMENT_INVALID_IMAGE,"Desktop attachment ids must be unique.","An attachment was referenced more than once.");const r=e.map(s=>{const n=this.completed.get(s);if(n===void 0)throw o(i.ATTACHMENT_INVALID_IMAGE,"Desktop attachment is not complete.","Complete the attachment upload before sending the prompt.");if(t!==void 0&&n.upload.sessionId.length>0&&n.upload.sessionId!==t)throw o(i.ATTACHMENT_INVALID_IMAGE,"Desktop attachment belongs to another session.","The attachment is not available in this session.");return{attachment:n.upload.attachment,bytes:n.upload.bytes}});return e.forEach(s=>this.removeCompleted(s)),r}replace(e,t){if(!this.completed.has(e))throw o(i.ATTACHMENT_INVALID_IMAGE,"Desktop attachment upload was not retained.","The attachment upload is not available.");this.removeCompleted(e),this.completed.set(e,this.createRetainedAttachment(t))}delete(e,t){const r=this.pending.get(e),s=this.completed.get(e),n=r?.sessionId??s?.upload.sessionId;if(t!==void 0&&n!==void 0&&n.length>0&&n!==t)throw o(i.ATTACHMENT_INVALID_IMAGE,"Desktop attachment belongs to another session.","The attachment is not available in this session.");this.removePending(e),this.removeCompleted(e)}clear(){for(const e of this.pending.values())this.clearExpiryTimer(e.expiryTimer);for(const e of this.completed.values())this.clearExpiryTimer(e.expiryTimer);this.pending.clear(),this.completed.clear()}totalReservedBytes(){let e=0;for(const t of this.pending.values())e+=t.sizeBytes;for(const t of this.completed.values())e+=t.upload.bytes.byteLength;return e}schedulePendingExpiry(e){this.clearExpiryTimer(e.expiryTimer);const t=setTimeout(()=>{this.pending.get(e.id)===e&&this.pending.delete(e.id)},this.ttlMs);t.unref?.(),e.expiryTimer=t}createRetainedAttachment(e){const t={upload:e},r=setTimeout(()=>{this.completed.get(e.attachment.id)===t&&this.completed.delete(e.attachment.id)},this.ttlMs);return r.unref?.(),t.expiryTimer=r,t}removePending(e){const t=this.pending.get(e);t!==void 0&&(this.clearExpiryTimer(t.expiryTimer),this.pending.delete(e))}removeCompleted(e){const t=this.completed.get(e);t!==void 0&&(this.clearExpiryTimer(t.expiryTimer),this.completed.delete(e))}clearExpiryTimer(e){e!==void 0&&clearTimeout(e)}}function p(a){if(!/^[A-Za-z0-9+/]*={0,2}$/u.test(a)||a.length===0||a.length%4!==0)throw o(i.ATTACHMENT_INVALID_IMAGE,"Desktop attachment chunk is not valid Base64.","The attachment upload is invalid.");return Buffer.from(a,"base64")}function T(a){return/^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/u.test(a)}function A(a){return a.length>0&&a.length<=200&&!a.includes("\0")}function g(a){return a.length<=h.jsonlIdBytes&&/^[A-Za-z0-9][A-Za-z0-9!#$&^_.+-]*\/[A-Za-z0-9][A-Za-z0-9!#$&^_.+-]*$/u.test(a)}function o(a,e,t,r=!1){return new d(a,e,{category:"attachment",userMessage:t,retryable:r})}export{E as DesktopAttachmentStore};
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
export type DesktopPermissionPolicy = 'ask' | 'allowlist' | 'review' | 'autonomous';
|
|
2
2
|
export type DesktopBrowserAccess = 'disabled' | 'read-only' | 'interactive' | 'autonomous';
|
|
3
|
+
export type DesktopBrowserExecution = 'isolated' | 'interactive';
|
|
3
4
|
export interface DesktopExecutionPolicy {
|
|
4
5
|
readonly permissionPolicy: DesktopPermissionPolicy;
|
|
5
6
|
readonly allowedTools?: readonly string[];
|
|
6
7
|
readonly browserAccess: DesktopBrowserAccess;
|
|
8
|
+
readonly browserExecution: DesktopBrowserExecution;
|
|
7
9
|
readonly isolation: 'sandbox' | 'host';
|
|
8
10
|
readonly hostFallback: 'ask' | 'deny' | 'allow';
|
|
9
11
|
readonly timeoutMs: number;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{RESOURCE_LIMITS as f}from"../../domain/runtime/resource-limits.js";function
|
|
1
|
+
import{RESOURCE_LIMITS as f}from"../../domain/runtime/resource-limits.js";function m(o){if(!a(o))throw new Error("Invalid Desktop execution policy.");const r=e(o.permissionPolicy,["ask","allowlist","review","autonomous"],"permissionPolicy"),t=e(o.browserAccess,["disabled","read-only","interactive","autonomous"],"browserAccess"),n=e(o.browserExecution,["isolated","interactive"],"browserExecution"),d=e(o.isolation,["sandbox","host"],"isolation"),l=e(o.hostFallback,["ask","deny","allow"],"hostFallback"),s=o.allowedTools===void 0?void 0:p(o.allowedTools),w=c(o.timeoutMs,1e4,7200*1e3,"timeoutMs"),i=o.retryAttempt===void 0?void 0:c(o.retryAttempt,0,3,"retryAttempt");return{permissionPolicy:r,...s===void 0?{}:{allowedTools:s},browserAccess:t,browserExecution:n,isolation:d,hostFallback:l,timeoutMs:w,...i===void 0?{}:{retryAttempt:i}}}function p(o){if(!Array.isArray(o)||o.length>128)throw new Error("Invalid Desktop allowedTools.");const r=o.map(t=>{if(typeof t!="string"||t.trim().length===0||Buffer.byteLength(t,"utf8")>f.jsonlIdBytes)throw new Error("Invalid Desktop allowedTools entry.");return t.trim()});if(new Set(r).size!==r.length)throw new Error("Desktop allowedTools must not contain duplicates.");return r}function e(o,r,t){if(typeof o!="string"||!r.includes(o))throw new Error(`Invalid Desktop ${t}.`);return o}function c(o,r,t,n){if(typeof o!="number"||!Number.isSafeInteger(o)||o<r||o>t)throw new Error(`Invalid Desktop ${n}.`);return o}function a(o){return typeof o=="object"&&o!==null&&!Array.isArray(o)}export{m as parseDesktopExecutionPolicy};
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { BrowserHostRequest, BrowserHostResponse } from './browser-host-tool-executor.js';
|
|
2
|
+
import type { DesktopHostExecutionRequest, DesktopHostExecutionResponse } from './desktop-host-tool-executor.js';
|
|
3
|
+
import type { ComputerHostRequest, ComputerHostResponse } from './computer-host-tool-executor.js';
|
|
4
|
+
import type { JsonLineWriter } from './json-line-writer.js';
|
|
5
|
+
type HostCancel = {
|
|
6
|
+
version: 1;
|
|
7
|
+
type: 'host.cancel';
|
|
8
|
+
requestId: string;
|
|
9
|
+
tool: 'browser' | 'computer' | 'filesystem' | 'shell' | 'git' | 'artifact';
|
|
10
|
+
sessionId: string;
|
|
11
|
+
};
|
|
12
|
+
export type DesktopHostWireMessage = BrowserHostRequest | BrowserHostResponse | DesktopHostExecutionRequest | DesktopHostExecutionResponse | ComputerHostRequest | ComputerHostResponse | HostCancel;
|
|
13
|
+
/** Owns the request/response lifecycle for Desktop-hosted capabilities. */
|
|
14
|
+
export declare class DesktopHostBridge {
|
|
15
|
+
private readonly writer;
|
|
16
|
+
private readonly isStopping;
|
|
17
|
+
private readonly browserRequests;
|
|
18
|
+
private readonly desktopRequests;
|
|
19
|
+
private readonly computerRequests;
|
|
20
|
+
constructor(writer: JsonLineWriter<DesktopHostWireMessage>, isStopping: () => boolean);
|
|
21
|
+
requestBrowser(request: BrowserHostRequest, signal?: AbortSignal): Promise<BrowserHostResponse>;
|
|
22
|
+
requestDesktop(request: DesktopHostExecutionRequest, signal?: AbortSignal): Promise<DesktopHostExecutionResponse>;
|
|
23
|
+
requestComputer(request: ComputerHostRequest, signal?: AbortSignal): Promise<ComputerHostResponse>;
|
|
24
|
+
resolveBrowser(response: BrowserHostResponse): void;
|
|
25
|
+
resolveDesktop(response: DesktopHostExecutionResponse): void;
|
|
26
|
+
resolveComputer(response: ComputerHostResponse): void;
|
|
27
|
+
close(): void;
|
|
28
|
+
private request;
|
|
29
|
+
private resolve;
|
|
30
|
+
private rejectAll;
|
|
31
|
+
private sendCancel;
|
|
32
|
+
}
|
|
33
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
class c{writer;isStopping;browserRequests=new Map;desktopRequests=new Map;computerRequests=new Map;constructor(e,t){this.writer=e,this.isStopping=t}requestBrowser(e,t){return this.request(e,t,this.browserRequests,"Browser action was cancelled.")}requestDesktop(e,t){return this.request(e,t,this.desktopRequests,"Host execution was cancelled.")}requestComputer(e,t){return this.request(e,t,this.computerRequests,"Computer action was cancelled.")}resolveBrowser(e){this.resolve(this.browserRequests,e)}resolveDesktop(e){this.resolve(this.desktopRequests,e)}resolveComputer(e){this.resolve(this.computerRequests,e)}close(){this.rejectAll(this.browserRequests,"The Desktop browser bridge was closed."),this.rejectAll(this.desktopRequests,"The Desktop execution bridge was closed."),this.rejectAll(this.computerRequests,"The Desktop computer bridge was closed.")}request(e,t,s,i){if(this.isStopping())return Promise.reject(new Error("The Desktop host bridge is stopping."));if(t?.aborted===!0)return Promise.reject(new Error(i));const n=new Promise((r,d)=>{const o={resolve:r,reject:d,...t===void 0?{}:{signal:t}};t!==void 0&&(o.onAbort=()=>{s.delete(e.requestId),this.sendCancel(e),d(new Error(i))},t.addEventListener("abort",o.onAbort,{once:!0})),s.set(e.requestId,o)});return n.catch(()=>{}),this.writer.write(e).then(()=>n).finally(()=>{const r=s.get(e.requestId);r!==void 0&&(r.onAbort!==void 0&&r.signal?.removeEventListener("abort",r.onAbort),s.delete(e.requestId))})}resolve(e,t){const s=e.get(t.requestId);s!==void 0&&(s.onAbort!==void 0&&s.signal?.removeEventListener("abort",s.onAbort),e.delete(t.requestId),s.resolve(t))}rejectAll(e,t){for(const s of e.values())s.onAbort!==void 0&&s.signal?.removeEventListener("abort",s.onAbort),s.reject(new Error(t));e.clear()}async sendCancel(e){this.isStopping()||await this.writer.write({version:1,type:"host.cancel",requestId:e.requestId,tool:e.tool,sessionId:e.sessionId}).catch(()=>{})}}export{c as DesktopHostBridge};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/** Capability metadata supplied by the Desktop native host during handshake. */
|
|
2
|
+
export interface DesktopHostCapability {
|
|
3
|
+
readonly available: boolean;
|
|
4
|
+
readonly provider: string;
|
|
5
|
+
readonly operations: readonly string[];
|
|
6
|
+
readonly reason?: string;
|
|
7
|
+
}
|
|
8
|
+
export interface DesktopHostCapabilities {
|
|
9
|
+
readonly computer?: DesktopHostCapability;
|
|
10
|
+
}
|
|
11
|
+
export declare function hostCapabilityAvailable(capabilities: DesktopHostCapabilities | undefined, capability: DesktopHostCapability | undefined): boolean;
|
|
12
|
+
export declare function normalizeDesktopHostCapabilities(value: unknown): DesktopHostCapabilities | undefined;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
function a(r,n){return r===void 0?n?.available!==!1:n?.available===!0}function s(r){if(!i(r))return;const n=o(r.computer);return{...n===void 0?{}:{computer:n}}}function o(r){if(!i(r)||typeof r.available!="boolean"||typeof r.provider!="string"||r.provider.trim().length===0||!Array.isArray(r.operations))return;const n=r.operations.filter(t=>typeof t=="string"&&t.length>0),e=typeof r.reason=="string"&&r.reason.length>0?r.reason:void 0;return{available:r.available,provider:r.provider,operations:n,...e===void 0?{}:{reason:e}}}function i(r){return typeof r=="object"&&r!==null&&!Array.isArray(r)}export{a as hostCapabilityAvailable,s as normalizeDesktopHostCapabilities};
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import type { ToolDefinition } from '@lotagate/sdk';
|
|
2
|
-
import type { ToolExecutionContext, ToolExecutor, ToolInfo, ToolResult } from '@lotagate/agent-sdk';
|
|
2
|
+
import type { ToolAuthorizationDecision, ToolExecutionContext, ToolExecutor, ToolInfo, ToolResult } from '@lotagate/agent-sdk';
|
|
3
3
|
import type { ToolActivityObserver } from '../../ports/tool-activity.js';
|
|
4
4
|
import type { ToolApprovalRequest } from '@lotagate/agent-sdk';
|
|
5
5
|
import type { FileChangeDiff } from '../../domain/filesystem/file-change-diff.js';
|
|
6
|
+
import { type HostPlatformContext } from '../../infrastructure/platform/host-platform.js';
|
|
6
7
|
export type DesktopHostExecutionTool = 'filesystem' | 'shell' | 'git' | 'artifact';
|
|
7
8
|
export interface DesktopHostExecutionRequest {
|
|
8
9
|
version: 1;
|
|
@@ -13,6 +14,7 @@ export interface DesktopHostExecutionRequest {
|
|
|
13
14
|
runId: string;
|
|
14
15
|
action: string;
|
|
15
16
|
params: Record<string, unknown>;
|
|
17
|
+
taskId?: string;
|
|
16
18
|
executionBoundary: 'sandbox' | 'host';
|
|
17
19
|
hostFallback: 'ask' | 'deny' | 'allow';
|
|
18
20
|
projectRoot?: string;
|
|
@@ -25,6 +27,7 @@ export interface DesktopHostExecutionResponse {
|
|
|
25
27
|
requestId: string;
|
|
26
28
|
tool: DesktopHostExecutionTool;
|
|
27
29
|
ok: boolean;
|
|
30
|
+
environmentId?: string;
|
|
28
31
|
result?: unknown;
|
|
29
32
|
error?: {
|
|
30
33
|
code: string;
|
|
@@ -35,6 +38,13 @@ export interface DesktopHostExecutionResponse {
|
|
|
35
38
|
executionBoundary?: 'sandbox' | 'host';
|
|
36
39
|
fallbackReason?: string;
|
|
37
40
|
fileChange?: FileChangeDiff;
|
|
41
|
+
artifacts?: readonly DesktopArtifactDescriptor[];
|
|
42
|
+
}
|
|
43
|
+
export interface DesktopArtifactDescriptor {
|
|
44
|
+
readonly id: string;
|
|
45
|
+
readonly name: string;
|
|
46
|
+
readonly kind: string;
|
|
47
|
+
readonly sizeBytes: number;
|
|
38
48
|
}
|
|
39
49
|
export type DesktopHostInvoker = (request: DesktopHostExecutionRequest, signal?: AbortSignal) => Promise<DesktopHostExecutionResponse>;
|
|
40
50
|
/**
|
|
@@ -52,8 +62,11 @@ export declare class DesktopHostToolExecutor implements ToolExecutor {
|
|
|
52
62
|
projectRoot?: string;
|
|
53
63
|
executionCwd?: string;
|
|
54
64
|
executionWorkspaceId?: string;
|
|
65
|
+
executionPlatform?: HostPlatformContext;
|
|
66
|
+
readOnly?: boolean;
|
|
55
67
|
onWrite?: (change: FileChangeDiff) => void | Promise<void>;
|
|
56
|
-
|
|
68
|
+
onArtifacts?: (artifacts: readonly DesktopArtifactDescriptor[], context: ToolExecutionContext) => void | Promise<void>;
|
|
69
|
+
onFallbackApproval?: (request: ToolApprovalRequest, reason: string) => boolean | ToolAuthorizationDecision | Promise<boolean | ToolAuthorizationDecision>;
|
|
57
70
|
onActivity?: ToolActivityObserver;
|
|
58
71
|
});
|
|
59
72
|
listTools(): readonly ToolDefinition[];
|