@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,418 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import fs from "node:fs";
|
|
3
|
+
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
4
|
+
import { createRequestContainer } from "@open-mercato/shared/lib/di/container";
|
|
5
|
+
import { getFixture } from "./tool-test-fixtures.js";
|
|
6
|
+
import { prepareMutation } from "./prepare-mutation.js";
|
|
7
|
+
import { executePendingActionConfirm } from "./pending-action-executor.js";
|
|
8
|
+
function isToolDefinition(value) {
|
|
9
|
+
if (!value || typeof value !== "object") return false;
|
|
10
|
+
const candidate = value;
|
|
11
|
+
return typeof candidate.name === "string" && typeof candidate.description === "string" && candidate.inputSchema !== void 0 && typeof candidate.handler === "function";
|
|
12
|
+
}
|
|
13
|
+
function findGeneratedAiToolsPath() {
|
|
14
|
+
const here = (() => {
|
|
15
|
+
try {
|
|
16
|
+
return fileURLToPath(import.meta.url);
|
|
17
|
+
} catch {
|
|
18
|
+
return null;
|
|
19
|
+
}
|
|
20
|
+
})();
|
|
21
|
+
if (!here) return null;
|
|
22
|
+
let cursor = path.dirname(here);
|
|
23
|
+
for (let i = 0; i < 12; i++) {
|
|
24
|
+
const candidate = path.join(cursor, "apps", "mercato", ".mercato", "generated", "ai-tools.generated.ts");
|
|
25
|
+
if (fs.existsSync(candidate)) return candidate;
|
|
26
|
+
const next = path.dirname(cursor);
|
|
27
|
+
if (next === cursor) break;
|
|
28
|
+
cursor = next;
|
|
29
|
+
}
|
|
30
|
+
const fromCwd = path.resolve(process.cwd(), "apps", "mercato", ".mercato", "generated", "ai-tools.generated.ts");
|
|
31
|
+
if (fs.existsSync(fromCwd)) return fromCwd;
|
|
32
|
+
const fromCwdDirect = path.resolve(process.cwd(), ".mercato", "generated", "ai-tools.generated.ts");
|
|
33
|
+
if (fs.existsSync(fromCwdDirect)) return fromCwdDirect;
|
|
34
|
+
return null;
|
|
35
|
+
}
|
|
36
|
+
async function compileAndImportGenerated(tsPath) {
|
|
37
|
+
const jsPath = tsPath.replace(/\.ts$/, ".mjs");
|
|
38
|
+
const appRoot = path.dirname(path.dirname(path.dirname(tsPath)));
|
|
39
|
+
const tsExists = fs.existsSync(tsPath);
|
|
40
|
+
if (!tsExists) {
|
|
41
|
+
throw new Error(`Generated file not found: ${tsPath}`);
|
|
42
|
+
}
|
|
43
|
+
const jsExists = fs.existsSync(jsPath);
|
|
44
|
+
const needsCompile = !jsExists || fs.statSync(tsPath).mtimeMs > fs.statSync(jsPath).mtimeMs;
|
|
45
|
+
if (needsCompile) {
|
|
46
|
+
const esbuild = await import("esbuild");
|
|
47
|
+
const tsSource = fs.readFileSync(tsPath, "utf-8");
|
|
48
|
+
const aliasRewritten = tsSource.replace(
|
|
49
|
+
/from\s+["']@\/([^"']+)["']/g,
|
|
50
|
+
(_match, p1) => {
|
|
51
|
+
const target = path.join(appRoot, p1);
|
|
52
|
+
const candidate = fs.existsSync(target) ? target : fs.existsSync(target + ".ts") ? target + ".ts" : target;
|
|
53
|
+
return `from ${JSON.stringify(pathToFileURL(candidate).href)}`;
|
|
54
|
+
}
|
|
55
|
+
).replace(
|
|
56
|
+
/import\s*\(\s*["']@\/([^"']+)["']\s*\)/g,
|
|
57
|
+
(_match, p1) => {
|
|
58
|
+
const target = path.join(appRoot, p1);
|
|
59
|
+
const candidate = fs.existsSync(target) ? target : fs.existsSync(target + ".ts") ? target + ".ts" : target;
|
|
60
|
+
return `import(${JSON.stringify(pathToFileURL(candidate).href)})`;
|
|
61
|
+
}
|
|
62
|
+
);
|
|
63
|
+
const result = await esbuild.transform(aliasRewritten, {
|
|
64
|
+
loader: "ts",
|
|
65
|
+
format: "esm",
|
|
66
|
+
target: "node18",
|
|
67
|
+
sourcemap: false,
|
|
68
|
+
sourcefile: tsPath
|
|
69
|
+
});
|
|
70
|
+
fs.writeFileSync(jsPath, result.code);
|
|
71
|
+
}
|
|
72
|
+
return await import(pathToFileURL(jsPath).href);
|
|
73
|
+
}
|
|
74
|
+
async function ensureApiRouteManifestsRegistered(generatedDir) {
|
|
75
|
+
const tsPath = path.join(generatedDir, "api-routes.generated.ts");
|
|
76
|
+
if (!fs.existsSync(tsPath)) return;
|
|
77
|
+
try {
|
|
78
|
+
const mod = await compileAndImportGenerated(tsPath);
|
|
79
|
+
const apiRoutes = mod.apiRoutes;
|
|
80
|
+
if (!Array.isArray(apiRoutes)) {
|
|
81
|
+
console.warn("[tool-test-runner] api-routes.generated.mjs returned no apiRoutes array");
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
const registry = await import("@open-mercato/shared/modules/registry");
|
|
85
|
+
registry.registerApiRouteManifests(
|
|
86
|
+
apiRoutes
|
|
87
|
+
);
|
|
88
|
+
if (process.env.OM_TOOL_TEST_DEBUG === "1") {
|
|
89
|
+
console.log(
|
|
90
|
+
`[tool-test-runner] Registered ${apiRoutes.length} api-route manifests; getApiRouteManifests().length=${registry.getApiRouteManifests().length}`
|
|
91
|
+
);
|
|
92
|
+
}
|
|
93
|
+
} catch (error) {
|
|
94
|
+
console.warn(
|
|
95
|
+
"[tool-test-runner] Could not register api-routes manifest:",
|
|
96
|
+
error instanceof Error ? error.message : error
|
|
97
|
+
);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
async function loadGeneratedTools() {
|
|
101
|
+
const tsPath = findGeneratedAiToolsPath();
|
|
102
|
+
if (!tsPath) {
|
|
103
|
+
throw new Error(
|
|
104
|
+
"Could not locate apps/<app>/.mercato/generated/ai-tools.generated.ts. Run `yarn generate` first."
|
|
105
|
+
);
|
|
106
|
+
}
|
|
107
|
+
await ensureApiRouteManifestsRegistered(path.dirname(tsPath));
|
|
108
|
+
const mod = await compileAndImportGenerated(tsPath);
|
|
109
|
+
const entries = mod.aiToolConfigEntriesRaw ?? mod.aiToolConfigEntries ?? [];
|
|
110
|
+
const result = [];
|
|
111
|
+
for (const entry of entries) {
|
|
112
|
+
if (!entry || typeof entry.moduleId !== "string") continue;
|
|
113
|
+
const tools = Array.isArray(entry.tools) ? entry.tools.filter(isToolDefinition) : [];
|
|
114
|
+
result.push({ moduleId: entry.moduleId, tools });
|
|
115
|
+
}
|
|
116
|
+
return result;
|
|
117
|
+
}
|
|
118
|
+
async function pickDefaultTenant(container) {
|
|
119
|
+
try {
|
|
120
|
+
const em = container.resolve("em");
|
|
121
|
+
const conn = em.getConnection();
|
|
122
|
+
const tenantRows = await conn.execute(
|
|
123
|
+
`SELECT id FROM tenants WHERE deleted_at IS NULL ORDER BY created_at ASC LIMIT 1`
|
|
124
|
+
);
|
|
125
|
+
const tenantId = Array.isArray(tenantRows) && tenantRows[0] ? String(tenantRows[0].id) : null;
|
|
126
|
+
if (!tenantId) return null;
|
|
127
|
+
const escaped = tenantId.replace(/'/g, "''");
|
|
128
|
+
const orgRows = await conn.execute(
|
|
129
|
+
`SELECT id FROM organizations WHERE tenant_id = '${escaped}' AND deleted_at IS NULL ORDER BY created_at ASC LIMIT 1`
|
|
130
|
+
);
|
|
131
|
+
const organizationId = Array.isArray(orgRows) && orgRows[0] ? String(orgRows[0].id) : null;
|
|
132
|
+
const userRows = await conn.execute(
|
|
133
|
+
`SELECT id FROM users WHERE tenant_id = '${escaped}' AND deleted_at IS NULL ORDER BY created_at ASC LIMIT 1`
|
|
134
|
+
);
|
|
135
|
+
const userId = Array.isArray(userRows) && userRows[0] ? String(userRows[0].id) : null;
|
|
136
|
+
return { tenantId, organizationId, userId };
|
|
137
|
+
} catch (error) {
|
|
138
|
+
if (process.env.OM_TOOL_TEST_DEBUG === "1") {
|
|
139
|
+
console.warn("[tool-test-runner] pickDefaultTenant failed:", error);
|
|
140
|
+
}
|
|
141
|
+
return null;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
function buildSuperAdminContext(container, tenantId, organizationId, userId) {
|
|
145
|
+
return {
|
|
146
|
+
tenantId,
|
|
147
|
+
organizationId,
|
|
148
|
+
userId,
|
|
149
|
+
container,
|
|
150
|
+
userFeatures: ["*"],
|
|
151
|
+
isSuperAdmin: true
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
function clipPreview(value) {
|
|
155
|
+
try {
|
|
156
|
+
const json = JSON.stringify(value);
|
|
157
|
+
if (json.length <= 400) return value;
|
|
158
|
+
return `${json.slice(0, 400)}\u2026(truncated, ${json.length} bytes)`;
|
|
159
|
+
} catch {
|
|
160
|
+
return "[unserializable]";
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
function dummyAgentForTool(tool) {
|
|
164
|
+
const policy = "destructive-confirm-required";
|
|
165
|
+
return {
|
|
166
|
+
id: `__test__.${tool.name}`,
|
|
167
|
+
moduleId: tool.name.split(".")[0] ?? "__test__",
|
|
168
|
+
label: "Tool Test Runner",
|
|
169
|
+
description: "Synthetic agent used by the tool test runner",
|
|
170
|
+
systemPrompt: "",
|
|
171
|
+
allowedTools: [tool.name],
|
|
172
|
+
readOnly: false,
|
|
173
|
+
mutationPolicy: policy
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
async function executeReadTool(tool, args, container, ctx) {
|
|
177
|
+
const fresh = await createRequestContainer();
|
|
178
|
+
const freshCtx = { ...ctx, container: fresh, tool };
|
|
179
|
+
void container;
|
|
180
|
+
return tool.handler(args, freshCtx);
|
|
181
|
+
}
|
|
182
|
+
async function executeMutationTool(tool, args, container, ctx) {
|
|
183
|
+
const fresh = await createRequestContainer();
|
|
184
|
+
const agent = dummyAgentForTool(tool);
|
|
185
|
+
const userId = ctx.userId ?? "00000000-0000-0000-0000-000000000000";
|
|
186
|
+
const { uiPart, pendingAction } = await prepareMutation(
|
|
187
|
+
{
|
|
188
|
+
agent,
|
|
189
|
+
tool,
|
|
190
|
+
toolCallArgs: args,
|
|
191
|
+
conversationId: null,
|
|
192
|
+
mutationPolicyOverride: null
|
|
193
|
+
},
|
|
194
|
+
{
|
|
195
|
+
tenantId: ctx.tenantId,
|
|
196
|
+
organizationId: ctx.organizationId,
|
|
197
|
+
userId,
|
|
198
|
+
features: ctx.userFeatures,
|
|
199
|
+
isSuperAdmin: ctx.isSuperAdmin,
|
|
200
|
+
container: fresh
|
|
201
|
+
}
|
|
202
|
+
);
|
|
203
|
+
const confirmTenantId = ctx.tenantId;
|
|
204
|
+
const confirmContainer = await createRequestContainer();
|
|
205
|
+
const confirmation = await executePendingActionConfirm({
|
|
206
|
+
action: pendingAction,
|
|
207
|
+
agent,
|
|
208
|
+
tool,
|
|
209
|
+
ctx: {
|
|
210
|
+
tenantId: confirmTenantId,
|
|
211
|
+
organizationId: ctx.organizationId,
|
|
212
|
+
userId,
|
|
213
|
+
container: confirmContainer,
|
|
214
|
+
userFeatures: ctx.userFeatures,
|
|
215
|
+
isSuperAdmin: ctx.isSuperAdmin
|
|
216
|
+
},
|
|
217
|
+
emitEvent: async () => {
|
|
218
|
+
}
|
|
219
|
+
});
|
|
220
|
+
if (!confirmation.ok) {
|
|
221
|
+
const error = confirmation.executionResult?.error;
|
|
222
|
+
const cause = confirmation.cause;
|
|
223
|
+
const causeMessage = cause instanceof Error ? cause.message : typeof cause === "string" ? cause : null;
|
|
224
|
+
const message = error?.message ?? causeMessage ?? "handler invocation failed";
|
|
225
|
+
throw new Error(message);
|
|
226
|
+
}
|
|
227
|
+
return {
|
|
228
|
+
status: "pending-confirmation",
|
|
229
|
+
pendingActionId: pendingAction.id,
|
|
230
|
+
expiresAt: pendingAction.expiresAt.toISOString(),
|
|
231
|
+
uiPartType: uiPart?.type ?? null,
|
|
232
|
+
handlerExecuted: true
|
|
233
|
+
};
|
|
234
|
+
}
|
|
235
|
+
async function resolveFixtureInput(fixture, resolved, container, ctx) {
|
|
236
|
+
if (fixture.input) return { ok: true, input: { ...fixture.input } };
|
|
237
|
+
if (!fixture.idFrom) {
|
|
238
|
+
return { ok: false, reason: "fixture has neither input nor idFrom" };
|
|
239
|
+
}
|
|
240
|
+
const sourceTool = resolved.get(fixture.idFrom);
|
|
241
|
+
if (!sourceTool) {
|
|
242
|
+
return { ok: false, reason: `idFrom tool not found: ${fixture.idFrom}` };
|
|
243
|
+
}
|
|
244
|
+
const sourceFixture = getFixture(fixture.idFrom);
|
|
245
|
+
if (!sourceFixture?.input) {
|
|
246
|
+
return {
|
|
247
|
+
ok: false,
|
|
248
|
+
reason: `idFrom source ${fixture.idFrom} has no static input fixture`
|
|
249
|
+
};
|
|
250
|
+
}
|
|
251
|
+
let listResult;
|
|
252
|
+
try {
|
|
253
|
+
listResult = await executeReadTool(
|
|
254
|
+
sourceTool,
|
|
255
|
+
{ ...sourceFixture.input },
|
|
256
|
+
container,
|
|
257
|
+
ctx
|
|
258
|
+
);
|
|
259
|
+
} catch (error) {
|
|
260
|
+
return {
|
|
261
|
+
ok: false,
|
|
262
|
+
reason: `idFrom source ${fixture.idFrom} threw: ${error instanceof Error ? error.message : String(error)}`
|
|
263
|
+
};
|
|
264
|
+
}
|
|
265
|
+
const records = extractRecords(listResult);
|
|
266
|
+
if (!records.length) {
|
|
267
|
+
return {
|
|
268
|
+
ok: false,
|
|
269
|
+
reason: `idFrom source ${fixture.idFrom} returned no records`
|
|
270
|
+
};
|
|
271
|
+
}
|
|
272
|
+
const idField = "id";
|
|
273
|
+
const id = records[0][idField];
|
|
274
|
+
if (typeof id !== "string" || !id) {
|
|
275
|
+
return {
|
|
276
|
+
ok: false,
|
|
277
|
+
reason: `idFrom source ${fixture.idFrom} first record has no string id`
|
|
278
|
+
};
|
|
279
|
+
}
|
|
280
|
+
const bindAs = fixture.bindAs ?? "id";
|
|
281
|
+
return {
|
|
282
|
+
ok: true,
|
|
283
|
+
input: { [bindAs]: id, ...fixture.extra ?? {} }
|
|
284
|
+
};
|
|
285
|
+
}
|
|
286
|
+
function extractRecords(value) {
|
|
287
|
+
if (!value || typeof value !== "object") return [];
|
|
288
|
+
const candidate = value;
|
|
289
|
+
for (const key of ["records", "items", "people", "companies", "deals", "results", "data"]) {
|
|
290
|
+
const arr = candidate[key];
|
|
291
|
+
if (Array.isArray(arr)) return arr;
|
|
292
|
+
}
|
|
293
|
+
return [];
|
|
294
|
+
}
|
|
295
|
+
async function runToolTests(options = {}) {
|
|
296
|
+
const includeMutations = options.includeMutations ?? true;
|
|
297
|
+
const container = await createRequestContainer();
|
|
298
|
+
let tenantId = options.tenantId ?? null;
|
|
299
|
+
let organizationId = options.organizationId ?? null;
|
|
300
|
+
let userId = options.userId ?? null;
|
|
301
|
+
if (!tenantId) {
|
|
302
|
+
const picked = await pickDefaultTenant(container);
|
|
303
|
+
if (picked) {
|
|
304
|
+
tenantId = picked.tenantId;
|
|
305
|
+
organizationId = picked.organizationId;
|
|
306
|
+
userId = userId ?? picked.userId;
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
const ctx = buildSuperAdminContext(container, tenantId, organizationId, userId);
|
|
310
|
+
const grouped = await loadGeneratedTools();
|
|
311
|
+
const flat = [];
|
|
312
|
+
for (const group of grouped) {
|
|
313
|
+
if (options.moduleFilter && group.moduleId !== options.moduleFilter) continue;
|
|
314
|
+
for (const tool of group.tools) flat.push({ moduleId: group.moduleId, tool });
|
|
315
|
+
}
|
|
316
|
+
const resolved = /* @__PURE__ */ new Map();
|
|
317
|
+
for (const { tool } of flat) resolved.set(tool.name, tool);
|
|
318
|
+
const records = [];
|
|
319
|
+
for (const { moduleId, tool } of flat) {
|
|
320
|
+
const start = Date.now();
|
|
321
|
+
const isMutation = tool.isMutation === true;
|
|
322
|
+
const fixture = getFixture(tool.name);
|
|
323
|
+
if (!fixture) {
|
|
324
|
+
records.push({
|
|
325
|
+
module: moduleId,
|
|
326
|
+
tool: tool.name,
|
|
327
|
+
isMutation,
|
|
328
|
+
status: "skip",
|
|
329
|
+
durationMs: Date.now() - start,
|
|
330
|
+
reason: "no fixture"
|
|
331
|
+
});
|
|
332
|
+
continue;
|
|
333
|
+
}
|
|
334
|
+
if (fixture.skip) {
|
|
335
|
+
records.push({
|
|
336
|
+
module: moduleId,
|
|
337
|
+
tool: tool.name,
|
|
338
|
+
isMutation,
|
|
339
|
+
status: "skip",
|
|
340
|
+
durationMs: Date.now() - start,
|
|
341
|
+
reason: fixture.note ?? "skip-by-fixture"
|
|
342
|
+
});
|
|
343
|
+
continue;
|
|
344
|
+
}
|
|
345
|
+
if (isMutation && !includeMutations) {
|
|
346
|
+
records.push({
|
|
347
|
+
module: moduleId,
|
|
348
|
+
tool: tool.name,
|
|
349
|
+
isMutation,
|
|
350
|
+
status: "skip",
|
|
351
|
+
durationMs: Date.now() - start,
|
|
352
|
+
reason: "mutations excluded"
|
|
353
|
+
});
|
|
354
|
+
continue;
|
|
355
|
+
}
|
|
356
|
+
const inputResult = await resolveFixtureInput(fixture, resolved, container, ctx);
|
|
357
|
+
if (!inputResult.ok) {
|
|
358
|
+
records.push({
|
|
359
|
+
module: moduleId,
|
|
360
|
+
tool: tool.name,
|
|
361
|
+
isMutation,
|
|
362
|
+
status: "skip",
|
|
363
|
+
durationMs: Date.now() - start,
|
|
364
|
+
reason: inputResult.reason
|
|
365
|
+
});
|
|
366
|
+
continue;
|
|
367
|
+
}
|
|
368
|
+
try {
|
|
369
|
+
const result = isMutation ? await executeMutationTool(tool, inputResult.input, container, ctx) : await executeReadTool(tool, inputResult.input, container, ctx);
|
|
370
|
+
JSON.stringify(result);
|
|
371
|
+
if (isMutation && (typeof result !== "object" || result === null || result.status !== "pending-confirmation")) {
|
|
372
|
+
records.push({
|
|
373
|
+
module: moduleId,
|
|
374
|
+
tool: tool.name,
|
|
375
|
+
isMutation,
|
|
376
|
+
status: "fail",
|
|
377
|
+
durationMs: Date.now() - start,
|
|
378
|
+
reason: "mutation tool did not route through prepareMutation (no pending-confirmation envelope)",
|
|
379
|
+
resultPreview: clipPreview(result)
|
|
380
|
+
});
|
|
381
|
+
continue;
|
|
382
|
+
}
|
|
383
|
+
records.push({
|
|
384
|
+
module: moduleId,
|
|
385
|
+
tool: tool.name,
|
|
386
|
+
isMutation,
|
|
387
|
+
status: "pass",
|
|
388
|
+
durationMs: Date.now() - start,
|
|
389
|
+
resultPreview: clipPreview(result)
|
|
390
|
+
});
|
|
391
|
+
} catch (error) {
|
|
392
|
+
records.push({
|
|
393
|
+
module: moduleId,
|
|
394
|
+
tool: tool.name,
|
|
395
|
+
isMutation,
|
|
396
|
+
status: "fail",
|
|
397
|
+
durationMs: Date.now() - start,
|
|
398
|
+
reason: error instanceof Error ? error.message : String(error)
|
|
399
|
+
});
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
const passed = records.filter((r) => r.status === "pass").length;
|
|
403
|
+
const failed = records.filter((r) => r.status === "fail").length;
|
|
404
|
+
const skipped = records.filter((r) => r.status === "skip").length;
|
|
405
|
+
return {
|
|
406
|
+
tenantId,
|
|
407
|
+
organizationId,
|
|
408
|
+
total: records.length,
|
|
409
|
+
passed,
|
|
410
|
+
failed,
|
|
411
|
+
skipped,
|
|
412
|
+
records
|
|
413
|
+
};
|
|
414
|
+
}
|
|
415
|
+
export {
|
|
416
|
+
runToolTests
|
|
417
|
+
};
|
|
418
|
+
//# sourceMappingURL=tool-test-runner.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../../src/modules/ai_assistant/lib/tool-test-runner.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * In-process AI tool test runner.\n *\n * Iterates every tool registered in `ai-tools.generated.ts`, invokes the\n * handler against a super-admin tenant context, and returns a structured\n * report. Used by the `mercato ai_assistant test-tools` CLI subcommand and\n * by `.ai/qa/tests/integration/TC-INT-AI-TOOLS.spec.ts`.\n *\n * Safety posture:\n * - No HTTP exposure \u2014 runs only inside the Node process driving the CLI.\n * - Mutation tools are exercised through `prepareMutation` and we assert a\n * pending-action envelope is returned. The pending-action row is created\n * in `ai_pending_actions` but never confirmed, so no real write happens.\n * - Tools without an explicit fixture entry are skipped with reason\n * `'no fixture'` rather than failing.\n */\nimport path from 'node:path'\nimport fs from 'node:fs'\nimport { fileURLToPath, pathToFileURL } from 'node:url'\nimport type { AwilixContainer } from 'awilix'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport type { McpToolContext, AiToolDefinition } from './types'\nimport type { AiAgentDefinition, AiAgentMutationPolicy } from './ai-agent-definition'\nimport { getFixture, type ToolFixture } from './tool-test-fixtures'\nimport { prepareMutation } from './prepare-mutation'\nimport { executePendingActionConfirm } from './pending-action-executor'\n\nexport type ToolTestStatus = 'pass' | 'fail' | 'skip'\n\nexport interface ToolTestRecord {\n module: string\n tool: string\n isMutation: boolean\n status: ToolTestStatus\n durationMs: number\n reason?: string\n resultPreview?: unknown\n}\n\nexport interface ToolTestReport {\n tenantId: string | null\n organizationId: string | null\n total: number\n passed: number\n failed: number\n skipped: number\n records: ToolTestRecord[]\n}\n\ninterface RawAiToolsModule {\n aiToolConfigEntriesRaw?: { moduleId: string; tools: unknown[] }[]\n aiToolConfigEntries?: { moduleId: string; tools: unknown[] }[]\n}\n\nfunction isToolDefinition(value: unknown): value is AiToolDefinition {\n if (!value || typeof value !== 'object') return false\n const candidate = value as Record<string, unknown>\n return (\n typeof candidate.name === 'string' &&\n typeof candidate.description === 'string' &&\n candidate.inputSchema !== undefined &&\n typeof candidate.handler === 'function'\n )\n}\n\n/**\n * Locate `apps/mercato/.mercato/generated/ai-tools.generated.ts` without\n * hardcoding the workspace layout. Searches upward from this file's\n * compiled location; in the monorepo dist this is\n * `packages/ai-assistant/dist/...` so we walk up until we hit a directory\n * containing `apps/mercato/.mercato/generated`.\n */\nfunction findGeneratedAiToolsPath(): string | null {\n const here = (() => {\n try {\n return fileURLToPath(import.meta.url)\n } catch {\n return null\n }\n })()\n if (!here) return null\n let cursor = path.dirname(here)\n for (let i = 0; i < 12; i++) {\n const candidate = path.join(cursor, 'apps', 'mercato', '.mercato', 'generated', 'ai-tools.generated.ts')\n if (fs.existsSync(candidate)) return candidate\n const next = path.dirname(cursor)\n if (next === cursor) break\n cursor = next\n }\n // Fallback: cwd-based lookup (when CLI is invoked from apps/mercato).\n const fromCwd = path.resolve(process.cwd(), 'apps', 'mercato', '.mercato', 'generated', 'ai-tools.generated.ts')\n if (fs.existsSync(fromCwd)) return fromCwd\n const fromCwdDirect = path.resolve(process.cwd(), '.mercato', 'generated', 'ai-tools.generated.ts')\n if (fs.existsSync(fromCwdDirect)) return fromCwdDirect\n return null\n}\n\n/**\n * Compile-and-import `ai-tools.generated.ts` on the fly. Mirrors the approach\n * used by `loadBootstrapData` in `@open-mercato/shared/lib/bootstrap/dynamicLoader`:\n * resolves the `@/` alias to the app root, marks every other package import as\n * external, and emits a sibling `.mjs` we can `import()` from Node. Cached on\n * mtime so repeat runs in the same process don't recompile.\n */\nasync function compileAndImportGenerated(tsPath: string): Promise<RawAiToolsModule> {\n const jsPath = tsPath.replace(/\\.ts$/, '.mjs')\n // appRoot is two directories up from `.mercato/generated/<file>.ts`.\n const appRoot = path.dirname(path.dirname(path.dirname(tsPath)))\n const tsExists = fs.existsSync(tsPath)\n if (!tsExists) {\n throw new Error(`Generated file not found: ${tsPath}`)\n }\n const jsExists = fs.existsSync(jsPath)\n const needsCompile =\n !jsExists || fs.statSync(tsPath).mtimeMs > fs.statSync(jsPath).mtimeMs\n if (needsCompile) {\n const esbuild = await import('esbuild')\n // Transpile-only: don't bundle. Generated registry files only declare an\n // array literal whose entries are static `import(\"\u2026\")` arrow functions \u2014\n // we want those `import()` strings to stay as runtime imports so Node\n // resolves them lazily through the workspace's normal module resolution.\n // Eagerly bundling them pulls Next.js / route handler internals into the\n // .mjs and breaks at runtime (e.g. `next/server` package-exports map).\n const tsSource = fs.readFileSync(tsPath, 'utf-8')\n // Rewrite `@/...` aliases to absolute paths so Node can resolve them.\n const aliasRewritten = tsSource.replace(\n /from\\s+[\"']@\\/([^\"']+)[\"']/g,\n (_match, p1: string) => {\n const target = path.join(appRoot, p1)\n const candidate = fs.existsSync(target)\n ? target\n : fs.existsSync(target + '.ts')\n ? target + '.ts'\n : target\n return `from ${JSON.stringify(pathToFileURL(candidate).href)}`\n },\n ).replace(\n /import\\s*\\(\\s*[\"']@\\/([^\"']+)[\"']\\s*\\)/g,\n (_match, p1: string) => {\n const target = path.join(appRoot, p1)\n const candidate = fs.existsSync(target)\n ? target\n : fs.existsSync(target + '.ts')\n ? target + '.ts'\n : target\n return `import(${JSON.stringify(pathToFileURL(candidate).href)})`\n },\n )\n const result = await esbuild.transform(aliasRewritten, {\n loader: 'ts',\n format: 'esm',\n target: 'node18',\n sourcemap: false,\n sourcefile: tsPath,\n })\n fs.writeFileSync(jsPath, result.code)\n }\n return (await import(pathToFileURL(jsPath).href)) as RawAiToolsModule\n}\n\n/**\n * Compile-and-import `api-routes.generated.ts` and register its manifest with\n * the shared registry. Many tool handlers delegate to `aiApiOperationRunner`\n * which fails closed when no manifest is registered. Idempotent: registering\n * the same array twice is safe.\n */\nasync function ensureApiRouteManifestsRegistered(generatedDir: string): Promise<void> {\n const tsPath = path.join(generatedDir, 'api-routes.generated.ts')\n if (!fs.existsSync(tsPath)) return\n try {\n const mod = (await compileAndImportGenerated(tsPath)) as Record<string, unknown>\n const apiRoutes = (mod as { apiRoutes?: unknown }).apiRoutes\n if (!Array.isArray(apiRoutes)) {\n console.warn('[tool-test-runner] api-routes.generated.mjs returned no apiRoutes array')\n return\n }\n const registry = await import('@open-mercato/shared/modules/registry')\n registry.registerApiRouteManifests(\n apiRoutes as Parameters<typeof registry.registerApiRouteManifests>[0],\n )\n if (process.env.OM_TOOL_TEST_DEBUG === '1') {\n console.log(\n `[tool-test-runner] Registered ${apiRoutes.length} api-route manifests; getApiRouteManifests().length=${registry.getApiRouteManifests().length}`,\n )\n }\n } catch (error) {\n console.warn(\n '[tool-test-runner] Could not register api-routes manifest:',\n error instanceof Error ? error.message : error,\n )\n }\n}\n\nasync function loadGeneratedTools(): Promise<{ moduleId: string; tools: AiToolDefinition[] }[]> {\n const tsPath = findGeneratedAiToolsPath()\n if (!tsPath) {\n throw new Error(\n 'Could not locate apps/<app>/.mercato/generated/ai-tools.generated.ts. Run `yarn generate` first.',\n )\n }\n await ensureApiRouteManifestsRegistered(path.dirname(tsPath))\n const mod = await compileAndImportGenerated(tsPath)\n const entries = mod.aiToolConfigEntriesRaw ?? mod.aiToolConfigEntries ?? []\n const result: { moduleId: string; tools: AiToolDefinition[] }[] = []\n for (const entry of entries) {\n if (!entry || typeof entry.moduleId !== 'string') continue\n const tools = Array.isArray(entry.tools)\n ? entry.tools.filter(isToolDefinition)\n : []\n result.push({ moduleId: entry.moduleId, tools })\n }\n return result\n}\n\nasync function pickDefaultTenant(\n container: AwilixContainer,\n): Promise<{ tenantId: string; organizationId: string | null; userId: string | null } | null> {\n try {\n const em = container.resolve<{\n getConnection: () => { execute: (sql: string) => Promise<unknown[]> }\n }>('em') as unknown as {\n getConnection: () => { execute: (sql: string) => Promise<Record<string, unknown>[]> }\n }\n const conn = em.getConnection()\n const tenantRows = await conn.execute(\n `SELECT id FROM tenants WHERE deleted_at IS NULL ORDER BY created_at ASC LIMIT 1`,\n )\n const tenantId =\n Array.isArray(tenantRows) && tenantRows[0]\n ? String((tenantRows[0] as Record<string, unknown>).id)\n : null\n if (!tenantId) return null\n const escaped = tenantId.replace(/'/g, \"''\")\n const orgRows = await conn.execute(\n `SELECT id FROM organizations WHERE tenant_id = '${escaped}' AND deleted_at IS NULL ORDER BY created_at ASC LIMIT 1`,\n )\n const organizationId =\n Array.isArray(orgRows) && orgRows[0]\n ? String((orgRows[0] as Record<string, unknown>).id)\n : null\n const userRows = await conn.execute(\n `SELECT id FROM users WHERE tenant_id = '${escaped}' AND deleted_at IS NULL ORDER BY created_at ASC LIMIT 1`,\n )\n const userId =\n Array.isArray(userRows) && userRows[0]\n ? String((userRows[0] as Record<string, unknown>).id)\n : null\n return { tenantId, organizationId, userId }\n } catch (error) {\n if (process.env.OM_TOOL_TEST_DEBUG === '1') {\n console.warn('[tool-test-runner] pickDefaultTenant failed:', error)\n }\n return null\n }\n}\n\nfunction buildSuperAdminContext(\n container: AwilixContainer,\n tenantId: string | null,\n organizationId: string | null,\n userId: string | null,\n): McpToolContext {\n return {\n tenantId,\n organizationId,\n userId,\n container,\n userFeatures: ['*'],\n isSuperAdmin: true,\n }\n}\n\nfunction clipPreview(value: unknown): unknown {\n try {\n const json = JSON.stringify(value)\n if (json.length <= 400) return value\n return `${json.slice(0, 400)}\u2026(truncated, ${json.length} bytes)`\n } catch {\n return '[unserializable]'\n }\n}\n\nfunction dummyAgentForTool(tool: AiToolDefinition): AiAgentDefinition {\n // The runner never registers this agent; it's only used as input to\n // `prepareMutation` for shape compatibility. The id namespace is namespaced\n // under `__test__` so it cannot collide with real agents.\n const policy: AiAgentMutationPolicy = 'destructive-confirm-required'\n return {\n id: `__test__.${tool.name}`,\n moduleId: tool.name.split('.')[0] ?? '__test__',\n label: 'Tool Test Runner',\n description: 'Synthetic agent used by the tool test runner',\n systemPrompt: '',\n allowedTools: [tool.name],\n readOnly: false,\n mutationPolicy: policy,\n } as AiAgentDefinition\n}\n\nasync function executeReadTool(\n tool: AiToolDefinition,\n args: Record<string, unknown>,\n container: AwilixContainer,\n ctx: McpToolContext,\n): Promise<unknown> {\n // Mirror the dispatcher: resolve a fresh container per call so EM identity\n // map state is clean between tools.\n const fresh = await createRequestContainer()\n const freshCtx: McpToolContext = { ...ctx, container: fresh, tool }\n void container // keep arg for future use\n return tool.handler(args as never, freshCtx)\n}\n\nasync function executeMutationTool(\n tool: AiToolDefinition,\n args: Record<string, unknown>,\n container: AwilixContainer,\n ctx: McpToolContext,\n): Promise<unknown> {\n // Route through prepareMutation so we assert the approval contract still\n // holds \u2014 the handler must NEVER write directly. We pass a destructive\n // policy so the runtime treats the call as a confirmation candidate and\n // creates a pending-action row.\n const fresh = await createRequestContainer()\n const agent = dummyAgentForTool(tool)\n const userId = ctx.userId ?? '00000000-0000-0000-0000-000000000000'\n const { uiPart, pendingAction } = await prepareMutation(\n {\n agent,\n tool,\n toolCallArgs: args,\n conversationId: null,\n mutationPolicyOverride: null,\n },\n {\n tenantId: ctx.tenantId,\n organizationId: ctx.organizationId,\n userId,\n features: ctx.userFeatures,\n isSuperAdmin: ctx.isSuperAdmin,\n container: fresh,\n },\n )\n // Exercise the actual handler via the same path the production confirm\n // route uses. Without this, mutation tools whose handler crashes (e.g.\n // missing `ctx.tool` for the API operation runner) would be reported as\n // passing because `prepareMutation` never invokes the handler.\n // `prepareMutation` already enforced `tenantId` is non-null above; cast\n // here for the stricter `PendingActionExecuteContext` shape.\n const confirmTenantId = ctx.tenantId as string\n const confirmContainer = await createRequestContainer()\n const confirmation = await executePendingActionConfirm({\n action: pendingAction,\n agent,\n tool,\n ctx: {\n tenantId: confirmTenantId,\n organizationId: ctx.organizationId,\n userId,\n container: confirmContainer,\n userFeatures: ctx.userFeatures,\n isSuperAdmin: ctx.isSuperAdmin,\n },\n emitEvent: async () => {},\n })\n if (!confirmation.ok) {\n const error = (confirmation.executionResult as { error?: { message?: string } } | undefined)?.error\n const cause = confirmation.cause\n const causeMessage =\n cause instanceof Error\n ? cause.message\n : typeof cause === 'string'\n ? cause\n : null\n const message = error?.message ?? causeMessage ?? 'handler invocation failed'\n throw new Error(message)\n }\n return {\n status: 'pending-confirmation',\n pendingActionId: pendingAction.id,\n expiresAt: pendingAction.expiresAt.toISOString(),\n uiPartType: (uiPart as { type?: string } | undefined)?.type ?? null,\n handlerExecuted: true,\n }\n}\n\nasync function resolveFixtureInput(\n fixture: ToolFixture,\n resolved: Map<string, AiToolDefinition>,\n container: AwilixContainer,\n ctx: McpToolContext,\n): Promise<{ ok: true; input: Record<string, unknown> } | { ok: false; reason: string }> {\n if (fixture.input) return { ok: true, input: { ...fixture.input } }\n if (!fixture.idFrom) {\n return { ok: false, reason: 'fixture has neither input nor idFrom' }\n }\n const sourceTool = resolved.get(fixture.idFrom)\n if (!sourceTool) {\n return { ok: false, reason: `idFrom tool not found: ${fixture.idFrom}` }\n }\n const sourceFixture = getFixture(fixture.idFrom)\n if (!sourceFixture?.input) {\n return {\n ok: false,\n reason: `idFrom source ${fixture.idFrom} has no static input fixture`,\n }\n }\n let listResult: unknown\n try {\n listResult = await executeReadTool(\n sourceTool,\n { ...sourceFixture.input },\n container,\n ctx,\n )\n } catch (error) {\n return {\n ok: false,\n reason: `idFrom source ${fixture.idFrom} threw: ${\n error instanceof Error ? error.message : String(error)\n }`,\n }\n }\n const records = extractRecords(listResult)\n if (!records.length) {\n return {\n ok: false,\n reason: `idFrom source ${fixture.idFrom} returned no records`,\n }\n }\n const idField = 'id'\n const id = (records[0] as Record<string, unknown>)[idField]\n if (typeof id !== 'string' || !id) {\n return {\n ok: false,\n reason: `idFrom source ${fixture.idFrom} first record has no string id`,\n }\n }\n const bindAs = fixture.bindAs ?? 'id'\n return {\n ok: true,\n input: { [bindAs]: id, ...(fixture.extra ?? {}) },\n }\n}\n\nfunction extractRecords(value: unknown): unknown[] {\n if (!value || typeof value !== 'object') return []\n const candidate = value as Record<string, unknown>\n for (const key of ['records', 'items', 'people', 'companies', 'deals', 'results', 'data']) {\n const arr = candidate[key]\n if (Array.isArray(arr)) return arr\n }\n return []\n}\n\nexport interface RunToolTestsOptions {\n tenantId?: string | null\n organizationId?: string | null\n userId?: string | null\n moduleFilter?: string | null\n includeMutations?: boolean\n}\n\nexport async function runToolTests(\n options: RunToolTestsOptions = {},\n): Promise<ToolTestReport> {\n const includeMutations = options.includeMutations ?? true\n const container = await createRequestContainer()\n let tenantId: string | null = options.tenantId ?? null\n let organizationId: string | null = options.organizationId ?? null\n let userId: string | null = options.userId ?? null\n if (!tenantId) {\n const picked = await pickDefaultTenant(container)\n if (picked) {\n tenantId = picked.tenantId\n organizationId = picked.organizationId\n userId = userId ?? picked.userId\n }\n }\n const ctx = buildSuperAdminContext(container, tenantId, organizationId, userId)\n const grouped = await loadGeneratedTools()\n\n const flat: { moduleId: string; tool: AiToolDefinition }[] = []\n for (const group of grouped) {\n if (options.moduleFilter && group.moduleId !== options.moduleFilter) continue\n for (const tool of group.tools) flat.push({ moduleId: group.moduleId, tool })\n }\n const resolved = new Map<string, AiToolDefinition>()\n for (const { tool } of flat) resolved.set(tool.name, tool)\n\n const records: ToolTestRecord[] = []\n for (const { moduleId, tool } of flat) {\n const start = Date.now()\n const isMutation = tool.isMutation === true\n const fixture = getFixture(tool.name)\n if (!fixture) {\n records.push({\n module: moduleId,\n tool: tool.name,\n isMutation,\n status: 'skip',\n durationMs: Date.now() - start,\n reason: 'no fixture',\n })\n continue\n }\n if (fixture.skip) {\n records.push({\n module: moduleId,\n tool: tool.name,\n isMutation,\n status: 'skip',\n durationMs: Date.now() - start,\n reason: fixture.note ?? 'skip-by-fixture',\n })\n continue\n }\n if (isMutation && !includeMutations) {\n records.push({\n module: moduleId,\n tool: tool.name,\n isMutation,\n status: 'skip',\n durationMs: Date.now() - start,\n reason: 'mutations excluded',\n })\n continue\n }\n const inputResult = await resolveFixtureInput(fixture, resolved, container, ctx)\n if (!inputResult.ok) {\n records.push({\n module: moduleId,\n tool: tool.name,\n isMutation,\n status: 'skip',\n durationMs: Date.now() - start,\n reason: inputResult.reason,\n })\n continue\n }\n try {\n const result = isMutation\n ? await executeMutationTool(tool, inputResult.input, container, ctx)\n : await executeReadTool(tool, inputResult.input, container, ctx)\n // Result must be JSON-serializable.\n JSON.stringify(result)\n // Mutation tools must return a pending-confirmation envelope.\n if (\n isMutation &&\n (typeof result !== 'object' ||\n result === null ||\n (result as Record<string, unknown>).status !== 'pending-confirmation')\n ) {\n records.push({\n module: moduleId,\n tool: tool.name,\n isMutation,\n status: 'fail',\n durationMs: Date.now() - start,\n reason: 'mutation tool did not route through prepareMutation (no pending-confirmation envelope)',\n resultPreview: clipPreview(result),\n })\n continue\n }\n records.push({\n module: moduleId,\n tool: tool.name,\n isMutation,\n status: 'pass',\n durationMs: Date.now() - start,\n resultPreview: clipPreview(result),\n })\n } catch (error) {\n records.push({\n module: moduleId,\n tool: tool.name,\n isMutation,\n status: 'fail',\n durationMs: Date.now() - start,\n reason: error instanceof Error ? error.message : String(error),\n })\n }\n }\n const passed = records.filter((r) => r.status === 'pass').length\n const failed = records.filter((r) => r.status === 'fail').length\n const skipped = records.filter((r) => r.status === 'skip').length\n return {\n tenantId,\n organizationId,\n total: records.length,\n passed,\n failed,\n skipped,\n records,\n }\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { Migration } from "@mikro-orm/migrations";
|
|
2
|
+
class Migration20260419100521_ai_assistant extends Migration {
|
|
3
|
+
async up() {
|
|
4
|
+
this.addSql(`create table "ai_agent_prompt_overrides" ("id" uuid not null default gen_random_uuid(), "tenant_id" uuid not null, "organization_id" uuid null, "agent_id" text not null, "version" int not null, "sections" jsonb not null, "notes" text null, "created_by_user_id" uuid null, "created_at" timestamptz not null, "updated_at" timestamptz not null, constraint "ai_agent_prompt_overrides_pkey" primary key ("id"));`);
|
|
5
|
+
this.addSql(`create unique index "ai_agent_prompt_overrides_tenant_org_agent_version_uq" on "ai_agent_prompt_overrides" ("tenant_id", "organization_id", "agent_id", "version") where "organization_id" is not null;`);
|
|
6
|
+
this.addSql(`create unique index "ai_agent_prompt_overrides_tenant_agent_version_null_org_uq" on "ai_agent_prompt_overrides" ("tenant_id", "agent_id", "version") where "organization_id" is null;`);
|
|
7
|
+
this.addSql(`create index "ai_agent_prompt_overrides_tenant_org_agent_version_idx" on "ai_agent_prompt_overrides" ("tenant_id", "organization_id", "agent_id", "version" desc);`);
|
|
8
|
+
this.addSql(`create index "ai_agent_prompt_overrides_tenant_agent_idx" on "ai_agent_prompt_overrides" ("tenant_id", "agent_id");`);
|
|
9
|
+
}
|
|
10
|
+
async down() {
|
|
11
|
+
this.addSql(`drop table if exists "ai_agent_prompt_overrides" cascade;`);
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
export {
|
|
15
|
+
Migration20260419100521_ai_assistant
|
|
16
|
+
};
|
|
17
|
+
//# sourceMappingURL=Migration20260419100521.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../../src/modules/ai_assistant/migrations/Migration20260419100521.ts"],
|
|
4
|
+
"sourcesContent": ["import { Migration } from '@mikro-orm/migrations';\n\nexport class Migration20260419100521_ai_assistant extends Migration {\n\n override async up(): Promise<void> {\n this.addSql(`create table \"ai_agent_prompt_overrides\" (\"id\" uuid not null default gen_random_uuid(), \"tenant_id\" uuid not null, \"organization_id\" uuid null, \"agent_id\" text not null, \"version\" int not null, \"sections\" jsonb not null, \"notes\" text null, \"created_by_user_id\" uuid null, \"created_at\" timestamptz not null, \"updated_at\" timestamptz not null, constraint \"ai_agent_prompt_overrides_pkey\" primary key (\"id\"));`);\n this.addSql(`create unique index \"ai_agent_prompt_overrides_tenant_org_agent_version_uq\" on \"ai_agent_prompt_overrides\" (\"tenant_id\", \"organization_id\", \"agent_id\", \"version\") where \"organization_id\" is not null;`);\n this.addSql(`create unique index \"ai_agent_prompt_overrides_tenant_agent_version_null_org_uq\" on \"ai_agent_prompt_overrides\" (\"tenant_id\", \"agent_id\", \"version\") where \"organization_id\" is null;`);\n this.addSql(`create index \"ai_agent_prompt_overrides_tenant_org_agent_version_idx\" on \"ai_agent_prompt_overrides\" (\"tenant_id\", \"organization_id\", \"agent_id\", \"version\" desc);`);\n this.addSql(`create index \"ai_agent_prompt_overrides_tenant_agent_idx\" on \"ai_agent_prompt_overrides\" (\"tenant_id\", \"agent_id\");`);\n }\n\n override async down(): Promise<void> {\n this.addSql(`drop table if exists \"ai_agent_prompt_overrides\" cascade;`);\n }\n\n}\n"],
|
|
5
|
+
"mappings": "AAAA,SAAS,iBAAiB;AAEnB,MAAM,6CAA6C,UAAU;AAAA,EAElE,MAAe,KAAoB;AACjC,SAAK,OAAO,wZAAwZ;AACpa,SAAK,OAAO,yMAAyM;AACrN,SAAK,OAAO,uLAAuL;AACnM,SAAK,OAAO,oKAAoK;AAChL,SAAK,OAAO,qHAAqH;AAAA,EACnI;AAAA,EAEA,MAAe,OAAsB;AACnC,SAAK,OAAO,2DAA2D;AAAA,EACzE;AAEF;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { Migration } from "@mikro-orm/migrations";
|
|
2
|
+
class Migration20260419132948_ai_assistant extends Migration {
|
|
3
|
+
async up() {
|
|
4
|
+
this.addSql(`create table "ai_agent_mutation_policy_overrides" ("id" uuid not null default gen_random_uuid(), "tenant_id" uuid not null, "organization_id" uuid null, "agent_id" text not null, "mutation_policy" text not null, "notes" text null, "created_by_user_id" uuid null, "created_at" timestamptz not null, "updated_at" timestamptz not null, constraint "ai_agent_mutation_policy_overrides_pkey" primary key ("id"));`);
|
|
5
|
+
this.addSql(`create unique index "ai_agent_mutation_policy_overrides_tenant_org_agent_uq" on "ai_agent_mutation_policy_overrides" ("tenant_id", "organization_id", "agent_id") where "organization_id" is not null;`);
|
|
6
|
+
this.addSql(`create unique index "ai_agent_mutation_policy_overrides_tenant_agent_null_org_uq" on "ai_agent_mutation_policy_overrides" ("tenant_id", "agent_id") where "organization_id" is null;`);
|
|
7
|
+
this.addSql(`create index "ai_agent_mutation_policy_overrides_tenant_agent_idx" on "ai_agent_mutation_policy_overrides" ("tenant_id", "agent_id");`);
|
|
8
|
+
}
|
|
9
|
+
async down() {
|
|
10
|
+
this.addSql(`drop table if exists "ai_agent_mutation_policy_overrides" cascade;`);
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
export {
|
|
14
|
+
Migration20260419132948_ai_assistant
|
|
15
|
+
};
|
|
16
|
+
//# sourceMappingURL=Migration20260419132948.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../../src/modules/ai_assistant/migrations/Migration20260419132948.ts"],
|
|
4
|
+
"sourcesContent": ["import { Migration } from '@mikro-orm/migrations';\n\nexport class Migration20260419132948_ai_assistant extends Migration {\n\n override async up(): Promise<void> {\n this.addSql(`create table \"ai_agent_mutation_policy_overrides\" (\"id\" uuid not null default gen_random_uuid(), \"tenant_id\" uuid not null, \"organization_id\" uuid null, \"agent_id\" text not null, \"mutation_policy\" text not null, \"notes\" text null, \"created_by_user_id\" uuid null, \"created_at\" timestamptz not null, \"updated_at\" timestamptz not null, constraint \"ai_agent_mutation_policy_overrides_pkey\" primary key (\"id\"));`);\n this.addSql(`create unique index \"ai_agent_mutation_policy_overrides_tenant_org_agent_uq\" on \"ai_agent_mutation_policy_overrides\" (\"tenant_id\", \"organization_id\", \"agent_id\") where \"organization_id\" is not null;`);\n this.addSql(`create unique index \"ai_agent_mutation_policy_overrides_tenant_agent_null_org_uq\" on \"ai_agent_mutation_policy_overrides\" (\"tenant_id\", \"agent_id\") where \"organization_id\" is null;`);\n this.addSql(`create index \"ai_agent_mutation_policy_overrides_tenant_agent_idx\" on \"ai_agent_mutation_policy_overrides\" (\"tenant_id\", \"agent_id\");`);\n }\n\n override async down(): Promise<void> {\n this.addSql(`drop table if exists \"ai_agent_mutation_policy_overrides\" cascade;`);\n }\n\n}\n"],
|
|
5
|
+
"mappings": "AAAA,SAAS,iBAAiB;AAEnB,MAAM,6CAA6C,UAAU;AAAA,EAElE,MAAe,KAAoB;AACjC,SAAK,OAAO,wZAAwZ;AACpa,SAAK,OAAO,wMAAwM;AACpN,SAAK,OAAO,sLAAsL;AAClM,SAAK,OAAO,uIAAuI;AAAA,EACrJ;AAAA,EAEA,MAAe,OAAsB;AACnC,SAAK,OAAO,oEAAoE;AAAA,EAClF;AAEF;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { Migration } from "@mikro-orm/migrations";
|
|
2
|
+
class Migration20260419134235_ai_assistant extends Migration {
|
|
3
|
+
async up() {
|
|
4
|
+
this.addSql(`create table "ai_pending_actions" ("id" uuid not null default gen_random_uuid(), "tenant_id" uuid not null, "organization_id" uuid null, "agent_id" text not null, "tool_name" text not null, "conversation_id" text null, "target_entity_type" text null, "target_record_id" text null, "normalized_input" jsonb not null, "field_diff" jsonb not null default '[]', "records" jsonb null, "failed_records" jsonb null, "side_effects_summary" text null, "record_version" text null, "attachment_ids" jsonb not null default '[]', "idempotency_key" text not null, "created_by_user_id" uuid not null, "status" text not null, "queue_mode" text not null default 'inline', "execution_result" jsonb null, "created_at" timestamptz not null, "expires_at" timestamptz not null, "resolved_at" timestamptz null, "resolved_by_user_id" uuid null, constraint "ai_pending_actions_pkey" primary key ("id"));`);
|
|
5
|
+
this.addSql(`create unique index "ai_pending_actions_tenant_org_idempotency_uq" on "ai_pending_actions" ("tenant_id", "organization_id", "idempotency_key") where "organization_id" is not null;`);
|
|
6
|
+
this.addSql(`create unique index "ai_pending_actions_tenant_idem_null_org_uq" on "ai_pending_actions" ("tenant_id", "idempotency_key") where "organization_id" is null;`);
|
|
7
|
+
this.addSql(`create index "ai_pending_actions_tenant_org_agent_status_idx" on "ai_pending_actions" ("tenant_id", "organization_id", "agent_id", "status");`);
|
|
8
|
+
this.addSql(`create index "ai_pending_actions_tenant_org_status_expires_idx" on "ai_pending_actions" ("tenant_id", "organization_id", "status", "expires_at");`);
|
|
9
|
+
}
|
|
10
|
+
async down() {
|
|
11
|
+
this.addSql(`drop table if exists "ai_pending_actions" cascade;`);
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
export {
|
|
15
|
+
Migration20260419134235_ai_assistant
|
|
16
|
+
};
|
|
17
|
+
//# sourceMappingURL=Migration20260419134235.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../../src/modules/ai_assistant/migrations/Migration20260419134235.ts"],
|
|
4
|
+
"sourcesContent": ["import { Migration } from '@mikro-orm/migrations';\n\nexport class Migration20260419134235_ai_assistant extends Migration {\n\n override async up(): Promise<void> {\n this.addSql(`create table \"ai_pending_actions\" (\"id\" uuid not null default gen_random_uuid(), \"tenant_id\" uuid not null, \"organization_id\" uuid null, \"agent_id\" text not null, \"tool_name\" text not null, \"conversation_id\" text null, \"target_entity_type\" text null, \"target_record_id\" text null, \"normalized_input\" jsonb not null, \"field_diff\" jsonb not null default '[]', \"records\" jsonb null, \"failed_records\" jsonb null, \"side_effects_summary\" text null, \"record_version\" text null, \"attachment_ids\" jsonb not null default '[]', \"idempotency_key\" text not null, \"created_by_user_id\" uuid not null, \"status\" text not null, \"queue_mode\" text not null default 'inline', \"execution_result\" jsonb null, \"created_at\" timestamptz not null, \"expires_at\" timestamptz not null, \"resolved_at\" timestamptz null, \"resolved_by_user_id\" uuid null, constraint \"ai_pending_actions_pkey\" primary key (\"id\"));`);\n this.addSql(`create unique index \"ai_pending_actions_tenant_org_idempotency_uq\" on \"ai_pending_actions\" (\"tenant_id\", \"organization_id\", \"idempotency_key\") where \"organization_id\" is not null;`);\n this.addSql(`create unique index \"ai_pending_actions_tenant_idem_null_org_uq\" on \"ai_pending_actions\" (\"tenant_id\", \"idempotency_key\") where \"organization_id\" is null;`);\n this.addSql(`create index \"ai_pending_actions_tenant_org_agent_status_idx\" on \"ai_pending_actions\" (\"tenant_id\", \"organization_id\", \"agent_id\", \"status\");`);\n this.addSql(`create index \"ai_pending_actions_tenant_org_status_expires_idx\" on \"ai_pending_actions\" (\"tenant_id\", \"organization_id\", \"status\", \"expires_at\");`);\n }\n\n override async down(): Promise<void> {\n this.addSql(`drop table if exists \"ai_pending_actions\" cascade;`);\n }\n\n}\n"],
|
|
5
|
+
"mappings": "AAAA,SAAS,iBAAiB;AAEnB,MAAM,6CAA6C,UAAU;AAAA,EAElE,MAAe,KAAoB;AACjC,SAAK,OAAO,g3BAAg3B;AAC53B,SAAK,OAAO,qLAAqL;AACjM,SAAK,OAAO,4JAA4J;AACxK,SAAK,OAAO,+IAA+I;AAC3J,SAAK,OAAO,mJAAmJ;AAAA,EACjK;AAAA,EAEA,MAAe,OAAsB;AACnC,SAAK,OAAO,oDAAoD;AAAA,EAClE;AAEF;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -1,3 +1,36 @@
|
|
|
1
|
+
const PENDING_ACTION_CLEANUP_SCHEDULE_ID = "ai_assistant:pending-action-cleanup";
|
|
2
|
+
async function ensurePendingActionCleanupSchedule(container) {
|
|
3
|
+
if (!container) return;
|
|
4
|
+
let schedulerService;
|
|
5
|
+
try {
|
|
6
|
+
schedulerService = container.resolve("schedulerService");
|
|
7
|
+
} catch {
|
|
8
|
+
schedulerService = void 0;
|
|
9
|
+
}
|
|
10
|
+
if (!schedulerService) return;
|
|
11
|
+
try {
|
|
12
|
+
await schedulerService.register({
|
|
13
|
+
id: PENDING_ACTION_CLEANUP_SCHEDULE_ID,
|
|
14
|
+
name: "AI pending-action cleanup",
|
|
15
|
+
description: "Sweep pending AI mutation approvals whose TTL elapsed without confirm/cancel and flip them to expired.",
|
|
16
|
+
scopeType: "system",
|
|
17
|
+
scheduleType: "interval",
|
|
18
|
+
scheduleValue: "5m",
|
|
19
|
+
timezone: "UTC",
|
|
20
|
+
targetType: "queue",
|
|
21
|
+
targetQueue: "ai-pending-action-cleanup",
|
|
22
|
+
targetPayload: {},
|
|
23
|
+
sourceType: "module",
|
|
24
|
+
sourceModule: "ai_assistant",
|
|
25
|
+
isEnabled: true
|
|
26
|
+
});
|
|
27
|
+
} catch (error) {
|
|
28
|
+
console.warn(
|
|
29
|
+
"[ai_assistant] Failed to register pending-action cleanup schedule:",
|
|
30
|
+
error instanceof Error ? error.message : error
|
|
31
|
+
);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
1
34
|
const setup = {
|
|
2
35
|
defaultRoleFeatures: {
|
|
3
36
|
admin: [
|
|
@@ -9,6 +42,9 @@ const setup = {
|
|
|
9
42
|
"ai_assistant.mcp_servers.manage"
|
|
10
43
|
],
|
|
11
44
|
employee: ["ai_assistant.view"]
|
|
45
|
+
},
|
|
46
|
+
async seedDefaults({ container }) {
|
|
47
|
+
await ensurePendingActionCleanupSchedule(container);
|
|
12
48
|
}
|
|
13
49
|
};
|
|
14
50
|
var setup_default = setup;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/modules/ai_assistant/setup.ts"],
|
|
4
|
-
"sourcesContent": ["import type { ModuleSetupConfig } from '@open-mercato/shared/modules/setup'\n\nexport const setup: ModuleSetupConfig = {\n defaultRoleFeatures: {\n admin: [\n 'ai_assistant.view',\n 'ai_assistant.settings.manage',\n 'ai_assistant.mcp.serve',\n 'ai_assistant.tools.list',\n 'ai_assistant.mcp_servers.view',\n 'ai_assistant.mcp_servers.manage',\n ],\n employee: ['ai_assistant.view'],\n },\n}\n\nexport default setup\n"],
|
|
5
|
-
"mappings": "AAEO,MAAM,QAA2B;AAAA,EACtC,qBAAqB;AAAA,IACnB,OAAO;AAAA,MACL;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,IACA,UAAU,CAAC,mBAAmB;AAAA,EAChC;AACF;AAEA,IAAO,gBAAQ;",
|
|
4
|
+
"sourcesContent": ["import type { ModuleSetupConfig } from '@open-mercato/shared/modules/setup'\n\nconst PENDING_ACTION_CLEANUP_SCHEDULE_ID = 'ai_assistant:pending-action-cleanup'\n\n/**\n * System-scoped recurring schedule: every 5 minutes, enqueue a job to the\n * `ai-pending-action-cleanup` queue so the worker can sweep rows whose TTL\n * elapsed without any confirm/cancel activity (Step 5.12). The schedule id\n * is stable and `scheduler.register()` is an upsert, so calling this from\n * every tenant bootstrap stays idempotent.\n */\nasync function ensurePendingActionCleanupSchedule(\n container: import('awilix').AwilixContainer | undefined,\n): Promise<void> {\n if (!container) return\n let schedulerService:\n | {\n register: (registration: Record<string, unknown>) => Promise<void>\n }\n | undefined\n try {\n schedulerService = container.resolve('schedulerService')\n } catch {\n schedulerService = undefined\n }\n if (!schedulerService) return\n try {\n await schedulerService.register({\n id: PENDING_ACTION_CLEANUP_SCHEDULE_ID,\n name: 'AI pending-action cleanup',\n description:\n 'Sweep pending AI mutation approvals whose TTL elapsed without confirm/cancel and flip them to expired.',\n scopeType: 'system',\n scheduleType: 'interval',\n scheduleValue: '5m',\n timezone: 'UTC',\n targetType: 'queue',\n targetQueue: 'ai-pending-action-cleanup',\n targetPayload: {},\n sourceType: 'module',\n sourceModule: 'ai_assistant',\n isEnabled: true,\n })\n } catch (error) {\n console.warn(\n '[ai_assistant] Failed to register pending-action cleanup schedule:',\n error instanceof Error ? error.message : error,\n )\n }\n}\n\nexport const setup: ModuleSetupConfig = {\n defaultRoleFeatures: {\n admin: [\n 'ai_assistant.view',\n 'ai_assistant.settings.manage',\n 'ai_assistant.mcp.serve',\n 'ai_assistant.tools.list',\n 'ai_assistant.mcp_servers.view',\n 'ai_assistant.mcp_servers.manage',\n ],\n employee: ['ai_assistant.view'],\n },\n\n async seedDefaults({ container }) {\n await ensurePendingActionCleanupSchedule(container)\n },\n}\n\nexport default setup\n"],
|
|
5
|
+
"mappings": "AAEA,MAAM,qCAAqC;AAS3C,eAAe,mCACb,WACe;AACf,MAAI,CAAC,UAAW;AAChB,MAAI;AAKJ,MAAI;AACF,uBAAmB,UAAU,QAAQ,kBAAkB;AAAA,EACzD,QAAQ;AACN,uBAAmB;AAAA,EACrB;AACA,MAAI,CAAC,iBAAkB;AACvB,MAAI;AACF,UAAM,iBAAiB,SAAS;AAAA,MAC9B,IAAI;AAAA,MACJ,MAAM;AAAA,MACN,aACE;AAAA,MACF,WAAW;AAAA,MACX,cAAc;AAAA,MACd,eAAe;AAAA,MACf,UAAU;AAAA,MACV,YAAY;AAAA,MACZ,aAAa;AAAA,MACb,eAAe,CAAC;AAAA,MAChB,YAAY;AAAA,MACZ,cAAc;AAAA,MACd,WAAW;AAAA,IACb,CAAC;AAAA,EACH,SAAS,OAAO;AACd,YAAQ;AAAA,MACN;AAAA,MACA,iBAAiB,QAAQ,MAAM,UAAU;AAAA,IAC3C;AAAA,EACF;AACF;AAEO,MAAM,QAA2B;AAAA,EACtC,qBAAqB;AAAA,IACnB,OAAO;AAAA,MACL;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,IACA,UAAU,CAAC,mBAAmB;AAAA,EAChC;AAAA,EAEA,MAAM,aAAa,EAAE,UAAU,GAAG;AAChC,UAAM,mCAAmC,SAAS;AAAA,EACpD;AACF;AAEA,IAAO,gBAAQ;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|