@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,1111 @@
1
+ "use client";
2
+ import { jsx, jsxs } from "react/jsx-runtime";
3
+ import * as React from "react";
4
+ import { useQuery, useQueryClient } from "@tanstack/react-query";
5
+ import {
6
+ AlertCircle,
7
+ Bot,
8
+ BookOpen,
9
+ CheckCircle2,
10
+ History,
11
+ Image as ImageIcon,
12
+ FileText,
13
+ Loader2,
14
+ Lock,
15
+ Paperclip,
16
+ RefreshCcw,
17
+ Save,
18
+ ShieldAlert,
19
+ Trash2,
20
+ Wand2,
21
+ Wrench
22
+ } from "lucide-react";
23
+ import { useT } from "@open-mercato/shared/lib/i18n/context";
24
+ import { Alert, AlertDescription, AlertTitle } from "@open-mercato/ui/primitives/alert";
25
+ import { Badge } from "@open-mercato/ui/primitives/badge";
26
+ import { Button } from "@open-mercato/ui/primitives/button";
27
+ import { IconButton } from "@open-mercato/ui/primitives/icon-button";
28
+ import { Label } from "@open-mercato/ui/primitives/label";
29
+ import { StatusBadge } from "@open-mercato/ui/primitives/status-badge";
30
+ import { Switch } from "@open-mercato/ui/primitives/switch";
31
+ import { Textarea } from "@open-mercato/ui/primitives/textarea";
32
+ import {
33
+ Tooltip,
34
+ TooltipContent,
35
+ TooltipProvider,
36
+ TooltipTrigger
37
+ } from "@open-mercato/ui/primitives/tooltip";
38
+ import { EmptyState } from "@open-mercato/ui/backend/EmptyState";
39
+ import { apiCall, apiCallOrThrow } from "@open-mercato/ui/backend/utils/apiCall";
40
+ import { useAiShortcuts } from "@open-mercato/ui/ai";
41
+ const PROMPT_SECTION_IDS = [
42
+ "role",
43
+ "scope",
44
+ "data",
45
+ "tools",
46
+ "attachments",
47
+ "mutationPolicy",
48
+ "responseStyle",
49
+ "overrides"
50
+ ];
51
+ const mutationPolicyStatusMap = {
52
+ "read-only": "neutral",
53
+ "confirm-required": "warning",
54
+ "destructive-confirm-required": "error"
55
+ };
56
+ const executionModeStatusMap = {
57
+ chat: "info",
58
+ object: "success"
59
+ };
60
+ const mediaTypeIconMap = {
61
+ image: ImageIcon,
62
+ pdf: FileText,
63
+ file: Paperclip
64
+ };
65
+ async function fetchAgents() {
66
+ const { result, status } = await apiCallOrThrow(
67
+ "/api/ai_assistant/ai/agents",
68
+ { method: "GET", credentials: "include" },
69
+ { errorMessage: "Failed to load agents" }
70
+ );
71
+ if (!result) throw new Error(`Failed to load agents (${status})`);
72
+ return result;
73
+ }
74
+ async function fetchOverride(agentId) {
75
+ const { result, status } = await apiCallOrThrow(
76
+ `/api/ai_assistant/ai/agents/${encodeURIComponent(agentId)}/prompt-override`,
77
+ { method: "GET", credentials: "include" },
78
+ { errorMessage: "Failed to load prompt override" }
79
+ );
80
+ if (!result) throw new Error(`Failed to load prompt override (${status})`);
81
+ return result;
82
+ }
83
+ const MUTATION_POLICY_OPTIONS = [
84
+ "read-only",
85
+ "destructive-confirm-required",
86
+ "confirm-required"
87
+ ];
88
+ const POLICY_RESTRICTIVENESS_UI = {
89
+ "read-only": 0,
90
+ "destructive-confirm-required": 1,
91
+ "confirm-required": 2
92
+ };
93
+ async function fetchMutationPolicy(agentId) {
94
+ const { result, status } = await apiCallOrThrow(
95
+ `/api/ai_assistant/ai/agents/${encodeURIComponent(agentId)}/mutation-policy`,
96
+ { method: "GET", credentials: "include" },
97
+ { errorMessage: "Failed to load mutation policy" }
98
+ );
99
+ if (!result) throw new Error(`Failed to load mutation policy (${status})`);
100
+ return result;
101
+ }
102
+ function SettingsLoading({ message }) {
103
+ return /* @__PURE__ */ jsxs(
104
+ "div",
105
+ {
106
+ className: "flex items-center gap-2 rounded-lg border border-border bg-background p-4 text-sm text-muted-foreground",
107
+ role: "status",
108
+ children: [
109
+ /* @__PURE__ */ jsx(Loader2, { className: "size-4 animate-spin", "aria-hidden": true }),
110
+ /* @__PURE__ */ jsx("span", { children: message })
111
+ ]
112
+ }
113
+ );
114
+ }
115
+ function EmptyAgents() {
116
+ const t = useT();
117
+ return /* @__PURE__ */ jsx(
118
+ EmptyState,
119
+ {
120
+ icon: /* @__PURE__ */ jsx(Bot, { className: "size-6", "aria-hidden": true }),
121
+ title: t(
122
+ "ai_assistant.agents.empty.title",
123
+ "No AI agents are registered for your role yet."
124
+ ),
125
+ description: t(
126
+ "ai_assistant.agents.empty.description",
127
+ "Declare agents inside `packages/<module>/src/modules/<module>/ai-agents.ts`, run `yarn generate`, and ensure the caller holds the agent's required features."
128
+ ),
129
+ children: /* @__PURE__ */ jsxs("div", { className: "mt-2 inline-flex items-center gap-2 text-xs text-muted-foreground", children: [
130
+ /* @__PURE__ */ jsx(BookOpen, { className: "size-3", "aria-hidden": true }),
131
+ /* @__PURE__ */ jsx("span", { children: t(
132
+ "ai_assistant.agents.empty.docLabel",
133
+ "See packages/ai-assistant/AGENTS.md for the agent definition reference."
134
+ ) })
135
+ ] })
136
+ }
137
+ );
138
+ }
139
+ function PromptSectionEditor({
140
+ sectionId,
141
+ defaultText,
142
+ overrideText,
143
+ override,
144
+ onToggleOverride,
145
+ onOverrideChange,
146
+ onSaveShortcut
147
+ }) {
148
+ const t = useT();
149
+ const sectionLabel = t(
150
+ `ai_assistant.agents.prompt.sections.${sectionId}`,
151
+ sectionId.charAt(0).toUpperCase() + sectionId.slice(1)
152
+ );
153
+ const textareaId = `ai-agent-prompt-${sectionId}`;
154
+ const textareaRef = React.useRef(null);
155
+ const { handleKeyDown } = useAiShortcuts({
156
+ onSubmit: onSaveShortcut,
157
+ onCancel: () => {
158
+ textareaRef.current?.blur();
159
+ }
160
+ });
161
+ return /* @__PURE__ */ jsxs(
162
+ "div",
163
+ {
164
+ className: "flex flex-col gap-2 rounded-md border border-border bg-muted/20 p-3",
165
+ "data-ai-agent-prompt-section": sectionId,
166
+ children: [
167
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-3", children: [
168
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-col", children: [
169
+ /* @__PURE__ */ jsx("span", { className: "text-overline font-semibold uppercase tracking-wider text-muted-foreground", children: sectionLabel }),
170
+ /* @__PURE__ */ jsx("span", { className: "text-xs text-muted-foreground", children: override ? t(
171
+ "ai_assistant.agents.prompt.overrideModeLabel",
172
+ "Override mode \u2014 replaces the default when persistence lands."
173
+ ) : t(
174
+ "ai_assistant.agents.prompt.defaultModeLabel",
175
+ "Default \u2014 shipped with the agent definition."
176
+ ) })
177
+ ] }),
178
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
179
+ /* @__PURE__ */ jsx(Label, { htmlFor: `${textareaId}-toggle`, className: "text-xs", children: t("ai_assistant.agents.prompt.toggleOverride", "Override") }),
180
+ /* @__PURE__ */ jsx(
181
+ Switch,
182
+ {
183
+ id: `${textareaId}-toggle`,
184
+ checked: override,
185
+ onCheckedChange: (next) => onToggleOverride(next),
186
+ "aria-label": t("ai_assistant.agents.prompt.toggleOverride", "Override"),
187
+ "data-ai-agent-prompt-toggle": sectionId
188
+ }
189
+ )
190
+ ] })
191
+ ] }),
192
+ override ? /* @__PURE__ */ jsx(
193
+ Textarea,
194
+ {
195
+ id: textareaId,
196
+ ref: textareaRef,
197
+ rows: 4,
198
+ value: overrideText,
199
+ onChange: (event) => onOverrideChange(event.target.value),
200
+ onKeyDown: handleKeyDown,
201
+ className: "resize-y font-mono text-xs",
202
+ placeholder: t(
203
+ "ai_assistant.agents.prompt.overridePlaceholder",
204
+ "Write the replacement text for this section..."
205
+ ),
206
+ "aria-label": `${sectionLabel} override`,
207
+ "data-ai-agent-prompt-override": sectionId
208
+ }
209
+ ) : /* @__PURE__ */ jsx(
210
+ "pre",
211
+ {
212
+ className: "max-h-40 overflow-auto rounded border border-border bg-background p-2 text-xs font-mono whitespace-pre-wrap",
213
+ "data-ai-agent-prompt-default": sectionId,
214
+ children: defaultText
215
+ }
216
+ )
217
+ ]
218
+ }
219
+ );
220
+ }
221
+ function ToolRow({ tool }) {
222
+ const t = useT();
223
+ return /* @__PURE__ */ jsxs(
224
+ "div",
225
+ {
226
+ className: "flex items-center justify-between gap-3 rounded-md border border-border bg-background px-3 py-2",
227
+ "data-ai-agent-tool-row": tool.name,
228
+ children: [
229
+ /* @__PURE__ */ jsxs("div", { className: "flex items-start gap-2 min-w-0", children: [
230
+ /* @__PURE__ */ jsx(Wrench, { className: "mt-0.5 size-4 text-muted-foreground", "aria-hidden": true }),
231
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-col min-w-0", children: [
232
+ /* @__PURE__ */ jsx("span", { className: "truncate text-sm font-medium", children: tool.displayName }),
233
+ /* @__PURE__ */ jsx("span", { className: "truncate text-xs font-mono text-muted-foreground", children: tool.name })
234
+ ] })
235
+ ] }),
236
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 flex-shrink-0", children: [
237
+ tool.isMutation ? /* @__PURE__ */ jsx(StatusBadge, { variant: "warning", dot: true, children: t("ai_assistant.agents.tools.mutationBadge", "Mutation") }) : /* @__PURE__ */ jsx(StatusBadge, { variant: "neutral", dot: true, children: t("ai_assistant.agents.tools.readBadge", "Read") }),
238
+ !tool.registered ? /* @__PURE__ */ jsx(StatusBadge, { variant: "error", dot: true, children: t("ai_assistant.agents.tools.missingBadge", "Missing") }) : null,
239
+ /* @__PURE__ */ jsxs(Tooltip, { children: [
240
+ /* @__PURE__ */ jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsxs("span", { className: "inline-flex items-center gap-1", children: [
241
+ /* @__PURE__ */ jsx(
242
+ Label,
243
+ {
244
+ htmlFor: `ai-agent-tool-${tool.name}`,
245
+ className: "text-xs text-muted-foreground",
246
+ children: t("ai_assistant.agents.tools.enabledLabel", "Enabled")
247
+ }
248
+ ),
249
+ /* @__PURE__ */ jsx(
250
+ Switch,
251
+ {
252
+ id: `ai-agent-tool-${tool.name}`,
253
+ checked: true,
254
+ disabled: true,
255
+ "aria-label": t("ai_assistant.agents.tools.enabledLabel", "Enabled"),
256
+ "data-ai-agent-tool-switch": tool.name
257
+ }
258
+ )
259
+ ] }) }),
260
+ /* @__PURE__ */ jsx(TooltipContent, { children: t(
261
+ "ai_assistant.agents.tools.tooltipDisabled",
262
+ "Editable after Phase 3 lands mutation policy controls."
263
+ ) })
264
+ ] })
265
+ ] })
266
+ ]
267
+ }
268
+ );
269
+ }
270
+ function AttachmentPolicyBadges({ mediaTypes }) {
271
+ const t = useT();
272
+ if (!mediaTypes.length) {
273
+ return /* @__PURE__ */ jsx(Badge, { variant: "neutral", children: t("ai_assistant.agents.attachments.noneBadge", "No attachments accepted") });
274
+ }
275
+ return /* @__PURE__ */ jsx("div", { className: "flex flex-wrap items-center gap-2", children: mediaTypes.map((mediaType) => {
276
+ const Icon = mediaTypeIconMap[mediaType] ?? Paperclip;
277
+ return /* @__PURE__ */ jsxs(
278
+ Badge,
279
+ {
280
+ variant: "info",
281
+ className: "gap-1.5",
282
+ "data-ai-agent-attachment-badge": mediaType,
283
+ children: [
284
+ /* @__PURE__ */ jsx(Icon, { className: "size-3", "aria-hidden": true }),
285
+ /* @__PURE__ */ jsx("span", { className: "font-mono text-xs", children: mediaType })
286
+ ]
287
+ },
288
+ mediaType
289
+ );
290
+ }) });
291
+ }
292
+ function MutationPolicySection({ agent }) {
293
+ const t = useT();
294
+ const queryClient = useQueryClient();
295
+ const query = useQuery({
296
+ queryKey: ["ai_assistant", "agent_settings", "mutation_policy", agent.id],
297
+ queryFn: () => fetchMutationPolicy(agent.id),
298
+ retry: false
299
+ });
300
+ const codeDeclared = query.data?.codeDeclared ?? agent.mutationPolicy;
301
+ const currentOverride = query.data?.override ?? null;
302
+ const [selected, setSelected] = React.useState(null);
303
+ const [isSaving, setIsSaving] = React.useState(false);
304
+ const [isClearing, setIsClearing] = React.useState(false);
305
+ const [state, setState] = React.useState({ kind: "idle" });
306
+ React.useEffect(() => {
307
+ setSelected(currentOverride?.mutationPolicy ?? null);
308
+ setState({ kind: "idle" });
309
+ }, [agent.id, currentOverride?.mutationPolicy]);
310
+ const codeRank = POLICY_RESTRICTIVENESS_UI[codeDeclared] ?? 0;
311
+ const effectivePolicy = (() => {
312
+ if (!currentOverride) return codeDeclared;
313
+ const overrideRank = POLICY_RESTRICTIVENESS_UI[currentOverride.mutationPolicy];
314
+ return overrideRank < codeRank ? currentOverride.mutationPolicy : codeDeclared;
315
+ })();
316
+ const hasChange = selected !== null && selected !== (currentOverride?.mutationPolicy ?? null);
317
+ const save = React.useCallback(async () => {
318
+ if (!selected || isSaving) return;
319
+ setIsSaving(true);
320
+ setState({ kind: "idle" });
321
+ try {
322
+ const { ok, status, result } = await apiCall(
323
+ `/api/ai_assistant/ai/agents/${encodeURIComponent(agent.id)}/mutation-policy`,
324
+ {
325
+ method: "POST",
326
+ headers: { "content-type": "application/json" },
327
+ credentials: "include",
328
+ body: JSON.stringify({ mutationPolicy: selected })
329
+ }
330
+ );
331
+ const payload = result ?? {};
332
+ if (!ok) {
333
+ const message = payload.code === "escalation_not_allowed" ? payload.error ?? t(
334
+ "ai_assistant.agents.mutation_policy.errors.escalationNotAllowed",
335
+ "Cannot upgrade beyond the agent's declared policy \u2014 this is a code-level change."
336
+ ) : payload.error ?? `Failed to save mutation policy (${status}).`;
337
+ setState({ kind: "error", message });
338
+ return;
339
+ }
340
+ setState({
341
+ kind: "success",
342
+ message: t(
343
+ "ai_assistant.agents.mutation_policy.savedMessage",
344
+ "Mutation policy override saved."
345
+ )
346
+ });
347
+ await queryClient.invalidateQueries({
348
+ queryKey: ["ai_assistant", "agent_settings", "mutation_policy", agent.id]
349
+ });
350
+ } catch (err) {
351
+ setState({
352
+ kind: "error",
353
+ message: err instanceof Error ? err.message : String(err)
354
+ });
355
+ } finally {
356
+ setIsSaving(false);
357
+ }
358
+ }, [agent.id, isSaving, queryClient, selected, t]);
359
+ const clear = React.useCallback(async () => {
360
+ if (isClearing) return;
361
+ setIsClearing(true);
362
+ setState({ kind: "idle" });
363
+ try {
364
+ const { ok, status, result } = await apiCall(
365
+ `/api/ai_assistant/ai/agents/${encodeURIComponent(agent.id)}/mutation-policy`,
366
+ {
367
+ method: "DELETE",
368
+ credentials: "include"
369
+ }
370
+ );
371
+ const payload = result ?? {};
372
+ if (!ok) {
373
+ setState({
374
+ kind: "error",
375
+ message: payload.error ?? `Failed to clear override (${status}).`
376
+ });
377
+ return;
378
+ }
379
+ setState({
380
+ kind: "success",
381
+ message: t(
382
+ "ai_assistant.agents.mutation_policy.clearedMessage",
383
+ "Mutation policy override cleared; agent is using its code-declared policy."
384
+ )
385
+ });
386
+ await queryClient.invalidateQueries({
387
+ queryKey: ["ai_assistant", "agent_settings", "mutation_policy", agent.id]
388
+ });
389
+ } catch (err) {
390
+ setState({
391
+ kind: "error",
392
+ message: err instanceof Error ? err.message : String(err)
393
+ });
394
+ } finally {
395
+ setIsClearing(false);
396
+ }
397
+ }, [agent.id, isClearing, queryClient, t]);
398
+ return /* @__PURE__ */ jsxs(
399
+ "section",
400
+ {
401
+ className: "rounded-lg border border-border bg-background p-4",
402
+ "data-ai-agent-mutation-policy": agent.id,
403
+ children: [
404
+ /* @__PURE__ */ jsxs("header", { className: "flex items-center justify-between gap-3 border-b border-border pb-3", children: [
405
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
406
+ /* @__PURE__ */ jsx(ShieldAlert, { className: "size-4 text-muted-foreground", "aria-hidden": true }),
407
+ /* @__PURE__ */ jsxs("div", { children: [
408
+ /* @__PURE__ */ jsx("h3", { className: "text-sm font-semibold", children: t("ai_assistant.agents.mutation_policy.title", "Mutation policy") }),
409
+ /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: t(
410
+ "ai_assistant.agents.mutation_policy.subtitle",
411
+ "Downgrade this agent's mutation capability per tenant. Upgrading beyond the code-declared policy is blocked by the server."
412
+ ) })
413
+ ] })
414
+ ] }),
415
+ /* @__PURE__ */ jsx(
416
+ StatusBadge,
417
+ {
418
+ variant: mutationPolicyStatusMap[effectivePolicy] ?? "neutral",
419
+ dot: true,
420
+ "data-ai-agent-mutation-policy-effective": true,
421
+ children: effectivePolicy
422
+ }
423
+ )
424
+ ] }),
425
+ /* @__PURE__ */ jsxs("div", { className: "mt-3 flex flex-col gap-3", children: [
426
+ /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-1 gap-3 sm:grid-cols-2", children: [
427
+ /* @__PURE__ */ jsxs("div", { children: [
428
+ /* @__PURE__ */ jsx("span", { className: "text-overline font-semibold uppercase tracking-wider text-muted-foreground", children: t("ai_assistant.agents.mutation_policy.codeDeclared", "Code-declared") }),
429
+ /* @__PURE__ */ jsxs("div", { className: "mt-1 flex items-center gap-2", children: [
430
+ /* @__PURE__ */ jsx(
431
+ StatusBadge,
432
+ {
433
+ variant: mutationPolicyStatusMap[codeDeclared] ?? "neutral",
434
+ dot: true,
435
+ "data-ai-agent-mutation-policy-code-declared": true,
436
+ children: codeDeclared
437
+ }
438
+ ),
439
+ /* @__PURE__ */ jsx(Lock, { className: "size-3 text-muted-foreground", "aria-hidden": true }),
440
+ /* @__PURE__ */ jsx("span", { className: "text-xs text-muted-foreground", children: t(
441
+ "ai_assistant.agents.mutation_policy.codeDeclaredHint",
442
+ "Compiled into the agent definition."
443
+ ) })
444
+ ] })
445
+ ] }),
446
+ /* @__PURE__ */ jsxs("div", { children: [
447
+ /* @__PURE__ */ jsx("span", { className: "text-overline font-semibold uppercase tracking-wider text-muted-foreground", children: t("ai_assistant.agents.mutation_policy.tenantOverride", "Tenant override") }),
448
+ /* @__PURE__ */ jsx("div", { className: "mt-1", children: currentOverride ? /* @__PURE__ */ jsx(
449
+ StatusBadge,
450
+ {
451
+ variant: mutationPolicyStatusMap[currentOverride.mutationPolicy] ?? "neutral",
452
+ dot: true,
453
+ "data-ai-agent-mutation-policy-override-current": true,
454
+ children: currentOverride.mutationPolicy
455
+ }
456
+ ) : /* @__PURE__ */ jsx(
457
+ "span",
458
+ {
459
+ className: "text-xs text-muted-foreground",
460
+ "data-ai-agent-mutation-policy-override-empty": true,
461
+ children: t(
462
+ "ai_assistant.agents.mutation_policy.noOverride",
463
+ "No override \u2014 using code-declared policy."
464
+ )
465
+ }
466
+ ) })
467
+ ] })
468
+ ] }),
469
+ /* @__PURE__ */ jsxs(Alert, { variant: "info", "data-ai-agent-mutation-policy-notice": true, children: [
470
+ /* @__PURE__ */ jsx(ShieldAlert, { className: "size-4", "aria-hidden": true }),
471
+ /* @__PURE__ */ jsx(AlertTitle, { children: t(
472
+ "ai_assistant.agents.mutation_policy.noticeTitle",
473
+ "Downgrade only \u2014 escalation is a code change"
474
+ ) }),
475
+ /* @__PURE__ */ jsx(AlertDescription, { children: t(
476
+ "ai_assistant.agents.mutation_policy.noticeBody",
477
+ "Overrides can only make the policy more restrictive. Options more permissive than the code-declared policy are disabled and rejected server-side."
478
+ ) })
479
+ ] }),
480
+ query.isLoading ? /* @__PURE__ */ jsx(
481
+ SettingsLoading,
482
+ {
483
+ message: t(
484
+ "ai_assistant.agents.mutation_policy.loading",
485
+ "Loading mutation policy..."
486
+ )
487
+ }
488
+ ) : query.isError ? /* @__PURE__ */ jsxs(Alert, { variant: "destructive", "data-ai-agent-mutation-policy-load-error": true, children: [
489
+ /* @__PURE__ */ jsx(AlertCircle, { className: "size-4", "aria-hidden": true }),
490
+ /* @__PURE__ */ jsx(AlertTitle, { children: t(
491
+ "ai_assistant.agents.mutation_policy.loadErrorTitle",
492
+ "Failed to load mutation policy"
493
+ ) }),
494
+ /* @__PURE__ */ jsx(AlertDescription, { children: query.error instanceof Error ? query.error.message : String(query.error) })
495
+ ] }) : /* @__PURE__ */ jsx(
496
+ "div",
497
+ {
498
+ className: "flex flex-col gap-2",
499
+ role: "radiogroup",
500
+ "aria-label": t(
501
+ "ai_assistant.agents.mutation_policy.pickerLabel",
502
+ "Mutation policy override"
503
+ ),
504
+ "data-ai-agent-mutation-policy-picker": true,
505
+ children: MUTATION_POLICY_OPTIONS.map((option) => {
506
+ const optionRank = POLICY_RESTRICTIVENESS_UI[option];
507
+ const wouldEscalate = optionRank > codeRank;
508
+ const isSelected = selected === option;
509
+ return /* @__PURE__ */ jsxs(Tooltip, { children: [
510
+ /* @__PURE__ */ jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsxs(
511
+ "label",
512
+ {
513
+ className: `flex items-start gap-3 rounded-md border px-3 py-2 text-sm cursor-pointer transition-colors ${wouldEscalate ? "border-border bg-muted/30 cursor-not-allowed opacity-60" : isSelected ? "border-primary bg-primary/5" : "border-border bg-background hover:bg-muted/40"}`,
514
+ "data-ai-agent-mutation-policy-option": option,
515
+ "data-ai-agent-mutation-policy-option-disabled": wouldEscalate ? "true" : "false",
516
+ children: [
517
+ /* @__PURE__ */ jsx(
518
+ "input",
519
+ {
520
+ type: "radio",
521
+ name: `mutation-policy-${agent.id}`,
522
+ value: option,
523
+ checked: isSelected,
524
+ disabled: wouldEscalate,
525
+ onChange: () => {
526
+ if (wouldEscalate) return;
527
+ setSelected(option);
528
+ },
529
+ className: "mt-0.5",
530
+ "aria-disabled": wouldEscalate
531
+ }
532
+ ),
533
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-0.5 min-w-0", children: [
534
+ /* @__PURE__ */ jsxs("span", { className: "flex items-center gap-2", children: [
535
+ /* @__PURE__ */ jsx(
536
+ StatusBadge,
537
+ {
538
+ variant: mutationPolicyStatusMap[option] ?? "neutral",
539
+ dot: true,
540
+ children: option
541
+ }
542
+ ),
543
+ wouldEscalate ? /* @__PURE__ */ jsx(Lock, { className: "size-3 text-muted-foreground", "aria-hidden": true }) : null
544
+ ] }),
545
+ /* @__PURE__ */ jsx("span", { className: "text-xs text-muted-foreground", children: t(
546
+ `ai_assistant.agents.mutation_policy.options.${option}`,
547
+ option
548
+ ) })
549
+ ] })
550
+ ]
551
+ }
552
+ ) }),
553
+ wouldEscalate ? /* @__PURE__ */ jsx(TooltipContent, { children: t(
554
+ "ai_assistant.agents.mutation_policy.escalationTooltip",
555
+ "Cannot be set above the agent's declared policy \u2014 this is a code-level change."
556
+ ) }) : null
557
+ ] }, option);
558
+ })
559
+ }
560
+ ),
561
+ state.kind === "success" ? /* @__PURE__ */ jsxs(Alert, { variant: "success", "data-ai-agent-mutation-policy-state": "success", children: [
562
+ /* @__PURE__ */ jsx(CheckCircle2, { className: "size-4", "aria-hidden": true }),
563
+ /* @__PURE__ */ jsx(AlertTitle, { children: t("ai_assistant.agents.mutation_policy.savedTitle", "Mutation policy updated") }),
564
+ /* @__PURE__ */ jsx(AlertDescription, { children: state.message })
565
+ ] }) : null,
566
+ state.kind === "error" ? /* @__PURE__ */ jsxs(Alert, { variant: "destructive", "data-ai-agent-mutation-policy-state": "error", children: [
567
+ /* @__PURE__ */ jsx(AlertCircle, { className: "size-4", "aria-hidden": true }),
568
+ /* @__PURE__ */ jsx(AlertTitle, { children: t(
569
+ "ai_assistant.agents.mutation_policy.errorTitle",
570
+ "Failed to update mutation policy"
571
+ ) }),
572
+ /* @__PURE__ */ jsx(AlertDescription, { children: state.message })
573
+ ] }) : null,
574
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-end gap-2", children: [
575
+ /* @__PURE__ */ jsxs(
576
+ Button,
577
+ {
578
+ type: "button",
579
+ size: "sm",
580
+ variant: "outline",
581
+ onClick: () => void clear(),
582
+ disabled: isClearing || isSaving || !currentOverride,
583
+ "data-ai-agent-mutation-policy-clear": true,
584
+ children: [
585
+ isClearing ? /* @__PURE__ */ jsx(Loader2, { className: "size-4 animate-spin", "aria-hidden": true }) : /* @__PURE__ */ jsx(Trash2, { className: "size-4", "aria-hidden": true }),
586
+ /* @__PURE__ */ jsx("span", { children: t("ai_assistant.agents.mutation_policy.clear", "Clear override") })
587
+ ]
588
+ }
589
+ ),
590
+ /* @__PURE__ */ jsxs(
591
+ Button,
592
+ {
593
+ type: "button",
594
+ size: "sm",
595
+ onClick: () => void save(),
596
+ disabled: isSaving || isClearing || !hasChange || selected === null,
597
+ "data-ai-agent-mutation-policy-save": true,
598
+ children: [
599
+ isSaving ? /* @__PURE__ */ jsx(Loader2, { className: "size-4 animate-spin", "aria-hidden": true }) : /* @__PURE__ */ jsx(Save, { className: "size-4", "aria-hidden": true }),
600
+ /* @__PURE__ */ jsx("span", { children: t("ai_assistant.agents.mutation_policy.save", "Save override") })
601
+ ]
602
+ }
603
+ )
604
+ ] })
605
+ ] })
606
+ ]
607
+ }
608
+ );
609
+ }
610
+ function AgentDetailPanel({ agent }) {
611
+ const t = useT();
612
+ const queryClient = useQueryClient();
613
+ const [overrideFlags, setOverrideFlags] = React.useState(() => ({
614
+ role: false,
615
+ scope: false,
616
+ data: false,
617
+ tools: false,
618
+ attachments: false,
619
+ mutationPolicy: false,
620
+ responseStyle: false,
621
+ overrides: false
622
+ }));
623
+ const [overrideDrafts, setOverrideDrafts] = React.useState(() => ({
624
+ role: "",
625
+ scope: "",
626
+ data: "",
627
+ tools: "",
628
+ attachments: "",
629
+ mutationPolicy: "",
630
+ responseStyle: "",
631
+ overrides: ""
632
+ }));
633
+ const [isSaving, setIsSaving] = React.useState(false);
634
+ const [saveState, setSaveState] = React.useState({ kind: "idle" });
635
+ const overrideQuery = useQuery({
636
+ queryKey: ["ai_assistant", "agent_settings", "override", agent.id],
637
+ queryFn: () => fetchOverride(agent.id),
638
+ retry: false
639
+ });
640
+ React.useEffect(() => {
641
+ setOverrideFlags({
642
+ role: false,
643
+ scope: false,
644
+ data: false,
645
+ tools: false,
646
+ attachments: false,
647
+ mutationPolicy: false,
648
+ responseStyle: false,
649
+ overrides: false
650
+ });
651
+ setOverrideDrafts({
652
+ role: "",
653
+ scope: "",
654
+ data: "",
655
+ tools: "",
656
+ attachments: "",
657
+ mutationPolicy: "",
658
+ responseStyle: "",
659
+ overrides: ""
660
+ });
661
+ setSaveState({ kind: "idle" });
662
+ }, [agent.id]);
663
+ React.useEffect(() => {
664
+ const latest = overrideQuery.data?.override;
665
+ if (!latest) return;
666
+ const nextFlags = {
667
+ role: false,
668
+ scope: false,
669
+ data: false,
670
+ tools: false,
671
+ attachments: false,
672
+ mutationPolicy: false,
673
+ responseStyle: false,
674
+ overrides: false
675
+ };
676
+ const nextDrafts = {
677
+ role: "",
678
+ scope: "",
679
+ data: "",
680
+ tools: "",
681
+ attachments: "",
682
+ mutationPolicy: "",
683
+ responseStyle: "",
684
+ overrides: ""
685
+ };
686
+ for (const [rawKey, value] of Object.entries(latest.sections ?? {})) {
687
+ if (typeof value !== "string") continue;
688
+ const key = rawKey;
689
+ if (PROMPT_SECTION_IDS.includes(key)) {
690
+ nextFlags[key] = true;
691
+ nextDrafts[key] = value;
692
+ }
693
+ }
694
+ setOverrideFlags(nextFlags);
695
+ setOverrideDrafts(nextDrafts);
696
+ }, [overrideQuery.data?.override]);
697
+ const activeOverrides = React.useMemo(() => {
698
+ const payload = {};
699
+ for (const section of PROMPT_SECTION_IDS) {
700
+ if (overrideFlags[section]) {
701
+ payload[section] = overrideDrafts[section];
702
+ }
703
+ }
704
+ return payload;
705
+ }, [overrideDrafts, overrideFlags]);
706
+ const hasAnyOverride = Object.keys(activeOverrides).length > 0;
707
+ const handleSave = React.useCallback(async () => {
708
+ if (isSaving) return;
709
+ setIsSaving(true);
710
+ setSaveState({ kind: "idle" });
711
+ try {
712
+ const { ok, status, result } = await apiCall(
713
+ `/api/ai_assistant/ai/agents/${encodeURIComponent(agent.id)}/prompt-override`,
714
+ {
715
+ method: "POST",
716
+ headers: { "content-type": "application/json" },
717
+ credentials: "include",
718
+ body: JSON.stringify({
719
+ // Send both keys so a pre-Step-5.3 server still accepts the payload.
720
+ sections: activeOverrides,
721
+ overrides: activeOverrides
722
+ })
723
+ }
724
+ );
725
+ const payload = result ?? {};
726
+ if (!ok) {
727
+ const message = payload.code === "reserved_key" ? t(
728
+ "ai_assistant.agents.override.errors.reservedKey",
729
+ "Prompt overrides cannot modify policy fields (mutationPolicy, readOnly, allowedTools, acceptedMediaTypes). Remove those sections and retry."
730
+ ) : payload.error ?? `Failed to save overrides (${status}).`;
731
+ setSaveState({ kind: "error", message });
732
+ return;
733
+ }
734
+ if (payload.ok === true && typeof payload.version === "number") {
735
+ setSaveState({
736
+ kind: "success",
737
+ version: payload.version,
738
+ message: t(
739
+ "ai_assistant.agents.override.savedMessage",
740
+ "Prompt override saved."
741
+ )
742
+ });
743
+ await queryClient.invalidateQueries({
744
+ queryKey: ["ai_assistant", "agent_settings", "override", agent.id]
745
+ });
746
+ return;
747
+ }
748
+ setSaveState({
749
+ kind: "success",
750
+ version: 0,
751
+ message: payload.message ?? t(
752
+ "ai_assistant.agents.prompt.pendingMessage",
753
+ "Prompt overrides accepted."
754
+ )
755
+ });
756
+ } catch (err) {
757
+ setSaveState({
758
+ kind: "error",
759
+ message: err instanceof Error ? err.message : String(err)
760
+ });
761
+ } finally {
762
+ setIsSaving(false);
763
+ }
764
+ }, [activeOverrides, agent.id, isSaving, queryClient, t]);
765
+ return /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-4", "data-ai-agent-detail": agent.id, children: [
766
+ /* @__PURE__ */ jsxs("section", { className: "rounded-lg border border-border bg-background p-4", children: [
767
+ /* @__PURE__ */ jsx("h2", { className: "text-xl font-semibold", children: agent.label }),
768
+ /* @__PURE__ */ jsx("p", { className: "mt-1 text-sm text-muted-foreground", children: agent.description }),
769
+ /* @__PURE__ */ jsxs("dl", { className: "mt-4 grid grid-cols-1 gap-3 sm:grid-cols-2 lg:grid-cols-4 text-sm", children: [
770
+ /* @__PURE__ */ jsxs("div", { children: [
771
+ /* @__PURE__ */ jsx("dt", { className: "text-overline font-semibold uppercase tracking-wider text-muted-foreground", children: t("ai_assistant.agents.meta.module", "Module") }),
772
+ /* @__PURE__ */ jsx("dd", { className: "mt-1 font-mono text-xs", children: agent.moduleId })
773
+ ] }),
774
+ /* @__PURE__ */ jsxs("div", { children: [
775
+ /* @__PURE__ */ jsx("dt", { className: "text-overline font-semibold uppercase tracking-wider text-muted-foreground", children: t("ai_assistant.agents.meta.id", "Agent id") }),
776
+ /* @__PURE__ */ jsx("dd", { className: "mt-1 font-mono text-xs", children: agent.id })
777
+ ] }),
778
+ /* @__PURE__ */ jsxs("div", { children: [
779
+ /* @__PURE__ */ jsx("dt", { className: "text-overline font-semibold uppercase tracking-wider text-muted-foreground", children: t("ai_assistant.agents.meta.executionMode", "Execution mode") }),
780
+ /* @__PURE__ */ jsx("dd", { className: "mt-1", children: /* @__PURE__ */ jsx(StatusBadge, { variant: executionModeStatusMap[agent.executionMode], children: agent.executionMode }) })
781
+ ] }),
782
+ /* @__PURE__ */ jsxs("div", { children: [
783
+ /* @__PURE__ */ jsx("dt", { className: "text-overline font-semibold uppercase tracking-wider text-muted-foreground", children: t("ai_assistant.agents.meta.mutationPolicy", "Mutation policy") }),
784
+ /* @__PURE__ */ jsx("dd", { className: "mt-1", children: /* @__PURE__ */ jsx(
785
+ StatusBadge,
786
+ {
787
+ variant: mutationPolicyStatusMap[agent.mutationPolicy] ?? "neutral",
788
+ dot: true,
789
+ children: agent.mutationPolicy
790
+ }
791
+ ) })
792
+ ] }),
793
+ /* @__PURE__ */ jsxs("div", { children: [
794
+ /* @__PURE__ */ jsx("dt", { className: "text-overline font-semibold uppercase tracking-wider text-muted-foreground", children: t("ai_assistant.agents.meta.readOnly", "Read-only") }),
795
+ /* @__PURE__ */ jsx("dd", { className: "mt-1 text-xs", children: agent.readOnly ? t("ai_assistant.agents.meta.readOnlyYes", "Yes") : t("ai_assistant.agents.meta.readOnlyNo", "No") })
796
+ ] }),
797
+ /* @__PURE__ */ jsxs("div", { children: [
798
+ /* @__PURE__ */ jsx("dt", { className: "text-overline font-semibold uppercase tracking-wider text-muted-foreground", children: t("ai_assistant.agents.meta.maxSteps", "Max steps") }),
799
+ /* @__PURE__ */ jsx("dd", { className: "mt-1 font-mono text-xs", children: agent.maxSteps ?? t("ai_assistant.agents.meta.unlimited", "Unlimited") })
800
+ ] })
801
+ ] })
802
+ ] }),
803
+ /* @__PURE__ */ jsx(MutationPolicySection, { agent }),
804
+ /* @__PURE__ */ jsxs(
805
+ "section",
806
+ {
807
+ className: "rounded-lg border border-border bg-background p-4",
808
+ "data-ai-agent-prompt-editor": agent.id,
809
+ children: [
810
+ /* @__PURE__ */ jsxs("header", { className: "flex items-center justify-between gap-3 border-b border-border pb-3", children: [
811
+ /* @__PURE__ */ jsxs("div", { children: [
812
+ /* @__PURE__ */ jsx("h3", { className: "text-sm font-semibold", children: t("ai_assistant.agents.prompt.title", "Prompt sections") }),
813
+ /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: t(
814
+ "ai_assistant.agents.prompt.subtitle",
815
+ "Toggle any section to write an additive override. Saving stores a new tenant-scoped version; built-in section text is always preserved."
816
+ ) })
817
+ ] }),
818
+ /* @__PURE__ */ jsxs(
819
+ Button,
820
+ {
821
+ type: "button",
822
+ size: "sm",
823
+ onClick: () => void handleSave(),
824
+ disabled: isSaving || !hasAnyOverride,
825
+ "data-ai-agent-prompt-save": true,
826
+ children: [
827
+ isSaving ? /* @__PURE__ */ jsx(Loader2, { className: "size-4 animate-spin", "aria-hidden": true }) : /* @__PURE__ */ jsx(Save, { className: "size-4", "aria-hidden": true }),
828
+ /* @__PURE__ */ jsx("span", { children: t("ai_assistant.agents.prompt.save", "Save overrides") })
829
+ ]
830
+ }
831
+ )
832
+ ] }),
833
+ /* @__PURE__ */ jsx("div", { className: "mt-3", children: /* @__PURE__ */ jsxs(Alert, { variant: "info", "data-ai-agent-prompt-notice": true, children: [
834
+ /* @__PURE__ */ jsx(Wand2, { className: "size-4", "aria-hidden": true }),
835
+ /* @__PURE__ */ jsx(AlertTitle, { children: t("ai_assistant.agents.override.noticeTitle", "Prompt overrides are additive") }),
836
+ /* @__PURE__ */ jsx(AlertDescription, { children: t(
837
+ "ai_assistant.agents.override.noticeBody",
838
+ "Overrides append to the built-in sections \u2014 they never remove or replace shipped text. Saved versions are tenant-scoped and auditable from the history panel below."
839
+ ) })
840
+ ] }) }),
841
+ saveState.kind === "success" ? /* @__PURE__ */ jsxs(Alert, { variant: "success", className: "mt-3", "data-ai-agent-prompt-state": "success", children: [
842
+ /* @__PURE__ */ jsx(CheckCircle2, { className: "size-4", "aria-hidden": true }),
843
+ /* @__PURE__ */ jsx(AlertTitle, { children: saveState.version > 0 ? t("ai_assistant.agents.override.savedTitle", "Prompt override saved") : t("ai_assistant.agents.prompt.pendingTitle", "Overrides accepted") }),
844
+ /* @__PURE__ */ jsx(AlertDescription, { children: saveState.version > 0 ? `${saveState.message} ${t("ai_assistant.agents.override.versionLabel", "Version")} ${saveState.version}.` : saveState.message })
845
+ ] }) : null,
846
+ saveState.kind === "error" ? /* @__PURE__ */ jsxs(Alert, { variant: "destructive", className: "mt-3", "data-ai-agent-prompt-state": "error", children: [
847
+ /* @__PURE__ */ jsx(AlertCircle, { className: "size-4", "aria-hidden": true }),
848
+ /* @__PURE__ */ jsx(AlertTitle, { children: t("ai_assistant.agents.override.errorTitle", "Failed to save prompt override") }),
849
+ /* @__PURE__ */ jsx(AlertDescription, { children: saveState.message })
850
+ ] }) : null,
851
+ /* @__PURE__ */ jsxs("div", { className: "mt-3 flex flex-col gap-3", children: [
852
+ /* @__PURE__ */ jsxs("div", { children: [
853
+ /* @__PURE__ */ jsx("span", { className: "text-overline font-semibold uppercase tracking-wider text-muted-foreground", children: t("ai_assistant.agents.prompt.fullSystemPromptLabel", "Full system prompt (default)") }),
854
+ /* @__PURE__ */ jsx("pre", { className: "mt-2 max-h-40 overflow-auto rounded border border-border bg-muted/30 p-2 text-xs font-mono whitespace-pre-wrap", children: agent.systemPrompt })
855
+ ] }),
856
+ PROMPT_SECTION_IDS.map((sectionId) => /* @__PURE__ */ jsx(
857
+ PromptSectionEditor,
858
+ {
859
+ sectionId,
860
+ defaultText: sectionId === "role" ? agent.systemPrompt : t(
861
+ "ai_assistant.agents.prompt.defaultSectionPlaceholder",
862
+ "No default copy declared for this section \u2014 the agent ships a single systemPrompt. Override to inject additional text once Step 5.3 lands."
863
+ ),
864
+ overrideText: overrideDrafts[sectionId],
865
+ override: overrideFlags[sectionId],
866
+ onToggleOverride: (next) => {
867
+ setOverrideFlags((prev) => ({ ...prev, [sectionId]: next }));
868
+ if (next) {
869
+ setOverrideDrafts((prev) => {
870
+ if (prev[sectionId]) return prev;
871
+ const defaultText = sectionId === "role" ? agent.systemPrompt : "";
872
+ return { ...prev, [sectionId]: defaultText };
873
+ });
874
+ }
875
+ },
876
+ onOverrideChange: (next) => setOverrideDrafts((prev) => ({ ...prev, [sectionId]: next })),
877
+ onSaveShortcut: () => void handleSave()
878
+ },
879
+ sectionId
880
+ ))
881
+ ] })
882
+ ]
883
+ }
884
+ ),
885
+ /* @__PURE__ */ jsxs(
886
+ "section",
887
+ {
888
+ className: "rounded-lg border border-border bg-background p-4",
889
+ "data-ai-agent-tools-list": agent.id,
890
+ children: [
891
+ /* @__PURE__ */ jsxs("header", { className: "flex items-center justify-between gap-3 border-b border-border pb-3", children: [
892
+ /* @__PURE__ */ jsxs("div", { children: [
893
+ /* @__PURE__ */ jsx("h3", { className: "text-sm font-semibold", children: t("ai_assistant.agents.tools.title", "Allowed tools") }),
894
+ /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: t(
895
+ "ai_assistant.agents.tools.subtitle",
896
+ "Read-only surface in Phase 2. Editing the per-tool toggle and mutation policy lands in Step 5.4 / Phase 3."
897
+ ) })
898
+ ] }),
899
+ /* @__PURE__ */ jsx(Badge, { variant: "neutral", className: "font-mono text-xs", children: agent.tools.length })
900
+ ] }),
901
+ /* @__PURE__ */ jsx("div", { className: "mt-3 flex flex-col gap-2", children: agent.tools.length === 0 ? /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: t(
902
+ "ai_assistant.agents.tools.emptyBody",
903
+ "This agent declares no tools in its allowedTools whitelist."
904
+ ) }) : agent.tools.map((tool) => /* @__PURE__ */ jsx(ToolRow, { tool }, tool.name)) })
905
+ ]
906
+ }
907
+ ),
908
+ /* @__PURE__ */ jsxs(
909
+ "section",
910
+ {
911
+ className: "rounded-lg border border-border bg-background p-4",
912
+ "data-ai-agent-override-history": agent.id,
913
+ children: [
914
+ /* @__PURE__ */ jsxs("header", { className: "flex items-center justify-between gap-3 border-b border-border pb-3", children: [
915
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
916
+ /* @__PURE__ */ jsx(History, { className: "size-4 text-muted-foreground", "aria-hidden": true }),
917
+ /* @__PURE__ */ jsxs("div", { children: [
918
+ /* @__PURE__ */ jsx("h3", { className: "text-sm font-semibold", children: t("ai_assistant.agents.override.history.title", "Prompt override history") }),
919
+ /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: t(
920
+ "ai_assistant.agents.override.history.subtitle",
921
+ "Newest first. Each save creates a new version scoped to the current tenant."
922
+ ) })
923
+ ] })
924
+ ] }),
925
+ overrideQuery.data ? /* @__PURE__ */ jsx(Badge, { variant: "neutral", className: "font-mono text-xs", children: overrideQuery.data.versions.length }) : null
926
+ ] }),
927
+ /* @__PURE__ */ jsx("div", { className: "mt-3 flex flex-col gap-2", children: overrideQuery.isLoading ? /* @__PURE__ */ jsx(
928
+ SettingsLoading,
929
+ {
930
+ message: t(
931
+ "ai_assistant.agents.override.history.loading",
932
+ "Loading override history..."
933
+ )
934
+ }
935
+ ) : overrideQuery.isError ? /* @__PURE__ */ jsxs(Alert, { variant: "destructive", "data-ai-agent-override-history-error": true, children: [
936
+ /* @__PURE__ */ jsx(AlertCircle, { className: "size-4", "aria-hidden": true }),
937
+ /* @__PURE__ */ jsx(AlertTitle, { children: t(
938
+ "ai_assistant.agents.override.history.errorTitle",
939
+ "Failed to load override history"
940
+ ) }),
941
+ /* @__PURE__ */ jsx(AlertDescription, { children: overrideQuery.error instanceof Error ? overrideQuery.error.message : String(overrideQuery.error) })
942
+ ] }) : (overrideQuery.data?.versions ?? []).length === 0 ? /* @__PURE__ */ jsx(
943
+ "p",
944
+ {
945
+ className: "text-xs text-muted-foreground",
946
+ "data-ai-agent-override-history-empty": true,
947
+ children: t(
948
+ "ai_assistant.agents.override.history.empty",
949
+ "No prompt overrides have been saved for this agent yet."
950
+ )
951
+ }
952
+ ) : (overrideQuery.data?.versions ?? []).slice(0, 5).map((entry) => /* @__PURE__ */ jsxs(
953
+ "div",
954
+ {
955
+ className: "flex items-center justify-between gap-3 rounded-md border border-border bg-background px-3 py-2",
956
+ "data-ai-agent-override-history-row": entry.version,
957
+ children: [
958
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-col min-w-0", children: [
959
+ /* @__PURE__ */ jsxs("span", { className: "text-xs font-semibold", children: [
960
+ t("ai_assistant.agents.override.versionLabel", "Version"),
961
+ " ",
962
+ entry.version
963
+ ] }),
964
+ /* @__PURE__ */ jsx("span", { className: "truncate text-xs text-muted-foreground", children: new Date(entry.updatedAt).toLocaleString() })
965
+ ] }),
966
+ /* @__PURE__ */ jsxs(Badge, { variant: "neutral", className: "font-mono text-xs", children: [
967
+ Object.keys(entry.sections ?? {}).length,
968
+ " ",
969
+ t("ai_assistant.agents.override.history.sectionsLabel", "sections")
970
+ ] })
971
+ ]
972
+ },
973
+ entry.id
974
+ )) })
975
+ ]
976
+ }
977
+ ),
978
+ /* @__PURE__ */ jsxs(
979
+ "section",
980
+ {
981
+ className: "rounded-lg border border-border bg-background p-4",
982
+ "data-ai-agent-attachments": agent.id,
983
+ children: [
984
+ /* @__PURE__ */ jsx("header", { className: "flex items-center justify-between gap-3 border-b border-border pb-3", children: /* @__PURE__ */ jsxs("div", { children: [
985
+ /* @__PURE__ */ jsx("h3", { className: "text-sm font-semibold", children: t("ai_assistant.agents.attachments.title", "Attachment policy") }),
986
+ /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: t(
987
+ "ai_assistant.agents.attachments.subtitle",
988
+ "Accepted media types the agent declares. Read-only in Phase 2."
989
+ ) })
990
+ ] }) }),
991
+ /* @__PURE__ */ jsx("div", { className: "mt-3", children: /* @__PURE__ */ jsx(AttachmentPolicyBadges, { mediaTypes: agent.acceptedMediaTypes }) })
992
+ ]
993
+ }
994
+ )
995
+ ] });
996
+ }
997
+ function AiAgentSettingsPageClient() {
998
+ const t = useT();
999
+ const [selectedAgentId, setSelectedAgentId] = React.useState(null);
1000
+ const { data, isLoading, isError, error, refetch, isFetching } = useQuery({
1001
+ queryKey: ["ai_assistant", "agent_settings", "agents"],
1002
+ queryFn: fetchAgents
1003
+ });
1004
+ const agents = React.useMemo(() => data?.agents ?? [], [data]);
1005
+ React.useEffect(() => {
1006
+ if (!agents.length) {
1007
+ if (selectedAgentId !== null) setSelectedAgentId(null);
1008
+ return;
1009
+ }
1010
+ if (!selectedAgentId || !agents.some((agent) => agent.id === selectedAgentId)) {
1011
+ setSelectedAgentId(agents[0].id);
1012
+ }
1013
+ }, [agents, selectedAgentId]);
1014
+ const selectedAgent = React.useMemo(() => {
1015
+ if (!selectedAgentId) return null;
1016
+ return agents.find((agent) => agent.id === selectedAgentId) ?? null;
1017
+ }, [agents, selectedAgentId]);
1018
+ if (isLoading) {
1019
+ return /* @__PURE__ */ jsx(
1020
+ SettingsLoading,
1021
+ {
1022
+ message: t("ai_assistant.agents.loadingAgents", "Loading AI agents...")
1023
+ }
1024
+ );
1025
+ }
1026
+ if (isError) {
1027
+ return /* @__PURE__ */ jsxs(Alert, { variant: "destructive", "data-ai-agent-settings-error": true, children: [
1028
+ /* @__PURE__ */ jsx(AlertCircle, { className: "size-4", "aria-hidden": true }),
1029
+ /* @__PURE__ */ jsx(AlertTitle, { children: t("ai_assistant.agents.loadErrorTitle", "Failed to load AI agents") }),
1030
+ /* @__PURE__ */ jsxs(AlertDescription, { children: [
1031
+ /* @__PURE__ */ jsx("span", { children: error instanceof Error ? error.message : String(error) }),
1032
+ /* @__PURE__ */ jsx("div", { className: "mt-2", children: /* @__PURE__ */ jsxs(
1033
+ Button,
1034
+ {
1035
+ type: "button",
1036
+ size: "sm",
1037
+ variant: "outline",
1038
+ onClick: () => {
1039
+ void refetch();
1040
+ },
1041
+ children: [
1042
+ /* @__PURE__ */ jsx(RefreshCcw, { className: "size-4", "aria-hidden": true }),
1043
+ /* @__PURE__ */ jsx("span", { children: t("ai_assistant.agents.retry", "Retry") })
1044
+ ]
1045
+ }
1046
+ ) })
1047
+ ] })
1048
+ ] });
1049
+ }
1050
+ if (!agents.length) {
1051
+ return /* @__PURE__ */ jsx(EmptyAgents, {});
1052
+ }
1053
+ return /* @__PURE__ */ jsx(TooltipProvider, { delayDuration: 200, children: /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-4", "data-ai-agent-settings": true, children: [
1054
+ /* @__PURE__ */ jsxs("header", { className: "flex flex-col gap-1", children: [
1055
+ /* @__PURE__ */ jsx("h1", { className: "text-2xl font-bold tracking-tight", children: t("ai_assistant.agents.title", "AI Agents") }),
1056
+ /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: t(
1057
+ "ai_assistant.agents.subtitle",
1058
+ "Inspect every registered agent and manage tenant-scoped additive prompt-section overrides."
1059
+ ) })
1060
+ ] }),
1061
+ /* @__PURE__ */ jsx(
1062
+ "section",
1063
+ {
1064
+ className: "flex flex-col gap-3 rounded-lg border border-border bg-background p-3",
1065
+ "data-ai-agent-settings-picker-wrap": true,
1066
+ children: /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-3 sm:flex-row sm:items-end sm:justify-between", children: [
1067
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-2 sm:flex-1", children: [
1068
+ /* @__PURE__ */ jsx(Label, { htmlFor: "ai-agent-settings-picker", children: t("ai_assistant.agents.agentPickerLabel", "Agent") }),
1069
+ /* @__PURE__ */ jsx(
1070
+ "select",
1071
+ {
1072
+ id: "ai-agent-settings-picker",
1073
+ "data-ai-agent-settings-picker": true,
1074
+ className: "h-9 rounded-md border border-input bg-background px-3 text-sm",
1075
+ value: selectedAgentId ?? "",
1076
+ onChange: (event) => setSelectedAgentId(event.target.value),
1077
+ children: agents.map((agent) => /* @__PURE__ */ jsxs("option", { value: agent.id, children: [
1078
+ agent.label,
1079
+ " (",
1080
+ agent.id,
1081
+ ")"
1082
+ ] }, agent.id))
1083
+ }
1084
+ )
1085
+ ] }),
1086
+ /* @__PURE__ */ jsx("div", { className: "flex items-center gap-2 sm:flex-shrink-0", children: /* @__PURE__ */ jsx(
1087
+ IconButton,
1088
+ {
1089
+ type: "button",
1090
+ variant: "ghost",
1091
+ size: "sm",
1092
+ onClick: () => {
1093
+ void refetch();
1094
+ },
1095
+ "aria-label": t("ai_assistant.agents.refresh", "Refresh agents"),
1096
+ disabled: isFetching,
1097
+ children: /* @__PURE__ */ jsx(RefreshCcw, { className: "size-4", "aria-hidden": true })
1098
+ }
1099
+ ) })
1100
+ ] })
1101
+ }
1102
+ ),
1103
+ selectedAgent ? /* @__PURE__ */ jsx(AgentDetailPanel, { agent: selectedAgent }) : null
1104
+ ] }) });
1105
+ }
1106
+ var AiAgentSettingsPageClient_default = AiAgentSettingsPageClient;
1107
+ export {
1108
+ AiAgentSettingsPageClient,
1109
+ AiAgentSettingsPageClient_default as default
1110
+ };
1111
+ //# sourceMappingURL=AiAgentSettingsPageClient.js.map