@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.
- package/.turbo/turbo-build.log +1 -1
- package/AGENTS.md +361 -0
- package/README.md +5 -0
- package/dist/index.js +154 -0
- package/dist/index.js.map +2 -2
- package/dist/modules/ai_assistant/__integration__/TC-AI-002-agent-policy.spec.js +73 -0
- package/dist/modules/ai_assistant/__integration__/TC-AI-002-agent-policy.spec.js.map +7 -0
- package/dist/modules/ai_assistant/__integration__/TC-AI-AGENT-SETTINGS-005-settings-page.spec.js +484 -0
- package/dist/modules/ai_assistant/__integration__/TC-AI-AGENT-SETTINGS-005-settings-page.spec.js.map +7 -0
- package/dist/modules/ai_assistant/__integration__/TC-AI-PLAYGROUND-004-playground.spec.js +251 -0
- package/dist/modules/ai_assistant/__integration__/TC-AI-PLAYGROUND-004-playground.spec.js.map +7 -0
- package/dist/modules/ai_assistant/__integration__/TC-INT-AI-TOOLS.spec.js +91 -0
- package/dist/modules/ai_assistant/__integration__/TC-INT-AI-TOOLS.spec.js.map +7 -0
- package/dist/modules/ai_assistant/ai-tools/attachments-pack.js +202 -0
- package/dist/modules/ai_assistant/ai-tools/attachments-pack.js.map +7 -0
- package/dist/modules/ai_assistant/ai-tools/meta-pack.js +121 -0
- package/dist/modules/ai_assistant/ai-tools/meta-pack.js.map +7 -0
- package/dist/modules/ai_assistant/ai-tools/search-pack.js +94 -0
- package/dist/modules/ai_assistant/ai-tools/search-pack.js.map +7 -0
- package/dist/modules/ai_assistant/ai-tools.js +14 -0
- package/dist/modules/ai_assistant/ai-tools.js.map +7 -0
- package/dist/modules/ai_assistant/api/ai/actions/[id]/cancel/route.js +175 -0
- package/dist/modules/ai_assistant/api/ai/actions/[id]/cancel/route.js.map +7 -0
- package/dist/modules/ai_assistant/api/ai/actions/[id]/confirm/route.js +174 -0
- package/dist/modules/ai_assistant/api/ai/actions/[id]/confirm/route.js.map +7 -0
- package/dist/modules/ai_assistant/api/ai/actions/[id]/route.js +101 -0
- package/dist/modules/ai_assistant/api/ai/actions/[id]/route.js.map +7 -0
- package/dist/modules/ai_assistant/api/ai/agents/[agentId]/mutation-policy/route.js +311 -0
- package/dist/modules/ai_assistant/api/ai/agents/[agentId]/mutation-policy/route.js.map +7 -0
- package/dist/modules/ai_assistant/api/ai/agents/[agentId]/prompt-override/route.js +246 -0
- package/dist/modules/ai_assistant/api/ai/agents/[agentId]/prompt-override/route.js.map +7 -0
- package/dist/modules/ai_assistant/api/ai/agents/route.js +94 -0
- package/dist/modules/ai_assistant/api/ai/agents/route.js.map +7 -0
- package/dist/modules/ai_assistant/api/ai/chat/route.js +173 -0
- package/dist/modules/ai_assistant/api/ai/chat/route.js.map +7 -0
- package/dist/modules/ai_assistant/api/ai/run-object/route.js +167 -0
- package/dist/modules/ai_assistant/api/ai/run-object/route.js.map +7 -0
- package/dist/modules/ai_assistant/backend/config/ai-assistant/agents/AiAgentSettingsPageClient.js +1111 -0
- package/dist/modules/ai_assistant/backend/config/ai-assistant/agents/AiAgentSettingsPageClient.js.map +7 -0
- package/dist/modules/ai_assistant/backend/config/ai-assistant/agents/page.js +10 -0
- package/dist/modules/ai_assistant/backend/config/ai-assistant/agents/page.js.map +7 -0
- package/dist/modules/ai_assistant/backend/config/ai-assistant/agents/page.meta.js +28 -0
- package/dist/modules/ai_assistant/backend/config/ai-assistant/agents/page.meta.js.map +7 -0
- package/dist/modules/ai_assistant/backend/config/ai-assistant/legacy/page.js +10 -0
- package/dist/modules/ai_assistant/backend/config/ai-assistant/legacy/page.js.map +7 -0
- package/dist/modules/ai_assistant/backend/config/ai-assistant/legacy/page.meta.js +30 -0
- package/dist/modules/ai_assistant/backend/config/ai-assistant/legacy/page.meta.js.map +7 -0
- package/dist/modules/ai_assistant/backend/config/ai-assistant/page.js +4 -6
- package/dist/modules/ai_assistant/backend/config/ai-assistant/page.js.map +2 -2
- package/dist/modules/ai_assistant/backend/config/ai-assistant/page.meta.js +1 -21
- package/dist/modules/ai_assistant/backend/config/ai-assistant/page.meta.js.map +2 -2
- package/dist/modules/ai_assistant/backend/config/ai-assistant/playground/AiPlaygroundPageClient.js +462 -0
- package/dist/modules/ai_assistant/backend/config/ai-assistant/playground/AiPlaygroundPageClient.js.map +7 -0
- package/dist/modules/ai_assistant/backend/config/ai-assistant/playground/page.js +10 -0
- package/dist/modules/ai_assistant/backend/config/ai-assistant/playground/page.js.map +7 -0
- package/dist/modules/ai_assistant/backend/config/ai-assistant/playground/page.meta.js +28 -0
- package/dist/modules/ai_assistant/backend/config/ai-assistant/playground/page.meta.js.map +7 -0
- package/dist/modules/ai_assistant/cli.js +78 -12
- package/dist/modules/ai_assistant/cli.js.map +2 -2
- package/dist/modules/ai_assistant/data/entities/AiAgentMutationPolicyOverride.js +5 -0
- package/dist/modules/ai_assistant/data/entities/AiAgentMutationPolicyOverride.js.map +7 -0
- package/dist/modules/ai_assistant/data/entities/AiAgentPromptOverride.js +5 -0
- package/dist/modules/ai_assistant/data/entities/AiAgentPromptOverride.js.map +7 -0
- package/dist/modules/ai_assistant/data/entities/AiPendingAction.js +5 -0
- package/dist/modules/ai_assistant/data/entities/AiPendingAction.js.map +7 -0
- package/dist/modules/ai_assistant/data/entities.js +228 -0
- package/dist/modules/ai_assistant/data/entities.js.map +7 -0
- package/dist/modules/ai_assistant/data/repositories/AiAgentMutationPolicyOverrideRepository.js +95 -0
- package/dist/modules/ai_assistant/data/repositories/AiAgentMutationPolicyOverrideRepository.js.map +7 -0
- package/dist/modules/ai_assistant/data/repositories/AiAgentPromptOverrideRepository.js +95 -0
- package/dist/modules/ai_assistant/data/repositories/AiAgentPromptOverrideRepository.js.map +7 -0
- package/dist/modules/ai_assistant/data/repositories/AiPendingActionRepository.js +223 -0
- package/dist/modules/ai_assistant/data/repositories/AiPendingActionRepository.js.map +7 -0
- package/dist/modules/ai_assistant/events.js +33 -0
- package/dist/modules/ai_assistant/events.js.map +7 -0
- package/dist/modules/ai_assistant/i18n/de.json +252 -0
- package/dist/modules/ai_assistant/i18n/en.json +252 -0
- package/dist/modules/ai_assistant/i18n/es.json +252 -0
- package/dist/modules/ai_assistant/i18n/pl.json +252 -0
- package/dist/modules/ai_assistant/lib/agent-policy.js +168 -0
- package/dist/modules/ai_assistant/lib/agent-policy.js.map +7 -0
- package/dist/modules/ai_assistant/lib/agent-registry.js +195 -0
- package/dist/modules/ai_assistant/lib/agent-registry.js.map +7 -0
- package/dist/modules/ai_assistant/lib/agent-runtime.js +451 -0
- package/dist/modules/ai_assistant/lib/agent-runtime.js.map +7 -0
- package/dist/modules/ai_assistant/lib/agent-tools.js +223 -0
- package/dist/modules/ai_assistant/lib/agent-tools.js.map +7 -0
- package/dist/modules/ai_assistant/lib/agent-transport.js +25 -0
- package/dist/modules/ai_assistant/lib/agent-transport.js.map +7 -0
- package/dist/modules/ai_assistant/lib/ai-agent-definition.js +11 -0
- package/dist/modules/ai_assistant/lib/ai-agent-definition.js.map +7 -0
- package/dist/modules/ai_assistant/lib/ai-agents-generated.d.js +1 -0
- package/dist/modules/ai_assistant/lib/ai-agents-generated.d.js.map +7 -0
- package/dist/modules/ai_assistant/lib/ai-api-operation-runner.js +239 -0
- package/dist/modules/ai_assistant/lib/ai-api-operation-runner.js.map +7 -0
- package/dist/modules/ai_assistant/lib/ai-overrides.js +189 -0
- package/dist/modules/ai_assistant/lib/ai-overrides.js.map +7 -0
- package/dist/modules/ai_assistant/lib/ai-tool-definition.js +7 -0
- package/dist/modules/ai_assistant/lib/ai-tool-definition.js.map +7 -0
- package/dist/modules/ai_assistant/lib/ai-tools-generated.d.js +1 -0
- package/dist/modules/ai_assistant/lib/ai-tools-generated.d.js.map +7 -0
- package/dist/modules/ai_assistant/lib/api-backed-tool.js +48 -0
- package/dist/modules/ai_assistant/lib/api-backed-tool.js.map +7 -0
- package/dist/modules/ai_assistant/lib/attachment-bridge-types.js +1 -0
- package/dist/modules/ai_assistant/lib/attachment-bridge-types.js.map +7 -0
- package/dist/modules/ai_assistant/lib/attachment-parts.js +276 -0
- package/dist/modules/ai_assistant/lib/attachment-parts.js.map +7 -0
- package/dist/modules/ai_assistant/lib/model-factory.js +68 -0
- package/dist/modules/ai_assistant/lib/model-factory.js.map +7 -0
- package/dist/modules/ai_assistant/lib/pending-action-cancel.js +86 -0
- package/dist/modules/ai_assistant/lib/pending-action-cancel.js.map +7 -0
- package/dist/modules/ai_assistant/lib/pending-action-client.js +35 -0
- package/dist/modules/ai_assistant/lib/pending-action-client.js.map +7 -0
- package/dist/modules/ai_assistant/lib/pending-action-executor.js +243 -0
- package/dist/modules/ai_assistant/lib/pending-action-executor.js.map +7 -0
- package/dist/modules/ai_assistant/lib/pending-action-recheck.js +246 -0
- package/dist/modules/ai_assistant/lib/pending-action-recheck.js.map +7 -0
- package/dist/modules/ai_assistant/lib/pending-action-types.js +70 -0
- package/dist/modules/ai_assistant/lib/pending-action-types.js.map +7 -0
- package/dist/modules/ai_assistant/lib/prepare-mutation.js +315 -0
- package/dist/modules/ai_assistant/lib/prepare-mutation.js.map +7 -0
- package/dist/modules/ai_assistant/lib/prompt-composition-types.js +7 -0
- package/dist/modules/ai_assistant/lib/prompt-composition-types.js.map +7 -0
- package/dist/modules/ai_assistant/lib/prompt-override-merge.js +175 -0
- package/dist/modules/ai_assistant/lib/prompt-override-merge.js.map +7 -0
- package/dist/modules/ai_assistant/lib/schema-utils.js +5 -1
- package/dist/modules/ai_assistant/lib/schema-utils.js.map +2 -2
- package/dist/modules/ai_assistant/lib/tool-executor.js +13 -2
- package/dist/modules/ai_assistant/lib/tool-executor.js.map +2 -2
- package/dist/modules/ai_assistant/lib/tool-loader.js +86 -11
- package/dist/modules/ai_assistant/lib/tool-loader.js.map +2 -2
- package/dist/modules/ai_assistant/lib/tool-test-fixtures.js +120 -0
- package/dist/modules/ai_assistant/lib/tool-test-fixtures.js.map +7 -0
- package/dist/modules/ai_assistant/lib/tool-test-runner.js +418 -0
- package/dist/modules/ai_assistant/lib/tool-test-runner.js.map +7 -0
- package/dist/modules/ai_assistant/migrations/Migration20260419100521.js +17 -0
- package/dist/modules/ai_assistant/migrations/Migration20260419100521.js.map +7 -0
- package/dist/modules/ai_assistant/migrations/Migration20260419132948.js +16 -0
- package/dist/modules/ai_assistant/migrations/Migration20260419132948.js.map +7 -0
- package/dist/modules/ai_assistant/migrations/Migration20260419134235.js +17 -0
- package/dist/modules/ai_assistant/migrations/Migration20260419134235.js.map +7 -0
- package/dist/modules/ai_assistant/setup.js +36 -0
- package/dist/modules/ai_assistant/setup.js.map +2 -2
- package/dist/modules/ai_assistant/workers/ai-pending-action-cleanup.js +161 -0
- package/dist/modules/ai_assistant/workers/ai-pending-action-cleanup.js.map +7 -0
- package/generated/entities/ai_agent_mutation_policy_override/index.ts +9 -0
- package/generated/entities/ai_agent_prompt_override/index.ts +10 -0
- package/generated/entities/ai_pending_action/index.ts +24 -0
- package/generated/entities.ids.generated.ts +13 -0
- package/generated/entity-fields-registry.ts +57 -0
- package/jest.config.cjs +7 -0
- package/package.json +4 -4
- package/src/index.ts +215 -0
- package/src/modules/ai_assistant/__integration__/README.md +5 -0
- package/src/modules/ai_assistant/__integration__/TC-AI-002-agent-policy.spec.ts +115 -0
- package/src/modules/ai_assistant/__integration__/TC-AI-AGENT-SETTINGS-005-settings-page.spec.ts +574 -0
- package/src/modules/ai_assistant/__integration__/TC-AI-PLAYGROUND-004-playground.spec.ts +333 -0
- package/src/modules/ai_assistant/__integration__/TC-INT-AI-TOOLS.spec.ts +135 -0
- package/src/modules/ai_assistant/__tests__/events.test.ts +145 -0
- package/src/modules/ai_assistant/__tests__/integration/pending-action-contract.test.ts +1015 -0
- package/src/modules/ai_assistant/__tests__/integration/ws-c-attachment-bridge.test.ts +235 -0
- package/src/modules/ai_assistant/__tests__/integration/ws-c-policy-and-tools.test.ts +330 -0
- package/src/modules/ai_assistant/__tests__/integration/ws-c-tool-pack-coverage.test.ts +285 -0
- package/src/modules/ai_assistant/ai-tools/__tests__/attachments-pack.test.ts +322 -0
- package/src/modules/ai_assistant/ai-tools/__tests__/meta-pack.test.ts +218 -0
- package/src/modules/ai_assistant/ai-tools/__tests__/search-pack.test.ts +192 -0
- package/src/modules/ai_assistant/ai-tools/attachments-pack.ts +269 -0
- package/src/modules/ai_assistant/ai-tools/meta-pack.ts +140 -0
- package/src/modules/ai_assistant/ai-tools/search-pack.ts +122 -0
- package/src/modules/ai_assistant/ai-tools.ts +21 -0
- package/src/modules/ai_assistant/api/ai/actions/[id]/__tests__/route.test.ts +222 -0
- package/src/modules/ai_assistant/api/ai/actions/[id]/cancel/__tests__/route.test.ts +286 -0
- package/src/modules/ai_assistant/api/ai/actions/[id]/cancel/route.ts +237 -0
- package/src/modules/ai_assistant/api/ai/actions/[id]/confirm/__tests__/route.test.ts +339 -0
- package/src/modules/ai_assistant/api/ai/actions/[id]/confirm/route.ts +229 -0
- package/src/modules/ai_assistant/api/ai/actions/[id]/route.ts +142 -0
- package/src/modules/ai_assistant/api/ai/agents/[agentId]/mutation-policy/__tests__/route.test.ts +367 -0
- package/src/modules/ai_assistant/api/ai/agents/[agentId]/mutation-policy/route.ts +380 -0
- package/src/modules/ai_assistant/api/ai/agents/[agentId]/prompt-override/__tests__/route.test.ts +333 -0
- package/src/modules/ai_assistant/api/ai/agents/[agentId]/prompt-override/route.ts +307 -0
- package/src/modules/ai_assistant/api/ai/agents/route.ts +107 -0
- package/src/modules/ai_assistant/api/ai/chat/__tests__/route.test.ts +282 -0
- package/src/modules/ai_assistant/api/ai/chat/route.ts +207 -0
- package/src/modules/ai_assistant/api/ai/run-object/__tests__/route.test.ts +282 -0
- package/src/modules/ai_assistant/api/ai/run-object/route.ts +204 -0
- package/src/modules/ai_assistant/backend/config/ai-assistant/agents/AiAgentSettingsPageClient.tsx +1419 -0
- package/src/modules/ai_assistant/backend/config/ai-assistant/agents/page.meta.ts +26 -0
- package/src/modules/ai_assistant/backend/config/ai-assistant/agents/page.tsx +12 -0
- package/src/modules/ai_assistant/backend/config/ai-assistant/legacy/page.meta.ts +28 -0
- package/src/modules/ai_assistant/backend/config/ai-assistant/legacy/page.tsx +12 -0
- package/src/modules/ai_assistant/backend/config/ai-assistant/page.meta.ts +8 -23
- package/src/modules/ai_assistant/backend/config/ai-assistant/page.tsx +15 -10
- package/src/modules/ai_assistant/backend/config/ai-assistant/playground/AiPlaygroundPageClient.tsx +604 -0
- package/src/modules/ai_assistant/backend/config/ai-assistant/playground/page.meta.ts +26 -0
- package/src/modules/ai_assistant/backend/config/ai-assistant/playground/page.tsx +12 -0
- package/src/modules/ai_assistant/cli.ts +99 -24
- package/src/modules/ai_assistant/data/__tests__/schema-unique-indexes.test.ts +69 -0
- package/src/modules/ai_assistant/data/entities/AiAgentMutationPolicyOverride.ts +7 -0
- package/src/modules/ai_assistant/data/entities/AiAgentPromptOverride.ts +7 -0
- package/src/modules/ai_assistant/data/entities/AiPendingAction.ts +7 -0
- package/src/modules/ai_assistant/data/entities.ts +270 -0
- package/src/modules/ai_assistant/data/repositories/AiAgentMutationPolicyOverrideRepository.ts +129 -0
- package/src/modules/ai_assistant/data/repositories/AiAgentPromptOverrideRepository.ts +132 -0
- package/src/modules/ai_assistant/data/repositories/AiPendingActionRepository.ts +334 -0
- package/src/modules/ai_assistant/data/repositories/__tests__/AiAgentMutationPolicyOverrideRepository.test.ts +195 -0
- package/src/modules/ai_assistant/data/repositories/__tests__/AiAgentPromptOverrideRepository.test.ts +197 -0
- package/src/modules/ai_assistant/data/repositories/__tests__/AiPendingActionRepository.test.ts +357 -0
- package/src/modules/ai_assistant/events.ts +112 -0
- package/src/modules/ai_assistant/i18n/de.json +252 -0
- package/src/modules/ai_assistant/i18n/en.json +252 -0
- package/src/modules/ai_assistant/i18n/es.json +252 -0
- package/src/modules/ai_assistant/i18n/pl.json +252 -0
- package/src/modules/ai_assistant/lib/__tests__/agent-policy.mutation-override.test.ts +203 -0
- package/src/modules/ai_assistant/lib/__tests__/agent-policy.test.ts +385 -0
- package/src/modules/ai_assistant/lib/__tests__/agent-registry.test.ts +217 -0
- package/src/modules/ai_assistant/lib/__tests__/agent-runtime-object.test.ts +329 -0
- package/src/modules/ai_assistant/lib/__tests__/agent-runtime-parity.test.ts +573 -0
- package/src/modules/ai_assistant/lib/__tests__/agent-runtime.test.ts +291 -0
- package/src/modules/ai_assistant/lib/__tests__/agent-tools.test.ts +172 -0
- package/src/modules/ai_assistant/lib/__tests__/agent-transport.test.ts +41 -0
- package/src/modules/ai_assistant/lib/__tests__/ai-agent-definition.test.ts +183 -0
- package/src/modules/ai_assistant/lib/__tests__/ai-api-operation-runner.test.ts +432 -0
- package/src/modules/ai_assistant/lib/__tests__/ai-overrides.test.ts +308 -0
- package/src/modules/ai_assistant/lib/__tests__/api-backed-tool.test.ts +302 -0
- package/src/modules/ai_assistant/lib/__tests__/attachment-bridge-and-prompt-types.test.ts +188 -0
- package/src/modules/ai_assistant/lib/__tests__/attachment-parts.test.ts +531 -0
- package/src/modules/ai_assistant/lib/__tests__/max-steps-budget.integration.test.ts +263 -0
- package/src/modules/ai_assistant/lib/__tests__/model-factory.integration.test.ts +183 -0
- package/src/modules/ai_assistant/lib/__tests__/model-factory.test.ts +168 -0
- package/src/modules/ai_assistant/lib/__tests__/pending-action-cancel.test.ts +235 -0
- package/src/modules/ai_assistant/lib/__tests__/pending-action-client.test.ts +148 -0
- package/src/modules/ai_assistant/lib/__tests__/pending-action-executor.test.ts +348 -0
- package/src/modules/ai_assistant/lib/__tests__/pending-action-recheck.test.ts +378 -0
- package/src/modules/ai_assistant/lib/__tests__/phase-0-additive-contract.test.ts +299 -0
- package/src/modules/ai_assistant/lib/__tests__/prepare-mutation.test.ts +610 -0
- package/src/modules/ai_assistant/lib/__tests__/prompt-override-merge.test.ts +136 -0
- package/src/modules/ai_assistant/lib/__tests__/tool-loader.test.ts +125 -0
- package/src/modules/ai_assistant/lib/agent-policy.ts +270 -0
- package/src/modules/ai_assistant/lib/agent-registry.ts +277 -0
- package/src/modules/ai_assistant/lib/agent-runtime.ts +751 -0
- package/src/modules/ai_assistant/lib/agent-tools.ts +396 -0
- package/src/modules/ai_assistant/lib/agent-transport.ts +51 -0
- package/src/modules/ai_assistant/lib/ai-agent-definition.ts +86 -0
- package/src/modules/ai_assistant/lib/ai-agents-generated.d.ts +18 -0
- package/src/modules/ai_assistant/lib/ai-api-operation-runner.ts +333 -0
- package/src/modules/ai_assistant/lib/ai-overrides.ts +389 -0
- package/src/modules/ai_assistant/lib/ai-tool-definition.ts +7 -0
- package/src/modules/ai_assistant/lib/ai-tools-generated.d.ts +7 -0
- package/src/modules/ai_assistant/lib/api-backed-tool.ts +85 -0
- package/src/modules/ai_assistant/lib/attachment-bridge-types.ts +24 -0
- package/src/modules/ai_assistant/lib/attachment-parts.ts +433 -0
- package/src/modules/ai_assistant/lib/model-factory.ts +212 -0
- package/src/modules/ai_assistant/lib/pending-action-cancel.ts +179 -0
- package/src/modules/ai_assistant/lib/pending-action-client.ts +126 -0
- package/src/modules/ai_assistant/lib/pending-action-executor.ts +424 -0
- package/src/modules/ai_assistant/lib/pending-action-recheck.ts +410 -0
- package/src/modules/ai_assistant/lib/pending-action-types.ts +194 -0
- package/src/modules/ai_assistant/lib/prepare-mutation.ts +448 -0
- package/src/modules/ai_assistant/lib/prompt-composition-types.ts +24 -0
- package/src/modules/ai_assistant/lib/prompt-override-merge.ts +253 -0
- package/src/modules/ai_assistant/lib/schema-utils.ts +14 -2
- package/src/modules/ai_assistant/lib/tool-executor.ts +25 -3
- package/src/modules/ai_assistant/lib/tool-loader.ts +159 -13
- package/src/modules/ai_assistant/lib/tool-test-fixtures.ts +160 -0
- package/src/modules/ai_assistant/lib/tool-test-runner.ts +596 -0
- package/src/modules/ai_assistant/lib/types.ts +105 -2
- package/src/modules/ai_assistant/migrations/.snapshot-open-mercato.json +871 -0
- package/src/modules/ai_assistant/migrations/Migration20260419100521.ts +17 -0
- package/src/modules/ai_assistant/migrations/Migration20260419132948.ts +16 -0
- package/src/modules/ai_assistant/migrations/Migration20260419134235.ts +17 -0
- package/src/modules/ai_assistant/setup.ts +53 -0
- package/src/modules/ai_assistant/workers/__tests__/ai-pending-action-cleanup.test.ts +333 -0
- package/src/modules/ai_assistant/workers/ai-pending-action-cleanup.ts +269 -0
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { test, expect, request as playwrightRequest } from "@playwright/test";
|
|
2
|
+
import { getAuthToken } from "@open-mercato/core/modules/core/__integration__/helpers/api";
|
|
3
|
+
test.describe("TC-AI-002: AI agent dispatcher policy gate", () => {
|
|
4
|
+
const chatPath = "/api/ai_assistant/ai/chat";
|
|
5
|
+
test("unknown agent returns 404 agent_unknown", async ({ request }) => {
|
|
6
|
+
const token = await getAuthToken(request, "superadmin");
|
|
7
|
+
const response = await request.fetch(`${chatPath}?agent=does.not_exist`, {
|
|
8
|
+
method: "POST",
|
|
9
|
+
headers: {
|
|
10
|
+
Authorization: `Bearer ${token}`,
|
|
11
|
+
"Content-Type": "application/json"
|
|
12
|
+
},
|
|
13
|
+
data: JSON.stringify({
|
|
14
|
+
messages: [{ role: "user", content: "hello" }]
|
|
15
|
+
})
|
|
16
|
+
});
|
|
17
|
+
expect(response.status()).toBe(404);
|
|
18
|
+
const body = await response.json();
|
|
19
|
+
expect(body.code).toBe("agent_unknown");
|
|
20
|
+
});
|
|
21
|
+
test("malformed agent query param returns 400 validation_error", async ({ request }) => {
|
|
22
|
+
const token = await getAuthToken(request, "superadmin");
|
|
23
|
+
const response = await request.fetch(`${chatPath}?agent=BadAgent`, {
|
|
24
|
+
method: "POST",
|
|
25
|
+
headers: {
|
|
26
|
+
Authorization: `Bearer ${token}`,
|
|
27
|
+
"Content-Type": "application/json"
|
|
28
|
+
},
|
|
29
|
+
data: JSON.stringify({
|
|
30
|
+
messages: [{ role: "user", content: "hello" }]
|
|
31
|
+
})
|
|
32
|
+
});
|
|
33
|
+
expect(response.status()).toBe(400);
|
|
34
|
+
const body = await response.json();
|
|
35
|
+
expect(body.code).toBe("validation_error");
|
|
36
|
+
});
|
|
37
|
+
test("missing agent query param returns 400 validation_error", async ({ request }) => {
|
|
38
|
+
const token = await getAuthToken(request, "superadmin");
|
|
39
|
+
const response = await request.fetch(chatPath, {
|
|
40
|
+
method: "POST",
|
|
41
|
+
headers: {
|
|
42
|
+
Authorization: `Bearer ${token}`,
|
|
43
|
+
"Content-Type": "application/json"
|
|
44
|
+
},
|
|
45
|
+
data: JSON.stringify({
|
|
46
|
+
messages: [{ role: "user", content: "hello" }]
|
|
47
|
+
})
|
|
48
|
+
});
|
|
49
|
+
expect(response.status()).toBe(400);
|
|
50
|
+
const body = await response.json();
|
|
51
|
+
expect(body.code).toBe("validation_error");
|
|
52
|
+
});
|
|
53
|
+
test("unauthenticated caller returns 401", async ({ baseURL }) => {
|
|
54
|
+
const context = await playwrightRequest.newContext({ baseURL });
|
|
55
|
+
try {
|
|
56
|
+
const response = await context.fetch(`${chatPath}?agent=does.not_exist`, {
|
|
57
|
+
method: "POST",
|
|
58
|
+
headers: {
|
|
59
|
+
"Content-Type": "application/json"
|
|
60
|
+
},
|
|
61
|
+
data: JSON.stringify({
|
|
62
|
+
messages: [{ role: "user", content: "hello" }]
|
|
63
|
+
})
|
|
64
|
+
});
|
|
65
|
+
expect(response.status()).toBe(401);
|
|
66
|
+
const body = await response.json();
|
|
67
|
+
expect(body.code === "unauthenticated" || body.error === "Unauthorized").toBe(true);
|
|
68
|
+
} finally {
|
|
69
|
+
await context.dispose();
|
|
70
|
+
}
|
|
71
|
+
});
|
|
72
|
+
});
|
|
73
|
+
//# sourceMappingURL=TC-AI-002-agent-policy.spec.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../../src/modules/ai_assistant/__integration__/TC-AI-002-agent-policy.spec.ts"],
|
|
4
|
+
"sourcesContent": ["import { test, expect, request as playwrightRequest } from '@playwright/test';\nimport { getAuthToken } from '@open-mercato/core/modules/core/__integration__/helpers/api';\n\n/**\n * TC-AI-002: AI agent dispatcher policy gate (Step 3.13 / Phase 3 WS-C).\n *\n * Exercises the HTTP surface of the Step 3.2 / 3.3 runtime policy gate end-to-end\n * against the authenticated superadmin session. The dispatcher is\n * `POST /api/ai_assistant/ai/chat?agent=<module>.<agent>` (see\n * `packages/ai-assistant/src/modules/ai_assistant/lib/agent-transport.ts`).\n *\n * Asserted:\n * - Unknown agent id -> 404 + `{ code: 'agent_unknown' }` \u2014 verified against\n * `packages/ai-assistant/src/modules/ai_assistant/lib/agent-policy.ts`.\n * - Malformed agent query param -> 400 + `{ code: 'validation_error' }`.\n * - Missing agent query param -> 400 + `{ code: 'validation_error' }`.\n * - Unauthenticated caller -> 401 + `{ code: 'unauthenticated' }`.\n *\n * The chat dispatcher requires `ai_assistant.view` \u2014 superadmin always carries it.\n * The \"forbidden agent\" branch (`agent_features_denied`) is exercised at the\n * runtime-helper layer by the Jest integration suite at\n * `packages/ai-assistant/src/modules/ai_assistant/__tests__/integration/\n * ws-c-policy-and-tools.test.ts` because no seeded non-superadmin role has the\n * shape required to create a deterministic forbidden-agent fixture without\n * touching ACL tables.\n */\ntest.describe('TC-AI-002: AI agent dispatcher policy gate', () => {\n const chatPath = '/api/ai_assistant/ai/chat';\n\n test('unknown agent returns 404 agent_unknown', async ({ request }) => {\n const token = await getAuthToken(request, 'superadmin');\n\n const response = await request.fetch(`${chatPath}?agent=does.not_exist`, {\n method: 'POST',\n headers: {\n Authorization: `Bearer ${token}`,\n 'Content-Type': 'application/json',\n },\n data: JSON.stringify({\n messages: [{ role: 'user', content: 'hello' }],\n }),\n });\n\n expect(response.status()).toBe(404);\n const body = (await response.json()) as { code?: unknown; error?: unknown };\n expect(body.code).toBe('agent_unknown');\n });\n\n test('malformed agent query param returns 400 validation_error', async ({ request }) => {\n const token = await getAuthToken(request, 'superadmin');\n\n const response = await request.fetch(`${chatPath}?agent=BadAgent`, {\n method: 'POST',\n headers: {\n Authorization: `Bearer ${token}`,\n 'Content-Type': 'application/json',\n },\n data: JSON.stringify({\n messages: [{ role: 'user', content: 'hello' }],\n }),\n });\n\n expect(response.status()).toBe(400);\n const body = (await response.json()) as { code?: unknown };\n expect(body.code).toBe('validation_error');\n });\n\n test('missing agent query param returns 400 validation_error', async ({ request }) => {\n const token = await getAuthToken(request, 'superadmin');\n\n const response = await request.fetch(chatPath, {\n method: 'POST',\n headers: {\n Authorization: `Bearer ${token}`,\n 'Content-Type': 'application/json',\n },\n data: JSON.stringify({\n messages: [{ role: 'user', content: 'hello' }],\n }),\n });\n\n expect(response.status()).toBe(400);\n const body = (await response.json()) as { code?: unknown };\n expect(body.code).toBe('validation_error');\n });\n\n test('unauthenticated caller returns 401', async ({ baseURL }) => {\n // Use a fresh request context with no cookies \u2014 shared `request` fixture\n // accumulates the superadmin session cookie from prior getAuthToken calls.\n const context = await playwrightRequest.newContext({ baseURL });\n try {\n const response = await context.fetch(`${chatPath}?agent=does.not_exist`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n },\n data: JSON.stringify({\n messages: [{ role: 'user', content: 'hello' }],\n }),\n });\n\n expect(response.status()).toBe(401);\n // The framework-level `requireAuth` guard (page metadata) short-circuits\n // unauthenticated POSTs before the route handler runs, so the response\n // carries the framework's `{ error: 'Unauthorized' }` envelope rather\n // than the route-local `{ code: 'unauthenticated' }` envelope. Either\n // shape satisfies the contract \"unauth callers cannot invoke the\n // dispatcher\"; we accept whichever one the framework actually returns.\n const body = (await response.json()) as { code?: unknown; error?: unknown };\n expect(body.code === 'unauthenticated' || body.error === 'Unauthorized').toBe(true);\n } finally {\n await context.dispose();\n }\n });\n});\n"],
|
|
5
|
+
"mappings": "AAAA,SAAS,MAAM,QAAQ,WAAW,yBAAyB;AAC3D,SAAS,oBAAoB;AAyB7B,KAAK,SAAS,8CAA8C,MAAM;AAChE,QAAM,WAAW;AAEjB,OAAK,2CAA2C,OAAO,EAAE,QAAQ,MAAM;AACrE,UAAM,QAAQ,MAAM,aAAa,SAAS,YAAY;AAEtD,UAAM,WAAW,MAAM,QAAQ,MAAM,GAAG,QAAQ,yBAAyB;AAAA,MACvE,QAAQ;AAAA,MACR,SAAS;AAAA,QACP,eAAe,UAAU,KAAK;AAAA,QAC9B,gBAAgB;AAAA,MAClB;AAAA,MACA,MAAM,KAAK,UAAU;AAAA,QACnB,UAAU,CAAC,EAAE,MAAM,QAAQ,SAAS,QAAQ,CAAC;AAAA,MAC/C,CAAC;AAAA,IACH,CAAC;AAED,WAAO,SAAS,OAAO,CAAC,EAAE,KAAK,GAAG;AAClC,UAAM,OAAQ,MAAM,SAAS,KAAK;AAClC,WAAO,KAAK,IAAI,EAAE,KAAK,eAAe;AAAA,EACxC,CAAC;AAED,OAAK,4DAA4D,OAAO,EAAE,QAAQ,MAAM;AACtF,UAAM,QAAQ,MAAM,aAAa,SAAS,YAAY;AAEtD,UAAM,WAAW,MAAM,QAAQ,MAAM,GAAG,QAAQ,mBAAmB;AAAA,MACjE,QAAQ;AAAA,MACR,SAAS;AAAA,QACP,eAAe,UAAU,KAAK;AAAA,QAC9B,gBAAgB;AAAA,MAClB;AAAA,MACA,MAAM,KAAK,UAAU;AAAA,QACnB,UAAU,CAAC,EAAE,MAAM,QAAQ,SAAS,QAAQ,CAAC;AAAA,MAC/C,CAAC;AAAA,IACH,CAAC;AAED,WAAO,SAAS,OAAO,CAAC,EAAE,KAAK,GAAG;AAClC,UAAM,OAAQ,MAAM,SAAS,KAAK;AAClC,WAAO,KAAK,IAAI,EAAE,KAAK,kBAAkB;AAAA,EAC3C,CAAC;AAED,OAAK,0DAA0D,OAAO,EAAE,QAAQ,MAAM;AACpF,UAAM,QAAQ,MAAM,aAAa,SAAS,YAAY;AAEtD,UAAM,WAAW,MAAM,QAAQ,MAAM,UAAU;AAAA,MAC7C,QAAQ;AAAA,MACR,SAAS;AAAA,QACP,eAAe,UAAU,KAAK;AAAA,QAC9B,gBAAgB;AAAA,MAClB;AAAA,MACA,MAAM,KAAK,UAAU;AAAA,QACnB,UAAU,CAAC,EAAE,MAAM,QAAQ,SAAS,QAAQ,CAAC;AAAA,MAC/C,CAAC;AAAA,IACH,CAAC;AAED,WAAO,SAAS,OAAO,CAAC,EAAE,KAAK,GAAG;AAClC,UAAM,OAAQ,MAAM,SAAS,KAAK;AAClC,WAAO,KAAK,IAAI,EAAE,KAAK,kBAAkB;AAAA,EAC3C,CAAC;AAED,OAAK,sCAAsC,OAAO,EAAE,QAAQ,MAAM;AAGhE,UAAM,UAAU,MAAM,kBAAkB,WAAW,EAAE,QAAQ,CAAC;AAC9D,QAAI;AACF,YAAM,WAAW,MAAM,QAAQ,MAAM,GAAG,QAAQ,yBAAyB;AAAA,QACvE,QAAQ;AAAA,QACR,SAAS;AAAA,UACP,gBAAgB;AAAA,QAClB;AAAA,QACA,MAAM,KAAK,UAAU;AAAA,UACnB,UAAU,CAAC,EAAE,MAAM,QAAQ,SAAS,QAAQ,CAAC;AAAA,QAC/C,CAAC;AAAA,MACH,CAAC;AAED,aAAO,SAAS,OAAO,CAAC,EAAE,KAAK,GAAG;AAOlC,YAAM,OAAQ,MAAM,SAAS,KAAK;AAClC,aAAO,KAAK,SAAS,qBAAqB,KAAK,UAAU,cAAc,EAAE,KAAK,IAAI;AAAA,IACpF,UAAE;AACA,YAAM,QAAQ,QAAQ;AAAA,IACxB;AAAA,EACF,CAAC;AACH,CAAC;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
package/dist/modules/ai_assistant/__integration__/TC-AI-AGENT-SETTINGS-005-settings-page.spec.js
ADDED
|
@@ -0,0 +1,484 @@
|
|
|
1
|
+
import { test, expect } from "@playwright/test";
|
|
2
|
+
import { login } from "@open-mercato/core/modules/core/__integration__/helpers/auth";
|
|
3
|
+
test.describe("TC-AI-AGENT-SETTINGS-005: AI Agent settings", () => {
|
|
4
|
+
const settingsPath = "/backend/config/ai-assistant/agents";
|
|
5
|
+
test("page loads and renders empty-state for superadmin when no agents are registered", async ({
|
|
6
|
+
page
|
|
7
|
+
}) => {
|
|
8
|
+
await login(page, "superadmin");
|
|
9
|
+
await page.route("**/api/ai_assistant/ai/agents", async (route) => {
|
|
10
|
+
await route.fulfill({
|
|
11
|
+
status: 200,
|
|
12
|
+
contentType: "application/json",
|
|
13
|
+
body: JSON.stringify({ agents: [], total: 0 })
|
|
14
|
+
});
|
|
15
|
+
});
|
|
16
|
+
await page.route("**/api/ai_assistant/ai/agents/*/prompt-override", async (route) => {
|
|
17
|
+
await route.fulfill({
|
|
18
|
+
status: 200,
|
|
19
|
+
contentType: "application/json",
|
|
20
|
+
body: JSON.stringify({
|
|
21
|
+
pending: true,
|
|
22
|
+
agentId: "stub.stub",
|
|
23
|
+
message: "Persistence lands in Phase 3 Step 5.3."
|
|
24
|
+
})
|
|
25
|
+
});
|
|
26
|
+
});
|
|
27
|
+
await page.goto(settingsPath, { waitUntil: "domcontentloaded" });
|
|
28
|
+
const emptyState = page.getByText(/No AI agents are registered/i).first();
|
|
29
|
+
const container = page.locator("[data-ai-agent-settings]");
|
|
30
|
+
const loadError = page.locator("[data-ai-agent-settings-error]");
|
|
31
|
+
await expect(emptyState.or(container).or(loadError)).toBeVisible({ timeout: 15e3 });
|
|
32
|
+
if (await emptyState.isVisible().catch(() => false)) {
|
|
33
|
+
await expect(emptyState).toBeVisible();
|
|
34
|
+
} else if (await container.isVisible().catch(() => false)) {
|
|
35
|
+
const picker = page.locator("[data-ai-agent-settings-picker]");
|
|
36
|
+
await expect(picker).toBeVisible();
|
|
37
|
+
} else {
|
|
38
|
+
await expect(loadError).toBeVisible();
|
|
39
|
+
}
|
|
40
|
+
});
|
|
41
|
+
test("unauthenticated visit redirects to login", async ({ browser }) => {
|
|
42
|
+
const context = await browser.newContext();
|
|
43
|
+
const page = await context.newPage();
|
|
44
|
+
try {
|
|
45
|
+
await page.goto(settingsPath, { waitUntil: "domcontentloaded" });
|
|
46
|
+
await page.waitForURL(/\/login/, { timeout: 15e3 });
|
|
47
|
+
expect(page.url()).toMatch(/\/login/);
|
|
48
|
+
} finally {
|
|
49
|
+
await context.close();
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
test("Cmd/Ctrl+Enter inside a prompt-override textarea triggers the placeholder save", async ({
|
|
53
|
+
page
|
|
54
|
+
}) => {
|
|
55
|
+
test.setTimeout(12e4);
|
|
56
|
+
await login(page, "superadmin");
|
|
57
|
+
await page.route("**/api/ai_assistant/ai/agents", async (route) => {
|
|
58
|
+
await route.fulfill({
|
|
59
|
+
status: 200,
|
|
60
|
+
contentType: "application/json",
|
|
61
|
+
body: JSON.stringify({
|
|
62
|
+
agents: [
|
|
63
|
+
{
|
|
64
|
+
id: "customers.assistant",
|
|
65
|
+
moduleId: "customers",
|
|
66
|
+
label: "Customers assistant",
|
|
67
|
+
description: "Answers questions about customer records.",
|
|
68
|
+
systemPrompt: "You are a customers assistant.",
|
|
69
|
+
executionMode: "chat",
|
|
70
|
+
mutationPolicy: "read-only",
|
|
71
|
+
readOnly: true,
|
|
72
|
+
maxSteps: 10,
|
|
73
|
+
allowedTools: ["customers.list_people"],
|
|
74
|
+
tools: [
|
|
75
|
+
{
|
|
76
|
+
name: "customers.list_people",
|
|
77
|
+
displayName: "List people",
|
|
78
|
+
isMutation: false,
|
|
79
|
+
registered: true
|
|
80
|
+
}
|
|
81
|
+
],
|
|
82
|
+
requiredFeatures: [],
|
|
83
|
+
acceptedMediaTypes: [],
|
|
84
|
+
hasOutputSchema: false
|
|
85
|
+
}
|
|
86
|
+
],
|
|
87
|
+
total: 1
|
|
88
|
+
})
|
|
89
|
+
});
|
|
90
|
+
});
|
|
91
|
+
let saveCalls = 0;
|
|
92
|
+
await page.route("**/api/ai_assistant/ai/agents/*/prompt-override", async (route) => {
|
|
93
|
+
const method = route.request().method();
|
|
94
|
+
if (method === "GET") {
|
|
95
|
+
await route.fulfill({
|
|
96
|
+
status: 200,
|
|
97
|
+
contentType: "application/json",
|
|
98
|
+
body: JSON.stringify({
|
|
99
|
+
agentId: "customers.assistant",
|
|
100
|
+
override: null,
|
|
101
|
+
versions: []
|
|
102
|
+
})
|
|
103
|
+
});
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
saveCalls += 1;
|
|
107
|
+
await route.fulfill({
|
|
108
|
+
status: 200,
|
|
109
|
+
contentType: "application/json",
|
|
110
|
+
body: JSON.stringify({
|
|
111
|
+
pending: true,
|
|
112
|
+
agentId: "customers.assistant",
|
|
113
|
+
message: "Persistence lands in Phase 3 Step 5.3."
|
|
114
|
+
})
|
|
115
|
+
});
|
|
116
|
+
});
|
|
117
|
+
await page.route("**/api/ai_assistant/ai/agents/*/mutation-policy", async (route) => {
|
|
118
|
+
await route.fulfill({
|
|
119
|
+
status: 200,
|
|
120
|
+
contentType: "application/json",
|
|
121
|
+
body: JSON.stringify({
|
|
122
|
+
agentId: "customers.assistant",
|
|
123
|
+
codeDeclared: "read-only",
|
|
124
|
+
override: null
|
|
125
|
+
})
|
|
126
|
+
});
|
|
127
|
+
});
|
|
128
|
+
await page.goto(settingsPath, { waitUntil: "domcontentloaded" });
|
|
129
|
+
const container = page.locator("[data-ai-agent-settings]");
|
|
130
|
+
await expect(container).toBeVisible({ timeout: 6e4 });
|
|
131
|
+
const toggle = page.locator('[data-ai-agent-prompt-toggle="role"]');
|
|
132
|
+
await toggle.click({ timeout: 3e4 });
|
|
133
|
+
const textarea = page.locator('[data-ai-agent-prompt-override="role"]');
|
|
134
|
+
await expect(textarea).toBeVisible();
|
|
135
|
+
await textarea.fill("Custom role text.");
|
|
136
|
+
await textarea.press("Meta+Enter");
|
|
137
|
+
await page.waitForTimeout(250);
|
|
138
|
+
if (saveCalls === 0) {
|
|
139
|
+
await textarea.press("Control+Enter");
|
|
140
|
+
await page.waitForTimeout(250);
|
|
141
|
+
}
|
|
142
|
+
expect(saveCalls).toBeGreaterThanOrEqual(1);
|
|
143
|
+
});
|
|
144
|
+
test("selecting an agent renders detail panel with meta badges, tool toggles, and attachment policy", async ({
|
|
145
|
+
page
|
|
146
|
+
}) => {
|
|
147
|
+
test.setTimeout(12e4);
|
|
148
|
+
await login(page, "superadmin");
|
|
149
|
+
await page.route("**/api/ai_assistant/ai/agents", async (route) => {
|
|
150
|
+
await route.fulfill({
|
|
151
|
+
status: 200,
|
|
152
|
+
contentType: "application/json",
|
|
153
|
+
body: JSON.stringify({
|
|
154
|
+
agents: [
|
|
155
|
+
{
|
|
156
|
+
id: "customers.account_assistant",
|
|
157
|
+
moduleId: "customers",
|
|
158
|
+
label: "Customers account assistant",
|
|
159
|
+
description: "Answers questions about customer records.",
|
|
160
|
+
systemPrompt: "You are a helpful read-only customers assistant.",
|
|
161
|
+
executionMode: "chat",
|
|
162
|
+
mutationPolicy: "read-only",
|
|
163
|
+
readOnly: true,
|
|
164
|
+
maxSteps: 10,
|
|
165
|
+
allowedTools: ["customers.list_people", "customers.get_person"],
|
|
166
|
+
tools: [
|
|
167
|
+
{
|
|
168
|
+
name: "customers.list_people",
|
|
169
|
+
displayName: "List people",
|
|
170
|
+
isMutation: false,
|
|
171
|
+
registered: true
|
|
172
|
+
},
|
|
173
|
+
{
|
|
174
|
+
name: "customers.get_person",
|
|
175
|
+
displayName: "Get person",
|
|
176
|
+
isMutation: false,
|
|
177
|
+
registered: true
|
|
178
|
+
}
|
|
179
|
+
],
|
|
180
|
+
requiredFeatures: ["customers.people.view"],
|
|
181
|
+
acceptedMediaTypes: ["image/png", "application/pdf"],
|
|
182
|
+
hasOutputSchema: false
|
|
183
|
+
}
|
|
184
|
+
],
|
|
185
|
+
total: 1
|
|
186
|
+
})
|
|
187
|
+
});
|
|
188
|
+
});
|
|
189
|
+
await page.goto(settingsPath, { waitUntil: "domcontentloaded" });
|
|
190
|
+
const container = page.locator("[data-ai-agent-settings]");
|
|
191
|
+
await expect(container).toBeVisible({ timeout: 6e4 });
|
|
192
|
+
const detailPanel = page.locator('[data-ai-agent-detail="customers.account_assistant"]');
|
|
193
|
+
await expect(detailPanel).toBeVisible();
|
|
194
|
+
const listRow = page.locator('[data-ai-agent-tool-row="customers.list_people"]');
|
|
195
|
+
await expect(listRow).toBeVisible();
|
|
196
|
+
const listSwitch = page.locator('[data-ai-agent-tool-switch="customers.list_people"]');
|
|
197
|
+
const ariaDisabled = await listSwitch.getAttribute("aria-disabled");
|
|
198
|
+
const dataDisabled = await listSwitch.getAttribute("data-disabled");
|
|
199
|
+
const disabledAttr = await listSwitch.getAttribute("disabled");
|
|
200
|
+
expect(
|
|
201
|
+
ariaDisabled === "true" || dataDisabled !== null || disabledAttr !== null
|
|
202
|
+
).toBe(true);
|
|
203
|
+
const pngBadge = page.locator('[data-ai-agent-attachment-badge="image/png"]');
|
|
204
|
+
await expect(pngBadge).toBeVisible();
|
|
205
|
+
const pdfBadge = page.locator('[data-ai-agent-attachment-badge="application/pdf"]');
|
|
206
|
+
await expect(pdfBadge).toBeVisible();
|
|
207
|
+
});
|
|
208
|
+
test("saving a valid override surfaces the new version in the history panel (Step 5.3)", async ({
|
|
209
|
+
page
|
|
210
|
+
}) => {
|
|
211
|
+
test.setTimeout(12e4);
|
|
212
|
+
await login(page, "superadmin");
|
|
213
|
+
await page.route("**/api/ai_assistant/ai/agents", async (route) => {
|
|
214
|
+
await route.fulfill({
|
|
215
|
+
status: 200,
|
|
216
|
+
contentType: "application/json",
|
|
217
|
+
body: JSON.stringify({
|
|
218
|
+
agents: [
|
|
219
|
+
{
|
|
220
|
+
id: "customers.account_assistant",
|
|
221
|
+
moduleId: "customers",
|
|
222
|
+
label: "Customers account assistant",
|
|
223
|
+
description: "Answers questions about customer records.",
|
|
224
|
+
systemPrompt: "You are a read-only customers assistant.",
|
|
225
|
+
executionMode: "chat",
|
|
226
|
+
mutationPolicy: "read-only",
|
|
227
|
+
readOnly: true,
|
|
228
|
+
maxSteps: 10,
|
|
229
|
+
allowedTools: [],
|
|
230
|
+
tools: [],
|
|
231
|
+
requiredFeatures: [],
|
|
232
|
+
acceptedMediaTypes: [],
|
|
233
|
+
hasOutputSchema: false
|
|
234
|
+
}
|
|
235
|
+
],
|
|
236
|
+
total: 1
|
|
237
|
+
})
|
|
238
|
+
});
|
|
239
|
+
});
|
|
240
|
+
let overrideState = null;
|
|
241
|
+
await page.route("**/api/ai_assistant/ai/agents/*/prompt-override", async (route) => {
|
|
242
|
+
const request = route.request();
|
|
243
|
+
if (request.method() === "GET") {
|
|
244
|
+
await route.fulfill({
|
|
245
|
+
status: 200,
|
|
246
|
+
contentType: "application/json",
|
|
247
|
+
body: JSON.stringify(
|
|
248
|
+
overrideState ? {
|
|
249
|
+
agentId: "customers.account_assistant",
|
|
250
|
+
override: {
|
|
251
|
+
id: "row-1",
|
|
252
|
+
agentId: "customers.account_assistant",
|
|
253
|
+
version: overrideState.version,
|
|
254
|
+
sections: overrideState.sections,
|
|
255
|
+
notes: null,
|
|
256
|
+
createdByUserId: null,
|
|
257
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
258
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
259
|
+
},
|
|
260
|
+
versions: [
|
|
261
|
+
{
|
|
262
|
+
id: "row-1",
|
|
263
|
+
agentId: "customers.account_assistant",
|
|
264
|
+
version: overrideState.version,
|
|
265
|
+
sections: overrideState.sections,
|
|
266
|
+
notes: null,
|
|
267
|
+
createdByUserId: null,
|
|
268
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
269
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
270
|
+
}
|
|
271
|
+
]
|
|
272
|
+
} : {
|
|
273
|
+
agentId: "customers.account_assistant",
|
|
274
|
+
override: null,
|
|
275
|
+
versions: []
|
|
276
|
+
}
|
|
277
|
+
)
|
|
278
|
+
});
|
|
279
|
+
return;
|
|
280
|
+
}
|
|
281
|
+
const body = JSON.parse(request.postData() || "{}");
|
|
282
|
+
const sections = body.sections ?? body.overrides ?? {};
|
|
283
|
+
overrideState = { version: (overrideState?.version ?? 0) + 1, sections };
|
|
284
|
+
await route.fulfill({
|
|
285
|
+
status: 200,
|
|
286
|
+
contentType: "application/json",
|
|
287
|
+
body: JSON.stringify({
|
|
288
|
+
ok: true,
|
|
289
|
+
agentId: "customers.account_assistant",
|
|
290
|
+
version: overrideState.version,
|
|
291
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
292
|
+
})
|
|
293
|
+
});
|
|
294
|
+
});
|
|
295
|
+
await page.goto(settingsPath, { waitUntil: "domcontentloaded" });
|
|
296
|
+
const container = page.locator("[data-ai-agent-settings]");
|
|
297
|
+
await expect(container).toBeVisible({ timeout: 6e4 });
|
|
298
|
+
const toggle = page.locator('[data-ai-agent-prompt-toggle="role"]');
|
|
299
|
+
await toggle.click();
|
|
300
|
+
const textarea = page.locator('[data-ai-agent-prompt-override="role"]');
|
|
301
|
+
await textarea.fill("Tenant-specific tone.");
|
|
302
|
+
const saveButton = page.locator("[data-ai-agent-prompt-save]");
|
|
303
|
+
await saveButton.click();
|
|
304
|
+
const successAlert = page.locator('[data-ai-agent-prompt-state="success"]');
|
|
305
|
+
await expect(successAlert).toBeVisible({ timeout: 15e3 });
|
|
306
|
+
const historyRow = page.locator('[data-ai-agent-override-history-row="1"]');
|
|
307
|
+
await expect(historyRow).toBeVisible({ timeout: 15e3 });
|
|
308
|
+
});
|
|
309
|
+
test("reserved-key override surfaces the validation error in the UI (Step 5.3)", async ({
|
|
310
|
+
page
|
|
311
|
+
}) => {
|
|
312
|
+
test.setTimeout(12e4);
|
|
313
|
+
await login(page, "superadmin");
|
|
314
|
+
await page.route("**/api/ai_assistant/ai/agents", async (route) => {
|
|
315
|
+
await route.fulfill({
|
|
316
|
+
status: 200,
|
|
317
|
+
contentType: "application/json",
|
|
318
|
+
body: JSON.stringify({
|
|
319
|
+
agents: [
|
|
320
|
+
{
|
|
321
|
+
id: "customers.account_assistant",
|
|
322
|
+
moduleId: "customers",
|
|
323
|
+
label: "Customers account assistant",
|
|
324
|
+
description: "Answers questions about customer records.",
|
|
325
|
+
systemPrompt: "You are a customers assistant.",
|
|
326
|
+
executionMode: "chat",
|
|
327
|
+
mutationPolicy: "read-only",
|
|
328
|
+
readOnly: true,
|
|
329
|
+
maxSteps: 10,
|
|
330
|
+
allowedTools: [],
|
|
331
|
+
tools: [],
|
|
332
|
+
requiredFeatures: [],
|
|
333
|
+
acceptedMediaTypes: [],
|
|
334
|
+
hasOutputSchema: false
|
|
335
|
+
}
|
|
336
|
+
],
|
|
337
|
+
total: 1
|
|
338
|
+
})
|
|
339
|
+
});
|
|
340
|
+
});
|
|
341
|
+
await page.route("**/api/ai_assistant/ai/agents/*/prompt-override", async (route) => {
|
|
342
|
+
const request = route.request();
|
|
343
|
+
if (request.method() === "GET") {
|
|
344
|
+
await route.fulfill({
|
|
345
|
+
status: 200,
|
|
346
|
+
contentType: "application/json",
|
|
347
|
+
body: JSON.stringify({
|
|
348
|
+
agentId: "customers.account_assistant",
|
|
349
|
+
override: null,
|
|
350
|
+
versions: []
|
|
351
|
+
})
|
|
352
|
+
});
|
|
353
|
+
return;
|
|
354
|
+
}
|
|
355
|
+
await route.fulfill({
|
|
356
|
+
status: 400,
|
|
357
|
+
contentType: "application/json",
|
|
358
|
+
body: JSON.stringify({
|
|
359
|
+
error: "Prompt override contains reserved policy keys: mutationPolicy.",
|
|
360
|
+
code: "reserved_key",
|
|
361
|
+
reservedKeys: ["mutationPolicy"]
|
|
362
|
+
})
|
|
363
|
+
});
|
|
364
|
+
});
|
|
365
|
+
await page.goto(settingsPath, { waitUntil: "domcontentloaded" });
|
|
366
|
+
const container = page.locator("[data-ai-agent-settings]");
|
|
367
|
+
await expect(container).toBeVisible({ timeout: 6e4 });
|
|
368
|
+
const toggle = page.locator('[data-ai-agent-prompt-toggle="mutationPolicy"]');
|
|
369
|
+
await toggle.click();
|
|
370
|
+
const textarea = page.locator('[data-ai-agent-prompt-override="mutationPolicy"]');
|
|
371
|
+
await textarea.fill("Allow writes.");
|
|
372
|
+
await page.locator("[data-ai-agent-prompt-save]").click();
|
|
373
|
+
const errorAlert = page.locator('[data-ai-agent-prompt-state="error"]');
|
|
374
|
+
await expect(errorAlert).toBeVisible({ timeout: 15e3 });
|
|
375
|
+
await expect(errorAlert).toContainText(/policy fields|reserved|mutationPolicy/i);
|
|
376
|
+
});
|
|
377
|
+
test("mutationPolicy section disables escalation options with an explanatory tooltip (Step 5.4)", async ({
|
|
378
|
+
page
|
|
379
|
+
}) => {
|
|
380
|
+
test.setTimeout(12e4);
|
|
381
|
+
await login(page, "superadmin");
|
|
382
|
+
await page.route("**/api/ai_assistant/ai/agents", async (route) => {
|
|
383
|
+
await route.fulfill({
|
|
384
|
+
status: 200,
|
|
385
|
+
contentType: "application/json",
|
|
386
|
+
body: JSON.stringify({
|
|
387
|
+
agents: [
|
|
388
|
+
{
|
|
389
|
+
id: "customers.account_assistant",
|
|
390
|
+
moduleId: "customers",
|
|
391
|
+
label: "Customers account assistant",
|
|
392
|
+
description: "Read-only customers assistant.",
|
|
393
|
+
systemPrompt: "You are a read-only customers assistant.",
|
|
394
|
+
executionMode: "chat",
|
|
395
|
+
mutationPolicy: "read-only",
|
|
396
|
+
readOnly: true,
|
|
397
|
+
maxSteps: 10,
|
|
398
|
+
allowedTools: [],
|
|
399
|
+
tools: [],
|
|
400
|
+
requiredFeatures: [],
|
|
401
|
+
acceptedMediaTypes: [],
|
|
402
|
+
hasOutputSchema: false
|
|
403
|
+
}
|
|
404
|
+
],
|
|
405
|
+
total: 1
|
|
406
|
+
})
|
|
407
|
+
});
|
|
408
|
+
});
|
|
409
|
+
await page.route("**/api/ai_assistant/ai/agents/*/prompt-override", async (route) => {
|
|
410
|
+
await route.fulfill({
|
|
411
|
+
status: 200,
|
|
412
|
+
contentType: "application/json",
|
|
413
|
+
body: JSON.stringify({
|
|
414
|
+
agentId: "customers.account_assistant",
|
|
415
|
+
override: null,
|
|
416
|
+
versions: []
|
|
417
|
+
})
|
|
418
|
+
});
|
|
419
|
+
});
|
|
420
|
+
await page.route(
|
|
421
|
+
"**/api/ai_assistant/ai/agents/*/mutation-policy",
|
|
422
|
+
async (route) => {
|
|
423
|
+
const request = route.request();
|
|
424
|
+
if (request.method() === "GET") {
|
|
425
|
+
await route.fulfill({
|
|
426
|
+
status: 200,
|
|
427
|
+
contentType: "application/json",
|
|
428
|
+
body: JSON.stringify({
|
|
429
|
+
agentId: "customers.account_assistant",
|
|
430
|
+
codeDeclared: "read-only",
|
|
431
|
+
override: null
|
|
432
|
+
})
|
|
433
|
+
});
|
|
434
|
+
return;
|
|
435
|
+
}
|
|
436
|
+
await route.fulfill({
|
|
437
|
+
status: 400,
|
|
438
|
+
contentType: "application/json",
|
|
439
|
+
body: JSON.stringify({
|
|
440
|
+
error: `Cannot set mutationPolicy="confirm-required" for agent "customers.account_assistant": the agent's code-declared policy is "read-only".`,
|
|
441
|
+
code: "escalation_not_allowed",
|
|
442
|
+
codeDeclared: "read-only",
|
|
443
|
+
requested: "confirm-required"
|
|
444
|
+
})
|
|
445
|
+
});
|
|
446
|
+
}
|
|
447
|
+
);
|
|
448
|
+
await page.goto(settingsPath, { waitUntil: "domcontentloaded" });
|
|
449
|
+
const container = page.locator("[data-ai-agent-settings]");
|
|
450
|
+
await expect(container).toBeVisible({ timeout: 6e4 });
|
|
451
|
+
const confirmOption = page.locator(
|
|
452
|
+
'[data-ai-agent-mutation-policy-option="confirm-required"]'
|
|
453
|
+
);
|
|
454
|
+
await expect(confirmOption).toBeVisible({ timeout: 15e3 });
|
|
455
|
+
const disabledAttr = await confirmOption.getAttribute(
|
|
456
|
+
"data-ai-agent-mutation-policy-option-disabled"
|
|
457
|
+
);
|
|
458
|
+
expect(disabledAttr).toBe("true");
|
|
459
|
+
const readOnlyOption = page.locator(
|
|
460
|
+
'[data-ai-agent-mutation-policy-option="read-only"]'
|
|
461
|
+
);
|
|
462
|
+
const readOnlyDisabled = await readOnlyOption.getAttribute(
|
|
463
|
+
"data-ai-agent-mutation-policy-option-disabled"
|
|
464
|
+
);
|
|
465
|
+
expect(readOnlyDisabled).toBe("false");
|
|
466
|
+
});
|
|
467
|
+
test("mutationPolicy escalation attempts are rejected by the server with 400 + escalation_not_allowed (Step 5.4)", async ({
|
|
468
|
+
request
|
|
469
|
+
}) => {
|
|
470
|
+
const response = await request.post(
|
|
471
|
+
"/api/ai_assistant/ai/agents/customers.account_assistant/mutation-policy",
|
|
472
|
+
{
|
|
473
|
+
data: { mutationPolicy: "confirm-required" },
|
|
474
|
+
headers: { "content-type": "application/json" }
|
|
475
|
+
}
|
|
476
|
+
);
|
|
477
|
+
expect([400, 401, 403]).toContain(response.status());
|
|
478
|
+
if (response.status() === 400) {
|
|
479
|
+
const body = await response.json();
|
|
480
|
+
expect(body.code).toBe("escalation_not_allowed");
|
|
481
|
+
}
|
|
482
|
+
});
|
|
483
|
+
});
|
|
484
|
+
//# sourceMappingURL=TC-AI-AGENT-SETTINGS-005-settings-page.spec.js.map
|
package/dist/modules/ai_assistant/__integration__/TC-AI-AGENT-SETTINGS-005-settings-page.spec.js.map
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../../src/modules/ai_assistant/__integration__/TC-AI-AGENT-SETTINGS-005-settings-page.spec.ts"],
|
|
4
|
+
"sourcesContent": ["import { test, expect } from '@playwright/test';\nimport { login } from '@open-mercato/core/modules/core/__integration__/helpers/auth';\n\n/**\n * TC-AI-AGENT-SETTINGS-005: AI Agent settings page smoke (Step 4.5 / Phase 2 WS-B).\n *\n * Covers the backend agent settings route at\n * `/backend/config/ai-assistant/agents`. The page is guarded by\n * `ai_assistant.settings.manage`; superadmin always holds it. The agent\n * registry is empty by default in CI (first production agent lands in Step 4.7),\n * so the primary assertion is that the empty-state renders.\n *\n * We also assert that an unauthenticated visit redirects to `/login`.\n * The `POST /api/ai_assistant/ai/agents/:agentId/prompt-override` route is\n * stubbed so the \"save overrides\" path is exercisable end-to-end when an\n * agent happens to be present in the registry.\n */\ntest.describe('TC-AI-AGENT-SETTINGS-005: AI Agent settings', () => {\n const settingsPath = '/backend/config/ai-assistant/agents';\n\n test('page loads and renders empty-state for superadmin when no agents are registered', async ({\n page,\n }) => {\n await login(page, 'superadmin');\n\n await page.route('**/api/ai_assistant/ai/agents', async (route) => {\n await route.fulfill({\n status: 200,\n contentType: 'application/json',\n body: JSON.stringify({ agents: [], total: 0 }),\n });\n });\n\n await page.route('**/api/ai_assistant/ai/agents/*/prompt-override', async (route) => {\n await route.fulfill({\n status: 200,\n contentType: 'application/json',\n body: JSON.stringify({\n pending: true,\n agentId: 'stub.stub',\n message: 'Persistence lands in Phase 3 Step 5.3.',\n }),\n });\n });\n\n await page.goto(settingsPath, { waitUntil: 'domcontentloaded' });\n\n const emptyState = page.getByText(/No AI agents are registered/i).first();\n const container = page.locator('[data-ai-agent-settings]');\n const loadError = page.locator('[data-ai-agent-settings-error]');\n\n await expect(emptyState.or(container).or(loadError)).toBeVisible({ timeout: 15_000 });\n\n if (await emptyState.isVisible().catch(() => false)) {\n await expect(emptyState).toBeVisible();\n } else if (await container.isVisible().catch(() => false)) {\n // Registry non-empty in this env \u2014 the detail panel should have rendered.\n const picker = page.locator('[data-ai-agent-settings-picker]');\n await expect(picker).toBeVisible();\n } else {\n await expect(loadError).toBeVisible();\n }\n });\n\n test('unauthenticated visit redirects to login', async ({ browser }) => {\n const context = await browser.newContext();\n const page = await context.newPage();\n try {\n await page.goto(settingsPath, { waitUntil: 'domcontentloaded' });\n await page.waitForURL(/\\/login/, { timeout: 15_000 });\n expect(page.url()).toMatch(/\\/login/);\n } finally {\n await context.close();\n }\n });\n\n test('Cmd/Ctrl+Enter inside a prompt-override textarea triggers the placeholder save', async ({\n page,\n }) => {\n // CI cold-compile of the settings page + stubbed agents fetch can exceed\n // the default 20s test timeout; give this test 2 minutes.\n test.setTimeout(120_000);\n await login(page, 'superadmin');\n\n // Non-empty registry so the detail panel renders and the textareas exist.\n await page.route('**/api/ai_assistant/ai/agents', async (route) => {\n await route.fulfill({\n status: 200,\n contentType: 'application/json',\n body: JSON.stringify({\n agents: [\n {\n id: 'customers.assistant',\n moduleId: 'customers',\n label: 'Customers assistant',\n description: 'Answers questions about customer records.',\n systemPrompt: 'You are a customers assistant.',\n executionMode: 'chat',\n mutationPolicy: 'read-only',\n readOnly: true,\n maxSteps: 10,\n allowedTools: ['customers.list_people'],\n tools: [\n {\n name: 'customers.list_people',\n displayName: 'List people',\n isMutation: false,\n registered: true,\n },\n ],\n requiredFeatures: [],\n acceptedMediaTypes: [],\n hasOutputSchema: false,\n },\n ],\n total: 1,\n }),\n });\n });\n\n let saveCalls = 0;\n await page.route('**/api/ai_assistant/ai/agents/*/prompt-override', async (route) => {\n const method = route.request().method();\n if (method === 'GET') {\n await route.fulfill({\n status: 200,\n contentType: 'application/json',\n body: JSON.stringify({\n agentId: 'customers.assistant',\n override: null,\n versions: [],\n }),\n });\n return;\n }\n saveCalls += 1;\n await route.fulfill({\n status: 200,\n contentType: 'application/json',\n body: JSON.stringify({\n pending: true,\n agentId: 'customers.assistant',\n message: 'Persistence lands in Phase 3 Step 5.3.',\n }),\n });\n });\n\n // Also stub the mutation-policy GET so selecting the fake agent doesn't\n // 404 into an error panel on CI (the real route doesn't know our fake id).\n await page.route('**/api/ai_assistant/ai/agents/*/mutation-policy', async (route) => {\n await route.fulfill({\n status: 200,\n contentType: 'application/json',\n body: JSON.stringify({\n agentId: 'customers.assistant',\n codeDeclared: 'read-only',\n override: null,\n }),\n });\n });\n\n await page.goto(settingsPath, { waitUntil: 'domcontentloaded' });\n\n const container = page.locator('[data-ai-agent-settings]');\n await expect(container).toBeVisible({ timeout: 60_000 });\n\n // Flip the `role` section into override mode so a textarea is present.\n const toggle = page.locator('[data-ai-agent-prompt-toggle=\"role\"]');\n await toggle.click({ timeout: 30_000 });\n\n const textarea = page.locator('[data-ai-agent-prompt-override=\"role\"]');\n await expect(textarea).toBeVisible();\n await textarea.fill('Custom role text.');\n\n // Cmd+Enter (mac) / Ctrl+Enter (others) \u2014 Playwright supports both.\n await textarea.press('Meta+Enter');\n await page.waitForTimeout(250);\n if (saveCalls === 0) {\n await textarea.press('Control+Enter');\n await page.waitForTimeout(250);\n }\n\n expect(saveCalls).toBeGreaterThanOrEqual(1);\n });\n\n test('selecting an agent renders detail panel with meta badges, tool toggles, and attachment policy', async ({\n page,\n }) => {\n test.setTimeout(120_000);\n await login(page, 'superadmin');\n\n await page.route('**/api/ai_assistant/ai/agents', async (route) => {\n await route.fulfill({\n status: 200,\n contentType: 'application/json',\n body: JSON.stringify({\n agents: [\n {\n id: 'customers.account_assistant',\n moduleId: 'customers',\n label: 'Customers account assistant',\n description: 'Answers questions about customer records.',\n systemPrompt: 'You are a helpful read-only customers assistant.',\n executionMode: 'chat',\n mutationPolicy: 'read-only',\n readOnly: true,\n maxSteps: 10,\n allowedTools: ['customers.list_people', 'customers.get_person'],\n tools: [\n {\n name: 'customers.list_people',\n displayName: 'List people',\n isMutation: false,\n registered: true,\n },\n {\n name: 'customers.get_person',\n displayName: 'Get person',\n isMutation: false,\n registered: true,\n },\n ],\n requiredFeatures: ['customers.people.view'],\n acceptedMediaTypes: ['image/png', 'application/pdf'],\n hasOutputSchema: false,\n },\n ],\n total: 1,\n }),\n });\n });\n\n await page.goto(settingsPath, { waitUntil: 'domcontentloaded' });\n\n const container = page.locator('[data-ai-agent-settings]');\n await expect(container).toBeVisible({ timeout: 60_000 });\n\n // Detail panel renders for the first (only) agent.\n const detailPanel = page.locator('[data-ai-agent-detail=\"customers.account_assistant\"]');\n await expect(detailPanel).toBeVisible();\n\n // Tool rows render for every declared tool, with disabled switches.\n const listRow = page.locator('[data-ai-agent-tool-row=\"customers.list_people\"]');\n await expect(listRow).toBeVisible();\n const listSwitch = page.locator('[data-ai-agent-tool-switch=\"customers.list_people\"]');\n // Radix `Switch` primitives surface the disabled state via\n // `aria-disabled`/`data-disabled` rather than the native attribute,\n // so assert both. Either one is sufficient evidence the switch is\n // read-only in Phase 2.\n const ariaDisabled = await listSwitch.getAttribute('aria-disabled');\n const dataDisabled = await listSwitch.getAttribute('data-disabled');\n const disabledAttr = await listSwitch.getAttribute('disabled');\n expect(\n ariaDisabled === 'true' || dataDisabled !== null || disabledAttr !== null,\n ).toBe(true);\n\n // Attachment policy badges surface for each declared media type.\n const pngBadge = page.locator('[data-ai-agent-attachment-badge=\"image/png\"]');\n await expect(pngBadge).toBeVisible();\n const pdfBadge = page.locator('[data-ai-agent-attachment-badge=\"application/pdf\"]');\n await expect(pdfBadge).toBeVisible();\n });\n\n test('saving a valid override surfaces the new version in the history panel (Step 5.3)', async ({\n page,\n }) => {\n test.setTimeout(120_000);\n await login(page, 'superadmin');\n\n await page.route('**/api/ai_assistant/ai/agents', async (route) => {\n await route.fulfill({\n status: 200,\n contentType: 'application/json',\n body: JSON.stringify({\n agents: [\n {\n id: 'customers.account_assistant',\n moduleId: 'customers',\n label: 'Customers account assistant',\n description: 'Answers questions about customer records.',\n systemPrompt: 'You are a read-only customers assistant.',\n executionMode: 'chat',\n mutationPolicy: 'read-only',\n readOnly: true,\n maxSteps: 10,\n allowedTools: [],\n tools: [],\n requiredFeatures: [],\n acceptedMediaTypes: [],\n hasOutputSchema: false,\n },\n ],\n total: 1,\n }),\n });\n });\n\n let overrideState: { version: number; sections: Record<string, string> } | null = null;\n await page.route('**/api/ai_assistant/ai/agents/*/prompt-override', async (route) => {\n const request = route.request();\n if (request.method() === 'GET') {\n await route.fulfill({\n status: 200,\n contentType: 'application/json',\n body: JSON.stringify(\n overrideState\n ? {\n agentId: 'customers.account_assistant',\n override: {\n id: 'row-1',\n agentId: 'customers.account_assistant',\n version: overrideState.version,\n sections: overrideState.sections,\n notes: null,\n createdByUserId: null,\n createdAt: new Date().toISOString(),\n updatedAt: new Date().toISOString(),\n },\n versions: [\n {\n id: 'row-1',\n agentId: 'customers.account_assistant',\n version: overrideState.version,\n sections: overrideState.sections,\n notes: null,\n createdByUserId: null,\n createdAt: new Date().toISOString(),\n updatedAt: new Date().toISOString(),\n },\n ],\n }\n : {\n agentId: 'customers.account_assistant',\n override: null,\n versions: [],\n },\n ),\n });\n return;\n }\n const body = JSON.parse(request.postData() || '{}');\n const sections = body.sections ?? body.overrides ?? {};\n overrideState = { version: (overrideState?.version ?? 0) + 1, sections };\n await route.fulfill({\n status: 200,\n contentType: 'application/json',\n body: JSON.stringify({\n ok: true,\n agentId: 'customers.account_assistant',\n version: overrideState.version,\n updatedAt: new Date().toISOString(),\n }),\n });\n });\n\n await page.goto(settingsPath, { waitUntil: 'domcontentloaded' });\n\n const container = page.locator('[data-ai-agent-settings]');\n await expect(container).toBeVisible({ timeout: 60_000 });\n\n const toggle = page.locator('[data-ai-agent-prompt-toggle=\"role\"]');\n await toggle.click();\n const textarea = page.locator('[data-ai-agent-prompt-override=\"role\"]');\n await textarea.fill('Tenant-specific tone.');\n\n const saveButton = page.locator('[data-ai-agent-prompt-save]');\n await saveButton.click();\n\n // Success alert surfaces.\n const successAlert = page.locator('[data-ai-agent-prompt-state=\"success\"]');\n await expect(successAlert).toBeVisible({ timeout: 15_000 });\n\n // History panel shows version 1.\n const historyRow = page.locator('[data-ai-agent-override-history-row=\"1\"]');\n await expect(historyRow).toBeVisible({ timeout: 15_000 });\n });\n\n test('reserved-key override surfaces the validation error in the UI (Step 5.3)', async ({\n page,\n }) => {\n test.setTimeout(120_000);\n await login(page, 'superadmin');\n\n await page.route('**/api/ai_assistant/ai/agents', async (route) => {\n await route.fulfill({\n status: 200,\n contentType: 'application/json',\n body: JSON.stringify({\n agents: [\n {\n id: 'customers.account_assistant',\n moduleId: 'customers',\n label: 'Customers account assistant',\n description: 'Answers questions about customer records.',\n systemPrompt: 'You are a customers assistant.',\n executionMode: 'chat',\n mutationPolicy: 'read-only',\n readOnly: true,\n maxSteps: 10,\n allowedTools: [],\n tools: [],\n requiredFeatures: [],\n acceptedMediaTypes: [],\n hasOutputSchema: false,\n },\n ],\n total: 1,\n }),\n });\n });\n\n await page.route('**/api/ai_assistant/ai/agents/*/prompt-override', async (route) => {\n const request = route.request();\n if (request.method() === 'GET') {\n await route.fulfill({\n status: 200,\n contentType: 'application/json',\n body: JSON.stringify({\n agentId: 'customers.account_assistant',\n override: null,\n versions: [],\n }),\n });\n return;\n }\n await route.fulfill({\n status: 400,\n contentType: 'application/json',\n body: JSON.stringify({\n error: 'Prompt override contains reserved policy keys: mutationPolicy.',\n code: 'reserved_key',\n reservedKeys: ['mutationPolicy'],\n }),\n });\n });\n\n await page.goto(settingsPath, { waitUntil: 'domcontentloaded' });\n\n const container = page.locator('[data-ai-agent-settings]');\n await expect(container).toBeVisible({ timeout: 60_000 });\n\n const toggle = page.locator('[data-ai-agent-prompt-toggle=\"mutationPolicy\"]');\n await toggle.click();\n const textarea = page.locator('[data-ai-agent-prompt-override=\"mutationPolicy\"]');\n await textarea.fill('Allow writes.');\n await page.locator('[data-ai-agent-prompt-save]').click();\n\n const errorAlert = page.locator('[data-ai-agent-prompt-state=\"error\"]');\n await expect(errorAlert).toBeVisible({ timeout: 15_000 });\n await expect(errorAlert).toContainText(/policy fields|reserved|mutationPolicy/i);\n });\n\n test('mutationPolicy section disables escalation options with an explanatory tooltip (Step 5.4)', async ({\n page,\n }) => {\n test.setTimeout(120_000);\n await login(page, 'superadmin');\n\n await page.route('**/api/ai_assistant/ai/agents', async (route) => {\n await route.fulfill({\n status: 200,\n contentType: 'application/json',\n body: JSON.stringify({\n agents: [\n {\n id: 'customers.account_assistant',\n moduleId: 'customers',\n label: 'Customers account assistant',\n description: 'Read-only customers assistant.',\n systemPrompt: 'You are a read-only customers assistant.',\n executionMode: 'chat',\n mutationPolicy: 'read-only',\n readOnly: true,\n maxSteps: 10,\n allowedTools: [],\n tools: [],\n requiredFeatures: [],\n acceptedMediaTypes: [],\n hasOutputSchema: false,\n },\n ],\n total: 1,\n }),\n });\n });\n\n await page.route('**/api/ai_assistant/ai/agents/*/prompt-override', async (route) => {\n await route.fulfill({\n status: 200,\n contentType: 'application/json',\n body: JSON.stringify({\n agentId: 'customers.account_assistant',\n override: null,\n versions: [],\n }),\n });\n });\n\n await page.route(\n '**/api/ai_assistant/ai/agents/*/mutation-policy',\n async (route) => {\n const request = route.request();\n if (request.method() === 'GET') {\n await route.fulfill({\n status: 200,\n contentType: 'application/json',\n body: JSON.stringify({\n agentId: 'customers.account_assistant',\n codeDeclared: 'read-only',\n override: null,\n }),\n });\n return;\n }\n await route.fulfill({\n status: 400,\n contentType: 'application/json',\n body: JSON.stringify({\n error:\n 'Cannot set mutationPolicy=\"confirm-required\" for agent \"customers.account_assistant\": the agent\\'s code-declared policy is \"read-only\".',\n code: 'escalation_not_allowed',\n codeDeclared: 'read-only',\n requested: 'confirm-required',\n }),\n });\n },\n );\n\n await page.goto(settingsPath, { waitUntil: 'domcontentloaded' });\n\n const container = page.locator('[data-ai-agent-settings]');\n await expect(container).toBeVisible({ timeout: 60_000 });\n\n const confirmOption = page.locator(\n '[data-ai-agent-mutation-policy-option=\"confirm-required\"]',\n );\n await expect(confirmOption).toBeVisible({ timeout: 15_000 });\n const disabledAttr = await confirmOption.getAttribute(\n 'data-ai-agent-mutation-policy-option-disabled',\n );\n expect(disabledAttr).toBe('true');\n\n // The read-only option (matching code-declared) is selectable.\n const readOnlyOption = page.locator(\n '[data-ai-agent-mutation-policy-option=\"read-only\"]',\n );\n const readOnlyDisabled = await readOnlyOption.getAttribute(\n 'data-ai-agent-mutation-policy-option-disabled',\n );\n expect(readOnlyDisabled).toBe('false');\n });\n\n test('mutationPolicy escalation attempts are rejected by the server with 400 + escalation_not_allowed (Step 5.4)', async ({\n request,\n }) => {\n // Use request.fetch via the request fixture (auth cookies won't be set, so we\n // expect 401; the test's primary purpose is to confirm the route is mounted\n // AND to document the escalation-guard contract surface. When auth is\n // available in the env the guard returns 400 + escalation_not_allowed.)\n const response = await request.post(\n '/api/ai_assistant/ai/agents/customers.account_assistant/mutation-policy',\n {\n data: { mutationPolicy: 'confirm-required' },\n headers: { 'content-type': 'application/json' },\n },\n );\n // Route should exist (never 404 on the path itself).\n expect([400, 401, 403]).toContain(response.status());\n if (response.status() === 400) {\n const body = await response.json();\n expect(body.code).toBe('escalation_not_allowed');\n }\n });\n});\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|