@open-mercato/ai-assistant 0.5.1-develop.3036.f02c281f23 → 0.5.1-develop.3045.b4b3320cc2

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 (273) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/AGENTS.md +361 -0
  3. package/README.md +5 -0
  4. package/dist/index.js +154 -0
  5. package/dist/index.js.map +2 -2
  6. package/dist/modules/ai_assistant/__integration__/TC-AI-002-agent-policy.spec.js +73 -0
  7. package/dist/modules/ai_assistant/__integration__/TC-AI-002-agent-policy.spec.js.map +7 -0
  8. package/dist/modules/ai_assistant/__integration__/TC-AI-AGENT-SETTINGS-005-settings-page.spec.js +484 -0
  9. package/dist/modules/ai_assistant/__integration__/TC-AI-AGENT-SETTINGS-005-settings-page.spec.js.map +7 -0
  10. package/dist/modules/ai_assistant/__integration__/TC-AI-PLAYGROUND-004-playground.spec.js +251 -0
  11. package/dist/modules/ai_assistant/__integration__/TC-AI-PLAYGROUND-004-playground.spec.js.map +7 -0
  12. package/dist/modules/ai_assistant/__integration__/TC-INT-AI-TOOLS.spec.js +91 -0
  13. package/dist/modules/ai_assistant/__integration__/TC-INT-AI-TOOLS.spec.js.map +7 -0
  14. package/dist/modules/ai_assistant/ai-tools/attachments-pack.js +202 -0
  15. package/dist/modules/ai_assistant/ai-tools/attachments-pack.js.map +7 -0
  16. package/dist/modules/ai_assistant/ai-tools/meta-pack.js +121 -0
  17. package/dist/modules/ai_assistant/ai-tools/meta-pack.js.map +7 -0
  18. package/dist/modules/ai_assistant/ai-tools/search-pack.js +94 -0
  19. package/dist/modules/ai_assistant/ai-tools/search-pack.js.map +7 -0
  20. package/dist/modules/ai_assistant/ai-tools.js +14 -0
  21. package/dist/modules/ai_assistant/ai-tools.js.map +7 -0
  22. package/dist/modules/ai_assistant/api/ai/actions/[id]/cancel/route.js +175 -0
  23. package/dist/modules/ai_assistant/api/ai/actions/[id]/cancel/route.js.map +7 -0
  24. package/dist/modules/ai_assistant/api/ai/actions/[id]/confirm/route.js +174 -0
  25. package/dist/modules/ai_assistant/api/ai/actions/[id]/confirm/route.js.map +7 -0
  26. package/dist/modules/ai_assistant/api/ai/actions/[id]/route.js +101 -0
  27. package/dist/modules/ai_assistant/api/ai/actions/[id]/route.js.map +7 -0
  28. package/dist/modules/ai_assistant/api/ai/agents/[agentId]/mutation-policy/route.js +311 -0
  29. package/dist/modules/ai_assistant/api/ai/agents/[agentId]/mutation-policy/route.js.map +7 -0
  30. package/dist/modules/ai_assistant/api/ai/agents/[agentId]/prompt-override/route.js +246 -0
  31. package/dist/modules/ai_assistant/api/ai/agents/[agentId]/prompt-override/route.js.map +7 -0
  32. package/dist/modules/ai_assistant/api/ai/agents/route.js +94 -0
  33. package/dist/modules/ai_assistant/api/ai/agents/route.js.map +7 -0
  34. package/dist/modules/ai_assistant/api/ai/chat/route.js +173 -0
  35. package/dist/modules/ai_assistant/api/ai/chat/route.js.map +7 -0
  36. package/dist/modules/ai_assistant/api/ai/run-object/route.js +167 -0
  37. package/dist/modules/ai_assistant/api/ai/run-object/route.js.map +7 -0
  38. package/dist/modules/ai_assistant/backend/config/ai-assistant/agents/AiAgentSettingsPageClient.js +1111 -0
  39. package/dist/modules/ai_assistant/backend/config/ai-assistant/agents/AiAgentSettingsPageClient.js.map +7 -0
  40. package/dist/modules/ai_assistant/backend/config/ai-assistant/agents/page.js +10 -0
  41. package/dist/modules/ai_assistant/backend/config/ai-assistant/agents/page.js.map +7 -0
  42. package/dist/modules/ai_assistant/backend/config/ai-assistant/agents/page.meta.js +28 -0
  43. package/dist/modules/ai_assistant/backend/config/ai-assistant/agents/page.meta.js.map +7 -0
  44. package/dist/modules/ai_assistant/backend/config/ai-assistant/legacy/page.js +10 -0
  45. package/dist/modules/ai_assistant/backend/config/ai-assistant/legacy/page.js.map +7 -0
  46. package/dist/modules/ai_assistant/backend/config/ai-assistant/legacy/page.meta.js +30 -0
  47. package/dist/modules/ai_assistant/backend/config/ai-assistant/legacy/page.meta.js.map +7 -0
  48. package/dist/modules/ai_assistant/backend/config/ai-assistant/page.js +4 -6
  49. package/dist/modules/ai_assistant/backend/config/ai-assistant/page.js.map +2 -2
  50. package/dist/modules/ai_assistant/backend/config/ai-assistant/page.meta.js +1 -21
  51. package/dist/modules/ai_assistant/backend/config/ai-assistant/page.meta.js.map +2 -2
  52. package/dist/modules/ai_assistant/backend/config/ai-assistant/playground/AiPlaygroundPageClient.js +462 -0
  53. package/dist/modules/ai_assistant/backend/config/ai-assistant/playground/AiPlaygroundPageClient.js.map +7 -0
  54. package/dist/modules/ai_assistant/backend/config/ai-assistant/playground/page.js +10 -0
  55. package/dist/modules/ai_assistant/backend/config/ai-assistant/playground/page.js.map +7 -0
  56. package/dist/modules/ai_assistant/backend/config/ai-assistant/playground/page.meta.js +28 -0
  57. package/dist/modules/ai_assistant/backend/config/ai-assistant/playground/page.meta.js.map +7 -0
  58. package/dist/modules/ai_assistant/cli.js +78 -12
  59. package/dist/modules/ai_assistant/cli.js.map +2 -2
  60. package/dist/modules/ai_assistant/data/entities/AiAgentMutationPolicyOverride.js +5 -0
  61. package/dist/modules/ai_assistant/data/entities/AiAgentMutationPolicyOverride.js.map +7 -0
  62. package/dist/modules/ai_assistant/data/entities/AiAgentPromptOverride.js +5 -0
  63. package/dist/modules/ai_assistant/data/entities/AiAgentPromptOverride.js.map +7 -0
  64. package/dist/modules/ai_assistant/data/entities/AiPendingAction.js +5 -0
  65. package/dist/modules/ai_assistant/data/entities/AiPendingAction.js.map +7 -0
  66. package/dist/modules/ai_assistant/data/entities.js +228 -0
  67. package/dist/modules/ai_assistant/data/entities.js.map +7 -0
  68. package/dist/modules/ai_assistant/data/repositories/AiAgentMutationPolicyOverrideRepository.js +95 -0
  69. package/dist/modules/ai_assistant/data/repositories/AiAgentMutationPolicyOverrideRepository.js.map +7 -0
  70. package/dist/modules/ai_assistant/data/repositories/AiAgentPromptOverrideRepository.js +95 -0
  71. package/dist/modules/ai_assistant/data/repositories/AiAgentPromptOverrideRepository.js.map +7 -0
  72. package/dist/modules/ai_assistant/data/repositories/AiPendingActionRepository.js +223 -0
  73. package/dist/modules/ai_assistant/data/repositories/AiPendingActionRepository.js.map +7 -0
  74. package/dist/modules/ai_assistant/events.js +33 -0
  75. package/dist/modules/ai_assistant/events.js.map +7 -0
  76. package/dist/modules/ai_assistant/i18n/de.json +252 -0
  77. package/dist/modules/ai_assistant/i18n/en.json +252 -0
  78. package/dist/modules/ai_assistant/i18n/es.json +252 -0
  79. package/dist/modules/ai_assistant/i18n/pl.json +252 -0
  80. package/dist/modules/ai_assistant/lib/agent-policy.js +168 -0
  81. package/dist/modules/ai_assistant/lib/agent-policy.js.map +7 -0
  82. package/dist/modules/ai_assistant/lib/agent-registry.js +195 -0
  83. package/dist/modules/ai_assistant/lib/agent-registry.js.map +7 -0
  84. package/dist/modules/ai_assistant/lib/agent-runtime.js +451 -0
  85. package/dist/modules/ai_assistant/lib/agent-runtime.js.map +7 -0
  86. package/dist/modules/ai_assistant/lib/agent-tools.js +223 -0
  87. package/dist/modules/ai_assistant/lib/agent-tools.js.map +7 -0
  88. package/dist/modules/ai_assistant/lib/agent-transport.js +25 -0
  89. package/dist/modules/ai_assistant/lib/agent-transport.js.map +7 -0
  90. package/dist/modules/ai_assistant/lib/ai-agent-definition.js +11 -0
  91. package/dist/modules/ai_assistant/lib/ai-agent-definition.js.map +7 -0
  92. package/dist/modules/ai_assistant/lib/ai-agents-generated.d.js +1 -0
  93. package/dist/modules/ai_assistant/lib/ai-agents-generated.d.js.map +7 -0
  94. package/dist/modules/ai_assistant/lib/ai-api-operation-runner.js +239 -0
  95. package/dist/modules/ai_assistant/lib/ai-api-operation-runner.js.map +7 -0
  96. package/dist/modules/ai_assistant/lib/ai-overrides.js +189 -0
  97. package/dist/modules/ai_assistant/lib/ai-overrides.js.map +7 -0
  98. package/dist/modules/ai_assistant/lib/ai-tool-definition.js +7 -0
  99. package/dist/modules/ai_assistant/lib/ai-tool-definition.js.map +7 -0
  100. package/dist/modules/ai_assistant/lib/ai-tools-generated.d.js +1 -0
  101. package/dist/modules/ai_assistant/lib/ai-tools-generated.d.js.map +7 -0
  102. package/dist/modules/ai_assistant/lib/api-backed-tool.js +48 -0
  103. package/dist/modules/ai_assistant/lib/api-backed-tool.js.map +7 -0
  104. package/dist/modules/ai_assistant/lib/attachment-bridge-types.js +1 -0
  105. package/dist/modules/ai_assistant/lib/attachment-bridge-types.js.map +7 -0
  106. package/dist/modules/ai_assistant/lib/attachment-parts.js +276 -0
  107. package/dist/modules/ai_assistant/lib/attachment-parts.js.map +7 -0
  108. package/dist/modules/ai_assistant/lib/model-factory.js +68 -0
  109. package/dist/modules/ai_assistant/lib/model-factory.js.map +7 -0
  110. package/dist/modules/ai_assistant/lib/pending-action-cancel.js +86 -0
  111. package/dist/modules/ai_assistant/lib/pending-action-cancel.js.map +7 -0
  112. package/dist/modules/ai_assistant/lib/pending-action-client.js +35 -0
  113. package/dist/modules/ai_assistant/lib/pending-action-client.js.map +7 -0
  114. package/dist/modules/ai_assistant/lib/pending-action-executor.js +243 -0
  115. package/dist/modules/ai_assistant/lib/pending-action-executor.js.map +7 -0
  116. package/dist/modules/ai_assistant/lib/pending-action-recheck.js +246 -0
  117. package/dist/modules/ai_assistant/lib/pending-action-recheck.js.map +7 -0
  118. package/dist/modules/ai_assistant/lib/pending-action-types.js +70 -0
  119. package/dist/modules/ai_assistant/lib/pending-action-types.js.map +7 -0
  120. package/dist/modules/ai_assistant/lib/prepare-mutation.js +315 -0
  121. package/dist/modules/ai_assistant/lib/prepare-mutation.js.map +7 -0
  122. package/dist/modules/ai_assistant/lib/prompt-composition-types.js +7 -0
  123. package/dist/modules/ai_assistant/lib/prompt-composition-types.js.map +7 -0
  124. package/dist/modules/ai_assistant/lib/prompt-override-merge.js +175 -0
  125. package/dist/modules/ai_assistant/lib/prompt-override-merge.js.map +7 -0
  126. package/dist/modules/ai_assistant/lib/schema-utils.js +5 -1
  127. package/dist/modules/ai_assistant/lib/schema-utils.js.map +2 -2
  128. package/dist/modules/ai_assistant/lib/tool-executor.js +13 -2
  129. package/dist/modules/ai_assistant/lib/tool-executor.js.map +2 -2
  130. package/dist/modules/ai_assistant/lib/tool-loader.js +86 -11
  131. package/dist/modules/ai_assistant/lib/tool-loader.js.map +2 -2
  132. package/dist/modules/ai_assistant/lib/tool-test-fixtures.js +120 -0
  133. package/dist/modules/ai_assistant/lib/tool-test-fixtures.js.map +7 -0
  134. package/dist/modules/ai_assistant/lib/tool-test-runner.js +418 -0
  135. package/dist/modules/ai_assistant/lib/tool-test-runner.js.map +7 -0
  136. package/dist/modules/ai_assistant/migrations/Migration20260419100521.js +17 -0
  137. package/dist/modules/ai_assistant/migrations/Migration20260419100521.js.map +7 -0
  138. package/dist/modules/ai_assistant/migrations/Migration20260419132948.js +16 -0
  139. package/dist/modules/ai_assistant/migrations/Migration20260419132948.js.map +7 -0
  140. package/dist/modules/ai_assistant/migrations/Migration20260419134235.js +17 -0
  141. package/dist/modules/ai_assistant/migrations/Migration20260419134235.js.map +7 -0
  142. package/dist/modules/ai_assistant/setup.js +36 -0
  143. package/dist/modules/ai_assistant/setup.js.map +2 -2
  144. package/dist/modules/ai_assistant/workers/ai-pending-action-cleanup.js +161 -0
  145. package/dist/modules/ai_assistant/workers/ai-pending-action-cleanup.js.map +7 -0
  146. package/generated/entities/ai_agent_mutation_policy_override/index.ts +9 -0
  147. package/generated/entities/ai_agent_prompt_override/index.ts +10 -0
  148. package/generated/entities/ai_pending_action/index.ts +24 -0
  149. package/generated/entities.ids.generated.ts +13 -0
  150. package/generated/entity-fields-registry.ts +57 -0
  151. package/jest.config.cjs +7 -0
  152. package/package.json +4 -4
  153. package/src/index.ts +215 -0
  154. package/src/modules/ai_assistant/__integration__/README.md +5 -0
  155. package/src/modules/ai_assistant/__integration__/TC-AI-002-agent-policy.spec.ts +115 -0
  156. package/src/modules/ai_assistant/__integration__/TC-AI-AGENT-SETTINGS-005-settings-page.spec.ts +574 -0
  157. package/src/modules/ai_assistant/__integration__/TC-AI-PLAYGROUND-004-playground.spec.ts +333 -0
  158. package/src/modules/ai_assistant/__integration__/TC-INT-AI-TOOLS.spec.ts +135 -0
  159. package/src/modules/ai_assistant/__tests__/events.test.ts +145 -0
  160. package/src/modules/ai_assistant/__tests__/integration/pending-action-contract.test.ts +1015 -0
  161. package/src/modules/ai_assistant/__tests__/integration/ws-c-attachment-bridge.test.ts +235 -0
  162. package/src/modules/ai_assistant/__tests__/integration/ws-c-policy-and-tools.test.ts +330 -0
  163. package/src/modules/ai_assistant/__tests__/integration/ws-c-tool-pack-coverage.test.ts +285 -0
  164. package/src/modules/ai_assistant/ai-tools/__tests__/attachments-pack.test.ts +322 -0
  165. package/src/modules/ai_assistant/ai-tools/__tests__/meta-pack.test.ts +218 -0
  166. package/src/modules/ai_assistant/ai-tools/__tests__/search-pack.test.ts +192 -0
  167. package/src/modules/ai_assistant/ai-tools/attachments-pack.ts +269 -0
  168. package/src/modules/ai_assistant/ai-tools/meta-pack.ts +140 -0
  169. package/src/modules/ai_assistant/ai-tools/search-pack.ts +122 -0
  170. package/src/modules/ai_assistant/ai-tools.ts +21 -0
  171. package/src/modules/ai_assistant/api/ai/actions/[id]/__tests__/route.test.ts +222 -0
  172. package/src/modules/ai_assistant/api/ai/actions/[id]/cancel/__tests__/route.test.ts +286 -0
  173. package/src/modules/ai_assistant/api/ai/actions/[id]/cancel/route.ts +237 -0
  174. package/src/modules/ai_assistant/api/ai/actions/[id]/confirm/__tests__/route.test.ts +339 -0
  175. package/src/modules/ai_assistant/api/ai/actions/[id]/confirm/route.ts +229 -0
  176. package/src/modules/ai_assistant/api/ai/actions/[id]/route.ts +142 -0
  177. package/src/modules/ai_assistant/api/ai/agents/[agentId]/mutation-policy/__tests__/route.test.ts +367 -0
  178. package/src/modules/ai_assistant/api/ai/agents/[agentId]/mutation-policy/route.ts +380 -0
  179. package/src/modules/ai_assistant/api/ai/agents/[agentId]/prompt-override/__tests__/route.test.ts +333 -0
  180. package/src/modules/ai_assistant/api/ai/agents/[agentId]/prompt-override/route.ts +307 -0
  181. package/src/modules/ai_assistant/api/ai/agents/route.ts +107 -0
  182. package/src/modules/ai_assistant/api/ai/chat/__tests__/route.test.ts +282 -0
  183. package/src/modules/ai_assistant/api/ai/chat/route.ts +207 -0
  184. package/src/modules/ai_assistant/api/ai/run-object/__tests__/route.test.ts +282 -0
  185. package/src/modules/ai_assistant/api/ai/run-object/route.ts +204 -0
  186. package/src/modules/ai_assistant/backend/config/ai-assistant/agents/AiAgentSettingsPageClient.tsx +1419 -0
  187. package/src/modules/ai_assistant/backend/config/ai-assistant/agents/page.meta.ts +26 -0
  188. package/src/modules/ai_assistant/backend/config/ai-assistant/agents/page.tsx +12 -0
  189. package/src/modules/ai_assistant/backend/config/ai-assistant/legacy/page.meta.ts +28 -0
  190. package/src/modules/ai_assistant/backend/config/ai-assistant/legacy/page.tsx +12 -0
  191. package/src/modules/ai_assistant/backend/config/ai-assistant/page.meta.ts +8 -23
  192. package/src/modules/ai_assistant/backend/config/ai-assistant/page.tsx +15 -10
  193. package/src/modules/ai_assistant/backend/config/ai-assistant/playground/AiPlaygroundPageClient.tsx +604 -0
  194. package/src/modules/ai_assistant/backend/config/ai-assistant/playground/page.meta.ts +26 -0
  195. package/src/modules/ai_assistant/backend/config/ai-assistant/playground/page.tsx +12 -0
  196. package/src/modules/ai_assistant/cli.ts +99 -24
  197. package/src/modules/ai_assistant/data/__tests__/schema-unique-indexes.test.ts +69 -0
  198. package/src/modules/ai_assistant/data/entities/AiAgentMutationPolicyOverride.ts +7 -0
  199. package/src/modules/ai_assistant/data/entities/AiAgentPromptOverride.ts +7 -0
  200. package/src/modules/ai_assistant/data/entities/AiPendingAction.ts +7 -0
  201. package/src/modules/ai_assistant/data/entities.ts +270 -0
  202. package/src/modules/ai_assistant/data/repositories/AiAgentMutationPolicyOverrideRepository.ts +129 -0
  203. package/src/modules/ai_assistant/data/repositories/AiAgentPromptOverrideRepository.ts +132 -0
  204. package/src/modules/ai_assistant/data/repositories/AiPendingActionRepository.ts +334 -0
  205. package/src/modules/ai_assistant/data/repositories/__tests__/AiAgentMutationPolicyOverrideRepository.test.ts +195 -0
  206. package/src/modules/ai_assistant/data/repositories/__tests__/AiAgentPromptOverrideRepository.test.ts +197 -0
  207. package/src/modules/ai_assistant/data/repositories/__tests__/AiPendingActionRepository.test.ts +357 -0
  208. package/src/modules/ai_assistant/events.ts +112 -0
  209. package/src/modules/ai_assistant/i18n/de.json +252 -0
  210. package/src/modules/ai_assistant/i18n/en.json +252 -0
  211. package/src/modules/ai_assistant/i18n/es.json +252 -0
  212. package/src/modules/ai_assistant/i18n/pl.json +252 -0
  213. package/src/modules/ai_assistant/lib/__tests__/agent-policy.mutation-override.test.ts +203 -0
  214. package/src/modules/ai_assistant/lib/__tests__/agent-policy.test.ts +385 -0
  215. package/src/modules/ai_assistant/lib/__tests__/agent-registry.test.ts +217 -0
  216. package/src/modules/ai_assistant/lib/__tests__/agent-runtime-object.test.ts +329 -0
  217. package/src/modules/ai_assistant/lib/__tests__/agent-runtime-parity.test.ts +573 -0
  218. package/src/modules/ai_assistant/lib/__tests__/agent-runtime.test.ts +291 -0
  219. package/src/modules/ai_assistant/lib/__tests__/agent-tools.test.ts +172 -0
  220. package/src/modules/ai_assistant/lib/__tests__/agent-transport.test.ts +41 -0
  221. package/src/modules/ai_assistant/lib/__tests__/ai-agent-definition.test.ts +183 -0
  222. package/src/modules/ai_assistant/lib/__tests__/ai-api-operation-runner.test.ts +432 -0
  223. package/src/modules/ai_assistant/lib/__tests__/ai-overrides.test.ts +308 -0
  224. package/src/modules/ai_assistant/lib/__tests__/api-backed-tool.test.ts +302 -0
  225. package/src/modules/ai_assistant/lib/__tests__/attachment-bridge-and-prompt-types.test.ts +188 -0
  226. package/src/modules/ai_assistant/lib/__tests__/attachment-parts.test.ts +531 -0
  227. package/src/modules/ai_assistant/lib/__tests__/max-steps-budget.integration.test.ts +263 -0
  228. package/src/modules/ai_assistant/lib/__tests__/model-factory.integration.test.ts +183 -0
  229. package/src/modules/ai_assistant/lib/__tests__/model-factory.test.ts +168 -0
  230. package/src/modules/ai_assistant/lib/__tests__/pending-action-cancel.test.ts +235 -0
  231. package/src/modules/ai_assistant/lib/__tests__/pending-action-client.test.ts +148 -0
  232. package/src/modules/ai_assistant/lib/__tests__/pending-action-executor.test.ts +348 -0
  233. package/src/modules/ai_assistant/lib/__tests__/pending-action-recheck.test.ts +378 -0
  234. package/src/modules/ai_assistant/lib/__tests__/phase-0-additive-contract.test.ts +299 -0
  235. package/src/modules/ai_assistant/lib/__tests__/prepare-mutation.test.ts +610 -0
  236. package/src/modules/ai_assistant/lib/__tests__/prompt-override-merge.test.ts +136 -0
  237. package/src/modules/ai_assistant/lib/__tests__/tool-loader.test.ts +125 -0
  238. package/src/modules/ai_assistant/lib/agent-policy.ts +270 -0
  239. package/src/modules/ai_assistant/lib/agent-registry.ts +277 -0
  240. package/src/modules/ai_assistant/lib/agent-runtime.ts +751 -0
  241. package/src/modules/ai_assistant/lib/agent-tools.ts +396 -0
  242. package/src/modules/ai_assistant/lib/agent-transport.ts +51 -0
  243. package/src/modules/ai_assistant/lib/ai-agent-definition.ts +86 -0
  244. package/src/modules/ai_assistant/lib/ai-agents-generated.d.ts +18 -0
  245. package/src/modules/ai_assistant/lib/ai-api-operation-runner.ts +333 -0
  246. package/src/modules/ai_assistant/lib/ai-overrides.ts +389 -0
  247. package/src/modules/ai_assistant/lib/ai-tool-definition.ts +7 -0
  248. package/src/modules/ai_assistant/lib/ai-tools-generated.d.ts +7 -0
  249. package/src/modules/ai_assistant/lib/api-backed-tool.ts +85 -0
  250. package/src/modules/ai_assistant/lib/attachment-bridge-types.ts +24 -0
  251. package/src/modules/ai_assistant/lib/attachment-parts.ts +433 -0
  252. package/src/modules/ai_assistant/lib/model-factory.ts +212 -0
  253. package/src/modules/ai_assistant/lib/pending-action-cancel.ts +179 -0
  254. package/src/modules/ai_assistant/lib/pending-action-client.ts +126 -0
  255. package/src/modules/ai_assistant/lib/pending-action-executor.ts +424 -0
  256. package/src/modules/ai_assistant/lib/pending-action-recheck.ts +410 -0
  257. package/src/modules/ai_assistant/lib/pending-action-types.ts +194 -0
  258. package/src/modules/ai_assistant/lib/prepare-mutation.ts +448 -0
  259. package/src/modules/ai_assistant/lib/prompt-composition-types.ts +24 -0
  260. package/src/modules/ai_assistant/lib/prompt-override-merge.ts +253 -0
  261. package/src/modules/ai_assistant/lib/schema-utils.ts +14 -2
  262. package/src/modules/ai_assistant/lib/tool-executor.ts +25 -3
  263. package/src/modules/ai_assistant/lib/tool-loader.ts +159 -13
  264. package/src/modules/ai_assistant/lib/tool-test-fixtures.ts +160 -0
  265. package/src/modules/ai_assistant/lib/tool-test-runner.ts +596 -0
  266. package/src/modules/ai_assistant/lib/types.ts +105 -2
  267. package/src/modules/ai_assistant/migrations/.snapshot-open-mercato.json +871 -0
  268. package/src/modules/ai_assistant/migrations/Migration20260419100521.ts +17 -0
  269. package/src/modules/ai_assistant/migrations/Migration20260419132948.ts +16 -0
  270. package/src/modules/ai_assistant/migrations/Migration20260419134235.ts +17 -0
  271. package/src/modules/ai_assistant/setup.ts +53 -0
  272. package/src/modules/ai_assistant/workers/__tests__/ai-pending-action-cleanup.test.ts +333 -0
  273. package/src/modules/ai_assistant/workers/ai-pending-action-cleanup.ts +269 -0
@@ -0,0 +1,235 @@
1
+ /**
2
+ * Step 3.13 — Phase 1 WS-C integration tests (attachment bridge).
3
+ *
4
+ * Asserts the Step 3.7 attachment-to-model bridge upholds two invariants that
5
+ * matter to every downstream agent turn:
6
+ *
7
+ * 1. Cross-tenant attachment ids passed by the caller are silently dropped —
8
+ * they MUST NOT surface in the resolved parts array and MUST NOT leak any
9
+ * metadata about the foreign tenant.
10
+ * 2. An oversized image (> DEFAULT_MAX_INLINE_BYTES = 4 MB) with no
11
+ * `attachmentSigner` registered falls back to `source: 'metadata-only'`
12
+ * instead of failing the turn — the model is informed that the file
13
+ * exists without getting any raw bytes.
14
+ *
15
+ * The underlying `resolveAttachmentParts` implementation is unit-tested in
16
+ * `packages/ai-assistant/src/modules/ai_assistant/lib/__tests__/
17
+ * attachment-parts.test.ts`. This integration suite re-exercises the bridge
18
+ * through higher-level fixtures that mimic how the chat dispatcher calls it
19
+ * (two tenants, a signer slot, a real-ish attachment row shape).
20
+ */
21
+
22
+ import type { AiChatRequestContext } from '../../lib/attachment-bridge-types'
23
+ import type { AttachmentSigner } from '../../lib/attachment-parts'
24
+
25
+ const findOneWithDecryptionMock = jest.fn()
26
+
27
+ jest.mock('@open-mercato/shared/lib/encryption/find', () => ({
28
+ findOneWithDecryption: (...args: unknown[]) => findOneWithDecryptionMock(...args),
29
+ }))
30
+
31
+ jest.mock('@open-mercato/core/modules/attachments/data/entities', () => ({
32
+ Attachment: class AttachmentStub {},
33
+ }))
34
+
35
+ const resolveAttachmentAbsolutePathMock = jest.fn(
36
+ (_partition: string, storagePath: string) => `/fake/root/${storagePath}`,
37
+ )
38
+
39
+ jest.mock('@open-mercato/core/modules/attachments/lib/storage', () => ({
40
+ resolveAttachmentAbsolutePath: (...args: unknown[]) =>
41
+ resolveAttachmentAbsolutePathMock(...(args as [string, string, string?])),
42
+ }))
43
+
44
+ const fsReadFileMock = jest.fn()
45
+ jest.mock('fs', () => {
46
+ const actual = jest.requireActual('fs')
47
+ return {
48
+ ...actual,
49
+ promises: {
50
+ ...actual.promises,
51
+ readFile: (...args: unknown[]) => fsReadFileMock(...args),
52
+ },
53
+ }
54
+ })
55
+
56
+ import { resolveAttachmentParts } from '../../lib/attachment-parts'
57
+
58
+ type AttachmentRowOverrides = {
59
+ id?: string
60
+ fileName?: string
61
+ mimeType?: string
62
+ fileSize?: number
63
+ storagePath?: string
64
+ storageDriver?: string
65
+ partitionCode?: string
66
+ tenantId?: string | null
67
+ organizationId?: string | null
68
+ content?: string | null
69
+ entityId?: string
70
+ }
71
+
72
+ function makeRow(overrides: AttachmentRowOverrides = {}): Record<string, unknown> {
73
+ return {
74
+ id: 'attachment-1',
75
+ entityId: 'attachments:attachment',
76
+ fileName: 'file.bin',
77
+ mimeType: 'application/octet-stream',
78
+ fileSize: 1024,
79
+ storagePath: 'path/to/file',
80
+ storageDriver: 'local',
81
+ partitionCode: 'default',
82
+ tenantId: 'tenant-a',
83
+ organizationId: 'org-a',
84
+ content: null,
85
+ ...overrides,
86
+ }
87
+ }
88
+
89
+ function makeContainer(options?: { signer?: AttachmentSigner | null; omitEm?: boolean }) {
90
+ const registry: Record<string, unknown> = {}
91
+ if (!options?.omitEm) {
92
+ registry.em = { __label: 'em-stub' }
93
+ }
94
+ if (options?.signer) {
95
+ registry.attachmentSigner = options.signer
96
+ }
97
+ return {
98
+ resolve: jest.fn((name: string) => {
99
+ if (!(name in registry)) {
100
+ throw new Error(`Unknown registration: ${name}`)
101
+ }
102
+ return registry[name] as never
103
+ }),
104
+ } as unknown as Parameters<typeof resolveAttachmentParts>[0]['container']
105
+ }
106
+
107
+ function makeAuth(overrides: Partial<AiChatRequestContext> = {}): AiChatRequestContext {
108
+ return {
109
+ tenantId: 'tenant-a',
110
+ organizationId: 'org-a',
111
+ userId: 'user-a',
112
+ features: [],
113
+ isSuperAdmin: false,
114
+ ...overrides,
115
+ }
116
+ }
117
+
118
+ describe('WS-C integration — attachment bridge', () => {
119
+ let warnSpy: jest.SpyInstance
120
+
121
+ beforeEach(() => {
122
+ jest.clearAllMocks()
123
+ warnSpy = jest.spyOn(console, 'warn').mockImplementation(() => {})
124
+ })
125
+
126
+ afterEach(() => {
127
+ warnSpy.mockRestore()
128
+ })
129
+
130
+ it('drops cross-tenant attachments without leaking foreign metadata', async () => {
131
+ // Simulated tenants: caller is tenant-a; foreign-attachment belongs to tenant-b.
132
+ // findOneWithDecryption accepts the scope tuple but its mock returns the row
133
+ // as-if the query were scope-unaware, so the in-process row scope check is
134
+ // the gate that MUST drop this.
135
+ findOneWithDecryptionMock.mockImplementation(
136
+ async (_em: unknown, _entity: unknown, where: Record<string, unknown>) => {
137
+ if (where.id === 'own-attachment') {
138
+ return makeRow({ id: 'own-attachment', tenantId: 'tenant-a', organizationId: 'org-a' })
139
+ }
140
+ if (where.id === 'foreign-attachment') {
141
+ // The attachment belongs to tenant-b; the resolver should drop it.
142
+ return makeRow({
143
+ id: 'foreign-attachment',
144
+ tenantId: 'tenant-b',
145
+ organizationId: 'org-b',
146
+ mimeType: 'image/png',
147
+ fileSize: 1024,
148
+ })
149
+ }
150
+ return null
151
+ },
152
+ )
153
+ fsReadFileMock.mockResolvedValue(Buffer.from([1, 2, 3]))
154
+
155
+ const auth = makeAuth()
156
+ const parts = await resolveAttachmentParts({
157
+ attachmentIds: ['own-attachment', 'foreign-attachment', 'not-found'],
158
+ authContext: auth,
159
+ container: makeContainer(),
160
+ })
161
+
162
+ const ids = parts.map((part) => part.attachmentId)
163
+ expect(ids).toEqual(['own-attachment'])
164
+ expect(ids).not.toContain('foreign-attachment')
165
+ expect(ids).not.toContain('not-found')
166
+
167
+ // The warn for the foreign attachment MUST NOT reveal tenant-b or org-b.
168
+ const warnCalls = warnSpy.mock.calls.map((call) => call.join(' '))
169
+ const foreignWarn = warnCalls.find((message) => message.includes('foreign-attachment'))
170
+ expect(foreignWarn).toBeDefined()
171
+ expect(foreignWarn).not.toMatch(/tenant-b/)
172
+ expect(foreignWarn).not.toMatch(/org-b/)
173
+ })
174
+
175
+ it('oversized image with no attachment signer falls back to source=metadata-only', async () => {
176
+ findOneWithDecryptionMock.mockResolvedValue(
177
+ makeRow({
178
+ id: 'huge-image',
179
+ tenantId: 'tenant-a',
180
+ organizationId: 'org-a',
181
+ mimeType: 'image/png',
182
+ fileSize: 8 * 1024 * 1024,
183
+ }),
184
+ )
185
+
186
+ const parts = await resolveAttachmentParts({
187
+ attachmentIds: ['huge-image'],
188
+ authContext: makeAuth(),
189
+ container: makeContainer(),
190
+ })
191
+
192
+ expect(parts).toHaveLength(1)
193
+ expect(parts[0].attachmentId).toBe('huge-image')
194
+ expect(parts[0].source).toBe('metadata-only')
195
+ // `fs.readFile` MUST NOT be consulted for oversized images — that would
196
+ // blow past the default 4 MB inline ceiling before the downgrade fires.
197
+ expect(fsReadFileMock).not.toHaveBeenCalled()
198
+ })
199
+
200
+ it('oversized image WITH a signer is promoted to source=signed-url', async () => {
201
+ findOneWithDecryptionMock.mockResolvedValue(
202
+ makeRow({
203
+ id: 'huge-image',
204
+ tenantId: 'tenant-a',
205
+ organizationId: 'org-a',
206
+ mimeType: 'image/png',
207
+ fileSize: 8 * 1024 * 1024,
208
+ }),
209
+ )
210
+ const signer: AttachmentSigner = {
211
+ sign: jest.fn().mockResolvedValue('https://example.com/signed/huge-image'),
212
+ }
213
+
214
+ const parts = await resolveAttachmentParts({
215
+ attachmentIds: ['huge-image'],
216
+ authContext: makeAuth(),
217
+ container: makeContainer({ signer }),
218
+ })
219
+
220
+ expect(parts).toHaveLength(1)
221
+ expect(parts[0].source).toBe('signed-url')
222
+ expect((parts[0] as { url?: string }).url).toBe('https://example.com/signed/huge-image')
223
+ expect(signer.sign).toHaveBeenCalledTimes(1)
224
+ })
225
+
226
+ it('missing DI container returns [] with a warn and never throws', async () => {
227
+ const parts = await resolveAttachmentParts({
228
+ attachmentIds: ['any-id'],
229
+ authContext: makeAuth(),
230
+ // container omitted deliberately
231
+ })
232
+ expect(parts).toEqual([])
233
+ expect(warnSpy).toHaveBeenCalled()
234
+ })
235
+ })
@@ -0,0 +1,330 @@
1
+ /**
2
+ * Step 3.13 — Phase 1 WS-C integration tests (policy gate + tool resolution).
3
+ *
4
+ * These tests sit between the per-module unit tests (under each pack's own
5
+ * `__tests__/`) and the HTTP Playwright coverage under `.ai/qa/tests/ai-framework/`.
6
+ * The goal is to exercise the full runtime pipeline — agent registry → policy
7
+ * gate → tool resolution → AI SDK adapter — against realistic fixtures and
8
+ * assert the cross-cutting invariants that neither layer alone can catch.
9
+ *
10
+ * Why mock the AI SDK at the module boundary instead of a true HTTP e2e:
11
+ * the Vercel AI SDK model factory tries to reach a real LLM provider by
12
+ * default. The existing `agent-runtime-parity.test.ts` contract suite adopted
13
+ * the same mock stance; this integration suite mirrors that choice so we
14
+ * remain deterministic, hermetic, and provider-agnostic.
15
+ */
16
+
17
+ import { z } from 'zod'
18
+ import type { AiAgentDefinition } from '../../lib/ai-agent-definition'
19
+ import type { AiChatRequestContext } from '../../lib/attachment-bridge-types'
20
+ import type { AiToolDefinition } from '../../lib/types'
21
+
22
+ // --- SDK mocks (mirrors agent-runtime-parity.test.ts) ---------------------
23
+
24
+ const streamTextMock = jest.fn()
25
+ const generateObjectMock = jest.fn()
26
+ const streamObjectMock = jest.fn()
27
+
28
+ jest.mock('ai', () => {
29
+ const actual = jest.requireActual('ai')
30
+ return {
31
+ ...actual,
32
+ streamText: (...args: unknown[]) => streamTextMock(...args),
33
+ generateObject: (...args: unknown[]) => generateObjectMock(...args),
34
+ streamObject: (...args: unknown[]) => streamObjectMock(...args),
35
+ }
36
+ })
37
+
38
+ const createModelMock = jest.fn(
39
+ (options: { modelId: string; apiKey: string }) => ({ id: options.modelId, apiKey: options.apiKey }),
40
+ )
41
+ const resolveApiKeyMock = jest.fn(() => 'test-api-key')
42
+
43
+ jest.mock('@open-mercato/shared/lib/ai/llm-provider-registry', () => ({
44
+ llmProviderRegistry: {
45
+ resolveFirstConfigured: () => ({
46
+ id: 'test-provider',
47
+ defaultModel: 'provider-default-model',
48
+ resolveApiKey: resolveApiKeyMock,
49
+ createModel: createModelMock,
50
+ }),
51
+ },
52
+ }))
53
+
54
+ import {
55
+ resetAgentRegistryForTests,
56
+ seedAgentRegistryForTests,
57
+ } from '../../lib/agent-registry'
58
+ import { toolRegistry, registerMcpTool } from '../../lib/tool-registry'
59
+ import { resolveAiAgentTools, AgentPolicyError } from '../../lib/agent-tools'
60
+ import { checkAgentPolicy } from '../../lib/agent-policy'
61
+ import { runAiAgentText } from '../../lib/agent-runtime'
62
+
63
+ function makeAgent(
64
+ overrides: Partial<AiAgentDefinition> & Pick<AiAgentDefinition, 'id' | 'moduleId'>,
65
+ ): AiAgentDefinition {
66
+ return {
67
+ label: `${overrides.id} label`,
68
+ description: `${overrides.id} description`,
69
+ systemPrompt: 'System prompt base.',
70
+ allowedTools: [],
71
+ ...overrides,
72
+ }
73
+ }
74
+
75
+ function makeTool(
76
+ overrides: Partial<AiToolDefinition> & Pick<AiToolDefinition, 'name'>,
77
+ ): AiToolDefinition {
78
+ return {
79
+ description: `${overrides.name} description`,
80
+ inputSchema: z.object({}),
81
+ handler: async () => ({ ok: true }),
82
+ ...overrides,
83
+ }
84
+ }
85
+
86
+ function makeAuth(overrides: Partial<AiChatRequestContext> = {}): AiChatRequestContext {
87
+ return {
88
+ tenantId: 'tenant-a',
89
+ organizationId: 'org-a',
90
+ userId: 'user-a',
91
+ features: [],
92
+ isSuperAdmin: false,
93
+ ...overrides,
94
+ }
95
+ }
96
+
97
+ function fakeStreamTextResult() {
98
+ return {
99
+ toTextStreamResponse: jest.fn(
100
+ () =>
101
+ new Response('ok', {
102
+ status: 200,
103
+ headers: { 'Content-Type': 'text/event-stream' },
104
+ }),
105
+ ),
106
+ toUIMessageStreamResponse: jest.fn(
107
+ () =>
108
+ new Response('ok', {
109
+ status: 200,
110
+ headers: { 'Content-Type': 'text/event-stream' },
111
+ }),
112
+ ),
113
+ }
114
+ }
115
+
116
+ describe('WS-C integration — agent policy gate + tool resolution', () => {
117
+ let warnSpy: jest.SpyInstance
118
+
119
+ beforeEach(() => {
120
+ jest.clearAllMocks()
121
+ resetAgentRegistryForTests()
122
+ toolRegistry.clear()
123
+ warnSpy = jest.spyOn(console, 'warn').mockImplementation(() => {})
124
+ streamTextMock.mockImplementation(() => fakeStreamTextResult())
125
+ })
126
+
127
+ afterEach(() => {
128
+ warnSpy.mockRestore()
129
+ })
130
+
131
+ afterAll(() => {
132
+ resetAgentRegistryForTests()
133
+ toolRegistry.clear()
134
+ })
135
+
136
+ it('unknown agent throws AgentPolicyError(agent_unknown)', async () => {
137
+ await expect(
138
+ resolveAiAgentTools({
139
+ agentId: 'does.not_exist',
140
+ authContext: makeAuth({ isSuperAdmin: true }),
141
+ }),
142
+ ).rejects.toBeInstanceOf(AgentPolicyError)
143
+
144
+ await expect(
145
+ resolveAiAgentTools({
146
+ agentId: 'does.not_exist',
147
+ authContext: makeAuth({ isSuperAdmin: true }),
148
+ }),
149
+ ).rejects.toMatchObject({ code: 'agent_unknown' })
150
+ })
151
+
152
+ it('forbidden agent (missing requiredFeatures) throws agent_features_denied', async () => {
153
+ seedAgentRegistryForTests([
154
+ makeAgent({
155
+ id: 'customers.assistant',
156
+ moduleId: 'customers',
157
+ requiredFeatures: ['customers.assistant.use'],
158
+ }),
159
+ ])
160
+
161
+ await expect(
162
+ resolveAiAgentTools({
163
+ agentId: 'customers.assistant',
164
+ authContext: makeAuth({ features: ['customers.people.view'] }),
165
+ }),
166
+ ).rejects.toMatchObject({
167
+ name: 'AgentPolicyError',
168
+ code: 'agent_features_denied',
169
+ })
170
+ })
171
+
172
+ it('super-admin bypass: agent.requiredFeatures does not block a super-admin caller', async () => {
173
+ seedAgentRegistryForTests([
174
+ makeAgent({
175
+ id: 'customers.assistant',
176
+ moduleId: 'customers',
177
+ requiredFeatures: ['customers.assistant.use'],
178
+ }),
179
+ ])
180
+
181
+ const resolved = await resolveAiAgentTools({
182
+ agentId: 'customers.assistant',
183
+ authContext: makeAuth({ isSuperAdmin: true, features: [] }),
184
+ })
185
+ expect(resolved.agent.id).toBe('customers.assistant')
186
+ expect(resolved.tools).toEqual({})
187
+ })
188
+
189
+ it('allowedTools filtering: tools listed in allowedTools are resolved; extras never leak', async () => {
190
+ seedAgentRegistryForTests([
191
+ makeAgent({
192
+ id: 'catalog.assistant',
193
+ moduleId: 'catalog',
194
+ allowedTools: ['catalog.list_products'],
195
+ }),
196
+ ])
197
+ registerMcpTool(
198
+ makeTool({
199
+ name: 'catalog.list_products',
200
+ requiredFeatures: ['catalog.products.view'],
201
+ }),
202
+ )
203
+ // A tool that the agent did NOT whitelist. Resolution MUST NOT include it.
204
+ registerMcpTool(
205
+ makeTool({
206
+ name: 'catalog.delete_product',
207
+ isMutation: true,
208
+ requiredFeatures: ['catalog.products.manage'],
209
+ }),
210
+ )
211
+
212
+ const resolved = await resolveAiAgentTools({
213
+ agentId: 'catalog.assistant',
214
+ authContext: makeAuth({ isSuperAdmin: true }),
215
+ })
216
+
217
+ expect(Object.keys(resolved.tools).sort()).toEqual(['catalog__list_products'])
218
+ expect(Object.prototype.hasOwnProperty.call(resolved.tools, 'catalog__delete_product')).toBe(false)
219
+ })
220
+
221
+ it('tool-level requiredFeatures: tool is skipped with a warn when caller lacks the feature; remaining tools still reach the model', async () => {
222
+ seedAgentRegistryForTests([
223
+ makeAgent({
224
+ id: 'catalog.assistant',
225
+ moduleId: 'catalog',
226
+ allowedTools: ['catalog.list_products', 'catalog.get_product'],
227
+ }),
228
+ ])
229
+ registerMcpTool(
230
+ makeTool({
231
+ name: 'catalog.list_products',
232
+ requiredFeatures: ['catalog.products.view'],
233
+ }),
234
+ )
235
+ registerMcpTool(
236
+ makeTool({
237
+ name: 'catalog.get_product',
238
+ requiredFeatures: ['catalog.products.secret'],
239
+ }),
240
+ )
241
+
242
+ const resolved = await resolveAiAgentTools({
243
+ agentId: 'catalog.assistant',
244
+ authContext: makeAuth({ features: ['catalog.products.view'], isSuperAdmin: false }),
245
+ })
246
+ expect(Object.keys(resolved.tools)).toEqual(['catalog__list_products'])
247
+ expect(warnSpy).toHaveBeenCalledWith(
248
+ expect.stringContaining('catalog.get_product'),
249
+ )
250
+ })
251
+
252
+ it('mutation tool blocked by readOnly agent: checkAgentPolicy returns mutation_blocked_by_readonly', async () => {
253
+ seedAgentRegistryForTests([
254
+ makeAgent({
255
+ id: 'catalog.assistant',
256
+ moduleId: 'catalog',
257
+ allowedTools: ['catalog.delete_product'],
258
+ readOnly: true,
259
+ }),
260
+ ])
261
+ registerMcpTool(
262
+ makeTool({
263
+ name: 'catalog.delete_product',
264
+ isMutation: true,
265
+ }),
266
+ )
267
+
268
+ const decision = checkAgentPolicy({
269
+ agentId: 'catalog.assistant',
270
+ toolName: 'catalog.delete_product',
271
+ authContext: { userFeatures: ['*'], isSuperAdmin: true },
272
+ })
273
+ expect(decision.ok).toBe(false)
274
+ if (!decision.ok) {
275
+ expect(decision.code).toBe('mutation_blocked_by_readonly')
276
+ }
277
+ })
278
+
279
+ it('runAiAgentText end-to-end: unknown agent produces AgentPolicyError before the SDK is called', async () => {
280
+ await expect(
281
+ runAiAgentText({
282
+ agentId: 'missing.agent',
283
+ messages: [
284
+ { role: 'user', id: 'm1', parts: [{ type: 'text' as const, text: 'hi' }] } as never,
285
+ ],
286
+ authContext: makeAuth({ isSuperAdmin: true }),
287
+ }),
288
+ ).rejects.toMatchObject({ name: 'AgentPolicyError', code: 'agent_unknown' })
289
+ expect(streamTextMock).not.toHaveBeenCalled()
290
+ })
291
+
292
+ it('runAiAgentText end-to-end: allowedTools filter passes through to the SDK tool map', async () => {
293
+ seedAgentRegistryForTests([
294
+ makeAgent({
295
+ id: 'catalog.assistant',
296
+ moduleId: 'catalog',
297
+ allowedTools: ['catalog.list_products'],
298
+ }),
299
+ ])
300
+ registerMcpTool(
301
+ makeTool({
302
+ name: 'catalog.list_products',
303
+ requiredFeatures: ['catalog.products.view'],
304
+ }),
305
+ )
306
+ // Tool not whitelisted by the agent; MUST NOT appear in the SDK args.
307
+ registerMcpTool(
308
+ makeTool({
309
+ name: 'catalog.update_product',
310
+ isMutation: true,
311
+ requiredFeatures: ['catalog.products.manage'],
312
+ }),
313
+ )
314
+
315
+ await runAiAgentText({
316
+ agentId: 'catalog.assistant',
317
+ messages: [
318
+ { role: 'user', id: 'm1', parts: [{ type: 'text' as const, text: 'hi' }] } as never,
319
+ ],
320
+ authContext: makeAuth({ isSuperAdmin: true }),
321
+ })
322
+
323
+ expect(streamTextMock).toHaveBeenCalledTimes(1)
324
+ const sdkArg = streamTextMock.mock.calls[0]?.[0] as Record<string, unknown> | undefined
325
+ expect(sdkArg).toBeDefined()
326
+ const toolsArg = (sdkArg?.tools ?? {}) as Record<string, unknown>
327
+ expect(Object.keys(toolsArg)).toEqual(['catalog__list_products'])
328
+ expect(Object.prototype.hasOwnProperty.call(toolsArg, 'catalog__update_product')).toBe(false)
329
+ })
330
+ })