@dizzlkheinz/ynab-mcpb 0.12.1
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/.chunkhound.json +11 -0
- package/.code/agents/0427d95e-edca-431f-a214-5e53264e29c4/error.txt +8 -0
- package/.code/agents/0d675174-d1e1-41c3-9975-4c2e275819a9/error.txt +3 -0
- package/.code/agents/0d8c5afd-4787-422b-abf8-2e5943fc7e67/error.txt +3 -0
- package/.code/agents/0ec34a70-ed5d-4b9e-bee4-bb0e4cccbc4b/error.txt +1 -0
- package/.code/agents/0ef51a21-1ab1-49d7-9561-0eaa43875ebc/error.txt +12 -0
- package/.code/agents/15db95d7-abad-4b4d-9c3b-8446089cb61d/error.txt +1 -0
- package/.code/agents/19ab9acb-f675-4ff0-902a-09a5476f8149/error.txt +1 -0
- package/.code/agents/1ef7e12d-f6ff-4897-8a9b-152d523d898e/error.txt +5 -0
- package/.code/agents/2465/exec-call_lroN9KKzJVWC7t5423DK1nT9.txt +1453 -0
- package/.code/agents/28edb6fe-95a9-41a0-ae69-aa0100d26c0c/error.txt +8 -0
- package/.code/agents/2ae40cf5-b4bf-42e2-92bf-7ea350a7755e/error.txt +9 -0
- package/.code/agents/2bfc4e1f-ac4b-45a5-b6df-bf89d4dbb54c/error.txt +1 -0
- package/.code/agents/2e2e1134-eff0-49be-ba25-8e2c3468a564/error.txt +5 -0
- package/.code/agents/3/exec-call_203OC4TNVkLxW7z2HCVEQ1cM.txt +81 -0
- package/.code/agents/3/exec-call_SS5T0XSiXB4LSNzUKTl75wkh.txt +610 -0
- package/.code/agents/3322c003-ce5e-48e3-a342-f5049c5bf9a2/error.txt +1 -0
- package/.code/agents/391e9b08-1ebc-468c-9bcd-6d0cc3193b37/error.txt +1 -0
- package/.code/agents/3ab0aa84-b7bb-4054-afa3-40b8fd7d3be0/error.txt +1 -0
- package/.code/agents/3bed368d-50fe-477e-aee3-a6707eaa1ab9/error.txt +3 -0
- package/.code/agents/3e40b925-db12-442f-8d7a-a25fc69a6672/error.txt +8 -0
- package/.code/agents/414d5776-cf58-41f3-9328-a6daed503a50/error.txt +5 -0
- package/.code/agents/42687751-4565-4610-b240-67835b17d861/error.txt +1 -0
- package/.code/agents/46b98876-1a39-43c9-9e2f-507ca6d47335/error.txt +9 -0
- package/.code/agents/4a7d9491-b26f-43dd-850d-2ecdc49b5d1b/error.txt +1 -0
- package/.code/agents/4e60f00a-1b3e-447f-87f3-7faf9deddec3/error.txt +13 -0
- package/.code/agents/5138fc1c-4d49-4b74-a7da-ccdb3a8e44e7/error.txt +14 -0
- package/.code/agents/521cff39-a7a3-42e5-a557-134f0f7daaa0/error.txt +5 -0
- package/.code/agents/53302dc5-3857-4413-9a47-9e0f64a51dc4/error.txt +5 -0
- package/.code/agents/567c7c2e-6a6f-4761-a08d-d36deeb2e0ac/error.txt +5 -0
- package/.code/agents/57b00845-80dc-47c9-953c-3028d16275d6/error.txt +3 -0
- package/.code/agents/593d9005-c2a5-48fd-8813-ece0d3f2de96/error.txt +1 -0
- package/.code/agents/5a112e66-0e1a-42f9-877c-53af56ea3551/error.txt +1 -0
- package/.code/agents/5b05e8ed-7788-4738-b7ee-9faa8180f992/error.txt +5 -0
- package/.code/agents/5f888d6f-d7ca-4ac8-be23-9ea1bf753951/error.txt +5 -0
- package/.code/agents/607db3ab-e4b0-435b-b497-93e9aa525549/error.txt +8 -0
- package/.code/agents/67dcb2a2-900f-4c78-b3fc-80b5213e0ddf/error.txt +8 -0
- package/.code/agents/69ad848c-4e98-49b3-b16c-0094ac2d1759/error.txt +5 -0
- package/.code/agents/6c9cfc5f-0d0b-445c-b121-9f60082c4f70/error.txt +1 -0
- package/.code/agents/6f6f8f77-4ab0-4f6e-9f30-40e8be0bd8f5/error.txt +1 -0
- package/.code/agents/72a7cde4-fa8a-4024-9038-27faa550539b/error.txt +1 -0
- package/.code/agents/7b48335c-8247-43aa-9949-5f820ba8e199/error.txt +1 -0
- package/.code/agents/80944249-bea9-4ac5-87de-a666c4df306e/error.txt +1 -0
- package/.code/agents/826099df-1b66-4186-a915-7eb59f9db19d/error.txt +5 -0
- package/.code/agents/8291d158-18a8-4a92-b799-4e9a4d9cce88/error.txt +1 -0
- package/.code/agents/82fb71a3-20fb-4341-804a-a2fc900f95bc/error.txt +1 -0
- package/.code/agents/855790ea-54ee-43e4-8209-a66994e37590/error.txt +1 -0
- package/.code/agents/88ce3a2e-04f2-42be-9062-bf97aa798da0/error.txt +3 -0
- package/.code/agents/9a17e398-b6ed-4218-bb55-bc64a8d38ce8/error.txt +8 -0
- package/.code/agents/9a4f4bfc-a2a6-4f40-a896-9335b41a7ed1/error.txt +1 -0
- package/.code/agents/9b633e55-ef84-47d6-94bb-fd3dd172ad97/error.txt +1 -0
- package/.code/agents/9b81f3ab-c72b-4a81-9a8f-28a49ddba84a/error.txt +8 -0
- package/.code/agents/a35daf29-b2d1-4aef-9b42-dad63a76bd47/error.txt +3 -0
- package/.code/agents/a81990cc-69ee-44d2-b907-17403c9bc5d7/error.txt +5 -0
- package/.code/agents/ab56260a-4a83-4ad4-9410-f88a23d6520a/error.txt +1 -0
- package/.code/agents/ad722c31-2d1d-45f7-bae2-3f02ca455b60/error.txt +1 -0
- package/.code/agents/b62e8690-3324-4b97-9309-731bee79416b/error.txt +5 -0
- package/.code/agents/baf60a3a-752b-4ad8-99d6-df32423ed2eb/error.txt +1 -0
- package/.code/agents/be049042-7dcb-4ac8-9beb-c8f1aea67742/error.txt +14 -0
- package/.code/agents/bed1dcb4-bfce-4a9f-8594-0f994962aafd/error.txt +1 -0
- package/.code/agents/c324a6cf-e935-4ede-9529-b3ebc18e8d6b/error.txt +5 -0
- package/.code/agents/c37c06ff-dfe3-43f2-9bbc-3ec73ec8f41d/error.txt +5 -0
- package/.code/agents/c8cd6671-433a-456b-9f88-e51cb2df6bfc/error.txt +11 -0
- package/.code/agents/ca2ccb67-2f24-428e-b27d-9365beadd140/error.txt +1 -0
- package/.code/agents/cf08c0c8-e7f0-423e-93ba-547e8e818340/error.txt +8 -0
- package/.code/agents/d579c74f-874b-40a4-9d56-ced1eb6a701d/error.txt +1 -0
- package/.code/agents/df412c98-7378-4deb-8e1e-76c416931181/error.txt +3 -0
- package/.code/agents/e5134eb3-2af4-45b0-8998-051cb4afdb45/error.txt +3 -0
- package/.code/agents/e6308471-aa45-4e9e-9496-2e9404164d97/error.txt +8 -0
- package/.code/agents/e7bd8bc7-23fb-4f46-98dc-b0dcf11b75a1/error.txt +1 -0
- package/.code/agents/e92bec35-378d-4fe1-8ac0-6e1bb3c86911/error.txt +5 -0
- package/.code/agents/ed918fbf-2dc4-4aa2-bfc5-04b65d9471ea/error.txt +1 -0
- package/.code/agents/ef1d756f-b272-48fc-8729-f05c494674f7/error.txt +1 -0
- package/.code/agents/ef359853-0249-4e41-a804-c0fc459fe456/error.txt +1 -0
- package/.code/agents/effc7b4a-4b90-40a0-8c86-a7a99d2d5fd2/error.txt +1 -0
- package/.code/agents/fa15f8d5-8359-4a8b-83a3-2f2056b3ff40/error.txt +3 -0
- package/.code/agents/fbef4193-eadf-4c8a-83ff-4878a6310f25/error.txt +8 -0
- package/.code/agents/fd0a4b4a-fda4-4964-a6d6-2b8a2da387c6/error.txt +1 -0
- package/.dxtignore +57 -0
- package/.env.example +44 -0
- package/.gemini/settings.json +8 -0
- package/.github/ISSUE_TEMPLATE/bug_report.md +41 -0
- package/.github/ISSUE_TEMPLATE/config.yml +5 -0
- package/.github/ISSUE_TEMPLATE/feature_request.md +24 -0
- package/.github/ISSUE_TEMPLATE/release_checklist.md +31 -0
- package/.github/pull_request_template.md +41 -0
- package/.github/workflows/ci-tests.yml +41 -0
- package/.github/workflows/claude-code-review.yml +57 -0
- package/.github/workflows/claude.yml +50 -0
- package/.github/workflows/full-integration.yml +22 -0
- package/.github/workflows/pr-description-check.yml +88 -0
- package/.github/workflows/publish.yml +33 -0
- package/.github/workflows/release.yml +89 -0
- package/.mcpbignore +58 -0
- package/.prettierignore +10 -0
- package/.prettierrc.json +10 -0
- package/ADOS-2-Module-1-Complete-Manual.md +757 -0
- package/AGENTS.md +36 -0
- package/CHANGELOG.md +187 -0
- package/CLAUDE.md +414 -0
- package/CODEREVIEW_RESPONSE.md +128 -0
- package/LICENSE +17 -0
- package/NUL +1 -0
- package/README.md +222 -0
- package/SCHEMA_IMPROVEMENT_SUMMARY.md +120 -0
- package/TESTING_NOTES.md +217 -0
- package/WARP.md +245 -0
- package/accountactivity-merged.csv +149 -0
- package/bin/ynab-mcp-server.cjs +4 -0
- package/bin/ynab-mcp-server.js +8 -0
- package/bundle-analysis.html +13110 -0
- package/dist/bundle/index.cjs +124 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +85 -0
- package/dist/server/YNABMCPServer.d.ts +264 -0
- package/dist/server/YNABMCPServer.js +845 -0
- package/dist/server/budgetResolver.d.ts +15 -0
- package/dist/server/budgetResolver.js +99 -0
- package/dist/server/cacheManager.d.ts +74 -0
- package/dist/server/cacheManager.js +306 -0
- package/dist/server/config.d.ts +3 -0
- package/dist/server/config.js +19 -0
- package/dist/server/deltaCache.d.ts +61 -0
- package/dist/server/deltaCache.js +206 -0
- package/dist/server/deltaCache.merge.d.ts +9 -0
- package/dist/server/deltaCache.merge.js +111 -0
- package/dist/server/diagnostics.d.ts +90 -0
- package/dist/server/diagnostics.js +163 -0
- package/dist/server/errorHandler.d.ts +69 -0
- package/dist/server/errorHandler.js +524 -0
- package/dist/server/prompts.d.ts +31 -0
- package/dist/server/prompts.js +205 -0
- package/dist/server/rateLimiter.d.ts +27 -0
- package/dist/server/rateLimiter.js +82 -0
- package/dist/server/requestLogger.d.ts +62 -0
- package/dist/server/requestLogger.js +190 -0
- package/dist/server/resources.d.ts +39 -0
- package/dist/server/resources.js +85 -0
- package/dist/server/responseFormatter.d.ts +14 -0
- package/dist/server/responseFormatter.js +42 -0
- package/dist/server/securityMiddleware.d.ts +87 -0
- package/dist/server/securityMiddleware.js +117 -0
- package/dist/server/serverKnowledgeStore.d.ts +11 -0
- package/dist/server/serverKnowledgeStore.js +42 -0
- package/dist/server/toolRegistry.d.ts +85 -0
- package/dist/server/toolRegistry.js +272 -0
- package/dist/tools/__tests__/deltaTestUtils.d.ts +18 -0
- package/dist/tools/__tests__/deltaTestUtils.js +26 -0
- package/dist/tools/accountTools.d.ts +37 -0
- package/dist/tools/accountTools.js +175 -0
- package/dist/tools/budgetTools.d.ts +10 -0
- package/dist/tools/budgetTools.js +68 -0
- package/dist/tools/categoryTools.d.ts +27 -0
- package/dist/tools/categoryTools.js +232 -0
- package/dist/tools/compareTransactions/formatter.d.ts +71 -0
- package/dist/tools/compareTransactions/formatter.js +97 -0
- package/dist/tools/compareTransactions/index.d.ts +30 -0
- package/dist/tools/compareTransactions/index.js +160 -0
- package/dist/tools/compareTransactions/matcher.d.ts +12 -0
- package/dist/tools/compareTransactions/matcher.js +140 -0
- package/dist/tools/compareTransactions/parser.d.ts +14 -0
- package/dist/tools/compareTransactions/parser.js +430 -0
- package/dist/tools/compareTransactions/types.d.ts +27 -0
- package/dist/tools/compareTransactions/types.js +1 -0
- package/dist/tools/compareTransactions.d.ts +1 -0
- package/dist/tools/compareTransactions.js +1 -0
- package/dist/tools/deltaFetcher.d.ts +22 -0
- package/dist/tools/deltaFetcher.js +137 -0
- package/dist/tools/deltaSupport.d.ts +20 -0
- package/dist/tools/deltaSupport.js +176 -0
- package/dist/tools/exportTransactions.d.ts +17 -0
- package/dist/tools/exportTransactions.js +191 -0
- package/dist/tools/monthTools.d.ts +16 -0
- package/dist/tools/monthTools.js +107 -0
- package/dist/tools/payeeTools.d.ts +17 -0
- package/dist/tools/payeeTools.js +82 -0
- package/dist/tools/reconcileAdapter.d.ts +25 -0
- package/dist/tools/reconcileAdapter.js +167 -0
- package/dist/tools/reconciliation/analyzer.d.ts +3 -0
- package/dist/tools/reconciliation/analyzer.js +567 -0
- package/dist/tools/reconciliation/executor.d.ts +94 -0
- package/dist/tools/reconciliation/executor.js +611 -0
- package/dist/tools/reconciliation/index.d.ts +54 -0
- package/dist/tools/reconciliation/index.js +249 -0
- package/dist/tools/reconciliation/matcher.d.ts +3 -0
- package/dist/tools/reconciliation/matcher.js +160 -0
- package/dist/tools/reconciliation/payeeNormalizer.d.ts +6 -0
- package/dist/tools/reconciliation/payeeNormalizer.js +77 -0
- package/dist/tools/reconciliation/recommendationEngine.d.ts +2 -0
- package/dist/tools/reconciliation/recommendationEngine.js +273 -0
- package/dist/tools/reconciliation/reportFormatter.d.ts +13 -0
- package/dist/tools/reconciliation/reportFormatter.js +214 -0
- package/dist/tools/reconciliation/types.d.ts +172 -0
- package/dist/tools/reconciliation/types.js +7 -0
- package/dist/tools/schemas/outputs/accountOutputs.d.ts +58 -0
- package/dist/tools/schemas/outputs/accountOutputs.js +24 -0
- package/dist/tools/schemas/outputs/budgetOutputs.d.ts +48 -0
- package/dist/tools/schemas/outputs/budgetOutputs.js +15 -0
- package/dist/tools/schemas/outputs/categoryOutputs.d.ts +93 -0
- package/dist/tools/schemas/outputs/categoryOutputs.js +37 -0
- package/dist/tools/schemas/outputs/comparisonOutputs.d.ts +269 -0
- package/dist/tools/schemas/outputs/comparisonOutputs.js +181 -0
- package/dist/tools/schemas/outputs/index.d.ts +14 -0
- package/dist/tools/schemas/outputs/index.js +14 -0
- package/dist/tools/schemas/outputs/monthOutputs.d.ts +122 -0
- package/dist/tools/schemas/outputs/monthOutputs.js +51 -0
- package/dist/tools/schemas/outputs/payeeOutputs.d.ts +34 -0
- package/dist/tools/schemas/outputs/payeeOutputs.js +16 -0
- package/dist/tools/schemas/outputs/reconciliationOutputs.d.ts +1275 -0
- package/dist/tools/schemas/outputs/reconciliationOutputs.js +377 -0
- package/dist/tools/schemas/outputs/transactionMutationOutputs.d.ts +717 -0
- package/dist/tools/schemas/outputs/transactionMutationOutputs.js +260 -0
- package/dist/tools/schemas/outputs/transactionOutputs.d.ts +98 -0
- package/dist/tools/schemas/outputs/transactionOutputs.js +49 -0
- package/dist/tools/schemas/outputs/utilityOutputs.d.ts +219 -0
- package/dist/tools/schemas/outputs/utilityOutputs.js +120 -0
- package/dist/tools/schemas/shared/commonOutputs.d.ts +24 -0
- package/dist/tools/schemas/shared/commonOutputs.js +27 -0
- package/dist/tools/toolCategories.d.ts +32 -0
- package/dist/tools/toolCategories.js +32 -0
- package/dist/tools/transactionTools.d.ts +315 -0
- package/dist/tools/transactionTools.js +1722 -0
- package/dist/tools/utilityTools.d.ts +10 -0
- package/dist/tools/utilityTools.js +56 -0
- package/dist/types/index.d.ts +20 -0
- package/dist/types/index.js +16 -0
- package/dist/types/toolAnnotations.d.ts +7 -0
- package/dist/types/toolAnnotations.js +1 -0
- package/dist/utils/amountUtils.d.ts +3 -0
- package/dist/utils/amountUtils.js +10 -0
- package/dist/utils/dateUtils.d.ts +9 -0
- package/dist/utils/dateUtils.js +43 -0
- package/dist/utils/money.d.ts +21 -0
- package/dist/utils/money.js +51 -0
- package/docs/README.md +72 -0
- package/docs/assets/examples/reconciliation-with-recommendations.json +68 -0
- package/docs/assets/schemas/reconciliation-v2.json +338 -0
- package/docs/getting-started/CONFIGURATION.md +175 -0
- package/docs/getting-started/INSTALLATION.md +333 -0
- package/docs/getting-started/QUICKSTART.md +282 -0
- package/docs/guides/ARCHITECTURE.md +650 -0
- package/docs/guides/DEPLOYMENT.md +189 -0
- package/docs/guides/INTEGRATION_TESTING.md +730 -0
- package/docs/guides/TESTING.md +591 -0
- package/docs/reconciliation-flow.md +83 -0
- package/docs/reference/API.md +1450 -0
- package/docs/reference/EXAMPLES.md +946 -0
- package/docs/reference/TOOLS.md +348 -0
- package/docs/reference/TROUBLESHOOTING.md +481 -0
- package/esbuild.config.mjs +68 -0
- package/eslint.config.js +49 -0
- package/fix-types.sh +17 -0
- package/meta.json +12550 -0
- package/package.json +105 -0
- package/package.json.tmp +105 -0
- package/scripts/analyze-bundle.mjs +41 -0
- package/scripts/create-pr-description.js +203 -0
- package/scripts/generate-mcpb.ps1 +96 -0
- package/scripts/run-domain-integration-tests.js +33 -0
- package/scripts/run-generate-mcpb.js +29 -0
- package/scripts/run-throttled-integration-tests.js +116 -0
- package/scripts/test-delta-params.mjs +140 -0
- package/scripts/test-recommendations.ts +53 -0
- package/scripts/tmpTransaction.ts +48 -0
- package/scripts/validate-env.js +122 -0
- package/scripts/verify-build.js +105 -0
- package/scripts/watch-and-restart.ps1 +50 -0
- package/src/__tests__/comprehensive.integration.test.ts +1196 -0
- package/src/__tests__/delta.performance.test.ts +80 -0
- package/src/__tests__/performance.test.ts +725 -0
- package/src/__tests__/setup.ts +449 -0
- package/src/__tests__/testRunner.ts +444 -0
- package/src/__tests__/testUtils.ts +563 -0
- package/src/__tests__/workflows.e2e.test.ts +1675 -0
- package/src/index.ts +124 -0
- package/src/server/.gitkeep +1 -0
- package/src/server/YNABMCPServer.ts +1188 -0
- package/src/server/__tests__/YNABMCPServer.integration.test.ts +903 -0
- package/src/server/__tests__/YNABMCPServer.test.ts +894 -0
- package/src/server/__tests__/budgetResolver.test.ts +425 -0
- package/src/server/__tests__/cacheManager.test.ts +880 -0
- package/src/server/__tests__/config.test.ts +166 -0
- package/src/server/__tests__/deltaCache.merge.test.ts +724 -0
- package/src/server/__tests__/deltaCache.swr.test.ts +168 -0
- package/src/server/__tests__/deltaCache.test.ts +774 -0
- package/src/server/__tests__/diagnostics.test.ts +823 -0
- package/src/server/__tests__/errorHandler.integration.test.ts +466 -0
- package/src/server/__tests__/errorHandler.test.ts +416 -0
- package/src/server/__tests__/prompts.test.ts +354 -0
- package/src/server/__tests__/rateLimiter.test.ts +314 -0
- package/src/server/__tests__/requestLogger.test.ts +408 -0
- package/src/server/__tests__/resources.test.ts +299 -0
- package/src/server/__tests__/security.integration.test.ts +426 -0
- package/src/server/__tests__/securityMiddleware.test.ts +449 -0
- package/src/server/__tests__/server-startup.integration.test.ts +477 -0
- package/src/server/__tests__/serverKnowledgeStore.test.ts +174 -0
- package/src/server/__tests__/toolRegistry.test.ts +855 -0
- package/src/server/budgetResolver.ts +235 -0
- package/src/server/cacheManager.ts +503 -0
- package/src/server/config.ts +41 -0
- package/src/server/deltaCache.merge.ts +149 -0
- package/src/server/deltaCache.ts +341 -0
- package/src/server/diagnostics.ts +338 -0
- package/src/server/errorHandler.ts +756 -0
- package/src/server/prompts.ts +291 -0
- package/src/server/rateLimiter.ts +156 -0
- package/src/server/requestLogger.ts +344 -0
- package/src/server/resources.ts +168 -0
- package/src/server/responseFormatter.ts +51 -0
- package/src/server/securityMiddleware.ts +236 -0
- package/src/server/serverKnowledgeStore.ts +91 -0
- package/src/server/toolRegistry.ts +489 -0
- package/src/tools/.gitkeep +1 -0
- package/src/tools/__tests__/accountTools.delta.integration.test.ts +128 -0
- package/src/tools/__tests__/accountTools.integration.test.ts +117 -0
- package/src/tools/__tests__/accountTools.test.ts +653 -0
- package/src/tools/__tests__/budgetTools.delta.integration.test.ts +90 -0
- package/src/tools/__tests__/budgetTools.integration.test.ts +134 -0
- package/src/tools/__tests__/budgetTools.test.ts +423 -0
- package/src/tools/__tests__/categoryTools.delta.integration.test.ts +80 -0
- package/src/tools/__tests__/categoryTools.integration.test.ts +295 -0
- package/src/tools/__tests__/categoryTools.test.ts +622 -0
- package/src/tools/__tests__/compareTransactions/formatter.test.ts +486 -0
- package/src/tools/__tests__/compareTransactions/index.test.ts +383 -0
- package/src/tools/__tests__/compareTransactions/matcher.test.ts +410 -0
- package/src/tools/__tests__/compareTransactions/parser.test.ts +764 -0
- package/src/tools/__tests__/compareTransactions.test.ts +342 -0
- package/src/tools/__tests__/compareTransactions.window.test.ts +147 -0
- package/src/tools/__tests__/deltaFetcher.scheduled.integration.test.ts +76 -0
- package/src/tools/__tests__/deltaFetcher.test.ts +270 -0
- package/src/tools/__tests__/deltaSupport.test.ts +188 -0
- package/src/tools/__tests__/deltaTestUtils.ts +46 -0
- package/src/tools/__tests__/exportTransactions.test.ts +213 -0
- package/src/tools/__tests__/monthTools.delta.integration.test.ts +80 -0
- package/src/tools/__tests__/monthTools.integration.test.ts +174 -0
- package/src/tools/__tests__/monthTools.test.ts +523 -0
- package/src/tools/__tests__/payeeTools.delta.integration.test.ts +80 -0
- package/src/tools/__tests__/payeeTools.integration.test.ts +150 -0
- package/src/tools/__tests__/payeeTools.test.ts +445 -0
- package/src/tools/__tests__/transactionTools.integration.test.ts +762 -0
- package/src/tools/__tests__/transactionTools.test.ts +3521 -0
- package/src/tools/__tests__/utilityTools.integration.test.ts +128 -0
- package/src/tools/__tests__/utilityTools.test.ts +205 -0
- package/src/tools/accountTools.ts +283 -0
- package/src/tools/budgetTools.ts +112 -0
- package/src/tools/categoryTools.ts +366 -0
- package/src/tools/compareTransactions/formatter.ts +163 -0
- package/src/tools/compareTransactions/index.ts +228 -0
- package/src/tools/compareTransactions/matcher.ts +240 -0
- package/src/tools/compareTransactions/parser.ts +557 -0
- package/src/tools/compareTransactions/types.ts +60 -0
- package/src/tools/compareTransactions.ts +3 -0
- package/src/tools/deltaFetcher.ts +278 -0
- package/src/tools/deltaSupport.ts +293 -0
- package/src/tools/exportTransactions.ts +273 -0
- package/src/tools/monthTools.ts +164 -0
- package/src/tools/payeeTools.ts +140 -0
- package/src/tools/reconcileAdapter.ts +312 -0
- package/src/tools/reconciliation/__tests__/adapter.causes.test.ts +122 -0
- package/src/tools/reconciliation/__tests__/adapter.test.ts +234 -0
- package/src/tools/reconciliation/__tests__/analyzer.test.ts +406 -0
- package/src/tools/reconciliation/__tests__/executor.integration.test.ts +366 -0
- package/src/tools/reconciliation/__tests__/executor.test.ts +779 -0
- package/src/tools/reconciliation/__tests__/matcher.test.ts +650 -0
- package/src/tools/reconciliation/__tests__/payeeNormalizer.test.ts +278 -0
- package/src/tools/reconciliation/__tests__/recommendationEngine.integration.test.ts +658 -0
- package/src/tools/reconciliation/__tests__/recommendationEngine.test.ts +1000 -0
- package/src/tools/reconciliation/__tests__/reconciliation.delta.integration.test.ts +151 -0
- package/src/tools/reconciliation/__tests__/reportFormatter.test.ts +573 -0
- package/src/tools/reconciliation/__tests__/scenarios/adapterCurrency.scenario.test.ts +78 -0
- package/src/tools/reconciliation/__tests__/scenarios/extremes.scenario.test.ts +47 -0
- package/src/tools/reconciliation/__tests__/scenarios/repeatAmount.scenario.test.ts +61 -0
- package/src/tools/reconciliation/__tests__/schemaUrl.test.ts +49 -0
- package/src/tools/reconciliation/analyzer.ts +824 -0
- package/src/tools/reconciliation/executor.ts +880 -0
- package/src/tools/reconciliation/index.ts +400 -0
- package/src/tools/reconciliation/matcher.ts +269 -0
- package/src/tools/reconciliation/payeeNormalizer.ts +167 -0
- package/src/tools/reconciliation/recommendationEngine.ts +506 -0
- package/src/tools/reconciliation/reportFormatter.ts +363 -0
- package/src/tools/reconciliation/types.ts +314 -0
- package/src/tools/schemas/outputs/__tests__/accountOutputs.test.ts +424 -0
- package/src/tools/schemas/outputs/__tests__/budgetOutputs.test.ts +310 -0
- package/src/tools/schemas/outputs/__tests__/categoryOutputs.test.ts +448 -0
- package/src/tools/schemas/outputs/__tests__/comparisonOutputs.test.ts +519 -0
- package/src/tools/schemas/outputs/__tests__/dateValidation.test.ts +155 -0
- package/src/tools/schemas/outputs/__tests__/discrepancyDirection.test.ts +288 -0
- package/src/tools/schemas/outputs/__tests__/monthOutputs.test.ts +478 -0
- package/src/tools/schemas/outputs/__tests__/payeeOutputs.test.ts +370 -0
- package/src/tools/schemas/outputs/__tests__/reconciliationOutputs.test.ts +401 -0
- package/src/tools/schemas/outputs/__tests__/transactionMutationSchemas.test.ts +213 -0
- package/src/tools/schemas/outputs/__tests__/transactionOutputs.test.ts +474 -0
- package/src/tools/schemas/outputs/__tests__/utilityOutputs.test.ts +333 -0
- package/src/tools/schemas/outputs/accountOutputs.ts +137 -0
- package/src/tools/schemas/outputs/budgetOutputs.ts +86 -0
- package/src/tools/schemas/outputs/categoryOutputs.ts +194 -0
- package/src/tools/schemas/outputs/comparisonOutputs.ts +600 -0
- package/src/tools/schemas/outputs/index.ts +270 -0
- package/src/tools/schemas/outputs/monthOutputs.ts +243 -0
- package/src/tools/schemas/outputs/payeeOutputs.ts +105 -0
- package/src/tools/schemas/outputs/reconciliationOutputs.ts +796 -0
- package/src/tools/schemas/outputs/transactionMutationOutputs.ts +758 -0
- package/src/tools/schemas/outputs/transactionOutputs.ts +243 -0
- package/src/tools/schemas/outputs/utilityOutputs.ts +411 -0
- package/src/tools/schemas/shared/commonOutputs.ts +140 -0
- package/src/tools/toolCategories.ts +140 -0
- package/src/tools/transactionTools.ts +2509 -0
- package/src/tools/utilityTools.ts +90 -0
- package/src/types/.gitkeep +1 -0
- package/src/types/__tests__/index.test.ts +52 -0
- package/src/types/index.ts +67 -0
- package/src/types/integration-tests.d.ts +35 -0
- package/src/types/toolAnnotations.ts +44 -0
- package/src/utils/__tests__/dateUtils.test.ts +170 -0
- package/src/utils/__tests__/money.test.ts +189 -0
- package/src/utils/amountUtils.ts +32 -0
- package/src/utils/dateUtils.ts +108 -0
- package/src/utils/money.ts +123 -0
- package/test-csv-sample.csv +28 -0
- package/test-exports/sample_bank_statement.csv +7 -0
- package/test-exports/ynab_account_e9ddc2a6_minimal_1items_2025-11-19_09-04-53.json +23 -0
- package/test-exports/ynab_account_e9ddc2a6_minimal_1items_2025-11-19_10-37-42.json +23 -0
- package/test-exports/ynab_account_e9ddc2a6_minimal_4items_2025-11-19_09-02-09.json +44 -0
- package/test-exports/ynab_account_e9ddc2a6_minimal_6items_2025-11-19_10-37-52.json +58 -0
- package/test-exports/ynab_since_2025-11-01_account_4c18e9f0_minimal_14items_2025-11-16_10-07-10.json +115 -0
- package/test-reconcile-autodetect.js +40 -0
- package/test-reconcile-tool.js +152 -0
- package/test-reconcile-with-csv.cjs +89 -0
- package/test-statement.csv +8 -0
- package/test_debug.js +47 -0
- package/test_simple.mjs +16 -0
- package/tsconfig.json +31 -0
- package/tsconfig.prod.json +18 -0
- package/vitest-reporters/split-json-reporter.ts +211 -0
- package/vitest.config.ts +96 -0
|
@@ -0,0 +1,796 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Reconciliation analysis output schemas for YNAB MCP server.
|
|
3
|
+
* Defines Zod validation schemas for account reconciliation including transaction matching,
|
|
4
|
+
* balance verification, insights, actionable recommendations, and optional execution results.
|
|
5
|
+
*
|
|
6
|
+
* @see src/tools/reconciliation/index.ts - Main reconciliation handler (lines 147-362)
|
|
7
|
+
* @see src/tools/reconciliation/types.ts - Type definitions for reconciliation entities
|
|
8
|
+
* @see src/tools/reconciliation/executor.ts - Execution engine for auto-applying recommendations
|
|
9
|
+
* @see src/tools/reconcileAdapter.ts - Adapter for building reconciliation payloads
|
|
10
|
+
* @see src/utils/money.ts - Money value formatting utilities
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* // Human-readable narrative only (default)
|
|
14
|
+
* {
|
|
15
|
+
* human: "Successfully matched 45 of 50 bank transactions. Found 3 suggested matches..."
|
|
16
|
+
* }
|
|
17
|
+
*
|
|
18
|
+
* @example
|
|
19
|
+
* // With structured data (include_structured_data=true)
|
|
20
|
+
* {
|
|
21
|
+
* human: "Successfully matched 45 of 50 bank transactions...",
|
|
22
|
+
* structured: {
|
|
23
|
+
* success: true,
|
|
24
|
+
* phase: "analysis",
|
|
25
|
+
* summary: {
|
|
26
|
+
* statement_date_range: "2025-10-01 to 2025-10-31",
|
|
27
|
+
* bank_transactions_count: 50,
|
|
28
|
+
* ynab_transactions_count: 52,
|
|
29
|
+
* auto_matched: 45,
|
|
30
|
+
* suggested_matches: 3,
|
|
31
|
+
* unmatched_bank: 2,
|
|
32
|
+
* unmatched_ynab: 4,
|
|
33
|
+
* discrepancy: { amount: -25.50, currency: "USD", formatted: "-$25.50" }
|
|
34
|
+
* },
|
|
35
|
+
* balance_info: {
|
|
36
|
+
* current_cleared: { amount: 1250.00, currency: "USD", formatted: "$1,250.00" },
|
|
37
|
+
* target_statement: { amount: 1275.50, currency: "USD", formatted: "$1,275.50" },
|
|
38
|
+
* discrepancy: { amount: -25.50, currency: "USD", formatted: "-$25.50" },
|
|
39
|
+
* on_track: false
|
|
40
|
+
* },
|
|
41
|
+
* recommendations: [...],
|
|
42
|
+
* audit_metadata: { data_freshness: "real-time", ... }
|
|
43
|
+
* }
|
|
44
|
+
* }
|
|
45
|
+
*/
|
|
46
|
+
|
|
47
|
+
import { z } from 'zod';
|
|
48
|
+
|
|
49
|
+
// ============================================================================
|
|
50
|
+
// NESTED SCHEMAS FOR COMPOSITION
|
|
51
|
+
// ============================================================================
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Structured monetary value with formatting.
|
|
55
|
+
* Used throughout reconciliation for balances and discrepancies.
|
|
56
|
+
*
|
|
57
|
+
* @see src/utils/money.ts - MoneyValue type definition
|
|
58
|
+
*/
|
|
59
|
+
export const MoneyValueSchema = z.object({
|
|
60
|
+
amount: z.number().finite(),
|
|
61
|
+
currency: z.string(),
|
|
62
|
+
formatted: z.string(),
|
|
63
|
+
memo: z.string().optional(),
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
export type MoneyValue = z.infer<typeof MoneyValueSchema>;
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* ISO 8601 date string with calendar validation.
|
|
70
|
+
* Ensures dates are in YYYY-MM-DD format and represent valid calendar dates.
|
|
71
|
+
*
|
|
72
|
+
* @remarks
|
|
73
|
+
* This schema validates both format (regex) and calendar validity (refinement).
|
|
74
|
+
* It catches invalid dates like "2025-02-31" which Date.parse might coerce to "2025-03-03".
|
|
75
|
+
*
|
|
76
|
+
* @example
|
|
77
|
+
* ```typescript
|
|
78
|
+
* IsoDateWithCalendarValidationSchema.parse("2025-01-15"); // ✓ Valid
|
|
79
|
+
* IsoDateWithCalendarValidationSchema.parse("2025-02-31"); // ✗ Invalid (no Feb 31st)
|
|
80
|
+
* IsoDateWithCalendarValidationSchema.parse("2025-13-01"); // ✗ Invalid (month > 12)
|
|
81
|
+
* IsoDateWithCalendarValidationSchema.parse("2025-1-15"); // ✗ Invalid (must be zero-padded)
|
|
82
|
+
* ```
|
|
83
|
+
*/
|
|
84
|
+
export const IsoDateWithCalendarValidationSchema = z
|
|
85
|
+
.string()
|
|
86
|
+
.regex(/^\d{4}-\d{2}-\d{2}$/, 'Date must be in ISO format (YYYY-MM-DD)')
|
|
87
|
+
.refine(
|
|
88
|
+
(dateStr) => {
|
|
89
|
+
const parsed = Date.parse(dateStr);
|
|
90
|
+
if (isNaN(parsed)) {
|
|
91
|
+
return false;
|
|
92
|
+
}
|
|
93
|
+
// Verify that the parsed date components match the original string
|
|
94
|
+
// This catches cases like "2025-02-31" which Date.parse might coerce to "2025-03-03"
|
|
95
|
+
const date = new Date(parsed);
|
|
96
|
+
const year = date.getUTCFullYear();
|
|
97
|
+
const month = String(date.getUTCMonth() + 1).padStart(2, '0');
|
|
98
|
+
const day = String(date.getUTCDate()).padStart(2, '0');
|
|
99
|
+
const reconstructed = `${year}-${month}-${day}`;
|
|
100
|
+
return reconstructed === dateStr;
|
|
101
|
+
},
|
|
102
|
+
{
|
|
103
|
+
message: 'Invalid calendar date (e.g., month must be 01-12, day must be valid for the month)',
|
|
104
|
+
},
|
|
105
|
+
);
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Bank transaction from CSV import (output format with money formatting).
|
|
109
|
+
* Represents a single transaction from the user's bank statement.
|
|
110
|
+
*
|
|
111
|
+
* @remarks
|
|
112
|
+
* The adapter adds `amount_money` field with formatted money value.
|
|
113
|
+
* Date field is always serialized as an ISO 8601 date string (YYYY-MM-DD) in tool output,
|
|
114
|
+
* even though the internal type may use Date objects during analysis.
|
|
115
|
+
*
|
|
116
|
+
* @see src/tools/reconciliation/types.ts - BankTransaction interface (internal type)
|
|
117
|
+
* @see src/tools/reconcileAdapter.ts:77-80 - toBankTransactionView function
|
|
118
|
+
*/
|
|
119
|
+
export const BankTransactionSchema = z.object({
|
|
120
|
+
id: z.string().uuid(),
|
|
121
|
+
date: IsoDateWithCalendarValidationSchema,
|
|
122
|
+
amount: z.number(),
|
|
123
|
+
payee: z.string(),
|
|
124
|
+
memo: z.string().optional(),
|
|
125
|
+
original_csv_row: z.number(),
|
|
126
|
+
amount_money: MoneyValueSchema, // Added by adapter
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
export type BankTransaction = z.infer<typeof BankTransactionSchema>;
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Simplified YNAB transaction for reconciliation matching (output format with money formatting).
|
|
133
|
+
* Contains essential fields for transaction comparison.
|
|
134
|
+
*
|
|
135
|
+
* @remarks
|
|
136
|
+
* The adapter adds `amount_money` field with formatted money value.
|
|
137
|
+
* Date field is always serialized as an ISO 8601 date string (YYYY-MM-DD) in tool output,
|
|
138
|
+
* even though the internal type may use Date objects during analysis.
|
|
139
|
+
*
|
|
140
|
+
* @see src/tools/reconciliation/types.ts - YNABTransaction interface (internal type)
|
|
141
|
+
* @see src/tools/reconcileAdapter.ts:82-85 - toYNABTransactionView function
|
|
142
|
+
*/
|
|
143
|
+
export const YNABTransactionSimpleSchema = z.object({
|
|
144
|
+
id: z.string(),
|
|
145
|
+
date: IsoDateWithCalendarValidationSchema,
|
|
146
|
+
amount: z.number(),
|
|
147
|
+
payee_name: z.string().nullable(),
|
|
148
|
+
category_name: z.string().nullable(),
|
|
149
|
+
cleared: z.enum(['cleared', 'uncleared', 'reconciled']),
|
|
150
|
+
approved: z.boolean(),
|
|
151
|
+
memo: z.string().nullable().optional(),
|
|
152
|
+
amount_money: MoneyValueSchema, // Added by adapter
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
export type YNABTransactionSimple = z.infer<typeof YNABTransactionSimpleSchema>;
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Potential match candidate with confidence scoring.
|
|
159
|
+
* Represents a possible match between bank and YNAB transactions.
|
|
160
|
+
*
|
|
161
|
+
* @see src/tools/reconciliation/types.ts - MatchCandidate interface
|
|
162
|
+
*/
|
|
163
|
+
export const MatchCandidateSchema = z.object({
|
|
164
|
+
ynab_transaction: YNABTransactionSimpleSchema,
|
|
165
|
+
confidence: z.number().min(0).max(100),
|
|
166
|
+
match_reason: z.string(),
|
|
167
|
+
explanation: z.string(),
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
export type MatchCandidate = z.infer<typeof MatchCandidateSchema>;
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Derives the confidence enum value from a numeric confidence score.
|
|
174
|
+
* Used to enforce consistency between confidence and confidence_score fields.
|
|
175
|
+
*
|
|
176
|
+
* @param score - Numeric confidence score (0-100)
|
|
177
|
+
* @returns Corresponding confidence level enum value
|
|
178
|
+
*
|
|
179
|
+
* @remarks
|
|
180
|
+
* Export this function to use in application logic when constructing
|
|
181
|
+
* TransactionMatch objects to ensure consistency between the two fields.
|
|
182
|
+
*
|
|
183
|
+
* Thresholds:
|
|
184
|
+
* - 'high': score >= 90
|
|
185
|
+
* - 'medium': score >= 60
|
|
186
|
+
* - 'low': score >= 1
|
|
187
|
+
* - 'none': score === 0
|
|
188
|
+
*
|
|
189
|
+
* @example
|
|
190
|
+
* ```typescript
|
|
191
|
+
* const confidenceScore = 85;
|
|
192
|
+
* const transactionMatch = {
|
|
193
|
+
* // ... other fields
|
|
194
|
+
* confidence: deriveConfidenceFromScore(confidenceScore),
|
|
195
|
+
* confidence_score: confidenceScore,
|
|
196
|
+
* };
|
|
197
|
+
* ```
|
|
198
|
+
*/
|
|
199
|
+
export function deriveConfidenceFromScore(score: number): 'high' | 'medium' | 'low' | 'none' {
|
|
200
|
+
if (score >= 90) return 'high';
|
|
201
|
+
if (score >= 60) return 'medium';
|
|
202
|
+
if (score >= 1) return 'low';
|
|
203
|
+
return 'none';
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* Transaction match result with confidence and candidates.
|
|
208
|
+
* Links a bank transaction to a YNAB transaction or suggests candidates.
|
|
209
|
+
*
|
|
210
|
+
* @see src/tools/reconciliation/types.ts - TransactionMatch interface
|
|
211
|
+
*
|
|
212
|
+
* @remarks
|
|
213
|
+
* This schema contains both `confidence` (enum) and `confidence_score` (0-100)
|
|
214
|
+
* for backwards compatibility. A validation rule enforces consistency between
|
|
215
|
+
* the two fields by deriving the expected enum value from the numeric score
|
|
216
|
+
* and rejecting mismatches.
|
|
217
|
+
*
|
|
218
|
+
* Confidence thresholds:
|
|
219
|
+
* - 'high': confidence_score >= 90
|
|
220
|
+
* - 'medium': confidence_score >= 60
|
|
221
|
+
* - 'low': confidence_score >= 1
|
|
222
|
+
* - 'none': confidence_score === 0
|
|
223
|
+
*/
|
|
224
|
+
export const TransactionMatchSchema = z
|
|
225
|
+
.object({
|
|
226
|
+
bank_transaction: BankTransactionSchema,
|
|
227
|
+
ynab_transaction: YNABTransactionSimpleSchema.optional(),
|
|
228
|
+
candidates: z.array(MatchCandidateSchema).optional(),
|
|
229
|
+
confidence: z.enum(['high', 'medium', 'low', 'none']),
|
|
230
|
+
confidence_score: z.number().min(0).max(100),
|
|
231
|
+
match_reason: z.string(),
|
|
232
|
+
top_confidence: z.number().optional(),
|
|
233
|
+
action_hint: z.string().optional(),
|
|
234
|
+
recommendation: z.string().optional(),
|
|
235
|
+
})
|
|
236
|
+
.refine(
|
|
237
|
+
(data) => {
|
|
238
|
+
const expectedConfidence = deriveConfidenceFromScore(data.confidence_score);
|
|
239
|
+
return data.confidence === expectedConfidence;
|
|
240
|
+
},
|
|
241
|
+
{
|
|
242
|
+
message: 'Confidence mismatch: confidence enum does not match confidence_score',
|
|
243
|
+
path: ['confidence'],
|
|
244
|
+
},
|
|
245
|
+
);
|
|
246
|
+
|
|
247
|
+
export type TransactionMatch = z.infer<typeof TransactionMatchSchema>;
|
|
248
|
+
|
|
249
|
+
/**
|
|
250
|
+
* Balance reconciliation status.
|
|
251
|
+
* Compares current account balances to target statement balance.
|
|
252
|
+
*
|
|
253
|
+
* @see src/tools/reconciliation/types.ts - BalanceInfo interface
|
|
254
|
+
*/
|
|
255
|
+
export const BalanceInfoSchema = z.object({
|
|
256
|
+
current_cleared: MoneyValueSchema,
|
|
257
|
+
current_uncleared: MoneyValueSchema,
|
|
258
|
+
current_total: MoneyValueSchema,
|
|
259
|
+
target_statement: MoneyValueSchema,
|
|
260
|
+
discrepancy: MoneyValueSchema,
|
|
261
|
+
on_track: z.boolean(),
|
|
262
|
+
});
|
|
263
|
+
|
|
264
|
+
export type BalanceInfo = z.infer<typeof BalanceInfoSchema>;
|
|
265
|
+
|
|
266
|
+
/**
|
|
267
|
+
* Reconciliation summary statistics.
|
|
268
|
+
* High-level overview of matching results and balance status.
|
|
269
|
+
*
|
|
270
|
+
* @see src/tools/reconciliation/types.ts - ReconciliationSummary interface
|
|
271
|
+
*/
|
|
272
|
+
export const ReconciliationSummarySchema = z.object({
|
|
273
|
+
statement_date_range: z.string(),
|
|
274
|
+
bank_transactions_count: z.number(),
|
|
275
|
+
ynab_transactions_count: z.number(),
|
|
276
|
+
auto_matched: z.number(),
|
|
277
|
+
suggested_matches: z.number(),
|
|
278
|
+
unmatched_bank: z.number(),
|
|
279
|
+
unmatched_ynab: z.number(),
|
|
280
|
+
current_cleared_balance: MoneyValueSchema,
|
|
281
|
+
target_statement_balance: MoneyValueSchema,
|
|
282
|
+
discrepancy: MoneyValueSchema,
|
|
283
|
+
discrepancy_explanation: z.string(),
|
|
284
|
+
});
|
|
285
|
+
|
|
286
|
+
export type ReconciliationSummary = z.infer<typeof ReconciliationSummarySchema>;
|
|
287
|
+
|
|
288
|
+
/**
|
|
289
|
+
* Reconciliation analysis insight.
|
|
290
|
+
* Highlights patterns, anomalies, or issues discovered during analysis.
|
|
291
|
+
*
|
|
292
|
+
* @see src/tools/reconciliation/types.ts - ReconciliationInsight interface
|
|
293
|
+
*/
|
|
294
|
+
export const ReconciliationInsightSchema = z.object({
|
|
295
|
+
id: z.string(),
|
|
296
|
+
type: z.enum(['repeat_amount', 'near_match', 'anomaly']),
|
|
297
|
+
severity: z.enum(['info', 'warning', 'critical']),
|
|
298
|
+
title: z.string(),
|
|
299
|
+
description: z.string(),
|
|
300
|
+
evidence: z.record(z.string(), z.unknown()).optional(),
|
|
301
|
+
});
|
|
302
|
+
|
|
303
|
+
export type ReconciliationInsight = z.infer<typeof ReconciliationInsightSchema>;
|
|
304
|
+
|
|
305
|
+
/**
|
|
306
|
+
* Actionable recommendation discriminated union.
|
|
307
|
+
* Suggests specific actions to resolve discrepancies (create, update, review).
|
|
308
|
+
*
|
|
309
|
+
* @see src/tools/reconciliation/types.ts - ActionableRecommendation union type
|
|
310
|
+
*/
|
|
311
|
+
export const ActionableRecommendationSchema = z.discriminatedUnion('action_type', [
|
|
312
|
+
// Create transaction recommendation
|
|
313
|
+
z.object({
|
|
314
|
+
id: z.string(),
|
|
315
|
+
action_type: z.literal('create_transaction'),
|
|
316
|
+
priority: z.enum(['high', 'medium', 'low']),
|
|
317
|
+
confidence: z.number().min(0).max(1),
|
|
318
|
+
message: z.string(),
|
|
319
|
+
reason: z.string(),
|
|
320
|
+
estimated_impact: MoneyValueSchema,
|
|
321
|
+
account_id: z.string(),
|
|
322
|
+
source_insight_id: z.string().optional(),
|
|
323
|
+
metadata: z.record(z.string(), z.unknown()).optional(),
|
|
324
|
+
parameters: z.object({
|
|
325
|
+
account_id: z.string(),
|
|
326
|
+
date: IsoDateWithCalendarValidationSchema,
|
|
327
|
+
amount: z.number(),
|
|
328
|
+
payee_name: z.string(),
|
|
329
|
+
memo: z.string().optional(),
|
|
330
|
+
cleared: z.enum(['cleared', 'uncleared']),
|
|
331
|
+
approved: z.boolean(),
|
|
332
|
+
category_id: z.string().optional(),
|
|
333
|
+
}),
|
|
334
|
+
}),
|
|
335
|
+
// Update cleared status recommendation
|
|
336
|
+
z.object({
|
|
337
|
+
id: z.string(),
|
|
338
|
+
action_type: z.literal('update_cleared'),
|
|
339
|
+
priority: z.enum(['high', 'medium', 'low']),
|
|
340
|
+
confidence: z.number().min(0).max(1),
|
|
341
|
+
message: z.string(),
|
|
342
|
+
reason: z.string(),
|
|
343
|
+
estimated_impact: MoneyValueSchema,
|
|
344
|
+
account_id: z.string(),
|
|
345
|
+
source_insight_id: z.string().optional(),
|
|
346
|
+
metadata: z.record(z.string(), z.unknown()).optional(),
|
|
347
|
+
parameters: z.object({
|
|
348
|
+
transaction_id: z.string(),
|
|
349
|
+
cleared: z.enum(['cleared', 'uncleared', 'reconciled']),
|
|
350
|
+
}),
|
|
351
|
+
}),
|
|
352
|
+
// Review duplicate recommendation
|
|
353
|
+
z.object({
|
|
354
|
+
id: z.string(),
|
|
355
|
+
action_type: z.literal('review_duplicate'),
|
|
356
|
+
priority: z.enum(['high', 'medium', 'low']),
|
|
357
|
+
confidence: z.number().min(0).max(1),
|
|
358
|
+
message: z.string(),
|
|
359
|
+
reason: z.string(),
|
|
360
|
+
estimated_impact: MoneyValueSchema,
|
|
361
|
+
account_id: z.string(),
|
|
362
|
+
source_insight_id: z.string().optional(),
|
|
363
|
+
metadata: z.record(z.string(), z.unknown()).optional(),
|
|
364
|
+
parameters: z.object({
|
|
365
|
+
candidate_ids: z.array(z.string()),
|
|
366
|
+
bank_transaction: BankTransactionSchema,
|
|
367
|
+
suggested_match_id: z.string().optional(),
|
|
368
|
+
}),
|
|
369
|
+
}),
|
|
370
|
+
// Manual review recommendation
|
|
371
|
+
z.object({
|
|
372
|
+
id: z.string(),
|
|
373
|
+
action_type: z.literal('manual_review'),
|
|
374
|
+
priority: z.enum(['high', 'medium', 'low']),
|
|
375
|
+
confidence: z.number().min(0).max(1),
|
|
376
|
+
message: z.string(),
|
|
377
|
+
reason: z.string(),
|
|
378
|
+
estimated_impact: MoneyValueSchema,
|
|
379
|
+
account_id: z.string(),
|
|
380
|
+
source_insight_id: z.string().optional(),
|
|
381
|
+
metadata: z.record(z.string(), z.unknown()).optional(),
|
|
382
|
+
parameters: z.object({
|
|
383
|
+
issue_type: z.string(),
|
|
384
|
+
related_transactions: z.array(z.string()),
|
|
385
|
+
}),
|
|
386
|
+
}),
|
|
387
|
+
]);
|
|
388
|
+
|
|
389
|
+
export type ActionableRecommendation = z.infer<typeof ActionableRecommendationSchema>;
|
|
390
|
+
|
|
391
|
+
/**
|
|
392
|
+
* Account balance snapshot with money formatting.
|
|
393
|
+
* Used in execution result to show before/after balance states.
|
|
394
|
+
*
|
|
395
|
+
* @see src/tools/reconcileAdapter.ts:138-142 - convertAccountSnapshot function
|
|
396
|
+
*/
|
|
397
|
+
export const AccountSnapshotSchema = z.object({
|
|
398
|
+
balance: MoneyValueSchema,
|
|
399
|
+
cleared_balance: MoneyValueSchema,
|
|
400
|
+
uncleared_balance: MoneyValueSchema,
|
|
401
|
+
});
|
|
402
|
+
|
|
403
|
+
export type AccountSnapshot = z.infer<typeof AccountSnapshotSchema>;
|
|
404
|
+
|
|
405
|
+
/**
|
|
406
|
+
* Transaction data shapes for different execution action types.
|
|
407
|
+
* Provides stronger typing than generic Record<string, unknown>.
|
|
408
|
+
*/
|
|
409
|
+
|
|
410
|
+
/**
|
|
411
|
+
* Created transaction from YNAB API response.
|
|
412
|
+
* Used when a transaction is successfully created.
|
|
413
|
+
*/
|
|
414
|
+
export const CreatedTransactionSchema = z
|
|
415
|
+
.object({
|
|
416
|
+
id: z.string(),
|
|
417
|
+
date: z.string(),
|
|
418
|
+
amount: z.number(),
|
|
419
|
+
memo: z.string().nullable().optional(),
|
|
420
|
+
cleared: z.enum(['cleared', 'uncleared', 'reconciled']).optional(),
|
|
421
|
+
approved: z.boolean().optional(),
|
|
422
|
+
payee_name: z.string().nullable().optional(),
|
|
423
|
+
category_name: z.string().nullable().optional(),
|
|
424
|
+
import_id: z.string().nullable().optional(),
|
|
425
|
+
})
|
|
426
|
+
.passthrough(); // Allow additional YNAB API fields
|
|
427
|
+
|
|
428
|
+
/**
|
|
429
|
+
* Transaction creation payload.
|
|
430
|
+
* Used when documenting what would be created (dry run) or what failed to create.
|
|
431
|
+
*/
|
|
432
|
+
export const TransactionCreationPayloadSchema = z.object({
|
|
433
|
+
account_id: z.string(),
|
|
434
|
+
date: z.string(),
|
|
435
|
+
amount: z.number(),
|
|
436
|
+
payee_name: z.string().optional(),
|
|
437
|
+
memo: z.string().optional(),
|
|
438
|
+
cleared: z.enum(['cleared', 'uncleared']).optional(),
|
|
439
|
+
approved: z.boolean().optional(),
|
|
440
|
+
import_id: z.string().optional(),
|
|
441
|
+
});
|
|
442
|
+
|
|
443
|
+
/**
|
|
444
|
+
* Transaction update payload.
|
|
445
|
+
* Used when documenting status or date changes.
|
|
446
|
+
*/
|
|
447
|
+
export const TransactionUpdatePayloadSchema = z.object({
|
|
448
|
+
transaction_id: z.string(),
|
|
449
|
+
new_date: z.string().optional(),
|
|
450
|
+
cleared: z.enum(['cleared', 'uncleared', 'reconciled']).optional(),
|
|
451
|
+
});
|
|
452
|
+
|
|
453
|
+
/**
|
|
454
|
+
* Duplicate detection payload.
|
|
455
|
+
* Used when a transaction creation is skipped due to duplicate detection.
|
|
456
|
+
*/
|
|
457
|
+
export const DuplicateDetectionPayloadSchema = z.object({
|
|
458
|
+
transaction_id: z.string().nullable(),
|
|
459
|
+
import_id: z.string().optional(),
|
|
460
|
+
});
|
|
461
|
+
|
|
462
|
+
/**
|
|
463
|
+
* Execution action record with correlation tracking.
|
|
464
|
+
* Documents individual actions taken during reconciliation execution.
|
|
465
|
+
*
|
|
466
|
+
* Uses discriminated union based on action type for stronger type safety.
|
|
467
|
+
* Each action type has its own transaction payload schema.
|
|
468
|
+
*
|
|
469
|
+
* @see src/tools/reconciliation/executor.ts:29-36 - ExecutionActionRecord interface
|
|
470
|
+
* @see src/tools/reconciliation/executor.ts:226-238 - create_transaction action
|
|
471
|
+
* @see src/tools/reconciliation/executor.ts:278-290 - create_transaction_failed action
|
|
472
|
+
* @see src/tools/reconciliation/executor.ts:339-351 - create_transaction_duplicate action
|
|
473
|
+
* @see src/tools/reconciliation/executor.ts:472-480 - update_transaction action (dry run)
|
|
474
|
+
* @see src/tools/reconciliation/executor.ts:515-520 - update_transaction action (real)
|
|
475
|
+
*/
|
|
476
|
+
export const ExecutionActionRecordSchema = z.discriminatedUnion('type', [
|
|
477
|
+
// Successful transaction creation
|
|
478
|
+
z.object({
|
|
479
|
+
type: z.literal('create_transaction'),
|
|
480
|
+
transaction: CreatedTransactionSchema.nullable(),
|
|
481
|
+
reason: z.string(),
|
|
482
|
+
bulk_chunk_index: z.number().optional(),
|
|
483
|
+
correlation_key: z.string().optional(),
|
|
484
|
+
}),
|
|
485
|
+
// Failed transaction creation
|
|
486
|
+
z.object({
|
|
487
|
+
type: z.literal('create_transaction_failed'),
|
|
488
|
+
transaction: TransactionCreationPayloadSchema,
|
|
489
|
+
reason: z.string(),
|
|
490
|
+
bulk_chunk_index: z.number().optional(),
|
|
491
|
+
correlation_key: z.string().optional(),
|
|
492
|
+
}),
|
|
493
|
+
// Duplicate transaction detected
|
|
494
|
+
z.object({
|
|
495
|
+
type: z.literal('create_transaction_duplicate'),
|
|
496
|
+
transaction: DuplicateDetectionPayloadSchema,
|
|
497
|
+
reason: z.string(),
|
|
498
|
+
bulk_chunk_index: z.number(),
|
|
499
|
+
correlation_key: z.string().optional(),
|
|
500
|
+
duplicate: z.literal(true),
|
|
501
|
+
}),
|
|
502
|
+
// Transaction update (status/date change)
|
|
503
|
+
z.object({
|
|
504
|
+
type: z.literal('update_transaction'),
|
|
505
|
+
transaction: z.union([
|
|
506
|
+
CreatedTransactionSchema.nullable(), // Real execution
|
|
507
|
+
TransactionUpdatePayloadSchema, // Dry run
|
|
508
|
+
]),
|
|
509
|
+
reason: z.string(),
|
|
510
|
+
}),
|
|
511
|
+
// Balance alignment checkpoint
|
|
512
|
+
z.object({
|
|
513
|
+
type: z.literal('balance_checkpoint'),
|
|
514
|
+
transaction: z.null(),
|
|
515
|
+
reason: z.string(),
|
|
516
|
+
}),
|
|
517
|
+
// Bulk create fallback to sequential
|
|
518
|
+
z.object({
|
|
519
|
+
type: z.literal('bulk_create_fallback'),
|
|
520
|
+
transaction: z.null(),
|
|
521
|
+
reason: z.string(),
|
|
522
|
+
bulk_chunk_index: z.number(),
|
|
523
|
+
}),
|
|
524
|
+
]);
|
|
525
|
+
|
|
526
|
+
export type ExecutionActionRecord = z.infer<typeof ExecutionActionRecordSchema>;
|
|
527
|
+
|
|
528
|
+
/**
|
|
529
|
+
* Execution summary statistics.
|
|
530
|
+
* High-level overview of reconciliation execution results.
|
|
531
|
+
*
|
|
532
|
+
* @see src/tools/reconciliation/executor.ts:38-48 - ExecutionSummary interface
|
|
533
|
+
*/
|
|
534
|
+
export const ExecutionSummarySchema = z.object({
|
|
535
|
+
bank_transactions_count: z.number(),
|
|
536
|
+
ynab_transactions_count: z.number(),
|
|
537
|
+
matches_found: z.number(),
|
|
538
|
+
missing_in_ynab: z.number(),
|
|
539
|
+
missing_in_bank: z.number(),
|
|
540
|
+
transactions_created: z.number(),
|
|
541
|
+
transactions_updated: z.number(),
|
|
542
|
+
dates_adjusted: z.number(),
|
|
543
|
+
dry_run: z.boolean(),
|
|
544
|
+
});
|
|
545
|
+
|
|
546
|
+
export type ExecutionSummary = z.infer<typeof ExecutionSummarySchema>;
|
|
547
|
+
|
|
548
|
+
/**
|
|
549
|
+
* Bulk operation metrics for reconciliation transaction creation.
|
|
550
|
+
* Tracks performance and correlation behavior during bulk transaction operations.
|
|
551
|
+
*
|
|
552
|
+
* @remarks
|
|
553
|
+
* Note on failure counters:
|
|
554
|
+
* - `transaction_failures` is the canonical counter for per-transaction failures
|
|
555
|
+
* - `failed_transactions` is maintained for backward compatibility and should always
|
|
556
|
+
* mirror `transaction_failures` rather than represent an independent count
|
|
557
|
+
*
|
|
558
|
+
* @see src/tools/reconciliation/executor.ts:50-68 - BulkOperationDetails interface
|
|
559
|
+
*/
|
|
560
|
+
export const BulkOperationDetailsSchema = z
|
|
561
|
+
.object({
|
|
562
|
+
chunks_processed: z.number(),
|
|
563
|
+
bulk_successes: z.number(),
|
|
564
|
+
sequential_fallbacks: z.number(),
|
|
565
|
+
duplicates_detected: z.number(),
|
|
566
|
+
failed_transactions: z.number(),
|
|
567
|
+
bulk_chunk_failures: z.number(),
|
|
568
|
+
transaction_failures: z.number(),
|
|
569
|
+
sequential_attempts: z.number().optional(),
|
|
570
|
+
})
|
|
571
|
+
.refine((data) => data.failed_transactions === data.transaction_failures, {
|
|
572
|
+
message: 'failed_transactions must equal transaction_failures',
|
|
573
|
+
});
|
|
574
|
+
|
|
575
|
+
export type BulkOperationDetails = z.infer<typeof BulkOperationDetailsSchema>;
|
|
576
|
+
|
|
577
|
+
/**
|
|
578
|
+
* Execution result (present if auto-execution enabled).
|
|
579
|
+
* Documents actions taken and resulting balance changes.
|
|
580
|
+
*
|
|
581
|
+
* @remarks
|
|
582
|
+
* This schema matches the actual payload shape built by convertExecution()
|
|
583
|
+
* in reconcileAdapter.ts, which differs from the legacy ExecutionResult interface.
|
|
584
|
+
* The adapter converts the ExecutionResult to a structured payload with formatted
|
|
585
|
+
* money values and nested balance snapshots.
|
|
586
|
+
*
|
|
587
|
+
* @see src/tools/reconcileAdapter.ts:199-232 - convertExecution function
|
|
588
|
+
* @see src/tools/reconciliation/executor.ts:69-79 - ExecutionResult interface
|
|
589
|
+
*/
|
|
590
|
+
export const ExecutionResultSchema = z.object({
|
|
591
|
+
summary: ExecutionSummarySchema,
|
|
592
|
+
account_balance: z.object({
|
|
593
|
+
before: AccountSnapshotSchema,
|
|
594
|
+
after: AccountSnapshotSchema,
|
|
595
|
+
}),
|
|
596
|
+
actions_taken: z.array(ExecutionActionRecordSchema),
|
|
597
|
+
recommendations: z.array(z.string()),
|
|
598
|
+
balance_reconciliation: z.unknown().optional(),
|
|
599
|
+
bulk_operation_details: BulkOperationDetailsSchema.optional(),
|
|
600
|
+
});
|
|
601
|
+
|
|
602
|
+
export type ExecutionResult = z.infer<typeof ExecutionResultSchema>;
|
|
603
|
+
|
|
604
|
+
/**
|
|
605
|
+
* Data freshness audit metadata.
|
|
606
|
+
* Documents data sources, cache status, and staleness.
|
|
607
|
+
*
|
|
608
|
+
* @remarks
|
|
609
|
+
* This schema uses `.catchall(z.unknown())` to support forward-compatible extensions
|
|
610
|
+
* through the AdapterOptions.auditMetadata pattern. Known keys are typed explicitly,
|
|
611
|
+
* but additional fields can be passed through without validation errors.
|
|
612
|
+
*
|
|
613
|
+
* @see src/tools/reconciliation/index.ts:272-284 - Audit metadata construction
|
|
614
|
+
* @see src/tools/reconcileAdapter.ts:19-25 - AdapterOptions interface with extensible auditMetadata
|
|
615
|
+
*/
|
|
616
|
+
export const AuditMetadataSchema = z
|
|
617
|
+
.object({
|
|
618
|
+
data_freshness: z.string(),
|
|
619
|
+
data_source: z.string(),
|
|
620
|
+
server_knowledge: z.number().optional(),
|
|
621
|
+
fetched_at: z.string(),
|
|
622
|
+
accounts_count: z.number().optional(),
|
|
623
|
+
transactions_count: z.number().optional(),
|
|
624
|
+
cache_status: z.object({
|
|
625
|
+
accounts_cached: z.boolean(),
|
|
626
|
+
transactions_cached: z.boolean(),
|
|
627
|
+
delta_merge_applied: z.boolean(),
|
|
628
|
+
}),
|
|
629
|
+
})
|
|
630
|
+
.catchall(z.unknown());
|
|
631
|
+
|
|
632
|
+
export type AuditMetadata = z.infer<typeof AuditMetadataSchema>;
|
|
633
|
+
|
|
634
|
+
// ============================================================================
|
|
635
|
+
// MAIN OUTPUT SCHEMA
|
|
636
|
+
// ============================================================================
|
|
637
|
+
|
|
638
|
+
/**
|
|
639
|
+
* Complete reconciliation analysis output.
|
|
640
|
+
* Discriminated union for human-only vs human+structured response modes.
|
|
641
|
+
*
|
|
642
|
+
* @see src/tools/reconciliation/index.ts:147-362 - Main handler
|
|
643
|
+
* @see src/tools/reconcileAdapter.ts - buildReconciliationPayload function
|
|
644
|
+
*
|
|
645
|
+
* @example
|
|
646
|
+
* // Human-readable narrative only (default)
|
|
647
|
+
* {
|
|
648
|
+
* human: "Successfully matched 45 of 50 bank transactions. Current cleared balance..."
|
|
649
|
+
* }
|
|
650
|
+
*
|
|
651
|
+
* @example
|
|
652
|
+
* // With structured data (include_structured_data=true)
|
|
653
|
+
* {
|
|
654
|
+
* human: "Successfully matched 45 of 50 bank transactions...",
|
|
655
|
+
* structured: {
|
|
656
|
+
* success: true,
|
|
657
|
+
* phase: "analysis",
|
|
658
|
+
* summary: {
|
|
659
|
+
* statement_date_range: "2025-10-01 to 2025-10-31",
|
|
660
|
+
* bank_transactions_count: 50,
|
|
661
|
+
* auto_matched: 45,
|
|
662
|
+
* suggested_matches: 3,
|
|
663
|
+
* unmatched_bank: 2,
|
|
664
|
+
* discrepancy: { amount: -25.50, currency: "USD", formatted: "-$25.50" }
|
|
665
|
+
* },
|
|
666
|
+
* auto_matches: [...],
|
|
667
|
+
* suggested_matches: [...],
|
|
668
|
+
* unmatched_bank: [...],
|
|
669
|
+
* unmatched_ynab: [...],
|
|
670
|
+
* balance_info: {
|
|
671
|
+
* current_cleared: { amount: 1250.00, currency: "USD", formatted: "$1,250.00" },
|
|
672
|
+
* target_statement: { amount: 1275.50, currency: "USD", formatted: "$1,275.50" },
|
|
673
|
+
* on_track: false
|
|
674
|
+
* },
|
|
675
|
+
* insights: [
|
|
676
|
+
* { id: "ins-1", type: "repeat_amount", severity: "warning", title: "Duplicate amount detected" }
|
|
677
|
+
* ],
|
|
678
|
+
* recommendations: [
|
|
679
|
+
* {
|
|
680
|
+
* id: "rec-1",
|
|
681
|
+
* action_type: "create_transaction",
|
|
682
|
+
* priority: "high",
|
|
683
|
+
* confidence: 0.95,
|
|
684
|
+
* message: "Create missing transaction for bank entry",
|
|
685
|
+
* parameters: { account_id: "acct-1", date: "2025-10-15", amount: -25500, ... }
|
|
686
|
+
* }
|
|
687
|
+
* ],
|
|
688
|
+
* execution_result: {
|
|
689
|
+
* executed: true,
|
|
690
|
+
* actions_taken: [{ action_type: "create_transaction", status: "success", transaction_id: "txn-new" }],
|
|
691
|
+
* reconciliation_complete: false,
|
|
692
|
+
* remaining_discrepancy: { amount: 0, currency: "USD", formatted: "$0.00" }
|
|
693
|
+
* },
|
|
694
|
+
* audit_metadata: {
|
|
695
|
+
* data_freshness: "real-time",
|
|
696
|
+
* data_source: "YNAB API + CSV",
|
|
697
|
+
* fetched_at: "2025-11-18T10:30:00Z",
|
|
698
|
+
* cache_status: { accounts_cached: false, transactions_cached: false }
|
|
699
|
+
* }
|
|
700
|
+
* }
|
|
701
|
+
* }
|
|
702
|
+
*/
|
|
703
|
+
/**
|
|
704
|
+
* CSV format configuration metadata.
|
|
705
|
+
* Documents the CSV format detected or specified by the user.
|
|
706
|
+
*
|
|
707
|
+
* @see src/tools/reconciliation/index.ts:364-402 - mapCsvFormatForPayload function
|
|
708
|
+
*/
|
|
709
|
+
export const CsvFormatMetadataSchema = z.object({
|
|
710
|
+
delimiter: z.string(),
|
|
711
|
+
decimal_separator: z.string(),
|
|
712
|
+
thousands_separator: z.string().nullable(),
|
|
713
|
+
date_format: z.string(),
|
|
714
|
+
header_row: z.boolean(),
|
|
715
|
+
date_column: z.string().nullable(),
|
|
716
|
+
amount_column: z.string().nullable(),
|
|
717
|
+
payee_column: z.string().nullable(),
|
|
718
|
+
});
|
|
719
|
+
|
|
720
|
+
export type CsvFormatMetadata = z.infer<typeof CsvFormatMetadataSchema>;
|
|
721
|
+
|
|
722
|
+
// Define the structured data schema without refinement first
|
|
723
|
+
const StructuredReconciliationDataBaseSchema = z.object({
|
|
724
|
+
version: z.string(),
|
|
725
|
+
schema_url: z.string(),
|
|
726
|
+
generated_at: z.string(),
|
|
727
|
+
account: z.object({
|
|
728
|
+
id: z.string().optional(),
|
|
729
|
+
name: z.string().optional(),
|
|
730
|
+
}),
|
|
731
|
+
summary: ReconciliationSummarySchema,
|
|
732
|
+
balance: BalanceInfoSchema.extend({
|
|
733
|
+
discrepancy_direction: z.enum(['balanced', 'ynab_higher', 'bank_higher']),
|
|
734
|
+
}),
|
|
735
|
+
insights: z.array(ReconciliationInsightSchema),
|
|
736
|
+
next_steps: z.array(z.string()),
|
|
737
|
+
matches: z.object({
|
|
738
|
+
auto: z.array(TransactionMatchSchema),
|
|
739
|
+
suggested: z.array(TransactionMatchSchema),
|
|
740
|
+
}),
|
|
741
|
+
unmatched: z.object({
|
|
742
|
+
bank: z.array(BankTransactionSchema),
|
|
743
|
+
ynab: z.array(YNABTransactionSimpleSchema),
|
|
744
|
+
}),
|
|
745
|
+
recommendations: z.array(ActionableRecommendationSchema).optional(),
|
|
746
|
+
csv_format: CsvFormatMetadataSchema.optional(),
|
|
747
|
+
execution: ExecutionResultSchema.optional(),
|
|
748
|
+
audit: AuditMetadataSchema.optional(),
|
|
749
|
+
});
|
|
750
|
+
|
|
751
|
+
export const ReconcileAccountOutputSchema = z
|
|
752
|
+
.union([
|
|
753
|
+
// Human + structured data (when include_structured_data=true) - check this FIRST
|
|
754
|
+
z.object({
|
|
755
|
+
human: z.string(),
|
|
756
|
+
structured: StructuredReconciliationDataBaseSchema,
|
|
757
|
+
}),
|
|
758
|
+
// Human narrative only (default mode) - check this SECOND
|
|
759
|
+
z.object({
|
|
760
|
+
human: z.string(),
|
|
761
|
+
}),
|
|
762
|
+
])
|
|
763
|
+
.refine(
|
|
764
|
+
(data) => {
|
|
765
|
+
// Only validate if this is the structured variant (has 'structured' property)
|
|
766
|
+
if ('structured' in data && data.structured) {
|
|
767
|
+
const discrepancyAmount = data.structured.balance.discrepancy.amount;
|
|
768
|
+
const direction = data.structured.balance.discrepancy_direction;
|
|
769
|
+
|
|
770
|
+
// If absolute discrepancy < 0.01, direction must be 'balanced'
|
|
771
|
+
if (Math.abs(discrepancyAmount) < 0.01) {
|
|
772
|
+
return direction === 'balanced';
|
|
773
|
+
}
|
|
774
|
+
|
|
775
|
+
// If discrepancy > 0, direction must be 'ynab_higher'
|
|
776
|
+
if (discrepancyAmount > 0) {
|
|
777
|
+
return direction === 'ynab_higher';
|
|
778
|
+
}
|
|
779
|
+
|
|
780
|
+
// If discrepancy < 0, direction must be 'bank_higher'
|
|
781
|
+
if (discrepancyAmount < 0) {
|
|
782
|
+
return direction === 'bank_higher';
|
|
783
|
+
}
|
|
784
|
+
}
|
|
785
|
+
|
|
786
|
+
// Human-only variant always passes validation
|
|
787
|
+
return true;
|
|
788
|
+
},
|
|
789
|
+
{
|
|
790
|
+
message:
|
|
791
|
+
'Discrepancy direction mismatch: direction must match the numeric discrepancy amount',
|
|
792
|
+
path: ['balance', 'discrepancy_direction'],
|
|
793
|
+
},
|
|
794
|
+
);
|
|
795
|
+
|
|
796
|
+
export type ReconcileAccountOutput = z.infer<typeof ReconcileAccountOutputSchema>;
|