@lotagate/cli 0.1.25 → 0.1.26

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (231) 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/capability-profile.d.ts +0 -38
  10. package/dist/application/agent/capability-profile.js +0 -1
  11. package/dist/application/agent/cli-agent-execution-service.d.ts +22 -12
  12. package/dist/application/agent/cli-agent-execution-service.js +1 -1
  13. package/dist/application/agent/cli-agent-preparation-cache.d.ts +39 -0
  14. package/dist/application/agent/cli-agent-preparation-cache.js +1 -0
  15. package/dist/application/agent/context-compaction.d.ts +1 -0
  16. package/dist/application/agent/context-compaction.js +7 -6
  17. package/dist/application/agent/context-token-cache.d.ts +21 -0
  18. package/dist/application/agent/context-token-cache.js +4 -0
  19. package/dist/application/agent/context-usage-service.d.ts +52 -0
  20. package/dist/application/agent/context-usage-service.js +1 -0
  21. package/dist/application/agent/continuation-checkpoint.d.ts +15 -0
  22. package/dist/application/agent/continuation-checkpoint.js +2 -0
  23. package/dist/application/agent/interactive-agent-guidance.d.ts +3 -3
  24. package/dist/application/agent/interactive-agent-guidance.js +2 -2
  25. package/dist/application/chat/pending-attachment-store.d.ts +5 -2
  26. package/dist/application/chat/pending-attachment-store.js +1 -1
  27. package/dist/application/commands/application-command-executor.js +1 -1
  28. package/dist/application/commands/command-catalog.js +1 -1
  29. package/dist/application/commands/goal-command-handlers.js +6 -6
  30. package/dist/application/commands/media-command-handlers.js +7 -6
  31. package/dist/application/commands/runtime-command-handlers.js +9 -9
  32. package/dist/application/intent/completion-gate.d.ts +2 -5
  33. package/dist/application/intent/completion-gate.js +1 -1
  34. package/dist/application/intent/evidence-ledger.d.ts +2 -1
  35. package/dist/application/intent/evidence-recording-tool-executor.js +1 -1
  36. package/dist/application/intent/intent-compiler.js +2 -2
  37. package/dist/application/memory/memory-extractor.js +2 -2
  38. package/dist/application/model/cached-model-catalog.d.ts +7 -1
  39. package/dist/application/model/cached-model-catalog.js +1 -1
  40. package/dist/application/model/model-catalog-service.js +1 -1
  41. package/dist/application/model/model-context-window.js +1 -1
  42. package/dist/application/model/model-response-retry.d.ts +7 -1
  43. package/dist/application/model/model-response-retry.js +1 -1
  44. package/dist/application/orchestration/cli-subagent-service.d.ts +4 -0
  45. package/dist/application/orchestration/cli-subagent-service.js +3 -3
  46. package/dist/application/orchestration/cli-turn-event-projector.js +1 -1
  47. package/dist/application/orchestration/cli-turn-events.d.ts +0 -7
  48. package/dist/application/orchestration/goal-input.d.ts +1 -1
  49. package/dist/application/orchestration/goal-input.js +1 -1
  50. package/dist/application/orchestration/headless-agent-turn.d.ts +1 -0
  51. package/dist/application/orchestration/headless-agent-turn.js +3 -3
  52. package/dist/application/orchestration/orchestration-constants.d.ts +3 -0
  53. package/dist/application/orchestration/orchestration-constants.js +1 -0
  54. package/dist/application/orchestration/subagent-handoff.js +3 -3
  55. package/dist/application/project-intelligence/project-intelligence-constants.d.ts +56 -0
  56. package/dist/application/project-intelligence/project-intelligence-constants.js +1 -0
  57. package/dist/application/project-intelligence/project-intelligence-service.js +1 -1
  58. package/dist/application/protocol/browser-host-tool-executor.d.ts +13 -3
  59. package/dist/application/protocol/browser-host-tool-executor.js +2 -2
  60. package/dist/application/protocol/computer-host-tool-executor.d.ts +3 -0
  61. package/dist/application/protocol/computer-host-tool-executor.js +2 -2
  62. package/dist/application/protocol/desktop-agent-command.d.ts +2 -2
  63. package/dist/application/protocol/desktop-agent-command.js +2 -2
  64. package/dist/application/protocol/desktop-agent-contracts.d.ts +34 -0
  65. package/dist/application/protocol/desktop-agent-contracts.js +1 -0
  66. package/dist/application/protocol/desktop-agent-helpers.d.ts +5 -0
  67. package/dist/application/protocol/desktop-agent-helpers.js +1 -0
  68. package/dist/application/protocol/desktop-agent-request-parser.d.ts +31 -6
  69. package/dist/application/protocol/desktop-agent-request-parser.js +1 -1
  70. package/dist/application/protocol/desktop-approval-broker.d.ts +14 -4
  71. package/dist/application/protocol/desktop-approval-broker.js +1 -1
  72. package/dist/application/protocol/desktop-attachment-store.d.ts +19 -2
  73. package/dist/application/protocol/desktop-attachment-store.js +1 -1
  74. package/dist/application/protocol/desktop-execution-policy.d.ts +2 -0
  75. package/dist/application/protocol/desktop-execution-policy.js +1 -1
  76. package/dist/application/protocol/desktop-host-bridge.d.ts +33 -0
  77. package/dist/application/protocol/desktop-host-bridge.js +1 -0
  78. package/dist/application/protocol/desktop-host-capabilities.d.ts +12 -0
  79. package/dist/application/protocol/desktop-host-capabilities.js +1 -0
  80. package/dist/application/protocol/desktop-host-tool-executor.d.ts +15 -2
  81. package/dist/application/protocol/desktop-host-tool-executor.js +2 -2
  82. package/dist/application/protocol/desktop-intent-event-writer.js +1 -1
  83. package/dist/application/protocol/desktop-protocol-helpers.js +1 -1
  84. package/dist/application/protocol/json-line-writer.d.ts +24 -2
  85. package/dist/application/protocol/json-line-writer.js +2 -2
  86. package/dist/application/protocol/jsonl-agent-command.d.ts +1 -1
  87. package/dist/application/protocol/jsonl-agent-command.js +2 -2
  88. package/dist/application/protocol/jsonl-protocol.d.ts +1 -1
  89. package/dist/application/protocol/jsonl-protocol.js +2 -2
  90. package/dist/application/security/desktop-tool-scope-policy.js +1 -1
  91. package/dist/application/security/tool-action-risk-analyzer.js +1 -1
  92. package/dist/application/security/tool-approval-service.d.ts +9 -3
  93. package/dist/application/security/tool-approval-service.js +1 -1
  94. package/dist/application/security/tool-policy-registry.d.ts +20 -0
  95. package/dist/application/security/tool-policy-registry.js +1 -0
  96. package/dist/application/security/tool-risk-classifier.d.ts +2 -1
  97. package/dist/application/security/tool-risk-classifier.js +1 -1
  98. package/dist/application/security/workspace-permission-policy.js +1 -1
  99. package/dist/application/workspace/session-execution-workspace.d.ts +2 -2
  100. package/dist/application/workspace/session-execution-workspace.js +3 -3
  101. package/dist/application/workspace/workspace-constants.d.ts +17 -0
  102. package/dist/application/workspace/workspace-constants.js +1 -0
  103. package/dist/application/workspace/workspace-file-index.d.ts +0 -1
  104. package/dist/application/workspace/workspace-file-index.js +1 -1
  105. package/dist/application/workspace/workspace-instruction-loader.js +1 -1
  106. package/dist/bootstrap/composition-root.d.ts +1 -0
  107. package/dist/bootstrap/runtime-factory.d.ts +5 -0
  108. package/dist/bootstrap/runtime-factory.js +1 -1
  109. package/dist/domain/errors/cli-error.js +1 -1
  110. package/dist/domain/extensions/extension-constants.d.ts +1 -0
  111. package/dist/domain/extensions/extension-constants.js +1 -0
  112. package/dist/domain/extensions/host-tool-plugin-ids.d.ts +0 -4
  113. package/dist/domain/extensions/host-tool-plugin-ids.js +1 -1
  114. package/dist/domain/extensions/plugin-manifest.js +1 -1
  115. package/dist/domain/intent/intent-contract.d.ts +0 -8
  116. package/dist/domain/intent/intent-contract.js +1 -1
  117. package/dist/domain/media/gateway-media-contract.js +1 -1
  118. package/dist/domain/media/media-constants.d.ts +3 -0
  119. package/dist/domain/media/media-constants.js +1 -0
  120. package/dist/domain/media/media-execution-policy.js +1 -1
  121. package/dist/domain/memory/memory-constants.d.ts +3 -0
  122. package/dist/domain/memory/memory-constants.js +1 -0
  123. package/dist/domain/memory/memory-search.js +2 -2
  124. package/dist/domain/orchestration/goal.d.ts +0 -1
  125. package/dist/domain/orchestration/goal.js +1 -1
  126. package/dist/domain/orchestration/work-plan.d.ts +0 -62
  127. package/dist/domain/orchestration/work-plan.js +0 -1
  128. package/dist/domain/runtime/assistant-stream-text.d.ts +11 -0
  129. package/dist/domain/runtime/assistant-stream-text.js +1 -0
  130. package/dist/domain/runtime/gateway-request-budget.d.ts +19 -0
  131. package/dist/domain/runtime/gateway-request-budget.js +1 -0
  132. package/dist/domain/runtime/resource-limits.d.ts +17 -0
  133. package/dist/domain/runtime/resource-limits.js +1 -1
  134. package/dist/domain/session/attachment.d.ts +2 -0
  135. package/dist/domain/workspace/workspace-settings.d.ts +2 -0
  136. package/dist/domain/workspace/workspace-settings.js +1 -1
  137. package/dist/infrastructure/agent-sdk/model-response-metrics.d.ts +7 -0
  138. package/dist/infrastructure/agent-sdk/model-response-metrics.js +1 -0
  139. package/dist/infrastructure/agent-sdk/model-tool-name-mapper.js +1 -1
  140. package/dist/infrastructure/agent-sdk/sdk-constants.d.ts +2 -0
  141. package/dist/infrastructure/agent-sdk/sdk-constants.js +1 -0
  142. package/dist/infrastructure/agent-sdk/tool-calling-model-client.js +1 -1
  143. package/dist/infrastructure/cache/cache-policy.d.ts +2 -0
  144. package/dist/infrastructure/cache/cache-policy.js +1 -1
  145. package/dist/infrastructure/cache/file-cache-store.d.ts +8 -0
  146. package/dist/infrastructure/cache/file-cache-store.js +2 -2
  147. package/dist/infrastructure/clipboard/clipboard-constants.d.ts +1 -0
  148. package/dist/infrastructure/clipboard/clipboard-constants.js +1 -0
  149. package/dist/infrastructure/clipboard/image-normalizer.js +1 -1
  150. package/dist/infrastructure/extensions/extensions-constants.d.ts +6 -0
  151. package/dist/infrastructure/extensions/extensions-constants.js +1 -0
  152. package/dist/infrastructure/extensions/mcp-runtime.d.ts +7 -0
  153. package/dist/infrastructure/extensions/mcp-runtime.js +1 -1
  154. package/dist/infrastructure/extensions/plugin-manager.js +2 -2
  155. package/dist/infrastructure/extensions/skill-tool-executor.d.ts +3 -1
  156. package/dist/infrastructure/extensions/skill-tool-executor.js +2 -2
  157. package/dist/infrastructure/filesystem/atomic-file-store.d.ts +11 -0
  158. package/dist/infrastructure/filesystem/atomic-file-store.js +1 -1
  159. package/dist/infrastructure/filesystem/filesystem-constants.d.ts +8 -0
  160. package/dist/infrastructure/filesystem/filesystem-constants.js +1 -0
  161. package/dist/infrastructure/filesystem/local-filesystem-tool-executor.d.ts +3 -0
  162. package/dist/infrastructure/filesystem/local-filesystem-tool-executor.js +3 -3
  163. package/dist/infrastructure/logging/daily-rotating-file-writer.d.ts +1 -1
  164. package/dist/infrastructure/logging/daily-rotating-file-writer.js +2 -2
  165. package/dist/infrastructure/logging/log-redactor.js +1 -1
  166. package/dist/infrastructure/logging/logging-constants.d.ts +3 -0
  167. package/dist/infrastructure/logging/logging-constants.js +1 -0
  168. package/dist/infrastructure/logging/structured-logger.d.ts +1 -1
  169. package/dist/infrastructure/logging/structured-logger.js +2 -2
  170. package/dist/infrastructure/observability/tool-execution-logger.d.ts +11 -0
  171. package/dist/infrastructure/observability/tool-execution-logger.js +1 -0
  172. package/dist/infrastructure/platform/host-platform.d.ts +2 -0
  173. package/dist/infrastructure/platform/host-platform.js +1 -1
  174. package/dist/infrastructure/platform/host-process.d.ts +3 -1
  175. package/dist/infrastructure/platform/host-process.js +1 -1
  176. package/dist/infrastructure/platform/platform-constants.d.ts +4 -0
  177. package/dist/infrastructure/platform/platform-constants.js +1 -0
  178. package/dist/infrastructure/platform/process-identity.d.ts +8 -0
  179. package/dist/infrastructure/platform/process-identity.js +1 -0
  180. package/dist/infrastructure/platform/read-only-executable.d.ts +2 -1
  181. package/dist/infrastructure/platform/read-only-executable.js +1 -1
  182. package/dist/infrastructure/sdk/lotagate-client-factory.js +1 -1
  183. package/dist/infrastructure/sdk/sdk-media-gateway-client.d.ts +4 -0
  184. package/dist/infrastructure/sdk/sdk-media-gateway-client.js +1 -1
  185. package/dist/infrastructure/sessions/file-session-store.d.ts +3 -0
  186. package/dist/infrastructure/sessions/file-session-store.js +4 -4
  187. package/dist/infrastructure/sessions/session-constants.d.ts +3 -0
  188. package/dist/infrastructure/sessions/session-constants.js +1 -0
  189. package/dist/infrastructure/sessions/session-index.js +3 -3
  190. package/dist/infrastructure/sessions/session-journal-health.d.ts +7 -0
  191. package/dist/infrastructure/sessions/session-journal-health.js +1 -0
  192. package/dist/infrastructure/shell/shell-tool-executor.d.ts +5 -1
  193. package/dist/infrastructure/shell/shell-tool-executor.js +2 -2
  194. package/dist/infrastructure/tokens/tiktoken-token-estimator.js +1 -1
  195. package/dist/infrastructure/tokens/token-constants.d.ts +1 -0
  196. package/dist/infrastructure/tokens/token-constants.js +1 -0
  197. package/dist/infrastructure/workspace/workspace-settings-store.d.ts +1 -0
  198. package/dist/infrastructure/workspace/workspace-settings-store.js +4 -4
  199. package/dist/main.js +3 -3
  200. package/dist/ports/media-gateway-client.d.ts +6 -0
  201. package/dist/ports/session-store.d.ts +1 -0
  202. package/dist/presentation/tui/ink/components/composer.js +4 -4
  203. package/dist/presentation/tui/ink/components/transcript.d.ts +1 -0
  204. package/dist/presentation/tui/ink/components/transcript.js +5 -5
  205. package/dist/presentation/tui/ink/tui-bridge.d.ts +2 -2
  206. package/dist/presentation/tui/ink/tui-bridge.js +1 -1
  207. package/dist/presentation/tui/tui-agent-turn-controller.d.ts +45 -0
  208. package/dist/presentation/tui/tui-agent-turn-controller.js +1 -0
  209. package/dist/presentation/tui/tui-application-contract.d.ts +1 -0
  210. package/dist/presentation/tui/tui-application.d.ts +1 -4
  211. package/dist/presentation/tui/tui-application.js +3 -3
  212. package/dist/presentation/tui/tui-clipboard-paste-handlers.d.ts +2 -2
  213. package/dist/presentation/tui/tui-clipboard-paste-handlers.js +1 -1
  214. package/dist/presentation/tui/tui-command-executor.js +3 -3
  215. package/dist/presentation/tui/tui-context-compaction.d.ts +4 -1
  216. package/dist/presentation/tui/tui-context-compaction.js +1 -1
  217. package/dist/presentation/tui/tui-context-service.d.ts +2 -34
  218. package/dist/presentation/tui/tui-context-service.js +1 -2
  219. package/dist/presentation/tui/tui-initial-model.js +1 -1
  220. package/dist/presentation/tui/tui-tool-approval.d.ts +1 -1
  221. package/dist/presentation/tui/tui-tool-approval.js +1 -1
  222. package/dist/version.d.ts +1 -1
  223. package/dist/version.js +1 -1
  224. package/native-targets.mjs +1 -1
  225. package/package.json +9 -10
  226. package/scripts/install-native.mjs +5 -13
  227. package/bin/lotagate.exe +0 -1
  228. package/dist/application/protocol/document-host-tool-definitions.d.ts +0 -4
  229. package/dist/application/protocol/document-host-tool-definitions.js +0 -1
  230. package/dist/application/protocol/document-host-tool-executor.d.ts +0 -49
  231. package/dist/application/protocol/document-host-tool-executor.js +0 -2
@@ -1,2 +1,2 @@
1
- import m from"node:crypto";import p from"node:fs";import a from"node:fs/promises";import c from"node:path";import{atomicWriteFile as y,withFileLock as w}from"../filesystem/atomic-file-store.js";import{isFileNotFound as u}from"../filesystem/fs-errors.js";import{readJsonFileBounded as l}from"../filesystem/bounded-json-reader.js";class v{root;policy;now;prunePromise;constructor(t,i,r=Date.now){this.root=t,this.policy=i,this.now=r;try{p.mkdirSync(t,{recursive:!0,mode:448})}catch{}}async get(t,i){if(!this.policy.enabled)return;const r=this.filePath(t,i);try{const e=await l(r,this.policy.maxEntryBytes);if(e.schemaVersion!==1||e.namespace!==t||e.keyHash!==f(t,i)||typeof e.expiresAt!="string")return;if(Date.parse(e.expiresAt)<=this.now()){await this.delete(t,i);return}return e.value}catch(e){u(e)||await this.delete(t,i).catch(()=>{});return}}async set(t,i,r,e){if(!this.policy.enabled||e.ttlMs<=0)return;const s={schemaVersion:1,namespace:t,keyHash:f(t,i),createdAt:new Date(this.now()).toISOString(),expiresAt:new Date(this.now()+e.ttlMs).toISOString(),value:r},n=`${JSON.stringify(s)}
2
- `;if(Buffer.byteLength(n,"utf8")>this.policy.maxEntryBytes)return;const h=this.filePath(t,i);await w(h,()=>y(h,n)),this.schedulePrune()}async delete(t,i){await a.rm(this.filePath(t,i),{force:!0})}async clear(t){await a.rm(t===void 0?this.root:this.namespacePath(t),{recursive:!0,force:!0})}async prune(){if(!this.policy.enabled)return;const t=await d(this.root),i=[];for(const e of t)try{const s=await a.stat(e),n=await l(e,this.policy.maxEntryBytes);if(n.schemaVersion!==1||typeof n.expiresAt!="string"||Date.parse(n.expiresAt)<=this.now()){await a.rm(e,{force:!0});continue}i.push({filePath:e,size:s.size,modifiedAt:s.mtimeMs})}catch{await a.rm(e,{force:!0})}let r=i.reduce((e,s)=>e+s.size,0);for(const e of i.sort((s,n)=>s.modifiedAt-n.modifiedAt)){if(r<=this.policy.maxTotalBytes)break;await a.rm(e.filePath,{force:!0}),r-=e.size}}schedulePrune(){this.prunePromise===void 0&&(this.prunePromise=this.prune().catch(()=>{}).finally(()=>{this.prunePromise=void 0}))}namespacePath(t){return c.join(this.root,P(t))}filePath(t,i){return c.join(this.namespacePath(t),`${f(t,i)}.json`)}}function f(o,t){return m.createHash("sha256").update(`${o}\0${t}`).digest("hex")}function P(o){return Buffer.from(o,"utf8").toString("base64url")}async function d(o){try{const t=await a.readdir(o,{withFileTypes:!0}),i=[];for(const r of t){const e=c.join(o,r.name);r.isDirectory()?i.push(...await d(e)):r.isFile()&&r.name.endsWith(".json")&&i.push(e)}return i}catch(t){if(u(t))return[];throw t}}export{v as FileCacheStore};
1
+ import p from"node:crypto";import w from"node:fs";import h from"node:fs/promises";import y from"node:path";import{atomicWriteFile as M,withFileLock as E}from"../filesystem/atomic-file-store.js";import{isFileNotFound as u}from"../filesystem/fs-errors.js";import{readJsonFileBounded as l}from"../filesystem/bounded-json-reader.js";const P=600*1e3;class F{root;policy;now;prunePromise;memoryCache=new Map;memoryBytes=0;lastPruneAt=0;constructor(e,t,r=Date.now){this.root=e,this.policy=t,this.now=r;try{w.mkdirSync(e,{recursive:!0,mode:448})}catch{}}async get(e,t){if(!this.policy.enabled)return;const r=`${e}\0${t}`,i=this.memoryCache.get(r);if(i!==void 0){if(i.expiresAtMs>this.now())return this.touchMemoryEntry(r,i),i.value;this.removeMemoryEntry(r)}const o=this.filePath(e,t);try{const s=await l(o,this.policy.maxEntryBytes);if(s.schemaVersion!==1||s.namespace!==e||s.keyHash!==m(e,t)||typeof s.expiresAt!="string")return;const a=Date.parse(s.expiresAt);if(a<=this.now()){await this.delete(e,t);return}return this.setMemoryEntry(r,a,s.value),s.value}catch(s){u(s)||await this.delete(e,t).catch(()=>{});return}}async set(e,t,r,i){if(!this.policy.enabled||i.ttlMs<=0)return;const o=this.now()+i.ttlMs,s=`${e}\0${t}`,a={schemaVersion:1,namespace:e,keyHash:m(e,t),createdAt:new Date(this.now()).toISOString(),expiresAt:new Date(o).toISOString(),value:r},c=`${JSON.stringify(a)}
2
+ `;if(Buffer.byteLength(c,"utf8")>this.policy.maxEntryBytes)return;const f=this.filePath(e,t);await E(f,()=>M(f,c)),this.setMemoryEntry(s,o,r),this.schedulePrune()}async delete(e,t){this.removeMemoryEntry(`${e}\0${t}`),await h.rm(this.filePath(e,t),{force:!0})}async clear(e){if(e===void 0)this.clearMemory();else{const t=`${e}\0`;for(const r of[...this.memoryCache.keys()])r.startsWith(t)&&this.removeMemoryEntry(r)}await h.rm(e===void 0?this.root:this.namespacePath(e),{recursive:!0,force:!0})}async prune(){if(!this.policy.enabled)return;this.pruneMemory();const e=await d(this.root),t=[];for(const i of e)try{const o=await h.stat(i),s=await l(i,this.policy.maxEntryBytes);if(s.schemaVersion!==1||typeof s.expiresAt!="string"||Date.parse(s.expiresAt)<=this.now()){await h.rm(i,{force:!0});continue}t.push({filePath:i,size:o.size,modifiedAt:o.mtimeMs})}catch{await h.rm(i,{force:!0})}let r=t.reduce((i,o)=>i+o.size,0);for(const i of t.sort((o,s)=>o.modifiedAt-s.modifiedAt)){if(r<=this.policy.maxTotalBytes)break;await h.rm(i.filePath,{force:!0}),r-=i.size}this.pruneMemory()}schedulePrune(){this.prunePromise===void 0&&(this.now()-this.lastPruneAt<P||(this.lastPruneAt=this.now(),this.prunePromise=this.prune().catch(()=>{}).finally(()=>{this.prunePromise=void 0})))}namespacePath(e){return y.join(this.root,x(e))}filePath(e,t){return y.join(this.namespacePath(e),`${m(e,t)}.json`)}setMemoryEntry(e,t,r){const i=A(r);this.removeMemoryEntry(e),!(i>this.policy.maxMemoryBytes)&&(this.memoryCache.set(e,{expiresAtMs:t,value:r,sizeBytes:i}),this.memoryBytes+=i,this.pruneMemory())}removeMemoryEntry(e){const t=this.memoryCache.get(e);t!==void 0&&(this.memoryCache.delete(e),this.memoryBytes-=t.sizeBytes)}touchMemoryEntry(e,t){this.memoryCache.delete(e),this.memoryCache.set(e,t)}clearMemory(){this.memoryCache.clear(),this.memoryBytes=0}pruneMemory(){const e=this.now();for(const[t,r]of[...this.memoryCache])r.expiresAtMs<=e&&this.removeMemoryEntry(t);for(;this.memoryCache.size>this.policy.maxMemoryEntries||this.memoryBytes>this.policy.maxMemoryBytes;){const t=this.memoryCache.keys().next().value;if(typeof t!="string")break;this.removeMemoryEntry(t)}}}function m(n,e){return p.createHash("sha256").update(`${n}\0${e}`).digest("hex")}function x(n){return Buffer.from(n,"utf8").toString("base64url")}function A(n){try{const e=JSON.stringify(n);return Buffer.byteLength(e??"","utf8")}catch{return Number.MAX_SAFE_INTEGER}}async function d(n){try{const e=await h.readdir(n,{withFileTypes:!0}),t=[];for(const r of e){const i=y.join(n,r.name);r.isDirectory()?t.push(...await d(i)):r.isFile()&&r.name.endsWith(".json")&&t.push(i)}return t}catch(e){if(u(e))return[];throw e}}export{F as FileCacheStore};
@@ -0,0 +1 @@
1
+ export declare const IMAGE_MIME_SIGNATURES: Map<string, string>;
@@ -0,0 +1 @@
1
+ const a=new Map([["89504e470d0a1a0a","image/png"],["ffd8ff","image/jpeg"],["47494638","image/gif"],["424d","image/bmp"]]);export{a as IMAGE_MIME_SIGNATURES};
@@ -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};
@@ -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,9 @@ 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 keyLocks;
42
+ private closed;
40
43
  constructor(storeFactory?: (cwd: string) => McpConfigReader, policy?: McpRuntimePolicy, logger?: Logger | undefined, now?: () => number, environment?: Readonly<Record<string, string | undefined>>);
41
44
  ensure(cwd: string, config: CliMcpConfig, trusted: boolean, options?: {
42
45
  onEvent?: McpRuntimeEventListener;
@@ -45,6 +48,10 @@ export declare class McpRuntime {
45
48
  connectConfigured(cwd: string, trusted: boolean): Promise<readonly McpConnectionSummary[]>;
46
49
  invalidate(cwd: string): Promise<void>;
47
50
  close(): Promise<void>;
51
+ private ensureUnlocked;
52
+ private invalidateUnlocked;
53
+ private waitForRetiring;
54
+ private withKeyLock;
48
55
  private scheduleIdleInvalidation;
49
56
  private connectAndDiscover;
50
57
  }
@@ -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 k from"node:crypto";import T from"node:path";import{AgentSdkError as _,AgentSdkErrorCode as D,DEFAULT_MCP_STARTUP_CONCURRENCY as C,mapWithMcpConcurrency as j,McpClientManager as F,McpToolExecutor as G}from"@lotagate/agent-sdk";import{CliError as A}from"../../domain/errors/cli-error.js";import{CLI_ERROR_CODES as P}from"../../domain/errors/error-codes.js";import{FileMcpConfigStore as $}from"./file-mcp-config-store.js";import{readBoundedEnvironmentInteger as h}from"../config/environment-values.js";import{redactLogText as v}from"../logging/log-redactor.js";import{DEFAULT_MCP_CONNECT_TIMEOUT_MS as H,DEFAULT_MCP_DISCONNECT_TIMEOUT_MS as M}from"./extensions-constants.js";function K(t=process.env){return{connectTimeoutMs:h(t.LOTAGATE_MCP_CONNECT_TIMEOUT_MS,H,500,12e4),disconnectTimeoutMs:h(t.LOTAGATE_MCP_DISCONNECT_TIMEOUT_MS,M,500,12e4),idleTtlMs:h(t.LOTAGATE_MCP_RUNTIME_TTL_SECONDS,300,1,86400)*1e3,startupConcurrency:h(t.LOTAGATE_MCP_STARTUP_CONCURRENCY,C,1,32),probeTimeoutMs:h(t.LOTAGATE_MCP_PROBE_TIMEOUT_MS,5e3,500,12e4),probeMaxRetries:h(t.LOTAGATE_MCP_PROBE_MAX_RETRIES,1,0,5),reconnectMaxAttempts:h(t.LOTAGATE_MCP_RECONNECT_MAX_ATTEMPTS,3,0,10),healthCheckIntervalMs:h(t.LOTAGATE_MCP_HEALTH_CHECK_INTERVAL_SECONDS,30,5,3600)*1e3,healthCheckTimeoutMs:h(t.LOTAGATE_MCP_HEALTH_CHECK_TIMEOUT_MS,5e3,500,12e4)}}class re{storeFactory;policy;logger;now;environment;runtimes=new Map;retiring=new Map;keyLocks=new Map;closed=!1;constructor(e=a=>new $(a),n=K(),o,s=Date.now,r=process.env){this.storeFactory=e,this.policy=n,this.logger=o,this.now=s,this.environment=r}async ensure(e,n,o,s={}){if(!o)throw new A(P.PROJECT_TRUST_REQUIRED,"MCP connections require project trust.",{category:"project",userMessage:"Trust the project before connecting to MCP servers."});const r=T.resolve(e);return this.withKeyLock(r,async()=>this.ensureUnlocked(r,n,s))}async connectConfigured(e,n){if(!n)throw new A(P.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(),s=S(I(o,this.environment),this.policy,new Set);try{return await this.connectAndDiscover(T.resolve(e),s.manager,s.executor,new Set)}finally{await R(s.manager,this.policy.disconnectTimeoutMs??M,this.logger,T.resolve(e))}}async invalidate(e){const n=T.resolve(e);await this.withKeyLock(n,()=>this.invalidateUnlocked(n))}async close(){this.closed=!0,await Promise.all([...this.keyLocks.values()]);const e=[...this.runtimes.values()];this.runtimes.clear();for(const n of e)p(n);await Promise.allSettled(e.map(n=>R(n.manager,this.policy.disconnectTimeoutMs??M,this.logger,n.cwd)))}async ensureUnlocked(e,n,o){if(this.closed)throw new Error("MCP runtime is closed.");await this.waitForRetiring(e);const s=B(n),r=this.runtimes.get(e);if(r?.failed&&r.fingerprint===s&&r.leases>0)return p(r),r.lastUsedAt=this.now(),r.leases+=1,E(r,o.onEvent),await r.ready,w(r,this.now,o.onEvent,()=>this.scheduleIdleInvalidation(e,r));if(r!==void 0&&!r.failed&&r.fingerprint===s&&(r.leases>0||this.now()-r.lastUsedAt<=this.policy.idleTtlMs)){p(r);const u=r.leases===0;r.lastUsedAt=this.now(),r.leases+=1,E(r,o.onEvent),await r.ready;const d=w(r,this.now,o.onEvent,()=>this.scheduleIdleInvalidation(e,r));return u&&!r.failed&&z(r.manager)?(d.release(),await this.invalidateUnlocked(e),this.ensureUnlocked(e,n,o)):d}r!==void 0&&await this.invalidateUnlocked(e),await this.waitForRetiring(e);const a=new Set;E(a,o.onEvent);const i=S(I(n,this.environment),this.policy,a,o.reconnect??!0),c={cwd:e,fingerprint:s,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 l=await c.ready;return c.failed=l.some(u=>u.status==="failed"),w(c,this.now,o.onEvent,()=>this.scheduleIdleInvalidation(e,c))}async invalidateUnlocked(e){const n=this.runtimes.get(e);if(n===void 0)return;this.runtimes.delete(e),p(n),n.listeners.clear();const o=O(n.manager,this.policy.disconnectTimeoutMs??M,this.logger,e),s=o.settled.finally(()=>{this.retiring.get(e)===s&&this.retiring.delete(e)});this.retiring.set(e,s),await o.withinTimeout,this.logger?.info("MCP runtime invalidated.",{cwd:e})}async waitForRetiring(e){await this.retiring.get(e)}async withKeyLock(e,n){const o=this.keyLocks.get(e);let s;const r=new Promise(i=>{s=i}),a=(o??Promise.resolve()).then(()=>r);this.keyLocks.set(e,a),await o;try{return await n()}finally{s(),this.keyLocks.get(e)===a&&this.keyLocks.delete(e)}}scheduleIdleInvalidation(e,n){p(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,o,s){const r=performance.now();this.logger?.debug("MCP runtime startup started.",{cwd:e,serverCount:n.configuredServerIds.length,startupConcurrency:this.policy.startupConcurrency??C});const a=await j(n.configuredServerIds,async i=>{const c=performance.now();try{m(s,{type:"mcp.server.status",summary:{serverId:i,status:"connecting"}});const l=await b(n.connect(i),this.policy.connectTimeoutMs,()=>n.disconnect(i),"connection");if(m(s,{type:"mcp.server.status",summary:{...l,latencyMs:f(c)}}),this.logger?.debug("MCP server connected.",{cwd:e,serverId:i,durationMs:f(c),protocolEra:l.protocolEra,protocolVersion:l.protocolVersion}),l.status!=="connected")return l;try{const u=performance.now(),d=await b(n.listTools(i),this.policy.connectTimeoutMs,()=>n.disconnect(i),"tool discovery");o.replaceServerTools(i,d.tools),m(s,{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:f(u),totalDurationMs:f(c)})}catch(u){const d=U(u,i);return m(s,{type:"mcp.server.status",summary:{...l,status:"failed",error:d,latencyMs:f(c)}}),m(s,{type:"mcp.tool.catalog.updated",serverId:i,toolCount:0,error:d}),this.logger?.warn("MCP tool discovery failed.",{cwd:e,serverId:i,durationMs:f(c),reason:d.message,...L(u)}),{...l,status:"failed",error:d}}return l}catch(l){const u=U(l,i);return m(s,{type:"mcp.server.status",summary:{serverId:i,status:"failed",error:u,latencyMs:f(c)}}),this.logger?.warn("MCP server connection failed.",{cwd:e,serverId:i,durationMs:f(c),reason:u.message,...L(l)}),{serverId:i,status:"failed",error:u}}},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:f(r)}),a}}function S(t,e,n,o=!0){let s;const r=new F(t,{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=>{s!==void 0&&(a.tools===null||a.error!==null?s.removeServerTools(a.serverId):s.replaceServerTools(a.serverId,a.tools),m(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"&&s?.removeServerTools(a.serverId)}});return s=new G(r),{manager:r,executor:s}}function O(t,e,n,o){let s,r=!1;const a=Promise.resolve().then(()=>t.disconnectAll()),i=new Promise(u=>{s=setTimeout(()=>{r=!0,u()},e)}),c=a.then(()=>{},u=>{r||n?.warn("MCP runtime disconnect failed.",{cwd:o,reason:u instanceof Error?u.message:String(u)})});return{withinTimeout:Promise.race([c,i]).finally(()=>{s!==void 0&&clearTimeout(s),r&&n?.warn("MCP runtime disconnect timed out.",{cwd:o,timeoutMs:e})}),settled:c}}async function R(t,e,n,o){await O(t,e,n,o).withinTimeout}function w(t,e,n,o){let s=!1;return{executor:t.executor,release:()=>{s||(s=!0,t.leases=Math.max(0,t.leases-1),t.lastUsedAt=e(),J(t,n),t.leases===0&&o?.())}}}function p(t){t.idleTimer!==void 0&&clearTimeout(t.idleTimer),t.idleTimer=void 0}function E(t,e){if(e!==void 0){if(t instanceof Set){t.add(e);return}t.listeners.add(e)}}function J(t,e){e!==void 0&&t.listeners.delete(e)}function m(t,e){for(const n of t)Promise.resolve(n(e)).catch(()=>{})}function z(t){const e=t.connectionSummaries;return e.length>0&&e.some(n=>n.status!=="connected")}function B(t){return k.createHash("sha256").update(y(t)).digest("hex")}function y(t){return Array.isArray(t)?`[${t.map(y).join(",")}]`:typeof t!="object"||t===null?JSON.stringify(t):`{${Object.keys(t).sort().map(e=>`${JSON.stringify(e)}:${y(t[e])}`).join(",")}}`}async function b(t,e,n,o){let s,r=!1;const a=new Promise((i,c)=>{s=setTimeout(()=>{r||(r=!0,c(new Error(`MCP ${o} timed out after ${e}ms.`)),Promise.resolve(n()).catch(()=>{}))},e)});try{return await Promise.race([t.then(c=>(r=!0,c)),a])}finally{s!==void 0&&clearTimeout(s),t.catch(()=>{})}}function f(t){return Math.max(0,Math.round(performance.now()-t))}function U(t,e){return t instanceof _?t:new _(D.McpConnectionFailed,"MCP server connection failed.",{cause:t,details:{serverId:e}})}function L(t){const e=[],n=[],o=[],s=[];let r=t;for(let a=0;a<4&&r!==void 0&&r!==null;a+=1){if(r instanceof Error){e.push(v(r.message)),n.push(r.name),r=r.cause;continue}if(typeof r!="object")break;const i=r,c=i.message;typeof c=="string"&&e.push(v(c));const l=i.name;typeof l=="string"&&n.push(l);const u=i.code;typeof u=="string"&&o.push(u);for(const d of["status","statusCode","httpStatus"]){const g=i[d];typeof g=="number"&&Number.isInteger(g)&&s.push(g)}r=i.cause}return{...e.length===0?{}:{errorMessages:e},...n.length===0?{}:{errorTypes:n},...o.length===0?{}:{errorCodes:o},...s.length===0?{}:{httpStatuses:s}}}function I(t,e=process.env){const n={};for(const[o,s]of Object.entries(t))s.enabled&&(n[o]=V(s.config,e));return n}function V(t,e){if(t.type==="stdio"){const{env:r,...a}=t;return{...a,...t.args===void 0?{}:{args:[...t.args]},...r===void 0?{}:{env:x(r,e)}}}const{headers:n,auth:o,...s}=t;return{...s,...n===void 0?{}:{headers:x(n,e)},...o?.type==="bearer"?{auth:{type:"bearer",token:N(o.token,e,"MCP bearer token")}}:{}}}function x(t,e){return Object.fromEntries(Object.entries(t).map(([n,o])=>[n,N(o,e,`MCP secret ${n}`)]))}function N(t,e,n){if(typeof t=="string")return t;const o=e[t.name];if(o===void 0||o.length===0)throw new Error(`${n} environment variable is not set.`);return o}export{re as McpRuntime,K as resolveMcpRuntimePolicy,I as toAgentMcpConfig};
@@ -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};
@@ -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>;
@@ -0,0 +1 @@
1
+ import{execFile as i}from"node:child_process";import{promisify as n}from"node:util";const s=n(i);function c(){return Math.floor(Date.now()-process.uptime()*1e3)}async function f(e){if(!(!Number.isSafeInteger(e)||e<1)){if(e===process.pid)return c();if(process.platform==="win32")try{const t=`$process = Get-Process -Id ${e} -ErrorAction Stop; ([DateTimeOffset]$process.StartTime.ToUniversalTime()).ToUnixTimeMilliseconds()`,o=await s("powershell.exe",["-NoLogo","-NoProfile","-NonInteractive","-Command",t],{windowsHide:!0,timeout:1e3,maxBuffer:4*1024}),r=Number(o.stdout.trim());return Number.isFinite(r)?r:void 0}catch{return}}}export{c as currentProcessStartTimeMs,f as readProcessStartTimeMs};
@@ -1,3 +1,4 @@
1
+ import type { HostPlatformContext } from './host-platform.js';
1
2
  /** Resolves user-installed host binaries from PATH, never from the workspace. */
2
- export declare function resolveReadOnlyExecutable(command: string, root: string): Promise<string | undefined>;
3
+ export declare function resolveReadOnlyExecutable(command: string, root: string, platform?: HostPlatformContext): Promise<string | undefined>;
3
4
  export declare function readOnlyArguments(command: string, args: readonly string[]): string[];
@@ -1 +1 @@
1
- import f from"node:fs/promises";import{constants as p}from"node:fs";import t from"node:path";async function m(o,e){if(/[\\/:]/u.test(o)||/\.(?:cmd|bat|ps1)$/iu.test(o))return;let n;try{n=await f.realpath(e)}catch{return}const s=process.platform==="win32"&&!/\.(?:exe|com)$/iu.test(o)?[".exe",".com"]:[""];for(const i of(process.env.PATH??process.env.Path??"").split(t.delimiter))if(t.isAbsolute(i))for(const l of s){const u=t.join(i,`${o}${l}`),r=t.relative(n,u);if(!(r===""||!r.startsWith(`..${t.sep}`)&&r!==".."&&!t.isAbsolute(r)))try{const c=await f.realpath(u),a=t.relative(n,c);if(a===""||!a.startsWith(`..${t.sep}`)&&a!==".."&&!t.isAbsolute(a)||!(await f.stat(c)).isFile())continue;return await f.access(c,process.platform==="win32"?p.F_OK:p.X_OK),c}catch{}}}function v(o,e){if(o.toLowerCase().replace(/\.exe$/u,"")!=="git")return[...e];const n=e.find(l=>!l.startsWith("-")),s=["--no-pager","--no-optional-locks","-c","core.fsmonitor=false",...["diff","log","show"].includes(n??"")?["--no-ext-diff","--no-textconv"]:[]],i=e.indexOf("--");return i<0?[...s,...e]:[...s,...e.slice(0,i),...e.slice(i)]}export{v as readOnlyArguments,m as resolveReadOnlyExecutable};
1
+ import l from"node:fs/promises";import{constants as p}from"node:fs";import t from"node:path";async function b(n,e,i){if(/[\\/:]/u.test(n)||/\.(?:cmd|bat|ps1)$/iu.test(n))return;if(i?.isWindows===!1)return n;let o;try{o=await l.realpath(e)}catch{return}const r=process.platform==="win32"&&!/\.(?:exe|com)$/iu.test(n)?[".exe",".com"]:[""];for(const s of(process.env.PATH??process.env.Path??"").split(t.delimiter))if(t.isAbsolute(s))for(const u of r){const d=t.join(s,`${n}${u}`),c=t.relative(o,d);if(!(c===""||!c.startsWith(`..${t.sep}`)&&c!==".."&&!t.isAbsolute(c)))try{const a=await l.realpath(d),f=t.relative(o,a);if(f===""||!f.startsWith(`..${t.sep}`)&&f!==".."&&!t.isAbsolute(f)||!(await l.stat(a)).isFile())continue;return await l.access(a,process.platform==="win32"?p.F_OK:p.X_OK),a}catch{}}}function w(n,e){if(n.toLowerCase().replace(/\.exe$/u,"")!=="git")return[...e];const i=e.findIndex(u=>!u.startsWith("-")),o=i<0?void 0:e[i],r=["--no-pager","--no-optional-locks","-c","core.fsmonitor=false"],s=["diff","log","show"].includes(o??"")?["--no-ext-diff","--no-textconv"]:[];return i<0||s.length===0?[...r,...e]:[...r,...e.slice(0,i+1),...s,...e.slice(i+1)]}export{w as readOnlyArguments,b as resolveReadOnlyExecutable};
@@ -1 +1 @@
1
- import{LotaGate as a}from"@lotagate/sdk";function r(t,e){const o={apiKey:e,baseURL:t.baseUrl};return new a(o)}export{r as createLotaGateClient};
1
+ import{LotaGate as a}from"@lotagate/sdk";function o(e,t){return new a({apiKey:t,baseURL:e.baseUrl})}export{o as createLotaGateClient};
@@ -1,4 +1,5 @@
1
1
  import type { LotaGate } from '@lotagate/sdk';
2
+ import type { AudioSpeechStreamEvent } from '@lotagate/sdk';
2
3
  import type { MediaGatewayClient, AudioUploadInput, ImageEditInput, ImageGenerationInput, MediaRequestOptions, SpeechInput } from '../../ports/media-gateway-client.js';
3
4
  import type { GeneratedImage, MediaBinary, VideoOperation } from '../../domain/media/media-contract.js';
4
5
  /** SDK boundary adapter. Application code only sees CLI-owned media contracts. */
@@ -8,6 +9,9 @@ export declare class SdkMediaGatewayClient implements MediaGatewayClient {
8
9
  generateImage(input: ImageGenerationInput, options?: MediaRequestOptions): Promise<readonly GeneratedImage[]>;
9
10
  editImage(input: ImageEditInput, options?: MediaRequestOptions): Promise<readonly GeneratedImage[]>;
10
11
  createSpeech(input: SpeechInput, options?: MediaRequestOptions): Promise<MediaBinary>;
12
+ createSpeechStream(input: SpeechInput & {
13
+ readonly streamFormat: 'sse';
14
+ }, options?: MediaRequestOptions): AsyncIterable<AudioSpeechStreamEvent>;
11
15
  transcribe(input: AudioUploadInput, options?: MediaRequestOptions): Promise<string>;
12
16
  translate(input: AudioUploadInput, options?: MediaRequestOptions): Promise<string>;
13
17
  createVideo(input: {
@@ -1 +1 @@
1
- class f{client;constructor(r){this.client=r}async generateImage(r,t){return(await this.client.images.generations.create(a(r),t)).data.map(n=>({...typeof n.b64_json=="string"?{base64:n.b64_json}:{},...typeof n.url=="string"?{url:n.url}:{},...typeof n.revised_prompt=="string"?{revisedPrompt:n.revised_prompt}:{}}))}async editImage(r,t){const s={...a(r),image:r.images,...r.mask===void 0?{}:{mask:r.mask},...r.inputFidelity===void 0?{}:{input_fidelity:r.inputFidelity}};return(await this.client.images.edits.create(s,t)).data.map(o=>({...typeof o.b64_json=="string"?{base64:o.b64_json}:{},...typeof o.url=="string"?{url:o.url}:{},...typeof o.revised_prompt=="string"?{revisedPrompt:o.revised_prompt}:{}}))}async createSpeech(r,t){const s=await this.client.audio.speech.create({model:r.model,input:r.input,voice:r.voice,...r.instructions===void 0?{}:{instructions:r.instructions},...r.responseFormat===void 0?{}:{response_format:r.responseFormat},...r.speed===void 0?{}:{speed:r.speed}},t);return d(s)}async transcribe(r,t){return(await this.client.audio.transcriptions.create(m(r),t)).text}async translate(r,t){return(await this.client.audio.translations.create(c(r),t)).text}async createVideo(r,t){const s={model:r.model,instances:[{prompt:r.prompt}],...r.parameters===void 0?{}:{parameters:r.parameters}};return i(await this.client.video.generations.create(s,t))}async getVideoOperation(r,t){return i(await this.client.video.operations.retrieve(r,t))}async downloadVideo(r,t){return d(await this.client.video.operations.download(r,t))}}function a(e){return{model:e.model,prompt:e.prompt,...e.n===void 0?{}:{n:e.n},...e.size===void 0?{}:{size:e.size},...e.quality===void 0?{}:{quality:e.quality},...e.background===void 0?{}:{background:e.background},...e.outputFormat===void 0?{}:{output_format:e.outputFormat},...e.outputCompression===void 0?{}:{output_compression:e.outputCompression},...e.moderation===void 0?{}:{moderation:e.moderation},...e.user===void 0?{}:{user:e.user}}}function m(e){const r={model:e.model,file:e.file,filename:e.filename};return e.prompt!==void 0&&(r.prompt=e.prompt),e.temperature!==void 0&&(r.temperature=e.temperature),e.responseFormat!==void 0&&(r.response_format=e.responseFormat),e.language!==void 0&&(r.language=e.language),e.timestampGranularities!==void 0&&(r.timestamp_granularities=[...e.timestampGranularities]),e.include!==void 0&&(r.include=[...e.include]),r}function c(e){const r={model:e.model,file:e.file,filename:e.filename};return e.prompt!==void 0&&(r.prompt=e.prompt),e.temperature!==void 0&&(r.temperature=e.temperature),e.responseFormat!==void 0&&(r.response_format=e.responseFormat),r}function d(e){const r=e.headers.get("content-type")?.split(";",1)[0]?.trim();return{bytes:new Uint8Array(e.data),...r===void 0||r.length===0?{}:{contentType:r}}}function i(e){return{id:e.name,done:e.done===!0,...typeof e.error?.message=="string"?{errorMessage:e.error.message}:{}}}export{f as SdkMediaGatewayClient};
1
+ class f{client;constructor(e){this.client=e}async generateImage(e,s){return(await this.client.images.generations.create(a(e),s)).data.map(t=>({...typeof t.b64_json=="string"?{base64:t.b64_json}:{},...typeof t.url=="string"?{url:t.url}:{},...typeof t.revised_prompt=="string"?{revisedPrompt:t.revised_prompt}:{}}))}async editImage(e,s){const o={...a(e),image:e.images,...e.mask===void 0?{}:{mask:e.mask},...e.inputFidelity===void 0?{}:{input_fidelity:e.inputFidelity}};return(await this.client.images.edits.create(o,s)).data.map(n=>({...typeof n.b64_json=="string"?{base64:n.b64_json}:{},...typeof n.url=="string"?{url:n.url}:{},...typeof n.revised_prompt=="string"?{revisedPrompt:n.revised_prompt}:{}}))}async createSpeech(e,s){if(e.streamFormat==="sse")throw new Error("Use createSpeechStream for SSE speech responses.");const o=e.streamFormat??"audio",t=await this.client.audio.speech.create({model:e.model,input:e.input,voice:e.voice,...e.instructions===void 0?{}:{instructions:e.instructions},...e.responseFormat===void 0?{}:{response_format:e.responseFormat},...e.speed===void 0?{}:{speed:e.speed},stream_format:o},s);return d(t)}createSpeechStream(e,s){return this.client.audio.speech.create({model:e.model,input:e.input,voice:e.voice,...e.instructions===void 0?{}:{instructions:e.instructions},...e.responseFormat===void 0?{}:{response_format:e.responseFormat},...e.speed===void 0?{}:{speed:e.speed},stream_format:"sse"},s)}async transcribe(e,s){return(await this.client.audio.transcriptions.create(m(e),s)).text}async translate(e,s){return(await this.client.audio.translations.create(c(e),s)).text}async createVideo(e,s){const o={model:e.model,instances:[{prompt:e.prompt}],...e.parameters===void 0?{}:{parameters:e.parameters}};return i(await this.client.video.generations.create(o,s))}async getVideoOperation(e,s){return i(await this.client.video.operations.retrieve(e,s))}async downloadVideo(e,s){return d(await this.client.video.operations.download(e,s))}}function a(r){return{model:r.model,prompt:r.prompt,...r.n===void 0?{}:{n:r.n},...r.size===void 0?{}:{size:r.size},...r.quality===void 0?{}:{quality:r.quality},...r.background===void 0?{}:{background:r.background},...r.outputFormat===void 0?{}:{output_format:r.outputFormat},...r.outputCompression===void 0?{}:{output_compression:r.outputCompression},...r.moderation===void 0?{}:{moderation:r.moderation},...r.user===void 0?{}:{user:r.user}}}function m(r){const e={model:r.model,file:r.file,filename:r.filename};return r.prompt!==void 0&&(e.prompt=r.prompt),r.temperature!==void 0&&(e.temperature=r.temperature),r.responseFormat!==void 0&&(e.response_format=r.responseFormat),r.language!==void 0&&(e.language=r.language),r.timestampGranularities!==void 0&&(e.timestamp_granularities=[...r.timestampGranularities]),r.include!==void 0&&(e.include=[...r.include]),e}function c(r){const e={model:r.model,file:r.file,filename:r.filename};return r.prompt!==void 0&&(e.prompt=r.prompt),r.temperature!==void 0&&(e.temperature=r.temperature),r.responseFormat!==void 0&&(e.response_format=r.responseFormat),e}function d(r){const e=r.headers.get("content-type")?.split(";",1)[0]?.trim();return{bytes:new Uint8Array(r.data),...e===void 0||e.length===0?{}:{contentType:e}}}function i(r){return{id:r.name,done:r.done===!0,...typeof r.error?.message=="string"?{errorMessage:r.error.message}:{}}}export{f as SdkMediaGatewayClient};
@@ -4,6 +4,7 @@ import type { SessionStore, SessionBundle, SessionHistory, SessionJournalAppend,
4
4
  import type { PlatformPaths } from '../platform/platform-paths.js';
5
5
  export declare class FileSessionStore implements SessionStore {
6
6
  private readonly paths;
7
+ private readonly journalHealth;
7
8
  constructor(paths: PlatformPaths);
8
9
  create(input: Omit<SessionMetadata, 'id' | 'createdAt' | 'updatedAt'>): Promise<SessionMetadata>;
9
10
  get(sessionId: string): Promise<SessionMetadata | undefined>;
@@ -28,6 +29,7 @@ export declare class FileSessionStore implements SessionStore {
28
29
  saveAttachment(sessionId: string, attachment: Attachment, bytes: Uint8Array): Promise<Attachment>;
29
30
  private persistAttachment;
30
31
  readAttachment(sessionId: string, attachmentId: string): Promise<Uint8Array | undefined>;
32
+ deleteAttachment(sessionId: string, attachmentId: string): Promise<boolean>;
31
33
  recoverStaleLock(sessionId: string): Promise<boolean>;
32
34
  repair(sessionId: string): Promise<SessionRepairResult>;
33
35
  withLock<T>(sessionId: string, action: () => Promise<T>): Promise<T>;
@@ -42,6 +44,7 @@ export declare class FileSessionStore implements SessionStore {
42
44
  private rebuildProjectIndex;
43
45
  private readSnapshot;
44
46
  private appendJournalLocked;
47
+ private readSessionJournal;
45
48
  private readAttachmentMetadata;
46
49
  private corrupt;
47
50
  }