@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,424 @@
1
+ /**
2
+ * Pending-action executor (spec §9.4, Step 5.8).
3
+ *
4
+ * Transitions an `AiPendingAction` from `pending → confirmed → executing`,
5
+ * invokes the wrapped tool handler, and records the outcome. Isolated from
6
+ * the HTTP route so the unit suite can exercise the state-machine +
7
+ * event-emission + idempotency guarantees without constructing a
8
+ * `NextRequest`.
9
+ *
10
+ * Atomicity:
11
+ * - The `pending → confirmed` and `confirmed → executing` transitions go
12
+ * through the repository's `em.transactional` boundary. If the process
13
+ * crashes between steps, the row is left in an intermediate terminal
14
+ * state (`executing` or `confirmed`) that the operator can recover —
15
+ * NEVER in a partially-applied state that hides the crash.
16
+ * - The tool handler itself runs OUTSIDE the repo transaction so that a
17
+ * long-running write does not hold an `ai_pending_actions` row lock.
18
+ * The handler's own transaction boundary (typically a command) is the
19
+ * unit of atomicity for the underlying data change.
20
+ */
21
+ import { AiPendingActionRepository } from '../data/repositories/AiPendingActionRepository'
22
+ import type { AiPendingAction } from '../data/entities'
23
+ import { emitAiAssistantEvent } from '../events'
24
+ import type { AiActionConfirmedPayload } from '../events'
25
+ import type { AiAgentDefinition } from './ai-agent-definition'
26
+ import type { AiToolDefinition, McpToolContext } from './types'
27
+ import type {
28
+ AiPendingActionExecutionResult,
29
+ AiPendingActionFailedRecord,
30
+ } from './pending-action-types'
31
+
32
+ export interface PendingActionExecuteContext {
33
+ tenantId: string
34
+ organizationId: string | null
35
+ userId: string
36
+ userFeatures: string[]
37
+ isSuperAdmin: boolean
38
+ container: import('awilix').AwilixContainer
39
+ }
40
+
41
+ export interface PendingActionExecuteInput {
42
+ action: AiPendingAction
43
+ agent: AiAgentDefinition
44
+ tool: AiToolDefinition
45
+ ctx: PendingActionExecuteContext
46
+ /** Carried over from the re-check; written onto the row with status=confirmed. */
47
+ failedRecords?: AiPendingActionFailedRecord[] | null
48
+ repo?: AiPendingActionRepository
49
+ /**
50
+ * Injection seam for unit tests. When omitted, emission is routed via
51
+ * the typed `emitAiAssistantEvent` helper (the normal production path).
52
+ * When supplied, the raw bus is used directly — kept for legacy tests
53
+ * that assert on the bus call surface.
54
+ */
55
+ emitEvent?: (
56
+ eventId: 'ai.action.confirmed',
57
+ payload: AiActionConfirmedPayload,
58
+ ) => Promise<void>
59
+ now?: Date
60
+ }
61
+
62
+ export interface PendingActionExecuteOk {
63
+ ok: true
64
+ action: AiPendingAction
65
+ executionResult: AiPendingActionExecutionResult
66
+ }
67
+
68
+ export interface PendingActionExecuteFail {
69
+ ok: false
70
+ action: AiPendingAction
71
+ executionResult: AiPendingActionExecutionResult
72
+ /** The underlying error — the route translates into a 200 with `executionResult.error` set. */
73
+ cause: unknown
74
+ }
75
+
76
+ export type PendingActionExecuteResult = PendingActionExecuteOk | PendingActionExecuteFail
77
+
78
+ const CONFIRMED_EVENT_ID = 'ai.action.confirmed' as const
79
+
80
+ type ConfirmedEmitter = (
81
+ eventId: 'ai.action.confirmed',
82
+ payload: AiActionConfirmedPayload,
83
+ ) => Promise<void>
84
+
85
+ const defaultConfirmedEmitter: ConfirmedEmitter = async (eventId, payload) => {
86
+ await emitAiAssistantEvent(eventId, payload as unknown as Record<string, unknown>, {
87
+ persistent: true,
88
+ })
89
+ }
90
+
91
+ async function emitConfirmed(
92
+ emitter: ConfirmedEmitter,
93
+ payload: AiActionConfirmedPayload,
94
+ ): Promise<void> {
95
+ try {
96
+ await emitter(CONFIRMED_EVENT_ID, payload)
97
+ } catch (error) {
98
+ console.warn(`[AI Pending Action] Failed to emit ${CONFIRMED_EVENT_ID}:`, error)
99
+ }
100
+ }
101
+
102
+ function normalizeExecutionResult(
103
+ raw: unknown,
104
+ ): AiPendingActionExecutionResult {
105
+ if (!raw || typeof raw !== 'object') return {}
106
+ const source = raw as Record<string, unknown>
107
+ const result: AiPendingActionExecutionResult = {}
108
+ if (typeof source.recordId === 'string') result.recordId = source.recordId
109
+ if (typeof source.commandName === 'string') result.commandName = source.commandName
110
+ return result
111
+ }
112
+
113
+ /**
114
+ * Extract per-record handler failures from a bulk tool's return value so
115
+ * they can be persisted onto the pending-action row's `failedRecords[]`.
116
+ *
117
+ * Bulk mutation tools (Step 5.14) return a result of the shape:
118
+ * { commandName, records: [{ recordId, status, before, after, error? }],
119
+ * failedRecordIds: string[], error? }
120
+ *
121
+ * We pull the entries whose `status !== 'updated'` AND carry an `error`
122
+ * object, coerce them to the `AiPendingActionFailedRecord` shape, and
123
+ * return them so the executor can merge with re-check-sourced failures
124
+ * at the final `executing → confirmed` transition (spec §9.8 line 746:
125
+ * "a failure inside the confirm handler ... is recorded per-record in
126
+ * executionResult.failedRecords[] / row.failedRecords").
127
+ *
128
+ * Returns an empty array when the handler output does not carry the
129
+ * batch shape (single-record tools never populate this — their failures
130
+ * either throw from the handler and land in `executionResult.error` or
131
+ * succeed cleanly).
132
+ */
133
+ /**
134
+ * Convert a thrown handler error into the structured shape we persist on
135
+ * `executionResult.error`. The previous implementation only kept
136
+ * `{ code: 'handler_error', message }` — for ZodError that flattened to
137
+ * the literal string "Invalid input", which was not enough context for
138
+ * the operator's "Fix with AI" retry to actually fix anything. This
139
+ * version preserves the original error name, Zod issues / fieldErrors,
140
+ * an `input` echo of the arguments the handler was called with, and any
141
+ * structured `cause` so the model can self-correct.
142
+ *
143
+ * Stack traces are deliberately gated behind `OM_AI_INCLUDE_HANDLER_STACK=1`
144
+ * — they're noise to the model and a leak risk in tenant-visible UI.
145
+ */
146
+ function buildHandlerErrorFromThrown(
147
+ error: unknown,
148
+ input: unknown,
149
+ ): NonNullable<AiPendingActionExecutionResult['error']> {
150
+ const message = error instanceof Error ? error.message : String(error)
151
+ const name = error instanceof Error ? error.name : undefined
152
+ const out: NonNullable<AiPendingActionExecutionResult['error']> = {
153
+ code: 'handler_error',
154
+ message: message || 'Tool handler threw an error.',
155
+ }
156
+ if (name) out.name = name
157
+
158
+ // Echo the input so the model can compare what it sent vs. what the
159
+ // schema expected. `normalizedInput` has already been Zod-parsed at
160
+ // prepareMutation time so the values are JSON-safe.
161
+ if (input !== undefined) {
162
+ try {
163
+ out.input = JSON.parse(JSON.stringify(input))
164
+ } catch {
165
+ // ignore — non-serializable input is rare and the model can still
166
+ // work from the message + Zod issues.
167
+ }
168
+ }
169
+
170
+ const details: Record<string, unknown> = {}
171
+
172
+ if (error && typeof error === 'object') {
173
+ const err = error as Record<string, unknown>
174
+ // ZodError: forward `issues[]` verbatim and a flat `fieldErrors`
175
+ // form so the model can locate the failing field by name even when
176
+ // the message has been collapsed to "Invalid input".
177
+ const issues = err.issues
178
+ if (Array.isArray(issues) && issues.length > 0) {
179
+ details.issues = issues.map((issue) => {
180
+ if (!issue || typeof issue !== 'object') return issue
181
+ const obj = issue as Record<string, unknown>
182
+ return {
183
+ path: Array.isArray(obj.path) ? obj.path : undefined,
184
+ message: typeof obj.message === 'string' ? obj.message : undefined,
185
+ code: typeof obj.code === 'string' ? obj.code : undefined,
186
+ ...(typeof obj.expected === 'string' ? { expected: obj.expected } : {}),
187
+ ...(typeof obj.received === 'string' ? { received: obj.received } : {}),
188
+ }
189
+ })
190
+ const fieldErrors: Record<string, string[]> = {}
191
+ for (const issue of issues as Array<Record<string, unknown>>) {
192
+ const path = Array.isArray(issue.path) ? issue.path.join('.') : ''
193
+ const msg = typeof issue.message === 'string' ? issue.message : null
194
+ if (!path || !msg) continue
195
+ if (!fieldErrors[path]) fieldErrors[path] = []
196
+ fieldErrors[path].push(msg)
197
+ }
198
+ if (Object.keys(fieldErrors).length > 0) {
199
+ details.fieldErrors = fieldErrors
200
+ }
201
+ if (out.code === 'handler_error') out.code = 'validation_error'
202
+ }
203
+ // Forward a known `code` if the handler error carries one.
204
+ if (typeof err.code === 'string' && err.code.length > 0) {
205
+ out.code = err.code
206
+ }
207
+ // Carry the cause when it is JSON-serializable (string, number, plain object).
208
+ if (err.cause !== undefined) {
209
+ try {
210
+ details.cause = JSON.parse(JSON.stringify(err.cause))
211
+ } catch {
212
+ if (err.cause instanceof Error) {
213
+ details.cause = { message: err.cause.message, name: err.cause.name }
214
+ }
215
+ }
216
+ }
217
+ // Pull through any other plain-object enumerable own props the handler
218
+ // attached (e.g. `expected`, `actual`, `target`).
219
+ for (const key of Object.keys(err)) {
220
+ if (key === 'issues' || key === 'cause' || key === 'code' || key === 'message' || key === 'name' || key === 'stack') continue
221
+ const value = err[key]
222
+ if (value === undefined) continue
223
+ try {
224
+ details[key] = JSON.parse(JSON.stringify(value))
225
+ } catch {
226
+ // skip non-serializable
227
+ }
228
+ }
229
+ }
230
+
231
+ if (Object.keys(details).length > 0) {
232
+ out.details = details
233
+ }
234
+
235
+ if (process.env.OM_AI_INCLUDE_HANDLER_STACK === '1' && error instanceof Error && error.stack) {
236
+ // Trim to the top frames so the persisted result stays bounded.
237
+ const lines = error.stack.split('\n').slice(0, 6)
238
+ out.stack = lines.join('\n')
239
+ }
240
+
241
+ return out
242
+ }
243
+
244
+ function extractHandlerFailedRecords(raw: unknown): AiPendingActionFailedRecord[] {
245
+ if (!raw || typeof raw !== 'object') return []
246
+ const source = raw as Record<string, unknown>
247
+ const records = source.records
248
+ if (!Array.isArray(records) || records.length === 0) return []
249
+ const out: AiPendingActionFailedRecord[] = []
250
+ for (const entry of records) {
251
+ if (!entry || typeof entry !== 'object') continue
252
+ const record = entry as Record<string, unknown>
253
+ if (typeof record.recordId !== 'string') continue
254
+ const status = typeof record.status === 'string' ? record.status : null
255
+ if (status === 'updated') continue
256
+ const errorField = record.error
257
+ if (!errorField || typeof errorField !== 'object') continue
258
+ const error = errorField as Record<string, unknown>
259
+ const code = typeof error.code === 'string' ? error.code : 'handler_error'
260
+ const message = typeof error.message === 'string' ? error.message : 'Record update failed.'
261
+ out.push({
262
+ recordId: record.recordId,
263
+ error: { code, message },
264
+ })
265
+ }
266
+ return out
267
+ }
268
+
269
+ function mergeFailedRecords(
270
+ recheck: AiPendingActionFailedRecord[] | null | undefined,
271
+ handler: AiPendingActionFailedRecord[] | null | undefined,
272
+ ): AiPendingActionFailedRecord[] | null {
273
+ const seen = new Map<string, AiPendingActionFailedRecord>()
274
+ for (const entry of recheck ?? []) {
275
+ if (entry && typeof entry.recordId === 'string' && !seen.has(entry.recordId)) {
276
+ seen.set(entry.recordId, entry)
277
+ }
278
+ }
279
+ for (const entry of handler ?? []) {
280
+ if (entry && typeof entry.recordId === 'string' && !seen.has(entry.recordId)) {
281
+ seen.set(entry.recordId, entry)
282
+ }
283
+ }
284
+ if (seen.size === 0) return null
285
+ return Array.from(seen.values())
286
+ }
287
+
288
+ function toToolHandlerContext(
289
+ ctx: PendingActionExecuteContext,
290
+ tool: AiToolDefinition,
291
+ ): McpToolContext {
292
+ return {
293
+ tenantId: ctx.tenantId,
294
+ organizationId: ctx.organizationId,
295
+ userId: ctx.userId,
296
+ container: ctx.container,
297
+ userFeatures: ctx.userFeatures,
298
+ isSuperAdmin: ctx.isSuperAdmin,
299
+ tool,
300
+ }
301
+ }
302
+
303
+ /**
304
+ * Idempotent entry point for the Step 5.8 confirm route.
305
+ *
306
+ * - If the action is already `confirmed` with a stored `executionResult`,
307
+ * returns that prior result without re-invoking the handler (double-click /
308
+ * retry contract).
309
+ * - If the action is already `confirmed` without a stored `executionResult`
310
+ * (shouldn't happen in practice), returns a synthesized empty result.
311
+ * - If the action is still `pending`, runs the transitions and the handler.
312
+ * - Any other status is rejected at the re-check layer before this helper
313
+ * is ever called; this helper treats them as invariant violations.
314
+ */
315
+ export async function executePendingActionConfirm(
316
+ input: PendingActionExecuteInput,
317
+ ): Promise<PendingActionExecuteResult> {
318
+ const { action, agent, tool, ctx, failedRecords, now } = input
319
+ const repo = input.repo ?? new AiPendingActionRepository(ctx.container.resolve('em'))
320
+ const scope = {
321
+ tenantId: ctx.tenantId,
322
+ organizationId: ctx.organizationId,
323
+ userId: ctx.userId,
324
+ }
325
+ const clock = now ?? new Date()
326
+ const emitter: ConfirmedEmitter = input.emitEvent ?? defaultConfirmedEmitter
327
+
328
+ if (action.status === 'confirmed') {
329
+ const prior = (action.executionResult ?? {}) as AiPendingActionExecutionResult
330
+ return { ok: true, action, executionResult: prior }
331
+ }
332
+
333
+ if (action.status === 'executing') {
334
+ const prior = (action.executionResult ?? {}) as AiPendingActionExecutionResult
335
+ return { ok: true, action, executionResult: prior }
336
+ }
337
+
338
+ if (action.status !== 'pending') {
339
+ return {
340
+ ok: false,
341
+ action,
342
+ executionResult: {
343
+ error: { code: 'invalid_status', message: `Action is in status "${action.status}".` },
344
+ },
345
+ cause: new Error(`Action is in status "${action.status}"`),
346
+ }
347
+ }
348
+
349
+ const partialFailedRecords =
350
+ Array.isArray(failedRecords) && failedRecords.length > 0 ? failedRecords : null
351
+
352
+ const confirmedRow = await repo.setStatus(action.id, 'confirmed', scope, {
353
+ resolvedByUserId: ctx.userId,
354
+ now: clock,
355
+ ...(partialFailedRecords ? { failedRecords: partialFailedRecords } : {}),
356
+ })
357
+ const executingRow = await repo.setStatus(confirmedRow.id, 'executing', scope, { now: clock })
358
+
359
+ let handlerOutput: unknown
360
+ try {
361
+ handlerOutput = await tool.handler(action.normalizedInput as never, toToolHandlerContext(ctx, tool))
362
+ } catch (error) {
363
+ const failureResult: AiPendingActionExecutionResult = {
364
+ error: buildHandlerErrorFromThrown(error, action.normalizedInput),
365
+ }
366
+ const failedRow = await repo.setStatus(executingRow.id, 'failed', scope, {
367
+ executionResult: failureResult,
368
+ now: clock,
369
+ })
370
+ await emitConfirmed(emitter, {
371
+ pendingActionId: failedRow.id,
372
+ agentId: agent.id,
373
+ toolName: tool.name,
374
+ status: failedRow.status,
375
+ tenantId: ctx.tenantId,
376
+ organizationId: ctx.organizationId ?? null,
377
+ userId: ctx.userId,
378
+ resolvedByUserId: ctx.userId,
379
+ resolvedAt: (failedRow.resolvedAt ?? clock).toISOString?.() ?? new Date(clock).toISOString(),
380
+ executionResult: failureResult,
381
+ })
382
+ return {
383
+ ok: false,
384
+ action: failedRow,
385
+ executionResult: failureResult,
386
+ cause: error,
387
+ }
388
+ }
389
+
390
+ const successResult = normalizeExecutionResult(handlerOutput)
391
+ const handlerFailedRecords = extractHandlerFailedRecords(handlerOutput)
392
+ const mergedFailedRecords = mergeFailedRecords(partialFailedRecords, handlerFailedRecords)
393
+ const confirmedExtra: Record<string, unknown> = {
394
+ executionResult: successResult,
395
+ now: clock,
396
+ }
397
+ // Always write `failedRecords` on the final transition so a batch that
398
+ // had re-check-stale records but zero handler failures keeps the
399
+ // original list, and a batch with handler failures merges both sets.
400
+ // Explicit `null` collapses to "no failures" in the repository's
401
+ // `normalizeFailedRecords` helper.
402
+ confirmedExtra.failedRecords = mergedFailedRecords
403
+ const confirmedFinal = await repo.setStatus(executingRow.id, 'confirmed', scope, confirmedExtra)
404
+ const emitFailedRecordsPayload =
405
+ Array.isArray(mergedFailedRecords) && mergedFailedRecords.length > 0
406
+ ? mergedFailedRecords
407
+ : null
408
+ await emitConfirmed(emitter, {
409
+ pendingActionId: confirmedFinal.id,
410
+ agentId: agent.id,
411
+ toolName: tool.name,
412
+ status: confirmedFinal.status,
413
+ tenantId: ctx.tenantId,
414
+ organizationId: ctx.organizationId ?? null,
415
+ userId: ctx.userId,
416
+ resolvedByUserId: ctx.userId,
417
+ resolvedAt: (confirmedFinal.resolvedAt ?? clock).toISOString?.() ?? new Date(clock).toISOString(),
418
+ executionResult: successResult,
419
+ ...(emitFailedRecordsPayload ? { failedRecords: emitFailedRecordsPayload } : {}),
420
+ })
421
+ return { ok: true, action: confirmedFinal, executionResult: successResult }
422
+ }
423
+
424
+ export const PENDING_ACTION_CONFIRMED_EVENT_ID = CONFIRMED_EVENT_ID