@resolveio/server-lib 22.3.220 → 22.3.221

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/.nodemon.json +5 -0
  2. package/.vscode/settings.json +21 -0
  3. package/AGENTS.md +195 -0
  4. package/README.md +22 -0
  5. package/build_package.sh +5 -0
  6. package/compileDTS.pl +64 -0
  7. package/docs/ai-assistant-nightly-eval.md +65 -0
  8. package/docs/ai-assistant-preflight-checklist.md +23 -0
  9. package/docs/ai-assistant-report-builder-bridge-playbook.md +115 -0
  10. package/eslint-plugin-custom/index.js +7 -0
  11. package/eslint-plugin-custom/rules/no-filter-zero-index.js +44 -0
  12. package/eslint.config.js +103 -0
  13. package/gulpfile.js +216 -0
  14. package/methodAndPublicationListGenerator.py +375 -0
  15. package/mongodbensurers.js +2 -0
  16. package/mongostop.js +3 -0
  17. package/package.json +1 -1
  18. package/scripts/cleanup-bypassed-callmethod-logs.js +616 -0
  19. package/settings.development.json +25 -0
  20. package/settings.development.redacted.json +25 -0
  21. package/src/.env +12 -0
  22. package/src/ai/assistant-core-heuristics.ts +379 -0
  23. package/src/ai/resolveio-platform-intelligence-memory-corpus.ts +185 -0
  24. package/src/ai/resolveio-platform-intelligence-memory.ts +325 -0
  25. package/{ai/resolveio-platform-intelligence-types.d.ts → src/ai/resolveio-platform-intelligence-types.ts} +20 -15
  26. package/src/ai/resolveio-platform-intelligence.ts +462 -0
  27. package/src/client-server-app.ts +12 -0
  28. package/src/collections/ai-run.collection.ts +117 -0
  29. package/src/collections/ai-terminal-conversation.collection.ts +91 -0
  30. package/src/collections/ai-terminal-issue-report.collection.ts +99 -0
  31. package/src/collections/ai-terminal-message.collection.ts +77 -0
  32. package/src/collections/app-setting.collection.ts +104 -0
  33. package/src/collections/app-status.collection.ts +58 -0
  34. package/src/collections/communication-metric.collection.ts +84 -0
  35. package/src/collections/counter.collection.ts +56 -0
  36. package/src/collections/cron-job-history.collection.ts +94 -0
  37. package/src/collections/cron-job.collection.ts +92 -0
  38. package/src/collections/customer-notification.collection.ts +131 -0
  39. package/src/collections/customer-portal-password.collection.ts +76 -0
  40. package/src/collections/email-history.collection.ts +134 -0
  41. package/src/collections/email-verified.collection.ts +62 -0
  42. package/src/collections/file.collection.ts +74 -0
  43. package/src/collections/flag-update.collection.ts +57 -0
  44. package/src/collections/flag.collection.ts +57 -0
  45. package/src/collections/log-method-latency.collection.ts +77 -0
  46. package/src/collections/log-subscription.collection.ts +80 -0
  47. package/src/collections/log.collection.ts +93 -0
  48. package/src/collections/logged-in-users.collection.ts +67 -0
  49. package/src/collections/monitor-cpu.collection.ts +65 -0
  50. package/src/collections/monitor-function.collection.ts +74 -0
  51. package/src/collections/monitor-memory.collection.ts +77 -0
  52. package/src/collections/monitor-mongo.collection.ts +71 -0
  53. package/src/collections/notification.collection.ts +57 -0
  54. package/src/collections/openai-usage-ledger.collection.ts +131 -0
  55. package/src/collections/report-builder-dashboard-builder.collection.ts +109 -0
  56. package/src/collections/report-builder-library.collection.ts +89 -0
  57. package/src/collections/report-builder-report.collection.ts +184 -0
  58. package/src/collections/user-group.collection.ts +89 -0
  59. package/src/collections/user-guide.collection.ts +57 -0
  60. package/src/collections/user.collection.ts +181 -0
  61. package/src/cron/cron.ts +117 -0
  62. package/src/fixtures/cron-jobs.ts +95 -0
  63. package/src/fixtures/init.ts +35 -0
  64. package/src/http/auth.ts +818 -0
  65. package/src/http/health.ts +7 -0
  66. package/src/http/home.ts +90 -0
  67. package/src/http/slow-query-publication.ts +49 -0
  68. package/src/index.ts +1 -0
  69. package/src/managers/ai-assistant-codex-manager.manager.ts +1131 -0
  70. package/src/managers/ai-run-evidence.manager.ts +264 -0
  71. package/src/managers/communication-metric.manager.ts +82 -0
  72. package/src/managers/cron.manager.ts +333 -0
  73. package/src/managers/customer-notification-content.manager.ts +236 -0
  74. package/src/managers/diagnostic-manager-bootstrap.ts +165 -0
  75. package/src/managers/error-auto-fix.manager.ts +2767 -0
  76. package/src/managers/local-log.manager.ts +113 -0
  77. package/src/managers/method.manager.ts +1857 -0
  78. package/src/managers/mongo.manager.ts +4575 -0
  79. package/src/managers/monitor.manager.ts +507 -0
  80. package/src/managers/openai-usage-ledger.manager.ts +112 -0
  81. package/src/managers/slow-query-verifier.manager.ts +3590 -0
  82. package/src/managers/slow-query.manager.ts +519 -0
  83. package/src/managers/subscription.manager.ts +3128 -0
  84. package/src/managers/websocket.manager.ts +746 -0
  85. package/src/managers/worker-dispatcher.manager.ts +1360 -0
  86. package/src/managers/worker-server.manager.ts +536 -0
  87. package/src/methods/accounts.ts +532 -0
  88. package/src/methods/ai-terminal.ts +29070 -0
  89. package/src/methods/app-settings.ts +114 -0
  90. package/src/methods/aws.ts +649 -0
  91. package/src/methods/collections.ts +641 -0
  92. package/src/methods/counters.ts +69 -0
  93. package/src/methods/cron-jobs.ts +2614 -0
  94. package/src/methods/customer-notifications.ts +458 -0
  95. package/src/methods/diagnostics.ts +616 -0
  96. package/src/methods/flag-updates.ts +7 -0
  97. package/src/methods/flags.ts +7 -0
  98. package/src/methods/logs.ts +657 -0
  99. package/src/methods/mongo-explorer.ts +1880 -0
  100. package/src/methods/monitor.ts +540 -0
  101. package/src/methods/pdf.ts +1236 -0
  102. package/src/methods/publications.ts +129 -0
  103. package/src/methods/report-builder.ts +3300 -0
  104. package/src/methods/support.ts +335 -0
  105. package/src/models/ai-run.model.ts +27 -0
  106. package/src/models/ai-terminal-conversation.model.ts +19 -0
  107. package/src/models/ai-terminal-issue-report.model.ts +21 -0
  108. package/src/models/ai-terminal-message.model.ts +24 -0
  109. package/src/models/app-setting.model.ts +17 -0
  110. package/{models/app-status.model.d.ts → src/models/app-status.model.ts} +3 -2
  111. package/{models/billing-logged-in-users.model.d.ts → src/models/billing-logged-in-users.model.ts} +5 -4
  112. package/src/models/collection-document.model.ts +24 -0
  113. package/src/models/communication-metric.model.ts +23 -0
  114. package/{models/counter.model.d.ts → src/models/counter.model.ts} +4 -3
  115. package/src/models/cron-job-history.model.ts +16 -0
  116. package/src/models/cron-job.model.ts +15 -0
  117. package/src/models/customer-notification.model.ts +28 -0
  118. package/src/models/customer-portal-password.model.ts +12 -0
  119. package/src/models/dialog.model.ts +25 -0
  120. package/{models/email-history.model.js → src/models/email-history.model.ts} +36 -4
  121. package/{models/email-verified.model.d.ts → src/models/email-verified.model.ts} +6 -5
  122. package/{models/file.model.d.ts → src/models/file.model.ts} +8 -7
  123. package/{models/flag-update.model.d.ts → src/models/flag-update.model.ts} +4 -3
  124. package/{models/flag.model.d.ts → src/models/flag.model.ts} +4 -3
  125. package/src/models/log-method-latency.model.ts +11 -0
  126. package/{models/log-subscription.model.d.ts → src/models/log-subscription.model.ts} +11 -9
  127. package/src/models/log.model.ts +19 -0
  128. package/{models/logged-in-users.model.d.ts → src/models/logged-in-users.model.ts} +6 -5
  129. package/{models/method-response.model.d.ts → src/models/method-response.model.ts} +7 -6
  130. package/src/models/method.model.ts +25 -0
  131. package/{models/monitor-cpu.model.d.ts → src/models/monitor-cpu.model.ts} +9 -7
  132. package/src/models/monitor-function.model.ts +16 -0
  133. package/src/models/monitor-memory.model.ts +17 -0
  134. package/src/models/monitor-mongo.model.ts +15 -0
  135. package/{models/notification.model.d.ts → src/models/notification.model.ts} +6 -4
  136. package/src/models/openai-usage-ledger.model.ts +56 -0
  137. package/src/models/pagination.model.ts +35 -0
  138. package/src/models/permission.model.ts +14 -0
  139. package/src/models/report-builder-dashboard-builder.model.ts +29 -0
  140. package/src/models/report-builder-library.model.ts +20 -0
  141. package/src/models/report-builder-report.model.ts +136 -0
  142. package/src/models/report-builder.model.ts +68 -0
  143. package/src/models/select-data-label.model.ts +9 -0
  144. package/src/models/server-message.model.ts +31 -0
  145. package/src/models/slow-query-report.model.ts +23 -0
  146. package/src/models/subscription.model.ts +73 -0
  147. package/src/models/support-ticket.model.ts +104 -0
  148. package/src/models/user-group.model.ts +24 -0
  149. package/{models/user-guide.model.d.ts → src/models/user-guide.model.ts} +5 -4
  150. package/src/models/user.model.ts +96 -0
  151. package/src/private/images/ResolveIO.png +0 -0
  152. package/src/publications/ai-terminal.ts +73 -0
  153. package/src/publications/app-settings.ts +25 -0
  154. package/src/publications/app-status.ts +13 -0
  155. package/src/publications/cron-jobs.ts +40 -0
  156. package/src/publications/customer-notifications.ts +101 -0
  157. package/src/publications/files.ts +33 -0
  158. package/src/publications/flags-update.ts +19 -0
  159. package/src/publications/flags.ts +19 -0
  160. package/src/publications/logs.ts +163 -0
  161. package/src/publications/notifications.ts +13 -0
  162. package/src/publications/report-builder-dashboard-builders.ts +39 -0
  163. package/src/publications/report-builder-libraries.ts +41 -0
  164. package/src/publications/report-builder-reports.ts +47 -0
  165. package/src/publications/super-admin.ts +13 -0
  166. package/src/publications/user-groups.ts +12 -0
  167. package/src/publications/user-guides.ts +12 -0
  168. package/src/resolveio-server-app.ts +617 -0
  169. package/src/server-app.ts +3354 -0
  170. package/src/services/codex-client.ts +1231 -0
  171. package/src/services/openai-client.ts +265 -0
  172. package/src/types/error-report.ts +26 -0
  173. package/src/types/js-tiktoken.d.ts +11 -0
  174. package/src/types/slow-query-report.ts +28 -0
  175. package/src/util/ai-qa-policy.ts +925 -0
  176. package/src/util/ai-run-evidence-adapters.ts +8347 -0
  177. package/src/util/ai-run-evidence-dashboard.ts +323 -0
  178. package/src/util/ai-run-evidence-eval.ts +1057 -0
  179. package/src/util/ai-run-evidence.ts +1430 -0
  180. package/src/util/ai-runner-artifacts.ts +586 -0
  181. package/src/util/ai-runner-manager-autopilot.ts +961 -0
  182. package/src/util/ai-runner-manager-policy.ts +5011 -0
  183. package/src/util/ai-runner-qa-auth.ts +838 -0
  184. package/src/util/ai-runner-qa-tools.ts +3536 -0
  185. package/src/util/aicoder-runner-v6.ts +3121 -0
  186. package/src/util/common.ts +649 -0
  187. package/src/util/customer-portal-password.ts +183 -0
  188. package/src/util/error-reporter.ts +332 -0
  189. package/src/util/error-tracking.ts +79 -0
  190. package/src/util/openai-usage-cost.ts +114 -0
  191. package/src/util/report-builder-unwinds.ts +180 -0
  192. package/src/util/runner-process-janitor.ts +219 -0
  193. package/src/util/schema-report-builder.ts +448 -0
  194. package/src/util/slow-query-reporter.ts +216 -0
  195. package/src/util/subscription-dependency-context.ts +1096 -0
  196. package/src/util/support-runner-v5.ts +10040 -0
  197. package/src/util/tokenizer.ts +38 -0
  198. package/src/workers/codex-runner.worker.ts +142 -0
  199. package/start_server.sh +5 -0
  200. package/tests/ai-assistant-corpus-build.ts +484 -0
  201. package/tests/ai-assistant-corpus-replay-e2e.ts +774 -0
  202. package/tests/ai-assistant-data-parity-e2e.ts +1989 -0
  203. package/tests/ai-assistant-eval-triage.ts +831 -0
  204. package/tests/ai-assistant-openai-e2e.ts +1061 -0
  205. package/tests/ai-assistant-openai-git-e2e.ts +155 -0
  206. package/tests/ai-assistant-preflight-matrix.ts +215 -0
  207. package/tests/ai-assistant-routing-eval.test.ts +585 -0
  208. package/tests/ai-assistant-snf-live-eval.ts +975 -0
  209. package/tests/ai-assistant-utils.test.ts +4834 -0
  210. package/tests/ai-manager-autopilot-snapshot.test.ts +193 -0
  211. package/tests/ai-manager-recovery-checkpoint.test.ts +1383 -0
  212. package/tests/ai-run-eval.test.ts +132 -0
  213. package/tests/ai-run-evidence.test.ts +3773 -0
  214. package/tests/ai-runner-contract.test.ts +515 -0
  215. package/tests/aicoder-runner-v6.test.ts +822 -0
  216. package/tests/error-reporter.test.ts +145 -0
  217. package/tests/method-publication-generator.test.ts +46 -0
  218. package/tests/report-builder-linking.test.ts +79 -0
  219. package/tests/resolveio-platform-intelligence.test.ts +352 -0
  220. package/tests/server-app-cron-owner.test.ts +127 -0
  221. package/tests/subscription-connect-race.test.ts +158 -0
  222. package/tests/subscription-dependency-context.test.ts +324 -0
  223. package/tests/subscription-manager-collection-tracking.test.ts +86 -0
  224. package/tests/subscription-manager-invalidation.test.ts +86 -0
  225. package/tests/support-runner-v5.test.ts +3201 -0
  226. package/tsconfig.json +34 -0
  227. package/ai/assistant-core-heuristics.d.ts +0 -11
  228. package/ai/assistant-core-heuristics.js +0 -356
  229. package/ai/assistant-core-heuristics.js.map +0 -1
  230. package/ai/resolveio-platform-intelligence-memory-corpus.d.ts +0 -3
  231. package/ai/resolveio-platform-intelligence-memory-corpus.js +0 -214
  232. package/ai/resolveio-platform-intelligence-memory-corpus.js.map +0 -1
  233. package/ai/resolveio-platform-intelligence-memory.d.ts +0 -20
  234. package/ai/resolveio-platform-intelligence-memory.js +0 -341
  235. package/ai/resolveio-platform-intelligence-memory.js.map +0 -1
  236. package/ai/resolveio-platform-intelligence-types.js +0 -4
  237. package/ai/resolveio-platform-intelligence-types.js.map +0 -1
  238. package/ai/resolveio-platform-intelligence.d.ts +0 -6
  239. package/ai/resolveio-platform-intelligence.js +0 -463
  240. package/ai/resolveio-platform-intelligence.js.map +0 -1
  241. package/client-server-app.d.ts +0 -1
  242. package/client-server-app.js +0 -68
  243. package/client-server-app.js.map +0 -1
  244. package/collections/ai-run.collection.d.ts +0 -3
  245. package/collections/ai-run.collection.js +0 -170
  246. package/collections/ai-run.collection.js.map +0 -1
  247. package/collections/ai-terminal-conversation.collection.d.ts +0 -2
  248. package/collections/ai-terminal-conversation.collection.js +0 -140
  249. package/collections/ai-terminal-conversation.collection.js.map +0 -1
  250. package/collections/ai-terminal-issue-report.collection.d.ts +0 -2
  251. package/collections/ai-terminal-issue-report.collection.js +0 -148
  252. package/collections/ai-terminal-issue-report.collection.js.map +0 -1
  253. package/collections/ai-terminal-message.collection.d.ts +0 -2
  254. package/collections/ai-terminal-message.collection.js +0 -121
  255. package/collections/ai-terminal-message.collection.js.map +0 -1
  256. package/collections/app-setting.collection.d.ts +0 -3
  257. package/collections/app-setting.collection.js +0 -103
  258. package/collections/app-setting.collection.js.map +0 -1
  259. package/collections/app-status.collection.d.ts +0 -3
  260. package/collections/app-status.collection.js +0 -57
  261. package/collections/app-status.collection.js.map +0 -1
  262. package/collections/communication-metric.collection.d.ts +0 -2
  263. package/collections/communication-metric.collection.js +0 -133
  264. package/collections/communication-metric.collection.js.map +0 -1
  265. package/collections/counter.collection.d.ts +0 -3
  266. package/collections/counter.collection.js +0 -56
  267. package/collections/counter.collection.js.map +0 -1
  268. package/collections/cron-job-history.collection.d.ts +0 -3
  269. package/collections/cron-job-history.collection.js +0 -137
  270. package/collections/cron-job-history.collection.js.map +0 -1
  271. package/collections/cron-job.collection.d.ts +0 -3
  272. package/collections/cron-job.collection.js +0 -92
  273. package/collections/cron-job.collection.js.map +0 -1
  274. package/collections/customer-notification.collection.d.ts +0 -3
  275. package/collections/customer-notification.collection.js +0 -130
  276. package/collections/customer-notification.collection.js.map +0 -1
  277. package/collections/customer-portal-password.collection.d.ts +0 -3
  278. package/collections/customer-portal-password.collection.js +0 -75
  279. package/collections/customer-portal-password.collection.js.map +0 -1
  280. package/collections/email-history.collection.d.ts +0 -3
  281. package/collections/email-history.collection.js +0 -134
  282. package/collections/email-history.collection.js.map +0 -1
  283. package/collections/email-verified.collection.d.ts +0 -3
  284. package/collections/email-verified.collection.js +0 -62
  285. package/collections/email-verified.collection.js.map +0 -1
  286. package/collections/file.collection.d.ts +0 -3
  287. package/collections/file.collection.js +0 -74
  288. package/collections/file.collection.js.map +0 -1
  289. package/collections/flag-update.collection.d.ts +0 -3
  290. package/collections/flag-update.collection.js +0 -57
  291. package/collections/flag-update.collection.js.map +0 -1
  292. package/collections/flag.collection.d.ts +0 -3
  293. package/collections/flag.collection.js +0 -57
  294. package/collections/flag.collection.js.map +0 -1
  295. package/collections/log-method-latency.collection.d.ts +0 -3
  296. package/collections/log-method-latency.collection.js +0 -77
  297. package/collections/log-method-latency.collection.js.map +0 -1
  298. package/collections/log-subscription.collection.d.ts +0 -3
  299. package/collections/log-subscription.collection.js +0 -80
  300. package/collections/log-subscription.collection.js.map +0 -1
  301. package/collections/log.collection.d.ts +0 -3
  302. package/collections/log.collection.js +0 -93
  303. package/collections/log.collection.js.map +0 -1
  304. package/collections/logged-in-users.collection.d.ts +0 -3
  305. package/collections/logged-in-users.collection.js +0 -67
  306. package/collections/logged-in-users.collection.js.map +0 -1
  307. package/collections/monitor-cpu.collection.d.ts +0 -3
  308. package/collections/monitor-cpu.collection.js +0 -65
  309. package/collections/monitor-cpu.collection.js.map +0 -1
  310. package/collections/monitor-function.collection.d.ts +0 -3
  311. package/collections/monitor-function.collection.js +0 -74
  312. package/collections/monitor-function.collection.js.map +0 -1
  313. package/collections/monitor-memory.collection.d.ts +0 -3
  314. package/collections/monitor-memory.collection.js +0 -77
  315. package/collections/monitor-memory.collection.js.map +0 -1
  316. package/collections/monitor-mongo.collection.d.ts +0 -3
  317. package/collections/monitor-mongo.collection.js +0 -71
  318. package/collections/monitor-mongo.collection.js.map +0 -1
  319. package/collections/notification.collection.d.ts +0 -3
  320. package/collections/notification.collection.js +0 -57
  321. package/collections/notification.collection.js.map +0 -1
  322. package/collections/openai-usage-ledger.collection.d.ts +0 -2
  323. package/collections/openai-usage-ledger.collection.js +0 -188
  324. package/collections/openai-usage-ledger.collection.js.map +0 -1
  325. package/collections/report-builder-dashboard-builder.collection.d.ts +0 -3
  326. package/collections/report-builder-dashboard-builder.collection.js +0 -109
  327. package/collections/report-builder-dashboard-builder.collection.js.map +0 -1
  328. package/collections/report-builder-library.collection.d.ts +0 -3
  329. package/collections/report-builder-library.collection.js +0 -87
  330. package/collections/report-builder-library.collection.js.map +0 -1
  331. package/collections/report-builder-report.collection.d.ts +0 -4
  332. package/collections/report-builder-report.collection.js +0 -184
  333. package/collections/report-builder-report.collection.js.map +0 -1
  334. package/collections/user-group.collection.d.ts +0 -4
  335. package/collections/user-group.collection.js +0 -89
  336. package/collections/user-group.collection.js.map +0 -1
  337. package/collections/user-guide.collection.d.ts +0 -3
  338. package/collections/user-guide.collection.js +0 -57
  339. package/collections/user-guide.collection.js.map +0 -1
  340. package/collections/user.collection.d.ts +0 -4
  341. package/collections/user.collection.js +0 -180
  342. package/collections/user.collection.js.map +0 -1
  343. package/cron/cron.d.ts +0 -14
  344. package/cron/cron.js +0 -216
  345. package/cron/cron.js.map +0 -1
  346. package/fixtures/cron-jobs.d.ts +0 -1
  347. package/fixtures/cron-jobs.js +0 -150
  348. package/fixtures/cron-jobs.js.map +0 -1
  349. package/fixtures/init.d.ts +0 -1
  350. package/fixtures/init.js +0 -91
  351. package/fixtures/init.js.map +0 -1
  352. package/http/auth.d.ts +0 -2
  353. package/http/auth.js +0 -951
  354. package/http/auth.js.map +0 -1
  355. package/http/health.d.ts +0 -1
  356. package/http/health.js +0 -11
  357. package/http/health.js.map +0 -1
  358. package/http/home.d.ts +0 -1
  359. package/http/home.js +0 -134
  360. package/http/home.js.map +0 -1
  361. package/http/slow-query-publication.d.ts +0 -2
  362. package/http/slow-query-publication.js +0 -99
  363. package/http/slow-query-publication.js.map +0 -1
  364. package/index.d.ts +0 -1
  365. package/index.js +0 -19
  366. package/index.js.map +0 -1
  367. package/managers/ai-assistant-codex-manager.manager.d.ts +0 -67
  368. package/managers/ai-assistant-codex-manager.manager.js +0 -1113
  369. package/managers/ai-assistant-codex-manager.manager.js.map +0 -1
  370. package/managers/ai-run-evidence.manager.d.ts +0 -36
  371. package/managers/ai-run-evidence.manager.js +0 -377
  372. package/managers/ai-run-evidence.manager.js.map +0 -1
  373. package/managers/communication-metric.manager.d.ts +0 -16
  374. package/managers/communication-metric.manager.js +0 -134
  375. package/managers/communication-metric.manager.js.map +0 -1
  376. package/managers/cron.manager.d.ts +0 -20
  377. package/managers/cron.manager.js +0 -534
  378. package/managers/cron.manager.js.map +0 -1
  379. package/managers/customer-notification-content.manager.d.ts +0 -55
  380. package/managers/customer-notification-content.manager.js +0 -158
  381. package/managers/customer-notification-content.manager.js.map +0 -1
  382. package/managers/diagnostic-manager-bootstrap.d.ts +0 -9
  383. package/managers/diagnostic-manager-bootstrap.js +0 -260
  384. package/managers/diagnostic-manager-bootstrap.js.map +0 -1
  385. package/managers/error-auto-fix.manager.d.ts +0 -149
  386. package/managers/error-auto-fix.manager.js +0 -3064
  387. package/managers/error-auto-fix.manager.js.map +0 -1
  388. package/managers/local-log.manager.d.ts +0 -18
  389. package/managers/local-log.manager.js +0 -88
  390. package/managers/local-log.manager.js.map +0 -1
  391. package/managers/method.manager.d.ts +0 -84
  392. package/managers/method.manager.js +0 -1964
  393. package/managers/method.manager.js.map +0 -1
  394. package/managers/mongo.manager.d.ts +0 -224
  395. package/managers/mongo.manager.js +0 -5000
  396. package/managers/mongo.manager.js.map +0 -1
  397. package/managers/monitor.manager.d.ts +0 -70
  398. package/managers/monitor.manager.js +0 -550
  399. package/managers/monitor.manager.js.map +0 -1
  400. package/managers/openai-usage-ledger.manager.d.ts +0 -30
  401. package/managers/openai-usage-ledger.manager.js +0 -142
  402. package/managers/openai-usage-ledger.manager.js.map +0 -1
  403. package/managers/slow-query-verifier.manager.d.ts +0 -144
  404. package/managers/slow-query-verifier.manager.js +0 -3857
  405. package/managers/slow-query-verifier.manager.js.map +0 -1
  406. package/managers/slow-query.manager.d.ts +0 -28
  407. package/managers/slow-query.manager.js +0 -468
  408. package/managers/slow-query.manager.js.map +0 -1
  409. package/managers/subscription.manager.d.ts +0 -169
  410. package/managers/subscription.manager.js +0 -3434
  411. package/managers/subscription.manager.js.map +0 -1
  412. package/managers/websocket.manager.d.ts +0 -73
  413. package/managers/websocket.manager.js +0 -673
  414. package/managers/websocket.manager.js.map +0 -1
  415. package/managers/worker-dispatcher.manager.d.ts +0 -120
  416. package/managers/worker-dispatcher.manager.js +0 -1266
  417. package/managers/worker-dispatcher.manager.js.map +0 -1
  418. package/managers/worker-server.manager.d.ts +0 -35
  419. package/managers/worker-server.manager.js +0 -582
  420. package/managers/worker-server.manager.js.map +0 -1
  421. package/methods/accounts.d.ts +0 -2
  422. package/methods/accounts.js +0 -624
  423. package/methods/accounts.js.map +0 -1
  424. package/methods/ai-terminal.d.ts +0 -458
  425. package/methods/ai-terminal.js +0 -27991
  426. package/methods/ai-terminal.js.map +0 -1
  427. package/methods/app-settings.d.ts +0 -2
  428. package/methods/app-settings.js +0 -169
  429. package/methods/app-settings.js.map +0 -1
  430. package/methods/aws.d.ts +0 -2
  431. package/methods/aws.js +0 -877
  432. package/methods/aws.js.map +0 -1
  433. package/methods/collections.d.ts +0 -2
  434. package/methods/collections.js +0 -719
  435. package/methods/collections.js.map +0 -1
  436. package/methods/counters.d.ts +0 -2
  437. package/methods/counters.js +0 -113
  438. package/methods/counters.js.map +0 -1
  439. package/methods/cron-jobs.d.ts +0 -2
  440. package/methods/cron-jobs.js +0 -2475
  441. package/methods/cron-jobs.js.map +0 -1
  442. package/methods/customer-notifications.d.ts +0 -2
  443. package/methods/customer-notifications.js +0 -528
  444. package/methods/customer-notifications.js.map +0 -1
  445. package/methods/diagnostics.d.ts +0 -2
  446. package/methods/diagnostics.js +0 -703
  447. package/methods/diagnostics.js.map +0 -1
  448. package/methods/flag-updates.d.ts +0 -2
  449. package/methods/flag-updates.js +0 -8
  450. package/methods/flag-updates.js.map +0 -1
  451. package/methods/flags.d.ts +0 -2
  452. package/methods/flags.js +0 -8
  453. package/methods/flags.js.map +0 -1
  454. package/methods/logs.d.ts +0 -2
  455. package/methods/logs.js +0 -751
  456. package/methods/logs.js.map +0 -1
  457. package/methods/mongo-explorer.d.ts +0 -2
  458. package/methods/mongo-explorer.js +0 -1808
  459. package/methods/mongo-explorer.js.map +0 -1
  460. package/methods/monitor.d.ts +0 -2
  461. package/methods/monitor.js +0 -543
  462. package/methods/monitor.js.map +0 -1
  463. package/methods/pdf.d.ts +0 -2
  464. package/methods/pdf.js +0 -1216
  465. package/methods/pdf.js.map +0 -1
  466. package/methods/publications.d.ts +0 -1
  467. package/methods/publications.js +0 -183
  468. package/methods/publications.js.map +0 -1
  469. package/methods/report-builder.d.ts +0 -2
  470. package/methods/report-builder.js +0 -3094
  471. package/methods/report-builder.js.map +0 -1
  472. package/methods/support.d.ts +0 -2
  473. package/methods/support.js +0 -430
  474. package/methods/support.js.map +0 -1
  475. package/models/ai-run.model.d.ts +0 -19
  476. package/models/ai-run.model.js +0 -4
  477. package/models/ai-run.model.js.map +0 -1
  478. package/models/ai-terminal-conversation.model.d.ts +0 -17
  479. package/models/ai-terminal-conversation.model.js +0 -4
  480. package/models/ai-terminal-conversation.model.js.map +0 -1
  481. package/models/ai-terminal-issue-report.model.d.ts +0 -19
  482. package/models/ai-terminal-issue-report.model.js +0 -4
  483. package/models/ai-terminal-issue-report.model.js.map +0 -1
  484. package/models/ai-terminal-message.model.d.ts +0 -22
  485. package/models/ai-terminal-message.model.js +0 -4
  486. package/models/ai-terminal-message.model.js.map +0 -1
  487. package/models/app-setting.model.d.ts +0 -16
  488. package/models/app-setting.model.js +0 -4
  489. package/models/app-setting.model.js.map +0 -1
  490. package/models/app-status.model.js +0 -4
  491. package/models/app-status.model.js.map +0 -1
  492. package/models/billing-logged-in-users.model.js +0 -4
  493. package/models/billing-logged-in-users.model.js.map +0 -1
  494. package/models/collection-document.model.d.ts +0 -21
  495. package/models/collection-document.model.js +0 -4
  496. package/models/collection-document.model.js.map +0 -1
  497. package/models/communication-metric.model.d.ts +0 -20
  498. package/models/communication-metric.model.js +0 -4
  499. package/models/communication-metric.model.js.map +0 -1
  500. package/models/counter.model.js +0 -4
  501. package/models/counter.model.js.map +0 -1
  502. package/models/cron-job-history.model.d.ts +0 -15
  503. package/models/cron-job-history.model.js +0 -4
  504. package/models/cron-job-history.model.js.map +0 -1
  505. package/models/cron-job.model.d.ts +0 -14
  506. package/models/cron-job.model.js +0 -4
  507. package/models/cron-job.model.js.map +0 -1
  508. package/models/customer-notification.model.d.ts +0 -26
  509. package/models/customer-notification.model.js +0 -4
  510. package/models/customer-notification.model.js.map +0 -1
  511. package/models/customer-portal-password.model.d.ts +0 -11
  512. package/models/customer-portal-password.model.js +0 -4
  513. package/models/customer-portal-password.model.js.map +0 -1
  514. package/models/dialog.model.d.ts +0 -23
  515. package/models/dialog.model.js +0 -4
  516. package/models/dialog.model.js.map +0 -1
  517. package/models/email-history.model.d.ts +0 -32
  518. package/models/email-history.model.js.map +0 -1
  519. package/models/email-verified.model.js +0 -4
  520. package/models/email-verified.model.js.map +0 -1
  521. package/models/file.model.js +0 -4
  522. package/models/file.model.js.map +0 -1
  523. package/models/flag-update.model.js +0 -4
  524. package/models/flag-update.model.js.map +0 -1
  525. package/models/flag.model.js +0 -4
  526. package/models/flag.model.js.map +0 -1
  527. package/models/log-method-latency.model.d.ts +0 -10
  528. package/models/log-method-latency.model.js +0 -4
  529. package/models/log-method-latency.model.js.map +0 -1
  530. package/models/log-subscription.model.js +0 -4
  531. package/models/log-subscription.model.js.map +0 -1
  532. package/models/log.model.d.ts +0 -17
  533. package/models/log.model.js +0 -4
  534. package/models/log.model.js.map +0 -1
  535. package/models/logged-in-users.model.js +0 -4
  536. package/models/logged-in-users.model.js.map +0 -1
  537. package/models/method-response.model.js +0 -4
  538. package/models/method-response.model.js.map +0 -1
  539. package/models/method.model.d.ts +0 -26
  540. package/models/method.model.js +0 -4
  541. package/models/method.model.js.map +0 -1
  542. package/models/monitor-cpu.model.js +0 -4
  543. package/models/monitor-cpu.model.js.map +0 -1
  544. package/models/monitor-function.model.d.ts +0 -14
  545. package/models/monitor-function.model.js +0 -4
  546. package/models/monitor-function.model.js.map +0 -1
  547. package/models/monitor-memory.model.d.ts +0 -15
  548. package/models/monitor-memory.model.js +0 -4
  549. package/models/monitor-memory.model.js.map +0 -1
  550. package/models/monitor-mongo.model.d.ts +0 -13
  551. package/models/monitor-mongo.model.js +0 -4
  552. package/models/monitor-mongo.model.js.map +0 -1
  553. package/models/notification.model.js +0 -4
  554. package/models/notification.model.js.map +0 -1
  555. package/models/openai-usage-ledger.model.d.ts +0 -30
  556. package/models/openai-usage-ledger.model.js +0 -4
  557. package/models/openai-usage-ledger.model.js.map +0 -1
  558. package/models/pagination.model.d.ts +0 -11
  559. package/models/pagination.model.js +0 -28
  560. package/models/pagination.model.js.map +0 -1
  561. package/models/permission.model.d.ts +0 -12
  562. package/models/permission.model.js +0 -4
  563. package/models/permission.model.js.map +0 -1
  564. package/models/report-builder-dashboard-builder.model.d.ts +0 -25
  565. package/models/report-builder-dashboard-builder.model.js +0 -4
  566. package/models/report-builder-dashboard-builder.model.js.map +0 -1
  567. package/models/report-builder-library.model.d.ts +0 -17
  568. package/models/report-builder-library.model.js +0 -4
  569. package/models/report-builder-library.model.js.map +0 -1
  570. package/models/report-builder-report.model.d.ts +0 -121
  571. package/models/report-builder-report.model.js +0 -4
  572. package/models/report-builder-report.model.js.map +0 -1
  573. package/models/report-builder.model.d.ts +0 -61
  574. package/models/report-builder.model.js +0 -4
  575. package/models/report-builder.model.js.map +0 -1
  576. package/models/select-data-label.model.d.ts +0 -9
  577. package/models/select-data-label.model.js +0 -4
  578. package/models/select-data-label.model.js.map +0 -1
  579. package/models/server-message.model.d.ts +0 -32
  580. package/models/server-message.model.js +0 -4
  581. package/models/server-message.model.js.map +0 -1
  582. package/models/slow-query-report.model.d.ts +0 -23
  583. package/models/slow-query-report.model.js +0 -4
  584. package/models/slow-query-report.model.js.map +0 -1
  585. package/models/subscription.model.d.ts +0 -31
  586. package/models/subscription.model.js +0 -4
  587. package/models/subscription.model.js.map +0 -1
  588. package/models/support-ticket.model.d.ts +0 -87
  589. package/models/support-ticket.model.js +0 -4
  590. package/models/support-ticket.model.js.map +0 -1
  591. package/models/user-group.model.d.ts +0 -20
  592. package/models/user-group.model.js +0 -4
  593. package/models/user-group.model.js.map +0 -1
  594. package/models/user-guide.model.js +0 -4
  595. package/models/user-guide.model.js.map +0 -1
  596. package/models/user.model.d.ts +0 -84
  597. package/models/user.model.js +0 -4
  598. package/models/user.model.js.map +0 -1
  599. package/private/images/ResolveIO.png +0 -0
  600. package/public_api.js +0 -127
  601. package/public_api.js.map +0 -1
  602. package/publications/ai-terminal.d.ts +0 -1
  603. package/publications/ai-terminal.js +0 -122
  604. package/publications/ai-terminal.js.map +0 -1
  605. package/publications/app-settings.d.ts +0 -2
  606. package/publications/app-settings.js +0 -28
  607. package/publications/app-settings.js.map +0 -1
  608. package/publications/app-status.d.ts +0 -2
  609. package/publications/app-status.js +0 -16
  610. package/publications/app-status.js.map +0 -1
  611. package/publications/cron-jobs.d.ts +0 -2
  612. package/publications/cron-jobs.js +0 -88
  613. package/publications/cron-jobs.js.map +0 -1
  614. package/publications/customer-notifications.d.ts +0 -2
  615. package/publications/customer-notifications.js +0 -161
  616. package/publications/customer-notifications.js.map +0 -1
  617. package/publications/files.d.ts +0 -2
  618. package/publications/files.js +0 -36
  619. package/publications/files.js.map +0 -1
  620. package/publications/flags-update.d.ts +0 -2
  621. package/publications/flags-update.js +0 -22
  622. package/publications/flags-update.js.map +0 -1
  623. package/publications/flags.d.ts +0 -2
  624. package/publications/flags.js +0 -22
  625. package/publications/flags.js.map +0 -1
  626. package/publications/logs.d.ts +0 -2
  627. package/publications/logs.js +0 -164
  628. package/publications/logs.js.map +0 -1
  629. package/publications/notifications.d.ts +0 -2
  630. package/publications/notifications.js +0 -16
  631. package/publications/notifications.js.map +0 -1
  632. package/publications/report-builder-dashboard-builders.d.ts +0 -2
  633. package/publications/report-builder-dashboard-builders.js +0 -42
  634. package/publications/report-builder-dashboard-builders.js.map +0 -1
  635. package/publications/report-builder-libraries.d.ts +0 -2
  636. package/publications/report-builder-libraries.js +0 -90
  637. package/publications/report-builder-libraries.js.map +0 -1
  638. package/publications/report-builder-reports.d.ts +0 -2
  639. package/publications/report-builder-reports.js +0 -50
  640. package/publications/report-builder-reports.js.map +0 -1
  641. package/publications/super-admin.d.ts +0 -2
  642. package/publications/super-admin.js +0 -16
  643. package/publications/super-admin.js.map +0 -1
  644. package/publications/user-groups.d.ts +0 -1
  645. package/publications/user-groups.js +0 -16
  646. package/publications/user-groups.js.map +0 -1
  647. package/publications/user-guides.d.ts +0 -1
  648. package/publications/user-guides.js +0 -16
  649. package/publications/user-guides.js.map +0 -1
  650. package/resolveio-server-app.d.ts +0 -70
  651. package/resolveio-server-app.js +0 -801
  652. package/resolveio-server-app.js.map +0 -1
  653. package/server-app.d.ts +0 -228
  654. package/server-app.js +0 -3566
  655. package/server-app.js.map +0 -1
  656. package/services/codex-client.d.ts +0 -128
  657. package/services/codex-client.js +0 -1629
  658. package/services/codex-client.js.map +0 -1
  659. package/services/openai-client.d.ts +0 -46
  660. package/services/openai-client.js +0 -318
  661. package/services/openai-client.js.map +0 -1
  662. package/types/error-report.d.ts +0 -25
  663. package/types/error-report.js +0 -4
  664. package/types/error-report.js.map +0 -1
  665. package/types/slow-query-report.d.ts +0 -27
  666. package/types/slow-query-report.js +0 -6
  667. package/types/slow-query-report.js.map +0 -1
  668. package/util/ai-qa-policy.d.ts +0 -124
  669. package/util/ai-qa-policy.js +0 -736
  670. package/util/ai-qa-policy.js.map +0 -1
  671. package/util/ai-run-evidence-adapters.d.ts +0 -109
  672. package/util/ai-run-evidence-adapters.js +0 -7234
  673. package/util/ai-run-evidence-adapters.js.map +0 -1
  674. package/util/ai-run-evidence-dashboard.d.ts +0 -88
  675. package/util/ai-run-evidence-dashboard.js +0 -343
  676. package/util/ai-run-evidence-dashboard.js.map +0 -1
  677. package/util/ai-run-evidence-eval.d.ts +0 -86
  678. package/util/ai-run-evidence-eval.js +0 -1018
  679. package/util/ai-run-evidence-eval.js.map +0 -1
  680. package/util/ai-run-evidence.d.ts +0 -244
  681. package/util/ai-run-evidence.js +0 -1096
  682. package/util/ai-run-evidence.js.map +0 -1
  683. package/util/ai-runner-artifacts.d.ts +0 -82
  684. package/util/ai-runner-artifacts.js +0 -713
  685. package/util/ai-runner-artifacts.js.map +0 -1
  686. package/util/ai-runner-manager-autopilot.d.ts +0 -210
  687. package/util/ai-runner-manager-autopilot.js +0 -642
  688. package/util/ai-runner-manager-autopilot.js.map +0 -1
  689. package/util/ai-runner-manager-policy.d.ts +0 -807
  690. package/util/ai-runner-manager-policy.js +0 -3501
  691. package/util/ai-runner-manager-policy.js.map +0 -1
  692. package/util/ai-runner-qa-auth.d.ts +0 -5
  693. package/util/ai-runner-qa-auth.js +0 -839
  694. package/util/ai-runner-qa-auth.js.map +0 -1
  695. package/util/ai-runner-qa-tools.d.ts +0 -26
  696. package/util/ai-runner-qa-tools.js +0 -3520
  697. package/util/ai-runner-qa-tools.js.map +0 -1
  698. package/util/aicoder-runner-v6.d.ts +0 -426
  699. package/util/aicoder-runner-v6.js +0 -2464
  700. package/util/aicoder-runner-v6.js.map +0 -1
  701. package/util/common.d.ts +0 -31
  702. package/util/common.js +0 -683
  703. package/util/common.js.map +0 -1
  704. package/util/customer-portal-password.d.ts +0 -13
  705. package/util/customer-portal-password.js +0 -209
  706. package/util/customer-portal-password.js.map +0 -1
  707. package/util/error-reporter.d.ts +0 -52
  708. package/util/error-reporter.js +0 -326
  709. package/util/error-reporter.js.map +0 -1
  710. package/util/error-tracking.d.ts +0 -13
  711. package/util/error-tracking.js +0 -120
  712. package/util/error-tracking.js.map +0 -1
  713. package/util/openai-usage-cost.d.ts +0 -6
  714. package/util/openai-usage-cost.js +0 -103
  715. package/util/openai-usage-cost.js.map +0 -1
  716. package/util/report-builder-unwinds.d.ts +0 -15
  717. package/util/report-builder-unwinds.js +0 -156
  718. package/util/report-builder-unwinds.js.map +0 -1
  719. package/util/runner-process-janitor.d.ts +0 -27
  720. package/util/runner-process-janitor.js +0 -208
  721. package/util/runner-process-janitor.js.map +0 -1
  722. package/util/schema-report-builder.d.ts +0 -6
  723. package/util/schema-report-builder.js +0 -481
  724. package/util/schema-report-builder.js.map +0 -1
  725. package/util/slow-query-reporter.d.ts +0 -28
  726. package/util/slow-query-reporter.js +0 -226
  727. package/util/slow-query-reporter.js.map +0 -1
  728. package/util/subscription-dependency-context.d.ts +0 -34
  729. package/util/subscription-dependency-context.js +0 -1283
  730. package/util/subscription-dependency-context.js.map +0 -1
  731. package/util/support-runner-v5.d.ts +0 -1426
  732. package/util/support-runner-v5.js +0 -7631
  733. package/util/support-runner-v5.js.map +0 -1
  734. package/util/tokenizer.d.ts +0 -5
  735. package/util/tokenizer.js +0 -41
  736. package/util/tokenizer.js.map +0 -1
  737. package/workers/codex-runner.worker.d.ts +0 -1
  738. package/workers/codex-runner.worker.js +0 -192
  739. package/workers/codex-runner.worker.js.map +0 -1
  740. /package/{private → src/private}/email-templates/enrollment.html +0 -0
  741. /package/{private → src/private}/email-templates/forgot-password.html +0 -0
  742. /package/{private → src/private}/email-templates/support-ticket-deleted.html +0 -0
  743. /package/{private → src/private}/email-templates/support-ticket-modified.html +0 -0
  744. /package/{private → src/private}/email-templates/support-ticket.html +0 -0
  745. /package/{public_api.d.ts → src/public_api.ts} +0 -0
@@ -0,0 +1,2767 @@
1
+ import { ResolveIOServer } from '../resolveio-server-app';
2
+ import { objectIdHexString, pad } from '../util/common';
3
+ import axios, { AxiosRequestConfig } from 'axios';
4
+ import { createHash } from 'crypto';
5
+ import { URL } from 'url';
6
+ import { Users } from '../collections/user.collection';
7
+ import { AppSettings } from '../collections/app-setting.collection';
8
+ import { buildGeneratedAppAutoFixSummary, buildResolveIOProjectAutoFixDetails } from './customer-notification-content.manager';
9
+ import { ErrorReportAttachment, ErrorReportPayload } from '../types/error-report';
10
+
11
+ type AICoderAppModel = Record<string, any>;
12
+ type AIDashboardJob = Record<string, any>;
13
+ type ClientModel = Record<string, any>;
14
+ type ErrorAutoFixLogModel = Record<string, any>;
15
+
16
+ export interface ErrorAutoFixManagerDependencies {
17
+ Clients?: any;
18
+ ErrorAutoFixLogs: any;
19
+ AICoderApps?: any;
20
+ AIDashboardJobs?: any;
21
+ }
22
+
23
+ const OPTIONAL_COLLECTION = {
24
+ findOne: () => Promise.resolve(null),
25
+ findById: () => Promise.resolve(null),
26
+ find: () => Promise.resolve([])
27
+ };
28
+
29
+ let AICoderApps: any = OPTIONAL_COLLECTION;
30
+ let AIDashboardJobs: any = OPTIONAL_COLLECTION;
31
+ let Clients: any = OPTIONAL_COLLECTION;
32
+ let ErrorAutoFixLogs: any = null;
33
+ let configuredErrorAutoFixDependencies: ErrorAutoFixManagerDependencies | null = null;
34
+
35
+ function resolveErrorAutoFixManagerDependencies(
36
+ overrides?: Partial<ErrorAutoFixManagerDependencies>
37
+ ): ErrorAutoFixManagerDependencies {
38
+ const resolved: ErrorAutoFixManagerDependencies = {
39
+ ...(configuredErrorAutoFixDependencies || {} as ErrorAutoFixManagerDependencies),
40
+ ...(overrides || {})
41
+ };
42
+
43
+ if (!resolved.ErrorAutoFixLogs) {
44
+ throw new Error('ErrorAutoFixManager dependencies are not configured.');
45
+ }
46
+
47
+ return resolved;
48
+ }
49
+
50
+ function applyErrorAutoFixDependencies(dependencies: ErrorAutoFixManagerDependencies): void {
51
+ configuredErrorAutoFixDependencies = dependencies;
52
+ Clients = dependencies.Clients || OPTIONAL_COLLECTION;
53
+ ErrorAutoFixLogs = dependencies.ErrorAutoFixLogs;
54
+ AICoderApps = dependencies.AICoderApps || OPTIONAL_COLLECTION;
55
+ AIDashboardJobs = dependencies.AIDashboardJobs || OPTIONAL_COLLECTION;
56
+ }
57
+
58
+ export function registerErrorAutoFixManagerDependencies(dependencies: ErrorAutoFixManagerDependencies): void {
59
+ applyErrorAutoFixDependencies(dependencies);
60
+ ensureErrorAutoFixManager();
61
+ }
62
+
63
+ interface AutoFixConfig {
64
+ enabled: boolean;
65
+ repoRoot: string;
66
+ baseBranch: string;
67
+ branchPrefix: string;
68
+ githubToken?: string;
69
+ githubOwner: string;
70
+ githubRepo: string;
71
+ commandTimeoutMs: number;
72
+ notifyEmails: string[];
73
+ debugLogging: boolean;
74
+ configSource: string;
75
+ openaiEnvironment?: string;
76
+ openaiProjectId?: string;
77
+ autoOptimizeEnabled: boolean;
78
+ ingestKeys: string[];
79
+ dashboardWorkflowEnabled: boolean;
80
+ dashboardFallbackToGithub: boolean;
81
+ dashboardWaitTimeoutMs: number;
82
+ dashboardPublishRequired: boolean;
83
+ maxAutoRunAttemptsPerIssue: number;
84
+ escalationEmails: string[];
85
+ }
86
+
87
+ interface ErrorEmail {
88
+ key: string;
89
+ subject: string;
90
+ body: string;
91
+ from?: string;
92
+ messageId?: string;
93
+ hash?: string;
94
+ rawHash?: string;
95
+ issueHash?: string;
96
+ logId?: string;
97
+ raw: string;
98
+ fromAddress?: string;
99
+ id_client?: string;
100
+ client_name?: string;
101
+ source_type?: 'app' | 'infrastructure' | 'noise';
102
+ classification_reason?: string;
103
+ openai_environment?: string;
104
+ sourceApp?: string;
105
+ sourceEnvironment?: string;
106
+ severity?: string;
107
+ reportedBy?: string;
108
+ reportMetadata?: Record<string, any>;
109
+ reportContext?: Record<string, any>;
110
+ attachments?: ErrorReportAttachment[];
111
+ reportedAt?: Date;
112
+ }
113
+
114
+ interface AutoFixResult {
115
+ status: 'skipped' | 'failed' | 'success' | 'in_progress';
116
+ email: ErrorEmail;
117
+ error?: string;
118
+ branchName?: string;
119
+ prUrl?: string;
120
+ log?: ErrorAutoFixLogModel;
121
+ reason?: string;
122
+ summary?: string;
123
+ notes?: string;
124
+ }
125
+
126
+ interface LogReservation {
127
+ proceed: boolean;
128
+ log?: ErrorAutoFixLogModel;
129
+ reason?: string;
130
+ }
131
+
132
+ interface EmailClassification {
133
+ source: 'app' | 'infrastructure' | 'noise';
134
+ skip: boolean;
135
+ reason?: string;
136
+ notify?: boolean;
137
+ markIgnored?: boolean;
138
+ }
139
+
140
+ interface LibraryIssueGateDecision {
141
+ blocked: boolean;
142
+ reason?: string;
143
+ evidence: string[];
144
+ }
145
+
146
+ const INFRA_SUBJECT_KEYWORDS = [
147
+ 'failed snapshot',
148
+ 'replica set',
149
+ 'data size used',
150
+ 'oplog',
151
+ 'lock during backup',
152
+ 'backup',
153
+ 'no primary',
154
+ 'no secondary',
155
+ 'preferred primary',
156
+ 'exceeding memory',
157
+ 'exceeding storage',
158
+ 'unhealthy status',
159
+ 'disconnected status',
160
+ 'replication lag',
161
+ 'database storage size',
162
+ 'compacting collections',
163
+ 'failed connection',
164
+ 'heartbeat',
165
+ 'high cpu usage',
166
+ 'high ram usage'
167
+ ];
168
+ const MONGO_SOURCE_KEYWORDS = [
169
+ 'mongo-explorer',
170
+ 'mongo explorer',
171
+ 'mongo-manager',
172
+ 'mongo manager',
173
+ 'mongomanager',
174
+ 'mongodb'
175
+ ];
176
+ const MONGO_TEXT_PATTERNS: RegExp[] = [
177
+ /\[mongo\]/i,
178
+ /\bmongo(?:db)?\s+(?:server\s+)?error\b/i,
179
+ /\bmongoservererror\b/i,
180
+ /\bmongonetwork(?:timeout)?error\b/i,
181
+ /\bmongonetworktimeouterror\b/i
182
+ ];
183
+ const DEFAULT_ERROR_ALERT_EMAIL = 'dev@resolveio.com';
184
+ const MAX_LOCAL_NOTIFICATION_USERS = 5000;
185
+ const LIBRARY_MARKER_PATTERNS: Array<{label: string; pattern: RegExp}> = [
186
+ {label: 'node_modules stack', pattern: /(?:^|[\/\\])node_modules(?:[\/\\]|$)/i},
187
+ {label: '@resolveio/client-lib', pattern: /@resolveio\/client-lib(?:-[a-z0-9-]+)?/i},
188
+ {label: '@resolveio/server-lib', pattern: /@resolveio\/server-lib/i},
189
+ {label: 'resolveio-client-lib path', pattern: /(?:^|[\/\\])resolveio-client-lib(?:[\/\\]|$)/i},
190
+ {label: 'resolveio-server-lib path', pattern: /(?:^|[\/\\])resolveio-server-lib(?:[\/\\]|$)/i},
191
+ {label: 'base template path', pattern: /(?:^|[\/\\])aicoder[\/\\]templates[\/\\]base_(?:small|medium|large|tool)(?:[\/\\]|$)/i}
192
+ ];
193
+ const APP_OWNED_PATH_PATTERN = /(?:^|[\/\\])(angular[\/\\]app|server[\/\\]src)(?:[\/\\]|$)/i;
194
+ const FILE_PATH_PATTERN = /(?:webpack:\/\/\/?|\/|\.\/|[A-Za-z]:\\)[^\s'"`]+?\.(?:ts|tsx|js|jsx|mjs|cjs|html|scss|css)/g;
195
+
196
+ interface RepoTarget {
197
+ owner: string;
198
+ repo: string;
199
+ }
200
+
201
+ interface GithubPullRequest {
202
+ number: number;
203
+ html_url: string;
204
+ }
205
+
206
+ interface ErrorReportIngestResult {
207
+ status: 'queued' | 'skipped' | 'duplicate' | 'ignored' | 'success' | 'failed' | 'in_progress';
208
+ reason?: string;
209
+ log?: ErrorAutoFixLogModel;
210
+ }
211
+
212
+ export interface DevErrorReportOptions {
213
+ subject: string;
214
+ body?: string;
215
+ sourceApp?: string;
216
+ environment?: string;
217
+ severity?: string;
218
+ fingerprint?: string;
219
+ idempotencyKey?: string;
220
+ metadata?: Record<string, any>;
221
+ clientId?: string;
222
+ clientName?: string;
223
+ clientSlug?: string;
224
+ }
225
+
226
+ export class ErrorAutoFixManager {
227
+ private config: AutoFixConfig;
228
+ private _serverConfig = null;
229
+ private readonly githubApiBase = 'https://api.github.com';
230
+ private readonly dashboardMonitors = new Map<string, Promise<void>>();
231
+ private static readonly APP_SETTINGS_CACHE_TTL_MS = 10000;
232
+ private appSettingsAutoOptimizeCacheExpiresAt = 0;
233
+ private appSettingsAutoOptimizeCacheValue: boolean | null = null;
234
+ private ready = false;
235
+
236
+ constructor(serverConfig, dependencies?: Partial<ErrorAutoFixManagerDependencies>) {
237
+ const resolvedDependencies = resolveErrorAutoFixManagerDependencies(dependencies);
238
+ applyErrorAutoFixDependencies(resolvedDependencies);
239
+
240
+ this._serverConfig = serverConfig || {};
241
+ this.config = this.resolveConfig();
242
+
243
+ if (!resolvedDependencies.AICoderApps || !resolvedDependencies.AIDashboardJobs) {
244
+ this.config.dashboardWorkflowEnabled = false;
245
+ }
246
+
247
+ if (!this.config.enabled) {
248
+ return;
249
+ }
250
+
251
+ if (!this.config.dashboardWorkflowEnabled && (!this.config.repoRoot || !this.config.githubOwner || !this.config.githubRepo)) {
252
+ console.warn('ErrorAutoFixManager auto-optimize unavailable - missing repository configuration. Ingest and manual runs remain available.');
253
+ }
254
+
255
+ this.ready = true;
256
+
257
+ // console.log('ErrorAutoFixManager initialized (HTTP intake mode).');
258
+ // console.log('ErrorAutoFixManager config source:', this.config.configSource);
259
+ // if (this.config.debugLogging) {
260
+ // console.log('ErrorAutoFixManager debug logging enabled.');
261
+ // }
262
+ // if (!this.config.autoOptimizeEnabled) {
263
+ // console.log('ErrorAutoFixManager auto-run disabled. Manual OpenAI runs required.');
264
+ // }
265
+ }
266
+
267
+ public isReady(): boolean {
268
+ return !!(this.ready && this.config?.enabled);
269
+ }
270
+
271
+ public validateIngestKey(candidate?: string): boolean {
272
+ const keys = Array.isArray(this.config?.ingestKeys) ? this.config.ingestKeys.filter(Boolean) : [];
273
+
274
+ if (!keys.length) {
275
+ return true;
276
+ }
277
+
278
+ if (!candidate) {
279
+ return false;
280
+ }
281
+
282
+ return keys.includes(candidate.trim());
283
+ }
284
+
285
+ private debugLog(message: string, details?: any): void {
286
+ if (!this.config?.debugLogging) {
287
+ return;
288
+ }
289
+
290
+ return;
291
+
292
+ if (typeof details === 'undefined') {
293
+ console.log(`[ErrorAutoFixManager][debug] ${message}`);
294
+ }
295
+ else {
296
+ console.log(`[ErrorAutoFixManager][debug] ${message}`, details);
297
+ }
298
+ }
299
+
300
+ private resolveConfig(): AutoFixConfig {
301
+ const scAutofix = (this._serverConfig && (this._serverConfig.autofix || this._serverConfig.AUTOFIX)) || {};
302
+ let notifyEmails: string[] = [];
303
+ let escalationEmails: string[] = [];
304
+
305
+ if (process.env.AUTOFIX_NOTIFY_EMAILS) {
306
+ notifyEmails = process.env.AUTOFIX_NOTIFY_EMAILS.split(',').map(a => a.trim()).filter(Boolean);
307
+ }
308
+ else if (Array.isArray(scAutofix.notifyEmails)) {
309
+ notifyEmails = scAutofix.notifyEmails.map(a => (a || '').trim()).filter(Boolean);
310
+ }
311
+ else if (typeof scAutofix.notifyEmails === 'string') {
312
+ notifyEmails = scAutofix.notifyEmails.split(',').map(a => a.trim()).filter(Boolean);
313
+ }
314
+
315
+ if (process.env.AUTOFIX_ESCALATION_EMAILS) {
316
+ escalationEmails = process.env.AUTOFIX_ESCALATION_EMAILS.split(',').map(a => a.trim()).filter(Boolean);
317
+ }
318
+ else if (Array.isArray(scAutofix.escalationEmails)) {
319
+ escalationEmails = scAutofix.escalationEmails.map(a => (a || '').trim()).filter(Boolean);
320
+ }
321
+ else if (typeof scAutofix.escalationEmails === 'string') {
322
+ escalationEmails = scAutofix.escalationEmails.split(',').map(a => a.trim()).filter(Boolean);
323
+ }
324
+
325
+ const getString = (envKey: string, scKey: string, fallback = ''): string => {
326
+ return process.env[envKey] || scAutofix[scKey] || fallback;
327
+ };
328
+
329
+ const getNumber = (envKey: string, scKey: string, fallback: number) => {
330
+ const envVal = Number(process.env[envKey]);
331
+ if (!Number.isNaN(envVal) && process.env[envKey] !== undefined) {
332
+ return envVal;
333
+ }
334
+ const scVal = Number(scAutofix[scKey]);
335
+ if (!Number.isNaN(scVal) && scAutofix[scKey] !== undefined) {
336
+ return scVal;
337
+ }
338
+ return fallback;
339
+ };
340
+
341
+ const getBoolean = (envKey: string, scKey: string, fallback = false) => {
342
+ if (typeof process.env[envKey] !== 'undefined') {
343
+ return process.env[envKey] === 'true';
344
+ }
345
+ if (typeof scAutofix[scKey] !== 'undefined') {
346
+ return !!scAutofix[scKey];
347
+ }
348
+ return fallback;
349
+ };
350
+
351
+ const parseKeyList = (value: any): string[] => {
352
+ if (Array.isArray(value)) {
353
+ return value.map(val => `${val || ''}`.trim()).filter(Boolean);
354
+ }
355
+
356
+ if (typeof value === 'string') {
357
+ return value.split(',').map(val => val.trim()).filter(Boolean);
358
+ }
359
+
360
+ return [];
361
+ };
362
+
363
+ const normalizePositiveInt = (value: number, fallback: number): number => {
364
+ if (!Number.isFinite(value) || value <= 0) {
365
+ return fallback;
366
+ }
367
+ return Math.floor(value);
368
+ };
369
+
370
+ const openaiProjectId = getString('OPENAI_PROJECT_ID', 'openaiProjectId');
371
+ const openaiEnvironment = getString('OPENAI_ENVIRONMENT', 'openaiEnvironment', openaiProjectId);
372
+ const ingestSource = typeof process.env.AUTOFIX_INGEST_KEYS !== 'undefined'
373
+ ? process.env.AUTOFIX_INGEST_KEYS
374
+ : scAutofix.ingestKeys;
375
+ const hasAutoOptimizeEnv = typeof process.env.AUTOFIX_AUTO_OPTIMIZE_ENABLED !== 'undefined';
376
+ const autoOptimizeEnabled = getBoolean('AUTOFIX_AUTO_OPTIMIZE_ENABLED', 'autoOptimizeEnabled', false);
377
+
378
+ return {
379
+ enabled: true,
380
+ repoRoot: getString('AUTOFIX_REPO_ROOT', 'repoRoot', ''),
381
+ baseBranch: getString('AUTOFIX_BASE_BRANCH', 'baseBranch', 'main'),
382
+ branchPrefix: getString('AUTOFIX_BRANCH_PREFIX', 'branchPrefix', 'openai/auto'),
383
+ githubToken: getString('GITHUB_TOKEN', 'githubToken', getString('AUTOFIX_GITHUB_TOKEN', 'githubToken')),
384
+ githubOwner: getString('AUTOFIX_GITHUB_OWNER', 'githubOwner', 'resolveio'),
385
+ githubRepo: getString('AUTOFIX_GITHUB_REPO', 'githubRepo', 'resolveio-all'),
386
+ commandTimeoutMs: getNumber('AUTOFIX_COMMAND_TIMEOUT_MS', 'commandTimeoutMs', 600000),
387
+ notifyEmails,
388
+ debugLogging: getBoolean('AUTOFIX_DEBUG_LOGS', 'debugLogging', false),
389
+ configSource: hasAutoOptimizeEnv ? 'environment' : (Object.keys(scAutofix).length ? 'serverConfig' : 'defaults'),
390
+ openaiEnvironment,
391
+ openaiProjectId,
392
+ autoOptimizeEnabled: autoOptimizeEnabled,
393
+ ingestKeys: parseKeyList(ingestSource),
394
+ dashboardWorkflowEnabled: getBoolean('AUTOFIX_DASHBOARD_WORKFLOW_ENABLED', 'dashboardWorkflowEnabled', true),
395
+ dashboardFallbackToGithub: getBoolean('AUTOFIX_DASHBOARD_FALLBACK_TO_GITHUB', 'dashboardFallbackToGithub', true),
396
+ dashboardWaitTimeoutMs: getNumber('AUTOFIX_DASHBOARD_WAIT_TIMEOUT_MS', 'dashboardWaitTimeoutMs', 45 * 60 * 1000),
397
+ dashboardPublishRequired: getBoolean('AUTOFIX_DASHBOARD_PUBLISH_REQUIRED', 'dashboardPublishRequired', true),
398
+ maxAutoRunAttemptsPerIssue: normalizePositiveInt(
399
+ getNumber('AUTOFIX_MAX_AUTORUN_ATTEMPTS_PER_ISSUE', 'maxAutoRunAttemptsPerIssue', 3),
400
+ 3
401
+ ),
402
+ escalationEmails
403
+ };
404
+ }
405
+
406
+ private extractEmailAddress(value: string): string {
407
+ if (!value) {
408
+ return '';
409
+ }
410
+
411
+ let email = value;
412
+ const angleMatch = value.match(/<([^>]+)>/);
413
+
414
+ if (angleMatch && angleMatch[1]) {
415
+ email = angleMatch[1];
416
+ }
417
+
418
+ email = email.split(';')[0].split(',')[0];
419
+ return email.trim().toLowerCase();
420
+ }
421
+
422
+ private escapeRegex(value: string): string {
423
+ return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
424
+ }
425
+
426
+ private async resolveClientForEnvironment(environment?: string): Promise<ClientModel> {
427
+ const host = this.extractHostFromUrl(environment);
428
+ if (!host) {
429
+ return null;
430
+ }
431
+
432
+ try {
433
+ const escaped = this.escapeRegex(host);
434
+ const query = {
435
+ website: {
436
+ $regex: escaped,
437
+ $options: 'i'
438
+ }
439
+ };
440
+
441
+ return await Clients.findOne(query);
442
+ }
443
+ catch (err) {
444
+ console.error('ErrorAutoFixManager resolveClientForEnvironment error:', err);
445
+ return null;
446
+ }
447
+ }
448
+
449
+ private extractHostFromUrl(value?: string): string | null {
450
+ if (!value) {
451
+ return null;
452
+ }
453
+
454
+ const trimmed = value.trim();
455
+ if (!trimmed) {
456
+ return null;
457
+ }
458
+
459
+ try {
460
+ const parsed = new URL(trimmed);
461
+ return parsed.hostname.toLowerCase();
462
+ }
463
+ catch {
464
+ return trimmed.toLowerCase();
465
+ }
466
+ }
467
+
468
+ private async resolveClientForReport(report: ErrorReportPayload): Promise<ClientModel> {
469
+ if (!report) {
470
+ return null;
471
+ }
472
+
473
+ try {
474
+ if (report.clientId) {
475
+ const byId = await Clients.findById(report.clientId);
476
+ if (byId) {
477
+ return byId;
478
+ }
479
+ }
480
+
481
+ const tryField = async (field: string, value?: string): Promise<ClientModel> => {
482
+ if (!value) {
483
+ return null;
484
+ }
485
+
486
+ const escaped = this.escapeRegex(value);
487
+ const query: {[key: string]: any} = {};
488
+ query[field] = {$regex: `^${escaped}$`, $options: 'i'};
489
+ return await Clients.findOne(query);
490
+ };
491
+
492
+ return (
493
+ (await tryField('name', report.clientName)) ||
494
+ (await tryField('demo_name', report.clientSlug)) ||
495
+ (await tryField('repo', report.clientRepo)) ||
496
+ (await this.resolveClientForEnvironment(report.environment))
497
+ );
498
+ }
499
+ catch (err) {
500
+ console.error('ErrorAutoFixManager resolveClientForReport error:', err);
501
+ return null;
502
+ }
503
+ }
504
+
505
+ private determineOpenAIEnvironment(client?: ClientModel): string {
506
+ const repoCandidate = (client?.repo || '').trim();
507
+
508
+ if (repoCandidate) {
509
+ return repoCandidate;
510
+ }
511
+
512
+ const configEnv = (this.config.openaiEnvironment || '').trim();
513
+
514
+ if (configEnv) {
515
+ return configEnv;
516
+ }
517
+
518
+ if ((this.config.githubOwner || '').trim() && (this.config.githubRepo || '').trim()) {
519
+ return `${this.config.githubOwner}/${this.config.githubRepo}`.trim();
520
+ }
521
+
522
+ return '';
523
+ }
524
+
525
+ private resolveEnvironmentForTask(email: ErrorEmail, log: ErrorAutoFixLogModel): string {
526
+ if (email && email.openai_environment && email.openai_environment.trim()) {
527
+ return email.openai_environment.trim();
528
+ }
529
+
530
+ if (log && log.openai_environment && log.openai_environment.trim()) {
531
+ return log.openai_environment.trim();
532
+ }
533
+
534
+ return this.determineOpenAIEnvironment();
535
+ }
536
+
537
+
538
+ private buildEmailFromReport(report: ErrorReportPayload): ErrorEmail {
539
+ const timestamp = report.reportedAt instanceof Date && !Number.isNaN(report.reportedAt.getTime())
540
+ ? report.reportedAt
541
+ : new Date();
542
+ const safeMessage = (report.message || '').toString().trim() || 'Unknown error';
543
+ const subjectBase = `[AppError] ${report.sourceApp || 'application'}${report.environment ? ` (${report.environment})` : ''}`;
544
+ const subject = `${subjectBase} - ${safeMessage.slice(0, 120)}`;
545
+ const lines: string[] = [
546
+ `Source App:\t${report.sourceApp}`,
547
+ `Environment:\t${report.environment || 'n/a'}`,
548
+ `Severity:\t${report.severity || 'error'}`,
549
+ `Client Hint:\t${report.clientName || report.clientSlug || report.clientRepo || report.clientId || 'n/a'}`,
550
+ `Reported By:\t${report.reportedBy || report.clientEmail || 'n/a'}`,
551
+ `Reported At:\t${timestamp.toISOString()}`,
552
+ `Message:\t${safeMessage}`
553
+ ];
554
+
555
+ if (report.stack) {
556
+ lines.push('', 'Stack Trace:', report.stack);
557
+ }
558
+
559
+ if (report.context && typeof report.context === 'object' && Object.keys(report.context).length) {
560
+ lines.push('', 'Context JSON:', JSON.stringify(report.context, null, 2));
561
+ }
562
+
563
+ if (report.metadata && typeof report.metadata === 'object' && Object.keys(report.metadata).length) {
564
+ lines.push('', 'Metadata JSON:', JSON.stringify(report.metadata, null, 2));
565
+ }
566
+
567
+ if (Array.isArray(report.attachments) && report.attachments.length) {
568
+ lines.push('', 'Attachments:');
569
+ report.attachments.forEach((att: ErrorReportAttachment) => {
570
+ const desc: string[] = [];
571
+ if (att.name) {
572
+ desc.push(att.name);
573
+ }
574
+ if (att.contentType) {
575
+ desc.push(att.contentType);
576
+ }
577
+ if (typeof att.size === 'number') {
578
+ desc.push(`${att.size}b`);
579
+ }
580
+
581
+ const summary = desc.length ? desc.join(' | ') : 'attachment';
582
+ lines.push(`- ${summary}${att.url ? ` -> ${att.url}` : ''}`);
583
+ });
584
+ }
585
+
586
+ if (report.fingerprint) {
587
+ lines.push('', `Fingerprint:\t${report.fingerprint}`);
588
+ }
589
+
590
+ if (report.idempotencyKey) {
591
+ lines.push(`Idempotency Key:\t${report.idempotencyKey}`);
592
+ }
593
+
594
+ const body = lines.join('\n');
595
+
596
+ return {
597
+ key: `report:${timestamp.getTime().toString(36)}:${Math.random().toString(36).slice(2, 10)}`,
598
+ subject,
599
+ body,
600
+ raw: body,
601
+ from: report.reportedBy || report.clientEmail || 'errors@resolveio.com',
602
+ sourceApp: report.sourceApp,
603
+ sourceEnvironment: report.environment,
604
+ severity: report.severity,
605
+ reportedBy: report.reportedBy,
606
+ reportMetadata: report.metadata,
607
+ reportContext: report.context,
608
+ attachments: Array.isArray(report.attachments) ? report.attachments : [],
609
+ openai_environment: report.clientRepo,
610
+ reportedAt: timestamp
611
+ };
612
+ }
613
+
614
+ private classifyEmail(email: ErrorEmail): EmailClassification {
615
+ const subject = (email.subject || '').toLowerCase();
616
+ const body = email.body || '';
617
+ const normalizedBody = body.toLowerCase();
618
+
619
+ if (
620
+ subject.includes('autofix') &&
621
+ (normalizedBody.includes('auto-fix workflow') || normalizedBody.includes('autofix workflow'))
622
+ ) {
623
+ return {source: 'noise', skip: true, reason: 'AutoFix status notification'};
624
+ }
625
+
626
+ if (this.isMongoOriginError(email)) {
627
+ return {
628
+ source: 'infrastructure',
629
+ skip: true,
630
+ reason: 'Mongo-origin error ignored for auto-fix',
631
+ markIgnored: true
632
+ };
633
+ }
634
+
635
+ if (subject.includes('[database]') || subject.includes('[server]')) {
636
+ return {source: 'infrastructure', skip: true, reason: 'Infrastructure alert (subject tag)'};
637
+ }
638
+
639
+ const infraKeyword = INFRA_SUBJECT_KEYWORDS.find(keyword => subject.includes(keyword));
640
+ if (infraKeyword) {
641
+ return {source: 'infrastructure', skip: true, reason: `Infrastructure alert (${infraKeyword})`};
642
+ }
643
+
644
+ if (!body.trim()) {
645
+ return {source: 'noise', skip: true, reason: 'Empty error payload'};
646
+ }
647
+
648
+ const emptyErrorsRegex = /errors\s*[\r\n]+(\{\s*\}|\[\s*\])/i;
649
+ if (emptyErrorsRegex.test(body)) {
650
+ return {source: 'noise', skip: true, reason: 'Empty error details'};
651
+ }
652
+
653
+ if (subject.includes('database is down') || normalizedBody.includes('database is down')) {
654
+ return {source: 'infrastructure', skip: true, reason: 'Database outage alert'};
655
+ }
656
+
657
+ return {source: 'app', skip: false};
658
+ }
659
+
660
+ private isMongoOriginError(email: ErrorEmail): boolean {
661
+ const sourceApp = String(email?.sourceApp || '').toLowerCase();
662
+ const sourceEnvironment = String(email?.sourceEnvironment || '').toLowerCase();
663
+ const subject = String(email?.subject || '').toLowerCase();
664
+ const body = String(email?.body || '');
665
+
666
+ const sourceMatches = MONGO_SOURCE_KEYWORDS.some(keyword =>
667
+ sourceApp.includes(keyword) || sourceEnvironment.includes(keyword)
668
+ );
669
+ if (sourceMatches) {
670
+ return true;
671
+ }
672
+
673
+ const text = `${subject}\n${body}`;
674
+ return MONGO_TEXT_PATTERNS.some(pattern => pattern.test(text));
675
+ }
676
+
677
+ private generateRawEmailHash(email: ErrorEmail): string {
678
+ return createHash('sha256').update(`${email.subject || ''}\n${email.body || ''}`, 'utf8').digest('hex');
679
+ }
680
+
681
+ private generateIssueHash(email: ErrorEmail): string {
682
+ const subject = this.normalizeTextForHash(email.subject || '');
683
+ const body = this.normalizeBodyForHash(email.body || '');
684
+ return createHash('sha256').update(`${subject}\n${body}`, 'utf8').digest('hex');
685
+ }
686
+
687
+ private normalizeTextForHash(value: string): string {
688
+ if (!value) {
689
+ return '';
690
+ }
691
+
692
+ const normalized = value
693
+ .replace(/\r/g, '\n')
694
+ .toLowerCase()
695
+ .replace(/0x[0-9a-f]+/gi, '0x#')
696
+ .replace(/\b[0-9a-f]{7,64}\b/gi, '#')
697
+ .replace(/\b[0-9]{4}[-/][0-9]{2}[-/][0-9]{2}(?:[ t][0-9]{2}:[0-9]{2}:[0-9]{2}(?:\.[0-9]{1,6})?)?\b/g, '#datetime#')
698
+ .replace(/\b[0-9]{2}:[0-9]{2}:[0-9]{2}\b/g, '#time#')
699
+ .replace(/[0-9]+/g, '#')
700
+ .replace(/[^\S\n]+/g, ' ')
701
+ .split('\n')
702
+ .map(line => line.trim())
703
+ .filter(Boolean)
704
+ .slice(0, 60)
705
+ .join('\n');
706
+
707
+ return normalized.length > 4000 ? normalized.slice(0, 4000) : normalized;
708
+ }
709
+
710
+ private normalizeBodyForHash(body: string): string {
711
+ if (!body) {
712
+ return '';
713
+ }
714
+
715
+ const withoutStackNoise = body
716
+ .replace(/=+\s*stack trace\s*=+/gi, 'stack trace')
717
+ .replace(/-{3,}/g, '-')
718
+ .replace(/\[[^\]]*request id[^\]]*\]/gi, '[request id]')
719
+ .replace(/guid:[^\s]+/gi, 'guid:#')
720
+ .replace(/request id:[^\s]+/gi, 'request id:#')
721
+ .replace(/correlation id:[^\s]+/gi, 'correlation id:#');
722
+
723
+ return this.normalizeTextForHash(withoutStackNoise);
724
+ }
725
+
726
+ private parseRepoTarget(env: string): RepoTarget {
727
+ const trimmed = (env || '').trim();
728
+ if (!trimmed) {
729
+ return null;
730
+ }
731
+
732
+ const [owner, repo] = trimmed.split('/', 2);
733
+ if (!owner || !repo) {
734
+ return null;
735
+ }
736
+
737
+ return {
738
+ owner: owner.trim(),
739
+ repo: repo.trim()
740
+ };
741
+ }
742
+
743
+ private buildWorkBranch(hash: string): string {
744
+ const shortHash = (hash || '').toLowerCase().replace(/[^a-z0-9]/g, '').slice(0, 12) || 'autofix';
745
+ const prefix = (this.config.branchPrefix || 'openai/auto').trim();
746
+ if (!prefix) {
747
+ return shortHash;
748
+ }
749
+
750
+ const segments = prefix.split('/').map(segment => segment.trim()).filter(Boolean);
751
+ const sanitized = segments.map(segment => segment.replace(/[^a-zA-Z0-9._-]/g, '-'));
752
+ return `${sanitized.join('/')}/${shortHash}`;
753
+ }
754
+
755
+ private normalizeEmailText(value: string, limit = 20000): string {
756
+ if (!value) {
757
+ return '';
758
+ }
759
+
760
+ const cleaned = value.replace(/\r/g, '');
761
+ if (cleaned.length <= limit) {
762
+ return cleaned;
763
+ }
764
+
765
+ return `${cleaned.slice(0, limit)}\n... (truncated)`;
766
+ }
767
+
768
+ private buildContextMarkdown(email: ErrorEmail, identifier: string, normalizedBody: string): string {
769
+ const lines: string[] = [
770
+ `# OpenAI Error Context: ${identifier}`,
771
+ '',
772
+ `**Subject**: ${email.subject || '(no subject)'}`,
773
+ email.fromAddress ? `**From**: ${email.fromAddress}` : undefined,
774
+ email.client_name ? `**Client**: ${email.client_name}` : undefined,
775
+ email.source_type ? `**Classification**: ${email.source_type}${email.classification_reason ? ` (${email.classification_reason})` : ''}` : undefined,
776
+ email.id_client ? `**Client Id**: ${email.id_client}` : undefined,
777
+ '',
778
+ '## Body',
779
+ '```',
780
+ normalizedBody || '(empty body)',
781
+ '```',
782
+ '',
783
+ '_Generated by ErrorAutoFixManager_'
784
+ ];
785
+
786
+ return lines.filter(line => typeof line !== 'undefined').join('\n');
787
+ }
788
+
789
+ private buildOpenAIComment(email: ErrorEmail, contextPath: string, normalizedBody: string): string {
790
+ const lines: string[] = [
791
+ '@openai review',
792
+ '',
793
+ `**Email Subject**: ${email.subject || '(no subject)'}`,
794
+ email.fromAddress ? `**From**: ${email.fromAddress}` : undefined,
795
+ email.client_name ? `**Client**: ${email.client_name}` : undefined,
796
+ contextPath ? `Context file: \`${contextPath}\`` : undefined,
797
+ '',
798
+ '```',
799
+ normalizedBody || '(empty body)',
800
+ '```',
801
+ '',
802
+ 'Please investigate this error and propose a minimal, safe fix.'
803
+ ];
804
+
805
+ return lines.filter(line => typeof line !== 'undefined').join('\n');
806
+ }
807
+
808
+ private formatGithubError(error: any): string {
809
+ if (!error) {
810
+ return 'GitHub request failed.';
811
+ }
812
+
813
+ const status = error?.response?.status;
814
+ const message = error?.response?.data?.message || error?.message || 'GitHub request failed.';
815
+
816
+ if (status) {
817
+ return `GitHub API error (${status}): ${message}`;
818
+ }
819
+
820
+ return `GitHub API error: ${message}`;
821
+ }
822
+
823
+ private buildGithubAuthHeader(token?: string): string {
824
+ const trimmed = (token || '').trim();
825
+ if (!trimmed) {
826
+ return '';
827
+ }
828
+
829
+ if (/^(token|Bearer)\s+/i.test(trimmed)) {
830
+ return trimmed;
831
+ }
832
+
833
+ if (trimmed.includes('.') && !trimmed.startsWith('gh')) {
834
+ return `Bearer ${trimmed}`;
835
+ }
836
+
837
+ return `token ${trimmed}`;
838
+ }
839
+
840
+ private async githubRequest<T>(config: AxiosRequestConfig, allow404 = false): Promise<T> {
841
+ const authHeader = this.buildGithubAuthHeader(this.config.githubToken);
842
+ const headers = {
843
+ ...(authHeader ? {Authorization: authHeader} : {}),
844
+ Accept: 'application/vnd.github+json',
845
+ 'User-Agent': 'ResolveIO-AutoFix',
846
+ 'X-GitHub-Api-Version': '2022-11-28'
847
+ };
848
+
849
+ const finalConfig: AxiosRequestConfig = {
850
+ ...config,
851
+ baseURL: this.githubApiBase,
852
+ headers: {
853
+ ...headers,
854
+ ...(config.headers || {})
855
+ },
856
+ timeout: Math.min(Math.max(this.config.commandTimeoutMs || 600000, 1000), 120000)
857
+ };
858
+
859
+ try {
860
+ const response = await axios.request<T>(finalConfig);
861
+ return response.data;
862
+ }
863
+ catch (err) {
864
+ const status = err?.response?.status;
865
+ if (allow404 && status === 404) {
866
+ return null as unknown as T;
867
+ }
868
+ throw new Error(this.formatGithubError(err));
869
+ }
870
+ }
871
+
872
+ private async resolveBaseBranch(target: RepoTarget): Promise<string> {
873
+ const fallback = (this.config.baseBranch || 'main').trim();
874
+ try {
875
+ const repo = await this.githubRequest<{default_branch?: string}>(
876
+ {method: 'GET', url: `/repos/${target.owner}/${target.repo}`}
877
+ );
878
+ const defaultBranch = (repo?.default_branch || '').trim();
879
+ return defaultBranch || fallback;
880
+ }
881
+ catch (err) {
882
+ this.debugLog('Failed to fetch default branch. Falling back to configured branch.', {
883
+ target,
884
+ error: err?.message
885
+ });
886
+ return fallback;
887
+ }
888
+ }
889
+
890
+ private async getBranchSha(target: RepoTarget, branch: string): Promise<string> {
891
+ const ref = await this.githubRequest<{object?: {sha?: string}}>(
892
+ {method: 'GET', url: `/repos/${target.owner}/${target.repo}/git/ref/heads/${branch}`}
893
+ );
894
+ const sha = ref?.object?.sha;
895
+ if (!sha) {
896
+ throw new Error(`Base branch ${branch} not found.`);
897
+ }
898
+ return sha;
899
+ }
900
+
901
+ private async ensureBranch(target: RepoTarget, newBranch: string, baseBranch: string, baseSha: string): Promise<void> {
902
+ const existing = await this.githubRequest<any>(
903
+ {method: 'GET', url: `/repos/${target.owner}/${target.repo}/git/ref/heads/${newBranch}`},
904
+ true
905
+ );
906
+ if (existing) {
907
+ return;
908
+ }
909
+
910
+ await this.githubRequest(
911
+ {
912
+ method: 'POST',
913
+ url: `/repos/${target.owner}/${target.repo}/git/refs`,
914
+ data: {
915
+ ref: `refs/heads/${newBranch}`,
916
+ sha: baseSha
917
+ }
918
+ }
919
+ );
920
+ }
921
+
922
+ private async upsertContextFile(target: RepoTarget, branch: string, contextPath: string, content: string): Promise<void> {
923
+ const encodedPath = encodeURIComponent(contextPath).replace(/%2F/g, '/');
924
+ const existing = await this.githubRequest<any>(
925
+ {
926
+ method: 'GET',
927
+ url: `/repos/${target.owner}/${target.repo}/contents/${encodedPath}`,
928
+ params: {ref: branch}
929
+ },
930
+ true
931
+ );
932
+ const sha = existing && existing.sha ? existing.sha : undefined;
933
+ const commitMessage = `chore(openai): update error context ${contextPath}`;
934
+
935
+ await this.githubRequest(
936
+ {
937
+ method: 'PUT',
938
+ url: `/repos/${target.owner}/${target.repo}/contents/${encodedPath}`,
939
+ data: {
940
+ message: commitMessage.slice(0, 200),
941
+ content: Buffer.from(content, 'utf8').toString('base64'),
942
+ branch,
943
+ sha
944
+ }
945
+ }
946
+ );
947
+ }
948
+
949
+ private async findOpenPullRequest(target: RepoTarget, branch: string, base: string): Promise<GithubPullRequest> {
950
+ const prs = await this.githubRequest<GithubPullRequest[]>(
951
+ {
952
+ method: 'GET',
953
+ url: `/repos/${target.owner}/${target.repo}/pulls`,
954
+ params: {
955
+ state: 'open',
956
+ head: `${target.owner}:${branch}`,
957
+ base,
958
+ per_page: 1
959
+ }
960
+ }
961
+ );
962
+ if (prs && prs.length) {
963
+ return prs[0];
964
+ }
965
+ return null;
966
+ }
967
+
968
+ private async createPullRequest(target: RepoTarget, branch: string, base: string, title: string, body: string): Promise<GithubPullRequest> {
969
+ const pr = await this.githubRequest<GithubPullRequest>(
970
+ {
971
+ method: 'POST',
972
+ url: `/repos/${target.owner}/${target.repo}/pulls`,
973
+ data: {
974
+ title,
975
+ head: branch,
976
+ base,
977
+ body
978
+ }
979
+ }
980
+ );
981
+ return pr;
982
+ }
983
+
984
+ private async commentOnPullRequest(target: RepoTarget, prNumber: number, body: string): Promise<void> {
985
+ await this.githubRequest(
986
+ {
987
+ method: 'POST',
988
+ url: `/repos/${target.owner}/${target.repo}/issues/${prNumber}/comments`,
989
+ data: {
990
+ body
991
+ }
992
+ }
993
+ );
994
+ }
995
+
996
+ private parseBooleanEnv(envKey: string): boolean | null {
997
+ if (typeof process.env[envKey] === 'undefined') {
998
+ return null;
999
+ }
1000
+
1001
+ return process.env[envKey] === 'true';
1002
+ }
1003
+
1004
+ private async resolveAutoOptimizeEnabled(): Promise<boolean> {
1005
+ const now = Date.now();
1006
+ if (this.appSettingsAutoOptimizeCacheValue !== null && now < this.appSettingsAutoOptimizeCacheExpiresAt) {
1007
+ return this.appSettingsAutoOptimizeCacheValue;
1008
+ }
1009
+
1010
+ const envValue = this.parseBooleanEnv('AUTOFIX_AUTO_OPTIMIZE_ENABLED');
1011
+ let enabled = envValue !== null ? envValue : !!this.config.autoOptimizeEnabled;
1012
+ try {
1013
+ if (AppSettings && typeof AppSettings.findOne === 'function') {
1014
+ const activeSetting = await AppSettings.findOne({
1015
+ is_active: {
1016
+ $ne: false
1017
+ }
1018
+ }, {
1019
+ sort: {
1020
+ updatedAt: -1,
1021
+ createdAt: -1
1022
+ }
1023
+ }) || await AppSettings.findOne({}, {
1024
+ sort: {
1025
+ updatedAt: -1,
1026
+ createdAt: -1
1027
+ }
1028
+ });
1029
+
1030
+ if (activeSetting && typeof activeSetting.enable_auto_fix === 'boolean') {
1031
+ enabled = !!activeSetting.enable_auto_fix;
1032
+ }
1033
+ }
1034
+ }
1035
+ catch (error) {
1036
+ if (this.config?.debugLogging) {
1037
+ console.warn('ErrorAutoFixManager failed to read app settings auto-fix toggle', error);
1038
+ }
1039
+ }
1040
+
1041
+ this.appSettingsAutoOptimizeCacheValue = enabled;
1042
+ this.appSettingsAutoOptimizeCacheExpiresAt = now + ErrorAutoFixManager.APP_SETTINGS_CACHE_TTL_MS;
1043
+ return enabled;
1044
+ }
1045
+
1046
+ private async reserveLog(email: ErrorEmail, client?: ClientModel, autoOptimizeEnabled = false): Promise<LogReservation> {
1047
+ const hash = email.hash;
1048
+ const rawHash = email.rawHash || hash;
1049
+ const now = email.reportedAt || new Date();
1050
+
1051
+ let existing: ErrorAutoFixLogModel = null;
1052
+
1053
+ if (hash) {
1054
+ existing = await ErrorAutoFixLogs.findOne({issue_hash: hash});
1055
+ }
1056
+
1057
+ if (!existing && rawHash) {
1058
+ existing = await ErrorAutoFixLogs.findOne({email_hash: rawHash});
1059
+ }
1060
+
1061
+ if (!existing && hash && hash !== rawHash) {
1062
+ existing = await ErrorAutoFixLogs.findOne({email_hash: hash});
1063
+ }
1064
+
1065
+ if (existing) {
1066
+ await this.markDuplicate(existing._id, now);
1067
+ const updatedExisting = await ErrorAutoFixLogs.findOne({_id: existing._id}) || existing;
1068
+
1069
+ const metaUpdate: Partial<ErrorAutoFixLogModel> = {};
1070
+
1071
+ if (hash && (!updatedExisting.issue_hash || updatedExisting.issue_hash !== hash)) {
1072
+ metaUpdate.issue_hash = hash;
1073
+ }
1074
+
1075
+ if (rawHash && (!updatedExisting.email_hash || updatedExisting.email_hash !== rawHash)) {
1076
+ metaUpdate.email_hash = rawHash;
1077
+ }
1078
+
1079
+ if (email.fromAddress && email.fromAddress !== updatedExisting.from_email) {
1080
+ metaUpdate.from_email = email.fromAddress;
1081
+ }
1082
+
1083
+ if (client) {
1084
+ if (!updatedExisting.id_client || updatedExisting.id_client !== client._id) {
1085
+ metaUpdate.id_client = client._id;
1086
+ }
1087
+
1088
+ if (!updatedExisting.client_name || updatedExisting.client_name !== client.name) {
1089
+ metaUpdate.client_name = client.name;
1090
+ }
1091
+ }
1092
+
1093
+ if (email.openai_environment && email.openai_environment !== updatedExisting.openai_environment) {
1094
+ metaUpdate.openai_environment = email.openai_environment;
1095
+ }
1096
+
1097
+ if (email.sourceApp && email.sourceApp !== updatedExisting.source_app) {
1098
+ metaUpdate.source_app = email.sourceApp;
1099
+ }
1100
+
1101
+ if (email.sourceEnvironment && email.sourceEnvironment !== updatedExisting.source_environment) {
1102
+ metaUpdate.source_environment = email.sourceEnvironment;
1103
+ }
1104
+
1105
+ if (email.severity && email.severity !== updatedExisting.severity) {
1106
+ metaUpdate.severity = email.severity;
1107
+ }
1108
+
1109
+ if (email.reportedBy && email.reportedBy !== updatedExisting.reported_by) {
1110
+ metaUpdate.reported_by = email.reportedBy;
1111
+ }
1112
+
1113
+ if (typeof email.reportMetadata !== 'undefined') {
1114
+ metaUpdate.report_metadata = email.reportMetadata;
1115
+ }
1116
+
1117
+ if (typeof email.reportContext !== 'undefined') {
1118
+ metaUpdate.report_context = email.reportContext;
1119
+ }
1120
+
1121
+ if (typeof email.attachments !== 'undefined') {
1122
+ metaUpdate.attachments = email.attachments;
1123
+ }
1124
+
1125
+ metaUpdate.last_reported_at = now;
1126
+
1127
+ if (Object.keys(metaUpdate).length) {
1128
+ await this.updateLog(updatedExisting._id, metaUpdate);
1129
+ }
1130
+
1131
+ if (updatedExisting.status === 'completed') {
1132
+ const reactivated = (await ErrorAutoFixLogs.findOneAndUpdate({_id: updatedExisting._id}, {
1133
+ $set: {
1134
+ status: 'pending',
1135
+ ignored: false,
1136
+ last_error: '',
1137
+ openai_task_id: '',
1138
+ branch_name: '',
1139
+ pr_url: ''
1140
+ }
1141
+ }, {
1142
+ returnDocument: 'after'
1143
+ })) || updatedExisting;
1144
+
1145
+ return {
1146
+ proceed: true,
1147
+ log: reactivated
1148
+ };
1149
+ }
1150
+
1151
+ if (updatedExisting.ignored) {
1152
+ return {
1153
+ proceed: false,
1154
+ log: updatedExisting,
1155
+ reason: 'ignored'
1156
+ };
1157
+ }
1158
+
1159
+ if (autoOptimizeEnabled && (updatedExisting.status === 'failed' || updatedExisting.status === 'skipped') && !updatedExisting.ignored) {
1160
+ const attemptsUsed = Number.isFinite(Number(updatedExisting.attempt_count))
1161
+ ? Number(updatedExisting.attempt_count)
1162
+ : 0;
1163
+ const maxAttempts = Number.isFinite(Number(this.config.maxAutoRunAttemptsPerIssue))
1164
+ ? Number(this.config.maxAutoRunAttemptsPerIssue)
1165
+ : 0;
1166
+ if (maxAttempts > 0 && attemptsUsed >= maxAttempts) {
1167
+ const reason = `Auto-fix attempt budget reached (${attemptsUsed}/${maxAttempts}).`;
1168
+ const exhausted = (await ErrorAutoFixLogs.findOneAndUpdate({_id: updatedExisting._id}, {
1169
+ $set: {
1170
+ status: 'skipped',
1171
+ ignored: true,
1172
+ last_error: `${reason} Marked ignored pending manual intervention.`,
1173
+ last_result_at: now,
1174
+ updatedAt: now
1175
+ }
1176
+ }, {
1177
+ returnDocument: 'after'
1178
+ })) || updatedExisting;
1179
+
1180
+ await this.sendEscalationNotice(exhausted, reason);
1181
+ return {
1182
+ proceed: false,
1183
+ log: exhausted,
1184
+ reason: 'budget_exhausted'
1185
+ };
1186
+ }
1187
+
1188
+ const resumed = await ErrorAutoFixLogs.findOneAndUpdate({_id: updatedExisting._id}, {
1189
+ $inc: {
1190
+ attempt_count: 1
1191
+ },
1192
+ $set: {
1193
+ status: 'in_progress',
1194
+ last_attempt_at: now,
1195
+ openai_task_id: '',
1196
+ branch_name: '',
1197
+ pr_url: '',
1198
+ last_error: ''
1199
+ }
1200
+ }, {
1201
+ returnDocument: 'after'
1202
+ });
1203
+
1204
+ return {
1205
+ proceed: true,
1206
+ log: resumed || updatedExisting
1207
+ };
1208
+ }
1209
+
1210
+ if (updatedExisting.status === 'in_progress') {
1211
+ return {
1212
+ proceed: false,
1213
+ log: updatedExisting,
1214
+ reason: 'in_progress'
1215
+ };
1216
+ }
1217
+
1218
+ if (updatedExisting.status === 'success') {
1219
+ return {
1220
+ proceed: false,
1221
+ log: updatedExisting,
1222
+ reason: 'success'
1223
+ };
1224
+ }
1225
+
1226
+ return {
1227
+ proceed: false,
1228
+ log: updatedExisting,
1229
+ reason: 'duplicate'
1230
+ };
1231
+ }
1232
+
1233
+ const counterValue = await ResolveIOServer.getMainServer().getMethodManager().callMethod('incCounter', 'autofix_error');
1234
+ const counterString = pad(counterValue, 6);
1235
+
1236
+ const doc: ErrorAutoFixLogModel = {
1237
+ _id: objectIdHexString(),
1238
+ email_hash: rawHash,
1239
+ issue_hash: hash,
1240
+ subject: email.subject,
1241
+ message_id: email.messageId,
1242
+ body: email.body,
1243
+ status: autoOptimizeEnabled ? 'in_progress' : 'pending',
1244
+ ignored: false,
1245
+ attempt_count: autoOptimizeEnabled ? 1 : 0,
1246
+ duplicate_count: 0,
1247
+ autofix_error_count: counterValue,
1248
+ autofix_error_count_string: counterString,
1249
+ first_seen_at: now,
1250
+ last_attempt_at: autoOptimizeEnabled ? now : undefined,
1251
+ from_email: email.fromAddress || '',
1252
+ id_client: client?._id,
1253
+ client_name: client?.name,
1254
+ openai_environment: email.openai_environment || this.determineOpenAIEnvironment(client),
1255
+ source_app: email.sourceApp,
1256
+ source_environment: email.sourceEnvironment,
1257
+ severity: email.severity,
1258
+ reported_by: email.reportedBy || email.fromAddress || '',
1259
+ last_reported_at: now,
1260
+ report_metadata: email.reportMetadata,
1261
+ report_context: email.reportContext,
1262
+ attachments: email.attachments
1263
+ };
1264
+
1265
+ await ErrorAutoFixLogs.insertOne(doc as any);
1266
+
1267
+ return {
1268
+ proceed: true,
1269
+ log: doc
1270
+ };
1271
+ }
1272
+
1273
+ private async markDuplicate(logId: string, timestamp: Date): Promise<void> {
1274
+ await ErrorAutoFixLogs.updateOne({_id: logId}, {
1275
+ $inc: {
1276
+ duplicate_count: 1
1277
+ },
1278
+ $set: {
1279
+ last_duplicate_at: timestamp,
1280
+ last_reported_at: timestamp,
1281
+ updatedAt: timestamp
1282
+ }
1283
+ });
1284
+ }
1285
+
1286
+ private async updateLog(logId: string, set: Partial<ErrorAutoFixLogModel>, inc?: {[key: string]: number}): Promise<ErrorAutoFixLogModel> {
1287
+ const cleanedSet: {[key: string]: any} = {};
1288
+ const rawSet: {[key: string]: any} = set ? {...set} : {};
1289
+
1290
+ Object.keys(rawSet).forEach(key => {
1291
+ if (typeof rawSet[key] !== 'undefined') {
1292
+ cleanedSet[key] = rawSet[key];
1293
+ }
1294
+ });
1295
+
1296
+ cleanedSet.updatedAt = new Date();
1297
+
1298
+ const update: any = {
1299
+ $set: cleanedSet
1300
+ };
1301
+
1302
+ if (inc) {
1303
+ update.$inc = inc;
1304
+ }
1305
+
1306
+ const result = await ErrorAutoFixLogs.findOneAndUpdate({_id: logId}, update, {
1307
+ returnDocument: 'after'
1308
+ });
1309
+
1310
+ return result;
1311
+ }
1312
+
1313
+ private getEscalationRecipients(): string[] {
1314
+ const unique = new Set<string>();
1315
+ const add = (value: string) => {
1316
+ const normalized = String(value || '').trim().toLowerCase();
1317
+ if (!normalized) {
1318
+ return;
1319
+ }
1320
+ unique.add(normalized);
1321
+ };
1322
+
1323
+ (this.config.escalationEmails || []).forEach(add);
1324
+ (this.config.notifyEmails || []).forEach(add);
1325
+ if (!unique.size) {
1326
+ add(DEFAULT_ERROR_ALERT_EMAIL);
1327
+ }
1328
+ return Array.from(unique);
1329
+ }
1330
+
1331
+ private async sendEscalationNotice(log: ErrorAutoFixLogModel, reason: string): Promise<void> {
1332
+ const recipients = this.getEscalationRecipients();
1333
+ if (!recipients.length || !log?._id) {
1334
+ return;
1335
+ }
1336
+
1337
+ const identifier = log.autofix_error_count_string || log._id;
1338
+ const subject = `ResolveIO AutoFix Escalation: ${identifier}`;
1339
+ const body = [
1340
+ 'Auto-fix retry budget was exhausted for this recurring issue and further autonomous attempts were stopped.',
1341
+ '',
1342
+ `Reason: ${reason}`,
1343
+ `Issue Log ID: ${log._id}`,
1344
+ `Issue Counter: ${log.autofix_error_count_string || 'n/a'}`,
1345
+ `Client: ${log.client_name || log.id_client || 'unknown'}`,
1346
+ `Source App: ${log.source_app || 'unknown'}`,
1347
+ `Environment: ${log.source_environment || log.openai_environment || 'unknown'}`,
1348
+ `Attempts: ${log.attempt_count || 0}/${this.config.maxAutoRunAttemptsPerIssue}`,
1349
+ `Issue Hash: ${log.issue_hash || log.email_hash || 'n/a'}`,
1350
+ '',
1351
+ 'The issue is now marked ignored for auto-fix to protect customer token usage. Manual intervention is required.'
1352
+ ].join('\n');
1353
+
1354
+ const methodManager = ResolveIOServer.getMainServer().getMethodManager();
1355
+ for (const recipient of recipients) {
1356
+ try {
1357
+ await methodManager.sendEmail(recipient, subject, body);
1358
+ }
1359
+ catch (error) {
1360
+ console.error('Failed sending auto-fix escalation email', {recipient, logId: log._id, error});
1361
+ }
1362
+ }
1363
+ }
1364
+
1365
+ private collectLibraryIssueSignalText(email: ErrorEmail, log: ErrorAutoFixLogModel): string {
1366
+ const attachments = Array.isArray(log?.attachments) ? log.attachments : [];
1367
+ const attachmentSignals = attachments.map((attachment) => {
1368
+ return [
1369
+ attachment?.name || '',
1370
+ attachment?.url || '',
1371
+ attachment?.contentType || ''
1372
+ ].filter(Boolean).join(' ');
1373
+ });
1374
+ const metadata = log?.report_metadata && Object.keys(log.report_metadata).length
1375
+ ? JSON.stringify(log.report_metadata)
1376
+ : '';
1377
+ const context = log?.report_context && Object.keys(log.report_context).length
1378
+ ? JSON.stringify(log.report_context)
1379
+ : '';
1380
+ return [
1381
+ email?.subject || '',
1382
+ email?.body || '',
1383
+ log?.body || '',
1384
+ metadata,
1385
+ context,
1386
+ attachmentSignals.join('\n')
1387
+ ].filter(Boolean).join('\n');
1388
+ }
1389
+
1390
+ private evaluateLibraryIssueGate(email: ErrorEmail, log: ErrorAutoFixLogModel): LibraryIssueGateDecision {
1391
+ const signalText = this.collectLibraryIssueSignalText(email, log);
1392
+ if (!signalText) {
1393
+ return {blocked: false, evidence: []};
1394
+ }
1395
+
1396
+ const evidence: string[] = [];
1397
+ let markerHits = 0;
1398
+ for (const marker of LIBRARY_MARKER_PATTERNS) {
1399
+ if (!marker.pattern.test(signalText)) {
1400
+ continue;
1401
+ }
1402
+ markerHits += 1;
1403
+ evidence.push(marker.label);
1404
+ }
1405
+
1406
+ const rawPaths = signalText.match(FILE_PATH_PATTERN) || [];
1407
+ const normalizedPaths = rawPaths
1408
+ .map((value) => String(value || '').trim())
1409
+ .filter(Boolean)
1410
+ .map((value) => value.replace(/\\/g, '/').toLowerCase());
1411
+ const appOwnedPathCount = normalizedPaths.filter((value) => APP_OWNED_PATH_PATTERN.test(value)).length;
1412
+ const libraryPathCount = normalizedPaths.filter((value) => {
1413
+ return value.includes('/node_modules/')
1414
+ || value.includes('@resolveio/client-lib')
1415
+ || value.includes('@resolveio/server-lib')
1416
+ || value.includes('/resolveio-client-lib/')
1417
+ || value.includes('/resolveio-server-lib/')
1418
+ || value.includes('/aicoder/templates/base_');
1419
+ }).length;
1420
+
1421
+ const shouldBlock = (libraryPathCount > 0 && appOwnedPathCount === 0)
1422
+ || (markerHits >= 2 && appOwnedPathCount === 0)
1423
+ || (markerHits >= 1 && libraryPathCount >= 2);
1424
+ if (!shouldBlock) {
1425
+ return {blocked: false, evidence};
1426
+ }
1427
+
1428
+ const compactEvidence = Array.from(new Set([
1429
+ ...evidence,
1430
+ ...(libraryPathCount ? [`library_paths=${libraryPathCount}`] : []),
1431
+ ...(appOwnedPathCount ? [`app_paths=${appOwnedPathCount}`] : [])
1432
+ ])).slice(0, 8);
1433
+ return {
1434
+ blocked: true,
1435
+ reason: 'Root cause appears to be in shared ResolveIO libraries/templates; app-level auto-fix was blocked.',
1436
+ evidence: compactEvidence
1437
+ };
1438
+ }
1439
+
1440
+ private async sendLibraryIssueNotice(log: ErrorAutoFixLogModel, reason: string, evidence: string[]): Promise<void> {
1441
+ const recipients = this.getEscalationRecipients();
1442
+ if (!recipients.length || !log?._id) {
1443
+ return;
1444
+ }
1445
+
1446
+ const identifier = log.autofix_error_count_string || log._id;
1447
+ const subject = `ResolveIO AutoFix Blocked (Library): ${identifier}`;
1448
+ const body = [
1449
+ 'Auto-fix was blocked because the issue appears library-owned, so no autonomous patch was attempted.',
1450
+ '',
1451
+ `Reason: ${reason}`,
1452
+ evidence.length ? `Evidence: ${evidence.join(', ')}` : '',
1453
+ `Issue Log ID: ${log._id}`,
1454
+ `Issue Counter: ${log.autofix_error_count_string || 'n/a'}`,
1455
+ `Client: ${log.client_name || log.id_client || 'unknown'}`,
1456
+ `Source App: ${log.source_app || 'unknown'}`,
1457
+ `Environment: ${log.source_environment || log.openai_environment || 'unknown'}`,
1458
+ `Issue Hash: ${log.issue_hash || log.email_hash || 'n/a'}`,
1459
+ '',
1460
+ 'Manual library release/fix is required. App-level auto-fix remains blocked for this issue to protect credits.'
1461
+ ].filter(Boolean).join('\n');
1462
+
1463
+ const methodManager = ResolveIOServer.getMainServer().getMethodManager();
1464
+ for (const recipient of recipients) {
1465
+ try {
1466
+ await methodManager.sendEmail(recipient, subject, body);
1467
+ }
1468
+ catch (error) {
1469
+ console.error('Failed sending library-issue auto-fix email', {recipient, logId: log._id, error});
1470
+ }
1471
+ }
1472
+ }
1473
+
1474
+ private async blockLibraryOwnedIssue(email: ErrorEmail, log: ErrorAutoFixLogModel, gate: LibraryIssueGateDecision): Promise<AutoFixResult> {
1475
+ const reason = gate.reason || 'Library-owned issue detected.';
1476
+ const details = gate.evidence.length ? `Signals: ${gate.evidence.join(', ')}` : '';
1477
+ const message = [reason, details].filter(Boolean).join(' ');
1478
+ const updated = (await this.updateLog(log._id, {
1479
+ status: 'skipped',
1480
+ ignored: true,
1481
+ last_error: message,
1482
+ last_result_at: new Date()
1483
+ })) || log;
1484
+ await this.notifyCustomerWorkflowStatus('blocked_library_issue', email, updated, {
1485
+ error: reason,
1486
+ notes: details
1487
+ });
1488
+ await this.sendLibraryIssueNotice(updated, reason, gate.evidence);
1489
+ return {
1490
+ status: 'skipped',
1491
+ email,
1492
+ error: message,
1493
+ log: updated,
1494
+ reason: 'library_issue_blocked',
1495
+ summary: updated.plan_summary || email.subject,
1496
+ notes: details
1497
+ };
1498
+ }
1499
+
1500
+ private shouldNotifySkip(reason: string): boolean {
1501
+ return ['in_progress', 'success', 'ignored', 'duplicate', 'pending'].indexOf(reason) === -1;
1502
+ }
1503
+
1504
+ private async isGeneratedAICoderClient(idClient: string): Promise<boolean> {
1505
+ const normalizedClientId = String(idClient || '').trim();
1506
+ if (!normalizedClientId) {
1507
+ return false;
1508
+ }
1509
+
1510
+ const match = await AICoderApps.findOne({
1511
+ client_id: normalizedClientId
1512
+ }, {
1513
+ projection: {
1514
+ _id: 1
1515
+ }
1516
+ });
1517
+
1518
+ return !!match?._id;
1519
+ }
1520
+
1521
+ private buildAICoderWorkflowSummary(
1522
+ stage: 'detected_autofix_enabled' | 'detected_autofix_disabled' | 'completed_success' | 'completed_failed' | 'blocked_library_issue',
1523
+ email: ErrorEmail,
1524
+ log: ErrorAutoFixLogModel,
1525
+ extra?: { error?: string; notes?: string }
1526
+ ): string {
1527
+ return buildGeneratedAppAutoFixSummary(stage, {
1528
+ source_app: String(log?.source_app || email?.sourceApp || '').trim(),
1529
+ environment: String(log?.source_environment || email?.sourceEnvironment || '').trim(),
1530
+ severity: String(log?.severity || email?.severity || '').trim(),
1531
+ error: extra?.error
1532
+ }, extra);
1533
+ }
1534
+
1535
+ private buildResolveIOProjectWorkflowDetails(
1536
+ email: ErrorEmail,
1537
+ log: ErrorAutoFixLogModel,
1538
+ extra?: { error?: string; notes?: string }
1539
+ ): string {
1540
+ return buildResolveIOProjectAutoFixDetails({
1541
+ client_name: log?.client_name || email?.client_name || '',
1542
+ issue_counter: log?.autofix_error_count_string || '',
1543
+ source_app: log?.source_app || email?.sourceApp || '',
1544
+ environment: log?.source_environment || email?.sourceEnvironment || log?.openai_environment || '',
1545
+ severity: log?.severity || email?.severity || '',
1546
+ issue_hash: log?.issue_hash || email?.issueHash || log?.email_hash || '',
1547
+ log_id: log?._id || '',
1548
+ notes: extra?.notes,
1549
+ error: extra?.error
1550
+ });
1551
+ }
1552
+
1553
+ private buildLocalInternalUserCriteria(): Record<string, any> {
1554
+ return {
1555
+ $and: [
1556
+ {
1557
+ is_customer: {
1558
+ $ne: true
1559
+ }
1560
+ },
1561
+ {
1562
+ $nor: [
1563
+ {
1564
+ 'other.id_customer': {
1565
+ $exists: true,
1566
+ $nin: ['', null]
1567
+ }
1568
+ },
1569
+ {
1570
+ id_customer: {
1571
+ $exists: true,
1572
+ $nin: ['', null]
1573
+ }
1574
+ }
1575
+ ]
1576
+ }
1577
+ ]
1578
+ };
1579
+ }
1580
+
1581
+ private buildLocalAdminUserCriteria(): Record<string, any> {
1582
+ return {
1583
+ $or: [
1584
+ {
1585
+ 'roles.super_admin': true
1586
+ },
1587
+ {
1588
+ 'roles.groups.name': {
1589
+ $regex: 'admin',
1590
+ $options: 'i'
1591
+ }
1592
+ },
1593
+ {
1594
+ 'roles.groups.views': {
1595
+ $regex: '^/manage'
1596
+ }
1597
+ },
1598
+ {
1599
+ 'roles.groups.views': '/manage'
1600
+ }
1601
+ ]
1602
+ };
1603
+ }
1604
+
1605
+ private async resolveLocalNotificationUserIds(isGeneratedApp: boolean): Promise<string[]> {
1606
+ const andClauses: Record<string, any>[] = [
1607
+ {
1608
+ active: true
1609
+ },
1610
+ {
1611
+ username: {
1612
+ $exists: true
1613
+ }
1614
+ }
1615
+ ];
1616
+
1617
+ if (isGeneratedApp) {
1618
+ andClauses.push(this.buildLocalInternalUserCriteria());
1619
+ }
1620
+ else {
1621
+ andClauses.push(this.buildLocalAdminUserCriteria());
1622
+ }
1623
+
1624
+ const users = await Users.find({
1625
+ $and: andClauses
1626
+ }, {
1627
+ projection: {
1628
+ _id: 1
1629
+ },
1630
+ limit: MAX_LOCAL_NOTIFICATION_USERS
1631
+ });
1632
+
1633
+ if (!Array.isArray(users) || !users.length) {
1634
+ return [];
1635
+ }
1636
+
1637
+ const unique = new Set<string>();
1638
+ for (const user of users) {
1639
+ const idUser = String(user?._id || '').trim();
1640
+ if (idUser) {
1641
+ unique.add(idUser);
1642
+ }
1643
+ if (unique.size >= MAX_LOCAL_NOTIFICATION_USERS) {
1644
+ break;
1645
+ }
1646
+ }
1647
+ return Array.from(unique);
1648
+ }
1649
+
1650
+ private async notifyCustomerWorkflowStatus(
1651
+ stage: 'detected_autofix_enabled' | 'detected_autofix_disabled' | 'completed_success' | 'completed_failed' | 'blocked_library_issue',
1652
+ email: ErrorEmail,
1653
+ log: ErrorAutoFixLogModel,
1654
+ extra?: { error?: string; notes?: string }
1655
+ ): Promise<void> {
1656
+ const idClient = String(log?.id_client || email?.id_client || '').trim();
1657
+ let isGeneratedApp = false;
1658
+ if (idClient) {
1659
+ isGeneratedApp = await this.isGeneratedAICoderClient(idClient);
1660
+ }
1661
+ else {
1662
+ const resolvedEnvironment = this.resolveEnvironmentForTask(email, log);
1663
+ const app = await this.resolveAutoFixApp(email, log, resolvedEnvironment);
1664
+ isGeneratedApp = !!app;
1665
+ }
1666
+
1667
+ if (!isGeneratedApp && stage !== 'completed_success') {
1668
+ return;
1669
+ }
1670
+
1671
+ const issueKey = String(log?.issue_hash || log?.email_hash || email?.issueHash || email?.hash || log?._id || '').trim();
1672
+ const clientName = String(log?.client_name || email?.client_name || '').trim();
1673
+ const targetPayload: Record<string, any> = {};
1674
+ if (idClient) {
1675
+ targetPayload.target_type = isGeneratedApp ? 'client_internal' : 'client_admins';
1676
+ targetPayload.id_client = idClient;
1677
+ targetPayload.client_name = clientName || undefined;
1678
+ }
1679
+ else {
1680
+ const idUsers = await this.resolveLocalNotificationUserIds(isGeneratedApp);
1681
+ if (!idUsers.length) {
1682
+ return;
1683
+ }
1684
+ targetPayload.target_type = 'users';
1685
+ targetPayload.id_users = idUsers;
1686
+ targetPayload.client_name = clientName || undefined;
1687
+ }
1688
+
1689
+ const basePayload: Record<string, any> = {
1690
+ ...targetPayload,
1691
+ category: 'autofix',
1692
+ source: 'error-auto-fix',
1693
+ source_id: String(log?._id || '').trim() || undefined,
1694
+ dedupe_key: issueKey ? `autofix:${issueKey}:${stage}` : `autofix:${String(log?._id || '').trim()}:${stage}`,
1695
+ metadata: {
1696
+ issue_hash: log?.issue_hash || email?.issueHash || '',
1697
+ log_id: log?._id || '',
1698
+ severity: log?.severity || email?.severity || '',
1699
+ source_app: log?.source_app || email?.sourceApp || '',
1700
+ source_environment: log?.source_environment || email?.sourceEnvironment || '',
1701
+ error: extra?.error || '',
1702
+ notes: extra?.notes || ''
1703
+ }
1704
+ };
1705
+
1706
+ let payload: Record<string, any> = {};
1707
+ if (isGeneratedApp) {
1708
+ if (stage === 'detected_autofix_enabled') {
1709
+ payload = {
1710
+ title: 'Issue detected. Auto-fix is in progress.',
1711
+ message: 'We detected an issue in your app and started an automatic fix workflow. We will notify you when it is complete.',
1712
+ severity: 'info',
1713
+ details: this.buildAICoderWorkflowSummary(stage, email, log, extra)
1714
+ };
1715
+ }
1716
+ else if (stage === 'detected_autofix_disabled') {
1717
+ payload = {
1718
+ title: 'Issue detected. Auto-fix is disabled.',
1719
+ message: 'We detected an issue, but automatic fixing is not enabled. Please ask the AI assistant to investigate this issue.',
1720
+ severity: 'warning',
1721
+ details: this.buildAICoderWorkflowSummary(stage, email, log, extra)
1722
+ };
1723
+ }
1724
+ else if (stage === 'completed_success') {
1725
+ payload = {
1726
+ title: 'Auto-fix completed successfully.',
1727
+ message: 'Your issue was fixed automatically and the update was published.',
1728
+ severity: 'success',
1729
+ details: this.buildAICoderWorkflowSummary(stage, email, log, extra)
1730
+ };
1731
+ }
1732
+ else if (stage === 'blocked_library_issue') {
1733
+ payload = {
1734
+ title: 'Issue requires a platform library update.',
1735
+ message: 'We detected this issue in shared platform libraries, so app auto-fix was stopped. Our engineering team has been notified to ship a library-level fix.',
1736
+ severity: 'warning',
1737
+ details: this.buildAICoderWorkflowSummary(stage, email, log, extra)
1738
+ };
1739
+ }
1740
+ else {
1741
+ payload = {
1742
+ title: 'Auto-fix needs manual review.',
1743
+ message: 'We could not complete an automatic fix for this issue. Manual follow-up is required.',
1744
+ severity: 'warning',
1745
+ details: this.buildAICoderWorkflowSummary(stage, email, log, extra)
1746
+ };
1747
+ }
1748
+ }
1749
+ else {
1750
+ payload = {
1751
+ title: 'ResolveIO project auto-fix completed',
1752
+ message: `A fix for ${String(log?.source_app || email?.sourceApp || 'the project').trim()} completed successfully.`,
1753
+ severity: 'success',
1754
+ details: this.buildResolveIOProjectWorkflowDetails(email, log, extra)
1755
+ };
1756
+ }
1757
+
1758
+ try {
1759
+ await ResolveIOServer.getMainServer().getMethodManager().callMethod('createCustomerNotification', {
1760
+ ...basePayload,
1761
+ ...payload
1762
+ });
1763
+ }
1764
+ catch (error) {
1765
+ this.debugLog('Failed to create customer auto-fix notification', {
1766
+ logId: log?._id,
1767
+ stage,
1768
+ error: error?.message || error
1769
+ });
1770
+ }
1771
+ }
1772
+
1773
+ private async delay(ms: number): Promise<void> {
1774
+ // eslint-disable-next-line no-restricted-syntax
1775
+ await new Promise(resolve => setTimeout(resolve, ms));
1776
+ }
1777
+
1778
+ public async reportDevError(options: DevErrorReportOptions): Promise<void> {
1779
+ const subject = (options?.subject || '').trim() || 'ResolveIO alert';
1780
+ const body = options?.body || '';
1781
+ const sourceApp = options?.sourceApp || ResolveIOServer.getClientName() || 'resolveio-server';
1782
+ const environment = options?.environment || process.env.NODE_ENV || '';
1783
+ const severity = options?.severity || 'infrastructure';
1784
+ const fingerprint = (options?.fingerprint || createHash('sha256').update(`${sourceApp}:${environment || 'env'}:${subject}`).digest('hex')).trim();
1785
+ const metadata = options?.metadata && Object.keys(options.metadata).length ? options.metadata : undefined;
1786
+
1787
+ if (!this.isReady()) {
1788
+ console.warn('AutoFix manager not ready for dev alert', {subject, environment});
1789
+ return;
1790
+ }
1791
+
1792
+ try {
1793
+ await this.ingestErrorReport({
1794
+ sourceApp,
1795
+ environment,
1796
+ message: subject,
1797
+ stack: body,
1798
+ severity,
1799
+ metadata,
1800
+ fingerprint,
1801
+ idempotencyKey: options?.idempotencyKey,
1802
+ clientId: options?.clientId,
1803
+ clientName: options?.clientName,
1804
+ clientSlug: options?.clientSlug,
1805
+ reportedBy: 'resolveio-server'
1806
+ });
1807
+ }
1808
+ catch (err) {
1809
+ console.error('Failed to log developer alert', subject, err);
1810
+ }
1811
+ }
1812
+
1813
+ public async ingestErrorReport(report: ErrorReportPayload): Promise<ErrorReportIngestResult> {
1814
+ if (!this.isReady()) {
1815
+ throw new Error('ErrorAutoFixManager is not enabled.');
1816
+ }
1817
+
1818
+ if (!report || !report.sourceApp || !report.message) {
1819
+ throw new Error('Invalid error report payload.');
1820
+ }
1821
+
1822
+ const normalizedReport: ErrorReportPayload = {
1823
+ ...report,
1824
+ message: (report.message || '').toString(),
1825
+ stack: report.stack ? report.stack.toString() : undefined,
1826
+ metadata: report.metadata || undefined,
1827
+ context: report.context || undefined,
1828
+ attachments: Array.isArray(report.attachments) ? report.attachments : undefined,
1829
+ reportedAt: report.reportedAt instanceof Date && !Number.isNaN(report.reportedAt.getTime()) ? report.reportedAt : new Date()
1830
+ };
1831
+
1832
+ const email = this.buildEmailFromReport(normalizedReport);
1833
+ email.reportedAt = normalizedReport.reportedAt;
1834
+ email.openai_environment = normalizedReport.clientRepo || email.openai_environment;
1835
+ email.fromAddress = this.extractEmailAddress(normalizedReport.clientEmail || normalizedReport.reportedBy || email.from || '');
1836
+ if (!email.fromAddress && normalizedReport.clientSlug) {
1837
+ email.fromAddress = `${normalizedReport.clientSlug}@errors.resolveio.com`;
1838
+ }
1839
+
1840
+ const rawHash = normalizedReport.idempotencyKey ? normalizedReport.idempotencyKey.trim() : '';
1841
+ email.rawHash = rawHash || this.generateRawEmailHash(email);
1842
+ const fingerprint = normalizedReport.fingerprint ? normalizedReport.fingerprint.trim() : '';
1843
+ if (fingerprint) {
1844
+ email.issueHash = fingerprint;
1845
+ email.hash = fingerprint;
1846
+ }
1847
+ else {
1848
+ email.issueHash = this.generateIssueHash(email);
1849
+ email.hash = email.issueHash || email.rawHash;
1850
+ }
1851
+
1852
+ const client = await this.resolveClientForReport(normalizedReport);
1853
+ const autoOptimizeEnabled = await this.resolveAutoOptimizeEnabled();
1854
+ const reservation = await this.reserveLog(email, client, autoOptimizeEnabled);
1855
+
1856
+ if (!reservation.proceed) {
1857
+ if (reservation.reason && this.shouldNotifySkip(reservation.reason)) {
1858
+ await this.notify({
1859
+ status: 'skipped',
1860
+ email,
1861
+ reason: reservation.reason,
1862
+ error: `Skipped auto-fix (${reservation.reason}).`,
1863
+ log: reservation.log
1864
+ });
1865
+ }
1866
+
1867
+ return {
1868
+ status: reservation.reason === 'duplicate' ? 'duplicate' : (reservation.reason as any) || 'skipped',
1869
+ reason: reservation.reason,
1870
+ log: reservation.log
1871
+ };
1872
+ }
1873
+
1874
+ email.logId = reservation.log._id;
1875
+ const classification = this.classifyEmail(email);
1876
+ await this.updateLog(email.logId, {
1877
+ id_client: client?._id,
1878
+ client_name: client?.name,
1879
+ from_email: email.fromAddress,
1880
+ source_type: classification.source,
1881
+ classification_reason: classification.reason || '',
1882
+ openai_environment: email.openai_environment || reservation.log.openai_environment || this.determineOpenAIEnvironment(client),
1883
+ source_app: email.sourceApp,
1884
+ source_environment: email.sourceEnvironment,
1885
+ severity: email.severity,
1886
+ reported_by: email.reportedBy || email.fromAddress,
1887
+ report_metadata: email.reportMetadata,
1888
+ report_context: email.reportContext,
1889
+ attachments: email.attachments,
1890
+ last_reported_at: email.reportedAt || new Date()
1891
+ });
1892
+
1893
+ if (classification.skip) {
1894
+ const updated = await this.updateLog(email.logId, {
1895
+ status: 'skipped',
1896
+ ignored: !!classification.markIgnored,
1897
+ last_error: classification.reason || 'Ignored non-app alert',
1898
+ last_result_at: new Date()
1899
+ });
1900
+
1901
+ if (classification.notify) {
1902
+ await this.notify({
1903
+ status: 'skipped',
1904
+ email,
1905
+ error: classification.reason,
1906
+ log: updated,
1907
+ reason: 'classified'
1908
+ });
1909
+ }
1910
+
1911
+ return {
1912
+ status: 'skipped',
1913
+ reason: classification.reason,
1914
+ log: updated
1915
+ };
1916
+ }
1917
+
1918
+ if (!autoOptimizeEnabled) {
1919
+ const queued = await this.updateLog(email.logId, {
1920
+ status: 'pending'
1921
+ });
1922
+ await this.notifyCustomerWorkflowStatus('detected_autofix_disabled', email, queued || reservation.log);
1923
+
1924
+ return {
1925
+ status: 'queued',
1926
+ log: queued
1927
+ };
1928
+ }
1929
+
1930
+ await this.notifyCustomerWorkflowStatus('detected_autofix_enabled', email, reservation.log);
1931
+ const result = await this.processEmail(email, reservation.log);
1932
+ await this.notify(result);
1933
+ const finalLog = result.log || (await ErrorAutoFixLogs.findOne({_id: email.logId}));
1934
+ return {
1935
+ status: result.status,
1936
+ reason: result.reason,
1937
+ log: finalLog || result.log
1938
+ };
1939
+ }
1940
+
1941
+ public async runLog(logId: string): Promise<AutoFixResult> {
1942
+ if (!this.config.enabled) {
1943
+ throw new Error('Auto-fix manager disabled.');
1944
+ }
1945
+
1946
+ const log = await ErrorAutoFixLogs.findOne({_id: logId});
1947
+ if (!log) {
1948
+ throw new Error('Auto-fix log not found.');
1949
+ }
1950
+
1951
+ if (log.ignored) {
1952
+ throw new Error('Auto-fix log is ignored.');
1953
+ }
1954
+
1955
+ if (log.status === 'in_progress') {
1956
+ throw new Error('Auto-fix workflow already running.');
1957
+ }
1958
+
1959
+ if (!log.body) {
1960
+ throw new Error('Auto-fix log is missing email body.');
1961
+ }
1962
+
1963
+ const now = new Date();
1964
+ const email = this.buildEmailFromLog(log);
1965
+ const classification = this.classifyEmail(email);
1966
+ if (classification.skip && classification.markIgnored) {
1967
+ const ignoredLog = await this.updateLog(logId, {
1968
+ status: 'skipped',
1969
+ ignored: true,
1970
+ last_error: classification.reason || 'Mongo-origin error ignored for auto-fix',
1971
+ last_result_at: now
1972
+ });
1973
+ return {
1974
+ status: 'skipped',
1975
+ email,
1976
+ reason: 'classified',
1977
+ error: classification.reason || 'Mongo-origin error ignored for auto-fix',
1978
+ log: ignoredLog
1979
+ };
1980
+ }
1981
+
1982
+ const inProgressLog = (await ErrorAutoFixLogs.findOneAndUpdate({_id: logId}, {
1983
+ $inc: {
1984
+ attempt_count: 1
1985
+ },
1986
+ $set: {
1987
+ status: 'in_progress',
1988
+ last_attempt_at: now,
1989
+ last_error: '',
1990
+ ignored: false
1991
+ }
1992
+ }, {
1993
+ returnDocument: 'after'
1994
+ })) || log;
1995
+
1996
+ const result = await this.processEmail(email, inProgressLog);
1997
+ await this.notify(result);
1998
+ return result;
1999
+ }
2000
+
2001
+ private buildEmailFromLog(log: ErrorAutoFixLogModel): ErrorEmail {
2002
+ return {
2003
+ key: log.message_id || log._id,
2004
+ subject: log.subject,
2005
+ body: log.body || '',
2006
+ messageId: log.message_id,
2007
+ hash: log.issue_hash || log.email_hash,
2008
+ rawHash: log.email_hash,
2009
+ issueHash: log.issue_hash,
2010
+ raw: log.body || '',
2011
+ id_client: log.id_client,
2012
+ client_name: log.client_name,
2013
+ fromAddress: log.from_email,
2014
+ source_type: log.source_type || 'app',
2015
+ classification_reason: log.classification_reason,
2016
+ openai_environment: log.openai_environment,
2017
+ sourceApp: log.source_app,
2018
+ sourceEnvironment: log.source_environment,
2019
+ severity: log.severity,
2020
+ reportedBy: log.reported_by,
2021
+ reportMetadata: log.report_metadata,
2022
+ reportContext: log.report_context,
2023
+ attachments: log.attachments,
2024
+ reportedAt: log.last_reported_at
2025
+ };
2026
+ }
2027
+
2028
+ private async processEmail(email: ErrorEmail, log: ErrorAutoFixLogModel): Promise<AutoFixResult> {
2029
+ const libraryIssueGate = this.evaluateLibraryIssueGate(email, log);
2030
+ if (libraryIssueGate.blocked) {
2031
+ return await this.blockLibraryOwnedIssue(email, log, libraryIssueGate);
2032
+ }
2033
+
2034
+ if (this.config.dashboardWorkflowEnabled) {
2035
+ const dashboardResult = await this.dispatchDashboardWorkflow(email, log);
2036
+ if (dashboardResult.status === 'in_progress' || dashboardResult.status === 'success') {
2037
+ return dashboardResult;
2038
+ }
2039
+ if (!this.config.dashboardFallbackToGithub) {
2040
+ return dashboardResult;
2041
+ }
2042
+
2043
+ this.debugLog('Dashboard workflow unavailable; falling back to GitHub workflow.', {
2044
+ logId: log?._id,
2045
+ reason: dashboardResult.reason,
2046
+ error: dashboardResult.error
2047
+ });
2048
+ }
2049
+
2050
+ return await this.dispatchGithubWorkflow(email, log);
2051
+ }
2052
+
2053
+ private shouldFallbackDashboardMethod(error: any): boolean {
2054
+ const message = `${error?.message || ''}`.toLowerCase();
2055
+ return message.includes('not found')
2056
+ || message.includes('worker dispatch unavailable')
2057
+ || message.includes('no worker')
2058
+ || message.includes('unavailable for aidashboard');
2059
+ }
2060
+
2061
+ private async createDashboardJob(payload: Record<string, any>): Promise<AIDashboardJob> {
2062
+ const methodManager = ResolveIOServer.getMainServer().getMethodManager();
2063
+ try {
2064
+ return await methodManager.callMethod('aiDashboardCreateJob', payload);
2065
+ }
2066
+ catch (error) {
2067
+ if (!this.shouldFallbackDashboardMethod(error)) {
2068
+ throw error;
2069
+ }
2070
+ }
2071
+
2072
+ const manager: any = ResolveIOServer['AIDashboardManager'];
2073
+ if (manager && manager.isEnabled && manager.isEnabled()) {
2074
+ return await manager.createJob(payload);
2075
+ }
2076
+
2077
+ throw new Error('AI Dashboard manager is not available.');
2078
+ }
2079
+
2080
+ private async waitForDashboardJobStop(jobId: string, timeoutMs: number): Promise<void> {
2081
+ const methodManager = ResolveIOServer.getMainServer().getMethodManager();
2082
+ try {
2083
+ await methodManager.callMethod('aiDashboardWaitForJobStop', jobId, timeoutMs);
2084
+ return;
2085
+ }
2086
+ catch (error) {
2087
+ if (!this.shouldFallbackDashboardMethod(error)) {
2088
+ throw error;
2089
+ }
2090
+ }
2091
+
2092
+ const manager: any = ResolveIOServer['AIDashboardManager'];
2093
+ if (manager && manager.isEnabled && manager.isEnabled()) {
2094
+ await manager.waitForJobStop(jobId, timeoutMs);
2095
+ return;
2096
+ }
2097
+
2098
+ throw new Error('AI Dashboard manager is not available.');
2099
+ }
2100
+
2101
+ private async isDashboardJobRunning(jobId: string): Promise<boolean> {
2102
+ const methodManager = ResolveIOServer.getMainServer().getMethodManager();
2103
+ try {
2104
+ const running = await methodManager.callMethod('aiDashboardIsJobRunning', jobId);
2105
+ return !!running;
2106
+ }
2107
+ catch (error) {
2108
+ if (!this.shouldFallbackDashboardMethod(error)) {
2109
+ throw error;
2110
+ }
2111
+ }
2112
+
2113
+ const manager: any = ResolveIOServer['AIDashboardManager'];
2114
+ if (manager && manager.isEnabled && manager.isEnabled()) {
2115
+ return !!manager.isJobRunning(jobId);
2116
+ }
2117
+
2118
+ throw new Error('AI Dashboard manager is not available.');
2119
+ }
2120
+
2121
+ private async resolveAutoFixApp(email: ErrorEmail, log: ErrorAutoFixLogModel, resolvedEnvironment: string): Promise<AICoderAppModel | null> {
2122
+ const clientId = (email.id_client || log.id_client || '').trim();
2123
+ if (clientId) {
2124
+ const byClientId = await AICoderApps.findOne({client_id: clientId}, {
2125
+ sort: {
2126
+ updatedAt: -1,
2127
+ createdAt: -1
2128
+ }
2129
+ });
2130
+ if (byClientId) {
2131
+ return byClientId;
2132
+ }
2133
+ }
2134
+
2135
+ const lookupClientCandidates = [
2136
+ (email.client_name || '').trim(),
2137
+ (log.client_name || '').trim()
2138
+ ].filter(Boolean);
2139
+ for (const candidate of lookupClientCandidates) {
2140
+ const escaped = this.escapeRegex(candidate);
2141
+ const clientDoc = await Clients.findOne({
2142
+ $or: [
2143
+ {name: {$regex: `^${escaped}$`, $options: 'i'}},
2144
+ {demo_name: {$regex: `^${escaped}$`, $options: 'i'}},
2145
+ {repo: {$regex: `^${escaped}$`, $options: 'i'}}
2146
+ ]
2147
+ });
2148
+ if (!clientDoc?._id) {
2149
+ continue;
2150
+ }
2151
+ const byClient = await AICoderApps.findOne({client_id: clientDoc._id}, {
2152
+ sort: {
2153
+ updatedAt: -1,
2154
+ createdAt: -1
2155
+ }
2156
+ });
2157
+ if (byClient) {
2158
+ return byClient;
2159
+ }
2160
+ }
2161
+
2162
+ const environmentRepo = (resolvedEnvironment || '').trim();
2163
+ if (environmentRepo) {
2164
+ const escapedRepo = this.escapeRegex(environmentRepo);
2165
+ const byRepo = await AICoderApps.findOne({
2166
+ repo: {
2167
+ $regex: `^${escapedRepo}$`,
2168
+ $options: 'i'
2169
+ }
2170
+ }, {
2171
+ sort: {
2172
+ updatedAt: -1,
2173
+ createdAt: -1
2174
+ }
2175
+ });
2176
+ if (byRepo) {
2177
+ return byRepo;
2178
+ }
2179
+ }
2180
+
2181
+ const appNameCandidates = [
2182
+ (email.sourceApp || '').trim(),
2183
+ (log.source_app || '').trim()
2184
+ ].filter(Boolean);
2185
+ for (const candidate of appNameCandidates) {
2186
+ const escaped = this.escapeRegex(candidate);
2187
+ const bySlugOrName = await AICoderApps.findOne({
2188
+ $or: [
2189
+ {slug: {$regex: `^${escaped}$`, $options: 'i'}},
2190
+ {name: {$regex: `^${escaped}$`, $options: 'i'}}
2191
+ ]
2192
+ }, {
2193
+ sort: {
2194
+ updatedAt: -1,
2195
+ createdAt: -1
2196
+ }
2197
+ });
2198
+ if (bySlugOrName) {
2199
+ return bySlugOrName;
2200
+ }
2201
+ }
2202
+
2203
+ return null;
2204
+ }
2205
+
2206
+ private buildDashboardAutoFixTitle(email: ErrorEmail, log: ErrorAutoFixLogModel, app: AICoderAppModel): string {
2207
+ const identifier = log.autofix_error_count_string || (log._id || '').slice(-6) || 'error';
2208
+ const source = (email.sourceApp || log.source_app || app?.name || 'application').trim();
2209
+ return `Auto-fix ${identifier}: ${source}`;
2210
+ }
2211
+
2212
+ private buildDashboardAutoFixDescription(email: ErrorEmail, log: ErrorAutoFixLogModel, app: AICoderAppModel): string {
2213
+ const normalizedBody = this.normalizeEmailText(email.body || log.body || '', 15000);
2214
+ const metadata = log.report_metadata && Object.keys(log.report_metadata).length
2215
+ ? JSON.stringify(log.report_metadata, null, 2)
2216
+ : '';
2217
+ const context = log.report_context && Object.keys(log.report_context).length
2218
+ ? JSON.stringify(log.report_context, null, 2)
2219
+ : '';
2220
+ const attachments = Array.isArray(log.attachments) ? log.attachments : [];
2221
+ const attachmentLines = attachments.length
2222
+ ? attachments.map(att => `- ${(att?.name || 'attachment').trim()} ${att?.url ? `(${att.url})` : ''}`.trim()).join('\n')
2223
+ : '(none)';
2224
+ const lines: string[] = [
2225
+ 'Autonomous production error remediation request.',
2226
+ '',
2227
+ 'Hard requirements:',
2228
+ '1. Before changing code, query the project MongoDB diagnostics logs for the latest context.',
2229
+ '2. Query `error-autofix-logs` using `_id`, `issue_hash`, and `email_hash` from this task, then use the newest matching records.',
2230
+ '3. If a previous dashboard job id is provided, query `ai-development-jobs` by that `_id` and review recent failure logs before retrying.',
2231
+ '4. Treat `.dashboard-output/build-*.log` as primary build evidence, and `.build-output/build-*.log` as retained history when diagnosing failures.',
2232
+ '5. Reproduce and fix the reported issue with the smallest safe code change.',
2233
+ '6. Keep query/result behavior backward compatible unless explicitly required by the bug fix.',
2234
+ '7. Run project build checks and continue iterating until the build is green.',
2235
+ '8. Ensure there are no new lint/type/runtime regressions introduced by the fix.',
2236
+ '9. Finish by publishing the passing fix to the default branch and deploying artifacts.',
2237
+ '',
2238
+ `App: ${app?.name || app?._id || 'unknown'}`,
2239
+ `Repo: ${app?.repo || 'unknown'}`,
2240
+ `Environment: ${email.sourceEnvironment || log.source_environment || 'unknown'}`,
2241
+ `Severity: ${email.severity || log.severity || 'error'}`,
2242
+ `Fingerprint: ${email.issueHash || log.issue_hash || log.email_hash || 'n/a'}`,
2243
+ `Error Log Id: ${String(log?._id || '').trim() || 'n/a'}`,
2244
+ `Previous Dashboard Job Id: ${String(log?.openai_task_id || '').trim() || 'n/a'}`,
2245
+ '',
2246
+ 'Reported error payload:',
2247
+ '```',
2248
+ normalizedBody || '(empty error body)',
2249
+ '```',
2250
+ '',
2251
+ 'Attachments:',
2252
+ attachmentLines
2253
+ ];
2254
+ if (metadata) {
2255
+ lines.push('', 'Metadata JSON:', '```json', metadata, '```');
2256
+ }
2257
+ if (context) {
2258
+ lines.push('', 'Context JSON:', '```json', context, '```');
2259
+ }
2260
+ return lines.join('\n');
2261
+ }
2262
+
2263
+ private evaluateDashboardPublishOutcome(job: AIDashboardJob): { success: boolean; branchName?: string; message: string } {
2264
+ const logEntries = Array.isArray(job?.log) ? job.log : [];
2265
+ const lastMatch = (predicate): string => {
2266
+ for (let i = logEntries.length - 1; i >= 0; i -= 1) {
2267
+ if (predicate(logEntries[i] || '')) {
2268
+ return logEntries[i];
2269
+ }
2270
+ }
2271
+ return '';
2272
+ };
2273
+
2274
+ const failureEntry = lastMatch(entry => /publish failed|artifact publish failed|deploy failed/i.test(entry || ''));
2275
+ if (failureEntry) {
2276
+ return {
2277
+ success: false,
2278
+ message: failureEntry
2279
+ };
2280
+ }
2281
+
2282
+ const publishEntry = lastMatch(entry => /Published build to /i.test(entry || ''));
2283
+ if (publishEntry) {
2284
+ const branchMatch = publishEntry.match(/\(([^()]+)\)\.?$/);
2285
+ return {
2286
+ success: true,
2287
+ branchName: (branchMatch && branchMatch[1]) ? branchMatch[1].trim() : undefined,
2288
+ message: publishEntry
2289
+ };
2290
+ }
2291
+
2292
+ const skippedEntry = lastMatch(entry => /Publish skipped/i.test(entry || ''));
2293
+ if (skippedEntry) {
2294
+ return {
2295
+ success: !this.config.dashboardPublishRequired,
2296
+ message: skippedEntry
2297
+ };
2298
+ }
2299
+
2300
+ return {
2301
+ success: !this.config.dashboardPublishRequired,
2302
+ message: 'Dashboard job completed without a publish confirmation log entry.'
2303
+ };
2304
+ }
2305
+
2306
+ private queueDashboardMonitor(log: ErrorAutoFixLogModel, email: ErrorEmail, jobId: string, effectiveEnvironment: string): void {
2307
+ const logId = (log?._id || '').trim();
2308
+ const normalizedJobId = (jobId || '').trim();
2309
+ if (!logId || !normalizedJobId) {
2310
+ return;
2311
+ }
2312
+ if (this.dashboardMonitors.has(logId)) {
2313
+ return;
2314
+ }
2315
+
2316
+ const monitor = (async () => {
2317
+ try {
2318
+ await this.monitorDashboardAutoFixJob(log, email, normalizedJobId, effectiveEnvironment);
2319
+ }
2320
+ catch (error) {
2321
+ console.error('Auto-fix dashboard monitor failed', {logId, jobId: normalizedJobId, error});
2322
+ }
2323
+ finally {
2324
+ this.dashboardMonitors.delete(logId);
2325
+ }
2326
+ })();
2327
+
2328
+ this.dashboardMonitors.set(logId, monitor);
2329
+ }
2330
+
2331
+ private async monitorDashboardAutoFixJob(log: ErrorAutoFixLogModel, email: ErrorEmail, jobId: string, effectiveEnvironment: string): Promise<void> {
2332
+ const logId = (log?._id || '').trim();
2333
+ if (!logId) {
2334
+ return;
2335
+ }
2336
+
2337
+ const fail = async (message: string): Promise<void> => {
2338
+ const failedLog = (await this.updateLog(logId, {
2339
+ status: 'failed',
2340
+ last_error: message,
2341
+ last_result_at: new Date(),
2342
+ openai_task_id: jobId,
2343
+ openai_environment: effectiveEnvironment
2344
+ })) || log;
2345
+ await this.notifyCustomerWorkflowStatus('completed_failed', email, failedLog, {error: message});
2346
+ await this.notify({
2347
+ status: 'failed',
2348
+ email,
2349
+ error: message,
2350
+ log: failedLog,
2351
+ summary: failedLog.plan_summary || email.subject
2352
+ });
2353
+ };
2354
+
2355
+ try {
2356
+ await this.waitForDashboardJobStop(jobId, this.config.dashboardWaitTimeoutMs);
2357
+ }
2358
+ catch (error) {
2359
+ const message = error?.message || 'Failed while waiting for dashboard job completion.';
2360
+ await fail(message);
2361
+ return;
2362
+ }
2363
+
2364
+ let isRunning = false;
2365
+ try {
2366
+ isRunning = await this.isDashboardJobRunning(jobId);
2367
+ }
2368
+ catch (error) {
2369
+ const message = error?.message || 'Unable to confirm dashboard job completion state.';
2370
+ await fail(message);
2371
+ return;
2372
+ }
2373
+
2374
+ if (isRunning) {
2375
+ await fail(`Dashboard job ${jobId} timed out before completion.`);
2376
+ return;
2377
+ }
2378
+
2379
+ const job = await AIDashboardJobs.findOne({_id: jobId}) as AIDashboardJob | null;
2380
+ if (!job) {
2381
+ await fail(`Dashboard job ${jobId} not found.`);
2382
+ return;
2383
+ }
2384
+
2385
+ if (job.phase !== 'COMPLETE' || job.paused) {
2386
+ await fail(`Dashboard job ${jobId} ended in phase ${job.phase || 'unknown'}${job.paused ? ' (paused)' : ''}.`);
2387
+ return;
2388
+ }
2389
+
2390
+ const publishOutcome = this.evaluateDashboardPublishOutcome(job);
2391
+ if (!publishOutcome.success) {
2392
+ await fail(publishOutcome.message);
2393
+ return;
2394
+ }
2395
+
2396
+ const fallbackBranch = (String(this._serverConfig?.['GITHUB_DEFAULT_BRANCH'] || process.env.GITHUB_DEFAULT_BRANCH || 'main').trim() || 'main');
2397
+ const branchName = publishOutcome.branchName || fallbackBranch;
2398
+ const successLog = (await this.updateLog(logId, {
2399
+ status: 'success',
2400
+ branch_name: branchName,
2401
+ pr_url: '',
2402
+ last_error: '',
2403
+ last_result_at: new Date(),
2404
+ openai_task_id: jobId,
2405
+ openai_environment: effectiveEnvironment
2406
+ })) || log;
2407
+ await this.notifyCustomerWorkflowStatus('completed_success', email, successLog, {notes: publishOutcome.message});
2408
+
2409
+ await this.notify({
2410
+ status: 'success',
2411
+ email,
2412
+ branchName,
2413
+ log: successLog,
2414
+ summary: successLog.plan_summary || email.subject,
2415
+ notes: publishOutcome.message
2416
+ });
2417
+ }
2418
+
2419
+ private async dispatchDashboardWorkflow(email: ErrorEmail, log: ErrorAutoFixLogModel): Promise<AutoFixResult> {
2420
+ const fail = async (
2421
+ status: 'failed' | 'skipped',
2422
+ reason: string,
2423
+ message: string
2424
+ ): Promise<AutoFixResult> => {
2425
+ const updated = (await this.updateLog(log._id, {
2426
+ status,
2427
+ last_error: message,
2428
+ last_result_at: new Date()
2429
+ })) || log;
2430
+ return {
2431
+ status,
2432
+ email,
2433
+ error: message,
2434
+ log: updated,
2435
+ reason
2436
+ };
2437
+ };
2438
+
2439
+ const openaiEnvironment = this.resolveEnvironmentForTask(email, log);
2440
+ const app = await this.resolveAutoFixApp(email, log, openaiEnvironment);
2441
+ if (!app?._id) {
2442
+ return await fail('skipped', 'dashboard_app_not_found', 'No AI Coder app found for this error log.');
2443
+ }
2444
+
2445
+ const repo = (app.repo || '').trim();
2446
+ if (!repo) {
2447
+ return await fail('skipped', 'dashboard_repo_missing', 'Resolved AI Coder app is missing repo configuration.');
2448
+ }
2449
+
2450
+ const effectiveEnvironment = repo;
2451
+ const emailHash = email.issueHash || email.hash || this.generateIssueHash(email);
2452
+ const rawHash = email.rawHash || this.generateRawEmailHash(email);
2453
+ const title = this.buildDashboardAutoFixTitle(email, log, app);
2454
+ const description = this.buildDashboardAutoFixDescription(email, log, app);
2455
+
2456
+ let job: AIDashboardJob;
2457
+ try {
2458
+ job = await this.createDashboardJob({
2459
+ project: app._id,
2460
+ title,
2461
+ description,
2462
+ repo,
2463
+ path: app.git_local_path || undefined,
2464
+ projectRoot: app.project_root || undefined
2465
+ });
2466
+ }
2467
+ catch (error) {
2468
+ const message = error?.message || 'Unable to enqueue dashboard auto-fix job.';
2469
+ return await fail('failed', 'dashboard_job_create_failed', message);
2470
+ }
2471
+
2472
+ const jobId = (job?._id || '').trim();
2473
+ if (!jobId) {
2474
+ return await fail('failed', 'dashboard_job_missing_id', 'Dashboard job created without an id.');
2475
+ }
2476
+
2477
+ const queuedLog = (await this.updateLog(log._id, {
2478
+ status: 'in_progress',
2479
+ last_attempt_at: new Date(),
2480
+ branch_name: '',
2481
+ pr_url: '',
2482
+ last_error: '',
2483
+ openai_environment: effectiveEnvironment,
2484
+ issue_hash: emailHash,
2485
+ email_hash: rawHash,
2486
+ openai_task_id: jobId,
2487
+ plan_summary: title,
2488
+ plan_body: description
2489
+ })) || log;
2490
+ this.queueDashboardMonitor(queuedLog, email, jobId, effectiveEnvironment);
2491
+
2492
+ return {
2493
+ status: 'in_progress',
2494
+ email,
2495
+ log: queuedLog,
2496
+ reason: 'dashboard_job_queued',
2497
+ summary: title,
2498
+ notes: `Dashboard job ${jobId} queued for autonomous build/fix/publish/deploy.`
2499
+ };
2500
+ }
2501
+
2502
+ private async dispatchGithubWorkflow(email: ErrorEmail, log: ErrorAutoFixLogModel): Promise<AutoFixResult> {
2503
+ const openaiEnvironment = this.resolveEnvironmentForTask(email, log);
2504
+ const fallbackEnv = this.config.githubOwner && this.config.githubRepo ? `${this.config.githubOwner}/${this.config.githubRepo}` : '';
2505
+ const resolvedEnvironment = (openaiEnvironment || fallbackEnv || '').trim();
2506
+ const repoTarget = this.parseRepoTarget(resolvedEnvironment) || this.parseRepoTarget(fallbackEnv);
2507
+
2508
+ this.debugLog('Starting GitHub PR workflow.', {
2509
+ emailKey: email.key,
2510
+ logId: log._id,
2511
+ openaiEnvironment: resolvedEnvironment,
2512
+ repoTarget
2513
+ });
2514
+
2515
+ if (!this.config.githubToken) {
2516
+ const updated = (await this.updateLog(log._id, {
2517
+ status: 'skipped',
2518
+ last_error: 'GitHub token not configured',
2519
+ last_result_at: new Date(),
2520
+ id_client: email.id_client,
2521
+ client_name: email.client_name,
2522
+ from_email: email.fromAddress,
2523
+ source_type: email.source_type,
2524
+ classification_reason: email.classification_reason || '',
2525
+ openai_environment: resolvedEnvironment
2526
+ })) || log;
2527
+
2528
+ return {
2529
+ status: 'skipped',
2530
+ email,
2531
+ error: 'GitHub token not configured',
2532
+ log: updated,
2533
+ reason: 'missing_github_token'
2534
+ };
2535
+ }
2536
+
2537
+ if (!repoTarget) {
2538
+ const updated = (await this.updateLog(log._id, {
2539
+ status: 'skipped',
2540
+ last_error: 'GitHub repository not configured',
2541
+ last_result_at: new Date(),
2542
+ id_client: email.id_client,
2543
+ client_name: email.client_name,
2544
+ from_email: email.fromAddress,
2545
+ source_type: email.source_type,
2546
+ classification_reason: email.classification_reason || '',
2547
+ openai_environment: resolvedEnvironment
2548
+ })) || log;
2549
+
2550
+ return {
2551
+ status: 'skipped',
2552
+ email,
2553
+ error: 'GitHub repository not configured',
2554
+ log: updated,
2555
+ reason: 'missing_github_repo'
2556
+ };
2557
+ }
2558
+
2559
+ const effectiveEnv = `${repoTarget.owner}/${repoTarget.repo}`;
2560
+ const emailHash = email.issueHash || email.hash || this.generateIssueHash(email);
2561
+ const rawHash = email.rawHash || this.generateRawEmailHash(email);
2562
+ const workBranch = this.buildWorkBranch(emailHash);
2563
+ const contextIdentifier = (emailHash || '').slice(0, 12) || 'autofix';
2564
+ const contextPath = `.openai/error-context/${contextIdentifier}.md`;
2565
+ const normalizedBody = this.normalizeEmailText(email.body || '');
2566
+ const contextContent = this.buildContextMarkdown(email, contextIdentifier, normalizedBody);
2567
+ const commentBody = this.buildOpenAIComment(email, contextPath, normalizedBody);
2568
+
2569
+ await this.updateLog(log._id, {
2570
+ status: 'in_progress',
2571
+ last_attempt_at: new Date(),
2572
+ branch_name: workBranch,
2573
+ pr_url: '',
2574
+ last_error: '',
2575
+ openai_environment: effectiveEnv,
2576
+ issue_hash: emailHash,
2577
+ email_hash: rawHash
2578
+ });
2579
+
2580
+ try {
2581
+ const baseBranch = await this.resolveBaseBranch(repoTarget);
2582
+ const baseSha = await this.getBranchSha(repoTarget, baseBranch);
2583
+ await this.ensureBranch(repoTarget, workBranch, baseBranch, baseSha);
2584
+ await this.upsertContextFile(repoTarget, workBranch, contextPath, contextContent);
2585
+
2586
+ let pr = await this.findOpenPullRequest(repoTarget, workBranch, baseBranch);
2587
+ if (!pr) {
2588
+ const title = `AI Autofix: ${(email.subject || 'ResolveIO Error').slice(0, 180)}`;
2589
+ const bodyLines = [
2590
+ 'This PR was opened automatically to let AI investigate the reported failure.',
2591
+ '',
2592
+ `Context file: \`${contextPath}\``
2593
+ ];
2594
+ pr = await this.createPullRequest(repoTarget, workBranch, baseBranch, title, bodyLines.join('\n'));
2595
+ }
2596
+
2597
+ await this.commentOnPullRequest(repoTarget, pr.number, commentBody);
2598
+
2599
+ const updated = (await this.updateLog(log._id, {
2600
+ status: 'success',
2601
+ branch_name: workBranch,
2602
+ pr_url: pr.html_url,
2603
+ plan_summary: email.subject,
2604
+ plan_body: commentBody,
2605
+ last_result_at: new Date(),
2606
+ id_client: email.id_client,
2607
+ client_name: email.client_name,
2608
+ from_email: email.fromAddress,
2609
+ source_type: email.source_type,
2610
+ classification_reason: email.classification_reason || '',
2611
+ openai_environment: effectiveEnv,
2612
+ issue_hash: emailHash,
2613
+ email_hash: rawHash
2614
+ })) || log;
2615
+
2616
+ return {
2617
+ status: 'success',
2618
+ email,
2619
+ branchName: workBranch,
2620
+ prUrl: pr.html_url,
2621
+ log: updated,
2622
+ summary: email.subject,
2623
+ notes: `Context file: ${contextPath}`
2624
+ };
2625
+ }
2626
+ catch (err) {
2627
+ const message = err?.message || 'GitHub workflow failed.';
2628
+ const updated = (await this.updateLog(log._id, {
2629
+ status: 'failed',
2630
+ last_error: message,
2631
+ last_result_at: new Date(),
2632
+ id_client: email.id_client,
2633
+ client_name: email.client_name,
2634
+ from_email: email.fromAddress,
2635
+ source_type: email.source_type,
2636
+ classification_reason: email.classification_reason || '',
2637
+ openai_environment: effectiveEnv,
2638
+ issue_hash: emailHash,
2639
+ email_hash: rawHash
2640
+ })) || log;
2641
+
2642
+ return {
2643
+ status: 'failed',
2644
+ email,
2645
+ error: message,
2646
+ log: updated
2647
+ };
2648
+ }
2649
+ }
2650
+
2651
+ private async notify(result: AutoFixResult): Promise<void> {
2652
+ if (result.status === 'in_progress') {
2653
+ return;
2654
+ }
2655
+
2656
+ if (result.status === 'skipped' && result.reason && !this.shouldNotifySkip(result.reason)) {
2657
+ return;
2658
+ }
2659
+
2660
+ const recipients = this.config.notifyEmails.length
2661
+ ? this.config.notifyEmails
2662
+ : (result.status === 'failed' ? [DEFAULT_ERROR_ALERT_EMAIL] : []);
2663
+ if (!recipients.length) {
2664
+ return;
2665
+ }
2666
+
2667
+ let subject = '';
2668
+ let bodyLines: string[] = [];
2669
+ const logId = result.log?._id || '';
2670
+ const summary = result.summary || result.log?.plan_summary || result.email.subject;
2671
+
2672
+ if (result.status === 'success') {
2673
+ subject = `ResolveIO AutoFix ✔ ${summary}`;
2674
+ bodyLines = [
2675
+ 'Auto-fix workflow completed successfully.',
2676
+ `Client: ${result.log?.client_name || result.email.client_name || 'Unknown'}`,
2677
+ `Source: ${result.log?.source_type || result.email.source_type || 'app'}`,
2678
+ '',
2679
+ `Branch: ${result.branchName || 'unknown'}`,
2680
+ `PR: ${result.prUrl || 'pending'}`,
2681
+ '',
2682
+ 'Summary:',
2683
+ summary || '(none)',
2684
+ ''
2685
+ ];
2686
+
2687
+ if (result.notes) {
2688
+ bodyLines.push(`Notes: ${result.notes}`, '');
2689
+ }
2690
+
2691
+ if (logId) {
2692
+ bodyLines.push(`Log ID: ${logId}`);
2693
+ }
2694
+ }
2695
+ else if (result.status === 'failed') {
2696
+ subject = `ResolveIO AutoFix ✖ ${summary}`;
2697
+ bodyLines = [
2698
+ 'Auto-fix workflow failed.',
2699
+ `Reason: ${result.error || result.log?.last_error || 'Unknown error'}`,
2700
+ `Client: ${result.log?.client_name || result.email.client_name || 'Unknown'}`,
2701
+ `Source: ${result.log?.source_type || result.email.source_type || 'app'}`,
2702
+ '',
2703
+ 'Email Subject:',
2704
+ result.email.subject
2705
+ ];
2706
+
2707
+ if (logId) {
2708
+ bodyLines.push('', `Log ID: ${logId}`);
2709
+ }
2710
+ }
2711
+ else {
2712
+ subject = `ResolveIO AutoFix ⚠ ${result.email.subject}`;
2713
+ const reason = result.reason || 'skipped';
2714
+
2715
+ bodyLines = [
2716
+ `Auto-fix skipped. Reason: ${reason}.`,
2717
+ result.error && result.error !== reason ? `Details: ${result.error}` : '',
2718
+ `Client: ${result.log?.client_name || result.email.client_name || 'Unknown'}`,
2719
+ `Source: ${result.log?.source_type || result.email.source_type || 'app'}`,
2720
+ ''
2721
+ ];
2722
+
2723
+ if (logId) {
2724
+ bodyLines.push(`Log ID: ${logId}`);
2725
+ }
2726
+
2727
+ bodyLines.push('Update or remove the log entry to retry processing.');
2728
+ }
2729
+
2730
+ const textBody = bodyLines.filter(line => line !== undefined && line !== null && line !== '').join('\n');
2731
+
2732
+ for (let email of recipients) {
2733
+ await ResolveIOServer.getMainServer().getMethodManager().sendEmail(email, subject, textBody);
2734
+ }
2735
+ }
2736
+
2737
+ }
2738
+
2739
+ export function ensureErrorAutoFixManager(serverConfig?: any): ErrorAutoFixManager | null {
2740
+ const existing = ResolveIOServer['AutoFixManager'] as ErrorAutoFixManager | null;
2741
+ if (existing) {
2742
+ return existing;
2743
+ }
2744
+
2745
+ if (!configuredErrorAutoFixDependencies) {
2746
+ return null;
2747
+ }
2748
+
2749
+ const resolvedServerConfig = serverConfig || ResolveIOServer.getServerConfig();
2750
+ if (!resolvedServerConfig) {
2751
+ return null;
2752
+ }
2753
+
2754
+ const manager = new ErrorAutoFixManager(resolvedServerConfig);
2755
+ ResolveIOServer['AutoFixManager'] = manager;
2756
+ return manager;
2757
+ }
2758
+
2759
+ export async function reportDevError(options: DevErrorReportOptions): Promise<void> {
2760
+ const manager = ensureErrorAutoFixManager();
2761
+ if (!manager) {
2762
+ console.warn('AutoFix manager not initialized for dev alert', options?.subject);
2763
+ return;
2764
+ }
2765
+
2766
+ await manager.reportDevError(options);
2767
+ }