@resolveio/server-lib 22.3.197 → 22.3.199

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 (745) 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 +188 -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 +30 -0
  176. package/managers/openai-usage-ledger.manager.js +142 -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 +338 -0
  200. package/methods/ai-terminal.js +23454 -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 +30 -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 +127 -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 +109 -0
  462. package/util/ai-run-evidence-adapters.js +4410 -0
  463. package/util/ai-run-evidence-adapters.js.map +1 -0
  464. package/util/ai-run-evidence-dashboard.d.ts +88 -0
  465. package/util/ai-run-evidence-dashboard.js +343 -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 +1018 -0
  469. package/util/ai-run-evidence-eval.js.map +1 -0
  470. package/util/ai-run-evidence.d.ts +244 -0
  471. package/util/ai-run-evidence.js +852 -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-manager-autopilot.d.ts +210 -0
  477. package/util/ai-runner-manager-autopilot.js +642 -0
  478. package/util/ai-runner-manager-autopilot.js.map +1 -0
  479. package/util/ai-runner-manager-policy.d.ts +787 -0
  480. package/util/ai-runner-manager-policy.js +3342 -0
  481. package/util/ai-runner-manager-policy.js.map +1 -0
  482. package/util/ai-runner-qa-auth.d.ts +5 -0
  483. package/util/ai-runner-qa-auth.js +822 -0
  484. package/util/ai-runner-qa-auth.js.map +1 -0
  485. package/util/ai-runner-qa-tools.d.ts +26 -0
  486. package/util/ai-runner-qa-tools.js +3029 -0
  487. package/util/ai-runner-qa-tools.js.map +1 -0
  488. package/util/aicoder-runner-v6.d.ts +424 -0
  489. package/util/aicoder-runner-v6.js +2325 -0
  490. package/util/aicoder-runner-v6.js.map +1 -0
  491. package/util/common.d.ts +31 -0
  492. package/util/common.js +683 -0
  493. package/util/common.js.map +1 -0
  494. package/util/customer-portal-password.d.ts +13 -0
  495. package/util/customer-portal-password.js +209 -0
  496. package/util/customer-portal-password.js.map +1 -0
  497. package/util/error-reporter.d.ts +52 -0
  498. package/util/error-reporter.js +326 -0
  499. package/util/error-reporter.js.map +1 -0
  500. package/util/error-tracking.d.ts +13 -0
  501. package/util/error-tracking.js +120 -0
  502. package/util/error-tracking.js.map +1 -0
  503. package/util/openai-usage-cost.d.ts +6 -0
  504. package/util/openai-usage-cost.js +103 -0
  505. package/util/openai-usage-cost.js.map +1 -0
  506. package/util/report-builder-unwinds.d.ts +15 -0
  507. package/util/report-builder-unwinds.js +156 -0
  508. package/util/report-builder-unwinds.js.map +1 -0
  509. package/util/runner-process-janitor.d.ts +27 -0
  510. package/util/runner-process-janitor.js +208 -0
  511. package/util/runner-process-janitor.js.map +1 -0
  512. package/util/schema-report-builder.d.ts +6 -0
  513. package/util/schema-report-builder.js +481 -0
  514. package/util/schema-report-builder.js.map +1 -0
  515. package/util/slow-query-reporter.d.ts +28 -0
  516. package/util/slow-query-reporter.js +226 -0
  517. package/util/slow-query-reporter.js.map +1 -0
  518. package/util/subscription-dependency-context.d.ts +34 -0
  519. package/util/subscription-dependency-context.js +1283 -0
  520. package/util/subscription-dependency-context.js.map +1 -0
  521. package/util/support-runner-v5.d.ts +1055 -0
  522. package/util/support-runner-v5.js +4808 -0
  523. package/util/support-runner-v5.js.map +1 -0
  524. package/util/tokenizer.d.ts +5 -0
  525. package/util/tokenizer.js +41 -0
  526. package/util/tokenizer.js.map +1 -0
  527. package/workers/codex-runner.worker.d.ts +1 -0
  528. package/workers/codex-runner.worker.js +192 -0
  529. package/workers/codex-runner.worker.js.map +1 -0
  530. package/.nodemon.json +0 -5
  531. package/.vscode/settings.json +0 -21
  532. package/AGENTS.md +0 -195
  533. package/README.md +0 -22
  534. package/build_package.sh +0 -5
  535. package/compileDTS.pl +0 -64
  536. package/docs/ai-assistant-nightly-eval.md +0 -65
  537. package/docs/ai-assistant-preflight-checklist.md +0 -23
  538. package/docs/ai-assistant-report-builder-bridge-playbook.md +0 -115
  539. package/eslint-plugin-custom/index.js +0 -7
  540. package/eslint-plugin-custom/rules/no-filter-zero-index.js +0 -44
  541. package/eslint.config.js +0 -103
  542. package/gulpfile.js +0 -216
  543. package/methodAndPublicationListGenerator.py +0 -375
  544. package/mongodbensurers.js +0 -2
  545. package/mongostop.js +0 -3
  546. package/scripts/cleanup-bypassed-callmethod-logs.js +0 -616
  547. package/settings.development.json +0 -25
  548. package/settings.development.redacted.json +0 -25
  549. package/src/.env +0 -12
  550. package/src/ai/assistant-core-heuristics.ts +0 -379
  551. package/src/ai/resolveio-platform-intelligence-memory-corpus.ts +0 -185
  552. package/src/ai/resolveio-platform-intelligence-memory.ts +0 -325
  553. package/src/ai/resolveio-platform-intelligence.ts +0 -462
  554. package/src/client-server-app.ts +0 -12
  555. package/src/collections/ai-run.collection.ts +0 -117
  556. package/src/collections/ai-terminal-conversation.collection.ts +0 -91
  557. package/src/collections/ai-terminal-issue-report.collection.ts +0 -99
  558. package/src/collections/ai-terminal-message.collection.ts +0 -77
  559. package/src/collections/app-setting.collection.ts +0 -104
  560. package/src/collections/app-status.collection.ts +0 -58
  561. package/src/collections/communication-metric.collection.ts +0 -84
  562. package/src/collections/counter.collection.ts +0 -56
  563. package/src/collections/cron-job-history.collection.ts +0 -94
  564. package/src/collections/cron-job.collection.ts +0 -92
  565. package/src/collections/customer-notification.collection.ts +0 -131
  566. package/src/collections/customer-portal-password.collection.ts +0 -76
  567. package/src/collections/email-history.collection.ts +0 -134
  568. package/src/collections/email-verified.collection.ts +0 -62
  569. package/src/collections/file.collection.ts +0 -74
  570. package/src/collections/flag-update.collection.ts +0 -57
  571. package/src/collections/flag.collection.ts +0 -57
  572. package/src/collections/log-method-latency.collection.ts +0 -77
  573. package/src/collections/log-subscription.collection.ts +0 -80
  574. package/src/collections/log.collection.ts +0 -93
  575. package/src/collections/logged-in-users.collection.ts +0 -67
  576. package/src/collections/monitor-cpu.collection.ts +0 -65
  577. package/src/collections/monitor-function.collection.ts +0 -74
  578. package/src/collections/monitor-memory.collection.ts +0 -77
  579. package/src/collections/monitor-mongo.collection.ts +0 -71
  580. package/src/collections/notification.collection.ts +0 -57
  581. package/src/collections/openai-usage-ledger.collection.ts +0 -131
  582. package/src/collections/report-builder-dashboard-builder.collection.ts +0 -109
  583. package/src/collections/report-builder-library.collection.ts +0 -89
  584. package/src/collections/report-builder-report.collection.ts +0 -184
  585. package/src/collections/user-group.collection.ts +0 -89
  586. package/src/collections/user-guide.collection.ts +0 -57
  587. package/src/collections/user.collection.ts +0 -181
  588. package/src/cron/cron.ts +0 -117
  589. package/src/fixtures/cron-jobs.ts +0 -95
  590. package/src/fixtures/init.ts +0 -35
  591. package/src/http/auth.ts +0 -818
  592. package/src/http/health.ts +0 -7
  593. package/src/http/home.ts +0 -90
  594. package/src/http/slow-query-publication.ts +0 -49
  595. package/src/index.ts +0 -1
  596. package/src/managers/ai-assistant-codex-manager.manager.ts +0 -1131
  597. package/src/managers/ai-run-evidence.manager.ts +0 -264
  598. package/src/managers/communication-metric.manager.ts +0 -82
  599. package/src/managers/cron.manager.ts +0 -333
  600. package/src/managers/customer-notification-content.manager.ts +0 -236
  601. package/src/managers/diagnostic-manager-bootstrap.ts +0 -165
  602. package/src/managers/error-auto-fix.manager.ts +0 -2767
  603. package/src/managers/local-log.manager.ts +0 -113
  604. package/src/managers/method.manager.ts +0 -1857
  605. package/src/managers/mongo.manager.ts +0 -4575
  606. package/src/managers/monitor.manager.ts +0 -507
  607. package/src/managers/openai-usage-ledger.manager.ts +0 -112
  608. package/src/managers/slow-query-verifier.manager.ts +0 -3590
  609. package/src/managers/slow-query.manager.ts +0 -519
  610. package/src/managers/subscription.manager.ts +0 -3128
  611. package/src/managers/websocket.manager.ts +0 -746
  612. package/src/managers/worker-dispatcher.manager.ts +0 -1360
  613. package/src/managers/worker-server.manager.ts +0 -536
  614. package/src/methods/accounts.ts +0 -532
  615. package/src/methods/ai-terminal.ts +0 -23825
  616. package/src/methods/app-settings.ts +0 -114
  617. package/src/methods/aws.ts +0 -649
  618. package/src/methods/collections.ts +0 -641
  619. package/src/methods/counters.ts +0 -69
  620. package/src/methods/cron-jobs.ts +0 -2614
  621. package/src/methods/customer-notifications.ts +0 -458
  622. package/src/methods/diagnostics.ts +0 -616
  623. package/src/methods/flag-updates.ts +0 -7
  624. package/src/methods/flags.ts +0 -7
  625. package/src/methods/logs.ts +0 -657
  626. package/src/methods/mongo-explorer.ts +0 -1880
  627. package/src/methods/monitor.ts +0 -540
  628. package/src/methods/pdf.ts +0 -1236
  629. package/src/methods/publications.ts +0 -129
  630. package/src/methods/report-builder.ts +0 -3300
  631. package/src/methods/support.ts +0 -335
  632. package/src/models/ai-run.model.ts +0 -27
  633. package/src/models/ai-terminal-conversation.model.ts +0 -19
  634. package/src/models/ai-terminal-issue-report.model.ts +0 -21
  635. package/src/models/ai-terminal-message.model.ts +0 -24
  636. package/src/models/app-setting.model.ts +0 -17
  637. package/src/models/collection-document.model.ts +0 -24
  638. package/src/models/communication-metric.model.ts +0 -23
  639. package/src/models/cron-job-history.model.ts +0 -16
  640. package/src/models/cron-job.model.ts +0 -15
  641. package/src/models/customer-notification.model.ts +0 -28
  642. package/src/models/customer-portal-password.model.ts +0 -12
  643. package/src/models/dialog.model.ts +0 -25
  644. package/src/models/log-method-latency.model.ts +0 -11
  645. package/src/models/log.model.ts +0 -19
  646. package/src/models/method.model.ts +0 -25
  647. package/src/models/monitor-function.model.ts +0 -16
  648. package/src/models/monitor-memory.model.ts +0 -17
  649. package/src/models/monitor-mongo.model.ts +0 -15
  650. package/src/models/openai-usage-ledger.model.ts +0 -56
  651. package/src/models/pagination.model.ts +0 -35
  652. package/src/models/permission.model.ts +0 -14
  653. package/src/models/report-builder-dashboard-builder.model.ts +0 -29
  654. package/src/models/report-builder-library.model.ts +0 -20
  655. package/src/models/report-builder-report.model.ts +0 -136
  656. package/src/models/report-builder.model.ts +0 -68
  657. package/src/models/select-data-label.model.ts +0 -9
  658. package/src/models/server-message.model.ts +0 -31
  659. package/src/models/slow-query-report.model.ts +0 -23
  660. package/src/models/subscription.model.ts +0 -73
  661. package/src/models/support-ticket.model.ts +0 -104
  662. package/src/models/user-group.model.ts +0 -24
  663. package/src/models/user.model.ts +0 -96
  664. package/src/private/images/ResolveIO.png +0 -0
  665. package/src/publications/ai-terminal.ts +0 -73
  666. package/src/publications/app-settings.ts +0 -25
  667. package/src/publications/app-status.ts +0 -13
  668. package/src/publications/cron-jobs.ts +0 -40
  669. package/src/publications/customer-notifications.ts +0 -101
  670. package/src/publications/files.ts +0 -33
  671. package/src/publications/flags-update.ts +0 -19
  672. package/src/publications/flags.ts +0 -19
  673. package/src/publications/logs.ts +0 -163
  674. package/src/publications/notifications.ts +0 -13
  675. package/src/publications/report-builder-dashboard-builders.ts +0 -39
  676. package/src/publications/report-builder-libraries.ts +0 -41
  677. package/src/publications/report-builder-reports.ts +0 -47
  678. package/src/publications/super-admin.ts +0 -13
  679. package/src/publications/user-groups.ts +0 -12
  680. package/src/publications/user-guides.ts +0 -12
  681. package/src/resolveio-server-app.ts +0 -617
  682. package/src/server-app.ts +0 -3354
  683. package/src/services/codex-client.ts +0 -1231
  684. package/src/services/openai-client.ts +0 -265
  685. package/src/types/error-report.ts +0 -26
  686. package/src/types/js-tiktoken.d.ts +0 -11
  687. package/src/types/slow-query-report.ts +0 -28
  688. package/src/util/ai-qa-policy.ts +0 -925
  689. package/src/util/ai-run-evidence-adapters.ts +0 -4642
  690. package/src/util/ai-run-evidence-dashboard.ts +0 -323
  691. package/src/util/ai-run-evidence-eval.ts +0 -1057
  692. package/src/util/ai-run-evidence.ts +0 -1185
  693. package/src/util/ai-runner-artifacts.ts +0 -586
  694. package/src/util/ai-runner-manager-autopilot.ts +0 -961
  695. package/src/util/ai-runner-manager-policy.ts +0 -4806
  696. package/src/util/ai-runner-qa-auth.ts +0 -821
  697. package/src/util/ai-runner-qa-tools.ts +0 -3045
  698. package/src/util/aicoder-runner-v6.ts +0 -2979
  699. package/src/util/common.ts +0 -649
  700. package/src/util/customer-portal-password.ts +0 -183
  701. package/src/util/error-reporter.ts +0 -332
  702. package/src/util/error-tracking.ts +0 -79
  703. package/src/util/openai-usage-cost.ts +0 -114
  704. package/src/util/report-builder-unwinds.ts +0 -180
  705. package/src/util/runner-process-janitor.ts +0 -219
  706. package/src/util/schema-report-builder.ts +0 -448
  707. package/src/util/slow-query-reporter.ts +0 -216
  708. package/src/util/subscription-dependency-context.ts +0 -1096
  709. package/src/util/support-runner-v5.ts +0 -6573
  710. package/src/util/tokenizer.ts +0 -38
  711. package/src/workers/codex-runner.worker.ts +0 -142
  712. package/start_server.sh +0 -5
  713. package/tests/ai-assistant-corpus-build.ts +0 -484
  714. package/tests/ai-assistant-corpus-replay-e2e.ts +0 -774
  715. package/tests/ai-assistant-data-parity-e2e.ts +0 -1989
  716. package/tests/ai-assistant-eval-triage.ts +0 -831
  717. package/tests/ai-assistant-openai-e2e.ts +0 -1061
  718. package/tests/ai-assistant-openai-git-e2e.ts +0 -155
  719. package/tests/ai-assistant-preflight-matrix.ts +0 -215
  720. package/tests/ai-assistant-routing-eval.test.ts +0 -560
  721. package/tests/ai-assistant-snf-live-eval.ts +0 -975
  722. package/tests/ai-assistant-utils.test.ts +0 -3057
  723. package/tests/ai-manager-autopilot-snapshot.test.ts +0 -193
  724. package/tests/ai-manager-recovery-checkpoint.test.ts +0 -1287
  725. package/tests/ai-run-eval.test.ts +0 -132
  726. package/tests/ai-run-evidence.test.ts +0 -2129
  727. package/tests/ai-runner-contract.test.ts +0 -488
  728. package/tests/aicoder-runner-v6.test.ts +0 -751
  729. package/tests/error-reporter.test.ts +0 -145
  730. package/tests/method-publication-generator.test.ts +0 -46
  731. package/tests/report-builder-linking.test.ts +0 -79
  732. package/tests/resolveio-platform-intelligence.test.ts +0 -352
  733. package/tests/server-app-cron-owner.test.ts +0 -127
  734. package/tests/subscription-connect-race.test.ts +0 -158
  735. package/tests/subscription-dependency-context.test.ts +0 -324
  736. package/tests/subscription-manager-collection-tracking.test.ts +0 -86
  737. package/tests/subscription-manager-invalidation.test.ts +0 -86
  738. package/tests/support-runner-v5.test.ts +0 -1473
  739. package/tsconfig.json +0 -34
  740. /package/{src/private → private}/email-templates/enrollment.html +0 -0
  741. /package/{src/private → private}/email-templates/forgot-password.html +0 -0
  742. /package/{src/private → private}/email-templates/support-ticket-deleted.html +0 -0
  743. /package/{src/private → private}/email-templates/support-ticket-modified.html +0 -0
  744. /package/{src/private → private}/email-templates/support-ticket.html +0 -0
  745. /package/{src/public_api.ts → public_api.d.ts} +0 -0
@@ -1,3057 +0,0 @@
1
- import { ObjectId } from 'mongodb';
2
- import {
3
- buildAssistantChangeHistorySummaryFromCommits,
4
- buildAssistantSystemPromptForTesting,
5
- applyAssistantDisplayTableToResponseForTesting,
6
- buildAssistantToolFallbackResponseForTesting,
7
- buildAssistantAnswerQualityFromExecution,
8
- buildAssistantWorkspaceRootCandidates,
9
- buildAssistantDatedPivotDisplay,
10
- buildAssistantSchemaHints,
11
- buildAssistantToolRequest,
12
- deriveAssistantCommandExecutionStatus,
13
- evaluateAssistantGuardrailsForTesting,
14
- buildAssistantInvoiceCustomerLabelExpr,
15
- buildDisplayTable,
16
- executeAiAssistantMongoAggregate,
17
- executeAiAssistantMongoRead,
18
- extractAssistantMongoDirective,
19
- flattenForTable,
20
- formatDisplayTableMarkdown,
21
- normalizeAssistantMonthlyCalendarWindowPipeline,
22
- normalizeAssistantAggregatePipeline,
23
- expandAggregateDateMatchFallbackForTesting,
24
- normalizeAssistantNowExprPlaceholders,
25
- repairAssistantPositionalPathSegmentsInPipeline,
26
- repairAssistantFieldPathReferenceInPipeline,
27
- normalizeIdsForTargetField,
28
- rankAssistantNavigationRoutes,
29
- resolveAssistantCollectionOverride,
30
- resolveAssistantCollectionNameForTesting,
31
- resolveCollectionOverrideWithContextForTesting,
32
- resolveAssistantAvailableCrossCollectionFallbacksFromNames,
33
- resolveAssistantAppCollectionHintCandidates,
34
- resolveAssistantHeuristicDirectiveForTesting,
35
- resolveAssistantCrossCollectionFallbackCandidates,
36
- resolveAssistantReportBuilderBridgeCollection,
37
- isAssistantDeterministicHeuristicEnabledForApp,
38
- shouldAcceptAssistantFallbackDocuments,
39
- shouldEnforceAssistantDatedDirective,
40
- shouldLockAssistantRequestedCollectionForExecutionForTesting,
41
- shouldPreserveAssistantProbeCollectionForTesting,
42
- rewriteMatchExpressionsToExpr,
43
- rewriteEmbeddedMatchObjects,
44
- resolveCodexThoughtLevel,
45
- resolveAssistantReadDisplayMaxRows,
46
- resolveReadMultiTermJobRegexFallbackForTesting,
47
- serializeMongoValue,
48
- shouldRunAssistantPlanner,
49
- shouldUseAssistantChangeHistoryFastPath,
50
- shouldUseAssistantNavigationFastPath,
51
- stripQueryFieldPathsDeep,
52
- stripScopedFieldsFromPipeline
53
- } from '../src/methods/ai-terminal';
54
- import { ResolveIOServer } from '../src/resolveio-server-app';
55
- import * as UserCollection from '../src/collections/user.collection';
56
-
57
- function assert(condition: boolean, message: string) {
58
- if (!condition) {
59
- throw new Error(message);
60
- }
61
- }
62
-
63
- function testDirectiveParsing() {
64
- const content = [
65
- 'Summary line',
66
- 'MONGO_READ: {"collection":"users","query":{"active":true},"options":{"limit":5}}',
67
- 'More context',
68
- 'MONGO_AGG: {"collection":"invoices","pipeline":[{"$match":{"status":"Open"}}]}'
69
- ].join('\n');
70
- const result = extractAssistantMongoDirective(content);
71
- assert(!!result, 'Expected directive to be detected');
72
- assert(result?.type === 'aggregate', 'Expected last directive to win');
73
- assert(result?.payload?.collection === 'invoices', 'Expected payload collection to parse');
74
- assert(!result?.cleaned.includes('MONGO_READ'), 'Expected cleaned content to remove directives');
75
- assert(!result?.cleaned.includes('MONGO_AGG'), 'Expected cleaned content to remove directives');
76
- }
77
-
78
- function testReportBuilderDirectiveParsing() {
79
- const content = [
80
- 'Summary line',
81
- 'REPORT_BUILDER_READ: {"collection":"users","query":{"active":true},"options":{"limit":5}}',
82
- 'REPORT_BUILDER_AGG: {"collection":"invoices","pipeline":[{"$match":{"status":"Open"}}]}'
83
- ].join('\n');
84
- const result = extractAssistantMongoDirective(content);
85
- assert(!!result, 'Expected report builder directive to be detected');
86
- assert(result?.type === 'aggregate', 'Expected report builder aggregate directive to win');
87
- assert(result?.payload?.collection === 'invoices', 'Expected report builder payload collection to parse');
88
- assert(!result?.cleaned.includes('REPORT_BUILDER_READ'), 'Expected cleaned content to remove report builder read directive');
89
- assert(!result?.cleaned.includes('REPORT_BUILDER_AGG'), 'Expected cleaned content to remove report builder agg directive');
90
- }
91
-
92
- function testFlattenForTable() {
93
- const flattened = flattenForTable({
94
- _id: { status: 'Open', type: 'A' },
95
- count: 3
96
- }, { includeGroupFromId: true });
97
- assert(flattened.status === 'Open', 'Expected _id.status flattened to status');
98
- assert(flattened.type === 'A', 'Expected _id.type flattened to type');
99
- assert(flattened.count === 3, 'Expected count retained');
100
- }
101
-
102
- function testDisplayTableDefaults() {
103
- const docs = [
104
- {
105
- _id: { toHexString: () => 'abcdef123456' },
106
- name: 'Alpha',
107
- createdAt: new Date('2024-01-01T00:00:00.000Z'),
108
- updatedAt: new Date('2024-01-02T00:00:00.000Z'),
109
- id_client: 'C1',
110
- __v: 0
111
- },
112
- {
113
- _id: { toHexString: () => '123456abcdef' },
114
- name: 'Beta',
115
- createdAt: new Date('2024-01-03T00:00:00.000Z'),
116
- updatedAt: new Date('2024-01-04T00:00:00.000Z'),
117
- id_client: 'C1',
118
- __v: 0
119
- }
120
- ];
121
- const display = buildDisplayTable(docs);
122
- assert(display.columns.includes('Created At'), 'Expected Created At column');
123
- assert(display.columns.includes('Updated At'), 'Expected Updated At column');
124
- assert(!display.columns.some(col => col.toLowerCase() === 'id_client'), 'Expected id_client hidden');
125
- assert(!display.columns.some(col => col.toLowerCase() === '_id'), 'Expected _id hidden');
126
- assert(!display.columns.some(col => col.toLowerCase() === '__v'), 'Expected __v hidden');
127
- }
128
-
129
- function testSerializeMongoValue() {
130
- const value = serializeMongoValue(['A', 'B', 'C', 'D']);
131
- assert(typeof value === 'string', 'Expected array to serialize to string');
132
- assert(String(value).includes('...'), 'Expected array preview to be truncated');
133
- }
134
-
135
- function testDisplayMarkdownTable() {
136
- const display = buildDisplayTable([
137
- { name: 'Alpha', status: 'Active' },
138
- { name: 'Beta', status: 'Paused' }
139
- ]);
140
- const markdown = formatDisplayTableMarkdown(display);
141
- assert(markdown.includes('| name |') || markdown.includes('| Name |'), 'Expected markdown header row');
142
- assert(markdown.split('\n').length >= 3, 'Expected markdown table rows');
143
- }
144
-
145
- function testAssistantToolFallbackResponseIncludesUsefulLeadSummary() {
146
- const response = buildAssistantToolFallbackResponseForTesting({
147
- type: 'mongo_agg',
148
- input: { collection: 'work-order-dynamics' },
149
- output: {
150
- collection: 'work-order-dynamics',
151
- rowCount: 2,
152
- columns: ['Customer', 'Active', 'Closed', 'Completed', 'Work Orders'],
153
- display: {
154
- columns: ['Customer', 'Active', 'Closed', 'Completed', 'Work Orders'],
155
- rows: [
156
- { Customer: 'Avid', Active: 0, Closed: 4, Completed: 99, 'Work Orders': 217 },
157
- { Customer: 'Beta', Active: 0, Closed: 0, Completed: 10, 'Work Orders': 11 }
158
- ],
159
- rowCount: 2,
160
- truncated: false,
161
- includeIds: false
162
- }
163
- }
164
- });
165
- assert(
166
- response.includes('I found 2 matching rows from work-order-dynamics.'),
167
- 'Expected fallback response to state row count and source in plain language'
168
- );
169
- assert(
170
- response.includes('Top row: Customer Avid, Work Orders 217.'),
171
- 'Expected fallback response to summarize the leading row before the table'
172
- );
173
- assert(response.includes('| Customer | Active | Closed | Completed | Work Orders |'), 'Expected fallback response to include table');
174
- }
175
-
176
- function testAssistantToolFallbackResponseExplainsZeroRows() {
177
- const response = buildAssistantToolFallbackResponseForTesting({
178
- type: 'mongo_agg',
179
- input: { collection: 'work-order-dynamics' },
180
- output: {
181
- collection: 'work-order-dynamics',
182
- rowCount: 0,
183
- columns: ['Customer', 'Work Orders'],
184
- display: {
185
- columns: ['Customer', 'Work Orders'],
186
- rows: [],
187
- rowCount: 0,
188
- truncated: false,
189
- includeIds: false
190
- }
191
- }
192
- });
193
- assert(
194
- response.includes('I checked the data') && response.includes('no matching rows were returned.'),
195
- 'Expected zero-row fallback response to explain successful zero-result execution'
196
- );
197
- assert(response.includes('- Rows returned: 0'), 'Expected zero-row fallback response to include row count');
198
- }
199
-
200
- function testBuildAssistantAnswerQualityFromExecution() {
201
- const rowEvidence = buildAssistantAnswerQualityFromExecution({
202
- requestMessage: 'Show revenue by month for the last 6 months.',
203
- dataQuestion: true,
204
- reportStyleIntent: 'dated',
205
- requestedTimeGrain: 'month',
206
- toolResult: {
207
- type: 'mongo_agg',
208
- input: { collection: 'invoices', pipeline: [{ $match: { status: 'Paid' } }] },
209
- output: {
210
- collection: 'invoices',
211
- rowCount: 2,
212
- columns: ['Month', 'Revenue'],
213
- display: { columns: ['Month', 'Revenue'], rows: [{ Month: '2026-05', Revenue: 100 }], rowCount: 1 },
214
- verification: {
215
- metrics: {
216
- window: {
217
- startDate: '2025-12-01',
218
- endDate: '2026-05-31',
219
- months: 6,
220
- type: 'full_month'
221
- }
222
- }
223
- }
224
- }
225
- },
226
- now: '2026-06-16T12:00:00.000Z'
227
- });
228
- assert(rowEvidence.queryStatus === 'ok', 'Expected successful tool execution to become ok queryStatus');
229
- assert(rowEvidence.requiresDateWindow === true, 'Expected dated request to require date window evidence');
230
- assert(rowEvidence.dateWindow?.startDate === '2025-12-01', 'Expected date-window start evidence');
231
- assert(rowEvidence.dateWindow?.endDate === '2026-05-31', 'Expected date-window end evidence');
232
- assert(rowEvidence.confidenceLevel === 'high', 'Expected successful data answer to be high confidence');
233
- assert(Array.isArray(rowEvidence.citationRefs) && rowEvidence.citationRefs.length === 1, 'Expected tool evidence citation');
234
- assert(rowEvidence.requiresQueryEvidence === true, 'Expected data answers to require structured query evidence');
235
- assert(Array.isArray(rowEvidence.queryExecutions) && rowEvidence.queryExecutions.length === 1, 'Expected structured query execution proof');
236
- assert(rowEvidence.queryExecutions[0].id === rowEvidence.citationRefs[0], 'Expected query execution id to match cited tool evidence');
237
- assert(rowEvidence.queryExecutions[0].tool === 'aiAssistantMongoAggregate', 'Expected aggregate tool proof');
238
- assert(rowEvidence.queryExecutions[0].resultCount === 2, 'Expected resultCount proof');
239
-
240
- const noDataEvidence = buildAssistantAnswerQualityFromExecution({
241
- requestMessage: 'Show invoices for this customer this month.',
242
- dataQuestion: true,
243
- toolResult: {
244
- type: 'mongo_read',
245
- input: { collection: 'invoices', query: { id_customer: 'cust-1' } },
246
- output: {
247
- collection: 'invoices',
248
- rowCount: 0,
249
- columns: ['Invoice'],
250
- display: { columns: ['Invoice'], rows: [], rowCount: 0 }
251
- }
252
- },
253
- now: '2026-06-16T12:00:00.000Z'
254
- });
255
- assert(noDataEvidence.queryStatus === 'no_data', 'Expected zero rows to become no_data');
256
- assert(noDataEvidence.noDataConfirmed === true, 'Expected zero-row tool result to confirm no-data');
257
- assert(noDataEvidence.confidenceLevel === 'medium', 'Expected no-data answer confidence to be medium');
258
- assert(Array.isArray(noDataEvidence.queryExecutions) && noDataEvidence.queryExecutions.length === 1, 'Expected no-data answers to retain query execution proof');
259
- assert(noDataEvidence.queryExecutions[0].queryStatus === 'no_data', 'Expected no-data query proof status');
260
- assert(noDataEvidence.queryExecutions[0].resultCount === 0, 'Expected no-data resultCount proof');
261
- assert(Array.isArray(noDataEvidence.queryEvidenceRefs) && noDataEvidence.queryEvidenceRefs.length === 1, 'Expected no-data query evidence refs');
262
-
263
- const permissionEvidence = buildAssistantAnswerQualityFromExecution({
264
- requestMessage: 'Show invoice totals.',
265
- dataQuestion: true,
266
- toolExecution: {
267
- permissionDenied: true,
268
- outcome: { error: 'AI assistant report builder bridge: Access denied.' }
269
- },
270
- now: '2026-06-16T12:00:00.000Z'
271
- });
272
- assert(permissionEvidence.queryStatus === 'permission_error', 'Expected permission failures to stay distinct from no-data');
273
- assert(permissionEvidence.requiresHumanReview === true, 'Expected permission failures to require review');
274
- assert(permissionEvidence.canAnswerCustomer === false, 'Expected permission failures to block customer answer');
275
- assert(permissionEvidence.requiresQueryEvidence === true, 'Expected failed data answers to keep query-evidence requirement visible');
276
-
277
- const nonDataEvidence = buildAssistantAnswerQualityFromExecution({
278
- requestMessage: 'Say hello.',
279
- dataQuestion: false,
280
- now: '2026-06-16T12:00:00.000Z'
281
- });
282
- assert(nonDataEvidence.queryStatus === 'ok', 'Expected non-data assistant answer to stay ok without tools');
283
- assert(nonDataEvidence.requiresQueryEvidence === false, 'Expected non-data answers to avoid query evidence requirement');
284
- assert(!Array.isArray(nonDataEvidence.queryExecutions), 'Expected non-data answers to avoid synthetic query proof');
285
- }
286
-
287
- function testAssistantZeroRowDisplayDoesNotAppendSyntheticTable() {
288
- const response = applyAssistantDisplayTableToResponseForTesting(
289
- 'The query ran successfully against jobs, but no matching rows were returned.\n- Source: jobs\n- Rows returned: 0',
290
- {
291
- columns: ['Job', 'Customer', 'Chemical', 'Quantity Sent'],
292
- rows: [],
293
- rowCount: 0,
294
- truncated: false,
295
- includeIds: false
296
- }
297
- );
298
- assert(!response.includes('| Job |'), 'Expected zero-row responses to omit synthetic display tables');
299
- assert(!response.includes('No rows matched | 0'), 'Expected zero-row responses to avoid fake table rows');
300
- assert(response.includes('Rows returned: 0'), 'Expected zero-row response summary to remain intact');
301
- }
302
-
303
- function testDisplayMarkdownTableDefaultsToShortDateFormat() {
304
- const priorInline = process.env.AI_ASSISTANT_APP_HEURISTICS;
305
- const priorFile = process.env.AI_ASSISTANT_APP_HEURISTICS_FILE;
306
- const priorAppId = process.env.AI_ASSISTANT_APP_ID;
307
- try {
308
- delete process.env.AI_ASSISTANT_APP_HEURISTICS_FILE;
309
- delete process.env.AI_ASSISTANT_APP_HEURISTICS;
310
- delete process.env.AI_ASSISTANT_APP_ID;
311
- const display = buildDisplayTable([
312
- { day_utc: '2026-02-25T00:00:00.000Z', completed_count: 4 }
313
- ]);
314
- const markdown = formatDisplayTableMarkdown(display);
315
- assert(
316
- markdown.includes('02/25/2026'),
317
- 'Expected default date format to render dates as MM/DD/YYYY'
318
- );
319
- assert(
320
- !markdown.includes('2026-02-25'),
321
- 'Expected default date format to avoid YYYY-MM-DD display dates'
322
- );
323
- }
324
- finally {
325
- if (priorInline === undefined) {
326
- delete process.env.AI_ASSISTANT_APP_HEURISTICS;
327
- }
328
- else {
329
- process.env.AI_ASSISTANT_APP_HEURISTICS = priorInline;
330
- }
331
- if (priorFile === undefined) {
332
- delete process.env.AI_ASSISTANT_APP_HEURISTICS_FILE;
333
- }
334
- else {
335
- process.env.AI_ASSISTANT_APP_HEURISTICS_FILE = priorFile;
336
- }
337
- if (priorAppId === undefined) {
338
- delete process.env.AI_ASSISTANT_APP_ID;
339
- }
340
- else {
341
- process.env.AI_ASSISTANT_APP_ID = priorAppId;
342
- }
343
- }
344
- }
345
-
346
- function testDeriveAssistantCommandExecutionStatus() {
347
- assert(
348
- deriveAssistantCommandExecutionStatus('/bin/bash -lc "rg --files /var/app/current"') === 'Searching files',
349
- 'Expected rg command to map to Searching files'
350
- );
351
- assert(
352
- deriveAssistantCommandExecutionStatus('/bin/bash -lc "find /var/app/current -type f"') === 'Finding files',
353
- 'Expected find command to map to Finding files'
354
- );
355
- assert(
356
- deriveAssistantCommandExecutionStatus('/bin/bash -lc "sed -n \'1,120p\' /var/app/current/file.ts"') === 'Opening files',
357
- 'Expected sed command to map to Opening files'
358
- );
359
- assert(
360
- deriveAssistantCommandExecutionStatus('/bin/bash -lc "ls -la /var/app/current"') === 'Scanning files',
361
- 'Expected ls command to map to Scanning files'
362
- );
363
- assert(
364
- deriveAssistantCommandExecutionStatus('/bin/bash -lc "echo hello"') === 'Running command',
365
- 'Expected unknown command to map to Running command'
366
- );
367
- }
368
-
369
- function testResolveCodexThoughtLevelPolicy() {
370
- const keys = [
371
- 'AI_ASSISTANT_CODEX_THOUGHT_LEVEL',
372
- 'AI_TERMINAL_CODEX_THOUGHT_LEVEL',
373
- 'AI_DASHBOARD_CODEX_THOUGHT_LEVEL'
374
- ] as const;
375
- const previous = keys.map((key) => [key, process.env[key]] as const);
376
- try {
377
- keys.forEach((key) => {
378
- delete process.env[key];
379
- });
380
- const defaultNonBugLevel = resolveCodexThoughtLevel({
381
- message: 'Explain what the deploy instance dashboard does',
382
- requestType: 'feature_info'
383
- });
384
- assert(defaultNonBugLevel === 'low', 'Expected simple non-bug requests to default to low thought level');
385
-
386
- keys.forEach((key) => {
387
- process.env[key] = 'high';
388
- });
389
- const nonBugLevel = resolveCodexThoughtLevel({
390
- message: 'Explain what the deploy instance dashboard does',
391
- requestType: 'feature_info'
392
- });
393
- assert(nonBugLevel === 'medium', 'Expected non-bug requests to clamp to medium thought level');
394
-
395
- const bugLevel = resolveCodexThoughtLevel({
396
- message: "Why won't this save button work?",
397
- requestType: 'bug_issue'
398
- });
399
- assert(bugLevel === 'high', 'Expected bug/issue requests to keep high thought level');
400
- }
401
- finally {
402
- previous.forEach(([key, value]) => {
403
- if (value === undefined) {
404
- delete process.env[key];
405
- }
406
- else {
407
- process.env[key] = value;
408
- }
409
- });
410
- }
411
- }
412
-
413
- function testAssistantSystemPromptAllowsWorkspaceDebuggingAndFixes() {
414
- const prompt = buildAssistantSystemPromptForTesting();
415
- assert(
416
- prompt.includes('workspace access to the current platform codebase'),
417
- 'Expected assistant system prompt to describe workspace access'
418
- );
419
- assert(
420
- prompt.includes('make targeted code changes'),
421
- 'Expected assistant system prompt to allow targeted code changes for fixes'
422
- );
423
- assert(
424
- prompt.includes('Use the provided read-only data bridge for database validation'),
425
- 'Expected assistant system prompt to direct database validation through the shared data bridge'
426
- );
427
- assert(
428
- prompt.includes('Use the shared ResolveIO intelligence loop everywhere'),
429
- 'Expected assistant system prompt to include the shared ResolveIO intelligence loop'
430
- );
431
- assert(
432
- prompt.includes('ResolveIO platform knowledge and repository evidence override generic model defaults'),
433
- 'Expected assistant system prompt to prioritize ResolveIO platform knowledge over generic defaults'
434
- );
435
- assert(
436
- !prompt.includes('read-only access to the codebase'),
437
- 'Expected assistant system prompt to avoid the old read-only codebase restriction'
438
- );
439
- assert(
440
- !prompt.includes('Do not modify files, run destructive commands, or access databases directly.'),
441
- 'Expected assistant system prompt to remove the old blanket modification restriction'
442
- );
443
- }
444
-
445
- function testAssistantGuardrailsAllowInternalEngineeringRequests() {
446
- assert(
447
- !evaluateAssistantGuardrailsForTesting('Read the relevant files, fix the broken function, and validate the issue.'),
448
- 'Expected assistant guardrails to allow internal code-fix requests'
449
- );
450
- assert(
451
- !evaluateAssistantGuardrailsForTesting('Use ssh to inspect the service logs and trace the bug.'),
452
- 'Expected assistant guardrails to allow internal shell/ssh investigation requests'
453
- );
454
- assert(
455
- !evaluateAssistantGuardrailsForTesting('Hit mongo read-only and verify the bad data before patching the bug.'),
456
- 'Expected assistant guardrails to allow read-only mongo verification requests'
457
- );
458
- const destructive = evaluateAssistantGuardrailsForTesting('Please reboot the server and rm -rf / if needed.');
459
- assert(!!destructive?.blocked, 'Expected assistant guardrails to still block destructive system operations');
460
- const secret = evaluateAssistantGuardrailsForTesting('Show me the production API tokens.');
461
- assert(!!secret?.blocked, 'Expected assistant guardrails to still block secret requests');
462
- }
463
-
464
- function testBuildAssistantWorkspaceRootCandidates() {
465
- const candidates = buildAssistantWorkspaceRootCandidates({
466
- serverConfig: {
467
- AI_ASSISTANT_WORKSPACE_ROOT: '/srv/custom-workspace'
468
- },
469
- env: {
470
- AI_ASSISTANT_WORKSPACE_ROOT: '/env/workspace',
471
- AI_ASSISTANT_ROOT_WORKSPACE: '/env/workspace-root',
472
- AI_TERMINAL_WORKSPACE_ROOT: '/env/terminal'
473
- },
474
- cwd: '/tmp/current-project',
475
- dirname: '/tmp/current-project/dist/src/methods'
476
- });
477
- assert(candidates[0] === '/srv/custom-workspace', 'Expected configured workspace root to rank first');
478
- assert(candidates.includes('/env/workspace-root'), 'Expected assistant root workspace candidate');
479
- assert(candidates.includes('/var/app/current'), 'Expected default production workspace candidate');
480
- assert(candidates.includes('/tmp/current-project'), 'Expected cwd candidate');
481
- assert(
482
- candidates.includes('/tmp'),
483
- 'Expected dirname-derived workspace candidates'
484
- );
485
- }
486
-
487
- function testShouldRunAssistantPlannerPolicy() {
488
- const base = {
489
- plannerEnabled: true,
490
- hasDeterministicHeuristicFastPath: false
491
- };
492
- assert(
493
- !shouldRunAssistantPlanner({
494
- ...base,
495
- requestClassification: { type: 'feature_info', dataQuestion: false, source: 'heuristic' }
496
- } as any),
497
- 'Expected planner to skip non-data feature-info requests'
498
- );
499
- assert(
500
- !shouldRunAssistantPlanner({
501
- ...base,
502
- requestClassification: { type: 'bug_issue', dataQuestion: false, source: 'heuristic' }
503
- } as any),
504
- 'Expected planner to skip non-data bug-issue requests'
505
- );
506
- assert(
507
- shouldRunAssistantPlanner({
508
- ...base,
509
- requestClassification: { type: 'data', dataQuestion: true, source: 'heuristic' }
510
- } as any),
511
- 'Expected planner to run for data requests'
512
- );
513
- assert(
514
- shouldRunAssistantPlanner({
515
- ...base,
516
- requestClassification: { type: 'unknown', dataQuestion: false, source: 'heuristic' }
517
- } as any),
518
- 'Expected planner to run for unknown intent requests'
519
- );
520
- assert(
521
- !shouldRunAssistantPlanner({
522
- ...base,
523
- requestClassification: { type: 'data', dataQuestion: true, source: 'heuristic' },
524
- hasDeterministicHeuristicFastPath: true
525
- } as any),
526
- 'Expected deterministic heuristic fast path to skip planner'
527
- );
528
- }
529
-
530
- function testAssistantAppHeuristicRegistryTermHintsAndDeterministicEnablement() {
531
- const priorInline = process.env.AI_ASSISTANT_APP_HEURISTICS;
532
- const priorFile = process.env.AI_ASSISTANT_APP_HEURISTICS_FILE;
533
- const priorAppId = process.env.AI_ASSISTANT_APP_ID;
534
- try {
535
- process.env.AI_ASSISTANT_APP_HEURISTICS_FILE = '';
536
- process.env.AI_ASSISTANT_APP_HEURISTICS = JSON.stringify({
537
- apps: {
538
- snf: {
539
- enabled_deterministic_heuristics: ['schema_hours_user_time'],
540
- collection_term_hints: [
541
- {
542
- terms: ['well', 'wells'],
543
- collections: ['jobs', 'work-order-dynamics']
544
- }
545
- ]
546
- }
547
- }
548
- });
549
- process.env.AI_ASSISTANT_APP_ID = 'snf';
550
- assert(
551
- isAssistantDeterministicHeuristicEnabledForApp('schema_hours_user_time', 'snf'),
552
- 'Expected app heuristics to enable schema_hours_user_time for snf'
553
- );
554
- assert(
555
- !isAssistantDeterministicHeuristicEnabledForApp('schema_hours_user_time', 'resolveio'),
556
- 'Expected schema_hours_user_time to remain disabled for non-snf apps'
557
- );
558
- const hints = resolveAssistantAppCollectionHintCandidates('How many active wells are there?', 'snf');
559
- assert(
560
- hints.includes('jobs'),
561
- 'Expected app collection term hints to return jobs for wells in snf'
562
- );
563
- }
564
- finally {
565
- if (priorInline === undefined) {
566
- delete process.env.AI_ASSISTANT_APP_HEURISTICS;
567
- }
568
- else {
569
- process.env.AI_ASSISTANT_APP_HEURISTICS = priorInline;
570
- }
571
- if (priorFile === undefined) {
572
- delete process.env.AI_ASSISTANT_APP_HEURISTICS_FILE;
573
- }
574
- else {
575
- process.env.AI_ASSISTANT_APP_HEURISTICS_FILE = priorFile;
576
- }
577
- if (priorAppId === undefined) {
578
- delete process.env.AI_ASSISTANT_APP_ID;
579
- }
580
- else {
581
- process.env.AI_ASSISTANT_APP_ID = priorAppId;
582
- }
583
- }
584
- }
585
-
586
- function testAssistantAppHeuristicRegistryLoadsRelativeFileFromParityServerDir() {
587
- const fs = require('fs');
588
- const os = require('os');
589
- const path = require('path');
590
- const tmpRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'ai-assistant-heuristics-'));
591
- const serverDir = path.join(tmpRoot, 'server');
592
- const heuristicsDir = path.join(serverDir, 'src', 'ai');
593
- const heuristicsPath = path.join(heuristicsDir, 'assistant-heuristics.json');
594
- const priorInline = process.env.AI_ASSISTANT_APP_HEURISTICS;
595
- const priorFile = process.env.AI_ASSISTANT_APP_HEURISTICS_FILE;
596
- const priorAppId = process.env.AI_ASSISTANT_APP_ID;
597
- const priorParityServerDir = process.env.AI_ASSISTANT_PARITY_SERVER_DIR;
598
- const priorWorkspaceRoot = process.env.AI_ASSISTANT_WORKSPACE_ROOT;
599
- try {
600
- fs.mkdirSync(heuristicsDir, { recursive: true });
601
- fs.writeFileSync(heuristicsPath, JSON.stringify({
602
- apps: {
603
- parityapp: {
604
- collection_term_hints: [
605
- {
606
- terms: ['widgetfoo'],
607
- collections: ['dashboard-widgets']
608
- }
609
- ]
610
- }
611
- }
612
- }));
613
- delete process.env.AI_ASSISTANT_APP_HEURISTICS;
614
- process.env.AI_ASSISTANT_APP_HEURISTICS_FILE = 'src/ai/assistant-heuristics.json';
615
- process.env.AI_ASSISTANT_APP_ID = 'parityapp';
616
- process.env.AI_ASSISTANT_PARITY_SERVER_DIR = serverDir;
617
- process.env.AI_ASSISTANT_WORKSPACE_ROOT = tmpRoot;
618
- const hints = resolveAssistantAppCollectionHintCandidates('Show me widgetfoo usage', 'parityapp');
619
- assert(
620
- hints.includes('dashboard-widgets'),
621
- 'Expected relative app heuristics file to resolve from AI_ASSISTANT_PARITY_SERVER_DIR'
622
- );
623
- }
624
- finally {
625
- fs.rmSync(tmpRoot, { recursive: true, force: true });
626
- if (priorInline === undefined) {
627
- delete process.env.AI_ASSISTANT_APP_HEURISTICS;
628
- }
629
- else {
630
- process.env.AI_ASSISTANT_APP_HEURISTICS = priorInline;
631
- }
632
- if (priorFile === undefined) {
633
- delete process.env.AI_ASSISTANT_APP_HEURISTICS_FILE;
634
- }
635
- else {
636
- process.env.AI_ASSISTANT_APP_HEURISTICS_FILE = priorFile;
637
- }
638
- if (priorAppId === undefined) {
639
- delete process.env.AI_ASSISTANT_APP_ID;
640
- }
641
- else {
642
- process.env.AI_ASSISTANT_APP_ID = priorAppId;
643
- }
644
- if (priorParityServerDir === undefined) {
645
- delete process.env.AI_ASSISTANT_PARITY_SERVER_DIR;
646
- }
647
- else {
648
- process.env.AI_ASSISTANT_PARITY_SERVER_DIR = priorParityServerDir;
649
- }
650
- if (priorWorkspaceRoot === undefined) {
651
- delete process.env.AI_ASSISTANT_WORKSPACE_ROOT;
652
- }
653
- else {
654
- process.env.AI_ASSISTANT_WORKSPACE_ROOT = priorWorkspaceRoot;
655
- }
656
- }
657
- }
658
-
659
- function testAssistantCoreHeuristicsStayPlatformScoped() {
660
- const hints = resolveAssistantAppCollectionHintCandidates(
661
- 'The report builder collapse-table data table in the navbar module is broken and users cannot login because sessions keep expiring.',
662
- 'resolveio'
663
- );
664
- assert(
665
- hints.includes('reports') || hints.includes('report-templates') || hints.includes('report-layouts'),
666
- 'Expected core heuristics to map report-builder/widget language to report collections'
667
- );
668
- assert(
669
- hints.includes('dashboard-configs') || hints.includes('homepage-configs') || hints.includes('roles'),
670
- 'Expected core heuristics to map navbar/module language to shared nav/system collections'
671
- );
672
- assert(
673
- hints.includes('users') || hints.includes('client-users') || hints.includes('billing-logged-in-users'),
674
- 'Expected core heuristics to map login/session language to user/auth collections'
675
- );
676
- assert(
677
- !hints.includes('support-tickets'),
678
- 'Expected core heuristics to avoid app-domain support ticket hints'
679
- );
680
- assert(
681
- !hints.includes('invoices'),
682
- 'Expected core heuristics to avoid app-domain invoice hints'
683
- );
684
- assert(
685
- !hints.includes('work-order-dynamics') && !hints.includes('chemical-blends'),
686
- 'Expected core heuristics to avoid app-domain work-order and blend hints'
687
- );
688
- }
689
-
690
- function testAssistantAppHeuristicsCanOwnDomainCollectionHints() {
691
- const priorInline = process.env.AI_ASSISTANT_APP_HEURISTICS;
692
- const priorFile = process.env.AI_ASSISTANT_APP_HEURISTICS_FILE;
693
- try {
694
- process.env.AI_ASSISTANT_APP_HEURISTICS_FILE = '';
695
- process.env.AI_ASSISTANT_APP_HEURISTICS = JSON.stringify({
696
- apps: {
697
- snf: {
698
- collection_term_hints: [
699
- {
700
- terms: ['invoice', 'invoices'],
701
- collections: ['invoices']
702
- },
703
- {
704
- terms: ['work order', 'work orders'],
705
- collections: ['work-order-dynamics']
706
- },
707
- {
708
- terms: ['blend', 'blending'],
709
- collections: ['chemical-blends']
710
- }
711
- ]
712
- }
713
- }
714
- });
715
- const hints = resolveAssistantAppCollectionHintCandidates(
716
- 'Show invoices, work orders, and blend throughput.',
717
- 'snf'
718
- );
719
- assert(
720
- hints.includes('invoices'),
721
- 'Expected app heuristics to own invoice collection hints'
722
- );
723
- assert(
724
- hints.includes('work-order-dynamics'),
725
- 'Expected app heuristics to own work-order collection hints'
726
- );
727
- assert(
728
- hints.includes('chemical-blends'),
729
- 'Expected app heuristics to own blend collection hints'
730
- );
731
- }
732
- finally {
733
- if (priorInline === undefined) {
734
- delete process.env.AI_ASSISTANT_APP_HEURISTICS;
735
- }
736
- else {
737
- process.env.AI_ASSISTANT_APP_HEURISTICS = priorInline;
738
- }
739
- if (priorFile === undefined) {
740
- delete process.env.AI_ASSISTANT_APP_HEURISTICS_FILE;
741
- }
742
- else {
743
- process.env.AI_ASSISTANT_APP_HEURISTICS_FILE = priorFile;
744
- }
745
- }
746
- }
747
-
748
- function testAssistantAppDataIntentsResolveSnfDomainRequests() {
749
- const priorInline = process.env.AI_ASSISTANT_APP_HEURISTICS;
750
- const priorFile = process.env.AI_ASSISTANT_APP_HEURISTICS_FILE;
751
- try {
752
- process.env.AI_ASSISTANT_APP_HEURISTICS_FILE = '';
753
- process.env.AI_ASSISTANT_APP_HEURISTICS = JSON.stringify({
754
- apps: {
755
- snf: {
756
- enabled_deterministic_heuristics: ['app_data_intent'],
757
- data_intents: [
758
- {
759
- id: 'snf_job_chemical_shipments',
760
- terms: ['amount of chemicals sent', 'chemicals sent', 'shipped chemicals', 'chemical shipments'],
761
- collection: 'work-order-dynamics',
762
- permission_view: '/work-order-dynamic/list',
763
- date_field: 'date_completed',
764
- quantity_field: 'chemicals.shipped.quantity',
765
- fallback_collections: ['invoices', 'jobs'],
766
- progress: ['Checking {{customer}} work orders for {{year}}', 'Summarizing shipped chemical quantities'],
767
- assumptions: ['Sent means shipped chemical quantity']
768
- },
769
- {
770
- id: 'snf_invoice_chemical_quantities',
771
- terms: ['invoice', 'invoices', 'billed chemicals', 'amount charged'],
772
- collection: 'invoices',
773
- permission_view: '/invoice/list',
774
- date_field: 'date_created',
775
- quantity_field: 'items_chemicals.quantity'
776
- },
777
- {
778
- id: 'snf_planned_job_chemicals',
779
- terms: ['planned chemicals', 'planned chemical'],
780
- collection: 'jobs',
781
- permission_view: '/customer-info/jobs',
782
- date_field: 'date_job_created',
783
- quantity_field: 'planned_chemicals.quantity'
784
- }
785
- ]
786
- }
787
- }
788
- });
789
- const collectionNames = ['jobs', 'work-order-dynamics', 'invoices'];
790
- const shipmentDirective = resolveAssistantHeuristicDirectiveForTesting(
791
- 'Summarize Devon Energy jobs and amount of chemicals sent to them for this year',
792
- ['chemicals', 'jobs', 'work-order-dynamics'],
793
- collectionNames,
794
- 'snf'
795
- );
796
- assert(shipmentDirective?.payload?.collection === 'work-order-dynamics', 'Expected shipped-chemical job request to resolve to work-order-dynamics');
797
- assert(String(shipmentDirective?.rawLine || '').includes('app-data-intent:snf_job_chemical_shipments'), 'Expected shipped-chemical request to use the SNF shipment recipe');
798
- assert(
799
- String(shipmentDirective?.metadata?.acknowledgementText || '').includes('I understand you want a summary of chemicals sent to Devon Energy jobs'),
800
- 'Expected shipment recipe to create a custom acknowledgement'
801
- );
802
- assert(
803
- JSON.stringify(shipmentDirective?.payload?.pipeline || []).includes('chemicals.shipped.quantity'),
804
- 'Expected shipment recipe to aggregate shipped chemical quantity'
805
- );
806
- const invoiceDirective = resolveAssistantHeuristicDirectiveForTesting(
807
- 'Devon Energy invoices this year',
808
- ['invoices'],
809
- collectionNames,
810
- 'snf'
811
- );
812
- assert(invoiceDirective?.payload?.collection === 'invoices', 'Expected invoice request to resolve to invoices');
813
- const plannedDirective = resolveAssistantHeuristicDirectiveForTesting(
814
- 'planned chemicals for Devon jobs',
815
- ['jobs'],
816
- collectionNames,
817
- 'snf'
818
- );
819
- assert(plannedDirective?.payload?.collection === 'jobs', 'Expected planned chemicals request to resolve to jobs');
820
- }
821
- finally {
822
- if (priorInline === undefined) {
823
- delete process.env.AI_ASSISTANT_APP_HEURISTICS;
824
- }
825
- else {
826
- process.env.AI_ASSISTANT_APP_HEURISTICS = priorInline;
827
- }
828
- if (priorFile === undefined) {
829
- delete process.env.AI_ASSISTANT_APP_HEURISTICS_FILE;
830
- }
831
- else {
832
- process.env.AI_ASSISTANT_APP_HEURISTICS_FILE = priorFile;
833
- }
834
- }
835
- }
836
-
837
- function testAssistantRemovedDomainHeuristicIdsRemainAppSpecific() {
838
- const priorInline = process.env.AI_ASSISTANT_APP_HEURISTICS;
839
- const priorFile = process.env.AI_ASSISTANT_APP_HEURISTICS_FILE;
840
- try {
841
- process.env.AI_ASSISTANT_APP_HEURISTICS_FILE = '';
842
- process.env.AI_ASSISTANT_APP_HEURISTICS = JSON.stringify({
843
- apps: {
844
- snf: {
845
- enabled_deterministic_heuristics: [
846
- 'active_clients_count',
847
- 'current_bookings_count',
848
- 'active_holds_count',
849
- 'inventory_chemical_quantity',
850
- 'work_order_week_customer_breakdown',
851
- 'work_order_created_week_status',
852
- 'work_order_completed_per_day',
853
- 'work_order_completed_delivery_breakdown',
854
- 'work_order_top_customers_window',
855
- 'deliveries_per_driver_last_month',
856
- 'active_jobs_count',
857
- 'revenue_division_top_customers_window'
858
- ]
859
- }
860
- }
861
- });
862
- const cases = [
863
- {
864
- message: 'How many active clients are there?',
865
- collectionHints: ['clients', 'customers'],
866
- collectionNames: ['clients', 'customers'],
867
- reason: 'Expected shared library to avoid emitting active client count directives'
868
- },
869
- {
870
- message: 'How many current bookings does Acme have?',
871
- collectionHints: ['bookings'],
872
- collectionNames: ['bookings'],
873
- reason: 'Expected shared library to avoid emitting booking count directives'
874
- },
875
- {
876
- message: 'How many active holds are there?',
877
- collectionHints: ['chemical-holds', 'holds'],
878
- collectionNames: ['chemical-holds', 'holds'],
879
- reason: 'Expected shared library to avoid emitting active hold directives'
880
- },
881
- {
882
- message: 'How many Sack -1760 of FLOJET DRMAX P 9115 does midland plant have currently?',
883
- collectionHints: ['chemical-inventory-snapshots', 'inventory-reports'],
884
- collectionNames: ['chemical-inventory-snapshots', 'inventory-reports'],
885
- reason: 'Expected shared library to avoid emitting inventory quantity directives'
886
- },
887
- {
888
- message: 'Summarize the work orders this week by customer.',
889
- collectionHints: ['work-order-dynamics', 'orders'],
890
- collectionNames: ['work-order-dynamics', 'orders'],
891
- reason: 'Expected shared library to avoid emitting work-order customer breakdown directives'
892
- },
893
- {
894
- message: 'Show work orders created this week by status.',
895
- collectionHints: ['work-order-dynamics', 'orders'],
896
- collectionNames: ['work-order-dynamics', 'orders'],
897
- reason: 'Expected shared library to avoid emitting work-order status directives'
898
- },
899
- {
900
- message: 'Show completed work orders by day for the last 30 days.',
901
- collectionHints: ['work-order-dynamics', 'orders'],
902
- collectionNames: ['work-order-dynamics', 'orders'],
903
- reason: 'Expected shared library to avoid emitting completed work-order per-day directives'
904
- },
905
- {
906
- message: 'Break down the last 20 completed work orders and tell me what customer, what product and how much we delivered or returned.',
907
- collectionHints: ['work-order-dynamics', 'orders'],
908
- collectionNames: ['work-order-dynamics', 'orders'],
909
- reason: 'Expected shared library to avoid emitting work-order delivery breakdown directives'
910
- },
911
- {
912
- message: 'Show the top 10 customers by number of work orders in the last 6 months.',
913
- collectionHints: ['work-order-dynamics', 'orders'],
914
- collectionNames: ['work-order-dynamics', 'orders'],
915
- reason: 'Expected shared library to avoid emitting work-order ranking directives'
916
- },
917
- {
918
- message: 'Show deliveries per driver last month.',
919
- collectionHints: ['work-order-dynamics', 'orders'],
920
- collectionNames: ['work-order-dynamics', 'orders'],
921
- reason: 'Expected shared library to avoid emitting delivery ranking directives'
922
- },
923
- {
924
- message: 'How many active jobs are there?',
925
- collectionHints: ['jobs'],
926
- collectionNames: ['jobs'],
927
- reason: 'Expected shared library to avoid emitting active jobs directives'
928
- },
929
- {
930
- message: 'Show the top 10 customers by revenue by division in the last 6 months.',
931
- collectionHints: ['invoices'],
932
- collectionNames: ['invoices'],
933
- reason: 'Expected shared library to avoid emitting revenue ranking directives'
934
- }
935
- ];
936
- cases.forEach((testCase) => {
937
- assert(
938
- !resolveAssistantHeuristicDirectiveForTesting(
939
- testCase.message,
940
- testCase.collectionHints,
941
- testCase.collectionNames,
942
- 'snf'
943
- ),
944
- testCase.reason
945
- );
946
- });
947
- }
948
- finally {
949
- if (priorInline === undefined) {
950
- delete process.env.AI_ASSISTANT_APP_HEURISTICS;
951
- }
952
- else {
953
- process.env.AI_ASSISTANT_APP_HEURISTICS = priorInline;
954
- }
955
- if (priorFile === undefined) {
956
- delete process.env.AI_ASSISTANT_APP_HEURISTICS_FILE;
957
- }
958
- else {
959
- process.env.AI_ASSISTANT_APP_HEURISTICS_FILE = priorFile;
960
- }
961
- }
962
- }
963
-
964
- function testAssistantHeuristicBlendIdsRemainAppSpecific() {
965
- const priorInline = process.env.AI_ASSISTANT_APP_HEURISTICS;
966
- const priorFile = process.env.AI_ASSISTANT_APP_HEURISTICS_FILE;
967
- try {
968
- process.env.AI_ASSISTANT_APP_HEURISTICS_FILE = '';
969
- process.env.AI_ASSISTANT_APP_HEURISTICS = JSON.stringify({
970
- apps: {
971
- snf: {
972
- enabled_deterministic_heuristics: [
973
- 'blend_ticket_summary',
974
- 'blend_throughput_day_window',
975
- 'blend_user_window_count'
976
- ]
977
- }
978
- }
979
- });
980
- const summaryDirective = resolveAssistantHeuristicDirectiveForTesting(
981
- 'For blending: summarize the last 10 blend tickets with product, total volume, and created date.',
982
- ['chemical-blends'],
983
- ['chemical-blends', 'report-chemical-blends'],
984
- 'snf'
985
- );
986
- assert(
987
- !summaryDirective,
988
- 'Expected shared library to avoid emitting app-specific blend ticket summary directives'
989
- );
990
- const throughputDirective = resolveAssistantHeuristicDirectiveForTesting(
991
- 'Show blend throughput by day for the last 2 weeks.',
992
- ['chemical-blends'],
993
- ['chemical-blends', 'report-chemical-blends'],
994
- 'snf'
995
- );
996
- assert(
997
- !throughputDirective,
998
- 'Expected shared library to avoid emitting app-specific blend throughput directives'
999
- );
1000
- assert(
1001
- !resolveAssistantHeuristicDirectiveForTesting(
1002
- 'How many blends has John Doe completed in the last 30 days?',
1003
- ['chemical-blends'],
1004
- ['chemical-blends', 'report-chemical-blends'],
1005
- 'snf'
1006
- ),
1007
- 'Expected shared library to avoid emitting app-specific blend user-window directives'
1008
- );
1009
- }
1010
- finally {
1011
- if (priorInline === undefined) {
1012
- delete process.env.AI_ASSISTANT_APP_HEURISTICS;
1013
- }
1014
- else {
1015
- process.env.AI_ASSISTANT_APP_HEURISTICS = priorInline;
1016
- }
1017
- if (priorFile === undefined) {
1018
- delete process.env.AI_ASSISTANT_APP_HEURISTICS_FILE;
1019
- }
1020
- else {
1021
- process.env.AI_ASSISTANT_APP_HEURISTICS_FILE = priorFile;
1022
- }
1023
- }
1024
- }
1025
-
1026
- function testShouldEnforceAssistantDatedDirectiveSkipsWindowedRankingRequests() {
1027
- const classification = { type: 'data', dataQuestion: true, source: 'heuristic' } as any;
1028
- const shouldEnforce = shouldEnforceAssistantDatedDirective(
1029
- 'List the top 10 customers by number of work orders in the last 6 months.',
1030
- {},
1031
- classification
1032
- );
1033
- assert(!shouldEnforce, 'Expected windowed ranking request to avoid forced dated grouping');
1034
- }
1035
-
1036
- function testShouldEnforceAssistantDatedDirectiveForExplicitTemporalBreakdown() {
1037
- const classification = { type: 'data', dataQuestion: true, source: 'heuristic' } as any;
1038
- const shouldEnforce = shouldEnforceAssistantDatedDirective(
1039
- 'Break down total revenue over the last 6 months by month.',
1040
- {},
1041
- classification
1042
- );
1043
- assert(shouldEnforce, 'Expected explicit by-month request to enforce dated grouping');
1044
- }
1045
-
1046
- function testShouldEnforceAssistantDatedDirectiveForPlannerTemporalGroupBy() {
1047
- const classification = { type: 'data', dataQuestion: true, source: 'planner' } as any;
1048
- const shouldEnforce = shouldEnforceAssistantDatedDirective(
1049
- 'Show revenue for the last 6 months.',
1050
- { dataPlan: { queryPlan: { groupBy: 'month' } } },
1051
- classification
1052
- );
1053
- assert(shouldEnforce, 'Expected planner temporal groupBy to enforce dated grouping');
1054
- }
1055
-
1056
- function testBuildAssistantDatedPivotDisplayWithYearMonthAndCustomer() {
1057
- const display = {
1058
- columns: ['Customer', 'Year', 'Month', 'Total Revenue'],
1059
- rows: [
1060
- { Customer: 'Apache', Year: 2025, Month: 11, 'Total Revenue': 1000 },
1061
- { Customer: 'Apache', Year: 2025, Month: 12, 'Total Revenue': 2000 },
1062
- { Customer: 'Pioneer', Year: 2025, Month: 11, 'Total Revenue': 1500 },
1063
- { Customer: 'Pioneer', Year: 2025, Month: 12, 'Total Revenue': 500 }
1064
- ],
1065
- rowCount: 4,
1066
- truncated: false,
1067
- includeIds: false
1068
- };
1069
- const pivoted = buildAssistantDatedPivotDisplay(display as any);
1070
- assert(!!pivoted, 'Expected dated pivot display to be produced');
1071
- assert(
1072
- pivoted?.columns?.[0] === 'Customer',
1073
- 'Expected customer to remain first pivot column'
1074
- );
1075
- assert(
1076
- pivoted?.columns?.includes('2025-11') && pivoted?.columns?.includes('2025-12'),
1077
- 'Expected YYYY-MM month columns'
1078
- );
1079
- const apacheRow = (pivoted?.rows || []).find((row: any) => row?.Customer === 'Apache');
1080
- assert(!!apacheRow, 'Expected Apache row in pivot');
1081
- assert(
1082
- String(apacheRow?.['2025-11'] || '').includes('$'),
1083
- 'Expected revenue cells to be currency formatted'
1084
- );
1085
- }
1086
-
1087
- function testBuildAssistantDatedPivotDisplayWithoutDimensionCreatesMetricGrid() {
1088
- const display = {
1089
- columns: ['Year', 'Month', 'Total Revenue'],
1090
- rows: [
1091
- { Year: 2025, Month: 11, 'Total Revenue': 3200 },
1092
- { Year: 2025, Month: 12, 'Total Revenue': 4100 },
1093
- { Year: 2026, Month: 1, 'Total Revenue': 5050 }
1094
- ],
1095
- rowCount: 3,
1096
- truncated: false,
1097
- includeIds: false
1098
- };
1099
- const pivoted = buildAssistantDatedPivotDisplay(display as any);
1100
- assert(!!pivoted, 'Expected dated pivot grid even without explicit dimension');
1101
- assert(pivoted?.columns?.[0] === 'Metric', 'Expected synthetic Metric column');
1102
- assert(
1103
- pivoted?.columns?.includes('2025-11')
1104
- && pivoted?.columns?.includes('2025-12')
1105
- && pivoted?.columns?.includes('2026-01'),
1106
- 'Expected month header columns for all buckets'
1107
- );
1108
- assert((pivoted?.rows || []).length === 1, 'Expected single metric row for monthly totals');
1109
- assert(
1110
- normalizeCellText((pivoted?.rows || [])[0]?.Metric) === 'Total Revenue',
1111
- 'Expected metric row label to use value column'
1112
- );
1113
- }
1114
-
1115
- function testBuildAssistantInvoiceCustomerLabelExprIncludesClientFallbacks() {
1116
- const expression = buildAssistantInvoiceCustomerLabelExpr();
1117
- const serialized = JSON.stringify(expression);
1118
- const requiredTokens = [
1119
- '$customer_name',
1120
- '$customer.fullname',
1121
- '$customer.name',
1122
- '$customer',
1123
- '$client_name',
1124
- '$client.fullname',
1125
- '$client.name',
1126
- '$client',
1127
- '$id_customer',
1128
- '$id_client',
1129
- 'Unknown Customer',
1130
- '$strLenCP'
1131
- ];
1132
- requiredTokens.forEach((token) => {
1133
- assert(
1134
- serialized.includes(token),
1135
- `Expected invoice customer expression to include ${token}`
1136
- );
1137
- });
1138
- }
1139
-
1140
- function testResolveAssistantReadDisplayMaxRows() {
1141
- assert(
1142
- resolveAssistantReadDisplayMaxRows(0, 92) === 20,
1143
- 'Expected limit=0 to fallback to preview rows instead of zero rows'
1144
- );
1145
- assert(
1146
- resolveAssistantReadDisplayMaxRows(undefined, 7) === 7,
1147
- 'Expected small row counts to be preserved for preview'
1148
- );
1149
- assert(
1150
- resolveAssistantReadDisplayMaxRows(5, 92) === 5,
1151
- 'Expected explicit positive limit to be respected'
1152
- );
1153
- assert(
1154
- resolveAssistantReadDisplayMaxRows(undefined, 0) === 20,
1155
- 'Expected empty reads to keep default preview row ceiling'
1156
- );
1157
- }
1158
-
1159
- function testResolveAssistantCollectionOverrideKeepsSpecificOrdersCollection() {
1160
- const override = resolveAssistantCollectionOverride(
1161
- {
1162
- ranked: [
1163
- { name: 'orders', score: 40 },
1164
- { name: 'maintenance-orders', score: 20 }
1165
- ]
1166
- },
1167
- 'maintenance-orders'
1168
- );
1169
- assert(!override, 'Expected maintenance-orders to not be overridden by generic orders');
1170
- }
1171
-
1172
- function testResolveAssistantCollectionOverrideStillAppliesForUnrelatedCollection() {
1173
- const override = resolveAssistantCollectionOverride(
1174
- {
1175
- ranked: [
1176
- { name: 'orders', score: 40 },
1177
- { name: 'maintenance-orders', score: 20 }
1178
- ]
1179
- },
1180
- 'clients'
1181
- );
1182
- assert(!!override, 'Expected unrelated collection to be overridden by highest-ranked candidate');
1183
- assert(override?.to === 'orders', 'Expected override target to be orders');
1184
- }
1185
-
1186
- function testResolveAssistantCollectionOverrideSkipsBorderlineGapForWorkOrderRanking() {
1187
- const override = resolveAssistantCollectionOverride(
1188
- {
1189
- tokens: ['top', '10', 'customers', 'customer', 'number', 'work', 'orders', 'order', '6', 'months'],
1190
- tokenWeights: {
1191
- top: 1,
1192
- '10': 1,
1193
- customers: 1,
1194
- customer: 1,
1195
- number: 1,
1196
- work: 1,
1197
- orders: 1,
1198
- order: 1,
1199
- '6': 1,
1200
- months: 1
1201
- },
1202
- ranked: [
1203
- { name: 'customers', score: 40 },
1204
- { name: 'orders', score: 40 }
1205
- ]
1206
- },
1207
- 'work-order-dynamics'
1208
- );
1209
- assert(!override, 'Expected work-order-dynamics to be preserved when score gap is only at threshold');
1210
- }
1211
-
1212
- function testResolveAssistantCollectionOverrideKeepsWorkOrderCollectionWhenOrdersScoresHigher() {
1213
- const override = resolveAssistantCollectionOverride(
1214
- {
1215
- ranked: [
1216
- { name: 'orders', score: 85 },
1217
- { name: 'work-order-dynamics', score: 64 }
1218
- ]
1219
- },
1220
- 'work-order-dynamics'
1221
- );
1222
- assert(!override, 'Expected work-order-dynamics to stay selected over generic orders');
1223
- }
1224
-
1225
- function testResolveCollectionOverrideWithContextRespectsPermissionRouteHints() {
1226
- const override = resolveCollectionOverrideWithContextForTesting({
1227
- message: 'How many pods do we have in inventory?',
1228
- collectionRanking: {
1229
- ranked: [
1230
- { name: 'inventory-report-text-users', score: 39 },
1231
- { name: 'assets', score: 0 }
1232
- ]
1233
- },
1234
- requestedCollection: 'assets',
1235
- permissionView: '/asset/list',
1236
- collectionNames: ['assets', 'inventory-report-text-users']
1237
- });
1238
- assert(!override, 'Expected /asset/list route hints to keep assets collection');
1239
- }
1240
-
1241
- function testResolveCollectionOverrideWithContextUsesSpecificPermissionRouteCollection() {
1242
- const override = resolveCollectionOverrideWithContextForTesting({
1243
- message: 'Summarize the work orders this week by customer.',
1244
- collectionRanking: {
1245
- ranked: [
1246
- { name: 'production-sales-orders', score: 80 },
1247
- { name: 'work-order-dynamics', score: 70 }
1248
- ]
1249
- },
1250
- requestedCollection: 'production-sales-orders',
1251
- permissionView: '/work-order-dynamic/list',
1252
- collectionNames: ['production-sales-orders', 'work-order-dynamics']
1253
- });
1254
- assert(!!override, 'Expected work-order permission route to correct mismatched production-sales-orders collection');
1255
- assert(
1256
- override?.to === 'work-order-dynamics',
1257
- 'Expected override target to be work-order-dynamics'
1258
- );
1259
- }
1260
-
1261
- function testResolveCollectionOverrideWithContextUsesOrderedCollectionHints() {
1262
- const override = resolveCollectionOverrideWithContextForTesting({
1263
- message: 'Summarize the work orders this week by customer.',
1264
- collectionRanking: {
1265
- ranked: [
1266
- { name: 'customers', score: 90 },
1267
- { name: 'production-sales-orders', score: 80 }
1268
- ]
1269
- },
1270
- requestedCollection: 'customers',
1271
- permissionView: '/work-order-dynamic/list',
1272
- collectionNames: ['customers', 'production-sales-orders'],
1273
- collectionHints: ['work-order-dynamics', 'jobs', 'maintenance-orders', 'customers', 'orders']
1274
- });
1275
- assert(!!override, 'Expected ordered collection hints to correct grouped-dimension collection selection');
1276
- assert(
1277
- override?.to === 'work-order-dynamics',
1278
- 'Expected override target to be the first ordered collection hint'
1279
- );
1280
- }
1281
-
1282
- function testResolveCollectionOverrideWithContextPreservesPrimaryCollectionHint() {
1283
- const override = resolveCollectionOverrideWithContextForTesting({
1284
- message: 'Summarize the work orders this week by customer.',
1285
- collectionRanking: {
1286
- ranked: [
1287
- { name: 'customer-portal-passwords', score: 45 },
1288
- { name: 'work-order-dynamics', score: 20 }
1289
- ]
1290
- },
1291
- requestedCollection: 'work-order-dynamics',
1292
- permissionView: '/work-order-dynamic/list',
1293
- collectionNames: ['customer-portal-passwords', 'work-order-dynamics'],
1294
- collectionHints: ['work-order-dynamics', 'jobs', 'maintenance-orders', 'customers', 'orders']
1295
- });
1296
- assert(!override, 'Expected primary collection hint to preserve the model-selected collection');
1297
- }
1298
-
1299
- function testResolveCollectionOverrideWithContextKeepsRouteMatchedJobsOverChemicalHint() {
1300
- const override = resolveCollectionOverrideWithContextForTesting({
1301
- message: 'Summarize Devon Energy jobs and amount of chemicals sent to them for this year',
1302
- collectionRanking: {
1303
- ranked: [
1304
- { name: 'chemicals', score: 79 },
1305
- { name: 'jobs', score: 76 },
1306
- { name: 'work-order-dynamics', score: 38 }
1307
- ]
1308
- },
1309
- requestedCollection: 'jobs',
1310
- permissionView: '/job/list',
1311
- collectionNames: ['chemicals', 'jobs', 'work-order-dynamics'],
1312
- collectionHints: ['chemicals', 'jobs', 'work-order-dynamics', 'chemical-blends']
1313
- });
1314
- assert(!override, 'Expected /job/list route to preserve model-selected jobs over a chemical keyword hint');
1315
- }
1316
-
1317
- function testResolveCollectionOverrideWithContextPreservesJobDomainDetailCollections() {
1318
- const override = resolveCollectionOverrideWithContextForTesting({
1319
- message: 'Summarize Devon Energy jobs and amount of chemicals sent to them for this year',
1320
- collectionRanking: {
1321
- ranked: [
1322
- { name: 'work-order-dynamics', score: 80 },
1323
- { name: 'jobs', score: 76 }
1324
- ]
1325
- },
1326
- requestedCollection: 'work-order-dynamics',
1327
- permissionView: '/customer-info/jobs',
1328
- collectionNames: ['jobs', 'work-order-dynamics', 'invoices'],
1329
- collectionHints: ['work-order-dynamics', 'jobs', 'invoices']
1330
- });
1331
- assert(!override, 'Expected job route to preserve work-order-dynamics for shipped chemical job summaries');
1332
- }
1333
-
1334
- function testResolveCollectionOverrideWithContextKeepsCompetitiveRootCollectionHint() {
1335
- const override = resolveCollectionOverrideWithContextForTesting({
1336
- message: 'Summarize Devon Energy jobs and amount of chemicals sent to them for this year',
1337
- collectionRanking: {
1338
- ranked: [
1339
- { name: 'chemicals', score: 79 },
1340
- { name: 'jobs', score: 76 },
1341
- { name: 'work-order-dynamics', score: 38 }
1342
- ]
1343
- },
1344
- requestedCollection: 'jobs',
1345
- collectionNames: ['chemicals', 'jobs', 'work-order-dynamics'],
1346
- collectionHints: ['chemicals', 'jobs', 'work-order-dynamics', 'chemical-blends']
1347
- });
1348
- assert(!override, 'Expected close-ranked model-selected root collection to survive a related detail collection hint');
1349
- }
1350
-
1351
- function testShouldPreserveAssistantProbeCollectionForPrimaryHintAndRoute() {
1352
- const directive = {
1353
- type: 'aggregate',
1354
- payload: {
1355
- collection: 'work-order-dynamics',
1356
- permissionView: '/work-order-dynamic/list'
1357
- }
1358
- };
1359
- const preserveFromHints = shouldPreserveAssistantProbeCollectionForTesting(
1360
- directive,
1361
- 'production-sales-orders',
1362
- {
1363
- debug: {
1364
- requestHints: {
1365
- collectionHints: ['work-order-dynamics', 'production-sales-orders']
1366
- }
1367
- }
1368
- }
1369
- );
1370
- assert(preserveFromHints, 'Expected probe rewrite to preserve primary hinted work-order collection');
1371
-
1372
- const preserveFromRoute = shouldPreserveAssistantProbeCollectionForTesting(
1373
- directive,
1374
- 'production-sales-orders'
1375
- );
1376
- assert(preserveFromRoute, 'Expected probe rewrite to preserve collection matching the permission route');
1377
-
1378
- const lockForExecution = shouldLockAssistantRequestedCollectionForExecutionForTesting(
1379
- 'work-order-dynamics',
1380
- '/work-order-dynamic',
1381
- {
1382
- collectionHints: ['work-order-dynamics', 'production-sales-orders']
1383
- }
1384
- );
1385
- assert(lockForExecution, 'Expected execution to disable cross-collection retry for route/primary hinted collection');
1386
- }
1387
-
1388
- function testResolveCollectionOverrideWithContextStillOverridesWithoutRouteHints() {
1389
- const override = resolveCollectionOverrideWithContextForTesting({
1390
- message: 'How many pods do we have in inventory?',
1391
- collectionRanking: {
1392
- ranked: [
1393
- { name: 'inventory-report-text-users', score: 39 },
1394
- { name: 'assets', score: 0 }
1395
- ]
1396
- },
1397
- requestedCollection: 'assets',
1398
- permissionView: '/dashboard/home',
1399
- collectionNames: ['assets', 'inventory-report-text-users']
1400
- });
1401
- assert(!!override, 'Expected override to still apply when route has no collection hints');
1402
- assert(
1403
- override?.to === 'inventory-report-text-users',
1404
- 'Expected override target to remain the top ranked collection when route hints are absent'
1405
- );
1406
- }
1407
-
1408
- function testResolveAssistantCrossCollectionFallbackCandidatesForOrders() {
1409
- const candidates = resolveAssistantCrossCollectionFallbackCandidates('orders');
1410
- assert(candidates.length === 0, 'Expected non-aliased collections to have no hardcoded fallback list');
1411
- }
1412
-
1413
- function testResolveAssistantCrossCollectionFallbackCandidatesForWorkOrderDynamics() {
1414
- const candidates = resolveAssistantCrossCollectionFallbackCandidates('work-order-dynamics');
1415
- assert(candidates.length === 0, 'Expected work-order-dynamics to have no hardcoded fallback list');
1416
- }
1417
-
1418
- function testResolveAssistantCrossCollectionFallbackCandidatesForUnknownCollection() {
1419
- const candidates = resolveAssistantCrossCollectionFallbackCandidates('foobar-collection');
1420
- assert(candidates.length === 0, 'Expected unknown collection fallback candidates to be empty');
1421
- }
1422
-
1423
- function testResolveAssistantCrossCollectionFallbackCandidatesForTimeEntries() {
1424
- const candidates = resolveAssistantCrossCollectionFallbackCandidates('time_entries');
1425
- assert(candidates.length === 0, 'Expected time entries to have no hardcoded fallback list');
1426
- }
1427
-
1428
- function testResolveAssistantAvailableCrossCollectionFallbacksFromNamesUsesSimilarityRanking() {
1429
- const available = resolveAssistantAvailableCrossCollectionFallbacksFromNames(
1430
- 'orders',
1431
- new Set<string>(['orders']),
1432
- ['orders', 'maintenance-orders', 'repair-orders', 'customers']
1433
- );
1434
- assert(
1435
- available.includes('maintenance-orders') || available.includes('repair-orders'),
1436
- 'Expected orders fallback to include related collections from token similarity'
1437
- );
1438
- }
1439
-
1440
- function testResolveAssistantAvailableCrossCollectionFallbacksFromNamesSkipsCurrentFamilyOnlyMatch() {
1441
- const available = resolveAssistantAvailableCrossCollectionFallbacksFromNames(
1442
- 'time_entries',
1443
- new Set<string>(['time_entries']),
1444
- ['time_entries']
1445
- );
1446
- assert(
1447
- available.length === 0,
1448
- 'Expected no fallback when only the current collection family is available'
1449
- );
1450
- }
1451
-
1452
- function testResolveAssistantCrossCollectionFallbackCandidatesForSupportAlias() {
1453
- const candidates = resolveAssistantCrossCollectionFallbackCandidates('supporttickets');
1454
- assert(
1455
- candidates.length === 0,
1456
- 'Expected shared library to avoid injecting support ticket fallback aliases'
1457
- );
1458
- }
1459
-
1460
- function testShouldAcceptAssistantFallbackDocumentsRejectsBlankDimensionRows() {
1461
- const accepted = shouldAcceptAssistantFallbackDocuments([
1462
- { 'Work Orders': 23, Customer: '' }
1463
- ]);
1464
- assert(!accepted, 'Expected fallback documents with empty dimensions to be rejected');
1465
- }
1466
-
1467
- function testShouldAcceptAssistantFallbackDocumentsAcceptsNamedDimensions() {
1468
- const accepted = shouldAcceptAssistantFallbackDocuments([
1469
- { 'Work Orders': 23, Customer: 'Acme Services' }
1470
- ]);
1471
- assert(accepted, 'Expected fallback documents with populated dimensions to be accepted');
1472
- }
1473
-
1474
- function testShouldAcceptAssistantFallbackDocumentsAcceptsMetricOnlyRows() {
1475
- const accepted = shouldAcceptAssistantFallbackDocuments([
1476
- { Count: 42, Total: 4200 }
1477
- ]);
1478
- assert(accepted, 'Expected metric-only fallback documents to be accepted');
1479
- }
1480
-
1481
- function normalizeCellText(value: any): string {
1482
- return String(value || '').trim();
1483
- }
1484
-
1485
- function testDisplayMarkdownInvoiceNumbersAndCountsStayNumeric() {
1486
- const display = buildDisplayTable([
1487
- { invoice_number: 0, total: 558, voidedCount: 558 },
1488
- { invoice_number: 203746, total: 2, voidedCount: 2 }
1489
- ]);
1490
- const markdown = formatDisplayTableMarkdown(display);
1491
- assert(!markdown.includes('$0.00'), 'Expected invoice number 0 to remain numeric');
1492
- assert(!markdown.includes('$203,746.00'), 'Expected invoice number to remain numeric');
1493
- assert(!markdown.includes('$558.00'), 'Expected duplicate count total to remain numeric');
1494
- assert(markdown.includes('203,746'), 'Expected large invoice number to be number-formatted');
1495
- }
1496
-
1497
- function testDisplayMarkdownCurrencyColumnsStillUseCurrencyFormat() {
1498
- const display = buildDisplayTable([
1499
- { invoice_number: 1001, grand_total: 1234.5, sales_tax: 88.1, paid_total: 500 }
1500
- ]);
1501
- const markdown = formatDisplayTableMarkdown(display);
1502
- assert(markdown.includes('$1,234.50'), 'Expected grand_total to be currency formatted');
1503
- assert(markdown.includes('$88.10'), 'Expected sales_tax to be currency formatted');
1504
- assert(markdown.includes('$500.00'), 'Expected paid_total to be currency formatted');
1505
- assert(!markdown.includes('$1,001.00'), 'Expected invoice_number to not be currency formatted');
1506
- }
1507
-
1508
- function testDisplayMarkdownKeepsAlphanumericBlendAndChemicalNames() {
1509
- const display = buildDisplayTable([
1510
- {
1511
- blend_name: 'ASFlow-100 Bulk Winter Blend (Surf Pkg)',
1512
- chemical: 'INT 50/50 - CO INDEPENDENCE',
1513
- chemical_recipe_quantity: 4500
1514
- }
1515
- ]);
1516
- const markdown = formatDisplayTableMarkdown(display);
1517
- assert(markdown.includes('ASFlow-100 Bulk Winter Blend (Surf Pkg)'), 'Expected blend name to remain text');
1518
- assert(markdown.includes('INT 50/50 - CO INDEPENDENCE'), 'Expected chemical name to remain text');
1519
- assert(!markdown.includes('| -100 |'), 'Expected blend/chemical names to not be coerced into numeric -100');
1520
- assert(!markdown.includes('| 5,050 |'), 'Expected blend/chemical names to not be coerced into numeric 5,050');
1521
- }
1522
-
1523
- function testDisplayTableCoalescesStringVariantFields() {
1524
- const display = buildDisplayTable([
1525
- {
1526
- batch_number: 202792,
1527
- batch_number_string: '202,792',
1528
- createdAt: '2026-02-06T21:47:00.000Z'
1529
- },
1530
- {
1531
- batch_number: null,
1532
- batch_number_string: '202,791',
1533
- createdAt: '2026-02-06T20:18:00.000Z'
1534
- }
1535
- ]);
1536
- assert(display.columns.includes('Batch Number'), 'Expected base batch number column');
1537
- assert(!display.columns.includes('Batch Number String'), 'Expected string variant batch column to be removed');
1538
- const secondRow = display.rows?.[1] || {};
1539
- assert(String(secondRow['Batch Number'] || '').includes('202,791'), 'Expected fallback from batch_number_string into Batch Number');
1540
- }
1541
-
1542
- function testDisplayTableCoalescesEquivalentCreatedDateFields() {
1543
- const display = buildDisplayTable([
1544
- {
1545
- createdAt: '2026-02-06T21:47:00.000Z',
1546
- date: '2026-02-06T21:47:00.000Z',
1547
- blend_name: 'Blend A'
1548
- },
1549
- {
1550
- createdAt: '2026-02-06T20:18:00.000Z',
1551
- date: '2026-02-06T20:18:00.000Z',
1552
- blend_name: 'Blend B'
1553
- }
1554
- ]);
1555
- assert(display.columns.includes('Created At'), 'Expected Created At column to remain');
1556
- assert(!display.columns.includes('Date'), 'Expected duplicate Date column to be removed');
1557
- }
1558
-
1559
- function testDisplayTableKeepsDistinctCreatedDateAndDateFields() {
1560
- const display = buildDisplayTable([
1561
- {
1562
- createdAt: '2026-02-06T21:47:00.000Z',
1563
- date: '2026-02-01T00:00:00.000Z',
1564
- blend_name: 'Blend A'
1565
- },
1566
- {
1567
- createdAt: '2026-02-06T20:18:00.000Z',
1568
- date: '2026-02-01T00:00:00.000Z',
1569
- blend_name: 'Blend B'
1570
- }
1571
- ]);
1572
- assert(display.columns.includes('Created At'), 'Expected Created At column to remain');
1573
- assert(display.columns.includes('Date'), 'Expected Date column to remain when values differ');
1574
- }
1575
-
1576
- function testStripQueryFieldPathsDeep() {
1577
- const query = { $and: [{ id_customer: 'Acme' }, { status: 'open' }] };
1578
- const stripped = stripQueryFieldPathsDeep(query, ['id_customer', 'other.id_customer']);
1579
- assert(JSON.stringify(stripped) === JSON.stringify({ status: 'open' }), 'Expected id_customer to be stripped');
1580
-
1581
- const nested = { other: { id_customer: 'Acme', foo: 1 }, status: 'open' };
1582
- const strippedNested = stripQueryFieldPathsDeep(nested, ['id_customer', 'other.id_customer']);
1583
- assert(
1584
- JSON.stringify(strippedNested) === JSON.stringify({ other: { foo: 1 }, status: 'open' }),
1585
- 'Expected nested other.id_customer to be stripped'
1586
- );
1587
-
1588
- const pipeline = [{ $match: { id_customer: 'Acme', status: 'open' } }];
1589
- const strippedPipeline = stripScopedFieldsFromPipeline(pipeline, ['id_customer', 'other.id_customer']);
1590
- assert(
1591
- JSON.stringify(strippedPipeline[0]?.$match) === JSON.stringify({ status: 'open' }),
1592
- 'Expected pipeline $match id_customer to be stripped'
1593
- );
1594
- }
1595
-
1596
- function testResolveReadMultiTermJobRegexFallbackForTesting() {
1597
- const query = {
1598
- $and: [
1599
- {
1600
- $or: [
1601
- { job_number_string: { $regex: 'hazelwood', $options: 'i' } },
1602
- { job_number: { $regex: 'hazelwood', $options: 'i' } },
1603
- { bid: { $regex: 'hazelwood', $options: 'i' } },
1604
- { jca: { $regex: 'hazelwood', $options: 'i' } }
1605
- ]
1606
- },
1607
- {
1608
- $or: [
1609
- { job_number_string: { $regex: 'cross', $options: 'i' } },
1610
- { job_number: { $regex: 'cross', $options: 'i' } },
1611
- { bid: { $regex: 'cross', $options: 'i' } },
1612
- { jca: { $regex: 'cross', $options: 'i' } }
1613
- ]
1614
- },
1615
- {
1616
- $or: [
1617
- { customer: { $regex: 'pioneer', $options: 'i' } },
1618
- { operator: { $regex: 'pioneer', $options: 'i' } }
1619
- ]
1620
- }
1621
- ]
1622
- };
1623
- const probeDocs = [
1624
- {
1625
- job_number_string: 'J-1001',
1626
- pad: 'Hazelwood Cross',
1627
- wells: [{ well: 'Hazelwood Cross #1' }],
1628
- customer: 'Pioneer',
1629
- operator: 'Pioneer'
1630
- }
1631
- ];
1632
- const schemaFields = ['job_number_string', 'job_number', 'bid', 'jca', 'pad', 'wells.$.well', 'customer', 'operator'];
1633
- const fallback = resolveReadMultiTermJobRegexFallbackForTesting(query as any, probeDocs, schemaFields);
1634
- assert(!!fallback, 'Expected multi-term job regex fallback to resolve');
1635
- assert(
1636
- fallback?.terms.includes('hazelwood') && fallback?.terms.includes('cross'),
1637
- 'Expected fallback terms to include hazelwood and cross'
1638
- );
1639
- assert(
1640
- fallback?.fields.includes('pad') || fallback?.fields.includes('wells.well'),
1641
- 'Expected fallback fields to include pad or wells.well'
1642
- );
1643
- const serialized = JSON.stringify(fallback?.query || {});
1644
- assert(serialized.includes('pioneer'), 'Expected non-job customer/operator filters to be preserved');
1645
- assert(serialized.includes('hazelwood') && serialized.includes('cross'), 'Expected fallback query to preserve both job terms');
1646
- }
1647
-
1648
- function testNormalizeIdsForTargetField() {
1649
- const hex = '64b7f3f5c3a18e2f2b4a1f01';
1650
- const objectId = new ObjectId(hex);
1651
- const stringDocs = [{ id_customer: 'CUST-123' }];
1652
- const normalizedStrings = normalizeIdsForTargetField([objectId], stringDocs, 'id_customer');
1653
- assert(
1654
- Array.isArray(normalizedStrings) && normalizedStrings[0] === hex,
1655
- 'Expected ObjectId to normalize to string hex for string fields'
1656
- );
1657
-
1658
- const objectDocs = [{ id_customer: objectId }];
1659
- const normalizedObjects = normalizeIdsForTargetField([hex], objectDocs, 'id_customer');
1660
- const normalizedIsObjectId = normalizedObjects.some(value => value && typeof value.toHexString === 'function');
1661
- assert(normalizedIsObjectId, 'Expected string hex to normalize to ObjectId for objectId fields');
1662
- }
1663
-
1664
- function testRewriteEmbeddedMatchObjects() {
1665
- const query = { customer: { name: 'Acme' }, status: 'open' };
1666
- const rewritten = rewriteEmbeddedMatchObjects(query);
1667
- assert(
1668
- JSON.stringify(rewritten) === JSON.stringify({ 'customer.name': 'Acme', status: 'open' }),
1669
- 'Expected embedded match object to be rewritten to dot notation'
1670
- );
1671
-
1672
- const nestedOperator = { customer: { name: { $regex: 'Acme', $options: 'i' } } };
1673
- const rewrittenOperator = rewriteEmbeddedMatchObjects(nestedOperator);
1674
- assert(
1675
- !!rewrittenOperator['customer.name']?.$regex,
1676
- 'Expected operator object to be preserved under dot notation'
1677
- );
1678
-
1679
- const fieldOperator = { customer: { $in: ['A', 'B'] } };
1680
- const rewrittenFieldOperator = rewriteEmbeddedMatchObjects(fieldOperator);
1681
- assert(
1682
- JSON.stringify(rewrittenFieldOperator) === JSON.stringify(fieldOperator),
1683
- 'Expected field-level operator objects to remain unchanged'
1684
- );
1685
-
1686
- const logicalOperator = {
1687
- $or: [
1688
- { customer: { name: 'Acme' } },
1689
- { customer: { name: 'Globex' } }
1690
- ]
1691
- };
1692
- const rewrittenLogical = rewriteEmbeddedMatchObjects(logicalOperator);
1693
- assert(
1694
- JSON.stringify(rewrittenLogical) === JSON.stringify({
1695
- $or: [
1696
- { 'customer.name': 'Acme' },
1697
- { 'customer.name': 'Globex' }
1698
- ]
1699
- }),
1700
- 'Expected logical operator entries to be flattened'
1701
- );
1702
- }
1703
-
1704
- function testRewriteMatchExpressionsToExpr() {
1705
- const query = {
1706
- status: 'Paid/Closed',
1707
- date_paid: {
1708
- $gte: { $dateSubtract: { startDate: '$$NOW', unit: 'month', amount: 6 } },
1709
- $lte: '$$NOW'
1710
- }
1711
- };
1712
- const rewritten = rewriteMatchExpressionsToExpr(query);
1713
- assert(
1714
- JSON.stringify(rewritten) === JSON.stringify({
1715
- status: 'Paid/Closed',
1716
- $expr: {
1717
- $and: [
1718
- { $gte: ['$date_paid', { $dateSubtract: { startDate: '$$NOW', unit: 'month', amount: 6 } }] },
1719
- { $lte: ['$date_paid', '$$NOW'] }
1720
- ]
1721
- }
1722
- }),
1723
- 'Expected expression-style match operators to be rewritten into $expr'
1724
- );
1725
- }
1726
-
1727
- function testRewriteMatchExpressionsToExprCompactNowToken() {
1728
- const query = {
1729
- date_paid: {
1730
- $gte: '$$NOW-6M',
1731
- $lte: '$$NOW'
1732
- }
1733
- };
1734
- const rewritten = rewriteMatchExpressionsToExpr(query);
1735
- assert(
1736
- JSON.stringify(rewritten) === JSON.stringify({
1737
- $expr: {
1738
- $and: [
1739
- { $gte: ['$date_paid', { $dateSubtract: { startDate: '$$NOW', unit: 'month', amount: 6 } }] },
1740
- { $lte: ['$date_paid', '$$NOW'] }
1741
- ]
1742
- }
1743
- }),
1744
- 'Expected compact $$NOW-6M token to be normalized into $dateSubtract'
1745
- );
1746
- }
1747
-
1748
- function testNormalizeAssistantNowExprPlaceholdersDateArithmeticArgs() {
1749
- const query = {
1750
- $expr: {
1751
- $gte: [
1752
- '$date_paid',
1753
- { $dateSubtract: { date: '$$NOW', unit: 'month', amount: 6 } }
1754
- ]
1755
- }
1756
- };
1757
- const normalized = normalizeAssistantNowExprPlaceholders(query);
1758
- const dateSubtractArgs = normalized?.$expr?.$gte?.[1]?.$dateSubtract;
1759
- assert(!!dateSubtractArgs, 'Expected $dateSubtract args to exist');
1760
- assert(dateSubtractArgs.startDate === '$$NOW', 'Expected $dateSubtract.date to normalize to startDate');
1761
- assert(!Object.prototype.hasOwnProperty.call(dateSubtractArgs, 'date'), 'Expected legacy $dateSubtract.date arg to be removed');
1762
- }
1763
-
1764
- function testNormalizeAssistantNowExprPlaceholdersDateArithmeticUnknownKeyAsStartDate() {
1765
- const query = {
1766
- $expr: {
1767
- $gte: [
1768
- '$date_paid',
1769
- { $dateSubtract: { 'misc_charges.amount': '$$NOW', unit: 'month', amount: 6 } }
1770
- ]
1771
- }
1772
- };
1773
- const normalized = normalizeAssistantNowExprPlaceholders(query);
1774
- const dateSubtractArgs = normalized?.$expr?.$gte?.[1]?.$dateSubtract;
1775
- assert(!!dateSubtractArgs, 'Expected $dateSubtract args to exist');
1776
- assert(dateSubtractArgs.startDate === '$$NOW', 'Expected unknown key value to normalize to startDate');
1777
- assert(dateSubtractArgs.amount === 6, 'Expected amount to remain numeric');
1778
- assert(!Object.prototype.hasOwnProperty.call(dateSubtractArgs, 'misc_charges.amount'), 'Expected unknown key to be removed');
1779
- }
1780
-
1781
- function testNormalizeAssistantNowExprPlaceholdersDateArithmeticUnknownKeyAsAmount() {
1782
- const query = {
1783
- $expr: {
1784
- $gte: [
1785
- '$date_paid',
1786
- { $dateSubtract: { startDate: '$$NOW', unit: 'month', 'misc_charges.amount': 6 } }
1787
- ]
1788
- }
1789
- };
1790
- const normalized = normalizeAssistantNowExprPlaceholders(query);
1791
- const dateSubtractArgs = normalized?.$expr?.$gte?.[1]?.$dateSubtract;
1792
- assert(!!dateSubtractArgs, 'Expected $dateSubtract args to exist');
1793
- assert(dateSubtractArgs.startDate === '$$NOW', 'Expected startDate to remain intact');
1794
- assert(dateSubtractArgs.amount === 6, 'Expected unknown numeric key to normalize to amount');
1795
- assert(!Object.prototype.hasOwnProperty.call(dateSubtractArgs, 'misc_charges.amount'), 'Expected unknown key to be removed');
1796
- }
1797
-
1798
- function testNormalizeAssistantMonthlyCalendarWindowPipelineConvertsRollingMonthlyRange() {
1799
- const pipeline = [
1800
- {
1801
- $match: {
1802
- $expr: {
1803
- $and: [
1804
- {
1805
- $gte: [
1806
- '$date_invoice',
1807
- { $dateSubtract: { startDate: '$$NOW', unit: 'month', amount: 6 } }
1808
- ]
1809
- },
1810
- { $lte: ['$date_invoice', '$$NOW'] }
1811
- ]
1812
- }
1813
- }
1814
- },
1815
- {
1816
- $group: {
1817
- _id: {
1818
- $dateToString: {
1819
- format: '%Y-%m',
1820
- date: '$date_invoice'
1821
- }
1822
- },
1823
- total_revenue: { $sum: '$grand_total' }
1824
- }
1825
- }
1826
- ];
1827
- const normalized = normalizeAssistantMonthlyCalendarWindowPipeline(pipeline as any);
1828
- const andClauses = normalized?.[0]?.$match?.$expr?.$and;
1829
- assert(Array.isArray(andClauses), 'Expected normalized $expr.$and clauses');
1830
- const lowerClause = andClauses.find((entry: any) => entry?.$gte?.[0] === '$date_invoice');
1831
- assert(!!lowerClause, 'Expected lower month bound clause');
1832
- const lowerSubtract = lowerClause?.$gte?.[1]?.$dateSubtract;
1833
- assert(!!lowerSubtract, 'Expected $dateSubtract lower bound');
1834
- assert(lowerSubtract?.startDate?.$dateTrunc?.date === '$$NOW', 'Expected lower bound anchored to month start');
1835
- assert(lowerSubtract?.startDate?.$dateTrunc?.unit === 'month', 'Expected lower bound month truncation');
1836
- assert(lowerSubtract?.amount === 6, 'Expected month count preserved');
1837
- const upperClause = andClauses.find((entry: any) => entry?.$lt?.[0] === '$date_invoice');
1838
- assert(!!upperClause, 'Expected strict upper month-start clause');
1839
- assert(upperClause?.$lt?.[1]?.$dateTrunc?.date === '$$NOW', 'Expected upper bound anchored to current month start');
1840
- assert(upperClause?.$lt?.[1]?.$dateTrunc?.unit === 'month', 'Expected upper bound month truncation');
1841
- assert(!andClauses.some((entry: any) => entry?.$lte?.[1] === '$$NOW'), 'Expected rolling $$NOW upper bound removed');
1842
- }
1843
-
1844
- function testNormalizeAssistantMonthlyCalendarWindowPipelineSkipsDailyGrouping() {
1845
- const pipeline = [
1846
- {
1847
- $match: {
1848
- $expr: {
1849
- $and: [
1850
- {
1851
- $gte: [
1852
- '$date_invoice',
1853
- { $dateSubtract: { startDate: '$$NOW', unit: 'month', amount: 6 } }
1854
- ]
1855
- },
1856
- { $lte: ['$date_invoice', '$$NOW'] }
1857
- ]
1858
- }
1859
- }
1860
- },
1861
- {
1862
- $group: {
1863
- _id: {
1864
- $dateToString: {
1865
- format: '%Y-%m-%d',
1866
- date: '$date_invoice'
1867
- }
1868
- },
1869
- total_revenue: { $sum: '$grand_total' }
1870
- }
1871
- }
1872
- ];
1873
- const normalized = normalizeAssistantMonthlyCalendarWindowPipeline(pipeline as any);
1874
- const andClauses = normalized?.[0]?.$match?.$expr?.$and;
1875
- assert(Array.isArray(andClauses), 'Expected daily grouping to preserve $and clauses');
1876
- assert(andClauses.some((entry: any) => entry?.$lte?.[1] === '$$NOW'), 'Expected rolling $$NOW upper bound to remain for non-monthly grouping');
1877
- }
1878
-
1879
- function testExpandAggregateDateMatchFallbackExpandsNestedOrClauses() {
1880
- const pipeline = [
1881
- {
1882
- $match: {
1883
- $and: [
1884
- {
1885
- $or: [
1886
- {
1887
- date_created: { $gte: new Date('2026-02-16T00:00:00.000Z') },
1888
- $expr: { $lte: ['$date_created', '$$NOW'] }
1889
- },
1890
- {
1891
- createdAt: { $gte: new Date('2026-02-16T00:00:00.000Z') },
1892
- $expr: { $lte: ['$createdAt', '$$NOW'] }
1893
- }
1894
- ]
1895
- }
1896
- ]
1897
- }
1898
- },
1899
- {
1900
- $group: {
1901
- _id: '$customer',
1902
- work_orders: { $sum: 1 }
1903
- }
1904
- }
1905
- ];
1906
- const expanded = expandAggregateDateMatchFallbackForTesting(
1907
- pipeline as any,
1908
- ['date_completed', 'date_closed', 'date_created', 'createdAt']
1909
- );
1910
- assert(!!expanded, 'Expected aggregate date fallback to expand nested $or clauses');
1911
- assert(
1912
- Array.isArray(expanded?.fields) && expanded?.fields.includes('date_completed') && expanded?.fields.includes('date_closed'),
1913
- 'Expected aggregate date fallback to add completed and closed date fields'
1914
- );
1915
- const expandedOr = expanded?.pipeline?.[0]?.$match?.$and?.[0]?.$or;
1916
- assert(Array.isArray(expandedOr), 'Expected expanded nested $or array');
1917
- assert(
1918
- expandedOr.some((entry: any) => Object.prototype.hasOwnProperty.call(entry, 'date_completed')),
1919
- 'Expected nested $or to include date_completed fallback branch'
1920
- );
1921
- assert(
1922
- expandedOr.some((entry: any) => Object.prototype.hasOwnProperty.call(entry, 'date_closed')),
1923
- 'Expected nested $or to include date_closed fallback branch'
1924
- );
1925
- }
1926
-
1927
- function testNormalizeAssistantAggregatePipelineRankSortBySingleKey() {
1928
- const pipeline = [
1929
- {
1930
- $setWindowFields: {
1931
- partitionBy: null,
1932
- sortBy: {
1933
- total_by_driver: -1,
1934
- driver: 1
1935
- },
1936
- output: {
1937
- driver_rank: { $rank: {} }
1938
- }
1939
- }
1940
- }
1941
- ];
1942
- const normalized = normalizeAssistantAggregatePipeline(pipeline as any, 'production-deliveries');
1943
- const rankStage = normalized?.[0]?.$setWindowFields;
1944
- const sortBy = rankStage?.sortBy || {};
1945
- const keys = Object.keys(sortBy);
1946
- assert(keys.length === 1, 'Expected rank sortBy to be normalized to exactly one key');
1947
- assert(keys[0] === 'total_by_driver', 'Expected first sortBy key to be preserved for rank');
1948
- assert(sortBy.total_by_driver === -1, 'Expected rank sort direction to be preserved');
1949
- }
1950
-
1951
- function testNormalizeAssistantAggregatePipelineMovesLastNLimitBeforeGroup() {
1952
- const pipeline = [
1953
- {
1954
- $match: {
1955
- date_created: {
1956
- $gte: new Date('2026-02-09T00:00:00.000Z'),
1957
- $lt: new Date('2026-02-10T00:00:00.000Z')
1958
- }
1959
- }
1960
- },
1961
- {
1962
- $group: {
1963
- _id: '$status',
1964
- count: { $sum: 1 }
1965
- }
1966
- },
1967
- { $sort: { count: -1 } },
1968
- { $limit: 20 }
1969
- ];
1970
- const normalized = normalizeAssistantAggregatePipeline(
1971
- pipeline as any,
1972
- 'maintenance-orders',
1973
- 'Show me the last 20 work orders created this week, grouped by status.'
1974
- );
1975
- const groupIndex = normalized.findIndex((stage: any) => !!stage?.$group);
1976
- const limitIndex = normalized.findIndex((stage: any) => typeof stage?.$limit === 'number');
1977
- const sortIndex = normalized.findIndex((stage: any) => !!stage?.$sort && stage.$sort.date_created === -1);
1978
- assert(groupIndex > -1, 'Expected group stage to remain in pipeline');
1979
- assert(limitIndex > -1, 'Expected limit stage to exist');
1980
- assert(sortIndex > -1, 'Expected created-date sort stage to be added before grouping');
1981
- assert(limitIndex < groupIndex, 'Expected record limit to be applied before grouping');
1982
- assert(sortIndex < limitIndex, 'Expected record sort to happen before record limit');
1983
- assert((normalized[limitIndex] as any).$limit === 20, 'Expected requested record limit to be preserved');
1984
- }
1985
-
1986
- function testNormalizeAssistantAggregatePipelineKeepsTrendGroupingLimitAfterGroup() {
1987
- const pipeline = [
1988
- {
1989
- $match: {
1990
- date_invoice: {
1991
- $gte: new Date('2025-08-01T00:00:00.000Z'),
1992
- $lt: new Date('2026-02-01T00:00:00.000Z')
1993
- }
1994
- }
1995
- },
1996
- {
1997
- $group: {
1998
- _id: {
1999
- $dateToString: {
2000
- format: '%Y-%m',
2001
- date: '$date_invoice'
2002
- }
2003
- },
2004
- total: { $sum: '$grand_total' }
2005
- }
2006
- },
2007
- { $sort: { _id: 1 } },
2008
- { $limit: 20 }
2009
- ];
2010
- const normalized = normalizeAssistantAggregatePipeline(
2011
- pipeline as any,
2012
- 'invoices',
2013
- 'Break down total revenue over the last 6 months by month.'
2014
- );
2015
- const groupIndex = normalized.findIndex((stage: any) => !!stage?.$group);
2016
- const limitIndex = normalized.findIndex((stage: any) => typeof stage?.$limit === 'number');
2017
- assert(groupIndex > -1, 'Expected group stage to remain in trend pipeline');
2018
- assert(limitIndex > groupIndex, 'Expected trend/month grouping limit to remain post-group');
2019
- }
2020
-
2021
- function testBuildAssistantToolRequestRestoresMongoSafeDirectiveKeys() {
2022
- const request = buildAssistantToolRequest({
2023
- type: 'aggregate',
2024
- payload: {
2025
- collection: 'customers',
2026
- pipeline: [
2027
- {
2028
- _dollar_match: {
2029
- 'customer__dot__name': 'Acme',
2030
- total: { _dollar_gt: 0 }
2031
- }
2032
- }
2033
- ]
2034
- },
2035
- cleaned: '',
2036
- rawLine: 'REPORT_BUILDER_AGG: {}'
2037
- } as any, {
2038
- message: 'Show active clients'
2039
- } as any);
2040
- assert(request?.pipeline?.[0]?.$match, 'Expected Mongo-safe stage keys to restore to $ operators');
2041
- assert(
2042
- request?.pipeline?.[0]?.$match?.['customer.name'] === 'Acme',
2043
- 'Expected Mongo-safe dotted field names to restore before tool execution'
2044
- );
2045
- assert(
2046
- request?.pipeline?.[0]?.$match?.total?.$gt === 0,
2047
- 'Expected nested Mongo-safe operators to restore before tool execution'
2048
- );
2049
- }
2050
-
2051
- function testRepairAssistantFieldPathReferenceInPipelineUsesGetFieldForDottedPath() {
2052
- const pipeline = [
2053
- {
2054
- $addFields: {
2055
- volume_unit: '$blend_chemicals.unit',
2056
- volume_value: '$blend_chemicals.containers.quantity'
2057
- }
2058
- }
2059
- ];
2060
- const repaired = repairAssistantFieldPathReferenceInPipeline(pipeline as any, 'blend_chemicals.unit');
2061
- assert(repaired.changed, 'Expected dotted field-path references to be rewritten');
2062
- const unitExpr = repaired.pipeline?.[0]?.$addFields?.volume_unit;
2063
- assert(!!unitExpr?.$getField, 'Expected volume_unit to use $getField expression');
2064
- assert(unitExpr?.$getField?.field === 'unit', 'Expected final $getField segment to target unit');
2065
- assert(unitExpr?.$getField?.input === '$blend_chemicals', 'Expected $getField input to be blend_chemicals root');
2066
- const quantityExpr = repaired.pipeline?.[0]?.$addFields?.volume_value;
2067
- assert(
2068
- quantityExpr === '$blend_chemicals.containers.quantity',
2069
- 'Expected non-targeted field path references to remain unchanged'
2070
- );
2071
- }
2072
-
2073
- function testRepairAssistantPositionalPathSegmentsInPipeline() {
2074
- const pipeline = [
2075
- {
2076
- $match: {
2077
- 'routes.$.date_needed.date': {
2078
- $gte: new Date('2026-02-16T00:00:00.000Z')
2079
- }
2080
- }
2081
- },
2082
- {
2083
- $group: {
2084
- _id: '$routes.$.id_location',
2085
- count: { $sum: 1 }
2086
- }
2087
- },
2088
- {
2089
- $sort: {
2090
- '_id.routes.$.id_location': 1
2091
- }
2092
- },
2093
- {
2094
- $project: {
2095
- route_date: '$routes.$.date_needed.date'
2096
- }
2097
- }
2098
- ];
2099
- const repaired = repairAssistantPositionalPathSegmentsInPipeline(pipeline as any);
2100
- assert(repaired.changed, 'Expected positional path segments to be repaired');
2101
- const matchKeys = Object.keys(repaired.pipeline?.[0]?.$match || {});
2102
- assert(
2103
- matchKeys.includes('routes.date_needed.date'),
2104
- 'Expected positional $ segment to be removed from match field'
2105
- );
2106
- assert(
2107
- !matchKeys.includes('routes.$.date_needed.date'),
2108
- 'Expected legacy positional match field to be removed'
2109
- );
2110
- assert(
2111
- repaired.pipeline?.[1]?.$group?._id === '$routes.id_location',
2112
- 'Expected positional $ segment to be removed from field reference'
2113
- );
2114
- assert(
2115
- Object.prototype.hasOwnProperty.call(repaired.pipeline?.[2]?.$sort || {}, '_id.routes.id_location'),
2116
- 'Expected positional $ segment to be removed from sort key'
2117
- );
2118
- assert(
2119
- repaired.pipeline?.[3]?.$project?.route_date === '$routes.date_needed.date',
2120
- 'Expected positional $ segment to be removed from projection field reference'
2121
- );
2122
- }
2123
-
2124
- function testRepairAssistantFieldPathReferenceInPipelineSkipsUndottedPath() {
2125
- const pipeline = [
2126
- {
2127
- $addFields: {
2128
- volume_unit: '$volume_unit'
2129
- }
2130
- }
2131
- ];
2132
- const repaired = repairAssistantFieldPathReferenceInPipeline(pipeline as any, 'volume_unit');
2133
- assert(!repaired.changed, 'Expected non-dotted field path to remain unchanged');
2134
- assert(
2135
- repaired.pipeline?.[0]?.$addFields?.volume_unit === '$volume_unit',
2136
- 'Expected original reference to be preserved'
2137
- );
2138
- }
2139
-
2140
- function testShouldUseAssistantNavigationFastPath() {
2141
- assert(
2142
- shouldUseAssistantNavigationFastPath('What is the route link for invoice list?', false),
2143
- 'Expected route-link request to use navigation fast path'
2144
- );
2145
- assert(
2146
- !shouldUseAssistantNavigationFastPath('How many invoices were paid this month?', false),
2147
- 'Expected data-count request to skip navigation fast path'
2148
- );
2149
- assert(
2150
- !shouldUseAssistantNavigationFastPath('What changed on this page?', false),
2151
- 'Expected change-history prompt to skip navigation fast path'
2152
- );
2153
- assert(
2154
- !shouldUseAssistantNavigationFastPath('How long has the exception list been on this page?', false),
2155
- 'Expected "how long on this page" prompt to skip navigation fast path'
2156
- );
2157
- assert(
2158
- !shouldUseAssistantNavigationFastPath('Why don\'t I see this asset in this dropdown?', false),
2159
- 'Expected bug/issue prompt to skip navigation fast path'
2160
- );
2161
- assert(
2162
- !shouldUseAssistantNavigationFastPath('Route link for invoices', true),
2163
- 'Expected attachments to disable navigation fast path'
2164
- );
2165
- }
2166
-
2167
- function testRankAssistantNavigationRoutes() {
2168
- const ranked = rankAssistantNavigationRoutes(
2169
- 'Need the invoice list route link',
2170
- ['/dashboard', '/invoice/list', '/invoice/detail', '/chemical/list'],
2171
- 2
2172
- );
2173
- assert(ranked.length === 2, 'Expected ranked route limit to apply');
2174
- assert(ranked[0] === '/invoice/list', 'Expected invoice list route to rank first');
2175
- }
2176
-
2177
- function testShouldUseAssistantChangeHistoryFastPath() {
2178
- assert(
2179
- shouldUseAssistantChangeHistoryFastPath('What changed recently in GitHub?', false),
2180
- 'Expected change-history prompt to use change-history fast path'
2181
- );
2182
- assert(
2183
- shouldUseAssistantChangeHistoryFastPath('When was the last time the invoice document was updated?', false),
2184
- 'Expected "last updated" prompt to use change-history fast path'
2185
- );
2186
- assert(
2187
- shouldUseAssistantChangeHistoryFastPath('When does this dropdown for lease type get added to this page?', false),
2188
- 'Expected "when does this dropdown get added" prompt to use change-history fast path'
2189
- );
2190
- assert(
2191
- shouldUseAssistantChangeHistoryFastPath('What changed on this page?', false),
2192
- 'Expected "what changed on this page" prompt to use change-history fast path'
2193
- );
2194
- assert(
2195
- shouldUseAssistantChangeHistoryFastPath('What changed on the invoice page?', false),
2196
- 'Expected "what changed on the invoice page" prompt to use change-history fast path'
2197
- );
2198
- assert(
2199
- shouldUseAssistantChangeHistoryFastPath('What did the last update just do?', false),
2200
- 'Expected "what did the last update do" prompt to use change-history fast path'
2201
- );
2202
- assert(
2203
- shouldUseAssistantChangeHistoryFastPath('What did the last system update do?', false),
2204
- 'Expected "what did the last system update do" prompt to use change-history fast path'
2205
- );
2206
- assert(
2207
- shouldUseAssistantChangeHistoryFastPath('How long has this been this way?', false),
2208
- 'Expected duration prompt to use change-history fast path'
2209
- );
2210
- assert(
2211
- shouldUseAssistantChangeHistoryFastPath('What is this new exception list? How long has it been around?', false),
2212
- 'Expected "been around" prompt to use change-history fast path'
2213
- );
2214
- assert(
2215
- shouldUseAssistantChangeHistoryFastPath('How long has the exception list been on this page?', false),
2216
- 'Expected "been on this page" prompt to use change-history fast path'
2217
- );
2218
- assert(
2219
- !shouldUseAssistantChangeHistoryFastPath('How many invoices were paid this month?', false),
2220
- 'Expected quantitative data request to skip change-history fast path'
2221
- );
2222
- assert(
2223
- !shouldUseAssistantChangeHistoryFastPath('What changed recently?', true),
2224
- 'Expected attachments to disable change-history fast path'
2225
- );
2226
- }
2227
-
2228
- function testBuildAssistantChangeHistorySummaryPermissionAware() {
2229
- const commits = [
2230
- {
2231
- date: '2026-02-05T12:00:00.000Z',
2232
- subject: 'Fix invoice total rounding for customer statements',
2233
- files: ['server/invoice/report-builder.ts'],
2234
- modules: ['invoice']
2235
- },
2236
- {
2237
- date: '2026-02-01T12:00:00.000Z',
2238
- subject: 'Improve work order assignment reliability',
2239
- files: ['server/work-order/assignment.ts'],
2240
- modules: ['work_order']
2241
- }
2242
- ] as any[];
2243
-
2244
- const invoiceUser = {
2245
- roles: {
2246
- super_admin: false,
2247
- groups: [{ name: 'Accounting', views: ['/invoice/list', '/dashboard'] }],
2248
- miscs: []
2249
- },
2250
- other: {}
2251
- };
2252
- const noInvoiceUser = {
2253
- roles: {
2254
- super_admin: false,
2255
- groups: [{ name: 'Field', views: ['/work-order/list'] }],
2256
- miscs: []
2257
- },
2258
- other: {}
2259
- };
2260
-
2261
- const detailed = buildAssistantChangeHistorySummaryFromCommits({
2262
- message: 'What changed in invoices recently?',
2263
- user: invoiceUser,
2264
- isSuperAdmin: false,
2265
- commits,
2266
- branch: 'main',
2267
- now: new Date('2026-02-08T00:00:00.000Z')
2268
- });
2269
- assert(detailed.reason === 'detailed', 'Expected detailed summary for invoice-permitted user');
2270
- assert(!detailed.generic, 'Expected detailed summary to be non-generic');
2271
- assert(detailed.commitCount > 0, 'Expected at least one permitted commit in detailed summary');
2272
- assert(
2273
- String(detailed.response || '').includes('Fix invoice total rounding'),
2274
- 'Expected detailed summary to include invoice change subject'
2275
- );
2276
-
2277
- const generic = buildAssistantChangeHistorySummaryFromCommits({
2278
- message: 'What changed in invoices recently?',
2279
- user: noInvoiceUser,
2280
- isSuperAdmin: false,
2281
- commits,
2282
- branch: 'main',
2283
- now: new Date('2026-02-08T00:00:00.000Z')
2284
- });
2285
- assert(generic.reason === 'generic', 'Expected generic summary for invoice-restricted user');
2286
- assert(generic.generic, 'Expected generic summary to be marked generic');
2287
- assert(generic.commitCount === 0, 'Expected generic summary to hide commit details');
2288
- assert(
2289
- !String(generic.response || '').includes('Fix invoice total rounding'),
2290
- 'Expected generic summary to redact invoice change subject'
2291
- );
2292
- }
2293
-
2294
- function testBuildAssistantChangeHistorySummaryDuration() {
2295
- const workOrderUser = {
2296
- roles: {
2297
- super_admin: false,
2298
- groups: [{ name: 'Operations', views: ['/work-order/list'] }],
2299
- miscs: []
2300
- },
2301
- other: {}
2302
- };
2303
- const commits = [
2304
- {
2305
- date: '2025-11-15T10:00:00.000Z',
2306
- subject: 'Adjust work order scheduler defaults',
2307
- files: ['server/work-order/scheduler.ts'],
2308
- modules: ['work_order']
2309
- },
2310
- {
2311
- date: '2026-02-06T10:00:00.000Z',
2312
- subject: 'Fix work order dispatch edge case',
2313
- files: ['server/work-order/dispatch.ts'],
2314
- modules: ['work_order']
2315
- }
2316
- ] as any[];
2317
- const summary = buildAssistantChangeHistorySummaryFromCommits({
2318
- message: 'How long has this been this way for work orders?',
2319
- user: workOrderUser,
2320
- isSuperAdmin: false,
2321
- commits,
2322
- branch: 'main',
2323
- now: new Date('2026-02-08T00:00:00.000Z')
2324
- });
2325
- assert(summary.reason === 'detailed', 'Expected detailed duration summary for permitted module');
2326
- assert(
2327
- String(summary.response || '').includes('since at least 2025-11-15'),
2328
- 'Expected duration summary to include oldest known date'
2329
- );
2330
- }
2331
-
2332
- function testBuildAssistantChangeHistorySummaryFeatureKeywordScoping() {
2333
- const commits = [
2334
- {
2335
- date: '2026-02-08T12:00:00.000Z',
2336
- subject: 'Improve deploy queue resiliency for worker restarts',
2337
- files: ['server/deploy/queue.ts'],
2338
- modules: ['general']
2339
- },
2340
- {
2341
- date: '2025-09-01T12:00:00.000Z',
2342
- subject: 'Add exception list filtering for deploy instances',
2343
- files: ['resolveio/angular/app/dashboard/deploy-instance/deploy-instances.component.ts'],
2344
- modules: ['general']
2345
- },
2346
- {
2347
- date: '2024-01-15T12:00:00.000Z',
2348
- subject: 'Migrate logging pipeline internals',
2349
- files: ['server/logging/pipeline.ts'],
2350
- modules: ['internal']
2351
- }
2352
- ] as any[];
2353
-
2354
- const summary = buildAssistantChangeHistorySummaryFromCommits({
2355
- message: 'What is this new exception list? How long has it been around?',
2356
- user: { roles: { super_admin: true }, other: {} },
2357
- isSuperAdmin: true,
2358
- commits,
2359
- branch: 'main',
2360
- now: new Date('2026-02-08T00:00:00.000Z')
2361
- });
2362
- assert(summary.reason === 'detailed', 'Expected detailed summary for super admin');
2363
- assert(
2364
- String(summary.response || '').includes('since at least 2025-09-01'),
2365
- 'Expected duration summary to scope oldest date to exception-list related history'
2366
- );
2367
- assert(
2368
- !String(summary.response || '').includes('since at least 2024-01-15'),
2369
- 'Expected duration summary to avoid unrelated older commit history'
2370
- );
2371
- }
2372
-
2373
- function testBuildAssistantChangeHistorySummaryLibraryRepoKeepsDatesButRedactsDetails() {
2374
- const commits = [
2375
- {
2376
- date: '2026-01-15T12:00:00.000Z',
2377
- subject: 'Refactor invoice aggregation fallback logic',
2378
- files: ['src/methods/ai-terminal.ts'],
2379
- modules: ['invoice']
2380
- }
2381
- ] as any[];
2382
- const summary = buildAssistantChangeHistorySummaryFromCommits({
2383
- message: 'What changed recently in invoices?',
2384
- user: { roles: { super_admin: true }, other: {} },
2385
- isSuperAdmin: true,
2386
- commits,
2387
- branch: 'main',
2388
- repositoryName: 'resolveio-server-lib',
2389
- now: new Date('2026-02-08T00:00:00.000Z')
2390
- });
2391
- assert(summary.reason === 'detailed', 'Expected detailed summary to remain available for library repos');
2392
- assert(
2393
- String(summary.response || '').includes('2026-01-15'),
2394
- 'Expected library summary to retain date visibility'
2395
- );
2396
- assert(
2397
- !String(summary.response || '').includes('Refactor invoice aggregation fallback logic'),
2398
- 'Expected library summary to redact low-level implementation details'
2399
- );
2400
- assert(
2401
- String(summary.response || '').includes('Platform/library update'),
2402
- 'Expected library summary to use high-level library wording'
2403
- );
2404
- }
2405
-
2406
- async function testResolveAssistantReportBuilderBridgeCollectionModeAndPath() {
2407
- const originalGetMongoManager = (ResolveIOServer as any).getMongoManager;
2408
- try {
2409
- (ResolveIOServer as any).getMongoManager = () => ({
2410
- collection: (name: string) => (name === 'report-orders' ? { useRB: true } : {})
2411
- });
2412
- const rbPreferred = await resolveAssistantReportBuilderBridgeCollection('orders');
2413
- assert(rbPreferred.collection === 'report-orders', 'Expected report collection selection when useRB is available');
2414
- assert(rbPreferred.mode === 'report-builder', 'Expected report-builder mode when useRB is available');
2415
- assert(
2416
- Array.isArray(rbPreferred.resolutionPath)
2417
- && rbPreferred.resolutionPath.some(step => String(step).includes('manager.useRB:report-orders')),
2418
- 'Expected bridge resolution path to include manager.useRB step'
2419
- );
2420
-
2421
- (ResolveIOServer as any).getMongoManager = () => ({
2422
- collection: (_name: string) => ({})
2423
- });
2424
- const fakeDb = {
2425
- listCollections: (filter?: Record<string, any>) => ({
2426
- toArray: async () => {
2427
- const name = String(filter?.name || '');
2428
- if (!name) {
2429
- return [{ name: 'orders' }];
2430
- }
2431
- return name === 'orders' ? [{ name: 'orders' }] : [];
2432
- }
2433
- })
2434
- };
2435
- const directFallback = await resolveAssistantReportBuilderBridgeCollection('orders', fakeDb as any, 'resolveio');
2436
- assert(directFallback.collection === 'orders', 'Expected direct fallback to keep base collection');
2437
- assert(directFallback.mode === 'direct-mongo', 'Expected direct-mongo mode when RB is unavailable');
2438
- assert(
2439
- Array.isArray(directFallback.resolutionPath)
2440
- && directFallback.resolutionPath.some(step => String(step).includes('db.exists:orders')),
2441
- 'Expected bridge resolution path to include db existence fallback'
2442
- );
2443
- }
2444
- finally {
2445
- (ResolveIOServer as any).getMongoManager = originalGetMongoManager;
2446
- }
2447
- }
2448
-
2449
- async function testResolveAssistantReportBuilderBridgeCollectionPreservesConfiguredCollection() {
2450
- const originalGetMongoManager = (ResolveIOServer as any).getMongoManager;
2451
- try {
2452
- (ResolveIOServer as any).getMongoManager = () => ({
2453
- collections: () => [
2454
- { collectionName: 'work-order-dynamics' },
2455
- { collectionName: 'production-sales-orders' }
2456
- ],
2457
- collection: (_name: string) => ({})
2458
- });
2459
- const fakeDb = {
2460
- listCollections: (filter?: Record<string, any>) => ({
2461
- toArray: async () => {
2462
- const name = String(filter?.name || '');
2463
- if (!name) {
2464
- return [{ name: 'production-sales-orders' }];
2465
- }
2466
- return name === 'production-sales-orders' ? [{ name }] : [];
2467
- }
2468
- })
2469
- };
2470
- const resolved = await resolveAssistantReportBuilderBridgeCollection(
2471
- 'work-order-dynamics',
2472
- fakeDb as any,
2473
- 'resolveio',
2474
- {
2475
- requestHints: {
2476
- collectionHints: ['work-order-dynamics', 'production-sales-orders'],
2477
- rankedCollections: ['production-sales-orders', 'work-order-dynamics']
2478
- } as any
2479
- }
2480
- );
2481
- assert(
2482
- resolved.collection === 'work-order-dynamics',
2483
- 'Expected configured work-order collection to win over neighboring DB-listed collections'
2484
- );
2485
- assert(resolved.mode === 'direct-mongo', 'Expected configured collection fallback to use direct-mongo mode');
2486
- assert(
2487
- Array.isArray(resolved.resolutionPath)
2488
- && resolved.resolutionPath.some(step => String(step).includes('manager.configured:work-order-dynamics')),
2489
- 'Expected bridge resolution path to include manager.configured step'
2490
- );
2491
- }
2492
- finally {
2493
- (ResolveIOServer as any).getMongoManager = originalGetMongoManager;
2494
- }
2495
- }
2496
-
2497
- async function testResolveAssistantReportBuilderBridgeCollectionPreservesPrimaryHintCollection() {
2498
- const originalGetMongoManager = (ResolveIOServer as any).getMongoManager;
2499
- try {
2500
- (ResolveIOServer as any).getMongoManager = () => ({
2501
- collections: () => [],
2502
- collection: (_name: string) => ({})
2503
- });
2504
- const fakeDb = {
2505
- listCollections: (filter?: Record<string, any>) => ({
2506
- toArray: async () => {
2507
- const name = String(filter?.name || '');
2508
- if (!name) {
2509
- return [{ name: 'production-sales-orders' }];
2510
- }
2511
- return name === 'production-sales-orders' ? [{ name }] : [];
2512
- }
2513
- })
2514
- };
2515
- const resolved = await resolveAssistantReportBuilderBridgeCollection(
2516
- 'work-order-dynamics',
2517
- fakeDb as any,
2518
- 'resolveio',
2519
- {
2520
- requestHints: {
2521
- collectionHints: ['work-order-dynamics', 'jobs', 'production-sales-orders'],
2522
- rankedCollections: ['production-sales-orders', 'work-order-dynamics']
2523
- } as any
2524
- }
2525
- );
2526
- assert(
2527
- resolved.collection === 'work-order-dynamics',
2528
- 'Expected primary collection hint to prevent bridge fallback to neighboring collections'
2529
- );
2530
- assert(
2531
- Array.isArray(resolved.resolutionPath)
2532
- && resolved.resolutionPath.some(step => String(step).includes('hints.primary:work-order-dynamics')),
2533
- 'Expected bridge resolution path to include primary hint preservation'
2534
- );
2535
- }
2536
- finally {
2537
- (ResolveIOServer as any).getMongoManager = originalGetMongoManager;
2538
- }
2539
- }
2540
-
2541
- async function testResolveAssistantCollectionNameMergesConfiguredCollectionsIntoFreshCache() {
2542
- const originalGetMongoManager = (ResolveIOServer as any).getMongoManager;
2543
- const fakeDb = {
2544
- listCollections: (filter?: Record<string, any>) => ({
2545
- toArray: async () => {
2546
- const name = String(filter?.name || '');
2547
- if (!name) {
2548
- return [{ name: 'production-sales-orders' }];
2549
- }
2550
- return name === 'production-sales-orders' ? [{ name }] : [];
2551
- }
2552
- })
2553
- };
2554
- try {
2555
- (ResolveIOServer as any).getMongoManager = () => ({
2556
- collections: () => [],
2557
- collection: (_name: string) => ({})
2558
- });
2559
- const dbName = 'resolveio-cache-test-configured-collections';
2560
- const cachedBeforeManager = await resolveAssistantCollectionNameForTesting(
2561
- fakeDb as any,
2562
- dbName,
2563
- 'work-order-dynamics',
2564
- 'snf'
2565
- );
2566
- assert(
2567
- cachedBeforeManager.name === 'work-order-dynamics'
2568
- && cachedBeforeManager.matched === false
2569
- && cachedBeforeManager.candidates.includes('production-sales-orders'),
2570
- 'Expected initial sparse DB cache to miss configured work-order collection while retaining neighboring candidates'
2571
- );
2572
-
2573
- (ResolveIOServer as any).getMongoManager = () => ({
2574
- collections: () => [
2575
- { collectionName: 'work-order-dynamics' },
2576
- { collectionName: 'production-sales-orders' }
2577
- ],
2578
- collection: (_name: string) => ({})
2579
- });
2580
- const resolvedAfterManager = await resolveAssistantCollectionNameForTesting(
2581
- fakeDb as any,
2582
- dbName,
2583
- 'work-order-dynamics',
2584
- 'snf'
2585
- );
2586
- assert(
2587
- resolvedAfterManager.name === 'work-order-dynamics',
2588
- 'Expected configured manager collection to be merged into fresh collection cache'
2589
- );
2590
- assert(resolvedAfterManager.matched === true, 'Expected configured collection to be treated as matched');
2591
- }
2592
- finally {
2593
- (ResolveIOServer as any).getMongoManager = originalGetMongoManager;
2594
- }
2595
- }
2596
-
2597
- async function testResolveAssistantReportBuilderBridgeCollectionUsesBlendHintsWithoutCrossDomainFallback() {
2598
- const originalGetMongoManager = (ResolveIOServer as any).getMongoManager;
2599
- const buildFakeDb = (collections: string[]) => ({
2600
- listCollections: (filter?: Record<string, any>) => ({
2601
- toArray: async () => {
2602
- const name = String(filter?.name || '');
2603
- if (!name) {
2604
- return collections.map(collectionName => ({ name: collectionName }));
2605
- }
2606
- return collections.includes(name) ? [{ name }] : [];
2607
- }
2608
- })
2609
- });
2610
- const blendHints = {
2611
- collectionHints: ['chemical-blends', 'support-tickets'],
2612
- rankedCollections: ['support-tickets', 'chemical-blends'],
2613
- fieldHints: ['Field hints (chemical-blends): blend_name, chemical_recipe_quantity'],
2614
- schemaHints: ['Schema hints (chemical-blends): fields: blend_name, quantity']
2615
- };
2616
- try {
2617
- (ResolveIOServer as any).getMongoManager = () => ({
2618
- collection: (_name: string) => ({})
2619
- });
2620
-
2621
- const resolvedBlend = await resolveAssistantReportBuilderBridgeCollection(
2622
- 'blend-tickets',
2623
- buildFakeDb(['support-tickets', 'chemical-blends']) as any,
2624
- 'resolveio',
2625
- { requestHints: blendHints as any }
2626
- );
2627
- assert(
2628
- resolvedBlend.collection === 'chemical-blends',
2629
- 'Expected blend hints to resolve bridge collection to chemical-blends when available'
2630
- );
2631
- assert(
2632
- resolvedBlend.mode === 'direct-mongo',
2633
- 'Expected blend hint fallback to use direct-mongo mode when RB is unavailable'
2634
- );
2635
-
2636
- let threwMissingBlend = false;
2637
- try {
2638
- await resolveAssistantReportBuilderBridgeCollection(
2639
- 'blend-tickets',
2640
- buildFakeDb(['support-tickets']) as any,
2641
- 'resolveio',
2642
- { requestHints: blendHints as any }
2643
- );
2644
- }
2645
- catch (error: any) {
2646
- threwMissingBlend = String(error?.message || '')
2647
- .includes('No queryable collection could be resolved');
2648
- }
2649
- assert(
2650
- threwMissingBlend,
2651
- 'Expected blend ticket requests to fail when no blend collection exists'
2652
- );
2653
- }
2654
- finally {
2655
- (ResolveIOServer as any).getMongoManager = originalGetMongoManager;
2656
- }
2657
- }
2658
-
2659
- function testBuildAssistantSchemaHintsIncludesFieldsAndLookups() {
2660
- const originalGetMongoManager = (ResolveIOServer as any).getMongoManager;
2661
- try {
2662
- (ResolveIOServer as any).getMongoManager = () => ({
2663
- collection: (name: string) => {
2664
- if (name !== 'work-orders') {
2665
- return {};
2666
- }
2667
- return {
2668
- simplschema: {
2669
- schema: () => ({
2670
- status: { type: String },
2671
- date_created: { type: Date },
2672
- id_customer: { type: String }
2673
- })
2674
- },
2675
- rbSchema: {
2676
- id_customer: {
2677
- lookup_collection: 'customers',
2678
- local_key: 'id_customer',
2679
- lookup_key: '_id'
2680
- }
2681
- }
2682
- };
2683
- }
2684
- });
2685
- const hints = buildAssistantSchemaHints(['work-orders'], {
2686
- maxCollections: 1,
2687
- maxFields: 8,
2688
- maxLookups: 4
2689
- });
2690
- assert(hints.length === 1, 'Expected a single schema hint line');
2691
- assert(hints[0].includes('Schema hints (work-orders):'), 'Expected schema hint collection label');
2692
- assert(hints[0].includes('fields:'), 'Expected schema fields section');
2693
- assert(hints[0].includes('report-builder lookups:'), 'Expected lookup section');
2694
- assert(
2695
- hints[0].includes('id_customer->customers._id'),
2696
- 'Expected lookup mapping details in schema hints'
2697
- );
2698
- }
2699
- finally {
2700
- (ResolveIOServer as any).getMongoManager = originalGetMongoManager;
2701
- }
2702
- }
2703
-
2704
- async function testBuildAssistantToolRequestHintsAndReadDebugBridgeResolution() {
2705
- const request = buildAssistantToolRequest(
2706
- {
2707
- type: 'read',
2708
- payload: {
2709
- collection: 'orders',
2710
- query: { status: 'Open' }
2711
- },
2712
- cleaned: '',
2713
- rawLine: 'REPORT_BUILDER_READ: {"collection":"orders"}'
2714
- } as any,
2715
- {
2716
- id_client: 'client-1',
2717
- mongo: {
2718
- database: 'resolveio',
2719
- databases: ['resolveio'],
2720
- access: 'read'
2721
- }
2722
- } as any,
2723
- {
2724
- collectionHints: ['orders', 'orders'],
2725
- rankedCollections: ['orders', 'report-orders'],
2726
- fieldHints: ['Field hints (orders): status', 'Field hints (orders): status'],
2727
- schemaHints: ['Schema hints (orders): fields: status', 'Schema hints (orders): fields: status']
2728
- }
2729
- );
2730
- assert(!!request.__assistantHints, 'Expected normalized assistant hints to be attached');
2731
- assert(
2732
- Array.isArray(request.__assistantHints.collectionHints)
2733
- && request.__assistantHints.collectionHints.length === 1,
2734
- 'Expected collection hints to be deduplicated'
2735
- );
2736
- assert(
2737
- Array.isArray(request.__assistantHints.schemaHints)
2738
- && request.__assistantHints.schemaHints.length === 1,
2739
- 'Expected schema hints to be deduplicated'
2740
- );
2741
-
2742
- const originalGetMongoManager = (ResolveIOServer as any).getMongoManager;
2743
- const originalGetMongoConnection = (ResolveIOServer as any).getMongoConnection;
2744
- const originalGetServerConfig = (ResolveIOServer as any).getServerConfig;
2745
- const originalUsersCollection = (UserCollection as any).Users;
2746
- try {
2747
- (UserCollection as any).Users = {
2748
- findById: async (_id: string) => ({
2749
- _id: 'user-1',
2750
- username: 'admin',
2751
- roles: {
2752
- super_admin: true,
2753
- groups: [],
2754
- miscs: []
2755
- },
2756
- other: {}
2757
- })
2758
- };
2759
- (ResolveIOServer as any).getServerConfig = () => ({
2760
- DATABASE: 'resolveio'
2761
- });
2762
- (ResolveIOServer as any).getMongoManager = () => ({
2763
- getWatchedDatabases: () => ['resolveio'],
2764
- collection: (_name: string) => ({})
2765
- });
2766
- const fakeDb = {
2767
- listCollections: (filter?: Record<string, any>) => ({
2768
- toArray: async () => {
2769
- const name = String(filter?.name || '');
2770
- if (!name) {
2771
- return [{ name: 'orders' }];
2772
- }
2773
- return name === 'orders' ? [{ name: 'orders' }] : [];
2774
- }
2775
- }),
2776
- collection: (_name: string) => ({
2777
- find: (_query: Record<string, any>, _options: Record<string, any>) => ({
2778
- toArray: async () => [{ _id: 'order-1', name: 'Order Alpha', status: 'Open' }]
2779
- }),
2780
- countDocuments: async (_query: Record<string, any>) => 1
2781
- })
2782
- };
2783
- (ResolveIOServer as any).getMongoConnection = () => ({
2784
- db: (_dbName: string) => fakeDb
2785
- });
2786
-
2787
- const readResult = await executeAiAssistantMongoRead({
2788
- collection: 'orders',
2789
- query: { status: 'Open' },
2790
- options: { limit: 1 },
2791
- permissionView: '/order/list',
2792
- mongo: {
2793
- database: 'resolveio',
2794
- databases: ['resolveio'],
2795
- access: 'read'
2796
- },
2797
- __assistantHints: {
2798
- collectionHints: ['orders'],
2799
- schemaHints: ['Schema hints (orders): fields: status']
2800
- }
2801
- } as any, { id_user: 'user-1' });
2802
- assert(readResult?.debug?.bridge === 'direct-mongo', 'Expected direct-mongo bridge mode in debug payload');
2803
- assert(
2804
- readResult?.debug?.bridgeResolution?.mode === 'direct-mongo',
2805
- 'Expected bridgeResolution mode to be direct-mongo in debug payload'
2806
- );
2807
- assert(
2808
- readResult?.debug?.requestHints?.collectionHints?.[0] === 'orders',
2809
- 'Expected debug payload to include request hint metadata'
2810
- );
2811
- }
2812
- finally {
2813
- (ResolveIOServer as any).getMongoManager = originalGetMongoManager;
2814
- (ResolveIOServer as any).getMongoConnection = originalGetMongoConnection;
2815
- (ResolveIOServer as any).getServerConfig = originalGetServerConfig;
2816
- (UserCollection as any).Users = originalUsersCollection;
2817
- }
2818
- }
2819
-
2820
- async function testExecuteAiAssistantMongoAggregateFallsBackWithinCollectionForMissingActiveFields() {
2821
- const originalGetMongoManager = (ResolveIOServer as any).getMongoManager;
2822
- const originalGetMongoConnection = (ResolveIOServer as any).getMongoConnection;
2823
- const originalGetServerConfig = (ResolveIOServer as any).getServerConfig;
2824
- const originalUsersCollection = (UserCollection as any).Users;
2825
- const aggregateCalls: string[] = [];
2826
- try {
2827
- (UserCollection as any).Users = {
2828
- findById: async (_id: string) => ({
2829
- _id: 'user-1',
2830
- username: 'admin',
2831
- roles: {
2832
- super_admin: true,
2833
- groups: [],
2834
- miscs: []
2835
- },
2836
- other: {}
2837
- })
2838
- };
2839
- (ResolveIOServer as any).getServerConfig = () => ({
2840
- DATABASE: 'resolveio'
2841
- });
2842
- (ResolveIOServer as any).getMongoManager = () => ({
2843
- getWatchedDatabases: () => ['resolveio'],
2844
- collection: (_name: string) => ({})
2845
- });
2846
- const fakeDb = {
2847
- listCollections: (filter?: Record<string, any>) => ({
2848
- toArray: async () => {
2849
- const name = String(filter?.name || '');
2850
- const collections = ['customers', 'support-tickets'];
2851
- if (!name) {
2852
- return collections.map(collectionName => ({ name: collectionName }));
2853
- }
2854
- return collections.includes(name) ? [{ name }] : [];
2855
- }
2856
- }),
2857
- collection: (name: string) => ({
2858
- find: (_query: Record<string, any>) => ({
2859
- toArray: async () => {
2860
- if (name === 'customers') {
2861
- return [{ _id: 'customer-1', name: 'Acme', state: 'Texas' }];
2862
- }
2863
- return [];
2864
- }
2865
- }),
2866
- aggregate: (pipeline: Array<Record<string, any>>) => ({
2867
- toArray: async () => {
2868
- aggregateCalls.push(name);
2869
- if (name !== 'customers') {
2870
- return [{ active_clients: 334 }];
2871
- }
2872
- const matchStage = pipeline.find(stage => stage?.$match && typeof stage.$match === 'object');
2873
- const matchText = JSON.stringify(matchStage?.$match || {});
2874
- const hasActiveFilters = /\"(status|state|active|is_active|isactive|enabled|is_enabled|isenabled)\"/.test(matchText);
2875
- return hasActiveFilters ? [] : [{ active_clients: 476 }];
2876
- }
2877
- }),
2878
- countDocuments: async () => 0
2879
- })
2880
- };
2881
- (ResolveIOServer as any).getMongoConnection = () => ({
2882
- db: (_dbName: string) => fakeDb
2883
- });
2884
-
2885
- const aggregateResult = await executeAiAssistantMongoAggregate({
2886
- collection: 'customers',
2887
- permissionView: '/client/list',
2888
- pipeline: [
2889
- {
2890
- $match: {
2891
- $and: [
2892
- {
2893
- $or: [
2894
- { active: true },
2895
- { is_active: true },
2896
- { isactive: true },
2897
- { enabled: true },
2898
- { is_enabled: true },
2899
- { isenabled: true },
2900
- { status: { $regex: '^active$', $options: 'i' } },
2901
- { state: { $regex: '^active$', $options: 'i' } }
2902
- ]
2903
- }
2904
- ]
2905
- }
2906
- },
2907
- {
2908
- $group: {
2909
- _id: null,
2910
- active_clients: { $sum: 1 }
2911
- }
2912
- },
2913
- {
2914
- $project: {
2915
- _id: 0,
2916
- active_clients: 1
2917
- }
2918
- }
2919
- ],
2920
- options: {
2921
- allowDiskUse: true,
2922
- limit: 1
2923
- },
2924
- mongo: {
2925
- database: 'resolveio',
2926
- databases: ['resolveio'],
2927
- access: 'read'
2928
- }
2929
- } as any, { id_user: 'user-1' });
2930
-
2931
- assert(
2932
- Array.isArray(aggregateResult?.documents) && aggregateResult.documents[0]?.active_clients === 476,
2933
- 'Expected missing active-field aggregate fallback to stay on the requested collection and count all matching records'
2934
- );
2935
- assert(
2936
- aggregateResult?.debug?.fallbacks?.activeStatus?.used === true,
2937
- 'Expected aggregate debug payload to record activeStatus fallback usage'
2938
- );
2939
- assert(
2940
- !aggregateCalls.includes('support-tickets'),
2941
- 'Expected active-status aggregate fallback to avoid cross-collection retry'
2942
- );
2943
- }
2944
- finally {
2945
- (ResolveIOServer as any).getMongoManager = originalGetMongoManager;
2946
- (ResolveIOServer as any).getMongoConnection = originalGetMongoConnection;
2947
- (ResolveIOServer as any).getServerConfig = originalGetServerConfig;
2948
- (UserCollection as any).Users = originalUsersCollection;
2949
- }
2950
- }
2951
-
2952
- async function run() {
2953
- testDirectiveParsing();
2954
- testReportBuilderDirectiveParsing();
2955
- testFlattenForTable();
2956
- testDisplayTableDefaults();
2957
- testSerializeMongoValue();
2958
- testDisplayMarkdownTable();
2959
- testAssistantToolFallbackResponseIncludesUsefulLeadSummary();
2960
- testAssistantToolFallbackResponseExplainsZeroRows();
2961
- testBuildAssistantAnswerQualityFromExecution();
2962
- testAssistantZeroRowDisplayDoesNotAppendSyntheticTable();
2963
- testDisplayMarkdownTableDefaultsToShortDateFormat();
2964
- testDeriveAssistantCommandExecutionStatus();
2965
- testResolveCodexThoughtLevelPolicy();
2966
- testAssistantSystemPromptAllowsWorkspaceDebuggingAndFixes();
2967
- testAssistantGuardrailsAllowInternalEngineeringRequests();
2968
- testBuildAssistantWorkspaceRootCandidates();
2969
- testShouldRunAssistantPlannerPolicy();
2970
- testAssistantAppHeuristicRegistryTermHintsAndDeterministicEnablement();
2971
- testAssistantAppHeuristicRegistryLoadsRelativeFileFromParityServerDir();
2972
- testAssistantCoreHeuristicsStayPlatformScoped();
2973
- testAssistantAppHeuristicsCanOwnDomainCollectionHints();
2974
- testAssistantAppDataIntentsResolveSnfDomainRequests();
2975
- testAssistantRemovedDomainHeuristicIdsRemainAppSpecific();
2976
- testAssistantHeuristicBlendIdsRemainAppSpecific();
2977
- testShouldEnforceAssistantDatedDirectiveSkipsWindowedRankingRequests();
2978
- testShouldEnforceAssistantDatedDirectiveForExplicitTemporalBreakdown();
2979
- testShouldEnforceAssistantDatedDirectiveForPlannerTemporalGroupBy();
2980
- testBuildAssistantDatedPivotDisplayWithYearMonthAndCustomer();
2981
- testBuildAssistantDatedPivotDisplayWithoutDimensionCreatesMetricGrid();
2982
- testBuildAssistantInvoiceCustomerLabelExprIncludesClientFallbacks();
2983
- testResolveAssistantReadDisplayMaxRows();
2984
- testResolveAssistantCollectionOverrideKeepsSpecificOrdersCollection();
2985
- testResolveAssistantCollectionOverrideStillAppliesForUnrelatedCollection();
2986
- testResolveAssistantCollectionOverrideSkipsBorderlineGapForWorkOrderRanking();
2987
- testResolveAssistantCollectionOverrideKeepsWorkOrderCollectionWhenOrdersScoresHigher();
2988
- testResolveCollectionOverrideWithContextRespectsPermissionRouteHints();
2989
- testResolveCollectionOverrideWithContextUsesSpecificPermissionRouteCollection();
2990
- testResolveCollectionOverrideWithContextUsesOrderedCollectionHints();
2991
- testResolveCollectionOverrideWithContextPreservesPrimaryCollectionHint();
2992
- testResolveCollectionOverrideWithContextKeepsRouteMatchedJobsOverChemicalHint();
2993
- testResolveCollectionOverrideWithContextPreservesJobDomainDetailCollections();
2994
- testResolveCollectionOverrideWithContextKeepsCompetitiveRootCollectionHint();
2995
- testShouldPreserveAssistantProbeCollectionForPrimaryHintAndRoute();
2996
- testResolveCollectionOverrideWithContextStillOverridesWithoutRouteHints();
2997
- testResolveAssistantCrossCollectionFallbackCandidatesForOrders();
2998
- testResolveAssistantCrossCollectionFallbackCandidatesForWorkOrderDynamics();
2999
- testResolveAssistantCrossCollectionFallbackCandidatesForUnknownCollection();
3000
- testResolveAssistantCrossCollectionFallbackCandidatesForTimeEntries();
3001
- testResolveAssistantAvailableCrossCollectionFallbacksFromNamesUsesSimilarityRanking();
3002
- testResolveAssistantAvailableCrossCollectionFallbacksFromNamesSkipsCurrentFamilyOnlyMatch();
3003
- testResolveAssistantCrossCollectionFallbackCandidatesForSupportAlias();
3004
- testShouldAcceptAssistantFallbackDocumentsRejectsBlankDimensionRows();
3005
- testShouldAcceptAssistantFallbackDocumentsAcceptsNamedDimensions();
3006
- testShouldAcceptAssistantFallbackDocumentsAcceptsMetricOnlyRows();
3007
- testDisplayMarkdownInvoiceNumbersAndCountsStayNumeric();
3008
- testDisplayMarkdownCurrencyColumnsStillUseCurrencyFormat();
3009
- testDisplayMarkdownKeepsAlphanumericBlendAndChemicalNames();
3010
- testDisplayTableCoalescesStringVariantFields();
3011
- testDisplayTableCoalescesEquivalentCreatedDateFields();
3012
- testDisplayTableKeepsDistinctCreatedDateAndDateFields();
3013
- testStripQueryFieldPathsDeep();
3014
- testResolveReadMultiTermJobRegexFallbackForTesting();
3015
- testNormalizeIdsForTargetField();
3016
- testRewriteEmbeddedMatchObjects();
3017
- testRewriteMatchExpressionsToExpr();
3018
- testRewriteMatchExpressionsToExprCompactNowToken();
3019
- testNormalizeAssistantNowExprPlaceholdersDateArithmeticArgs();
3020
- testNormalizeAssistantNowExprPlaceholdersDateArithmeticUnknownKeyAsStartDate();
3021
- testNormalizeAssistantNowExprPlaceholdersDateArithmeticUnknownKeyAsAmount();
3022
- testNormalizeAssistantMonthlyCalendarWindowPipelineConvertsRollingMonthlyRange();
3023
- testNormalizeAssistantMonthlyCalendarWindowPipelineSkipsDailyGrouping();
3024
- testExpandAggregateDateMatchFallbackExpandsNestedOrClauses();
3025
- testNormalizeAssistantAggregatePipelineRankSortBySingleKey();
3026
- testNormalizeAssistantAggregatePipelineMovesLastNLimitBeforeGroup();
3027
- testNormalizeAssistantAggregatePipelineKeepsTrendGroupingLimitAfterGroup();
3028
- testBuildAssistantToolRequestRestoresMongoSafeDirectiveKeys();
3029
- testRepairAssistantPositionalPathSegmentsInPipeline();
3030
- testRepairAssistantFieldPathReferenceInPipelineUsesGetFieldForDottedPath();
3031
- testRepairAssistantFieldPathReferenceInPipelineSkipsUndottedPath();
3032
- testShouldUseAssistantNavigationFastPath();
3033
- testRankAssistantNavigationRoutes();
3034
- testShouldUseAssistantChangeHistoryFastPath();
3035
- testBuildAssistantChangeHistorySummaryPermissionAware();
3036
- testBuildAssistantChangeHistorySummaryDuration();
3037
- testBuildAssistantChangeHistorySummaryFeatureKeywordScoping();
3038
- testBuildAssistantChangeHistorySummaryLibraryRepoKeepsDatesButRedactsDetails();
3039
- await testResolveAssistantReportBuilderBridgeCollectionModeAndPath();
3040
- await testResolveAssistantReportBuilderBridgeCollectionPreservesConfiguredCollection();
3041
- await testResolveAssistantReportBuilderBridgeCollectionPreservesPrimaryHintCollection();
3042
- await testResolveAssistantCollectionNameMergesConfiguredCollectionsIntoFreshCache();
3043
- await testResolveAssistantReportBuilderBridgeCollectionUsesBlendHintsWithoutCrossDomainFallback();
3044
- testBuildAssistantSchemaHintsIncludesFieldsAndLookups();
3045
- await testBuildAssistantToolRequestHintsAndReadDebugBridgeResolution();
3046
- await testExecuteAiAssistantMongoAggregateFallsBackWithinCollectionForMissingActiveFields();
3047
- console.log('ai assistant utils tests passed');
3048
- }
3049
-
3050
- run()
3051
- .then(() => {
3052
- process.exit(process.exitCode ?? 0);
3053
- })
3054
- .catch((error) => {
3055
- console.error(error);
3056
- process.exit(1);
3057
- });