@resolveio/server-lib 22.3.126 → 22.3.127

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.
Files changed (735) hide show
  1. package/ai/assistant-core-heuristics.d.ts +11 -0
  2. package/ai/assistant-core-heuristics.js +356 -0
  3. package/ai/assistant-core-heuristics.js.map +1 -0
  4. package/ai/resolveio-platform-intelligence-memory-corpus.d.ts +3 -0
  5. package/ai/resolveio-platform-intelligence-memory-corpus.js +214 -0
  6. package/ai/resolveio-platform-intelligence-memory-corpus.js.map +1 -0
  7. package/ai/resolveio-platform-intelligence-memory.d.ts +20 -0
  8. package/ai/resolveio-platform-intelligence-memory.js +341 -0
  9. package/ai/resolveio-platform-intelligence-memory.js.map +1 -0
  10. package/{src/ai/resolveio-platform-intelligence-types.ts → ai/resolveio-platform-intelligence-types.d.ts} +15 -20
  11. package/ai/resolveio-platform-intelligence-types.js +4 -0
  12. package/ai/resolveio-platform-intelligence-types.js.map +1 -0
  13. package/ai/resolveio-platform-intelligence.d.ts +6 -0
  14. package/ai/resolveio-platform-intelligence.js +463 -0
  15. package/ai/resolveio-platform-intelligence.js.map +1 -0
  16. package/client-server-app.d.ts +1 -0
  17. package/client-server-app.js +68 -0
  18. package/client-server-app.js.map +1 -0
  19. package/collections/ai-run.collection.d.ts +3 -0
  20. package/collections/ai-run.collection.js +170 -0
  21. package/collections/ai-run.collection.js.map +1 -0
  22. package/collections/ai-terminal-conversation.collection.d.ts +2 -0
  23. package/collections/ai-terminal-conversation.collection.js +140 -0
  24. package/collections/ai-terminal-conversation.collection.js.map +1 -0
  25. package/collections/ai-terminal-issue-report.collection.d.ts +2 -0
  26. package/collections/ai-terminal-issue-report.collection.js +148 -0
  27. package/collections/ai-terminal-issue-report.collection.js.map +1 -0
  28. package/collections/ai-terminal-message.collection.d.ts +2 -0
  29. package/collections/ai-terminal-message.collection.js +121 -0
  30. package/collections/ai-terminal-message.collection.js.map +1 -0
  31. package/collections/app-setting.collection.d.ts +3 -0
  32. package/collections/app-setting.collection.js +103 -0
  33. package/collections/app-setting.collection.js.map +1 -0
  34. package/collections/app-status.collection.d.ts +3 -0
  35. package/collections/app-status.collection.js +57 -0
  36. package/collections/app-status.collection.js.map +1 -0
  37. package/collections/communication-metric.collection.d.ts +2 -0
  38. package/collections/communication-metric.collection.js +133 -0
  39. package/collections/communication-metric.collection.js.map +1 -0
  40. package/collections/counter.collection.d.ts +3 -0
  41. package/collections/counter.collection.js +56 -0
  42. package/collections/counter.collection.js.map +1 -0
  43. package/collections/cron-job-history.collection.d.ts +3 -0
  44. package/collections/cron-job-history.collection.js +137 -0
  45. package/collections/cron-job-history.collection.js.map +1 -0
  46. package/collections/cron-job.collection.d.ts +3 -0
  47. package/collections/cron-job.collection.js +92 -0
  48. package/collections/cron-job.collection.js.map +1 -0
  49. package/collections/customer-notification.collection.d.ts +3 -0
  50. package/collections/customer-notification.collection.js +130 -0
  51. package/collections/customer-notification.collection.js.map +1 -0
  52. package/collections/customer-portal-password.collection.d.ts +3 -0
  53. package/collections/customer-portal-password.collection.js +75 -0
  54. package/collections/customer-portal-password.collection.js.map +1 -0
  55. package/collections/email-history.collection.d.ts +3 -0
  56. package/collections/email-history.collection.js +134 -0
  57. package/collections/email-history.collection.js.map +1 -0
  58. package/collections/email-verified.collection.d.ts +3 -0
  59. package/collections/email-verified.collection.js +62 -0
  60. package/collections/email-verified.collection.js.map +1 -0
  61. package/collections/file.collection.d.ts +3 -0
  62. package/collections/file.collection.js +74 -0
  63. package/collections/file.collection.js.map +1 -0
  64. package/collections/flag-update.collection.d.ts +3 -0
  65. package/collections/flag-update.collection.js +57 -0
  66. package/collections/flag-update.collection.js.map +1 -0
  67. package/collections/flag.collection.d.ts +3 -0
  68. package/collections/flag.collection.js +57 -0
  69. package/collections/flag.collection.js.map +1 -0
  70. package/collections/log-method-latency.collection.d.ts +3 -0
  71. package/collections/log-method-latency.collection.js +77 -0
  72. package/collections/log-method-latency.collection.js.map +1 -0
  73. package/collections/log-subscription.collection.d.ts +3 -0
  74. package/collections/log-subscription.collection.js +80 -0
  75. package/collections/log-subscription.collection.js.map +1 -0
  76. package/collections/log.collection.d.ts +3 -0
  77. package/collections/log.collection.js +93 -0
  78. package/collections/log.collection.js.map +1 -0
  79. package/collections/logged-in-users.collection.d.ts +3 -0
  80. package/collections/logged-in-users.collection.js +67 -0
  81. package/collections/logged-in-users.collection.js.map +1 -0
  82. package/collections/monitor-cpu.collection.d.ts +3 -0
  83. package/collections/monitor-cpu.collection.js +65 -0
  84. package/collections/monitor-cpu.collection.js.map +1 -0
  85. package/collections/monitor-function.collection.d.ts +3 -0
  86. package/collections/monitor-function.collection.js +74 -0
  87. package/collections/monitor-function.collection.js.map +1 -0
  88. package/collections/monitor-memory.collection.d.ts +3 -0
  89. package/collections/monitor-memory.collection.js +77 -0
  90. package/collections/monitor-memory.collection.js.map +1 -0
  91. package/collections/monitor-mongo.collection.d.ts +3 -0
  92. package/collections/monitor-mongo.collection.js +71 -0
  93. package/collections/monitor-mongo.collection.js.map +1 -0
  94. package/collections/notification.collection.d.ts +3 -0
  95. package/collections/notification.collection.js +57 -0
  96. package/collections/notification.collection.js.map +1 -0
  97. package/collections/openai-usage-ledger.collection.d.ts +2 -0
  98. package/collections/openai-usage-ledger.collection.js +124 -0
  99. package/collections/openai-usage-ledger.collection.js.map +1 -0
  100. package/collections/report-builder-dashboard-builder.collection.d.ts +3 -0
  101. package/collections/report-builder-dashboard-builder.collection.js +109 -0
  102. package/collections/report-builder-dashboard-builder.collection.js.map +1 -0
  103. package/collections/report-builder-library.collection.d.ts +3 -0
  104. package/collections/report-builder-library.collection.js +87 -0
  105. package/collections/report-builder-library.collection.js.map +1 -0
  106. package/collections/report-builder-report.collection.d.ts +4 -0
  107. package/collections/report-builder-report.collection.js +184 -0
  108. package/collections/report-builder-report.collection.js.map +1 -0
  109. package/collections/user-group.collection.d.ts +4 -0
  110. package/collections/user-group.collection.js +89 -0
  111. package/collections/user-group.collection.js.map +1 -0
  112. package/collections/user-guide.collection.d.ts +3 -0
  113. package/collections/user-guide.collection.js +57 -0
  114. package/collections/user-guide.collection.js.map +1 -0
  115. package/collections/user.collection.d.ts +4 -0
  116. package/collections/user.collection.js +180 -0
  117. package/collections/user.collection.js.map +1 -0
  118. package/cron/cron.d.ts +14 -0
  119. package/cron/cron.js +216 -0
  120. package/cron/cron.js.map +1 -0
  121. package/fixtures/cron-jobs.d.ts +1 -0
  122. package/fixtures/cron-jobs.js +150 -0
  123. package/fixtures/cron-jobs.js.map +1 -0
  124. package/fixtures/init.d.ts +1 -0
  125. package/fixtures/init.js +91 -0
  126. package/fixtures/init.js.map +1 -0
  127. package/http/auth.d.ts +2 -0
  128. package/http/auth.js +951 -0
  129. package/http/auth.js.map +1 -0
  130. package/http/health.d.ts +1 -0
  131. package/http/health.js +11 -0
  132. package/http/health.js.map +1 -0
  133. package/http/home.d.ts +1 -0
  134. package/http/home.js +134 -0
  135. package/http/home.js.map +1 -0
  136. package/http/slow-query-publication.d.ts +2 -0
  137. package/http/slow-query-publication.js +99 -0
  138. package/http/slow-query-publication.js.map +1 -0
  139. package/index.d.ts +1 -0
  140. package/index.js +19 -0
  141. package/index.js.map +1 -0
  142. package/managers/ai-assistant-codex-manager.manager.d.ts +67 -0
  143. package/managers/ai-assistant-codex-manager.manager.js +1113 -0
  144. package/managers/ai-assistant-codex-manager.manager.js.map +1 -0
  145. package/managers/ai-run-evidence.manager.d.ts +36 -0
  146. package/managers/ai-run-evidence.manager.js +377 -0
  147. package/managers/ai-run-evidence.manager.js.map +1 -0
  148. package/managers/communication-metric.manager.d.ts +16 -0
  149. package/managers/communication-metric.manager.js +134 -0
  150. package/managers/communication-metric.manager.js.map +1 -0
  151. package/managers/cron.manager.d.ts +20 -0
  152. package/managers/cron.manager.js +534 -0
  153. package/managers/cron.manager.js.map +1 -0
  154. package/managers/customer-notification-content.manager.d.ts +55 -0
  155. package/managers/customer-notification-content.manager.js +158 -0
  156. package/managers/customer-notification-content.manager.js.map +1 -0
  157. package/managers/diagnostic-manager-bootstrap.d.ts +9 -0
  158. package/managers/diagnostic-manager-bootstrap.js +260 -0
  159. package/managers/diagnostic-manager-bootstrap.js.map +1 -0
  160. package/managers/error-auto-fix.manager.d.ts +149 -0
  161. package/managers/error-auto-fix.manager.js +3064 -0
  162. package/managers/error-auto-fix.manager.js.map +1 -0
  163. package/managers/local-log.manager.d.ts +18 -0
  164. package/managers/local-log.manager.js +88 -0
  165. package/managers/local-log.manager.js.map +1 -0
  166. package/managers/method.manager.d.ts +84 -0
  167. package/managers/method.manager.js +1964 -0
  168. package/managers/method.manager.js.map +1 -0
  169. package/managers/mongo.manager.d.ts +224 -0
  170. package/managers/mongo.manager.js +5000 -0
  171. package/managers/mongo.manager.js.map +1 -0
  172. package/managers/monitor.manager.d.ts +70 -0
  173. package/managers/monitor.manager.js +550 -0
  174. package/managers/monitor.manager.js.map +1 -0
  175. package/managers/openai-usage-ledger.manager.d.ts +16 -0
  176. package/managers/openai-usage-ledger.manager.js +93 -0
  177. package/managers/openai-usage-ledger.manager.js.map +1 -0
  178. package/managers/slow-query-verifier.manager.d.ts +144 -0
  179. package/managers/slow-query-verifier.manager.js +3857 -0
  180. package/managers/slow-query-verifier.manager.js.map +1 -0
  181. package/managers/slow-query.manager.d.ts +28 -0
  182. package/managers/slow-query.manager.js +468 -0
  183. package/managers/slow-query.manager.js.map +1 -0
  184. package/managers/subscription.manager.d.ts +169 -0
  185. package/managers/subscription.manager.js +3434 -0
  186. package/managers/subscription.manager.js.map +1 -0
  187. package/managers/websocket.manager.d.ts +73 -0
  188. package/managers/websocket.manager.js +673 -0
  189. package/managers/websocket.manager.js.map +1 -0
  190. package/managers/worker-dispatcher.manager.d.ts +120 -0
  191. package/managers/worker-dispatcher.manager.js +1266 -0
  192. package/managers/worker-dispatcher.manager.js.map +1 -0
  193. package/managers/worker-server.manager.d.ts +35 -0
  194. package/managers/worker-server.manager.js +582 -0
  195. package/managers/worker-server.manager.js.map +1 -0
  196. package/methods/accounts.d.ts +2 -0
  197. package/methods/accounts.js +624 -0
  198. package/methods/accounts.js.map +1 -0
  199. package/methods/ai-terminal.d.ts +337 -0
  200. package/methods/ai-terminal.js +23166 -0
  201. package/methods/ai-terminal.js.map +1 -0
  202. package/methods/app-settings.d.ts +2 -0
  203. package/methods/app-settings.js +169 -0
  204. package/methods/app-settings.js.map +1 -0
  205. package/methods/aws.d.ts +2 -0
  206. package/methods/aws.js +877 -0
  207. package/methods/aws.js.map +1 -0
  208. package/methods/collections.d.ts +2 -0
  209. package/methods/collections.js +719 -0
  210. package/methods/collections.js.map +1 -0
  211. package/methods/counters.d.ts +2 -0
  212. package/methods/counters.js +113 -0
  213. package/methods/counters.js.map +1 -0
  214. package/methods/cron-jobs.d.ts +2 -0
  215. package/methods/cron-jobs.js +2475 -0
  216. package/methods/cron-jobs.js.map +1 -0
  217. package/methods/customer-notifications.d.ts +2 -0
  218. package/methods/customer-notifications.js +528 -0
  219. package/methods/customer-notifications.js.map +1 -0
  220. package/methods/diagnostics.d.ts +2 -0
  221. package/methods/diagnostics.js +703 -0
  222. package/methods/diagnostics.js.map +1 -0
  223. package/methods/flag-updates.d.ts +2 -0
  224. package/methods/flag-updates.js +8 -0
  225. package/methods/flag-updates.js.map +1 -0
  226. package/methods/flags.d.ts +2 -0
  227. package/methods/flags.js +8 -0
  228. package/methods/flags.js.map +1 -0
  229. package/methods/logs.d.ts +2 -0
  230. package/methods/logs.js +751 -0
  231. package/methods/logs.js.map +1 -0
  232. package/methods/mongo-explorer.d.ts +2 -0
  233. package/methods/mongo-explorer.js +1808 -0
  234. package/methods/mongo-explorer.js.map +1 -0
  235. package/methods/monitor.d.ts +2 -0
  236. package/methods/monitor.js +543 -0
  237. package/methods/monitor.js.map +1 -0
  238. package/methods/pdf.d.ts +2 -0
  239. package/methods/pdf.js +1216 -0
  240. package/methods/pdf.js.map +1 -0
  241. package/methods/publications.d.ts +1 -0
  242. package/methods/publications.js +183 -0
  243. package/methods/publications.js.map +1 -0
  244. package/methods/report-builder.d.ts +2 -0
  245. package/methods/report-builder.js +3094 -0
  246. package/methods/report-builder.js.map +1 -0
  247. package/methods/support.d.ts +2 -0
  248. package/methods/support.js +430 -0
  249. package/methods/support.js.map +1 -0
  250. package/models/ai-run.model.d.ts +19 -0
  251. package/models/ai-run.model.js +4 -0
  252. package/models/ai-run.model.js.map +1 -0
  253. package/models/ai-terminal-conversation.model.d.ts +17 -0
  254. package/models/ai-terminal-conversation.model.js +4 -0
  255. package/models/ai-terminal-conversation.model.js.map +1 -0
  256. package/models/ai-terminal-issue-report.model.d.ts +19 -0
  257. package/models/ai-terminal-issue-report.model.js +4 -0
  258. package/models/ai-terminal-issue-report.model.js.map +1 -0
  259. package/models/ai-terminal-message.model.d.ts +22 -0
  260. package/models/ai-terminal-message.model.js +4 -0
  261. package/models/ai-terminal-message.model.js.map +1 -0
  262. package/models/app-setting.model.d.ts +16 -0
  263. package/models/app-setting.model.js +4 -0
  264. package/models/app-setting.model.js.map +1 -0
  265. package/{src/models/app-status.model.ts → models/app-status.model.d.ts} +2 -3
  266. package/models/app-status.model.js +4 -0
  267. package/models/app-status.model.js.map +1 -0
  268. package/{src/models/billing-logged-in-users.model.ts → models/billing-logged-in-users.model.d.ts} +4 -5
  269. package/models/billing-logged-in-users.model.js +4 -0
  270. package/models/billing-logged-in-users.model.js.map +1 -0
  271. package/models/collection-document.model.d.ts +21 -0
  272. package/models/collection-document.model.js +4 -0
  273. package/models/collection-document.model.js.map +1 -0
  274. package/models/communication-metric.model.d.ts +20 -0
  275. package/models/communication-metric.model.js +4 -0
  276. package/models/communication-metric.model.js.map +1 -0
  277. package/{src/models/counter.model.ts → models/counter.model.d.ts} +3 -4
  278. package/models/counter.model.js +4 -0
  279. package/models/counter.model.js.map +1 -0
  280. package/models/cron-job-history.model.d.ts +15 -0
  281. package/models/cron-job-history.model.js +4 -0
  282. package/models/cron-job-history.model.js.map +1 -0
  283. package/models/cron-job.model.d.ts +14 -0
  284. package/models/cron-job.model.js +4 -0
  285. package/models/cron-job.model.js.map +1 -0
  286. package/models/customer-notification.model.d.ts +26 -0
  287. package/models/customer-notification.model.js +4 -0
  288. package/models/customer-notification.model.js.map +1 -0
  289. package/models/customer-portal-password.model.d.ts +11 -0
  290. package/models/customer-portal-password.model.js +4 -0
  291. package/models/customer-portal-password.model.js.map +1 -0
  292. package/models/dialog.model.d.ts +23 -0
  293. package/models/dialog.model.js +4 -0
  294. package/models/dialog.model.js.map +1 -0
  295. package/models/email-history.model.d.ts +32 -0
  296. package/{src/models/email-history.model.ts → models/email-history.model.js} +4 -36
  297. package/models/email-history.model.js.map +1 -0
  298. package/{src/models/email-verified.model.ts → models/email-verified.model.d.ts} +5 -6
  299. package/models/email-verified.model.js +4 -0
  300. package/models/email-verified.model.js.map +1 -0
  301. package/{src/models/file.model.ts → models/file.model.d.ts} +7 -8
  302. package/models/file.model.js +4 -0
  303. package/models/file.model.js.map +1 -0
  304. package/{src/models/flag-update.model.ts → models/flag-update.model.d.ts} +3 -4
  305. package/models/flag-update.model.js +4 -0
  306. package/models/flag-update.model.js.map +1 -0
  307. package/{src/models/flag.model.ts → models/flag.model.d.ts} +3 -4
  308. package/models/flag.model.js +4 -0
  309. package/models/flag.model.js.map +1 -0
  310. package/models/log-method-latency.model.d.ts +10 -0
  311. package/models/log-method-latency.model.js +4 -0
  312. package/models/log-method-latency.model.js.map +1 -0
  313. package/{src/models/log-subscription.model.ts → models/log-subscription.model.d.ts} +9 -11
  314. package/models/log-subscription.model.js +4 -0
  315. package/models/log-subscription.model.js.map +1 -0
  316. package/models/log.model.d.ts +17 -0
  317. package/models/log.model.js +4 -0
  318. package/models/log.model.js.map +1 -0
  319. package/{src/models/logged-in-users.model.ts → models/logged-in-users.model.d.ts} +5 -6
  320. package/models/logged-in-users.model.js +4 -0
  321. package/models/logged-in-users.model.js.map +1 -0
  322. package/{src/models/method-response.model.ts → models/method-response.model.d.ts} +6 -7
  323. package/models/method-response.model.js +4 -0
  324. package/models/method-response.model.js.map +1 -0
  325. package/models/method.model.d.ts +26 -0
  326. package/models/method.model.js +4 -0
  327. package/models/method.model.js.map +1 -0
  328. package/{src/models/monitor-cpu.model.ts → models/monitor-cpu.model.d.ts} +7 -9
  329. package/models/monitor-cpu.model.js +4 -0
  330. package/models/monitor-cpu.model.js.map +1 -0
  331. package/models/monitor-function.model.d.ts +14 -0
  332. package/models/monitor-function.model.js +4 -0
  333. package/models/monitor-function.model.js.map +1 -0
  334. package/models/monitor-memory.model.d.ts +15 -0
  335. package/models/monitor-memory.model.js +4 -0
  336. package/models/monitor-memory.model.js.map +1 -0
  337. package/models/monitor-mongo.model.d.ts +13 -0
  338. package/models/monitor-mongo.model.js +4 -0
  339. package/models/monitor-mongo.model.js.map +1 -0
  340. package/{src/models/notification.model.ts → models/notification.model.d.ts} +4 -6
  341. package/models/notification.model.js +4 -0
  342. package/models/notification.model.js.map +1 -0
  343. package/models/openai-usage-ledger.model.d.ts +15 -0
  344. package/models/openai-usage-ledger.model.js +4 -0
  345. package/models/openai-usage-ledger.model.js.map +1 -0
  346. package/models/pagination.model.d.ts +11 -0
  347. package/models/pagination.model.js +28 -0
  348. package/models/pagination.model.js.map +1 -0
  349. package/models/permission.model.d.ts +12 -0
  350. package/models/permission.model.js +4 -0
  351. package/models/permission.model.js.map +1 -0
  352. package/models/report-builder-dashboard-builder.model.d.ts +25 -0
  353. package/models/report-builder-dashboard-builder.model.js +4 -0
  354. package/models/report-builder-dashboard-builder.model.js.map +1 -0
  355. package/models/report-builder-library.model.d.ts +17 -0
  356. package/models/report-builder-library.model.js +4 -0
  357. package/models/report-builder-library.model.js.map +1 -0
  358. package/models/report-builder-report.model.d.ts +121 -0
  359. package/models/report-builder-report.model.js +4 -0
  360. package/models/report-builder-report.model.js.map +1 -0
  361. package/models/report-builder.model.d.ts +61 -0
  362. package/models/report-builder.model.js +4 -0
  363. package/models/report-builder.model.js.map +1 -0
  364. package/models/select-data-label.model.d.ts +9 -0
  365. package/models/select-data-label.model.js +4 -0
  366. package/models/select-data-label.model.js.map +1 -0
  367. package/models/server-message.model.d.ts +32 -0
  368. package/models/server-message.model.js +4 -0
  369. package/models/server-message.model.js.map +1 -0
  370. package/models/slow-query-report.model.d.ts +23 -0
  371. package/models/slow-query-report.model.js +4 -0
  372. package/models/slow-query-report.model.js.map +1 -0
  373. package/models/subscription.model.d.ts +31 -0
  374. package/models/subscription.model.js +4 -0
  375. package/models/subscription.model.js.map +1 -0
  376. package/models/support-ticket.model.d.ts +87 -0
  377. package/models/support-ticket.model.js +4 -0
  378. package/models/support-ticket.model.js.map +1 -0
  379. package/models/user-group.model.d.ts +20 -0
  380. package/models/user-group.model.js +4 -0
  381. package/models/user-group.model.js.map +1 -0
  382. package/{src/models/user-guide.model.ts → models/user-guide.model.d.ts} +4 -5
  383. package/models/user-guide.model.js +4 -0
  384. package/models/user-guide.model.js.map +1 -0
  385. package/models/user.model.d.ts +84 -0
  386. package/models/user.model.js +4 -0
  387. package/models/user.model.js.map +1 -0
  388. package/package.json +1 -1
  389. package/private/images/ResolveIO.png +0 -0
  390. package/public_api.js +125 -0
  391. package/public_api.js.map +1 -0
  392. package/publications/ai-terminal.d.ts +1 -0
  393. package/publications/ai-terminal.js +122 -0
  394. package/publications/ai-terminal.js.map +1 -0
  395. package/publications/app-settings.d.ts +2 -0
  396. package/publications/app-settings.js +28 -0
  397. package/publications/app-settings.js.map +1 -0
  398. package/publications/app-status.d.ts +2 -0
  399. package/publications/app-status.js +16 -0
  400. package/publications/app-status.js.map +1 -0
  401. package/publications/cron-jobs.d.ts +2 -0
  402. package/publications/cron-jobs.js +88 -0
  403. package/publications/cron-jobs.js.map +1 -0
  404. package/publications/customer-notifications.d.ts +2 -0
  405. package/publications/customer-notifications.js +161 -0
  406. package/publications/customer-notifications.js.map +1 -0
  407. package/publications/files.d.ts +2 -0
  408. package/publications/files.js +36 -0
  409. package/publications/files.js.map +1 -0
  410. package/publications/flags-update.d.ts +2 -0
  411. package/publications/flags-update.js +22 -0
  412. package/publications/flags-update.js.map +1 -0
  413. package/publications/flags.d.ts +2 -0
  414. package/publications/flags.js +22 -0
  415. package/publications/flags.js.map +1 -0
  416. package/publications/logs.d.ts +2 -0
  417. package/publications/logs.js +164 -0
  418. package/publications/logs.js.map +1 -0
  419. package/publications/notifications.d.ts +2 -0
  420. package/publications/notifications.js +16 -0
  421. package/publications/notifications.js.map +1 -0
  422. package/publications/report-builder-dashboard-builders.d.ts +2 -0
  423. package/publications/report-builder-dashboard-builders.js +42 -0
  424. package/publications/report-builder-dashboard-builders.js.map +1 -0
  425. package/publications/report-builder-libraries.d.ts +2 -0
  426. package/publications/report-builder-libraries.js +90 -0
  427. package/publications/report-builder-libraries.js.map +1 -0
  428. package/publications/report-builder-reports.d.ts +2 -0
  429. package/publications/report-builder-reports.js +50 -0
  430. package/publications/report-builder-reports.js.map +1 -0
  431. package/publications/super-admin.d.ts +2 -0
  432. package/publications/super-admin.js +16 -0
  433. package/publications/super-admin.js.map +1 -0
  434. package/publications/user-groups.d.ts +1 -0
  435. package/publications/user-groups.js +16 -0
  436. package/publications/user-groups.js.map +1 -0
  437. package/publications/user-guides.d.ts +1 -0
  438. package/publications/user-guides.js +16 -0
  439. package/publications/user-guides.js.map +1 -0
  440. package/resolveio-server-app.d.ts +70 -0
  441. package/resolveio-server-app.js +801 -0
  442. package/resolveio-server-app.js.map +1 -0
  443. package/server-app.d.ts +228 -0
  444. package/server-app.js +3566 -0
  445. package/server-app.js.map +1 -0
  446. package/services/codex-client.d.ts +128 -0
  447. package/services/codex-client.js +1629 -0
  448. package/services/codex-client.js.map +1 -0
  449. package/services/openai-client.d.ts +46 -0
  450. package/services/openai-client.js +318 -0
  451. package/services/openai-client.js.map +1 -0
  452. package/types/error-report.d.ts +25 -0
  453. package/types/error-report.js +4 -0
  454. package/types/error-report.js.map +1 -0
  455. package/types/slow-query-report.d.ts +27 -0
  456. package/types/slow-query-report.js +6 -0
  457. package/types/slow-query-report.js.map +1 -0
  458. package/util/ai-qa-policy.d.ts +124 -0
  459. package/util/ai-qa-policy.js +736 -0
  460. package/util/ai-qa-policy.js.map +1 -0
  461. package/util/ai-run-evidence-adapters.d.ts +33 -0
  462. package/util/ai-run-evidence-adapters.js +660 -0
  463. package/util/ai-run-evidence-adapters.js.map +1 -0
  464. package/util/ai-run-evidence-dashboard.d.ts +67 -0
  465. package/util/ai-run-evidence-dashboard.js +309 -0
  466. package/util/ai-run-evidence-dashboard.js.map +1 -0
  467. package/util/ai-run-evidence-eval.d.ts +86 -0
  468. package/util/ai-run-evidence-eval.js +854 -0
  469. package/util/ai-run-evidence-eval.js.map +1 -0
  470. package/util/ai-run-evidence.d.ts +212 -0
  471. package/util/ai-run-evidence.js +649 -0
  472. package/util/ai-run-evidence.js.map +1 -0
  473. package/util/ai-runner-artifacts.d.ts +82 -0
  474. package/util/ai-runner-artifacts.js +713 -0
  475. package/util/ai-runner-artifacts.js.map +1 -0
  476. package/util/ai-runner-qa-auth.d.ts +5 -0
  477. package/util/ai-runner-qa-auth.js +822 -0
  478. package/util/ai-runner-qa-auth.js.map +1 -0
  479. package/util/ai-runner-qa-tools.d.ts +26 -0
  480. package/util/ai-runner-qa-tools.js +3029 -0
  481. package/util/ai-runner-qa-tools.js.map +1 -0
  482. package/util/aicoder-runner-v6.d.ts +168 -0
  483. package/util/aicoder-runner-v6.js +347 -0
  484. package/util/aicoder-runner-v6.js.map +1 -0
  485. package/util/common.d.ts +31 -0
  486. package/util/common.js +683 -0
  487. package/util/common.js.map +1 -0
  488. package/util/customer-portal-password.d.ts +13 -0
  489. package/util/customer-portal-password.js +209 -0
  490. package/util/customer-portal-password.js.map +1 -0
  491. package/util/error-reporter.d.ts +52 -0
  492. package/util/error-reporter.js +326 -0
  493. package/util/error-reporter.js.map +1 -0
  494. package/util/error-tracking.d.ts +13 -0
  495. package/util/error-tracking.js +120 -0
  496. package/util/error-tracking.js.map +1 -0
  497. package/util/openai-usage-cost.d.ts +6 -0
  498. package/util/openai-usage-cost.js +103 -0
  499. package/util/openai-usage-cost.js.map +1 -0
  500. package/util/report-builder-unwinds.d.ts +15 -0
  501. package/util/report-builder-unwinds.js +156 -0
  502. package/util/report-builder-unwinds.js.map +1 -0
  503. package/util/runner-process-janitor.d.ts +27 -0
  504. package/util/runner-process-janitor.js +208 -0
  505. package/util/runner-process-janitor.js.map +1 -0
  506. package/util/schema-report-builder.d.ts +6 -0
  507. package/util/schema-report-builder.js +481 -0
  508. package/util/schema-report-builder.js.map +1 -0
  509. package/util/slow-query-reporter.d.ts +28 -0
  510. package/util/slow-query-reporter.js +226 -0
  511. package/util/slow-query-reporter.js.map +1 -0
  512. package/util/subscription-dependency-context.d.ts +34 -0
  513. package/util/subscription-dependency-context.js +1283 -0
  514. package/util/subscription-dependency-context.js.map +1 -0
  515. package/util/support-runner-v5.d.ts +250 -0
  516. package/util/support-runner-v5.js +634 -0
  517. package/util/support-runner-v5.js.map +1 -0
  518. package/util/tokenizer.d.ts +5 -0
  519. package/util/tokenizer.js +41 -0
  520. package/util/tokenizer.js.map +1 -0
  521. package/workers/codex-runner.worker.d.ts +1 -0
  522. package/workers/codex-runner.worker.js +192 -0
  523. package/workers/codex-runner.worker.js.map +1 -0
  524. package/.nodemon.json +0 -5
  525. package/.vscode/settings.json +0 -21
  526. package/AGENTS.md +0 -195
  527. package/README.md +0 -22
  528. package/build_package.sh +0 -5
  529. package/compileDTS.pl +0 -64
  530. package/docs/ai-assistant-nightly-eval.md +0 -65
  531. package/docs/ai-assistant-preflight-checklist.md +0 -23
  532. package/docs/ai-assistant-report-builder-bridge-playbook.md +0 -115
  533. package/eslint-plugin-custom/index.js +0 -7
  534. package/eslint-plugin-custom/rules/no-filter-zero-index.js +0 -44
  535. package/eslint.config.js +0 -103
  536. package/gulpfile.js +0 -216
  537. package/methodAndPublicationListGenerator.py +0 -375
  538. package/mongodbensurers.js +0 -2
  539. package/mongostop.js +0 -3
  540. package/scripts/cleanup-bypassed-callmethod-logs.js +0 -616
  541. package/settings.development.json +0 -25
  542. package/settings.development.redacted.json +0 -25
  543. package/src/.env +0 -12
  544. package/src/ai/assistant-core-heuristics.ts +0 -379
  545. package/src/ai/resolveio-platform-intelligence-memory-corpus.ts +0 -185
  546. package/src/ai/resolveio-platform-intelligence-memory.ts +0 -325
  547. package/src/ai/resolveio-platform-intelligence.ts +0 -462
  548. package/src/client-server-app.ts +0 -12
  549. package/src/collections/ai-run.collection.ts +0 -117
  550. package/src/collections/ai-terminal-conversation.collection.ts +0 -91
  551. package/src/collections/ai-terminal-issue-report.collection.ts +0 -99
  552. package/src/collections/ai-terminal-message.collection.ts +0 -77
  553. package/src/collections/app-setting.collection.ts +0 -104
  554. package/src/collections/app-status.collection.ts +0 -58
  555. package/src/collections/communication-metric.collection.ts +0 -84
  556. package/src/collections/counter.collection.ts +0 -56
  557. package/src/collections/cron-job-history.collection.ts +0 -94
  558. package/src/collections/cron-job.collection.ts +0 -92
  559. package/src/collections/customer-notification.collection.ts +0 -131
  560. package/src/collections/customer-portal-password.collection.ts +0 -76
  561. package/src/collections/email-history.collection.ts +0 -134
  562. package/src/collections/email-verified.collection.ts +0 -62
  563. package/src/collections/file.collection.ts +0 -74
  564. package/src/collections/flag-update.collection.ts +0 -57
  565. package/src/collections/flag.collection.ts +0 -57
  566. package/src/collections/log-method-latency.collection.ts +0 -77
  567. package/src/collections/log-subscription.collection.ts +0 -80
  568. package/src/collections/log.collection.ts +0 -93
  569. package/src/collections/logged-in-users.collection.ts +0 -67
  570. package/src/collections/monitor-cpu.collection.ts +0 -65
  571. package/src/collections/monitor-function.collection.ts +0 -74
  572. package/src/collections/monitor-memory.collection.ts +0 -77
  573. package/src/collections/monitor-mongo.collection.ts +0 -71
  574. package/src/collections/notification.collection.ts +0 -57
  575. package/src/collections/openai-usage-ledger.collection.ts +0 -77
  576. package/src/collections/report-builder-dashboard-builder.collection.ts +0 -109
  577. package/src/collections/report-builder-library.collection.ts +0 -89
  578. package/src/collections/report-builder-report.collection.ts +0 -184
  579. package/src/collections/user-group.collection.ts +0 -89
  580. package/src/collections/user-guide.collection.ts +0 -57
  581. package/src/collections/user.collection.ts +0 -181
  582. package/src/cron/cron.ts +0 -117
  583. package/src/fixtures/cron-jobs.ts +0 -95
  584. package/src/fixtures/init.ts +0 -35
  585. package/src/http/auth.ts +0 -818
  586. package/src/http/health.ts +0 -7
  587. package/src/http/home.ts +0 -90
  588. package/src/http/slow-query-publication.ts +0 -49
  589. package/src/index.ts +0 -1
  590. package/src/managers/ai-assistant-codex-manager.manager.ts +0 -1131
  591. package/src/managers/ai-run-evidence.manager.ts +0 -264
  592. package/src/managers/communication-metric.manager.ts +0 -82
  593. package/src/managers/cron.manager.ts +0 -333
  594. package/src/managers/customer-notification-content.manager.ts +0 -236
  595. package/src/managers/diagnostic-manager-bootstrap.ts +0 -165
  596. package/src/managers/error-auto-fix.manager.ts +0 -2767
  597. package/src/managers/local-log.manager.ts +0 -113
  598. package/src/managers/method.manager.ts +0 -1857
  599. package/src/managers/mongo.manager.ts +0 -4575
  600. package/src/managers/monitor.manager.ts +0 -507
  601. package/src/managers/openai-usage-ledger.manager.ts +0 -57
  602. package/src/managers/slow-query-verifier.manager.ts +0 -3590
  603. package/src/managers/slow-query.manager.ts +0 -519
  604. package/src/managers/subscription.manager.ts +0 -3128
  605. package/src/managers/websocket.manager.ts +0 -746
  606. package/src/managers/worker-dispatcher.manager.ts +0 -1360
  607. package/src/managers/worker-server.manager.ts +0 -536
  608. package/src/methods/accounts.ts +0 -532
  609. package/src/methods/ai-terminal.ts +0 -23497
  610. package/src/methods/app-settings.ts +0 -114
  611. package/src/methods/aws.ts +0 -649
  612. package/src/methods/collections.ts +0 -641
  613. package/src/methods/counters.ts +0 -69
  614. package/src/methods/cron-jobs.ts +0 -2614
  615. package/src/methods/customer-notifications.ts +0 -458
  616. package/src/methods/diagnostics.ts +0 -616
  617. package/src/methods/flag-updates.ts +0 -7
  618. package/src/methods/flags.ts +0 -7
  619. package/src/methods/logs.ts +0 -657
  620. package/src/methods/mongo-explorer.ts +0 -1880
  621. package/src/methods/monitor.ts +0 -540
  622. package/src/methods/pdf.ts +0 -1236
  623. package/src/methods/publications.ts +0 -129
  624. package/src/methods/report-builder.ts +0 -3300
  625. package/src/methods/support.ts +0 -335
  626. package/src/models/ai-run.model.ts +0 -27
  627. package/src/models/ai-terminal-conversation.model.ts +0 -19
  628. package/src/models/ai-terminal-issue-report.model.ts +0 -21
  629. package/src/models/ai-terminal-message.model.ts +0 -24
  630. package/src/models/app-setting.model.ts +0 -17
  631. package/src/models/collection-document.model.ts +0 -24
  632. package/src/models/communication-metric.model.ts +0 -23
  633. package/src/models/cron-job-history.model.ts +0 -16
  634. package/src/models/cron-job.model.ts +0 -15
  635. package/src/models/customer-notification.model.ts +0 -28
  636. package/src/models/customer-portal-password.model.ts +0 -12
  637. package/src/models/dialog.model.ts +0 -25
  638. package/src/models/log-method-latency.model.ts +0 -11
  639. package/src/models/log.model.ts +0 -19
  640. package/src/models/method.model.ts +0 -25
  641. package/src/models/monitor-function.model.ts +0 -16
  642. package/src/models/monitor-memory.model.ts +0 -17
  643. package/src/models/monitor-mongo.model.ts +0 -15
  644. package/src/models/openai-usage-ledger.model.ts +0 -16
  645. package/src/models/pagination.model.ts +0 -35
  646. package/src/models/permission.model.ts +0 -14
  647. package/src/models/report-builder-dashboard-builder.model.ts +0 -29
  648. package/src/models/report-builder-library.model.ts +0 -20
  649. package/src/models/report-builder-report.model.ts +0 -136
  650. package/src/models/report-builder.model.ts +0 -68
  651. package/src/models/select-data-label.model.ts +0 -9
  652. package/src/models/server-message.model.ts +0 -31
  653. package/src/models/slow-query-report.model.ts +0 -23
  654. package/src/models/subscription.model.ts +0 -73
  655. package/src/models/support-ticket.model.ts +0 -104
  656. package/src/models/user-group.model.ts +0 -24
  657. package/src/models/user.model.ts +0 -96
  658. package/src/private/images/ResolveIO.png +0 -0
  659. package/src/publications/ai-terminal.ts +0 -73
  660. package/src/publications/app-settings.ts +0 -25
  661. package/src/publications/app-status.ts +0 -13
  662. package/src/publications/cron-jobs.ts +0 -40
  663. package/src/publications/customer-notifications.ts +0 -101
  664. package/src/publications/files.ts +0 -33
  665. package/src/publications/flags-update.ts +0 -19
  666. package/src/publications/flags.ts +0 -19
  667. package/src/publications/logs.ts +0 -163
  668. package/src/publications/notifications.ts +0 -13
  669. package/src/publications/report-builder-dashboard-builders.ts +0 -39
  670. package/src/publications/report-builder-libraries.ts +0 -41
  671. package/src/publications/report-builder-reports.ts +0 -47
  672. package/src/publications/super-admin.ts +0 -13
  673. package/src/publications/user-groups.ts +0 -12
  674. package/src/publications/user-guides.ts +0 -12
  675. package/src/resolveio-server-app.ts +0 -617
  676. package/src/server-app.ts +0 -3354
  677. package/src/services/codex-client.ts +0 -1231
  678. package/src/services/openai-client.ts +0 -265
  679. package/src/types/error-report.ts +0 -26
  680. package/src/types/js-tiktoken.d.ts +0 -11
  681. package/src/types/slow-query-report.ts +0 -28
  682. package/src/util/ai-qa-policy.ts +0 -925
  683. package/src/util/ai-run-evidence-adapters.ts +0 -521
  684. package/src/util/ai-run-evidence-dashboard.ts +0 -271
  685. package/src/util/ai-run-evidence-eval.ts +0 -885
  686. package/src/util/ai-run-evidence.ts +0 -964
  687. package/src/util/ai-runner-artifacts.ts +0 -586
  688. package/src/util/ai-runner-qa-auth.ts +0 -821
  689. package/src/util/ai-runner-qa-tools.ts +0 -3045
  690. package/src/util/aicoder-runner-v6.ts +0 -526
  691. package/src/util/common.ts +0 -649
  692. package/src/util/customer-portal-password.ts +0 -183
  693. package/src/util/error-reporter.ts +0 -332
  694. package/src/util/error-tracking.ts +0 -79
  695. package/src/util/openai-usage-cost.ts +0 -114
  696. package/src/util/report-builder-unwinds.ts +0 -180
  697. package/src/util/runner-process-janitor.ts +0 -219
  698. package/src/util/schema-report-builder.ts +0 -448
  699. package/src/util/slow-query-reporter.ts +0 -216
  700. package/src/util/subscription-dependency-context.ts +0 -1096
  701. package/src/util/support-runner-v5.ts +0 -897
  702. package/src/util/tokenizer.ts +0 -38
  703. package/src/workers/codex-runner.worker.ts +0 -142
  704. package/start_server.sh +0 -5
  705. package/tests/ai-assistant-corpus-build.ts +0 -484
  706. package/tests/ai-assistant-corpus-replay-e2e.ts +0 -774
  707. package/tests/ai-assistant-data-parity-e2e.ts +0 -1989
  708. package/tests/ai-assistant-eval-triage.ts +0 -831
  709. package/tests/ai-assistant-openai-e2e.ts +0 -1061
  710. package/tests/ai-assistant-openai-git-e2e.ts +0 -155
  711. package/tests/ai-assistant-preflight-matrix.ts +0 -215
  712. package/tests/ai-assistant-routing-eval.test.ts +0 -560
  713. package/tests/ai-assistant-snf-live-eval.ts +0 -975
  714. package/tests/ai-assistant-utils.test.ts +0 -2968
  715. package/tests/ai-run-eval.test.ts +0 -88
  716. package/tests/ai-run-evidence.test.ts +0 -232
  717. package/tests/ai-runner-contract.test.ts +0 -488
  718. package/tests/aicoder-runner-v6.test.ts +0 -92
  719. package/tests/error-reporter.test.ts +0 -145
  720. package/tests/method-publication-generator.test.ts +0 -46
  721. package/tests/report-builder-linking.test.ts +0 -79
  722. package/tests/resolveio-platform-intelligence.test.ts +0 -352
  723. package/tests/server-app-cron-owner.test.ts +0 -127
  724. package/tests/subscription-connect-race.test.ts +0 -158
  725. package/tests/subscription-dependency-context.test.ts +0 -324
  726. package/tests/subscription-manager-collection-tracking.test.ts +0 -86
  727. package/tests/subscription-manager-invalidation.test.ts +0 -86
  728. package/tests/support-runner-v5.test.ts +0 -191
  729. package/tsconfig.json +0 -34
  730. /package/{src/private → private}/email-templates/enrollment.html +0 -0
  731. /package/{src/private → private}/email-templates/forgot-password.html +0 -0
  732. /package/{src/private → private}/email-templates/support-ticket-deleted.html +0 -0
  733. /package/{src/private → private}/email-templates/support-ticket-modified.html +0 -0
  734. /package/{src/private → private}/email-templates/support-ticket.html +0 -0
  735. /package/{src/public_api.ts → public_api.d.ts} +0 -0
@@ -1,1989 +0,0 @@
1
- import * as fs from 'fs';
2
- import * as path from 'path';
3
- import { ResolveIOServer } from '../src/resolveio-server-app';
4
- import {
5
- buildAssistantDatedPivotDisplay,
6
- buildDisplayTable
7
- } from '../src/methods/ai-terminal';
8
-
9
- type ParityCaseExpected = {
10
- rows: Array<Record<string, any>>;
11
- meta?: Record<string, any>;
12
- };
13
-
14
- type ParityCase = {
15
- id: string;
16
- prompt: string;
17
- runExpected: (context: ParityContext) => Promise<ParityCaseExpected>;
18
- compare: (params: {
19
- toolResult: any;
20
- assistantContent: string;
21
- expected: ParityCaseExpected;
22
- context: ParityContext;
23
- }) => string[];
24
- };
25
-
26
- type ParityCaseResult = {
27
- id: string;
28
- prompt: string;
29
- pass: boolean;
30
- errors: string[];
31
- conversationId?: string;
32
- toolSource?: string;
33
- directive?: {
34
- type?: string;
35
- collection?: string;
36
- source?: string;
37
- };
38
- tool?: {
39
- type?: string;
40
- collection?: string;
41
- rowCount?: number;
42
- total?: number | null;
43
- columns?: string[];
44
- };
45
- replay?: {
46
- pass: boolean;
47
- errors: string[];
48
- };
49
- expected?: {
50
- rows: number;
51
- meta?: Record<string, any>;
52
- };
53
- };
54
-
55
- type ParityContext = {
56
- db: any;
57
- now: Date;
58
- startOfWeek: Date;
59
- last6MonthsStart: Date;
60
- last6FullMonthsStart: Date;
61
- startOfCurrentMonth: Date;
62
- methodManager: any;
63
- idUser: string;
64
- appId: string;
65
- };
66
-
67
- type AssistantDirective = {
68
- type: 'read' | 'aggregate';
69
- payload: Record<string, any>;
70
- source: 'metadata.debug.directive' | 'assistant.directive_line';
71
- };
72
-
73
- function parseBoolean(value: any): boolean {
74
- return ['true', '1', 'yes', 'y', 'on'].includes(String(value || '').trim().toLowerCase());
75
- }
76
-
77
- function normalizeOptionalString(value: any): string {
78
- return String(value ?? '').trim();
79
- }
80
-
81
- function loadDotEnvFile(filePath: string): Record<string, string> {
82
- const parsed: Record<string, string> = {};
83
- if (!fs.existsSync(filePath)) {
84
- return parsed;
85
- }
86
- const content = fs.readFileSync(filePath, 'utf8');
87
- content.split(/\r?\n/g).forEach((lineRaw) => {
88
- const line = lineRaw.trim();
89
- if (!line || line.startsWith('#')) {
90
- return;
91
- }
92
- const jsonLike = line.match(/^"([A-Za-z_][A-Za-z0-9_]*)"\s*:\s*(.+?)\s*,?$/);
93
- if (jsonLike?.[1]) {
94
- const key = jsonLike[1];
95
- let value = String(jsonLike[2] || '').trim();
96
- if ((value.startsWith('"') && value.endsWith('"')) || (value.startsWith('\'') && value.endsWith('\''))) {
97
- value = value.slice(1, -1);
98
- }
99
- if (key) {
100
- parsed[key] = value;
101
- }
102
- return;
103
- }
104
- const separator = line.indexOf('=');
105
- if (separator <= 0) {
106
- return;
107
- }
108
- const key = line.slice(0, separator).trim();
109
- let value = line.slice(separator + 1).trim();
110
- if ((value.startsWith('"') && value.endsWith('"')) || (value.startsWith('\'') && value.endsWith('\''))) {
111
- value = value.slice(1, -1);
112
- }
113
- if (key) {
114
- parsed[key] = value;
115
- }
116
- });
117
- return parsed;
118
- }
119
-
120
- function applyEnvFromServerDir(serverDir: string): void {
121
- const envFiles = [
122
- path.join(serverDir, '.env.codex'),
123
- path.join(serverDir, '.env.production'),
124
- path.join(serverDir, '.env')
125
- ];
126
- envFiles.forEach((filePath) => {
127
- const values = loadDotEnvFile(filePath);
128
- Object.keys(values).forEach((key) => {
129
- if (!process.env[key]) {
130
- process.env[key] = values[key];
131
- }
132
- });
133
- });
134
- }
135
-
136
- function addDaysUtc(date: Date, days: number): Date {
137
- return new Date(Date.UTC(
138
- date.getUTCFullYear(),
139
- date.getUTCMonth(),
140
- date.getUTCDate() + days,
141
- date.getUTCHours(),
142
- date.getUTCMinutes(),
143
- date.getUTCSeconds(),
144
- date.getUTCMilliseconds()
145
- ));
146
- }
147
-
148
- function addMonthsUtc(date: Date, months: number): Date {
149
- return new Date(Date.UTC(
150
- date.getUTCFullYear(),
151
- date.getUTCMonth() + months,
152
- date.getUTCDate(),
153
- date.getUTCHours(),
154
- date.getUTCMinutes(),
155
- date.getUTCSeconds(),
156
- date.getUTCMilliseconds()
157
- ));
158
- }
159
-
160
- function startOfDayUtc(date: Date): Date {
161
- return new Date(Date.UTC(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate(), 0, 0, 0, 0));
162
- }
163
-
164
- function startOfWeekUtcMonday(date: Date): Date {
165
- const day = date.getUTCDay();
166
- const diff = (day + 6) % 7;
167
- return startOfDayUtc(addDaysUtc(date, -diff));
168
- }
169
-
170
- function startOfMonthUtc(date: Date): Date {
171
- return new Date(Date.UTC(date.getUTCFullYear(), date.getUTCMonth(), 1, 0, 0, 0, 0));
172
- }
173
-
174
- function toNormalizedKey(value: string): string {
175
- return String(value || '').toLowerCase().replace(/[^a-z0-9]+/g, '');
176
- }
177
-
178
- function parseNumericLoose(value: any): number | null {
179
- if (typeof value === 'number' && Number.isFinite(value)) {
180
- return value;
181
- }
182
- if (typeof value !== 'string') {
183
- return null;
184
- }
185
- const trimmed = value.trim();
186
- if (!trimmed) {
187
- return null;
188
- }
189
- const normalized = trimmed
190
- .replace(/\$/g, '')
191
- .replace(/,/g, '')
192
- .replace(/%/g, '')
193
- .replace(/\s+/g, '');
194
- if (!normalized || !/^[-+]?\d*\.?\d+$/.test(normalized)) {
195
- return null;
196
- }
197
- const parsed = Number(normalized);
198
- return Number.isFinite(parsed) ? parsed : null;
199
- }
200
-
201
- function normalizeCellValue(value: any): any {
202
- const numeric = parseNumericLoose(value);
203
- if (numeric !== null) {
204
- return Number(numeric.toFixed(6));
205
- }
206
- if (value instanceof Date) {
207
- return value.toISOString();
208
- }
209
- if (typeof value === 'string') {
210
- return value.trim();
211
- }
212
- if (value === undefined) {
213
- return null;
214
- }
215
- return value;
216
- }
217
-
218
- function normalizeDisplayRows(display: any): Array<Record<string, any>> {
219
- const rows = Array.isArray(display?.rows) ? display.rows : [];
220
- return rows.map((row: any) => {
221
- const next: Record<string, any> = {};
222
- Object.keys(row || {}).forEach((key) => {
223
- next[toNormalizedKey(key)] = normalizeCellValue((row as any)[key]);
224
- });
225
- return next;
226
- });
227
- }
228
-
229
- function compareDisplayParity(sourceDisplay: any, replayDisplay: any): string[] {
230
- const sourceRows = normalizeDisplayRows(sourceDisplay);
231
- const replayRows = normalizeDisplayRows(replayDisplay);
232
- const sortRows = (rows: Array<Record<string, any>>) => rows
233
- .map(row => JSON.stringify(row, Object.keys(row).sort()))
234
- .sort();
235
-
236
- const sourceSorted = sortRows(sourceRows);
237
- const replaySorted = sortRows(replayRows);
238
- if (sourceSorted.length === replaySorted.length) {
239
- let strictMismatch = false;
240
- for (let index = 0; index < sourceSorted.length; index += 1) {
241
- if (sourceSorted[index] !== replaySorted[index]) {
242
- strictMismatch = true;
243
- break;
244
- }
245
- }
246
- if (!strictMismatch) {
247
- return [];
248
- }
249
- }
250
-
251
- // Dated outputs can be pivoted into a different row shape but still contain matching totals.
252
- const sourceMonthly = parseMonthlyTotalsFromDisplay(sourceDisplay);
253
- const replayMonthly = parseMonthlyTotalsFromDisplay(replayDisplay);
254
- if (sourceMonthly && replayMonthly) {
255
- const monthErrors = compareNumericMaps('Replay monthly totals', sourceMonthly, replayMonthly, 0.05);
256
- if (!monthErrors.length) {
257
- return [];
258
- }
259
- }
260
-
261
- const sourceCustomer = parseCustomerMonthTotalsFromDisplay(sourceDisplay);
262
- const replayCustomer = parseCustomerMonthTotalsFromDisplay(replayDisplay);
263
- if (sourceCustomer && replayCustomer) {
264
- const customerErrors = compareNumericMaps('Replay customer monthly totals', sourceCustomer.monthTotals, replayCustomer.monthTotals, 0.05);
265
- if (!customerErrors.length) {
266
- return [];
267
- }
268
- }
269
-
270
- return [
271
- `Replay display mismatch: tool_result rows=${sourceRows.length}, replay rows=${replayRows.length}.`
272
- ];
273
- }
274
-
275
- function findFirstMarkdownTable(content: string): { header: string[]; rows: string[][] } | null {
276
- const lines = String(content || '').split('\n');
277
- for (let index = 0; index < lines.length - 1; index += 1) {
278
- const header = lines[index]?.trim();
279
- const separator = lines[index + 1]?.trim();
280
- if (!header || !separator) {
281
- continue;
282
- }
283
- if (!/^\|.+\|$/.test(header)) {
284
- continue;
285
- }
286
- if (!/^\|?\s*:?-{3,}:?\s*(\|\s*:?-{3,}:?\s*)+\|?\s*$/.test(separator)) {
287
- continue;
288
- }
289
- const headerCells = header.split('|').map(value => value.trim()).filter(Boolean);
290
- const dataRows: string[][] = [];
291
- for (let rowIndex = index + 2; rowIndex < lines.length; rowIndex += 1) {
292
- const line = String(lines[rowIndex] || '');
293
- if (!line.trim() || !line.includes('|')) {
294
- break;
295
- }
296
- dataRows.push(line.split('|').map(value => value.trim()).filter(Boolean));
297
- }
298
- return { header: headerCells, rows: dataRows };
299
- }
300
- return null;
301
- }
302
-
303
- function collectCommonFormattingErrors(content: string): string[] {
304
- const errors: string[] = [];
305
- const table = findFirstMarkdownTable(content);
306
- if (table && table.rows.length === 0) {
307
- errors.push('Assistant response contains an empty markdown table header with no data rows.');
308
- }
309
- return errors;
310
- }
311
-
312
- function collectMonthColumns(columns: string[]): string[] {
313
- return columns.filter(column => /^\d{4}-\d{2}$/.test(String(column || '').trim()));
314
- }
315
-
316
- function parseMonthlyTotalsFromDisplay(display: any): Map<string, number> | null {
317
- if (!display || !Array.isArray(display.columns) || !Array.isArray(display.rows) || !display.rows.length) {
318
- return null;
319
- }
320
- const columns = display.columns.map((column: any) => String(column || '').trim());
321
- const monthColumns = collectMonthColumns(columns);
322
- if (monthColumns.length) {
323
- const firstColumn = columns[0] || '';
324
- const normalizedFirst = toNormalizedKey(firstColumn);
325
- if (normalizedFirst === 'metric') {
326
- const metricRow = display.rows.find((row: any) => {
327
- const metricValue = String(row?.[firstColumn] || '').toLowerCase();
328
- return metricValue.includes('revenue') || metricValue.includes('total');
329
- }) || display.rows[0];
330
- const result = new Map<string, number>();
331
- monthColumns.forEach((month) => {
332
- const numeric = parseNumericLoose(metricRow?.[month]);
333
- if (numeric !== null) {
334
- result.set(month, numeric);
335
- }
336
- });
337
- return result.size ? result : null;
338
- }
339
- const result = new Map<string, number>();
340
- monthColumns.forEach((month) => {
341
- let sum = 0;
342
- display.rows.forEach((row: any) => {
343
- const numeric = parseNumericLoose(row?.[month]);
344
- if (numeric !== null) {
345
- sum += numeric;
346
- }
347
- });
348
- result.set(month, sum);
349
- });
350
- return result;
351
- }
352
- const normalizedColumns = columns.map(toNormalizedKey);
353
- const monthColumnIndex = normalizedColumns.findIndex((column) => column.includes('month'));
354
- const totalColumnIndex = normalizedColumns.findIndex((column) => column.includes('revenue') || column.includes('total'));
355
- if (monthColumnIndex === -1 || totalColumnIndex === -1) {
356
- return null;
357
- }
358
- const monthColumn = columns[monthColumnIndex];
359
- const totalColumn = columns[totalColumnIndex];
360
- const result = new Map<string, number>();
361
- display.rows.forEach((row: any) => {
362
- const monthValue = String(row?.[monthColumn] || '').trim();
363
- if (!/^\d{4}-\d{2}$/.test(monthValue)) {
364
- return;
365
- }
366
- const numeric = parseNumericLoose(row?.[totalColumn]);
367
- if (numeric === null) {
368
- return;
369
- }
370
- result.set(monthValue, (result.get(monthValue) || 0) + numeric);
371
- });
372
- return result.size ? result : null;
373
- }
374
-
375
- function parseCustomerMonthTotalsFromDisplay(display: any): {
376
- entries: Array<{ customer: string; month: string; total: number }>;
377
- nonUnknownCustomers: Set<string>;
378
- monthTotals: Map<string, number>;
379
- } | null {
380
- if (!display || !Array.isArray(display.columns) || !Array.isArray(display.rows) || !display.rows.length) {
381
- return null;
382
- }
383
- const columns = display.columns.map((column: any) => String(column || '').trim());
384
- const normalizedColumns = columns.map(toNormalizedKey);
385
- const monthColumns = collectMonthColumns(columns);
386
- const entries: Array<{ customer: string; month: string; total: number }> = [];
387
-
388
- if (monthColumns.length && normalizedColumns[0]?.includes('customer')) {
389
- const customerColumn = columns[0];
390
- display.rows.forEach((row: any) => {
391
- const customer = String(row?.[customerColumn] || '').trim() || 'Unknown Customer';
392
- monthColumns.forEach((month) => {
393
- const total = parseNumericLoose(row?.[month]);
394
- if (total === null) {
395
- return;
396
- }
397
- entries.push({ customer, month, total });
398
- });
399
- });
400
- }
401
- else {
402
- const customerColumnIndex = normalizedColumns.findIndex((column) => column.includes('customer'));
403
- const monthColumnIndex = normalizedColumns.findIndex((column) => column.includes('month'));
404
- const totalColumnIndex = normalizedColumns.findIndex((column) => column.includes('revenue') || column.includes('total'));
405
- if (customerColumnIndex === -1 || monthColumnIndex === -1 || totalColumnIndex === -1) {
406
- return null;
407
- }
408
- const customerColumn = columns[customerColumnIndex];
409
- const monthColumn = columns[monthColumnIndex];
410
- const totalColumn = columns[totalColumnIndex];
411
- display.rows.forEach((row: any) => {
412
- const customer = String(row?.[customerColumn] || '').trim() || 'Unknown Customer';
413
- const month = String(row?.[monthColumn] || '').trim();
414
- if (!/^\d{4}-\d{2}$/.test(month)) {
415
- return;
416
- }
417
- const total = parseNumericLoose(row?.[totalColumn]);
418
- if (total === null) {
419
- return;
420
- }
421
- entries.push({ customer, month, total });
422
- });
423
- }
424
- if (!entries.length) {
425
- return null;
426
- }
427
- const nonUnknownCustomers = new Set<string>();
428
- const monthTotals = new Map<string, number>();
429
- entries.forEach((entry) => {
430
- if (!/^unknown customer$/i.test(entry.customer)) {
431
- nonUnknownCustomers.add(entry.customer.toLowerCase());
432
- }
433
- monthTotals.set(entry.month, (monthTotals.get(entry.month) || 0) + entry.total);
434
- });
435
- return {
436
- entries,
437
- nonUnknownCustomers,
438
- monthTotals
439
- };
440
- }
441
-
442
- function parseDimensionMonthTotalsFromDisplay(
443
- display: any,
444
- dimensionCandidates: string[]
445
- ): {
446
- entries: Array<{ dimension: string; month: string; total: number }>;
447
- nonUnknownDimensions: Set<string>;
448
- monthTotals: Map<string, number>;
449
- dimensionTotals: Map<string, number>;
450
- } | null {
451
- if (!display || !Array.isArray(display.columns) || !Array.isArray(display.rows) || !display.rows.length) {
452
- return null;
453
- }
454
- const columns = display.columns.map((column: any) => String(column || '').trim());
455
- const normalizedColumns = columns.map(toNormalizedKey);
456
- const normalizedDimensionCandidates = (Array.isArray(dimensionCandidates) ? dimensionCandidates : [])
457
- .map(candidate => toNormalizedKey(candidate))
458
- .filter(Boolean);
459
- const isDimensionColumn = (value: string): boolean => {
460
- return normalizedDimensionCandidates.some((candidate) => value === candidate || value.includes(candidate));
461
- };
462
- const monthColumns = collectMonthColumns(columns);
463
- const entries: Array<{ dimension: string; month: string; total: number }> = [];
464
-
465
- if (monthColumns.length && isDimensionColumn(normalizedColumns[0] || '')) {
466
- const dimensionColumn = columns[0];
467
- display.rows.forEach((row: any) => {
468
- const dimension = String(row?.[dimensionColumn] || '').trim() || 'Unknown';
469
- monthColumns.forEach((month) => {
470
- const total = parseNumericLoose(row?.[month]);
471
- if (total === null) {
472
- return;
473
- }
474
- entries.push({ dimension, month, total });
475
- });
476
- });
477
- }
478
- else {
479
- const dimensionColumnIndex = normalizedColumns.findIndex(column => isDimensionColumn(column));
480
- const monthColumnIndex = normalizedColumns.findIndex(column => column.includes('month'));
481
- const totalColumnIndex = normalizedColumns.findIndex((column) => (
482
- column.includes('revenue')
483
- || column.includes('total')
484
- || column.includes('hour')
485
- || column.includes('amount')
486
- || column.includes('billable')
487
- ));
488
- if (dimensionColumnIndex === -1 || monthColumnIndex === -1 || totalColumnIndex === -1) {
489
- return null;
490
- }
491
- const dimensionColumn = columns[dimensionColumnIndex];
492
- const monthColumn = columns[monthColumnIndex];
493
- const totalColumn = columns[totalColumnIndex];
494
- display.rows.forEach((row: any) => {
495
- const dimension = String(row?.[dimensionColumn] || '').trim() || 'Unknown';
496
- const month = String(row?.[monthColumn] || '').trim();
497
- if (!/^\d{4}-\d{2}$/.test(month)) {
498
- return;
499
- }
500
- const total = parseNumericLoose(row?.[totalColumn]);
501
- if (total === null) {
502
- return;
503
- }
504
- entries.push({ dimension, month, total });
505
- });
506
- }
507
- if (!entries.length) {
508
- return null;
509
- }
510
- const nonUnknownDimensions = new Set<string>();
511
- const monthTotals = new Map<string, number>();
512
- const dimensionTotals = new Map<string, number>();
513
- entries.forEach((entry) => {
514
- if (!/^(unknown|unassigned)(\s|$)/i.test(entry.dimension)) {
515
- nonUnknownDimensions.add(entry.dimension.toLowerCase());
516
- }
517
- monthTotals.set(entry.month, (monthTotals.get(entry.month) || 0) + entry.total);
518
- dimensionTotals.set(entry.dimension, (dimensionTotals.get(entry.dimension) || 0) + entry.total);
519
- });
520
- return {
521
- entries,
522
- nonUnknownDimensions,
523
- monthTotals,
524
- dimensionTotals
525
- };
526
- }
527
-
528
- function mapExpectedStatusCounts(rows: Array<Record<string, any>>): Map<string, number> {
529
- const map = new Map<string, number>();
530
- rows.forEach((row) => {
531
- const status = String(row?.status || row?.state || row?._id || 'Unknown').trim() || 'Unknown';
532
- const count = Number(row?.work_order_count || row?.count || 0);
533
- map.set(status, count);
534
- });
535
- return map;
536
- }
537
-
538
- function mapDisplayStatusCounts(display: any): Map<string, number> | null {
539
- if (!display || !Array.isArray(display.columns) || !Array.isArray(display.rows)) {
540
- return null;
541
- }
542
- const columns = display.columns.map((column: any) => String(column || '').trim());
543
- const normalizedColumns = columns.map(toNormalizedKey);
544
- const statusIndex = normalizedColumns.findIndex((column) => column.includes('status') || column.includes('state') || column === 'group');
545
- const countIndex = normalizedColumns.findIndex((column) => column.includes('count') || column.includes('total') || column.includes('workorder'));
546
- if (statusIndex === -1 || countIndex === -1) {
547
- return null;
548
- }
549
- const statusColumn = columns[statusIndex];
550
- const countColumn = columns[countIndex];
551
- const map = new Map<string, number>();
552
- display.rows.forEach((row: any) => {
553
- const status = String(row?.[statusColumn] || 'Unknown').trim() || 'Unknown';
554
- const count = parseNumericLoose(row?.[countColumn]);
555
- if (count === null) {
556
- return;
557
- }
558
- map.set(status, count);
559
- });
560
- return map;
561
- }
562
-
563
- function resolveFirstExistingField(row: Record<string, any>, candidates: string[]): string | null {
564
- const keys = Object.keys(row || {});
565
- const normalizedToRaw = new Map<string, string>();
566
- keys.forEach((key) => {
567
- normalizedToRaw.set(toNormalizedKey(key), key);
568
- });
569
- for (const candidate of candidates) {
570
- const raw = normalizedToRaw.get(toNormalizedKey(candidate));
571
- if (raw) {
572
- return raw;
573
- }
574
- }
575
- return null;
576
- }
577
-
578
- function mapExpectedByDimension(
579
- rows: Array<Record<string, any>>,
580
- dimensionCandidates: string[],
581
- valueCandidates: string[]
582
- ): Map<string, number> | null {
583
- const firstRow = rows.find(row => row && typeof row === 'object') || null;
584
- if (!firstRow) {
585
- return new Map<string, number>();
586
- }
587
- const dimensionField = resolveFirstExistingField(firstRow, dimensionCandidates);
588
- const valueField = resolveFirstExistingField(firstRow, valueCandidates);
589
- if (!dimensionField || !valueField) {
590
- return null;
591
- }
592
- const map = new Map<string, number>();
593
- rows.forEach((row) => {
594
- const key = normalizeOptionalString(row?.[dimensionField]);
595
- if (!key) {
596
- return;
597
- }
598
- const numeric = parseNumericLoose(row?.[valueField]);
599
- if (numeric === null) {
600
- return;
601
- }
602
- map.set(key, (map.get(key) || 0) + numeric);
603
- });
604
- return map;
605
- }
606
-
607
- function mapDisplayByDimension(
608
- display: any,
609
- dimensionCandidates: string[],
610
- valueCandidates: string[]
611
- ): Map<string, number> | null {
612
- if (!display || !Array.isArray(display.columns) || !Array.isArray(display.rows)) {
613
- return null;
614
- }
615
- const columns = display.columns.map((column: any) => String(column || '').trim());
616
- const normalizedColumns = columns.map(toNormalizedKey);
617
- const dimensionIndex = dimensionCandidates
618
- .map(candidate => normalizedColumns.findIndex(column => column === toNormalizedKey(candidate) || column.includes(toNormalizedKey(candidate))))
619
- .find(index => index !== undefined && index >= 0);
620
- const valueIndex = valueCandidates
621
- .map(candidate => normalizedColumns.findIndex(column => column === toNormalizedKey(candidate) || column.includes(toNormalizedKey(candidate))))
622
- .find(index => index !== undefined && index >= 0);
623
- if (dimensionIndex === undefined || dimensionIndex < 0 || valueIndex === undefined || valueIndex < 0) {
624
- return null;
625
- }
626
- const dimensionColumn = columns[dimensionIndex];
627
- const valueColumn = columns[valueIndex];
628
- const map = new Map<string, number>();
629
- display.rows.forEach((row: any) => {
630
- const key = normalizeOptionalString(row?.[dimensionColumn]);
631
- if (!key) {
632
- return;
633
- }
634
- const numeric = parseNumericLoose(row?.[valueColumn]);
635
- if (numeric === null) {
636
- return;
637
- }
638
- map.set(key, (map.get(key) || 0) + numeric);
639
- });
640
- return map;
641
- }
642
-
643
- function parseSingleCountFromDisplay(display: any): number | null {
644
- if (!display || !Array.isArray(display.columns) || !Array.isArray(display.rows) || !display.rows.length) {
645
- return null;
646
- }
647
- const columns = display.columns.map((column: any) => String(column || '').trim());
648
- const normalizedColumns = columns.map(toNormalizedKey);
649
- const countIndex = normalizedColumns.findIndex((column) => (
650
- column.includes('count')
651
- || column.includes('total')
652
- || column.includes('active')
653
- || column.includes('clients')
654
- || column.includes('customers')
655
- ));
656
- if (countIndex === -1) {
657
- return null;
658
- }
659
- const countColumn = columns[countIndex];
660
- for (const row of display.rows) {
661
- const numeric = parseNumericLoose(row?.[countColumn]);
662
- if (numeric !== null) {
663
- return numeric;
664
- }
665
- }
666
- return null;
667
- }
668
-
669
- function compareNumericMaps(
670
- label: string,
671
- expected: Map<string, number>,
672
- actual: Map<string, number>,
673
- tolerance = 0.01
674
- ): string[] {
675
- const errors: string[] = [];
676
- const keys = new Set<string>([...Array.from(expected.keys()), ...Array.from(actual.keys())]);
677
- keys.forEach((key) => {
678
- const left = expected.get(key);
679
- const right = actual.get(key);
680
- if (left === undefined) {
681
- errors.push(`${label}: unexpected key "${key}" in AI output.`);
682
- return;
683
- }
684
- if (right === undefined) {
685
- errors.push(`${label}: missing key "${key}" in AI output.`);
686
- return;
687
- }
688
- if (Math.abs(left - right) > tolerance) {
689
- errors.push(`${label}: mismatch for "${key}" expected=${left} actual=${right}.`);
690
- }
691
- });
692
- return errors;
693
- }
694
-
695
- async function sleep(ms: number): Promise<void> {
696
- return await new Promise(resolve => setTimeout(resolve, ms));
697
- }
698
-
699
- async function findSuperAdminUserId(db: any): Promise<string> {
700
- const user = await db.collection('users').findOne(
701
- { 'roles.super_admin': true },
702
- { projection: { _id: 1 }, sort: { updatedAt: -1, createdAt: -1 } }
703
- );
704
- if (!user?._id) {
705
- throw new Error('Could not find super admin user.');
706
- }
707
- return String(user._id);
708
- }
709
-
710
- async function resolveExistingCollection(db: any, candidates: string[]): Promise<string | null> {
711
- const rows = await db.listCollections({}, { nameOnly: true }).toArray();
712
- const names = new Set(rows.map((row: any) => normalizeOptionalString(row?.name)));
713
- for (const candidate of candidates) {
714
- if (names.has(candidate)) {
715
- return candidate;
716
- }
717
- }
718
- return null;
719
- }
720
-
721
- async function detectFieldPresence(db: any, collection: string, fields: string[]): Promise<Record<string, boolean>> {
722
- const projection = fields.reduce((acc: Record<string, number>, field) => {
723
- acc[field] = 1;
724
- return acc;
725
- }, {});
726
- const docs = await db.collection(collection).find({}, { projection, limit: 200 }).toArray();
727
- const result: Record<string, boolean> = {};
728
- fields.forEach((field) => {
729
- result[field] = docs.some((doc: any) => doc && doc[field] !== undefined && doc[field] !== null && String(doc[field]).trim() !== '');
730
- });
731
- return result;
732
- }
733
-
734
- async function pollFinalAssistantMessage(db: any, idConversation: string, timeoutMs = 180000): Promise<any> {
735
- const started = Date.now();
736
- while (Date.now() - started < timeoutMs) {
737
- const message = await db.collection('ai-terminal-messages').findOne(
738
- { id_conversation: idConversation, role: 'assistant' },
739
- { sort: { createdAt: -1 } }
740
- );
741
- if (message && message?.metadata?.pending !== true) {
742
- return message;
743
- }
744
- await sleep(1200);
745
- }
746
- throw new Error(`Timed out waiting for final assistant message (${idConversation}).`);
747
- }
748
-
749
- function buildMongoCommonContext(methodManager: any, idUser: string): any {
750
- return Object.assign({}, methodManager, Object.getPrototypeOf(methodManager), {
751
- id_user: idUser,
752
- user: 'AI Parity Runner',
753
- id_ws: 'ai-assistant-data-parity-e2e'
754
- });
755
- }
756
-
757
- async function callMethodAsUser(methodManager: any, idUser: string, method: string, payload: Record<string, any>): Promise<any> {
758
- const ctx = buildMongoCommonContext(methodManager, idUser);
759
- return await methodManager.callMethod.call(ctx, method, payload);
760
- }
761
-
762
- async function runPrompt(methodManager: any, db: any, idUser: string, prompt: string, appId: string): Promise<{ conversationId: string; message: any }> {
763
- const model = String(process.env.AI_ASSISTANT_CODEX_MODEL || process.env.OPENAI_MODEL || '').trim();
764
- const payload = {
765
- message: prompt,
766
- id_app: appId,
767
- max_history: 0,
768
- config: {
769
- ...(model ? { model } : {})
770
- }
771
- };
772
- const response = await callMethodAsUser(methodManager, idUser, 'aiCoderTerminalRunCodex', payload);
773
- const conversationId = String(response?.conversation?._id || response?.conversation?.id_conversation || '').trim();
774
- if (!conversationId) {
775
- throw new Error('No conversation id returned from aiCoderTerminalRunCodex.');
776
- }
777
- const message = await pollFinalAssistantMessage(db, conversationId);
778
- return { conversationId, message };
779
- }
780
-
781
- function parseDirectiveLine(content: string): AssistantDirective | null {
782
- const lines = String(content || '').split(/\r?\n/g);
783
- for (const lineRaw of lines) {
784
- const line = lineRaw.trim();
785
- if (!line) {
786
- continue;
787
- }
788
- const readPrefix = 'REPORT_BUILDER_READ:';
789
- const aggPrefix = 'REPORT_BUILDER_AGG:';
790
- let type: AssistantDirective['type'] | null = null;
791
- let jsonText = '';
792
- if (line.startsWith(readPrefix)) {
793
- type = 'read';
794
- jsonText = line.slice(readPrefix.length).trim();
795
- }
796
- else if (line.startsWith(aggPrefix)) {
797
- type = 'aggregate';
798
- jsonText = line.slice(aggPrefix.length).trim();
799
- }
800
- if (!type || !jsonText) {
801
- continue;
802
- }
803
- try {
804
- const payload = JSON.parse(jsonText);
805
- if (!payload || typeof payload !== 'object' || Array.isArray(payload)) {
806
- continue;
807
- }
808
- return {
809
- type,
810
- payload,
811
- source: 'assistant.directive_line'
812
- };
813
- }
814
- catch {
815
- continue;
816
- }
817
- }
818
- return null;
819
- }
820
-
821
- function extractDirectiveFromMessage(message: any): AssistantDirective | null {
822
- const debugDirective = message?.metadata?.debug?.directive;
823
- if (debugDirective && typeof debugDirective === 'object') {
824
- const normalizedType = normalizeOptionalString(debugDirective?.type).toLowerCase();
825
- if ((normalizedType === 'read' || normalizedType === 'aggregate')
826
- && debugDirective.payload
827
- && typeof debugDirective.payload === 'object'
828
- && !Array.isArray(debugDirective.payload)) {
829
- return {
830
- type: normalizedType as AssistantDirective['type'],
831
- payload: debugDirective.payload,
832
- source: 'metadata.debug.directive'
833
- };
834
- }
835
- const rawLineDirective = parseDirectiveLine(normalizeOptionalString(debugDirective?.rawLine));
836
- if (rawLineDirective) {
837
- return {
838
- ...rawLineDirective,
839
- source: 'metadata.debug.directive'
840
- };
841
- }
842
- }
843
- return parseDirectiveLine(normalizeOptionalString(message?.content));
844
- }
845
-
846
- function buildSyntheticToolResultFromDirective(directive: AssistantDirective, methodResponse: any): any {
847
- const display = methodResponse?.display && typeof methodResponse.display === 'object'
848
- ? methodResponse.display
849
- : {
850
- columns: [] as string[],
851
- rows: [] as Array<Record<string, any>>,
852
- rowCount: 0,
853
- truncated: false
854
- };
855
- const rowCount = Array.isArray(methodResponse?.documents)
856
- ? methodResponse.documents.length
857
- : Number(display?.rowCount || 0);
858
- const total = typeof methodResponse?.total === 'number' ? methodResponse.total : undefined;
859
- const verification = methodResponse?.verification && typeof methodResponse.verification === 'object'
860
- ? methodResponse.verification
861
- : undefined;
862
- const collection = normalizeOptionalString(methodResponse?.debug?.collectionResolved)
863
- || normalizeOptionalString(methodResponse?.debug?.collection)
864
- || normalizeOptionalString(directive.payload?.collection);
865
- return {
866
- type: directive.type === 'aggregate' ? 'mongo_agg' : 'mongo_read',
867
- input: directive.payload,
868
- output: {
869
- display,
870
- total,
871
- collection: collection || undefined,
872
- rowCount,
873
- columns: Array.isArray(display?.columns) ? display.columns : [],
874
- truncated: display?.truncated === true,
875
- verification,
876
- debug: methodResponse?.debug && typeof methodResponse.debug === 'object' ? methodResponse.debug : undefined
877
- }
878
- };
879
- }
880
-
881
- function resolveParityCases(): ParityCase[] {
882
- return [
883
- {
884
- id: 'active_clients_count',
885
- prompt: 'How many active clients do I have right now?',
886
- runExpected: async (context) => {
887
- const collection = await resolveExistingCollection(context.db, ['clients', 'customers']);
888
- if (!collection) {
889
- return { rows: [], meta: { activeCount: 0, collection: null, basis: 'missing_collection' } };
890
- }
891
- const collectionFamily = normalizeOptionalString(collection).toLowerCase();
892
- const activeFields = ['status', 'active', 'is_active', 'isactive', 'enabled', 'is_enabled', 'isenabled'];
893
- if (!['clients', 'customers'].includes(collectionFamily)) {
894
- activeFields.splice(1, 0, 'state');
895
- }
896
- const fieldPresence = await detectFieldPresence(context.db, collection, activeFields);
897
- const hasAnyActiveField = activeFields.some(field => fieldPresence[field] === true);
898
- let query: Record<string, any> = {};
899
- let basis = 'all_records_default_active';
900
- if (hasAnyActiveField) {
901
- const activeBranches: Record<string, any>[] = [
902
- { status: { $regex: '^active$', $options: 'i' } },
903
- { active: true },
904
- { is_active: true },
905
- { isactive: true },
906
- { enabled: true },
907
- { is_enabled: true },
908
- { isenabled: true }
909
- ];
910
- if (!['clients', 'customers'].includes(collectionFamily)) {
911
- activeBranches.splice(1, 0, { state: { $regex: '^active$', $options: 'i' } });
912
- }
913
- query = {
914
- $or: activeBranches
915
- };
916
- basis = 'status_or_active_field';
917
- }
918
- const count = await context.db.collection(collection).countDocuments(query);
919
- const rows = await context.db.collection(collection).find(
920
- query,
921
- {
922
- projection: {
923
- status: 1,
924
- state: 1,
925
- active: 1,
926
- is_active: 1,
927
- isactive: 1,
928
- enabled: 1,
929
- is_enabled: 1,
930
- isenabled: 1
931
- },
932
- limit: 50
933
- }
934
- ).toArray();
935
- return { rows, meta: { activeCount: count, collection, basis } };
936
- },
937
- compare: ({ toolResult, assistantContent, expected }) => {
938
- const errors: string[] = [];
939
- const expectedCount = Number(expected?.meta?.activeCount || 0);
940
- const displayCount = parseSingleCountFromDisplay(toolResult?.output?.display);
941
- const actualTotal = typeof toolResult?.output?.total === 'number'
942
- ? toolResult.output.total
943
- : (displayCount !== null ? displayCount : Number(toolResult?.output?.rowCount || 0));
944
- if (actualTotal !== expectedCount) {
945
- errors.push(
946
- `Active client count mismatch: expected=${expectedCount}, ai=${actualTotal}, basis=${String(expected?.meta?.basis || 'unknown')}, collection=${String(expected?.meta?.collection || 'unknown')}.`
947
- );
948
- }
949
- errors.push(...collectCommonFormattingErrors(assistantContent));
950
- return errors;
951
- }
952
- },
953
- {
954
- id: 'wo_last20_this_week_group_status',
955
- prompt: 'Show me the last 20 work orders created this week, grouped by status.',
956
- runExpected: async (context) => {
957
- const rows = await context.db.collection('work-order-dynamics').aggregate([
958
- {
959
- $match: {
960
- date_created: { $gte: context.startOfWeek, $lt: context.now }
961
- }
962
- },
963
- { $sort: { date_created: -1 } },
964
- { $limit: 20 },
965
- {
966
- $group: {
967
- _id: { $ifNull: ['$status', 'Unknown'] },
968
- work_order_count: { $sum: 1 }
969
- }
970
- },
971
- { $sort: { work_order_count: -1, _id: 1 } },
972
- {
973
- $project: {
974
- _id: 0,
975
- status: '$_id',
976
- work_order_count: 1
977
- }
978
- }
979
- ]).toArray();
980
- return { rows };
981
- },
982
- compare: ({ toolResult, assistantContent, expected }) => {
983
- const errors: string[] = [];
984
- const expectedMap = mapExpectedStatusCounts(expected.rows || []);
985
- const actualRowCount = Number(toolResult?.output?.rowCount || 0);
986
- if (expectedMap.size === 0 && actualRowCount === 0) {
987
- // No rows in either source; status grouping table may be absent by design.
988
- }
989
- else {
990
- const actualMap = mapDisplayStatusCounts(toolResult?.output?.display);
991
- if (!actualMap) {
992
- errors.push('Could not parse status/count columns from AI display.');
993
- }
994
- else {
995
- errors.push(...compareNumericMaps('Status counts', expectedMap, actualMap, 0));
996
- }
997
- }
998
- errors.push(...collectCommonFormattingErrors(assistantContent));
999
- return errors;
1000
- }
1001
- },
1002
- {
1003
- id: 'wo_last20_this_week_by_status_alias',
1004
- prompt: 'Show me the last 20 work orders created this week by status.',
1005
- runExpected: async (context) => {
1006
- const rows = await context.db.collection('work-order-dynamics').aggregate([
1007
- {
1008
- $match: {
1009
- date_created: { $gte: context.startOfWeek, $lt: context.now }
1010
- }
1011
- },
1012
- { $sort: { date_created: -1 } },
1013
- { $limit: 20 },
1014
- {
1015
- $group: {
1016
- _id: { $ifNull: ['$status', 'Unknown'] },
1017
- work_order_count: { $sum: 1 }
1018
- }
1019
- },
1020
- { $sort: { work_order_count: -1, _id: 1 } },
1021
- {
1022
- $project: {
1023
- _id: 0,
1024
- status: '$_id',
1025
- work_order_count: 1
1026
- }
1027
- }
1028
- ]).toArray();
1029
- return { rows };
1030
- },
1031
- compare: ({ toolResult, assistantContent, expected }) => {
1032
- const errors: string[] = [];
1033
- const expectedMap = mapExpectedStatusCounts(expected.rows || []);
1034
- const actualRowCount = Number(toolResult?.output?.rowCount || 0);
1035
- if (expectedMap.size === 0 && actualRowCount === 0) {
1036
- // No rows in either source; status grouping table may be absent by design.
1037
- }
1038
- else {
1039
- const actualMap = mapDisplayStatusCounts(toolResult?.output?.display);
1040
- if (!actualMap) {
1041
- errors.push('Could not parse status/count columns from AI display.');
1042
- }
1043
- else {
1044
- errors.push(...compareNumericMaps('Status counts', expectedMap, actualMap, 0));
1045
- }
1046
- }
1047
- errors.push(...collectCommonFormattingErrors(assistantContent));
1048
- return errors;
1049
- }
1050
- },
1051
- {
1052
- id: 'wo_completed_per_day_last_30d',
1053
- prompt: 'For the last 30 days, how many work orders were completed per day?',
1054
- runExpected: async (context) => {
1055
- const last30Days = addDaysUtc(context.now, -30);
1056
- const rows = await context.db.collection('work-order-dynamics').aggregate([
1057
- {
1058
- $match: {
1059
- date_completed: { $gte: last30Days, $lt: context.now }
1060
- }
1061
- },
1062
- {
1063
- $group: {
1064
- _id: {
1065
- $dateToString: { format: '%Y-%m-%d', date: '$date_completed', timezone: 'UTC' }
1066
- },
1067
- completed_work_orders: { $sum: 1 }
1068
- }
1069
- },
1070
- { $sort: { _id: 1 } },
1071
- {
1072
- $project: {
1073
- _id: 0,
1074
- day_utc: '$_id',
1075
- completed_work_orders: 1
1076
- }
1077
- }
1078
- ]).toArray();
1079
- return { rows };
1080
- },
1081
- compare: ({ toolResult, assistantContent, expected }) => {
1082
- const errors: string[] = [];
1083
- const expectedRows = Array.isArray(expected.rows) ? expected.rows.length : 0;
1084
- const actualRows = Number(toolResult?.output?.rowCount || 0);
1085
- if (expectedRows === 0 && actualRows === 0) {
1086
- errors.push(...collectCommonFormattingErrors(assistantContent));
1087
- return errors;
1088
- }
1089
- const expectedMap = mapExpectedByDimension(expected.rows || [], ['day_utc', 'day'], ['completed_work_orders', 'work_order_count', 'count']);
1090
- const actualMap = mapDisplayByDimension(toolResult?.output?.display, ['day', 'day_utc', 'date'], ['completed', 'count', 'work_order_count', 'total']);
1091
- if (!expectedMap || !actualMap) {
1092
- errors.push('Could not parse day/count map for completed work orders.');
1093
- }
1094
- else {
1095
- errors.push(...compareNumericMaps('Completed work orders per day', expectedMap, actualMap, 0));
1096
- }
1097
- errors.push(...collectCommonFormattingErrors(assistantContent));
1098
- return errors;
1099
- }
1100
- },
1101
- {
1102
- id: 'wo_top_customers_last_6m',
1103
- prompt: 'List the top 10 customers by number of work orders in the last 6 months.',
1104
- runExpected: async (context) => {
1105
- const collectionRows = await context.db.listCollections({}, { nameOnly: true }).toArray();
1106
- const collectionNames = new Set(collectionRows.map((row: any) => normalizeOptionalString(row?.name)));
1107
- const collectionCandidates = ['work-order-dynamics', 'maintenance-orders', 'orders']
1108
- .filter(name => collectionNames.has(name));
1109
- if (!collectionCandidates.length) {
1110
- return {
1111
- rows: [],
1112
- meta: { collection: null, basis: 'missing_collection' }
1113
- };
1114
- }
1115
- for (const candidate of collectionCandidates) {
1116
- const rows = await context.db.collection(candidate).aggregate([
1117
- {
1118
- $addFields: {
1119
- effective_date: {
1120
- $ifNull: [
1121
- '$date_created',
1122
- { $ifNull: ['$date_create', '$createdAt'] }
1123
- ]
1124
- },
1125
- customer_label: {
1126
- $ifNull: [
1127
- '$customer',
1128
- {
1129
- $ifNull: [
1130
- '$customer_name',
1131
- {
1132
- $ifNull: [
1133
- '$client_name',
1134
- {
1135
- $ifNull: [
1136
- { $toString: '$id_customer' },
1137
- { $ifNull: ['$qb_ListID_class', 'Unknown'] }
1138
- ]
1139
- }
1140
- ]
1141
- }
1142
- ]
1143
- }
1144
- ]
1145
- }
1146
- }
1147
- },
1148
- {
1149
- $match: {
1150
- effective_date: { $gte: context.last6MonthsStart, $lt: context.now },
1151
- 'deleted.date': { $exists: false }
1152
- }
1153
- },
1154
- {
1155
- $group: {
1156
- _id: '$customer_label',
1157
- work_order_count: { $sum: 1 }
1158
- }
1159
- },
1160
- { $sort: { work_order_count: -1, _id: 1 } },
1161
- { $limit: 10 },
1162
- {
1163
- $project: {
1164
- _id: 0,
1165
- customer: '$_id',
1166
- work_order_count: 1
1167
- }
1168
- }
1169
- ]).toArray();
1170
- if (rows.length) {
1171
- return { rows, meta: { collection: candidate, basis: 'first_non_empty_candidate' } };
1172
- }
1173
- }
1174
- return { rows: [], meta: { collection: collectionCandidates[0], basis: 'zero_rows_all_candidates' } };
1175
- },
1176
- compare: ({ toolResult, assistantContent, expected }) => {
1177
- const errors: string[] = [];
1178
- const expectedRows = Array.isArray(expected.rows) ? expected.rows.length : 0;
1179
- const actualRows = Number(toolResult?.output?.rowCount || 0);
1180
- if (expectedRows === 0 && actualRows === 0) {
1181
- errors.push(...collectCommonFormattingErrors(assistantContent));
1182
- return errors;
1183
- }
1184
- if (expectedRows > 0 && actualRows === 0) {
1185
- errors.push(`Expected top-customer work-order rows (${expectedRows}), but AI returned zero rows.`);
1186
- }
1187
- const expectedMap = mapExpectedByDimension(expected.rows || [], ['customer'], ['work_order_count', 'count']);
1188
- const actualMap = mapDisplayByDimension(toolResult?.output?.display, ['customer', 'client', 'account'], ['work_order_count', 'count', 'total']);
1189
- if (!expectedMap || !actualMap) {
1190
- errors.push('Could not parse customer/count map for top work-order customers.');
1191
- }
1192
- else {
1193
- errors.push(...compareNumericMaps('Top customers by work-order count', expectedMap, actualMap, 0));
1194
- }
1195
- errors.push(...collectCommonFormattingErrors(assistantContent));
1196
- return errors;
1197
- }
1198
- },
1199
- {
1200
- id: 'blend_last10_summary',
1201
- prompt: 'For blending: summarize the last 10 blend tickets with product, total volume, and created date.',
1202
- runExpected: async (context) => {
1203
- const rows = await context.db.collection('chemical-blends').aggregate([
1204
- { $sort: { date: -1, createdAt: -1 } },
1205
- { $limit: 10 },
1206
- {
1207
- $project: {
1208
- _id: 1,
1209
- product: { $ifNull: ['$blend_name', '$chemical'] },
1210
- total_volume: '$chemical_recipe_quantity',
1211
- volume_unit: { $cond: [{ $eq: ['$blend_in_pounds', true] }, 'lb', 'gal'] },
1212
- created_date: { $ifNull: ['$date', '$createdAt'] },
1213
- batch_number: 1
1214
- }
1215
- }
1216
- ]).toArray();
1217
- return { rows };
1218
- },
1219
- compare: ({ toolResult, assistantContent, expected }) => {
1220
- const errors: string[] = [];
1221
- const expectedCount = Array.isArray(expected.rows) ? expected.rows.length : 0;
1222
- const actualCount = Number(toolResult?.output?.rowCount || 0);
1223
- if (actualCount !== expectedCount) {
1224
- errors.push(`Blend last-10 row count mismatch: expected=${expectedCount}, ai=${actualCount}.`);
1225
- }
1226
- const normalizedColumns = (toolResult?.output?.columns || []).map((column: string) => toNormalizedKey(column));
1227
- if (!normalizedColumns.some((column: string) => column.includes('product') || column.includes('blend') || column.includes('chemical'))) {
1228
- errors.push('Blend summary missing a product/blend column.');
1229
- }
1230
- if (!normalizedColumns.some((column: string) => column.includes('volume') || column.includes('quantity'))) {
1231
- errors.push('Blend summary missing a volume column.');
1232
- }
1233
- errors.push(...collectCommonFormattingErrors(assistantContent));
1234
- return errors;
1235
- }
1236
- },
1237
- {
1238
- id: 'invoice_top_customers_total_last_6m',
1239
- prompt: 'Show top 10 customers by invoice total in the last 6 months.',
1240
- runExpected: async (context) => {
1241
- const rows = await context.db.collection('invoices').aggregate([
1242
- {
1243
- $addFields: {
1244
- effective_date: {
1245
- $ifNull: [
1246
- '$date_paid',
1247
- { $ifNull: ['$date_invoiced', '$createdAt'] }
1248
- ]
1249
- },
1250
- effective_total: { $ifNull: ['$paid_total', '$grand_total'] },
1251
- customer_label: {
1252
- $ifNull: [
1253
- '$customer_name',
1254
- {
1255
- $ifNull: [
1256
- '$customer.fullname',
1257
- {
1258
- $ifNull: [
1259
- '$customer.name',
1260
- {
1261
- $ifNull: [
1262
- '$customer',
1263
- { $ifNull: ['$client_name', { $ifNull: ['$client.fullname', { $ifNull: ['$client.name', { $ifNull: ['$client', 'Unknown Customer'] }] }] }] }
1264
- ]
1265
- }
1266
- ]
1267
- }
1268
- ]
1269
- }
1270
- ]
1271
- }
1272
- }
1273
- },
1274
- {
1275
- $match: {
1276
- effective_date: { $gte: context.last6MonthsStart, $lt: context.now }
1277
- }
1278
- },
1279
- {
1280
- $group: {
1281
- _id: '$customer_label',
1282
- invoice_total: { $sum: { $ifNull: ['$effective_total', 0] } },
1283
- invoice_count: { $sum: 1 }
1284
- }
1285
- },
1286
- {
1287
- $project: {
1288
- _id: 0,
1289
- customer: '$_id',
1290
- invoice_total: 1,
1291
- invoice_count: 1
1292
- }
1293
- },
1294
- { $sort: { invoice_total: -1, invoice_count: -1 } },
1295
- { $limit: 10 }
1296
- ]).toArray();
1297
- return { rows };
1298
- },
1299
- compare: ({ toolResult, assistantContent, expected }) => {
1300
- const errors: string[] = [];
1301
- const expectedMap = mapExpectedByDimension(expected.rows || [], ['customer'], ['invoice_total', 'total_revenue', 'total']);
1302
- const actualMap = mapDisplayByDimension(toolResult?.output?.display, ['customer', 'client', 'account'], ['invoice_total', 'total_revenue', 'total', 'revenue']);
1303
- if (!expectedMap || !actualMap) {
1304
- errors.push('Could not parse customer/total map for invoice top customers.');
1305
- }
1306
- else {
1307
- errors.push(...compareNumericMaps('Top customers by invoice total', expectedMap, actualMap, 0.05));
1308
- }
1309
- errors.push(...collectCommonFormattingErrors(assistantContent));
1310
- return errors;
1311
- }
1312
- },
1313
- {
1314
- id: 'deliveries_per_driver_last_month',
1315
- prompt: 'Break down the number of deliveries per driver last month',
1316
- runExpected: async (context) => {
1317
- const thisMonthStart = startOfMonthUtc(context.now);
1318
- const lastMonthStart = startOfMonthUtc(addMonthsUtc(thisMonthStart, -1));
1319
- const rows = await context.db.collection('work-order-dynamics').aggregate([
1320
- {
1321
- $match: {
1322
- date_completed: { $gte: lastMonthStart, $lt: thisMonthStart },
1323
- status: { $in: ['Completed', 'Closed'] }
1324
- }
1325
- },
1326
- { $unwind: { path: '$drivers', preserveNullAndEmptyArrays: true } },
1327
- {
1328
- $group: {
1329
- _id: { $ifNull: ['$drivers.user', 'Unassigned'] },
1330
- delivery_count: { $sum: 1 }
1331
- }
1332
- },
1333
- { $sort: { delivery_count: -1, _id: 1 } },
1334
- {
1335
- $project: {
1336
- _id: 0,
1337
- driver: '$_id',
1338
- delivery_count: 1
1339
- }
1340
- }
1341
- ]).toArray();
1342
- return { rows };
1343
- },
1344
- compare: ({ toolResult, assistantContent, expected }) => {
1345
- const errors: string[] = [];
1346
- const expectedMap = mapExpectedByDimension(expected.rows || [], ['driver', 'user'], ['delivery_count', 'count']);
1347
- const actualMap = mapDisplayByDimension(toolResult?.output?.display, ['driver', 'user', 'name'], ['delivery_count', 'count', 'total']);
1348
- if (!expectedMap || !actualMap) {
1349
- errors.push('Could not parse driver/delivery-count map.');
1350
- }
1351
- else {
1352
- errors.push(...compareNumericMaps('Deliveries per driver', expectedMap, actualMap, 0));
1353
- }
1354
- errors.push(...collectCommonFormattingErrors(assistantContent));
1355
- return errors;
1356
- }
1357
- },
1358
- {
1359
- id: 'invoice_revenue_monthly_grid_last_6m',
1360
- prompt: 'Break down my total revenue over the last 6 month by month',
1361
- runExpected: async (context) => {
1362
- const rows = await context.db.collection('invoices').aggregate([
1363
- {
1364
- $addFields: {
1365
- effective_date: {
1366
- $ifNull: [
1367
- '$date_paid',
1368
- { $ifNull: ['$date_invoiced', '$createdAt'] }
1369
- ]
1370
- },
1371
- effective_total: { $ifNull: ['$paid_total', '$grand_total'] }
1372
- }
1373
- },
1374
- {
1375
- $match: {
1376
- $expr: {
1377
- $and: [
1378
- { $gte: ['$effective_date', context.last6FullMonthsStart] },
1379
- { $lt: ['$effective_date', context.startOfCurrentMonth] }
1380
- ]
1381
- }
1382
- }
1383
- },
1384
- {
1385
- $group: {
1386
- _id: {
1387
- bucket: {
1388
- $dateTrunc: {
1389
- date: '$effective_date',
1390
- unit: 'month'
1391
- }
1392
- }
1393
- },
1394
- total_revenue: {
1395
- $sum: { $ifNull: ['$effective_total', 0] }
1396
- }
1397
- }
1398
- },
1399
- {
1400
- $project: {
1401
- _id: 0,
1402
- month: { $dateToString: { format: '%Y-%m', date: '$_id.bucket' } },
1403
- total_revenue: 1
1404
- }
1405
- },
1406
- { $sort: { month: 1 } }
1407
- ]).toArray();
1408
- return { rows };
1409
- },
1410
- compare: ({ toolResult, assistantContent, expected }) => {
1411
- const errors: string[] = [];
1412
- const expectedDisplay = buildDisplayTable(expected.rows || [], { maxColumns: 40, maxRows: 1000 });
1413
- const expectedPivot = buildAssistantDatedPivotDisplay(expectedDisplay);
1414
- const expectedTotals = expectedPivot
1415
- ? parseMonthlyTotalsFromDisplay(expectedPivot)
1416
- : parseMonthlyTotalsFromDisplay(expectedDisplay);
1417
- const actualTotals = parseMonthlyTotalsFromDisplay(toolResult?.output?.display);
1418
- if (!expectedTotals || !actualTotals) {
1419
- errors.push('Could not parse monthly totals for expected or AI display.');
1420
- }
1421
- else {
1422
- errors.push(...compareNumericMaps('Monthly revenue totals', expectedTotals, actualTotals, 0.05));
1423
- }
1424
- errors.push(...collectCommonFormattingErrors(assistantContent));
1425
- return errors;
1426
- }
1427
- },
1428
- {
1429
- id: 'invoice_revenue_monthly_customer_grid_last_6m',
1430
- prompt: 'Break down my total revenue over the last 6 month by month by each customer',
1431
- runExpected: async (context) => {
1432
- const rows = await context.db.collection('invoices').aggregate([
1433
- {
1434
- $addFields: {
1435
- effective_date: {
1436
- $ifNull: [
1437
- '$date_paid',
1438
- { $ifNull: ['$date_invoiced', '$createdAt'] }
1439
- ]
1440
- },
1441
- effective_total: { $ifNull: ['$paid_total', '$grand_total'] }
1442
- }
1443
- },
1444
- {
1445
- $match: {
1446
- $expr: {
1447
- $and: [
1448
- { $gte: ['$effective_date', context.last6FullMonthsStart] },
1449
- { $lt: ['$effective_date', context.startOfCurrentMonth] }
1450
- ]
1451
- }
1452
- }
1453
- },
1454
- {
1455
- $group: {
1456
- _id: {
1457
- customer: {
1458
- $ifNull: [
1459
- '$customer_name',
1460
- {
1461
- $ifNull: [
1462
- '$customer.fullname',
1463
- {
1464
- $ifNull: [
1465
- '$customer.name',
1466
- {
1467
- $ifNull: [{ $toString: '$id_customer' }, 'Unknown Customer']
1468
- }
1469
- ]
1470
- }
1471
- ]
1472
- }
1473
- ]
1474
- },
1475
- bucket: {
1476
- $dateTrunc: {
1477
- date: '$effective_date',
1478
- unit: 'month'
1479
- }
1480
- }
1481
- },
1482
- total_revenue: { $sum: { $ifNull: ['$effective_total', 0] } }
1483
- }
1484
- },
1485
- {
1486
- $project: {
1487
- _id: 0,
1488
- customer: '$_id.customer',
1489
- month: { $dateToString: { format: '%Y-%m', date: '$_id.bucket' } },
1490
- total_revenue: 1
1491
- }
1492
- },
1493
- { $sort: { customer: 1, month: 1 } }
1494
- ]).toArray();
1495
- return { rows };
1496
- },
1497
- compare: ({ toolResult, assistantContent, expected }) => {
1498
- const errors: string[] = [];
1499
- const expectedDisplay = buildDisplayTable(expected.rows || [], { maxColumns: 50, maxRows: 5000 });
1500
- const expectedPivot = buildAssistantDatedPivotDisplay(expectedDisplay) || expectedDisplay;
1501
- const expectedParsed = parseCustomerMonthTotalsFromDisplay(expectedPivot);
1502
- const actualParsed = parseCustomerMonthTotalsFromDisplay(toolResult?.output?.display);
1503
- if (!expectedParsed || !actualParsed) {
1504
- errors.push('Could not parse customer-month totals for expected or AI display.');
1505
- }
1506
- else {
1507
- errors.push(...compareNumericMaps('Customer monthly totals (summed by month)', expectedParsed.monthTotals, actualParsed.monthTotals, 0.05));
1508
- if (expectedParsed.nonUnknownCustomers.size >= 2 && actualParsed.nonUnknownCustomers.size < 2) {
1509
- errors.push(
1510
- `Expected multiple named customers (${expectedParsed.nonUnknownCustomers.size}), but AI returned ${actualParsed.nonUnknownCustomers.size}.`
1511
- );
1512
- }
1513
- const expectedCustomerCount = new Set(expectedParsed.entries.map(entry => entry.customer.toLowerCase())).size;
1514
- const actualCustomerCount = new Set(actualParsed.entries.map(entry => entry.customer.toLowerCase())).size;
1515
- const outputRowCount = Number(toolResult?.output?.rowCount || 0);
1516
- const outputDisplayRows = Array.isArray(toolResult?.output?.display?.rows) ? toolResult.output.display.rows.length : 0;
1517
- const outputTruncated = toolResult?.output?.truncated === true || toolResult?.output?.display?.truncated === true;
1518
- if (outputTruncated && outputRowCount > outputDisplayRows && expectedCustomerCount > actualCustomerCount) {
1519
- errors.push(
1520
- `Customer breakdown display is truncated (${outputDisplayRows} shown of ${outputRowCount} rows); expected ${expectedCustomerCount} customers but output shows ${actualCustomerCount}.`
1521
- );
1522
- }
1523
- }
1524
- errors.push(...collectCommonFormattingErrors(assistantContent));
1525
- return errors;
1526
- }
1527
- },
1528
- {
1529
- id: 'invoice_revenue_monthly_customer_grid_last_6m_per_customer',
1530
- prompt: 'Break down my total revenue over the last 6 month per month per customer',
1531
- runExpected: async (context) => {
1532
- const rows = await context.db.collection('invoices').aggregate([
1533
- {
1534
- $addFields: {
1535
- effective_date: {
1536
- $ifNull: [
1537
- '$date_paid',
1538
- { $ifNull: ['$date_invoiced', '$createdAt'] }
1539
- ]
1540
- },
1541
- effective_total: { $ifNull: ['$paid_total', '$grand_total'] }
1542
- }
1543
- },
1544
- {
1545
- $match: {
1546
- $expr: {
1547
- $and: [
1548
- { $gte: ['$effective_date', context.last6FullMonthsStart] },
1549
- { $lt: ['$effective_date', context.startOfCurrentMonth] }
1550
- ]
1551
- }
1552
- }
1553
- },
1554
- {
1555
- $group: {
1556
- _id: {
1557
- customer: {
1558
- $ifNull: [
1559
- '$customer_name',
1560
- {
1561
- $ifNull: [
1562
- '$customer.fullname',
1563
- {
1564
- $ifNull: [
1565
- '$customer.name',
1566
- {
1567
- $ifNull: [{ $toString: '$id_customer' }, 'Unknown Customer']
1568
- }
1569
- ]
1570
- }
1571
- ]
1572
- }
1573
- ]
1574
- },
1575
- bucket: {
1576
- $dateTrunc: {
1577
- date: '$effective_date',
1578
- unit: 'month'
1579
- }
1580
- }
1581
- },
1582
- total_revenue: { $sum: { $ifNull: ['$effective_total', 0] } }
1583
- }
1584
- },
1585
- {
1586
- $project: {
1587
- _id: 0,
1588
- customer: '$_id.customer',
1589
- month: { $dateToString: { format: '%Y-%m', date: '$_id.bucket' } },
1590
- total_revenue: 1
1591
- }
1592
- },
1593
- { $sort: { customer: 1, month: 1 } }
1594
- ]).toArray();
1595
- return { rows };
1596
- },
1597
- compare: ({ toolResult, assistantContent, expected }) => {
1598
- const errors: string[] = [];
1599
- const expectedDisplay = buildDisplayTable(expected.rows || [], { maxColumns: 50, maxRows: 5000 });
1600
- const expectedPivot = buildAssistantDatedPivotDisplay(expectedDisplay) || expectedDisplay;
1601
- const expectedParsed = parseCustomerMonthTotalsFromDisplay(expectedPivot);
1602
- const actualParsed = parseCustomerMonthTotalsFromDisplay(toolResult?.output?.display);
1603
- if (!expectedParsed || !actualParsed) {
1604
- errors.push('Could not parse customer-month totals for expected or AI display.');
1605
- }
1606
- else {
1607
- errors.push(...compareNumericMaps('Customer monthly totals (summed by month)', expectedParsed.monthTotals, actualParsed.monthTotals, 0.05));
1608
- if (expectedParsed.nonUnknownCustomers.size >= 2 && actualParsed.nonUnknownCustomers.size < 2) {
1609
- errors.push(
1610
- `Expected multiple named customers (${expectedParsed.nonUnknownCustomers.size}), but AI returned ${actualParsed.nonUnknownCustomers.size}.`
1611
- );
1612
- }
1613
- const expectedCustomerCount = new Set(expectedParsed.entries.map(entry => entry.customer.toLowerCase())).size;
1614
- const actualCustomerCount = new Set(actualParsed.entries.map(entry => entry.customer.toLowerCase())).size;
1615
- const outputRowCount = Number(toolResult?.output?.rowCount || 0);
1616
- const outputDisplayRows = Array.isArray(toolResult?.output?.display?.rows) ? toolResult.output.display.rows.length : 0;
1617
- const outputTruncated = toolResult?.output?.truncated === true || toolResult?.output?.display?.truncated === true;
1618
- if (outputTruncated && outputRowCount > outputDisplayRows && expectedCustomerCount > actualCustomerCount) {
1619
- errors.push(
1620
- `Customer breakdown display is truncated (${outputDisplayRows} shown of ${outputRowCount} rows); expected ${expectedCustomerCount} customers but output shows ${actualCustomerCount}.`
1621
- );
1622
- }
1623
- }
1624
- errors.push(...collectCommonFormattingErrors(assistantContent));
1625
- return errors;
1626
- }
1627
- },
1628
- {
1629
- id: 'support_ticket_billable_hours_per_user_per_month_last_6m',
1630
- prompt: 'Break up the support tickets last 6 months, give me a total per user per month of the amount of hours they are billing for',
1631
- runExpected: async (context) => {
1632
- const collection = await resolveExistingCollection(context.db, ['support-tickets', 'supporttickets', 'support-ticket']);
1633
- if (!collection) {
1634
- return {
1635
- rows: [],
1636
- meta: { collection: null, basis: 'missing_collection' }
1637
- };
1638
- }
1639
- const rows = await context.db.collection(collection).aggregate([
1640
- {
1641
- $addFields: {
1642
- effective_date: { $ifNull: ['$date_created', '$createdAt'] },
1643
- effective_hours: { $ifNull: ['$billable_hours', { $ifNull: ['$estimated_billable_hours', 0] }] },
1644
- assigned_user_first: {
1645
- $let: {
1646
- vars: {
1647
- first_assigned: { $arrayElemAt: [{ $ifNull: ['$users_assigned', []] }, 0] }
1648
- },
1649
- in: {
1650
- $ifNull: ['$$first_assigned.user', '$$first_assigned.id_user']
1651
- }
1652
- }
1653
- }
1654
- }
1655
- },
1656
- {
1657
- $match: {
1658
- $expr: {
1659
- $and: [
1660
- { $gte: ['$effective_date', context.last6FullMonthsStart] },
1661
- { $lt: ['$effective_date', context.startOfCurrentMonth] }
1662
- ]
1663
- }
1664
- }
1665
- },
1666
- {
1667
- $group: {
1668
- _id: {
1669
- user: {
1670
- $ifNull: [
1671
- '$user_created',
1672
- {
1673
- $ifNull: [
1674
- '$client_user.user',
1675
- {
1676
- $ifNull: [
1677
- '$assigned_user_first',
1678
- { $ifNull: [{ $toString: '$id_user_created' }, 'Unknown User'] }
1679
- ]
1680
- }
1681
- ]
1682
- }
1683
- ]
1684
- },
1685
- bucket: {
1686
- $dateTrunc: {
1687
- date: '$effective_date',
1688
- unit: 'month'
1689
- }
1690
- }
1691
- },
1692
- billable_hours: { $sum: { $ifNull: ['$effective_hours', 0] } }
1693
- }
1694
- },
1695
- {
1696
- $project: {
1697
- _id: 0,
1698
- user: '$_id.user',
1699
- month: { $dateToString: { format: '%Y-%m', date: '$_id.bucket' } },
1700
- billable_hours: 1
1701
- }
1702
- },
1703
- { $sort: { user: 1, month: 1 } }
1704
- ]).toArray();
1705
- return {
1706
- rows,
1707
- meta: { collection, basis: 'billable_hours_or_estimated_billable_hours' }
1708
- };
1709
- },
1710
- compare: ({ toolResult, assistantContent, expected }) => {
1711
- const errors: string[] = [];
1712
- const collection = normalizeOptionalString(expected?.meta?.collection);
1713
- if (!collection) {
1714
- const rowCount = Number(toolResult?.output?.rowCount || 0);
1715
- if (rowCount > 0) {
1716
- errors.push(`Expected zero rows because support ticket collection is missing, but AI returned ${rowCount} rows.`);
1717
- }
1718
- errors.push(...collectCommonFormattingErrors(assistantContent));
1719
- return errors;
1720
- }
1721
- const expectedRows = Array.isArray(expected.rows) ? expected.rows.length : 0;
1722
- const actualRowCount = Number(toolResult?.output?.rowCount || 0);
1723
- if (expectedRows === 0 && actualRowCount === 0) {
1724
- errors.push(...collectCommonFormattingErrors(assistantContent));
1725
- return errors;
1726
- }
1727
- const expectedDisplay = buildDisplayTable(expected.rows || [], { maxColumns: 60, maxRows: 5000 });
1728
- const expectedPivot = buildAssistantDatedPivotDisplay(expectedDisplay) || expectedDisplay;
1729
- const expectedParsed = parseDimensionMonthTotalsFromDisplay(expectedPivot, ['user', 'assignee', 'assigned', 'technician', 'owner']);
1730
- const actualParsed = parseDimensionMonthTotalsFromDisplay(toolResult?.output?.display, ['user', 'assignee', 'assigned', 'technician', 'owner']);
1731
- if (!expectedParsed || !actualParsed) {
1732
- errors.push('Could not parse user-month billable-hours totals for expected or AI display.');
1733
- }
1734
- else {
1735
- errors.push(...compareNumericMaps('Support ticket billable hours by month (summed)', expectedParsed.monthTotals, actualParsed.monthTotals, 0.05));
1736
- errors.push(...compareNumericMaps('Support ticket billable hours by user (summed)', expectedParsed.dimensionTotals, actualParsed.dimensionTotals, 0.05));
1737
- if (expectedParsed.nonUnknownDimensions.size >= 2 && actualParsed.nonUnknownDimensions.size < 2) {
1738
- errors.push(
1739
- `Expected multiple named users (${expectedParsed.nonUnknownDimensions.size}), but AI returned ${actualParsed.nonUnknownDimensions.size}.`
1740
- );
1741
- }
1742
- }
1743
- errors.push(...collectCommonFormattingErrors(assistantContent));
1744
- return errors;
1745
- }
1746
- }
1747
- ];
1748
- }
1749
-
1750
- async function runCase(context: ParityContext, parityCase: ParityCase): Promise<ParityCaseResult> {
1751
- const result: ParityCaseResult = {
1752
- id: parityCase.id,
1753
- prompt: parityCase.prompt,
1754
- pass: false,
1755
- errors: []
1756
- };
1757
- try {
1758
- const ai = await runPrompt(context.methodManager, context.db, context.idUser, parityCase.prompt, context.appId);
1759
- result.conversationId = ai.conversationId;
1760
- const assistantContent = String(ai.message?.content || '');
1761
- const directive = extractDirectiveFromMessage(ai.message);
1762
- if (directive) {
1763
- result.directive = {
1764
- type: directive.type,
1765
- collection: normalizeOptionalString(directive.payload?.collection),
1766
- source: directive.source
1767
- };
1768
- }
1769
- let toolResult = ai.message?.metadata?.tool_result;
1770
- let toolSource = 'metadata.tool_result';
1771
- if (!toolResult?.input || !toolResult?.output) {
1772
- if (!directive) {
1773
- result.errors.push('No tool_result found in assistant metadata and no directive line was recoverable.');
1774
- return result;
1775
- }
1776
- const replayMethod = directive.type === 'aggregate' ? 'aiAssistantMongoAggregate' : 'aiAssistantMongoRead';
1777
- try {
1778
- const directiveReplay = await callMethodAsUser(context.methodManager, context.idUser, replayMethod, directive.payload || {});
1779
- toolResult = buildSyntheticToolResultFromDirective(directive, directiveReplay);
1780
- toolSource = `synthetic_from_${directive.source}`;
1781
- }
1782
- catch (directiveError: any) {
1783
- result.errors.push(
1784
- `No tool_result found; directive replay failed: ${String(directiveError?.message || directiveError || 'unknown error')}`
1785
- );
1786
- return result;
1787
- }
1788
- }
1789
- result.toolSource = toolSource;
1790
- result.tool = {
1791
- type: normalizeOptionalString(toolResult?.type),
1792
- collection: normalizeOptionalString(toolResult?.output?.collection) || normalizeOptionalString(toolResult?.input?.collection),
1793
- rowCount: Number(toolResult?.output?.rowCount || 0),
1794
- total: typeof toolResult?.output?.total === 'number' ? toolResult.output.total : null,
1795
- columns: Array.isArray(toolResult?.output?.columns) ? toolResult.output.columns : []
1796
- };
1797
-
1798
- if (toolSource === 'metadata.tool_result') {
1799
- const replayMethod = toolResult.type === 'mongo_agg' ? 'aiAssistantMongoAggregate' : 'aiAssistantMongoRead';
1800
- const replay = await callMethodAsUser(context.methodManager, context.idUser, replayMethod, toolResult.input || {});
1801
- const replayErrors = compareDisplayParity(toolResult?.output?.display, replay?.display);
1802
- const toolRowCount = Number(toolResult?.output?.rowCount || 0);
1803
- const replayRowCount = Array.isArray(replay?.documents)
1804
- ? replay.documents.length
1805
- : Number(replay?.display?.rowCount || 0);
1806
- if (toolRowCount > 0 && replayRowCount > 0 && toolRowCount !== replayRowCount) {
1807
- replayErrors.push(`Replay rowCount mismatch: tool_result=${toolRowCount}, replay=${replayRowCount}.`);
1808
- }
1809
- result.replay = {
1810
- pass: replayErrors.length === 0,
1811
- errors: replayErrors
1812
- };
1813
- if (replayErrors.length) {
1814
- result.errors.push(...replayErrors);
1815
- }
1816
- }
1817
- else {
1818
- result.replay = {
1819
- pass: true,
1820
- errors: []
1821
- };
1822
- }
1823
-
1824
- const expected = await parityCase.runExpected(context);
1825
- result.expected = {
1826
- rows: Array.isArray(expected?.rows) ? expected.rows.length : 0,
1827
- meta: expected?.meta
1828
- };
1829
- const caseErrors = parityCase.compare({
1830
- toolResult,
1831
- assistantContent,
1832
- expected,
1833
- context
1834
- });
1835
- if (caseErrors.length) {
1836
- result.errors.push(...caseErrors);
1837
- }
1838
-
1839
- result.pass = result.errors.length === 0;
1840
- return result;
1841
- }
1842
- catch (error: any) {
1843
- result.errors.push(String(error?.message || error || 'Unknown error'));
1844
- result.pass = false;
1845
- return result;
1846
- }
1847
- }
1848
-
1849
- async function run(): Promise<void> {
1850
- const repoRoot = process.cwd();
1851
- const defaultServerDir = path.resolve(repoRoot, '../resolveio-all/geochem/server');
1852
- const serverDir = normalizeOptionalString(process.env.AI_ASSISTANT_PARITY_SERVER_DIR) || defaultServerDir;
1853
- const settingsPath = path.join(serverDir, normalizeOptionalString(process.env.AI_ASSISTANT_PARITY_SETTINGS) || 'settings.local.json');
1854
- const appId = normalizeOptionalString(process.env.AI_ASSISTANT_PARITY_APP_ID) || path.basename(path.dirname(serverDir));
1855
- const dryRun = parseBoolean(process.env.AI_ASSISTANT_PARITY_DRY_RUN);
1856
-
1857
- if (!fs.existsSync(settingsPath)) {
1858
- throw new Error(`Settings file not found: ${settingsPath}`);
1859
- }
1860
-
1861
- const allCases = resolveParityCases();
1862
- const caseFilter = normalizeOptionalString(process.env.AI_ASSISTANT_PARITY_CASES)
1863
- .split(',')
1864
- .map(value => value.trim())
1865
- .filter(Boolean);
1866
- const selectedCases = caseFilter.length
1867
- ? allCases.filter(parityCase => caseFilter.includes(parityCase.id))
1868
- : allCases;
1869
- if (!selectedCases.length) {
1870
- throw new Error('No parity cases selected.');
1871
- }
1872
-
1873
- if (dryRun) {
1874
- console.log(JSON.stringify({
1875
- mode: 'dry-run',
1876
- serverDir,
1877
- settingsPath,
1878
- appId,
1879
- cases: selectedCases.map(parityCase => parityCase.id)
1880
- }, null, 2));
1881
- return;
1882
- }
1883
-
1884
- applyEnvFromServerDir(serverDir);
1885
- process.env.AI_ASSISTANT_WORKSPACE_ROOT = process.env.AI_ASSISTANT_WORKSPACE_ROOT || path.resolve(serverDir, '../..');
1886
- if (!process.env.OPENAI_API_KEY) {
1887
- throw new Error('OPENAI_API_KEY is required for parity E2E run.');
1888
- }
1889
-
1890
- const runtimeTmpDir = path.join(serverDir, 'tmp');
1891
- const runtimeSrcDir = path.join(serverDir, 'src');
1892
- const runtimeDir = fs.existsSync(path.join(runtimeTmpDir, 'server-app.js')) ? runtimeTmpDir : runtimeSrcDir;
1893
- const clientRoutesPath = path.join(runtimeDir, 'client-routes');
1894
- if (!fs.existsSync(clientRoutesPath) && !fs.existsSync(`${clientRoutesPath}.js`)) {
1895
- throw new Error(`client-routes module not found under runtime dir: ${runtimeDir}`);
1896
- }
1897
- const { CLIENT_ROUTES } = require(clientRoutesPath);
1898
- const serverConfig = JSON.parse(fs.readFileSync(settingsPath, 'utf8'));
1899
- const envDatabase = normalizeOptionalString(process.env.DATABASE);
1900
- const envMongoUrl = normalizeOptionalString(process.env.MONGO_URL);
1901
- const envOplogUrl = normalizeOptionalString(process.env.OPLOG_URL);
1902
- if (envDatabase) {
1903
- serverConfig.DATABASE = envDatabase;
1904
- }
1905
- if (envMongoUrl) {
1906
- serverConfig.MONGO_URL = envMongoUrl;
1907
- }
1908
- if (envOplogUrl) {
1909
- serverConfig.OPLOG_URL = envOplogUrl;
1910
- }
1911
-
1912
- const codexHome = process.env.CODEX_HOME || path.join(repoRoot, 'tmp', '.codex-ai-parity');
1913
- const codexSessions = path.join(codexHome, 'sessions');
1914
- const codexZdotdir = process.env.AI_DASHBOARD_CODEX_ZDOTDIR || path.join(repoRoot, 'tmp', '.codex-zdotdir-parity');
1915
- fs.mkdirSync(codexSessions, { recursive: true });
1916
- fs.mkdirSync(codexZdotdir, { recursive: true });
1917
- process.env.CODEX_HOME = codexHome;
1918
- process.env.AI_DASHBOARD_CODEX_ZDOTDIR = codexZdotdir;
1919
- process.env.IS_WORKERS_ENABLED = 'false';
1920
- process.env.IS_WORKER_INSTANCE = 'false';
1921
- process.env.WORKER_INDEX = '';
1922
- process.env.NODE_APP_INSTANCE = process.env.NODE_APP_INSTANCE || '8';
1923
- process.env.AI_ASSISTANT_WORKER_DEBUG = process.env.AI_ASSISTANT_WORKER_DEBUG || 'false';
1924
-
1925
- await ResolveIOServer.create(serverConfig, CLIENT_ROUTES(), appId.toUpperCase(), runtimeDir, false, false);
1926
- const mainServer = ResolveIOServer.getMainServer();
1927
- const methodManager = mainServer?.getMethodManager();
1928
- if (!methodManager) {
1929
- throw new Error('MethodManager unavailable after ResolveIOServer initialization.');
1930
- }
1931
- await methodManager.waitUntilReady(120000);
1932
-
1933
- const dbName = normalizeOptionalString(serverConfig.DATABASE) || 'resolveio';
1934
- const db = ResolveIOServer.getMongoConnection().db(dbName);
1935
- const idUser = normalizeOptionalString(process.env.AI_ASSISTANT_PARITY_USER_ID) || await findSuperAdminUserId(db);
1936
- const now = new Date();
1937
-
1938
- const context: ParityContext = {
1939
- db,
1940
- now,
1941
- startOfWeek: startOfWeekUtcMonday(now),
1942
- last6MonthsStart: addMonthsUtc(now, -6),
1943
- startOfCurrentMonth: startOfMonthUtc(now),
1944
- last6FullMonthsStart: addMonthsUtc(startOfMonthUtc(now), -6),
1945
- methodManager,
1946
- idUser,
1947
- appId
1948
- };
1949
-
1950
- const results: ParityCaseResult[] = [];
1951
- for (const parityCase of selectedCases) {
1952
- const caseResult = await runCase(context, parityCase);
1953
- results.push(caseResult);
1954
- const status = caseResult.pass ? 'PASS' : 'FAIL';
1955
- const message = caseResult.errors.length ? `error=${caseResult.errors[0]}` : '';
1956
- console.log(`${status} ${parityCase.id} ${message}`.trim());
1957
- }
1958
-
1959
- const passed = results.filter(result => result.pass).length;
1960
- const failed = results.length - passed;
1961
- const output = {
1962
- now: now.toISOString(),
1963
- serverDir,
1964
- settingsPath,
1965
- appId,
1966
- database: dbName,
1967
- total: results.length,
1968
- passed,
1969
- failed,
1970
- results
1971
- };
1972
- const outputPath = path.join(repoRoot, 'tmp', 'ai-assistant-data-parity-e2e-output.json');
1973
- fs.mkdirSync(path.dirname(outputPath), { recursive: true });
1974
- fs.writeFileSync(outputPath, JSON.stringify(output, null, 2));
1975
- console.log(`ai assistant data parity e2e: ${passed}/${results.length} passed`);
1976
- console.log(`wrote ${outputPath}`);
1977
- if (failed > 0) {
1978
- process.exitCode = 1;
1979
- }
1980
- }
1981
-
1982
- run()
1983
- .then(() => {
1984
- process.exit(process.exitCode ?? 0);
1985
- })
1986
- .catch((error) => {
1987
- console.error(error);
1988
- process.exit(1);
1989
- });