@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,246 @@
|
|
|
1
|
+
import { findWithDecryption } from "@open-mercato/shared/lib/encryption/find";
|
|
2
|
+
import { Attachment } from "@open-mercato/core/modules/attachments/data/entities";
|
|
3
|
+
import { resolveEffectiveMutationPolicy } from "./agent-policy.js";
|
|
4
|
+
import { hasRequiredFeatures } from "./auth.js";
|
|
5
|
+
function checkStatusAndExpiry(action, options = {}) {
|
|
6
|
+
const now = options.now ?? /* @__PURE__ */ new Date();
|
|
7
|
+
if (action.status !== "pending") {
|
|
8
|
+
return {
|
|
9
|
+
ok: false,
|
|
10
|
+
status: 409,
|
|
11
|
+
code: "invalid_status",
|
|
12
|
+
message: `Pending action is in status "${action.status}"; expected "pending".`
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
const expiresAt = action.expiresAt instanceof Date ? action.expiresAt : new Date(action.expiresAt);
|
|
16
|
+
if (expiresAt.getTime() <= now.getTime()) {
|
|
17
|
+
return {
|
|
18
|
+
ok: false,
|
|
19
|
+
status: 409,
|
|
20
|
+
code: "expired",
|
|
21
|
+
message: "Pending action has expired. The model must re-propose the mutation."
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
return { ok: true };
|
|
25
|
+
}
|
|
26
|
+
function checkAgentAndFeatures(agent, ctx) {
|
|
27
|
+
if (!agent) {
|
|
28
|
+
return {
|
|
29
|
+
ok: false,
|
|
30
|
+
status: 404,
|
|
31
|
+
code: "agent_unknown",
|
|
32
|
+
message: "Agent is no longer registered."
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
const required = agent.requiredFeatures ?? [];
|
|
36
|
+
if (!hasRequiredFeatures(required, ctx.userFeatures, ctx.isSuperAdmin)) {
|
|
37
|
+
return {
|
|
38
|
+
ok: false,
|
|
39
|
+
status: 403,
|
|
40
|
+
code: "agent_features_denied",
|
|
41
|
+
message: `Caller lacks one of the agent's required features: ${required.join(", ")}`
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
return { ok: true };
|
|
45
|
+
}
|
|
46
|
+
function checkToolWhitelist(agent, tool, action, options = {}) {
|
|
47
|
+
if (!tool) {
|
|
48
|
+
return {
|
|
49
|
+
ok: false,
|
|
50
|
+
status: 403,
|
|
51
|
+
code: "tool_not_whitelisted",
|
|
52
|
+
message: `Tool "${action.toolName}" is not registered.`
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
if (!agent.allowedTools.includes(tool.name) || tool.isMutation !== true) {
|
|
56
|
+
return {
|
|
57
|
+
ok: false,
|
|
58
|
+
status: 403,
|
|
59
|
+
code: "tool_not_whitelisted",
|
|
60
|
+
message: `Tool "${tool.name}" is no longer whitelisted as a mutation tool for agent "${agent.id}".`
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
const effective = resolveEffectiveMutationPolicy(
|
|
64
|
+
agent.mutationPolicy,
|
|
65
|
+
options.mutationPolicyOverride ?? null,
|
|
66
|
+
agent.id
|
|
67
|
+
);
|
|
68
|
+
if (effective === "read-only") {
|
|
69
|
+
return {
|
|
70
|
+
ok: false,
|
|
71
|
+
status: 403,
|
|
72
|
+
code: "read_only_agent",
|
|
73
|
+
message: `Agent "${agent.id}" effective mutationPolicy=read-only; mutation tool "${tool.name}" cannot be executed.`
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
return { ok: true };
|
|
77
|
+
}
|
|
78
|
+
async function checkAttachmentScope(action, ctx) {
|
|
79
|
+
const ids = Array.isArray(action.attachmentIds) ? action.attachmentIds : [];
|
|
80
|
+
if (ids.length === 0) return { ok: true };
|
|
81
|
+
const em = ctx.em;
|
|
82
|
+
if (!em) {
|
|
83
|
+
return {
|
|
84
|
+
ok: false,
|
|
85
|
+
status: 500,
|
|
86
|
+
code: "attachment_cross_tenant",
|
|
87
|
+
message: "Attachment scope check requires an EntityManager."
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
const rows = await findWithDecryption(
|
|
91
|
+
em,
|
|
92
|
+
Attachment,
|
|
93
|
+
{ id: { $in: ids } },
|
|
94
|
+
{},
|
|
95
|
+
{ tenantId: ctx.tenantId, organizationId: ctx.organizationId }
|
|
96
|
+
);
|
|
97
|
+
if (rows.length !== ids.length) {
|
|
98
|
+
return {
|
|
99
|
+
ok: false,
|
|
100
|
+
status: 403,
|
|
101
|
+
code: "attachment_cross_tenant",
|
|
102
|
+
message: "One or more attachments are not accessible to the caller."
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
for (const row of rows) {
|
|
106
|
+
if ((row.tenantId ?? null) !== ctx.tenantId) {
|
|
107
|
+
return {
|
|
108
|
+
ok: false,
|
|
109
|
+
status: 403,
|
|
110
|
+
code: "attachment_cross_tenant",
|
|
111
|
+
message: "One or more attachments belong to a different tenant."
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
if (ctx.organizationId !== null && row.organizationId && row.organizationId !== ctx.organizationId) {
|
|
115
|
+
return {
|
|
116
|
+
ok: false,
|
|
117
|
+
status: 403,
|
|
118
|
+
code: "attachment_cross_tenant",
|
|
119
|
+
message: "One or more attachments belong to a different organization."
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
return { ok: true };
|
|
124
|
+
}
|
|
125
|
+
async function checkRecordVersion(action, tool, ctx) {
|
|
126
|
+
const parseResult = tool.inputSchema.safeParse(action.normalizedInput ?? {});
|
|
127
|
+
if (!parseResult.success) {
|
|
128
|
+
return {
|
|
129
|
+
ok: false,
|
|
130
|
+
status: 412,
|
|
131
|
+
code: "schema_drift",
|
|
132
|
+
message: "Pending input no longer satisfies the tool schema.",
|
|
133
|
+
extra: { issues: parseResult.error.issues }
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
const handlerContext = toHandlerContext(ctx);
|
|
137
|
+
const records = Array.isArray(action.records) ? action.records : null;
|
|
138
|
+
if (records && records.length > 0) {
|
|
139
|
+
if (!tool.loadBeforeRecords) {
|
|
140
|
+
return { ok: true };
|
|
141
|
+
}
|
|
142
|
+
const currentRows = await tool.loadBeforeRecords(parseResult.data, handlerContext);
|
|
143
|
+
const currentVersionById = /* @__PURE__ */ new Map();
|
|
144
|
+
for (const row of currentRows ?? []) {
|
|
145
|
+
currentVersionById.set(row.recordId, row.recordVersion ?? null);
|
|
146
|
+
}
|
|
147
|
+
const stale = [];
|
|
148
|
+
for (const record of records) {
|
|
149
|
+
const current2 = currentVersionById.get(record.recordId);
|
|
150
|
+
const captured2 = record.recordVersion ?? null;
|
|
151
|
+
if (current2 === void 0) {
|
|
152
|
+
stale.push({
|
|
153
|
+
recordId: record.recordId,
|
|
154
|
+
error: { code: "stale_version", message: "Record no longer exists." }
|
|
155
|
+
});
|
|
156
|
+
continue;
|
|
157
|
+
}
|
|
158
|
+
if (captured2 !== null && current2 !== captured2) {
|
|
159
|
+
stale.push({
|
|
160
|
+
recordId: record.recordId,
|
|
161
|
+
error: { code: "stale_version", message: "Record version changed since preview." }
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
if (stale.length === records.length) {
|
|
166
|
+
return {
|
|
167
|
+
ok: false,
|
|
168
|
+
status: 412,
|
|
169
|
+
code: "stale_version",
|
|
170
|
+
message: "All pending records are stale; the model must re-propose the batch.",
|
|
171
|
+
extra: { staleRecords: stale.map((entry) => entry.recordId) }
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
if (stale.length > 0) {
|
|
175
|
+
return { ok: true, failedRecords: stale };
|
|
176
|
+
}
|
|
177
|
+
return { ok: true };
|
|
178
|
+
}
|
|
179
|
+
if (!tool.loadBeforeRecord) {
|
|
180
|
+
return { ok: true };
|
|
181
|
+
}
|
|
182
|
+
const before = await tool.loadBeforeRecord(parseResult.data, handlerContext);
|
|
183
|
+
if (!before) return { ok: true };
|
|
184
|
+
const captured = action.recordVersion ?? null;
|
|
185
|
+
const current = before.recordVersion ?? null;
|
|
186
|
+
if (captured !== null && current !== captured) {
|
|
187
|
+
return {
|
|
188
|
+
ok: false,
|
|
189
|
+
status: 412,
|
|
190
|
+
code: "stale_version",
|
|
191
|
+
message: "Record version changed since preview; re-propose the mutation.",
|
|
192
|
+
extra: { recordId: before.recordId }
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
return { ok: true };
|
|
196
|
+
}
|
|
197
|
+
function toHandlerContext(ctx) {
|
|
198
|
+
return {
|
|
199
|
+
tenantId: ctx.tenantId,
|
|
200
|
+
organizationId: ctx.organizationId,
|
|
201
|
+
userId: ctx.userId,
|
|
202
|
+
container: ctx.container,
|
|
203
|
+
userFeatures: ctx.userFeatures,
|
|
204
|
+
isSuperAdmin: ctx.isSuperAdmin
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
async function runPendingActionRechecks(input) {
|
|
208
|
+
const { action, agent, tool, ctx, now, mutationPolicyOverride } = input;
|
|
209
|
+
const statusCheck = checkStatusAndExpiry(action, { now });
|
|
210
|
+
if (!statusCheck.ok) return statusCheck;
|
|
211
|
+
const agentCheck = checkAgentAndFeatures(agent, ctx);
|
|
212
|
+
if (!agentCheck.ok) return agentCheck;
|
|
213
|
+
const whitelistCheck = checkToolWhitelist(agent, tool, action, {
|
|
214
|
+
mutationPolicyOverride: mutationPolicyOverride ?? null
|
|
215
|
+
});
|
|
216
|
+
if (!whitelistCheck.ok) return whitelistCheck;
|
|
217
|
+
const attachmentCheck = await checkAttachmentScope(action, ctx);
|
|
218
|
+
if (!attachmentCheck.ok) return attachmentCheck;
|
|
219
|
+
const versionCheck = await checkRecordVersion(action, tool, ctx);
|
|
220
|
+
return versionCheck;
|
|
221
|
+
}
|
|
222
|
+
const PENDING_ACTION_RECHECK_CODES = [
|
|
223
|
+
"invalid_status",
|
|
224
|
+
"expired",
|
|
225
|
+
"agent_unknown",
|
|
226
|
+
"agent_features_denied",
|
|
227
|
+
"tool_not_whitelisted",
|
|
228
|
+
"read_only_agent",
|
|
229
|
+
"attachment_cross_tenant",
|
|
230
|
+
"stale_version",
|
|
231
|
+
"schema_drift"
|
|
232
|
+
];
|
|
233
|
+
function isPendingActionRecheckCode(value) {
|
|
234
|
+
return typeof value === "string" && PENDING_ACTION_RECHECK_CODES.includes(value);
|
|
235
|
+
}
|
|
236
|
+
export {
|
|
237
|
+
PENDING_ACTION_RECHECK_CODES,
|
|
238
|
+
checkAgentAndFeatures,
|
|
239
|
+
checkAttachmentScope,
|
|
240
|
+
checkRecordVersion,
|
|
241
|
+
checkStatusAndExpiry,
|
|
242
|
+
checkToolWhitelist,
|
|
243
|
+
isPendingActionRecheckCode,
|
|
244
|
+
runPendingActionRechecks
|
|
245
|
+
};
|
|
246
|
+
//# sourceMappingURL=pending-action-recheck.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../../src/modules/ai_assistant/lib/pending-action-recheck.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * Pending-action re-check contract (spec \u00A79.4 / Step 5.8).\n *\n * The operator presses \"Confirm\" on a `mutation-preview-card`. The server\n * MUST re-verify every invariant before executing the wrapped tool \u2014 the\n * pending row was created at propose-time and agents / policies / record\n * versions can all drift between then and confirm. This module is the\n * single source of truth for that contract.\n *\n * The individual `check*` helpers are exported independently so the Step\n * 5.8 unit suite can exercise each guard in isolation. The orchestrator\n * {@link runPendingActionRechecks} stops at the first failure and returns\n * a structured denial the route turns into a JSON error envelope.\n *\n * This module is pure aside from DB reads; the caller owns the transaction\n * boundary. The Step 5.9 cancel route reuses {@link checkStatusAndExpiry}.\n */\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport { findWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport { Attachment } from '@open-mercato/core/modules/attachments/data/entities'\nimport type { AiAgentDefinition } from './ai-agent-definition'\nimport type { AiToolDefinition, McpToolContext } from './types'\nimport type { AiPendingAction } from '../data/entities'\nimport type {\n AiPendingActionFailedRecord,\n AiPendingActionRecordDiff,\n AiPendingActionStatus,\n} from './pending-action-types'\nimport { resolveEffectiveMutationPolicy } from './agent-policy'\nimport { hasRequiredFeatures } from './auth'\nimport type { AiAgentMutationPolicy } from './ai-agent-definition'\n\nexport type PendingActionRecheckCode =\n | 'invalid_status'\n | 'expired'\n | 'agent_unknown'\n | 'agent_features_denied'\n | 'tool_not_whitelisted'\n | 'read_only_agent'\n | 'attachment_cross_tenant'\n | 'stale_version'\n | 'schema_drift'\n\nexport interface PendingActionRecheckOkResult {\n ok: true\n /**\n * Per-record stale list for batch mutations when only some records were\n * stale. Present only when the batch path produced a partial-stale set;\n * the caller should persist these via `repo.setStatus(..., { failedRecords })`\n * and proceed with the remaining records.\n */\n failedRecords?: AiPendingActionFailedRecord[]\n}\n\nexport interface PendingActionRecheckFailResult {\n ok: false\n status: number\n code: PendingActionRecheckCode\n message: string\n extra?: Record<string, unknown>\n}\n\nexport type PendingActionRecheckResult =\n | PendingActionRecheckOkResult\n | PendingActionRecheckFailResult\n\nexport interface PendingActionAuthContext {\n tenantId: string\n organizationId: string | null\n userId: string\n userFeatures: string[]\n isSuperAdmin: boolean\n /**\n * Optional DI container used by `checkRecordVersion` to hand the tool's\n * `loadBeforeRecord` resolver an `McpToolContext`.\n */\n container?: import('awilix').AwilixContainer\n em?: EntityManager\n}\n\nexport interface PendingActionRecheckInput {\n action: AiPendingAction\n agent: AiAgentDefinition | null | undefined\n tool: AiToolDefinition | null | undefined\n ctx: PendingActionAuthContext\n /** Explicit clock for deterministic tests. */\n now?: Date\n /**\n * Optional tenant-scoped mutation-policy downgrade resolved by the caller\n * (Step 5.4). Missing / null \u2192 agent's code-declared policy stands alone.\n */\n mutationPolicyOverride?: AiAgentMutationPolicy | null\n}\n\n/**\n * Guards 3 + 2: pending action is still in `pending` and has not expired.\n * Returns a structured 409 on mismatch. Reused by the cancel route (which\n * only runs 1-3 of the re-check list).\n */\nexport function checkStatusAndExpiry(\n action: AiPendingAction,\n options: { now?: Date } = {},\n): PendingActionRecheckResult {\n const now = options.now ?? new Date()\n if (action.status !== 'pending') {\n return {\n ok: false,\n status: 409,\n code: 'invalid_status',\n message: `Pending action is in status \"${action.status}\"; expected \"pending\".`,\n }\n }\n const expiresAt =\n action.expiresAt instanceof Date ? action.expiresAt : new Date(action.expiresAt)\n if (expiresAt.getTime() <= now.getTime()) {\n return {\n ok: false,\n status: 409,\n code: 'expired',\n message: 'Pending action has expired. The model must re-propose the mutation.',\n }\n }\n return { ok: true }\n}\n\n/**\n * Guard 4: the agent is still registered AND the caller still carries the\n * agent's `requiredFeatures`. Missing agent \u2192 404. Missing features \u2192 403.\n */\nexport function checkAgentAndFeatures(\n agent: AiAgentDefinition | null | undefined,\n ctx: PendingActionAuthContext,\n): PendingActionRecheckResult {\n if (!agent) {\n return {\n ok: false,\n status: 404,\n code: 'agent_unknown',\n message: 'Agent is no longer registered.',\n }\n }\n const required = agent.requiredFeatures ?? []\n if (!hasRequiredFeatures(required, ctx.userFeatures, ctx.isSuperAdmin)) {\n return {\n ok: false,\n status: 403,\n code: 'agent_features_denied',\n message: `Caller lacks one of the agent's required features: ${required.join(', ')}`,\n }\n }\n return { ok: true }\n}\n\n/**\n * Guards 5 + 6: effective mutation policy still allows mutation, AND the\n * tool is still whitelisted + still declared `isMutation: true`. Read-only\n * policy or whitelist drop \u2192 403.\n */\nexport function checkToolWhitelist(\n agent: AiAgentDefinition,\n tool: AiToolDefinition | null | undefined,\n action: AiPendingAction,\n options: { mutationPolicyOverride?: AiAgentMutationPolicy | null } = {},\n): PendingActionRecheckResult {\n if (!tool) {\n return {\n ok: false,\n status: 403,\n code: 'tool_not_whitelisted',\n message: `Tool \"${action.toolName}\" is not registered.`,\n }\n }\n if (!agent.allowedTools.includes(tool.name) || tool.isMutation !== true) {\n return {\n ok: false,\n status: 403,\n code: 'tool_not_whitelisted',\n message: `Tool \"${tool.name}\" is no longer whitelisted as a mutation tool for agent \"${agent.id}\".`,\n }\n }\n const effective = resolveEffectiveMutationPolicy(\n agent.mutationPolicy,\n options.mutationPolicyOverride ?? null,\n agent.id,\n )\n if (effective === 'read-only') {\n return {\n ok: false,\n status: 403,\n code: 'read_only_agent',\n message: `Agent \"${agent.id}\" effective mutationPolicy=read-only; mutation tool \"${tool.name}\" cannot be executed.`,\n }\n }\n return { ok: true }\n}\n\n/**\n * Guard 7: every attachment id referenced by the pending row belongs to the\n * caller's tenant/org. Any cross-tenant id short-circuits with 403 \u2014 we do\n * not leak which specific id was rejected.\n */\nexport async function checkAttachmentScope(\n action: AiPendingAction,\n ctx: PendingActionAuthContext,\n): Promise<PendingActionRecheckResult> {\n const ids = Array.isArray(action.attachmentIds) ? action.attachmentIds : []\n if (ids.length === 0) return { ok: true }\n const em = ctx.em\n if (!em) {\n return {\n ok: false,\n status: 500,\n code: 'attachment_cross_tenant',\n message: 'Attachment scope check requires an EntityManager.',\n }\n }\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const rows = (await findWithDecryption<any>(\n em,\n Attachment as any,\n { id: { $in: ids } } as any,\n {},\n { tenantId: ctx.tenantId, organizationId: ctx.organizationId },\n )) as Array<{ id: string; tenantId?: string | null; organizationId?: string | null }>\n if (rows.length !== ids.length) {\n return {\n ok: false,\n status: 403,\n code: 'attachment_cross_tenant',\n message: 'One or more attachments are not accessible to the caller.',\n }\n }\n for (const row of rows) {\n if ((row.tenantId ?? null) !== ctx.tenantId) {\n return {\n ok: false,\n status: 403,\n code: 'attachment_cross_tenant',\n message: 'One or more attachments belong to a different tenant.',\n }\n }\n if (ctx.organizationId !== null && row.organizationId && row.organizationId !== ctx.organizationId) {\n return {\n ok: false,\n status: 403,\n code: 'attachment_cross_tenant',\n message: 'One or more attachments belong to a different organization.',\n }\n }\n }\n return { ok: true }\n}\n\n/**\n * Guard 8: record version matches the row's captured `recordVersion`. For\n * single-record actions a mismatch is a hard 412. For batch actions we\n * compute per-record and return a `failedRecords[]` entry for each stale\n * record; the caller proceeds with the remaining records. If the batch\n * has NO remaining records (every one is stale) the function returns 412.\n *\n * Also acts as the schema-drift guard: re-parses `action.normalizedInput`\n * through the tool's current zod schema. A shape change between propose\n * and confirm surfaces as 412 `schema_drift` so the model re-proposes.\n */\nexport async function checkRecordVersion(\n action: AiPendingAction,\n tool: AiToolDefinition,\n ctx: PendingActionAuthContext,\n): Promise<PendingActionRecheckResult> {\n const parseResult = tool.inputSchema.safeParse(action.normalizedInput ?? {})\n if (!parseResult.success) {\n return {\n ok: false,\n status: 412,\n code: 'schema_drift',\n message: 'Pending input no longer satisfies the tool schema.',\n extra: { issues: parseResult.error.issues },\n }\n }\n\n const handlerContext = toHandlerContext(ctx)\n\n const records = Array.isArray(action.records) ? action.records : null\n if (records && records.length > 0) {\n if (!tool.loadBeforeRecords) {\n return { ok: true }\n }\n const currentRows = await tool.loadBeforeRecords(parseResult.data as never, handlerContext)\n const currentVersionById = new Map<string, string | null>()\n for (const row of currentRows ?? []) {\n currentVersionById.set(row.recordId, row.recordVersion ?? null)\n }\n const stale: AiPendingActionFailedRecord[] = []\n for (const record of records as AiPendingActionRecordDiff[]) {\n const current = currentVersionById.get(record.recordId)\n const captured = record.recordVersion ?? null\n if (current === undefined) {\n stale.push({\n recordId: record.recordId,\n error: { code: 'stale_version', message: 'Record no longer exists.' },\n })\n continue\n }\n if (captured !== null && current !== captured) {\n stale.push({\n recordId: record.recordId,\n error: { code: 'stale_version', message: 'Record version changed since preview.' },\n })\n }\n }\n if (stale.length === records.length) {\n return {\n ok: false,\n status: 412,\n code: 'stale_version',\n message: 'All pending records are stale; the model must re-propose the batch.',\n extra: { staleRecords: stale.map((entry) => entry.recordId) },\n }\n }\n if (stale.length > 0) {\n return { ok: true, failedRecords: stale }\n }\n return { ok: true }\n }\n\n if (!tool.loadBeforeRecord) {\n return { ok: true }\n }\n const before = await tool.loadBeforeRecord(parseResult.data as never, handlerContext)\n if (!before) return { ok: true }\n const captured = action.recordVersion ?? null\n const current = before.recordVersion ?? null\n if (captured !== null && current !== captured) {\n return {\n ok: false,\n status: 412,\n code: 'stale_version',\n message: 'Record version changed since preview; re-propose the mutation.',\n extra: { recordId: before.recordId },\n }\n }\n return { ok: true }\n}\n\nfunction toHandlerContext(ctx: PendingActionAuthContext): McpToolContext {\n return {\n tenantId: ctx.tenantId,\n organizationId: ctx.organizationId,\n userId: ctx.userId,\n container: ctx.container as never,\n userFeatures: ctx.userFeatures,\n isSuperAdmin: ctx.isSuperAdmin,\n }\n}\n\n/**\n * Orchestrator: runs every guard in spec \u00A79.4 order and returns the first\n * failure. Callers receive either `{ ok: true, failedRecords? }` \u2014 where a\n * non-empty `failedRecords` indicates a partial-stale batch that should\n * proceed with the non-stale subset \u2014 or `{ ok: false, status, code, ... }`\n * ready to serialize as an HTTP error envelope.\n */\nexport async function runPendingActionRechecks(\n input: PendingActionRecheckInput,\n): Promise<PendingActionRecheckResult> {\n const { action, agent, tool, ctx, now, mutationPolicyOverride } = input\n\n const statusCheck = checkStatusAndExpiry(action, { now })\n if (!statusCheck.ok) return statusCheck\n\n const agentCheck = checkAgentAndFeatures(agent, ctx)\n if (!agentCheck.ok) return agentCheck\n\n const whitelistCheck = checkToolWhitelist(agent!, tool, action, {\n mutationPolicyOverride: mutationPolicyOverride ?? null,\n })\n if (!whitelistCheck.ok) return whitelistCheck\n\n const attachmentCheck = await checkAttachmentScope(action, ctx)\n if (!attachmentCheck.ok) return attachmentCheck\n\n const versionCheck = await checkRecordVersion(action, tool!, ctx)\n return versionCheck\n}\n\n/**\n * Test-only helper shadowed here to let the route unit tests assert the\n * exhaustive set of codes without importing each guard individually.\n */\nexport const PENDING_ACTION_RECHECK_CODES: ReadonlyArray<PendingActionRecheckCode> = [\n 'invalid_status',\n 'expired',\n 'agent_unknown',\n 'agent_features_denied',\n 'tool_not_whitelisted',\n 'read_only_agent',\n 'attachment_cross_tenant',\n 'stale_version',\n 'schema_drift',\n]\n\nexport function isPendingActionRecheckCode(\n value: unknown,\n): value is PendingActionRecheckCode {\n return typeof value === 'string' && (PENDING_ACTION_RECHECK_CODES as readonly string[]).includes(value)\n}\n\nexport type { AiPendingActionFailedRecord } from './pending-action-types'\n\nexport type PendingActionRecheckStatus = AiPendingActionStatus\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": ["current", "captured"]
|
|
7
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
const AI_PENDING_ACTION_STATUSES = [
|
|
2
|
+
"pending",
|
|
3
|
+
"confirmed",
|
|
4
|
+
"cancelled",
|
|
5
|
+
"expired",
|
|
6
|
+
"executing",
|
|
7
|
+
"failed"
|
|
8
|
+
];
|
|
9
|
+
const AI_PENDING_ACTION_QUEUE_MODES = ["inline", "stack"];
|
|
10
|
+
const AI_PENDING_ACTION_ALLOWED_TRANSITIONS = {
|
|
11
|
+
pending: ["confirmed", "cancelled", "expired"],
|
|
12
|
+
confirmed: ["executing"],
|
|
13
|
+
executing: ["confirmed", "failed"],
|
|
14
|
+
cancelled: [],
|
|
15
|
+
expired: [],
|
|
16
|
+
failed: []
|
|
17
|
+
};
|
|
18
|
+
const AI_PENDING_ACTION_TERMINAL_STATUSES = [
|
|
19
|
+
"confirmed",
|
|
20
|
+
"cancelled",
|
|
21
|
+
"expired",
|
|
22
|
+
"failed"
|
|
23
|
+
];
|
|
24
|
+
class AiPendingActionStateError extends Error {
|
|
25
|
+
constructor(from, to) {
|
|
26
|
+
super(`Illegal AiPendingAction status transition: ${from} \u2192 ${to}`);
|
|
27
|
+
this.from = from;
|
|
28
|
+
this.to = to;
|
|
29
|
+
this.code = "ai_pending_action_invalid_transition";
|
|
30
|
+
this.name = "AiPendingActionStateError";
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
function isAiPendingActionStatus(value) {
|
|
34
|
+
return typeof value === "string" && AI_PENDING_ACTION_STATUSES.includes(value);
|
|
35
|
+
}
|
|
36
|
+
function isAiPendingActionQueueMode(value) {
|
|
37
|
+
return typeof value === "string" && AI_PENDING_ACTION_QUEUE_MODES.includes(value);
|
|
38
|
+
}
|
|
39
|
+
function isTerminalAiPendingActionStatus(status) {
|
|
40
|
+
return AI_PENDING_ACTION_TERMINAL_STATUSES.includes(status);
|
|
41
|
+
}
|
|
42
|
+
function isAllowedAiPendingActionTransition(from, to) {
|
|
43
|
+
return (AI_PENDING_ACTION_ALLOWED_TRANSITIONS[from] ?? []).includes(to);
|
|
44
|
+
}
|
|
45
|
+
const AI_PENDING_ACTION_DEFAULT_TTL_SECONDS = 900;
|
|
46
|
+
const AI_PENDING_ACTION_TTL_ENV_VAR = "AI_PENDING_ACTION_TTL_SECONDS";
|
|
47
|
+
function resolveAiPendingActionTtlSeconds(env = process.env) {
|
|
48
|
+
const raw = env[AI_PENDING_ACTION_TTL_ENV_VAR];
|
|
49
|
+
if (raw == null) return AI_PENDING_ACTION_DEFAULT_TTL_SECONDS;
|
|
50
|
+
const parsed = Number.parseInt(String(raw), 10);
|
|
51
|
+
if (!Number.isFinite(parsed) || parsed <= 0) {
|
|
52
|
+
return AI_PENDING_ACTION_DEFAULT_TTL_SECONDS;
|
|
53
|
+
}
|
|
54
|
+
return parsed;
|
|
55
|
+
}
|
|
56
|
+
export {
|
|
57
|
+
AI_PENDING_ACTION_ALLOWED_TRANSITIONS,
|
|
58
|
+
AI_PENDING_ACTION_DEFAULT_TTL_SECONDS,
|
|
59
|
+
AI_PENDING_ACTION_QUEUE_MODES,
|
|
60
|
+
AI_PENDING_ACTION_STATUSES,
|
|
61
|
+
AI_PENDING_ACTION_TERMINAL_STATUSES,
|
|
62
|
+
AI_PENDING_ACTION_TTL_ENV_VAR,
|
|
63
|
+
AiPendingActionStateError,
|
|
64
|
+
isAiPendingActionQueueMode,
|
|
65
|
+
isAiPendingActionStatus,
|
|
66
|
+
isAllowedAiPendingActionTransition,
|
|
67
|
+
isTerminalAiPendingActionStatus,
|
|
68
|
+
resolveAiPendingActionTtlSeconds
|
|
69
|
+
};
|
|
70
|
+
//# sourceMappingURL=pending-action-types.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../../src/modules/ai_assistant/lib/pending-action-types.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * Shared enums + error type for the Phase 3 WS-C mutation approval gate\n * (spec \u00A78 `AiPendingAction` + \u00A79 server contract).\n *\n * These values are referenced by the entity, the repository, the\n * `/api/ai/actions/*` routes (Steps 5.7 / 5.8 / 5.9), and the cleanup\n * worker (Step 5.12). Colocated here so every consumer shares the same\n * source of truth.\n */\n\nexport const AI_PENDING_ACTION_STATUSES = [\n 'pending',\n 'confirmed',\n 'cancelled',\n 'expired',\n 'executing',\n 'failed',\n] as const\n\nexport type AiPendingActionStatus = (typeof AI_PENDING_ACTION_STATUSES)[number]\n\nexport const AI_PENDING_ACTION_QUEUE_MODES = ['inline', 'stack'] as const\n\nexport type AiPendingActionQueueMode = (typeof AI_PENDING_ACTION_QUEUE_MODES)[number]\n\n/**\n * Allowed state-machine edges for `AiPendingAction.status`:\n *\n * ```\n * pending \u2500\u2500\u252C\u2500\u25B6 confirmed \u2500\u2500\u25B6 executing \u2500\u2500\u25B6 failed\n * \u2502 \u2514\u2500\u2500\u25B6 (terminal success keeps status = 'confirmed'\n * \u2502 and stores executionResult.recordId)\n * \u251C\u2500\u25B6 cancelled\n * \u2514\u2500\u25B6 expired\n * ```\n *\n * Every other transition is rejected with `AiPendingActionStateError`.\n */\nexport const AI_PENDING_ACTION_ALLOWED_TRANSITIONS: Record<\n AiPendingActionStatus,\n ReadonlyArray<AiPendingActionStatus>\n> = {\n pending: ['confirmed', 'cancelled', 'expired'],\n confirmed: ['executing'],\n executing: ['confirmed', 'failed'],\n cancelled: [],\n expired: [],\n failed: [],\n}\n\nexport const AI_PENDING_ACTION_TERMINAL_STATUSES: ReadonlyArray<AiPendingActionStatus> = [\n 'confirmed',\n 'cancelled',\n 'expired',\n 'failed',\n]\n\n/**\n * Per-record batch diff entry, mirrored in `AiPendingAction.records`.\n *\n * When present, the batch diff is authoritative and `fieldDiff` at the\n * top level is ignored by every consumer (spec \u00A78 rule 2).\n */\nexport type AiPendingActionRecordDiff = {\n recordId: string\n entityType: string\n label: string\n fieldDiff: Array<{ field: string; before: unknown; after: unknown }>\n recordVersion: string | null\n attachmentIds?: string[]\n}\n\n/**\n * Per-record failure shape populated by the confirm handler (Step 5.8)\n * when partial success occurs inside a batch.\n */\nexport type AiPendingActionFailedRecord = {\n recordId: string\n error: { code: string; message: string }\n}\n\nexport type AiPendingActionFieldDiff = {\n field: string\n before: unknown\n after: unknown\n}\n\n/**\n * Structured error context the confirm-executor stamps on\n * `executionResult.error` when a tool handler throws. Additive \u2014 every\n * field is optional so older serialized snapshots remain valid.\n *\n * - `details`: free-form structured payload extracted from the thrown\n * error (e.g. ZodError `issues`, `cause`, custom error properties).\n * Forwarded verbatim to the operator's \"Fix with AI\" prompt so the\n * model can correct the call instead of staring at \"Invalid input\".\n * - `input`: a JSON-serializable echo of the arguments the handler was\n * invoked with. Lets the model compare what it sent vs. what the\n * schema expected. PII-redaction is the caller's responsibility (the\n * confirm-executor passes through `normalizedInput` which has already\n * been Zod-parsed and stripped of unknown keys).\n * - `name`: the constructor name of the thrown error (e.g. `ZodError`,\n * `TypeError`) \u2014 useful when the message is generic.\n * - `stack`: short stack snippet for handler-side diagnostics. Kept off\n * by default (only populated when `OM_AI_INCLUDE_HANDLER_STACK=1`).\n */\nexport type AiPendingActionExecutionErrorDetails = {\n issues?: Array<{ path?: (string | number)[]; message?: string; code?: string }>\n fieldErrors?: Record<string, string[]>\n cause?: unknown\n [key: string]: unknown\n}\n\nexport type AiPendingActionExecutionResult = {\n recordId?: string\n commandName?: string\n error?: {\n code: string\n message: string\n name?: string\n details?: AiPendingActionExecutionErrorDetails\n input?: unknown\n stack?: string\n }\n}\n\n/**\n * Thrown by the repository when a caller attempts an illegal status\n * transition (e.g. `confirmed \u2192 pending`). Callers at the route layer\n * turn this into a `409 Conflict` response.\n */\nexport class AiPendingActionStateError extends Error {\n public readonly code = 'ai_pending_action_invalid_transition'\n\n constructor(\n public readonly from: AiPendingActionStatus,\n public readonly to: AiPendingActionStatus,\n ) {\n super(`Illegal AiPendingAction status transition: ${from} \u2192 ${to}`)\n this.name = 'AiPendingActionStateError'\n }\n}\n\nexport function isAiPendingActionStatus(\n value: unknown,\n): value is AiPendingActionStatus {\n return (\n typeof value === 'string' &&\n (AI_PENDING_ACTION_STATUSES as readonly string[]).includes(value)\n )\n}\n\nexport function isAiPendingActionQueueMode(\n value: unknown,\n): value is AiPendingActionQueueMode {\n return (\n typeof value === 'string' &&\n (AI_PENDING_ACTION_QUEUE_MODES as readonly string[]).includes(value)\n )\n}\n\nexport function isTerminalAiPendingActionStatus(\n status: AiPendingActionStatus,\n): boolean {\n return AI_PENDING_ACTION_TERMINAL_STATUSES.includes(status)\n}\n\nexport function isAllowedAiPendingActionTransition(\n from: AiPendingActionStatus,\n to: AiPendingActionStatus,\n): boolean {\n return (AI_PENDING_ACTION_ALLOWED_TRANSITIONS[from] ?? []).includes(to)\n}\n\n/**\n * Default TTL for a pending action (spec \u00A78 rule `expiresAt defaults to 10 min;\n * overridable per agent`). The runtime default is 15 min here because the\n * Step 5.5 brief pins it there; the repo reads `AI_PENDING_ACTION_TTL_SECONDS`\n * from the environment to allow override without a code change.\n */\nexport const AI_PENDING_ACTION_DEFAULT_TTL_SECONDS = 900\nexport const AI_PENDING_ACTION_TTL_ENV_VAR = 'AI_PENDING_ACTION_TTL_SECONDS'\n\nexport function resolveAiPendingActionTtlSeconds(\n env: NodeJS.ProcessEnv = process.env,\n): number {\n const raw = env[AI_PENDING_ACTION_TTL_ENV_VAR]\n if (raw == null) return AI_PENDING_ACTION_DEFAULT_TTL_SECONDS\n const parsed = Number.parseInt(String(raw), 10)\n if (!Number.isFinite(parsed) || parsed <= 0) {\n return AI_PENDING_ACTION_DEFAULT_TTL_SECONDS\n }\n return parsed\n}\n"],
|
|
5
|
+
"mappings": "AAUO,MAAM,6BAA6B;AAAA,EACxC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAIO,MAAM,gCAAgC,CAAC,UAAU,OAAO;AAiBxD,MAAM,wCAGT;AAAA,EACF,SAAS,CAAC,aAAa,aAAa,SAAS;AAAA,EAC7C,WAAW,CAAC,WAAW;AAAA,EACvB,WAAW,CAAC,aAAa,QAAQ;AAAA,EACjC,WAAW,CAAC;AAAA,EACZ,SAAS,CAAC;AAAA,EACV,QAAQ,CAAC;AACX;AAEO,MAAM,sCAA4E;AAAA,EACvF;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AA4EO,MAAM,kCAAkC,MAAM;AAAA,EAGnD,YACkB,MACA,IAChB;AACA,UAAM,8CAA8C,IAAI,WAAM,EAAE,EAAE;AAHlD;AACA;AAJlB,SAAgB,OAAO;AAOrB,SAAK,OAAO;AAAA,EACd;AACF;AAEO,SAAS,wBACd,OACgC;AAChC,SACE,OAAO,UAAU,YAChB,2BAAiD,SAAS,KAAK;AAEpE;AAEO,SAAS,2BACd,OACmC;AACnC,SACE,OAAO,UAAU,YAChB,8BAAoD,SAAS,KAAK;AAEvE;AAEO,SAAS,gCACd,QACS;AACT,SAAO,oCAAoC,SAAS,MAAM;AAC5D;AAEO,SAAS,mCACd,MACA,IACS;AACT,UAAQ,sCAAsC,IAAI,KAAK,CAAC,GAAG,SAAS,EAAE;AACxE;AAQO,MAAM,wCAAwC;AAC9C,MAAM,gCAAgC;AAEtC,SAAS,iCACd,MAAyB,QAAQ,KACzB;AACR,QAAM,MAAM,IAAI,6BAA6B;AAC7C,MAAI,OAAO,KAAM,QAAO;AACxB,QAAM,SAAS,OAAO,SAAS,OAAO,GAAG,GAAG,EAAE;AAC9C,MAAI,CAAC,OAAO,SAAS,MAAM,KAAK,UAAU,GAAG;AAC3C,WAAO;AAAA,EACT;AACA,SAAO;AACT;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|