@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,174 @@
1
+ import { NextResponse } from "next/server";
2
+ import { z } from "zod";
3
+ import { getAuthFromRequest } from "@open-mercato/shared/lib/auth/server";
4
+ import { createRequestContainer } from "@open-mercato/shared/lib/di/container";
5
+ import { AiPendingActionRepository } from "../../../../../data/repositories/AiPendingActionRepository.js";
6
+ import { hasRequiredFeatures } from "../../../../../lib/auth.js";
7
+ import { serializePendingActionForClient } from "../../../../../lib/pending-action-client.js";
8
+ import { getAgent, loadAgentRegistry } from "../../../../../lib/agent-registry.js";
9
+ import { toolRegistry } from "../../../../../lib/tool-registry.js";
10
+ import { loadAllModuleTools } from "../../../../../lib/tool-loader.js";
11
+ import {
12
+ runPendingActionRechecks
13
+ } from "../../../../../lib/pending-action-recheck.js";
14
+ import {
15
+ executePendingActionConfirm
16
+ } from "../../../../../lib/pending-action-executor.js";
17
+ import { AiAgentMutationPolicyOverrideRepository } from "../../../../../data/repositories/AiAgentMutationPolicyOverrideRepository.js";
18
+ import { isKnownMutationPolicy } from "../../../../../lib/agent-policy.js";
19
+ const REQUIRED_FEATURE = "ai_assistant.view";
20
+ const idParamSchema = z.object({
21
+ id: z.string().trim().min(1, "id must be a non-empty string").max(128, "id exceeds the maximum length of 128 characters")
22
+ });
23
+ const openApi = {
24
+ tag: "AI Assistant",
25
+ summary: "Pending action (mutation approval gate) confirm",
26
+ methods: {
27
+ POST: {
28
+ operationId: "aiAssistantConfirmPendingAction",
29
+ summary: "Confirm an AI pending action, re-running every server-side check before execution.",
30
+ description: "Re-verifies the full contract from spec \xA79.4 (status, expiry, agent registration, required features, mutation policy, tool whitelist, attachment tenant scope, record version, and schema drift), flips the pending-action state machine to `executing`, invokes the wrapped tool handler, and persists `executionResult`. Idempotent: a second call on a row already in `confirmed` state returns the prior result without re-executing the handler.",
31
+ responses: [
32
+ {
33
+ status: 200,
34
+ description: "Confirmation complete; body includes the serialized pending action and the mutation result.",
35
+ mediaType: "application/json"
36
+ }
37
+ ],
38
+ errors: [
39
+ { status: 401, description: "Unauthenticated caller." },
40
+ { status: 403, description: "Caller lacks `ai_assistant.view`, a required agent feature, tool whitelist, or accesses attachments outside their tenant." },
41
+ { status: 404, description: "Pending action or agent not found in the caller scope." },
42
+ { status: 409, description: "Pending action is not in `pending` status or has expired." },
43
+ { status: 412, description: "Record version changed between propose and confirm, or the input schema no longer accepts the stored payload." },
44
+ { status: 500, description: "Unexpected server failure during confirm." }
45
+ ]
46
+ }
47
+ }
48
+ };
49
+ const metadata = {
50
+ POST: { requireAuth: true, requireFeatures: [REQUIRED_FEATURE] }
51
+ };
52
+ function jsonError(status, message, code, extra) {
53
+ return NextResponse.json({ error: message, code, ...extra ?? {} }, { status });
54
+ }
55
+ function fromRecheckFailure(result) {
56
+ return jsonError(result.status, result.message, result.code, result.extra);
57
+ }
58
+ async function POST(req, context) {
59
+ const auth = await getAuthFromRequest(req);
60
+ if (!auth) {
61
+ return jsonError(401, "Unauthorized", "unauthenticated");
62
+ }
63
+ const rawParams = await context.params;
64
+ const paramResult = idParamSchema.safeParse(rawParams);
65
+ if (!paramResult.success) {
66
+ return jsonError(400, "Invalid pending action id.", "validation_error", {
67
+ issues: paramResult.error.issues
68
+ });
69
+ }
70
+ const pendingActionId = paramResult.data.id;
71
+ try {
72
+ const container = await createRequestContainer();
73
+ const rbacService = container.resolve("rbacService");
74
+ const acl = await rbacService.loadAcl(auth.sub, {
75
+ tenantId: auth.tenantId,
76
+ organizationId: auth.orgId
77
+ });
78
+ if (!hasRequiredFeatures([REQUIRED_FEATURE], acl.features, acl.isSuperAdmin, rbacService)) {
79
+ return jsonError(403, `Caller lacks required feature "${REQUIRED_FEATURE}".`, "forbidden");
80
+ }
81
+ if (!auth.tenantId) {
82
+ return jsonError(
83
+ 404,
84
+ `No pending action "${pendingActionId}" accessible to the caller.`,
85
+ "pending_action_not_found"
86
+ );
87
+ }
88
+ const em = container.resolve("em");
89
+ const repo = new AiPendingActionRepository(em);
90
+ const row = await repo.getById(pendingActionId, {
91
+ tenantId: auth.tenantId,
92
+ organizationId: auth.orgId ?? null,
93
+ userId: auth.sub
94
+ });
95
+ if (!row) {
96
+ return jsonError(
97
+ 404,
98
+ `No pending action "${pendingActionId}" accessible to the caller.`,
99
+ "pending_action_not_found"
100
+ );
101
+ }
102
+ if (row.status === "confirmed" || row.status === "failed") {
103
+ const executionResult = row.executionResult ?? null;
104
+ return NextResponse.json({
105
+ ok: row.status === "confirmed",
106
+ pendingAction: serializePendingActionForClient(row),
107
+ mutationResult: executionResult
108
+ });
109
+ }
110
+ await loadAgentRegistry();
111
+ await loadAllModuleTools();
112
+ const agent = getAgent(row.agentId);
113
+ const tool = toolRegistry.getTool(row.toolName);
114
+ const policyOverrideRepo = new AiAgentMutationPolicyOverrideRepository(em);
115
+ const overrideRow = await policyOverrideRepo.get(row.agentId, {
116
+ tenantId: auth.tenantId,
117
+ organizationId: auth.orgId ?? null
118
+ });
119
+ const rawOverridePolicy = overrideRow?.mutationPolicy ?? null;
120
+ const mutationPolicyOverride = rawOverridePolicy && isKnownMutationPolicy(rawOverridePolicy) ? rawOverridePolicy : null;
121
+ const recheckResult = await runPendingActionRechecks({
122
+ action: row,
123
+ agent,
124
+ tool,
125
+ ctx: {
126
+ tenantId: auth.tenantId,
127
+ organizationId: auth.orgId ?? null,
128
+ userId: auth.sub,
129
+ userFeatures: acl.features,
130
+ isSuperAdmin: acl.isSuperAdmin,
131
+ container,
132
+ em
133
+ },
134
+ mutationPolicyOverride
135
+ });
136
+ if (!recheckResult.ok) {
137
+ return fromRecheckFailure(recheckResult);
138
+ }
139
+ const executeCtx = {
140
+ tenantId: auth.tenantId,
141
+ organizationId: auth.orgId ?? null,
142
+ userId: auth.sub,
143
+ userFeatures: acl.features,
144
+ isSuperAdmin: acl.isSuperAdmin,
145
+ container
146
+ };
147
+ const executed = await executePendingActionConfirm({
148
+ action: row,
149
+ agent,
150
+ tool,
151
+ ctx: executeCtx,
152
+ repo,
153
+ failedRecords: recheckResult.failedRecords ?? null
154
+ });
155
+ return NextResponse.json({
156
+ ok: executed.ok,
157
+ pendingAction: serializePendingActionForClient(executed.action),
158
+ mutationResult: executed.executionResult
159
+ });
160
+ } catch (error) {
161
+ console.error("[AI Pending Action CONFIRM] Failure:", error);
162
+ return jsonError(
163
+ 500,
164
+ error instanceof Error ? error.message : "Failed to confirm pending action.",
165
+ "confirm_internal_error"
166
+ );
167
+ }
168
+ }
169
+ export {
170
+ POST,
171
+ metadata,
172
+ openApi
173
+ };
174
+ //# sourceMappingURL=route.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../../../../../src/modules/ai_assistant/api/ai/actions/%5Bid%5D/confirm/route.ts"],
4
+ "sourcesContent": ["import { NextResponse, type NextRequest } from 'next/server'\nimport { z } from 'zod'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'\nimport { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport type { RbacService } from '@open-mercato/core/modules/auth/services/rbacService'\nimport { AiPendingActionRepository } from '../../../../../data/repositories/AiPendingActionRepository'\nimport { hasRequiredFeatures } from '../../../../../lib/auth'\nimport { serializePendingActionForClient } from '../../../../../lib/pending-action-client'\nimport { getAgent, loadAgentRegistry } from '../../../../../lib/agent-registry'\nimport { toolRegistry } from '../../../../../lib/tool-registry'\nimport { loadAllModuleTools } from '../../../../../lib/tool-loader'\nimport type { AiToolDefinition } from '../../../../../lib/types'\nimport {\n runPendingActionRechecks,\n type PendingActionRecheckResult,\n} from '../../../../../lib/pending-action-recheck'\nimport {\n executePendingActionConfirm,\n type PendingActionExecuteContext,\n} from '../../../../../lib/pending-action-executor'\nimport { AiAgentMutationPolicyOverrideRepository } from '../../../../../data/repositories/AiAgentMutationPolicyOverrideRepository'\nimport type { AiAgentMutationPolicy } from '../../../../../lib/ai-agent-definition'\nimport { isKnownMutationPolicy } from '../../../../../lib/agent-policy'\n\n/**\n * POST `/api/ai/actions/:id/confirm` \u2014 mutation approval gate confirm\n * endpoint (spec \u00A79.4, Step 5.8).\n *\n * Invariant: every check from spec \u00A79.4 is re-run on the server before the\n * tool handler executes. The original model-proposed tool arguments are\n * re-parsed through the CURRENT tool `inputSchema` so that a schema\n * migration between propose and confirm surfaces as `schema_drift`\n * (412) instead of silently passing through invalid data.\n *\n * Idempotency: calling this endpoint twice on the same pending-action id\n * returns the prior execution result without re-invoking the handler.\n */\n\nconst REQUIRED_FEATURE = 'ai_assistant.view'\n\nconst idParamSchema = z.object({\n id: z\n .string()\n .trim()\n .min(1, 'id must be a non-empty string')\n .max(128, 'id exceeds the maximum length of 128 characters'),\n})\n\nexport const openApi: OpenApiRouteDoc = {\n tag: 'AI Assistant',\n summary: 'Pending action (mutation approval gate) confirm',\n methods: {\n POST: {\n operationId: 'aiAssistantConfirmPendingAction',\n summary: 'Confirm an AI pending action, re-running every server-side check before execution.',\n description:\n 'Re-verifies the full contract from spec \u00A79.4 (status, expiry, agent registration, ' +\n 'required features, mutation policy, tool whitelist, attachment tenant scope, record ' +\n 'version, and schema drift), flips the pending-action state machine to `executing`, ' +\n 'invokes the wrapped tool handler, and persists `executionResult`. Idempotent: a ' +\n 'second call on a row already in `confirmed` state returns the prior result without ' +\n 're-executing the handler.',\n responses: [\n {\n status: 200,\n description: 'Confirmation complete; body includes the serialized pending action and the mutation result.',\n mediaType: 'application/json',\n },\n ],\n errors: [\n { status: 401, description: 'Unauthenticated caller.' },\n { status: 403, description: 'Caller lacks `ai_assistant.view`, a required agent feature, tool whitelist, or accesses attachments outside their tenant.' },\n { status: 404, description: 'Pending action or agent not found in the caller scope.' },\n { status: 409, description: 'Pending action is not in `pending` status or has expired.' },\n { status: 412, description: 'Record version changed between propose and confirm, or the input schema no longer accepts the stored payload.' },\n { status: 500, description: 'Unexpected server failure during confirm.' },\n ],\n },\n },\n}\n\nexport const metadata = {\n POST: { requireAuth: true, requireFeatures: [REQUIRED_FEATURE] },\n}\n\ninterface RouteContext {\n params: Promise<{ id: string }>\n}\n\nfunction jsonError(\n status: number,\n message: string,\n code: string,\n extra?: Record<string, unknown>,\n): NextResponse {\n return NextResponse.json({ error: message, code, ...(extra ?? {}) }, { status })\n}\n\nfunction fromRecheckFailure(result: PendingActionRecheckResult & { ok: false }): NextResponse {\n return jsonError(result.status, result.message, result.code, result.extra)\n}\n\nexport async function POST(req: NextRequest, context: RouteContext): Promise<Response> {\n const auth = await getAuthFromRequest(req)\n if (!auth) {\n return jsonError(401, 'Unauthorized', 'unauthenticated')\n }\n\n const rawParams = await context.params\n const paramResult = idParamSchema.safeParse(rawParams)\n if (!paramResult.success) {\n return jsonError(400, 'Invalid pending action id.', 'validation_error', {\n issues: paramResult.error.issues,\n })\n }\n const pendingActionId = paramResult.data.id\n\n try {\n const container = await createRequestContainer()\n const rbacService = container.resolve<RbacService>('rbacService')\n const acl = await rbacService.loadAcl(auth.sub, {\n tenantId: auth.tenantId,\n organizationId: auth.orgId,\n })\n\n if (!hasRequiredFeatures([REQUIRED_FEATURE], acl.features, acl.isSuperAdmin, rbacService)) {\n return jsonError(403, `Caller lacks required feature \"${REQUIRED_FEATURE}\".`, 'forbidden')\n }\n\n if (!auth.tenantId) {\n return jsonError(\n 404,\n `No pending action \"${pendingActionId}\" accessible to the caller.`,\n 'pending_action_not_found',\n )\n }\n\n const em = container.resolve<EntityManager>('em')\n const repo = new AiPendingActionRepository(em)\n const row = await repo.getById(pendingActionId, {\n tenantId: auth.tenantId,\n organizationId: auth.orgId ?? null,\n userId: auth.sub,\n })\n if (!row) {\n return jsonError(\n 404,\n `No pending action \"${pendingActionId}\" accessible to the caller.`,\n 'pending_action_not_found',\n )\n }\n\n // Idempotency short-circuit \u2014 a prior confirm already ran.\n if (row.status === 'confirmed' || row.status === 'failed') {\n const executionResult = row.executionResult ?? null\n return NextResponse.json({\n ok: row.status === 'confirmed',\n pendingAction: serializePendingActionForClient(row),\n mutationResult: executionResult,\n })\n }\n\n await loadAgentRegistry()\n await loadAllModuleTools()\n const agent = getAgent(row.agentId)\n const tool = toolRegistry.getTool(row.toolName) as AiToolDefinition | undefined\n\n const policyOverrideRepo = new AiAgentMutationPolicyOverrideRepository(em)\n const overrideRow = await policyOverrideRepo.get(row.agentId, {\n tenantId: auth.tenantId,\n organizationId: auth.orgId ?? null,\n })\n const rawOverridePolicy = overrideRow?.mutationPolicy ?? null\n const mutationPolicyOverride: AiAgentMutationPolicy | null =\n rawOverridePolicy && isKnownMutationPolicy(rawOverridePolicy) ? rawOverridePolicy : null\n\n const recheckResult = await runPendingActionRechecks({\n action: row,\n agent,\n tool,\n ctx: {\n tenantId: auth.tenantId,\n organizationId: auth.orgId ?? null,\n userId: auth.sub,\n userFeatures: acl.features,\n isSuperAdmin: acl.isSuperAdmin,\n container,\n em,\n },\n mutationPolicyOverride,\n })\n if (!recheckResult.ok) {\n return fromRecheckFailure(recheckResult)\n }\n\n const executeCtx: PendingActionExecuteContext = {\n tenantId: auth.tenantId,\n organizationId: auth.orgId ?? null,\n userId: auth.sub,\n userFeatures: acl.features,\n isSuperAdmin: acl.isSuperAdmin,\n container,\n }\n\n const executed = await executePendingActionConfirm({\n action: row,\n agent: agent!,\n tool: tool!,\n ctx: executeCtx,\n repo,\n failedRecords: recheckResult.failedRecords ?? null,\n })\n\n return NextResponse.json({\n ok: executed.ok,\n pendingAction: serializePendingActionForClient(executed.action),\n mutationResult: executed.executionResult,\n })\n } catch (error) {\n console.error('[AI Pending Action CONFIRM] Failure:', error)\n return jsonError(\n 500,\n error instanceof Error ? error.message : 'Failed to confirm pending action.',\n 'confirm_internal_error',\n )\n }\n}\n"],
5
+ "mappings": "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",
6
+ "names": []
7
+ }
@@ -0,0 +1,101 @@
1
+ import { NextResponse } from "next/server";
2
+ import { z } from "zod";
3
+ import { getAuthFromRequest } from "@open-mercato/shared/lib/auth/server";
4
+ import { createRequestContainer } from "@open-mercato/shared/lib/di/container";
5
+ import { AiPendingActionRepository } from "../../../../data/repositories/AiPendingActionRepository.js";
6
+ import { hasRequiredFeatures } from "../../../../lib/auth.js";
7
+ import { serializePendingActionForClient } from "../../../../lib/pending-action-client.js";
8
+ const REQUIRED_FEATURE = "ai_assistant.view";
9
+ const idParamSchema = z.object({
10
+ id: z.string().trim().min(1, "id must be a non-empty string").max(128, "id exceeds the maximum length of 128 characters")
11
+ });
12
+ const openApi = {
13
+ tag: "AI Assistant",
14
+ summary: "Pending action (mutation approval gate) read-side",
15
+ methods: {
16
+ GET: {
17
+ operationId: "aiAssistantGetPendingAction",
18
+ summary: "Fetch the current state of an AI pending action by id.",
19
+ description: "Returns the tenant-scoped {@link AiPendingAction} addressed by `:id`. Powers the chat UI reconnect/polling path: after a page reload or SSE reconnect the client carries the `pendingActionId` from an earlier `mutation-preview-card` UI part and calls this route to re-hydrate the card. Server-internal fields (`normalizedInput`, `createdByUserId`, `idempotencyKey`) are stripped by a whitelist serializer. Enforces tenant/org scoping via the repository.",
20
+ responses: [
21
+ {
22
+ status: 200,
23
+ description: "Serialized pending action. Never includes normalizedInput, createdByUserId, or idempotencyKey.",
24
+ mediaType: "application/json"
25
+ }
26
+ ],
27
+ errors: [
28
+ { status: 401, description: "Unauthenticated caller." },
29
+ { status: 403, description: "Caller lacks the `ai_assistant.view` feature." },
30
+ { status: 404, description: "No pending action with the given id is accessible to the caller." },
31
+ { status: 500, description: "Internal runtime failure." }
32
+ ]
33
+ }
34
+ }
35
+ };
36
+ const metadata = {
37
+ GET: { requireAuth: true, requireFeatures: [REQUIRED_FEATURE] }
38
+ };
39
+ function jsonError(status, message, code, extra) {
40
+ return NextResponse.json({ error: message, code, ...extra ?? {} }, { status });
41
+ }
42
+ async function GET(req, context) {
43
+ const auth = await getAuthFromRequest(req);
44
+ if (!auth) {
45
+ return jsonError(401, "Unauthorized", "unauthenticated");
46
+ }
47
+ const rawParams = await context.params;
48
+ const paramResult = idParamSchema.safeParse(rawParams);
49
+ if (!paramResult.success) {
50
+ return jsonError(400, "Invalid pending action id.", "validation_error", {
51
+ issues: paramResult.error.issues
52
+ });
53
+ }
54
+ const pendingActionId = paramResult.data.id;
55
+ try {
56
+ const container = await createRequestContainer();
57
+ const rbacService = container.resolve("rbacService");
58
+ const acl = await rbacService.loadAcl(auth.sub, {
59
+ tenantId: auth.tenantId,
60
+ organizationId: auth.orgId
61
+ });
62
+ if (!hasRequiredFeatures([REQUIRED_FEATURE], acl.features, acl.isSuperAdmin, rbacService)) {
63
+ return jsonError(403, `Caller lacks required feature "${REQUIRED_FEATURE}".`, "forbidden");
64
+ }
65
+ if (!auth.tenantId) {
66
+ return jsonError(
67
+ 404,
68
+ `No pending action "${pendingActionId}" accessible to the caller.`,
69
+ "pending_action_not_found"
70
+ );
71
+ }
72
+ const em = container.resolve("em");
73
+ const repo = new AiPendingActionRepository(em);
74
+ const row = await repo.getById(pendingActionId, {
75
+ tenantId: auth.tenantId,
76
+ organizationId: auth.orgId ?? null,
77
+ userId: auth.sub
78
+ });
79
+ if (!row) {
80
+ return jsonError(
81
+ 404,
82
+ `No pending action "${pendingActionId}" accessible to the caller.`,
83
+ "pending_action_not_found"
84
+ );
85
+ }
86
+ return NextResponse.json(serializePendingActionForClient(row));
87
+ } catch (error) {
88
+ console.error("[AI Pending Action GET] Failure:", error);
89
+ return jsonError(
90
+ 500,
91
+ error instanceof Error ? error.message : "Failed to load pending action.",
92
+ "internal_error"
93
+ );
94
+ }
95
+ }
96
+ export {
97
+ GET,
98
+ metadata,
99
+ openApi
100
+ };
101
+ //# sourceMappingURL=route.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../../../../src/modules/ai_assistant/api/ai/actions/%5Bid%5D/route.ts"],
4
+ "sourcesContent": ["import { NextResponse, type NextRequest } from 'next/server'\nimport { z } from 'zod'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'\nimport { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport type { RbacService } from '@open-mercato/core/modules/auth/services/rbacService'\nimport { AiPendingActionRepository } from '../../../../data/repositories/AiPendingActionRepository'\nimport { hasRequiredFeatures } from '../../../../lib/auth'\nimport { serializePendingActionForClient } from '../../../../lib/pending-action-client'\n\n/**\n * GET `/api/ai/actions/:id` \u2014 reconnect/polling endpoint for the Phase 3 WS-C\n * mutation approval gate (spec \u00A79, Step 5.7).\n *\n * When the chat UI bounces (reload, tab switch, SSE reconnect) it carries a\n * `pendingActionId` from an earlier `mutation-preview-card` UI part and needs\n * to re-hydrate the pending row's current state. This route is the authoritative\n * read-side; the confirm/cancel routes (Steps 5.8 / 5.9) use the same\n * whitelist serializer so the UI always sees the same shape.\n */\n\nconst REQUIRED_FEATURE = 'ai_assistant.view'\n\nconst idParamSchema = z.object({\n id: z\n .string()\n .trim()\n .min(1, 'id must be a non-empty string')\n .max(128, 'id exceeds the maximum length of 128 characters'),\n})\n\nexport const openApi: OpenApiRouteDoc = {\n tag: 'AI Assistant',\n summary: 'Pending action (mutation approval gate) read-side',\n methods: {\n GET: {\n operationId: 'aiAssistantGetPendingAction',\n summary: 'Fetch the current state of an AI pending action by id.',\n description:\n 'Returns the tenant-scoped {@link AiPendingAction} addressed by `:id`. Powers the ' +\n 'chat UI reconnect/polling path: after a page reload or SSE reconnect the client ' +\n 'carries the `pendingActionId` from an earlier `mutation-preview-card` UI part and ' +\n 'calls this route to re-hydrate the card. Server-internal fields (`normalizedInput`, ' +\n '`createdByUserId`, `idempotencyKey`) are stripped by a whitelist serializer. ' +\n 'Enforces tenant/org scoping via the repository.',\n responses: [\n {\n status: 200,\n description:\n 'Serialized pending action. Never includes normalizedInput, createdByUserId, or idempotencyKey.',\n mediaType: 'application/json',\n },\n ],\n errors: [\n { status: 401, description: 'Unauthenticated caller.' },\n { status: 403, description: 'Caller lacks the `ai_assistant.view` feature.' },\n { status: 404, description: 'No pending action with the given id is accessible to the caller.' },\n { status: 500, description: 'Internal runtime failure.' },\n ],\n },\n },\n}\n\nexport const metadata = {\n GET: { requireAuth: true, requireFeatures: [REQUIRED_FEATURE] },\n}\n\ninterface RouteContext {\n params: Promise<{ id: string }>\n}\n\nfunction jsonError(\n status: number,\n message: string,\n code: string,\n extra?: Record<string, unknown>,\n): NextResponse {\n return NextResponse.json({ error: message, code, ...(extra ?? {}) }, { status })\n}\n\nexport async function GET(req: NextRequest, context: RouteContext): Promise<Response> {\n const auth = await getAuthFromRequest(req)\n if (!auth) {\n return jsonError(401, 'Unauthorized', 'unauthenticated')\n }\n\n const rawParams = await context.params\n const paramResult = idParamSchema.safeParse(rawParams)\n if (!paramResult.success) {\n return jsonError(400, 'Invalid pending action id.', 'validation_error', {\n issues: paramResult.error.issues,\n })\n }\n const pendingActionId = paramResult.data.id\n\n try {\n const container = await createRequestContainer()\n const rbacService = container.resolve<RbacService>('rbacService')\n const acl = await rbacService.loadAcl(auth.sub, {\n tenantId: auth.tenantId,\n organizationId: auth.orgId,\n })\n\n if (!hasRequiredFeatures([REQUIRED_FEATURE], acl.features, acl.isSuperAdmin, rbacService)) {\n return jsonError(403, `Caller lacks required feature \"${REQUIRED_FEATURE}\".`, 'forbidden')\n }\n\n if (!auth.tenantId) {\n return jsonError(\n 404,\n `No pending action \"${pendingActionId}\" accessible to the caller.`,\n 'pending_action_not_found',\n )\n }\n\n const em = container.resolve<EntityManager>('em')\n const repo = new AiPendingActionRepository(em)\n const row = await repo.getById(pendingActionId, {\n tenantId: auth.tenantId,\n organizationId: auth.orgId ?? null,\n userId: auth.sub,\n })\n\n if (!row) {\n return jsonError(\n 404,\n `No pending action \"${pendingActionId}\" accessible to the caller.`,\n 'pending_action_not_found',\n )\n }\n\n return NextResponse.json(serializePendingActionForClient(row))\n } catch (error) {\n console.error('[AI Pending Action GET] Failure:', error)\n return jsonError(\n 500,\n error instanceof Error ? error.message : 'Failed to load pending action.',\n 'internal_error',\n )\n }\n}\n"],
5
+ "mappings": "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",
6
+ "names": []
7
+ }
@@ -0,0 +1,311 @@
1
+ import { NextResponse } from "next/server";
2
+ import { z } from "zod";
3
+ import { getAuthFromRequest } from "@open-mercato/shared/lib/auth/server";
4
+ import { createRequestContainer } from "@open-mercato/shared/lib/di/container";
5
+ import { getAgent, loadAgentRegistry } from "../../../../../lib/agent-registry.js";
6
+ import { hasRequiredFeatures } from "../../../../../lib/auth.js";
7
+ import {
8
+ isKnownMutationPolicy,
9
+ isMutationPolicyEscalation
10
+ } from "../../../../../lib/agent-policy.js";
11
+ import { AiAgentMutationPolicyOverrideRepository } from "../../../../../data/repositories/AiAgentMutationPolicyOverrideRepository.js";
12
+ const agentIdPattern = /^[a-z0-9_]+\.[a-z0-9_]+$/;
13
+ const agentIdParamSchema = z.object({
14
+ agentId: z.string().regex(agentIdPattern, 'agentId must match "<module>.<agent>" (lowercase, digits, underscores only)')
15
+ });
16
+ const mutationPolicySchema = z.enum([
17
+ "read-only",
18
+ "confirm-required",
19
+ "destructive-confirm-required"
20
+ ]);
21
+ const mutationPolicyRequestSchema = z.object({
22
+ mutationPolicy: mutationPolicySchema,
23
+ notes: z.string().max(2e3).optional()
24
+ });
25
+ const VIEW_FEATURE = "ai_assistant.view";
26
+ const MANAGE_FEATURE = "ai_assistant.settings.manage";
27
+ const openApi = {
28
+ tag: "AI Assistant",
29
+ summary: "Tenant-scoped mutationPolicy override for an AI agent",
30
+ methods: {
31
+ GET: {
32
+ operationId: "aiAssistantGetMutationPolicyOverride",
33
+ summary: "Read the effective mutationPolicy for an agent \u2014 code-declared value plus any tenant override.",
34
+ description: "Returns `{ agentId, codeDeclared, override }` where `codeDeclared` is the agent's compiled-in `mutationPolicy` and `override` is the persisted tenant-scoped override (or `null`). Requires `ai_assistant.view`.",
35
+ responses: [
36
+ {
37
+ status: 200,
38
+ description: "Effective mutationPolicy payload.",
39
+ mediaType: "application/json"
40
+ }
41
+ ],
42
+ errors: [
43
+ { status: 400, description: "Invalid agent id." },
44
+ { status: 401, description: "Unauthenticated caller." },
45
+ { status: 403, description: "Caller lacks `ai_assistant.view`." },
46
+ { status: 404, description: "Unknown agent id." }
47
+ ]
48
+ },
49
+ POST: {
50
+ operationId: "aiAssistantSaveMutationPolicyOverride",
51
+ summary: "Set (or replace) the tenant-scoped mutationPolicy override for this agent.",
52
+ description: 'Body: `{ mutationPolicy: "read-only" | "confirm-required" | "destructive-confirm-required", notes? }`. The override MUST NOT escalate beyond the agent\'s code-declared policy. Escalation attempts are rejected with 400 + `code: "escalation_not_allowed"`. Requires `ai_assistant.settings.manage`.',
53
+ requestBody: {
54
+ contentType: "application/json",
55
+ description: "Body: `{ mutationPolicy, notes? }`.",
56
+ schema: mutationPolicyRequestSchema
57
+ },
58
+ responses: [
59
+ {
60
+ status: 200,
61
+ description: "Override persisted.",
62
+ mediaType: "application/json"
63
+ }
64
+ ],
65
+ errors: [
66
+ { status: 400, description: "Invalid agent id, malformed body, or escalation attempt." },
67
+ { status: 401, description: "Unauthenticated caller." },
68
+ { status: 403, description: "Caller lacks `ai_assistant.settings.manage`." },
69
+ { status: 404, description: "Unknown agent id." }
70
+ ]
71
+ },
72
+ DELETE: {
73
+ operationId: "aiAssistantClearMutationPolicyOverride",
74
+ summary: "Remove the tenant-scoped mutationPolicy override for this agent.",
75
+ description: "Deletes the override row if it exists; subsequent calls fall back to the agent's code-declared policy. Idempotent \u2014 returns 200 even when no override exists. Requires `ai_assistant.settings.manage`.",
76
+ responses: [
77
+ {
78
+ status: 200,
79
+ description: "Override cleared (or already absent).",
80
+ mediaType: "application/json"
81
+ }
82
+ ],
83
+ errors: [
84
+ { status: 400, description: "Invalid agent id." },
85
+ { status: 401, description: "Unauthenticated caller." },
86
+ { status: 403, description: "Caller lacks `ai_assistant.settings.manage`." },
87
+ { status: 404, description: "Unknown agent id." }
88
+ ]
89
+ }
90
+ }
91
+ };
92
+ const metadata = {
93
+ GET: { requireAuth: true, requireFeatures: [VIEW_FEATURE] },
94
+ POST: { requireAuth: true, requireFeatures: [MANAGE_FEATURE] },
95
+ DELETE: { requireAuth: true, requireFeatures: [MANAGE_FEATURE] }
96
+ };
97
+ function jsonError(status, message, code, extra) {
98
+ return NextResponse.json({ error: message, code, ...extra ?? {} }, { status });
99
+ }
100
+ async function resolveAuthOrRespond(req, requiredFeature) {
101
+ const auth = await getAuthFromRequest(req);
102
+ if (!auth) {
103
+ return jsonError(401, "Unauthorized", "unauthenticated");
104
+ }
105
+ const container = await createRequestContainer();
106
+ const rbacService = container.resolve("rbacService");
107
+ const acl = await rbacService.loadAcl(auth.sub, {
108
+ tenantId: auth.tenantId,
109
+ organizationId: auth.orgId
110
+ });
111
+ if (!hasRequiredFeatures([requiredFeature], acl.features, acl.isSuperAdmin, rbacService)) {
112
+ return jsonError(403, `Caller lacks required feature "${requiredFeature}".`, "forbidden");
113
+ }
114
+ return {
115
+ tenantId: auth.tenantId ?? null,
116
+ organizationId: auth.orgId ?? null,
117
+ userId: auth.sub,
118
+ isSuperAdmin: acl.isSuperAdmin,
119
+ features: acl.features,
120
+ container,
121
+ rbacService
122
+ };
123
+ }
124
+ function serializeOverride(row) {
125
+ return {
126
+ id: row.id,
127
+ agentId: row.agentId,
128
+ mutationPolicy: row.mutationPolicy,
129
+ notes: row.notes ?? null,
130
+ createdByUserId: row.createdByUserId ?? null,
131
+ createdAt: row.createdAt?.toISOString?.() ?? (/* @__PURE__ */ new Date()).toISOString(),
132
+ updatedAt: row.updatedAt?.toISOString?.() ?? (/* @__PURE__ */ new Date()).toISOString()
133
+ };
134
+ }
135
+ function codeDeclaredPolicy(agent) {
136
+ const declared = agent.mutationPolicy;
137
+ return declared && isKnownMutationPolicy(declared) ? declared : "read-only";
138
+ }
139
+ async function GET(req, context) {
140
+ const authResult = await resolveAuthOrRespond(req, VIEW_FEATURE);
141
+ if (authResult instanceof NextResponse) return authResult;
142
+ const rawParams = await context.params;
143
+ const paramResult = agentIdParamSchema.safeParse(rawParams);
144
+ if (!paramResult.success) {
145
+ return jsonError(400, "Invalid agent id.", "validation_error", {
146
+ issues: paramResult.error.issues
147
+ });
148
+ }
149
+ try {
150
+ await loadAgentRegistry();
151
+ const agent = getAgent(paramResult.data.agentId);
152
+ if (!agent) {
153
+ return jsonError(404, `Unknown agent "${paramResult.data.agentId}".`, "agent_unknown");
154
+ }
155
+ const declared = codeDeclaredPolicy(agent);
156
+ if (!authResult.tenantId) {
157
+ return NextResponse.json({
158
+ agentId: agent.id,
159
+ codeDeclared: declared,
160
+ override: null
161
+ });
162
+ }
163
+ const em = authResult.container.resolve("em");
164
+ const repo = new AiAgentMutationPolicyOverrideRepository(em);
165
+ const current = await repo.get(agent.id, {
166
+ tenantId: authResult.tenantId,
167
+ organizationId: authResult.organizationId
168
+ });
169
+ return NextResponse.json({
170
+ agentId: agent.id,
171
+ codeDeclared: declared,
172
+ override: current ? serializeOverride(current) : null
173
+ });
174
+ } catch (error) {
175
+ console.error("[AI Mutation Policy GET] Failure:", error);
176
+ return jsonError(
177
+ 500,
178
+ error instanceof Error ? error.message : "Failed to load mutationPolicy override.",
179
+ "internal_error"
180
+ );
181
+ }
182
+ }
183
+ async function POST(req, context) {
184
+ const authResult = await resolveAuthOrRespond(req, MANAGE_FEATURE);
185
+ if (authResult instanceof NextResponse) return authResult;
186
+ const rawParams = await context.params;
187
+ const paramResult = agentIdParamSchema.safeParse(rawParams);
188
+ if (!paramResult.success) {
189
+ return jsonError(400, "Invalid agent id.", "validation_error", {
190
+ issues: paramResult.error.issues
191
+ });
192
+ }
193
+ let parsedBody;
194
+ try {
195
+ parsedBody = await req.json();
196
+ } catch {
197
+ return jsonError(400, "Request body must be valid JSON.", "validation_error");
198
+ }
199
+ const bodyResult = mutationPolicyRequestSchema.safeParse(parsedBody);
200
+ if (!bodyResult.success) {
201
+ return jsonError(400, "Invalid request body.", "validation_error", {
202
+ issues: bodyResult.error.issues
203
+ });
204
+ }
205
+ try {
206
+ await loadAgentRegistry();
207
+ const agent = getAgent(paramResult.data.agentId);
208
+ if (!agent) {
209
+ return jsonError(404, `Unknown agent "${paramResult.data.agentId}".`, "agent_unknown");
210
+ }
211
+ const declared = codeDeclaredPolicy(agent);
212
+ if (isMutationPolicyEscalation(declared, bodyResult.data.mutationPolicy)) {
213
+ return jsonError(
214
+ 400,
215
+ `Cannot set mutationPolicy="${bodyResult.data.mutationPolicy}" for agent "${agent.id}": the agent's code-declared policy is "${declared}". Upgrading beyond the declared policy is a code-level change, not a configuration change.`,
216
+ "escalation_not_allowed",
217
+ { codeDeclared: declared, requested: bodyResult.data.mutationPolicy }
218
+ );
219
+ }
220
+ if (!authResult.tenantId) {
221
+ return jsonError(
222
+ 400,
223
+ "Caller has no tenant context; cannot persist tenant-scoped mutationPolicy override.",
224
+ "tenant_required"
225
+ );
226
+ }
227
+ const em = authResult.container.resolve("em");
228
+ const repo = new AiAgentMutationPolicyOverrideRepository(em);
229
+ const saved = await repo.set(
230
+ {
231
+ agentId: agent.id,
232
+ mutationPolicy: bodyResult.data.mutationPolicy,
233
+ notes: bodyResult.data.notes ?? null
234
+ },
235
+ {
236
+ tenantId: authResult.tenantId,
237
+ organizationId: authResult.organizationId,
238
+ userId: authResult.userId
239
+ }
240
+ );
241
+ return NextResponse.json({
242
+ ok: true,
243
+ agentId: agent.id,
244
+ codeDeclared: declared,
245
+ override: serializeOverride(saved)
246
+ });
247
+ } catch (error) {
248
+ console.error("[AI Mutation Policy POST] Failure:", error);
249
+ return jsonError(
250
+ 500,
251
+ error instanceof Error ? error.message : "Failed to save mutationPolicy override.",
252
+ "internal_error"
253
+ );
254
+ }
255
+ }
256
+ async function DELETE(req, context) {
257
+ const authResult = await resolveAuthOrRespond(req, MANAGE_FEATURE);
258
+ if (authResult instanceof NextResponse) return authResult;
259
+ const rawParams = await context.params;
260
+ const paramResult = agentIdParamSchema.safeParse(rawParams);
261
+ if (!paramResult.success) {
262
+ return jsonError(400, "Invalid agent id.", "validation_error", {
263
+ issues: paramResult.error.issues
264
+ });
265
+ }
266
+ try {
267
+ await loadAgentRegistry();
268
+ const agent = getAgent(paramResult.data.agentId);
269
+ if (!agent) {
270
+ return jsonError(404, `Unknown agent "${paramResult.data.agentId}".`, "agent_unknown");
271
+ }
272
+ const declared = codeDeclaredPolicy(agent);
273
+ if (!authResult.tenantId) {
274
+ return NextResponse.json({
275
+ ok: true,
276
+ agentId: agent.id,
277
+ codeDeclared: declared,
278
+ override: null,
279
+ cleared: false
280
+ });
281
+ }
282
+ const em = authResult.container.resolve("em");
283
+ const repo = new AiAgentMutationPolicyOverrideRepository(em);
284
+ const cleared = await repo.clear(agent.id, {
285
+ tenantId: authResult.tenantId,
286
+ organizationId: authResult.organizationId
287
+ });
288
+ return NextResponse.json({
289
+ ok: true,
290
+ agentId: agent.id,
291
+ codeDeclared: declared,
292
+ override: null,
293
+ cleared
294
+ });
295
+ } catch (error) {
296
+ console.error("[AI Mutation Policy DELETE] Failure:", error);
297
+ return jsonError(
298
+ 500,
299
+ error instanceof Error ? error.message : "Failed to clear mutationPolicy override.",
300
+ "internal_error"
301
+ );
302
+ }
303
+ }
304
+ export {
305
+ DELETE,
306
+ GET,
307
+ POST,
308
+ metadata,
309
+ openApi
310
+ };
311
+ //# sourceMappingURL=route.js.map