@open-mercato/ai-assistant 0.6.1-develop.3291.1.6fad645fd0 → 0.6.1

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 (135) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/AGENTS.md +30 -4
  3. package/dist/frontend/components/AiChatButton.js +3 -2
  4. package/dist/frontend/components/AiChatButton.js.map +2 -2
  5. package/dist/modules/ai_assistant/__integration__/TC-AI-AGENT-LOOP-001-006.spec.js +364 -0
  6. package/dist/modules/ai_assistant/__integration__/TC-AI-AGENT-LOOP-001-006.spec.js.map +7 -0
  7. package/dist/modules/ai_assistant/__integration__/TC-AI-RUNTIME-OVERRIDES-006-model-picker.spec.js +7 -7
  8. package/dist/modules/ai_assistant/__integration__/TC-AI-RUNTIME-OVERRIDES-006-model-picker.spec.js.map +2 -2
  9. package/dist/modules/ai_assistant/__integration__/TC-AI-TOKEN-USAGE-001-005.spec.js +182 -0
  10. package/dist/modules/ai_assistant/__integration__/TC-AI-TOKEN-USAGE-001-005.spec.js.map +7 -0
  11. package/dist/modules/ai_assistant/api/ai/agents/[agentId]/loop-override/route.js +316 -0
  12. package/dist/modules/ai_assistant/api/ai/agents/[agentId]/loop-override/route.js.map +7 -0
  13. package/dist/modules/ai_assistant/api/ai/agents/[agentId]/models/route.js +8 -7
  14. package/dist/modules/ai_assistant/api/ai/agents/[agentId]/models/route.js.map +2 -2
  15. package/dist/modules/ai_assistant/api/ai/chat/route.js +43 -20
  16. package/dist/modules/ai_assistant/api/ai/chat/route.js.map +2 -2
  17. package/dist/modules/ai_assistant/api/settings/route.js +4 -3
  18. package/dist/modules/ai_assistant/api/settings/route.js.map +2 -2
  19. package/dist/modules/ai_assistant/api/usage/daily/route.js +111 -0
  20. package/dist/modules/ai_assistant/api/usage/daily/route.js.map +7 -0
  21. package/dist/modules/ai_assistant/api/usage/sessions/[sessionId]/route.js +108 -0
  22. package/dist/modules/ai_assistant/api/usage/sessions/[sessionId]/route.js.map +7 -0
  23. package/dist/modules/ai_assistant/api/usage/sessions/route.js +153 -0
  24. package/dist/modules/ai_assistant/api/usage/sessions/route.js.map +7 -0
  25. package/dist/modules/ai_assistant/backend/config/ai-assistant/agents/AiAgentSettingsPageClient.js +335 -38
  26. package/dist/modules/ai_assistant/backend/config/ai-assistant/agents/AiAgentSettingsPageClient.js.map +2 -2
  27. package/dist/modules/ai_assistant/backend/config/ai-assistant/allowlist/AiTenantAllowlistPageClient.js +2 -7
  28. package/dist/modules/ai_assistant/backend/config/ai-assistant/allowlist/AiTenantAllowlistPageClient.js.map +2 -2
  29. package/dist/modules/ai_assistant/backend/config/ai-assistant/playground/AiPlaygroundPageClient.js +44 -35
  30. package/dist/modules/ai_assistant/backend/config/ai-assistant/playground/AiPlaygroundPageClient.js.map +2 -2
  31. package/dist/modules/ai_assistant/backend/config/ai-assistant/usage/AiUsageStatsPageClient.js +282 -0
  32. package/dist/modules/ai_assistant/backend/config/ai-assistant/usage/AiUsageStatsPageClient.js.map +7 -0
  33. package/dist/modules/ai_assistant/backend/config/ai-assistant/usage/page.js +10 -0
  34. package/dist/modules/ai_assistant/backend/config/ai-assistant/usage/page.js.map +7 -0
  35. package/dist/modules/ai_assistant/backend/config/ai-assistant/usage/page.meta.js +25 -0
  36. package/dist/modules/ai_assistant/backend/config/ai-assistant/usage/page.meta.js.map +7 -0
  37. package/dist/modules/ai_assistant/cli.js +12 -0
  38. package/dist/modules/ai_assistant/cli.js.map +2 -2
  39. package/dist/modules/ai_assistant/components/AiAssistantSettingsPageClient.js.map +1 -1
  40. package/dist/modules/ai_assistant/data/entities.js +177 -1
  41. package/dist/modules/ai_assistant/data/entities.js.map +2 -2
  42. package/dist/modules/ai_assistant/data/repositories/AiAgentRuntimeOverrideRepository.js +104 -2
  43. package/dist/modules/ai_assistant/data/repositories/AiAgentRuntimeOverrideRepository.js.map +2 -2
  44. package/dist/modules/ai_assistant/data/repositories/AiTokenUsageRepository.js +168 -0
  45. package/dist/modules/ai_assistant/data/repositories/AiTokenUsageRepository.js.map +7 -0
  46. package/dist/modules/ai_assistant/events.js +8 -0
  47. package/dist/modules/ai_assistant/events.js.map +2 -2
  48. package/dist/modules/ai_assistant/i18n/de.json +74 -1
  49. package/dist/modules/ai_assistant/i18n/en.json +74 -1
  50. package/dist/modules/ai_assistant/i18n/es.json +75 -2
  51. package/dist/modules/ai_assistant/i18n/pl.json +74 -1
  52. package/dist/modules/ai_assistant/lib/agent-policy.js.map +2 -2
  53. package/dist/modules/ai_assistant/lib/agent-runtime.js +588 -23
  54. package/dist/modules/ai_assistant/lib/agent-runtime.js.map +3 -3
  55. package/dist/modules/ai_assistant/lib/agent-tools.js +6 -1
  56. package/dist/modules/ai_assistant/lib/agent-tools.js.map +2 -2
  57. package/dist/modules/ai_assistant/lib/ai-agent-definition.js.map +2 -2
  58. package/dist/modules/ai_assistant/lib/model-factory.js +63 -22
  59. package/dist/modules/ai_assistant/lib/model-factory.js.map +2 -2
  60. package/dist/modules/ai_assistant/lib/token-usage-recorder.js +78 -0
  61. package/dist/modules/ai_assistant/lib/token-usage-recorder.js.map +7 -0
  62. package/dist/modules/ai_assistant/lib/usage-serialization.js +33 -0
  63. package/dist/modules/ai_assistant/lib/usage-serialization.js.map +7 -0
  64. package/dist/modules/ai_assistant/migrations/Migration20260508160000_ai_agent_loop_overrides.js +25 -0
  65. package/dist/modules/ai_assistant/migrations/Migration20260508160000_ai_agent_loop_overrides.js.map +7 -0
  66. package/dist/modules/ai_assistant/migrations/Migration20260508170000_ai_token_usage.js +88 -0
  67. package/dist/modules/ai_assistant/migrations/Migration20260508170000_ai_token_usage.js.map +7 -0
  68. package/dist/modules/ai_assistant/setup.js +34 -0
  69. package/dist/modules/ai_assistant/setup.js.map +2 -2
  70. package/dist/modules/ai_assistant/workers/ai-token-usage-prune.js +114 -0
  71. package/dist/modules/ai_assistant/workers/ai-token-usage-prune.js.map +7 -0
  72. package/generated/entities/ai_agent_runtime_override/index.ts +7 -0
  73. package/generated/entities/ai_token_usage_daily/index.ts +16 -0
  74. package/generated/entities/ai_token_usage_event/index.ts +19 -0
  75. package/generated/entities.ids.generated.ts +2 -0
  76. package/generated/entity-fields-registry.ts +47 -1
  77. package/package.json +15 -7
  78. package/src/frontend/components/AiChatButton.tsx +3 -2
  79. package/src/modules/ai_assistant/__integration__/TC-AI-AGENT-LOOP-001-006.spec.ts +521 -0
  80. package/src/modules/ai_assistant/__integration__/TC-AI-RUNTIME-OVERRIDES-006-model-picker.spec.ts +8 -8
  81. package/src/modules/ai_assistant/__integration__/TC-AI-TOKEN-USAGE-001-005.spec.ts +231 -0
  82. package/src/modules/ai_assistant/__tests__/events.test.ts +4 -3
  83. package/src/modules/ai_assistant/__tests__/settings-page-logic.test.ts +5 -5
  84. package/src/modules/ai_assistant/__tests__/token-usage-recorder.test.ts +109 -0
  85. package/src/modules/ai_assistant/api/ai/agents/[agentId]/loop-override/route.ts +388 -0
  86. package/src/modules/ai_assistant/api/ai/agents/[agentId]/models/__tests__/route.test.ts +5 -0
  87. package/src/modules/ai_assistant/api/ai/agents/[agentId]/models/route.ts +8 -7
  88. package/src/modules/ai_assistant/api/ai/chat/__tests__/route.test.ts +102 -5
  89. package/src/modules/ai_assistant/api/ai/chat/route.ts +55 -18
  90. package/src/modules/ai_assistant/api/settings/route.ts +5 -3
  91. package/src/modules/ai_assistant/api/usage/daily/__tests__/route.test.ts +159 -0
  92. package/src/modules/ai_assistant/api/usage/daily/route.ts +126 -0
  93. package/src/modules/ai_assistant/api/usage/sessions/[sessionId]/__tests__/route.test.ts +143 -0
  94. package/src/modules/ai_assistant/api/usage/sessions/[sessionId]/route.ts +130 -0
  95. package/src/modules/ai_assistant/api/usage/sessions/__tests__/route.test.ts +123 -0
  96. package/src/modules/ai_assistant/api/usage/sessions/route.ts +184 -0
  97. package/src/modules/ai_assistant/backend/config/ai-assistant/agents/AiAgentSettingsPageClient.tsx +372 -16
  98. package/src/modules/ai_assistant/backend/config/ai-assistant/allowlist/AiTenantAllowlistPageClient.tsx +1 -4
  99. package/src/modules/ai_assistant/backend/config/ai-assistant/playground/AiPlaygroundPageClient.tsx +26 -9
  100. package/src/modules/ai_assistant/backend/config/ai-assistant/usage/AiUsageStatsPageClient.tsx +469 -0
  101. package/src/modules/ai_assistant/backend/config/ai-assistant/usage/page.meta.ts +23 -0
  102. package/src/modules/ai_assistant/backend/config/ai-assistant/usage/page.tsx +12 -0
  103. package/src/modules/ai_assistant/cli.ts +18 -0
  104. package/src/modules/ai_assistant/components/AiAssistantSettingsPageClient.tsx +1 -1
  105. package/src/modules/ai_assistant/data/entities.ts +237 -0
  106. package/src/modules/ai_assistant/data/repositories/AiAgentRuntimeOverrideRepository.ts +135 -3
  107. package/src/modules/ai_assistant/data/repositories/AiTokenUsageRepository.ts +213 -0
  108. package/src/modules/ai_assistant/data/repositories/__tests__/AiAgentRuntimeOverrideRepository.test.ts +223 -0
  109. package/src/modules/ai_assistant/data/repositories/__tests__/AiTokenUsageRepository.test.ts +58 -0
  110. package/src/modules/ai_assistant/events.ts +8 -0
  111. package/src/modules/ai_assistant/i18n/de.json +74 -1
  112. package/src/modules/ai_assistant/i18n/en.json +74 -1
  113. package/src/modules/ai_assistant/i18n/es.json +75 -2
  114. package/src/modules/ai_assistant/i18n/pl.json +74 -1
  115. package/src/modules/ai_assistant/lib/__tests__/agent-runtime-loop-phase0.test.ts +439 -0
  116. package/src/modules/ai_assistant/lib/__tests__/agent-runtime-loop-phase1.test.ts +243 -0
  117. package/src/modules/ai_assistant/lib/__tests__/agent-runtime-loop-phase2.test.ts +388 -0
  118. package/src/modules/ai_assistant/lib/__tests__/agent-runtime-loop-phase3.test.ts +359 -0
  119. package/src/modules/ai_assistant/lib/__tests__/agent-runtime-phase4a.test.ts +2 -2
  120. package/src/modules/ai_assistant/lib/__tests__/agent-runtime.test.ts +2 -1
  121. package/src/modules/ai_assistant/lib/__tests__/max-steps-budget.integration.test.ts +12 -13
  122. package/src/modules/ai_assistant/lib/__tests__/model-factory.test.ts +77 -14
  123. package/src/modules/ai_assistant/lib/agent-policy.ts +9 -0
  124. package/src/modules/ai_assistant/lib/agent-runtime.ts +1148 -43
  125. package/src/modules/ai_assistant/lib/agent-tools.ts +5 -1
  126. package/src/modules/ai_assistant/lib/ai-agent-definition.ts +289 -2
  127. package/src/modules/ai_assistant/lib/model-factory.ts +128 -43
  128. package/src/modules/ai_assistant/lib/token-usage-recorder.ts +122 -0
  129. package/src/modules/ai_assistant/lib/usage-serialization.ts +29 -0
  130. package/src/modules/ai_assistant/migrations/.snapshot-open-mercato.json +791 -0
  131. package/src/modules/ai_assistant/migrations/Migration20260508160000_ai_agent_loop_overrides.ts +25 -0
  132. package/src/modules/ai_assistant/migrations/Migration20260508170000_ai_token_usage.ts +89 -0
  133. package/src/modules/ai_assistant/setup.ts +49 -0
  134. package/src/modules/ai_assistant/workers/__tests__/ai-token-usage-prune.test.ts +144 -0
  135. package/src/modules/ai_assistant/workers/ai-token-usage-prune.ts +188 -0
@@ -0,0 +1,316 @@
1
+ import { NextResponse } from "next/server";
2
+ import { z } from "zod";
3
+ import { getAuthFromRequest } from "@open-mercato/shared/lib/auth/server";
4
+ import { createRequestContainer } from "@open-mercato/shared/lib/di/container";
5
+ import { getAgent, loadAgentRegistry } from "../../../../../lib/agent-registry.js";
6
+ import { hasRequiredFeatures } from "../../../../../lib/auth.js";
7
+ import {
8
+ AiAgentRuntimeOverrideRepository,
9
+ AiAgentRuntimeOverrideValidationError
10
+ } from "../../../../../data/repositories/AiAgentRuntimeOverrideRepository.js";
11
+ const agentIdPattern = /^[a-z0-9_]+\.[a-z0-9_]+$/;
12
+ const agentIdParamSchema = z.object({
13
+ agentId: z.string().regex(agentIdPattern, 'agentId must match "<module>.<agent>" (lowercase, digits, underscores only)')
14
+ });
15
+ const loopOverrideRequestSchema = z.object({
16
+ loopDisabled: z.boolean().nullable().optional(),
17
+ loopMaxSteps: z.number().int().min(1).max(1e3).nullable().optional(),
18
+ loopMaxToolCalls: z.number().int().min(1).max(1e4).nullable().optional(),
19
+ loopMaxWallClockMs: z.number().int().min(100).max(36e5).nullable().optional(),
20
+ loopMaxTokens: z.number().int().min(1).max(1e7).nullable().optional(),
21
+ loopStopWhenJson: z.array(
22
+ z.discriminatedUnion("kind", [
23
+ z.object({ kind: z.literal("stepCount"), count: z.number().int().min(1) }),
24
+ z.object({ kind: z.literal("hasToolCall"), toolName: z.string().min(1) })
25
+ ])
26
+ ).nullable().optional(),
27
+ loopActiveToolsJson: z.array(z.string().min(1)).nullable().optional()
28
+ });
29
+ const VIEW_FEATURE = "ai_assistant.view";
30
+ const MANAGE_FEATURE = "ai_assistant.settings.manage";
31
+ const openApi = {
32
+ tag: "AI Assistant",
33
+ summary: "Tenant-scoped loop-policy override for an AI agent",
34
+ methods: {
35
+ GET: {
36
+ operationId: "aiAssistantGetLoopOverride",
37
+ summary: "Read the current loop-policy override for this agent, if any.",
38
+ description: "Returns `{ agentId, override }` where `override` is the agent-scoped loop-policy row from `ai_agent_runtime_overrides` (or `null`). Requires `ai_assistant.view`.",
39
+ responses: [
40
+ {
41
+ status: 200,
42
+ description: "Loop override payload.",
43
+ mediaType: "application/json"
44
+ }
45
+ ],
46
+ errors: [
47
+ { status: 400, description: "Invalid agent id." },
48
+ { status: 401, description: "Unauthenticated caller." },
49
+ { status: 403, description: "Caller lacks `ai_assistant.view`." },
50
+ { status: 404, description: "Unknown agent id." }
51
+ ]
52
+ },
53
+ PUT: {
54
+ operationId: "aiAssistantSaveLoopOverride",
55
+ summary: "Set (or replace) the tenant-scoped loop-policy override for this agent.",
56
+ description: "Body: loop columns. All fields are nullable/optional; `null` explicitly clears that axis. Validates `loopStopWhenJson` items and `loopActiveToolsJson` membership. Requires `ai_assistant.settings.manage`.",
57
+ requestBody: {
58
+ contentType: "application/json",
59
+ description: "Loop override payload.",
60
+ schema: loopOverrideRequestSchema
61
+ },
62
+ responses: [
63
+ {
64
+ status: 200,
65
+ description: "Override persisted.",
66
+ mediaType: "application/json"
67
+ }
68
+ ],
69
+ errors: [
70
+ { status: 400, description: "Invalid agent id or validation error." },
71
+ { status: 401, description: "Unauthenticated caller." },
72
+ { status: 403, description: "Caller lacks `ai_assistant.settings.manage`." },
73
+ { status: 404, description: "Unknown agent id." }
74
+ ]
75
+ },
76
+ DELETE: {
77
+ operationId: "aiAssistantClearLoopOverride",
78
+ summary: "Remove the loop-policy columns from the agent-scoped runtime override row.",
79
+ description: "Nulls out all seven loop columns on the agent-scoped `ai_agent_runtime_overrides` row. Idempotent \u2014 returns 200 even when no override exists. Requires `ai_assistant.settings.manage`.",
80
+ responses: [
81
+ {
82
+ status: 200,
83
+ description: "Loop override cleared (or already absent).",
84
+ mediaType: "application/json"
85
+ }
86
+ ],
87
+ errors: [
88
+ { status: 400, description: "Invalid agent id." },
89
+ { status: 401, description: "Unauthenticated caller." },
90
+ { status: 403, description: "Caller lacks `ai_assistant.settings.manage`." },
91
+ { status: 404, description: "Unknown agent id." }
92
+ ]
93
+ }
94
+ }
95
+ };
96
+ const metadata = {
97
+ GET: { requireAuth: true, requireFeatures: [VIEW_FEATURE] },
98
+ PUT: { requireAuth: true, requireFeatures: [MANAGE_FEATURE] },
99
+ DELETE: { requireAuth: true, requireFeatures: [MANAGE_FEATURE] }
100
+ };
101
+ function jsonError(status, message, code, extra) {
102
+ return NextResponse.json({ error: message, code, ...extra ?? {} }, { status });
103
+ }
104
+ async function resolveAuthOrRespond(req, requiredFeature) {
105
+ const auth = await getAuthFromRequest(req);
106
+ if (!auth) {
107
+ return jsonError(401, "Unauthorized", "unauthenticated");
108
+ }
109
+ const container = await createRequestContainer();
110
+ const rbacService = container.resolve("rbacService");
111
+ const acl = await rbacService.loadAcl(auth.sub, {
112
+ tenantId: auth.tenantId,
113
+ organizationId: auth.orgId
114
+ });
115
+ if (!hasRequiredFeatures([requiredFeature], acl.features, acl.isSuperAdmin, rbacService)) {
116
+ return jsonError(403, `Caller lacks required feature "${requiredFeature}".`, "forbidden");
117
+ }
118
+ return {
119
+ tenantId: auth.tenantId ?? null,
120
+ organizationId: auth.orgId ?? null,
121
+ userId: auth.sub,
122
+ isSuperAdmin: acl.isSuperAdmin,
123
+ features: acl.features,
124
+ container
125
+ };
126
+ }
127
+ function serializeLoopOverride(row) {
128
+ return {
129
+ id: row.id,
130
+ agentId: row.agentId ?? null,
131
+ loopDisabled: row.loopDisabled ?? null,
132
+ loopMaxSteps: row.loopMaxSteps ?? null,
133
+ loopMaxToolCalls: row.loopMaxToolCalls ?? null,
134
+ loopMaxWallClockMs: row.loopMaxWallClockMs ?? null,
135
+ loopMaxTokens: row.loopMaxTokens ?? null,
136
+ loopStopWhenJson: row.loopStopWhenJson ?? null,
137
+ loopActiveToolsJson: row.loopActiveToolsJson ?? null,
138
+ updatedAt: row.updatedAt?.toISOString?.() ?? (/* @__PURE__ */ new Date()).toISOString()
139
+ };
140
+ }
141
+ async function GET(req, context) {
142
+ const authResult = await resolveAuthOrRespond(req, VIEW_FEATURE);
143
+ if (authResult instanceof NextResponse) return authResult;
144
+ const rawParams = await context.params;
145
+ const paramResult = agentIdParamSchema.safeParse(rawParams);
146
+ if (!paramResult.success) {
147
+ return jsonError(400, "Invalid agent id.", "validation_error", {
148
+ issues: paramResult.error.issues
149
+ });
150
+ }
151
+ try {
152
+ await loadAgentRegistry();
153
+ const agent = getAgent(paramResult.data.agentId);
154
+ if (!agent) {
155
+ return jsonError(404, `Unknown agent "${paramResult.data.agentId}".`, "agent_unknown");
156
+ }
157
+ if (!authResult.tenantId) {
158
+ return NextResponse.json({ agentId: agent.id, override: null });
159
+ }
160
+ const em = authResult.container.resolve("em");
161
+ const repo = new AiAgentRuntimeOverrideRepository(em);
162
+ const row = await repo.getDefault({
163
+ tenantId: authResult.tenantId,
164
+ organizationId: authResult.organizationId,
165
+ agentId: agent.id
166
+ });
167
+ const hasLoopData = row !== null && (row.loopDisabled !== null || row.loopMaxSteps !== null || row.loopMaxToolCalls !== null || row.loopMaxWallClockMs !== null || row.loopMaxTokens !== null || row.loopStopWhenJson !== null || row.loopActiveToolsJson !== null);
168
+ return NextResponse.json({
169
+ agentId: agent.id,
170
+ override: hasLoopData ? serializeLoopOverride(row) : null
171
+ });
172
+ } catch (error) {
173
+ console.error("[AI Loop Override GET] Failure:", error);
174
+ return jsonError(
175
+ 500,
176
+ error instanceof Error ? error.message : "Failed to load loop override.",
177
+ "internal_error"
178
+ );
179
+ }
180
+ }
181
+ async function PUT(req, context) {
182
+ const authResult = await resolveAuthOrRespond(req, MANAGE_FEATURE);
183
+ if (authResult instanceof NextResponse) return authResult;
184
+ const rawParams = await context.params;
185
+ const paramResult = agentIdParamSchema.safeParse(rawParams);
186
+ if (!paramResult.success) {
187
+ return jsonError(400, "Invalid agent id.", "validation_error", {
188
+ issues: paramResult.error.issues
189
+ });
190
+ }
191
+ let parsedBody;
192
+ try {
193
+ parsedBody = await req.json();
194
+ } catch {
195
+ return jsonError(400, "Request body must be valid JSON.", "validation_error");
196
+ }
197
+ const bodyResult = loopOverrideRequestSchema.safeParse(parsedBody);
198
+ if (!bodyResult.success) {
199
+ return jsonError(400, "Invalid request body.", "validation_error", {
200
+ issues: bodyResult.error.issues
201
+ });
202
+ }
203
+ try {
204
+ await loadAgentRegistry();
205
+ const agent = getAgent(paramResult.data.agentId);
206
+ if (!agent) {
207
+ return jsonError(404, `Unknown agent "${paramResult.data.agentId}".`, "agent_unknown");
208
+ }
209
+ if (!authResult.tenantId) {
210
+ return jsonError(
211
+ 400,
212
+ "Caller has no tenant context; cannot persist tenant-scoped loop override.",
213
+ "tenant_required"
214
+ );
215
+ }
216
+ const em = authResult.container.resolve("em");
217
+ const repo = new AiAgentRuntimeOverrideRepository(em);
218
+ const row = await repo.upsertDefault(
219
+ {
220
+ agentId: agent.id,
221
+ agentAllowedTools: agent.allowedTools,
222
+ loopDisabled: bodyResult.data.loopDisabled ?? null,
223
+ loopMaxSteps: bodyResult.data.loopMaxSteps ?? null,
224
+ loopMaxToolCalls: bodyResult.data.loopMaxToolCalls ?? null,
225
+ loopMaxWallClockMs: bodyResult.data.loopMaxWallClockMs ?? null,
226
+ loopMaxTokens: bodyResult.data.loopMaxTokens ?? null,
227
+ loopStopWhenJson: bodyResult.data.loopStopWhenJson ?? null,
228
+ loopActiveToolsJson: bodyResult.data.loopActiveToolsJson ?? null
229
+ },
230
+ {
231
+ tenantId: authResult.tenantId,
232
+ organizationId: authResult.organizationId,
233
+ userId: authResult.userId
234
+ }
235
+ );
236
+ return NextResponse.json({
237
+ ok: true,
238
+ agentId: agent.id,
239
+ override: serializeLoopOverride(row)
240
+ });
241
+ } catch (error) {
242
+ if (error instanceof AiAgentRuntimeOverrideValidationError) {
243
+ return jsonError(400, error.message, error.code);
244
+ }
245
+ console.error("[AI Loop Override PUT] Failure:", error);
246
+ return jsonError(
247
+ 500,
248
+ error instanceof Error ? error.message : "Failed to save loop override.",
249
+ "internal_error"
250
+ );
251
+ }
252
+ }
253
+ async function DELETE(req, context) {
254
+ const authResult = await resolveAuthOrRespond(req, MANAGE_FEATURE);
255
+ if (authResult instanceof NextResponse) return authResult;
256
+ const rawParams = await context.params;
257
+ const paramResult = agentIdParamSchema.safeParse(rawParams);
258
+ if (!paramResult.success) {
259
+ return jsonError(400, "Invalid agent id.", "validation_error", {
260
+ issues: paramResult.error.issues
261
+ });
262
+ }
263
+ try {
264
+ await loadAgentRegistry();
265
+ const agent = getAgent(paramResult.data.agentId);
266
+ if (!agent) {
267
+ return jsonError(404, `Unknown agent "${paramResult.data.agentId}".`, "agent_unknown");
268
+ }
269
+ if (!authResult.tenantId) {
270
+ return NextResponse.json({ ok: true, agentId: agent.id, cleared: false });
271
+ }
272
+ const em = authResult.container.resolve("em");
273
+ const repo = new AiAgentRuntimeOverrideRepository(em);
274
+ const existing = await repo.getDefault({
275
+ tenantId: authResult.tenantId,
276
+ organizationId: authResult.organizationId,
277
+ agentId: agent.id
278
+ });
279
+ if (!existing) {
280
+ return NextResponse.json({ ok: true, agentId: agent.id, cleared: false });
281
+ }
282
+ await repo.upsertDefault(
283
+ {
284
+ agentId: agent.id,
285
+ loopDisabled: null,
286
+ loopMaxSteps: null,
287
+ loopMaxToolCalls: null,
288
+ loopMaxWallClockMs: null,
289
+ loopMaxTokens: null,
290
+ loopStopWhenJson: null,
291
+ loopActiveToolsJson: null
292
+ },
293
+ {
294
+ tenantId: authResult.tenantId,
295
+ organizationId: authResult.organizationId,
296
+ userId: authResult.userId
297
+ }
298
+ );
299
+ return NextResponse.json({ ok: true, agentId: agent.id, cleared: true });
300
+ } catch (error) {
301
+ console.error("[AI Loop Override DELETE] Failure:", error);
302
+ return jsonError(
303
+ 500,
304
+ error instanceof Error ? error.message : "Failed to clear loop override.",
305
+ "internal_error"
306
+ );
307
+ }
308
+ }
309
+ export {
310
+ DELETE,
311
+ GET,
312
+ PUT,
313
+ metadata,
314
+ openApi
315
+ };
316
+ //# sourceMappingURL=route.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../../../../../src/modules/ai_assistant/api/ai/agents/%5BagentId%5D/loop-override/route.ts"],
4
+ "sourcesContent": ["import { NextResponse, type NextRequest } from 'next/server'\nimport { z } from 'zod'\nimport type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'\nimport { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport type { RbacService } from '@open-mercato/core/modules/auth/services/rbacService'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport { getAgent, loadAgentRegistry } from '../../../../../lib/agent-registry'\nimport { hasRequiredFeatures } from '../../../../../lib/auth'\nimport {\n AiAgentRuntimeOverrideRepository,\n AiAgentRuntimeOverrideValidationError,\n} from '../../../../../data/repositories/AiAgentRuntimeOverrideRepository'\nimport type { AiAgentRuntimeOverride } from '../../../../../data/entities'\n\nconst agentIdPattern = /^[a-z0-9_]+\\.[a-z0-9_]+$/\n\nconst agentIdParamSchema = z.object({\n agentId: z\n .string()\n .regex(agentIdPattern, 'agentId must match \"<module>.<agent>\" (lowercase, digits, underscores only)'),\n})\n\nconst loopOverrideRequestSchema = z.object({\n loopDisabled: z.boolean().nullable().optional(),\n loopMaxSteps: z.number().int().min(1).max(1000).nullable().optional(),\n loopMaxToolCalls: z.number().int().min(1).max(10000).nullable().optional(),\n loopMaxWallClockMs: z.number().int().min(100).max(3_600_000).nullable().optional(),\n loopMaxTokens: z.number().int().min(1).max(10_000_000).nullable().optional(),\n loopStopWhenJson: z\n .array(\n z.discriminatedUnion('kind', [\n z.object({ kind: z.literal('stepCount'), count: z.number().int().min(1) }),\n z.object({ kind: z.literal('hasToolCall'), toolName: z.string().min(1) }),\n ]),\n )\n .nullable()\n .optional(),\n loopActiveToolsJson: z.array(z.string().min(1)).nullable().optional(),\n})\n\nconst VIEW_FEATURE = 'ai_assistant.view'\nconst MANAGE_FEATURE = 'ai_assistant.settings.manage'\n\nexport const openApi: OpenApiRouteDoc = {\n tag: 'AI Assistant',\n summary: 'Tenant-scoped loop-policy override for an AI agent',\n methods: {\n GET: {\n operationId: 'aiAssistantGetLoopOverride',\n summary:\n 'Read the current loop-policy override for this agent, if any.',\n description:\n 'Returns `{ agentId, override }` where `override` is the agent-scoped loop-policy ' +\n 'row from `ai_agent_runtime_overrides` (or `null`). Requires `ai_assistant.view`.',\n responses: [\n {\n status: 200,\n description: 'Loop override payload.',\n mediaType: 'application/json',\n },\n ],\n errors: [\n { status: 400, description: 'Invalid agent id.' },\n { status: 401, description: 'Unauthenticated caller.' },\n { status: 403, description: 'Caller lacks `ai_assistant.view`.' },\n { status: 404, description: 'Unknown agent id.' },\n ],\n },\n PUT: {\n operationId: 'aiAssistantSaveLoopOverride',\n summary: 'Set (or replace) the tenant-scoped loop-policy override for this agent.',\n description:\n 'Body: loop columns. All fields are nullable/optional; `null` explicitly clears ' +\n 'that axis. Validates `loopStopWhenJson` items and `loopActiveToolsJson` membership. ' +\n 'Requires `ai_assistant.settings.manage`.',\n requestBody: {\n contentType: 'application/json',\n description: 'Loop override payload.',\n schema: loopOverrideRequestSchema,\n },\n responses: [\n {\n status: 200,\n description: 'Override persisted.',\n mediaType: 'application/json',\n },\n ],\n errors: [\n { status: 400, description: 'Invalid agent id or validation error.' },\n { status: 401, description: 'Unauthenticated caller.' },\n { status: 403, description: 'Caller lacks `ai_assistant.settings.manage`.' },\n { status: 404, description: 'Unknown agent id.' },\n ],\n },\n DELETE: {\n operationId: 'aiAssistantClearLoopOverride',\n summary: 'Remove the loop-policy columns from the agent-scoped runtime override row.',\n description:\n 'Nulls out all seven loop columns on the agent-scoped `ai_agent_runtime_overrides` row. ' +\n 'Idempotent \u2014 returns 200 even when no override exists. ' +\n 'Requires `ai_assistant.settings.manage`.',\n responses: [\n {\n status: 200,\n description: 'Loop override cleared (or already absent).',\n mediaType: 'application/json',\n },\n ],\n errors: [\n { status: 400, description: 'Invalid agent id.' },\n { status: 401, description: 'Unauthenticated caller.' },\n { status: 403, description: 'Caller lacks `ai_assistant.settings.manage`.' },\n { status: 404, description: 'Unknown agent id.' },\n ],\n },\n },\n}\n\nexport const metadata = {\n GET: { requireAuth: true, requireFeatures: [VIEW_FEATURE] },\n PUT: { requireAuth: true, requireFeatures: [MANAGE_FEATURE] },\n DELETE: { requireAuth: true, requireFeatures: [MANAGE_FEATURE] },\n}\n\ninterface RouteContext {\n params: Promise<{ agentId: string }>\n}\n\nfunction jsonError(\n status: number,\n message: string,\n code: string,\n extra?: Record<string, unknown>,\n): NextResponse {\n return NextResponse.json({ error: message, code, ...(extra ?? {}) }, { status })\n}\n\ninterface ResolvedAuth {\n tenantId: string | null\n organizationId: string | null\n userId: string\n isSuperAdmin: boolean\n features: string[]\n container: Awaited<ReturnType<typeof createRequestContainer>>\n}\n\nasync function resolveAuthOrRespond(\n req: NextRequest,\n requiredFeature: string,\n): Promise<ResolvedAuth | NextResponse> {\n const auth = await getAuthFromRequest(req)\n if (!auth) {\n return jsonError(401, 'Unauthorized', 'unauthenticated')\n }\n const container = await createRequestContainer()\n const rbacService = container.resolve<RbacService>('rbacService')\n const acl = await rbacService.loadAcl(auth.sub, {\n tenantId: auth.tenantId,\n organizationId: auth.orgId,\n })\n if (!hasRequiredFeatures([requiredFeature], acl.features, acl.isSuperAdmin, rbacService)) {\n return jsonError(403, `Caller lacks required feature \"${requiredFeature}\".`, 'forbidden')\n }\n return {\n tenantId: auth.tenantId ?? null,\n organizationId: auth.orgId ?? null,\n userId: auth.sub,\n isSuperAdmin: acl.isSuperAdmin,\n features: acl.features,\n container,\n }\n}\n\nfunction serializeLoopOverride(row: AiAgentRuntimeOverride) {\n return {\n id: row.id,\n agentId: row.agentId ?? null,\n loopDisabled: row.loopDisabled ?? null,\n loopMaxSteps: row.loopMaxSteps ?? null,\n loopMaxToolCalls: row.loopMaxToolCalls ?? null,\n loopMaxWallClockMs: row.loopMaxWallClockMs ?? null,\n loopMaxTokens: row.loopMaxTokens ?? null,\n loopStopWhenJson: row.loopStopWhenJson ?? null,\n loopActiveToolsJson: row.loopActiveToolsJson ?? null,\n updatedAt: row.updatedAt?.toISOString?.() ?? new Date().toISOString(),\n }\n}\n\nexport async function GET(req: NextRequest, context: RouteContext): Promise<Response> {\n const authResult = await resolveAuthOrRespond(req, VIEW_FEATURE)\n if (authResult instanceof NextResponse) return authResult\n\n const rawParams = await context.params\n const paramResult = agentIdParamSchema.safeParse(rawParams)\n if (!paramResult.success) {\n return jsonError(400, 'Invalid agent id.', 'validation_error', {\n issues: paramResult.error.issues,\n })\n }\n\n try {\n await loadAgentRegistry()\n const agent = getAgent(paramResult.data.agentId)\n if (!agent) {\n return jsonError(404, `Unknown agent \"${paramResult.data.agentId}\".`, 'agent_unknown')\n }\n\n if (!authResult.tenantId) {\n return NextResponse.json({ agentId: agent.id, override: null })\n }\n\n const em = authResult.container.resolve<EntityManager>('em')\n const repo = new AiAgentRuntimeOverrideRepository(em)\n const row = await repo.getDefault({\n tenantId: authResult.tenantId,\n organizationId: authResult.organizationId,\n agentId: agent.id,\n })\n\n const hasLoopData =\n row !== null &&\n (row.loopDisabled !== null ||\n row.loopMaxSteps !== null ||\n row.loopMaxToolCalls !== null ||\n row.loopMaxWallClockMs !== null ||\n row.loopMaxTokens !== null ||\n row.loopStopWhenJson !== null ||\n row.loopActiveToolsJson !== null)\n\n return NextResponse.json({\n agentId: agent.id,\n override: hasLoopData ? serializeLoopOverride(row!) : null,\n })\n } catch (error) {\n console.error('[AI Loop Override GET] Failure:', error)\n return jsonError(\n 500,\n error instanceof Error ? error.message : 'Failed to load loop override.',\n 'internal_error',\n )\n }\n}\n\nexport async function PUT(req: NextRequest, context: RouteContext): Promise<Response> {\n const authResult = await resolveAuthOrRespond(req, MANAGE_FEATURE)\n if (authResult instanceof NextResponse) return authResult\n\n const rawParams = await context.params\n const paramResult = agentIdParamSchema.safeParse(rawParams)\n if (!paramResult.success) {\n return jsonError(400, 'Invalid agent id.', 'validation_error', {\n issues: paramResult.error.issues,\n })\n }\n\n let parsedBody: unknown\n try {\n parsedBody = await req.json()\n } catch {\n return jsonError(400, 'Request body must be valid JSON.', 'validation_error')\n }\n\n const bodyResult = loopOverrideRequestSchema.safeParse(parsedBody)\n if (!bodyResult.success) {\n return jsonError(400, 'Invalid request body.', 'validation_error', {\n issues: bodyResult.error.issues,\n })\n }\n\n try {\n await loadAgentRegistry()\n const agent = getAgent(paramResult.data.agentId)\n if (!agent) {\n return jsonError(404, `Unknown agent \"${paramResult.data.agentId}\".`, 'agent_unknown')\n }\n\n if (!authResult.tenantId) {\n return jsonError(\n 400,\n 'Caller has no tenant context; cannot persist tenant-scoped loop override.',\n 'tenant_required',\n )\n }\n\n const em = authResult.container.resolve<EntityManager>('em')\n const repo = new AiAgentRuntimeOverrideRepository(em)\n const row = await repo.upsertDefault(\n {\n agentId: agent.id,\n agentAllowedTools: agent.allowedTools,\n loopDisabled: bodyResult.data.loopDisabled ?? null,\n loopMaxSteps: bodyResult.data.loopMaxSteps ?? null,\n loopMaxToolCalls: bodyResult.data.loopMaxToolCalls ?? null,\n loopMaxWallClockMs: bodyResult.data.loopMaxWallClockMs ?? null,\n loopMaxTokens: bodyResult.data.loopMaxTokens ?? null,\n loopStopWhenJson: bodyResult.data.loopStopWhenJson ?? null,\n loopActiveToolsJson: bodyResult.data.loopActiveToolsJson ?? null,\n },\n {\n tenantId: authResult.tenantId,\n organizationId: authResult.organizationId,\n userId: authResult.userId,\n },\n )\n\n return NextResponse.json({\n ok: true,\n agentId: agent.id,\n override: serializeLoopOverride(row),\n })\n } catch (error) {\n if (error instanceof AiAgentRuntimeOverrideValidationError) {\n return jsonError(400, error.message, error.code)\n }\n console.error('[AI Loop Override PUT] Failure:', error)\n return jsonError(\n 500,\n error instanceof Error ? error.message : 'Failed to save loop override.',\n 'internal_error',\n )\n }\n}\n\nexport async function DELETE(req: NextRequest, context: RouteContext): Promise<Response> {\n const authResult = await resolveAuthOrRespond(req, MANAGE_FEATURE)\n if (authResult instanceof NextResponse) return authResult\n\n const rawParams = await context.params\n const paramResult = agentIdParamSchema.safeParse(rawParams)\n if (!paramResult.success) {\n return jsonError(400, 'Invalid agent id.', 'validation_error', {\n issues: paramResult.error.issues,\n })\n }\n\n try {\n await loadAgentRegistry()\n const agent = getAgent(paramResult.data.agentId)\n if (!agent) {\n return jsonError(404, `Unknown agent \"${paramResult.data.agentId}\".`, 'agent_unknown')\n }\n\n if (!authResult.tenantId) {\n return NextResponse.json({ ok: true, agentId: agent.id, cleared: false })\n }\n\n const em = authResult.container.resolve<EntityManager>('em')\n const repo = new AiAgentRuntimeOverrideRepository(em)\n\n const existing = await repo.getDefault({\n tenantId: authResult.tenantId,\n organizationId: authResult.organizationId,\n agentId: agent.id,\n })\n\n if (!existing) {\n return NextResponse.json({ ok: true, agentId: agent.id, cleared: false })\n }\n\n await repo.upsertDefault(\n {\n agentId: agent.id,\n loopDisabled: null,\n loopMaxSteps: null,\n loopMaxToolCalls: null,\n loopMaxWallClockMs: null,\n loopMaxTokens: null,\n loopStopWhenJson: null,\n loopActiveToolsJson: null,\n },\n {\n tenantId: authResult.tenantId,\n organizationId: authResult.organizationId,\n userId: authResult.userId,\n },\n )\n\n return NextResponse.json({ ok: true, agentId: agent.id, cleared: true })\n } catch (error) {\n console.error('[AI Loop Override DELETE] Failure:', error)\n return jsonError(\n 500,\n error instanceof Error ? error.message : 'Failed to clear loop override.',\n 'internal_error',\n )\n }\n}\n"],
5
+ "mappings": "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",
6
+ "names": []
7
+ }
@@ -5,7 +5,7 @@ import { createRequestContainer } from "@open-mercato/shared/lib/di/container";
5
5
  import { llmProviderRegistry } from "@open-mercato/shared/lib/ai/llm-provider-registry";
6
6
  import { getAgent, loadAgentRegistry } from "../../../../../lib/agent-registry.js";
7
7
  import { hasRequiredFeatures } from "../../../../../lib/auth.js";
8
- import { createModelFactory } from "../../../../../lib/model-factory.js";
8
+ import { createModelFactory, resolveAllowRuntimeOverride } from "../../../../../lib/model-factory.js";
9
9
  import {
10
10
  hasAllowlistSnapshotRestrictions,
11
11
  intersectEffectiveAllowlistWithSnapshot,
@@ -31,11 +31,11 @@ const openApi = {
31
31
  GET: {
32
32
  operationId: "aiAssistantGetAgentModels",
33
33
  summary: "Get the providers and curated models available for the chat-UI picker for this agent",
34
- description: 'Returns all configured providers with their curated model catalogs, filtered to providers that have an API key configured in the current environment. When the agent declares `allowRuntimeModelOverride: false`, the response reflects that constraint so the UI picker can hide itself. Includes the agent\'s resolved default provider/model so the picker can render a "(default)" badge next to the right entry. RBAC: requires the same features as the agent itself (typically `ai_assistant.view`).',
34
+ description: 'Returns all configured providers with their curated model catalogs, filtered to providers that have an API key configured in the current environment. When the agent declares `allowRuntimeOverride: false`, the response reflects that constraint so the UI picker can hide itself. Includes the agent\'s resolved default provider/model so the picker can render a "(default)" badge next to the right entry. RBAC: requires the same features as the agent itself (typically `ai_assistant.view`).',
35
35
  responses: [
36
36
  {
37
37
  status: 200,
38
- description: "Providers and curated models available for the agent picker. Empty `providers` array when `allowRuntimeModelOverride` is false."
38
+ description: "Providers and curated models available for the agent picker. Empty `providers` array when `allowRuntimeOverride` is false."
39
39
  }
40
40
  ],
41
41
  errors: [
@@ -88,7 +88,7 @@ async function GET(req, { params }) {
88
88
  );
89
89
  }
90
90
  }
91
- const allowRuntimeModelOverride = agent.allowRuntimeModelOverride !== false;
91
+ const allowRuntimeOverride = resolveAllowRuntimeOverride(agent);
92
92
  let tenantAllowlistSnapshot = null;
93
93
  let agentRuntimeOverrideAllowlist = null;
94
94
  let tenantRuntimeOverride = null;
@@ -131,7 +131,7 @@ async function GET(req, { params }) {
131
131
  agentDefaultModel: agent.defaultModel,
132
132
  agentDefaultProvider: agent.defaultProvider,
133
133
  agentDefaultBaseUrl: agent.defaultBaseUrl,
134
- allowRuntimeModelOverride,
134
+ allowRuntimeOverride,
135
135
  tenantOverride: tenantRuntimeOverride ?? void 0,
136
136
  tenantAllowlist: tenantAllowlistSnapshot
137
137
  });
@@ -154,7 +154,7 @@ async function GET(req, { params }) {
154
154
  knownProviderIds,
155
155
  agentRuntimeOverrideAllowlist
156
156
  );
157
- const providers = allowRuntimeModelOverride ? llmProviderRegistry.list().filter((provider) => provider.isConfigured()).filter((provider) => isProviderAllowedInEffective(effectiveAllowlist, provider.id)).map((provider) => {
157
+ const providers = allowRuntimeOverride ? llmProviderRegistry.list().filter((provider) => provider.isConfigured()).filter((provider) => isProviderAllowedInEffective(effectiveAllowlist, provider.id)).map((provider) => {
158
158
  const allowedModelIds = effectiveAllowlist.modelsByProvider[provider.id];
159
159
  const filteredModels = modelsForPicker(provider, allowedModelIds).filter(
160
160
  (model) => isModelAllowedForProviderInEffective(effectiveAllowlist, provider.id, model.id)
@@ -174,7 +174,8 @@ async function GET(req, { params }) {
174
174
  }) : [];
175
175
  return NextResponse.json({
176
176
  agentId,
177
- allowRuntimeModelOverride,
177
+ allowRuntimeOverride,
178
+ allowRuntimeModelOverride: allowRuntimeOverride,
178
179
  defaultProviderId,
179
180
  defaultModelId,
180
181
  defaultProviderName: llmProviderRegistry.get(defaultProviderId)?.name ?? defaultProviderId,
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../../../../src/modules/ai_assistant/api/ai/agents/%5BagentId%5D/models/route.ts"],
4
- "sourcesContent": ["import { NextResponse, type NextRequest } from 'next/server'\nimport { z } from 'zod'\nimport type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport type { RbacService } from '@open-mercato/core/modules/auth/services/rbacService'\nimport { llmProviderRegistry } from '@open-mercato/shared/lib/ai/llm-provider-registry'\nimport { getAgent, loadAgentRegistry } from '../../../../../lib/agent-registry'\nimport { hasRequiredFeatures } from '../../../../../lib/auth'\nimport { createModelFactory } from '../../../../../lib/model-factory'\nimport {\n hasAllowlistSnapshotRestrictions,\n intersectEffectiveAllowlistWithSnapshot,\n intersectAllowlists,\n isModelAllowedForProviderInEffective,\n isProviderAllowedInEffective,\n readAgentRuntimeOverrideAllowlist,\n type TenantAllowlistSnapshot,\n} from '../../../../../lib/model-allowlist'\nimport { AiTenantModelAllowlistRepository } from '../../../../../data/repositories/AiTenantModelAllowlistRepository'\nimport { AiAgentRuntimeOverrideRepository } from '../../../../../data/repositories/AiAgentRuntimeOverrideRepository'\n\nfunction modelsForPicker(\n provider: ReturnType<typeof llmProviderRegistry.list>[number],\n allowedModelIds: string[] | undefined,\n): ReadonlyArray<{ id: string; name: string; contextWindow?: number | null; tags?: readonly string[] }> {\n if (provider.defaultModels.length > 0) return provider.defaultModels\n return (allowedModelIds ?? []).map((id) => ({ id, name: id }))\n}\n\nconst agentIdPattern = /^[a-z0-9_]+\\.[a-z0-9_]+$/\n\nconst agentIdParamSchema = z.object({\n agentId: z\n .string()\n .regex(agentIdPattern, 'agentId must match \"<module>.<agent>\" (lowercase, digits, underscores only)'),\n})\n\nexport const openApi: OpenApiRouteDoc = {\n tag: 'AI Assistant',\n summary: 'Available models for an AI agent',\n methods: {\n GET: {\n operationId: 'aiAssistantGetAgentModels',\n summary: 'Get the providers and curated models available for the chat-UI picker for this agent',\n description:\n 'Returns all configured providers with their curated model catalogs, filtered to providers ' +\n 'that have an API key configured in the current environment. When the agent declares ' +\n '`allowRuntimeModelOverride: false`, the response reflects that constraint so the ' +\n 'UI picker can hide itself. Includes the agent\\'s resolved default provider/model so ' +\n 'the picker can render a \"(default)\" badge next to the right entry. ' +\n 'RBAC: requires the same features as the agent itself (typically `ai_assistant.view`).',\n responses: [\n {\n status: 200,\n description:\n 'Providers and curated models available for the agent picker. ' +\n 'Empty `providers` array when `allowRuntimeModelOverride` is false.',\n },\n ],\n errors: [\n { status: 401, description: 'Unauthenticated.' },\n { status: 403, description: 'Caller lacks the agent\\'s required features.' },\n { status: 404, description: 'Unknown agent id.' },\n ],\n },\n },\n}\n\nexport const metadata = {\n GET: { requireAuth: true, requireFeatures: ['ai_assistant.view'] },\n}\n\nexport async function GET(\n req: NextRequest,\n { params }: { params: Promise<{ agentId: string }> },\n): Promise<Response> {\n const auth = await getAuthFromRequest(req)\n if (!auth?.sub) {\n return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })\n }\n\n const rawParams = await params\n const paramResult = agentIdParamSchema.safeParse(rawParams)\n if (!paramResult.success) {\n return NextResponse.json(\n { error: 'Invalid agentId path parameter.', code: 'validation_error', issues: paramResult.error.issues },\n { status: 400 },\n )\n }\n const agentId = paramResult.data.agentId\n\n try {\n await loadAgentRegistry()\n\n const container = await createRequestContainer()\n const rbacService = container.resolve<RbacService>('rbacService')\n const acl = await rbacService.loadAcl(auth.sub, {\n tenantId: auth.tenantId,\n organizationId: auth.orgId,\n })\n\n const agent = getAgent(agentId)\n if (!agent) {\n return NextResponse.json({ error: `Agent \"${agentId}\" not found.`, code: 'agent_unknown' }, { status: 404 })\n }\n\n const agentFeatures = agent.requiredFeatures ?? []\n if (agentFeatures.length > 0) {\n const permitted = hasRequiredFeatures(agentFeatures, acl.features, acl.isSuperAdmin)\n if (!permitted) {\n return NextResponse.json(\n {\n error: `Access to agent \"${agentId}\" requires features: ${agentFeatures.join(', ')}.`,\n code: 'agent_features_denied',\n },\n { status: 403 },\n )\n }\n }\n\n const allowRuntimeModelOverride = agent.allowRuntimeModelOverride !== false\n\n // Load the per-tenant allowlist snapshot so the picker reflects both env\n // and admin-edited tenant constraints (Phase 1780-6).\n let tenantAllowlistSnapshot: TenantAllowlistSnapshot | null = null\n let agentRuntimeOverrideAllowlist: TenantAllowlistSnapshot | null = null\n let tenantRuntimeOverride: {\n providerId: string | null\n modelId: string | null\n baseURL: string | null\n } | null = null\n if (auth.tenantId) {\n try {\n const em = container.resolve<EntityManager>('em')\n const allowlistRepo = new AiTenantModelAllowlistRepository(em)\n tenantAllowlistSnapshot = await allowlistRepo.getSnapshot({\n tenantId: auth.tenantId,\n organizationId: auth.orgId ?? null,\n })\n const runtimeOverrideRepo = new AiAgentRuntimeOverrideRepository(em)\n const runtimeOverrideDefaultRow = await runtimeOverrideRepo.getDefault({\n tenantId: auth.tenantId,\n organizationId: auth.orgId ?? null,\n agentId,\n })\n tenantRuntimeOverride = runtimeOverrideDefaultRow\n ? {\n providerId: runtimeOverrideDefaultRow.providerId ?? null,\n modelId: runtimeOverrideDefaultRow.modelId ?? null,\n baseURL: runtimeOverrideDefaultRow.baseUrl ?? null,\n }\n : null\n const runtimeOverrideRow = await runtimeOverrideRepo.getExact({\n tenantId: auth.tenantId,\n organizationId: auth.orgId ?? null,\n agentId,\n })\n const tenantAgentAllowlist = runtimeOverrideRow\n ? {\n allowedProviders: runtimeOverrideRow.allowedOverrideProviders ?? null,\n allowedModelsByProvider: runtimeOverrideRow.allowedOverrideModelsByProvider ?? {},\n }\n : null\n agentRuntimeOverrideAllowlist = hasAllowlistSnapshotRestrictions(tenantAgentAllowlist)\n ? tenantAgentAllowlist\n : null\n } catch (snapshotError) {\n // Picker still renders against env-only so the UI does not break, but log at\n // error level so an outage is operationally visible. The chat dispatcher\n // refuses to dispatch when this lookup fails, so writes stay safe.\n console.error('[AI Agents Models] Failed to load tenant allowlist:', snapshotError)\n }\n }\n\n // Resolve the agent's current default provider/model for the \"(default)\" badge\n const factory = createModelFactory(container)\n const defaultResolution = factory.resolveModel({\n moduleId: agent.moduleId,\n agentDefaultModel: agent.defaultModel,\n agentDefaultProvider: agent.defaultProvider,\n agentDefaultBaseUrl: agent.defaultBaseUrl,\n allowRuntimeModelOverride,\n tenantOverride: tenantRuntimeOverride ?? undefined,\n tenantAllowlist: tenantAllowlistSnapshot,\n })\n const defaultProviderId = defaultResolution.providerId\n const defaultModelId = defaultResolution.modelId\n\n // Build provider list \u2014 only configured providers, with curated model\n // catalogs, clipped to the EFFECTIVE allowlist (env \u2229 tenant) so the\n // chat-UI picker can never offer a value the runtime would refuse.\n const env = process.env as Record<string, string | undefined>\n const knownProviderIds = llmProviderRegistry.list().map((p) => p.id)\n const baseEffectiveAllowlist = intersectAllowlists(\n env,\n knownProviderIds,\n tenantAllowlistSnapshot,\n )\n const envAgentAllowlist = readAgentRuntimeOverrideAllowlist(env, agentId, knownProviderIds)\n const effectiveAllowlist = intersectEffectiveAllowlistWithSnapshot(\n intersectEffectiveAllowlistWithSnapshot(\n baseEffectiveAllowlist,\n knownProviderIds,\n envAgentAllowlist,\n ),\n knownProviderIds,\n agentRuntimeOverrideAllowlist,\n )\n const providers = allowRuntimeModelOverride\n ? llmProviderRegistry.list()\n .filter((provider) => provider.isConfigured())\n .filter((provider) => isProviderAllowedInEffective(effectiveAllowlist, provider.id))\n .map((provider) => {\n const allowedModelIds = effectiveAllowlist.modelsByProvider[provider.id]\n const filteredModels = modelsForPicker(provider, allowedModelIds).filter((model) =>\n isModelAllowedForProviderInEffective(effectiveAllowlist, provider.id, model.id),\n )\n return {\n id: provider.id,\n name: provider.name,\n isDefault: provider.id === defaultProviderId,\n models: filteredModels.map((model) => ({\n id: model.id,\n name: model.name,\n contextWindow: model.contextWindow,\n tags: model.tags,\n isDefault: provider.id === defaultProviderId && model.id === defaultModelId,\n })),\n }\n })\n : []\n\n return NextResponse.json({\n agentId,\n allowRuntimeModelOverride,\n defaultProviderId,\n defaultModelId,\n defaultProviderName: llmProviderRegistry.get(defaultProviderId)?.name ?? defaultProviderId,\n defaultModelName:\n llmProviderRegistry\n .get(defaultProviderId)\n ?.defaultModels.find((model) => model.id === defaultModelId)?.name ?? defaultModelId,\n providers,\n })\n } catch (error) {\n console.error('[AI Agents Models] GET error:', error)\n return NextResponse.json({ error: 'Failed to resolve agent models.' }, { status: 500 })\n }\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import { NextResponse, type NextRequest } from 'next/server'\nimport { z } from 'zod'\nimport type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport type { RbacService } from '@open-mercato/core/modules/auth/services/rbacService'\nimport { llmProviderRegistry } from '@open-mercato/shared/lib/ai/llm-provider-registry'\nimport { getAgent, loadAgentRegistry } from '../../../../../lib/agent-registry'\nimport { hasRequiredFeatures } from '../../../../../lib/auth'\nimport { createModelFactory, resolveAllowRuntimeOverride } from '../../../../../lib/model-factory'\nimport {\n hasAllowlistSnapshotRestrictions,\n intersectEffectiveAllowlistWithSnapshot,\n intersectAllowlists,\n isModelAllowedForProviderInEffective,\n isProviderAllowedInEffective,\n readAgentRuntimeOverrideAllowlist,\n type TenantAllowlistSnapshot,\n} from '../../../../../lib/model-allowlist'\nimport { AiTenantModelAllowlistRepository } from '../../../../../data/repositories/AiTenantModelAllowlistRepository'\nimport { AiAgentRuntimeOverrideRepository } from '../../../../../data/repositories/AiAgentRuntimeOverrideRepository'\n\nfunction modelsForPicker(\n provider: ReturnType<typeof llmProviderRegistry.list>[number],\n allowedModelIds: string[] | undefined,\n): ReadonlyArray<{ id: string; name: string; contextWindow?: number | null; tags?: readonly string[] }> {\n if (provider.defaultModels.length > 0) return provider.defaultModels\n return (allowedModelIds ?? []).map((id) => ({ id, name: id }))\n}\n\nconst agentIdPattern = /^[a-z0-9_]+\\.[a-z0-9_]+$/\n\nconst agentIdParamSchema = z.object({\n agentId: z\n .string()\n .regex(agentIdPattern, 'agentId must match \"<module>.<agent>\" (lowercase, digits, underscores only)'),\n})\n\nexport const openApi: OpenApiRouteDoc = {\n tag: 'AI Assistant',\n summary: 'Available models for an AI agent',\n methods: {\n GET: {\n operationId: 'aiAssistantGetAgentModels',\n summary: 'Get the providers and curated models available for the chat-UI picker for this agent',\n description:\n 'Returns all configured providers with their curated model catalogs, filtered to providers ' +\n 'that have an API key configured in the current environment. When the agent declares ' +\n '`allowRuntimeOverride: false`, the response reflects that constraint so the ' +\n 'UI picker can hide itself. Includes the agent\\'s resolved default provider/model so ' +\n 'the picker can render a \"(default)\" badge next to the right entry. ' +\n 'RBAC: requires the same features as the agent itself (typically `ai_assistant.view`).',\n responses: [\n {\n status: 200,\n description:\n 'Providers and curated models available for the agent picker. ' +\n 'Empty `providers` array when `allowRuntimeOverride` is false.',\n },\n ],\n errors: [\n { status: 401, description: 'Unauthenticated.' },\n { status: 403, description: 'Caller lacks the agent\\'s required features.' },\n { status: 404, description: 'Unknown agent id.' },\n ],\n },\n },\n}\n\nexport const metadata = {\n GET: { requireAuth: true, requireFeatures: ['ai_assistant.view'] },\n}\n\nexport async function GET(\n req: NextRequest,\n { params }: { params: Promise<{ agentId: string }> },\n): Promise<Response> {\n const auth = await getAuthFromRequest(req)\n if (!auth?.sub) {\n return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })\n }\n\n const rawParams = await params\n const paramResult = agentIdParamSchema.safeParse(rawParams)\n if (!paramResult.success) {\n return NextResponse.json(\n { error: 'Invalid agentId path parameter.', code: 'validation_error', issues: paramResult.error.issues },\n { status: 400 },\n )\n }\n const agentId = paramResult.data.agentId\n\n try {\n await loadAgentRegistry()\n\n const container = await createRequestContainer()\n const rbacService = container.resolve<RbacService>('rbacService')\n const acl = await rbacService.loadAcl(auth.sub, {\n tenantId: auth.tenantId,\n organizationId: auth.orgId,\n })\n\n const agent = getAgent(agentId)\n if (!agent) {\n return NextResponse.json({ error: `Agent \"${agentId}\" not found.`, code: 'agent_unknown' }, { status: 404 })\n }\n\n const agentFeatures = agent.requiredFeatures ?? []\n if (agentFeatures.length > 0) {\n const permitted = hasRequiredFeatures(agentFeatures, acl.features, acl.isSuperAdmin)\n if (!permitted) {\n return NextResponse.json(\n {\n error: `Access to agent \"${agentId}\" requires features: ${agentFeatures.join(', ')}.`,\n code: 'agent_features_denied',\n },\n { status: 403 },\n )\n }\n }\n\n const allowRuntimeOverride = resolveAllowRuntimeOverride(agent)\n\n // Load the per-tenant allowlist snapshot so the picker reflects both env\n // and admin-edited tenant constraints (Phase 1780-6).\n let tenantAllowlistSnapshot: TenantAllowlistSnapshot | null = null\n let agentRuntimeOverrideAllowlist: TenantAllowlistSnapshot | null = null\n let tenantRuntimeOverride: {\n providerId: string | null\n modelId: string | null\n baseURL: string | null\n } | null = null\n if (auth.tenantId) {\n try {\n const em = container.resolve<EntityManager>('em')\n const allowlistRepo = new AiTenantModelAllowlistRepository(em)\n tenantAllowlistSnapshot = await allowlistRepo.getSnapshot({\n tenantId: auth.tenantId,\n organizationId: auth.orgId ?? null,\n })\n const runtimeOverrideRepo = new AiAgentRuntimeOverrideRepository(em)\n const runtimeOverrideDefaultRow = await runtimeOverrideRepo.getDefault({\n tenantId: auth.tenantId,\n organizationId: auth.orgId ?? null,\n agentId,\n })\n tenantRuntimeOverride = runtimeOverrideDefaultRow\n ? {\n providerId: runtimeOverrideDefaultRow.providerId ?? null,\n modelId: runtimeOverrideDefaultRow.modelId ?? null,\n baseURL: runtimeOverrideDefaultRow.baseUrl ?? null,\n }\n : null\n const runtimeOverrideRow = await runtimeOverrideRepo.getExact({\n tenantId: auth.tenantId,\n organizationId: auth.orgId ?? null,\n agentId,\n })\n const tenantAgentAllowlist = runtimeOverrideRow\n ? {\n allowedProviders: runtimeOverrideRow.allowedOverrideProviders ?? null,\n allowedModelsByProvider: runtimeOverrideRow.allowedOverrideModelsByProvider ?? {},\n }\n : null\n agentRuntimeOverrideAllowlist = hasAllowlistSnapshotRestrictions(tenantAgentAllowlist)\n ? tenantAgentAllowlist\n : null\n } catch (snapshotError) {\n // Picker still renders against env-only so the UI does not break, but log at\n // error level so an outage is operationally visible. The chat dispatcher\n // refuses to dispatch when this lookup fails, so writes stay safe.\n console.error('[AI Agents Models] Failed to load tenant allowlist:', snapshotError)\n }\n }\n\n // Resolve the agent's current default provider/model for the \"(default)\" badge\n const factory = createModelFactory(container)\n const defaultResolution = factory.resolveModel({\n moduleId: agent.moduleId,\n agentDefaultModel: agent.defaultModel,\n agentDefaultProvider: agent.defaultProvider,\n agentDefaultBaseUrl: agent.defaultBaseUrl,\n allowRuntimeOverride,\n tenantOverride: tenantRuntimeOverride ?? undefined,\n tenantAllowlist: tenantAllowlistSnapshot,\n })\n const defaultProviderId = defaultResolution.providerId\n const defaultModelId = defaultResolution.modelId\n\n // Build provider list \u2014 only configured providers, with curated model\n // catalogs, clipped to the EFFECTIVE allowlist (env \u2229 tenant) so the\n // chat-UI picker can never offer a value the runtime would refuse.\n const env = process.env as Record<string, string | undefined>\n const knownProviderIds = llmProviderRegistry.list().map((p) => p.id)\n const baseEffectiveAllowlist = intersectAllowlists(\n env,\n knownProviderIds,\n tenantAllowlistSnapshot,\n )\n const envAgentAllowlist = readAgentRuntimeOverrideAllowlist(env, agentId, knownProviderIds)\n const effectiveAllowlist = intersectEffectiveAllowlistWithSnapshot(\n intersectEffectiveAllowlistWithSnapshot(\n baseEffectiveAllowlist,\n knownProviderIds,\n envAgentAllowlist,\n ),\n knownProviderIds,\n agentRuntimeOverrideAllowlist,\n )\n const providers = allowRuntimeOverride\n ? llmProviderRegistry.list()\n .filter((provider) => provider.isConfigured())\n .filter((provider) => isProviderAllowedInEffective(effectiveAllowlist, provider.id))\n .map((provider) => {\n const allowedModelIds = effectiveAllowlist.modelsByProvider[provider.id]\n const filteredModels = modelsForPicker(provider, allowedModelIds).filter((model) =>\n isModelAllowedForProviderInEffective(effectiveAllowlist, provider.id, model.id),\n )\n return {\n id: provider.id,\n name: provider.name,\n isDefault: provider.id === defaultProviderId,\n models: filteredModels.map((model) => ({\n id: model.id,\n name: model.name,\n contextWindow: model.contextWindow,\n tags: model.tags,\n isDefault: provider.id === defaultProviderId && model.id === defaultModelId,\n })),\n }\n })\n : []\n\n return NextResponse.json({\n agentId,\n allowRuntimeOverride,\n allowRuntimeModelOverride: allowRuntimeOverride,\n defaultProviderId,\n defaultModelId,\n defaultProviderName: llmProviderRegistry.get(defaultProviderId)?.name ?? defaultProviderId,\n defaultModelName:\n llmProviderRegistry\n .get(defaultProviderId)\n ?.defaultModels.find((model) => model.id === defaultModelId)?.name ?? defaultModelId,\n providers,\n })\n } catch (error) {\n console.error('[AI Agents Models] GET error:', error)\n return NextResponse.json({ error: 'Failed to resolve agent models.' }, { status: 500 })\n }\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }