@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,424 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pending-action executor (spec §9.4, Step 5.8).
|
|
3
|
+
*
|
|
4
|
+
* Transitions an `AiPendingAction` from `pending → confirmed → executing`,
|
|
5
|
+
* invokes the wrapped tool handler, and records the outcome. Isolated from
|
|
6
|
+
* the HTTP route so the unit suite can exercise the state-machine +
|
|
7
|
+
* event-emission + idempotency guarantees without constructing a
|
|
8
|
+
* `NextRequest`.
|
|
9
|
+
*
|
|
10
|
+
* Atomicity:
|
|
11
|
+
* - The `pending → confirmed` and `confirmed → executing` transitions go
|
|
12
|
+
* through the repository's `em.transactional` boundary. If the process
|
|
13
|
+
* crashes between steps, the row is left in an intermediate terminal
|
|
14
|
+
* state (`executing` or `confirmed`) that the operator can recover —
|
|
15
|
+
* NEVER in a partially-applied state that hides the crash.
|
|
16
|
+
* - The tool handler itself runs OUTSIDE the repo transaction so that a
|
|
17
|
+
* long-running write does not hold an `ai_pending_actions` row lock.
|
|
18
|
+
* The handler's own transaction boundary (typically a command) is the
|
|
19
|
+
* unit of atomicity for the underlying data change.
|
|
20
|
+
*/
|
|
21
|
+
import { AiPendingActionRepository } from '../data/repositories/AiPendingActionRepository'
|
|
22
|
+
import type { AiPendingAction } from '../data/entities'
|
|
23
|
+
import { emitAiAssistantEvent } from '../events'
|
|
24
|
+
import type { AiActionConfirmedPayload } from '../events'
|
|
25
|
+
import type { AiAgentDefinition } from './ai-agent-definition'
|
|
26
|
+
import type { AiToolDefinition, McpToolContext } from './types'
|
|
27
|
+
import type {
|
|
28
|
+
AiPendingActionExecutionResult,
|
|
29
|
+
AiPendingActionFailedRecord,
|
|
30
|
+
} from './pending-action-types'
|
|
31
|
+
|
|
32
|
+
export interface PendingActionExecuteContext {
|
|
33
|
+
tenantId: string
|
|
34
|
+
organizationId: string | null
|
|
35
|
+
userId: string
|
|
36
|
+
userFeatures: string[]
|
|
37
|
+
isSuperAdmin: boolean
|
|
38
|
+
container: import('awilix').AwilixContainer
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export interface PendingActionExecuteInput {
|
|
42
|
+
action: AiPendingAction
|
|
43
|
+
agent: AiAgentDefinition
|
|
44
|
+
tool: AiToolDefinition
|
|
45
|
+
ctx: PendingActionExecuteContext
|
|
46
|
+
/** Carried over from the re-check; written onto the row with status=confirmed. */
|
|
47
|
+
failedRecords?: AiPendingActionFailedRecord[] | null
|
|
48
|
+
repo?: AiPendingActionRepository
|
|
49
|
+
/**
|
|
50
|
+
* Injection seam for unit tests. When omitted, emission is routed via
|
|
51
|
+
* the typed `emitAiAssistantEvent` helper (the normal production path).
|
|
52
|
+
* When supplied, the raw bus is used directly — kept for legacy tests
|
|
53
|
+
* that assert on the bus call surface.
|
|
54
|
+
*/
|
|
55
|
+
emitEvent?: (
|
|
56
|
+
eventId: 'ai.action.confirmed',
|
|
57
|
+
payload: AiActionConfirmedPayload,
|
|
58
|
+
) => Promise<void>
|
|
59
|
+
now?: Date
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export interface PendingActionExecuteOk {
|
|
63
|
+
ok: true
|
|
64
|
+
action: AiPendingAction
|
|
65
|
+
executionResult: AiPendingActionExecutionResult
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export interface PendingActionExecuteFail {
|
|
69
|
+
ok: false
|
|
70
|
+
action: AiPendingAction
|
|
71
|
+
executionResult: AiPendingActionExecutionResult
|
|
72
|
+
/** The underlying error — the route translates into a 200 with `executionResult.error` set. */
|
|
73
|
+
cause: unknown
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export type PendingActionExecuteResult = PendingActionExecuteOk | PendingActionExecuteFail
|
|
77
|
+
|
|
78
|
+
const CONFIRMED_EVENT_ID = 'ai.action.confirmed' as const
|
|
79
|
+
|
|
80
|
+
type ConfirmedEmitter = (
|
|
81
|
+
eventId: 'ai.action.confirmed',
|
|
82
|
+
payload: AiActionConfirmedPayload,
|
|
83
|
+
) => Promise<void>
|
|
84
|
+
|
|
85
|
+
const defaultConfirmedEmitter: ConfirmedEmitter = async (eventId, payload) => {
|
|
86
|
+
await emitAiAssistantEvent(eventId, payload as unknown as Record<string, unknown>, {
|
|
87
|
+
persistent: true,
|
|
88
|
+
})
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
async function emitConfirmed(
|
|
92
|
+
emitter: ConfirmedEmitter,
|
|
93
|
+
payload: AiActionConfirmedPayload,
|
|
94
|
+
): Promise<void> {
|
|
95
|
+
try {
|
|
96
|
+
await emitter(CONFIRMED_EVENT_ID, payload)
|
|
97
|
+
} catch (error) {
|
|
98
|
+
console.warn(`[AI Pending Action] Failed to emit ${CONFIRMED_EVENT_ID}:`, error)
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function normalizeExecutionResult(
|
|
103
|
+
raw: unknown,
|
|
104
|
+
): AiPendingActionExecutionResult {
|
|
105
|
+
if (!raw || typeof raw !== 'object') return {}
|
|
106
|
+
const source = raw as Record<string, unknown>
|
|
107
|
+
const result: AiPendingActionExecutionResult = {}
|
|
108
|
+
if (typeof source.recordId === 'string') result.recordId = source.recordId
|
|
109
|
+
if (typeof source.commandName === 'string') result.commandName = source.commandName
|
|
110
|
+
return result
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Extract per-record handler failures from a bulk tool's return value so
|
|
115
|
+
* they can be persisted onto the pending-action row's `failedRecords[]`.
|
|
116
|
+
*
|
|
117
|
+
* Bulk mutation tools (Step 5.14) return a result of the shape:
|
|
118
|
+
* { commandName, records: [{ recordId, status, before, after, error? }],
|
|
119
|
+
* failedRecordIds: string[], error? }
|
|
120
|
+
*
|
|
121
|
+
* We pull the entries whose `status !== 'updated'` AND carry an `error`
|
|
122
|
+
* object, coerce them to the `AiPendingActionFailedRecord` shape, and
|
|
123
|
+
* return them so the executor can merge with re-check-sourced failures
|
|
124
|
+
* at the final `executing → confirmed` transition (spec §9.8 line 746:
|
|
125
|
+
* "a failure inside the confirm handler ... is recorded per-record in
|
|
126
|
+
* executionResult.failedRecords[] / row.failedRecords").
|
|
127
|
+
*
|
|
128
|
+
* Returns an empty array when the handler output does not carry the
|
|
129
|
+
* batch shape (single-record tools never populate this — their failures
|
|
130
|
+
* either throw from the handler and land in `executionResult.error` or
|
|
131
|
+
* succeed cleanly).
|
|
132
|
+
*/
|
|
133
|
+
/**
|
|
134
|
+
* Convert a thrown handler error into the structured shape we persist on
|
|
135
|
+
* `executionResult.error`. The previous implementation only kept
|
|
136
|
+
* `{ code: 'handler_error', message }` — for ZodError that flattened to
|
|
137
|
+
* the literal string "Invalid input", which was not enough context for
|
|
138
|
+
* the operator's "Fix with AI" retry to actually fix anything. This
|
|
139
|
+
* version preserves the original error name, Zod issues / fieldErrors,
|
|
140
|
+
* an `input` echo of the arguments the handler was called with, and any
|
|
141
|
+
* structured `cause` so the model can self-correct.
|
|
142
|
+
*
|
|
143
|
+
* Stack traces are deliberately gated behind `OM_AI_INCLUDE_HANDLER_STACK=1`
|
|
144
|
+
* — they're noise to the model and a leak risk in tenant-visible UI.
|
|
145
|
+
*/
|
|
146
|
+
function buildHandlerErrorFromThrown(
|
|
147
|
+
error: unknown,
|
|
148
|
+
input: unknown,
|
|
149
|
+
): NonNullable<AiPendingActionExecutionResult['error']> {
|
|
150
|
+
const message = error instanceof Error ? error.message : String(error)
|
|
151
|
+
const name = error instanceof Error ? error.name : undefined
|
|
152
|
+
const out: NonNullable<AiPendingActionExecutionResult['error']> = {
|
|
153
|
+
code: 'handler_error',
|
|
154
|
+
message: message || 'Tool handler threw an error.',
|
|
155
|
+
}
|
|
156
|
+
if (name) out.name = name
|
|
157
|
+
|
|
158
|
+
// Echo the input so the model can compare what it sent vs. what the
|
|
159
|
+
// schema expected. `normalizedInput` has already been Zod-parsed at
|
|
160
|
+
// prepareMutation time so the values are JSON-safe.
|
|
161
|
+
if (input !== undefined) {
|
|
162
|
+
try {
|
|
163
|
+
out.input = JSON.parse(JSON.stringify(input))
|
|
164
|
+
} catch {
|
|
165
|
+
// ignore — non-serializable input is rare and the model can still
|
|
166
|
+
// work from the message + Zod issues.
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
const details: Record<string, unknown> = {}
|
|
171
|
+
|
|
172
|
+
if (error && typeof error === 'object') {
|
|
173
|
+
const err = error as Record<string, unknown>
|
|
174
|
+
// ZodError: forward `issues[]` verbatim and a flat `fieldErrors`
|
|
175
|
+
// form so the model can locate the failing field by name even when
|
|
176
|
+
// the message has been collapsed to "Invalid input".
|
|
177
|
+
const issues = err.issues
|
|
178
|
+
if (Array.isArray(issues) && issues.length > 0) {
|
|
179
|
+
details.issues = issues.map((issue) => {
|
|
180
|
+
if (!issue || typeof issue !== 'object') return issue
|
|
181
|
+
const obj = issue as Record<string, unknown>
|
|
182
|
+
return {
|
|
183
|
+
path: Array.isArray(obj.path) ? obj.path : undefined,
|
|
184
|
+
message: typeof obj.message === 'string' ? obj.message : undefined,
|
|
185
|
+
code: typeof obj.code === 'string' ? obj.code : undefined,
|
|
186
|
+
...(typeof obj.expected === 'string' ? { expected: obj.expected } : {}),
|
|
187
|
+
...(typeof obj.received === 'string' ? { received: obj.received } : {}),
|
|
188
|
+
}
|
|
189
|
+
})
|
|
190
|
+
const fieldErrors: Record<string, string[]> = {}
|
|
191
|
+
for (const issue of issues as Array<Record<string, unknown>>) {
|
|
192
|
+
const path = Array.isArray(issue.path) ? issue.path.join('.') : ''
|
|
193
|
+
const msg = typeof issue.message === 'string' ? issue.message : null
|
|
194
|
+
if (!path || !msg) continue
|
|
195
|
+
if (!fieldErrors[path]) fieldErrors[path] = []
|
|
196
|
+
fieldErrors[path].push(msg)
|
|
197
|
+
}
|
|
198
|
+
if (Object.keys(fieldErrors).length > 0) {
|
|
199
|
+
details.fieldErrors = fieldErrors
|
|
200
|
+
}
|
|
201
|
+
if (out.code === 'handler_error') out.code = 'validation_error'
|
|
202
|
+
}
|
|
203
|
+
// Forward a known `code` if the handler error carries one.
|
|
204
|
+
if (typeof err.code === 'string' && err.code.length > 0) {
|
|
205
|
+
out.code = err.code
|
|
206
|
+
}
|
|
207
|
+
// Carry the cause when it is JSON-serializable (string, number, plain object).
|
|
208
|
+
if (err.cause !== undefined) {
|
|
209
|
+
try {
|
|
210
|
+
details.cause = JSON.parse(JSON.stringify(err.cause))
|
|
211
|
+
} catch {
|
|
212
|
+
if (err.cause instanceof Error) {
|
|
213
|
+
details.cause = { message: err.cause.message, name: err.cause.name }
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
// Pull through any other plain-object enumerable own props the handler
|
|
218
|
+
// attached (e.g. `expected`, `actual`, `target`).
|
|
219
|
+
for (const key of Object.keys(err)) {
|
|
220
|
+
if (key === 'issues' || key === 'cause' || key === 'code' || key === 'message' || key === 'name' || key === 'stack') continue
|
|
221
|
+
const value = err[key]
|
|
222
|
+
if (value === undefined) continue
|
|
223
|
+
try {
|
|
224
|
+
details[key] = JSON.parse(JSON.stringify(value))
|
|
225
|
+
} catch {
|
|
226
|
+
// skip non-serializable
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
if (Object.keys(details).length > 0) {
|
|
232
|
+
out.details = details
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
if (process.env.OM_AI_INCLUDE_HANDLER_STACK === '1' && error instanceof Error && error.stack) {
|
|
236
|
+
// Trim to the top frames so the persisted result stays bounded.
|
|
237
|
+
const lines = error.stack.split('\n').slice(0, 6)
|
|
238
|
+
out.stack = lines.join('\n')
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
return out
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
function extractHandlerFailedRecords(raw: unknown): AiPendingActionFailedRecord[] {
|
|
245
|
+
if (!raw || typeof raw !== 'object') return []
|
|
246
|
+
const source = raw as Record<string, unknown>
|
|
247
|
+
const records = source.records
|
|
248
|
+
if (!Array.isArray(records) || records.length === 0) return []
|
|
249
|
+
const out: AiPendingActionFailedRecord[] = []
|
|
250
|
+
for (const entry of records) {
|
|
251
|
+
if (!entry || typeof entry !== 'object') continue
|
|
252
|
+
const record = entry as Record<string, unknown>
|
|
253
|
+
if (typeof record.recordId !== 'string') continue
|
|
254
|
+
const status = typeof record.status === 'string' ? record.status : null
|
|
255
|
+
if (status === 'updated') continue
|
|
256
|
+
const errorField = record.error
|
|
257
|
+
if (!errorField || typeof errorField !== 'object') continue
|
|
258
|
+
const error = errorField as Record<string, unknown>
|
|
259
|
+
const code = typeof error.code === 'string' ? error.code : 'handler_error'
|
|
260
|
+
const message = typeof error.message === 'string' ? error.message : 'Record update failed.'
|
|
261
|
+
out.push({
|
|
262
|
+
recordId: record.recordId,
|
|
263
|
+
error: { code, message },
|
|
264
|
+
})
|
|
265
|
+
}
|
|
266
|
+
return out
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
function mergeFailedRecords(
|
|
270
|
+
recheck: AiPendingActionFailedRecord[] | null | undefined,
|
|
271
|
+
handler: AiPendingActionFailedRecord[] | null | undefined,
|
|
272
|
+
): AiPendingActionFailedRecord[] | null {
|
|
273
|
+
const seen = new Map<string, AiPendingActionFailedRecord>()
|
|
274
|
+
for (const entry of recheck ?? []) {
|
|
275
|
+
if (entry && typeof entry.recordId === 'string' && !seen.has(entry.recordId)) {
|
|
276
|
+
seen.set(entry.recordId, entry)
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
for (const entry of handler ?? []) {
|
|
280
|
+
if (entry && typeof entry.recordId === 'string' && !seen.has(entry.recordId)) {
|
|
281
|
+
seen.set(entry.recordId, entry)
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
if (seen.size === 0) return null
|
|
285
|
+
return Array.from(seen.values())
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
function toToolHandlerContext(
|
|
289
|
+
ctx: PendingActionExecuteContext,
|
|
290
|
+
tool: AiToolDefinition,
|
|
291
|
+
): McpToolContext {
|
|
292
|
+
return {
|
|
293
|
+
tenantId: ctx.tenantId,
|
|
294
|
+
organizationId: ctx.organizationId,
|
|
295
|
+
userId: ctx.userId,
|
|
296
|
+
container: ctx.container,
|
|
297
|
+
userFeatures: ctx.userFeatures,
|
|
298
|
+
isSuperAdmin: ctx.isSuperAdmin,
|
|
299
|
+
tool,
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
/**
|
|
304
|
+
* Idempotent entry point for the Step 5.8 confirm route.
|
|
305
|
+
*
|
|
306
|
+
* - If the action is already `confirmed` with a stored `executionResult`,
|
|
307
|
+
* returns that prior result without re-invoking the handler (double-click /
|
|
308
|
+
* retry contract).
|
|
309
|
+
* - If the action is already `confirmed` without a stored `executionResult`
|
|
310
|
+
* (shouldn't happen in practice), returns a synthesized empty result.
|
|
311
|
+
* - If the action is still `pending`, runs the transitions and the handler.
|
|
312
|
+
* - Any other status is rejected at the re-check layer before this helper
|
|
313
|
+
* is ever called; this helper treats them as invariant violations.
|
|
314
|
+
*/
|
|
315
|
+
export async function executePendingActionConfirm(
|
|
316
|
+
input: PendingActionExecuteInput,
|
|
317
|
+
): Promise<PendingActionExecuteResult> {
|
|
318
|
+
const { action, agent, tool, ctx, failedRecords, now } = input
|
|
319
|
+
const repo = input.repo ?? new AiPendingActionRepository(ctx.container.resolve('em'))
|
|
320
|
+
const scope = {
|
|
321
|
+
tenantId: ctx.tenantId,
|
|
322
|
+
organizationId: ctx.organizationId,
|
|
323
|
+
userId: ctx.userId,
|
|
324
|
+
}
|
|
325
|
+
const clock = now ?? new Date()
|
|
326
|
+
const emitter: ConfirmedEmitter = input.emitEvent ?? defaultConfirmedEmitter
|
|
327
|
+
|
|
328
|
+
if (action.status === 'confirmed') {
|
|
329
|
+
const prior = (action.executionResult ?? {}) as AiPendingActionExecutionResult
|
|
330
|
+
return { ok: true, action, executionResult: prior }
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
if (action.status === 'executing') {
|
|
334
|
+
const prior = (action.executionResult ?? {}) as AiPendingActionExecutionResult
|
|
335
|
+
return { ok: true, action, executionResult: prior }
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
if (action.status !== 'pending') {
|
|
339
|
+
return {
|
|
340
|
+
ok: false,
|
|
341
|
+
action,
|
|
342
|
+
executionResult: {
|
|
343
|
+
error: { code: 'invalid_status', message: `Action is in status "${action.status}".` },
|
|
344
|
+
},
|
|
345
|
+
cause: new Error(`Action is in status "${action.status}"`),
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
const partialFailedRecords =
|
|
350
|
+
Array.isArray(failedRecords) && failedRecords.length > 0 ? failedRecords : null
|
|
351
|
+
|
|
352
|
+
const confirmedRow = await repo.setStatus(action.id, 'confirmed', scope, {
|
|
353
|
+
resolvedByUserId: ctx.userId,
|
|
354
|
+
now: clock,
|
|
355
|
+
...(partialFailedRecords ? { failedRecords: partialFailedRecords } : {}),
|
|
356
|
+
})
|
|
357
|
+
const executingRow = await repo.setStatus(confirmedRow.id, 'executing', scope, { now: clock })
|
|
358
|
+
|
|
359
|
+
let handlerOutput: unknown
|
|
360
|
+
try {
|
|
361
|
+
handlerOutput = await tool.handler(action.normalizedInput as never, toToolHandlerContext(ctx, tool))
|
|
362
|
+
} catch (error) {
|
|
363
|
+
const failureResult: AiPendingActionExecutionResult = {
|
|
364
|
+
error: buildHandlerErrorFromThrown(error, action.normalizedInput),
|
|
365
|
+
}
|
|
366
|
+
const failedRow = await repo.setStatus(executingRow.id, 'failed', scope, {
|
|
367
|
+
executionResult: failureResult,
|
|
368
|
+
now: clock,
|
|
369
|
+
})
|
|
370
|
+
await emitConfirmed(emitter, {
|
|
371
|
+
pendingActionId: failedRow.id,
|
|
372
|
+
agentId: agent.id,
|
|
373
|
+
toolName: tool.name,
|
|
374
|
+
status: failedRow.status,
|
|
375
|
+
tenantId: ctx.tenantId,
|
|
376
|
+
organizationId: ctx.organizationId ?? null,
|
|
377
|
+
userId: ctx.userId,
|
|
378
|
+
resolvedByUserId: ctx.userId,
|
|
379
|
+
resolvedAt: (failedRow.resolvedAt ?? clock).toISOString?.() ?? new Date(clock).toISOString(),
|
|
380
|
+
executionResult: failureResult,
|
|
381
|
+
})
|
|
382
|
+
return {
|
|
383
|
+
ok: false,
|
|
384
|
+
action: failedRow,
|
|
385
|
+
executionResult: failureResult,
|
|
386
|
+
cause: error,
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
const successResult = normalizeExecutionResult(handlerOutput)
|
|
391
|
+
const handlerFailedRecords = extractHandlerFailedRecords(handlerOutput)
|
|
392
|
+
const mergedFailedRecords = mergeFailedRecords(partialFailedRecords, handlerFailedRecords)
|
|
393
|
+
const confirmedExtra: Record<string, unknown> = {
|
|
394
|
+
executionResult: successResult,
|
|
395
|
+
now: clock,
|
|
396
|
+
}
|
|
397
|
+
// Always write `failedRecords` on the final transition so a batch that
|
|
398
|
+
// had re-check-stale records but zero handler failures keeps the
|
|
399
|
+
// original list, and a batch with handler failures merges both sets.
|
|
400
|
+
// Explicit `null` collapses to "no failures" in the repository's
|
|
401
|
+
// `normalizeFailedRecords` helper.
|
|
402
|
+
confirmedExtra.failedRecords = mergedFailedRecords
|
|
403
|
+
const confirmedFinal = await repo.setStatus(executingRow.id, 'confirmed', scope, confirmedExtra)
|
|
404
|
+
const emitFailedRecordsPayload =
|
|
405
|
+
Array.isArray(mergedFailedRecords) && mergedFailedRecords.length > 0
|
|
406
|
+
? mergedFailedRecords
|
|
407
|
+
: null
|
|
408
|
+
await emitConfirmed(emitter, {
|
|
409
|
+
pendingActionId: confirmedFinal.id,
|
|
410
|
+
agentId: agent.id,
|
|
411
|
+
toolName: tool.name,
|
|
412
|
+
status: confirmedFinal.status,
|
|
413
|
+
tenantId: ctx.tenantId,
|
|
414
|
+
organizationId: ctx.organizationId ?? null,
|
|
415
|
+
userId: ctx.userId,
|
|
416
|
+
resolvedByUserId: ctx.userId,
|
|
417
|
+
resolvedAt: (confirmedFinal.resolvedAt ?? clock).toISOString?.() ?? new Date(clock).toISOString(),
|
|
418
|
+
executionResult: successResult,
|
|
419
|
+
...(emitFailedRecordsPayload ? { failedRecords: emitFailedRecordsPayload } : {}),
|
|
420
|
+
})
|
|
421
|
+
return { ok: true, action: confirmedFinal, executionResult: successResult }
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
export const PENDING_ACTION_CONFIRMED_EVENT_ID = CONFIRMED_EVENT_ID
|