@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,448 @@
1
+ import { createHash } from 'node:crypto'
2
+ import type { AwilixContainer } from 'awilix'
3
+ import type { EntityManager } from '@mikro-orm/postgresql'
4
+ import type { AiAgentDefinition, AiAgentMutationPolicy } from './ai-agent-definition'
5
+ import type { AiChatRequestContext, AiUiPart } from './attachment-bridge-types'
6
+ import type {
7
+ AiToolDefinition,
8
+ AiToolLoadBeforeRecord,
9
+ AiToolLoadBeforeSingleRecord,
10
+ McpToolContext,
11
+ } from './types'
12
+ import { resolveEffectiveMutationPolicy } from './agent-policy'
13
+ import { AiPendingActionRepository } from '../data/repositories/AiPendingActionRepository'
14
+ import type { AiPendingAction } from '../data/entities'
15
+ import type {
16
+ AiPendingActionFieldDiff,
17
+ AiPendingActionRecordDiff,
18
+ } from './pending-action-types'
19
+
20
+ /**
21
+ * Structured error raised by {@link prepareMutation}. Callers (today the
22
+ * agent-runtime tool wrapper installed by `resolveAiAgentTools`) turn this
23
+ * into a tool-call failure that the model surfaces back to the user without
24
+ * leaking internals. The runtime NEVER reaches this helper when the agent
25
+ * is declared read-only — the policy gate rejects the tool call upstream —
26
+ * but we keep the fail-closed check as a defensive guard.
27
+ */
28
+ export class AiMutationPreparationError extends Error {
29
+ constructor(
30
+ public readonly code:
31
+ | 'not_a_mutation_tool'
32
+ | 'read_only_agent'
33
+ | 'tenant_scope_missing'
34
+ | 'container_missing'
35
+ | 'em_missing',
36
+ message: string,
37
+ ) {
38
+ super(message)
39
+ this.name = 'AiMutationPreparationError'
40
+ }
41
+ }
42
+
43
+ export interface PrepareMutationInput {
44
+ agent: AiAgentDefinition
45
+ tool: AiToolDefinition
46
+ toolCallArgs: Record<string, unknown>
47
+ conversationId?: string | null
48
+ /**
49
+ * Optional downgrade the caller already resolved (mirror of
50
+ * `resolveAiAgentTools({ mutationPolicyOverride })`). When omitted, the
51
+ * agent's code-declared policy stands alone.
52
+ */
53
+ mutationPolicyOverride?: AiAgentMutationPolicy | null
54
+ /**
55
+ * Deterministic clock hook for tests. Defaults to `new Date()`.
56
+ */
57
+ now?: Date
58
+ }
59
+
60
+ export interface PrepareMutationContext extends AiChatRequestContext {
61
+ container: AwilixContainer
62
+ }
63
+
64
+ export interface PrepareMutationResult {
65
+ uiPart: AiUiPart
66
+ pendingAction: AiPendingAction
67
+ }
68
+
69
+ const MUTATION_PREVIEW_CARD_COMPONENT_ID = 'mutation-preview-card'
70
+
71
+ const NO_RESOLVER_SIDE_EFFECTS_MESSAGE =
72
+ 'Tool did not declare a field-diff resolver; action will proceed without a preview.'
73
+
74
+ function assertTenantScope(ctx: PrepareMutationContext): string {
75
+ if (!ctx.tenantId) {
76
+ throw new AiMutationPreparationError(
77
+ 'tenant_scope_missing',
78
+ 'prepareMutation requires a tenant-scoped request context.',
79
+ )
80
+ }
81
+ return ctx.tenantId
82
+ }
83
+
84
+ function resolveEm(container: AwilixContainer): EntityManager {
85
+ if (!container) {
86
+ throw new AiMutationPreparationError(
87
+ 'container_missing',
88
+ 'prepareMutation requires an Awilix container to resolve the EntityManager.',
89
+ )
90
+ }
91
+ let em: EntityManager | null = null
92
+ try {
93
+ em = container.resolve<EntityManager>('em')
94
+ } catch {
95
+ em = null
96
+ }
97
+ if (!em) {
98
+ throw new AiMutationPreparationError(
99
+ 'em_missing',
100
+ 'prepareMutation could not resolve "em" from the container.',
101
+ )
102
+ }
103
+ return em
104
+ }
105
+
106
+ function toolHandlerContext(ctx: PrepareMutationContext): McpToolContext {
107
+ return {
108
+ tenantId: ctx.tenantId,
109
+ organizationId: ctx.organizationId,
110
+ userId: ctx.userId,
111
+ container: ctx.container,
112
+ userFeatures: ctx.features,
113
+ isSuperAdmin: ctx.isSuperAdmin,
114
+ }
115
+ }
116
+
117
+ function safeStringify(value: unknown): string {
118
+ const seen = new WeakSet<object>()
119
+ return JSON.stringify(value, (_key, raw) => {
120
+ if (raw && typeof raw === 'object') {
121
+ if (seen.has(raw as object)) return '[Circular]'
122
+ seen.add(raw as object)
123
+ const entries = Object.entries(raw as Record<string, unknown>)
124
+ entries.sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0))
125
+ return entries.reduce<Record<string, unknown>>((acc, [k, v]) => {
126
+ acc[k] = v
127
+ return acc
128
+ }, {})
129
+ }
130
+ return raw
131
+ })
132
+ }
133
+
134
+ /**
135
+ * Hashes `(tenantId, orgId, agentId, conversationId, toolName, normalizedInput)`
136
+ * into a stable SHA-256 digest so that retries of the same tool call with the
137
+ * same payload collapse to a single `AiPendingAction` row inside the TTL
138
+ * window. The input is normalized through `safeStringify` to make object key
139
+ * order irrelevant (spec §8 rule `idempotencyKey prevents double-submission`).
140
+ * Attachments are NOT included — the attachment set is captured separately on
141
+ * the pending row so that re-uploading the same file set with a different
142
+ * tool-call object never accidentally collides.
143
+ */
144
+ export function computeMutationIdempotencyKey(input: {
145
+ tenantId: string
146
+ organizationId: string | null
147
+ agentId: string
148
+ conversationId: string | null
149
+ toolName: string
150
+ normalizedInput: Record<string, unknown>
151
+ }): string {
152
+ const canonical = safeStringify({
153
+ tenant: input.tenantId,
154
+ org: input.organizationId ?? null,
155
+ agent: input.agentId,
156
+ conversation: input.conversationId ?? null,
157
+ tool: input.toolName,
158
+ input: input.normalizedInput ?? {},
159
+ })
160
+ return createHash('sha256').update(canonical).digest('hex')
161
+ }
162
+
163
+ function computeFieldDiff(
164
+ before: Record<string, unknown>,
165
+ after: Record<string, unknown>,
166
+ ): AiPendingActionFieldDiff[] {
167
+ const diff: AiPendingActionFieldDiff[] = []
168
+ const keys = new Set<string>([
169
+ ...Object.keys(before ?? {}),
170
+ ...Object.keys(after ?? {}),
171
+ ])
172
+ for (const field of keys) {
173
+ const beforeValue = before ? before[field] : undefined
174
+ const afterValue = after ? after[field] : undefined
175
+ if (!Object.is(beforeValue, afterValue) && safeStringify(beforeValue) !== safeStringify(afterValue)) {
176
+ diff.push({ field, before: beforeValue, after: afterValue })
177
+ }
178
+ }
179
+ return diff
180
+ }
181
+
182
+ function extractPatchFromArgs(
183
+ args: Record<string, unknown>,
184
+ ): Record<string, unknown> {
185
+ const raw = args?.patch
186
+ if (raw && typeof raw === 'object' && !Array.isArray(raw)) {
187
+ return raw as Record<string, unknown>
188
+ }
189
+ // Fall back: treat the whole args object (minus well-known envelope keys)
190
+ // as the patch. This preserves compatibility with tools whose schema is
191
+ // flat (`{ productId, name }`) rather than nested (`{ productId, patch }`).
192
+ const envelope = new Set([
193
+ 'id',
194
+ 'recordId',
195
+ 'records',
196
+ 'attachmentIds',
197
+ '_sessionToken',
198
+ ])
199
+ const reduced: Record<string, unknown> = {}
200
+ for (const [key, value] of Object.entries(args ?? {})) {
201
+ if (envelope.has(key)) continue
202
+ reduced[key] = value
203
+ }
204
+ return reduced
205
+ }
206
+
207
+ function matchBatchPatch(
208
+ args: Record<string, unknown>,
209
+ recordId: string,
210
+ ): Record<string, unknown> {
211
+ const rawList = args?.records
212
+ if (Array.isArray(rawList)) {
213
+ const match = rawList.find((entry) => {
214
+ if (!entry || typeof entry !== 'object') return false
215
+ const candidate = entry as Record<string, unknown>
216
+ return candidate.recordId === recordId || candidate.id === recordId
217
+ })
218
+ if (match && typeof match === 'object') {
219
+ const patch = (match as Record<string, unknown>).patch
220
+ if (patch && typeof patch === 'object' && !Array.isArray(patch)) {
221
+ return patch as Record<string, unknown>
222
+ }
223
+ const envelope = new Set(['recordId', 'id'])
224
+ const reduced: Record<string, unknown> = {}
225
+ for (const [key, value] of Object.entries(match as Record<string, unknown>)) {
226
+ if (envelope.has(key)) continue
227
+ reduced[key] = value
228
+ }
229
+ return reduced
230
+ }
231
+ }
232
+ return {}
233
+ }
234
+
235
+ function normalizeAttachmentIds(args: Record<string, unknown>): string[] {
236
+ const raw = args?.attachmentIds
237
+ if (!Array.isArray(raw)) return []
238
+ return raw.filter((value): value is string => typeof value === 'string' && value.length > 0)
239
+ }
240
+
241
+ async function buildSingleRecordDiff(
242
+ tool: AiToolDefinition,
243
+ input: PrepareMutationInput,
244
+ ctx: PrepareMutationContext,
245
+ ): Promise<{
246
+ fieldDiff: AiPendingActionFieldDiff[]
247
+ targetEntityType: string | null
248
+ targetRecordId: string | null
249
+ recordVersion: string | null
250
+ sideEffectsSummary: string | null
251
+ }> {
252
+ const resolver = tool.loadBeforeRecord
253
+ if (!resolver) {
254
+ console.warn(
255
+ `[AI Agents] prepareMutation: tool "${tool.name}" declared isMutation=true but no loadBeforeRecord resolver; shipping empty fieldDiff.`,
256
+ )
257
+ return {
258
+ fieldDiff: [],
259
+ targetEntityType: null,
260
+ targetRecordId: null,
261
+ recordVersion: null,
262
+ sideEffectsSummary: NO_RESOLVER_SIDE_EFFECTS_MESSAGE,
263
+ }
264
+ }
265
+ const handlerContext = toolHandlerContext(ctx)
266
+ const before: AiToolLoadBeforeSingleRecord | null = await resolver(
267
+ input.toolCallArgs as never,
268
+ handlerContext,
269
+ )
270
+ if (!before) {
271
+ return {
272
+ fieldDiff: [],
273
+ targetEntityType: null,
274
+ targetRecordId: null,
275
+ recordVersion: null,
276
+ sideEffectsSummary: null,
277
+ }
278
+ }
279
+ const patch = extractPatchFromArgs(input.toolCallArgs)
280
+ const fieldDiff = computeFieldDiff(before.before, patch)
281
+ return {
282
+ fieldDiff,
283
+ targetEntityType: before.entityType,
284
+ targetRecordId: before.recordId,
285
+ recordVersion: before.recordVersion,
286
+ sideEffectsSummary: null,
287
+ }
288
+ }
289
+
290
+ async function buildBatchRecords(
291
+ tool: AiToolDefinition,
292
+ input: PrepareMutationInput,
293
+ ctx: PrepareMutationContext,
294
+ ): Promise<{
295
+ records: AiPendingActionRecordDiff[] | null
296
+ targetEntityType: string | null
297
+ sideEffectsSummary: string | null
298
+ }> {
299
+ const resolver = tool.loadBeforeRecords
300
+ if (!resolver) {
301
+ console.warn(
302
+ `[AI Agents] prepareMutation: bulk tool "${tool.name}" declared isMutation=true but no loadBeforeRecords resolver; shipping empty records[].`,
303
+ )
304
+ return {
305
+ records: null,
306
+ targetEntityType: null,
307
+ sideEffectsSummary: NO_RESOLVER_SIDE_EFFECTS_MESSAGE,
308
+ }
309
+ }
310
+ const handlerContext = toolHandlerContext(ctx)
311
+ const rows: AiToolLoadBeforeRecord[] = await resolver(
312
+ input.toolCallArgs as never,
313
+ handlerContext,
314
+ )
315
+ if (!Array.isArray(rows) || rows.length === 0) {
316
+ return {
317
+ records: null,
318
+ targetEntityType: null,
319
+ sideEffectsSummary: null,
320
+ }
321
+ }
322
+ const diffs: AiPendingActionRecordDiff[] = rows.map((row) => {
323
+ const patch = matchBatchPatch(input.toolCallArgs, row.recordId)
324
+ return {
325
+ recordId: row.recordId,
326
+ entityType: row.entityType,
327
+ label: row.label,
328
+ fieldDiff: computeFieldDiff(row.before, patch),
329
+ recordVersion: row.recordVersion ?? null,
330
+ }
331
+ })
332
+ const [firstEntity] = rows
333
+ return {
334
+ records: diffs,
335
+ targetEntityType: firstEntity ? firstEntity.entityType : null,
336
+ sideEffectsSummary: null,
337
+ }
338
+ }
339
+
340
+ /**
341
+ * Intercepts a mutation tool call and turns it into an `AiPendingAction` +
342
+ * `mutation-preview-card` UI part (spec Phase 3 WS-C §9). The caller MUST
343
+ * have already confirmed the agent's effective `mutationPolicy` is NOT
344
+ * `read-only`; this helper repeats the check defensively because skipping it
345
+ * would be a policy-bypass.
346
+ *
347
+ * The tool handler is NEVER invoked by this function — the write is
348
+ * short-circuited and only runs from the Step 5.8 confirm route. See the
349
+ * unit test `does not call the tool handler` for the guard.
350
+ */
351
+ export async function prepareMutation(
352
+ input: PrepareMutationInput,
353
+ ctx: PrepareMutationContext,
354
+ ): Promise<PrepareMutationResult> {
355
+ const { agent, tool } = input
356
+ if (tool.isMutation !== true) {
357
+ throw new AiMutationPreparationError(
358
+ 'not_a_mutation_tool',
359
+ `Tool "${tool.name}" is not a mutation tool; prepareMutation should not be invoked.`,
360
+ )
361
+ }
362
+ const effectivePolicy = resolveEffectiveMutationPolicy(
363
+ agent.mutationPolicy,
364
+ input.mutationPolicyOverride ?? null,
365
+ agent.id,
366
+ )
367
+ if (effectivePolicy === 'read-only') {
368
+ throw new AiMutationPreparationError(
369
+ 'read_only_agent',
370
+ `Agent "${agent.id}" has effective mutationPolicy=read-only; mutation tool "${tool.name}" cannot be prepared.`,
371
+ )
372
+ }
373
+
374
+ const tenantId = assertTenantScope(ctx)
375
+ const em = resolveEm(ctx.container)
376
+ const repo = new AiPendingActionRepository(em)
377
+
378
+ const isBulk = tool.isBulk === true
379
+ let fieldDiff: AiPendingActionFieldDiff[] = []
380
+ let records: AiPendingActionRecordDiff[] | null = null
381
+ let targetEntityType: string | null = null
382
+ let targetRecordId: string | null = null
383
+ let recordVersion: string | null = null
384
+ let sideEffectsSummary: string | null = null
385
+
386
+ if (isBulk) {
387
+ const batch = await buildBatchRecords(tool, input, ctx)
388
+ records = batch.records
389
+ targetEntityType = batch.targetEntityType
390
+ sideEffectsSummary = batch.sideEffectsSummary
391
+ } else {
392
+ const single = await buildSingleRecordDiff(tool, input, ctx)
393
+ fieldDiff = single.fieldDiff
394
+ targetEntityType = single.targetEntityType
395
+ targetRecordId = single.targetRecordId
396
+ recordVersion = single.recordVersion
397
+ sideEffectsSummary = single.sideEffectsSummary
398
+ }
399
+
400
+ const normalizedInput = input.toolCallArgs ?? {}
401
+ const conversationId = input.conversationId ?? null
402
+ const idempotencyKey = computeMutationIdempotencyKey({
403
+ tenantId,
404
+ organizationId: ctx.organizationId ?? null,
405
+ agentId: agent.id,
406
+ conversationId,
407
+ toolName: tool.name,
408
+ normalizedInput,
409
+ })
410
+
411
+ const pendingAction = await repo.create(
412
+ {
413
+ agentId: agent.id,
414
+ toolName: tool.name,
415
+ idempotencyKey,
416
+ createdByUserId: ctx.userId,
417
+ normalizedInput,
418
+ conversationId,
419
+ targetEntityType,
420
+ targetRecordId,
421
+ fieldDiff,
422
+ records,
423
+ sideEffectsSummary,
424
+ recordVersion,
425
+ attachmentIds: normalizeAttachmentIds(normalizedInput),
426
+ now: input.now,
427
+ },
428
+ {
429
+ tenantId,
430
+ organizationId: ctx.organizationId ?? null,
431
+ userId: ctx.userId,
432
+ },
433
+ )
434
+
435
+ const uiPart: AiUiPart = {
436
+ componentId: MUTATION_PREVIEW_CARD_COMPONENT_ID,
437
+ props: {
438
+ pendingActionId: pendingAction.id,
439
+ expiresAt: pendingAction.expiresAt.toISOString(),
440
+ ...(records ? { records } : { fieldDiff }),
441
+ ...(sideEffectsSummary ? { sideEffectsSummary } : {}),
442
+ },
443
+ }
444
+
445
+ return { uiPart, pendingAction }
446
+ }
447
+
448
+ export const MUTATION_PREVIEW_CARD_COMPONENT = MUTATION_PREVIEW_CARD_COMPONENT_ID
@@ -0,0 +1,24 @@
1
+ export type PromptSectionName =
2
+ | 'role'
3
+ | 'scope'
4
+ | 'data'
5
+ | 'tools'
6
+ | 'attachments'
7
+ | 'mutationPolicy'
8
+ | 'responseStyle'
9
+ | 'overrides'
10
+
11
+ export interface PromptSection {
12
+ name: PromptSectionName
13
+ content: string
14
+ order?: number
15
+ }
16
+
17
+ export interface PromptTemplate {
18
+ id: string
19
+ sections: PromptSection[]
20
+ }
21
+
22
+ export function definePromptTemplate(template: PromptTemplate): PromptTemplate {
23
+ return template
24
+ }