@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,223 @@
1
+ import { dynamicTool } from "ai";
2
+ import { createRequestContainer } from "@open-mercato/shared/lib/di/container";
3
+ import { loadAgentRegistry } from "./agent-registry.js";
4
+ import {
5
+ checkAgentPolicy,
6
+ resolveEffectiveMutationPolicy
7
+ } from "./agent-policy.js";
8
+ import { toolRegistry } from "./tool-registry.js";
9
+ import { toSafeZodSchema } from "./schema-utils.js";
10
+ import { prepareMutation } from "./prepare-mutation.js";
11
+ class AgentPolicyError extends Error {
12
+ constructor(code, message) {
13
+ super(message);
14
+ this.name = "AgentPolicyError";
15
+ this.code = code;
16
+ }
17
+ }
18
+ function createAiUiPartQueue() {
19
+ const buffer = [];
20
+ return {
21
+ enqueue: (part) => {
22
+ buffer.push(part);
23
+ },
24
+ drain: () => {
25
+ const snapshot = buffer.slice();
26
+ buffer.length = 0;
27
+ return snapshot;
28
+ },
29
+ size: () => buffer.length
30
+ };
31
+ }
32
+ function toPolicyAuthContext(ctx) {
33
+ return {
34
+ userFeatures: ctx.features,
35
+ isSuperAdmin: ctx.isSuperAdmin
36
+ };
37
+ }
38
+ function sanitizeToolNameForModel(name) {
39
+ return name.replace(/\./g, "__");
40
+ }
41
+ function formatToolResult(result) {
42
+ if (result === null || result === void 0) return "No result returned";
43
+ if (typeof result === "string") return result;
44
+ if (typeof result === "number" || typeof result === "boolean") return String(result);
45
+ try {
46
+ return JSON.stringify(result, null, 2);
47
+ } catch {
48
+ return String(result);
49
+ }
50
+ }
51
+ function buildToolHandlerContext(ctx, container, tool) {
52
+ return {
53
+ tenantId: ctx.tenantId,
54
+ organizationId: ctx.organizationId,
55
+ userId: ctx.userId,
56
+ container: container ?? void 0,
57
+ userFeatures: ctx.features,
58
+ isSuperAdmin: ctx.isSuperAdmin,
59
+ ...tool ? { tool } : {}
60
+ };
61
+ }
62
+ function formatPendingActionToolResult(agent, tool, pendingActionId, expiresAt) {
63
+ return formatToolResult({
64
+ status: "pending-confirmation",
65
+ agentId: agent.id,
66
+ toolName: tool.name,
67
+ pendingActionId,
68
+ expiresAt: expiresAt.toISOString(),
69
+ message: `Awaiting user confirmation for mutation "${tool.name}". The action will NOT run until the user approves it.`
70
+ });
71
+ }
72
+ function resolveIsDestructive(isDestructive, args) {
73
+ if (typeof isDestructive === "function") {
74
+ try {
75
+ return isDestructive(args) === true;
76
+ } catch {
77
+ return true;
78
+ }
79
+ }
80
+ return isDestructive === true;
81
+ }
82
+ function adaptToolToAiSdk(tool, ctx, mutation, container, effectiveMutationPolicy) {
83
+ const safeSchema = toSafeZodSchema(tool.inputSchema);
84
+ const handlerContext = buildToolHandlerContext(ctx, container, tool);
85
+ return dynamicTool({
86
+ description: tool.description,
87
+ inputSchema: safeSchema,
88
+ execute: async (args) => {
89
+ const requiresApprovalNow = (() => {
90
+ if (!mutation) return false;
91
+ if (effectiveMutationPolicy === "confirm-required") return true;
92
+ if (effectiveMutationPolicy === "destructive-confirm-required") {
93
+ return resolveIsDestructive(mutation.tool.isDestructive, args);
94
+ }
95
+ return false;
96
+ })();
97
+ if (mutation && requiresApprovalNow) {
98
+ try {
99
+ const toolCallArgs = args && typeof args === "object" && !Array.isArray(args) ? { ...args } : {};
100
+ const { uiPart, pendingAction } = await prepareMutation(
101
+ {
102
+ agent: mutation.agent,
103
+ tool: mutation.tool,
104
+ toolCallArgs,
105
+ conversationId: mutation.conversationId,
106
+ mutationPolicyOverride: mutation.mutationPolicyOverride
107
+ },
108
+ {
109
+ tenantId: mutation.ctx.tenantId,
110
+ organizationId: mutation.ctx.organizationId,
111
+ userId: mutation.ctx.userId,
112
+ features: mutation.ctx.features,
113
+ isSuperAdmin: mutation.ctx.isSuperAdmin,
114
+ container: mutation.container
115
+ }
116
+ );
117
+ mutation.uiPartQueue.enqueue(uiPart);
118
+ return formatPendingActionToolResult(
119
+ mutation.agent,
120
+ mutation.tool,
121
+ pendingAction.id,
122
+ pendingAction.expiresAt
123
+ );
124
+ } catch (error) {
125
+ const message = error instanceof Error ? error.message : String(error);
126
+ throw new Error(
127
+ `Tool "${tool.name}" could not be prepared for confirmation: ${message}`
128
+ );
129
+ }
130
+ }
131
+ try {
132
+ const freshContainer = await createRequestContainer();
133
+ const freshContext = {
134
+ ...handlerContext,
135
+ container: freshContainer
136
+ };
137
+ const { executeTool } = await import("./tool-executor.js");
138
+ const execResult = await executeTool(tool.name, args, freshContext);
139
+ if (!execResult.success) {
140
+ throw new Error(execResult.error || "Tool execution failed");
141
+ }
142
+ return formatToolResult(execResult.result);
143
+ } catch (error) {
144
+ const message = error instanceof Error ? error.message : String(error);
145
+ throw new Error(`Tool "${tool.name}" failed: ${message}`);
146
+ }
147
+ }
148
+ });
149
+ }
150
+ async function resolveAiAgentTools(input) {
151
+ await loadAgentRegistry();
152
+ const policyAuth = toPolicyAuthContext(input.authContext);
153
+ const mutationPolicyOverride = input.mutationPolicyOverride ?? null;
154
+ const agentDecision = checkAgentPolicy({
155
+ agentId: input.agentId,
156
+ authContext: policyAuth,
157
+ requestedExecutionMode: input.requestedExecutionMode ?? "chat",
158
+ mutationPolicyOverride
159
+ });
160
+ if (!agentDecision.ok) {
161
+ throw new AgentPolicyError(agentDecision.code, agentDecision.message);
162
+ }
163
+ const { agent } = agentDecision;
164
+ const tools = {};
165
+ const uiPartQueue = createAiUiPartQueue();
166
+ const effectiveMutationPolicy = resolveEffectiveMutationPolicy(
167
+ agent.mutationPolicy,
168
+ mutationPolicyOverride,
169
+ agent.id
170
+ );
171
+ const canInterceptMutations = effectiveMutationPolicy !== "read-only" && typeof input.container !== "undefined";
172
+ for (const toolName of agent.allowedTools) {
173
+ const toolDecision = checkAgentPolicy({
174
+ agentId: input.agentId,
175
+ authContext: policyAuth,
176
+ toolName,
177
+ mutationPolicyOverride
178
+ });
179
+ if (!toolDecision.ok) {
180
+ console.warn(
181
+ `[AI Agents] Skipping tool "${toolName}" for agent "${agent.id}": ${toolDecision.message}`
182
+ );
183
+ continue;
184
+ }
185
+ const record = toolDecision.tool ?? toolRegistry.getTool(toolName);
186
+ if (!record) {
187
+ console.warn(
188
+ `[AI Agents] Tool "${toolName}" vanished from registry between policy checks; skipping.`
189
+ );
190
+ continue;
191
+ }
192
+ try {
193
+ const mutationOptions = record.isMutation === true && canInterceptMutations && input.container ? {
194
+ agent,
195
+ tool: record,
196
+ container: input.container,
197
+ ctx: input.authContext,
198
+ mutationPolicyOverride,
199
+ conversationId: input.conversationId ?? null,
200
+ uiPartQueue
201
+ } : null;
202
+ const modelSafeToolName = sanitizeToolNameForModel(toolName);
203
+ tools[modelSafeToolName] = adaptToolToAiSdk(
204
+ record,
205
+ input.authContext,
206
+ mutationOptions,
207
+ input.container,
208
+ effectiveMutationPolicy
209
+ );
210
+ } catch (error) {
211
+ console.error(
212
+ `[AI Agents] Failed to adapt tool "${toolName}" for agent "${agent.id}":`,
213
+ error
214
+ );
215
+ }
216
+ }
217
+ return { agent, tools, uiPartQueue };
218
+ }
219
+ export {
220
+ AgentPolicyError,
221
+ resolveAiAgentTools
222
+ };
223
+ //# sourceMappingURL=agent-tools.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../src/modules/ai_assistant/lib/agent-tools.ts"],
4
+ "sourcesContent": ["import { dynamicTool, type Tool } from 'ai'\nimport type { AwilixContainer } from 'awilix'\nimport type { ZodType } from 'zod'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport type { AiAgentDefinition, AiAgentMutationPolicy } from './ai-agent-definition'\nimport type { AiChatRequestContext, AiUiPart } from './attachment-bridge-types'\nimport type { AiToolDefinition, McpToolContext } from './types'\nimport { loadAgentRegistry } from './agent-registry'\nimport {\n checkAgentPolicy,\n resolveEffectiveMutationPolicy,\n type AgentPolicyDenyCode,\n} from './agent-policy'\nimport { toolRegistry } from './tool-registry'\nimport { toSafeZodSchema } from './schema-utils'\nimport { prepareMutation } from './prepare-mutation'\n\n/**\n * Error thrown by `resolveAiAgentTools` (and downstream `runAiAgentText`) when\n * the agent-level policy check denies a request. Carries the structured deny\n * code so HTTP dispatchers can map it to a stable status and JSON body.\n */\nexport class AgentPolicyError extends Error {\n readonly code: AgentPolicyDenyCode\n\n constructor(code: AgentPolicyDenyCode, message: string) {\n super(message)\n this.name = 'AgentPolicyError'\n this.code = code\n }\n}\n\nexport interface ResolveAiAgentToolsInput {\n agentId: string\n authContext: AiChatRequestContext\n pageContext?: Record<string, unknown>\n attachmentIds?: string[]\n /**\n * Execution mode the caller intends to run the agent in. Defaults to\n * `'chat'` to preserve the existing chat dispatcher contract. Object-mode\n * callers (see `runAiAgentObject`) MUST pass `'object'` so the policy gate\n * can reject chat-only agents early with `execution_mode_not_supported`.\n */\n requestedExecutionMode?: 'chat' | 'object'\n /**\n * Optional tenant-scoped mutation-policy DOWNGRADE. When supplied, the\n * effective policy evaluated by `checkAgentPolicy` is the most restrictive\n * of `{ agent.mutationPolicy, mutationPolicyOverride }`. Escalation is\n * rejected at the route layer; this helper trusts callers to pass only\n * values produced by the override repository.\n */\n mutationPolicyOverride?: AiAgentMutationPolicy | null\n /**\n * DI container used by the `prepareMutation` tool-call wrapper (Step 5.6).\n * When present AND the agent's effective mutation policy is non-read-only,\n * `isMutation: true` tools are intercepted: the runtime creates an\n * `AiPendingAction` row and enqueues a `mutation-preview-card` UI part in\n * the returned {@link ResolvedAgentTools.uiPartQueue} instead of running the\n * tool's handler. When absent, mutation tools degrade-gracefully to the\n * pre-5.6 pass-through adapter \u2014 existing read-only agents are unaffected.\n */\n container?: AwilixContainer\n /**\n * Optional chat-turn correlation id used when hashing the\n * `AiPendingAction.idempotencyKey` so retries of the same mutation collapse\n * to a single row. The chat dispatcher supplies the OpenCode / AI SDK turn\n * id here; when omitted the hash falls back to `null` which still preserves\n * per-tenant/org uniqueness within the TTL window.\n */\n conversationId?: string | null\n}\n\n/**\n * Queue of UI parts the mutation-preview wrapper accumulates during a turn.\n * The chat/object dispatcher flushes these on the next emission boundary\n * (spec \u00A79 allows either direct streaming or this queue pattern \u2014 we ship the\n * queue in Step 5.6 and the chat dispatcher will drain it in Step 5.10 when\n * the `mutation-preview-card` component registers). BC: callers that ignore\n * the field are unaffected.\n */\nexport interface AiUiPartQueue {\n /** Pushed by the mutation wrapper; drained by the dispatcher in order. */\n enqueue: (part: AiUiPart) => void\n drain: () => AiUiPart[]\n size: () => number\n}\n\nfunction createAiUiPartQueue(): AiUiPartQueue {\n const buffer: AiUiPart[] = []\n return {\n enqueue: (part) => {\n buffer.push(part)\n },\n drain: () => {\n const snapshot = buffer.slice()\n buffer.length = 0\n return snapshot\n },\n size: () => buffer.length,\n }\n}\n\nexport interface ResolvedAgentTools {\n agent: AiAgentDefinition\n tools: Record<string, Tool<unknown, unknown>>\n /**\n * Per-request UI-part queue the chat dispatcher drains between streamText\n * chunks (Step 5.10 contract). Always present; empty when no mutation-tool\n * calls fire during the turn.\n */\n uiPartQueue: AiUiPartQueue\n}\n\nfunction toPolicyAuthContext(ctx: AiChatRequestContext): {\n userFeatures: string[]\n isSuperAdmin: boolean\n} {\n return {\n userFeatures: ctx.features,\n isSuperAdmin: ctx.isSuperAdmin,\n }\n}\n\n/**\n * Sanitize a dotted tool name (e.g. `search.hybrid_search`) into a format\n * accepted by all major LLM providers. OpenAI requires tool names to match\n * `^[a-zA-Z0-9_-]+$`; dots are replaced with double underscores (`__`).\n * Anthropic and Google accept both formats, so this is safe across providers.\n */\nfunction sanitizeToolNameForModel(name: string): string {\n return name.replace(/\\./g, '__')\n}\n\nfunction formatToolResult(result: unknown): string {\n if (result === null || result === undefined) return 'No result returned'\n if (typeof result === 'string') return result\n if (typeof result === 'number' || typeof result === 'boolean') return String(result)\n try {\n return JSON.stringify(result, null, 2)\n } catch {\n return String(result)\n }\n}\n\nfunction buildToolHandlerContext(\n ctx: AiChatRequestContext,\n container?: AwilixContainer,\n tool?: AiToolDefinition,\n): McpToolContext {\n return {\n tenantId: ctx.tenantId,\n organizationId: ctx.organizationId,\n userId: ctx.userId,\n container: (container ?? undefined) as unknown as McpToolContext['container'],\n userFeatures: ctx.features,\n isSuperAdmin: ctx.isSuperAdmin,\n ...(tool ? { tool } : {}),\n }\n}\n\ninterface MutationInterceptorOptions {\n agent: AiAgentDefinition\n tool: AiToolDefinition\n container: AwilixContainer\n ctx: AiChatRequestContext\n mutationPolicyOverride: AiAgentMutationPolicy | null\n conversationId: string | null\n uiPartQueue: AiUiPartQueue\n}\n\nfunction formatPendingActionToolResult(\n agent: AiAgentDefinition,\n tool: AiToolDefinition,\n pendingActionId: string,\n expiresAt: Date,\n): string {\n return formatToolResult({\n status: 'pending-confirmation',\n agentId: agent.id,\n toolName: tool.name,\n pendingActionId,\n expiresAt: expiresAt.toISOString(),\n message: `Awaiting user confirmation for mutation \"${tool.name}\". The action will NOT run until the user approves it.`,\n })\n}\n\n/**\n * Per-call gating decision for the destructive-confirm-required policy.\n * Honors a static boolean OR a predicate evaluated against the actual\n * tool-call args, so a multi-operation tool (e.g.\n * `customers.manage_deal_comment` with `operation: 'create' | 'update'\n * | 'delete'`) can gate only its destructive branches without being\n * split into multiple tools.\n */\nfunction resolveIsDestructive(\n isDestructive: boolean | ((input: unknown) => boolean) | undefined,\n args: unknown,\n): boolean {\n if (typeof isDestructive === 'function') {\n try {\n return isDestructive(args) === true\n } catch {\n // If the predicate throws (e.g. unexpected input shape), default to\n // gating \u2014 fail-safe for destructive policy.\n return true\n }\n }\n return isDestructive === true\n}\n\nfunction adaptToolToAiSdk(\n tool: AiToolDefinition,\n ctx: AiChatRequestContext,\n mutation: MutationInterceptorOptions | null,\n container?: AwilixContainer,\n effectiveMutationPolicy?: 'read-only' | 'confirm-required' | 'destructive-confirm-required',\n): Tool<unknown, unknown> {\n const safeSchema = toSafeZodSchema(tool.inputSchema as ZodType)\n const handlerContext = buildToolHandlerContext(ctx, container, tool)\n return dynamicTool({\n description: tool.description,\n inputSchema: safeSchema,\n execute: async (args: unknown) => {\n // Per-call gating decision. Under `destructive-confirm-required`,\n // tools with a predicate `isDestructive(args)` may flip per call \u2014\n // e.g. comment delete gates while comment create runs direct.\n const requiresApprovalNow = (() => {\n if (!mutation) return false\n if (effectiveMutationPolicy === 'confirm-required') return true\n if (effectiveMutationPolicy === 'destructive-confirm-required') {\n return resolveIsDestructive(mutation.tool.isDestructive, args)\n }\n return false\n })()\n if (mutation && requiresApprovalNow) {\n try {\n const toolCallArgs =\n args && typeof args === 'object' && !Array.isArray(args)\n ? { ...(args as Record<string, unknown>) }\n : {}\n const { uiPart, pendingAction } = await prepareMutation(\n {\n agent: mutation.agent,\n tool: mutation.tool,\n toolCallArgs,\n conversationId: mutation.conversationId,\n mutationPolicyOverride: mutation.mutationPolicyOverride,\n },\n {\n tenantId: mutation.ctx.tenantId,\n organizationId: mutation.ctx.organizationId,\n userId: mutation.ctx.userId,\n features: mutation.ctx.features,\n isSuperAdmin: mutation.ctx.isSuperAdmin,\n container: mutation.container,\n },\n )\n mutation.uiPartQueue.enqueue(uiPart)\n return formatPendingActionToolResult(\n mutation.agent,\n mutation.tool,\n pendingAction.id,\n pendingAction.expiresAt,\n )\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error)\n throw new Error(\n `Tool \"${tool.name}\" could not be prepared for confirmation: ${message}`,\n )\n }\n }\n try {\n // Create a fresh container per tool call so the EM is never stale.\n const freshContainer = await createRequestContainer()\n const freshContext: McpToolContext = {\n ...handlerContext,\n container: freshContainer as unknown as McpToolContext['container'],\n }\n const { executeTool } = await import('./tool-executor')\n const execResult = await executeTool(tool.name, args, freshContext)\n if (!execResult.success) {\n throw new Error(execResult.error || 'Tool execution failed')\n }\n return formatToolResult(execResult.result)\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error)\n throw new Error(`Tool \"${tool.name}\" failed: ${message}`)\n }\n },\n })\n}\n\n/**\n * Resolves the agent's whitelisted tools into an AI SDK `tools` map, enforcing\n * the same policy gate as the HTTP dispatcher. Throws {@link AgentPolicyError}\n * on agent-level deny; tool-level denies are skipped with a `console.warn`\n * because the agent author whitelisted a tool the caller is not currently\n * permitted to execute (deterministic non-failure \u2014 the remaining tools still\n * reach the model).\n */\nexport async function resolveAiAgentTools(\n input: ResolveAiAgentToolsInput,\n): Promise<ResolvedAgentTools> {\n await loadAgentRegistry()\n\n const policyAuth = toPolicyAuthContext(input.authContext)\n const mutationPolicyOverride = input.mutationPolicyOverride ?? null\n const agentDecision = checkAgentPolicy({\n agentId: input.agentId,\n authContext: policyAuth,\n requestedExecutionMode: input.requestedExecutionMode ?? 'chat',\n mutationPolicyOverride,\n })\n if (!agentDecision.ok) {\n throw new AgentPolicyError(agentDecision.code, agentDecision.message)\n }\n\n const { agent } = agentDecision\n const tools: Record<string, Tool<unknown, unknown>> = {}\n const uiPartQueue = createAiUiPartQueue()\n const effectiveMutationPolicy = resolveEffectiveMutationPolicy(\n agent.mutationPolicy,\n mutationPolicyOverride,\n agent.id,\n )\n const canInterceptMutations =\n effectiveMutationPolicy !== 'read-only' && typeof input.container !== 'undefined'\n\n for (const toolName of agent.allowedTools) {\n const toolDecision = checkAgentPolicy({\n agentId: input.agentId,\n authContext: policyAuth,\n toolName,\n mutationPolicyOverride,\n })\n if (!toolDecision.ok) {\n console.warn(\n `[AI Agents] Skipping tool \"${toolName}\" for agent \"${agent.id}\": ${toolDecision.message}`,\n )\n continue\n }\n\n const record = (toolDecision.tool ?? toolRegistry.getTool(toolName)) as\n | AiToolDefinition\n | undefined\n if (!record) {\n console.warn(\n `[AI Agents] Tool \"${toolName}\" vanished from registry between policy checks; skipping.`,\n )\n continue\n }\n\n try {\n // For any mutation tool whose policy isn't read-only, prepare the\n // interceptor options once. The actual gate-vs-run-direct decision\n // happens at call time inside `adaptToolToAiSdk` because under\n // `destructive-confirm-required` `isDestructive` may be a predicate\n // evaluated against the per-call args (e.g. a multi-operation\n // tool with `operation: 'create' | 'update' | 'delete'` gates only\n // the destructive branch).\n const mutationOptions: MutationInterceptorOptions | null =\n record.isMutation === true && canInterceptMutations && input.container\n ? {\n agent,\n tool: record,\n container: input.container,\n ctx: input.authContext,\n mutationPolicyOverride,\n conversationId: input.conversationId ?? null,\n uiPartQueue,\n }\n : null\n // Sanitize tool name for model API compatibility: OpenAI requires\n // names matching ^[a-zA-Z0-9_-]+$ (no dots). Replace dots with\n // double underscores so `search.hybrid_search` becomes\n // `search__hybrid_search`. The AI SDK uses the record key as the\n // tool name sent to the model; the original dotted name stays on\n // the `tool` object for logging and prepareMutation hashing.\n const modelSafeToolName = sanitizeToolNameForModel(toolName)\n tools[modelSafeToolName] = adaptToolToAiSdk(\n record,\n input.authContext,\n mutationOptions,\n input.container,\n effectiveMutationPolicy,\n )\n } catch (error) {\n console.error(\n `[AI Agents] Failed to adapt tool \"${toolName}\" for agent \"${agent.id}\":`,\n error,\n )\n }\n }\n\n return { agent, tools, uiPartQueue }\n}\n"],
5
+ "mappings": "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",
6
+ "names": []
7
+ }
@@ -0,0 +1,25 @@
1
+ import { DefaultChatTransport } from "ai";
2
+ const DEFAULT_ENDPOINT = "/api/ai_assistant/ai/chat";
3
+ function buildEndpointWithAgentQuery(endpoint, agentId) {
4
+ if (!agentId) return endpoint;
5
+ const separator = endpoint.includes("?") ? "&" : "?";
6
+ return `${endpoint}${separator}agent=${encodeURIComponent(agentId)}`;
7
+ }
8
+ function createAiAgentTransport(input) {
9
+ const endpoint = buildEndpointWithAgentQuery(
10
+ input.endpoint && input.endpoint.length > 0 ? input.endpoint : DEFAULT_ENDPOINT,
11
+ input.agentId
12
+ );
13
+ const extraBody = { ...input.body ?? {} };
14
+ if (typeof input.debug === "boolean") {
15
+ extraBody.debug = input.debug;
16
+ }
17
+ return new DefaultChatTransport({
18
+ api: endpoint,
19
+ body: extraBody
20
+ });
21
+ }
22
+ export {
23
+ createAiAgentTransport
24
+ };
25
+ //# sourceMappingURL=agent-transport.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../src/modules/ai_assistant/lib/agent-transport.ts"],
4
+ "sourcesContent": ["import { DefaultChatTransport, type ChatTransport, type UIMessage } from 'ai'\n\n/**\n * Default dispatcher URL. Mirrors the module-id-prefixed layout emitted by the\n * app router \u2014 the spec shorthand `/api/ai/chat` appears in the source spec\n * but the generator maps it to `/api/ai_assistant/ai/chat`. Downstream\n * consumers should import this helper rather than hardcoding the literal.\n */\nconst DEFAULT_ENDPOINT = '/api/ai_assistant/ai/chat'\n\nexport interface CreateAiAgentTransportInput {\n agentId: string\n endpoint?: string\n body?: Record<string, unknown>\n debug?: boolean\n}\n\nfunction buildEndpointWithAgentQuery(endpoint: string, agentId: string): string {\n if (!agentId) return endpoint\n const separator = endpoint.includes('?') ? '&' : '?'\n return `${endpoint}${separator}agent=${encodeURIComponent(agentId)}`\n}\n\n/**\n * Thin wrapper around `DefaultChatTransport` that binds the agent id as a\n * query parameter and merges any agent-specific body fields into every\n * outbound request. Returned value conforms to the AI SDK's `ChatTransport`\n * contract so consumers can plug it directly into `useChat({ transport })`.\n *\n * TODO: when the AI SDK standardizes agent-binding as a first-class input,\n * this helper can collapse into a one-liner. Until then it owns the\n * endpoint-URL convention so UI callers do not hardcode dispatcher paths.\n */\nexport function createAiAgentTransport<UI_MESSAGE extends UIMessage = UIMessage>(\n input: CreateAiAgentTransportInput,\n): ChatTransport<UI_MESSAGE> {\n const endpoint = buildEndpointWithAgentQuery(\n input.endpoint && input.endpoint.length > 0 ? input.endpoint : DEFAULT_ENDPOINT,\n input.agentId,\n )\n\n const extraBody: Record<string, unknown> = { ...(input.body ?? {}) }\n if (typeof input.debug === 'boolean') {\n extraBody.debug = input.debug\n }\n\n return new DefaultChatTransport<UI_MESSAGE>({\n api: endpoint,\n body: extraBody,\n })\n}\n"],
5
+ "mappings": "AAAA,SAAS,4BAAgE;AAQzE,MAAM,mBAAmB;AASzB,SAAS,4BAA4B,UAAkB,SAAyB;AAC9E,MAAI,CAAC,QAAS,QAAO;AACrB,QAAM,YAAY,SAAS,SAAS,GAAG,IAAI,MAAM;AACjD,SAAO,GAAG,QAAQ,GAAG,SAAS,SAAS,mBAAmB,OAAO,CAAC;AACpE;AAYO,SAAS,uBACd,OAC2B;AAC3B,QAAM,WAAW;AAAA,IACf,MAAM,YAAY,MAAM,SAAS,SAAS,IAAI,MAAM,WAAW;AAAA,IAC/D,MAAM;AAAA,EACR;AAEA,QAAM,YAAqC,EAAE,GAAI,MAAM,QAAQ,CAAC,EAAG;AACnE,MAAI,OAAO,MAAM,UAAU,WAAW;AACpC,cAAU,QAAQ,MAAM;AAAA,EAC1B;AAEA,SAAO,IAAI,qBAAiC;AAAA,IAC1C,KAAK;AAAA,IACL,MAAM;AAAA,EACR,CAAC;AACH;",
6
+ "names": []
7
+ }
@@ -0,0 +1,11 @@
1
+ function defineAiAgent(definition) {
2
+ return definition;
3
+ }
4
+ function defineAiAgentExtension(extension) {
5
+ return extension;
6
+ }
7
+ export {
8
+ defineAiAgent,
9
+ defineAiAgentExtension
10
+ };
11
+ //# sourceMappingURL=ai-agent-definition.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../src/modules/ai_assistant/lib/ai-agent-definition.ts"],
4
+ "sourcesContent": ["import type { AwilixContainer } from 'awilix'\nimport type { ZodTypeAny } from 'zod'\n\nexport type AiAgentExecutionMode = 'chat' | 'object'\n\nexport type AiAgentMutationPolicy =\n | 'read-only'\n | 'confirm-required'\n | 'destructive-confirm-required'\n\nexport type AiAgentAcceptedMediaType = 'image' | 'pdf' | 'file'\n\nexport type AiAgentDataOperation = 'read' | 'search' | 'aggregate'\n\nexport interface AiAgentPageContextInput {\n entityType: string\n recordId: string\n container: AwilixContainer\n tenantId: string | null\n organizationId: string | null\n}\n\nexport interface AiAgentStructuredOutput<TSchema = ZodTypeAny> {\n schemaName: string\n schema: TSchema\n mode?: 'generate' | 'stream'\n}\n\nexport interface AiAgentDataCapabilities {\n entities?: string[]\n operations?: AiAgentDataOperation[]\n searchableFields?: string[]\n}\n\nexport interface AiAgentSuggestion {\n label: string\n prompt: string\n}\n\nexport interface AiAgentDefinition {\n id: string\n moduleId: string\n label: string\n description: string\n systemPrompt: string\n allowedTools: string[]\n suggestions?: AiAgentSuggestion[]\n executionMode?: AiAgentExecutionMode\n defaultModel?: string\n acceptedMediaTypes?: AiAgentAcceptedMediaType[]\n requiredFeatures?: string[]\n uiParts?: string[]\n readOnly?: boolean\n mutationPolicy?: AiAgentMutationPolicy\n maxSteps?: number\n output?: AiAgentStructuredOutput\n resolvePageContext?: (ctx: AiAgentPageContextInput) => Promise<string | null>\n keywords?: string[]\n domain?: string\n dataCapabilities?: AiAgentDataCapabilities\n}\n\nexport interface AiAgentExtension {\n targetAgentId: string\n replaceAllowedTools?: string[]\n deleteAllowedTools?: string[]\n appendAllowedTools?: string[]\n replaceSystemPrompt?: string\n appendSystemPrompt?: string\n replaceSuggestions?: AiAgentSuggestion[]\n deleteSuggestions?: string[]\n appendSuggestions?: AiAgentSuggestion[]\n /**\n * @deprecated Use `appendSuggestions` for new code. Preserved as the\n * original append-only field for backward compatibility.\n */\n suggestions?: AiAgentSuggestion[]\n}\n\nexport function defineAiAgent(definition: AiAgentDefinition): AiAgentDefinition {\n return definition\n}\n\nexport function defineAiAgentExtension(extension: AiAgentExtension): AiAgentExtension {\n return extension\n}\n"],
5
+ "mappings": "AA+EO,SAAS,cAAc,YAAkD;AAC9E,SAAO;AACT;AAEO,SAAS,uBAAuB,WAA+C;AACpF,SAAO;AACT;",
6
+ "names": []
7
+ }
@@ -0,0 +1 @@
1
+ //# sourceMappingURL=ai-agents-generated.d.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": [],
4
+ "sourcesContent": [],
5
+ "mappings": "",
6
+ "names": []
7
+ }
@@ -0,0 +1,239 @@
1
+ import {
2
+ attachTrustedAuthContext
3
+ } from "@open-mercato/shared/lib/auth/server";
4
+ import {
5
+ findApiRouteManifestMatch,
6
+ getApiRouteManifests
7
+ } from "@open-mercato/shared/modules/registry";
8
+ import { hasAllFeatures } from "@open-mercato/shared/security/features";
9
+ const MUTATION_METHODS = /* @__PURE__ */ new Set(["POST", "PUT", "PATCH", "DELETE"]);
10
+ const SYNTHETIC_ORIGIN = "http://internal.local";
11
+ function normalizeMethod(method) {
12
+ const upper = method.toUpperCase();
13
+ if (upper === "GET" || upper === "POST" || upper === "PUT" || upper === "PATCH" || upper === "DELETE") {
14
+ return upper;
15
+ }
16
+ throw new Error(`Unsupported method "${method}"`);
17
+ }
18
+ function normalizePath(path) {
19
+ if (typeof path !== "string" || path.length === 0) return "/";
20
+ const trimmed = path.startsWith("/") ? path : `/${path}`;
21
+ let end = trimmed.length;
22
+ while (end > 1 && trimmed.charCodeAt(end - 1) === 47) end--;
23
+ return trimmed.slice(0, end);
24
+ }
25
+ function buildUrl(path, query) {
26
+ const url = new URL(`${SYNTHETIC_ORIGIN}/api${normalizePath(path)}`);
27
+ if (query) {
28
+ for (const [key, value] of Object.entries(query)) {
29
+ if (value === null || value === void 0) continue;
30
+ url.searchParams.append(key, String(value));
31
+ }
32
+ }
33
+ return url;
34
+ }
35
+ function buildAuthEnvelope(ctx) {
36
+ const userId = ctx.userId;
37
+ if (!userId) {
38
+ return { auth: null, status: "invalid" };
39
+ }
40
+ const auth = {
41
+ sub: userId,
42
+ userId,
43
+ tenantId: ctx.tenantId,
44
+ orgId: ctx.organizationId,
45
+ roles: [],
46
+ isSuperAdmin: ctx.isSuperAdmin
47
+ };
48
+ return { auth, status: "authenticated" };
49
+ }
50
+ function pickHandler(entry, mod, method) {
51
+ const direct = mod[method];
52
+ if (typeof direct === "function") return direct;
53
+ if (entry.kind === "legacy") {
54
+ const fallback = mod.default ?? mod.handler;
55
+ if (typeof fallback === "function") return fallback;
56
+ }
57
+ return null;
58
+ }
59
+ function extractMethodMetadata(metadata, method) {
60
+ if (!metadata || typeof metadata !== "object") return null;
61
+ const record = metadata;
62
+ const perMethod = record[method];
63
+ const source = perMethod && typeof perMethod === "object" ? perMethod : record;
64
+ const result = {};
65
+ if (typeof source.requireAuth === "boolean") {
66
+ result.requireAuth = source.requireAuth;
67
+ }
68
+ if (Array.isArray(source.requireFeatures)) {
69
+ const features = source.requireFeatures.filter((entry) => typeof entry === "string" && entry.length > 0);
70
+ if (features.length > 0) result.requireFeatures = features;
71
+ }
72
+ return result;
73
+ }
74
+ async function readResponseBody(res) {
75
+ const contentType = res.headers.get("content-type") ?? "";
76
+ if (contentType.includes("application/json")) {
77
+ try {
78
+ return await res.json();
79
+ } catch {
80
+ return null;
81
+ }
82
+ }
83
+ try {
84
+ const text = await res.text();
85
+ if (!text) return null;
86
+ try {
87
+ return JSON.parse(text);
88
+ } catch {
89
+ return text;
90
+ }
91
+ } catch {
92
+ return null;
93
+ }
94
+ }
95
+ function failure(statusCode, error, details) {
96
+ const response = { success: false, statusCode, error };
97
+ if (details !== void 0) response.details = details;
98
+ return response;
99
+ }
100
+ function normalizeError(body) {
101
+ if (body && typeof body === "object" && !Array.isArray(body)) {
102
+ const record = body;
103
+ const message = typeof record.error === "string" ? record.error : typeof record.message === "string" ? record.message : null;
104
+ if (message) {
105
+ const { error: _ignored, message: _ignoredMessage, ...rest } = record;
106
+ void _ignored;
107
+ void _ignoredMessage;
108
+ return Object.keys(rest).length > 0 ? { error: message, details: rest } : { error: message };
109
+ }
110
+ }
111
+ if (typeof body === "string" && body.length > 0) {
112
+ return { error: body };
113
+ }
114
+ return { error: "Request failed" };
115
+ }
116
+ function createAiApiOperationRunner(ctx, options = {}) {
117
+ let manifestPromise = null;
118
+ const loadManifest = async () => {
119
+ if (options.apiRoutes) return options.apiRoutes;
120
+ if (manifestPromise) return manifestPromise;
121
+ const loader = options.loadApiRoutes ?? defaultLoadApiRoutes;
122
+ manifestPromise = loader();
123
+ try {
124
+ return await manifestPromise;
125
+ } catch (error) {
126
+ manifestPromise = null;
127
+ throw error;
128
+ }
129
+ };
130
+ return {
131
+ async run(request) {
132
+ let method;
133
+ try {
134
+ method = normalizeMethod(request.method);
135
+ } catch (error) {
136
+ const message = error instanceof Error ? error.message : "Invalid method";
137
+ return failure(400, message);
138
+ }
139
+ const path = normalizePath(request.path);
140
+ let routes;
141
+ try {
142
+ routes = await loadManifest();
143
+ } catch (error) {
144
+ const message = error instanceof Error ? error.message : "Failed to load API route manifest";
145
+ return failure(500, `Operation runner manifest unavailable: ${message}`);
146
+ }
147
+ const match = findApiRouteManifestMatch(routes, method, path);
148
+ if (!match) {
149
+ return failure(
150
+ 404,
151
+ `No documented API route matches ${method} ${path}`
152
+ );
153
+ }
154
+ let mod;
155
+ try {
156
+ mod = await match.route.load();
157
+ } catch (error) {
158
+ const message = error instanceof Error ? error.message : "Failed to load route module";
159
+ return failure(500, `Failed to load route module: ${message}`);
160
+ }
161
+ if (!("openApi" in mod) || mod.openApi === void 0 || mod.openApi === null) {
162
+ return failure(
163
+ 501,
164
+ `Route ${method} ${path} is undocumented (missing openApi export); refusing to call from AI tool`
165
+ );
166
+ }
167
+ const handler = pickHandler(match.route, mod, method);
168
+ if (!handler) {
169
+ return failure(
170
+ 405,
171
+ `Route ${path} does not export a handler for method ${method}`
172
+ );
173
+ }
174
+ const methodMetadata = extractMethodMetadata(mod.metadata, method);
175
+ const routeFeatures = methodMetadata?.requireFeatures ?? [];
176
+ const isMutation = MUTATION_METHODS.has(method);
177
+ if (isMutation && routeFeatures.length === 0 && !request.allowFeaturelessMutation) {
178
+ return failure(
179
+ 403,
180
+ `Mutation route ${method} ${path} declares no requiredFeatures; refusing to call without allowFeaturelessMutation opt-in`
181
+ );
182
+ }
183
+ const toolFeatures = ctx.tool.requiredFeatures ?? [];
184
+ if (routeFeatures.length > 0 && !hasAllFeatures(toolFeatures, routeFeatures)) {
185
+ return failure(
186
+ 403,
187
+ `AI tool "${ctx.tool.name}" requiredFeatures do not cover route ${method} ${path} requiredFeatures`,
188
+ { toolFeatures, routeFeatures }
189
+ );
190
+ }
191
+ const url = buildUrl(path, request.query);
192
+ const headers = new Headers();
193
+ const requestInit = { method, headers };
194
+ if (request.body !== void 0 && method !== "GET") {
195
+ headers.set("content-type", "application/json");
196
+ requestInit.body = JSON.stringify(request.body);
197
+ }
198
+ const syntheticRequest = new Request(url, requestInit);
199
+ attachTrustedAuthContext(syntheticRequest, buildAuthEnvelope(ctx));
200
+ let response;
201
+ try {
202
+ response = await handler(syntheticRequest, { params: match.params });
203
+ } catch (error) {
204
+ const message = error instanceof Error ? error.message : "Route handler threw";
205
+ return failure(500, message);
206
+ }
207
+ const status = response.status;
208
+ const body = await readResponseBody(response);
209
+ if (status >= 200 && status < 300) {
210
+ return {
211
+ success: true,
212
+ statusCode: status,
213
+ data: body
214
+ };
215
+ }
216
+ const normalized = normalizeError(body);
217
+ return {
218
+ success: false,
219
+ statusCode: status,
220
+ error: normalized.error,
221
+ ...normalized.details !== void 0 ? { details: normalized.details } : {}
222
+ };
223
+ }
224
+ };
225
+ }
226
+ async function defaultLoadApiRoutes() {
227
+ const registered = getApiRouteManifests();
228
+ if (registered.length === 0) {
229
+ throw new Error(
230
+ "No API route manifest registered. Call registerApiRouteManifests(...) at app bootstrap or pass apiRoutes/loadApiRoutes to createAiApiOperationRunner."
231
+ );
232
+ }
233
+ return registered;
234
+ }
235
+ export {
236
+ createAiApiOperationRunner,
237
+ normalizePath
238
+ };
239
+ //# sourceMappingURL=ai-api-operation-runner.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../src/modules/ai_assistant/lib/ai-api-operation-runner.ts"],
4
+ "sourcesContent": ["// Phase 1 of spec 2026-04-27-ai-tools-api-backed-dry-refactor.md.\n//\n// In-process API operation runner used by typed AI tools to reuse existing\n// API route handlers without HTTP, fetch, or a second RBAC pass. The runner\n// resolves the matched route entry from the generated `apiRoutes` manifest,\n// validates that the route is documented (`openApi`) and that mutation routes\n// declare `requiredFeatures`, asserts the route's required features are\n// covered by the tool definition, then invokes the route handler directly with\n// a synthetic Request that carries a Symbol-keyed trusted-auth envelope so the\n// shared auth resolver short-circuits cookie/JWT/API-key parsing.\nimport {\n attachTrustedAuthContext,\n type AuthContext,\n type TrustedAuthContextEnvelope,\n} from '@open-mercato/shared/lib/auth/server'\nimport {\n findApiRouteManifestMatch,\n getApiRouteManifests,\n type ApiRouteManifestEntry,\n type RouteMatchParams,\n} from '@open-mercato/shared/modules/registry'\nimport { hasAllFeatures } from '@open-mercato/shared/security/features'\nimport type { AiToolDefinition, McpToolContext } from './types'\n\nexport type AiApiHttpMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE'\n\nconst MUTATION_METHODS: ReadonlySet<AiApiHttpMethod> = new Set(['POST', 'PUT', 'PATCH', 'DELETE'])\n\nconst SYNTHETIC_ORIGIN = 'http://internal.local'\n\nexport type AiApiOperationRequest = {\n method: AiApiHttpMethod\n path: string\n query?: Record<string, string | number | boolean | null | undefined>\n body?: Record<string, unknown>\n allowFeaturelessMutation?: boolean\n}\n\nexport type AiApiOperationResponse<T = unknown> = {\n success: boolean\n statusCode: number\n data?: T\n error?: string\n details?: unknown\n}\n\nexport interface AiToolExecutionContext extends McpToolContext {\n tool: AiToolDefinition\n}\n\nexport type AiApiOperationRunnerOptions = {\n /** Optional override of the API route manifest (used by tests). */\n apiRoutes?: ApiRouteManifestEntry[]\n /** Custom loader for the API route manifest (used by tests). */\n loadApiRoutes?: () => Promise<ApiRouteManifestEntry[]>\n}\n\nexport type AiApiOperationRunner = {\n run<T = unknown>(request: AiApiOperationRequest): Promise<AiApiOperationResponse<T>>\n}\n\ntype ResolvedHandler = (req: Request, ctx?: { params: RouteMatchParams }) => Promise<Response> | Response\n\ntype LoadedRouteModule = Record<string, unknown>\n\ntype MethodMetadata = {\n requireAuth?: boolean\n requireFeatures?: string[]\n}\n\nfunction normalizeMethod(method: string): AiApiHttpMethod {\n const upper = method.toUpperCase()\n if (upper === 'GET' || upper === 'POST' || upper === 'PUT' || upper === 'PATCH' || upper === 'DELETE') {\n return upper\n }\n throw new Error(`Unsupported method \"${method}\"`)\n}\n\n// Exported for unit testing \u2014 see __tests__/ai-api-operation-runner.test.ts.\n// `path` arrives from agent tool inputs (LLM-controlled), so trailing-slash\n// stripping is implemented as a linear character-code scan rather than the\n// /\\/+$/ regex (CodeQL js/polynomial-redos: regex exhibits polynomial\n// backtracking on long runs of `/`). The loop is O(n) regardless of input.\nexport function normalizePath(path: string): string {\n if (typeof path !== 'string' || path.length === 0) return '/'\n const trimmed = path.startsWith('/') ? path : `/${path}`\n let end = trimmed.length\n while (end > 1 && trimmed.charCodeAt(end - 1) === 47 /* '/' */) end--\n return trimmed.slice(0, end)\n}\n\nfunction buildUrl(path: string, query?: AiApiOperationRequest['query']): URL {\n const url = new URL(`${SYNTHETIC_ORIGIN}/api${normalizePath(path)}`)\n if (query) {\n for (const [key, value] of Object.entries(query)) {\n if (value === null || value === undefined) continue\n url.searchParams.append(key, String(value))\n }\n }\n return url\n}\n\nfunction buildAuthEnvelope(ctx: AiToolExecutionContext): TrustedAuthContextEnvelope {\n const userId = ctx.userId\n if (!userId) {\n return { auth: null, status: 'invalid' }\n }\n const auth: AuthContext = {\n sub: userId,\n userId,\n tenantId: ctx.tenantId,\n orgId: ctx.organizationId,\n roles: [],\n isSuperAdmin: ctx.isSuperAdmin,\n }\n return { auth, status: 'authenticated' }\n}\n\nfunction pickHandler(entry: ApiRouteManifestEntry, mod: LoadedRouteModule, method: AiApiHttpMethod): ResolvedHandler | null {\n const direct = mod[method]\n if (typeof direct === 'function') return direct as ResolvedHandler\n if (entry.kind === 'legacy') {\n const fallback = mod.default ?? mod.handler\n if (typeof fallback === 'function') return fallback as ResolvedHandler\n }\n return null\n}\n\nfunction extractMethodMetadata(metadata: unknown, method: AiApiHttpMethod): MethodMetadata | null {\n if (!metadata || typeof metadata !== 'object') return null\n const record = metadata as Record<string, unknown>\n const perMethod = record[method]\n const source: Record<string, unknown> = perMethod && typeof perMethod === 'object'\n ? (perMethod as Record<string, unknown>)\n : record\n const result: MethodMetadata = {}\n if (typeof source.requireAuth === 'boolean') {\n result.requireAuth = source.requireAuth\n }\n if (Array.isArray(source.requireFeatures)) {\n const features = source.requireFeatures.filter((entry): entry is string => typeof entry === 'string' && entry.length > 0)\n if (features.length > 0) result.requireFeatures = features\n }\n return result\n}\n\nasync function readResponseBody(res: Response): Promise<unknown> {\n const contentType = res.headers.get('content-type') ?? ''\n if (contentType.includes('application/json')) {\n try {\n return await res.json()\n } catch {\n return null\n }\n }\n try {\n const text = await res.text()\n if (!text) return null\n try {\n return JSON.parse(text)\n } catch {\n return text\n }\n } catch {\n return null\n }\n}\n\nfunction failure(statusCode: number, error: string, details?: unknown): AiApiOperationResponse {\n const response: AiApiOperationResponse = { success: false, statusCode, error }\n if (details !== undefined) response.details = details\n return response\n}\n\nfunction normalizeError(body: unknown): { error: string; details?: unknown } {\n if (body && typeof body === 'object' && !Array.isArray(body)) {\n const record = body as Record<string, unknown>\n const message = typeof record.error === 'string'\n ? record.error\n : typeof record.message === 'string'\n ? record.message\n : null\n if (message) {\n const { error: _ignored, message: _ignoredMessage, ...rest } = record\n void _ignored\n void _ignoredMessage\n return Object.keys(rest).length > 0 ? { error: message, details: rest } : { error: message }\n }\n }\n if (typeof body === 'string' && body.length > 0) {\n return { error: body }\n }\n return { error: 'Request failed' }\n}\n\nexport function createAiApiOperationRunner(\n ctx: AiToolExecutionContext,\n options: AiApiOperationRunnerOptions = {},\n): AiApiOperationRunner {\n let manifestPromise: Promise<ApiRouteManifestEntry[]> | null = null\n\n const loadManifest = async (): Promise<ApiRouteManifestEntry[]> => {\n if (options.apiRoutes) return options.apiRoutes\n if (manifestPromise) return manifestPromise\n const loader = options.loadApiRoutes ?? defaultLoadApiRoutes\n manifestPromise = loader()\n try {\n return await manifestPromise\n } catch (error) {\n manifestPromise = null\n throw error\n }\n }\n\n return {\n async run<T = unknown>(request: AiApiOperationRequest): Promise<AiApiOperationResponse<T>> {\n let method: AiApiHttpMethod\n try {\n method = normalizeMethod(request.method)\n } catch (error) {\n const message = error instanceof Error ? error.message : 'Invalid method'\n return failure(400, message) as AiApiOperationResponse<T>\n }\n const path = normalizePath(request.path)\n\n let routes: ApiRouteManifestEntry[]\n try {\n routes = await loadManifest()\n } catch (error) {\n const message = error instanceof Error ? error.message : 'Failed to load API route manifest'\n return failure(500, `Operation runner manifest unavailable: ${message}`) as AiApiOperationResponse<T>\n }\n\n const match = findApiRouteManifestMatch(routes, method, path)\n if (!match) {\n return failure(\n 404,\n `No documented API route matches ${method} ${path}`,\n ) as AiApiOperationResponse<T>\n }\n\n let mod: LoadedRouteModule\n try {\n mod = (await match.route.load()) as LoadedRouteModule\n } catch (error) {\n const message = error instanceof Error ? error.message : 'Failed to load route module'\n return failure(500, `Failed to load route module: ${message}`) as AiApiOperationResponse<T>\n }\n\n if (!('openApi' in mod) || mod.openApi === undefined || mod.openApi === null) {\n return failure(\n 501,\n `Route ${method} ${path} is undocumented (missing openApi export); refusing to call from AI tool`,\n ) as AiApiOperationResponse<T>\n }\n\n const handler = pickHandler(match.route, mod, method)\n if (!handler) {\n return failure(\n 405,\n `Route ${path} does not export a handler for method ${method}`,\n ) as AiApiOperationResponse<T>\n }\n\n const methodMetadata = extractMethodMetadata(mod.metadata, method)\n const routeFeatures = methodMetadata?.requireFeatures ?? []\n const isMutation = MUTATION_METHODS.has(method)\n\n if (isMutation && routeFeatures.length === 0 && !request.allowFeaturelessMutation) {\n return failure(\n 403,\n `Mutation route ${method} ${path} declares no requiredFeatures; refusing to call without allowFeaturelessMutation opt-in`,\n ) as AiApiOperationResponse<T>\n }\n\n const toolFeatures = ctx.tool.requiredFeatures ?? []\n if (routeFeatures.length > 0 && !hasAllFeatures(toolFeatures, routeFeatures)) {\n return failure(\n 403,\n `AI tool \"${ctx.tool.name}\" requiredFeatures do not cover route ${method} ${path} requiredFeatures`,\n { toolFeatures, routeFeatures },\n ) as AiApiOperationResponse<T>\n }\n\n const url = buildUrl(path, request.query)\n const headers = new Headers()\n const requestInit: RequestInit = { method, headers }\n if (request.body !== undefined && method !== 'GET') {\n headers.set('content-type', 'application/json')\n requestInit.body = JSON.stringify(request.body)\n }\n\n const syntheticRequest = new Request(url, requestInit)\n attachTrustedAuthContext(syntheticRequest, buildAuthEnvelope(ctx))\n\n let response: Response\n try {\n response = await handler(syntheticRequest, { params: match.params })\n } catch (error) {\n const message = error instanceof Error ? error.message : 'Route handler threw'\n return failure(500, message) as AiApiOperationResponse<T>\n }\n\n const status = response.status\n const body = await readResponseBody(response)\n if (status >= 200 && status < 300) {\n return {\n success: true,\n statusCode: status,\n data: body as T,\n }\n }\n const normalized = normalizeError(body)\n return {\n success: false,\n statusCode: status,\n error: normalized.error,\n ...(normalized.details !== undefined ? { details: normalized.details } : {}),\n }\n },\n }\n}\n\nasync function defaultLoadApiRoutes(): Promise<ApiRouteManifestEntry[]> {\n const registered = getApiRouteManifests()\n if (registered.length === 0) {\n throw new Error(\n 'No API route manifest registered. Call registerApiRouteManifests(...) at app bootstrap or pass apiRoutes/loadApiRoutes to createAiApiOperationRunner.',\n )\n }\n return registered\n}\n\n"],
5
+ "mappings": "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",
6
+ "names": []
7
+ }