@lotagate/cli 0.1.25 → 0.1.27

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 (256) hide show
  1. package/README.md +5 -4
  2. package/bin/lotagate.mjs +50 -8
  3. package/dist/application/agent/agent-constants.d.ts +6 -0
  4. package/dist/application/agent/agent-constants.js +1 -0
  5. package/dist/application/agent/agent-session-store-registry.d.ts +16 -0
  6. package/dist/application/agent/agent-session-store-registry.js +1 -0
  7. package/dist/application/agent/agent-session-store.d.ts +4 -0
  8. package/dist/application/agent/agent-session-store.js +5 -5
  9. package/dist/application/agent/bounded-text-accumulator.d.ts +15 -0
  10. package/dist/application/agent/bounded-text-accumulator.js +1 -0
  11. package/dist/application/agent/capability-profile.d.ts +0 -38
  12. package/dist/application/agent/capability-profile.js +0 -1
  13. package/dist/application/agent/cli-agent-execution-service.d.ts +23 -13
  14. package/dist/application/agent/cli-agent-execution-service.js +1 -1
  15. package/dist/application/agent/cli-agent-preparation-cache.d.ts +39 -0
  16. package/dist/application/agent/cli-agent-preparation-cache.js +1 -0
  17. package/dist/application/agent/context-compaction.d.ts +1 -0
  18. package/dist/application/agent/context-compaction.js +7 -6
  19. package/dist/application/agent/context-token-cache.d.ts +21 -0
  20. package/dist/application/agent/context-token-cache.js +4 -0
  21. package/dist/application/agent/context-usage-service.d.ts +52 -0
  22. package/dist/application/agent/context-usage-service.js +1 -0
  23. package/dist/application/agent/continuation-checkpoint.d.ts +15 -0
  24. package/dist/application/agent/continuation-checkpoint.js +2 -0
  25. package/dist/application/agent/interactive-agent-guidance.d.ts +3 -3
  26. package/dist/application/agent/interactive-agent-guidance.js +2 -2
  27. package/dist/application/chat/pending-attachment-store.d.ts +5 -2
  28. package/dist/application/chat/pending-attachment-store.js +1 -1
  29. package/dist/application/commands/application-command-executor.js +2 -2
  30. package/dist/application/commands/cli-command-services.d.ts +1 -1
  31. package/dist/application/commands/cli-command-services.js +31 -31
  32. package/dist/application/commands/command-catalog.js +1 -1
  33. package/dist/application/commands/goal-command-handlers.js +6 -6
  34. package/dist/application/commands/media-command-handlers.js +7 -6
  35. package/dist/application/commands/memory-command-handler.d.ts +1 -1
  36. package/dist/application/commands/memory-command-handler.js +11 -11
  37. package/dist/application/commands/runtime-command-handlers.js +11 -11
  38. package/dist/application/extensions/plugin-contribution-registry.js +1 -1
  39. package/dist/application/intent/completion-gate.d.ts +2 -5
  40. package/dist/application/intent/completion-gate.js +1 -1
  41. package/dist/application/intent/evidence-ledger.d.ts +2 -1
  42. package/dist/application/intent/evidence-recording-tool-executor.js +1 -1
  43. package/dist/application/intent/intent-compiler.js +2 -2
  44. package/dist/application/lifecycle/operation-registry.d.ts +11 -0
  45. package/dist/application/lifecycle/operation-registry.js +1 -0
  46. package/dist/application/memory/memory-extractor.js +2 -2
  47. package/dist/application/model/cached-model-catalog.d.ts +7 -1
  48. package/dist/application/model/cached-model-catalog.js +1 -1
  49. package/dist/application/model/model-catalog-service.js +1 -1
  50. package/dist/application/model/model-context-window.js +1 -1
  51. package/dist/application/model/model-response-retry.d.ts +7 -1
  52. package/dist/application/model/model-response-retry.js +1 -1
  53. package/dist/application/orchestration/cli-subagent-service.d.ts +4 -0
  54. package/dist/application/orchestration/cli-subagent-service.js +3 -3
  55. package/dist/application/orchestration/cli-turn-event-projector.js +1 -1
  56. package/dist/application/orchestration/cli-turn-events.d.ts +0 -7
  57. package/dist/application/orchestration/goal-input.d.ts +1 -1
  58. package/dist/application/orchestration/goal-input.js +1 -1
  59. package/dist/application/orchestration/headless-agent-turn.d.ts +1 -0
  60. package/dist/application/orchestration/headless-agent-turn.js +3 -3
  61. package/dist/application/orchestration/orchestration-constants.d.ts +3 -0
  62. package/dist/application/orchestration/orchestration-constants.js +1 -0
  63. package/dist/application/orchestration/subagent-handoff.js +3 -3
  64. package/dist/application/project-intelligence/project-intelligence-constants.d.ts +56 -0
  65. package/dist/application/project-intelligence/project-intelligence-constants.js +1 -0
  66. package/dist/application/project-intelligence/project-intelligence-service.js +1 -1
  67. package/dist/application/protocol/browser-host-tool-executor.d.ts +13 -3
  68. package/dist/application/protocol/browser-host-tool-executor.js +2 -2
  69. package/dist/application/protocol/computer-host-tool-executor.d.ts +3 -0
  70. package/dist/application/protocol/computer-host-tool-executor.js +2 -2
  71. package/dist/application/protocol/desktop-agent-command.d.ts +2 -2
  72. package/dist/application/protocol/desktop-agent-command.js +1 -3
  73. package/dist/application/protocol/desktop-agent-contracts.d.ts +34 -0
  74. package/dist/application/protocol/desktop-agent-contracts.js +1 -0
  75. package/dist/application/protocol/desktop-agent-helpers.d.ts +5 -0
  76. package/dist/application/protocol/desktop-agent-helpers.js +1 -0
  77. package/dist/application/protocol/desktop-agent-request-parser.d.ts +31 -6
  78. package/dist/application/protocol/desktop-agent-request-parser.js +1 -1
  79. package/dist/application/protocol/desktop-approval-broker.d.ts +14 -4
  80. package/dist/application/protocol/desktop-approval-broker.js +1 -1
  81. package/dist/application/protocol/desktop-attachment-store.d.ts +19 -2
  82. package/dist/application/protocol/desktop-attachment-store.js +1 -1
  83. package/dist/application/protocol/desktop-execution-policy.d.ts +2 -0
  84. package/dist/application/protocol/desktop-execution-policy.js +1 -1
  85. package/dist/application/protocol/desktop-host-bridge.d.ts +33 -0
  86. package/dist/application/protocol/desktop-host-bridge.js +1 -0
  87. package/dist/application/protocol/desktop-host-capabilities.d.ts +12 -0
  88. package/dist/application/protocol/desktop-host-capabilities.js +1 -0
  89. package/dist/application/protocol/desktop-host-tool-executor.d.ts +15 -2
  90. package/dist/application/protocol/desktop-host-tool-executor.js +2 -2
  91. package/dist/application/protocol/desktop-intent-event-writer.js +1 -1
  92. package/dist/application/protocol/desktop-protocol-helpers.js +1 -1
  93. package/dist/application/protocol/desktop-title-generator.d.ts +5 -0
  94. package/dist/application/protocol/desktop-title-generator.js +3 -0
  95. package/dist/application/protocol/json-line-writer.d.ts +24 -2
  96. package/dist/application/protocol/json-line-writer.js +2 -2
  97. package/dist/application/protocol/jsonl-agent-command.d.ts +1 -1
  98. package/dist/application/protocol/jsonl-agent-command.js +2 -2
  99. package/dist/application/protocol/jsonl-protocol.d.ts +1 -1
  100. package/dist/application/protocol/jsonl-protocol.js +2 -2
  101. package/dist/application/security/desktop-tool-scope-policy.js +1 -1
  102. package/dist/application/security/tool-action-risk-analyzer.js +1 -1
  103. package/dist/application/security/tool-approval-service.d.ts +9 -3
  104. package/dist/application/security/tool-approval-service.js +1 -1
  105. package/dist/application/security/tool-policy-registry.d.ts +20 -0
  106. package/dist/application/security/tool-policy-registry.js +1 -0
  107. package/dist/application/security/tool-risk-classifier.d.ts +2 -1
  108. package/dist/application/security/tool-risk-classifier.js +1 -1
  109. package/dist/application/security/workspace-permission-policy.js +1 -1
  110. package/dist/application/workspace/session-execution-workspace.d.ts +2 -2
  111. package/dist/application/workspace/session-execution-workspace.js +3 -3
  112. package/dist/application/workspace/workspace-constants.d.ts +17 -0
  113. package/dist/application/workspace/workspace-constants.js +1 -0
  114. package/dist/application/workspace/workspace-file-index.d.ts +2 -3
  115. package/dist/application/workspace/workspace-file-index.js +1 -1
  116. package/dist/application/workspace/workspace-instruction-loader.js +1 -1
  117. package/dist/bootstrap/composition-root.d.ts +1 -0
  118. package/dist/bootstrap/runtime-factory.d.ts +5 -0
  119. package/dist/bootstrap/runtime-factory.js +1 -1
  120. package/dist/domain/auth/auth-profile.d.ts +1 -0
  121. package/dist/domain/auth/auth-profile.js +1 -1
  122. package/dist/domain/errors/cli-error.js +1 -1
  123. package/dist/domain/extensions/extension-constants.d.ts +1 -0
  124. package/dist/domain/extensions/extension-constants.js +1 -0
  125. package/dist/domain/extensions/host-tool-plugin-ids.d.ts +0 -4
  126. package/dist/domain/extensions/host-tool-plugin-ids.js +1 -1
  127. package/dist/domain/extensions/plugin-manifest.js +1 -1
  128. package/dist/domain/intent/intent-contract.d.ts +0 -8
  129. package/dist/domain/intent/intent-contract.js +1 -1
  130. package/dist/domain/media/gateway-media-contract.js +1 -1
  131. package/dist/domain/media/media-constants.d.ts +3 -0
  132. package/dist/domain/media/media-constants.js +1 -0
  133. package/dist/domain/media/media-execution-policy.js +1 -1
  134. package/dist/domain/memory/memory-constants.d.ts +3 -0
  135. package/dist/domain/memory/memory-constants.js +1 -0
  136. package/dist/domain/memory/memory-search.js +2 -2
  137. package/dist/domain/orchestration/goal.d.ts +0 -1
  138. package/dist/domain/orchestration/goal.js +1 -1
  139. package/dist/domain/orchestration/work-plan.d.ts +0 -62
  140. package/dist/domain/orchestration/work-plan.js +0 -1
  141. package/dist/domain/runtime/assistant-stream-text.d.ts +11 -0
  142. package/dist/domain/runtime/assistant-stream-text.js +1 -0
  143. package/dist/domain/runtime/gateway-request-budget.d.ts +19 -0
  144. package/dist/domain/runtime/gateway-request-budget.js +1 -0
  145. package/dist/domain/runtime/resource-limits.d.ts +26 -0
  146. package/dist/domain/runtime/resource-limits.js +1 -1
  147. package/dist/domain/session/attachment.d.ts +2 -0
  148. package/dist/domain/workspace/workspace-settings.d.ts +2 -0
  149. package/dist/domain/workspace/workspace-settings.js +1 -1
  150. package/dist/infrastructure/agent-sdk/model-response-metrics.d.ts +7 -0
  151. package/dist/infrastructure/agent-sdk/model-response-metrics.js +1 -0
  152. package/dist/infrastructure/agent-sdk/model-tool-name-mapper.js +1 -1
  153. package/dist/infrastructure/agent-sdk/sdk-constants.d.ts +2 -0
  154. package/dist/infrastructure/agent-sdk/sdk-constants.js +1 -0
  155. package/dist/infrastructure/agent-sdk/tool-calling-model-client.js +1 -1
  156. package/dist/infrastructure/cache/cache-policy.d.ts +2 -0
  157. package/dist/infrastructure/cache/cache-policy.js +1 -1
  158. package/dist/infrastructure/cache/file-cache-store.d.ts +13 -0
  159. package/dist/infrastructure/cache/file-cache-store.js +2 -2
  160. package/dist/infrastructure/clipboard/clipboard-adapter-factory.js +1 -1
  161. package/dist/infrastructure/clipboard/clipboard-constants.d.ts +1 -0
  162. package/dist/infrastructure/clipboard/clipboard-constants.js +1 -0
  163. package/dist/infrastructure/clipboard/clipboard-process.d.ts +20 -0
  164. package/dist/infrastructure/clipboard/clipboard-process.js +1 -0
  165. package/dist/infrastructure/clipboard/clipboard-text-adapter-factory.js +1 -1
  166. package/dist/infrastructure/clipboard/image-normalizer.js +1 -1
  167. package/dist/infrastructure/clipboard/windows-clipboard-adapter.js +1 -1
  168. package/dist/infrastructure/clipboard/windows-clipboard-text-adapter.js +1 -1
  169. package/dist/infrastructure/credentials/encrypted-file-credential-store.js +2 -2
  170. package/dist/infrastructure/credentials/secret-input.d.ts +1 -1
  171. package/dist/infrastructure/credentials/secret-input.js +3 -3
  172. package/dist/infrastructure/extensions/extensions-constants.d.ts +6 -0
  173. package/dist/infrastructure/extensions/extensions-constants.js +1 -0
  174. package/dist/infrastructure/extensions/mcp-runtime.d.ts +9 -0
  175. package/dist/infrastructure/extensions/mcp-runtime.js +1 -1
  176. package/dist/infrastructure/extensions/plugin-agent-discovery.d.ts +3 -1
  177. package/dist/infrastructure/extensions/plugin-agent-discovery.js +1 -1
  178. package/dist/infrastructure/extensions/plugin-manager.js +2 -2
  179. package/dist/infrastructure/extensions/skill-manager.js +2 -2
  180. package/dist/infrastructure/extensions/skill-tool-executor.d.ts +3 -1
  181. package/dist/infrastructure/extensions/skill-tool-executor.js +2 -2
  182. package/dist/infrastructure/filesystem/atomic-file-store.d.ts +11 -0
  183. package/dist/infrastructure/filesystem/atomic-file-store.js +1 -1
  184. package/dist/infrastructure/filesystem/filesystem-constants.d.ts +8 -0
  185. package/dist/infrastructure/filesystem/filesystem-constants.js +1 -0
  186. package/dist/infrastructure/filesystem/local-filesystem-tool-executor.d.ts +3 -0
  187. package/dist/infrastructure/filesystem/local-filesystem-tool-executor.js +3 -3
  188. package/dist/infrastructure/logging/daily-rotating-file-writer.d.ts +1 -1
  189. package/dist/infrastructure/logging/daily-rotating-file-writer.js +2 -2
  190. package/dist/infrastructure/logging/log-redactor.js +1 -1
  191. package/dist/infrastructure/logging/logging-constants.d.ts +3 -0
  192. package/dist/infrastructure/logging/logging-constants.js +1 -0
  193. package/dist/infrastructure/logging/structured-logger.d.ts +1 -1
  194. package/dist/infrastructure/logging/structured-logger.js +2 -2
  195. package/dist/infrastructure/observability/tool-execution-logger.d.ts +11 -0
  196. package/dist/infrastructure/observability/tool-execution-logger.js +1 -0
  197. package/dist/infrastructure/platform/host-platform.d.ts +2 -0
  198. package/dist/infrastructure/platform/host-platform.js +1 -1
  199. package/dist/infrastructure/platform/host-process.d.ts +3 -1
  200. package/dist/infrastructure/platform/host-process.js +1 -1
  201. package/dist/infrastructure/platform/platform-constants.d.ts +4 -0
  202. package/dist/infrastructure/platform/platform-constants.js +1 -0
  203. package/dist/infrastructure/platform/process-identity.d.ts +8 -0
  204. package/dist/infrastructure/platform/process-identity.js +1 -0
  205. package/dist/infrastructure/platform/read-only-executable.d.ts +2 -1
  206. package/dist/infrastructure/platform/read-only-executable.js +1 -1
  207. package/dist/infrastructure/sdk/lotagate-client-factory.js +1 -1
  208. package/dist/infrastructure/sdk/sdk-media-gateway-client.d.ts +4 -0
  209. package/dist/infrastructure/sdk/sdk-media-gateway-client.js +1 -1
  210. package/dist/infrastructure/sessions/file-session-store.d.ts +11 -0
  211. package/dist/infrastructure/sessions/file-session-store.js +4 -4
  212. package/dist/infrastructure/sessions/session-constants.d.ts +3 -0
  213. package/dist/infrastructure/sessions/session-constants.js +1 -0
  214. package/dist/infrastructure/sessions/session-index.js +3 -3
  215. package/dist/infrastructure/sessions/session-journal-health.d.ts +7 -0
  216. package/dist/infrastructure/sessions/session-journal-health.js +1 -0
  217. package/dist/infrastructure/shell/shell-tool-executor.d.ts +5 -1
  218. package/dist/infrastructure/shell/shell-tool-executor.js +2 -2
  219. package/dist/infrastructure/tokens/tiktoken-token-estimator.js +1 -1
  220. package/dist/infrastructure/tokens/token-constants.d.ts +1 -0
  221. package/dist/infrastructure/tokens/token-constants.js +1 -0
  222. package/dist/infrastructure/workspace/workspace-settings-store.d.ts +1 -0
  223. package/dist/infrastructure/workspace/workspace-settings-store.js +4 -4
  224. package/dist/main.js +3 -3
  225. package/dist/ports/media-gateway-client.d.ts +6 -0
  226. package/dist/ports/session-store.d.ts +1 -0
  227. package/dist/presentation/tui/ink/components/composer.js +4 -4
  228. package/dist/presentation/tui/ink/components/transcript.d.ts +1 -0
  229. package/dist/presentation/tui/ink/components/transcript.js +5 -5
  230. package/dist/presentation/tui/ink/tui-bridge.d.ts +2 -2
  231. package/dist/presentation/tui/ink/tui-bridge.js +1 -1
  232. package/dist/presentation/tui/tui-agent-turn-controller.d.ts +45 -0
  233. package/dist/presentation/tui/tui-agent-turn-controller.js +1 -0
  234. package/dist/presentation/tui/tui-application-contract.d.ts +1 -0
  235. package/dist/presentation/tui/tui-application.d.ts +3 -4
  236. package/dist/presentation/tui/tui-application.js +3 -3
  237. package/dist/presentation/tui/tui-clipboard-paste-handlers.d.ts +2 -2
  238. package/dist/presentation/tui/tui-clipboard-paste-handlers.js +1 -1
  239. package/dist/presentation/tui/tui-command-executor.js +3 -3
  240. package/dist/presentation/tui/tui-context-compaction.d.ts +4 -1
  241. package/dist/presentation/tui/tui-context-compaction.js +1 -1
  242. package/dist/presentation/tui/tui-context-service.d.ts +2 -34
  243. package/dist/presentation/tui/tui-context-service.js +1 -2
  244. package/dist/presentation/tui/tui-initial-model.js +1 -1
  245. package/dist/presentation/tui/tui-tool-approval.d.ts +1 -1
  246. package/dist/presentation/tui/tui-tool-approval.js +1 -1
  247. package/dist/version.d.ts +1 -1
  248. package/dist/version.js +1 -1
  249. package/native-targets.mjs +1 -1
  250. package/package.json +9 -10
  251. package/scripts/install-native.mjs +5 -13
  252. package/bin/lotagate.exe +0 -1
  253. package/dist/application/protocol/document-host-tool-definitions.d.ts +0 -4
  254. package/dist/application/protocol/document-host-tool-definitions.js +0 -1
  255. package/dist/application/protocol/document-host-tool-executor.d.ts +0 -49
  256. package/dist/application/protocol/document-host-tool-executor.js +0 -2
@@ -1 +1 @@
1
- import{CliError as a}from"../../domain/errors/cli-error.js";import{CLI_ERROR_CODES as o}from"../../domain/errors/error-codes.js";import{RESOURCE_LIMITS as m}from"../../domain/runtime/resource-limits.js";const s=m.maxAttachmentBytes,f=new Map([["89504e470d0a1a0a","image/png"],["ffd8ff","image/jpeg"],["47494638","image/gif"],["424d","image/bmp"]]);function d(e,t,n=s){if(e.byteLength===0||e.byteLength>n)throw new a(o.ATTACHMENT_TOO_LARGE,"Image size is outside the allowed range.",{category:"attachment",userMessage:`Image size must be between 1 byte and ${n} bytes.`});const r=g(e),i=r??t;if(i===void 0||!i.startsWith("image/"))throw new a(o.ATTACHMENT_FORMAT_UNSUPPORTED,"Clipboard content is not a supported image.",{category:"attachment",userMessage:"The clipboard does not contain a supported image format."});if(r!==void 0&&t!==void 0&&t!==r)throw new a(o.ATTACHMENT_INVALID_IMAGE,"Declared image type does not match its bytes.",{category:"attachment",userMessage:"The image format does not match its actual content."});return{bytes:Buffer.from(e),mimeType:i}}function A(e,t,n,r){const i=n.trim();if(i.length===0)throw new a(o.ATTACHMENT_INVALID_IMAGE,"Attachment id is required.",{category:"attachment"});return{id:i,name:i,mimeType:e.mimeType,sizeBytes:e.bytes.byteLength,source:t,...r===void 0?{}:{storagePath:r}}}function c(e){const t=Buffer.from(e.bytes).toString("base64");return{type:"image_url",image_url:{url:`data:${e.attachment.mimeType};base64,${t}`,detail:"auto"}}}function u(e){if(e.attachment.mimeType.startsWith("image/"))return c(e);const t=Buffer.from(e.bytes).toString("base64");return{type:"input_file",file_data:`data:${e.attachment.mimeType};base64,${t}`,filename:e.attachment.name,media_type:e.attachment.mimeType}}function _(e,t=[]){return t.length===0?{role:"user",content:e}:{role:"user",content:[{type:"text",text:e},...t.map(u)]}}function g(e){const t=[...e.slice(0,8)].map(n=>n.toString(16).padStart(2,"0")).join("");for(const[n,r]of f)if(t.startsWith(n))return r;if(e.length>=12&&Buffer.from(e.slice(0,12)).toString("ascii",0,4)==="RIFF"&&Buffer.from(e.slice(8,12)).toString("ascii")==="WEBP")return"image/webp"}export{s as DEFAULT_MAX_IMAGE_BYTES,_ as buildUserMessage,A as createImageAttachment,d as normalizeImage,u as toAttachmentContentPart,c as toImageContentPart};
1
+ import{CliError as a}from"../../domain/errors/cli-error.js";import{CLI_ERROR_CODES as o}from"../../domain/errors/error-codes.js";import{RESOURCE_LIMITS as m}from"../../domain/runtime/resource-limits.js";import{IMAGE_MIME_SIGNATURES as s}from"./clipboard-constants.js";const c=m.maxAttachmentBytes,f=s;function E(t,e,n=c){if(t.byteLength===0||t.byteLength>n)throw new a(o.ATTACHMENT_TOO_LARGE,"Image size is outside the allowed range.",{category:"attachment",userMessage:`Image size must be between 1 byte and ${n} bytes.`});const r=h(t),i=r??e;if(i===void 0||!i.startsWith("image/"))throw new a(o.ATTACHMENT_FORMAT_UNSUPPORTED,"Clipboard content is not a supported image.",{category:"attachment",userMessage:"The clipboard does not contain a supported image format."});if(r!==void 0&&e!==void 0&&e!==r)throw new a(o.ATTACHMENT_INVALID_IMAGE,"Declared image type does not match its bytes.",{category:"attachment",userMessage:"The image format does not match its actual content."});return{bytes:Buffer.from(t),mimeType:i}}function _(t,e,n,r){const i=n.trim();if(i.length===0)throw new a(o.ATTACHMENT_INVALID_IMAGE,"Attachment id is required.",{category:"attachment"});return{id:i,name:i,mimeType:t.mimeType,sizeBytes:t.bytes.byteLength,source:e,...r===void 0?{}:{storagePath:r}}}function u(t){const e=Buffer.from(t.bytes).toString("base64");return{type:"image_url",image_url:{url:`data:${t.attachment.mimeType};base64,${e}`,detail:"auto"}}}function g(t){if(t.attachment.mimeType.startsWith("image/"))return u(t);const e=Buffer.from(t.bytes).toString("base64");return{type:"input_file",file_data:`data:${t.attachment.mimeType};base64,${e}`,filename:t.attachment.name,media_type:t.attachment.mimeType}}function I(t,e=[]){return e.length===0?{role:"user",content:t}:{role:"user",content:[{type:"text",text:t},...e.map(g)]}}function h(t){const e=[...t.slice(0,8)].map(n=>n.toString(16).padStart(2,"0")).join("");for(const[n,r]of f)if(e.startsWith(n))return r;if(t.length>=12&&Buffer.from(t.slice(0,12)).toString("ascii",0,4)==="RIFF"&&Buffer.from(t.slice(8,12)).toString("ascii")==="WEBP")return"image/webp"}export{c as DEFAULT_MAX_IMAGE_BYTES,I as buildUserMessage,_ as createImageAttachment,E as normalizeImage,g as toAttachmentContentPart,u as toImageContentPart};
@@ -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};
@@ -0,0 +1,6 @@
1
+ export declare const DEFAULT_MCP_CONNECT_TIMEOUT_MS = 30000;
2
+ export declare const DEFAULT_MCP_DISCONNECT_TIMEOUT_MS = 5000;
3
+ export declare const MAX_PLUGIN_FILES = 5000;
4
+ export declare const MAX_PLUGIN_FILE_BYTES: number;
5
+ export declare const MAX_PLUGIN_TOTAL_BYTES: number;
6
+ export declare const MAX_PLUGIN_DEPTH = 32;
@@ -0,0 +1 @@
1
+ const _=3e4,T=5e3,o=5e3,t=10485760,E=52428800,M=32;export{_ as DEFAULT_MCP_CONNECT_TIMEOUT_MS,T as DEFAULT_MCP_DISCONNECT_TIMEOUT_MS,M as MAX_PLUGIN_DEPTH,o as MAX_PLUGIN_FILES,t as MAX_PLUGIN_FILE_BYTES,E as MAX_PLUGIN_TOTAL_BYTES};
@@ -37,6 +37,10 @@ export declare class McpRuntime {
37
37
  private readonly now;
38
38
  private readonly environment;
39
39
  private readonly runtimes;
40
+ private readonly retiring;
41
+ private readonly cleanupOperations;
42
+ private readonly keyLocks;
43
+ private closed;
40
44
  constructor(storeFactory?: (cwd: string) => McpConfigReader, policy?: McpRuntimePolicy, logger?: Logger | undefined, now?: () => number, environment?: Readonly<Record<string, string | undefined>>);
41
45
  ensure(cwd: string, config: CliMcpConfig, trusted: boolean, options?: {
42
46
  onEvent?: McpRuntimeEventListener;
@@ -45,7 +49,12 @@ export declare class McpRuntime {
45
49
  connectConfigured(cwd: string, trusted: boolean): Promise<readonly McpConnectionSummary[]>;
46
50
  invalidate(cwd: string): Promise<void>;
47
51
  close(): Promise<void>;
52
+ private ensureUnlocked;
53
+ private invalidateUnlocked;
54
+ private waitForRetiring;
55
+ private withKeyLock;
48
56
  private scheduleIdleInvalidation;
49
57
  private connectAndDiscover;
58
+ private trackCleanup;
50
59
  }
51
60
  export declare function toAgentMcpConfig(config: CliMcpConfig, environment?: Readonly<Record<string, string | undefined>>): McpServerConfigMap;
@@ -1 +1 @@
1
- import D from"node:crypto";import M from"node:path";import{AgentSdkError as S,AgentSdkErrorCode as j,DEFAULT_MCP_STARTUP_CONCURRENCY as C,mapWithMcpConcurrency as k,McpClientManager as G,McpToolExecutor as F}from"@lotagate/agent-sdk";import{CliError as P}from"../../domain/errors/cli-error.js";import{CLI_ERROR_CODES as v}from"../../domain/errors/error-codes.js";import{FileMcpConfigStore as $}from"./file-mcp-config-store.js";import{readBoundedEnvironmentInteger as f}from"../config/environment-values.js";import{redactLogText as O}from"../logging/log-redactor.js";const H=3e4,g=5e3;function J(t=process.env){return{connectTimeoutMs:f(t.LOTAGATE_MCP_CONNECT_TIMEOUT_MS,H,500,12e4),disconnectTimeoutMs:f(t.LOTAGATE_MCP_DISCONNECT_TIMEOUT_MS,g,500,12e4),idleTtlMs:f(t.LOTAGATE_MCP_RUNTIME_TTL_SECONDS,300,1,86400)*1e3,startupConcurrency:f(t.LOTAGATE_MCP_STARTUP_CONCURRENCY,C,1,32),probeTimeoutMs:f(t.LOTAGATE_MCP_PROBE_TIMEOUT_MS,5e3,500,12e4),probeMaxRetries:f(t.LOTAGATE_MCP_PROBE_MAX_RETRIES,1,0,5),reconnectMaxAttempts:f(t.LOTAGATE_MCP_RECONNECT_MAX_ATTEMPTS,3,0,10),healthCheckIntervalMs:f(t.LOTAGATE_MCP_HEALTH_CHECK_INTERVAL_SECONDS,30,5,3600)*1e3,healthCheckTimeoutMs:f(t.LOTAGATE_MCP_HEALTH_CHECK_TIMEOUT_MS,5e3,500,12e4)}}class ne{storeFactory;policy;logger;now;environment;runtimes=new Map;constructor(e=a=>new $(a),n=J(),i,o=Date.now,s=process.env){this.storeFactory=e,this.policy=n,this.logger=i,this.now=o,this.environment=s}async ensure(e,n,i,o={}){if(!i)throw new P(v.PROJECT_TRUST_REQUIRED,"MCP connections require project trust.",{category:"project",userMessage:"Trust the project before connecting to MCP servers."});const s=M.resolve(e),a=V(n),r=this.runtimes.get(s);if(r?.failed&&r.fingerprint===a&&r.leases>0)return T(r),r.lastUsedAt=this.now(),r.leases+=1,_(r,o.onEvent),await r.ready,E(r,this.now,o.onEvent,()=>this.scheduleIdleInvalidation(s,r));if(r!==void 0&&!r.failed&&r.fingerprint===a&&(r.leases>0||this.now()-r.lastUsedAt<=this.policy.idleTtlMs)){T(r);const h=r.leases===0;r.lastUsedAt=this.now(),r.leases+=1,_(r,o.onEvent),await r.ready;const w=E(r,this.now,o.onEvent,()=>this.scheduleIdleInvalidation(s,r));return h&&!r.failed&&B(r.manager)?(w.release(),await this.invalidate(s),this.ensure(s,n,i,o)):w}r!==void 0&&await this.invalidate(s);const c=new Set;_(c,o.onEvent);const u=b(I(n,this.environment),this.policy,c,o.reconnect??!0),l={cwd:s,fingerprint:a,manager:u.manager,executor:u.executor,ready:this.connectAndDiscover(s,u.manager,u.executor,c),listeners:c,failed:!1,lastUsedAt:this.now(),leases:1,idleTimer:void 0};this.runtimes.set(s,l);const d=await l.ready;return l.failed=d.some(h=>h.status==="failed"),E(l,this.now,o.onEvent,()=>this.scheduleIdleInvalidation(s,l))}async connectConfigured(e,n){if(!n)throw new P(v.PROJECT_TRUST_REQUIRED,"MCP connections require project trust.",{category:"project",userMessage:"Trust the project before connecting to MCP servers."});const i=await this.storeFactory(e).list(),o=b(I(i,this.environment),this.policy,new Set);try{return await this.connectAndDiscover(M.resolve(e),o.manager,o.executor,new Set)}finally{await y(o.manager,this.policy.disconnectTimeoutMs??g,this.logger,M.resolve(e))}}async invalidate(e){const n=M.resolve(e),i=this.runtimes.get(n);i!==void 0&&(this.runtimes.delete(n),T(i),i.listeners.clear(),await y(i.manager,this.policy.disconnectTimeoutMs??g,this.logger,n),this.logger?.info("MCP runtime invalidated.",{cwd:n}))}async close(){const e=[...this.runtimes.values()];this.runtimes.clear();for(const n of e)T(n);await Promise.allSettled(e.map(n=>y(n.manager,this.policy.disconnectTimeoutMs??g,this.logger,n.cwd)))}scheduleIdleInvalidation(e,n){T(n),n.idleTimer=setTimeout(()=>{if(n.idleTimer=void 0,!(this.runtimes.get(e)!==n||n.leases>0)){if(this.now()-n.lastUsedAt<this.policy.idleTtlMs){this.scheduleIdleInvalidation(e,n);return}this.invalidate(e)}},this.policy.idleTtlMs),n.idleTimer.unref?.()}async connectAndDiscover(e,n,i,o){const s=performance.now();this.logger?.debug("MCP runtime startup started.",{cwd:e,serverCount:n.configuredServerIds.length,startupConcurrency:this.policy.startupConcurrency??C});const a=await k(n.configuredServerIds,async r=>{const c=performance.now();try{p(o,{type:"mcp.server.status",summary:{serverId:r,status:"connecting"}});const u=await R(n.connect(r),this.policy.connectTimeoutMs,()=>n.disconnect(r),"connection");if(p(o,{type:"mcp.server.status",summary:{...u,latencyMs:m(c)}}),this.logger?.debug("MCP server connected.",{cwd:e,serverId:r,durationMs:m(c),protocolEra:u.protocolEra,protocolVersion:u.protocolVersion}),u.status!=="connected")return u;try{const l=performance.now(),d=await R(n.listTools(r),this.policy.connectTimeoutMs,()=>n.disconnect(r),"tool discovery");i.replaceServerTools(r,d.tools),p(o,{type:"mcp.tool.catalog.updated",serverId:r,toolCount:d.tools.length}),this.logger?.debug("MCP tools discovered.",{cwd:e,serverId:r,toolCount:d.tools.length,durationMs:m(l),totalDurationMs:m(c)})}catch(l){const d=U(l,r);return p(o,{type:"mcp.server.status",summary:{...u,status:"failed",error:d,latencyMs:m(c)}}),p(o,{type:"mcp.tool.catalog.updated",serverId:r,toolCount:0,error:d}),this.logger?.warn("MCP tool discovery failed.",{cwd:e,serverId:r,durationMs:m(c),reason:d.message,...x(l)}),{...u,status:"failed",error:d}}return u}catch(u){const l=U(u,r);return p(o,{type:"mcp.server.status",summary:{serverId:r,status:"failed",error:l,latencyMs:m(c)}}),this.logger?.warn("MCP server connection failed.",{cwd:e,serverId:r,durationMs:m(c),reason:l.message,...x(u)}),{serverId:r,status:"failed",error:l}}},this.policy.startupConcurrency??C);return this.logger?.info("MCP runtime startup completed.",{cwd:e,serverCount:a.length,connectedCount:a.filter(r=>r.status==="connected").length,failedCount:a.filter(r=>r.status==="failed").length,durationMs:m(s)}),a}}function b(t,e,n,i=!0){let o;const s=new G(t,{protocol:{mode:"auto",...e.probeTimeoutMs===void 0?{}:{probeTimeoutMs:e.probeTimeoutMs},...e.probeMaxRetries===void 0?{}:{probeMaxRetries:e.probeMaxRetries}},reconnect:{enabled:i,...e.reconnectMaxAttempts===void 0?{}:{maxAttempts:e.reconnectMaxAttempts},...e.healthCheckIntervalMs===void 0?{}:{healthCheckIntervalMs:e.healthCheckIntervalMs},...e.healthCheckTimeoutMs===void 0?{}:{healthCheckTimeoutMs:e.healthCheckTimeoutMs}},onToolsChanged:a=>{o!==void 0&&(a.tools===null||a.error!==null?o.removeServerTools(a.serverId):o.replaceServerTools(a.serverId,a.tools),p(n,{type:"mcp.tool.catalog.updated",serverId:a.serverId,toolCount:a.tools?.length??0,...a.error===null?{}:{error:a.error}}))},onServerChanged:a=>{a.type==="removed"&&o?.removeServerTools(a.serverId)}});return o=new F(s),{manager:s,executor:o}}async function y(t,e,n,i){let o,s=!1;const a=t.disconnectAll(),r=new Promise(c=>{o=setTimeout(()=>{s=!0,c()},e)});try{await Promise.race([a,r])}finally{o!==void 0&&clearTimeout(o),a.catch(c=>{s||n?.warn("MCP runtime disconnect failed.",{cwd:i,reason:c instanceof Error?c.message:String(c)})})}s&&n?.warn("MCP runtime disconnect timed out.",{cwd:i,timeoutMs:e})}function E(t,e,n,i){let o=!1;return{executor:t.executor,release:()=>{o||(o=!0,t.leases=Math.max(0,t.leases-1),t.lastUsedAt=e(),z(t,n),t.leases===0&&i?.())}}}function T(t){t.idleTimer!==void 0&&clearTimeout(t.idleTimer),t.idleTimer=void 0}function _(t,e){if(e!==void 0){if(t instanceof Set){t.add(e);return}t.listeners.add(e)}}function z(t,e){e!==void 0&&t.listeners.delete(e)}function p(t,e){for(const n of t)Promise.resolve(n(e)).catch(()=>{})}function B(t){const e=t.connectionSummaries;return e.length>0&&e.some(n=>n.status!=="connected")}function V(t){return D.createHash("sha256").update(A(t)).digest("hex")}function A(t){return Array.isArray(t)?`[${t.map(A).join(",")}]`:typeof t!="object"||t===null?JSON.stringify(t):`{${Object.keys(t).sort().map(e=>`${JSON.stringify(e)}:${A(t[e])}`).join(",")}}`}async function R(t,e,n,i){let o,s=!1;const a=new Promise((r,c)=>{o=setTimeout(()=>{s||(s=!0,c(new Error(`MCP ${i} timed out after ${e}ms.`)),Promise.resolve(n()).catch(()=>{}))},e)});try{return await Promise.race([t.then(c=>(s=!0,c)),a])}finally{o!==void 0&&clearTimeout(o),t.catch(()=>{})}}function m(t){return Math.max(0,Math.round(performance.now()-t))}function U(t,e){return t instanceof S?t:new S(j.McpConnectionFailed,"MCP server connection failed.",{cause:t,details:{serverId:e}})}function x(t){const e=[],n=[],i=[],o=[];let s=t;for(let a=0;a<4&&s!==void 0&&s!==null;a+=1){if(s instanceof Error){e.push(O(s.message)),n.push(s.name),s=s.cause;continue}if(typeof s!="object")break;const r=s,c=r.message;typeof c=="string"&&e.push(O(c));const u=r.name;typeof u=="string"&&n.push(u);const l=r.code;typeof l=="string"&&i.push(l);for(const d of["status","statusCode","httpStatus"]){const h=r[d];typeof h=="number"&&Number.isInteger(h)&&o.push(h)}s=r.cause}return{...e.length===0?{}:{errorMessages:e},...n.length===0?{}:{errorTypes:n},...i.length===0?{}:{errorCodes:i},...o.length===0?{}:{httpStatuses:o}}}function I(t,e=process.env){const n={};for(const[i,o]of Object.entries(t))o.enabled&&(n[i]=q(o.config,e));return n}function q(t,e){if(t.type==="stdio"){const{env:s,...a}=t;return{...a,...t.args===void 0?{}:{args:[...t.args]},...s===void 0?{}:{env:L(s,e)}}}const{headers:n,auth:i,...o}=t;return{...o,...n===void 0?{}:{headers:L(n,e)},...i?.type==="bearer"?{auth:{type:"bearer",token:N(i.token,e,"MCP bearer token")}}:{}}}function L(t,e){return Object.fromEntries(Object.entries(t).map(([n,i])=>[n,N(i,e,`MCP secret ${n}`)]))}function N(t,e,n){if(typeof t=="string")return t;const i=e[t.name];if(i===void 0||i.length===0)throw new Error(`${n} environment variable is not set.`);return i}export{ne as McpRuntime,J as resolveMcpRuntimePolicy,I as toAgentMcpConfig};
1
+ import N from"node:crypto";import T from"node:path";import{AgentSdkError as P,AgentSdkErrorCode as k,DEFAULT_MCP_STARTUP_CONCURRENCY as C,mapWithMcpConcurrency as D,McpClientManager as j,McpToolExecutor as F}from"@lotagate/agent-sdk";import{CliError as v}from"../../domain/errors/cli-error.js";import{CLI_ERROR_CODES as A}from"../../domain/errors/error-codes.js";import{FileMcpConfigStore as G}from"./file-mcp-config-store.js";import{readBoundedEnvironmentInteger as h}from"../config/environment-values.js";import{redactLogText as S}from"../logging/log-redactor.js";import{DEFAULT_MCP_CONNECT_TIMEOUT_MS as $,DEFAULT_MCP_DISCONNECT_TIMEOUT_MS as g}from"./extensions-constants.js";function H(n=process.env){return{connectTimeoutMs:h(n.LOTAGATE_MCP_CONNECT_TIMEOUT_MS,$,500,12e4),disconnectTimeoutMs:h(n.LOTAGATE_MCP_DISCONNECT_TIMEOUT_MS,g,500,12e4),idleTtlMs:h(n.LOTAGATE_MCP_RUNTIME_TTL_SECONDS,300,1,86400)*1e3,startupConcurrency:h(n.LOTAGATE_MCP_STARTUP_CONCURRENCY,C,1,32),probeTimeoutMs:h(n.LOTAGATE_MCP_PROBE_TIMEOUT_MS,5e3,500,12e4),probeMaxRetries:h(n.LOTAGATE_MCP_PROBE_MAX_RETRIES,1,0,5),reconnectMaxAttempts:h(n.LOTAGATE_MCP_RECONNECT_MAX_ATTEMPTS,3,0,10),healthCheckIntervalMs:h(n.LOTAGATE_MCP_HEALTH_CHECK_INTERVAL_SECONDS,30,5,3600)*1e3,healthCheckTimeoutMs:h(n.LOTAGATE_MCP_HEALTH_CHECK_TIMEOUT_MS,5e3,500,12e4)}}class se{storeFactory;policy;logger;now;environment;runtimes=new Map;retiring=new Map;cleanupOperations=new Set;keyLocks=new Map;closed=!1;constructor(e=a=>new G(a),t=H(),o,r=Date.now,s=process.env){this.storeFactory=e,this.policy=t,this.logger=o,this.now=r,this.environment=s}async ensure(e,t,o,r={}){if(!o)throw new v(A.PROJECT_TRUST_REQUIRED,"MCP connections require project trust.",{category:"project",userMessage:"Trust the project before connecting to MCP servers."});const s=T.resolve(e);return this.withKeyLock(s,async()=>this.ensureUnlocked(s,t,r))}async connectConfigured(e,t){if(!t)throw new v(A.PROJECT_TRUST_REQUIRED,"MCP connections require project trust.",{category:"project",userMessage:"Trust the project before connecting to MCP servers."});const o=await this.storeFactory(e).list(),r=O(L(o,this.environment),this.policy,new Set);try{return await this.connectAndDiscover(T.resolve(e),r.manager,r.executor,new Set)}finally{await K(r.manager,this.policy.disconnectTimeoutMs??g,this.logger,T.resolve(e))}}async invalidate(e){const t=T.resolve(e);await this.withKeyLock(t,()=>this.invalidateUnlocked(t))}async close(){this.closed=!0,await Promise.all([...this.keyLocks.values()]);const e=[...this.runtimes.values()];this.runtimes.clear();for(const r of e)p(r);const t=this.policy.disconnectTimeoutMs??g,o=e.map(r=>w(r.manager,t,this.logger,r.cwd));await Promise.allSettled(o.map(r=>r.withinTimeout)),await J([...o.map(r=>r.settled),...this.retiring.values(),...this.cleanupOperations],t,this.logger)}async ensureUnlocked(e,t,o){if(this.closed)throw new Error("MCP runtime is closed.");await this.waitForRetiring(e);const r=V(t),s=this.runtimes.get(e);if(s?.failed&&s.fingerprint===r&&s.leases>0)return p(s),s.lastUsedAt=this.now(),s.leases+=1,y(s,o.onEvent),await s.ready,E(s,this.now,o.onEvent,()=>this.scheduleIdleInvalidation(e,s));if(s!==void 0&&!s.failed&&s.fingerprint===r&&(s.leases>0||this.now()-s.lastUsedAt<=this.policy.idleTtlMs)){p(s);const l=s.leases===0;s.lastUsedAt=this.now(),s.leases+=1,y(s,o.onEvent),await s.ready;const d=E(s,this.now,o.onEvent,()=>this.scheduleIdleInvalidation(e,s));return l&&!s.failed&&B(s.manager)?(d.release(),await this.invalidateUnlocked(e),this.ensureUnlocked(e,t,o)):d}s!==void 0&&await this.invalidateUnlocked(e),await this.waitForRetiring(e);const a=new Set;y(a,o.onEvent);const i=O(L(t,this.environment),this.policy,a,o.reconnect??!0),c={cwd:e,fingerprint:r,manager:i.manager,executor:i.executor,ready:this.connectAndDiscover(e,i.manager,i.executor,a),listeners:a,failed:!1,lastUsedAt:this.now(),leases:1,idleTimer:void 0};this.runtimes.set(e,c);const u=await c.ready;return c.failed=u.some(l=>l.status==="failed"),E(c,this.now,o.onEvent,()=>this.scheduleIdleInvalidation(e,c))}async invalidateUnlocked(e){const t=this.runtimes.get(e);if(t===void 0)return;this.runtimes.delete(e),p(t),t.listeners.clear();const o=w(t.manager,this.policy.disconnectTimeoutMs??g,this.logger,e),r=o.settled.finally(()=>{this.retiring.get(e)===r&&this.retiring.delete(e)});this.retiring.set(e,r),await o.withinTimeout,this.logger?.info("MCP runtime invalidated.",{cwd:e})}async waitForRetiring(e){await this.retiring.get(e)}async withKeyLock(e,t){const o=this.keyLocks.get(e);let r;const s=new Promise(i=>{r=i}),a=(o??Promise.resolve()).then(()=>s);this.keyLocks.set(e,a),await o;try{return await t()}finally{r(),this.keyLocks.get(e)===a&&this.keyLocks.delete(e)}}scheduleIdleInvalidation(e,t){p(t),t.idleTimer=setTimeout(()=>{if(t.idleTimer=void 0,!(this.runtimes.get(e)!==t||t.leases>0)){if(this.now()-t.lastUsedAt<this.policy.idleTtlMs){this.scheduleIdleInvalidation(e,t);return}this.invalidate(e)}},this.policy.idleTtlMs),t.idleTimer.unref?.()}async connectAndDiscover(e,t,o,r){const s=performance.now();this.logger?.debug("MCP runtime startup started.",{cwd:e,serverCount:t.configuredServerIds.length,startupConcurrency:this.policy.startupConcurrency??C});const a=await D(t.configuredServerIds,async i=>{const c=performance.now();try{f(r,{type:"mcp.server.status",summary:{serverId:i,status:"connecting"}});const u=await R(t.connect(i),this.policy.connectTimeoutMs,()=>this.trackCleanup(()=>t.disconnect(i)),"connection");if(f(r,{type:"mcp.server.status",summary:{...u,latencyMs:m(c)}}),this.logger?.debug("MCP server connected.",{cwd:e,serverId:i,durationMs:m(c),protocolEra:u.protocolEra,protocolVersion:u.protocolVersion}),u.status!=="connected")return u;try{const l=performance.now(),d=await R(t.listTools(i),this.policy.connectTimeoutMs,()=>this.trackCleanup(()=>t.disconnect(i)),"tool discovery");o.replaceServerTools(i,d.tools),f(r,{type:"mcp.tool.catalog.updated",serverId:i,toolCount:d.tools.length}),this.logger?.debug("MCP tools discovered.",{cwd:e,serverId:i,toolCount:d.tools.length,durationMs:m(l),totalDurationMs:m(c)})}catch(l){const d=b(l,i);return f(r,{type:"mcp.server.status",summary:{...u,status:"failed",error:d,latencyMs:m(c)}}),f(r,{type:"mcp.tool.catalog.updated",serverId:i,toolCount:0,error:d}),this.logger?.warn("MCP tool discovery failed.",{cwd:e,serverId:i,durationMs:m(c),reason:d.message,...U(l)}),{...u,status:"failed",error:d}}return u}catch(u){const l=b(u,i);return f(r,{type:"mcp.server.status",summary:{serverId:i,status:"failed",error:l,latencyMs:m(c)}}),this.logger?.warn("MCP server connection failed.",{cwd:e,serverId:i,durationMs:m(c),reason:l.message,...U(u)}),{serverId:i,status:"failed",error:l}}},this.policy.startupConcurrency??C);return this.logger?.info("MCP runtime startup completed.",{cwd:e,serverCount:a.length,connectedCount:a.filter(i=>i.status==="connected").length,failedCount:a.filter(i=>i.status==="failed").length,durationMs:m(s)}),a}trackCleanup(e){const t=Promise.resolve().then(e).catch(o=>{this.logger?.warn("MCP timeout cleanup failed.",{reason:o instanceof Error?o.message:String(o)})});return this.cleanupOperations.add(t),t.then(()=>this.cleanupOperations.delete(t),()=>this.cleanupOperations.delete(t)),t}}function O(n,e,t,o=!0){let r;const s=new j(n,{protocol:{mode:"auto",...e.probeTimeoutMs===void 0?{}:{probeTimeoutMs:e.probeTimeoutMs},...e.probeMaxRetries===void 0?{}:{probeMaxRetries:e.probeMaxRetries}},reconnect:{enabled:o,...e.reconnectMaxAttempts===void 0?{}:{maxAttempts:e.reconnectMaxAttempts},...e.healthCheckIntervalMs===void 0?{}:{healthCheckIntervalMs:e.healthCheckIntervalMs},...e.healthCheckTimeoutMs===void 0?{}:{healthCheckTimeoutMs:e.healthCheckTimeoutMs}},onToolsChanged:a=>{r!==void 0&&(a.tools===null||a.error!==null?r.removeServerTools(a.serverId):r.replaceServerTools(a.serverId,a.tools),f(t,{type:"mcp.tool.catalog.updated",serverId:a.serverId,toolCount:a.tools?.length??0,...a.error===null?{}:{error:a.error}}))},onServerChanged:a=>{a.type==="removed"&&r?.removeServerTools(a.serverId)}});return r=new F(s),{manager:s,executor:r}}function w(n,e,t,o){let r,s=!1;const a=Promise.resolve().then(()=>n.disconnectAll()),i=new Promise(l=>{r=setTimeout(()=>{s=!0,l()},e)}),c=a.then(()=>{},l=>{s||t?.warn("MCP runtime disconnect failed.",{cwd:o,reason:l instanceof Error?l.message:String(l)})});return{withinTimeout:Promise.race([c,i]).finally(()=>{r!==void 0&&clearTimeout(r),s&&t?.warn("MCP runtime disconnect timed out.",{cwd:o,timeoutMs:e})}),settled:c}}async function K(n,e,t,o){await w(n,e,t,o).withinTimeout}async function J(n,e,t){if(n.length===0)return;let o,r=!1;await Promise.race([Promise.allSettled(n).then(()=>{r=!0}),new Promise(s=>{o=setTimeout(s,e)})]),o!==void 0&&clearTimeout(o),r?t?.debug("MCP runtime drain completed.",{operationCount:n.length}):t?.warn("MCP runtime drain reached its deadline.",{operationCount:n.length,timeoutMs:e})}function E(n,e,t,o){let r=!1;return{executor:n.executor,release:()=>{r||(r=!0,n.leases=Math.max(0,n.leases-1),n.lastUsedAt=e(),z(n,t),n.leases===0&&o?.())}}}function p(n){n.idleTimer!==void 0&&clearTimeout(n.idleTimer),n.idleTimer=void 0}function y(n,e){if(e!==void 0){if(n instanceof Set){n.add(e);return}n.listeners.add(e)}}function z(n,e){e!==void 0&&n.listeners.delete(e)}function f(n,e){for(const t of n)Promise.resolve(t(e)).catch(()=>{})}function B(n){const e=n.connectionSummaries;return e.length>0&&e.some(t=>t.status!=="connected")}function V(n){return N.createHash("sha256").update(_(n)).digest("hex")}function _(n){return Array.isArray(n)?`[${n.map(_).join(",")}]`:typeof n!="object"||n===null?JSON.stringify(n):`{${Object.keys(n).sort().map(e=>`${JSON.stringify(e)}:${_(n[e])}`).join(",")}}`}async function R(n,e,t,o){let r,s=!1;const a=new Promise((i,c)=>{r=setTimeout(()=>{s||(s=!0,c(new Error(`MCP ${o} timed out after ${e}ms.`)),Promise.resolve(t()).catch(()=>{}))},e)});try{return await Promise.race([n.then(c=>(s=!0,c)),a])}finally{r!==void 0&&clearTimeout(r),n.catch(()=>{})}}function m(n){return Math.max(0,Math.round(performance.now()-n))}function b(n,e){return n instanceof P?n:new P(k.McpConnectionFailed,"MCP server connection failed.",{cause:n,details:{serverId:e}})}function U(n){const e=[],t=[],o=[],r=[];let s=n;for(let a=0;a<4&&s!==void 0&&s!==null;a+=1){if(s instanceof Error){e.push(S(s.message)),t.push(s.name),s=s.cause;continue}if(typeof s!="object")break;const i=s,c=i.message;typeof c=="string"&&e.push(S(c));const u=i.name;typeof u=="string"&&t.push(u);const l=i.code;typeof l=="string"&&o.push(l);for(const d of["status","statusCode","httpStatus"]){const M=i[d];typeof M=="number"&&Number.isInteger(M)&&r.push(M)}s=i.cause}return{...e.length===0?{}:{errorMessages:e},...t.length===0?{}:{errorTypes:t},...o.length===0?{}:{errorCodes:o},...r.length===0?{}:{httpStatuses:r}}}function L(n,e=process.env){const t={};for(const[o,r]of Object.entries(n))r.enabled&&(t[o]=q(r.config,e));return t}function q(n,e){if(n.type==="stdio"){const{env:s,...a}=n;return{...a,...n.args===void 0?{}:{args:[...n.args]},...s===void 0?{}:{env:I(s,e)}}}const{headers:t,auth:o,...r}=n;return{...r,...t===void 0?{}:{headers:I(t,e)},...o?.type==="bearer"?{auth:{type:"bearer",token:x(o.token,e,"MCP bearer token")}}:{}}}function I(n,e){return Object.fromEntries(Object.entries(n).map(([t,o])=>[t,x(o,e,`MCP secret ${t}`)]))}function x(n,e,t){if(typeof n=="string")return n;const o=e[n.name];if(o===void 0||o.length===0)throw new Error(`${t} environment variable is not set.`);return o}export{se as McpRuntime,H as resolveMcpRuntimePolicy,L as toAgentMcpConfig};
@@ -1,4 +1,6 @@
1
1
  import { type PluginAgentDefinition } from '../../domain/extensions/plugin-agent-manifest.js';
2
2
  export declare class PluginAgentDiscovery {
3
- discover(root: string): Promise<readonly PluginAgentDefinition[]>;
3
+ discover(root: string, options?: {
4
+ readonly maxEntries?: number;
5
+ }): Promise<readonly PluginAgentDefinition[]>;
4
6
  }
@@ -1 +1 @@
1
- import c from"node:fs/promises";import u from"node:path";import{parsePluginAgentDocument as f}from"../../domain/extensions/plugin-agent-manifest.js";import{CliError as p}from"../../domain/errors/cli-error.js";import{CLI_ERROR_CODES as g}from"../../domain/errors/error-codes.js";import{isFileNotFound as d}from"../filesystem/fs-errors.js";import{readFileBounded as h}from"../filesystem/bounded-file-reader.js";import{RESOURCE_LIMITS as y}from"../../domain/runtime/resource-limits.js";class C{async discover(r){let a;try{a=await c.readdir(r,{withFileTypes:!0})}catch(t){if(d(t))return[];throw t}const s=[];for(const t of a.filter(e=>e.isFile()&&e.name.endsWith(".yaml")).sort((e,n)=>e.name.localeCompare(n.name))){const e=t.name.slice(0,-5);if(!/^[a-z0-9][a-z0-9-]{0,63}$/u.test(e))throw i(`Invalid plugin agent file name: ${t.name}`);const n=u.join(r,t.name);if((await c.lstat(n)).isSymbolicLink())throw i(`Plugin agent must not be a symbolic link: ${t.name}`);const m=await h(n,y.skillDocumentBytes);if(m.truncated)throw i(`Plugin agent exceeds the supported size: ${t.name}`);const l=f(m.bytes.toString("utf8"),r);if(l.name!==e)throw i(`Plugin agent name must match its file name: ${t.name}`);s.push(l)}return s}}function i(o){return new p(g.PLUGIN_MANIFEST_INVALID,o,{category:"extension",userMessage:`Invalid plugin agent: ${o}`})}export{C as PluginAgentDiscovery};
1
+ import c from"node:fs/promises";import g from"node:path";import{parsePluginAgentDocument as p}from"../../domain/extensions/plugin-agent-manifest.js";import{CliError as d}from"../../domain/errors/cli-error.js";import{CLI_ERROR_CODES as h}from"../../domain/errors/error-codes.js";import{isFileNotFound as y}from"../filesystem/fs-errors.js";import{readFileBounded as w}from"../filesystem/bounded-file-reader.js";import{RESOURCE_LIMITS as u}from"../../domain/runtime/resource-limits.js";class C{async discover(r,f={}){let s;try{s=await c.readdir(r,{withFileTypes:!0})}catch(t){if(y(t))return[];throw t}const a=[];for(const t of s.filter(e=>e.isFile()&&e.name.endsWith(".yaml")).sort((e,i)=>e.name.localeCompare(i.name))){if(a.length>=(f.maxEntries??u.pluginMaxAgentFiles))throw n("Plugin contains too many agent definitions.");const e=t.name.slice(0,-5);if(!/^[a-z0-9][a-z0-9-]{0,63}$/u.test(e))throw n(`Invalid plugin agent file name: ${t.name}`);const i=g.join(r,t.name);if((await c.lstat(i)).isSymbolicLink())throw n(`Plugin agent must not be a symbolic link: ${t.name}`);const m=await w(i,u.skillDocumentBytes);if(m.truncated)throw n(`Plugin agent exceeds the supported size: ${t.name}`);const l=p(m.bytes.toString("utf8"),r);if(l.name!==e)throw n(`Plugin agent name must match its file name: ${t.name}`);a.push(l)}return a}}function n(o){return new d(h.PLUGIN_MANIFEST_INVALID,o,{category:"extension",userMessage:`Invalid plugin agent: ${o}`})}export{C as PluginAgentDiscovery};
@@ -1,2 +1,2 @@
1
- import g from"node:crypto";import{verifyExtensionIntegrity as M}from"./extension-integrity.js";import c from"node:fs/promises";import o from"node:path";import{parsePluginManifest as b,PLUGIN_LAYOUT as d}from"../../domain/extensions/plugin-manifest.js";import{CliError as P}from"../../domain/errors/cli-error.js";import{CLI_ERROR_CODES as j}from"../../domain/errors/error-codes.js";import{PluginDiscovery as x,validatePluginManifest as I}from"./plugin-discovery.js";import{atomicWriteFile as D,replaceDirectoryWithRollback as N,withFileLock as y}from"../filesystem/atomic-file-store.js";import{isFileNotFound as S}from"../filesystem/fs-errors.js";import{readJsonFileBounded as E}from"../filesystem/bounded-json-reader.js";import{RESOURCE_LIMITS as _}from"../../domain/runtime/resource-limits.js";import{parseExtensionState as U}from"../../domain/extensions/extension-state.js";class K{root;statePath;constructor(t){this.root=t,this.statePath=o.join(t,"state.json")}async list(t=[this.root],a={}){const i=await this.readState(),e=await new x().discover(t),r=[];for(const s of e){const f=a.verifyIntegrity===!1?void 0:await p(s.directory);r.push({manifest:s,enabled:i[s.name]?.enabled!==!1,...f===void 0?{}:{integrity:f}})}return r}async install(t,a={}){const i=o.resolve(t),e=o.join(i,d.markerDirectory,d.manifestFile);let r;try{r=await E(e,_.pluginManifestBytes)}catch(h){if(S(h)){const F=o.join(i,".codex-plugin","plugin.json");try{throw await c.stat(F),l("Only the .lotagate-plugin/plugin.json format is supported.")}catch(w){if(w instanceof P)throw w}}throw l("Plugin source must contain .lotagate-plugin/plugin.json.")}const s=b(r,i);await I(s);const f=await p(i);M(f,a,l),await c.mkdir(this.root,{recursive:!0});const m=o.join(this.root,L(s.name)),u=`${m}.tmp-${g.randomUUID()}`;return y(`${m}.install`,async()=>{try{await c.cp(i,u,{recursive:!0,force:!1,errorOnExist:!0}),await I({...s,directory:u});const h=await p(u);if(h!==f)throw l("Plugin changed while it was being installed.");return await N(u,m),await this.setEnabled(s.name,!0),{manifest:{...s,directory:m},enabled:!0,integrity:h}}finally{await c.rm(u,{recursive:!0,force:!0})}})}async uninstall(t){const a=o.join(this.root,L(t));return y(`${a}.install`,async()=>{const i=(await this.list([this.root],{verifyIntegrity:!1})).find(e=>e.manifest.name===t);return i===void 0?!1:(await c.rm(i.manifest.directory,{recursive:!0,force:!0}),await y(this.statePath,async()=>{const e=await this.readState();delete e[t],await this.writeState(e)}),!0)})}async setEnabled(t,a){await y(this.statePath,async()=>{const i=await this.readState();i[t]={enabled:a},await this.writeState(i)})}async readState(){try{return U(await E(this.statePath,_.workspaceSettingsBytes))}catch(t){if(S(t))return{};throw t}}async writeState(t){await D(this.statePath,`${JSON.stringify(t,null,2)}
2
- `)}}async function p(n){const t=g.createHash("sha256");let a=0;for(const i of await v(n)){const e=o.relative(n,i).split(o.sep).join("/"),r=await c.stat(i);if(r.size>k)throw l("Plugin file exceeds the integrity scan limit.");if(a+=r.size,a>A)throw l("Plugin exceeds the total integrity scan limit.");t.update(e),t.update("\0"),t.update(await c.readFile(i)),t.update("\0")}return t.digest("hex")}const T=5e3,k=10*1024*1024,A=50*1024*1024,O=32;async function v(n,t=0,a={files:0}){if(t>O)throw l("Plugin directory nesting exceeds the integrity scan limit.");const i=[];for(const e of(await c.readdir(n,{withFileTypes:!0})).sort((r,s)=>r.name.localeCompare(s.name))){const r=o.join(n,e.name);if(e.isSymbolicLink())throw l("Plugin packages must not contain symbolic links.");if(e.isDirectory())i.push(...await v(r,t+1,a));else if(e.isFile()&&(i.push(r),a.files+=1,a.files>T))throw l("Plugin contains too many files for the integrity scan.")}return i}function L(n){return`plugin-${Buffer.from(n,"utf8").toString("base64url")}`}function l(n){return new P(j.PLUGIN_MANIFEST_INVALID,n,{category:"extension",userMessage:`Unable to install plugin: ${n}`})}export{K as PluginManager,p as hashDirectory};
1
+ import g from"node:crypto";import{verifyExtensionIntegrity as M}from"./extension-integrity.js";import c from"node:fs/promises";import o from"node:path";import{parsePluginManifest as b,PLUGIN_LAYOUT as d}from"../../domain/extensions/plugin-manifest.js";import{CliError as P}from"../../domain/errors/cli-error.js";import{CLI_ERROR_CODES as j}from"../../domain/errors/error-codes.js";import{PluginDiscovery as x,validatePluginManifest as I}from"./plugin-discovery.js";import{atomicWriteFile as D,replaceDirectoryWithRollback as N,withFileLock as y}from"../filesystem/atomic-file-store.js";import{isFileNotFound as S}from"../filesystem/fs-errors.js";import{readJsonFileBounded as E}from"../filesystem/bounded-json-reader.js";import{RESOURCE_LIMITS as v}from"../../domain/runtime/resource-limits.js";import{parseExtensionState as U}from"../../domain/extensions/extension-state.js";import{MAX_PLUGIN_DEPTH as T,MAX_PLUGIN_FILE_BYTES as k,MAX_PLUGIN_FILES as A,MAX_PLUGIN_TOTAL_BYTES as O}from"./extensions-constants.js";class Q{root;statePath;constructor(t){this.root=t,this.statePath=o.join(t,"state.json")}async list(t=[this.root],a={}){const i=await this.readState(),e=await new x().discover(t),r=[];for(const s of e){const f=a.verifyIntegrity===!1?void 0:await p(s.directory);r.push({manifest:s,enabled:i[s.name]?.enabled!==!1,...f===void 0?{}:{integrity:f}})}return r}async install(t,a={}){const i=o.resolve(t),e=o.join(i,d.markerDirectory,d.manifestFile);let r;try{r=await E(e,v.pluginManifestBytes)}catch(h){if(S(h)){const F=o.join(i,".codex-plugin","plugin.json");try{throw await c.stat(F),l("Only the .lotagate-plugin/plugin.json format is supported.")}catch(w){if(w instanceof P)throw w}}throw l("Plugin source must contain .lotagate-plugin/plugin.json.")}const s=b(r,i);await I(s);const f=await p(i);M(f,a,l),await c.mkdir(this.root,{recursive:!0});const m=o.join(this.root,L(s.name)),u=`${m}.tmp-${g.randomUUID()}`;return y(`${m}.install`,async()=>{try{await c.cp(i,u,{recursive:!0,force:!1,errorOnExist:!0}),await I({...s,directory:u});const h=await p(u);if(h!==f)throw l("Plugin changed while it was being installed.");return await N(u,m),await this.setEnabled(s.name,!0),{manifest:{...s,directory:m},enabled:!0,integrity:h}}finally{await c.rm(u,{recursive:!0,force:!0})}})}async uninstall(t){const a=o.join(this.root,L(t));return y(`${a}.install`,async()=>{const i=(await this.list([this.root],{verifyIntegrity:!1})).find(e=>e.manifest.name===t);return i===void 0?!1:(await c.rm(i.manifest.directory,{recursive:!0,force:!0}),await y(this.statePath,async()=>{const e=await this.readState();delete e[t],await this.writeState(e)}),!0)})}async setEnabled(t,a){await y(this.statePath,async()=>{const i=await this.readState();i[t]={enabled:a},await this.writeState(i)})}async readState(){try{return U(await E(this.statePath,v.workspaceSettingsBytes))}catch(t){if(S(t))return{};throw t}}async writeState(t){await D(this.statePath,`${JSON.stringify(t,null,2)}
2
+ `)}}async function p(n){const t=g.createHash("sha256");let a=0;for(const i of await _(n)){const e=o.relative(n,i).split(o.sep).join("/"),r=await c.stat(i);if(r.size>k)throw l("Plugin file exceeds the integrity scan limit.");if(a+=r.size,a>O)throw l("Plugin exceeds the total integrity scan limit.");t.update(e),t.update("\0"),t.update(await c.readFile(i)),t.update("\0")}return t.digest("hex")}async function _(n,t=0,a={files:0}){if(t>T)throw l("Plugin directory nesting exceeds the integrity scan limit.");const i=[];for(const e of(await c.readdir(n,{withFileTypes:!0})).sort((r,s)=>r.name.localeCompare(s.name))){const r=o.join(n,e.name);if(e.isSymbolicLink())throw l("Plugin packages must not contain symbolic links.");if(e.isDirectory())i.push(...await _(r,t+1,a));else if(e.isFile()&&(i.push(r),a.files+=1,a.files>A))throw l("Plugin contains too many files for the integrity scan.")}return i}function L(n){return`plugin-${Buffer.from(n,"utf8").toString("base64url")}`}function l(n){return new P(j.PLUGIN_MANIFEST_INVALID,n,{category:"extension",userMessage:`Unable to install plugin: ${n}`})}export{Q as PluginManager,p as hashDirectory};
@@ -1,2 +1,2 @@
1
- import p from"node:fs/promises";import{SkillDiscovery as d}from"./skill-discovery.js";import{atomicWriteFile as f,withFileLock as u}from"../filesystem/atomic-file-store.js";import{isFileNotFound as w}from"../filesystem/fs-errors.js";import{readJsonFileBounded as S}from"../filesystem/bounded-json-reader.js";import{RESOURCE_LIMITS as y}from"../../domain/runtime/resource-limits.js";import{parseExtensionState as g}from"../../domain/extensions/extension-state.js";class M{statePath;constructor(e){this.statePath=e}async listScoped(e,s={}){const a=new Map,i=[],n=new Set,c=new Set;for(const t of e){a.has(t.statePath)||a.set(t.statePath,await this.readState(t.statePath));const m=a.get(t.statePath);for(const o of await new d().discover([t.root])){const r=t.scope==="plugin",h=n.has(o.name)||!r&&t.scope==="builtin"&&c.has(o.name);if(s.includeShadowed!==!0&&h)continue;r?c.add(o.name):t.scope!=="builtin"&&n.add(o.name);const l=r?`${t.pluginName??"plugin"}:${o.name}`:o.name;i.push({...o,skillKey:l,enabled:m[l]?.enabled!==!1,scope:t.scope,root:t.root,statePath:t.statePath,...t.pluginName===void 0?{}:{pluginName:t.pluginName}})}}return i}async setEnabled(e,s,a=this.statePath){await u(a,async()=>{const i=await this.readState(a);i[e]={enabled:s},await this.writeState(i,a)})}async remove(e,s,a=this.statePath){const i=(await new d().discover([s])).find(n=>n.name===e||`${n.name}`===e);return i===void 0?!1:(await p.rm(i.directory,{recursive:!0,force:!0}),await u(a,async()=>{const n=await this.readState(a);delete n[e],await this.writeState(n,a)}),!0)}async readState(e=this.statePath){try{return g(await S(e,y.workspaceSettingsBytes))}catch(s){if(w(s))return{};throw s}}async writeState(e,s=this.statePath){await f(s,`${JSON.stringify(e,null,2)}
2
- `)}}export{M as SkillManager};
1
+ import h from"node:fs/promises";import p from"node:path";import{SkillDiscovery as l}from"./skill-discovery.js";import{atomicWriteFile as w,withFileLock as d}from"../filesystem/atomic-file-store.js";import{isFileNotFound as S}from"../filesystem/fs-errors.js";import{readJsonFileBounded as y}from"../filesystem/bounded-json-reader.js";import{RESOURCE_LIMITS as g}from"../../domain/runtime/resource-limits.js";import{parseExtensionState as P}from"../../domain/extensions/extension-state.js";class x{statePath;constructor(e){this.statePath=e}async listScoped(e,a={}){const s=new Map,i=[],o=new Set,n=new Set;for(const t of e){s.has(t.statePath)||s.set(t.statePath,await this.readState(t.statePath));const m=s.get(t.statePath);for(const r of await new l().discover([t.root])){const c=t.scope==="plugin",f=o.has(r.name)||!c&&t.scope==="builtin"&&n.has(r.name);if(a.includeShadowed!==!0&&f)continue;c?n.add(r.name):t.scope!=="builtin"&&o.add(r.name);const u=c?`${t.pluginName??"plugin"}:${r.name}`:r.name;i.push({...r,skillKey:u,enabled:m[u]?.enabled!==!1,scope:t.scope,root:t.root,statePath:t.statePath,...t.pluginName===void 0?{}:{pluginName:t.pluginName}})}}return i}async setEnabled(e,a,s=this.statePath){await d(s,async()=>{const i=await this.readState(s);i[e]={enabled:a},await this.writeState(i,s)})}async remove(e,a,s=this.statePath){const i=(await new l().discover([a])).find(n=>n.name===e||`${n.name}`===e);if(i===void 0)return!1;const o=`${p.join(a,i.name)}.install`;return d(o,async()=>{const n=(await new l().discover([a])).find(t=>t.name===e||`${t.name}`===e);return n===void 0?!1:(await h.rm(n.directory,{recursive:!0,force:!0}),await d(s,async()=>{const t=await this.readState(s);delete t[e],await this.writeState(t,s)}),!0)})}async readState(e=this.statePath){try{return P(await y(e,g.workspaceSettingsBytes))}catch(a){if(S(a))return{};throw a}}async writeState(e,a=this.statePath){await w(a,`${JSON.stringify(e,null,2)}
2
+ `)}}export{x as SkillManager};
@@ -2,16 +2,18 @@ import type { ToolDefinition } from '@lotagate/sdk';
2
2
  import type { ToolExecutionContext, ToolExecutor, ToolInfo, ToolResult } from '@lotagate/agent-sdk';
3
3
  import type { SkillManifest } from '../../domain/extensions/skill-manifest.js';
4
4
  import type { SkillScopeStore } from '../../application/extensions/skill-scope-store.js';
5
+ import type { Logger } from '../../ports/logger.js';
5
6
  /** Exposes the same skill capability definitions to runtime and context accounting. */
6
7
  export declare function skillToolDefinitions(): readonly ToolDefinition[];
7
8
  /** Presents validated, model-invocable skills as lazy agent tools. */
8
9
  export declare class SkillToolExecutor implements ToolExecutor {
9
10
  private readonly scopes;
11
+ private readonly logger?;
10
12
  private readonly skills;
11
13
  constructor(skills: readonly (SkillManifest & {
12
14
  enabled: boolean;
13
15
  skillKey?: string;
14
- })[], scopes: SkillScopeStore);
16
+ })[], scopes: SkillScopeStore, logger?: Logger | undefined);
15
17
  listTools(): readonly ToolDefinition[];
16
18
  getToolInfo(toolName: string): ToolInfo | undefined;
17
19
  execute(toolName: string, input: Record<string, unknown>, context: ToolExecutionContext): Promise<ToolResult>;
@@ -1,2 +1,2 @@
1
- const a=[u("skill.list","List enabled skills available to the agent.",{type:"object",properties:{},additionalProperties:!1}),u("skill.load","Load instructions for one enabled skill by name when its description matches the task.",{type:"object",properties:{name:{type:"string",description:"Enabled skill name."}},required:["name"],additionalProperties:!1})];function f(){return a}class p{scopes;skills;constructor(e,n){this.scopes=n,this.skills=e.filter(s=>s.enabled&&s.modelInvocable)}listTools(){return f()}getToolInfo(e){return a.some(n=>d(n)===e)?{toolName:e,source:"custom",executorId:"skill-catalog",metadata:{category:"skill",risk:"read"}}:void 0}async execute(e,n,s){if(e==="skill.list")return c(JSON.stringify(this.skills.map(i=>({name:i.skillKey??i.name,description:i.description}))));if(e!=="skill.load")return o(`Unknown skill tool: ${e}`);const r=n.name;if(typeof r!="string")return o("skill.load requires a skill name.");const l=this.skills.find(i=>(i.skillKey??i.name)===r);return l===void 0?o(`Skill is unavailable: ${r}`):(this.scopes.activate(s.runId,l),c(k(l.body,s.maxResultBytes)))}}function u(t,e,n){return{type:"function",function:{name:t,description:e,parameters:n}}}function d(t){return"function"in t?t.function.name:void 0}function c(t){return{content:t,isError:!1}}function o(t){return{content:t,isError:!0}}function k(t,e){const n=Buffer.from(t,"utf8");return n.byteLength<=e?t:`${n.subarray(0,e).toString("utf8")}
2
- [truncated]`}export{p as SkillToolExecutor,f as skillToolDefinitions};
1
+ import{withToolExecutionLogging as f}from"../observability/tool-execution-logger.js";const a=[u("skill.list","List enabled skills available to the agent.",{type:"object",properties:{},additionalProperties:!1}),u("skill.load","Load instructions for one enabled skill by name when its description matches the task.",{type:"object",properties:{name:{type:"string",description:"Enabled skill name."}},required:["name"],additionalProperties:!1})];function d(){return a}class g{scopes;logger;skills;constructor(e,i,r){this.scopes=i,this.logger=r,this.skills=e.filter(n=>n.enabled&&n.modelInvocable)}listTools(){return d()}getToolInfo(e){return a.some(i=>k(i)===e)?{toolName:e,source:"custom",executorId:"skill-catalog",metadata:{category:"skill",risk:"read"}}:void 0}async execute(e,i,r){return f(this.logger,{toolName:e,source:"custom",context:r,details:{skillName:typeof i.name=="string"?i.name:void 0}},async()=>{if(e==="skill.list")return c(JSON.stringify(this.skills.map(s=>({name:s.skillKey??s.name,description:s.description}))));if(e!=="skill.load")return l(`Unknown skill tool: ${e}`);const n=i.name;if(typeof n!="string")return l("skill.load requires a skill name.");const o=this.skills.find(s=>(s.skillKey??s.name)===n);return o===void 0?l(`Skill is unavailable: ${n}`):(this.scopes.activate(r.runId,o),c(m(o.body,r.maxResultBytes)))})}}function u(t,e,i){return{type:"function",function:{name:t,description:e,parameters:i}}}function k(t){return"function"in t?t.function.name:void 0}function c(t){return{content:t,isError:!1}}function l(t){return{content:t,isError:!0}}function m(t,e){const i=Buffer.from(t,"utf8");return i.byteLength<=e?t:`${i.subarray(0,e).toString("utf8")}
2
+ [truncated]`}export{g as SkillToolExecutor,d as skillToolDefinitions};
@@ -1,4 +1,8 @@
1
1
  import fs from 'node:fs/promises';
2
+ export type AdvisoryLockProcessStartCache = Map<number, {
3
+ checkedAt: number;
4
+ startTimeMs?: number;
5
+ }>;
2
6
  /** Writes a file through a same-directory temporary file and rename. */
3
7
  export declare function atomicWriteFile(filePath: string, data: string | Uint8Array, mode?: number): Promise<void>;
4
8
  /** Replaces a directory under an advisory lock with rollback if the commit fails. */
@@ -15,3 +19,10 @@ export declare function withFileLock<T>(filePath: string, operation: () => Promi
15
19
  staleMs?: number;
16
20
  userMessage?: string;
17
21
  }): Promise<T>;
22
+ /** Acquires a cross-process advisory lock and returns its async release function. */
23
+ export declare function acquireFileLock(filePath: string, options?: {
24
+ timeoutMs?: number;
25
+ staleMs?: number;
26
+ userMessage?: string;
27
+ }): Promise<() => Promise<void>>;
28
+ export declare function recoverStaleAdvisoryLock(lockPath: string, staleMs: number, processStartTimes?: AdvisoryLockProcessStartCache): Promise<boolean>;
@@ -1 +1 @@
1
- import l from"node:crypto";import i from"node:fs/promises";import f from"node:path";import{CliError as p}from"../../domain/errors/cli-error.js";import{CLI_ERROR_CODES as d}from"../../domain/errors/error-codes.js";import{isAlreadyExists as E,isFileNotFound as w}from"./fs-errors.js";const h=5e3,g=900*1e3,v=25;async function $(r,e,t=384){await i.mkdir(f.dirname(r),{recursive:!0});const a=`${r}.tmp-${process.pid}-${l.randomUUID()}`;try{await i.writeFile(a,e,{mode:t}),await i.rename(a,r)}finally{await i.rm(a,{force:!0})}}async function T(r,e){const t=`${e}.backup-${process.pid}-${l.randomUUID()}`;let a=!1,o=!1;try{try{await i.rename(e,t),a=!0}catch(n){if(!w(n))throw n}try{await i.rename(r,e)}catch(n){if(a)try{await i.rename(t,e)}catch(c){throw o=!0,new AggregateError([n,c],`Installation and recovery failed. Previous contents are preserved at ${t}.`)}throw n}}finally{o||await i.rm(t,{recursive:!0,force:!0})}}async function C(r){const e=await i.open(r,"wx",384);try{return await e.writeFile(JSON.stringify({pid:process.pid,createdAt:new Date().toISOString()})),e}catch(t){throw await y(e,r),t}}async function L(r,e){if(E(r))return!0;if(!_(r))return!1;try{return await i.stat(e),!0}catch{return!1}}async function y(r,e){try{await r.close()}finally{await i.rm(e,{force:!0})}}async function I(r,e,t={}){const a=`${r}.lock`,o=t.timeoutMs??h,n=t.staleMs??g;await i.mkdir(f.dirname(r),{recursive:!0});const c=Date.now()+o;let s;for(;s===void 0;)try{s=await C(a)}catch(u){if(!await L(u,a))throw u;if(await k(a,n))continue;if(Date.now()>=c)throw new p(d.CONFIG_LOCKED,`Configuration file is locked: ${r}`,{category:"config",userMessage:t.userMessage??"A configuration update is already in progress. Try again shortly.",retryable:!0});await new Promise(m=>setTimeout(m,v))}try{return await e()}finally{await y(s,a)}}function _(r){return typeof r=="object"&&r!==null&&"code"in r&&(r.code==="EPERM"||r.code==="EACCES")}async function k(r,e){let t;try{t=await i.stat(r)}catch(o){return w(o)}if(Date.now()-t.mtimeMs<e)return!1;let a;try{const o=JSON.parse(await i.readFile(r,"utf8"));a=typeof o.pid=="number"?o.pid:void 0}catch{}return a!==void 0&&A(a)?!1:(await i.rm(r,{force:!0}),!0)}function A(r){if(r===process.pid)return!0;try{return process.kill(r,0),!0}catch{return!1}}export{C as acquireAdvisoryFileLock,$ as atomicWriteFile,L as isAdvisoryLockContention,y as releaseAdvisoryFileLock,T as replaceDirectoryWithRollback,I as withFileLock};
1
+ import m from"node:crypto";import a from"node:fs/promises";import p from"node:path";import{CliError as M}from"../../domain/errors/cli-error.js";import{CLI_ERROR_CODES as S}from"../../domain/errors/error-codes.js";import{isAlreadyExists as T,isFileNotFound as u}from"./fs-errors.js";import{currentProcessStartTimeMs as E,readProcessStartTimeMs as h}from"../platform/process-identity.js";import{DEFAULT_LOCK_STALE_MS as A,DEFAULT_LOCK_TIMEOUT_MS as _,LOCK_RETRY_MS as C,PROCESS_START_TIME_CACHE_MS as L,PROCESS_START_TIME_TOLERANCE_MS as g}from"./filesystem-constants.js";async function J(e,r,t=384){await a.mkdir(p.dirname(e),{recursive:!0});const i=`${e}.tmp-${process.pid}-${m.randomUUID()}`;try{await a.writeFile(i,r,{mode:t}),await a.rename(i,e)}finally{await a.rm(i,{force:!0})}}async function W(e,r){const t=`${r}.backup-${process.pid}-${m.randomUUID()}`;let i=!1,o=!1;try{try{await a.rename(r,t),i=!0}catch(n){if(!u(n))throw n}try{await a.rename(e,r)}catch(n){if(i)try{await a.rename(t,r)}catch(s){throw o=!0,new AggregateError([n,s],`Installation and recovery failed. Previous contents are preserved at ${t}.`)}throw n}}finally{o||await a.rm(t,{recursive:!0,force:!0})}}async function k(e){const r=await a.open(e,"wx",384);try{return await r.writeFile(JSON.stringify({pid:process.pid,processStartTimeMs:E(),createdAt:new Date().toISOString()})),r}catch(t){throw await l(r,e),t}}async function v(e,r){if(T(e))return!0;if(!w(e))return!1;try{return await a.stat(r),!0}catch(t){return u(t)||w(t)}}async function l(e,r){try{await e.close()}finally{await a.rm(r,{force:!0})}}async function B(e,r,t={}){const i=await O(e,t);try{return await r()}finally{await i()}}async function O(e,r={}){const t=`${e}.lock`,i=r.timeoutMs??_,o=r.staleMs??A;await a.mkdir(p.dirname(e),{recursive:!0});const n=Date.now()+i,s=new Map;let c;for(;c===void 0;)try{c=await k(t)}catch(d){if(!await v(d,t))throw d;if(await F(t,o,s))continue;if(Date.now()>=n)throw new M(S.CONFIG_LOCKED,`Configuration file is locked: ${e}`,{category:"config",userMessage:r.userMessage??"A configuration update is already in progress. Try again shortly.",retryable:!0});await new Promise(y=>setTimeout(y,C))}let f=!1;return async()=>{f||(f=!0,await l(c,t))}}function w(e){return typeof e=="object"&&e!==null&&"code"in e&&(e.code==="EPERM"||e.code==="EACCES")}async function F(e,r,t=new Map){let i;try{i=await a.stat(e)}catch(n){if(u(n))return!1;throw n}if(Date.now()-i.mtimeMs<r)return!1;let o={};try{o=R(JSON.parse(await a.readFile(e,"utf8")))}catch{}return o.pid!==void 0&&await b(o,t)?!1:(await a.rm(e,{force:!0}),!0)}function R(e){if(typeof e!="object"||e===null)return{};const r=e;return{...typeof r.pid=="number"&&Number.isSafeInteger(r.pid)&&r.pid>0?{pid:r.pid}:{},...typeof r.processStartTimeMs=="number"&&Number.isFinite(r.processStartTimeMs)?{processStartTimeMs:r.processStartTimeMs}:{}}}async function b(e,r){const t=e.pid;if(t===void 0||!x(t))return!1;if(e.processStartTimeMs===void 0)return!0;const i=await D(t,r);return i===void 0?!0:Math.abs(i-e.processStartTimeMs)<=g}async function D(e,r){const t=Date.now(),i=r.get(e);if(i!==void 0&&t-i.checkedAt<L)return i.startTimeMs;const o=await h(e);return r.set(e,{checkedAt:t,...o===void 0?{}:{startTimeMs:o}}),o}function x(e){if(e===process.pid)return!0;try{return process.kill(e,0),!0}catch(r){return r instanceof Error&&"code"in r&&r.code==="EPERM"}}export{k as acquireAdvisoryFileLock,O as acquireFileLock,J as atomicWriteFile,v as isAdvisoryLockContention,F as recoverStaleAdvisoryLock,l as releaseAdvisoryFileLock,W as replaceDirectoryWithRollback,B as withFileLock};
@@ -0,0 +1,8 @@
1
+ export declare const DEFAULT_LOCK_TIMEOUT_MS = 5000;
2
+ export declare const DEFAULT_LOCK_STALE_MS: number;
3
+ export declare const LOCK_RETRY_MS = 25;
4
+ export declare const PROCESS_START_TIME_TOLERANCE_MS = 2000;
5
+ export declare const PROCESS_START_TIME_CACHE_MS = 1000;
6
+ export declare const FILE_DIFF_MAX_BYTES: number;
7
+ export declare const FILE_DIFF_MAX_INPUT_LINES = 400;
8
+ export declare const FILE_DIFF_MAX_LINES = 240;
@@ -0,0 +1 @@
1
+ const _=5e3,E=9e5,o=25,t=2e3,S=1e3,T=65536,I=400,L=240;export{E as DEFAULT_LOCK_STALE_MS,_ as DEFAULT_LOCK_TIMEOUT_MS,T as FILE_DIFF_MAX_BYTES,I as FILE_DIFF_MAX_INPUT_LINES,L as FILE_DIFF_MAX_LINES,o as LOCK_RETRY_MS,S as PROCESS_START_TIME_CACHE_MS,t as PROCESS_START_TIME_TOLERANCE_MS};
@@ -2,14 +2,17 @@ import type { ToolDefinition } from '@lotagate/sdk';
2
2
  import type { ToolExecutionContext, ToolExecutor, ToolInfo, ToolResult } from '@lotagate/agent-sdk';
3
3
  import { type FileChangeDiff } from '../../domain/filesystem/file-change-diff.js';
4
4
  import type { ToolActivityObserver } from '../../ports/tool-activity.js';
5
+ import type { Logger } from '../../ports/logger.js';
5
6
  /** Exposes the same filesystem capability definitions to runtime and context accounting. */
6
7
  export declare function filesystemToolDefinitions(): readonly ToolDefinition[];
8
+ export declare function filesystemToolRisk(toolName: string): 'read' | 'write' | undefined;
7
9
  export declare class LocalFilesystemToolExecutor implements ToolExecutor {
8
10
  private readonly options;
9
11
  constructor(options?: {
10
12
  onWrite?: (change: FileChangeDiff) => void | Promise<void>;
11
13
  onActivity?: ToolActivityObserver;
12
14
  writeScope?: readonly string[];
15
+ logger?: Logger;
13
16
  });
14
17
  listTools(): readonly ToolDefinition[];
15
18
  getToolInfo(toolName: string): ToolInfo | undefined;
@@ -1,3 +1,3 @@
1
- import d from"node:fs/promises";import o from"node:path";import{createFileChangeDiff as g}from"../../domain/filesystem/file-change-diff.js";import{assertWorkspaceWriteTarget as h,resolveWorkspacePath as u}from"./path-resolver.js";import{readFileBounded as y}from"./bounded-file-reader.js";import{isFileNotFound as m}from"./fs-errors.js";import{atomicWriteFile as k}from"./atomic-file-store.js";import{formatFilesystemActivityName as F}from"../../application/agent/tool-display.js";const w=[p("filesystem.read","Read a UTF-8 text file inside the trusted workspace for inspection. Never reproduce detailed file contents in the final response unless the user explicitly requests them.",{path:{type:"string",description:"Workspace-relative file path."}},["path"]),p("filesystem.list","List entries inside a trusted workspace directory.",{path:{type:"string",description:"Optional workspace-relative directory path. Defaults to the workspace root."}},[]),p("filesystem.write","Write UTF-8 text to a file inside the trusted workspace.",{path:{type:"string",description:"Workspace-relative file path."},content:{type:"string",description:"UTF-8 file content."}},["path","content"]),p("filesystem.exists","Check whether a workspace-relative path exists.",{path:{type:"string",description:"Workspace-relative path."}},["path"])],b=64*1024,v=400,S=240;function T(){return w}class U{options;constructor(t={}){this.options=t}listTools(){return T()}getToolInfo(t){return w.some(e=>L(e)===t)?{toolName:t,source:"filesystem",executorId:"local-filesystem",metadata:{risk:t==="filesystem.write"?"write":"read"}}:void 0}async execute(t,e,r){const s=this.options.onActivity?.start({key:`${r.runId}:${t}`,toolName:t,source:"filesystem",label:x(t,e.path)});let a;try{switch(t){case"filesystem.read":a=await this.read(e,r);break;case"filesystem.list":a=await this.list(e,r);break;case"filesystem.write":a=await this.write(e,r);break;case"filesystem.exists":a=await this.exists(e,r);break;default:a=l(`Unknown filesystem tool: ${t}`);break}}catch(n){a=l(n instanceof Error?n.message:String(n))}return s?.complete(a.isError===!0,a.metadata),a}async read(t,e){const r=await u(t.path,e),s=await y(r,e.maxResultBytes);return c(`${s.bytes.toString("utf8")}${s.truncated?`
2
- [truncated]`:""}`)}async list(t,e){const r=await u(t.path===void 0||typeof t.path=="string"&&t.path.trim().length===0?".":t.path,e),s=[],a=Math.max(1,Math.floor(e.maxResultBytes/64)),n=await d.opendir(r);try{for(let f=await n.read();f!==null&&(s.push({name:f.name,kind:f.isDirectory()?"directory":"file"}),!(s.length>=a));f=await n.read());}finally{await n.close()}return c(W(Buffer.from(JSON.stringify(s)),e.maxResultBytes))}async write(t,e){if(typeof t.content!="string")return l("filesystem.write requires string content.");const r=await u(t.path,e,!0);if(this.options.writeScope!==void 0&&!await E(r,e,this.options.writeScope))return l("filesystem.write path is outside the delegated worker write scope.");const s=Buffer.from(t.content,"utf8");if(s.byteLength>e.maxResultBytes)return l("filesystem.write content exceeds the configured limit.");const a=await I(r);await d.mkdir(o.dirname(r),{recursive:!0}),await h(r,e),await k(r,s),await h(r,e);const n=g(String(t.path),a.content,t.content,{maxInputLines:v,maxLines:S,truncated:a.truncated});try{await this.options.onWrite?.(n)}catch{}return{...c(`Wrote ${s.byteLength} bytes.`),metadata:{fileChange:{path:n.path,additions:n.additions,deletions:n.deletions}}}}async exists(t,e){try{return await u(t.path,e),c("true")}catch(r){if(m(r))return c("false");throw r}}}async function E(i,t,e){const r=await d.realpath(o.resolve(t.workspaceRoot??t.cwd??process.cwd())),s=o.relative(r,o.resolve(i));return s.startsWith("..")||o.isAbsolute(s)?!1:e.some(a=>{const n=o.normalize(a).replace(/[\\/]$/u,"");return n==="."?!0:s===n||s.startsWith(`${n}${o.sep}`)})}async function I(i){try{const t=await y(i,b);return{content:t.bytes.toString("utf8"),truncated:t.truncated}}catch(t){return m(t)?{content:"",truncated:!1}:{content:"",truncated:!0}}}function p(i,t,e,r){return{type:"function",function:{name:i,description:t,parameters:{type:"object",properties:e,required:r,additionalProperties:!1}}}}function L(i){return"function"in i?i.function.name:void 0}function c(i){return{content:i,isError:!1}}function l(i){return{content:i,isError:!0}}function W(i,t){return i.byteLength<=t?i.toString("utf8"):`${i.subarray(0,t).toString("utf8")}
3
- [truncated]`}function x(i,t){return F(i,t)}export{U as LocalFilesystemToolExecutor,T as filesystemToolDefinitions,E as isWithinWriteScope};
1
+ import d from"node:fs/promises";import o from"node:path";import{createFileChangeDiff as g}from"../../domain/filesystem/file-change-diff.js";import{assertWorkspaceWriteTarget as h,resolveWorkspacePath as u}from"./path-resolver.js";import{readFileBounded as y}from"./bounded-file-reader.js";import{isFileNotFound as m}from"./fs-errors.js";import{atomicWriteFile as k}from"./atomic-file-store.js";import{formatFilesystemActivityName as F}from"../../application/agent/tool-display.js";import{FILE_DIFF_MAX_BYTES as T,FILE_DIFF_MAX_INPUT_LINES as b,FILE_DIFF_MAX_LINES as v}from"./filesystem-constants.js";import{withToolExecutionLogging as S}from"../observability/tool-execution-logger.js";const w=[p("filesystem.read","Read a UTF-8 text file inside the trusted workspace for inspection. Never reproduce detailed file contents in the final response unless the user explicitly requests them.",{path:{type:"string",description:"Workspace-relative file path."}},["path"]),p("filesystem.list","List entries inside a trusted workspace directory. Do not pass a file path to this tool. For inspecting or reading files, use filesystem.read instead.",{path:{type:"string",description:"Optional workspace-relative directory path. Defaults to the workspace root."}},[]),p("filesystem.write","Write UTF-8 text to a file inside the trusted workspace.",{path:{type:"string",description:"Workspace-relative file path."},content:{type:"string",description:"UTF-8 file content."}},["path","content"]),p("filesystem.exists","Check whether a workspace-relative path exists.",{path:{type:"string",description:"Workspace-relative path."}},["path"])];function E(){return w}function L(i){if(w.some(t=>W(t)===i))return i==="filesystem.write"?"write":"read"}class q{options;constructor(t={}){this.options=t}listTools(){return E()}getToolInfo(t){const e=L(t);return e===void 0?void 0:{toolName:t,source:"filesystem",executorId:"local-filesystem",metadata:{risk:e}}}async execute(t,e,r){return S(this.options.logger,{toolName:t,source:"filesystem",context:r,details:{path:typeof e.path=="string"?e.path:void 0}},async()=>{const s=this.options.onActivity?.start({key:`${r.runId}:${t}`,toolName:t,source:"filesystem",label:R(t,e.path)});let a;try{switch(t){case"filesystem.read":a=await this.read(e,r);break;case"filesystem.list":a=await this.list(e,r);break;case"filesystem.write":a=await this.write(e,r);break;case"filesystem.exists":a=await this.exists(e,r);break;default:a=l(`Unknown filesystem tool: ${t}`);break}}catch(n){a=l(n instanceof Error?n.message:String(n))}return s?.complete(a.isError===!0,a.metadata),a})}async read(t,e){const r=await u(t.path,e),s=await y(r,e.maxResultBytes);return c(`${s.bytes.toString("utf8")}${s.truncated?`
2
+ [truncated]`:""}`)}async list(t,e){const r=await u(t.path===void 0||typeof t.path=="string"&&t.path.trim().length===0?".":t.path,e),s=[],a=Math.max(1,Math.floor(e.maxResultBytes/64)),n=await d.opendir(r);try{for(let f=await n.read();f!==null&&(s.push({name:f.name,kind:f.isDirectory()?"directory":"file"}),!(s.length>=a));f=await n.read());}finally{await n.close()}return c(_(Buffer.from(JSON.stringify(s)),e.maxResultBytes))}async write(t,e){if(typeof t.content!="string")return l("filesystem.write requires string content.");const r=await u(t.path,e,!0);if(this.options.writeScope!==void 0&&!await x(r,e,this.options.writeScope))return l("filesystem.write path is outside the delegated worker write scope.");const s=Buffer.from(t.content,"utf8");if(s.byteLength>e.maxResultBytes)return l("filesystem.write content exceeds the configured limit.");const a=await I(r);await d.mkdir(o.dirname(r),{recursive:!0}),await h(r,e),await k(r,s),await h(r,e);const n=g(String(t.path),a.content,t.content,{maxInputLines:b,maxLines:v,truncated:a.truncated});try{await this.options.onWrite?.(n)}catch{}return{...c(`Wrote ${s.byteLength} bytes.`),metadata:{fileChange:{path:n.path,additions:n.additions,deletions:n.deletions}}}}async exists(t,e){try{return await u(t.path,e),c("true")}catch(r){if(m(r))return c("false");throw r}}}async function x(i,t,e){const r=await d.realpath(o.resolve(t.workspaceRoot??t.cwd??process.cwd())),s=o.relative(r,o.resolve(i));return s.startsWith("..")||o.isAbsolute(s)?!1:e.some(a=>{const n=o.normalize(a).replace(/[\\/]$/u,"");return n==="."?!0:s===n||s.startsWith(`${n}${o.sep}`)})}async function I(i){try{const t=await y(i,T);return{content:t.bytes.toString("utf8"),truncated:t.truncated}}catch(t){return m(t)?{content:"",truncated:!1}:{content:"",truncated:!0}}}function p(i,t,e,r){return{type:"function",function:{name:i,description:t,parameters:{type:"object",properties:e,required:r,additionalProperties:!1}}}}function W(i){return"function"in i?i.function.name:void 0}function c(i){return{content:i,isError:!1}}function l(i){return{content:i,isError:!0}}function _(i,t){return i.byteLength<=t?i.toString("utf8"):`${i.subarray(0,t).toString("utf8")}
3
+ [truncated]`}function R(i,t){return F(i,t)}export{q as LocalFilesystemToolExecutor,E as filesystemToolDefinitions,L as filesystemToolRisk,x as isWithinWriteScope};
@@ -22,5 +22,5 @@ export declare class DailyRotatingFileWriter {
22
22
  flush(): Promise<void>;
23
23
  close(): Promise<void>;
24
24
  private drain;
25
- private prune;
25
+ private pruneAsync;
26
26
  }
@@ -1,2 +1,2 @@
1
- import a from"node:fs";import d from"node:fs/promises";import u from"node:path";import{RESOURCE_LIMITS as l}from"../../domain/runtime/resource-limits.js";class y{directory;options;now;queue=[];queueBytes=0;dropped=0;drainPromise=Promise.resolve();closed=!1;lastPrunedDate;lastError;constructor(t,i){this.directory=t,this.options=i,this.now=i.now??Date.now,a.mkdirSync(t,{recursive:!0,mode:448}),this.lastPrunedDate=o(this.now()),this.prune()||(this.lastPrunedDate="")}get health(){return{dropped:this.dropped,...this.lastError===void 0?{}:{lastError:this.lastError}}}write(t){if(this.closed)return;const i=o(this.now());i!==this.lastPrunedDate&&this.prune()&&(this.lastPrunedDate=i);const r=Buffer.byteLength(t,"utf8");if(this.queue.length>=l.logQueueEntries||this.queueBytes+r>l.logQueueBytes){this.dropped+=1;return}this.queue.push({filePath:u.join(this.directory,`lotagate-${o(this.now())}.jsonl`),line:t}),this.queueBytes+=r,this.drainPromise=this.drainPromise.then(()=>this.drain()).catch(e=>{this.lastError=e instanceof Error?e:new Error(String(e))})}async flush(){await this.drainPromise,this.queue.length>0&&(this.drainPromise=this.drainPromise.then(()=>this.drain()).catch(t=>{this.lastError=t instanceof Error?t:new Error(String(t))}),await this.drainPromise)}async close(){this.closed=!0,await this.flush()}async drain(){if(this.queue.length===0)return;const t=this.queue.splice(0,this.queue.length);this.queueBytes=0;const i=this.dropped;i>0&&t.push({filePath:u.join(this.directory,`lotagate-${o(this.now())}.jsonl`),line:`${JSON.stringify({timestamp:new Date(this.now()).toISOString(),level:"warn",message:"Log entries dropped because the file writer queue was full.",dropped:i})}
2
- `});const r=new Map;for(const e of t)r.set(e.filePath,[...r.get(e.filePath)??[],e.line]);try{for(const[e,s]of r)await d.appendFile(e,s.join(""),{encoding:"utf8",mode:384});this.dropped=0,this.lastError=void 0}catch(e){const s=[...t.filter(n=>!n.line.includes("Log entries dropped because the file writer queue was full.")),...this.queue];throw this.queue=s,this.queueBytes=s.reduce((n,c)=>n+Buffer.byteLength(c.line,"utf8"),0),this.lastError=e instanceof Error?e:new Error(String(e)),e}}prune(){const t=this.now()-this.options.retentionDays*24*60*60*1e3;let i=!0;try{for(const r of a.readdirSync(this.directory,{withFileTypes:!0})){if(!r.isFile())continue;const e=/^lotagate-(\d{4}-\d{2}-\d{2})\.jsonl$/u.exec(r.name);if(e===null||e[1]===void 0)continue;const s=Date.parse(`${e[1]}T00:00:00.000Z`);if(Number.isFinite(s)&&s<t)try{a.rmSync(u.join(this.directory,r.name),{force:!0})}catch(n){i=!1,this.lastError=n instanceof Error?n:new Error(String(n))}}}catch(r){i=!1,this.lastError=r instanceof Error?r:new Error(String(r))}return i}}function o(h){return new Date(h).toISOString().slice(0,10)}export{y as DailyRotatingFileWriter};
1
+ import f from"node:fs";import a from"node:fs/promises";import u from"node:path";import{RESOURCE_LIMITS as c}from"../../domain/runtime/resource-limits.js";class g{directory;options;now;queue=[];queueBytes=0;dropped=0;drainPromise=Promise.resolve();closed=!1;lastPrunedDate;lastError;constructor(t,r){this.directory=t,this.options=r,this.now=r.now??Date.now;try{f.mkdirSync(t,{recursive:!0,mode:448})}catch{}this.lastPrunedDate=""}get health(){return{dropped:this.dropped,...this.lastError===void 0?{}:{lastError:this.lastError}}}write(t){if(this.closed)return;const r=Buffer.byteLength(t,"utf8");if(this.queue.length>=c.logQueueEntries||this.queueBytes+r>c.logQueueBytes){this.dropped+=1;return}this.queue.push({filePath:u.join(this.directory,`lotagate-${h(this.now())}.jsonl`),line:t}),this.queueBytes+=r,this.drainPromise=this.drainPromise.then(()=>this.drain()).catch(i=>{this.lastError=i instanceof Error?i:new Error(String(i))})}async flush(){await this.drainPromise,this.queue.length>0&&(this.drainPromise=this.drainPromise.then(()=>this.drain()).catch(t=>{this.lastError=t instanceof Error?t:new Error(String(t))}),await this.drainPromise)}async close(){this.closed=!0,await this.flush()}async drain(){const t=h(this.now());if(t!==this.lastPrunedDate&&await this.pruneAsync()&&(this.lastPrunedDate=t),this.queue.length===0)return;const r=this.queue.splice(0,this.queue.length);this.queueBytes=0;const i=this.dropped;i>0&&r.push({filePath:u.join(this.directory,`lotagate-${h(this.now())}.jsonl`),line:`${JSON.stringify({timestamp:new Date(this.now()).toISOString(),level:"warn",message:"Log entries dropped because the file writer queue was full.",dropped:i})}
2
+ `});const s=new Map;for(const e of r)s.set(e.filePath,[...s.get(e.filePath)??[],e.line]);try{for(const[e,n]of s)await a.appendFile(e,n.join(""),{encoding:"utf8",mode:384});this.dropped=0,this.lastError=void 0}catch(e){const n=[...r.filter(o=>!o.line.includes("Log entries dropped because the file writer queue was full.")),...this.queue];throw this.queue=n,this.queueBytes=n.reduce((o,d)=>o+Buffer.byteLength(d.line,"utf8"),0),this.lastError=e instanceof Error?e:new Error(String(e)),e}}async pruneAsync(){const t=this.now()-this.options.retentionDays*24*60*60*1e3;let r=!0;try{const i=await a.readdir(this.directory,{withFileTypes:!0});for(const s of i){if(!s.isFile())continue;const e=/^lotagate-(\d{4}-\d{2}-\d{2})\.jsonl$/u.exec(s.name);if(e===null||e[1]===void 0)continue;const n=Date.parse(`${e[1]}T00:00:00.000Z`);if(Number.isFinite(n)&&n<t)try{await a.rm(u.join(this.directory,s.name),{force:!0})}catch(o){r=!1,this.lastError=o instanceof Error?o:new Error(String(o))}}}catch(i){r=!1,this.lastError=i instanceof Error?i:new Error(String(i))}return r}}function h(l){return new Date(l).toISOString().slice(0,10)}export{g as DailyRotatingFileWriter};
@@ -1 +1 @@
1
- const s=/(api.?key|authorization|credential|password|private.?key|secret|token)/iu;function i(r){if(r!==void 0)return t(r)}function t(r){return Object.fromEntries(Object.entries(r).map(([e,o])=>[e,s.test(e)?"[REDACTED]":c(o)]))}function c(r){return typeof r=="string"?n(r):Array.isArray(r)?r.map(e=>e!==null&&typeof e=="object"?t(e):typeof e=="string"?n(e):e):r!==null&&typeof r=="object"?t(r):r}function a(r){return n(r)}function n(r){return r.replace(/Bearer\s+[^\s]+/giu,"Bearer [REDACTED]").replace(/sk-[A-Za-z0-9_-]{8,}/gu,"[REDACTED]").replace(/((?:api[_-]?key|password|secret|token|authorization)\s*[:=]\s*)[^\s,]+/giu,"$1[REDACTED]").replace(/([?&][^=&#\s]*(?:key|token|secret|password|auth)[^=&#\s]*=)[^&#\s]+/giu,"$1[REDACTED]")}export{i as redactLogDetails,a as redactLogText};
1
+ import{SENSITIVE_KEY as f}from"./logging-constants.js";const E=32,u=4.2,a=/[A-Za-z0-9+/_=-]{32,}/gu;function d(t){if(t!==void 0)return s(t)}function s(t){return Object.fromEntries(Object.entries(t).map(([r,e])=>[r,f.test(r)?"[REDACTED]":p(e)]))}function p(t){return typeof t=="string"?o(t):Array.isArray(t)?t.map(r=>r!==null&&typeof r=="object"?s(r):typeof r=="string"?o(r):r):t!==null&&typeof t=="object"?s(t):t}function A(t){return o(t)}function o(t){return t.replace(/Bearer\s+[^\s]+/giu,"Bearer [REDACTED]").replace(/sk-[A-Za-z0-9_-]{8,}/gu,"[REDACTED]").replace(/((?:api[_-]?key|password|secret|token|authorization)\s*[:=]\s*)[^\s,]+/giu,"$1[REDACTED]").replace(/([?&][^=&#\s]*(?:key|token|secret|password|auth)[^=&#\s]*=)[^&#\s]+/giu,"$1[REDACTED]").replace(a,e=>T(e)?"[REDACTED]":e)}function T(t){return t.length<E||/\s/u.test(t)?!1:/^[0-9a-f]+$/iu.test(t)?i(t)>=3.5:i(t)>=u&&D(t)}function D(t){let r=0;return/[a-z]/u.test(t)&&(r+=1),/[A-Z]/u.test(t)&&(r+=1),/[0-9]/u.test(t)&&(r+=1),/[+/_=-]/u.test(t)&&(r+=1),r>=2}function i(t){const r=new Map;for(const n of t)r.set(n,(r.get(n)??0)+1);let e=0;for(const n of r.values()){const c=n/t.length;e-=c*Math.log2(c)}return e}export{d as redactLogDetails,A as redactLogText};
@@ -0,0 +1,3 @@
1
+ import type { LogLevel } from '../../ports/logger.js';
2
+ export declare const SENSITIVE_KEY: RegExp;
3
+ export declare const LEVEL_PRIORITY: Record<LogLevel, number>;
@@ -0,0 +1 @@
1
+ const e=/(api.?key|authorization|credential|password|private.?key|secret|token)/iu,r={error:0,warn:1,info:2,debug:3,trace:4};export{r as LEVEL_PRIORITY,e as SENSITIVE_KEY};
@@ -3,7 +3,7 @@ import type { Writable } from 'node:stream';
3
3
  import type { DailyRotatingFileWriter } from './daily-rotating-file-writer.js';
4
4
  export interface StructuredLoggerOptions {
5
5
  level?: LogLevel;
6
- consoleLevel?: LogLevel;
6
+ consoleLevel?: LogLevel | 'none';
7
7
  json?: boolean;
8
8
  output?: Writable;
9
9
  file?: DailyRotatingFileWriter;
@@ -1,4 +1,4 @@
1
- import{redactLogDetails as u,redactLogText as c}from"./log-redactor.js";const r={error:0,warn:1,info:2,debug:3,trace:4};class f{minimumPriority;consoleMinimumPriority;output;json;file;constructor(i={}){this.minimumPriority=r[i.level??"info"],this.consoleMinimumPriority=r[i.consoleLevel??i.level??"info"],this.output=i.output??process.stderr,this.json=i.json??!1,this.file=i.file}error(i,t){this.write("error",i,t)}warn(i,t){this.write("warn",i,t)}info(i,t){this.write("info",i,t)}debug(i,t){this.write("debug",i,t)}trace(i,t){this.write("trace",i,t)}write(i,t,o){if(r[i]>this.minimumPriority)return;const n=u(o),e=c(t),s={timestamp:new Date().toISOString(),level:i,message:e,...n??{}};r[i]<=this.consoleMinimumPriority&&this.output.write(this.json?`${JSON.stringify(s)}
1
+ import{redactLogDetails as m,redactLogText as u}from"./log-redactor.js";import{LEVEL_PRIORITY as r}from"./logging-constants.js";class h{minimumPriority;consoleMinimumPriority;output;json;file;constructor(i={}){this.minimumPriority=r[i.level??"info"],this.consoleMinimumPriority=i.consoleLevel==="none"?-1:r[i.consoleLevel??i.level??"info"],this.output=i.output??process.stderr,this.json=i.json??!1,this.file=i.file}error(i,t){this.write("error",i,t)}warn(i,t){this.write("warn",i,t)}info(i,t){this.write("info",i,t)}debug(i,t){this.write("debug",i,t)}trace(i,t){this.write("trace",i,t)}write(i,t,o){if(r[i]>this.minimumPriority)return;const n=m(o),e=u(t),s={timestamp:new Date().toISOString(),level:i,message:e,...n??{}};r[i]<=this.consoleMinimumPriority&&this.output.write(this.json?`${JSON.stringify(s)}
2
2
  `:`[${i}] ${e}
3
3
  `);try{this.file?.write(`${JSON.stringify(s)}
4
- `)}catch{}}}export{f as StructuredLogger};
4
+ `)}catch{}}}export{h as StructuredLogger};
@@ -0,0 +1,11 @@
1
+ import type { ToolExecutionContext, ToolResult } from '@lotagate/agent-sdk';
2
+ import type { Logger } from '../../ports/logger.js';
3
+ interface ToolExecutionLogOptions {
4
+ toolName: string;
5
+ source: string;
6
+ context: ToolExecutionContext;
7
+ details?: Record<string, unknown>;
8
+ }
9
+ /** Adds consistent lifecycle logs without recording tool payloads or result content. */
10
+ export declare function withToolExecutionLogging<T extends ToolResult>(logger: Logger | undefined, options: ToolExecutionLogOptions, action: () => Promise<T>): Promise<T>;
11
+ export {};
@@ -0,0 +1 @@
1
+ async function u(t,e,s){const r=Date.now(),n={toolName:e.toolName,source:e.source,sessionId:e.context.sessionId,runId:e.context.runId,iteration:e.context.iteration,...e.details??{}};t?.info("tool.execution.started",n);try{const o=await s(),i={...n,durationMs:Date.now()-r,outcome:o.isError?"error":"success",...c(o)};return o.isError?t?.warn("tool.execution.failed",i):t?.info("tool.execution.completed",i),o}catch(o){const i=a(o);throw t?.error("tool.execution.failed",{...n,durationMs:Date.now()-r,outcome:"exception",error:o instanceof Error?o.message:String(o),...i===void 0?{}:{errorCode:i}}),o}}function c(t){const e=t.metadata;if(e===void 0)return{};const s={};for(const r of["errorCode","exitCode","timedOut"]){const n=e[r];(typeof n=="string"||typeof n=="number"||typeof n=="boolean")&&(s[r]=n)}return s}function a(t){if(t===null||typeof t!="object"||!("code"in t))return;const e=t.code;return typeof e=="string"&&e.length>0?e:void 0}export{u as withToolExecutionLogging};
@@ -12,3 +12,5 @@ export interface HostPlatformInput {
12
12
  env?: Readonly<Record<string, string | undefined>>;
13
13
  }
14
14
  export declare function detectHostPlatform(input?: HostPlatformInput): HostPlatformContext;
15
+ /** Describes the POSIX userspace contract used by Desktop's isolated guest boundary. */
16
+ export declare function createIsolatedGuestPlatform(): HostPlatformContext;
@@ -1 +1 @@
1
- function i(n={}){const e=n.nodePlatform??process.platform,s=o(e),r=s==="windows";return{id:s,nodePlatform:e,isWindows:r,supportsProcessGroups:!r,env:n.env??process.env}}function o(n){return n==="win32"||n==="cygwin"?"windows":n==="linux"||n==="android"?"linux":n==="darwin"?"macos":n==="freebsd"||n==="openbsd"||n==="netbsd"?"bsd":"unsupported"}export{i as detectHostPlatform};
1
+ function s(n={}){const e=n.nodePlatform??process.platform,r=t(e),o=r==="windows";return{id:r,nodePlatform:e,isWindows:o,supportsProcessGroups:!o,env:n.env??process.env}}function i(){return s({nodePlatform:"linux",env:{}})}function t(n){return n==="win32"||n==="cygwin"?"windows":n==="linux"||n==="android"?"linux":n==="darwin"?"macos":n==="freebsd"||n==="openbsd"||n==="netbsd"?"bsd":"unsupported"}export{i as createIsolatedGuestPlatform,s as detectHostPlatform};
@@ -7,7 +7,9 @@ export interface PreparedPlatformCommand {
7
7
  windowsVerbatimArguments: boolean;
8
8
  }
9
9
  /** Normalizes the structured shell contract for a complete PowerShell script. */
10
- export declare function normalizePowerShellScriptInput(input: Record<string, unknown>, platform: HostPlatformContext): Record<string, unknown>;
10
+ export declare function normalizePowerShellScriptInput(input: Record<string, unknown>, platform: HostPlatformContext, options?: {
11
+ readonly allowNonWindows?: boolean;
12
+ }): Record<string, unknown>;
11
13
  export declare function preparePowerShellScript(command: string, script: string): PreparedPlatformCommand;
12
14
  export declare function isPowerShellExecutable(command: string): boolean;
13
15
  export interface PlatformExecOptions {
@@ -1 +1 @@
1
- import T from"node:path";import{spawn as b}from"node:child_process";import{RESOURCE_LIMITS as L}from"../../domain/runtime/resource-limits.js";import{terminatePlatformProcess as M}from"./process-termination.js";import{terminatePlatformProcess as z}from"./process-termination.js";const _=2*1024*1024,h=/([()\][%!^"`<>&|;, *?])/gu,$=/\.(?:com|exe)$/iu,v=/node_modules[\\/]\.bin[\\/][^\\/]+\.cmd$/iu;function F(e,n){const r=e.script;if(r===void 0)return e;if(typeof r!="string"||r.trim().length===0)throw new Error("shell.exec script must be a non-empty string.");if(Buffer.byteLength(r,"utf8")>L.sessionTextBytes)throw new Error("shell.exec script exceeds the configured limit.");const t=e.command;if(typeof t!="string"||!I(t))throw new Error("shell.exec script requires powershell.exe or pwsh.exe as the command.");const o=e.args;if(o!==void 0&&(!Array.isArray(o)||o.length>0||o.some(i=>typeof i!="string")))throw new Error("shell.exec script cannot be combined with args.");const a=y(t,r),u={...e};return delete u.script,{...u,command:a.command,args:[...a.args]}}function y(e,n){return{command:e,args:["-NoProfile","-NonInteractive","-Command",n],windowsVerbatimArguments:!1}}function I(e){const n=e.replace(/^.*[\\/]/u,"").toLowerCase();return n==="powershell"||n==="powershell.exe"||n==="pwsh"||n==="pwsh.exe"}function D(e,n,r,t={}){const o=O(e,n,r);return b(o.command,[...o.args],{...t,shell:!1,windowsHide:!0,...o.windowsVerbatimArguments?{windowsVerbatimArguments:!0}:{}})}function G(e,n,r,t={}){if(t.signal?.aborted)return Promise.reject(new Error("Process cancelled."));const o=t.maxBufferBytes??_,a=t.timeoutMs??L.internalProcessTimeoutMs;return new Promise((u,i)=>{const d=D(e,n,r,{...t.cwd===void 0?{}:{cwd:t.cwd},env:t.env??B(r),detached:r.supportsProcessGroups,stdio:["ignore","pipe","pipe"]}),A=[],p=[];let w=0,c=!1,l=!1,x=!1,S=!1,g;const m=()=>{g!==void 0&&clearTimeout(g),t.signal?.removeEventListener("abort",P)},P=()=>{S=!0,E(new Error("Process cancelled."))},E=s=>{l||c||(l=!0,M(d,r).then(()=>{c||(c=!0,m(),i(s))},f=>{c||(c=!0,m(),i(new AggregateError([s,f],`${s.message} Termination could not be confirmed.`)))}))},C=(s,f)=>{if(!(c||l)){if(w+=f.byteLength,w>o){E(new Error("Process output exceeded the configured limit."));return}s.push(f)}};d.stdout?.on("data",s=>C(A,s)),d.stderr?.on("data",s=>C(p,s)),d.once("error",s=>{c||l||(c=!0,m(),i(s))}),d.once("close",s=>{c||(c=!0,m(),S?i(new Error("Process cancelled.")):x?i(new Error(`Process timed out after ${a}ms.`)):w>o?i(new Error("Process output exceeded the configured limit.")):s===0||t.rejectOnNonZero===!1?u({stdout:Buffer.concat(A).toString("utf8"),stderr:Buffer.concat(p).toString("utf8"),exitCode:s}):i(new Error(Buffer.concat(p).toString("utf8").trim()||`Process exited with code ${String(s)}.`)))}),g=setTimeout(()=>{x=!0,E(new Error(`Process timed out after ${a}ms.`))},a),t.signal?.aborted===!0?P():t.signal?.addEventListener("abort",P,{once:!0})})}function B(e){const n=e.isWindows?["PATHEXT","SystemRoot","ComSpec","COMSPEC","TEMP","TMP","HOME","USERPROFILE","APPDATA","LOCALAPPDATA","LANG","LC_ALL"]:["PATH","HOME","TMPDIR","TEMP","LANG","LC_ALL","XDG_RUNTIME_DIR","WAYLAND_DISPLAY","DISPLAY"],r={};if(e.isWindows){const t=e.env.Path??e.env.PATH;t!==void 0&&(r.Path=t)}for(const t of n){const o=e.env[t];o!==void 0&&(r[t]=o)}return r}function O(e,n,r){if(!r.isWindows||$.test(e))return{command:e,args:n,windowsVerbatimArguments:!1};const t=T.win32.normalize(e),o=v.test(t),a=W(t),u=n.map(i=>R(i,o));return{command:r.env.ComSpec??r.env.COMSPEC??"cmd.exe",args:["/d","/s","/c",`"${[a,...u].join(" ")}"`],windowsVerbatimArguments:!0}}function W(e){return e.replace(h,"^$1")}function R(e,n){let r=e.replace(/(?=(\\+?)?)\1"/gu,'$1$1\\"').replace(/(?=(\\+?)?)\1$/gu,"$1$1");return r=`"${r}"`.replace(h,"^$1"),n?r.replace(h,"^$1"):r}export{B as createSafeChildEnvironment,G as execPlatformCommand,I as isPowerShellExecutable,F as normalizePowerShellScriptInput,O as preparePlatformCommand,y as preparePowerShellScript,D as spawnPlatformProcess,z as terminatePlatformProcess};
1
+ import $ from"node:path";import{spawn as B}from"node:child_process";import{RESOURCE_LIMITS as A}from"../../domain/runtime/resource-limits.js";import{terminatePlatformProcess as T}from"./process-termination.js";import{terminatePlatformProcess as Y}from"./process-termination.js";import{DEFAULT_EXEC_BUFFER_BYTES as v,WINDOWS_CMD_SHIM as y,WINDOWS_DIRECT_EXECUTABLE as W,WINDOWS_META_CHARS as P}from"./platform-constants.js";function F(e,r,t={}){const n=e.script;if(n===void 0)return e;if(typeof n!="string"||n.trim().length===0)throw new Error("shell.exec script must be a non-empty string.");if(Buffer.byteLength(n,"utf8")>A.sessionTextBytes)throw new Error("shell.exec script exceeds the configured limit.");if(!r.isWindows&&t.allowNonWindows!==!0)throw new Error("shell.exec PowerShell scripts are unavailable in the isolated guest sandbox; use the supported host command with structured args.");const o=e.command;if(typeof o!="string"||!I(o))throw new Error("shell.exec script requires powershell.exe or pwsh.exe as the command.");const c=e.args;if(c!==void 0&&(!Array.isArray(c)||c.length>0||c.some(u=>typeof u!="string")))throw new Error("shell.exec script cannot be combined with args.");const d=_(o,n),i={...e};return delete i.script,{...i,command:d.command,args:[...d.args]}}function _(e,r){return{command:e,args:["-NoProfile","-NonInteractive","-Command",r],windowsVerbatimArguments:!1}}function I(e){const r=e.replace(/^.*[\\/]/u,"").toLowerCase();return r==="powershell"||r==="powershell.exe"||r==="pwsh"||r==="pwsh.exe"}function O(e,r,t,n={}){const o=M(e,r,t);return B(o.command,[...o.args],{...n,shell:!1,windowsHide:!0,...o.windowsVerbatimArguments?{windowsVerbatimArguments:!0}:{}})}function X(e,r,t,n={}){if(n.signal?.aborted)return Promise.reject(new Error("Process cancelled."));const o=n.maxBufferBytes??v,c=n.timeoutMs??A.internalProcessTimeoutMs;return new Promise((d,i)=>{const u=O(e,r,t,{...n.cwd===void 0?{}:{cwd:n.cwd},env:n.env??L(t),detached:t.supportsProcessGroups,stdio:["ignore","pipe","pipe"]}),x=[],p=[];let w=0,a=!1,l=!1,b=!1,S=!1,h;const m=()=>{h!==void 0&&clearTimeout(h),n.signal?.removeEventListener("abort",g)},g=()=>{S=!0,E(new Error("Process cancelled."))},E=s=>{l||a||(l=!0,T(u,t).then(()=>{a||(a=!0,m(),i(s))},f=>{a||(a=!0,m(),i(new AggregateError([s,f],`${s.message} Termination could not be confirmed.`)))}))},C=(s,f)=>{if(!(a||l)){if(w+=f.byteLength,w>o){E(new Error("Process output exceeded the configured limit."));return}s.push(f)}};u.stdout?.on("data",s=>C(x,s)),u.stderr?.on("data",s=>C(p,s)),u.once("error",s=>{a||l||(a=!0,m(),i(s))}),u.once("close",s=>{a||(a=!0,m(),S?i(new Error("Process cancelled.")):b?i(new Error(`Process timed out after ${c}ms.`)):w>o?i(new Error("Process output exceeded the configured limit.")):s===0||n.rejectOnNonZero===!1?d({stdout:Buffer.concat(x).toString("utf8"),stderr:Buffer.concat(p).toString("utf8"),exitCode:s}):i(new Error(Buffer.concat(p).toString("utf8").trim()||`Process exited with code ${String(s)}.`)))}),h=setTimeout(()=>{b=!0,E(new Error(`Process timed out after ${c}ms.`))},c),n.signal?.aborted===!0?g():n.signal?.addEventListener("abort",g,{once:!0})})}function L(e){const r=Object.fromEntries(Object.entries(e.env).filter(t=>t[1]!==void 0));if(e.isWindows){const t=r.Path??r.PATH;t!==void 0&&(delete r.Path,delete r.PATH,r.Path=t)}return r}function M(e,r,t){if(!t.isWindows||W.test(e))return{command:e,args:r,windowsVerbatimArguments:!1};const n=$.win32.normalize(e),o=y.test(n),c=N(n),d=r.map(i=>D(i,o));return{command:t.env.ComSpec??t.env.COMSPEC??"cmd.exe",args:["/d","/s","/c",`"${[c,...d].join(" ")}"`],windowsVerbatimArguments:!0}}function N(e){return e.replace(P,"^$1")}function D(e,r){let t=e.replace(/(?=(\\+?)?)\1"/gu,'$1$1\\"').replace(/(?=(\\+?)?)\1$/gu,"$1$1");return t=`"${t}"`.replace(P,"^$1"),r?t.replace(P,"^$1"):t}export{L as createSafeChildEnvironment,X as execPlatformCommand,I as isPowerShellExecutable,F as normalizePowerShellScriptInput,M as preparePlatformCommand,_ as preparePowerShellScript,O as spawnPlatformProcess,Y as terminatePlatformProcess};
@@ -0,0 +1,4 @@
1
+ export declare const DEFAULT_EXEC_BUFFER_BYTES: number;
2
+ export declare const WINDOWS_META_CHARS: RegExp;
3
+ export declare const WINDOWS_DIRECT_EXECUTABLE: RegExp;
4
+ export declare const WINDOWS_CMD_SHIM: RegExp;
@@ -0,0 +1 @@
1
+ const o=2097152,E=/([()\][%!^"`<>&|;, *?])/gu,_=/\.(?:com|exe)$/iu,e=/node_modules[\\/]\.bin[\\/][^\\/]+\.cmd$/iu;export{o as DEFAULT_EXEC_BUFFER_BYTES,e as WINDOWS_CMD_SHIM,_ as WINDOWS_DIRECT_EXECUTABLE,E as WINDOWS_META_CHARS};
@@ -0,0 +1,8 @@
1
+ /** Returns a stable-enough epoch timestamp for the current Node process. */
2
+ export declare function currentProcessStartTimeMs(): number;
3
+ /**
4
+ * Reads another process' creation time where the host exposes a safe native
5
+ * query. Unknown platforms deliberately return undefined so lock recovery can
6
+ * fail closed and retain a lock when liveness cannot be verified.
7
+ */
8
+ export declare function readProcessStartTimeMs(pid: number): Promise<number | undefined>;