@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
@@ -1,5 +1,14 @@
1
1
  import { getToolRegistry } from "./tool-registry.js";
2
2
  import { hasRequiredFeatures } from "./auth.js";
3
+ function sanitizeEmptyStrings(input) {
4
+ if (!input || typeof input !== "object" || Array.isArray(input)) return input;
5
+ const result = {};
6
+ for (const [key, value] of Object.entries(input)) {
7
+ if (typeof value === "string" && value.trim() === "") continue;
8
+ result[key] = value;
9
+ }
10
+ return result;
11
+ }
3
12
  async function executeTool(toolName, input, context) {
4
13
  const registry = getToolRegistry();
5
14
  const tool = registry.getTool(toolName);
@@ -26,7 +35,8 @@ async function executeTool(toolName, input, context) {
26
35
  };
27
36
  }
28
37
  }
29
- const parseResult = tool.inputSchema.safeParse(input);
38
+ const sanitizedInput = sanitizeEmptyStrings(input);
39
+ const parseResult = tool.inputSchema.safeParse(sanitizedInput);
30
40
  if (!parseResult.success) {
31
41
  const issues = parseResult.error.issues ?? [];
32
42
  const errorMessages = issues.map(
@@ -38,8 +48,9 @@ async function executeTool(toolName, input, context) {
38
48
  errorCode: "VALIDATION_ERROR"
39
49
  };
40
50
  }
51
+ const handlerContext = { ...context, tool };
41
52
  try {
42
- const result = await tool.handler(parseResult.data, context);
53
+ const result = await tool.handler(parseResult.data, handlerContext);
43
54
  return { success: true, result };
44
55
  } catch (error) {
45
56
  const message = error instanceof Error ? error.message : String(error);
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/modules/ai_assistant/lib/tool-executor.ts"],
4
- "sourcesContent": ["import type { McpToolContext, ToolExecutionResult } from './types'\nimport { getToolRegistry } from './tool-registry'\nimport { hasRequiredFeatures } from './auth'\nimport type { RbacService } from '@open-mercato/core/modules/auth/services/rbacService'\n\n/**\n * Execute a tool with full context and ACL checks.\n */\nexport async function executeTool(\n toolName: string,\n input: unknown,\n context: McpToolContext\n): Promise<ToolExecutionResult> {\n const registry = getToolRegistry()\n const tool = registry.getTool(toolName)\n\n if (!tool) {\n return {\n success: false,\n error: `Tool \"${toolName}\" not found`,\n errorCode: 'NOT_FOUND',\n }\n }\n\n // ACL check\n if (tool.requiredFeatures?.length) {\n const rbacService = context.container.resolve<RbacService>('rbacService')\n const hasAccess = hasRequiredFeatures(\n tool.requiredFeatures,\n context.userFeatures,\n context.isSuperAdmin,\n rbacService\n )\n\n if (!hasAccess) {\n return {\n success: false,\n error: `Insufficient permissions for tool \"${toolName}\". Required: ${tool.requiredFeatures.join(', ')}`,\n errorCode: 'UNAUTHORIZED',\n }\n }\n }\n\n // Input validation\n const parseResult = tool.inputSchema.safeParse(input)\n if (!parseResult.success) {\n // Use any cast for Zod v4 compatibility\n const issues = (parseResult.error as any).issues ?? []\n const errorMessages = issues\n .map((issue: { path: PropertyKey[]; message: string }) =>\n `${issue.path.join('.')}: ${issue.message}`\n )\n .join('; ')\n return {\n success: false,\n error: `Invalid input: ${errorMessages || 'Validation failed'}`,\n errorCode: 'VALIDATION_ERROR',\n }\n }\n\n // Execute tool\n try {\n const result = await tool.handler(parseResult.data, context)\n return { success: true, result }\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error)\n console.error(`[MCP Tool] Error executing \"${toolName}\":`, error)\n return {\n success: false,\n error: message,\n errorCode: 'EXECUTION_ERROR',\n }\n }\n}\n"],
5
- "mappings": "AACA,SAAS,uBAAuB;AAChC,SAAS,2BAA2B;AAMpC,eAAsB,YACpB,UACA,OACA,SAC8B;AAC9B,QAAM,WAAW,gBAAgB;AACjC,QAAM,OAAO,SAAS,QAAQ,QAAQ;AAEtC,MAAI,CAAC,MAAM;AACT,WAAO;AAAA,MACL,SAAS;AAAA,MACT,OAAO,SAAS,QAAQ;AAAA,MACxB,WAAW;AAAA,IACb;AAAA,EACF;AAGA,MAAI,KAAK,kBAAkB,QAAQ;AACjC,UAAM,cAAc,QAAQ,UAAU,QAAqB,aAAa;AACxE,UAAM,YAAY;AAAA,MAChB,KAAK;AAAA,MACL,QAAQ;AAAA,MACR,QAAQ;AAAA,MACR;AAAA,IACF;AAEA,QAAI,CAAC,WAAW;AACd,aAAO;AAAA,QACL,SAAS;AAAA,QACT,OAAO,sCAAsC,QAAQ,gBAAgB,KAAK,iBAAiB,KAAK,IAAI,CAAC;AAAA,QACrG,WAAW;AAAA,MACb;AAAA,IACF;AAAA,EACF;AAGA,QAAM,cAAc,KAAK,YAAY,UAAU,KAAK;AACpD,MAAI,CAAC,YAAY,SAAS;AAExB,UAAM,SAAU,YAAY,MAAc,UAAU,CAAC;AACrD,UAAM,gBAAgB,OACnB;AAAA,MAAI,CAAC,UACJ,GAAG,MAAM,KAAK,KAAK,GAAG,CAAC,KAAK,MAAM,OAAO;AAAA,IAC3C,EACC,KAAK,IAAI;AACZ,WAAO;AAAA,MACL,SAAS;AAAA,MACT,OAAO,kBAAkB,iBAAiB,mBAAmB;AAAA,MAC7D,WAAW;AAAA,IACb;AAAA,EACF;AAGA,MAAI;AACF,UAAM,SAAS,MAAM,KAAK,QAAQ,YAAY,MAAM,OAAO;AAC3D,WAAO,EAAE,SAAS,MAAM,OAAO;AAAA,EACjC,SAAS,OAAO;AACd,UAAM,UAAU,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;AACrE,YAAQ,MAAM,+BAA+B,QAAQ,MAAM,KAAK;AAChE,WAAO;AAAA,MACL,SAAS;AAAA,MACT,OAAO;AAAA,MACP,WAAW;AAAA,IACb;AAAA,EACF;AACF;",
4
+ "sourcesContent": ["import type { McpToolContext, ToolExecutionResult } from './types'\nimport { getToolRegistry } from './tool-registry'\nimport { hasRequiredFeatures } from './auth'\nimport type { RbacService } from '@open-mercato/core/modules/auth/services/rbacService'\n\n/**\n * Strip empty strings from object values so LLM-generated `\"\"` for optional\n * fields becomes `undefined` (passes `.optional()` Zod validators).\n */\nfunction sanitizeEmptyStrings(input: unknown): unknown {\n if (!input || typeof input !== 'object' || Array.isArray(input)) return input\n const result: Record<string, unknown> = {}\n for (const [key, value] of Object.entries(input as Record<string, unknown>)) {\n if (typeof value === 'string' && value.trim() === '') continue\n result[key] = value\n }\n return result\n}\n\n/**\n * Execute a tool with full context and ACL checks.\n */\nexport async function executeTool(\n toolName: string,\n input: unknown,\n context: McpToolContext\n): Promise<ToolExecutionResult> {\n const registry = getToolRegistry()\n const tool = registry.getTool(toolName)\n\n if (!tool) {\n return {\n success: false,\n error: `Tool \"${toolName}\" not found`,\n errorCode: 'NOT_FOUND',\n }\n }\n\n // ACL check\n if (tool.requiredFeatures?.length) {\n const rbacService = context.container.resolve<RbacService>('rbacService')\n const hasAccess = hasRequiredFeatures(\n tool.requiredFeatures,\n context.userFeatures,\n context.isSuperAdmin,\n rbacService\n )\n\n if (!hasAccess) {\n return {\n success: false,\n error: `Insufficient permissions for tool \"${toolName}\". Required: ${tool.requiredFeatures.join(', ')}`,\n errorCode: 'UNAUTHORIZED',\n }\n }\n }\n\n // LLMs often send empty strings for optional fields (e.g., `personId: \"\"`).\n // Strip empty strings to `undefined` before Zod parsing so `.uuid().optional()`\n // fields pass validation when the model meant \"omit this field\".\n const sanitizedInput = sanitizeEmptyStrings(input)\n\n // Input validation\n const parseResult = tool.inputSchema.safeParse(sanitizedInput)\n if (!parseResult.success) {\n // Use any cast for Zod v4 compatibility\n const issues = (parseResult.error as any).issues ?? []\n const errorMessages = issues\n .map((issue: { path: PropertyKey[]; message: string }) =>\n `${issue.path.join('.')}: ${issue.message}`\n )\n .join('; ')\n return {\n success: false,\n error: `Invalid input: ${errorMessages || 'Validation failed'}`,\n errorCode: 'VALIDATION_ERROR',\n }\n }\n\n // Execute tool. Attach `tool` to the context so handlers that build an\n // `AiToolExecutionContext` (e.g. via `createAiApiOperationRunner`) keep their\n // route-gate coverage check working.\n const handlerContext: McpToolContext = { ...context, tool }\n try {\n const result = await tool.handler(parseResult.data, handlerContext)\n return { success: true, result }\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error)\n console.error(`[MCP Tool] Error executing \"${toolName}\":`, error)\n return {\n success: false,\n error: message,\n errorCode: 'EXECUTION_ERROR',\n }\n }\n}\n"],
5
+ "mappings": "AACA,SAAS,uBAAuB;AAChC,SAAS,2BAA2B;AAOpC,SAAS,qBAAqB,OAAyB;AACrD,MAAI,CAAC,SAAS,OAAO,UAAU,YAAY,MAAM,QAAQ,KAAK,EAAG,QAAO;AACxE,QAAM,SAAkC,CAAC;AACzC,aAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,KAAgC,GAAG;AAC3E,QAAI,OAAO,UAAU,YAAY,MAAM,KAAK,MAAM,GAAI;AACtD,WAAO,GAAG,IAAI;AAAA,EAChB;AACA,SAAO;AACT;AAKA,eAAsB,YACpB,UACA,OACA,SAC8B;AAC9B,QAAM,WAAW,gBAAgB;AACjC,QAAM,OAAO,SAAS,QAAQ,QAAQ;AAEtC,MAAI,CAAC,MAAM;AACT,WAAO;AAAA,MACL,SAAS;AAAA,MACT,OAAO,SAAS,QAAQ;AAAA,MACxB,WAAW;AAAA,IACb;AAAA,EACF;AAGA,MAAI,KAAK,kBAAkB,QAAQ;AACjC,UAAM,cAAc,QAAQ,UAAU,QAAqB,aAAa;AACxE,UAAM,YAAY;AAAA,MAChB,KAAK;AAAA,MACL,QAAQ;AAAA,MACR,QAAQ;AAAA,MACR;AAAA,IACF;AAEA,QAAI,CAAC,WAAW;AACd,aAAO;AAAA,QACL,SAAS;AAAA,QACT,OAAO,sCAAsC,QAAQ,gBAAgB,KAAK,iBAAiB,KAAK,IAAI,CAAC;AAAA,QACrG,WAAW;AAAA,MACb;AAAA,IACF;AAAA,EACF;AAKA,QAAM,iBAAiB,qBAAqB,KAAK;AAGjD,QAAM,cAAc,KAAK,YAAY,UAAU,cAAc;AAC7D,MAAI,CAAC,YAAY,SAAS;AAExB,UAAM,SAAU,YAAY,MAAc,UAAU,CAAC;AACrD,UAAM,gBAAgB,OACnB;AAAA,MAAI,CAAC,UACJ,GAAG,MAAM,KAAK,KAAK,GAAG,CAAC,KAAK,MAAM,OAAO;AAAA,IAC3C,EACC,KAAK,IAAI;AACZ,WAAO;AAAA,MACL,SAAS;AAAA,MACT,OAAO,kBAAkB,iBAAiB,mBAAmB;AAAA,MAC7D,WAAW;AAAA,IACb;AAAA,EACF;AAKA,QAAM,iBAAiC,EAAE,GAAG,SAAS,KAAK;AAC1D,MAAI;AACF,UAAM,SAAS,MAAM,KAAK,QAAQ,YAAY,MAAM,cAAc;AAClE,WAAO,EAAE,SAAS,MAAM,OAAO;AAAA,EACjC,SAAS,OAAO;AACd,UAAM,UAAU,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;AACrE,YAAQ,MAAM,+BAA+B,QAAQ,MAAM,KAAK;AAChE,WAAO;AAAA,MACL,SAAS;AAAA,MACT,OAAO;AAAA,MACP,WAAW;AAAA,IACb;AAAA,EACF;AACF;",
6
6
  "names": []
7
7
  }
@@ -1,6 +1,15 @@
1
1
  import { z } from "zod";
2
- import { registerMcpTool, getToolRegistry } from "./tool-registry.js";
2
+ import { registerMcpTool, getToolRegistry, toolRegistry, unregisterMcpTool } from "./tool-registry.js";
3
+ import {
4
+ applyToolOverrideMap,
5
+ composeToolOverrideMap
6
+ } from "./ai-overrides.js";
3
7
  import { ToolSearchService } from "./tool-search.js";
8
+ function isModuleAiTool(value) {
9
+ if (!value || typeof value !== "object") return false;
10
+ const candidate = value;
11
+ return typeof candidate.name === "string" && typeof candidate.description === "string" && candidate.inputSchema !== void 0 && typeof candidate.handler === "function";
12
+ }
4
13
  const contextWhoamiTool = {
5
14
  name: "context_whoami",
6
15
  description: "Get the current authentication context including tenant ID, organization ID, user ID, and available features. Use this to understand your current scope before performing operations.",
@@ -20,16 +29,70 @@ const contextWhoamiTool = {
20
29
  };
21
30
  function loadModuleTools(moduleId, tools) {
22
31
  for (const tool of tools) {
23
- registerMcpTool(
24
- {
25
- name: tool.name,
26
- description: tool.description,
27
- inputSchema: tool.inputSchema,
28
- requiredFeatures: tool.requiredFeatures,
29
- handler: tool.handler
30
- },
31
- { moduleId }
32
+ registerMcpTool(tool, { moduleId });
33
+ }
34
+ }
35
+ function registerGeneratedAiToolEntries(entries) {
36
+ let registered = 0;
37
+ for (const entry of entries) {
38
+ if (!entry || typeof entry.moduleId !== "string") continue;
39
+ if (!Array.isArray(entry.tools) || entry.tools.length === 0) continue;
40
+ for (const candidate of entry.tools) {
41
+ if (!isModuleAiTool(candidate)) {
42
+ console.warn(
43
+ `[MCP Tools] Skipping malformed AI tool in module "${entry.moduleId}"`
44
+ );
45
+ continue;
46
+ }
47
+ registerMcpTool(candidate, { moduleId: entry.moduleId });
48
+ registered += 1;
49
+ }
50
+ }
51
+ return registered;
52
+ }
53
+ function applyAiToolOverrideEntries(entries) {
54
+ const overrideMap = composeToolOverrideMap(entries);
55
+ if (Object.keys(overrideMap).length === 0) return;
56
+ const baseTools = toolRegistry.getTools();
57
+ const overridden = applyToolOverrideMap(baseTools, overrideMap);
58
+ for (const [name, value] of Object.entries(overrideMap)) {
59
+ if (value === null) {
60
+ unregisterMcpTool(name);
61
+ console.info(`[AI Overrides] Tool "${name}" disabled by override.`);
62
+ continue;
63
+ }
64
+ const next = overridden.get(name);
65
+ if (!next) continue;
66
+ registerMcpTool(next, { moduleId: "ai_overrides" });
67
+ console.info(`[AI Overrides] Tool "${name}" replaced by override.`);
68
+ }
69
+ }
70
+ async function loadGeneratedAiToolOverrides() {
71
+ let entries = [];
72
+ try {
73
+ const mod = await import("@/.mercato/generated/ai-tools.generated");
74
+ entries = Array.isArray(mod.aiToolOverrideEntries) ? mod.aiToolOverrideEntries : [];
75
+ } catch {
76
+ }
77
+ applyAiToolOverrideEntries(entries);
78
+ }
79
+ async function loadGeneratedModuleAiTools() {
80
+ try {
81
+ const mod = await import("@/.mercato/generated/ai-tools.generated");
82
+ const entries = Array.isArray(mod.aiToolConfigEntries) ? mod.aiToolConfigEntries : [];
83
+ const count = registerGeneratedAiToolEntries(entries);
84
+ try {
85
+ await loadGeneratedAiToolOverrides();
86
+ } catch (error) {
87
+ console.error("[AI Overrides] Failed to apply tool overrides:", error);
88
+ }
89
+ return count;
90
+ } catch (error) {
91
+ console.error(
92
+ "[MCP Tools] Could not load ai-tools.generated.ts (module tools unavailable):",
93
+ error
32
94
  );
95
+ return 0;
33
96
  }
34
97
  }
35
98
  async function loadAllModuleTools() {
@@ -42,6 +105,14 @@ async function loadAllModuleTools() {
42
105
  } catch (error) {
43
106
  console.error("[MCP Tools] Could not load Code Mode tools:", error);
44
107
  }
108
+ try {
109
+ const moduleToolCount = await loadGeneratedModuleAiTools();
110
+ console.error(
111
+ `[MCP Tools] Registered ${moduleToolCount} module-contributed AI tools from ai-tools.generated.ts`
112
+ );
113
+ } catch (error) {
114
+ console.error("[MCP Tools] Could not load module AI tools:", error);
115
+ }
45
116
  }
46
117
  async function indexToolsForSearch(searchService, force = false) {
47
118
  const registry = getToolRegistry();
@@ -64,9 +135,13 @@ function createToolSearchService(searchService) {
64
135
  return new ToolSearchService(searchService, registry);
65
136
  }
66
137
  export {
138
+ applyAiToolOverrideEntries,
67
139
  createToolSearchService,
68
140
  indexToolsForSearch,
69
141
  loadAllModuleTools,
70
- loadModuleTools
142
+ loadGeneratedAiToolOverrides,
143
+ loadGeneratedModuleAiTools,
144
+ loadModuleTools,
145
+ registerGeneratedAiToolEntries
71
146
  };
72
147
  //# sourceMappingURL=tool-loader.js.map
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/modules/ai_assistant/lib/tool-loader.ts"],
4
- "sourcesContent": ["import { z } from 'zod'\nimport type { SearchService } from '@open-mercato/search/service'\nimport { registerMcpTool, getToolRegistry } from './tool-registry'\nimport type { McpToolDefinition, McpToolContext } from './types'\nimport { ToolSearchService } from './tool-search'\n\n/**\n * Module tool definition as exported from ai-tools.ts files.\n */\ntype ModuleAiTool = {\n name: string\n description: string\n inputSchema: any\n requiredFeatures?: string[]\n handler: (input: any, ctx: any) => Promise<unknown>\n}\n\n/**\n * Built-in context.whoami tool that returns the current authentication context.\n * This is useful for AI to understand its current tenant/org scope.\n */\nconst contextWhoamiTool: McpToolDefinition = {\n name: 'context_whoami',\n description:\n 'Get the current authentication context including tenant ID, organization ID, user ID, and available features. Use this to understand your current scope before performing operations.',\n inputSchema: z.object({}),\n requiredFeatures: [], // No specific feature required - available to all authenticated users\n handler: async (_input: unknown, ctx: McpToolContext) => {\n return {\n tenantId: ctx.tenantId,\n organizationId: ctx.organizationId,\n userId: ctx.userId,\n isSuperAdmin: ctx.isSuperAdmin,\n features: ctx.userFeatures,\n featureCount: ctx.userFeatures.length,\n }\n },\n}\n\n/**\n * Load and register AI tools from a module's ai-tools.ts export.\n *\n * @param moduleId - The module identifier (e.g., 'search', 'customers')\n * @param tools - Array of tool definitions from the module\n */\nexport function loadModuleTools(moduleId: string, tools: ModuleAiTool[]): void {\n for (const tool of tools) {\n registerMcpTool(\n {\n name: tool.name,\n description: tool.description,\n inputSchema: tool.inputSchema,\n requiredFeatures: tool.requiredFeatures,\n handler: tool.handler,\n } as McpToolDefinition,\n { moduleId }\n )\n }\n}\n\n/**\n * Dynamically load tools from known module paths.\n * This is called during MCP server startup.\n */\nexport async function loadAllModuleTools(): Promise<void> {\n // 1. Register built-in tools\n registerMcpTool(contextWhoamiTool, { moduleId: 'context' })\n console.error('[MCP Tools] Registered built-in context_whoami tool')\n\n // 2. Register Code Mode tools (search + execute)\n // These two tools replace the previous api_discover, call_api, discover_schema,\n // and all module-specific AI tools. The AI writes JavaScript that runs in a\n // node:vm sandbox with access to the OpenAPI spec and api.request().\n try {\n const { loadCodeModeTools } = await import('./codemode-tools')\n const toolCount = await loadCodeModeTools()\n console.error(`[MCP Tools] Registered ${toolCount} Code Mode tools`)\n } catch (error) {\n console.error('[MCP Tools] Could not load Code Mode tools:', error)\n }\n\n // Note: Auto-discovered module AI tools (from ai-tools.generated.ts) and\n // legacy API discovery tools (find_api, call_api, discover_schema) are no\n // longer loaded. Code Mode's search + execute tools cover all use cases.\n}\n\n/**\n * Index all registered tools for hybrid search discovery.\n * This should be called after loadAllModuleTools() when the search service is available.\n *\n * @param searchService - The search service from DI container\n * @param force - Force re-indexing even if checksums match\n * @returns Indexing result with statistics\n */\nexport async function indexToolsForSearch(\n searchService: SearchService,\n force = false\n): Promise<{\n indexed: number\n skipped: number\n strategies: string[]\n checksum: string\n}> {\n const registry = getToolRegistry()\n const toolSearchService = new ToolSearchService(searchService, registry)\n\n try {\n const result = await toolSearchService.indexTools(force)\n\n console.error(`[MCP Tools] Indexed ${result.indexed} tools for search`)\n console.error(`[MCP Tools] Search strategies available: ${result.strategies.join(', ')}`)\n\n if (result.skipped > 0) {\n console.error(`[MCP Tools] Skipped ${result.skipped} tools (unchanged)`)\n }\n\n return result\n } catch (error) {\n console.error('[MCP Tools] Failed to index tools for search:', error)\n throw error\n }\n}\n\n/**\n * Create a ToolSearchService instance for tool discovery.\n * Use this to get a configured service for discovering relevant tools.\n *\n * @param searchService - The search service from DI container\n * @returns Configured ToolSearchService\n */\nexport function createToolSearchService(searchService: SearchService): ToolSearchService {\n const registry = getToolRegistry()\n return new ToolSearchService(searchService, registry)\n}\n"],
5
- "mappings": "AAAA,SAAS,SAAS;AAElB,SAAS,iBAAiB,uBAAuB;AAEjD,SAAS,yBAAyB;AAiBlC,MAAM,oBAAuC;AAAA,EAC3C,MAAM;AAAA,EACN,aACE;AAAA,EACF,aAAa,EAAE,OAAO,CAAC,CAAC;AAAA,EACxB,kBAAkB,CAAC;AAAA;AAAA,EACnB,SAAS,OAAO,QAAiB,QAAwB;AACvD,WAAO;AAAA,MACL,UAAU,IAAI;AAAA,MACd,gBAAgB,IAAI;AAAA,MACpB,QAAQ,IAAI;AAAA,MACZ,cAAc,IAAI;AAAA,MAClB,UAAU,IAAI;AAAA,MACd,cAAc,IAAI,aAAa;AAAA,IACjC;AAAA,EACF;AACF;AAQO,SAAS,gBAAgB,UAAkB,OAA6B;AAC7E,aAAW,QAAQ,OAAO;AACxB;AAAA,MACE;AAAA,QACE,MAAM,KAAK;AAAA,QACX,aAAa,KAAK;AAAA,QAClB,aAAa,KAAK;AAAA,QAClB,kBAAkB,KAAK;AAAA,QACvB,SAAS,KAAK;AAAA,MAChB;AAAA,MACA,EAAE,SAAS;AAAA,IACb;AAAA,EACF;AACF;AAMA,eAAsB,qBAAoC;AAExD,kBAAgB,mBAAmB,EAAE,UAAU,UAAU,CAAC;AAC1D,UAAQ,MAAM,qDAAqD;AAMnE,MAAI;AACF,UAAM,EAAE,kBAAkB,IAAI,MAAM,OAAO,kBAAkB;AAC7D,UAAM,YAAY,MAAM,kBAAkB;AAC1C,YAAQ,MAAM,0BAA0B,SAAS,kBAAkB;AAAA,EACrE,SAAS,OAAO;AACd,YAAQ,MAAM,+CAA+C,KAAK;AAAA,EACpE;AAKF;AAUA,eAAsB,oBACpB,eACA,QAAQ,OAMP;AACD,QAAM,WAAW,gBAAgB;AACjC,QAAM,oBAAoB,IAAI,kBAAkB,eAAe,QAAQ;AAEvE,MAAI;AACF,UAAM,SAAS,MAAM,kBAAkB,WAAW,KAAK;AAEvD,YAAQ,MAAM,uBAAuB,OAAO,OAAO,mBAAmB;AACtE,YAAQ,MAAM,4CAA4C,OAAO,WAAW,KAAK,IAAI,CAAC,EAAE;AAExF,QAAI,OAAO,UAAU,GAAG;AACtB,cAAQ,MAAM,uBAAuB,OAAO,OAAO,oBAAoB;AAAA,IACzE;AAEA,WAAO;AAAA,EACT,SAAS,OAAO;AACd,YAAQ,MAAM,iDAAiD,KAAK;AACpE,UAAM;AAAA,EACR;AACF;AASO,SAAS,wBAAwB,eAAiD;AACvF,QAAM,WAAW,gBAAgB;AACjC,SAAO,IAAI,kBAAkB,eAAe,QAAQ;AACtD;",
4
+ "sourcesContent": ["import { z } from 'zod'\nimport type { SearchService } from '@open-mercato/search/service'\nimport { registerMcpTool, getToolRegistry, toolRegistry, unregisterMcpTool } from './tool-registry'\nimport {\n applyToolOverrideMap,\n composeToolOverrideMap,\n type AiToolOverrideConfigEntry,\n} from './ai-overrides'\nimport type { McpToolDefinition, McpToolContext } from './types'\nimport { ToolSearchService } from './tool-search'\n\n/**\n * Module tool definition as exported from ai-tools.ts files.\n */\ntype ModuleAiTool = {\n name: string\n description: string\n inputSchema: any\n requiredFeatures?: string[]\n handler: (input: any, ctx: any) => Promise<unknown>\n}\n\n/**\n * Shape of a single entry inside `ai-tools.generated.ts`.\n * Matches the structural contract emitted by\n * `packages/cli/src/lib/generators/extensions/ai-tools.ts`.\n */\nexport type AiToolConfigEntry = {\n moduleId: string\n tools: unknown[]\n}\n\nfunction isModuleAiTool(value: unknown): value is ModuleAiTool {\n if (!value || typeof value !== 'object') return false\n const candidate = value as Record<string, unknown>\n return (\n typeof candidate.name === 'string' &&\n typeof candidate.description === 'string' &&\n candidate.inputSchema !== undefined &&\n typeof candidate.handler === 'function'\n )\n}\n\n/**\n * Built-in context.whoami tool that returns the current authentication context.\n * This is useful for AI to understand its current tenant/org scope.\n */\nconst contextWhoamiTool: McpToolDefinition = {\n name: 'context_whoami',\n description:\n 'Get the current authentication context including tenant ID, organization ID, user ID, and available features. Use this to understand your current scope before performing operations.',\n inputSchema: z.object({}),\n requiredFeatures: [], // No specific feature required - available to all authenticated users\n handler: async (_input: unknown, ctx: McpToolContext) => {\n return {\n tenantId: ctx.tenantId,\n organizationId: ctx.organizationId,\n userId: ctx.userId,\n isSuperAdmin: ctx.isSuperAdmin,\n features: ctx.userFeatures,\n featureCount: ctx.userFeatures.length,\n }\n },\n}\n\n/**\n * Load and register AI tools from a module's ai-tools.ts export.\n *\n * @param moduleId - The module identifier (e.g., 'search', 'customers')\n * @param tools - Array of tool definitions from the module\n *\n * IMPORTANT: We register the full typed tool (spread) \u2014 never reconstruct a\n * minimal `{ name, description, inputSchema, requiredFeatures, handler }`\n * payload. Stripping fields like `isMutation`, `displayName`, `isBulk`,\n * `loadBeforeRecord(s)` makes the agent settings UI report mutation tools\n * as read-only and prevents the chat dispatcher's mutation interceptor\n * from firing.\n */\nexport function loadModuleTools(moduleId: string, tools: ModuleAiTool[]): void {\n for (const tool of tools) {\n registerMcpTool(tool as McpToolDefinition, { moduleId })\n }\n}\n\n/**\n * Register the generated `aiToolConfigEntries` shape emitted by the\n * `ai-tools.generated.ts` generator extension. Entries with an empty\n * `tools` array stay silent (the generator already filters those out).\n * Invalid tool objects are skipped with a warning instead of throwing.\n *\n * Returns the number of tools actually registered so callers can log it.\n */\nexport function registerGeneratedAiToolEntries(entries: AiToolConfigEntry[]): number {\n let registered = 0\n for (const entry of entries) {\n if (!entry || typeof entry.moduleId !== 'string') continue\n if (!Array.isArray(entry.tools) || entry.tools.length === 0) continue\n for (const candidate of entry.tools) {\n if (!isModuleAiTool(candidate)) {\n console.warn(\n `[MCP Tools] Skipping malformed AI tool in module \"${entry.moduleId}\"`\n )\n continue\n }\n // Register the full typed tool \u2014 see comment on `loadModuleTools`.\n registerMcpTool(candidate as McpToolDefinition, { moduleId: entry.moduleId })\n registered += 1\n }\n }\n return registered\n}\n\n/**\n * Apply the list of `aiToolOverrides` exports collected by the generator\n * (one entry per module) to the live tool registry. Tools mapped to\n * `null` are unregistered; tools mapped to a full definition replace the\n * existing registration. Module load order controls precedence \u2014 last\n * entry wins. The `modules.ts`-tier and programmatic overrides (set via\n * {@link applyAiOverridesFromEnabledModules} and\n * {@link applyAiToolOverrides}) supersede file-based entries.\n *\n * Safe to call when no override file is present (the entries array is\n * empty); it is a no-op then.\n */\nexport function applyAiToolOverrideEntries(\n entries: readonly AiToolOverrideConfigEntry[],\n): void {\n const overrideMap = composeToolOverrideMap(entries)\n if (Object.keys(overrideMap).length === 0) return\n const baseTools = toolRegistry.getTools() as Map<string, McpToolDefinition>\n const overridden = applyToolOverrideMap<McpToolDefinition>(baseTools, overrideMap)\n for (const [name, value] of Object.entries(overrideMap)) {\n if (value === null) {\n unregisterMcpTool(name)\n console.info(`[AI Overrides] Tool \"${name}\" disabled by override.`)\n continue\n }\n const next = overridden.get(name)\n if (!next) continue\n // Re-register through the public path so moduleMap stays consistent.\n registerMcpTool(next as McpToolDefinition, { moduleId: 'ai_overrides' })\n console.info(`[AI Overrides] Tool \"${name}\" replaced by override.`)\n }\n}\n\n/**\n * Read `aiToolOverrideEntries` from `ai-tools.generated.ts` and apply\n * them on top of the live tool registry. Safe to call when the file is\n * missing (pre-generate builds, tests) \u2014 applies only the\n * `modules.ts`-tier and programmatic overrides in that case.\n */\nexport async function loadGeneratedAiToolOverrides(): Promise<void> {\n let entries: AiToolOverrideConfigEntry[] = []\n try {\n const mod = (await import(\n '@/.mercato/generated/ai-tools.generated'\n )) as { aiToolOverrideEntries?: unknown[] }\n entries = Array.isArray(mod.aiToolOverrideEntries)\n ? (mod.aiToolOverrideEntries as AiToolOverrideConfigEntry[])\n : []\n } catch {\n // No override file generated.\n }\n applyAiToolOverrideEntries(entries)\n}\n\n/**\n * Load the generated `ai-tools.generated.ts` file emitted by\n * `yarn generate` and register every declared module tool through the\n * existing `registerMcpTool` path. Safe to call when the generated file\n * is missing (e.g., tests or pre-generate builds) \u2014 returns 0.\n */\nexport async function loadGeneratedModuleAiTools(): Promise<number> {\n try {\n const mod = (await import(\n '@/.mercato/generated/ai-tools.generated'\n )) as { aiToolConfigEntries?: AiToolConfigEntry[] }\n const entries = Array.isArray(mod.aiToolConfigEntries)\n ? mod.aiToolConfigEntries\n : []\n const count = registerGeneratedAiToolEntries(entries)\n // Apply module-to-module + programmatic tool overrides AFTER the base\n // registrations so disable / replace semantics work end-to-end.\n try {\n await loadGeneratedAiToolOverrides()\n } catch (error) {\n console.error('[AI Overrides] Failed to apply tool overrides:', error)\n }\n return count\n } catch (error) {\n console.error(\n '[MCP Tools] Could not load ai-tools.generated.ts (module tools unavailable):',\n error\n )\n return 0\n }\n}\n\n/**\n * Dynamically load tools from known module paths.\n * This is called during MCP server startup.\n */\nexport async function loadAllModuleTools(): Promise<void> {\n // 1. Register built-in tools\n registerMcpTool(contextWhoamiTool, { moduleId: 'context' })\n console.error('[MCP Tools] Registered built-in context_whoami tool')\n\n // 2. Register Code Mode tools (search + execute)\n // These two tools replace the previous api_discover, call_api, discover_schema,\n // and all module-specific AI tools. The AI writes JavaScript that runs in a\n // node:vm sandbox with access to the OpenAPI spec and api.request().\n try {\n const { loadCodeModeTools } = await import('./codemode-tools')\n const toolCount = await loadCodeModeTools()\n console.error(`[MCP Tools] Registered ${toolCount} Code Mode tools`)\n } catch (error) {\n console.error('[MCP Tools] Could not load Code Mode tools:', error)\n }\n\n // 3. Register module-contributed tools from ai-tools.generated.ts.\n // Code Mode stays untouched; module tools are additive. Missing\n // generated file is not fatal (pre-generate builds, tests).\n try {\n const moduleToolCount = await loadGeneratedModuleAiTools()\n console.error(\n `[MCP Tools] Registered ${moduleToolCount} module-contributed AI tools from ai-tools.generated.ts`\n )\n } catch (error) {\n console.error('[MCP Tools] Could not load module AI tools:', error)\n }\n}\n\n/**\n * Index all registered tools for hybrid search discovery.\n * This should be called after loadAllModuleTools() when the search service is available.\n *\n * @param searchService - The search service from DI container\n * @param force - Force re-indexing even if checksums match\n * @returns Indexing result with statistics\n */\nexport async function indexToolsForSearch(\n searchService: SearchService,\n force = false\n): Promise<{\n indexed: number\n skipped: number\n strategies: string[]\n checksum: string\n}> {\n const registry = getToolRegistry()\n const toolSearchService = new ToolSearchService(searchService, registry)\n\n try {\n const result = await toolSearchService.indexTools(force)\n\n console.error(`[MCP Tools] Indexed ${result.indexed} tools for search`)\n console.error(`[MCP Tools] Search strategies available: ${result.strategies.join(', ')}`)\n\n if (result.skipped > 0) {\n console.error(`[MCP Tools] Skipped ${result.skipped} tools (unchanged)`)\n }\n\n return result\n } catch (error) {\n console.error('[MCP Tools] Failed to index tools for search:', error)\n throw error\n }\n}\n\n/**\n * Create a ToolSearchService instance for tool discovery.\n * Use this to get a configured service for discovering relevant tools.\n *\n * @param searchService - The search service from DI container\n * @returns Configured ToolSearchService\n */\nexport function createToolSearchService(searchService: SearchService): ToolSearchService {\n const registry = getToolRegistry()\n return new ToolSearchService(searchService, registry)\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -0,0 +1,120 @@
1
+ const f = (fixture) => fixture;
2
+ const toolFixtures = {
3
+ // -------------------- ai_assistant module --------------------
4
+ "meta.describe_agent": f({
5
+ input: { agentId: "customers.account_assistant" }
6
+ }),
7
+ "attachments.list_record_attachments": f({
8
+ input: { entityType: "customers:person", recordId: "00000000-0000-0000-0000-000000000000", limit: 5 },
9
+ note: "Empty result is a valid response; we only assert shape."
10
+ }),
11
+ "attachments.read_attachment": f({
12
+ skip: true,
13
+ note: "Requires a real attachment id \u2014 covered by attachment-bridge integration tests."
14
+ }),
15
+ "search.hybrid_search": f({
16
+ input: { q: "demo", limit: 5 }
17
+ }),
18
+ "search.get_record_context": f({
19
+ skip: true,
20
+ note: "Requires a known record id from the search index; covered by hybrid_search tests."
21
+ }),
22
+ // -------------------- search module --------------------
23
+ search_query: f({ input: { q: "demo", limit: 5 } }),
24
+ search_status: f({ input: {} }),
25
+ search_get: f({ skip: true, note: "Requires a known indexed record id." }),
26
+ search_schema: f({ input: {} }),
27
+ search_aggregate: f({ skip: true, note: "Requires field config; covered by search module unit tests." }),
28
+ search_reindex: f({ skip: true, note: "Mutation tool \u2014 would trigger a real reindex job." }),
29
+ // -------------------- customers module --------------------
30
+ "customers.list_people": f({ input: { limit: 5 } }),
31
+ "customers.get_person": f({
32
+ skip: true,
33
+ note: "Underlying route packages/core/.../customers/api/people/[id]/route.js imports `next/server` which Node ESM cannot resolve from package dist. Bug \u2014 re-enable once the route is ESM-clean or routed through the operation runner with a different loader."
34
+ }),
35
+ "customers.list_companies": f({ input: { limit: 5 } }),
36
+ "customers.get_company": f({
37
+ skip: true,
38
+ note: "Same next/server ESM-resolution issue as customers.get_person."
39
+ }),
40
+ "customers.list_deals": f({ input: { limit: 5 } }),
41
+ "customers.get_deal": f({
42
+ skip: true,
43
+ note: "Same next/server ESM-resolution issue as customers.get_person."
44
+ }),
45
+ "customers.list_activities": f({ input: { limit: 5 } }),
46
+ "customers.list_tasks": f({ input: { limit: 5 } }),
47
+ "customers.list_addresses": f({
48
+ idFrom: "customers.list_people",
49
+ bindAs: "entityId",
50
+ extra: { entityType: "person" },
51
+ note: 'Schema requires { entityType: "person" | "company", entityId }.'
52
+ }),
53
+ "customers.list_tags": f({ input: { limit: 5 } }),
54
+ "customers.get_settings": f({ input: {} }),
55
+ "customers.update_deal_stage": f({
56
+ idFrom: "customers.list_deals",
57
+ bindAs: "dealId",
58
+ extra: { toStage: "open" },
59
+ note: "Mutation \u2014 schema requires exactly one of { toPipelineStageId, toStage }."
60
+ }),
61
+ // -------------------- catalog module --------------------
62
+ "catalog.list_products": f({ input: { limit: 5 } }),
63
+ "catalog.search_products": f({ input: { query: "demo", limit: 5 } }),
64
+ "catalog.get_product": f({ idFrom: "catalog.list_products", bindAs: "productId" }),
65
+ "catalog.get_product_bundle": f({ idFrom: "catalog.list_products", bindAs: "productId" }),
66
+ "catalog.list_categories": f({ input: { limit: 10 } }),
67
+ "catalog.list_variants": f({ idFrom: "catalog.list_products", bindAs: "productId" }),
68
+ "catalog.list_offers": f({
69
+ skip: true,
70
+ note: "Tool requiredFeatures do not cover the underlying GET /catalog/offers route requiredFeatures \u2014 real ACL config drift. Re-enable after fixing catalog.list_offers requiredFeatures to include the route gates."
71
+ }),
72
+ "catalog.list_media": f({ idFrom: "catalog.list_products", bindAs: "productId" }),
73
+ "catalog.list_tags": f({ input: { limit: 10 } }),
74
+ "catalog.get_configuration": f({ input: {} }),
75
+ // Authoring/structured-output tools — they call the LLM, skip in test mode
76
+ // so TC-INT-AI-TOOLS still passes in CI without provider API keys.
77
+ "catalog.draft_description_from_attributes": f({ skip: true, note: "LLM-backed authoring tool." }),
78
+ "catalog.extract_attributes_from_description": f({ skip: true, note: "LLM-backed authoring tool." }),
79
+ "catalog.draft_description_from_media": f({ skip: true, note: "LLM-backed authoring tool." }),
80
+ "catalog.suggest_title_variants": f({ skip: true, note: "LLM-backed authoring tool." }),
81
+ "catalog.suggest_price_adjustment": f({ skip: true, note: "LLM-backed authoring tool." }),
82
+ // Mutation tools — exercised via prepareMutation only.
83
+ "catalog.update_product": f({
84
+ idFrom: "catalog.list_products",
85
+ bindAs: "productId",
86
+ extra: { subtitle: "tool-test-runner-noop" },
87
+ note: "Single optional field tweak; prepareMutation produces a preview card."
88
+ }),
89
+ "catalog.bulk_update_products": f({
90
+ skip: true,
91
+ note: "Bulk mutation; needs records[] with versions \u2014 covered by catalog merchandising integration test."
92
+ }),
93
+ "catalog.apply_attribute_extraction": f({
94
+ skip: true,
95
+ note: "Bulk mutation tied to extract_attributes output."
96
+ }),
97
+ "catalog.update_product_media_descriptions": f({
98
+ skip: true,
99
+ note: "Bulk mutation tied to draft_media_descriptions output."
100
+ }),
101
+ // -------------------- inbox_ops module --------------------
102
+ inbox_ops_list_proposals: f({ input: { limit: 5 } }),
103
+ inbox_ops_get_proposal: f({ idFrom: "inbox_ops_list_proposals" }),
104
+ inbox_ops_categorize_email: f({
105
+ skip: true,
106
+ note: "Requires a queued email payload; LLM-backed."
107
+ }),
108
+ inbox_ops_accept_action: f({
109
+ skip: true,
110
+ note: "Mutation that finalizes a proposal; covered by inbox_ops integration tests."
111
+ })
112
+ };
113
+ function getFixture(toolName) {
114
+ return toolFixtures[toolName];
115
+ }
116
+ export {
117
+ getFixture,
118
+ toolFixtures
119
+ };
120
+ //# sourceMappingURL=tool-test-fixtures.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../src/modules/ai_assistant/lib/tool-test-fixtures.ts"],
4
+ "sourcesContent": ["/**\n * Per-tool sample inputs used by `tool-test-runner.ts` (see also the\n * `test-tools` CLI subcommand and `TC-INT-AI-TOOLS.spec.ts`).\n *\n * Inputs are deliberately conservative \u2014 every entry is the smallest valid\n * shape that exercises the handler against demo-seeded data. ID-shaped tools\n * declare `idFrom` so the runner first runs the sibling list tool and threads\n * the discovered id through; this avoids hand-rolling fixture UUIDs that go\n * stale every time the demo seed regenerates.\n *\n * Tools without a fixture are skipped with reason `'no fixture'` rather than\n * failing \u2014 keeps the test green for newly added tools until a maintainer\n * adds an entry. Mutation tools share this map and are exercised through\n * `prepareMutation`; the runner asserts they return a pending-action envelope\n * and never confirms the action.\n */\n\nexport type ToolFixtureValueRef = { idFrom: string; field?: string }\n\nexport type ToolFixtureInput = Record<string, unknown> | ToolFixtureValueRef\n\nexport interface ToolFixture {\n /** When `true` the runner skips this tool with reason `'skip-by-fixture'`. */\n skip?: boolean\n /** Static input passed to the handler. Overrides `idFrom`-based inputs. */\n input?: Record<string, unknown>\n /**\n * When set, the runner calls the named list tool first, picks the first\n * record, reads `field` (default `id`) from it, and merges it into the input\n * under the key declared by `bindAs` (default `id`).\n */\n idFrom?: string\n bindAs?: string\n /** Optional extra static fields merged on top of the chained input. */\n extra?: Record<string, unknown>\n /** Free-form note explaining the fixture choice. */\n note?: string\n}\n\nconst f = (fixture: ToolFixture): ToolFixture => fixture\n\nexport const toolFixtures: Record<string, ToolFixture> = {\n // -------------------- ai_assistant module --------------------\n 'meta.describe_agent': f({\n input: { agentId: 'customers.account_assistant' },\n }),\n 'attachments.list_record_attachments': f({\n input: { entityType: 'customers:person', recordId: '00000000-0000-0000-0000-000000000000', limit: 5 },\n note: 'Empty result is a valid response; we only assert shape.',\n }),\n 'attachments.read_attachment': f({\n skip: true,\n note: 'Requires a real attachment id \u2014 covered by attachment-bridge integration tests.',\n }),\n 'search.hybrid_search': f({\n input: { q: 'demo', limit: 5 },\n }),\n 'search.get_record_context': f({\n skip: true,\n note: 'Requires a known record id from the search index; covered by hybrid_search tests.',\n }),\n\n // -------------------- search module --------------------\n search_query: f({ input: { q: 'demo', limit: 5 } }),\n search_status: f({ input: {} }),\n search_get: f({ skip: true, note: 'Requires a known indexed record id.' }),\n search_schema: f({ input: {} }),\n search_aggregate: f({ skip: true, note: 'Requires field config; covered by search module unit tests.' }),\n search_reindex: f({ skip: true, note: 'Mutation tool \u2014 would trigger a real reindex job.' }),\n\n // -------------------- customers module --------------------\n 'customers.list_people': f({ input: { limit: 5 } }),\n 'customers.get_person': f({\n skip: true,\n note: 'Underlying route packages/core/.../customers/api/people/[id]/route.js imports `next/server` which Node ESM cannot resolve from package dist. Bug \u2014 re-enable once the route is ESM-clean or routed through the operation runner with a different loader.',\n }),\n 'customers.list_companies': f({ input: { limit: 5 } }),\n 'customers.get_company': f({\n skip: true,\n note: 'Same next/server ESM-resolution issue as customers.get_person.',\n }),\n 'customers.list_deals': f({ input: { limit: 5 } }),\n 'customers.get_deal': f({\n skip: true,\n note: 'Same next/server ESM-resolution issue as customers.get_person.',\n }),\n 'customers.list_activities': f({ input: { limit: 5 } }),\n 'customers.list_tasks': f({ input: { limit: 5 } }),\n 'customers.list_addresses': f({\n idFrom: 'customers.list_people',\n bindAs: 'entityId',\n extra: { entityType: 'person' },\n note: 'Schema requires { entityType: \"person\" | \"company\", entityId }.',\n }),\n 'customers.list_tags': f({ input: { limit: 5 } }),\n 'customers.get_settings': f({ input: {} }),\n 'customers.update_deal_stage': f({\n idFrom: 'customers.list_deals',\n bindAs: 'dealId',\n extra: { toStage: 'open' },\n note: 'Mutation \u2014 schema requires exactly one of { toPipelineStageId, toStage }.',\n }),\n\n // -------------------- catalog module --------------------\n 'catalog.list_products': f({ input: { limit: 5 } }),\n 'catalog.search_products': f({ input: { query: 'demo', limit: 5 } }),\n 'catalog.get_product': f({ idFrom: 'catalog.list_products', bindAs: 'productId' }),\n 'catalog.get_product_bundle': f({ idFrom: 'catalog.list_products', bindAs: 'productId' }),\n 'catalog.list_categories': f({ input: { limit: 10 } }),\n 'catalog.list_variants': f({ idFrom: 'catalog.list_products', bindAs: 'productId' }),\n 'catalog.list_offers': f({\n skip: true,\n note: 'Tool requiredFeatures do not cover the underlying GET /catalog/offers route requiredFeatures \u2014 real ACL config drift. Re-enable after fixing catalog.list_offers requiredFeatures to include the route gates.',\n }),\n 'catalog.list_media': f({ idFrom: 'catalog.list_products', bindAs: 'productId' }),\n 'catalog.list_tags': f({ input: { limit: 10 } }),\n 'catalog.get_configuration': f({ input: {} }),\n // Authoring/structured-output tools \u2014 they call the LLM, skip in test mode\n // so TC-INT-AI-TOOLS still passes in CI without provider API keys.\n 'catalog.draft_description_from_attributes': f({ skip: true, note: 'LLM-backed authoring tool.' }),\n 'catalog.extract_attributes_from_description': f({ skip: true, note: 'LLM-backed authoring tool.' }),\n 'catalog.draft_description_from_media': f({ skip: true, note: 'LLM-backed authoring tool.' }),\n 'catalog.suggest_title_variants': f({ skip: true, note: 'LLM-backed authoring tool.' }),\n 'catalog.suggest_price_adjustment': f({ skip: true, note: 'LLM-backed authoring tool.' }),\n // Mutation tools \u2014 exercised via prepareMutation only.\n 'catalog.update_product': f({\n idFrom: 'catalog.list_products',\n bindAs: 'productId',\n extra: { subtitle: 'tool-test-runner-noop' },\n note: 'Single optional field tweak; prepareMutation produces a preview card.',\n }),\n 'catalog.bulk_update_products': f({\n skip: true,\n note: 'Bulk mutation; needs records[] with versions \u2014 covered by catalog merchandising integration test.',\n }),\n 'catalog.apply_attribute_extraction': f({\n skip: true,\n note: 'Bulk mutation tied to extract_attributes output.',\n }),\n 'catalog.update_product_media_descriptions': f({\n skip: true,\n note: 'Bulk mutation tied to draft_media_descriptions output.',\n }),\n\n // -------------------- inbox_ops module --------------------\n inbox_ops_list_proposals: f({ input: { limit: 5 } }),\n inbox_ops_get_proposal: f({ idFrom: 'inbox_ops_list_proposals' }),\n inbox_ops_categorize_email: f({\n skip: true,\n note: 'Requires a queued email payload; LLM-backed.',\n }),\n inbox_ops_accept_action: f({\n skip: true,\n note: 'Mutation that finalizes a proposal; covered by inbox_ops integration tests.',\n }),\n}\n\nexport function getFixture(toolName: string): ToolFixture | undefined {\n return toolFixtures[toolName]\n}\n"],
5
+ "mappings": "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",
6
+ "names": []
7
+ }