@lotagate/cli 0.1.26 → 0.1.28

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. package/dist/application/agent/bounded-text-accumulator.d.ts +15 -0
  2. package/dist/application/agent/bounded-text-accumulator.js +1 -0
  3. package/dist/application/agent/cli-agent-execution-service.d.ts +1 -1
  4. package/dist/application/agent/cli-agent-execution-service.js +1 -1
  5. package/dist/application/commands/application-command-executor.js +2 -2
  6. package/dist/application/commands/cli-command-services.d.ts +1 -1
  7. package/dist/application/commands/cli-command-services.js +31 -31
  8. package/dist/application/commands/goal-command-handlers.js +6 -6
  9. package/dist/application/commands/memory-command-handler.d.ts +1 -1
  10. package/dist/application/commands/memory-command-handler.js +11 -11
  11. package/dist/application/commands/runtime-command-handlers.js +11 -11
  12. package/dist/application/extensions/plugin-contribution-registry.js +1 -1
  13. package/dist/application/lifecycle/operation-registry.d.ts +11 -0
  14. package/dist/application/lifecycle/operation-registry.js +1 -0
  15. package/dist/application/orchestration/headless-agent-turn.js +3 -3
  16. package/dist/application/protocol/desktop-agent-command.js +1 -3
  17. package/dist/application/protocol/desktop-title-generator.d.ts +5 -0
  18. package/dist/application/protocol/desktop-title-generator.js +3 -0
  19. package/dist/application/protocol/jsonl-agent-command.js +2 -2
  20. package/dist/application/workspace/workspace-file-index.d.ts +2 -2
  21. package/dist/application/workspace/workspace-file-index.js +1 -1
  22. package/dist/domain/auth/auth-profile.d.ts +1 -0
  23. package/dist/domain/auth/auth-profile.js +1 -1
  24. package/dist/domain/runtime/resource-limits.d.ts +9 -0
  25. package/dist/domain/runtime/resource-limits.js +1 -1
  26. package/dist/infrastructure/agent-sdk/tool-calling-model-client.js +1 -1
  27. package/dist/infrastructure/cache/file-cache-store.d.ts +5 -0
  28. package/dist/infrastructure/cache/file-cache-store.js +2 -2
  29. package/dist/infrastructure/clipboard/clipboard-adapter-factory.js +1 -1
  30. package/dist/infrastructure/clipboard/clipboard-process.d.ts +20 -0
  31. package/dist/infrastructure/clipboard/clipboard-process.js +1 -0
  32. package/dist/infrastructure/clipboard/clipboard-text-adapter-factory.js +1 -1
  33. package/dist/infrastructure/clipboard/windows-clipboard-adapter.js +1 -1
  34. package/dist/infrastructure/clipboard/windows-clipboard-text-adapter.js +1 -1
  35. package/dist/infrastructure/credentials/encrypted-file-credential-store.js +2 -2
  36. package/dist/infrastructure/credentials/secret-input.d.ts +1 -1
  37. package/dist/infrastructure/credentials/secret-input.js +3 -3
  38. package/dist/infrastructure/extensions/mcp-runtime.d.ts +2 -0
  39. package/dist/infrastructure/extensions/mcp-runtime.js +1 -1
  40. package/dist/infrastructure/extensions/plugin-agent-discovery.d.ts +3 -1
  41. package/dist/infrastructure/extensions/plugin-agent-discovery.js +1 -1
  42. package/dist/infrastructure/extensions/skill-manager.js +2 -2
  43. package/dist/infrastructure/filesystem/atomic-file-store.js +1 -1
  44. package/dist/infrastructure/sessions/file-session-store.d.ts +8 -0
  45. package/dist/infrastructure/sessions/file-session-store.js +4 -4
  46. package/dist/main.js +2 -2
  47. package/dist/presentation/tui/tui-agent-turn-controller.js +1 -1
  48. package/dist/presentation/tui/tui-application.d.ts +2 -0
  49. package/dist/presentation/tui/tui-application.js +3 -3
  50. package/dist/version.d.ts +1 -1
  51. package/dist/version.js +1 -1
  52. package/package.json +8 -8
@@ -1,3 +1 @@
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
-
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};
1
+ import{randomUUID as K}from"node:crypto";import{AuthenticationService as D}from"../auth/authentication-service.js";import{createLotaGateClient as V}from"../../infrastructure/sdk/lotagate-client-factory.js";import{loadExecutionConfig as L}from"../config/execution-config.js";import{prepareCliAgent as ot,warmMcpRuntime as it}from"../agent/cli-agent-execution-service.js";import{createContextManagement as nt}from"../agent/context-compaction.js";import{persistContextCompaction as rt}from"../agent/context-compaction-persistence.js";import{CliAgentSessionStoreRegistry as ct}from"../agent/agent-session-store-registry.js";import{AgentRunTracker as dt}from"../agent/agent-run-tracker.js";import{formatContinuationCheckpoint as pt}from"../agent/continuation-checkpoint.js";import{CliTurnEventProjector as mt}from"../orchestration/cli-turn-event-projector.js";import{persistCliTurnEvent as lt}from"../orchestration/cli-turn-persistence.js";import{createTurnId as ht,recordTurnCompletion as ut,recordTurnFailure as ft,recordTurnStart as wt}from"../session/session-turn-lifecycle.js";import{ProjectSessionAccess as Y}from"../session/project-session-access.js";import{permissionRuleForRequest as gt}from"../security/workspace-permission-policy.js";import{agentLimitNotice as yt,classifyAgentLimit as kt}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 _}from"../../domain/runtime/resource-limits.js";import{getDesktopCommandCatalog as bt}from"./desktop-command-catalog.js";import{COMMAND_REGISTRY as $}from"../commands/command-runtime.js";import{executeDesktopCommand as It}from"./desktop-command-executor.js";import{DesktopAttachmentStore as vt}from"./desktop-attachment-store.js";import{SubagentJobRegistry as St}from"../orchestration/cli-subagent-service.js";import{subagentDesktopPayload as xt}from"./desktop-orchestration-events.js";import{DesktopIntentEventWriter as Tt}from"./desktop-intent-event-writer.js";import{resolveModelContextWindow as Ct}from"../model/model-context-window.js";import{ContextUsageService as Rt}from"../agent/context-usage-service.js";import{isCliAgentPreparationMutation as Mt}from"../agent/cli-agent-preparation-cache.js";import{JsonLineWriter as Dt}from"./json-line-writer.js";import{desktopApprovalMode as Pt,sessionResult as Q,toDesktopError as I}from"./desktop-protocol-helpers.js";import{DesktopApprovalBroker as At}from"./desktop-approval-broker.js";import{SessionExecutionWorkspaceService as Nt}from"../workspace/session-execution-workspace.js";import{buildUserMessage as Ot}from"../../infrastructure/clipboard/image-normalizer.js";import{isBrowserHostResponse as Bt,isComputerHostResponse as Lt,isDesktopHostResponse as _t,parseDesktopRequest as jt}from"./desktop-agent-request-parser.js";import{desktopMcpEvent as X}from"./desktop-mcp-events.js";import{DesktopExtensionService as Ht}from"./desktop-extension-service.js";import{DesktopExtensionRequestHandler as Wt}from"./desktop-extension-request-handler.js";import{DesktopHostBridge as Ut}from"./desktop-host-bridge.js";import{normalizeDesktopHostCapabilities as Ft}from"./desktop-host-capabilities.js";import{loadModelCatalog as Z}from"../model/model-catalog-service.js";import{SdkMediaGatewayClient as Gt}from"../../infrastructure/sdk/sdk-media-gateway-client.js";import{artifactIdsData as j,resolveSpeechModel as zt}from"./desktop-agent-helpers.js";import{DESKTOP_PROTOCOL_VERSION as P,DESKTOP_SPEECH_MAX_BYTES as Jt,DESKTOP_SPEECH_SYNTHESIS_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{OperationRegistry as q}from"../lifecycle/operation-registry.js";import{BoundedTextAccumulator as $t}from"../agent/bounded-text-accumulator.js";import{generateDesktopTitle as Qt}from"./desktop-title-generator.js";import{DESKTOP_PROTOCOL_VERSION as Ze}from"./desktop-agent-contracts.js";async function $e(k,t,e,s,o){return new Xt(k,t,s).run(e,o)}class Xt{runtime;cwd;writer;turns=new Map;activeTurnBySession=new Map;subagentJobs=new St;sessionStores=new ct(_.desktopSessionStoreMaxEntries);turnContexts=new Map;attachments=new vt;approvalBroker=new At((t,e)=>this.writeEvent(t,e));commands=new Map;speechRequests=new Map;titleOperations=new q;backgroundOperations=new q;activeRequests=new Set;hostBridge;extensions=new Ht;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 Dt(s),this.executionWorkspaces=t.executionWorkspaces??new Nt(t.paths.globalConfigDir,t.platform),this.hostBridge=new Ut(this.writer,()=>this.stopping),this.extensionRequests=new Wt(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,this.backgroundOperations.abort(e?.reason);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);this.titleOperations.abort(e?.reason),t.destroy()};e?.addEventListener("abort",o,{once:!0});try{for await(const a of Et(t,_.desktopJsonlLineBytes)){if(a.trim().length===0)continue;let r;try{r=JSON.parse(a)}catch(p){s+=1,await this.writeError("unknown","protocol",I(p));continue}if(Bt(r)){this.hostBridge.resolveBrowser(r);continue}if(_t(r)){this.hostBridge.resolveDesktop(r);continue}if(Lt(r)){this.hostBridge.resolveComputer(r);continue}let d;try{d=jt(r)}catch(p){s+=1,await this.writeError("unknown","protocol",I(p));continue}if(this.activeRequests.size>=_.jsonlMaxInFlight&&!Zt(d)?(s+=1,await this.writeError(d.id,d.method,{code:"CLI_PROTOCOL_OVERLOADED",category:"protocol",message:"The CLI Desktop protocol is busy. Retry the request later.",retryable:!0})):this.dispatch(d,()=>{s+=1}),this.stopping)break}}catch(a){s+=1,await this.writeError("unknown","protocol",I(a))}finally{e?.removeEventListener("abort",o),this.backgroundOperations.abort(),this.titleOperations.abort();for(const a of this.turns.values())a.controller.abort();for(const a of this.commands.values())a.controller.abort();for(const a of this.speechRequests.values())a.abort();await Promise.allSettled(this.activeRequests),await Promise.allSettled([...this.turns.values()].map(a=>a.operation)),await Promise.allSettled([...this.commands.values()].map(a=>a.operation)),await this.titleOperations.close(),await this.backgroundOperations.close(),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}dispatch(t,e){let s;s=this.handle(t).catch(o=>(this.runtime.logger.error("CLI Desktop request failed.",{method:t.method,requestId:t.id,reason:o instanceof Error?o.message:String(o)}),e(),this.writeError(t.id,t.method,I(o)))).finally(()=>{this.activeRequests.delete(s)}),this.activeRequests.add(s)}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=Ft(t.params.hostCapabilities),this.respond(t,{protocol:"lotagate.desktop",version:P,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 L(this.runtime,this.cwd),o=s.config.profile??D.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,{...Q(a),executionWorkspace:r}),this.startBackgroundMcpWarmup()}async generateTitle(t){const e=await this.titleOperations.start(s=>Qt(this.runtime,this.cwd,t,s));await this.respond(t,{title:e})}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 Y(this.runtime.sessionStore).require(t.params.sessionId,e.fingerprint),o=await this.executionWorkspaces.prepare({sessionId:s.id,projectRoot:e.canonicalPath});await this.respond(t,{...Q(s),executionWorkspace:o,history:await this.runtime.sessionStore.readHistory(s.id)}),this.startBackgroundMcpWarmup()}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(m=>m.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 Y(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 d=async()=>{r||(r=!0,await a())};let p=!1;try{const m=await this.executionWorkspaces.prepare({sessionId:e,projectRoot:s.canonicalPath}),l=this.attachments.take(t.params.attachmentIds??[],e),f=ht(),E=new AbortController;this.turnContexts.set(f,{...m,...t.params.taskId===void 0?{}:{taskId:t.params.taskId}}),this.activeTurnBySession.set(e,f);const T=this.executeTurn(t,o,s,m,f,E,l,d).catch(async C=>{await this.writeEvent(E.signal.aborted?"turn.cancelled":"turn.failed",{turnId:f,sessionId:e,...E.signal.aborted?{}:{error:I(C)}})}).finally(()=>{this.turns.delete(f),this.turnContexts.delete(f),this.activeTurnBySession.get(e)===f&&this.activeTurnBySession.delete(e),this.sessionStores.prune(this.activeTurnBySession.keys())});this.turns.set(f,{sessionId:e,controller:E,operation:T}),p=!0,await this.respond(t,{accepted:!0,turnId:f,sessionId:e})}finally{p||await d()}}async executeTurn(t,e,s,o,a,r,d,p){try{await this.executeTurnBody(t,e,s,o,a,r,d)}finally{await p()}}async executeTurnBody(t,e,s,o,a,r,d){const p=await L(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??p.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 f=t.params.reasoningEffort??p.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 E=e.profile??p.config.profile??D.defaultProfileName(),T=await this.runtime.auth.requireCredential(E),C=V(T.profile,T.apiKey),H=await Ct(this.runtime,l,E),A=new dt,N=this.sessionStores.getOrCreate(e.id,this.activeTurnBySession.keys()),R=new Set,tt=new Rt(this.runtime,s.canonicalPath),W=async()=>{try{const n=await tt.buildForProject({sessionId:e.id,model:l,profileName:E,pluginNames:[],pendingAttachments:0,agentSessionStore:N});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)})}},S=new $t,x=[];for(const n of d){const h=n.attachment.storagePath===void 0?await this.runtime.sessionStore.saveAttachment(e.id,n.attachment,n.bytes):n.attachment;x.push({...n,attachment:h})}await wt(this.runtime.sessionStore,e.id,a,t.params.prompt,x.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 et={onSubagentLifecycle:n=>this.writeEvent(`subagent.${n.type}`,xt(n,a),{delivery:"best-effort"})},at={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 h={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},w=(u,v)=>{this.writeEvent(u,v,{delivery:"best-effort"}).catch(b=>this.runtime.logger.warn("Desktop tool activity event failed.",{event:u,reason:b instanceof Error?b.message:String(b)}))};return w("tool.activity.started",{...h,status:"running"}),{complete:(u,v)=>w("tool.activity.completed",{...h,...v??{},status:u?"failed":"completed",isError:u})}}};let O,B=!1;const U=setTimeout(()=>r.abort(),t.params.execution.timeoutMs);try{const n=t.params.execution,h=await ot({runtime:this.runtime,projectRoot:s.canonicalPath,executionCwd:o.executionCwd,executionWorkspaceId:o.id,model:l,client:C,reasoningEffort:f,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:et,subagentJobs:this.subagentJobs,...this.browserHostEnabled?{browserHost:(i,c)=>this.hostBridge.requestBrowser(i,c)}:{},...this.desktopHostEnabled?{desktopHost:((i,c)=>this.hostBridge.requestDesktop(i,c))}:{},...this.computerHostEnabled?{computerHost:((i,c)=>this.hostBridge.requestComputer(i,c))}:{},onArtifacts:i=>{for(const c of i)R.add(c.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,c)=>this.approvalBroker.waitForApproval(i,e.id,a,r.signal,"host",c)}:{},toolActivity:at,onIntentLifecycle:i=>this.intentEvents.write(i,e.id,a),contextManagement:nt({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:()=>pt({snapshot:A.snapshot()})}),agentSessionStore:N,prompt:(i,c)=>this.approvalBroker.waitForApproval(i,e.id,a,c??r.signal,n.isolation),rememberApproval:i=>this.runtime.workspaceSettings.rememberAllow(s.canonicalPath,gt(i.toolName,i.input)),audit:async i=>{const c={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.",c),await this.runtime.sessionStore.appendEvent({type:"tool.approval",timestamp:new Date().toISOString(),sessionId:e.id,turnId:a,data:c})},onModelComplete:i=>{A.observeModel(i),i.usage!==void 0&&this.writeEvent("usage.updated",{turnId:a,sessionId:e.id,model:l,usage:i.usage},{delivery:"best-effort"}).catch(c=>this.runtime.logger.warn("Desktop usage event failed.",{reason:c instanceof Error?c.message:String(c)}))},onWrite:i=>{this.writeEvent("file.changed",{turnId:a,sessionId:e.id,change:i},{delivery:"best-effort"}).catch(c=>this.runtime.logger.warn("Desktop file event failed.",{reason:c instanceof Error?c.message:String(c)}))},mcpLifecycle:i=>this.writeMcpRuntimeEvent(i,e.id,a)});O=h.close;const w=new mt,u=await h.buildInput(t.params.prompt),v=await this.runtime.sessionStore.readHistory(e.id);N.hydrate(e.id,v.transcript,v.events,u.input);const b=Yt(u.input,x.map(({attachment:i,bytes:c})=>({byteLength:c.byteLength,mimeType:i.mimeType,name:i.name})));if(!b.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 ${b.limitBytes/1e6} MB decimal Gateway JSON limit. Remove an attachment or shorten the prompt and retry.`,details:{kind:"gateway-json-body",maxBytes:b.limitBytes,projectedBytes:b.projectedBytes}});const F=x.length===0?void 0:Ot(u.input,x),G=F?.content===null?void 0:F?.content,st=await h.agent.initialize();for(const i of st)(i.type==="mcp.server.status"||i.type==="mcp.tool.catalog.updated")&&await this.writeMcpEvent(i,e.id,a);for await(const i of h.run({input:u.input,userInput:u.userInput,...u.workspaceContext===void 0?{}:{workspaceContext:u.workspaceContext},...G===void 0?{}:{inputContent:G},model:l,sessionId:e.id,stream:!0,signal:r.signal,metadata:{taskId:t.params.taskId,turnId:a}})){A.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 rt(this.runtime,h,e.id,i.metrics,a),await W());const c=w.observe(i);for(const M of c)await this.emitProjected(M,e.id,a,S,R);if(i.type==="session.failed"){const M=kt(i.error);if(M===void 0)throw i.error;const z=yt(M);S.append(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",...j(R)});break}}if(S.toString().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),B=!0,await ut(this.runtime.sessionStore,e.id,a,S.toString()),await this.runtime.sessionStore.updateStatus(e.id,"completed"),await W(),await this.writeEvent("turn.completed",{turnId:a,sessionId:e.id,content:S.toString(),...j(R)})}catch(n){if(!B)try{const w=await this.executionWorkspaces.apply(e.id);B=!0,w.appliedPaths.length>0&&this.runtime.logger.info("Applied session workspace changes after turn failure.",{sessionId:e.id,turnId:a,appliedPaths:w.appliedPaths})}catch(w){this.runtime.logger.error("Failed to apply session workspace changes after turn failure.",{sessionId:e.id,turnId:a,reason:w instanceof Error?w.message:String(w)})}const h=r.signal.aborted;throw await ft(this.runtime.sessionStore,e.id,a,n,h),await this.runtime.sessionStore.updateStatus(e.id,h?"cancelled":"failed"),n}finally{if(this.intentEvents.clear(a),U!==void 0&&clearTimeout(U),O!==void 0)try{await O()}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.append(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.replace(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"?j(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 lt(this.runtime.sessionStore,e,t,{turnId:s})}async writeMcpEvent(t,e,s){const o=X(t,e,s);await this.writeEvent(o.name,o.data)}async writeMcpRuntimeEvent(t,e,s){const o=X(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 Z(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??D.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 L(this.runtime,this.cwd)).config.profile??D.defaultProfileName(),o=await this.runtime.auth.requireCredential(s),a=await Z(this.runtime,this.cwd),r=zt(t.params.model,a.data),d=new AbortController;this.speechRequests.set(t.id,d);const p=setTimeout(()=>d.abort(new Error("Desktop speech synthesis timed out.")),Kt);try{const m=await new Gt(V(o.profile,o.apiKey)).createSpeech({model:r,input:t.params.input,voice:t.params.voice,responseFormat:t.params.responseFormat??"mp3"},{signal:d.signal});if(m.bytes.byteLength===0||m.bytes.byteLength>Jt)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:m.contentType??"audio/mpeg",bytesBase64:Buffer.from(m.bytes).toString("base64")})}finally{clearTimeout(p),this.speechRequests.get(t.id)===d&&this.speechRequests.delete(t.id)}}async listCommands(t){await this.respond(t,bt())}async startCommand(t){const s=$.get(t.params.actionId)?.desktop?.mutationKey;if(s!==void 0&&(this.turns.size>0||[...this.commands.values()].some(m=>m.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 d=Promise.resolve().then(()=>this.executeCommand(t,o,a)).catch(async m=>{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:I(m).code}),await this.writeEvent("command.failed",{commandId:o,actionId:t.params.actionId,error:I(m)})}).finally(()=>this.commands.delete(o));this.commands.set(o,{controller:a,operation:d,...s===void 0?{}:{mutationKey:s}});const p={accepted:!0,commandId:o,actionId:t.params.actionId};await this.respond(t,p)}async executeCommand(t,e,s){await this.writeEvent("command.started",{commandId:e,actionId:t.params.actionId});const o=await It({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,$.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)}startBackgroundMcpWarmup(){this.backgroundOperations.start(t=>it(this.runtime,this.cwd,t)).catch(t=>this.runtime.logger.warn("MCP session warm-up failed.",{reason:t instanceof Error?t.message:String(t)}))}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:P,id:t.id,type:"response",method:t.method,ok:!0,result:e})}async writeError(t,e,s){await this.writer.write({version:P,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:P,type:"event",scope:typeof e.sessionId=="string"?"session":"control",event:t,data:{...r,...e}},s)}}function Zt(k){return k.method==="turn.cancel"||k.method==="command.cancel"||k.method==="approval.respond"||k.method==="trust.respond"||k.method==="shutdown"}export{Ze as DESKTOP_PROTOCOL_VERSION,$e as runDesktopAgentCommand};
@@ -0,0 +1,5 @@
1
+ import type { DesktopRequest } from './desktop-agent-request-parser.js';
2
+ import type { CliRuntime } from '../../bootstrap/runtime-factory.js';
3
+ export declare function generateDesktopTitle(runtime: CliRuntime, cwd: string, request: Extract<DesktopRequest, {
4
+ method: 'title.generate';
5
+ }>, signal: AbortSignal): Promise<string>;
@@ -0,0 +1,3 @@
1
+ import{TITLE_GENERATION_TIMEOUT_MS as y}from"./desktop-agent-contracts.js";import{AuthenticationService as b}from"../auth/authentication-service.js";import{createLotaGateClient as h}from"../../infrastructure/sdk/lotagate-client-factory.js";import{loadExecutionConfig as O}from"../config/execution-config.js";import{prepareCliAgent as C}from"../agent/cli-agent-execution-service.js";import{BoundedTextAccumulator as x}from"../agent/bounded-text-accumulator.js";import{CliError as u}from"../../domain/errors/cli-error.js";import{CLI_ERROR_CODES as w}from"../../domain/errors/error-codes.js";async function P(a,i,s,t){t.throwIfAborted();const l=await O(a,i),o=s.params.model??l.config.model;if(o==null)throw new u(w.MODEL_NOT_FOUND,"No model is selected.",{category:"model",userMessage:"No model is available for session title generation."});const g=l.config.profile??b.defaultProfileName(),c=await a.auth.requireCredential(g),r=new AbortController,m=()=>r.abort(t.reason);t.addEventListener("abort",m,{once:!0});const E=setTimeout(()=>r.abort(),y);let p;const n=new x;try{const d=await C({runtime:a,projectRoot:i,executionCwd:i,model:o,client:h(c.profile,c.apiKey),profile:"title-generator",approvalMode:"auto",allowedTools:[],selectedSkills:[],allowSubagents:!1,browserAccess:"disabled"});p=d.close;const T=["## 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",s.params.prompt].join(`
2
+
3
+ `);for await(const e of d.run({input:T,model:o,signal:r.signal,stream:!0}))if(e.type==="model.delta")n.append(e.content);else if(e.type==="session.completed"&&n.isEmpty)n.replace(e.content);else if(e.type==="session.failed")throw e.error;const f=n.toString();if(f.trim().length===0)throw new u(w.MODEL_PROTOCOL_EMPTY_RESPONSE,"Title generation returned an empty response.",{category:"model",userMessage:"The model did not return a session title.",retryable:!0});return f}finally{clearTimeout(E),t.removeEventListener("abort",m);try{await p?.()}finally{r.abort()}}}export{P as generateDesktopTitle};
@@ -1,3 +1,3 @@
1
- import{AuthenticationService as O}from"../auth/authentication-service.js";import{JsonlProtocolRunner as _}from"./jsonl-protocol.js";import{createLotaGateClient as b}from"../../infrastructure/sdk/lotagate-client-factory.js";import{CliError as C}from"../../domain/errors/cli-error.js";import{CLI_ERROR_CODES as d}from"../../domain/errors/error-codes.js";import{prepareCliAgent as v}from"../agent/cli-agent-execution-service.js";import{AgentRunTracker as P}from"../agent/agent-run-tracker.js";import{loadExecutionConfig as S}from"../config/execution-config.js";import{agentLimitNotice as T,classifyAgentLimit as D}from"../agent/agent-limit.js";import{runDesktopAgentCommand as I}from"./desktop-agent-command.js";import{createContextManagement as F}from"../agent/context-compaction.js";import{formatContinuationCheckpoint as U}from"../agent/continuation-checkpoint.js";import{resolveModelContextWindow as G}from"../model/model-context-window.js";async function oe(k,t,i,h,w,y){const N=k[0]??"stdio";if(N==="desktop")return I(t,i,h,w,y);if(N!=="stdio")throw new C(d.INVALID_ARGUMENT,"Unsupported agent action.",{category:"arguments",userMessage:"Use `lotagate agent stdio` or `lotagate agent desktop`."});const s=await S(t,i);if(s.config.model===null)throw new C(d.MODEL_NOT_FOUND,"No model is selected.",{category:"model",userMessage:"Select a model before running the stdio agent."});const M=s.config.profile??O.defaultProfileName(),x=await t.auth.requireCredential(M);return(await new _(async function*(m,L){const n=m.params.model??s.config.model;t.logger.info("Protocol agent request started.",{model:n,promptLength:m.params.prompt.length});const a=new P,E=b(x.profile,x.apiKey),R=await G(t,n,M),A=o=>{a.observeModel(o),t.logger.debug("Model turn completed.",{model:o.model,stream:o.stream,contentLength:o.contentLength,toolCallCount:o.toolCallCount,toolNames:o.toolNames,finishReason:o.finishReason})},f=await v({runtime:t,projectRoot:i,executionCwd:i,model:n,client:E,profile:"protocol-safe",approvalMode:s.config.approvalMode,onModelComplete:A,contextManagement:F({client:E,model:n,...R===void 0?{}:{contextWindow:R},onSummaryFailure:o=>t.logger.warn("Context summarization failed; using fallback summary.",{reason:o instanceof Error?o.message:String(o)}),continuationContext:()=>U({snapshot:a.snapshot()})})});let g=!1,l="",r,c;try{const o=await f.buildInput(m.params.prompt);for await(const e of f.run({input:o.input,userInput:o.userInput,...o.workspaceContext===void 0?{}:{workspaceContext:o.workspaceContext},model:n,signal:L}))if(a.observe(e),e.type==="tool.called"&&t.logger.info("Agent tool called.",{toolName:e.toolName}),e.type==="tool.completed"&&t.logger.info("Agent tool completed.",{toolName:e.toolName,isError:e.isError}),e.type==="model.delta"&&e.content.length>0?(g=!0,l+=e.content,yield{type:"delta",content:e.content}):e.type==="session.completed"&&(r=e.content),e.type==="session.failed"){const p=D(e.error);if(p===void 0)throw e.error;c=T(p);break}if(c!==void 0){const e=l.length===0?c:`
1
+ import{AuthenticationService as L}from"../auth/authentication-service.js";import{JsonlProtocolRunner as O}from"./jsonl-protocol.js";import{createLotaGateClient as _}from"../../infrastructure/sdk/lotagate-client-factory.js";import{CliError as u}from"../../domain/errors/cli-error.js";import{CLI_ERROR_CODES as c}from"../../domain/errors/error-codes.js";import{prepareCliAgent as S}from"../agent/cli-agent-execution-service.js";import{AgentRunTracker as T}from"../agent/agent-run-tracker.js";import{loadExecutionConfig as b}from"../config/execution-config.js";import{agentLimitNotice as v,classifyAgentLimit as P}from"../agent/agent-limit.js";import{runDesktopAgentCommand as D}from"./desktop-agent-command.js";import{createContextManagement as I}from"../agent/context-compaction.js";import{formatContinuationCheckpoint as F}from"../agent/continuation-checkpoint.js";import{resolveModelContextWindow as U}from"../model/model-context-window.js";import{BoundedTextAccumulator as G}from"../agent/bounded-text-accumulator.js";async function to(R,t,l,C,h,w){const y=R[0]??"stdio";if(y==="desktop")return D(t,l,C,h,w);if(y!=="stdio")throw new u(c.INVALID_ARGUMENT,"Unsupported agent action.",{category:"arguments",userMessage:"Use `lotagate agent stdio` or `lotagate agent desktop`."});const i=await b(t,l);if(i.config.model===null)throw new u(c.MODEL_NOT_FOUND,"No model is selected.",{category:"model",userMessage:"Select a model before running the stdio agent."});const N=i.config.profile??L.defaultProfileName(),M=await t.auth.requireCredential(N);return(await new O(async function*(d,k){const n=d.params.model??i.config.model;t.logger.info("Protocol agent request started.",{model:n,promptLength:d.params.prompt.length});const a=new T,x=_(M.profile,M.apiKey),E=await U(t,n,N),A=e=>{a.observeModel(e),t.logger.debug("Model turn completed.",{model:e.model,stream:e.stream,contentLength:e.contentLength,toolCallCount:e.toolCallCount,toolNames:e.toolNames,finishReason:e.finishReason})},m=await S({runtime:t,projectRoot:l,executionCwd:l,model:n,client:x,profile:"protocol-safe",approvalMode:i.config.approvalMode,onModelComplete:A,contextManagement:I({client:x,model:n,...E===void 0?{}:{contextWindow:E},onSummaryFailure:e=>t.logger.warn("Context summarization failed; using fallback summary.",{reason:e instanceof Error?e.message:String(e)}),continuationContext:()=>F({snapshot:a.snapshot()})})});let f=!1;const r=new G;let s;try{const e=await m.buildInput(d.params.prompt);for await(const o of m.run({input:e.input,userInput:e.userInput,...e.workspaceContext===void 0?{}:{workspaceContext:e.workspaceContext},model:n,signal:k}))if(a.observe(o),o.type==="tool.called"&&t.logger.info("Agent tool called.",{toolName:o.toolName}),o.type==="tool.completed"&&t.logger.info("Agent tool completed.",{toolName:o.toolName,isError:o.isError}),o.type==="model.delta"&&o.content.length>0?(f=!0,r.append(o.content),yield{type:"delta",content:o.content}):o.type==="session.completed"&&r.replace(o.content),o.type==="session.failed"){const p=P(o.error);if(p===void 0)throw o.error;s=v(p);break}if(s!==void 0){const o=r.isEmpty?s:`
2
2
 
3
- ${c}`;g=!0,l+=e,r=l,yield{type:"delta",content:e}}const u=r===void 0||r.trim().length===0?l:r;if(u.trim().length===0){const e=a.snapshot();e.toolCalls>0&&a.markStuck();const p=e.toolCalls>0?d.MODEL_EMPTY_FINAL:d.MODEL_PROTOCOL_EMPTY_RESPONSE;throw new C(p,"Agent completed without a final text response.",{category:"model",userMessage:"The model completed without a final text response.",retryable:!0,details:{toolCalls:e.toolCalls,toolCompletions:e.toolCompletions,toolNames:e.toolNames,finishReason:e.lastFinishReason}})}g||(yield{type:"delta",content:u}),yield{type:"completed",content:u},t.logger.info("Protocol agent request completed.",{model:n})}finally{await f.close()}}).run(h,w,y)).errorCount===0?0:1}export{oe as runAgentCommand};
3
+ ${s}`;f=!0,r.append(o),yield{type:"delta",content:o}}const g=r.toString();if(g.trim().length===0){const o=a.snapshot();o.toolCalls>0&&a.markStuck();const p=o.toolCalls>0?c.MODEL_EMPTY_FINAL:c.MODEL_PROTOCOL_EMPTY_RESPONSE;throw new u(p,"Agent completed without a final text response.",{category:"model",userMessage:"The model completed without a final text response.",retryable:!0,details:{toolCalls:o.toolCalls,toolCompletions:o.toolCompletions,toolNames:o.toolNames,finishReason:o.lastFinishReason}})}f||(yield{type:"delta",content:g}),yield{type:"completed",content:g},t.logger.info("Protocol agent request completed.",{model:n})}finally{await m.close()}}).run(C,h,w)).errorCount===0?0:1}export{to as runAgentCommand};
@@ -8,7 +8,7 @@ export declare class WorkspaceFileIndex {
8
8
  private readonly root;
9
9
  private readonly ignoredDirectories;
10
10
  constructor(root: string, ignoredDirectories?: ReadonlySet<string>);
11
- list(): Promise<readonly string[]>;
12
- listEntries(): Promise<readonly WorkspaceEntry[]>;
11
+ list(signal?: AbortSignal): Promise<readonly string[]>;
12
+ listEntries(signal?: AbortSignal): Promise<readonly WorkspaceEntry[]>;
13
13
  private visit;
14
14
  }
@@ -1 +1 @@
1
- import a from"node:fs/promises";import e from"node:path";import{DEFAULT_IGNORED_WORKSPACE_DIRECTORIES as h,MAX_INDEXED_ENTRIES as n}from"./workspace-constants.js";class u{root;ignoredDirectories;constructor(i,t=h){this.root=i,this.ignoredDirectories=t}async list(){return(await this.listEntries()).filter(i=>i.kind==="file").map(i=>i.path)}async listEntries(){const i=[];return await this.visit(this.root,i),i.sort((t,s)=>t.path.localeCompare(s.path))}async visit(i,t){if(t.length>=n)return;let s;try{s=await a.readdir(i,{withFileTypes:!0})}catch{return}for(const r of s){if(t.length>=n)return;const o=e.join(i,r.name);if(!r.isSymbolicLink())if(r.isDirectory()){if(this.ignoredDirectories.has(r.name))continue;t.push({path:e.relative(this.root,o).split(e.sep).join("/"),kind:"directory"}),await this.visit(o,t)}else r.isFile()&&t.push({path:e.relative(this.root,o).split(e.sep).join("/"),kind:"file"})}}}export{u as WorkspaceFileIndex};
1
+ import h from"node:fs/promises";import e from"node:path";import{DEFAULT_IGNORED_WORKSPACE_DIRECTORIES as c,MAX_INDEXED_ENTRIES as n}from"./workspace-constants.js";class u{root;ignoredDirectories;constructor(i,t=c){this.root=i,this.ignoredDirectories=t}async list(i){return(await this.listEntries(i)).filter(t=>t.kind==="file").map(t=>t.path)}async listEntries(i){const t=[];return await this.visit(this.root,t,i),t.sort((o,s)=>o.path.localeCompare(s.path))}async visit(i,t,o){if(o?.throwIfAborted(),t.length>=n)return;let s;try{s=await h.readdir(i,{withFileTypes:!0})}catch(r){if(o?.aborted===!0)throw r;return}for(const r of s){if(o?.throwIfAborted(),t.length>=n)return;const a=e.join(i,r.name);if(!r.isSymbolicLink())if(r.isDirectory()){if(this.ignoredDirectories.has(r.name))continue;t.push({path:e.relative(this.root,a).split(e.sep).join("/"),kind:"directory"}),await this.visit(a,t,o)}else r.isFile()&&t.push({path:e.relative(this.root,a).split(e.sep).join("/"),kind:"file"})}}}export{u as WorkspaceFileIndex};
@@ -9,6 +9,7 @@ export interface StoredCredential {
9
9
  profile: AuthProfile;
10
10
  apiKey: string;
11
11
  }
12
+ export declare const AUTH_API_KEY_MAX_BYTES = 4096;
12
13
  export declare function validateProfileName(name: string): string;
13
14
  export declare function validateBaseUrl(value: string): string;
14
15
  export declare function validateApiKey(apiKey: string): string;
@@ -1 +1 @@
1
- import{CliError as t}from"../errors/cli-error.js";import{CLI_ERROR_CODES as a}from"../errors/error-codes.js";const s=/^[a-z][a-z0-9_-]{0,31}$/;function l(r){const e=r.trim().toLowerCase();if(!s.test(e))throw new t(a.AUTH_PROFILE_NOT_FOUND,`Invalid profile name: ${r}`,{category:"auth",userMessage:"Profile names may contain lowercase letters, digits, hyphens, or underscores; maximum 32 characters."});return e}function c(r){let e;try{e=new URL(r)}catch(o){throw new t(a.AUTH_BASE_URL_INVALID,"Base URL is invalid.",{category:"auth",userMessage:"Invalid base URL.",cause:o})}if(e.protocol!=="http:"&&e.protocol!=="https:")throw new t(a.AUTH_BASE_URL_INVALID,"Base URL must use HTTP or HTTPS.",{category:"auth",userMessage:"Base URL must use HTTP or HTTPS."});return r.replace(/\/+$/,"")}function u(r){const e=r.trim();if(e.length<8||e.length>4096||/[\r\n]/.test(e))throw new t(a.AUTH_API_KEY_INVALID,"API key has an invalid shape.",{category:"auth",userMessage:"API key is invalid or contains a newline."});return e}export{u as validateApiKey,c as validateBaseUrl,l as validateProfileName};
1
+ import{CliError as r}from"../errors/cli-error.js";import{CLI_ERROR_CODES as a}from"../errors/error-codes.js";const s=/^[a-z][a-z0-9_-]{0,31}$/,n=4096;function l(t){const e=t.trim().toLowerCase();if(!s.test(e))throw new r(a.AUTH_PROFILE_NOT_FOUND,`Invalid profile name: ${t}`,{category:"auth",userMessage:"Profile names may contain lowercase letters, digits, hyphens, or underscores; maximum 32 characters."});return e}function u(t){let e;try{e=new URL(t)}catch(o){throw new r(a.AUTH_BASE_URL_INVALID,"Base URL is invalid.",{category:"auth",userMessage:"Invalid base URL.",cause:o})}if(e.protocol!=="http:"&&e.protocol!=="https:")throw new r(a.AUTH_BASE_URL_INVALID,"Base URL must use HTTP or HTTPS.",{category:"auth",userMessage:"Base URL must use HTTP or HTTPS."});return t.replace(/\/+$/,"")}function _(t){const e=t.trim();if(e.length<8||Buffer.byteLength(e,"utf8")>n||/[\r\n]/.test(e))throw new r(a.AUTH_API_KEY_INVALID,"API key has an invalid shape.",{category:"auth",userMessage:"API key is invalid or contains a newline."});return e}export{n as AUTH_API_KEY_MAX_BYTES,_ as validateApiKey,u as validateBaseUrl,l as validateProfileName};
@@ -8,11 +8,16 @@ export declare const RESOURCE_LIMITS: {
8
8
  readonly mcpConfigBytes: number;
9
9
  readonly sessionBundleBytes: number;
10
10
  readonly sessionJournalBytes: number;
11
+ readonly sessionJournalCacheMaxEntries: 8;
12
+ readonly sessionJournalCacheMaxBytes: number;
11
13
  readonly sessionEvents: 100000;
12
14
  readonly sessionTranscriptEntries: 50000;
13
15
  readonly sessionAttachments: 32;
14
16
  readonly sessionAttachmentTotalBytes: number;
15
17
  readonly sessionTextBytes: number;
18
+ readonly agentStreamBytes: number;
19
+ readonly agentToolArgumentBytes: number;
20
+ readonly agentToolMetadataBytes: number;
16
21
  readonly memoryJournalBytes: number;
17
22
  readonly memoryIndexBytes: number;
18
23
  readonly memoryBundleBytes: number;
@@ -43,6 +48,10 @@ export declare const RESOURCE_LIMITS: {
43
48
  readonly instructionFiles: 32;
44
49
  readonly skillDocumentBytes: number;
45
50
  readonly pluginManifestBytes: number;
51
+ readonly pluginMaxEnabled: 64;
52
+ readonly pluginMaxAgentFiles: 64;
53
+ readonly pluginMaxAgentContributions: 256;
54
+ readonly pluginDiscoveryConcurrency: 4;
46
55
  readonly subagentTaskBytes: number;
47
56
  readonly subagentContextBytes: number;
48
57
  readonly subagentMaxWriteScopeItems: 32;
@@ -1 +1 @@
1
- const e=1e6,t={processTerminationGraceMs:500,processTerminationTimeoutMs:2e3,configFileBytes:262144,sessionIndexBytes:8388608,workspaceSettingsBytes:262144,mcpConfigBytes:524288,sessionBundleBytes:20971520,sessionJournalBytes:20971520,sessionEvents:1e5,sessionTranscriptEntries:5e4,sessionAttachments:32,sessionAttachmentTotalBytes:5e7,sessionTextBytes:524288,memoryJournalBytes:8388608,memoryIndexBytes:8388608,memoryBundleBytes:8388608,memoryRecords:2e3,memoryStatementBytes:4096,memoryRationaleBytes:2048,memoryTopics:16,memoryTopicBytes:256,memorySupersedes:16,memoryEvidenceReferences:32,memoryEvidenceReferenceBytes:512,memoryContextRecords:12,jsonlLineBytes:1048576,desktopJsonlLineBytes:4194304,desktopAttachmentChunkBytes:524288,jsonlIdBytes:256,jsonlPathBytes:4096,jsonlPromptBytes:524288,pendingAttachments:16,pendingAttachmentTotalBytes:5e7,desktopAttachmentTtlMs:9e5,maxAttachmentBytes:1e7,clipboardTextBytes:1e7,clipboardTimeoutMs:15e3,instructionFileBytes:131072,instructionTotalBytes:262144,instructionFiles:32,skillDocumentBytes:524288,pluginManifestBytes:262144,subagentTaskBytes:16384,subagentContextBytes:16384,subagentMaxWriteScopeItems:32,subagentWriteScopeItemBytes:512,subagentSummaryBytes:24576,subagentMaxDurationMs:18e5,subagentMaxTokens:2e5,subagentMaxConcurrent:4,subagentMaxRetainedJobs:32,desktopSessionStoreMaxEntries:20,subagentMaxIterations:200,subagentMaxToolCallsPerTurn:100,agentMaxIterations:500,agentMaxToolCallsPerTurn:1,intentMaxCompletionAttempts:2,subagentToolArgumentBytes:65536,subagentToolResultBytes:262144,goalObjectiveBytes:16384,goalReasonBytes:2048,goalEvidenceItems:8,goalEvidenceBytes:4096,goalDefaultMaxTurns:100,goalMaximumMaxTurns:1e4,goalMaximumTokenBudget:1e7,goalDefaultMaxDurationMs:72e5,goalMaximumMaxDurationMs:288e5,goalDefaultMaxEvaluatorRuns:100,tuiTranscriptMessages:2e3,tuiAgentSessionMaxEntries:4,tuiAgentSessionMaxBytes:8388608,agentPreparationCacheMaxEntries:8,agentPreparationCacheMaxBytes:16777216,contextTokenCacheMaxEntries:8,contextTokenCacheMaxBytes:8388608,jsonlMaxInFlight:4,protocolWriteQueueEntries:256,protocolWriteQueueBytes:16777216,workspaceReferenceCount:32,workspaceReferenceInstructionBytes:16384,logQueueEntries:1e4,logQueueBytes:4194304,internalProcessTimeoutMs:12e4,internalProcessOutputBytes:4194304,hostShellTimeoutMs:12e4,guestShellTimeoutMs:6e5,defaultShellTimeoutMs:3e4};export{t as RESOURCE_LIMITS};
1
+ const e=1e6,t={processTerminationGraceMs:500,processTerminationTimeoutMs:2e3,configFileBytes:262144,sessionIndexBytes:8388608,workspaceSettingsBytes:262144,mcpConfigBytes:524288,sessionBundleBytes:20971520,sessionJournalBytes:20971520,sessionJournalCacheMaxEntries:8,sessionJournalCacheMaxBytes:8e6,sessionEvents:1e5,sessionTranscriptEntries:5e4,sessionAttachments:32,sessionAttachmentTotalBytes:5e7,sessionTextBytes:524288,agentStreamBytes:4194304,agentToolArgumentBytes:262144,agentToolMetadataBytes:262144,memoryJournalBytes:8388608,memoryIndexBytes:8388608,memoryBundleBytes:8388608,memoryRecords:2e3,memoryStatementBytes:4096,memoryRationaleBytes:2048,memoryTopics:16,memoryTopicBytes:256,memorySupersedes:16,memoryEvidenceReferences:32,memoryEvidenceReferenceBytes:512,memoryContextRecords:12,jsonlLineBytes:1048576,desktopJsonlLineBytes:4194304,desktopAttachmentChunkBytes:524288,jsonlIdBytes:256,jsonlPathBytes:4096,jsonlPromptBytes:524288,pendingAttachments:16,pendingAttachmentTotalBytes:5e7,desktopAttachmentTtlMs:9e5,maxAttachmentBytes:1e7,clipboardTextBytes:1e7,clipboardTimeoutMs:15e3,instructionFileBytes:131072,instructionTotalBytes:262144,instructionFiles:32,skillDocumentBytes:524288,pluginManifestBytes:262144,pluginMaxEnabled:64,pluginMaxAgentFiles:64,pluginMaxAgentContributions:256,pluginDiscoveryConcurrency:4,subagentTaskBytes:16384,subagentContextBytes:16384,subagentMaxWriteScopeItems:32,subagentWriteScopeItemBytes:512,subagentSummaryBytes:24576,subagentMaxDurationMs:18e5,subagentMaxTokens:2e5,subagentMaxConcurrent:4,subagentMaxRetainedJobs:32,desktopSessionStoreMaxEntries:20,subagentMaxIterations:200,subagentMaxToolCallsPerTurn:100,agentMaxIterations:500,agentMaxToolCallsPerTurn:1,intentMaxCompletionAttempts:2,subagentToolArgumentBytes:65536,subagentToolResultBytes:262144,goalObjectiveBytes:16384,goalReasonBytes:2048,goalEvidenceItems:8,goalEvidenceBytes:4096,goalDefaultMaxTurns:100,goalMaximumMaxTurns:1e4,goalMaximumTokenBudget:1e7,goalDefaultMaxDurationMs:72e5,goalMaximumMaxDurationMs:288e5,goalDefaultMaxEvaluatorRuns:100,tuiTranscriptMessages:2e3,tuiAgentSessionMaxEntries:4,tuiAgentSessionMaxBytes:8388608,agentPreparationCacheMaxEntries:8,agentPreparationCacheMaxBytes:16777216,contextTokenCacheMaxEntries:8,contextTokenCacheMaxBytes:8388608,jsonlMaxInFlight:4,protocolWriteQueueEntries:256,protocolWriteQueueBytes:16777216,workspaceReferenceCount:32,workspaceReferenceInstructionBytes:16384,logQueueEntries:1e4,logQueueBytes:4194304,internalProcessTimeoutMs:12e4,internalProcessOutputBytes:4194304,hostShellTimeoutMs:12e4,guestShellTimeoutMs:6e5,defaultShellTimeoutMs:3e4};export{t as RESOURCE_LIMITS};
@@ -1 +1 @@
1
- import"@lotagate/sdk";import{AssistantDeltaNormalizer as q}from"../../domain/runtime/assistant-delta-normalizer.js";import{createModelToolNameMap as nn,mapChunkToolNames as tn,mapRequestToolNames as U,mapResponseToolNames as N}from"./model-tool-name-mapper.js";import{isRetryableModelResponseError as en,isRetryableToolResultFollowUpError as on,MODEL_RESPONSE_MAX_RETRIES as rn,retryModelStream as sn}from"../../application/model/model-response-retry.js";import{mapUsage as S,textLength as k}from"./model-response-metrics.js";const W=rn;function En(n,t={}){return{chat:{completions:{create:((e,i)=>{const r=nn(e.tools),a=e.tools!==void 0&&e.tools.length>0?1:void 0;if(e.stream===!0){const l=U(v({...e,stream:!0}),r),f=fn(e)?cn(n,l,i,a):sn({create:async()=>await n.chat.completions.create(l,i),isObservable:dn,...i?.signal===void 0?{}:{signal:i.signal}});return ln(n,l,e.tools,i,f,e.model,t,r,a)}const s=U(v({...e,stream:!1}),r);return an(n.chat.completions.create(s,i),e.model,t,r,a)})}}}}async function an(n,t,o,e,i){const r=O(N(b(await n),e),i),a=r.choices[0]?.message;return o.onComplete?.({model:t,stream:!1,contentLength:k(a?.content),toolCallCount:a?.tool_calls?.length??0,toolNames:(a?.tool_calls??[]).map(s=>s.function.name),...r.usage===void 0?{}:{usage:S(r.usage)},...r.choices[0]?.finish_reason===void 0?{}:{finishReason:r.choices[0].finish_reason}}),r}async function*ln(n,t,o,e,i,r,a,s,l){let f=0,c;const d=new Set,h=new Set,_=new mn(o);let z=!1,w,x,R=!1;const Z=new q;try{for await(const C of i){const g=tn(hn(C,Z),s),p=_.consume(g).chunk,y=p.choices[0];p.usage!==void 0&&(w=S(p.usage));const I=k(y?.delta.content);if(f+=I,c=y?.finish_reason??c,I>0&&(z=!0),y?.finish_reason==="tool_calls"&&!R){const J=_.flush(p);if(!z&&D(J)){const M=N(b(await T(n,t,e,l)),s);M.usage!==void 0&&(w=S(M.usage)),G(M,h,d),c=M.choices[0]?.finish_reason??c,yield E(M);return}const L=P(J,l);K(L,h,d),R=!0,yield p,yield L}else yield p;x=p}if(!R&&x!==void 0&&_.hasCalls()){const C=_.flush(x);if(!z&&D(C)){const m=N(b(await T(n,t,e,l)),s);m.usage!==void 0&&(w=S(m.usage)),G(m,h,d),c=m.choices[0]?.finish_reason??c,yield E(m);return}const g=P(C,l);K(g,h,d),R=!0,yield g}}catch(C){if(!z&&!R&&Y(C)&&e?.signal?.aborted!==!0){const g=N(b(await T(n,t,e,l)),s);g.usage!==void 0&&(w=S(g.usage));const m=g.choices[0]?.message,p=k(m?.content);f+=p;for(const y of m?.tool_calls??[])y.id.length>0&&h.add(y.id),y.function.name.length>0&&d.add(y.function.name);c=g.choices[0]?.finish_reason??c,yield E(g);return}throw C}finally{a.onComplete?.({model:r,stream:!0,contentLength:f,toolCallCount:h.size||d.size,toolNames:[...d],...c===void 0?{}:{finishReason:c},...w===void 0?{}:{usage:w}})}}async function T(n,t,o,e,i=!1){const r=v({...t,stream:!1});for(let a=0;;a+=1)try{const s=b(await n.chat.completions.create(r,o));if(a<W&&D(s))continue;return O(s,e)}catch(s){const l=i?on(s):Y(s);if(a>=W||!l||o?.signal?.aborted===!0)throw s}}async function*cn(n,t,o,e){yield E(await T(n,t,o,e,!0))}function v(n){return n.tools===void 0||n.tools.length===0||un(n)?n:{...n,tool_choice:"auto"}}function un(n){return n.tool_choice!==void 0||n.toolChoice!==void 0||n.toolConfig!==void 0}function fn(n){const t=n.messages.at(-1);return t!==void 0&&"role"in t&&t.role==="tool"}function dn(n){return n.choices.some(t=>{const o=t.delta.content,e=t.delta.tool_calls,i=t.delta;return typeof o=="string"&&o.length>0||Array.isArray(o)&&o.length>0||Array.isArray(e)&&e.length>0||typeof i.reasoning_content=="string"&&i.reasoning_content.length>0||u(i.function_call)})}function O(n,t){return t===void 0?n:{...n,choices:n.choices.map(o=>{const e=o.message.tool_calls;return e===void 0||e.length<=t?o:{...o,message:{...o.message,tool_calls:e.slice(0,t)}}})}}function P(n,t){return t===void 0?n:{...n,choices:n.choices.map(o=>{const e=o.delta.tool_calls;return e===void 0||e.length<=t?o:{...o,delta:{...o.delta,tool_calls:e.slice(0,t)}}})}}function E(n){const t=b(n),o=t.choices[0],e=o?.message,i=gn(e);return{id:t.id,object:"chat.completion.chunk",created:t.created,model:t.model,choices:[{index:o?.index??0,delta:{role:"assistant",content:j(e?.content),...i===void 0?{}:{reasoning_content:i},...e?.tool_calls===void 0?{}:{tool_calls:e.tool_calls.map((r,a)=>({...r,index:a}))}},...o?.finish_reason===void 0?{}:{finish_reason:o.finish_reason}}],...t.usage===void 0?{}:{usage:t.usage}}}function gn(n){if(!u(n))return;const t=n.reasoning_content;return typeof t=="string"&&t.length>0?t:void 0}function hn(n,t){return{...n,choices:n.choices.map(o=>{const e=o.delta.content,r=Array.isArray(e)?t.normalize(j(e)):typeof e=="string"?t.normalize(e):e,a=o.delta,s=[...Array.isArray(e)?H(e):[],...Q(a.function_call,0)];return r===e&&s.length===0?o:{...o,delta:{...o.delta,...r===e?{}:{content:r},...s.length===0?{}:{tool_calls:[...o.delta.tool_calls??[],...s]}}}})}}class mn{knownNames;calls=new Map;order=[];keysByPosition=new Map;constructor(t){this.knownNames=new Set((t??[]).flatMap(o=>bn(o)))}consume(t){let o=!1;const e=t.choices.map(i=>{const r=i.delta.tool_calls;if(r===void 0||r.length===0)return i;o=!0,r.forEach((s,l)=>{u(s)&&this.append(s,l)});const a={...i.delta};return delete a.tool_calls,{...i,delta:a}});return{chunk:{...t,choices:e},hadToolCalls:o}}hasCalls(){return this.order.length>0}flush(t){const o=this.order.map((a,s)=>{const l=this.calls.get(a);return{id:l.id||`tool-call-${s}`,type:l.type,function:{name:l.name,arguments:l.arguments},...l.providerMetadata===void 0?{}:{provider_metadata:l.providerMetadata}}}),e=new Set,i=o.filter(a=>{const s=`${a.function.name}\0${a.function.arguments}`;return e.has(s)?!1:(e.add(s),!0)}),r=t.choices.map((a,s)=>s===0?{...a,delta:{...a.delta,tool_calls:i}}:a);return{...t,choices:r}}append(t,o){const e=F(t),i=_n(t),r=B(t),a=X(t),s=A(t),l=pn(t,o),f=this.keysByPosition.get(l)??yn(e,l);this.keysByPosition.set(l,f);const c=this.calls.get(f),d=Cn(c?.name??"",r,this.knownNames),h=An(c?.arguments??"",a);if(c===void 0){this.calls.set(f,{id:e,type:i,name:d,arguments:h,...s===void 0?{}:{providerMetadata:s}}),this.order.push(f);return}const _=c.providerMetadata??s;this.calls.set(f,{id:c.id.length===0&&e.length>0?e:c.id,type:i||c.type,name:c.name+d,arguments:h,..._===void 0?{}:{providerMetadata:_}})}}function A(n){if(!u(n))return;const t=n.provider_metadata??n.providerMetadata;if(u(t))return t;const o=n.thoughtSignature??n.thought_signature;return typeof o=="string"&&o.length>0?{google:{thought_signature:o}}:void 0}function pn(n,t){const o=n.index;return typeof o=="number"&&Number.isInteger(o)?o:t}function yn(n,t){return n.length>0?`id:${n}`:`index:${t}`}function F(n){const t=n;return u(t)&&typeof t.id=="string"?t.id:""}function _n(n){const t=n;return u(t)&&typeof t.type=="string"&&t.type.length>0?t.type:"function"}function B(n){const t=n;return u(t)&&u(t.function)&&typeof t.function.name=="string"?t.function.name:""}function X(n){const t=n;return u(t)&&u(t.function)&&typeof t.function.arguments=="string"?t.function.arguments:""}function Cn(n,t,o){return t.length===0||t===n?"":o!==void 0&&o.has(t)&&t.startsWith(n)||t.startsWith(n)&&n.length>0?t.slice(n.length):t}function An(n,t){if(t.length===0)return n;if(n.length===0||t===n)return n.length===0?t:n;if(t.startsWith(n))return t;const o=wn(n,t);return o!==void 0?o:n+t}function D(n){const t=n.choices[0];return((t!==void 0&&"message"in t?t.message.tool_calls:t?.delta.tool_calls)??[]).some(e=>$(X(e))===void 0)}function wn(n,t){if(n.length===0||t.length===0)return;const o=$(n),e=$(t);if(!(o===void 0||e===void 0))return JSON.stringify({...o,...e})}function $(n){try{const t=JSON.parse(n);return u(t)?t:void 0}catch{return}}function K(n,t,o){for(const e of n.choices[0]?.delta.tool_calls??[]){const i=F(e),r=B(e);i.length>0&&t.add(i),r.length>0&&o.add(r)}}function G(n,t,o){for(const e of n.choices[0]?.message?.tool_calls??[]){const i=F(e),r=B(e);i.length>0&&t.add(i),r.length>0&&o.add(r)}}function bn(n){return"function"in n&&typeof n.function.name=="string"?[n.function.name]:"name"in n&&typeof n.name=="string"?[n.name]:"functionDeclarations"in n?n.functionDeclarations.flatMap(t=>typeof t.name=="string"?[t.name]:[]):[]}function b(n){return{...n,choices:n.choices.map(t=>({...t,message:Rn(t.message)}))}}function Rn(n){const t=n,o=[...Array.isArray(n.content)?H(n.content):[],...Q(t.function_call,0)],e=Array.isArray(n.content)?j(n.content):typeof n.content=="string"?n.content:void 0,i=e??null,r=Array.isArray(n.content)||typeof n.content=="string"&&e!==n.content;return!r&&o.length===0?n:{...n,...r?{content:i}:{},...o.length===0?{}:{tool_calls:[...n.tool_calls??[],...o]}}}function H(n){const t=[];for(const[o,e]of n.entries())if(!(typeof e!="object"||e===null)){if("functionCall"in e&&u(e.functionCall)&&typeof e.functionCall.name=="string"){const i=e.functionCall.args;t.push({id:typeof e.functionCall.id=="string"&&e.functionCall.id.length>0?e.functionCall.id:`provider-call-${o}`,type:"function",function:{name:e.functionCall.name,arguments:V(i)},...A(e)===void 0?{}:{provider_metadata:A(e)}});continue}if("tool_use"in e&&u(e.tool_use)&&typeof e.tool_use.name=="string"){const i=e.tool_use.input;t.push({id:typeof e.tool_use.id=="string"&&e.tool_use.id.length>0?e.tool_use.id:`provider-call-${o}`,type:"function",function:{name:e.tool_use.name,arguments:JSON.stringify(i??{})},...A(e)===void 0?{}:{provider_metadata:A(e)}})}}return t}function Q(n,t){if(!u(n)||typeof n.name!="string")return[];const o=n.arguments;return[{id:typeof n.id=="string"&&n.id.length>0?n.id:`provider-call-${t}`,type:"function",function:{name:n.name,arguments:V(o)},...A(n)===void 0?{}:{provider_metadata:A(n)}}]}function V(n){if(n===void 0)return"";if(typeof n=="string")return n;try{return JSON.stringify(n)}catch{return""}}function u(n){return typeof n=="object"&&n!==null&&!Array.isArray(n)}function Y(n){return en(n)}function j(n){return typeof n=="string"?n:Array.isArray(n)?n.reduce((t,o)=>{if(typeof o!="object"||o===null||!("text"in o))return t;const e=o.text;return t+(typeof e=="string"?e:"")},""):""}export{En as createToolCallingModelClient};
1
+ import"@lotagate/sdk";import{AssistantDeltaNormalizer as st}from"../../domain/runtime/assistant-delta-normalizer.js";import{createModelToolNameMap as rt,mapChunkToolNames as it,mapRequestToolNames as K,mapResponseToolNames as L}from"./model-tool-name-mapper.js";import{isRetryableModelResponseError as at,isRetryableToolResultFollowUpError as lt,MODEL_RESPONSE_MAX_RETRIES as ct,retryModelStream as ft}from"../../application/model/model-response-retry.js";import{mapUsage as N,textLength as F}from"./model-response-metrics.js";import{assertTextWithinLimit as ut,modelResponseLimitError as w}from"../../application/agent/bounded-text-accumulator.js";import{RESOURCE_LIMITS as u}from"../../domain/runtime/resource-limits.js";const G=ct;function Ft(t,n={}){return{chat:{completions:{create:((e,s)=>{const r=rt(e.tools),a=e.tools!==void 0&&e.tools.length>0?1:void 0;if(e.stream===!0){const l=K(D({...e,stream:!0}),r),g=yt(e)?ht(t,l,s,a):ft({create:async()=>await t.chat.completions.create(l,s),isObservable:pt,...s?.signal===void 0?{}:{signal:s.signal}});return gt(t,l,e.tools,s,g,e.model,n,r,a)}const i=K(D({...e,stream:!1}),r);return dt(t.chat.completions.create(i,s),e.model,n,r,a)})}}}}async function dt(t,n,o,e,s){const r=H(L(S(await t),e),s);U(r);const a=r.choices[0]?.message;return o.onComplete?.({model:n,stream:!1,contentLength:F(a?.content),toolCallCount:a?.tool_calls?.length??0,toolNames:(a?.tool_calls??[]).map(i=>i.function.name),...r.usage===void 0?{}:{usage:N(r.usage)},...r.choices[0]?.finish_reason===void 0?{}:{finishReason:r.choices[0].finish_reason}}),r}async function*gt(t,n,o,e,s,r,a,i,l){let g=0,h=0,M=0,f;const m=new Set,B=new Set,E=new wt(o);let x=!1,A,J,R=!1;const ot=new st;try{for await(const C of s){const d=it(Ct(C,ot),i);if(h+=Buffer.byteLength(JSON.stringify(d),"utf8"),h>u.agentStreamBytes)throw w("stream",u.agentStreamBytes);const p=E.consume(d).chunk,_=p.choices[0];p.usage!==void 0&&(A=N(p.usage));const W=F(_?.delta.content);if(M+=Buffer.byteLength(z(_?.delta.content),"utf8"),M>u.sessionTextBytes)throw w("text",u.sessionTextBytes);if(g+=W,f=_?.finish_reason??f,W>0&&(x=!0),_?.finish_reason==="tool_calls"&&!R){const P=E.flush(p);if(!x&&j(P)){const T=L(S(await v(t,n,e,l)),i);T.usage!==void 0&&(A=N(T.usage)),Z(T,B,m),f=T.choices[0]?.finish_reason??f,yield k(T);return}const X=Q(P,l);Y(X,B,m),R=!0,yield p,yield X}else yield p;J=p}if(!R&&J!==void 0&&E.hasCalls()){const C=E.flush(J);if(!x&&j(C)){const y=L(S(await v(t,n,e,l)),i);y.usage!==void 0&&(A=N(y.usage)),Z(y,B,m),f=y.choices[0]?.finish_reason??f,yield k(y);return}const d=Q(C,l);Y(d,B,m),R=!0,yield d}}catch(C){if(!x&&!R&&et(C)&&e?.signal?.aborted!==!0){const d=L(S(await v(t,n,e,l)),i);d.usage!==void 0&&(A=N(d.usage));const y=d.choices[0]?.message,p=F(y?.content);U(d),g+=p;for(const _ of y?.tool_calls??[])_.id.length>0&&B.add(_.id),_.function.name.length>0&&m.add(_.function.name);f=d.choices[0]?.finish_reason??f,yield k(d);return}throw C}finally{a.onComplete?.({model:r,stream:!0,contentLength:g,toolCallCount:B.size||m.size,toolNames:[...m],...f===void 0?{}:{finishReason:f},...A===void 0?{}:{usage:A}})}}async function v(t,n,o,e,s=!1){const r=D({...n,stream:!1});for(let a=0;;a+=1)try{const i=S(await t.chat.completions.create(r,o));if(a<G&&j(i))continue;const l=H(i,e);return U(l),l}catch(i){const l=s?lt(i):et(i);if(a>=G||!l||o?.signal?.aborted===!0)throw i}}async function*ht(t,n,o,e){yield k(await v(t,n,o,e,!0))}function D(t){return t.tools===void 0||t.tools.length===0||mt(t)?t:{...t,tool_choice:"auto"}}function mt(t){return t.tool_choice!==void 0||t.toolChoice!==void 0||t.toolConfig!==void 0}function yt(t){const n=t.messages.at(-1);return n!==void 0&&"role"in n&&n.role==="tool"}function pt(t){return t.choices.some(n=>{const o=n.delta.content,e=n.delta.tool_calls,s=n.delta;return typeof o=="string"&&o.length>0||Array.isArray(o)&&o.length>0||Array.isArray(e)&&e.length>0||typeof s.reasoning_content=="string"&&s.reasoning_content.length>0||c(s.function_call)})}function H(t,n){return n===void 0?t:{...t,choices:t.choices.map(o=>{const e=o.message.tool_calls;return e===void 0||e.length<=n?o:{...o,message:{...o.message,tool_calls:e.slice(0,n)}}})}}function Q(t,n){return n===void 0?t:{...t,choices:t.choices.map(o=>{const e=o.delta.tool_calls;return e===void 0||e.length<=n?o:{...o,delta:{...o.delta,tool_calls:e.slice(0,n)}}})}}function k(t){const n=S(t),o=n.choices[0],e=o?.message,s=_t(e);return{id:n.id,object:"chat.completion.chunk",created:n.created,model:n.model,choices:[{index:o?.index??0,delta:{role:"assistant",content:z(e?.content),...s===void 0?{}:{reasoning_content:s},...e?.tool_calls===void 0?{}:{tool_calls:e.tool_calls.map((r,a)=>({...r,index:a}))}},...o?.finish_reason===void 0?{}:{finish_reason:o.finish_reason}}],...n.usage===void 0?{}:{usage:n.usage}}}function _t(t){if(!c(t))return;const n=t.reasoning_content;return typeof n=="string"&&n.length>0?n:void 0}function Ct(t,n){return{...t,choices:t.choices.map(o=>{const e=o.delta.content,r=Array.isArray(e)?n.normalize(z(e)):typeof e=="string"?n.normalize(e):e,a=o.delta,i=[...Array.isArray(e)?q(e):[],...tt(a.function_call,0)];return r===e&&i.length===0?o:{...o,delta:{...o.delta,...r===e?{}:{content:r},...i.length===0?{}:{tool_calls:[...o.delta.tool_calls??[],...i]}}}})}}class wt{knownNames;calls=new Map;order=[];keysByPosition=new Map;constructor(n){this.knownNames=new Set((n??[]).flatMap(o=>Tt(o)))}consume(n){let o=!1;const e=n.choices.map(s=>{const r=s.delta.tool_calls;if(r===void 0||r.length===0)return s;o=!0,r.forEach((i,l)=>{c(i)&&this.append(i,l)});const a={...s.delta};return delete a.tool_calls,{...s,delta:a}});return{chunk:{...n,choices:e},hadToolCalls:o}}hasCalls(){return this.order.length>0}flush(n){const o=this.order.map((a,i)=>{const l=this.calls.get(a);return{id:l.id||`tool-call-${i}`,type:l.type,function:{name:l.name,arguments:l.arguments},...l.providerMetadata===void 0?{}:{provider_metadata:l.providerMetadata}}}),e=new Set,s=o.filter(a=>{const i=`${a.function.name}\0${a.function.arguments}`;return e.has(i)?!1:(e.add(i),!0)}),r=n.choices.map((a,i)=>i===0?{...a,delta:{...a.delta,tool_calls:s}}:a);return{...n,choices:r}}append(n,o){const e=I(n),s=At(n),r=$(n),a=V(n),i=b(n),l=bt(n,o),g=this.keysByPosition.get(l)??Bt(e,l);this.keysByPosition.set(l,g);const h=this.calls.get(g),M=St(h?.name??"",r,this.knownNames),f=Mt(h?.arguments??"",a);if(Buffer.byteLength(f,"utf8")>u.agentToolArgumentBytes)throw w("tool arguments",u.agentToolArgumentBytes);if(i!==void 0&&Buffer.byteLength(JSON.stringify(i),"utf8")>u.agentToolMetadataBytes)throw w("tool metadata",u.agentToolMetadataBytes);if(h===void 0){this.calls.set(g,{id:e,type:s,name:M,arguments:f,...i===void 0?{}:{providerMetadata:i}}),this.order.push(g);return}const m=h.providerMetadata??i;this.calls.set(g,{id:h.id.length===0&&e.length>0?e:h.id,type:s||h.type,name:h.name+M,arguments:f,...m===void 0?{}:{providerMetadata:m}})}}function b(t){if(!c(t))return;const n=t.provider_metadata??t.providerMetadata;if(c(n))return n;const o=t.thoughtSignature??t.thought_signature;return typeof o=="string"&&o.length>0?{google:{thought_signature:o}}:void 0}function bt(t,n){const o=t.index;return typeof o=="number"&&Number.isInteger(o)?o:n}function Bt(t,n){return t.length>0?`id:${t}`:`index:${n}`}function I(t){const n=t;return c(n)&&typeof n.id=="string"?n.id:""}function At(t){const n=t;return c(n)&&typeof n.type=="string"&&n.type.length>0?n.type:"function"}function $(t){const n=t;return c(n)&&c(n.function)&&typeof n.function.name=="string"?n.function.name:""}function V(t){const n=t;return c(n)&&c(n.function)&&typeof n.function.arguments=="string"?n.function.arguments:""}function St(t,n,o){return n.length===0||n===t?"":o!==void 0&&o.has(n)&&n.startsWith(t)||n.startsWith(t)&&t.length>0?n.slice(t.length):n}function Mt(t,n){if(n.length===0)return t;if(t.length===0||n===t)return t.length===0?n:t;if(n.startsWith(t))return n;const o=Rt(t,n);return o!==void 0?o:t+n}function j(t){const n=t.choices[0];return((n!==void 0&&"message"in n?n.message.tool_calls:n?.delta.tool_calls)??[]).some(e=>O(V(e))===void 0)}function Rt(t,n){if(t.length===0||n.length===0)return;const o=O(t),e=O(n);if(!(o===void 0||e===void 0))return JSON.stringify({...o,...e})}function O(t){try{const n=JSON.parse(t);return c(n)?n:void 0}catch{return}}function U(t){if(Buffer.byteLength(JSON.stringify(t),"utf8")>u.agentStreamBytes)throw w("response",u.agentStreamBytes);for(const o of t.choices){ut(z(o.message.content));for(const e of o.message.tool_calls??[]){if(Buffer.byteLength(e.function.arguments,"utf8")>u.agentToolArgumentBytes)throw w("tool arguments",u.agentToolArgumentBytes);const s=e.provider_metadata;if(s!==void 0&&Buffer.byteLength(JSON.stringify(s),"utf8")>u.agentToolMetadataBytes)throw w("tool metadata",u.agentToolMetadataBytes)}}}function Y(t,n,o){for(const e of t.choices[0]?.delta.tool_calls??[]){const s=I(e),r=$(e);s.length>0&&n.add(s),r.length>0&&o.add(r)}}function Z(t,n,o){for(const e of t.choices[0]?.message?.tool_calls??[]){const s=I(e),r=$(e);s.length>0&&n.add(s),r.length>0&&o.add(r)}}function Tt(t){return"function"in t&&typeof t.function.name=="string"?[t.function.name]:"name"in t&&typeof t.name=="string"?[t.name]:"functionDeclarations"in t?t.functionDeclarations.flatMap(n=>typeof n.name=="string"?[n.name]:[]):[]}function S(t){return{...t,choices:t.choices.map(n=>({...n,message:Nt(n.message)}))}}function Nt(t){const n=t,o=[...Array.isArray(t.content)?q(t.content):[],...tt(n.function_call,0)],e=Array.isArray(t.content)?z(t.content):typeof t.content=="string"?t.content:void 0,s=e??null,r=Array.isArray(t.content)||typeof t.content=="string"&&e!==t.content;return!r&&o.length===0?t:{...t,...r?{content:s}:{},...o.length===0?{}:{tool_calls:[...t.tool_calls??[],...o]}}}function q(t){const n=[];for(const[o,e]of t.entries())if(!(typeof e!="object"||e===null)){if("functionCall"in e&&c(e.functionCall)&&typeof e.functionCall.name=="string"){const s=e.functionCall.args;n.push({id:typeof e.functionCall.id=="string"&&e.functionCall.id.length>0?e.functionCall.id:`provider-call-${o}`,type:"function",function:{name:e.functionCall.name,arguments:nt(s)},...b(e)===void 0?{}:{provider_metadata:b(e)}});continue}if("tool_use"in e&&c(e.tool_use)&&typeof e.tool_use.name=="string"){const s=e.tool_use.input;n.push({id:typeof e.tool_use.id=="string"&&e.tool_use.id.length>0?e.tool_use.id:`provider-call-${o}`,type:"function",function:{name:e.tool_use.name,arguments:JSON.stringify(s??{})},...b(e)===void 0?{}:{provider_metadata:b(e)}})}}return n}function tt(t,n){if(!c(t)||typeof t.name!="string")return[];const o=t.arguments;return[{id:typeof t.id=="string"&&t.id.length>0?t.id:`provider-call-${n}`,type:"function",function:{name:t.name,arguments:nt(o)},...b(t)===void 0?{}:{provider_metadata:b(t)}}]}function nt(t){if(t===void 0)return"";if(typeof t=="string")return t;try{return JSON.stringify(t)}catch{return""}}function c(t){return typeof t=="object"&&t!==null&&!Array.isArray(t)}function et(t){return at(t)}function z(t){return typeof t=="string"?t:Array.isArray(t)?t.reduce((n,o)=>{if(typeof o!="object"||o===null||!("text"in o))return n;const e=o.text;return n+(typeof e=="string"?e:"")},""):""}export{Ft as createToolCallingModelClient};
@@ -5,6 +5,7 @@ export declare class FileCacheStore implements CacheStore {
5
5
  private readonly policy;
6
6
  private readonly now;
7
7
  private prunePromise;
8
+ private mutationTail;
8
9
  private readonly memoryCache;
9
10
  private memoryBytes;
10
11
  private lastPruneAt;
@@ -15,6 +16,10 @@ export declare class FileCacheStore implements CacheStore {
15
16
  clear(namespace?: string): Promise<void>;
16
17
  prune(): Promise<void>;
17
18
  private schedulePrune;
19
+ private removeExpired;
20
+ private pruneUnlocked;
21
+ private withMutationLock;
22
+ private mutationLockPath;
18
23
  private namespacePath;
19
24
  private filePath;
20
25
  private setMemoryEntry;
@@ -1,2 +1,2 @@
1
- import p from"node:crypto";import w from"node:fs";import h from"node:fs/promises";import y from"node:path";import{atomicWriteFile as M,withFileLock as E}from"../filesystem/atomic-file-store.js";import{isFileNotFound as u}from"../filesystem/fs-errors.js";import{readJsonFileBounded as l}from"../filesystem/bounded-json-reader.js";const P=600*1e3;class F{root;policy;now;prunePromise;memoryCache=new Map;memoryBytes=0;lastPruneAt=0;constructor(e,t,r=Date.now){this.root=e,this.policy=t,this.now=r;try{w.mkdirSync(e,{recursive:!0,mode:448})}catch{}}async get(e,t){if(!this.policy.enabled)return;const r=`${e}\0${t}`,i=this.memoryCache.get(r);if(i!==void 0){if(i.expiresAtMs>this.now())return this.touchMemoryEntry(r,i),i.value;this.removeMemoryEntry(r)}const o=this.filePath(e,t);try{const s=await l(o,this.policy.maxEntryBytes);if(s.schemaVersion!==1||s.namespace!==e||s.keyHash!==m(e,t)||typeof s.expiresAt!="string")return;const a=Date.parse(s.expiresAt);if(a<=this.now()){await this.delete(e,t);return}return this.setMemoryEntry(r,a,s.value),s.value}catch(s){u(s)||await this.delete(e,t).catch(()=>{});return}}async set(e,t,r,i){if(!this.policy.enabled||i.ttlMs<=0)return;const o=this.now()+i.ttlMs,s=`${e}\0${t}`,a={schemaVersion:1,namespace:e,keyHash:m(e,t),createdAt:new Date(this.now()).toISOString(),expiresAt:new Date(o).toISOString(),value:r},c=`${JSON.stringify(a)}
2
- `;if(Buffer.byteLength(c,"utf8")>this.policy.maxEntryBytes)return;const f=this.filePath(e,t);await E(f,()=>M(f,c)),this.setMemoryEntry(s,o,r),this.schedulePrune()}async delete(e,t){this.removeMemoryEntry(`${e}\0${t}`),await h.rm(this.filePath(e,t),{force:!0})}async clear(e){if(e===void 0)this.clearMemory();else{const t=`${e}\0`;for(const r of[...this.memoryCache.keys()])r.startsWith(t)&&this.removeMemoryEntry(r)}await h.rm(e===void 0?this.root:this.namespacePath(e),{recursive:!0,force:!0})}async prune(){if(!this.policy.enabled)return;this.pruneMemory();const e=await d(this.root),t=[];for(const i of e)try{const o=await h.stat(i),s=await l(i,this.policy.maxEntryBytes);if(s.schemaVersion!==1||typeof s.expiresAt!="string"||Date.parse(s.expiresAt)<=this.now()){await h.rm(i,{force:!0});continue}t.push({filePath:i,size:o.size,modifiedAt:o.mtimeMs})}catch{await h.rm(i,{force:!0})}let r=t.reduce((i,o)=>i+o.size,0);for(const i of t.sort((o,s)=>o.modifiedAt-s.modifiedAt)){if(r<=this.policy.maxTotalBytes)break;await h.rm(i.filePath,{force:!0}),r-=i.size}this.pruneMemory()}schedulePrune(){this.prunePromise===void 0&&(this.now()-this.lastPruneAt<P||(this.lastPruneAt=this.now(),this.prunePromise=this.prune().catch(()=>{}).finally(()=>{this.prunePromise=void 0})))}namespacePath(e){return y.join(this.root,x(e))}filePath(e,t){return y.join(this.namespacePath(e),`${m(e,t)}.json`)}setMemoryEntry(e,t,r){const i=A(r);this.removeMemoryEntry(e),!(i>this.policy.maxMemoryBytes)&&(this.memoryCache.set(e,{expiresAtMs:t,value:r,sizeBytes:i}),this.memoryBytes+=i,this.pruneMemory())}removeMemoryEntry(e){const t=this.memoryCache.get(e);t!==void 0&&(this.memoryCache.delete(e),this.memoryBytes-=t.sizeBytes)}touchMemoryEntry(e,t){this.memoryCache.delete(e),this.memoryCache.set(e,t)}clearMemory(){this.memoryCache.clear(),this.memoryBytes=0}pruneMemory(){const e=this.now();for(const[t,r]of[...this.memoryCache])r.expiresAtMs<=e&&this.removeMemoryEntry(t);for(;this.memoryCache.size>this.policy.maxMemoryEntries||this.memoryBytes>this.policy.maxMemoryBytes;){const t=this.memoryCache.keys().next().value;if(typeof t!="string")break;this.removeMemoryEntry(t)}}}function m(n,e){return p.createHash("sha256").update(`${n}\0${e}`).digest("hex")}function x(n){return Buffer.from(n,"utf8").toString("base64url")}function A(n){try{const e=JSON.stringify(n);return Buffer.byteLength(e??"","utf8")}catch{return Number.MAX_SAFE_INTEGER}}async function d(n){try{const e=await h.readdir(n,{withFileTypes:!0}),t=[];for(const r of e){const i=y.join(n,r.name);r.isDirectory()?t.push(...await d(i)):r.isFile()&&r.name.endsWith(".json")&&t.push(i)}return t}catch(e){if(u(e))return[];throw e}}export{F as FileCacheStore};
1
+ import p from"node:crypto";import w from"node:fs";import h from"node:fs/promises";import a from"node:path";import{atomicWriteFile as M,withFileLock as x}from"../filesystem/atomic-file-store.js";import{isFileNotFound as y}from"../filesystem/fs-errors.js";import{readJsonFileBounded as u}from"../filesystem/bounded-json-reader.js";const P=600*1e3;class b{root;policy;now;prunePromise;mutationTail=Promise.resolve();memoryCache=new Map;memoryBytes=0;lastPruneAt=0;constructor(t,e,r=Date.now){this.root=t,this.policy=e,this.now=r;try{w.mkdirSync(t,{recursive:!0,mode:448})}catch{}}async get(t,e){if(!this.policy.enabled)return;const r=`${t}\0${e}`,i=this.memoryCache.get(r);if(i!==void 0){if(i.expiresAtMs>this.now())return this.touchMemoryEntry(r,i),i.value;this.removeMemoryEntry(r)}const o=this.filePath(t,e);try{const s=await u(o,this.policy.maxEntryBytes);if(s.schemaVersion!==1||s.namespace!==t||s.keyHash!==m(t,e)||typeof s.expiresAt!="string")return;const c=Date.parse(s.expiresAt);if(c<=this.now()){await this.removeExpired(t,e);return}return this.setMemoryEntry(r,c,s.value),s.value}catch(s){y(s)||await this.delete(t,e).catch(()=>{});return}}async set(t,e,r,i){if(!this.policy.enabled||i.ttlMs<=0)return;const o=this.now()+i.ttlMs,s=`${t}\0${e}`,c={schemaVersion:1,namespace:t,keyHash:m(t,e),createdAt:new Date(this.now()).toISOString(),expiresAt:new Date(o).toISOString(),value:r},f=`${JSON.stringify(c)}
2
+ `;if(Buffer.byteLength(f,"utf8")>this.policy.maxEntryBytes)return;const d=this.filePath(t,e);await this.withMutationLock(()=>M(d,f)),this.setMemoryEntry(s,o,r),this.schedulePrune()}async delete(t,e){this.removeMemoryEntry(`${t}\0${e}`),await this.withMutationLock(()=>h.rm(this.filePath(t,e),{force:!0}))}async clear(t){if(t===void 0)this.clearMemory();else{const e=`${t}\0`;for(const r of[...this.memoryCache.keys()])r.startsWith(e)&&this.removeMemoryEntry(r)}await this.withMutationLock(()=>h.rm(t===void 0?this.root:this.namespacePath(t),{recursive:!0,force:!0}))}async prune(){this.policy.enabled&&await this.withMutationLock(()=>this.pruneUnlocked())}schedulePrune(){this.prunePromise===void 0&&(this.now()-this.lastPruneAt<P||(this.lastPruneAt=this.now(),this.prunePromise=this.prune().catch(()=>{}).finally(()=>{this.prunePromise=void 0})))}async removeExpired(t,e){const r=this.filePath(t,e);await this.withMutationLock(async()=>{try{const i=await u(r,this.policy.maxEntryBytes);if(i.schemaVersion===1&&i.namespace===t&&i.keyHash===m(t,e)&&typeof i.expiresAt=="string"&&Date.parse(i.expiresAt)>this.now())return}catch(i){if(y(i))return}await h.rm(r,{force:!0})})}async pruneUnlocked(){this.pruneMemory();const t=await l(this.root),e=[];for(const i of t)try{const o=await h.stat(i),s=await u(i,this.policy.maxEntryBytes);if(s.schemaVersion!==1||typeof s.expiresAt!="string"||Date.parse(s.expiresAt)<=this.now()){await h.rm(i,{force:!0});continue}e.push({filePath:i,size:o.size,modifiedAt:o.mtimeMs})}catch{await h.rm(i,{force:!0})}let r=e.reduce((i,o)=>i+o.size,0);for(const i of e.sort((o,s)=>o.modifiedAt-s.modifiedAt)){if(r<=this.policy.maxTotalBytes)break;await h.rm(i.filePath,{force:!0}),r-=i.size}this.pruneMemory()}withMutationLock(t){const e=this.mutationTail.then(()=>x(this.mutationLockPath(),t));return this.mutationTail=e.then(()=>{},()=>{}),e}mutationLockPath(){return a.join(a.dirname(this.root),`${a.basename(this.root)}.mutation`)}namespacePath(t){return a.join(this.root,E(t))}filePath(t,e){return a.join(this.namespacePath(t),`${m(t,e)}.json`)}setMemoryEntry(t,e,r){const i=A(r);this.removeMemoryEntry(t),!(i>this.policy.maxMemoryBytes)&&(this.memoryCache.set(t,{expiresAtMs:e,value:r,sizeBytes:i}),this.memoryBytes+=i,this.pruneMemory())}removeMemoryEntry(t){const e=this.memoryCache.get(t);e!==void 0&&(this.memoryCache.delete(t),this.memoryBytes-=e.sizeBytes)}touchMemoryEntry(t,e){this.memoryCache.delete(t),this.memoryCache.set(t,e)}clearMemory(){this.memoryCache.clear(),this.memoryBytes=0}pruneMemory(){const t=this.now();for(const[e,r]of[...this.memoryCache])r.expiresAtMs<=t&&this.removeMemoryEntry(e);for(;this.memoryCache.size>this.policy.maxMemoryEntries||this.memoryBytes>this.policy.maxMemoryBytes;){const e=this.memoryCache.keys().next().value;if(typeof e!="string")break;this.removeMemoryEntry(e)}}}function m(n,t){return p.createHash("sha256").update(`${n}\0${t}`).digest("hex")}function E(n){return Buffer.from(n,"utf8").toString("base64url")}function A(n){try{const t=JSON.stringify(n);return Buffer.byteLength(t??"","utf8")}catch{return Number.MAX_SAFE_INTEGER}}async function l(n){try{const t=await h.readdir(n,{withFileTypes:!0}),e=[];for(const r of t){const i=a.join(n,r.name);r.isDirectory()?e.push(...await l(i)):r.isFile()&&r.name.endsWith(".json")&&e.push(i)}return e}catch(t){if(y(t))return[];throw t}}export{b as FileCacheStore};
@@ -1 +1 @@
1
- import{CliError as f}from"../../domain/errors/cli-error.js";import{CLI_ERROR_CODES as b}from"../../domain/errors/error-codes.js";import{WindowsClipboardAdapter as h}from"./windows-clipboard-adapter.js";import{detectHostPlatform as c}from"../platform/host-platform.js";import{spawnPlatformProcess as w}from"../platform/host-process.js";import{CLIPBOARD_POLICY as d,clipboardUnavailableError as m,isClipboardUnavailableError as C}from"./clipboard-policy.js";function O(r=c()){return r.id==="windows"?new h(r):r.id==="macos"?new u([new p("pbpaste",["-Prefer","png"],"image/png",r)]):r.id==="linux"||r.id==="bsd"?new u([new p("wl-paste",["--no-newline","--type","image/png"],"image/png",r),new p("xclip",["-selection","clipboard","-target","image/png","-out"],"image/png",r)]):new y}class p{command;args;mimeType;platform;constructor(t,i,e,o=c()){this.command=t,this.args=i,this.mimeType=e,this.platform=o}async readImage(){return new Promise((t,i)=>{const e=w(this.command,this.args,this.platform,{stdio:["ignore","pipe","pipe"]}),o=[];let l=0,n=!1;const s=a=>{n||(n=!0,clearTimeout(g),a())},g=setTimeout(()=>{e.kill(),s(()=>i(m(`Clipboard image read timed out after ${d.timeoutMs}ms.`)))},d.timeoutMs);e.stdout?.on("data",a=>{if(!n){if(l+=a.byteLength,l>d.maxImageBytes){e.kill(),s(()=>i(new f(b.ATTACHMENT_TOO_LARGE,"Clipboard image is too large.",{category:"attachment",userMessage:"Clipboard image exceeds the maximum allowed size."})));return}o.push(a)}}),e.once("error",()=>s(()=>t(void 0))),e.once("close",a=>{s(()=>{a!==0||o.length===0?t(void 0):t({bytes:Buffer.concat(o),mimeType:this.mimeType})})})})}}class u{adapters;constructor(t){this.adapters=t}async readImage(){let t;for(const i of this.adapters)try{const e=await i.readImage();if(e!==void 0)return e}catch(e){if(!C(e))throw e;t=e}throw t??m("No supported clipboard image provider was found.")}}class y{async readImage(){throw m("Clipboard image support is unavailable on this platform.")}}export{p as ProcessClipboardAdapter,O as createClipboardAdapter};
1
+ import{CliError as d}from"../../domain/errors/cli-error.js";import{CLI_ERROR_CODES as l}from"../../domain/errors/error-codes.js";import{WindowsClipboardAdapter as c}from"./windows-clipboard-adapter.js";import{detectHostPlatform as n}from"../platform/host-platform.js";import{CLIPBOARD_POLICY as o,clipboardUnavailableError as t,isClipboardUnavailableError as g}from"./clipboard-policy.js";import{runBoundedClipboardProcess as u}from"./clipboard-process.js";function A(r=n()){return r.id==="windows"?new c(r):r.id==="macos"?new m([new s("pbpaste",["-Prefer","png"],"image/png",r)]):r.id==="linux"||r.id==="bsd"?new m([new s("wl-paste",["--no-newline","--type","image/png"],"image/png",r),new s("xclip",["-selection","clipboard","-target","image/png","-out"],"image/png",r)]):new b}class s{command;args;mimeType;platform;constructor(e,i,a,p=n()){this.command=e,this.args=i,this.mimeType=a,this.platform=p}async readImage(){try{const e=await u({command:this.command,args:this.args,platform:this.platform,maxStdoutBytes:o.maxImageBytes,maxStderrBytes:o.maxImageBytes,timeoutMs:o.timeoutMs,stdoutLimitError:new d(l.ATTACHMENT_TOO_LARGE,"Clipboard image is too large.",{category:"attachment",userMessage:"Clipboard image exceeds the maximum allowed size."}),stderrLimitError:t("Clipboard image helper diagnostics exceeded the supported limit.")});return e.exitCode===0&&e.stdout.length>0?{bytes:e.stdout,mimeType:this.mimeType}:void 0}catch(e){throw e instanceof d?e:t("Unable to read an image from the clipboard.",e)}}}class m{adapters;constructor(e){this.adapters=e}async readImage(){let e;for(const i of this.adapters)try{const a=await i.readImage();if(a!==void 0)return a}catch(a){if(!g(a))throw a;e=a}throw e??t("No supported clipboard image provider was found.")}}class b{async readImage(){throw t("Clipboard image support is unavailable on this platform.")}}export{s as ProcessClipboardAdapter,A as createClipboardAdapter};
@@ -0,0 +1,20 @@
1
+ import type { SpawnOptions } from 'node:child_process';
2
+ import type { HostPlatformContext } from '../platform/host-platform.js';
3
+ export interface BoundedClipboardProcessOptions {
4
+ readonly command: string;
5
+ readonly args: readonly string[];
6
+ readonly platform: HostPlatformContext;
7
+ readonly maxStdoutBytes: number;
8
+ readonly maxStderrBytes: number;
9
+ readonly timeoutMs: number;
10
+ readonly env?: NonNullable<SpawnOptions['env']>;
11
+ readonly stdoutLimitError: Error;
12
+ readonly stderrLimitError: Error;
13
+ }
14
+ export interface BoundedClipboardProcessResult {
15
+ readonly stdout: Buffer;
16
+ readonly stderr: Buffer;
17
+ readonly exitCode: number | null;
18
+ }
19
+ /** Runs a clipboard helper with bounded pipes and confirmed process termination. */
20
+ export declare function runBoundedClipboardProcess(options: BoundedClipboardProcessOptions): Promise<BoundedClipboardProcessResult>;
@@ -0,0 +1 @@
1
+ import{spawnPlatformProcess as w,terminatePlatformProcess as x}from"../platform/host-process.js";function v(t){return new Promise((g,u)=>{const o=w(t.command,t.args,t.platform,{env:t.env,stdio:["ignore","pipe","pipe"]}),f=[],l=[];let m=0,c=0,s=!1,r=!1,a;const y=()=>{a!==void 0&&clearTimeout(a)},n=e=>{s||(s=!0,y(),e())},p=e=>{s||r||(r=!0,x(o,t.platform).then(()=>n(()=>u(e)),d=>n(()=>u(new AggregateError([e,d],"Clipboard helper termination could not be confirmed.")))))},B=(e,d,P,b,h)=>{const i=P+d.byteLength;return i>b?(p(h),i):(e.push(d),i)};o.stdout?.on("data",e=>{!s&&!r&&(m=B(f,e,m,t.maxStdoutBytes,t.stdoutLimitError))}),o.stderr?.on("data",e=>{!s&&!r&&(c=B(l,e,c,t.maxStderrBytes,t.stderrLimitError))}),o.once("error",e=>{r||n(()=>u(e))}),o.once("close",e=>{r||n(()=>g({stdout:Buffer.concat(f),stderr:Buffer.concat(l),exitCode:e}))}),a=setTimeout(()=>p(new Error(`Clipboard helper timed out after ${t.timeoutMs}ms.`)),t.timeoutMs)})}export{v as runBoundedClipboardProcess};
@@ -1 +1 @@
1
- import{CliError as f}from"../../domain/errors/cli-error.js";import{CLI_ERROR_CODES as x}from"../../domain/errors/error-codes.js";import{WindowsClipboardTextAdapter as b}from"./windows-clipboard-text-adapter.js";import{detectHostPlatform as u}from"../platform/host-platform.js";import{spawnPlatformProcess as h}from"../platform/host-process.js";import{CLIPBOARD_POLICY as d,clipboardUnavailableError as l,isClipboardUnavailableError as w}from"./clipboard-policy.js";function L(e=u()){return e.id==="windows"?new b(e):e.id==="macos"?new p("pbpaste",[],e):e.id==="linux"||e.id==="bsd"?new C([new p("wl-paste",["--no-newline"],e),new p("xclip",["-selection","clipboard","-out"],e)]):new T}class p{command;args;platform;constructor(r,o,t=u()){this.command=r,this.args=o,this.platform=t}async readText(){return new Promise((r,o)=>{const t=h(this.command,this.args,this.platform,{stdio:["ignore","pipe","pipe"]}),s=[];let c=0,n=!1;const a=i=>{n||(n=!0,clearTimeout(m),i())},m=setTimeout(()=>{t.kill(),a(()=>o(l(`Clipboard text read timed out after ${d.timeoutMs}ms.`)))},d.timeoutMs);t.stdout?.on("data",i=>{if(!n){if(c+=i.byteLength,c>d.maxTextBytes){t.kill(),a(()=>o(new f(x.ATTACHMENT_TOO_LARGE,"Clipboard text is too large.",{category:"attachment",userMessage:"Clipboard text exceeds the maximum allowed size."})));return}s.push(i)}}),t.once("error",()=>a(()=>r(void 0))),t.once("close",i=>a(()=>{i!==0||s.length===0?r(void 0):r(Buffer.concat(s).toString("utf8"))}))})}}class C{adapters;constructor(r){this.adapters=r}async readText(){let r;for(const o of this.adapters)try{const t=await o.readText();if(t!==void 0)return t}catch(t){if(!w(t))throw t;r=t}throw r??l("No supported clipboard text provider was found.")}}class T{async readText(){throw l("Clipboard text support is unavailable on this platform.")}}export{p as ProcessClipboardTextAdapter,L as createClipboardTextAdapter};
1
+ import{CliError as d}from"../../domain/errors/cli-error.js";import{CLI_ERROR_CODES as p}from"../../domain/errors/error-codes.js";import{WindowsClipboardTextAdapter as l}from"./windows-clipboard-text-adapter.js";import{detectHostPlatform as n}from"../platform/host-platform.js";import{CLIPBOARD_POLICY as i,clipboardUnavailableError as o,isClipboardUnavailableError as c}from"./clipboard-policy.js";import{runBoundedClipboardProcess as m}from"./clipboard-process.js";function g(r=n()){return r.id==="windows"?new l(r):r.id==="macos"?new s("pbpaste",[],r):r.id==="linux"||r.id==="bsd"?new u([new s("wl-paste",["--no-newline"],r),new s("xclip",["-selection","clipboard","-out"],r)]):new x}class s{command;args;platform;constructor(t,a,e=n()){this.command=t,this.args=a,this.platform=e}async readText(){try{const t=await m({command:this.command,args:this.args,platform:this.platform,maxStdoutBytes:i.maxTextBytes,maxStderrBytes:i.maxTextBytes,timeoutMs:i.timeoutMs,stdoutLimitError:new d(p.ATTACHMENT_TOO_LARGE,"Clipboard text is too large.",{category:"attachment",userMessage:"Clipboard text exceeds the maximum allowed size."}),stderrLimitError:o("Clipboard text helper diagnostics exceeded the supported limit.")});return t.exitCode===0&&t.stdout.length>0?t.stdout.toString("utf8"):void 0}catch(t){throw t instanceof d?t:o("Unable to read text from the clipboard.",t)}}}class u{adapters;constructor(t){this.adapters=t}async readText(){let t;for(const a of this.adapters)try{const e=await a.readText();if(e!==void 0)return e}catch(e){if(!c(e))throw e;t=e}throw t??o("No supported clipboard text provider was found.")}}class x{async readText(){throw o("Clipboard text support is unavailable on this platform.")}}export{s as ProcessClipboardTextAdapter,g as createClipboardTextAdapter};
@@ -1 +1 @@
1
- import g from"node:crypto";import u from"node:fs/promises";import h from"node:os";import w from"node:path";import{CliError as l}from"../../domain/errors/cli-error.js";import{CLI_ERROR_CODES as T}from"../../domain/errors/error-codes.js";import{DEFAULT_MAX_IMAGE_BYTES as P}from"./image-normalizer.js";import{readFileBounded as A}from"../filesystem/bounded-file-reader.js";import{detectHostPlatform as b}from"../platform/host-platform.js";import{spawnPlatformProcess as C}from"../platform/host-process.js";import{CLIPBOARD_POLICY as c,clipboardUnavailableError as m}from"./clipboard-policy.js";class B{platform;constructor(o=b()){this.platform=o}async readImage(){if(!this.platform.isWindows)throw m("Windows clipboard image adapter cannot run on this platform.");const o=w.join(h.tmpdir(),`lotagate-clipboard-${process.pid}-${g.randomUUID()}.png`);try{await E(o,this.platform);const r=await A(o,P);if(r.truncated)throw new l(T.ATTACHMENT_TOO_LARGE,"Clipboard image exceeds the configured size limit.",{category:"attachment",userMessage:"The clipboard image is too large to attach."});return{bytes:r.bytes,mimeType:"image/png"}}catch(r){throw r instanceof l?r:m("Unable to read an image from the clipboard.",r)}finally{await u.rm(o,{force:!0})}}}function E(n,o){const r="$ErrorActionPreference='Stop'; $image=Get-Clipboard -Format Image; if ($null -eq $image) { exit 3 }; $image.Save($env:LOTAGATE_CLIPBOARD_OUTPUT, [System.Drawing.Imaging.ImageFormat]::Png)";return new Promise((f,i)=>{const e=C("powershell.exe",["-NoProfile","-NonInteractive","-Command",r],o,{env:{...process.env,LOTAGATE_CLIPBOARD_OUTPUT:n},stdio:["ignore","ignore","pipe"]}),s=[];let p=!1;const a=t=>{p||(p=!0,clearTimeout(d),t())},d=setTimeout(()=>{e.kill(),a(()=>i(m(`Clipboard image read timed out after ${c.timeoutMs}ms.`)))},c.timeoutMs);e.stderr?.on("data",t=>s.push(t)),e.once("error",t=>a(()=>i(t))),e.once("close",t=>a(()=>t===0?f():i(new Error(Buffer.concat(s).toString("utf8").trim()||`PowerShell exited with code ${String(t)}.`))))})}export{B as WindowsClipboardAdapter};
1
+ import n from"node:crypto";import p from"node:fs/promises";import d from"node:os";import s from"node:path";import{CliError as m}from"../../domain/errors/cli-error.js";import{CLI_ERROR_CODES as c}from"../../domain/errors/error-codes.js";import{DEFAULT_MAX_IMAGE_BYTES as l}from"./image-normalizer.js";import{readFileBounded as f}from"../filesystem/bounded-file-reader.js";import{detectHostPlatform as g}from"../platform/host-platform.js";import{CLIPBOARD_POLICY as i,clipboardUnavailableError as e}from"./clipboard-policy.js";import{runBoundedClipboardProcess as h}from"./clipboard-process.js";class _{platform;constructor(r=g()){this.platform=r}async readImage(){if(!this.platform.isWindows)throw e("Windows clipboard image adapter cannot run on this platform.");const r=s.join(d.tmpdir(),`lotagate-clipboard-${process.pid}-${n.randomUUID()}.png`);try{await u(r,this.platform);const t=await f(r,l);if(t.truncated)throw new m(c.ATTACHMENT_TOO_LARGE,"Clipboard image exceeds the configured size limit.",{category:"attachment",userMessage:"The clipboard image is too large to attach."});return{bytes:t.bytes,mimeType:"image/png"}}catch(t){throw t instanceof m?t:e("Unable to read an image from the clipboard.",t)}finally{await p.rm(r,{force:!0})}}}function u(a,r){return h({command:"powershell.exe",args:["-NoProfile","-NonInteractive","-Command","$ErrorActionPreference='Stop'; $image=Get-Clipboard -Format Image; if ($null -eq $image) { exit 3 }; $image.Save($env:LOTAGATE_CLIPBOARD_OUTPUT, [System.Drawing.Imaging.ImageFormat]::Png)"],platform:r,env:{...process.env,LOTAGATE_CLIPBOARD_OUTPUT:a},maxStdoutBytes:i.maxImageBytes,maxStderrBytes:i.maxImageBytes,timeoutMs:i.timeoutMs,stdoutLimitError:e("Clipboard image helper produced unexpected output."),stderrLimitError:e("Clipboard image helper diagnostics exceeded the supported limit.")}).then(o=>{if(o.exitCode!==0)throw new Error(o.stderr.toString("utf8").trim()||`PowerShell exited with code ${String(o.exitCode)}.`)})}export{_ as WindowsClipboardAdapter};
@@ -1 +1 @@
1
- import{CliError as p}from"../../domain/errors/cli-error.js";import{CLI_ERROR_CODES as h}from"../../domain/errors/error-codes.js";import{detectHostPlatform as x}from"../platform/host-platform.js";import{spawnPlatformProcess as w}from"../platform/host-process.js";import{CLIPBOARD_POLICY as a,clipboardUnavailableError as s}from"./clipboard-policy.js";class O{platform;constructor(e=x()){this.platform=e}async readText(){if(!this.platform.isWindows)throw s("Windows clipboard text adapter cannot run on this platform.");try{return await C(this.platform)}catch(e){throw e instanceof p?e:s("Unable to read text from the clipboard.",e)}}}function C(l){const e="$ErrorActionPreference='Stop'; [Console]::OutputEncoding = [System.Text.Encoding]::UTF8; $text=Get-Clipboard -Raw; if ($null -eq $text) { exit 3 }; [Console]::Out.Write($text)";return new Promise((f,o)=>{const r=w("powershell.exe",["-NoProfile","-NonInteractive","-Command",e],l,{stdio:["ignore","pipe","pipe"]}),m=[];let c=0,n=!1;const i=t=>{n||(n=!0,clearTimeout(u),t())},u=setTimeout(()=>{r.kill(),i(()=>o(s(`Clipboard text read timed out after ${a.timeoutMs}ms.`)))},a.timeoutMs);r.stdout?.on("data",t=>{if(!n){if(c+=t.byteLength,c>a.maxTextBytes){r.kill(),i(()=>o(new p(h.ATTACHMENT_TOO_LARGE,"Clipboard text is too large.",{category:"attachment",userMessage:"Clipboard text exceeds the maximum allowed size."})));return}m.push(t)}});const d=[];r.stderr?.on("data",t=>d.push(t)),r.once("error",t=>i(()=>o(t))),r.once("close",t=>i(()=>{t===3?f(void 0):t!==0?o(new Error(Buffer.concat(d).toString("utf8").trim()||`PowerShell exited with code ${String(t)}.`)):f(Buffer.concat(m).toString("utf8"))}))})}export{O as WindowsClipboardTextAdapter};
1
+ import{CliError as a}from"../../domain/errors/cli-error.js";import{CLI_ERROR_CODES as n}from"../../domain/errors/error-codes.js";import{detectHostPlatform as d}from"../platform/host-platform.js";import{CLIPBOARD_POLICY as r,clipboardUnavailableError as o}from"./clipboard-policy.js";import{runBoundedClipboardProcess as s}from"./clipboard-process.js";class u{platform;constructor(t=d()){this.platform=t}async readText(){if(!this.platform.isWindows)throw o("Windows clipboard text adapter cannot run on this platform.");try{return await m(this.platform)}catch(t){throw t instanceof a?t:o("Unable to read text from the clipboard.",t)}}}function m(i){return s({command:"powershell.exe",args:["-NoProfile","-NonInteractive","-Command","$ErrorActionPreference='Stop'; [Console]::OutputEncoding = [System.Text.Encoding]::UTF8; $text=Get-Clipboard -Raw; if ($null -eq $text) { exit 3 }; [Console]::Out.Write($text)"],platform:i,maxStdoutBytes:r.maxTextBytes,maxStderrBytes:r.maxTextBytes,timeoutMs:r.timeoutMs,stdoutLimitError:new a(n.ATTACHMENT_TOO_LARGE,"Clipboard text is too large.",{category:"attachment",userMessage:"Clipboard text exceeds the maximum allowed size."}),stderrLimitError:o("Clipboard text helper diagnostics exceeded the supported limit.")}).then(e=>{if(e.exitCode!==3){if(e.exitCode!==0)throw new Error(e.stderr.toString("utf8").trim()||`PowerShell exited with code ${String(e.exitCode)}.`);return e.stdout.toString("utf8")}})}export{u as WindowsClipboardTextAdapter};
@@ -1,3 +1,3 @@
1
- import o from"node:crypto";import n from"node:fs/promises";import g from"node:path";import{CliError as s}from"../../domain/errors/cli-error.js";import{CLI_ERROR_CODES as l}from"../../domain/errors/error-codes.js";import{validateApiKey as w,validateBaseUrl as E,validateProfileName as d}from"../../domain/auth/auth-profile.js";import{withFileLock as f}from"../filesystem/atomic-file-store.js";import{isAlreadyExists as m,isFileNotFound as y}from"../filesystem/fs-errors.js";class R{paths;secret;constructor(e,t=process.env.LOTAGATE_CREDENTIAL_SECRET){this.paths=e,this.secret=t}async list(){return(await this.readPayload()).credentials.map(({profile:t})=>t)}async get(e){const t=d(e);return(await this.readPayload()).credentials.find(a=>a.profile.name===t)}async set(e){const t={profile:{...e.profile,name:d(e.profile.name),baseUrl:E(e.profile.baseUrl),credentialRef:`file:${d(e.profile.name)}`},apiKey:w(e.apiKey)};await f(this.paths.credentialsFile,async()=>{const a=(await this.readPayload()).credentials.filter(({profile:i})=>i.name!==t.profile.name);a.push(t),await this.writePayload({version:1,credentials:a})})}async delete(e){const t=d(e);return f(this.paths.credentialsFile,async()=>{const r=await this.readPayload(),a=r.credentials.filter(({profile:i})=>i.name!==t);return a.length===r.credentials.length?!1:(await this.writePayload({version:1,credentials:a}),!0)})}async readPayload(){const e=await this.readFile();if(e===void 0)return{version:1,credentials:[]};const t=await this.deriveKey(e.salt);try{const r=o.createDecipheriv("aes-256-gcm",t,Buffer.from(e.iv,"base64"));r.setAuthTag(Buffer.from(e.authTag,"base64"));const a=Buffer.concat([r.update(Buffer.from(e.ciphertext,"base64")),r.final()]).toString("utf8"),i=JSON.parse(a);if(!T(i))throw new Error("Invalid credential payload.");return i}catch(r){throw new s(l.AUTH_CREDENTIAL_STORE_UNAVAILABLE,"Credential file cannot be decrypted.",{category:"auth",userMessage:"Unable to decrypt the credential store. Check LOTAGATE_CREDENTIAL_SECRET.",cause:r})}}async writePayload(e){const t=o.randomBytes(16),r=o.randomBytes(12),a=await this.deriveKey(t.toString("base64")),i=o.createCipheriv("aes-256-gcm",a,r),u=Buffer.concat([i.update(JSON.stringify(e),"utf8"),i.final()]),p={version:1,salt:t.toString("base64"),iv:r.toString("base64"),authTag:i.getAuthTag().toString("base64"),ciphertext:u.toString("base64")};await n.mkdir(this.paths.globalConfigDir,{recursive:!0});const h=g.join(this.paths.globalConfigDir,`.credentials.${process.pid}.${o.randomUUID()}.tmp`);try{await n.writeFile(h,`${JSON.stringify(p,null,2)}
2
- `,{encoding:"utf8",mode:384}),await n.rename(h,this.paths.credentialsFile)}finally{await n.rm(h,{force:!0})}}async readFile(){try{return A(await n.readFile(this.paths.credentialsFile,"utf8"))}catch(e){if(y(e))return;throw e instanceof s?e:new s(l.AUTH_CREDENTIAL_STORE_UNAVAILABLE,"Credential file cannot be read.",{category:"auth",userMessage:"Unable to read the credential store.",cause:e})}}async deriveKey(e){return o.scryptSync(await this.resolveSecret(),e,32)}async resolveSecret(){if(this.secret!==void 0&&this.secret.length>=16)return this.secret;await n.mkdir(this.paths.globalConfigDir,{recursive:!0});try{const t=(await n.readFile(this.paths.credentialKeyFile,"utf8")).trim();if(t.length>=16)return t;throw new Error("Credential key file is invalid.")}catch(t){if(!y(t))throw new s(l.AUTH_CREDENTIAL_STORE_UNAVAILABLE,"Credential key cannot be read.",{category:"auth",userMessage:"Unable to read the local credential key.",cause:t})}const e=o.randomBytes(32).toString("base64url");try{return await n.writeFile(this.paths.credentialKeyFile,`${e}
1
+ import o from"node:crypto";import n from"node:fs/promises";import g from"node:path";import{CliError as s}from"../../domain/errors/cli-error.js";import{CLI_ERROR_CODES as l}from"../../domain/errors/error-codes.js";import{validateApiKey as w,validateBaseUrl as E,validateProfileName as d}from"../../domain/auth/auth-profile.js";import{withFileLock as y}from"../filesystem/atomic-file-store.js";import{isAlreadyExists as m,isFileNotFound as u}from"../filesystem/fs-errors.js";class R{paths;secret;constructor(e,t=process.env.LOTAGATE_CREDENTIAL_SECRET){this.paths=e,this.secret=t}async list(){return(await this.readPayload()).credentials.map(({profile:t})=>t)}async get(e){const t=d(e);return(await this.readPayload()).credentials.find(a=>a.profile.name===t)}async set(e){const t={profile:{...e.profile,name:d(e.profile.name),baseUrl:E(e.profile.baseUrl),credentialRef:`file:${d(e.profile.name)}`},apiKey:w(e.apiKey)};await y(this.paths.credentialsFile,async()=>{const a=(await this.readPayload()).credentials.filter(({profile:i})=>i.name!==t.profile.name);a.push(t),await this.writePayload({version:1,credentials:a})})}async delete(e){const t=d(e);return y(this.paths.credentialsFile,async()=>{const r=await this.readPayload(),a=r.credentials.filter(({profile:i})=>i.name!==t);return a.length===r.credentials.length?!1:(await this.writePayload({version:1,credentials:a}),!0)})}async readPayload(){const e=await this.readFile();if(e===void 0)return{version:1,credentials:[]};const t=await this.deriveKey(e.salt);try{const r=o.createDecipheriv("aes-256-gcm",t,Buffer.from(e.iv,"base64"));r.setAuthTag(Buffer.from(e.authTag,"base64"));const a=Buffer.concat([r.update(Buffer.from(e.ciphertext,"base64")),r.final()]).toString("utf8"),i=JSON.parse(a);if(!T(i))throw new Error("Invalid credential payload.");return i}catch(r){throw new s(l.AUTH_CREDENTIAL_STORE_UNAVAILABLE,"Credential file cannot be decrypted.",{category:"auth",userMessage:"Unable to decrypt the credential store. Check LOTAGATE_CREDENTIAL_SECRET.",cause:r})}}async writePayload(e){const t=o.randomBytes(16),r=o.randomBytes(12),a=await this.deriveKey(t.toString("base64")),i=o.createCipheriv("aes-256-gcm",a,r),h=Buffer.concat([i.update(JSON.stringify(e),"utf8"),i.final()]),p={version:1,salt:t.toString("base64"),iv:r.toString("base64"),authTag:i.getAuthTag().toString("base64"),ciphertext:h.toString("base64")};await n.mkdir(this.paths.globalConfigDir,{recursive:!0});const f=g.join(this.paths.globalConfigDir,`.credentials.${process.pid}.${o.randomUUID()}.tmp`);try{await n.writeFile(f,`${JSON.stringify(p,null,2)}
2
+ `,{encoding:"utf8",mode:384}),await n.rename(f,this.paths.credentialsFile)}finally{await n.rm(f,{force:!0})}}async readFile(){try{return A(await n.readFile(this.paths.credentialsFile,"utf8"))}catch(e){if(u(e))return;throw e instanceof s?e:new s(l.AUTH_CREDENTIAL_STORE_UNAVAILABLE,"Credential file cannot be read.",{category:"auth",userMessage:"Unable to read the credential store.",cause:e})}}async deriveKey(e){const t=await this.resolveSecret();return new Promise((r,a)=>{o.scrypt(t,e,32,(i,h)=>{i!==null?a(i):r(h)})})}async resolveSecret(){if(this.secret!==void 0&&this.secret.length>=16)return this.secret;await n.mkdir(this.paths.globalConfigDir,{recursive:!0});try{const t=(await n.readFile(this.paths.credentialKeyFile,"utf8")).trim();if(t.length>=16)return t;throw new Error("Credential key file is invalid.")}catch(t){if(!u(t))throw new s(l.AUTH_CREDENTIAL_STORE_UNAVAILABLE,"Credential key cannot be read.",{category:"auth",userMessage:"Unable to read the local credential key.",cause:t})}const e=o.randomBytes(32).toString("base64url");try{return await n.writeFile(this.paths.credentialKeyFile,`${e}
3
3
  `,{encoding:"utf8",mode:384,flag:"wx"}),e}catch(t){if(!m(t))throw new s(l.AUTH_CREDENTIAL_STORE_UNAVAILABLE,"Credential key cannot be created.",{category:"auth",userMessage:"Unable to initialize secure local credential storage.",cause:t});try{const r=(await n.readFile(this.paths.credentialKeyFile,"utf8")).trim();if(r.length>=16)return r}catch(r){throw new s(l.AUTH_CREDENTIAL_STORE_UNAVAILABLE,"Credential key cannot be read after initialization.",{category:"auth",userMessage:"Unable to initialize secure local credential storage.",cause:r})}throw new s(l.AUTH_CREDENTIAL_STORE_UNAVAILABLE,"Credential key file is invalid.",{category:"auth",userMessage:"Unable to initialize secure local credential storage."})}}}function A(c){try{const e=JSON.parse(c);if(typeof e!="object"||e===null)throw new Error("Expected object.");const t=e;if(t.version!==1||typeof t.salt!="string"||typeof t.iv!="string"||typeof t.authTag!="string"||typeof t.ciphertext!="string")throw new Error("Invalid encrypted credential file.");return{version:1,salt:t.salt,iv:t.iv,authTag:t.authTag,ciphertext:t.ciphertext}}catch(e){throw e instanceof s?e:new s(l.AUTH_CREDENTIAL_STORE_UNAVAILABLE,"Credential file is invalid.",{category:"auth",userMessage:"Credential store is corrupted or has an invalid format.",cause:e})}}function T(c){if(typeof c!="object"||c===null)return!1;const e=c;return e.version===1&&Array.isArray(e.credentials)}export{R as EncryptedFileCredentialStore};
@@ -3,5 +3,5 @@ interface RawInput extends Readable {
3
3
  isTTY?: boolean;
4
4
  setRawMode?: (mode: boolean) => void;
5
5
  }
6
- export declare function readSecret(prompt: string, input?: RawInput, output?: Writable): Promise<string>;
6
+ export declare function readSecret(prompt: string, input?: RawInput, output?: Writable, signal?: AbortSignal): Promise<string>;
7
7
  export {};
@@ -1,3 +1,3 @@
1
- async function l(o,e=process.stdin,r=process.stderr){if(e.isTTY!==!0||e.setRawMode===void 0)return w(e);const s=e.setRawMode;return new Promise((c,u)=>{let n="";const f=i=>{const d=i.toString("utf8");for(const t of d){if(t===""){a(),u(new Error("Input interrupted."));return}if(t==="\r"||t===`
2
- `){a(),r.write(`
3
- `),c(n);return}if(t==="\b"||t==="\x7F"){n=n.slice(0,-1);continue}n+=t}},a=()=>{s(!1),e.pause(),e.off("data",f)};r.write(o),s(!0),e.resume(),e.on("data",f)})}async function w(o){const e=[];for await(const r of o)e.push(Buffer.isBuffer(r)?r:Buffer.from(String(r)));return Buffer.concat(e).toString("utf8").trim()}export{l as readSecret};
1
+ import{CliError as h}from"../../domain/errors/cli-error.js";import{CLI_ERROR_CODES as A}from"../../domain/errors/error-codes.js";import{AUTH_API_KEY_MAX_BYTES as E}from"../../domain/auth/auth-profile.js";async function _(a,r=process.stdin,n=process.stderr,e){if(e?.throwIfAborted(),r.isTTY!==!0||r.setRawMode===void 0)return I(r,e);const f=r.setRawMode;return new Promise((s,o)=>{let u="";const d=m=>{const b=m.toString("utf8");for(const t of b){if(t===""){c(),o(new Error("Input interrupted."));return}if(t==="\r"||t===`
2
+ `){c(),n.write(`
3
+ `),s(u);return}if(t==="\b"||t==="\x7F"){u=u.slice(0,-1);continue}if(Buffer.byteLength(`${u}${t}`,"utf8")>E){c(),o(w());return}u+=t}},c=()=>{f(!1),r.pause(),r.off("data",d),e?.removeEventListener("abort",i)},i=()=>{c(),o(e?.reason??new Error("Input interrupted."))};n.write(a),f(!0),r.resume(),r.on("data",d),e?.addEventListener("abort",i,{once:!0})})}async function I(a,r){const n=[];let e=0;const f=()=>{a.destroy(r?.reason instanceof Error?r.reason:new Error("Input interrupted."))};r?.addEventListener("abort",f,{once:!0});try{for await(const s of a){r?.throwIfAborted();const o=Buffer.isBuffer(s)?s:Buffer.from(String(s));if(e+=o.byteLength,e>E)throw w();n.push(o)}}finally{r?.removeEventListener("abort",f)}return Buffer.concat(n).toString("utf8").trim()}function w(){return new h(A.AUTH_API_KEY_INVALID,"Secret input exceeds the supported size.",{category:"auth",userMessage:"API key is too large."})}export{_ as readSecret};
@@ -38,6 +38,7 @@ export declare class McpRuntime {
38
38
  private readonly environment;
39
39
  private readonly runtimes;
40
40
  private readonly retiring;
41
+ private readonly cleanupOperations;
41
42
  private readonly keyLocks;
42
43
  private closed;
43
44
  constructor(storeFactory?: (cwd: string) => McpConfigReader, policy?: McpRuntimePolicy, logger?: Logger | undefined, now?: () => number, environment?: Readonly<Record<string, string | undefined>>);
@@ -54,5 +55,6 @@ export declare class McpRuntime {
54
55
  private withKeyLock;
55
56
  private scheduleIdleInvalidation;
56
57
  private connectAndDiscover;
58
+ private trackCleanup;
57
59
  }
58
60
  export declare function toAgentMcpConfig(config: CliMcpConfig, environment?: Readonly<Record<string, string | undefined>>): McpServerConfigMap;