@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,333 @@
|
|
|
1
|
+
// Phase 1 of spec 2026-04-27-ai-tools-api-backed-dry-refactor.md.
|
|
2
|
+
//
|
|
3
|
+
// In-process API operation runner used by typed AI tools to reuse existing
|
|
4
|
+
// API route handlers without HTTP, fetch, or a second RBAC pass. The runner
|
|
5
|
+
// resolves the matched route entry from the generated `apiRoutes` manifest,
|
|
6
|
+
// validates that the route is documented (`openApi`) and that mutation routes
|
|
7
|
+
// declare `requiredFeatures`, asserts the route's required features are
|
|
8
|
+
// covered by the tool definition, then invokes the route handler directly with
|
|
9
|
+
// a synthetic Request that carries a Symbol-keyed trusted-auth envelope so the
|
|
10
|
+
// shared auth resolver short-circuits cookie/JWT/API-key parsing.
|
|
11
|
+
import {
|
|
12
|
+
attachTrustedAuthContext,
|
|
13
|
+
type AuthContext,
|
|
14
|
+
type TrustedAuthContextEnvelope,
|
|
15
|
+
} from '@open-mercato/shared/lib/auth/server'
|
|
16
|
+
import {
|
|
17
|
+
findApiRouteManifestMatch,
|
|
18
|
+
getApiRouteManifests,
|
|
19
|
+
type ApiRouteManifestEntry,
|
|
20
|
+
type RouteMatchParams,
|
|
21
|
+
} from '@open-mercato/shared/modules/registry'
|
|
22
|
+
import { hasAllFeatures } from '@open-mercato/shared/security/features'
|
|
23
|
+
import type { AiToolDefinition, McpToolContext } from './types'
|
|
24
|
+
|
|
25
|
+
export type AiApiHttpMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE'
|
|
26
|
+
|
|
27
|
+
const MUTATION_METHODS: ReadonlySet<AiApiHttpMethod> = new Set(['POST', 'PUT', 'PATCH', 'DELETE'])
|
|
28
|
+
|
|
29
|
+
const SYNTHETIC_ORIGIN = 'http://internal.local'
|
|
30
|
+
|
|
31
|
+
export type AiApiOperationRequest = {
|
|
32
|
+
method: AiApiHttpMethod
|
|
33
|
+
path: string
|
|
34
|
+
query?: Record<string, string | number | boolean | null | undefined>
|
|
35
|
+
body?: Record<string, unknown>
|
|
36
|
+
allowFeaturelessMutation?: boolean
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export type AiApiOperationResponse<T = unknown> = {
|
|
40
|
+
success: boolean
|
|
41
|
+
statusCode: number
|
|
42
|
+
data?: T
|
|
43
|
+
error?: string
|
|
44
|
+
details?: unknown
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export interface AiToolExecutionContext extends McpToolContext {
|
|
48
|
+
tool: AiToolDefinition
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export type AiApiOperationRunnerOptions = {
|
|
52
|
+
/** Optional override of the API route manifest (used by tests). */
|
|
53
|
+
apiRoutes?: ApiRouteManifestEntry[]
|
|
54
|
+
/** Custom loader for the API route manifest (used by tests). */
|
|
55
|
+
loadApiRoutes?: () => Promise<ApiRouteManifestEntry[]>
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export type AiApiOperationRunner = {
|
|
59
|
+
run<T = unknown>(request: AiApiOperationRequest): Promise<AiApiOperationResponse<T>>
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
type ResolvedHandler = (req: Request, ctx?: { params: RouteMatchParams }) => Promise<Response> | Response
|
|
63
|
+
|
|
64
|
+
type LoadedRouteModule = Record<string, unknown>
|
|
65
|
+
|
|
66
|
+
type MethodMetadata = {
|
|
67
|
+
requireAuth?: boolean
|
|
68
|
+
requireFeatures?: string[]
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function normalizeMethod(method: string): AiApiHttpMethod {
|
|
72
|
+
const upper = method.toUpperCase()
|
|
73
|
+
if (upper === 'GET' || upper === 'POST' || upper === 'PUT' || upper === 'PATCH' || upper === 'DELETE') {
|
|
74
|
+
return upper
|
|
75
|
+
}
|
|
76
|
+
throw new Error(`Unsupported method "${method}"`)
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// Exported for unit testing — see __tests__/ai-api-operation-runner.test.ts.
|
|
80
|
+
// `path` arrives from agent tool inputs (LLM-controlled), so trailing-slash
|
|
81
|
+
// stripping is implemented as a linear character-code scan rather than the
|
|
82
|
+
// /\/+$/ regex (CodeQL js/polynomial-redos: regex exhibits polynomial
|
|
83
|
+
// backtracking on long runs of `/`). The loop is O(n) regardless of input.
|
|
84
|
+
export function normalizePath(path: string): string {
|
|
85
|
+
if (typeof path !== 'string' || path.length === 0) return '/'
|
|
86
|
+
const trimmed = path.startsWith('/') ? path : `/${path}`
|
|
87
|
+
let end = trimmed.length
|
|
88
|
+
while (end > 1 && trimmed.charCodeAt(end - 1) === 47 /* '/' */) end--
|
|
89
|
+
return trimmed.slice(0, end)
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function buildUrl(path: string, query?: AiApiOperationRequest['query']): URL {
|
|
93
|
+
const url = new URL(`${SYNTHETIC_ORIGIN}/api${normalizePath(path)}`)
|
|
94
|
+
if (query) {
|
|
95
|
+
for (const [key, value] of Object.entries(query)) {
|
|
96
|
+
if (value === null || value === undefined) continue
|
|
97
|
+
url.searchParams.append(key, String(value))
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
return url
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function buildAuthEnvelope(ctx: AiToolExecutionContext): TrustedAuthContextEnvelope {
|
|
104
|
+
const userId = ctx.userId
|
|
105
|
+
if (!userId) {
|
|
106
|
+
return { auth: null, status: 'invalid' }
|
|
107
|
+
}
|
|
108
|
+
const auth: AuthContext = {
|
|
109
|
+
sub: userId,
|
|
110
|
+
userId,
|
|
111
|
+
tenantId: ctx.tenantId,
|
|
112
|
+
orgId: ctx.organizationId,
|
|
113
|
+
roles: [],
|
|
114
|
+
isSuperAdmin: ctx.isSuperAdmin,
|
|
115
|
+
}
|
|
116
|
+
return { auth, status: 'authenticated' }
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function pickHandler(entry: ApiRouteManifestEntry, mod: LoadedRouteModule, method: AiApiHttpMethod): ResolvedHandler | null {
|
|
120
|
+
const direct = mod[method]
|
|
121
|
+
if (typeof direct === 'function') return direct as ResolvedHandler
|
|
122
|
+
if (entry.kind === 'legacy') {
|
|
123
|
+
const fallback = mod.default ?? mod.handler
|
|
124
|
+
if (typeof fallback === 'function') return fallback as ResolvedHandler
|
|
125
|
+
}
|
|
126
|
+
return null
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
function extractMethodMetadata(metadata: unknown, method: AiApiHttpMethod): MethodMetadata | null {
|
|
130
|
+
if (!metadata || typeof metadata !== 'object') return null
|
|
131
|
+
const record = metadata as Record<string, unknown>
|
|
132
|
+
const perMethod = record[method]
|
|
133
|
+
const source: Record<string, unknown> = perMethod && typeof perMethod === 'object'
|
|
134
|
+
? (perMethod as Record<string, unknown>)
|
|
135
|
+
: record
|
|
136
|
+
const result: MethodMetadata = {}
|
|
137
|
+
if (typeof source.requireAuth === 'boolean') {
|
|
138
|
+
result.requireAuth = source.requireAuth
|
|
139
|
+
}
|
|
140
|
+
if (Array.isArray(source.requireFeatures)) {
|
|
141
|
+
const features = source.requireFeatures.filter((entry): entry is string => typeof entry === 'string' && entry.length > 0)
|
|
142
|
+
if (features.length > 0) result.requireFeatures = features
|
|
143
|
+
}
|
|
144
|
+
return result
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
async function readResponseBody(res: Response): Promise<unknown> {
|
|
148
|
+
const contentType = res.headers.get('content-type') ?? ''
|
|
149
|
+
if (contentType.includes('application/json')) {
|
|
150
|
+
try {
|
|
151
|
+
return await res.json()
|
|
152
|
+
} catch {
|
|
153
|
+
return null
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
try {
|
|
157
|
+
const text = await res.text()
|
|
158
|
+
if (!text) return null
|
|
159
|
+
try {
|
|
160
|
+
return JSON.parse(text)
|
|
161
|
+
} catch {
|
|
162
|
+
return text
|
|
163
|
+
}
|
|
164
|
+
} catch {
|
|
165
|
+
return null
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
function failure(statusCode: number, error: string, details?: unknown): AiApiOperationResponse {
|
|
170
|
+
const response: AiApiOperationResponse = { success: false, statusCode, error }
|
|
171
|
+
if (details !== undefined) response.details = details
|
|
172
|
+
return response
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
function normalizeError(body: unknown): { error: string; details?: unknown } {
|
|
176
|
+
if (body && typeof body === 'object' && !Array.isArray(body)) {
|
|
177
|
+
const record = body as Record<string, unknown>
|
|
178
|
+
const message = typeof record.error === 'string'
|
|
179
|
+
? record.error
|
|
180
|
+
: typeof record.message === 'string'
|
|
181
|
+
? record.message
|
|
182
|
+
: null
|
|
183
|
+
if (message) {
|
|
184
|
+
const { error: _ignored, message: _ignoredMessage, ...rest } = record
|
|
185
|
+
void _ignored
|
|
186
|
+
void _ignoredMessage
|
|
187
|
+
return Object.keys(rest).length > 0 ? { error: message, details: rest } : { error: message }
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
if (typeof body === 'string' && body.length > 0) {
|
|
191
|
+
return { error: body }
|
|
192
|
+
}
|
|
193
|
+
return { error: 'Request failed' }
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
export function createAiApiOperationRunner(
|
|
197
|
+
ctx: AiToolExecutionContext,
|
|
198
|
+
options: AiApiOperationRunnerOptions = {},
|
|
199
|
+
): AiApiOperationRunner {
|
|
200
|
+
let manifestPromise: Promise<ApiRouteManifestEntry[]> | null = null
|
|
201
|
+
|
|
202
|
+
const loadManifest = async (): Promise<ApiRouteManifestEntry[]> => {
|
|
203
|
+
if (options.apiRoutes) return options.apiRoutes
|
|
204
|
+
if (manifestPromise) return manifestPromise
|
|
205
|
+
const loader = options.loadApiRoutes ?? defaultLoadApiRoutes
|
|
206
|
+
manifestPromise = loader()
|
|
207
|
+
try {
|
|
208
|
+
return await manifestPromise
|
|
209
|
+
} catch (error) {
|
|
210
|
+
manifestPromise = null
|
|
211
|
+
throw error
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
return {
|
|
216
|
+
async run<T = unknown>(request: AiApiOperationRequest): Promise<AiApiOperationResponse<T>> {
|
|
217
|
+
let method: AiApiHttpMethod
|
|
218
|
+
try {
|
|
219
|
+
method = normalizeMethod(request.method)
|
|
220
|
+
} catch (error) {
|
|
221
|
+
const message = error instanceof Error ? error.message : 'Invalid method'
|
|
222
|
+
return failure(400, message) as AiApiOperationResponse<T>
|
|
223
|
+
}
|
|
224
|
+
const path = normalizePath(request.path)
|
|
225
|
+
|
|
226
|
+
let routes: ApiRouteManifestEntry[]
|
|
227
|
+
try {
|
|
228
|
+
routes = await loadManifest()
|
|
229
|
+
} catch (error) {
|
|
230
|
+
const message = error instanceof Error ? error.message : 'Failed to load API route manifest'
|
|
231
|
+
return failure(500, `Operation runner manifest unavailable: ${message}`) as AiApiOperationResponse<T>
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
const match = findApiRouteManifestMatch(routes, method, path)
|
|
235
|
+
if (!match) {
|
|
236
|
+
return failure(
|
|
237
|
+
404,
|
|
238
|
+
`No documented API route matches ${method} ${path}`,
|
|
239
|
+
) as AiApiOperationResponse<T>
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
let mod: LoadedRouteModule
|
|
243
|
+
try {
|
|
244
|
+
mod = (await match.route.load()) as LoadedRouteModule
|
|
245
|
+
} catch (error) {
|
|
246
|
+
const message = error instanceof Error ? error.message : 'Failed to load route module'
|
|
247
|
+
return failure(500, `Failed to load route module: ${message}`) as AiApiOperationResponse<T>
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
if (!('openApi' in mod) || mod.openApi === undefined || mod.openApi === null) {
|
|
251
|
+
return failure(
|
|
252
|
+
501,
|
|
253
|
+
`Route ${method} ${path} is undocumented (missing openApi export); refusing to call from AI tool`,
|
|
254
|
+
) as AiApiOperationResponse<T>
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
const handler = pickHandler(match.route, mod, method)
|
|
258
|
+
if (!handler) {
|
|
259
|
+
return failure(
|
|
260
|
+
405,
|
|
261
|
+
`Route ${path} does not export a handler for method ${method}`,
|
|
262
|
+
) as AiApiOperationResponse<T>
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
const methodMetadata = extractMethodMetadata(mod.metadata, method)
|
|
266
|
+
const routeFeatures = methodMetadata?.requireFeatures ?? []
|
|
267
|
+
const isMutation = MUTATION_METHODS.has(method)
|
|
268
|
+
|
|
269
|
+
if (isMutation && routeFeatures.length === 0 && !request.allowFeaturelessMutation) {
|
|
270
|
+
return failure(
|
|
271
|
+
403,
|
|
272
|
+
`Mutation route ${method} ${path} declares no requiredFeatures; refusing to call without allowFeaturelessMutation opt-in`,
|
|
273
|
+
) as AiApiOperationResponse<T>
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
const toolFeatures = ctx.tool.requiredFeatures ?? []
|
|
277
|
+
if (routeFeatures.length > 0 && !hasAllFeatures(toolFeatures, routeFeatures)) {
|
|
278
|
+
return failure(
|
|
279
|
+
403,
|
|
280
|
+
`AI tool "${ctx.tool.name}" requiredFeatures do not cover route ${method} ${path} requiredFeatures`,
|
|
281
|
+
{ toolFeatures, routeFeatures },
|
|
282
|
+
) as AiApiOperationResponse<T>
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
const url = buildUrl(path, request.query)
|
|
286
|
+
const headers = new Headers()
|
|
287
|
+
const requestInit: RequestInit = { method, headers }
|
|
288
|
+
if (request.body !== undefined && method !== 'GET') {
|
|
289
|
+
headers.set('content-type', 'application/json')
|
|
290
|
+
requestInit.body = JSON.stringify(request.body)
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
const syntheticRequest = new Request(url, requestInit)
|
|
294
|
+
attachTrustedAuthContext(syntheticRequest, buildAuthEnvelope(ctx))
|
|
295
|
+
|
|
296
|
+
let response: Response
|
|
297
|
+
try {
|
|
298
|
+
response = await handler(syntheticRequest, { params: match.params })
|
|
299
|
+
} catch (error) {
|
|
300
|
+
const message = error instanceof Error ? error.message : 'Route handler threw'
|
|
301
|
+
return failure(500, message) as AiApiOperationResponse<T>
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
const status = response.status
|
|
305
|
+
const body = await readResponseBody(response)
|
|
306
|
+
if (status >= 200 && status < 300) {
|
|
307
|
+
return {
|
|
308
|
+
success: true,
|
|
309
|
+
statusCode: status,
|
|
310
|
+
data: body as T,
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
const normalized = normalizeError(body)
|
|
314
|
+
return {
|
|
315
|
+
success: false,
|
|
316
|
+
statusCode: status,
|
|
317
|
+
error: normalized.error,
|
|
318
|
+
...(normalized.details !== undefined ? { details: normalized.details } : {}),
|
|
319
|
+
}
|
|
320
|
+
},
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
async function defaultLoadApiRoutes(): Promise<ApiRouteManifestEntry[]> {
|
|
325
|
+
const registered = getApiRouteManifests()
|
|
326
|
+
if (registered.length === 0) {
|
|
327
|
+
throw new Error(
|
|
328
|
+
'No API route manifest registered. Call registerApiRouteManifests(...) at app bootstrap or pass apiRoutes/loadApiRoutes to createAiApiOperationRunner.',
|
|
329
|
+
)
|
|
330
|
+
}
|
|
331
|
+
return registered
|
|
332
|
+
}
|
|
333
|
+
|