@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,573 @@
1
+ /**
2
+ * Step 3.6 — chat-mode / object-mode parity contract.
3
+ *
4
+ * The Step 3.4 (`agent-runtime.test.ts`, `agent-tools.test.ts`,
5
+ * `agent-transport.test.ts`) and Step 3.5 (`agent-runtime-object.test.ts`)
6
+ * suites exercise each helper in isolation. THIS suite guards the
7
+ * cross-cutting INVARIANT: `runAiAgentText` and `runAiAgentObject` share
8
+ * the exact same policy gate, tool filtering, prompt composition, and
9
+ * `resolvePageContext` pathway. A bug that makes one path diverge from
10
+ * the other MUST land here first.
11
+ *
12
+ * Each invariant is expressed as a `describe.each` row so the fixture is
13
+ * identical and only the helper under test varies. The one exception is
14
+ * the execution-mode gate (invariant #8), which is an inverse-pair by
15
+ * construction and therefore uses two paired tests instead.
16
+ */
17
+
18
+ import type { AiAgentDefinition, AiAgentPageContextInput } from '../ai-agent-definition'
19
+ import type { AiToolDefinition } from '../types'
20
+
21
+ const streamTextMock = jest.fn()
22
+ const generateObjectMock = jest.fn()
23
+ const streamObjectMock = jest.fn()
24
+ const stepCountIsMock = jest.fn((count: number) => ({ __stopWhen: 'stepCount', count }))
25
+ const convertToModelMessagesMock = jest.fn((messages: unknown) => messages)
26
+
27
+ jest.mock('ai', () => {
28
+ const actual = jest.requireActual('ai')
29
+ return {
30
+ ...actual,
31
+ streamText: (...args: unknown[]) => streamTextMock(...args),
32
+ generateObject: (...args: unknown[]) => generateObjectMock(...args),
33
+ streamObject: (...args: unknown[]) => streamObjectMock(...args),
34
+ stepCountIs: (...args: unknown[]) => stepCountIsMock(...(args as [number])),
35
+ convertToModelMessages: (...args: unknown[]) => convertToModelMessagesMock(...args),
36
+ }
37
+ })
38
+
39
+ const createModelMock = jest.fn(
40
+ (options: { modelId: string; apiKey: string }) => ({ id: options.modelId, apiKey: options.apiKey }),
41
+ )
42
+ const resolveApiKeyMock = jest.fn(() => 'test-api-key')
43
+
44
+ jest.mock('@open-mercato/shared/lib/ai/llm-provider-registry', () => ({
45
+ llmProviderRegistry: {
46
+ resolveFirstConfigured: () => ({
47
+ id: 'test-provider',
48
+ defaultModel: 'provider-default-model',
49
+ resolveApiKey: resolveApiKeyMock,
50
+ createModel: createModelMock,
51
+ }),
52
+ },
53
+ }))
54
+
55
+ import { z } from 'zod'
56
+ import {
57
+ resetAgentRegistryForTests,
58
+ seedAgentRegistryForTests,
59
+ } from '../agent-registry'
60
+ import { toolRegistry, registerMcpTool } from '../tool-registry'
61
+ import * as agentToolsModule from '../agent-tools'
62
+ import { AgentPolicyError } from '../agent-tools'
63
+ import { runAiAgentObject, runAiAgentText } from '../agent-runtime'
64
+
65
+ function makeAgent(
66
+ overrides: Partial<AiAgentDefinition> & Pick<AiAgentDefinition, 'id' | 'moduleId'>,
67
+ ): AiAgentDefinition {
68
+ return {
69
+ label: `${overrides.id} label`,
70
+ description: `${overrides.id} description`,
71
+ systemPrompt: 'System prompt base.',
72
+ allowedTools: [],
73
+ ...overrides,
74
+ }
75
+ }
76
+
77
+ function makeTool(
78
+ overrides: Partial<AiToolDefinition> & Pick<AiToolDefinition, 'name'>,
79
+ ): AiToolDefinition {
80
+ return {
81
+ description: `${overrides.name} description`,
82
+ inputSchema: z.object({}),
83
+ handler: async () => ({ ok: true }),
84
+ ...overrides,
85
+ }
86
+ }
87
+
88
+ const baseAuth = {
89
+ tenantId: 'tenant-1',
90
+ organizationId: 'org-1',
91
+ userId: 'user-1',
92
+ features: ['*'],
93
+ isSuperAdmin: true,
94
+ }
95
+
96
+ const baseMessages = [
97
+ { role: 'user' as const, id: 'm1', parts: [{ type: 'text' as const, text: 'hi' }] },
98
+ ]
99
+
100
+ function fakeStreamTextResult() {
101
+ const toTextStreamResponse = jest.fn(
102
+ () =>
103
+ new Response('streamed', {
104
+ status: 200,
105
+ headers: { 'Content-Type': 'text/event-stream' },
106
+ }),
107
+ )
108
+ const toUIMessageStreamResponse = jest.fn(
109
+ () =>
110
+ new Response('streamed', {
111
+ status: 200,
112
+ headers: { 'Content-Type': 'text/event-stream' },
113
+ }),
114
+ )
115
+ return { toTextStreamResponse, toUIMessageStreamResponse }
116
+ }
117
+
118
+ /**
119
+ * Parity schema used for object-mode runs. Declared on the agent so that
120
+ * BOTH helper paths exercise the same fixture — chat-mode ignores it,
121
+ * object-mode consumes it.
122
+ */
123
+ const paritySchema = z.object({ name: z.string() })
124
+ const parityOutput = { schemaName: 'ParityOutput', schema: paritySchema }
125
+
126
+ interface HelperSpec {
127
+ helper: 'text' | 'object'
128
+ run: (args: {
129
+ agentId: string
130
+ authContext: typeof baseAuth
131
+ pageContext?: Record<string, unknown>
132
+ modelOverride?: string
133
+ attachmentIds?: string[]
134
+ container?: object
135
+ }) => Promise<unknown>
136
+ /** Last-seen positional args on the SDK-mock call (so callers can inspect `system`, `model`, etc.). */
137
+ lastSdkCallArg: () => Record<string, unknown> | undefined
138
+ }
139
+
140
+ const textHelper: HelperSpec = {
141
+ helper: 'text',
142
+ run: ({ agentId, authContext, pageContext, modelOverride, attachmentIds, container }) =>
143
+ runAiAgentText({
144
+ agentId,
145
+ messages: baseMessages as never,
146
+ authContext,
147
+ pageContext,
148
+ modelOverride,
149
+ attachmentIds,
150
+ container: container as never,
151
+ }),
152
+ lastSdkCallArg: () => streamTextMock.mock.calls.at(-1)?.[0] as Record<string, unknown> | undefined,
153
+ }
154
+
155
+ const objectHelper: HelperSpec = {
156
+ helper: 'object',
157
+ run: ({ agentId, authContext, pageContext, modelOverride, attachmentIds, container }) =>
158
+ runAiAgentObject({
159
+ agentId,
160
+ input: 'hi',
161
+ authContext,
162
+ pageContext,
163
+ modelOverride,
164
+ attachmentIds,
165
+ container: container as never,
166
+ }),
167
+ lastSdkCallArg: () =>
168
+ generateObjectMock.mock.calls.at(-1)?.[0] as Record<string, unknown> | undefined,
169
+ }
170
+
171
+ const helpers: HelperSpec[] = [textHelper, objectHelper]
172
+
173
+ function resetMocks() {
174
+ jest.clearAllMocks()
175
+ resetAgentRegistryForTests()
176
+ toolRegistry.clear()
177
+ streamTextMock.mockImplementation(() => fakeStreamTextResult())
178
+ generateObjectMock.mockImplementation(async () => ({
179
+ object: { name: 'X' },
180
+ finishReason: 'stop',
181
+ usage: { inputTokens: 1, outputTokens: 1 },
182
+ }))
183
+ streamObjectMock.mockImplementation(() => ({
184
+ object: Promise.resolve({ name: 'X' }),
185
+ partialObjectStream: (async function* () {
186
+ yield { name: 'X' } as Partial<{ name: string }>
187
+ })(),
188
+ textStream: (async function* () {
189
+ yield '{"name":"X"}'
190
+ })(),
191
+ finishReason: Promise.resolve('stop' as string | undefined),
192
+ usage: Promise.resolve({ inputTokens: 1, outputTokens: 1 }),
193
+ }))
194
+ }
195
+
196
+ describe('agent runtime parity (chat-mode ≡ object-mode)', () => {
197
+ beforeEach(resetMocks)
198
+
199
+ afterAll(() => {
200
+ resetAgentRegistryForTests()
201
+ toolRegistry.clear()
202
+ })
203
+
204
+ describe.each(helpers)('$helper helper', ({ helper, run, lastSdkCallArg }) => {
205
+ it('rejects unknown agents with AgentPolicyError(agent_unknown)', async () => {
206
+ await expect(
207
+ run({ agentId: 'nonexistent.agent', authContext: baseAuth }),
208
+ ).rejects.toMatchObject({
209
+ name: 'AgentPolicyError',
210
+ code: 'agent_unknown',
211
+ })
212
+ expect(streamTextMock).not.toHaveBeenCalled()
213
+ expect(generateObjectMock).not.toHaveBeenCalled()
214
+ expect(streamObjectMock).not.toHaveBeenCalled()
215
+ })
216
+
217
+ it('rejects callers missing agent.requiredFeatures with agent_features_denied', async () => {
218
+ seedAgentRegistryForTests([
219
+ makeAgent({
220
+ id: 'customers.assistant',
221
+ moduleId: 'customers',
222
+ requiredFeatures: ['customers.assistant.use'],
223
+ output: parityOutput,
224
+ }),
225
+ ])
226
+
227
+ await expect(
228
+ run({
229
+ agentId: 'customers.assistant',
230
+ authContext: { ...baseAuth, features: [], isSuperAdmin: false },
231
+ }),
232
+ ).rejects.toMatchObject({
233
+ name: 'AgentPolicyError',
234
+ code: 'agent_features_denied',
235
+ })
236
+ })
237
+
238
+ it('super-admin bypasses requiredFeatures symmetrically', async () => {
239
+ seedAgentRegistryForTests([
240
+ makeAgent({
241
+ id: 'customers.assistant',
242
+ moduleId: 'customers',
243
+ requiredFeatures: ['customers.assistant.use'],
244
+ output: parityOutput,
245
+ }),
246
+ ])
247
+
248
+ await expect(
249
+ run({
250
+ agentId: 'customers.assistant',
251
+ authContext: { ...baseAuth, features: [], isSuperAdmin: true },
252
+ }),
253
+ ).resolves.toBeDefined()
254
+ })
255
+
256
+ it('filters out isMutation tools on read-only agents (warn + continue)', async () => {
257
+ const warnSpy = jest.spyOn(console, 'warn').mockImplementation(() => {})
258
+ registerMcpTool(
259
+ makeTool({ name: 'customers.update_person', isMutation: true }),
260
+ { moduleId: 'customers' },
261
+ )
262
+ registerMcpTool(
263
+ makeTool({ name: 'customers.list_people' }),
264
+ { moduleId: 'customers' },
265
+ )
266
+ seedAgentRegistryForTests([
267
+ makeAgent({
268
+ id: 'customers.assistant',
269
+ moduleId: 'customers',
270
+ readOnly: true,
271
+ allowedTools: ['customers.update_person', 'customers.list_people'],
272
+ output: parityOutput,
273
+ }),
274
+ ])
275
+
276
+ const resolveSpy = jest.spyOn(agentToolsModule, 'resolveAiAgentTools')
277
+
278
+ await expect(
279
+ run({ agentId: 'customers.assistant', authContext: baseAuth }),
280
+ ).resolves.toBeDefined()
281
+
282
+ expect(resolveSpy).toHaveBeenCalledTimes(1)
283
+ const resolved = await resolveSpy.mock.results[0].value
284
+ // Mutation tool filtered out; read-only tool survives.
285
+ expect(Object.keys(resolved.tools)).toEqual(['customers__list_people'])
286
+ expect(warnSpy).toHaveBeenCalledWith(
287
+ expect.stringContaining('customers.update_person'),
288
+ )
289
+
290
+ // Chat path forwards the filtered tools map to streamText; object path
291
+ // resolves tools for the policy gate but does not thread them into the
292
+ // SDK call (AI SDK v6 object entries accept no `tools` map). Either
293
+ // way, the mutation tool is never adapted for the model.
294
+ if (helper === 'text') {
295
+ const callArg = lastSdkCallArg() as { tools: Record<string, unknown> } | undefined
296
+ expect(callArg).toBeDefined()
297
+ expect(Object.keys(callArg!.tools)).toEqual(['customers__list_people'])
298
+ }
299
+
300
+ resolveSpy.mockRestore()
301
+ warnSpy.mockRestore()
302
+ })
303
+
304
+ it('invokes resolvePageContext when entityType + recordId are supplied and appends to the prompt', async () => {
305
+ const resolvePageContext = jest.fn(async (_input: AiAgentPageContextInput) => 'Hydrated ctx.')
306
+ seedAgentRegistryForTests([
307
+ makeAgent({
308
+ id: 'customers.assistant',
309
+ moduleId: 'customers',
310
+ resolvePageContext,
311
+ output: parityOutput,
312
+ }),
313
+ ])
314
+
315
+ await run({
316
+ agentId: 'customers.assistant',
317
+ authContext: baseAuth,
318
+ pageContext: { entityType: 'customers:person', recordId: 'p-1' },
319
+ container: {},
320
+ })
321
+
322
+ expect(resolvePageContext).toHaveBeenCalledTimes(1)
323
+ const callArg = lastSdkCallArg() as { system: string } | undefined
324
+ expect(callArg?.system).toBe('System prompt base.\n\nHydrated ctx.')
325
+ })
326
+
327
+ it('skips resolvePageContext silently when entityType or recordId are absent', async () => {
328
+ const resolvePageContext = jest.fn(async () => 'should-not-append')
329
+ seedAgentRegistryForTests([
330
+ makeAgent({
331
+ id: 'customers.assistant',
332
+ moduleId: 'customers',
333
+ resolvePageContext,
334
+ output: parityOutput,
335
+ }),
336
+ ])
337
+
338
+ await run({
339
+ agentId: 'customers.assistant',
340
+ authContext: baseAuth,
341
+ pageContext: { pageId: 'customers.people' },
342
+ container: {},
343
+ })
344
+
345
+ expect(resolvePageContext).not.toHaveBeenCalled()
346
+ const callArg = lastSdkCallArg() as { system: string } | undefined
347
+ expect(callArg?.system).toBe('System prompt base.')
348
+ })
349
+
350
+ it('does not fail the request if resolvePageContext throws', async () => {
351
+ const errorSpy = jest.spyOn(console, 'error').mockImplementation(() => {})
352
+ const resolvePageContext = jest.fn(async () => {
353
+ throw new Error('boom')
354
+ })
355
+ seedAgentRegistryForTests([
356
+ makeAgent({
357
+ id: 'customers.assistant',
358
+ moduleId: 'customers',
359
+ resolvePageContext,
360
+ output: parityOutput,
361
+ }),
362
+ ])
363
+
364
+ await expect(
365
+ run({
366
+ agentId: 'customers.assistant',
367
+ authContext: baseAuth,
368
+ pageContext: { entityType: 'customers:person', recordId: 'p-1' },
369
+ container: {},
370
+ }),
371
+ ).resolves.toBeDefined()
372
+
373
+ const callArg = lastSdkCallArg() as { system: string } | undefined
374
+ expect(callArg?.system).toBe('System prompt base.')
375
+ errorSpy.mockRestore()
376
+ })
377
+
378
+ it('prefers modelOverride over agent.defaultModel', async () => {
379
+ seedAgentRegistryForTests([
380
+ makeAgent({
381
+ id: 'customers.assistant',
382
+ moduleId: 'customers',
383
+ defaultModel: 'agent-default',
384
+ output: parityOutput,
385
+ }),
386
+ ])
387
+
388
+ await run({
389
+ agentId: 'customers.assistant',
390
+ authContext: baseAuth,
391
+ modelOverride: 'override-model',
392
+ })
393
+
394
+ expect(createModelMock).toHaveBeenCalledWith(
395
+ expect.objectContaining({ modelId: 'override-model' }),
396
+ )
397
+ })
398
+
399
+ it('falls back to agent.defaultModel when no override is given', async () => {
400
+ seedAgentRegistryForTests([
401
+ makeAgent({
402
+ id: 'customers.assistant',
403
+ moduleId: 'customers',
404
+ defaultModel: 'agent-default',
405
+ output: parityOutput,
406
+ }),
407
+ ])
408
+
409
+ await run({
410
+ agentId: 'customers.assistant',
411
+ authContext: baseAuth,
412
+ })
413
+
414
+ expect(createModelMock).toHaveBeenCalledWith(
415
+ expect.objectContaining({ modelId: 'agent-default' }),
416
+ )
417
+ })
418
+
419
+ it('passes attachmentIds unchanged to the tool-resolution layer (Phase-1 pass-through)', async () => {
420
+ seedAgentRegistryForTests([
421
+ makeAgent({
422
+ id: 'customers.assistant',
423
+ moduleId: 'customers',
424
+ output: parityOutput,
425
+ }),
426
+ ])
427
+
428
+ const resolveSpy = jest.spyOn(agentToolsModule, 'resolveAiAgentTools')
429
+
430
+ await run({
431
+ agentId: 'customers.assistant',
432
+ authContext: baseAuth,
433
+ attachmentIds: ['att-1', 'att-2'],
434
+ })
435
+
436
+ expect(resolveSpy).toHaveBeenCalledTimes(1)
437
+ expect(resolveSpy.mock.calls[0][0]).toMatchObject({
438
+ attachmentIds: ['att-1', 'att-2'],
439
+ })
440
+ resolveSpy.mockRestore()
441
+ })
442
+
443
+ it('never exposes a non-whitelisted tool to the SDK', async () => {
444
+ registerMcpTool(makeTool({ name: 'customers.list_people' }), { moduleId: 'customers' })
445
+ registerMcpTool(makeTool({ name: 'catalog.list_products' }), { moduleId: 'catalog' })
446
+ seedAgentRegistryForTests([
447
+ makeAgent({
448
+ id: 'customers.assistant',
449
+ moduleId: 'customers',
450
+ // Only one of the two registered tools is whitelisted.
451
+ allowedTools: ['customers.list_people'],
452
+ output: parityOutput,
453
+ }),
454
+ ])
455
+
456
+ const resolveSpy = jest.spyOn(agentToolsModule, 'resolveAiAgentTools')
457
+
458
+ await run({ agentId: 'customers.assistant', authContext: baseAuth })
459
+
460
+ const resolved = await resolveSpy.mock.results[0].value
461
+ expect(Object.keys(resolved.tools)).toEqual(['customers__list_people'])
462
+ expect(Object.keys(resolved.tools)).not.toContain('catalog__list_products')
463
+
464
+ if (helper === 'text') {
465
+ const callArg = lastSdkCallArg() as { tools: Record<string, unknown> } | undefined
466
+ expect(callArg).toBeDefined()
467
+ expect(Object.keys(callArg!.tools)).toEqual(['customers__list_people'])
468
+ expect(Object.keys(callArg!.tools)).not.toContain('catalog__list_products')
469
+ }
470
+
471
+ resolveSpy.mockRestore()
472
+ })
473
+ })
474
+
475
+ describe('execution-mode gate is symmetric-by-design', () => {
476
+ it('object-mode agent requested through runAiAgentText → execution_mode_not_supported', async () => {
477
+ seedAgentRegistryForTests([
478
+ makeAgent({
479
+ id: 'catalog.extractor',
480
+ moduleId: 'catalog',
481
+ executionMode: 'object',
482
+ output: parityOutput,
483
+ }),
484
+ ])
485
+
486
+ await expect(
487
+ runAiAgentText({
488
+ agentId: 'catalog.extractor',
489
+ messages: baseMessages as never,
490
+ authContext: baseAuth,
491
+ }),
492
+ ).rejects.toMatchObject({
493
+ name: 'AgentPolicyError',
494
+ code: 'execution_mode_not_supported',
495
+ })
496
+ expect(streamTextMock).not.toHaveBeenCalled()
497
+ })
498
+
499
+ it('chat-mode agent requested through runAiAgentObject → execution_mode_not_supported', async () => {
500
+ seedAgentRegistryForTests([
501
+ makeAgent({
502
+ id: 'customers.assistant',
503
+ moduleId: 'customers',
504
+ executionMode: 'chat',
505
+ }),
506
+ ])
507
+
508
+ await expect(
509
+ runAiAgentObject({
510
+ agentId: 'customers.assistant',
511
+ input: 'go',
512
+ authContext: baseAuth,
513
+ }),
514
+ ).rejects.toMatchObject({
515
+ name: 'AgentPolicyError',
516
+ code: 'execution_mode_not_supported',
517
+ })
518
+ expect(generateObjectMock).not.toHaveBeenCalled()
519
+ })
520
+ })
521
+ })
522
+
523
+ /**
524
+ * Type-level assertion: the policy gate accepts the identical
525
+ * `AiChatRequestContext` shape from both helpers. Changing one without
526
+ * the other is a compile-time error.
527
+ */
528
+ describe('type-level parity', () => {
529
+ it('both helpers accept the same AiChatRequestContext shape', () => {
530
+ const authContext = baseAuth
531
+ // Compile-time only — calling either with an incompatible shape would
532
+ // fail type-check. These refs keep the imports live.
533
+ void runAiAgentText
534
+ void runAiAgentObject
535
+ expect(authContext).toBeDefined()
536
+ })
537
+ })
538
+
539
+ /**
540
+ * Invariant snapshot — tests that would also fail if someone modifies
541
+ * `AgentPolicyError` to drop the deny code. Keeps the re-export honest.
542
+ */
543
+ describe('AgentPolicyError re-export parity', () => {
544
+ it('AgentPolicyError thrown by both helpers carries the same structural shape', async () => {
545
+ const errors: AgentPolicyError[] = []
546
+
547
+ try {
548
+ await runAiAgentText({
549
+ agentId: 'ghost.agent',
550
+ messages: baseMessages as never,
551
+ authContext: baseAuth,
552
+ })
553
+ } catch (error) {
554
+ errors.push(error as AgentPolicyError)
555
+ }
556
+
557
+ try {
558
+ await runAiAgentObject({
559
+ agentId: 'ghost.agent',
560
+ input: 'hi',
561
+ authContext: baseAuth,
562
+ })
563
+ } catch (error) {
564
+ errors.push(error as AgentPolicyError)
565
+ }
566
+
567
+ expect(errors).toHaveLength(2)
568
+ for (const error of errors) {
569
+ expect(error).toBeInstanceOf(AgentPolicyError)
570
+ expect(error.code).toBe('agent_unknown')
571
+ }
572
+ })
573
+ })