@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,253 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
PromptSection,
|
|
3
|
+
PromptSectionName,
|
|
4
|
+
PromptTemplate,
|
|
5
|
+
} from './prompt-composition-types'
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Canonical prompt section names the built-in agent prompt templates ship.
|
|
9
|
+
* These align with the 7 section headers the spec (§8) mandates, plus the
|
|
10
|
+
* free-form `overrides` bucket the template already reserves.
|
|
11
|
+
*/
|
|
12
|
+
export const CANONICAL_PROMPT_SECTIONS: readonly PromptSectionName[] = [
|
|
13
|
+
'role',
|
|
14
|
+
'scope',
|
|
15
|
+
'data',
|
|
16
|
+
'tools',
|
|
17
|
+
'attachments',
|
|
18
|
+
'mutationPolicy',
|
|
19
|
+
'responseStyle',
|
|
20
|
+
'overrides',
|
|
21
|
+
] as const
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Reserved keys that MUST NOT appear in a prompt override. These name policy
|
|
25
|
+
* fields that live on the agent definition itself — allowing them to be
|
|
26
|
+
* modified via the prompt-override layer would let a tenant silently escalate
|
|
27
|
+
* an agent beyond the mutation / tool / attachment contract enforced by
|
|
28
|
+
* `checkAgentPolicy` at dispatch time.
|
|
29
|
+
*/
|
|
30
|
+
export const RESERVED_OVERRIDE_KEYS: readonly string[] = [
|
|
31
|
+
'mutationPolicy',
|
|
32
|
+
'readOnly',
|
|
33
|
+
'allowedTools',
|
|
34
|
+
'acceptedMediaTypes',
|
|
35
|
+
] as const
|
|
36
|
+
|
|
37
|
+
const HEADER_MAP: Readonly<Record<string, PromptSectionName>> = {
|
|
38
|
+
role: 'role',
|
|
39
|
+
scope: 'scope',
|
|
40
|
+
data: 'data',
|
|
41
|
+
tools: 'tools',
|
|
42
|
+
attachments: 'attachments',
|
|
43
|
+
'mutation policy': 'mutationPolicy',
|
|
44
|
+
mutation_policy: 'mutationPolicy',
|
|
45
|
+
mutationpolicy: 'mutationPolicy',
|
|
46
|
+
'response style': 'responseStyle',
|
|
47
|
+
response_style: 'responseStyle',
|
|
48
|
+
responsestyle: 'responseStyle',
|
|
49
|
+
overrides: 'overrides',
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function canonicalize(key: string): PromptSectionName | null {
|
|
53
|
+
const normalized = key.trim().toLowerCase()
|
|
54
|
+
if (!normalized) return null
|
|
55
|
+
return HEADER_MAP[normalized] ?? null
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function prettyHeader(name: PromptSectionName): string {
|
|
59
|
+
switch (name) {
|
|
60
|
+
case 'mutationPolicy':
|
|
61
|
+
return 'MUTATION POLICY'
|
|
62
|
+
case 'responseStyle':
|
|
63
|
+
return 'RESPONSE STYLE'
|
|
64
|
+
default:
|
|
65
|
+
return name.toUpperCase()
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export interface AppliedPromptOverride {
|
|
70
|
+
sections: PromptSection[]
|
|
71
|
+
systemPrompt: string
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export interface PromptOverrideInput {
|
|
75
|
+
/** Additive text keyed by canonical section id or free-form new header. */
|
|
76
|
+
sections: Record<string, string> | null | undefined
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Applies an additive prompt override to a built-in template. Rules:
|
|
81
|
+
*
|
|
82
|
+
* - A canonical section key (`role`, `scope`, `data`, `tools`, `attachments`,
|
|
83
|
+
* `MUTATION POLICY`, `RESPONSE STYLE`, `overrides`) APPENDS the override
|
|
84
|
+
* text below the built-in section content with a blank line separator.
|
|
85
|
+
* The built-in content is never removed or rewritten.
|
|
86
|
+
* - A non-canonical key is treated as a brand-new section and inserted
|
|
87
|
+
* after the canonical `RESPONSE STYLE` position (before `overrides`,
|
|
88
|
+
* if any). Canonical section order is always preserved.
|
|
89
|
+
* - Empty or whitespace-only override values are ignored.
|
|
90
|
+
* - If any reserved policy key is present (`mutationPolicy`, `readOnly`,
|
|
91
|
+
* `allowedTools`, `acceptedMediaTypes`), this function throws. Call sites
|
|
92
|
+
* SHOULD validate via {@link validatePromptOverrideInput} first so the
|
|
93
|
+
* error surfaces as a 400 to the API caller.
|
|
94
|
+
*/
|
|
95
|
+
export function applyPromptOverride(
|
|
96
|
+
template: PromptTemplate,
|
|
97
|
+
override: PromptOverrideInput | null | undefined,
|
|
98
|
+
): AppliedPromptOverride {
|
|
99
|
+
const baseSections = [...template.sections]
|
|
100
|
+
const sections = override?.sections ?? null
|
|
101
|
+
|
|
102
|
+
if (!sections || typeof sections !== 'object') {
|
|
103
|
+
return {
|
|
104
|
+
sections: baseSections,
|
|
105
|
+
systemPrompt: renderPrompt(baseSections),
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
assertNoReservedKeys(sections)
|
|
110
|
+
|
|
111
|
+
// First pass: append to canonical sections.
|
|
112
|
+
const byCanonical = new Map<PromptSectionName, string>()
|
|
113
|
+
const unknownKeys: Array<{ rawKey: string; value: string }> = []
|
|
114
|
+
for (const [rawKey, value] of Object.entries(sections)) {
|
|
115
|
+
if (typeof value !== 'string') continue
|
|
116
|
+
const trimmed = value.trim()
|
|
117
|
+
if (!trimmed) continue
|
|
118
|
+
const canonical = canonicalize(rawKey)
|
|
119
|
+
if (canonical) {
|
|
120
|
+
const existing = byCanonical.get(canonical)
|
|
121
|
+
byCanonical.set(canonical, existing ? `${existing}\n\n${trimmed}` : trimmed)
|
|
122
|
+
} else {
|
|
123
|
+
unknownKeys.push({ rawKey, value: trimmed })
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
const appended: PromptSection[] = baseSections.map((section) => {
|
|
128
|
+
const addendum = byCanonical.get(section.name)
|
|
129
|
+
if (!addendum) return section
|
|
130
|
+
const nextContent = section.content.trim().length === 0
|
|
131
|
+
? addendum
|
|
132
|
+
: `${section.content}\n\n${addendum}`
|
|
133
|
+
return { ...section, content: nextContent }
|
|
134
|
+
})
|
|
135
|
+
|
|
136
|
+
// If a canonical section wasn't already present but an override targets it,
|
|
137
|
+
// append it using its canonical header. Catalog / customer templates ship
|
|
138
|
+
// all 7 canonical headers today, but we defend against future templates
|
|
139
|
+
// that may omit some.
|
|
140
|
+
const declaredNames = new Set(appended.map((s) => s.name))
|
|
141
|
+
for (const [canonical, addendum] of byCanonical.entries()) {
|
|
142
|
+
if (!declaredNames.has(canonical)) {
|
|
143
|
+
appended.push({ name: canonical, content: addendum })
|
|
144
|
+
declaredNames.add(canonical)
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// Brand-new sections inject after RESPONSE STYLE. We insert by splitting the
|
|
149
|
+
// array at the index directly after `responseStyle`. If `overrides` is
|
|
150
|
+
// already present, new sections land before it so `overrides` stays last.
|
|
151
|
+
if (unknownKeys.length > 0) {
|
|
152
|
+
const newSections: PromptSection[] = unknownKeys.map(({ rawKey, value }) => ({
|
|
153
|
+
name: 'overrides',
|
|
154
|
+
content: `[${rawKey.trim().toUpperCase()}]\n${value}`,
|
|
155
|
+
}))
|
|
156
|
+
const responseStyleIndex = appended.findIndex((s) => s.name === 'responseStyle')
|
|
157
|
+
const overridesIndex = appended.findIndex((s) => s.name === 'overrides')
|
|
158
|
+
let insertAt: number
|
|
159
|
+
if (overridesIndex >= 0) {
|
|
160
|
+
insertAt = overridesIndex
|
|
161
|
+
} else if (responseStyleIndex >= 0) {
|
|
162
|
+
insertAt = responseStyleIndex + 1
|
|
163
|
+
} else {
|
|
164
|
+
insertAt = appended.length
|
|
165
|
+
}
|
|
166
|
+
appended.splice(insertAt, 0, ...newSections)
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
return {
|
|
170
|
+
sections: appended,
|
|
171
|
+
systemPrompt: renderPrompt(appended),
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Produces a flat string representation of a prompt template. The runtime uses
|
|
177
|
+
* this when the agent definition declares sections but the underlying call
|
|
178
|
+
* site expects a single-string `systemPrompt` (today: both `runAiAgentText`
|
|
179
|
+
* and `runAiAgentObject`).
|
|
180
|
+
*/
|
|
181
|
+
export function renderPrompt(sections: readonly PromptSection[]): string {
|
|
182
|
+
const lines: string[] = []
|
|
183
|
+
for (const section of sections) {
|
|
184
|
+
const content = section.content.trim()
|
|
185
|
+
if (!content) continue
|
|
186
|
+
lines.push(`[${prettyHeader(section.name)}]\n${content}`)
|
|
187
|
+
}
|
|
188
|
+
return lines.join('\n\n')
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* Returns the list of reserved keys present in the supplied override payload.
|
|
193
|
+
* Empty array means the body is safe for persistence. Call this before the
|
|
194
|
+
* repository `save` so the API layer can reject with a 400 / `reserved_key`.
|
|
195
|
+
*/
|
|
196
|
+
export function findReservedKeys(sections: Record<string, unknown> | null | undefined): string[] {
|
|
197
|
+
if (!sections || typeof sections !== 'object') return []
|
|
198
|
+
const reservedSet = new Set(RESERVED_OVERRIDE_KEYS.map((key) => key.toLowerCase()))
|
|
199
|
+
const hits: string[] = []
|
|
200
|
+
for (const key of Object.keys(sections)) {
|
|
201
|
+
if (reservedSet.has(key.trim().toLowerCase())) {
|
|
202
|
+
hits.push(key)
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
return hits
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
function assertNoReservedKeys(sections: Record<string, unknown>): void {
|
|
209
|
+
const hits = findReservedKeys(sections)
|
|
210
|
+
if (hits.length > 0) {
|
|
211
|
+
throw new PromptOverrideReservedKeyError(hits)
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
export class PromptOverrideReservedKeyError extends Error {
|
|
216
|
+
readonly code = 'reserved_key'
|
|
217
|
+
constructor(public readonly keys: readonly string[]) {
|
|
218
|
+
super(
|
|
219
|
+
`Prompt override includes reserved policy keys: ${keys.join(', ')}. ` +
|
|
220
|
+
`Policy fields (${RESERVED_OVERRIDE_KEYS.join(', ')}) are never editable via prompt overrides.`,
|
|
221
|
+
)
|
|
222
|
+
this.name = 'PromptOverrideReservedKeyError'
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* Convenience wrapper: composes the full system prompt for a legacy agent that
|
|
228
|
+
* ships a single-string `systemPrompt`. Returns the base unchanged when no
|
|
229
|
+
* override is present. When an override is present, the base is treated as the
|
|
230
|
+
* `role` section and overrides are layered via {@link applyPromptOverride}.
|
|
231
|
+
*/
|
|
232
|
+
export function composeSystemPromptWithOverride(
|
|
233
|
+
baseSystemPrompt: string,
|
|
234
|
+
override: PromptOverrideInput | null | undefined,
|
|
235
|
+
): string {
|
|
236
|
+
if (!override?.sections || Object.keys(override.sections).length === 0) {
|
|
237
|
+
return baseSystemPrompt
|
|
238
|
+
}
|
|
239
|
+
const template: PromptTemplate = {
|
|
240
|
+
id: 'legacy-system-prompt',
|
|
241
|
+
sections: [
|
|
242
|
+
{ name: 'role', content: baseSystemPrompt },
|
|
243
|
+
{ name: 'scope', content: '' },
|
|
244
|
+
{ name: 'data', content: '' },
|
|
245
|
+
{ name: 'tools', content: '' },
|
|
246
|
+
{ name: 'attachments', content: '' },
|
|
247
|
+
{ name: 'mutationPolicy', content: '' },
|
|
248
|
+
{ name: 'responseStyle', content: '' },
|
|
249
|
+
],
|
|
250
|
+
}
|
|
251
|
+
const applied = applyPromptOverride(template, override)
|
|
252
|
+
return applied.systemPrompt
|
|
253
|
+
}
|
|
@@ -44,8 +44,13 @@ export function jsonSchemaToZod(jsonSchema: Record<string, unknown>): ZodType {
|
|
|
44
44
|
const required = (jsonSchema.required as string[]) || []
|
|
45
45
|
const additionalProperties = jsonSchema.additionalProperties
|
|
46
46
|
|
|
47
|
-
// Handle z.record() - objects with additionalProperties but no fixed properties
|
|
48
|
-
|
|
47
|
+
// Handle z.record() - objects with additionalProperties but no fixed properties.
|
|
48
|
+
// Skip the record path when properties is present but empty — that is a
|
|
49
|
+
// no-arg object schema, not a dictionary. OpenAI requires `properties: {}`
|
|
50
|
+
// in the JSON Schema, and `z.object({})` produces that, whereas
|
|
51
|
+
// `z.record()` does not.
|
|
52
|
+
const hasFixedProperties = properties && Object.keys(properties).length > 0
|
|
53
|
+
if (additionalProperties && !hasFixedProperties && properties === undefined) {
|
|
49
54
|
// This is a record/dictionary type - allow any properties
|
|
50
55
|
if (typeof additionalProperties === 'object') {
|
|
51
56
|
return z.record(z.string(), jsonSchemaToZod(additionalProperties as Record<string, unknown>))
|
|
@@ -127,6 +132,13 @@ export function toSafeZodSchema(schema: ZodType): ZodType {
|
|
|
127
132
|
// Use Zod 4's toJSONSchema with unrepresentable: 'any' to handle Date types
|
|
128
133
|
const jsonSchema = z.toJSONSchema(schema, { unrepresentable: 'any' }) as Record<string, unknown>
|
|
129
134
|
|
|
135
|
+
// OpenAI requires `properties` on object schemas. Empty passthrough objects
|
|
136
|
+
// (tools that take no args) roundtrip as `{ type: "object", additionalProperties: true }`
|
|
137
|
+
// without `properties`, which OpenAI rejects. Ensure `properties` is present.
|
|
138
|
+
if (jsonSchema.type === 'object' && !jsonSchema.properties) {
|
|
139
|
+
jsonSchema.properties = {}
|
|
140
|
+
}
|
|
141
|
+
|
|
130
142
|
// Convert back to a simple Zod schema without Date types
|
|
131
143
|
const safeSchema = jsonSchemaToZod(jsonSchema)
|
|
132
144
|
|
|
@@ -3,6 +3,20 @@ import { getToolRegistry } from './tool-registry'
|
|
|
3
3
|
import { hasRequiredFeatures } from './auth'
|
|
4
4
|
import type { RbacService } from '@open-mercato/core/modules/auth/services/rbacService'
|
|
5
5
|
|
|
6
|
+
/**
|
|
7
|
+
* Strip empty strings from object values so LLM-generated `""` for optional
|
|
8
|
+
* fields becomes `undefined` (passes `.optional()` Zod validators).
|
|
9
|
+
*/
|
|
10
|
+
function sanitizeEmptyStrings(input: unknown): unknown {
|
|
11
|
+
if (!input || typeof input !== 'object' || Array.isArray(input)) return input
|
|
12
|
+
const result: Record<string, unknown> = {}
|
|
13
|
+
for (const [key, value] of Object.entries(input as Record<string, unknown>)) {
|
|
14
|
+
if (typeof value === 'string' && value.trim() === '') continue
|
|
15
|
+
result[key] = value
|
|
16
|
+
}
|
|
17
|
+
return result
|
|
18
|
+
}
|
|
19
|
+
|
|
6
20
|
/**
|
|
7
21
|
* Execute a tool with full context and ACL checks.
|
|
8
22
|
*/
|
|
@@ -41,8 +55,13 @@ export async function executeTool(
|
|
|
41
55
|
}
|
|
42
56
|
}
|
|
43
57
|
|
|
58
|
+
// LLMs often send empty strings for optional fields (e.g., `personId: ""`).
|
|
59
|
+
// Strip empty strings to `undefined` before Zod parsing so `.uuid().optional()`
|
|
60
|
+
// fields pass validation when the model meant "omit this field".
|
|
61
|
+
const sanitizedInput = sanitizeEmptyStrings(input)
|
|
62
|
+
|
|
44
63
|
// Input validation
|
|
45
|
-
const parseResult = tool.inputSchema.safeParse(
|
|
64
|
+
const parseResult = tool.inputSchema.safeParse(sanitizedInput)
|
|
46
65
|
if (!parseResult.success) {
|
|
47
66
|
// Use any cast for Zod v4 compatibility
|
|
48
67
|
const issues = (parseResult.error as any).issues ?? []
|
|
@@ -58,9 +77,12 @@ export async function executeTool(
|
|
|
58
77
|
}
|
|
59
78
|
}
|
|
60
79
|
|
|
61
|
-
// Execute tool
|
|
80
|
+
// Execute tool. Attach `tool` to the context so handlers that build an
|
|
81
|
+
// `AiToolExecutionContext` (e.g. via `createAiApiOperationRunner`) keep their
|
|
82
|
+
// route-gate coverage check working.
|
|
83
|
+
const handlerContext: McpToolContext = { ...context, tool }
|
|
62
84
|
try {
|
|
63
|
-
const result = await tool.handler(parseResult.data,
|
|
85
|
+
const result = await tool.handler(parseResult.data, handlerContext)
|
|
64
86
|
return { success: true, result }
|
|
65
87
|
} catch (error) {
|
|
66
88
|
const message = error instanceof Error ? error.message : String(error)
|
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
import { z } from 'zod'
|
|
2
2
|
import type { SearchService } from '@open-mercato/search/service'
|
|
3
|
-
import { registerMcpTool, getToolRegistry } from './tool-registry'
|
|
3
|
+
import { registerMcpTool, getToolRegistry, toolRegistry, unregisterMcpTool } from './tool-registry'
|
|
4
|
+
import {
|
|
5
|
+
applyToolOverrideMap,
|
|
6
|
+
composeToolOverrideMap,
|
|
7
|
+
type AiToolOverrideConfigEntry,
|
|
8
|
+
} from './ai-overrides'
|
|
4
9
|
import type { McpToolDefinition, McpToolContext } from './types'
|
|
5
10
|
import { ToolSearchService } from './tool-search'
|
|
6
11
|
|
|
@@ -15,6 +20,27 @@ type ModuleAiTool = {
|
|
|
15
20
|
handler: (input: any, ctx: any) => Promise<unknown>
|
|
16
21
|
}
|
|
17
22
|
|
|
23
|
+
/**
|
|
24
|
+
* Shape of a single entry inside `ai-tools.generated.ts`.
|
|
25
|
+
* Matches the structural contract emitted by
|
|
26
|
+
* `packages/cli/src/lib/generators/extensions/ai-tools.ts`.
|
|
27
|
+
*/
|
|
28
|
+
export type AiToolConfigEntry = {
|
|
29
|
+
moduleId: string
|
|
30
|
+
tools: unknown[]
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function isModuleAiTool(value: unknown): value is ModuleAiTool {
|
|
34
|
+
if (!value || typeof value !== 'object') return false
|
|
35
|
+
const candidate = value as Record<string, unknown>
|
|
36
|
+
return (
|
|
37
|
+
typeof candidate.name === 'string' &&
|
|
38
|
+
typeof candidate.description === 'string' &&
|
|
39
|
+
candidate.inputSchema !== undefined &&
|
|
40
|
+
typeof candidate.handler === 'function'
|
|
41
|
+
)
|
|
42
|
+
}
|
|
43
|
+
|
|
18
44
|
/**
|
|
19
45
|
* Built-in context.whoami tool that returns the current authentication context.
|
|
20
46
|
* This is useful for AI to understand its current tenant/org scope.
|
|
@@ -42,19 +68,131 @@ const contextWhoamiTool: McpToolDefinition = {
|
|
|
42
68
|
*
|
|
43
69
|
* @param moduleId - The module identifier (e.g., 'search', 'customers')
|
|
44
70
|
* @param tools - Array of tool definitions from the module
|
|
71
|
+
*
|
|
72
|
+
* IMPORTANT: We register the full typed tool (spread) — never reconstruct a
|
|
73
|
+
* minimal `{ name, description, inputSchema, requiredFeatures, handler }`
|
|
74
|
+
* payload. Stripping fields like `isMutation`, `displayName`, `isBulk`,
|
|
75
|
+
* `loadBeforeRecord(s)` makes the agent settings UI report mutation tools
|
|
76
|
+
* as read-only and prevents the chat dispatcher's mutation interceptor
|
|
77
|
+
* from firing.
|
|
45
78
|
*/
|
|
46
79
|
export function loadModuleTools(moduleId: string, tools: ModuleAiTool[]): void {
|
|
47
80
|
for (const tool of tools) {
|
|
48
|
-
registerMcpTool(
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
81
|
+
registerMcpTool(tool as McpToolDefinition, { moduleId })
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Register the generated `aiToolConfigEntries` shape emitted by the
|
|
87
|
+
* `ai-tools.generated.ts` generator extension. Entries with an empty
|
|
88
|
+
* `tools` array stay silent (the generator already filters those out).
|
|
89
|
+
* Invalid tool objects are skipped with a warning instead of throwing.
|
|
90
|
+
*
|
|
91
|
+
* Returns the number of tools actually registered so callers can log it.
|
|
92
|
+
*/
|
|
93
|
+
export function registerGeneratedAiToolEntries(entries: AiToolConfigEntry[]): number {
|
|
94
|
+
let registered = 0
|
|
95
|
+
for (const entry of entries) {
|
|
96
|
+
if (!entry || typeof entry.moduleId !== 'string') continue
|
|
97
|
+
if (!Array.isArray(entry.tools) || entry.tools.length === 0) continue
|
|
98
|
+
for (const candidate of entry.tools) {
|
|
99
|
+
if (!isModuleAiTool(candidate)) {
|
|
100
|
+
console.warn(
|
|
101
|
+
`[MCP Tools] Skipping malformed AI tool in module "${entry.moduleId}"`
|
|
102
|
+
)
|
|
103
|
+
continue
|
|
104
|
+
}
|
|
105
|
+
// Register the full typed tool — see comment on `loadModuleTools`.
|
|
106
|
+
registerMcpTool(candidate as McpToolDefinition, { moduleId: entry.moduleId })
|
|
107
|
+
registered += 1
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
return registered
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Apply the list of `aiToolOverrides` exports collected by the generator
|
|
115
|
+
* (one entry per module) to the live tool registry. Tools mapped to
|
|
116
|
+
* `null` are unregistered; tools mapped to a full definition replace the
|
|
117
|
+
* existing registration. Module load order controls precedence — last
|
|
118
|
+
* entry wins. The `modules.ts`-tier and programmatic overrides (set via
|
|
119
|
+
* {@link applyAiOverridesFromEnabledModules} and
|
|
120
|
+
* {@link applyAiToolOverrides}) supersede file-based entries.
|
|
121
|
+
*
|
|
122
|
+
* Safe to call when no override file is present (the entries array is
|
|
123
|
+
* empty); it is a no-op then.
|
|
124
|
+
*/
|
|
125
|
+
export function applyAiToolOverrideEntries(
|
|
126
|
+
entries: readonly AiToolOverrideConfigEntry[],
|
|
127
|
+
): void {
|
|
128
|
+
const overrideMap = composeToolOverrideMap(entries)
|
|
129
|
+
if (Object.keys(overrideMap).length === 0) return
|
|
130
|
+
const baseTools = toolRegistry.getTools() as Map<string, McpToolDefinition>
|
|
131
|
+
const overridden = applyToolOverrideMap<McpToolDefinition>(baseTools, overrideMap)
|
|
132
|
+
for (const [name, value] of Object.entries(overrideMap)) {
|
|
133
|
+
if (value === null) {
|
|
134
|
+
unregisterMcpTool(name)
|
|
135
|
+
console.info(`[AI Overrides] Tool "${name}" disabled by override.`)
|
|
136
|
+
continue
|
|
137
|
+
}
|
|
138
|
+
const next = overridden.get(name)
|
|
139
|
+
if (!next) continue
|
|
140
|
+
// Re-register through the public path so moduleMap stays consistent.
|
|
141
|
+
registerMcpTool(next as McpToolDefinition, { moduleId: 'ai_overrides' })
|
|
142
|
+
console.info(`[AI Overrides] Tool "${name}" replaced by override.`)
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Read `aiToolOverrideEntries` from `ai-tools.generated.ts` and apply
|
|
148
|
+
* them on top of the live tool registry. Safe to call when the file is
|
|
149
|
+
* missing (pre-generate builds, tests) — applies only the
|
|
150
|
+
* `modules.ts`-tier and programmatic overrides in that case.
|
|
151
|
+
*/
|
|
152
|
+
export async function loadGeneratedAiToolOverrides(): Promise<void> {
|
|
153
|
+
let entries: AiToolOverrideConfigEntry[] = []
|
|
154
|
+
try {
|
|
155
|
+
const mod = (await import(
|
|
156
|
+
'@/.mercato/generated/ai-tools.generated'
|
|
157
|
+
)) as { aiToolOverrideEntries?: unknown[] }
|
|
158
|
+
entries = Array.isArray(mod.aiToolOverrideEntries)
|
|
159
|
+
? (mod.aiToolOverrideEntries as AiToolOverrideConfigEntry[])
|
|
160
|
+
: []
|
|
161
|
+
} catch {
|
|
162
|
+
// No override file generated.
|
|
163
|
+
}
|
|
164
|
+
applyAiToolOverrideEntries(entries)
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Load the generated `ai-tools.generated.ts` file emitted by
|
|
169
|
+
* `yarn generate` and register every declared module tool through the
|
|
170
|
+
* existing `registerMcpTool` path. Safe to call when the generated file
|
|
171
|
+
* is missing (e.g., tests or pre-generate builds) — returns 0.
|
|
172
|
+
*/
|
|
173
|
+
export async function loadGeneratedModuleAiTools(): Promise<number> {
|
|
174
|
+
try {
|
|
175
|
+
const mod = (await import(
|
|
176
|
+
'@/.mercato/generated/ai-tools.generated'
|
|
177
|
+
)) as { aiToolConfigEntries?: AiToolConfigEntry[] }
|
|
178
|
+
const entries = Array.isArray(mod.aiToolConfigEntries)
|
|
179
|
+
? mod.aiToolConfigEntries
|
|
180
|
+
: []
|
|
181
|
+
const count = registerGeneratedAiToolEntries(entries)
|
|
182
|
+
// Apply module-to-module + programmatic tool overrides AFTER the base
|
|
183
|
+
// registrations so disable / replace semantics work end-to-end.
|
|
184
|
+
try {
|
|
185
|
+
await loadGeneratedAiToolOverrides()
|
|
186
|
+
} catch (error) {
|
|
187
|
+
console.error('[AI Overrides] Failed to apply tool overrides:', error)
|
|
188
|
+
}
|
|
189
|
+
return count
|
|
190
|
+
} catch (error) {
|
|
191
|
+
console.error(
|
|
192
|
+
'[MCP Tools] Could not load ai-tools.generated.ts (module tools unavailable):',
|
|
193
|
+
error
|
|
57
194
|
)
|
|
195
|
+
return 0
|
|
58
196
|
}
|
|
59
197
|
}
|
|
60
198
|
|
|
@@ -79,9 +217,17 @@ export async function loadAllModuleTools(): Promise<void> {
|
|
|
79
217
|
console.error('[MCP Tools] Could not load Code Mode tools:', error)
|
|
80
218
|
}
|
|
81
219
|
|
|
82
|
-
//
|
|
83
|
-
//
|
|
84
|
-
//
|
|
220
|
+
// 3. Register module-contributed tools from ai-tools.generated.ts.
|
|
221
|
+
// Code Mode stays untouched; module tools are additive. Missing
|
|
222
|
+
// generated file is not fatal (pre-generate builds, tests).
|
|
223
|
+
try {
|
|
224
|
+
const moduleToolCount = await loadGeneratedModuleAiTools()
|
|
225
|
+
console.error(
|
|
226
|
+
`[MCP Tools] Registered ${moduleToolCount} module-contributed AI tools from ai-tools.generated.ts`
|
|
227
|
+
)
|
|
228
|
+
} catch (error) {
|
|
229
|
+
console.error('[MCP Tools] Could not load module AI tools:', error)
|
|
230
|
+
}
|
|
85
231
|
}
|
|
86
232
|
|
|
87
233
|
/**
|