@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,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../../../../../src/modules/ai_assistant/api/ai/agents/%5BagentId%5D/mutation-policy/route.ts"],
4
+ "sourcesContent": ["import { NextResponse, type NextRequest } from 'next/server'\nimport { z } from 'zod'\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 type { EntityManager } from '@mikro-orm/postgresql'\nimport { getAgent, loadAgentRegistry } from '../../../../../lib/agent-registry'\nimport { hasRequiredFeatures } from '../../../../../lib/auth'\nimport {\n isKnownMutationPolicy,\n isMutationPolicyEscalation,\n} from '../../../../../lib/agent-policy'\nimport { AiAgentMutationPolicyOverrideRepository } from '../../../../../data/repositories/AiAgentMutationPolicyOverrideRepository'\nimport type { AiAgentMutationPolicyOverride } from '../../../../../data/entities'\nimport type {\n AiAgentDefinition,\n AiAgentMutationPolicy,\n} from '../../../../../lib/ai-agent-definition'\n\nconst agentIdPattern = /^[a-z0-9_]+\\.[a-z0-9_]+$/\n\nconst agentIdParamSchema = z.object({\n agentId: z\n .string()\n .regex(agentIdPattern, 'agentId must match \"<module>.<agent>\" (lowercase, digits, underscores only)'),\n})\n\nconst mutationPolicySchema = z.enum([\n 'read-only',\n 'confirm-required',\n 'destructive-confirm-required',\n])\n\nconst mutationPolicyRequestSchema = z.object({\n mutationPolicy: mutationPolicySchema,\n notes: z.string().max(2000).optional(),\n})\n\nconst VIEW_FEATURE = 'ai_assistant.view'\nconst MANAGE_FEATURE = 'ai_assistant.settings.manage'\n\nexport const openApi: OpenApiRouteDoc = {\n tag: 'AI Assistant',\n summary: 'Tenant-scoped mutationPolicy override for an AI agent',\n methods: {\n GET: {\n operationId: 'aiAssistantGetMutationPolicyOverride',\n summary:\n 'Read the effective mutationPolicy for an agent \u2014 code-declared value plus any tenant override.',\n description:\n 'Returns `{ agentId, codeDeclared, override }` where `codeDeclared` is the agent\\'s ' +\n 'compiled-in `mutationPolicy` and `override` is the persisted tenant-scoped override ' +\n '(or `null`). Requires `ai_assistant.view`.',\n responses: [\n {\n status: 200,\n description: 'Effective mutationPolicy payload.',\n mediaType: 'application/json',\n },\n ],\n errors: [\n { status: 400, description: 'Invalid agent id.' },\n { status: 401, description: 'Unauthenticated caller.' },\n { status: 403, description: 'Caller lacks `ai_assistant.view`.' },\n { status: 404, description: 'Unknown agent id.' },\n ],\n },\n POST: {\n operationId: 'aiAssistantSaveMutationPolicyOverride',\n summary:\n 'Set (or replace) the tenant-scoped mutationPolicy override for this agent.',\n description:\n 'Body: `{ mutationPolicy: \"read-only\" | \"confirm-required\" | \"destructive-confirm-required\", notes? }`. ' +\n 'The override MUST NOT escalate beyond the agent\\'s code-declared policy. Escalation attempts ' +\n 'are rejected with 400 + `code: \"escalation_not_allowed\"`. Requires `ai_assistant.settings.manage`.',\n requestBody: {\n contentType: 'application/json',\n description: 'Body: `{ mutationPolicy, notes? }`.',\n schema: mutationPolicyRequestSchema,\n },\n responses: [\n {\n status: 200,\n description: 'Override persisted.',\n mediaType: 'application/json',\n },\n ],\n errors: [\n { status: 400, description: 'Invalid agent id, malformed body, or escalation attempt.' },\n { status: 401, description: 'Unauthenticated caller.' },\n { status: 403, description: 'Caller lacks `ai_assistant.settings.manage`.' },\n { status: 404, description: 'Unknown agent id.' },\n ],\n },\n DELETE: {\n operationId: 'aiAssistantClearMutationPolicyOverride',\n summary: 'Remove the tenant-scoped mutationPolicy override for this agent.',\n description:\n 'Deletes the override row if it exists; subsequent calls fall back to the agent\\'s ' +\n 'code-declared policy. Idempotent \u2014 returns 200 even when no override exists. Requires ' +\n '`ai_assistant.settings.manage`.',\n responses: [\n {\n status: 200,\n description: 'Override cleared (or already absent).',\n mediaType: 'application/json',\n },\n ],\n errors: [\n { status: 400, description: 'Invalid agent id.' },\n { status: 401, description: 'Unauthenticated caller.' },\n { status: 403, description: 'Caller lacks `ai_assistant.settings.manage`.' },\n { status: 404, description: 'Unknown agent id.' },\n ],\n },\n },\n}\n\nexport const metadata = {\n GET: { requireAuth: true, requireFeatures: [VIEW_FEATURE] },\n POST: { requireAuth: true, requireFeatures: [MANAGE_FEATURE] },\n DELETE: { requireAuth: true, requireFeatures: [MANAGE_FEATURE] },\n}\n\ninterface RouteContext {\n params: Promise<{ agentId: 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\ninterface ResolvedAuth {\n tenantId: string | null\n organizationId: string | null\n userId: string\n isSuperAdmin: boolean\n features: string[]\n container: Awaited<ReturnType<typeof createRequestContainer>>\n rbacService: RbacService\n}\n\nasync function resolveAuthOrRespond(\n req: NextRequest,\n requiredFeature: string,\n): Promise<ResolvedAuth | NextResponse> {\n const auth = await getAuthFromRequest(req)\n if (!auth) {\n return jsonError(401, 'Unauthorized', 'unauthenticated')\n }\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 if (!hasRequiredFeatures([requiredFeature], acl.features, acl.isSuperAdmin, rbacService)) {\n return jsonError(403, `Caller lacks required feature \"${requiredFeature}\".`, 'forbidden')\n }\n return {\n tenantId: auth.tenantId ?? null,\n organizationId: auth.orgId ?? null,\n userId: auth.sub,\n isSuperAdmin: acl.isSuperAdmin,\n features: acl.features,\n container,\n rbacService,\n }\n}\n\nfunction serializeOverride(row: AiAgentMutationPolicyOverride) {\n return {\n id: row.id,\n agentId: row.agentId,\n mutationPolicy: row.mutationPolicy,\n notes: row.notes ?? null,\n createdByUserId: row.createdByUserId ?? null,\n createdAt: row.createdAt?.toISOString?.() ?? new Date().toISOString(),\n updatedAt: row.updatedAt?.toISOString?.() ?? new Date().toISOString(),\n }\n}\n\nfunction codeDeclaredPolicy(agent: AiAgentDefinition): AiAgentMutationPolicy {\n const declared = agent.mutationPolicy\n return declared && isKnownMutationPolicy(declared) ? declared : 'read-only'\n}\n\nexport async function GET(req: NextRequest, context: RouteContext): Promise<Response> {\n const authResult = await resolveAuthOrRespond(req, VIEW_FEATURE)\n if (authResult instanceof NextResponse) return authResult\n\n const rawParams = await context.params\n const paramResult = agentIdParamSchema.safeParse(rawParams)\n if (!paramResult.success) {\n return jsonError(400, 'Invalid agent id.', 'validation_error', {\n issues: paramResult.error.issues,\n })\n }\n\n try {\n await loadAgentRegistry()\n const agent = getAgent(paramResult.data.agentId)\n if (!agent) {\n return jsonError(404, `Unknown agent \"${paramResult.data.agentId}\".`, 'agent_unknown')\n }\n\n const declared = codeDeclaredPolicy(agent)\n if (!authResult.tenantId) {\n return NextResponse.json({\n agentId: agent.id,\n codeDeclared: declared,\n override: null,\n })\n }\n\n const em = authResult.container.resolve<EntityManager>('em')\n const repo = new AiAgentMutationPolicyOverrideRepository(em)\n const current = await repo.get(agent.id, {\n tenantId: authResult.tenantId,\n organizationId: authResult.organizationId,\n })\n\n return NextResponse.json({\n agentId: agent.id,\n codeDeclared: declared,\n override: current ? serializeOverride(current) : null,\n })\n } catch (error) {\n console.error('[AI Mutation Policy GET] Failure:', error)\n return jsonError(\n 500,\n error instanceof Error ? error.message : 'Failed to load mutationPolicy override.',\n 'internal_error',\n )\n }\n}\n\nexport async function POST(req: NextRequest, context: RouteContext): Promise<Response> {\n const authResult = await resolveAuthOrRespond(req, MANAGE_FEATURE)\n if (authResult instanceof NextResponse) return authResult\n\n const rawParams = await context.params\n const paramResult = agentIdParamSchema.safeParse(rawParams)\n if (!paramResult.success) {\n return jsonError(400, 'Invalid agent id.', 'validation_error', {\n issues: paramResult.error.issues,\n })\n }\n\n let parsedBody: unknown\n try {\n parsedBody = await req.json()\n } catch {\n return jsonError(400, 'Request body must be valid JSON.', 'validation_error')\n }\n\n const bodyResult = mutationPolicyRequestSchema.safeParse(parsedBody)\n if (!bodyResult.success) {\n return jsonError(400, 'Invalid request body.', 'validation_error', {\n issues: bodyResult.error.issues,\n })\n }\n\n try {\n await loadAgentRegistry()\n const agent = getAgent(paramResult.data.agentId)\n if (!agent) {\n return jsonError(404, `Unknown agent \"${paramResult.data.agentId}\".`, 'agent_unknown')\n }\n\n const declared = codeDeclaredPolicy(agent)\n if (isMutationPolicyEscalation(declared, bodyResult.data.mutationPolicy)) {\n return jsonError(\n 400,\n `Cannot set mutationPolicy=\"${bodyResult.data.mutationPolicy}\" for agent \"${agent.id}\": ` +\n `the agent\\'s code-declared policy is \"${declared}\". Upgrading beyond the declared ` +\n `policy is a code-level change, not a configuration change.`,\n 'escalation_not_allowed',\n { codeDeclared: declared, requested: bodyResult.data.mutationPolicy },\n )\n }\n\n if (!authResult.tenantId) {\n return jsonError(\n 400,\n 'Caller has no tenant context; cannot persist tenant-scoped mutationPolicy override.',\n 'tenant_required',\n )\n }\n\n const em = authResult.container.resolve<EntityManager>('em')\n const repo = new AiAgentMutationPolicyOverrideRepository(em)\n const saved = await repo.set(\n {\n agentId: agent.id,\n mutationPolicy: bodyResult.data.mutationPolicy,\n notes: bodyResult.data.notes ?? null,\n },\n {\n tenantId: authResult.tenantId,\n organizationId: authResult.organizationId,\n userId: authResult.userId,\n },\n )\n\n return NextResponse.json({\n ok: true,\n agentId: agent.id,\n codeDeclared: declared,\n override: serializeOverride(saved),\n })\n } catch (error) {\n console.error('[AI Mutation Policy POST] Failure:', error)\n return jsonError(\n 500,\n error instanceof Error ? error.message : 'Failed to save mutationPolicy override.',\n 'internal_error',\n )\n }\n}\n\nexport async function DELETE(req: NextRequest, context: RouteContext): Promise<Response> {\n const authResult = await resolveAuthOrRespond(req, MANAGE_FEATURE)\n if (authResult instanceof NextResponse) return authResult\n\n const rawParams = await context.params\n const paramResult = agentIdParamSchema.safeParse(rawParams)\n if (!paramResult.success) {\n return jsonError(400, 'Invalid agent id.', 'validation_error', {\n issues: paramResult.error.issues,\n })\n }\n\n try {\n await loadAgentRegistry()\n const agent = getAgent(paramResult.data.agentId)\n if (!agent) {\n return jsonError(404, `Unknown agent \"${paramResult.data.agentId}\".`, 'agent_unknown')\n }\n\n const declared = codeDeclaredPolicy(agent)\n if (!authResult.tenantId) {\n return NextResponse.json({\n ok: true,\n agentId: agent.id,\n codeDeclared: declared,\n override: null,\n cleared: false,\n })\n }\n\n const em = authResult.container.resolve<EntityManager>('em')\n const repo = new AiAgentMutationPolicyOverrideRepository(em)\n const cleared = await repo.clear(agent.id, {\n tenantId: authResult.tenantId,\n organizationId: authResult.organizationId,\n })\n\n return NextResponse.json({\n ok: true,\n agentId: agent.id,\n codeDeclared: declared,\n override: null,\n cleared,\n })\n } catch (error) {\n console.error('[AI Mutation Policy DELETE] Failure:', error)\n return jsonError(\n 500,\n error instanceof Error ? error.message : 'Failed to clear mutationPolicy override.',\n 'internal_error',\n )\n }\n}\n"],
5
+ "mappings": "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",
6
+ "names": []
7
+ }
@@ -0,0 +1,246 @@
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 { AiAgentPromptOverrideRepository } from "../../../../../data/repositories/AiAgentPromptOverrideRepository.js";
8
+ import { findReservedKeys } from "../../../../../lib/prompt-override-merge.js";
9
+ const agentIdPattern = /^[a-z0-9_]+\.[a-z0-9_]+$/;
10
+ const agentIdParamSchema = z.object({
11
+ agentId: z.string().regex(agentIdPattern, 'agentId must match "<module>.<agent>" (lowercase, digits, underscores only)')
12
+ });
13
+ const sectionsSchema = z.record(z.string(), z.string());
14
+ const promptOverrideRequestSchema = z.object({
15
+ sections: sectionsSchema.optional(),
16
+ overrides: sectionsSchema.optional(),
17
+ notes: z.string().max(2e3).optional()
18
+ }).refine((value) => value.sections !== void 0 || value.overrides !== void 0, {
19
+ message: "Body must include either `sections` or `overrides`."
20
+ });
21
+ const REQUIRED_FEATURE = "ai_assistant.settings.manage";
22
+ const HISTORY_LIMIT = 10;
23
+ const openApi = {
24
+ tag: "AI Assistant",
25
+ summary: "Versioned additive prompt-overrides for an AI agent",
26
+ methods: {
27
+ GET: {
28
+ operationId: "aiAssistantGetPromptOverride",
29
+ summary: "Read the latest prompt-section override for an agent plus recent version history.",
30
+ description: "Returns `{ agentId, override, versions }` where `override` is the latest persisted row (or `null`) and `versions` is the newest-first history capped at 10 rows. Tenant-scoped; requires `ai_assistant.settings.manage`.",
31
+ responses: [
32
+ {
33
+ status: 200,
34
+ description: "Latest override + recent version history.",
35
+ mediaType: "application/json"
36
+ }
37
+ ],
38
+ errors: [
39
+ { status: 400, description: "Invalid agent id." },
40
+ { status: 401, description: "Unauthenticated caller." },
41
+ { status: 403, description: "Caller lacks `ai_assistant.settings.manage`." },
42
+ { status: 404, description: "Unknown agent id." }
43
+ ]
44
+ },
45
+ POST: {
46
+ operationId: "aiAssistantSavePromptOverride",
47
+ summary: "Save a new prompt-section override version for the agent.",
48
+ description: "Persists an additive `{ sections: Record<sectionId, text>, notes? }` override, allocating the next monotonic version for `(tenant, org, agent)`. Reserved policy keys (`mutationPolicy`, `readOnly`, `allowedTools`, `acceptedMediaTypes`) are rejected with 400 / `reserved_key`. Requires `ai_assistant.settings.manage`.",
49
+ requestBody: {
50
+ contentType: "application/json",
51
+ description: "Body: `{ sections: Record<string, string>, notes?: string }`.",
52
+ schema: promptOverrideRequestSchema
53
+ },
54
+ responses: [
55
+ {
56
+ status: 200,
57
+ description: "Override persisted. Returns `{ ok: true, version, updatedAt }`.",
58
+ mediaType: "application/json"
59
+ }
60
+ ],
61
+ errors: [
62
+ { status: 400, description: "Invalid agent id, malformed body, or reserved policy key." },
63
+ { status: 401, description: "Unauthenticated caller." },
64
+ { status: 403, description: "Caller lacks `ai_assistant.settings.manage`." },
65
+ { status: 404, description: "Unknown agent id." }
66
+ ]
67
+ }
68
+ }
69
+ };
70
+ const metadata = {
71
+ requireAuth: true,
72
+ requireFeatures: [REQUIRED_FEATURE]
73
+ };
74
+ function jsonError(status, message, code, extra) {
75
+ return NextResponse.json({ error: message, code, ...extra ?? {} }, { status });
76
+ }
77
+ async function resolveAuthOrRespond(req) {
78
+ const auth = await getAuthFromRequest(req);
79
+ if (!auth) {
80
+ return jsonError(401, "Unauthorized", "unauthenticated");
81
+ }
82
+ const container = await createRequestContainer();
83
+ const rbacService = container.resolve("rbacService");
84
+ const acl = await rbacService.loadAcl(auth.sub, {
85
+ tenantId: auth.tenantId,
86
+ organizationId: auth.orgId
87
+ });
88
+ if (!hasRequiredFeatures([REQUIRED_FEATURE], acl.features, acl.isSuperAdmin, rbacService)) {
89
+ return jsonError(403, `Caller lacks required feature "${REQUIRED_FEATURE}".`, "forbidden");
90
+ }
91
+ return {
92
+ tenantId: auth.tenantId ?? null,
93
+ organizationId: auth.orgId ?? null,
94
+ userId: auth.sub,
95
+ isSuperAdmin: acl.isSuperAdmin,
96
+ features: acl.features,
97
+ container,
98
+ rbacService
99
+ };
100
+ }
101
+ function serializeOverride(row) {
102
+ return {
103
+ id: row.id,
104
+ agentId: row.agentId,
105
+ version: row.version,
106
+ sections: row.sections ?? {},
107
+ notes: row.notes ?? null,
108
+ createdByUserId: row.createdByUserId ?? null,
109
+ createdAt: row.createdAt?.toISOString?.() ?? (/* @__PURE__ */ new Date()).toISOString(),
110
+ updatedAt: row.updatedAt?.toISOString?.() ?? (/* @__PURE__ */ new Date()).toISOString()
111
+ };
112
+ }
113
+ async function GET(req, context) {
114
+ const authResult = await resolveAuthOrRespond(req);
115
+ if (authResult instanceof NextResponse) return authResult;
116
+ const rawParams = await context.params;
117
+ const paramResult = agentIdParamSchema.safeParse(rawParams);
118
+ if (!paramResult.success) {
119
+ return jsonError(400, "Invalid agent id.", "validation_error", {
120
+ issues: paramResult.error.issues
121
+ });
122
+ }
123
+ try {
124
+ await loadAgentRegistry();
125
+ const agent = getAgent(paramResult.data.agentId);
126
+ if (!agent) {
127
+ return jsonError(404, `Unknown agent "${paramResult.data.agentId}".`, "agent_unknown");
128
+ }
129
+ if (!authResult.tenantId) {
130
+ return NextResponse.json({
131
+ agentId: agent.id,
132
+ override: null,
133
+ versions: []
134
+ });
135
+ }
136
+ const em = authResult.container.resolve("em");
137
+ const repo = new AiAgentPromptOverrideRepository(em);
138
+ const [latest, versions] = await Promise.all([
139
+ repo.getLatest(agent.id, {
140
+ tenantId: authResult.tenantId,
141
+ organizationId: authResult.organizationId
142
+ }),
143
+ repo.listVersions(
144
+ agent.id,
145
+ {
146
+ tenantId: authResult.tenantId,
147
+ organizationId: authResult.organizationId
148
+ },
149
+ HISTORY_LIMIT
150
+ )
151
+ ]);
152
+ return NextResponse.json({
153
+ agentId: agent.id,
154
+ override: latest ? serializeOverride(latest) : null,
155
+ versions: versions.map(serializeOverride)
156
+ });
157
+ } catch (error) {
158
+ console.error("[AI Prompt Override GET] Failure:", error);
159
+ return jsonError(
160
+ 500,
161
+ error instanceof Error ? error.message : "Failed to load prompt override.",
162
+ "internal_error"
163
+ );
164
+ }
165
+ }
166
+ async function POST(req, context) {
167
+ const authResult = await resolveAuthOrRespond(req);
168
+ if (authResult instanceof NextResponse) return authResult;
169
+ const rawParams = await context.params;
170
+ const paramResult = agentIdParamSchema.safeParse(rawParams);
171
+ if (!paramResult.success) {
172
+ return jsonError(400, "Invalid agent id.", "validation_error", {
173
+ issues: paramResult.error.issues
174
+ });
175
+ }
176
+ let parsedBody;
177
+ try {
178
+ parsedBody = await req.json();
179
+ } catch {
180
+ return jsonError(400, "Request body must be valid JSON.", "validation_error");
181
+ }
182
+ const bodyResult = promptOverrideRequestSchema.safeParse(parsedBody);
183
+ if (!bodyResult.success) {
184
+ return jsonError(400, "Invalid request body.", "validation_error", {
185
+ issues: bodyResult.error.issues
186
+ });
187
+ }
188
+ const sections = bodyResult.data.sections ?? bodyResult.data.overrides ?? {};
189
+ const reservedHits = findReservedKeys(sections);
190
+ if (reservedHits.length > 0) {
191
+ return jsonError(
192
+ 400,
193
+ `Prompt override contains reserved policy keys: ${reservedHits.join(", ")}.`,
194
+ "reserved_key",
195
+ { reservedKeys: reservedHits }
196
+ );
197
+ }
198
+ try {
199
+ await loadAgentRegistry();
200
+ const agent = getAgent(paramResult.data.agentId);
201
+ if (!agent) {
202
+ return jsonError(404, `Unknown agent "${paramResult.data.agentId}".`, "agent_unknown");
203
+ }
204
+ if (!authResult.tenantId) {
205
+ return jsonError(
206
+ 400,
207
+ "Caller has no tenant context; cannot persist tenant-scoped prompt override.",
208
+ "tenant_required"
209
+ );
210
+ }
211
+ const em = authResult.container.resolve("em");
212
+ const repo = new AiAgentPromptOverrideRepository(em);
213
+ const saved = await repo.save(
214
+ {
215
+ agentId: agent.id,
216
+ sections,
217
+ notes: bodyResult.data.notes ?? null
218
+ },
219
+ {
220
+ tenantId: authResult.tenantId,
221
+ organizationId: authResult.organizationId,
222
+ userId: authResult.userId
223
+ }
224
+ );
225
+ return NextResponse.json({
226
+ ok: true,
227
+ agentId: agent.id,
228
+ version: saved.version,
229
+ updatedAt: saved.updatedAt?.toISOString?.() ?? (/* @__PURE__ */ new Date()).toISOString()
230
+ });
231
+ } catch (error) {
232
+ console.error("[AI Prompt Override POST] Failure:", error);
233
+ return jsonError(
234
+ 500,
235
+ error instanceof Error ? error.message : "Failed to save prompt override.",
236
+ "internal_error"
237
+ );
238
+ }
239
+ }
240
+ export {
241
+ GET,
242
+ POST,
243
+ metadata,
244
+ openApi
245
+ };
246
+ //# sourceMappingURL=route.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../../../../../src/modules/ai_assistant/api/ai/agents/%5BagentId%5D/prompt-override/route.ts"],
4
+ "sourcesContent": ["import { NextResponse, type NextRequest } from 'next/server'\nimport { z } from 'zod'\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 type { EntityManager } from '@mikro-orm/postgresql'\nimport { getAgent, loadAgentRegistry } from '../../../../../lib/agent-registry'\nimport { hasRequiredFeatures } from '../../../../../lib/auth'\nimport { AiAgentPromptOverrideRepository } from '../../../../../data/repositories/AiAgentPromptOverrideRepository'\nimport type { AiAgentPromptOverride } from '../../../../../data/entities'\nimport { findReservedKeys } from '../../../../../lib/prompt-override-merge'\n\nconst agentIdPattern = /^[a-z0-9_]+\\.[a-z0-9_]+$/\n\nconst agentIdParamSchema = z.object({\n agentId: z\n .string()\n .regex(agentIdPattern, 'agentId must match \"<module>.<agent>\" (lowercase, digits, underscores only)'),\n})\n\nconst sectionsSchema = z.record(z.string(), z.string())\n\n/**\n * Accept both `sections` (Step 5.3 canonical shape) and the Step 4.5\n * placeholder `overrides` key so UI callers that haven't redeployed still\n * work. `sections` wins when both are present.\n */\nconst promptOverrideRequestSchema = z\n .object({\n sections: sectionsSchema.optional(),\n overrides: sectionsSchema.optional(),\n notes: z.string().max(2000).optional(),\n })\n .refine((value) => value.sections !== undefined || value.overrides !== undefined, {\n message: 'Body must include either `sections` or `overrides`.',\n })\n\nexport type AiPromptOverrideRequest = z.infer<typeof promptOverrideRequestSchema>\n\nconst REQUIRED_FEATURE = 'ai_assistant.settings.manage'\nconst HISTORY_LIMIT = 10\n\nexport const openApi: OpenApiRouteDoc = {\n tag: 'AI Assistant',\n summary: 'Versioned additive prompt-overrides for an AI agent',\n methods: {\n GET: {\n operationId: 'aiAssistantGetPromptOverride',\n summary: 'Read the latest prompt-section override for an agent plus recent version history.',\n description:\n 'Returns `{ agentId, override, versions }` where `override` is the latest persisted ' +\n 'row (or `null`) and `versions` is the newest-first history capped at 10 rows. ' +\n 'Tenant-scoped; requires `ai_assistant.settings.manage`.',\n responses: [\n {\n status: 200,\n description: 'Latest override + recent version history.',\n mediaType: 'application/json',\n },\n ],\n errors: [\n { status: 400, description: 'Invalid agent id.' },\n { status: 401, description: 'Unauthenticated caller.' },\n { status: 403, description: 'Caller lacks `ai_assistant.settings.manage`.' },\n { status: 404, description: 'Unknown agent id.' },\n ],\n },\n POST: {\n operationId: 'aiAssistantSavePromptOverride',\n summary: 'Save a new prompt-section override version for the agent.',\n description:\n 'Persists an additive `{ sections: Record<sectionId, text>, notes? }` override, allocating ' +\n 'the next monotonic version for `(tenant, org, agent)`. Reserved policy keys ' +\n '(`mutationPolicy`, `readOnly`, `allowedTools`, `acceptedMediaTypes`) are rejected with ' +\n '400 / `reserved_key`. Requires `ai_assistant.settings.manage`.',\n requestBody: {\n contentType: 'application/json',\n description: 'Body: `{ sections: Record<string, string>, notes?: string }`.',\n schema: promptOverrideRequestSchema,\n },\n responses: [\n {\n status: 200,\n description: 'Override persisted. Returns `{ ok: true, version, updatedAt }`.',\n mediaType: 'application/json',\n },\n ],\n errors: [\n { status: 400, description: 'Invalid agent id, malformed body, or reserved policy key.' },\n { status: 401, description: 'Unauthenticated caller.' },\n { status: 403, description: 'Caller lacks `ai_assistant.settings.manage`.' },\n { status: 404, description: 'Unknown agent id.' },\n ],\n },\n },\n}\n\nexport const metadata = {\n requireAuth: true,\n requireFeatures: [REQUIRED_FEATURE],\n}\n\ninterface RouteContext {\n params: Promise<{ agentId: 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\ninterface ResolvedAuth {\n tenantId: string | null\n organizationId: string | null\n userId: string\n isSuperAdmin: boolean\n features: string[]\n container: Awaited<ReturnType<typeof createRequestContainer>>\n rbacService: RbacService\n}\n\nasync function resolveAuthOrRespond(\n req: NextRequest,\n): Promise<ResolvedAuth | NextResponse> {\n const auth = await getAuthFromRequest(req)\n if (!auth) {\n return jsonError(401, 'Unauthorized', 'unauthenticated')\n }\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 if (!hasRequiredFeatures([REQUIRED_FEATURE], acl.features, acl.isSuperAdmin, rbacService)) {\n return jsonError(403, `Caller lacks required feature \"${REQUIRED_FEATURE}\".`, 'forbidden')\n }\n return {\n tenantId: auth.tenantId ?? null,\n organizationId: auth.orgId ?? null,\n userId: auth.sub,\n isSuperAdmin: acl.isSuperAdmin,\n features: acl.features,\n container,\n rbacService,\n }\n}\n\nfunction serializeOverride(row: AiAgentPromptOverride) {\n return {\n id: row.id,\n agentId: row.agentId,\n version: row.version,\n sections: row.sections ?? {},\n notes: row.notes ?? null,\n createdByUserId: row.createdByUserId ?? null,\n createdAt: row.createdAt?.toISOString?.() ?? new Date().toISOString(),\n updatedAt: row.updatedAt?.toISOString?.() ?? new Date().toISOString(),\n }\n}\n\nexport async function GET(req: NextRequest, context: RouteContext): Promise<Response> {\n const authResult = await resolveAuthOrRespond(req)\n if (authResult instanceof NextResponse) return authResult\n\n const rawParams = await context.params\n const paramResult = agentIdParamSchema.safeParse(rawParams)\n if (!paramResult.success) {\n return jsonError(400, 'Invalid agent id.', 'validation_error', {\n issues: paramResult.error.issues,\n })\n }\n\n try {\n await loadAgentRegistry()\n const agent = getAgent(paramResult.data.agentId)\n if (!agent) {\n return jsonError(404, `Unknown agent \"${paramResult.data.agentId}\".`, 'agent_unknown')\n }\n\n if (!authResult.tenantId) {\n return NextResponse.json({\n agentId: agent.id,\n override: null,\n versions: [],\n })\n }\n\n const em = authResult.container.resolve<EntityManager>('em')\n const repo = new AiAgentPromptOverrideRepository(em)\n const [latest, versions] = await Promise.all([\n repo.getLatest(agent.id, {\n tenantId: authResult.tenantId,\n organizationId: authResult.organizationId,\n }),\n repo.listVersions(\n agent.id,\n {\n tenantId: authResult.tenantId,\n organizationId: authResult.organizationId,\n },\n HISTORY_LIMIT,\n ),\n ])\n\n return NextResponse.json({\n agentId: agent.id,\n override: latest ? serializeOverride(latest) : null,\n versions: versions.map(serializeOverride),\n })\n } catch (error) {\n console.error('[AI Prompt Override GET] Failure:', error)\n return jsonError(\n 500,\n error instanceof Error ? error.message : 'Failed to load prompt override.',\n 'internal_error',\n )\n }\n}\n\nexport async function POST(req: NextRequest, context: RouteContext): Promise<Response> {\n const authResult = await resolveAuthOrRespond(req)\n if (authResult instanceof NextResponse) return authResult\n\n const rawParams = await context.params\n const paramResult = agentIdParamSchema.safeParse(rawParams)\n if (!paramResult.success) {\n return jsonError(400, 'Invalid agent id.', 'validation_error', {\n issues: paramResult.error.issues,\n })\n }\n\n let parsedBody: unknown\n try {\n parsedBody = await req.json()\n } catch {\n return jsonError(400, 'Request body must be valid JSON.', 'validation_error')\n }\n\n const bodyResult = promptOverrideRequestSchema.safeParse(parsedBody)\n if (!bodyResult.success) {\n return jsonError(400, 'Invalid request body.', 'validation_error', {\n issues: bodyResult.error.issues,\n })\n }\n\n const sections = bodyResult.data.sections ?? bodyResult.data.overrides ?? {}\n const reservedHits = findReservedKeys(sections)\n if (reservedHits.length > 0) {\n return jsonError(\n 400,\n `Prompt override contains reserved policy keys: ${reservedHits.join(', ')}.`,\n 'reserved_key',\n { reservedKeys: reservedHits },\n )\n }\n\n try {\n await loadAgentRegistry()\n const agent = getAgent(paramResult.data.agentId)\n if (!agent) {\n return jsonError(404, `Unknown agent \"${paramResult.data.agentId}\".`, 'agent_unknown')\n }\n\n if (!authResult.tenantId) {\n return jsonError(\n 400,\n 'Caller has no tenant context; cannot persist tenant-scoped prompt override.',\n 'tenant_required',\n )\n }\n\n const em = authResult.container.resolve<EntityManager>('em')\n const repo = new AiAgentPromptOverrideRepository(em)\n const saved = await repo.save(\n {\n agentId: agent.id,\n sections,\n notes: bodyResult.data.notes ?? null,\n },\n {\n tenantId: authResult.tenantId,\n organizationId: authResult.organizationId,\n userId: authResult.userId,\n },\n )\n\n return NextResponse.json({\n ok: true,\n agentId: agent.id,\n version: saved.version,\n updatedAt: saved.updatedAt?.toISOString?.() ?? new Date().toISOString(),\n })\n } catch (error) {\n console.error('[AI Prompt Override POST] Failure:', error)\n return jsonError(\n 500,\n error instanceof Error ? error.message : 'Failed to save prompt override.',\n 'internal_error',\n )\n }\n}\n"],
5
+ "mappings": "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",
6
+ "names": []
7
+ }
@@ -0,0 +1,94 @@
1
+ import { NextResponse } from "next/server";
2
+ import { getAuthFromRequest } from "@open-mercato/shared/lib/auth/server";
3
+ import { createRequestContainer } from "@open-mercato/shared/lib/di/container";
4
+ import { llmProviderRegistry } from "@open-mercato/shared/lib/ai/llm-provider-registry";
5
+ import { listAgents, loadAgentRegistry } from "../../../lib/agent-registry.js";
6
+ import { hasRequiredFeatures } from "../../../lib/auth.js";
7
+ import { toolRegistry } from "../../../lib/tool-registry.js";
8
+ const openApi = {
9
+ tag: "AI Assistant",
10
+ summary: "List AI agents the caller can invoke",
11
+ methods: {
12
+ GET: {
13
+ operationId: "aiAssistantListAgents",
14
+ summary: "List registered AI agents, filtered by the caller's features.",
15
+ description: "Returns `{ agents: [...] }` \u2014 the subset of agents from `ai-agents.generated.ts` that the authenticated caller can invoke based on each agent's `requiredFeatures`. Mirrors the `meta.list_agents` tool handler so backoffice pages (e.g. the playground) can render an agent picker without going through the MCP tool transport.",
16
+ responses: [
17
+ {
18
+ status: 200,
19
+ description: "Accessible agent summaries.",
20
+ mediaType: "application/json"
21
+ }
22
+ ],
23
+ errors: [
24
+ { status: 401, description: "Unauthenticated caller." },
25
+ { status: 500, description: "Internal failure while loading the agent registry." }
26
+ ]
27
+ }
28
+ }
29
+ };
30
+ const metadata = {
31
+ GET: { requireAuth: true, requireFeatures: ["ai_assistant.view"] }
32
+ };
33
+ async function GET(req) {
34
+ const auth = await getAuthFromRequest(req);
35
+ if (!auth) {
36
+ return NextResponse.json({ error: "Unauthorized", code: "unauthenticated" }, { status: 401 });
37
+ }
38
+ try {
39
+ const container = await createRequestContainer();
40
+ const rbacService = container.resolve("rbacService");
41
+ const acl = await rbacService.loadAcl(auth.sub, {
42
+ tenantId: auth.tenantId,
43
+ organizationId: auth.orgId
44
+ });
45
+ const aiConfigured = llmProviderRegistry.resolveFirstConfigured() != null;
46
+ await loadAgentRegistry();
47
+ const all = listAgents();
48
+ const accessible = all.filter(
49
+ (agent) => hasRequiredFeatures(agent.requiredFeatures, acl.features, acl.isSuperAdmin, rbacService)
50
+ );
51
+ const agents = accessible.map((agent) => {
52
+ const tools = agent.allowedTools.map((toolName) => {
53
+ const tool = toolRegistry.getTool(toolName);
54
+ return {
55
+ name: toolName,
56
+ displayName: tool?.displayName ?? toolName,
57
+ isMutation: Boolean(tool?.isMutation),
58
+ registered: Boolean(tool)
59
+ };
60
+ });
61
+ return {
62
+ id: agent.id,
63
+ moduleId: agent.moduleId,
64
+ label: agent.label,
65
+ description: agent.description,
66
+ systemPrompt: agent.systemPrompt,
67
+ executionMode: agent.executionMode ?? "chat",
68
+ mutationPolicy: agent.mutationPolicy ?? "read-only",
69
+ readOnly: Boolean(agent.readOnly),
70
+ maxSteps: agent.maxSteps ?? null,
71
+ allowedTools: agent.allowedTools,
72
+ tools,
73
+ requiredFeatures: agent.requiredFeatures ?? [],
74
+ acceptedMediaTypes: agent.acceptedMediaTypes ?? [],
75
+ keywords: agent.keywords ?? [],
76
+ suggestions: agent.suggestions ?? [],
77
+ hasOutputSchema: Boolean(agent.output)
78
+ };
79
+ });
80
+ return NextResponse.json({ agents, total: agents.length, aiConfigured });
81
+ } catch (error) {
82
+ console.error("[AI Agents] Failed to list agents:", error);
83
+ return NextResponse.json(
84
+ { error: "Failed to list agents", code: "internal_error" },
85
+ { status: 500 }
86
+ );
87
+ }
88
+ }
89
+ export {
90
+ GET,
91
+ metadata,
92
+ openApi
93
+ };
94
+ //# sourceMappingURL=route.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../../../src/modules/ai_assistant/api/ai/agents/route.ts"],
4
+ "sourcesContent": ["import { NextResponse, type NextRequest } from 'next/server'\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 { llmProviderRegistry } from '@open-mercato/shared/lib/ai/llm-provider-registry'\nimport { listAgents, loadAgentRegistry } from '../../../lib/agent-registry'\nimport { hasRequiredFeatures } from '../../../lib/auth'\nimport { toolRegistry } from '../../../lib/tool-registry'\nimport type { AiToolDefinition } from '../../../lib/types'\n\nexport const openApi: OpenApiRouteDoc = {\n tag: 'AI Assistant',\n summary: 'List AI agents the caller can invoke',\n methods: {\n GET: {\n operationId: 'aiAssistantListAgents',\n summary: 'List registered AI agents, filtered by the caller\\'s features.',\n description:\n 'Returns `{ agents: [...] }` \u2014 the subset of agents from `ai-agents.generated.ts` that the ' +\n 'authenticated caller can invoke based on each agent\\'s `requiredFeatures`. Mirrors the ' +\n '`meta.list_agents` tool handler so backoffice pages (e.g. the playground) can render an ' +\n 'agent picker without going through the MCP tool transport.',\n responses: [\n {\n status: 200,\n description: 'Accessible agent summaries.',\n mediaType: 'application/json',\n },\n ],\n errors: [\n { status: 401, description: 'Unauthenticated caller.' },\n { status: 500, description: 'Internal failure while loading the agent registry.' },\n ],\n },\n },\n}\n\nexport const metadata = {\n GET: { requireAuth: true, requireFeatures: ['ai_assistant.view'] },\n}\n\nexport async function GET(req: NextRequest) {\n const auth = await getAuthFromRequest(req)\n if (!auth) {\n return NextResponse.json({ error: 'Unauthorized', code: 'unauthenticated' }, { status: 401 })\n }\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 // No LLM provider configured (no API keys set). The launcher uses the\n // `aiConfigured` flag to show a setup prompt; explicit `<AiChat>` mounts\n // and playground pages still see the full registry so they can show their\n // own configuration prompts instead of silently disappearing.\n const aiConfigured = llmProviderRegistry.resolveFirstConfigured() != null\n\n await loadAgentRegistry()\n const all = listAgents()\n const accessible = all.filter((agent) =>\n hasRequiredFeatures(agent.requiredFeatures, acl.features, acl.isSuperAdmin, rbacService),\n )\n\n const agents = accessible.map((agent) => {\n const tools = agent.allowedTools.map((toolName) => {\n const tool = toolRegistry.getTool(toolName) as AiToolDefinition | undefined\n return {\n name: toolName,\n displayName: tool?.displayName ?? toolName,\n isMutation: Boolean(tool?.isMutation),\n registered: Boolean(tool),\n }\n })\n return {\n id: agent.id,\n moduleId: agent.moduleId,\n label: agent.label,\n description: agent.description,\n systemPrompt: agent.systemPrompt,\n executionMode: agent.executionMode ?? 'chat',\n mutationPolicy: agent.mutationPolicy ?? 'read-only',\n readOnly: Boolean(agent.readOnly),\n maxSteps: agent.maxSteps ?? null,\n allowedTools: agent.allowedTools,\n tools,\n requiredFeatures: agent.requiredFeatures ?? [],\n acceptedMediaTypes: agent.acceptedMediaTypes ?? [],\n keywords: agent.keywords ?? [],\n suggestions: agent.suggestions ?? [],\n hasOutputSchema: Boolean(agent.output),\n }\n })\n\n return NextResponse.json({ agents, total: agents.length, aiConfigured })\n } catch (error) {\n console.error('[AI Agents] Failed to list agents:', error)\n return NextResponse.json(\n { error: 'Failed to list agents', code: 'internal_error' },\n { status: 500 },\n )\n }\n}\n"],
5
+ "mappings": "AAAA,SAAS,oBAAsC;AAE/C,SAAS,0BAA0B;AACnC,SAAS,8BAA8B;AAEvC,SAAS,2BAA2B;AACpC,SAAS,YAAY,yBAAyB;AAC9C,SAAS,2BAA2B;AACpC,SAAS,oBAAoB;AAGtB,MAAM,UAA2B;AAAA,EACtC,KAAK;AAAA,EACL,SAAS;AAAA,EACT,SAAS;AAAA,IACP,KAAK;AAAA,MACH,aAAa;AAAA,MACb,SAAS;AAAA,MACT,aACE;AAAA,MAIF,WAAW;AAAA,QACT;AAAA,UACE,QAAQ;AAAA,UACR,aAAa;AAAA,UACb,WAAW;AAAA,QACb;AAAA,MACF;AAAA,MACA,QAAQ;AAAA,QACN,EAAE,QAAQ,KAAK,aAAa,0BAA0B;AAAA,QACtD,EAAE,QAAQ,KAAK,aAAa,qDAAqD;AAAA,MACnF;AAAA,IACF;AAAA,EACF;AACF;AAEO,MAAM,WAAW;AAAA,EACtB,KAAK,EAAE,aAAa,MAAM,iBAAiB,CAAC,mBAAmB,EAAE;AACnE;AAEA,eAAsB,IAAI,KAAkB;AAC1C,QAAM,OAAO,MAAM,mBAAmB,GAAG;AACzC,MAAI,CAAC,MAAM;AACT,WAAO,aAAa,KAAK,EAAE,OAAO,gBAAgB,MAAM,kBAAkB,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,EAC9F;AAEA,MAAI;AACF,UAAM,YAAY,MAAM,uBAAuB;AAC/C,UAAM,cAAc,UAAU,QAAqB,aAAa;AAChE,UAAM,MAAM,MAAM,YAAY,QAAQ,KAAK,KAAK;AAAA,MAC9C,UAAU,KAAK;AAAA,MACf,gBAAgB,KAAK;AAAA,IACvB,CAAC;AAMD,UAAM,eAAe,oBAAoB,uBAAuB,KAAK;AAErE,UAAM,kBAAkB;AACxB,UAAM,MAAM,WAAW;AACvB,UAAM,aAAa,IAAI;AAAA,MAAO,CAAC,UAC7B,oBAAoB,MAAM,kBAAkB,IAAI,UAAU,IAAI,cAAc,WAAW;AAAA,IACzF;AAEA,UAAM,SAAS,WAAW,IAAI,CAAC,UAAU;AACvC,YAAM,QAAQ,MAAM,aAAa,IAAI,CAAC,aAAa;AACjD,cAAM,OAAO,aAAa,QAAQ,QAAQ;AAC1C,eAAO;AAAA,UACL,MAAM;AAAA,UACN,aAAa,MAAM,eAAe;AAAA,UAClC,YAAY,QAAQ,MAAM,UAAU;AAAA,UACpC,YAAY,QAAQ,IAAI;AAAA,QAC1B;AAAA,MACF,CAAC;AACD,aAAO;AAAA,QACL,IAAI,MAAM;AAAA,QACV,UAAU,MAAM;AAAA,QAChB,OAAO,MAAM;AAAA,QACb,aAAa,MAAM;AAAA,QACnB,cAAc,MAAM;AAAA,QACpB,eAAe,MAAM,iBAAiB;AAAA,QACtC,gBAAgB,MAAM,kBAAkB;AAAA,QACxC,UAAU,QAAQ,MAAM,QAAQ;AAAA,QAChC,UAAU,MAAM,YAAY;AAAA,QAC5B,cAAc,MAAM;AAAA,QACpB;AAAA,QACA,kBAAkB,MAAM,oBAAoB,CAAC;AAAA,QAC7C,oBAAoB,MAAM,sBAAsB,CAAC;AAAA,QACjD,UAAU,MAAM,YAAY,CAAC;AAAA,QAC7B,aAAa,MAAM,eAAe,CAAC;AAAA,QACnC,iBAAiB,QAAQ,MAAM,MAAM;AAAA,MACvC;AAAA,IACF,CAAC;AAED,WAAO,aAAa,KAAK,EAAE,QAAQ,OAAO,OAAO,QAAQ,aAAa,CAAC;AAAA,EACzE,SAAS,OAAO;AACd,YAAQ,MAAM,sCAAsC,KAAK;AACzD,WAAO,aAAa;AAAA,MAClB,EAAE,OAAO,yBAAyB,MAAM,iBAAiB;AAAA,MACzD,EAAE,QAAQ,IAAI;AAAA,IAChB;AAAA,EACF;AACF;",
6
+ "names": []
7
+ }
@@ -0,0 +1,173 @@
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 { loadAgentRegistry } from "../../../lib/agent-registry.js";
6
+ import { checkAgentPolicy } from "../../../lib/agent-policy.js";
7
+ import { runAiAgentText } from "../../../lib/agent-runtime.js";
8
+ import { AgentPolicyError } from "../../../lib/agent-tools.js";
9
+ const MAX_MESSAGES = 100;
10
+ const agentIdPattern = /^[a-z0-9_]+\.[a-z0-9_]+$/;
11
+ const chatMessageSchema = z.object({
12
+ role: z.enum(["user", "assistant", "system"]),
13
+ content: z.string()
14
+ });
15
+ const pageContextSchema = z.object({
16
+ pageId: z.string().nullable().optional(),
17
+ entityType: z.string().nullable().optional(),
18
+ recordId: z.string().nullable().optional()
19
+ }).passthrough();
20
+ const chatRequestSchema = z.object({
21
+ messages: z.array(chatMessageSchema).min(1, "messages must contain at least one message").max(MAX_MESSAGES, `messages must contain at most ${MAX_MESSAGES} entries`),
22
+ attachmentIds: z.array(z.string()).optional(),
23
+ debug: z.boolean().optional(),
24
+ pageContext: pageContextSchema.optional(),
25
+ /**
26
+ * Optional stable conversation id forwarded from `<AiChat>`. Bridged into
27
+ * the Step 5.6 `prepareMutation` idempotency hash so repeated turns within
28
+ * the same chat collapse onto the same pending action. Additive; omitted
29
+ * bodies continue to work as before.
30
+ */
31
+ conversationId: z.string().min(1).max(128).optional()
32
+ });
33
+ const agentQuerySchema = z.object({
34
+ agent: z.string().regex(agentIdPattern, 'agent must match "<module>.<agent>" (lowercase, digits, underscores only)')
35
+ });
36
+ const openApi = {
37
+ tag: "AI Assistant",
38
+ summary: "AI agent dispatcher",
39
+ methods: {
40
+ POST: {
41
+ operationId: "aiAssistantChatAgent",
42
+ summary: "Stream a chat turn for a registered AI agent",
43
+ description: "Dispatches a chat turn to the focused AI agent identified by `?agent=<module>.<agent>`. Enforces agent-level `requiredFeatures`, tool whitelisting, read-only / mutationPolicy, execution-mode compatibility, and attachment media-type policy. The streaming response body uses an AI SDK-compatible `text/event-stream` transport.",
44
+ query: agentQuerySchema,
45
+ requestBody: {
46
+ contentType: "application/json",
47
+ description: "Chat turn payload. `messages` is required; `attachmentIds`, `debug`, and `pageContext` are optional.",
48
+ schema: chatRequestSchema
49
+ },
50
+ responses: [
51
+ { status: 200, description: "Streaming text/event-stream response compatible with AI SDK chat transports.", mediaType: "text/event-stream" }
52
+ ],
53
+ errors: [
54
+ { status: 400, description: "Invalid query param, malformed payload, or message count above the cap." },
55
+ { status: 401, description: "Unauthenticated caller." },
56
+ { status: 403, description: "Caller lacks agent-level or tool-level required features." },
57
+ { status: 404, description: "Unknown agent id." },
58
+ { status: 409, description: "Agent/tool/execution-mode policy violation." },
59
+ { status: 500, description: "Internal runtime failure." }
60
+ ]
61
+ }
62
+ }
63
+ };
64
+ const metadata = {
65
+ POST: { requireAuth: true, requireFeatures: ["ai_assistant.view"] }
66
+ };
67
+ function jsonError(status, message, code, extra) {
68
+ return NextResponse.json({ error: message, code, ...extra ?? {} }, { status });
69
+ }
70
+ function statusForDenyCode(code) {
71
+ switch (code) {
72
+ case "agent_unknown":
73
+ return 404;
74
+ case "agent_features_denied":
75
+ case "tool_features_denied":
76
+ return 403;
77
+ case "tool_not_whitelisted":
78
+ case "tool_unknown":
79
+ case "mutation_blocked_by_readonly":
80
+ case "mutation_blocked_by_policy":
81
+ case "execution_mode_not_supported":
82
+ return 409;
83
+ case "attachment_type_not_accepted":
84
+ return 400;
85
+ default:
86
+ return 409;
87
+ }
88
+ }
89
+ async function POST(req) {
90
+ const auth = await getAuthFromRequest(req);
91
+ if (!auth) {
92
+ return jsonError(401, "Unauthorized", "unauthenticated");
93
+ }
94
+ const requestUrl = new URL(req.url);
95
+ const queryResult = agentQuerySchema.safeParse({
96
+ agent: requestUrl.searchParams.get("agent") ?? void 0
97
+ });
98
+ if (!queryResult.success) {
99
+ return jsonError(400, 'Invalid or missing "agent" query parameter.', "validation_error", {
100
+ issues: queryResult.error.issues
101
+ });
102
+ }
103
+ const agentId = queryResult.data.agent;
104
+ let parsedBody;
105
+ try {
106
+ parsedBody = await req.json();
107
+ } catch {
108
+ return jsonError(400, "Request body must be valid JSON.", "validation_error");
109
+ }
110
+ const bodyResult = chatRequestSchema.safeParse(parsedBody);
111
+ if (!bodyResult.success) {
112
+ return jsonError(400, "Invalid request body.", "validation_error", {
113
+ issues: bodyResult.error.issues
114
+ });
115
+ }
116
+ try {
117
+ await loadAgentRegistry();
118
+ const container = await createRequestContainer();
119
+ const rbacService = container.resolve("rbacService");
120
+ const acl = await rbacService.loadAcl(auth.sub, {
121
+ tenantId: auth.tenantId,
122
+ organizationId: auth.orgId
123
+ });
124
+ const decision = checkAgentPolicy({
125
+ agentId,
126
+ authContext: {
127
+ userFeatures: acl.features,
128
+ isSuperAdmin: acl.isSuperAdmin
129
+ },
130
+ requestedExecutionMode: "chat",
131
+ // TODO(step-3.7): resolve attachmentIds -> media types via attachment-bridge
132
+ // once the attachment-to-model conversion bridge lands. Until then the
133
+ // policy gate skips attachment-type validation because media types are
134
+ // not known at dispatch time.
135
+ attachmentMediaTypes: void 0
136
+ });
137
+ if (!decision.ok) {
138
+ return jsonError(statusForDenyCode(decision.code), decision.message, decision.code);
139
+ }
140
+ return await runAiAgentText({
141
+ agentId,
142
+ messages: bodyResult.data.messages,
143
+ attachmentIds: bodyResult.data.attachmentIds,
144
+ pageContext: bodyResult.data.pageContext,
145
+ debug: bodyResult.data.debug,
146
+ conversationId: bodyResult.data.conversationId ?? null,
147
+ authContext: {
148
+ tenantId: auth.tenantId ?? null,
149
+ organizationId: auth.orgId ?? null,
150
+ userId: auth.sub,
151
+ features: acl.features,
152
+ isSuperAdmin: acl.isSuperAdmin
153
+ },
154
+ container
155
+ });
156
+ } catch (error) {
157
+ if (error instanceof AgentPolicyError) {
158
+ return jsonError(statusForDenyCode(error.code), error.message, error.code);
159
+ }
160
+ console.error("[AI Chat Agent] Dispatch failure:", error);
161
+ return jsonError(
162
+ 500,
163
+ error instanceof Error ? error.message : "Agent dispatch failed.",
164
+ "internal_error"
165
+ );
166
+ }
167
+ }
168
+ export {
169
+ POST,
170
+ metadata,
171
+ openApi
172
+ };
173
+ //# sourceMappingURL=route.js.map