@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,189 @@
|
|
|
1
|
+
import {
|
|
2
|
+
registerModuleOverrideApplier
|
|
3
|
+
} from "@open-mercato/shared/modules/overrides";
|
|
4
|
+
const programmaticAgentOverrides = {};
|
|
5
|
+
const programmaticToolOverrides = {};
|
|
6
|
+
const programmaticAgentExtensions = [];
|
|
7
|
+
const modulesConfigAgentOverrides = {};
|
|
8
|
+
const modulesConfigToolOverrides = {};
|
|
9
|
+
const modulesConfigAgentExtensions = [];
|
|
10
|
+
function applyAiAgentOverrides(overrides) {
|
|
11
|
+
for (const [id, value] of Object.entries(overrides)) {
|
|
12
|
+
programmaticAgentOverrides[id] = value;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
function applyAiToolOverrides(overrides) {
|
|
16
|
+
for (const [name, value] of Object.entries(overrides)) {
|
|
17
|
+
programmaticToolOverrides[name] = value;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
function applyAiAgentExtensions(extensions) {
|
|
21
|
+
programmaticAgentExtensions.push(...extensions);
|
|
22
|
+
}
|
|
23
|
+
function applyAiOverridesFromEnabledModules(modules) {
|
|
24
|
+
for (const entry of modules) {
|
|
25
|
+
if (!entry || typeof entry.id !== "string" || !entry.id) continue;
|
|
26
|
+
const ai = entry.overrides?.ai;
|
|
27
|
+
if (!ai || typeof ai !== "object") continue;
|
|
28
|
+
if (ai.agents && typeof ai.agents === "object") {
|
|
29
|
+
for (const [id, value] of Object.entries(ai.agents)) {
|
|
30
|
+
modulesConfigAgentOverrides[id] = value;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
if (ai.tools && typeof ai.tools === "object") {
|
|
34
|
+
for (const [name, value] of Object.entries(ai.tools)) {
|
|
35
|
+
modulesConfigToolOverrides[name] = value;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
if (Array.isArray(ai.extensions)) {
|
|
39
|
+
modulesConfigAgentExtensions.push(...ai.extensions);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
function applyAiOverridesDispatcherEntries(entries) {
|
|
44
|
+
applyAiOverridesFromEnabledModules(
|
|
45
|
+
entries.map((entry) => ({
|
|
46
|
+
id: entry.moduleId,
|
|
47
|
+
overrides: { ai: entry.overrides }
|
|
48
|
+
}))
|
|
49
|
+
);
|
|
50
|
+
}
|
|
51
|
+
registerModuleOverrideApplier(
|
|
52
|
+
"ai",
|
|
53
|
+
(entries) => {
|
|
54
|
+
applyAiOverridesDispatcherEntries(entries);
|
|
55
|
+
}
|
|
56
|
+
);
|
|
57
|
+
function resetProgrammaticOverridesForTests() {
|
|
58
|
+
for (const key of Object.keys(programmaticAgentOverrides)) {
|
|
59
|
+
delete programmaticAgentOverrides[key];
|
|
60
|
+
}
|
|
61
|
+
for (const key of Object.keys(programmaticToolOverrides)) {
|
|
62
|
+
delete programmaticToolOverrides[key];
|
|
63
|
+
}
|
|
64
|
+
for (const key of Object.keys(modulesConfigAgentOverrides)) {
|
|
65
|
+
delete modulesConfigAgentOverrides[key];
|
|
66
|
+
}
|
|
67
|
+
for (const key of Object.keys(modulesConfigToolOverrides)) {
|
|
68
|
+
delete modulesConfigToolOverrides[key];
|
|
69
|
+
}
|
|
70
|
+
programmaticAgentExtensions.length = 0;
|
|
71
|
+
modulesConfigAgentExtensions.length = 0;
|
|
72
|
+
}
|
|
73
|
+
function composeAgentOverrideMap(fileEntries) {
|
|
74
|
+
const out = {};
|
|
75
|
+
for (const entry of fileEntries) {
|
|
76
|
+
const overrides = entry?.overrides;
|
|
77
|
+
if (!overrides || typeof overrides !== "object") continue;
|
|
78
|
+
for (const [id, value] of Object.entries(overrides)) {
|
|
79
|
+
if (typeof id !== "string" || !id) continue;
|
|
80
|
+
out[id] = value;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
for (const [id, value] of Object.entries(modulesConfigAgentOverrides)) {
|
|
84
|
+
out[id] = value;
|
|
85
|
+
}
|
|
86
|
+
for (const [id, value] of Object.entries(programmaticAgentOverrides)) {
|
|
87
|
+
out[id] = value;
|
|
88
|
+
}
|
|
89
|
+
return out;
|
|
90
|
+
}
|
|
91
|
+
function composeToolOverrideMap(fileEntries) {
|
|
92
|
+
const out = {};
|
|
93
|
+
for (const entry of fileEntries) {
|
|
94
|
+
const overrides = entry?.overrides;
|
|
95
|
+
if (!overrides || typeof overrides !== "object") continue;
|
|
96
|
+
for (const [name, value] of Object.entries(overrides)) {
|
|
97
|
+
if (typeof name !== "string" || !name) continue;
|
|
98
|
+
out[name] = value;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
for (const [name, value] of Object.entries(modulesConfigToolOverrides)) {
|
|
102
|
+
out[name] = value;
|
|
103
|
+
}
|
|
104
|
+
for (const [name, value] of Object.entries(programmaticToolOverrides)) {
|
|
105
|
+
out[name] = value;
|
|
106
|
+
}
|
|
107
|
+
return out;
|
|
108
|
+
}
|
|
109
|
+
function composeAgentExtensionEntries(fileEntries) {
|
|
110
|
+
const out = [];
|
|
111
|
+
for (const entry of fileEntries) {
|
|
112
|
+
if (Array.isArray(entry?.extensions)) out.push(...entry.extensions);
|
|
113
|
+
}
|
|
114
|
+
out.push(...modulesConfigAgentExtensions);
|
|
115
|
+
out.push(...programmaticAgentExtensions);
|
|
116
|
+
return out;
|
|
117
|
+
}
|
|
118
|
+
function applyAgentOverrideMap(base, overrides) {
|
|
119
|
+
if (!overrides || Object.keys(overrides).length === 0) return base.slice();
|
|
120
|
+
const byId = /* @__PURE__ */ new Map();
|
|
121
|
+
for (const agent of base) {
|
|
122
|
+
if (agent && typeof agent.id === "string" && agent.id) {
|
|
123
|
+
byId.set(agent.id, agent);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
for (const [id, value] of Object.entries(overrides)) {
|
|
127
|
+
if (!byId.has(id) && value !== null) {
|
|
128
|
+
console.warn(
|
|
129
|
+
`[AI Overrides] Override registers a new agent "${id}" \u2014 no base entry to replace.`
|
|
130
|
+
);
|
|
131
|
+
}
|
|
132
|
+
if (value === null) {
|
|
133
|
+
byId.delete(id);
|
|
134
|
+
continue;
|
|
135
|
+
}
|
|
136
|
+
if (!value || typeof value.id !== "string" || value.id !== id) {
|
|
137
|
+
console.warn(
|
|
138
|
+
`[AI Overrides] Skipping malformed agent override for id "${id}" \u2014 id mismatch or missing fields.`
|
|
139
|
+
);
|
|
140
|
+
continue;
|
|
141
|
+
}
|
|
142
|
+
byId.set(id, value);
|
|
143
|
+
}
|
|
144
|
+
return Array.from(byId.values());
|
|
145
|
+
}
|
|
146
|
+
function applyToolOverrideMap(base, overrides) {
|
|
147
|
+
const out = new Map(base);
|
|
148
|
+
if (!overrides) return out;
|
|
149
|
+
for (const [name, value] of Object.entries(overrides)) {
|
|
150
|
+
if (value === null) {
|
|
151
|
+
out.delete(name);
|
|
152
|
+
continue;
|
|
153
|
+
}
|
|
154
|
+
if (value === void 0) continue;
|
|
155
|
+
if (!value || typeof value.name !== "string" || value.name !== name) {
|
|
156
|
+
console.warn(
|
|
157
|
+
`[AI Overrides] Skipping malformed tool override for name "${name}" \u2014 name mismatch or missing fields.`
|
|
158
|
+
);
|
|
159
|
+
continue;
|
|
160
|
+
}
|
|
161
|
+
out.set(name, value);
|
|
162
|
+
}
|
|
163
|
+
return out;
|
|
164
|
+
}
|
|
165
|
+
function snapshotProgrammaticOverrides() {
|
|
166
|
+
return {
|
|
167
|
+
agents: { ...programmaticAgentOverrides },
|
|
168
|
+
tools: { ...programmaticToolOverrides },
|
|
169
|
+
modulesConfigAgents: { ...modulesConfigAgentOverrides },
|
|
170
|
+
modulesConfigTools: { ...modulesConfigToolOverrides },
|
|
171
|
+
agentExtensions: programmaticAgentExtensions.slice(),
|
|
172
|
+
modulesConfigAgentExtensions: modulesConfigAgentExtensions.slice()
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
export {
|
|
176
|
+
applyAgentOverrideMap,
|
|
177
|
+
applyAiAgentExtensions,
|
|
178
|
+
applyAiAgentOverrides,
|
|
179
|
+
applyAiOverridesDispatcherEntries,
|
|
180
|
+
applyAiOverridesFromEnabledModules,
|
|
181
|
+
applyAiToolOverrides,
|
|
182
|
+
applyToolOverrideMap,
|
|
183
|
+
composeAgentExtensionEntries,
|
|
184
|
+
composeAgentOverrideMap,
|
|
185
|
+
composeToolOverrideMap,
|
|
186
|
+
resetProgrammaticOverridesForTests,
|
|
187
|
+
snapshotProgrammaticOverrides
|
|
188
|
+
};
|
|
189
|
+
//# sourceMappingURL=ai-overrides.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../../src/modules/ai_assistant/lib/ai-overrides.ts"],
|
|
4
|
+
"sourcesContent": ["import {\n registerModuleOverrideApplier,\n type ModuleOverrideEntry,\n} from '@open-mercato/shared/modules/overrides'\n\n/**\n * Module-to-module override pipeline for AI agents and AI tools.\n *\n * Modules contribute overrides through two surfaces:\n *\n * 1. Per-module: declare additional `aiAgentOverrides` / `aiToolOverrides`\n * exports in the existing `<module>/ai-agents.ts` / `<module>/ai-tools.ts`\n * files (no separate `ai-overrides.ts` file). The generator picks the\n * exports up alongside `aiAgents` / `aiTools` and emits override entries\n * inside `apps/<app>/.mercato/generated/ai-agents.generated.ts`\n * (`aiAgentOverrideEntries`) and `ai-tools.generated.ts`\n * (`aiToolOverrideEntries`).\n *\n * 2. App-level: declare `aiAgentOverrides` / `aiToolOverrides` directly on a\n * `ModuleEntry` inside the app's `src/modules.ts`. {@link\n * applyAiOverridesFromEnabledModules} feeds these into the runtime; they\n * sit one tier higher than the per-module file-based entries but below\n * explicit programmatic calls.\n *\n * Tests and bootstrap code can also override imperatively via\n * {@link applyAiAgentOverrides} and {@link applyAiToolOverrides}, which\n * supersede every other tier and persist for the process lifetime.\n *\n * `null` always means \"remove from the registry\"; a definition replaces.\n *\n * @see ../../../../../../.ai/specs/2026-04-30-ai-overrides-and-module-disable.md\n */\nimport type { AiAgentDefinition, AiAgentExtension } from './ai-agent-definition'\nimport type { AiToolDefinition } from './types'\n\n/** Override for a single agent: replace with a definition or remove with `null`. */\nexport type AiAgentOverride = AiAgentDefinition | null\n\n/** Override for a single tool: replace with a definition or remove with `null`. */\nexport type AiToolOverride = AiToolDefinition | null\n\n/** Map of agent id \u2192 override. Used in the per-module `ai-agents.ts` file. */\nexport type AiAgentOverridesMap = Record<string, AiAgentOverride>\n\n/** Map of tool name \u2192 override. Used in the per-module `ai-tools.ts` file. */\nexport type AiToolOverridesMap = Record<string, AiToolOverride>\n\n/**\n * Per-entry shape produced by the agent generator. Mirrors the per-module\n * record format used elsewhere in the registry generators so the file\n * stays grep-friendly.\n */\nexport interface AiAgentOverrideConfigEntry {\n moduleId: string\n overrides: AiAgentOverridesMap\n}\n\nexport interface AiAgentExtensionConfigEntry {\n moduleId: string\n extensions: AiAgentExtension[]\n}\n\n/**\n * Per-entry shape produced by the tool generator. Same record format as\n * {@link AiAgentOverrideConfigEntry}, but with tool definitions.\n */\nexport interface AiToolOverrideConfigEntry {\n moduleId: string\n overrides: AiToolOverridesMap\n}\n\n/** Shape of the `entry.overrides.ai` sub-tree on a `modules.ts` entry. */\nexport interface AiModuleOverridesShape {\n agents?: AiAgentOverridesMap\n tools?: AiToolOverridesMap\n extensions?: AiAgentExtension[]\n}\n\n/** Shape of a `modules.ts` `ModuleEntry` with the umbrella `overrides.ai`. */\nexport interface EnabledModuleAiOverrides {\n id: string\n overrides?: { ai?: AiModuleOverridesShape }\n}\n\nconst programmaticAgentOverrides: AiAgentOverridesMap = {}\nconst programmaticToolOverrides: AiToolOverridesMap = {}\nconst programmaticAgentExtensions: AiAgentExtension[] = []\n\nconst modulesConfigAgentOverrides: AiAgentOverridesMap = {}\nconst modulesConfigToolOverrides: AiToolOverridesMap = {}\nconst modulesConfigAgentExtensions: AiAgentExtension[] = []\n\n/**\n * Apply programmatic agent overrides \u2014 survive after the registries load\n * and take precedence over both file-based and `modules.ts`-tier overrides\n * for the same id.\n *\n * @example\n * ```ts\n * applyAiAgentOverrides({\n * 'catalog.catalog_assistant': null, // disable\n * 'catalog.merchandising_assistant': customAgent, // replace\n * })\n * ```\n */\nexport function applyAiAgentOverrides(overrides: AiAgentOverridesMap): void {\n for (const [id, value] of Object.entries(overrides)) {\n programmaticAgentOverrides[id] = value\n }\n}\n\n/**\n * Apply programmatic tool overrides \u2014 survive after the registries load\n * and take precedence over both file-based and `modules.ts`-tier overrides\n * for the same name.\n */\nexport function applyAiToolOverrides(overrides: AiToolOverridesMap): void {\n for (const [name, value] of Object.entries(overrides)) {\n programmaticToolOverrides[name] = value\n }\n}\n\n/**\n * Apply programmatic additive agent extensions. Extensions append to an\n * already-registered agent after replacements/disable overrides resolve.\n */\nexport function applyAiAgentExtensions(extensions: readonly AiAgentExtension[]): void {\n programmaticAgentExtensions.push(...extensions)\n}\n\n/**\n * Walk a list of `enabledModules` entries (the `apps/<app>/src/modules.ts`\n * shape) and register their `overrides.ai.agents` / `overrides.ai.tools`\n * into the `modules.ts` tier. Tier precedence (highest first):\n *\n * 1. {@link applyAiAgentOverrides} / {@link applyAiToolOverrides}\n * 2. `modules.ts` inline (this function)\n * 3. `<module>/ai-agents.ts` `aiAgentOverrides` / `<module>/ai-tools.ts`\n * `aiToolOverrides`\n * 4. base `aiAgents` / `aiTools`\n *\n * Calling this multiple times is additive: later calls overlay on the\n * existing tier (last wins per id). Use only at boot time \u2014 re-entering\n * mid-request blurs the resolution order.\n *\n * In practice this is invoked from `applyModuleOverridesFromEnabledModules`\n * (the umbrella dispatcher in `@open-mercato/shared/modules/overrides`)\n * via the registered `'ai'` applier; downstream apps call the dispatcher\n * once from `bootstrap.ts`. The standalone signature is kept for tests\n * and ad-hoc use.\n */\nexport function applyAiOverridesFromEnabledModules(\n modules: ReadonlyArray<EnabledModuleAiOverrides>,\n): void {\n for (const entry of modules) {\n if (!entry || typeof entry.id !== 'string' || !entry.id) continue\n const ai = entry.overrides?.ai\n if (!ai || typeof ai !== 'object') continue\n if (ai.agents && typeof ai.agents === 'object') {\n for (const [id, value] of Object.entries(ai.agents)) {\n modulesConfigAgentOverrides[id] = value as AiAgentOverride\n }\n }\n if (ai.tools && typeof ai.tools === 'object') {\n for (const [name, value] of Object.entries(ai.tools)) {\n modulesConfigToolOverrides[name] = value as AiToolOverride\n }\n }\n if (Array.isArray(ai.extensions)) {\n modulesConfigAgentExtensions.push(...ai.extensions)\n }\n }\n}\n\n/**\n * Bucketed entry shape passed to the umbrella dispatcher's per-domain\n * applier. Each entry carries one module's `overrides.ai` sub-tree.\n */\ntype AiOverrideEntryFromDispatcher = {\n moduleId: string\n overrides: AiModuleOverridesShape\n}\n\n/**\n * Applier registered with `@open-mercato/shared/modules/overrides` for\n * the `'ai'` domain. The dispatcher hands us per-module entries already\n * scoped to `overrides.ai`; we re-shape into `EnabledModuleAiOverrides`\n * and reuse {@link applyAiOverridesFromEnabledModules} so the AI tier\n * has exactly one code path.\n */\nexport function applyAiOverridesDispatcherEntries(\n entries: ReadonlyArray<AiOverrideEntryFromDispatcher>,\n): void {\n applyAiOverridesFromEnabledModules(\n entries.map((entry) => ({\n id: entry.moduleId,\n overrides: { ai: entry.overrides },\n })),\n )\n}\n\n// Side-effect: register the `'ai'` applier on first module load so the\n// umbrella dispatcher in `@open-mercato/shared/modules/overrides` can\n// route `entry.overrides.ai` here. Any consumer that imports\n// `@open-mercato/ai-assistant` (which apps do via `bootstrap.ts`) gets\n// the registration for free \u2014 no second import needed.\nregisterModuleOverrideApplier<AiModuleOverridesShape>(\n 'ai',\n (entries: ReadonlyArray<ModuleOverrideEntry<AiModuleOverridesShape>>) => {\n applyAiOverridesDispatcherEntries(entries)\n },\n)\n\n/** @__internal Test-only hook \u2014 reset programmatic + modules.ts override state. */\nexport function resetProgrammaticOverridesForTests(): void {\n for (const key of Object.keys(programmaticAgentOverrides)) {\n delete programmaticAgentOverrides[key]\n }\n for (const key of Object.keys(programmaticToolOverrides)) {\n delete programmaticToolOverrides[key]\n }\n for (const key of Object.keys(modulesConfigAgentOverrides)) {\n delete modulesConfigAgentOverrides[key]\n }\n for (const key of Object.keys(modulesConfigToolOverrides)) {\n delete modulesConfigToolOverrides[key]\n }\n programmaticAgentExtensions.length = 0\n modulesConfigAgentExtensions.length = 0\n}\n\n/**\n * Resolve the final agent override map from a list of file-based entries\n * plus the `modules.ts` and programmatic state.\n *\n * Resolution order (lowest precedence \u2192 highest):\n * 1. file entries in module load order\n * 2. modules.ts entries\n * 3. programmatic\n */\nexport function composeAgentOverrideMap(\n fileEntries: readonly AiAgentOverrideConfigEntry[],\n): AiAgentOverridesMap {\n const out: AiAgentOverridesMap = {}\n for (const entry of fileEntries) {\n const overrides = entry?.overrides\n if (!overrides || typeof overrides !== 'object') continue\n for (const [id, value] of Object.entries(overrides)) {\n if (typeof id !== 'string' || !id) continue\n out[id] = value as AiAgentOverride\n }\n }\n for (const [id, value] of Object.entries(modulesConfigAgentOverrides)) {\n out[id] = value\n }\n for (const [id, value] of Object.entries(programmaticAgentOverrides)) {\n out[id] = value\n }\n return out\n}\n\n/**\n * Resolve the final tool override map from a list of file-based entries\n * plus the `modules.ts` and programmatic state.\n */\nexport function composeToolOverrideMap(\n fileEntries: readonly AiToolOverrideConfigEntry[],\n): AiToolOverridesMap {\n const out: AiToolOverridesMap = {}\n for (const entry of fileEntries) {\n const overrides = entry?.overrides\n if (!overrides || typeof overrides !== 'object') continue\n for (const [name, value] of Object.entries(overrides)) {\n if (typeof name !== 'string' || !name) continue\n out[name] = value as AiToolOverride\n }\n }\n for (const [name, value] of Object.entries(modulesConfigToolOverrides)) {\n out[name] = value\n }\n for (const [name, value] of Object.entries(programmaticToolOverrides)) {\n out[name] = value\n }\n return out\n}\n\nexport function composeAgentExtensionEntries(\n fileEntries: readonly AiAgentExtensionConfigEntry[],\n): AiAgentExtension[] {\n const out: AiAgentExtension[] = []\n for (const entry of fileEntries) {\n if (Array.isArray(entry?.extensions)) out.push(...entry.extensions)\n }\n out.push(...modulesConfigAgentExtensions)\n out.push(...programmaticAgentExtensions)\n return out\n}\n\n/**\n * Apply an agent override map to a base list. Returns a new array.\n * `null` removes the entry; a non-null override replaces it. Override\n * entries naming an id that is not in `base` log a structured warning so\n * an operator can spot stale override files.\n */\nexport function applyAgentOverrideMap(\n base: readonly AiAgentDefinition[],\n overrides: AiAgentOverridesMap,\n): AiAgentDefinition[] {\n if (!overrides || Object.keys(overrides).length === 0) return base.slice()\n const byId = new Map<string, AiAgentDefinition>()\n for (const agent of base) {\n if (agent && typeof agent.id === 'string' && agent.id) {\n byId.set(agent.id, agent)\n }\n }\n for (const [id, value] of Object.entries(overrides)) {\n if (!byId.has(id) && value !== null) {\n // Allow registering a brand-new agent through the override surface\n // \u2014 useful for app-level \"synthetic\" agents without authoring a\n // module file. Warn at the structured logger so the operator\n // notices a stale id slipping through.\n console.warn(\n `[AI Overrides] Override registers a new agent \"${id}\" \u2014 no base entry to replace.`,\n )\n }\n if (value === null) {\n byId.delete(id)\n continue\n }\n if (!value || typeof value.id !== 'string' || value.id !== id) {\n console.warn(\n `[AI Overrides] Skipping malformed agent override for id \"${id}\" \u2014 id mismatch or missing fields.`,\n )\n continue\n }\n byId.set(id, value)\n }\n return Array.from(byId.values())\n}\n\n/**\n * Apply a tool override map to a base map. Returns a new Map.\n * `null` removes the entry; a non-null override replaces it.\n */\nexport function applyToolOverrideMap<TTool extends { name: string }>(\n base: ReadonlyMap<string, TTool>,\n overrides: Record<string, TTool | null | undefined>,\n): Map<string, TTool> {\n const out = new Map<string, TTool>(base)\n if (!overrides) return out\n for (const [name, value] of Object.entries(overrides)) {\n if (value === null) {\n out.delete(name)\n continue\n }\n if (value === undefined) continue\n if (!value || typeof (value as TTool).name !== 'string' || (value as TTool).name !== name) {\n console.warn(\n `[AI Overrides] Skipping malformed tool override for name \"${name}\" \u2014 name mismatch or missing fields.`,\n )\n continue\n }\n out.set(name, value as TTool)\n }\n return out\n}\n\n/**\n * @__internal \u2014 read the snapshot of programmatic + modules.ts overrides.\n * Used by tests and by the diagnostic helper that reports which overrides\n * are in effect.\n */\nexport function snapshotProgrammaticOverrides(): {\n agents: Readonly<AiAgentOverridesMap>\n tools: Readonly<AiToolOverridesMap>\n modulesConfigAgents: Readonly<AiAgentOverridesMap>\n modulesConfigTools: Readonly<AiToolOverridesMap>\n agentExtensions: readonly AiAgentExtension[]\n modulesConfigAgentExtensions: readonly AiAgentExtension[]\n} {\n return {\n agents: { ...programmaticAgentOverrides },\n tools: { ...programmaticToolOverrides },\n modulesConfigAgents: { ...modulesConfigAgentOverrides },\n modulesConfigTools: { ...modulesConfigToolOverrides },\n agentExtensions: programmaticAgentExtensions.slice(),\n modulesConfigAgentExtensions: modulesConfigAgentExtensions.slice(),\n }\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../../src/modules/ai_assistant/lib/ai-tool-definition.ts"],
|
|
4
|
+
"sourcesContent": ["import type { AiToolDefinition } from './types'\n\nexport function defineAiTool<TInput = unknown, TOutput = unknown>(\n tool: AiToolDefinition<TInput, TOutput>,\n): AiToolDefinition<TInput, TOutput> {\n return tool\n}\n"],
|
|
5
|
+
"mappings": "AAEO,SAAS,aACd,MACmC;AACnC,SAAO;AACT;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
//# sourceMappingURL=ai-tools-generated.d.js.map
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { defineAiTool } from "./ai-tool-definition.js";
|
|
2
|
+
import {
|
|
3
|
+
createAiApiOperationRunner
|
|
4
|
+
} from "./ai-api-operation-runner.js";
|
|
5
|
+
function defineApiBackedAiTool(config) {
|
|
6
|
+
const {
|
|
7
|
+
name,
|
|
8
|
+
displayName,
|
|
9
|
+
description,
|
|
10
|
+
inputSchema,
|
|
11
|
+
requiredFeatures,
|
|
12
|
+
isMutation,
|
|
13
|
+
toOperation,
|
|
14
|
+
mapResponse,
|
|
15
|
+
loadBeforeRecord,
|
|
16
|
+
loadBeforeRecords
|
|
17
|
+
} = config;
|
|
18
|
+
let definition;
|
|
19
|
+
const handler = async (input, context) => {
|
|
20
|
+
const toolCtx = {
|
|
21
|
+
...context,
|
|
22
|
+
tool: definition
|
|
23
|
+
};
|
|
24
|
+
const operation = await toOperation(input, toolCtx);
|
|
25
|
+
const runner = createAiApiOperationRunner(toolCtx);
|
|
26
|
+
const response = await runner.run(operation);
|
|
27
|
+
if (!response.success) {
|
|
28
|
+
throw new Error(response.error ?? `API operation failed for tool "${name}"`);
|
|
29
|
+
}
|
|
30
|
+
return await mapResponse(response, input, toolCtx);
|
|
31
|
+
};
|
|
32
|
+
definition = defineAiTool({
|
|
33
|
+
name,
|
|
34
|
+
description,
|
|
35
|
+
inputSchema,
|
|
36
|
+
requiredFeatures,
|
|
37
|
+
handler,
|
|
38
|
+
...displayName !== void 0 ? { displayName } : {},
|
|
39
|
+
...isMutation !== void 0 ? { isMutation } : {},
|
|
40
|
+
...loadBeforeRecord !== void 0 ? { loadBeforeRecord } : {},
|
|
41
|
+
...loadBeforeRecords !== void 0 ? { loadBeforeRecords } : {}
|
|
42
|
+
});
|
|
43
|
+
return definition;
|
|
44
|
+
}
|
|
45
|
+
export {
|
|
46
|
+
defineApiBackedAiTool
|
|
47
|
+
};
|
|
48
|
+
//# sourceMappingURL=api-backed-tool.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../../src/modules/ai_assistant/lib/api-backed-tool.ts"],
|
|
4
|
+
"sourcesContent": ["// Phase 2 of spec 2026-04-27-ai-tools-api-backed-dry-refactor.md.\n//\n// Sugar over `defineAiTool` that wires the in-process API operation runner so\n// typed AI tools can reuse documented API route logic without HTTP, fetch, or\n// a second RBAC pass. The synthesized handler delegates to\n// `createAiApiOperationRunner(ctx).run(toOperation(input, ctx))` and pipes the\n// response through `mapResponse(...)`. All other tool-runtime concerns\n// (registry indexing, schema serialization, mutation policy, pending-action\n// flow, `loadBeforeRecord(s)`, telemetry) remain owned by `defineAiTool`.\nimport type { z } from 'zod'\nimport { defineAiTool } from './ai-tool-definition'\nimport {\n createAiApiOperationRunner,\n type AiApiOperationRequest,\n type AiApiOperationResponse,\n type AiToolExecutionContext,\n} from './ai-api-operation-runner'\nimport type { AiToolDefinition, McpToolContext } from './types'\n\nexport type ApiBackedAiToolConfig<TInput, TApi, TOutput> = {\n name: string\n displayName?: string\n description: string\n inputSchema: z.ZodType<TInput>\n requiredFeatures: string[]\n isMutation?: boolean\n toOperation: (\n input: TInput,\n ctx: AiToolExecutionContext,\n ) => AiApiOperationRequest | Promise<AiApiOperationRequest>\n mapResponse: (\n response: AiApiOperationResponse<TApi>,\n input: TInput,\n ctx: AiToolExecutionContext,\n ) => TOutput | Promise<TOutput>\n loadBeforeRecord?: AiToolDefinition<TInput, TOutput>['loadBeforeRecord']\n loadBeforeRecords?: AiToolDefinition<TInput, TOutput>['loadBeforeRecords']\n}\n\nexport function defineApiBackedAiTool<TInput, TApi, TOutput>(\n config: ApiBackedAiToolConfig<TInput, TApi, TOutput>,\n): AiToolDefinition<TInput, TOutput> {\n const {\n name,\n displayName,\n description,\n inputSchema,\n requiredFeatures,\n isMutation,\n toOperation,\n mapResponse,\n loadBeforeRecord,\n loadBeforeRecords,\n } = config\n\n let definition: AiToolDefinition<TInput, TOutput>\n\n const handler = async (input: TInput, context: McpToolContext): Promise<TOutput> => {\n const toolCtx: AiToolExecutionContext = {\n ...context,\n tool: definition as unknown as AiToolDefinition,\n }\n const operation = await toOperation(input, toolCtx)\n const runner = createAiApiOperationRunner(toolCtx)\n const response = await runner.run<TApi>(operation)\n if (!response.success) {\n throw new Error(response.error ?? `API operation failed for tool \"${name}\"`)\n }\n return await mapResponse(response, input, toolCtx)\n }\n\n definition = defineAiTool<TInput, TOutput>({\n name,\n description,\n inputSchema,\n requiredFeatures,\n handler,\n ...(displayName !== undefined ? { displayName } : {}),\n ...(isMutation !== undefined ? { isMutation } : {}),\n ...(loadBeforeRecord !== undefined ? { loadBeforeRecord } : {}),\n ...(loadBeforeRecords !== undefined ? { loadBeforeRecords } : {}),\n })\n\n return definition\n}\n"],
|
|
5
|
+
"mappings": "AAUA,SAAS,oBAAoB;AAC7B;AAAA,EACE;AAAA,OAIK;AAuBA,SAAS,sBACd,QACmC;AACnC,QAAM;AAAA,IACJ;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF,IAAI;AAEJ,MAAI;AAEJ,QAAM,UAAU,OAAO,OAAe,YAA8C;AAClF,UAAM,UAAkC;AAAA,MACtC,GAAG;AAAA,MACH,MAAM;AAAA,IACR;AACA,UAAM,YAAY,MAAM,YAAY,OAAO,OAAO;AAClD,UAAM,SAAS,2BAA2B,OAAO;AACjD,UAAM,WAAW,MAAM,OAAO,IAAU,SAAS;AACjD,QAAI,CAAC,SAAS,SAAS;AACrB,YAAM,IAAI,MAAM,SAAS,SAAS,kCAAkC,IAAI,GAAG;AAAA,IAC7E;AACA,WAAO,MAAM,YAAY,UAAU,OAAO,OAAO;AAAA,EACnD;AAEA,eAAa,aAA8B;AAAA,IACzC;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,GAAI,gBAAgB,SAAY,EAAE,YAAY,IAAI,CAAC;AAAA,IACnD,GAAI,eAAe,SAAY,EAAE,WAAW,IAAI,CAAC;AAAA,IACjD,GAAI,qBAAqB,SAAY,EAAE,iBAAiB,IAAI,CAAC;AAAA,IAC7D,GAAI,sBAAsB,SAAY,EAAE,kBAAkB,IAAI,CAAC;AAAA,EACjE,CAAC;AAED,SAAO;AACT;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
//# sourceMappingURL=attachment-bridge-types.js.map
|
|
@@ -0,0 +1,276 @@
|
|
|
1
|
+
import { promises as fs } from "fs";
|
|
2
|
+
import { findOneWithDecryption } from "@open-mercato/shared/lib/encryption/find";
|
|
3
|
+
const DEFAULT_MAX_INLINE_BYTES = 4 * 1024 * 1024;
|
|
4
|
+
const DEFAULT_MAX_TEXT_CHARS = 64 * 1024;
|
|
5
|
+
function classifyMediaType(mimeType) {
|
|
6
|
+
const normalized = (mimeType ?? "").toLowerCase().trim();
|
|
7
|
+
if (normalized.startsWith("image/")) return "image";
|
|
8
|
+
if (normalized === "application/pdf") return "pdf";
|
|
9
|
+
return "file";
|
|
10
|
+
}
|
|
11
|
+
function isTextLikeMime(mimeType) {
|
|
12
|
+
const normalized = (mimeType ?? "").toLowerCase().trim();
|
|
13
|
+
if (!normalized) return false;
|
|
14
|
+
if (normalized.startsWith("text/")) return true;
|
|
15
|
+
if (normalized === "application/json") return true;
|
|
16
|
+
if (normalized === "application/xml") return true;
|
|
17
|
+
if (normalized === "application/x-yaml" || normalized === "text/yaml") return true;
|
|
18
|
+
if (normalized === "application/csv") return true;
|
|
19
|
+
return false;
|
|
20
|
+
}
|
|
21
|
+
function truncateText(value, maxChars) {
|
|
22
|
+
if (value.length <= maxChars) return value;
|
|
23
|
+
return `${value.slice(0, Math.max(0, maxChars - 16))}
|
|
24
|
+
[... truncated]`;
|
|
25
|
+
}
|
|
26
|
+
function resolveEm(container) {
|
|
27
|
+
if (!container) return null;
|
|
28
|
+
try {
|
|
29
|
+
const candidate = container.resolve("em");
|
|
30
|
+
return candidate ?? null;
|
|
31
|
+
} catch {
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
function resolveSigner(container) {
|
|
36
|
+
if (!container) return null;
|
|
37
|
+
try {
|
|
38
|
+
const candidate = container.resolve("attachmentSigner");
|
|
39
|
+
if (candidate && typeof candidate.sign === "function") {
|
|
40
|
+
return candidate;
|
|
41
|
+
}
|
|
42
|
+
} catch {
|
|
43
|
+
return null;
|
|
44
|
+
}
|
|
45
|
+
return null;
|
|
46
|
+
}
|
|
47
|
+
async function loadAttachmentRow(em, attachmentId, authContext) {
|
|
48
|
+
const { Attachment } = await import("@open-mercato/core/modules/attachments/data/entities");
|
|
49
|
+
const record = await findOneWithDecryption(
|
|
50
|
+
em,
|
|
51
|
+
Attachment,
|
|
52
|
+
{ id: attachmentId },
|
|
53
|
+
void 0,
|
|
54
|
+
{
|
|
55
|
+
tenantId: authContext.tenantId,
|
|
56
|
+
organizationId: authContext.organizationId
|
|
57
|
+
}
|
|
58
|
+
);
|
|
59
|
+
if (!record) return null;
|
|
60
|
+
const row = record;
|
|
61
|
+
return {
|
|
62
|
+
id: row.id,
|
|
63
|
+
entityId: row.entityId,
|
|
64
|
+
fileName: row.fileName,
|
|
65
|
+
mimeType: row.mimeType,
|
|
66
|
+
fileSize: row.fileSize,
|
|
67
|
+
storagePath: row.storagePath,
|
|
68
|
+
storageDriver: row.storageDriver,
|
|
69
|
+
partitionCode: row.partitionCode,
|
|
70
|
+
tenantId: row.tenantId ?? null,
|
|
71
|
+
organizationId: row.organizationId ?? null,
|
|
72
|
+
content: row.content ?? null
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
function rowBelongsToCaller(row, authContext) {
|
|
76
|
+
if (authContext.isSuperAdmin) return true;
|
|
77
|
+
if (row.tenantId && row.tenantId !== authContext.tenantId) return false;
|
|
78
|
+
if (row.organizationId && row.organizationId !== authContext.organizationId) return false;
|
|
79
|
+
return true;
|
|
80
|
+
}
|
|
81
|
+
async function readAttachmentBytes(row) {
|
|
82
|
+
const { resolveAttachmentAbsolutePath } = await import("@open-mercato/core/modules/attachments/lib/storage");
|
|
83
|
+
const absolutePath = resolveAttachmentAbsolutePath(
|
|
84
|
+
row.partitionCode,
|
|
85
|
+
row.storagePath,
|
|
86
|
+
row.storageDriver
|
|
87
|
+
);
|
|
88
|
+
try {
|
|
89
|
+
const buffer = await fs.readFile(absolutePath);
|
|
90
|
+
return new Uint8Array(buffer);
|
|
91
|
+
} catch (error) {
|
|
92
|
+
console.warn(
|
|
93
|
+
`[AI Agents] Failed to read attachment ${row.id} from storage; falling back to metadata-only:`,
|
|
94
|
+
error
|
|
95
|
+
);
|
|
96
|
+
return null;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
async function classifyAndBuildPart(row, mediaClass, maxInlineBytes, maxTextChars, signer, authContext) {
|
|
100
|
+
const base = {
|
|
101
|
+
attachmentId: row.id,
|
|
102
|
+
fileName: row.fileName,
|
|
103
|
+
mediaType: row.mimeType || "application/octet-stream"
|
|
104
|
+
};
|
|
105
|
+
if (mediaClass === "file" && isTextLikeMime(row.mimeType) && typeof row.content === "string" && row.content.length > 0) {
|
|
106
|
+
return {
|
|
107
|
+
...base,
|
|
108
|
+
source: "text",
|
|
109
|
+
textContent: truncateText(row.content, maxTextChars)
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
if (mediaClass === "image" || mediaClass === "pdf") {
|
|
113
|
+
if (row.fileSize > 0 && row.fileSize <= maxInlineBytes) {
|
|
114
|
+
const bytes = await readAttachmentBytes(row);
|
|
115
|
+
if (bytes) {
|
|
116
|
+
return {
|
|
117
|
+
...base,
|
|
118
|
+
source: "bytes",
|
|
119
|
+
data: bytes
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
if (signer) {
|
|
124
|
+
try {
|
|
125
|
+
const url = await signer.sign({
|
|
126
|
+
attachmentId: row.id,
|
|
127
|
+
fileName: row.fileName,
|
|
128
|
+
mediaType: row.mimeType,
|
|
129
|
+
tenantId: authContext.tenantId,
|
|
130
|
+
organizationId: authContext.organizationId
|
|
131
|
+
});
|
|
132
|
+
if (typeof url === "string" && url.length > 0) {
|
|
133
|
+
return {
|
|
134
|
+
...base,
|
|
135
|
+
source: "signed-url",
|
|
136
|
+
url
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
} catch (error) {
|
|
140
|
+
console.warn(
|
|
141
|
+
`[AI Agents] attachmentSigner failed for ${row.id}; falling back to metadata-only:`,
|
|
142
|
+
error
|
|
143
|
+
);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
return { ...base, source: "metadata-only" };
|
|
147
|
+
}
|
|
148
|
+
return { ...base, source: "metadata-only" };
|
|
149
|
+
}
|
|
150
|
+
async function resolveAttachmentParts(input) {
|
|
151
|
+
const ids = Array.from(input.attachmentIds ?? []);
|
|
152
|
+
if (ids.length === 0) return [];
|
|
153
|
+
const em = resolveEm(input.container);
|
|
154
|
+
if (!em) {
|
|
155
|
+
console.warn(
|
|
156
|
+
"[AI Agents] resolveAttachmentParts called without a DI container exposing `em`; skipping attachment resolution."
|
|
157
|
+
);
|
|
158
|
+
return [];
|
|
159
|
+
}
|
|
160
|
+
const maxInlineBytes = input.maxInlineBytes ?? DEFAULT_MAX_INLINE_BYTES;
|
|
161
|
+
const maxTextChars = input.maxTextChars ?? DEFAULT_MAX_TEXT_CHARS;
|
|
162
|
+
const signer = resolveSigner(input.container);
|
|
163
|
+
const acceptedSet = input.acceptedMediaTypes ? new Set(input.acceptedMediaTypes) : null;
|
|
164
|
+
const parts = [];
|
|
165
|
+
for (const id of ids) {
|
|
166
|
+
if (typeof id !== "string" || id.length === 0) continue;
|
|
167
|
+
let row;
|
|
168
|
+
try {
|
|
169
|
+
row = await loadAttachmentRow(em, id, input.authContext);
|
|
170
|
+
} catch (error) {
|
|
171
|
+
console.warn(
|
|
172
|
+
`[AI Agents] Failed to load attachment ${id}; skipping:`,
|
|
173
|
+
error
|
|
174
|
+
);
|
|
175
|
+
continue;
|
|
176
|
+
}
|
|
177
|
+
if (!row) {
|
|
178
|
+
console.warn(`[AI Agents] Attachment ${id} not found; skipping.`);
|
|
179
|
+
continue;
|
|
180
|
+
}
|
|
181
|
+
if (!rowBelongsToCaller(row, input.authContext)) {
|
|
182
|
+
console.warn(
|
|
183
|
+
`[AI Agents] Attachment ${id} is out of scope for caller (tenant=${input.authContext.tenantId}, org=${input.authContext.organizationId}); skipping.`
|
|
184
|
+
);
|
|
185
|
+
continue;
|
|
186
|
+
}
|
|
187
|
+
const mediaClass = classifyMediaType(row.mimeType);
|
|
188
|
+
if (acceptedSet && !acceptedSet.has(mediaClass)) {
|
|
189
|
+
console.warn(
|
|
190
|
+
`[AI Agents] Attachment ${id} (${row.mimeType}) is not in agent acceptedMediaTypes=${[...acceptedSet].join(",")}; skipping.`
|
|
191
|
+
);
|
|
192
|
+
continue;
|
|
193
|
+
}
|
|
194
|
+
try {
|
|
195
|
+
const part = await classifyAndBuildPart(
|
|
196
|
+
row,
|
|
197
|
+
mediaClass,
|
|
198
|
+
maxInlineBytes,
|
|
199
|
+
maxTextChars,
|
|
200
|
+
signer,
|
|
201
|
+
input.authContext
|
|
202
|
+
);
|
|
203
|
+
parts.push(part);
|
|
204
|
+
} catch (error) {
|
|
205
|
+
console.warn(
|
|
206
|
+
`[AI Agents] Failed to build attachment part for ${id}; skipping:`,
|
|
207
|
+
error
|
|
208
|
+
);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
return parts;
|
|
212
|
+
}
|
|
213
|
+
async function resolveAttachmentPartsForAgent(input) {
|
|
214
|
+
if (!input.attachmentIds || input.attachmentIds.length === 0) return [];
|
|
215
|
+
return resolveAttachmentParts({
|
|
216
|
+
attachmentIds: input.attachmentIds,
|
|
217
|
+
authContext: input.authContext,
|
|
218
|
+
acceptedMediaTypes: input.agent.acceptedMediaTypes,
|
|
219
|
+
container: input.container
|
|
220
|
+
});
|
|
221
|
+
}
|
|
222
|
+
function attachmentPartsToUiFileParts(parts) {
|
|
223
|
+
const output = [];
|
|
224
|
+
for (const part of parts) {
|
|
225
|
+
if (part.source === "bytes" && part.data) {
|
|
226
|
+
const base64 = toBase64(part.data);
|
|
227
|
+
if (base64) {
|
|
228
|
+
output.push({
|
|
229
|
+
type: "file",
|
|
230
|
+
mediaType: part.mediaType,
|
|
231
|
+
filename: part.fileName,
|
|
232
|
+
url: `data:${part.mediaType};base64,${base64}`
|
|
233
|
+
});
|
|
234
|
+
}
|
|
235
|
+
continue;
|
|
236
|
+
}
|
|
237
|
+
if (part.source === "signed-url" && typeof part.url === "string" && part.url.length > 0) {
|
|
238
|
+
output.push({
|
|
239
|
+
type: "file",
|
|
240
|
+
mediaType: part.mediaType,
|
|
241
|
+
filename: part.fileName,
|
|
242
|
+
url: part.url
|
|
243
|
+
});
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
return output;
|
|
247
|
+
}
|
|
248
|
+
function summarizeAttachmentPartsForPrompt(parts) {
|
|
249
|
+
if (parts.length === 0) return null;
|
|
250
|
+
const lines = ["[ATTACHMENTS]"];
|
|
251
|
+
for (const part of parts) {
|
|
252
|
+
const header = `- ${part.fileName} (${part.mediaType}, source=${part.source})`;
|
|
253
|
+
if (part.source === "text" && typeof part.textContent === "string" && part.textContent.length > 0) {
|
|
254
|
+
lines.push(header);
|
|
255
|
+
lines.push(part.textContent);
|
|
256
|
+
} else {
|
|
257
|
+
lines.push(header);
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
return lines.join("\n");
|
|
261
|
+
}
|
|
262
|
+
function toBase64(data) {
|
|
263
|
+
if (typeof data === "string") return data;
|
|
264
|
+
try {
|
|
265
|
+
return Buffer.from(data).toString("base64");
|
|
266
|
+
} catch {
|
|
267
|
+
return null;
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
export {
|
|
271
|
+
attachmentPartsToUiFileParts,
|
|
272
|
+
resolveAttachmentParts,
|
|
273
|
+
resolveAttachmentPartsForAgent,
|
|
274
|
+
summarizeAttachmentPartsForPrompt
|
|
275
|
+
};
|
|
276
|
+
//# sourceMappingURL=attachment-parts.js.map
|