@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,604 @@
1
+ 'use client'
2
+
3
+ import * as React from 'react'
4
+ import { useQuery } from '@tanstack/react-query'
5
+ import { AlertCircle, Bot, BookOpen, Loader2, Play, RefreshCcw } from 'lucide-react'
6
+ import { useT } from '@open-mercato/shared/lib/i18n/context'
7
+ import { Alert, AlertDescription, AlertTitle } from '@open-mercato/ui/primitives/alert'
8
+ import { Button } from '@open-mercato/ui/primitives/button'
9
+ import { IconButton } from '@open-mercato/ui/primitives/icon-button'
10
+ import { Label } from '@open-mercato/ui/primitives/label'
11
+ import { Switch } from '@open-mercato/ui/primitives/switch'
12
+ import { Tabs, TabsList, TabsTrigger, TabsContent } from '@open-mercato/ui/primitives/tabs'
13
+ import { Textarea } from '@open-mercato/ui/primitives/textarea'
14
+ import { EmptyState } from '@open-mercato/ui/backend/EmptyState'
15
+ import { apiCall, apiCallOrThrow } from '@open-mercato/ui/backend/utils/apiCall'
16
+ import { AiChat, createAiUiPartRegistry, useAiShortcuts } from '@open-mercato/ui/ai'
17
+ import type { AiChatDebugPromptSection, AiChatDebugTool } from '@open-mercato/ui/ai'
18
+
19
+ type PlaygroundAgentTool = {
20
+ name: string
21
+ displayName?: string
22
+ isMutation?: boolean
23
+ registered?: boolean
24
+ requiredFeatures?: string[]
25
+ }
26
+
27
+ type PlaygroundAgent = {
28
+ id: string
29
+ moduleId: string
30
+ label: string
31
+ description: string
32
+ executionMode: 'chat' | 'object'
33
+ mutationPolicy: string
34
+ allowedTools: string[]
35
+ requiredFeatures: string[]
36
+ acceptedMediaTypes: string[]
37
+ hasOutputSchema: boolean
38
+ systemPrompt?: string
39
+ readOnly?: boolean
40
+ maxSteps?: number | null
41
+ tools?: PlaygroundAgentTool[]
42
+ }
43
+
44
+ type AgentsResponse = {
45
+ agents: PlaygroundAgent[]
46
+ total: number
47
+ }
48
+
49
+ type RunObjectResponse = {
50
+ object: unknown
51
+ finishReason?: string
52
+ usage?: { inputTokens?: number; outputTokens?: number }
53
+ }
54
+
55
+ type RunObjectError = {
56
+ error: string
57
+ code?: string
58
+ issues?: unknown
59
+ }
60
+
61
+ async function fetchAgents(): Promise<AgentsResponse> {
62
+ const { result, status } = await apiCallOrThrow<AgentsResponse>(
63
+ '/api/ai_assistant/ai/agents',
64
+ { method: 'GET', credentials: 'include' },
65
+ { errorMessage: 'Failed to load agents' },
66
+ )
67
+ if (!result) throw new Error(`Failed to load agents (${status})`)
68
+ return result
69
+ }
70
+
71
+ function PlaygroundLoading({ message }: { message: string }) {
72
+ return (
73
+ <div
74
+ className="flex items-center gap-2 rounded-lg border border-border bg-background p-4 text-sm text-muted-foreground"
75
+ role="status"
76
+ >
77
+ <Loader2 className="size-4 animate-spin" aria-hidden />
78
+ <span>{message}</span>
79
+ </div>
80
+ )
81
+ }
82
+
83
+ function PlaygroundNoAgents() {
84
+ const t = useT()
85
+ return (
86
+ <EmptyState
87
+ icon={<Bot className="size-6" aria-hidden />}
88
+ title={t(
89
+ 'ai_assistant.playground.empty.title',
90
+ 'No AI agents are registered for your role yet.',
91
+ )}
92
+ description={t(
93
+ 'ai_assistant.playground.empty.description',
94
+ 'Declare agents inside `packages/<module>/src/modules/<module>/ai-agents.ts`, run `yarn generate`, and ensure the caller holds the agent\'s required features.',
95
+ )}
96
+ >
97
+ <div className="mt-2 inline-flex items-center gap-2 text-xs text-muted-foreground">
98
+ <BookOpen className="size-3" aria-hidden />
99
+ <span>
100
+ {t(
101
+ 'ai_assistant.playground.empty.docLabel',
102
+ 'See packages/ai-assistant/AGENTS.md for the agent definition reference.',
103
+ )}
104
+ </span>
105
+ </div>
106
+ </EmptyState>
107
+ )
108
+ }
109
+
110
+ function AgentDetails({ agent }: { agent: PlaygroundAgent }) {
111
+ const t = useT()
112
+ return (
113
+ <div className="rounded-md border border-border bg-muted/30 p-3 text-sm">
114
+ <div className="font-semibold">{agent.label}</div>
115
+ <p className="mt-1 text-xs text-muted-foreground">{agent.description}</p>
116
+ <dl className="mt-3 grid grid-cols-2 gap-2 text-xs">
117
+ <div>
118
+ <dt className="font-medium text-muted-foreground">
119
+ {t('ai_assistant.playground.meta.module', 'Module')}
120
+ </dt>
121
+ <dd className="font-mono">{agent.moduleId}</dd>
122
+ </div>
123
+ <div>
124
+ <dt className="font-medium text-muted-foreground">
125
+ {t('ai_assistant.playground.meta.executionMode', 'Execution mode')}
126
+ </dt>
127
+ <dd className="font-mono">{agent.executionMode}</dd>
128
+ </div>
129
+ <div>
130
+ <dt className="font-medium text-muted-foreground">
131
+ {t('ai_assistant.playground.meta.mutationPolicy', 'Mutation policy')}
132
+ </dt>
133
+ <dd className="font-mono">{agent.mutationPolicy}</dd>
134
+ </div>
135
+ <div>
136
+ <dt className="font-medium text-muted-foreground">
137
+ {t('ai_assistant.playground.meta.tools', 'Allowed tools')}
138
+ </dt>
139
+ <dd className="font-mono">{agent.allowedTools.length}</dd>
140
+ </div>
141
+ </dl>
142
+ </div>
143
+ )
144
+ }
145
+
146
+ function buildDebugTools(agent: PlaygroundAgent): AiChatDebugTool[] {
147
+ if (agent.tools && agent.tools.length > 0) {
148
+ return agent.tools.map((tool) => ({
149
+ name: tool.name,
150
+ displayName: tool.displayName ?? tool.name,
151
+ isMutation: Boolean(tool.isMutation),
152
+ requiredFeatures: tool.requiredFeatures ?? [],
153
+ }))
154
+ }
155
+ return agent.allowedTools.map((toolName) => ({ name: toolName }))
156
+ }
157
+
158
+ function buildDebugPromptSections(agent: PlaygroundAgent): AiChatDebugPromptSection[] {
159
+ const sections: AiChatDebugPromptSection[] = []
160
+ if (agent.systemPrompt) {
161
+ sections.push({ id: 'role', source: 'default', text: agent.systemPrompt })
162
+ }
163
+ const placeholderIds = [
164
+ 'scope',
165
+ 'data',
166
+ 'tools',
167
+ 'attachments',
168
+ 'mutationPolicy',
169
+ 'responseStyle',
170
+ 'overrides',
171
+ ] as const
172
+ for (const id of placeholderIds) {
173
+ sections.push({ id, source: 'placeholder' })
174
+ }
175
+ return sections
176
+ }
177
+
178
+ type PlaygroundUiPartSeed = {
179
+ componentId: string
180
+ pendingActionId?: string
181
+ payload?: unknown
182
+ }
183
+
184
+ function readPlaygroundUiPartSeeds(): PlaygroundUiPartSeed[] {
185
+ if (typeof window === 'undefined') return []
186
+ try {
187
+ const params = new URLSearchParams(window.location.search)
188
+ const componentId = params.get('uiPart')
189
+ if (!componentId) return []
190
+ const pendingActionId = params.get('pendingActionId') ?? undefined
191
+ return [{ componentId, pendingActionId }]
192
+ } catch {
193
+ return []
194
+ }
195
+ }
196
+
197
+ function ChatLane({ agent, debug }: { agent: PlaygroundAgent; debug: boolean }) {
198
+ const t = useT()
199
+ // Scoped registry so repeated mounts do not share state with other pages.
200
+ // Step 5.10: opt in to the LIVE mutation-approval cards so the playground
201
+ // exercises the real cards when the chat response surfaces a pending
202
+ // action (via the `?uiPart=...` debug seed for Playwright).
203
+ const registry = React.useMemo(
204
+ () => createAiUiPartRegistry({ seedLiveApprovalCards: true }),
205
+ [],
206
+ )
207
+ const debugTools = React.useMemo(() => buildDebugTools(agent), [agent])
208
+ const debugPromptSections = React.useMemo(
209
+ () => buildDebugPromptSections(agent),
210
+ [agent],
211
+ )
212
+ const [uiParts, setUiParts] = React.useState<PlaygroundUiPartSeed[]>([])
213
+
214
+ // Step 5.10: the dispatcher does not yet surface `AiUiPart` entries through
215
+ // the plain-text stream consumed by `useAiChat`. For now the playground
216
+ // reads a `?uiPart=<componentId>&pendingActionId=...` seed from the URL
217
+ // so Playwright + operator debug flows can render the approval cards
218
+ // against a stubbed `/api/ai_assistant/ai/actions/:id` endpoint. When the
219
+ // dispatcher switches to the UIMessageChunk format this effect swaps over
220
+ // to the streamed `uiParts` payload.
221
+ React.useEffect(() => {
222
+ const seeds = readPlaygroundUiPartSeeds()
223
+ if (seeds.length > 0) setUiParts(seeds)
224
+ }, [])
225
+
226
+ if (agent.executionMode !== 'chat') {
227
+ return (
228
+ <Alert variant="info" data-ai-playground-unsupported="chat">
229
+ <AlertTitle>
230
+ {t(
231
+ 'ai_assistant.playground.chat.notSupportedTitle',
232
+ 'Chat mode is not available for this agent.',
233
+ )}
234
+ </AlertTitle>
235
+ <AlertDescription>
236
+ {t(
237
+ 'ai_assistant.playground.chat.notSupportedBody',
238
+ 'Pick an agent whose execution mode is "chat", or switch to the object-mode tab.',
239
+ )}
240
+ </AlertDescription>
241
+ </Alert>
242
+ )
243
+ }
244
+
245
+ return (
246
+ <AiChat
247
+ key={agent.id}
248
+ agent={agent.id}
249
+ pageContext={{ source: 'playground', pageId: 'ai_assistant.playground' }}
250
+ debug={debug}
251
+ registry={registry}
252
+ className="min-h-[360px]"
253
+ debugTools={debugTools}
254
+ debugPromptSections={debugPromptSections}
255
+ uiParts={uiParts}
256
+ />
257
+ )
258
+ }
259
+
260
+ function ObjectLane({ agent }: { agent: PlaygroundAgent }) {
261
+ const t = useT()
262
+ const [prompt, setPrompt] = React.useState('')
263
+ const [isRunning, setIsRunning] = React.useState(false)
264
+ const [result, setResult] = React.useState<RunObjectResponse | null>(null)
265
+ const [error, setError] = React.useState<RunObjectError | null>(null)
266
+ const [lastRequest, setLastRequest] = React.useState<unknown>(null)
267
+
268
+ const isSupported = agent.executionMode === 'object'
269
+ const canRun = isSupported && prompt.trim().length > 0 && !isRunning
270
+
271
+ const runObject = React.useCallback(async () => {
272
+ if (!canRun) return
273
+ const body = {
274
+ agent: agent.id,
275
+ messages: [{ role: 'user' as const, content: prompt }],
276
+ pageContext: { source: 'playground', pageId: 'ai_assistant.playground' },
277
+ }
278
+ setLastRequest(body)
279
+ setIsRunning(true)
280
+ setResult(null)
281
+ setError(null)
282
+ try {
283
+ const { ok, status, result } = await apiCall<RunObjectResponse | RunObjectError>(
284
+ '/api/ai_assistant/ai/run-object',
285
+ {
286
+ method: 'POST',
287
+ headers: { 'content-type': 'application/json' },
288
+ credentials: 'include',
289
+ body: JSON.stringify(body),
290
+ },
291
+ )
292
+ if (!ok) {
293
+ const payload = (result as RunObjectError | null) ?? { error: `HTTP ${status}` }
294
+ setError(payload)
295
+ return
296
+ }
297
+ setResult((result as RunObjectResponse | null) ?? { object: null })
298
+ } catch (err) {
299
+ setError({
300
+ error: err instanceof Error ? err.message : String(err),
301
+ code: 'network_error',
302
+ })
303
+ } finally {
304
+ setIsRunning(false)
305
+ }
306
+ }, [agent.id, canRun, prompt])
307
+
308
+ const { handleKeyDown } = useAiShortcuts({
309
+ onSubmit: () => {
310
+ void runObject()
311
+ },
312
+ onCancel: () => {
313
+ setError(null)
314
+ },
315
+ })
316
+
317
+ if (!isSupported) {
318
+ return (
319
+ <Alert variant="info" data-ai-playground-unsupported="object">
320
+ <AlertTitle>
321
+ {t(
322
+ 'ai_assistant.playground.object.notSupportedTitle',
323
+ 'Object mode is not available for this agent.',
324
+ )}
325
+ </AlertTitle>
326
+ <AlertDescription>
327
+ {t(
328
+ 'ai_assistant.playground.object.notSupportedBody',
329
+ 'This agent declares executionMode = "chat". Pick an object-mode agent to preview structured output, or switch to the chat tab.',
330
+ )}
331
+ </AlertDescription>
332
+ </Alert>
333
+ )
334
+ }
335
+
336
+ return (
337
+ <div className="flex flex-col gap-3" data-ai-playground-object>
338
+ <div className="flex flex-col gap-2">
339
+ <Label htmlFor="ai-playground-object-input">
340
+ {t('ai_assistant.playground.object.inputLabel', 'Prompt')}
341
+ </Label>
342
+ <Textarea
343
+ id="ai-playground-object-input"
344
+ rows={4}
345
+ value={prompt}
346
+ placeholder={t(
347
+ 'ai_assistant.playground.object.inputPlaceholder',
348
+ 'Describe what the agent should produce...',
349
+ )}
350
+ onChange={(event) => setPrompt(event.target.value)}
351
+ onKeyDown={handleKeyDown}
352
+ className="resize-none"
353
+ aria-label={t('ai_assistant.playground.object.inputLabel', 'Prompt')}
354
+ />
355
+ <div className="flex items-center justify-between">
356
+ <p className="text-xs text-muted-foreground">
357
+ {t(
358
+ 'ai_assistant.playground.object.shortcutHint',
359
+ 'Press Cmd/Ctrl+Enter to run.',
360
+ )}
361
+ </p>
362
+ <Button
363
+ type="button"
364
+ size="sm"
365
+ onClick={() => void runObject()}
366
+ disabled={!canRun}
367
+ data-ai-playground-object-run
368
+ >
369
+ {isRunning ? (
370
+ <Loader2 className="size-4 animate-spin" aria-hidden />
371
+ ) : (
372
+ <Play className="size-4" aria-hidden />
373
+ )}
374
+ <span>{t('ai_assistant.playground.object.run', 'Run object')}</span>
375
+ </Button>
376
+ </div>
377
+ </div>
378
+
379
+ {error ? (
380
+ <Alert variant="destructive" data-ai-playground-object-error={error.code ?? 'unknown'}>
381
+ <AlertTitle>
382
+ {t('ai_assistant.playground.object.errorTitle', 'Object run failed')}
383
+ </AlertTitle>
384
+ <AlertDescription>
385
+ {error.code ? <span className="mr-2 font-mono text-xs">{error.code}</span> : null}
386
+ {error.error}
387
+ </AlertDescription>
388
+ </Alert>
389
+ ) : null}
390
+
391
+ {result ? (
392
+ <section
393
+ className="rounded-md border border-border bg-muted/30 p-3"
394
+ data-ai-playground-object-result
395
+ >
396
+ <h3 className="text-sm font-semibold">
397
+ {t('ai_assistant.playground.object.resultTitle', 'Generated object')}
398
+ </h3>
399
+ <pre className="mt-2 max-h-96 overflow-auto rounded bg-background p-2 text-xs font-mono">
400
+ {JSON.stringify(result.object, null, 2)}
401
+ </pre>
402
+ {result.usage || result.finishReason ? (
403
+ <dl className="mt-3 grid grid-cols-3 gap-2 text-xs">
404
+ {result.finishReason ? (
405
+ <div>
406
+ <dt className="text-muted-foreground">
407
+ {t('ai_assistant.playground.object.finishReason', 'Finish reason')}
408
+ </dt>
409
+ <dd className="font-mono">{result.finishReason}</dd>
410
+ </div>
411
+ ) : null}
412
+ {result.usage?.inputTokens !== undefined ? (
413
+ <div>
414
+ <dt className="text-muted-foreground">
415
+ {t('ai_assistant.playground.object.inputTokens', 'Input tokens')}
416
+ </dt>
417
+ <dd className="font-mono">{result.usage.inputTokens}</dd>
418
+ </div>
419
+ ) : null}
420
+ {result.usage?.outputTokens !== undefined ? (
421
+ <div>
422
+ <dt className="text-muted-foreground">
423
+ {t('ai_assistant.playground.object.outputTokens', 'Output tokens')}
424
+ </dt>
425
+ <dd className="font-mono">{result.usage.outputTokens}</dd>
426
+ </div>
427
+ ) : null}
428
+ </dl>
429
+ ) : null}
430
+ </section>
431
+ ) : null}
432
+
433
+ {lastRequest && (error || result) ? (
434
+ <details
435
+ className="rounded-md border border-border bg-muted/20 p-2 text-xs"
436
+ data-ai-playground-object-debug
437
+ >
438
+ <summary className="cursor-pointer font-semibold">
439
+ {t('ai_assistant.playground.object.debugTitle', 'Last request payload')}
440
+ </summary>
441
+ <pre className="mt-2 max-h-64 overflow-auto whitespace-pre-wrap font-mono">
442
+ {JSON.stringify(lastRequest, null, 2)}
443
+ </pre>
444
+ </details>
445
+ ) : null}
446
+ </div>
447
+ )
448
+ }
449
+
450
+ export function AiPlaygroundPageClient() {
451
+ const t = useT()
452
+ const [selectedAgentId, setSelectedAgentId] = React.useState<string | null>(null)
453
+ const [debugEnabled, setDebugEnabled] = React.useState(false)
454
+ const [tab, setTab] = React.useState<'chat' | 'object'>('chat')
455
+
456
+ const { data, isLoading, isError, error, refetch, isFetching } = useQuery<AgentsResponse>({
457
+ queryKey: ['ai_assistant', 'playground', 'agents'],
458
+ queryFn: fetchAgents,
459
+ })
460
+
461
+ const agents = React.useMemo<PlaygroundAgent[]>(() => data?.agents ?? [], [data])
462
+
463
+ React.useEffect(() => {
464
+ if (!agents.length) {
465
+ if (selectedAgentId !== null) setSelectedAgentId(null)
466
+ return
467
+ }
468
+ if (!selectedAgentId || !agents.some((agent) => agent.id === selectedAgentId)) {
469
+ setSelectedAgentId(agents[0].id)
470
+ }
471
+ }, [agents, selectedAgentId])
472
+
473
+ const selectedAgent = React.useMemo<PlaygroundAgent | null>(() => {
474
+ if (!selectedAgentId) return null
475
+ return agents.find((agent) => agent.id === selectedAgentId) ?? null
476
+ }, [agents, selectedAgentId])
477
+
478
+ if (isLoading) {
479
+ return (
480
+ <PlaygroundLoading
481
+ message={t('ai_assistant.playground.loadingAgents', 'Loading AI agents...')}
482
+ />
483
+ )
484
+ }
485
+
486
+ if (isError) {
487
+ return (
488
+ <Alert variant="destructive" data-ai-playground-error>
489
+ <AlertCircle className="size-4" aria-hidden />
490
+ <AlertTitle>
491
+ {t('ai_assistant.playground.loadErrorTitle', 'Failed to load AI agents')}
492
+ </AlertTitle>
493
+ <AlertDescription>
494
+ <span>{error instanceof Error ? error.message : String(error)}</span>
495
+ <div className="mt-2">
496
+ <Button
497
+ type="button"
498
+ size="sm"
499
+ variant="outline"
500
+ onClick={() => {
501
+ void refetch()
502
+ }}
503
+ >
504
+ <RefreshCcw className="size-4" aria-hidden />
505
+ <span>{t('ai_assistant.playground.retry', 'Retry')}</span>
506
+ </Button>
507
+ </div>
508
+ </AlertDescription>
509
+ </Alert>
510
+ )
511
+ }
512
+
513
+ if (!agents.length) {
514
+ return <PlaygroundNoAgents />
515
+ }
516
+
517
+ return (
518
+ <div className="flex flex-col gap-4" data-ai-playground>
519
+ <header className="flex flex-col gap-1">
520
+ <h1 className="text-2xl font-bold tracking-tight">
521
+ {t('ai_assistant.playground.title', 'AI Playground')}
522
+ </h1>
523
+ <p className="text-sm text-muted-foreground">
524
+ {t(
525
+ 'ai_assistant.playground.subtitle',
526
+ 'Exercise every registered AI agent end-to-end. Use the debug panel to inspect request and response payloads, and the object-mode tab to preview structured output.',
527
+ )}
528
+ </p>
529
+ </header>
530
+
531
+ <section className="flex flex-col gap-3 rounded-lg border border-border bg-background p-3">
532
+ <div className="flex flex-col gap-3 sm:flex-row sm:items-end sm:justify-between">
533
+ <div className="flex flex-col gap-2 sm:flex-1">
534
+ <Label htmlFor="ai-playground-agent-picker">
535
+ {t('ai_assistant.playground.agentPickerLabel', 'Agent')}
536
+ </Label>
537
+ <select
538
+ id="ai-playground-agent-picker"
539
+ data-ai-playground-agent-picker
540
+ className="h-9 rounded-md border border-input bg-background px-3 text-sm"
541
+ value={selectedAgentId ?? ''}
542
+ onChange={(event) => setSelectedAgentId(event.target.value)}
543
+ >
544
+ {agents.map((agent) => (
545
+ <option key={agent.id} value={agent.id}>
546
+ {agent.label} ({agent.id})
547
+ </option>
548
+ ))}
549
+ </select>
550
+ </div>
551
+ <div className="flex items-center gap-3 sm:flex-shrink-0">
552
+ <Label htmlFor="ai-playground-debug" className="text-sm">
553
+ {t('ai_assistant.playground.debugToggle', 'Debug panel')}
554
+ </Label>
555
+ <Switch
556
+ id="ai-playground-debug"
557
+ checked={debugEnabled}
558
+ onCheckedChange={(next: boolean) => setDebugEnabled(next)}
559
+ aria-label={t('ai_assistant.playground.debugToggle', 'Debug panel')}
560
+ data-ai-playground-debug-toggle
561
+ />
562
+ <IconButton
563
+ type="button"
564
+ variant="ghost"
565
+ size="sm"
566
+ onClick={() => {
567
+ void refetch()
568
+ }}
569
+ aria-label={t('ai_assistant.playground.refresh', 'Refresh agents')}
570
+ disabled={isFetching}
571
+ >
572
+ <RefreshCcw className="size-4" aria-hidden />
573
+ </IconButton>
574
+ </div>
575
+ </div>
576
+ {selectedAgent ? <AgentDetails agent={selectedAgent} /> : null}
577
+ </section>
578
+
579
+ {selectedAgent ? (
580
+ <Tabs
581
+ value={tab}
582
+ onValueChange={(next: string) => setTab(next === 'object' ? 'object' : 'chat')}
583
+ >
584
+ <TabsList>
585
+ <TabsTrigger value="chat">
586
+ {t('ai_assistant.playground.tabs.chat', 'Chat')}
587
+ </TabsTrigger>
588
+ <TabsTrigger value="object">
589
+ {t('ai_assistant.playground.tabs.object', 'Object mode')}
590
+ </TabsTrigger>
591
+ </TabsList>
592
+ <TabsContent value="chat">
593
+ <ChatLane agent={selectedAgent} debug={debugEnabled} />
594
+ </TabsContent>
595
+ <TabsContent value="object">
596
+ <ObjectLane agent={selectedAgent} />
597
+ </TabsContent>
598
+ </Tabs>
599
+ ) : null}
600
+ </div>
601
+ )
602
+ }
603
+
604
+ export default AiPlaygroundPageClient
@@ -0,0 +1,26 @@
1
+ import React from 'react'
2
+
3
+ const playgroundIcon = React.createElement(
4
+ 'svg',
5
+ { width: 16, height: 16, viewBox: '0 0 24 24', fill: 'none', stroke: 'currentColor', strokeWidth: 2, strokeLinecap: 'round', strokeLinejoin: 'round' },
6
+ React.createElement('path', { d: 'M14 4h6v6' }),
7
+ React.createElement('path', { d: 'M10 20H4v-6' }),
8
+ React.createElement('path', { d: 'M20 4 14 10' }),
9
+ React.createElement('path', { d: 'm4 20 6-6' }),
10
+ )
11
+
12
+ export const metadata = {
13
+ requireAuth: true,
14
+ requireFeatures: ['ai_assistant.settings.manage'],
15
+ pageTitle: 'AI Playground',
16
+ pageTitleKey: 'ai_assistant.playground.navTitle',
17
+ pageGroup: 'Module Configs',
18
+ pageGroupKey: 'settings.sections.moduleConfigs',
19
+ pageOrder: 431,
20
+ icon: playgroundIcon,
21
+ pageContext: 'settings' as const,
22
+ breadcrumb: [
23
+ { label: 'AI Agents', labelKey: 'ai_assistant.agents.navTitle' },
24
+ { label: 'Playground', labelKey: 'ai_assistant.playground.navTitle' },
25
+ ],
26
+ } as const
@@ -0,0 +1,12 @@
1
+ import { Page, PageBody } from '@open-mercato/ui/backend/Page'
2
+ import { AiPlaygroundPageClient } from './AiPlaygroundPageClient'
3
+
4
+ export default async function AiAssistantPlaygroundPage() {
5
+ return (
6
+ <Page>
7
+ <PageBody>
8
+ <AiPlaygroundPageClient />
9
+ </PageBody>
10
+ </Page>
11
+ )
12
+ }