reflectt-node 0.1.0

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 (486) hide show
  1. package/LICENSE +178 -0
  2. package/README.md +188 -0
  3. package/dist/activationEvents.d.ts +110 -0
  4. package/dist/activationEvents.d.ts.map +1 -0
  5. package/dist/activationEvents.js +378 -0
  6. package/dist/activationEvents.js.map +1 -0
  7. package/dist/activity-signal.d.ts +30 -0
  8. package/dist/activity-signal.d.ts.map +1 -0
  9. package/dist/activity-signal.js +93 -0
  10. package/dist/activity-signal.js.map +1 -0
  11. package/dist/alert-integrity.d.ts +100 -0
  12. package/dist/alert-integrity.d.ts.map +1 -0
  13. package/dist/alert-integrity.js +333 -0
  14. package/dist/alert-integrity.js.map +1 -0
  15. package/dist/alert-preflight.d.ts +40 -0
  16. package/dist/alert-preflight.d.ts.map +1 -0
  17. package/dist/alert-preflight.js +235 -0
  18. package/dist/alert-preflight.js.map +1 -0
  19. package/dist/analytics.d.ts +131 -0
  20. package/dist/analytics.d.ts.map +1 -0
  21. package/dist/analytics.js +371 -0
  22. package/dist/analytics.js.map +1 -0
  23. package/dist/artifact-mirror.d.ts +26 -0
  24. package/dist/artifact-mirror.d.ts.map +1 -0
  25. package/dist/artifact-mirror.js +170 -0
  26. package/dist/artifact-mirror.js.map +1 -0
  27. package/dist/artifact-resolver.d.ts +48 -0
  28. package/dist/artifact-resolver.d.ts.map +1 -0
  29. package/dist/artifact-resolver.js +164 -0
  30. package/dist/artifact-resolver.js.map +1 -0
  31. package/dist/assignment.d.ts +116 -0
  32. package/dist/assignment.d.ts.map +1 -0
  33. package/dist/assignment.js +475 -0
  34. package/dist/assignment.js.map +1 -0
  35. package/dist/auditLedger.d.ts +50 -0
  36. package/dist/auditLedger.d.ts.map +1 -0
  37. package/dist/auditLedger.js +136 -0
  38. package/dist/auditLedger.js.map +1 -0
  39. package/dist/boardHealthWorker.d.ts +134 -0
  40. package/dist/boardHealthWorker.d.ts.map +1 -0
  41. package/dist/boardHealthWorker.js +882 -0
  42. package/dist/boardHealthWorker.js.map +1 -0
  43. package/dist/bootstrap-team.d.ts +42 -0
  44. package/dist/bootstrap-team.d.ts.map +1 -0
  45. package/dist/bootstrap-team.js +111 -0
  46. package/dist/bootstrap-team.js.map +1 -0
  47. package/dist/buildInfo.d.ts +17 -0
  48. package/dist/buildInfo.d.ts.map +1 -0
  49. package/dist/buildInfo.js +56 -0
  50. package/dist/buildInfo.js.map +1 -0
  51. package/dist/calendar-events.d.ts +133 -0
  52. package/dist/calendar-events.d.ts.map +1 -0
  53. package/dist/calendar-events.js +615 -0
  54. package/dist/calendar-events.js.map +1 -0
  55. package/dist/calendar-ical.d.ts +41 -0
  56. package/dist/calendar-ical.d.ts.map +1 -0
  57. package/dist/calendar-ical.js +413 -0
  58. package/dist/calendar-ical.js.map +1 -0
  59. package/dist/calendar-reminder-engine.d.ts +10 -0
  60. package/dist/calendar-reminder-engine.d.ts.map +1 -0
  61. package/dist/calendar-reminder-engine.js +143 -0
  62. package/dist/calendar-reminder-engine.js.map +1 -0
  63. package/dist/calendar.d.ts +75 -0
  64. package/dist/calendar.d.ts.map +1 -0
  65. package/dist/calendar.js +391 -0
  66. package/dist/calendar.js.map +1 -0
  67. package/dist/canvas-multiplexer.d.ts +44 -0
  68. package/dist/canvas-multiplexer.d.ts.map +1 -0
  69. package/dist/canvas-multiplexer.js +150 -0
  70. package/dist/canvas-multiplexer.js.map +1 -0
  71. package/dist/canvas-slots.d.ts +83 -0
  72. package/dist/canvas-slots.d.ts.map +1 -0
  73. package/dist/canvas-slots.js +144 -0
  74. package/dist/canvas-slots.js.map +1 -0
  75. package/dist/canvas-types.d.ts +56 -0
  76. package/dist/canvas-types.d.ts.map +1 -0
  77. package/dist/canvas-types.js +54 -0
  78. package/dist/canvas-types.js.map +1 -0
  79. package/dist/cf-keepalive.d.ts +40 -0
  80. package/dist/cf-keepalive.d.ts.map +1 -0
  81. package/dist/cf-keepalive.js +153 -0
  82. package/dist/cf-keepalive.js.map +1 -0
  83. package/dist/changeFeed.d.ts +38 -0
  84. package/dist/changeFeed.d.ts.map +1 -0
  85. package/dist/changeFeed.js +324 -0
  86. package/dist/changeFeed.js.map +1 -0
  87. package/dist/channels.d.ts +28 -0
  88. package/dist/channels.d.ts.map +1 -0
  89. package/dist/channels.js +23 -0
  90. package/dist/channels.js.map +1 -0
  91. package/dist/chat-approval-detector.d.ts +47 -0
  92. package/dist/chat-approval-detector.d.ts.map +1 -0
  93. package/dist/chat-approval-detector.js +224 -0
  94. package/dist/chat-approval-detector.js.map +1 -0
  95. package/dist/chat.d.ts +119 -0
  96. package/dist/chat.d.ts.map +1 -0
  97. package/dist/chat.js +666 -0
  98. package/dist/chat.js.map +1 -0
  99. package/dist/cli.d.ts +3 -0
  100. package/dist/cli.d.ts.map +1 -0
  101. package/dist/cli.js +1142 -0
  102. package/dist/cli.js.map +1 -0
  103. package/dist/cloud.d.ts +45 -0
  104. package/dist/cloud.d.ts.map +1 -0
  105. package/dist/cloud.js +962 -0
  106. package/dist/cloud.js.map +1 -0
  107. package/dist/config.d.ts +17 -0
  108. package/dist/config.d.ts.map +1 -0
  109. package/dist/config.js +33 -0
  110. package/dist/config.js.map +1 -0
  111. package/dist/connectivity.d.ts +59 -0
  112. package/dist/connectivity.d.ts.map +1 -0
  113. package/dist/connectivity.js +173 -0
  114. package/dist/connectivity.js.map +1 -0
  115. package/dist/contacts.d.ts +59 -0
  116. package/dist/contacts.d.ts.map +1 -0
  117. package/dist/contacts.js +183 -0
  118. package/dist/contacts.js.map +1 -0
  119. package/dist/content.d.ts +130 -0
  120. package/dist/content.d.ts.map +1 -0
  121. package/dist/content.js +186 -0
  122. package/dist/content.js.map +1 -0
  123. package/dist/context-budget.d.ts +87 -0
  124. package/dist/context-budget.d.ts.map +1 -0
  125. package/dist/context-budget.js +459 -0
  126. package/dist/context-budget.js.map +1 -0
  127. package/dist/continuity-loop.d.ts +55 -0
  128. package/dist/continuity-loop.d.ts.map +1 -0
  129. package/dist/continuity-loop.js +267 -0
  130. package/dist/continuity-loop.js.map +1 -0
  131. package/dist/dashboard.d.ts +6 -0
  132. package/dist/dashboard.d.ts.map +1 -0
  133. package/dist/dashboard.js +2348 -0
  134. package/dist/dashboard.js.map +1 -0
  135. package/dist/db.d.ts +44 -0
  136. package/dist/db.d.ts.map +1 -0
  137. package/dist/db.js +648 -0
  138. package/dist/db.js.map +1 -0
  139. package/dist/doctor.d.ts +30 -0
  140. package/dist/doctor.d.ts.map +1 -0
  141. package/dist/doctor.js +159 -0
  142. package/dist/doctor.js.map +1 -0
  143. package/dist/duplicateClosureGuard.d.ts +31 -0
  144. package/dist/duplicateClosureGuard.d.ts.map +1 -0
  145. package/dist/duplicateClosureGuard.js +83 -0
  146. package/dist/duplicateClosureGuard.js.map +1 -0
  147. package/dist/embeddings.d.ts +13 -0
  148. package/dist/embeddings.d.ts.map +1 -0
  149. package/dist/embeddings.js +78 -0
  150. package/dist/embeddings.js.map +1 -0
  151. package/dist/escalation.d.ts +80 -0
  152. package/dist/escalation.d.ts.map +1 -0
  153. package/dist/escalation.js +213 -0
  154. package/dist/escalation.js.map +1 -0
  155. package/dist/events.d.ts +130 -0
  156. package/dist/events.d.ts.map +1 -0
  157. package/dist/events.js +382 -0
  158. package/dist/events.js.map +1 -0
  159. package/dist/executionSweeper.d.ts +97 -0
  160. package/dist/executionSweeper.d.ts.map +1 -0
  161. package/dist/executionSweeper.js +875 -0
  162. package/dist/executionSweeper.js.map +1 -0
  163. package/dist/experiments.d.ts +47 -0
  164. package/dist/experiments.d.ts.map +1 -0
  165. package/dist/experiments.js +133 -0
  166. package/dist/experiments.js.map +1 -0
  167. package/dist/feedback.d.ts +179 -0
  168. package/dist/feedback.d.ts.map +1 -0
  169. package/dist/feedback.js +397 -0
  170. package/dist/feedback.js.map +1 -0
  171. package/dist/files.d.ts +52 -0
  172. package/dist/files.d.ts.map +1 -0
  173. package/dist/files.js +172 -0
  174. package/dist/files.js.map +1 -0
  175. package/dist/format-duration.d.ts +19 -0
  176. package/dist/format-duration.d.ts.map +1 -0
  177. package/dist/format-duration.js +33 -0
  178. package/dist/format-duration.js.map +1 -0
  179. package/dist/github-actor-auth.d.ts +20 -0
  180. package/dist/github-actor-auth.d.ts.map +1 -0
  181. package/dist/github-actor-auth.js +54 -0
  182. package/dist/github-actor-auth.js.map +1 -0
  183. package/dist/github-ci.d.ts +16 -0
  184. package/dist/github-ci.d.ts.map +1 -0
  185. package/dist/github-ci.js +37 -0
  186. package/dist/github-ci.js.map +1 -0
  187. package/dist/github-identity.d.ts +30 -0
  188. package/dist/github-identity.d.ts.map +1 -0
  189. package/dist/github-identity.js +96 -0
  190. package/dist/github-identity.js.map +1 -0
  191. package/dist/github-reviews.d.ts +24 -0
  192. package/dist/github-reviews.d.ts.map +1 -0
  193. package/dist/github-reviews.js +56 -0
  194. package/dist/github-reviews.js.map +1 -0
  195. package/dist/health.d.ts +391 -0
  196. package/dist/health.d.ts.map +1 -0
  197. package/dist/health.js +1841 -0
  198. package/dist/health.js.map +1 -0
  199. package/dist/host-keepalive.d.ts +22 -0
  200. package/dist/host-keepalive.d.ts.map +1 -0
  201. package/dist/host-keepalive.js +126 -0
  202. package/dist/host-keepalive.js.map +1 -0
  203. package/dist/host-registry.d.ts +43 -0
  204. package/dist/host-registry.d.ts.map +1 -0
  205. package/dist/host-registry.js +93 -0
  206. package/dist/host-registry.js.map +1 -0
  207. package/dist/inbox.d.ts +87 -0
  208. package/dist/inbox.d.ts.map +1 -0
  209. package/dist/inbox.js +410 -0
  210. package/dist/inbox.js.map +1 -0
  211. package/dist/index.d.ts +2 -0
  212. package/dist/index.d.ts.map +1 -0
  213. package/dist/index.js +306 -0
  214. package/dist/index.js.map +1 -0
  215. package/dist/insight-mutation.d.ts +32 -0
  216. package/dist/insight-mutation.d.ts.map +1 -0
  217. package/dist/insight-mutation.js +160 -0
  218. package/dist/insight-mutation.js.map +1 -0
  219. package/dist/insight-promotion.d.ts +89 -0
  220. package/dist/insight-promotion.d.ts.map +1 -0
  221. package/dist/insight-promotion.js +278 -0
  222. package/dist/insight-promotion.js.map +1 -0
  223. package/dist/insight-task-bridge.d.ts +77 -0
  224. package/dist/insight-task-bridge.d.ts.map +1 -0
  225. package/dist/insight-task-bridge.js +556 -0
  226. package/dist/insight-task-bridge.js.map +1 -0
  227. package/dist/insights.d.ts +222 -0
  228. package/dist/insights.d.ts.map +1 -0
  229. package/dist/insights.js +871 -0
  230. package/dist/insights.js.map +1 -0
  231. package/dist/intake-pipeline.d.ts +74 -0
  232. package/dist/intake-pipeline.d.ts.map +1 -0
  233. package/dist/intake-pipeline.js +199 -0
  234. package/dist/intake-pipeline.js.map +1 -0
  235. package/dist/intensity.d.ts +31 -0
  236. package/dist/intensity.d.ts.map +1 -0
  237. package/dist/intensity.js +94 -0
  238. package/dist/intensity.js.map +1 -0
  239. package/dist/knowledge-auto-index.d.ts +37 -0
  240. package/dist/knowledge-auto-index.d.ts.map +1 -0
  241. package/dist/knowledge-auto-index.js +149 -0
  242. package/dist/knowledge-auto-index.js.map +1 -0
  243. package/dist/knowledge-docs.d.ts +45 -0
  244. package/dist/knowledge-docs.d.ts.map +1 -0
  245. package/dist/knowledge-docs.js +188 -0
  246. package/dist/knowledge-docs.js.map +1 -0
  247. package/dist/lane-config.d.ts +25 -0
  248. package/dist/lane-config.d.ts.map +1 -0
  249. package/dist/lane-config.js +105 -0
  250. package/dist/lane-config.js.map +1 -0
  251. package/dist/lineage.d.ts +86 -0
  252. package/dist/lineage.d.ts.map +1 -0
  253. package/dist/lineage.js +303 -0
  254. package/dist/lineage.js.map +1 -0
  255. package/dist/logStore.d.ts +25 -0
  256. package/dist/logStore.d.ts.map +1 -0
  257. package/dist/logStore.js +83 -0
  258. package/dist/logStore.js.map +1 -0
  259. package/dist/manage.d.ts +12 -0
  260. package/dist/manage.d.ts.map +1 -0
  261. package/dist/manage.js +253 -0
  262. package/dist/manage.js.map +1 -0
  263. package/dist/mcp.d.ts +5 -0
  264. package/dist/mcp.d.ts.map +1 -0
  265. package/dist/mcp.js +604 -0
  266. package/dist/mcp.js.map +1 -0
  267. package/dist/memory.d.ts +47 -0
  268. package/dist/memory.d.ts.map +1 -0
  269. package/dist/memory.js +149 -0
  270. package/dist/memory.js.map +1 -0
  271. package/dist/mention-ack.d.ts +80 -0
  272. package/dist/mention-ack.d.ts.map +1 -0
  273. package/dist/mention-ack.js +175 -0
  274. package/dist/mention-ack.js.map +1 -0
  275. package/dist/messageRouter.d.ts +60 -0
  276. package/dist/messageRouter.d.ts.map +1 -0
  277. package/dist/messageRouter.js +309 -0
  278. package/dist/messageRouter.js.map +1 -0
  279. package/dist/mutationAlert.d.ts +44 -0
  280. package/dist/mutationAlert.d.ts.map +1 -0
  281. package/dist/mutationAlert.js +174 -0
  282. package/dist/mutationAlert.js.map +1 -0
  283. package/dist/noise-budget.d.ts +136 -0
  284. package/dist/noise-budget.d.ts.map +1 -0
  285. package/dist/noise-budget.js +340 -0
  286. package/dist/noise-budget.js.map +1 -0
  287. package/dist/notifications.d.ts +67 -0
  288. package/dist/notifications.d.ts.map +1 -0
  289. package/dist/notifications.js +253 -0
  290. package/dist/notifications.js.map +1 -0
  291. package/dist/openclaw.d.ts +34 -0
  292. package/dist/openclaw.d.ts.map +1 -0
  293. package/dist/openclaw.js +208 -0
  294. package/dist/openclaw.js.map +1 -0
  295. package/dist/pause-controls.d.ts +31 -0
  296. package/dist/pause-controls.d.ts.map +1 -0
  297. package/dist/pause-controls.js +130 -0
  298. package/dist/pause-controls.js.map +1 -0
  299. package/dist/pidlock.d.ts +25 -0
  300. package/dist/pidlock.d.ts.map +1 -0
  301. package/dist/pidlock.js +179 -0
  302. package/dist/pidlock.js.map +1 -0
  303. package/dist/policy.d.ts +139 -0
  304. package/dist/policy.d.ts.map +1 -0
  305. package/dist/policy.js +264 -0
  306. package/dist/policy.js.map +1 -0
  307. package/dist/polls.d.ts +47 -0
  308. package/dist/polls.d.ts.map +1 -0
  309. package/dist/polls.js +162 -0
  310. package/dist/polls.js.map +1 -0
  311. package/dist/portability.d.ts +55 -0
  312. package/dist/portability.d.ts.map +1 -0
  313. package/dist/portability.js +292 -0
  314. package/dist/portability.js.map +1 -0
  315. package/dist/pr-integrity.d.ts +45 -0
  316. package/dist/pr-integrity.d.ts.map +1 -0
  317. package/dist/pr-integrity.js +124 -0
  318. package/dist/pr-integrity.js.map +1 -0
  319. package/dist/prAutoMerge.d.ts +62 -0
  320. package/dist/prAutoMerge.d.ts.map +1 -0
  321. package/dist/prAutoMerge.js +493 -0
  322. package/dist/prAutoMerge.js.map +1 -0
  323. package/dist/preflight.d.ts +66 -0
  324. package/dist/preflight.d.ts.map +1 -0
  325. package/dist/preflight.js +864 -0
  326. package/dist/preflight.js.map +1 -0
  327. package/dist/presence.d.ts +98 -0
  328. package/dist/presence.d.ts.map +1 -0
  329. package/dist/presence.js +347 -0
  330. package/dist/presence.js.map +1 -0
  331. package/dist/provisioning.d.ts +101 -0
  332. package/dist/provisioning.d.ts.map +1 -0
  333. package/dist/provisioning.js +430 -0
  334. package/dist/provisioning.js.map +1 -0
  335. package/dist/reflection-automation.d.ts +59 -0
  336. package/dist/reflection-automation.d.ts.map +1 -0
  337. package/dist/reflection-automation.js +350 -0
  338. package/dist/reflection-automation.js.map +1 -0
  339. package/dist/reflections.d.ts +65 -0
  340. package/dist/reflections.d.ts.map +1 -0
  341. package/dist/reflections.js +306 -0
  342. package/dist/reflections.js.map +1 -0
  343. package/dist/release.d.ts +67 -0
  344. package/dist/release.d.ts.map +1 -0
  345. package/dist/release.js +275 -0
  346. package/dist/release.js.map +1 -0
  347. package/dist/request-tracker.d.ts +36 -0
  348. package/dist/request-tracker.d.ts.map +1 -0
  349. package/dist/request-tracker.js +109 -0
  350. package/dist/request-tracker.js.map +1 -0
  351. package/dist/research.d.ts +75 -0
  352. package/dist/research.d.ts.map +1 -0
  353. package/dist/research.js +171 -0
  354. package/dist/research.js.map +1 -0
  355. package/dist/routing-approvals.d.ts +73 -0
  356. package/dist/routing-approvals.d.ts.map +1 -0
  357. package/dist/routing-approvals.js +88 -0
  358. package/dist/routing-approvals.js.map +1 -0
  359. package/dist/routing-override.d.ts +94 -0
  360. package/dist/routing-override.d.ts.map +1 -0
  361. package/dist/routing-override.js +290 -0
  362. package/dist/routing-override.js.map +1 -0
  363. package/dist/scope-routing.d.ts +18 -0
  364. package/dist/scope-routing.d.ts.map +1 -0
  365. package/dist/scope-routing.js +29 -0
  366. package/dist/scope-routing.js.map +1 -0
  367. package/dist/secrets.d.ts +77 -0
  368. package/dist/secrets.d.ts.map +1 -0
  369. package/dist/secrets.js +287 -0
  370. package/dist/secrets.js.map +1 -0
  371. package/dist/server.d.ts +3 -0
  372. package/dist/server.d.ts.map +1 -0
  373. package/dist/server.js +10887 -0
  374. package/dist/server.js.map +1 -0
  375. package/dist/service-probe.d.ts +53 -0
  376. package/dist/service-probe.d.ts.map +1 -0
  377. package/dist/service-probe.js +225 -0
  378. package/dist/service-probe.js.map +1 -0
  379. package/dist/shared-workspace-api.d.ts +73 -0
  380. package/dist/shared-workspace-api.d.ts.map +1 -0
  381. package/dist/shared-workspace-api.js +281 -0
  382. package/dist/shared-workspace-api.js.map +1 -0
  383. package/dist/shipped-heartbeat.d.ts +91 -0
  384. package/dist/shipped-heartbeat.d.ts.map +1 -0
  385. package/dist/shipped-heartbeat.js +272 -0
  386. package/dist/shipped-heartbeat.js.map +1 -0
  387. package/dist/starter-team.d.ts +23 -0
  388. package/dist/starter-team.d.ts.map +1 -0
  389. package/dist/starter-team.js +88 -0
  390. package/dist/starter-team.js.map +1 -0
  391. package/dist/suppression-ledger.d.ts +73 -0
  392. package/dist/suppression-ledger.d.ts.map +1 -0
  393. package/dist/suppression-ledger.js +125 -0
  394. package/dist/suppression-ledger.js.map +1 -0
  395. package/dist/system-loop-state.d.ts +4 -0
  396. package/dist/system-loop-state.d.ts.map +1 -0
  397. package/dist/system-loop-state.js +40 -0
  398. package/dist/system-loop-state.js.map +1 -0
  399. package/dist/taskCommentIngest.d.ts +43 -0
  400. package/dist/taskCommentIngest.d.ts.map +1 -0
  401. package/dist/taskCommentIngest.js +59 -0
  402. package/dist/taskCommentIngest.js.map +1 -0
  403. package/dist/taskPrecheck.d.ts +20 -0
  404. package/dist/taskPrecheck.d.ts.map +1 -0
  405. package/dist/taskPrecheck.js +329 -0
  406. package/dist/taskPrecheck.js.map +1 -0
  407. package/dist/taskStateSync.d.ts +8 -0
  408. package/dist/taskStateSync.d.ts.map +1 -0
  409. package/dist/taskStateSync.js +79 -0
  410. package/dist/taskStateSync.js.map +1 -0
  411. package/dist/tasks.d.ts +140 -0
  412. package/dist/tasks.d.ts.map +1 -0
  413. package/dist/tasks.js +1281 -0
  414. package/dist/tasks.js.map +1 -0
  415. package/dist/team-config.d.ts +24 -0
  416. package/dist/team-config.d.ts.map +1 -0
  417. package/dist/team-config.js +221 -0
  418. package/dist/team-config.js.map +1 -0
  419. package/dist/team-doctor.d.ts +22 -0
  420. package/dist/team-doctor.d.ts.map +1 -0
  421. package/dist/team-doctor.js +270 -0
  422. package/dist/team-doctor.js.map +1 -0
  423. package/dist/team-pulse.d.ts +52 -0
  424. package/dist/team-pulse.d.ts.map +1 -0
  425. package/dist/team-pulse.js +176 -0
  426. package/dist/team-pulse.js.map +1 -0
  427. package/dist/telemetry.d.ts +74 -0
  428. package/dist/telemetry.d.ts.map +1 -0
  429. package/dist/telemetry.js +256 -0
  430. package/dist/telemetry.js.map +1 -0
  431. package/dist/test-task-filter.d.ts +21 -0
  432. package/dist/test-task-filter.d.ts.map +1 -0
  433. package/dist/test-task-filter.js +48 -0
  434. package/dist/test-task-filter.js.map +1 -0
  435. package/dist/types.d.ts +126 -0
  436. package/dist/types.d.ts.map +1 -0
  437. package/dist/types.js +4 -0
  438. package/dist/types.js.map +1 -0
  439. package/dist/usage-tracking.d.ts +101 -0
  440. package/dist/usage-tracking.d.ts.map +1 -0
  441. package/dist/usage-tracking.js +325 -0
  442. package/dist/usage-tracking.js.map +1 -0
  443. package/dist/vector-store.d.ts +87 -0
  444. package/dist/vector-store.d.ts.map +1 -0
  445. package/dist/vector-store.js +247 -0
  446. package/dist/vector-store.js.map +1 -0
  447. package/dist/watchdog/idleNudgeLane.d.ts +22 -0
  448. package/dist/watchdog/idleNudgeLane.d.ts.map +1 -0
  449. package/dist/watchdog/idleNudgeLane.js +98 -0
  450. package/dist/watchdog/idleNudgeLane.js.map +1 -0
  451. package/dist/webhooks.d.ts +103 -0
  452. package/dist/webhooks.d.ts.map +1 -0
  453. package/dist/webhooks.js +398 -0
  454. package/dist/webhooks.js.map +1 -0
  455. package/dist/working-contract.d.ts +42 -0
  456. package/dist/working-contract.d.ts.map +1 -0
  457. package/dist/working-contract.js +228 -0
  458. package/dist/working-contract.js.map +1 -0
  459. package/dist/ws-heartbeat.d.ts +66 -0
  460. package/dist/ws-heartbeat.d.ts.map +1 -0
  461. package/dist/ws-heartbeat.js +174 -0
  462. package/dist/ws-heartbeat.js.map +1 -0
  463. package/package.json +87 -0
  464. package/plugins/reflectt-channel/README.md +96 -0
  465. package/plugins/reflectt-channel/index.ts +789 -0
  466. package/plugins/reflectt-channel/openclaw.plugin.json +23 -0
  467. package/plugins/reflectt-channel/package.json +23 -0
  468. package/plugins/reflectt-channel/src/channel.ts +433 -0
  469. package/plugins/reflectt-channel/src/types.ts +29 -0
  470. package/public/avatars/echo.png +0 -0
  471. package/public/avatars/harmony.png +0 -0
  472. package/public/avatars/kai.png +0 -0
  473. package/public/avatars/link.png +0 -0
  474. package/public/avatars/pixel.png +0 -0
  475. package/public/avatars/rhythm.png +0 -0
  476. package/public/avatars/ryan.png +0 -0
  477. package/public/avatars/sage.png +0 -0
  478. package/public/avatars/scout.png +0 -0
  479. package/public/avatars/spark.png +0 -0
  480. package/public/dashboard-animations.css +381 -0
  481. package/public/dashboard.js +3479 -0
  482. package/public/docs.md +1062 -0
  483. package/public/file-upload-mock.html +1097 -0
  484. package/public/og-card.png +0 -0
  485. package/public/ui-kit.html +318 -0
  486. package/public/widget/feedback.js +194 -0
@@ -0,0 +1,875 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ // Copyright (c) Reflectt AI
3
+ /**
4
+ * Execution Sweeper — Zero-Leak Enforcement
5
+ *
6
+ * Periodically scans for:
7
+ * 1. Stale validating tasks (no reviewer activity within SLA)
8
+ * 2. Open PRs not linked to active tasks (orphan PRs)
9
+ * 3. Task/PR state drift (merged PR but task still validating)
10
+ *
11
+ * Escalates via chat messages when thresholds are breached.
12
+ * Provides drift report endpoint for full visibility.
13
+ */
14
+ import { taskManager } from './tasks.js';
15
+ import { chatManager } from './chat.js';
16
+ import { execSync } from 'child_process';
17
+ import { processAutoMerge, generateRemediation } from './prAutoMerge.js';
18
+ import { preflightCheck } from './alert-preflight.js';
19
+ /**
20
+ * Send an alert message through chatManager with preflight guard.
21
+ * If preflight suppresses the alert (in enforce mode), the message is not sent.
22
+ * In canary mode, it logs but still sends.
23
+ */
24
+ async function sendAlertWithPreflight(msg, preflight) {
25
+ const result = preflightCheck({
26
+ ...preflight,
27
+ content: msg.content,
28
+ channel: msg.channel,
29
+ });
30
+ if (!result.proceed) {
31
+ console.log(`[Sweeper] Alert suppressed by preflight: ${result.reason} (key: ${result.idempotentKey})`);
32
+ return;
33
+ }
34
+ await chatManager.sendMessage(msg);
35
+ }
36
+ import { msToMinutes, formatDuration } from './format-duration.js';
37
+ import { suggestReviewer } from './assignment.js';
38
+ import { getDuplicateClosureCanonicalRefError } from './duplicateClosureGuard.js';
39
+ /**
40
+ * Check live PR state via `gh` CLI. Returns 'unknown' if gh is unavailable.
41
+ * Results are cached for the duration of one sweep cycle to avoid rate limits.
42
+ */
43
+ const prStateCache = new Map();
44
+ const PR_CACHE_TTL_MS = 5 * 60 * 1000; // 5 minutes
45
+ export function checkLivePrState(prUrl) {
46
+ const now = Date.now();
47
+ const cached = prStateCache.get(prUrl);
48
+ if (cached && (now - cached.cachedAt) < PR_CACHE_TTL_MS) {
49
+ return cached.state;
50
+ }
51
+ try {
52
+ // Extract owner/repo and PR number from URL
53
+ const match = prUrl.match(/github\.com\/([^/]+\/[^/]+)\/pull\/(\d+)/);
54
+ if (!match)
55
+ return { state: 'unknown', error: 'Invalid PR URL format' };
56
+ const [, repo, prNumber] = match;
57
+ const raw = execSync(`gh pr view ${prNumber} --repo ${repo} --json state --jq .state`, { timeout: 10_000, encoding: 'utf8', stdio: ['pipe', 'pipe', 'pipe'] }).trim().toUpperCase();
58
+ let state = 'unknown';
59
+ if (raw === 'OPEN')
60
+ state = 'open';
61
+ else if (raw === 'MERGED')
62
+ state = 'merged';
63
+ else if (raw === 'CLOSED')
64
+ state = 'closed';
65
+ const result = { state };
66
+ prStateCache.set(prUrl, { state: result, cachedAt: now });
67
+ return result;
68
+ }
69
+ catch (err) {
70
+ const result = { state: 'unknown', error: String(err) };
71
+ prStateCache.set(prUrl, { state: result, cachedAt: now });
72
+ return result;
73
+ }
74
+ }
75
+ /** Clear PR state cache (for testing) */
76
+ export function _clearPrStateCache() {
77
+ prStateCache.clear();
78
+ }
79
+ // ── Configuration ──────────────────────────────────────────────────────────
80
+ /** How often the sweeper runs (ms) */
81
+ const SWEEP_INTERVAL_MS = 5 * 60 * 1000; // 5 minutes
82
+ /** Validating SLA: escalate after this many ms without reviewer activity */
83
+ const VALIDATING_SLA_MS = 2 * 60 * 60 * 1000; // 2 hours (was 30m — too aggressive for async AI review)
84
+ /** Critical SLA: second escalation tier */
85
+ const VALIDATING_CRITICAL_MS = 8 * 60 * 60 * 1000; // 8 hours (was 60m — reviewers aren't real-time)
86
+ /** Auto-reassign reviewer after this much time without reviewer activity */
87
+ const VALIDATING_REASSIGN_MS = 2 * 60 * 60 * 1000; // 2 hours
88
+ /** PR age threshold: flag PRs linked to non-active tasks older than this */
89
+ const ORPHAN_PR_THRESHOLD_MS = 2 * 60 * 60 * 1000; // 2 hours
90
+ /** Re-escalation cooldown: don't re-alert the same task within this window */
91
+ const ESCALATION_COOLDOWN_MS = 4 * 60 * 60 * 1000; // 4 hours
92
+ /** Artifact grace period: validating tasks without artifacts after this are auto-rejected */
93
+ const ARTIFACT_GRACE_MS = 24 * 60 * 60 * 1000; // 24 hours
94
+ /**
95
+ * @deprecated Use formatDuration(ms) from format-duration.ts instead.
96
+ * Kept temporarily for reference; all call sites now use formatDuration().
97
+ */
98
+ /** Max escalation count per task before silencing */
99
+ const MAX_ESCALATION_COUNT = 3;
100
+ /** Track which tasks we've already escalated (avoid spam) — in-memory cache */
101
+ const escalated = new Map();
102
+ /** Track which orphan PRs we've already flagged */
103
+ const flaggedOrphanPRs = new Set();
104
+ /** Track sweep stats for the /execution-health endpoint */
105
+ let lastSweepAt = 0;
106
+ let lastSweepResults = null;
107
+ /** Dry-run log for 24h evidence capture */
108
+ const dryRunLog = [];
109
+ const DRY_RUN_LOG_MAX = 500;
110
+ function logDryRun(event, detail) {
111
+ dryRunLog.push({ timestamp: Date.now(), event, detail });
112
+ if (dryRunLog.length > DRY_RUN_LOG_MAX) {
113
+ dryRunLog.splice(0, dryRunLog.length - DRY_RUN_LOG_MAX);
114
+ }
115
+ }
116
+ // ── Auto-close eligibility ─────────────────────────────────────────────────
117
+ /**
118
+ * Determines if a validating task can be auto-closed (no manual reviewer action needed).
119
+ *
120
+ * Conditions (ALL must be true):
121
+ * 1. metadata.reconciled === true (task was created from insight reconciliation)
122
+ * 2. Review is approved (reviewer_approved=true OR review_state='approved')
123
+ * 3. No code delta is required (no pr_url, or PR is already merged)
124
+ */
125
+ export function isAutoClosable(task, meta) {
126
+ // Must be reconciled
127
+ if (!meta.reconciled)
128
+ return false;
129
+ // Must have reviewer approval or approved review state
130
+ const reviewApproved = meta.reviewer_approved === true || meta.review_state === 'approved';
131
+ if (!reviewApproved)
132
+ return false;
133
+ // If there's a PR URL, it must already be merged to auto-close
134
+ const prUrl = extractPrUrl(meta);
135
+ if (prUrl) {
136
+ const prMerged = meta.pr_merged === true || meta.merge_commit;
137
+ if (!prMerged)
138
+ return false;
139
+ }
140
+ return true;
141
+ }
142
+ // ── Core Sweep Logic ───────────────────────────────────────────────────────
143
+ export async function sweepValidatingQueue() {
144
+ const now = Date.now();
145
+ // Snapshot tasks for load-balanced reviewer reassignment decisions
146
+ const tasksForScoring = taskManager.listTasks({}).map(t => ({
147
+ id: t.id,
148
+ title: t.title,
149
+ status: t.status,
150
+ assignee: t.assignee,
151
+ reviewer: t.reviewer,
152
+ tags: t.metadata?.tags,
153
+ metadata: t.metadata,
154
+ }));
155
+ const validating = taskManager.listTasks({ status: 'validating' });
156
+ const doneTasks = taskManager.listTasks({ status: 'done' });
157
+ const doingTasks = taskManager.listTasks({ status: 'doing' });
158
+ const todoTasks = taskManager.listTasks({ status: 'todo' });
159
+ const totalScanned = validating.length + doneTasks.length + doingTasks.length + todoTasks.length;
160
+ const violations = [];
161
+ // ── Auto-close reconciled validating tasks ────────────────────────────
162
+ // Reconciled tasks (metadata.reconciled=true) with an approved review
163
+ // or evidence packet and no code delta required can be auto-closed
164
+ // to prevent SLA noise.
165
+ const autoClosedIds = new Set();
166
+ for (const task of validating) {
167
+ const meta = (task.metadata || {});
168
+ if (isAutoClosable(task, meta)) {
169
+ const dupeErr = getDuplicateClosureCanonicalRefError(meta);
170
+ if (dupeErr) {
171
+ // Don't auto-close into a churny N/A duplicate packet — requeue for canonical refs.
172
+ try {
173
+ await taskManager.updateTask(task.id, {
174
+ status: 'todo',
175
+ metadata: {
176
+ ...meta,
177
+ auto_close_blocked: true,
178
+ auto_close_blocked_at: now,
179
+ auto_close_blocked_reason: dupeErr,
180
+ review_state: 'needs_author',
181
+ reviewer_approved: undefined,
182
+ reviewer_decision: undefined,
183
+ reviewer_notes: undefined,
184
+ },
185
+ });
186
+ chatManager.sendMessage({
187
+ from: 'system',
188
+ channel: 'task-notifications',
189
+ content: `⚠️ Auto-close blocked for duplicate closure without canonical refs: ${task.id}. Requeued to todo. @${task.assignee || 'unassigned'} please set duplicate_of + canonical_pr + canonical_commit.`,
190
+ }).catch(() => { });
191
+ }
192
+ catch { }
193
+ continue;
194
+ }
195
+ try {
196
+ await taskManager.updateTask(task.id, {
197
+ status: 'done',
198
+ metadata: {
199
+ ...meta,
200
+ auto_closed: true,
201
+ auto_closed_at: now,
202
+ auto_close_reason: 'reconciled_no_code_delta',
203
+ source_insight: meta.source_insight || meta.insight_id || null,
204
+ source_reflection: meta.source_reflection || null,
205
+ },
206
+ });
207
+ autoClosedIds.add(task.id);
208
+ escalated.delete(task.id);
209
+ logDryRun('auto_closed_reconciled', `${task.id} — reconciled + approved, no code delta required`);
210
+ // Notify in chat
211
+ chatManager.sendMessage({
212
+ from: 'system',
213
+ channel: 'task-notifications',
214
+ content: `✅ Auto-closed reconciled task "${task.title}" (${task.id}) — no code delta required. Insight: ${meta.source_insight || meta.insight_id || 'N/A'}`,
215
+ }).catch(() => { });
216
+ continue;
217
+ }
218
+ catch (err) {
219
+ logDryRun('auto_close_failed', `${task.id} — ${String(err)}`);
220
+ }
221
+ }
222
+ }
223
+ // Filter out auto-closed tasks from further checks
224
+ const remainingValidating = validating.filter(t => !autoClosedIds.has(t.id));
225
+ // ── Artifact grace period: auto-reject tasks missing artifacts after 24h ──
226
+ const artifactRejectedIds = new Set();
227
+ for (const task of remainingValidating) {
228
+ const meta = (task.metadata || {});
229
+ const enteredAt = meta.entered_validating_at || task.updatedAt;
230
+ const ageInValidating = now - enteredAt;
231
+ if (ageInValidating >= ARTIFACT_GRACE_MS && !hasRequiredArtifacts(meta)) {
232
+ try {
233
+ taskManager.updateTask(task.id, {
234
+ status: 'todo',
235
+ metadata: {
236
+ ...meta,
237
+ artifact_rejected: true,
238
+ artifact_rejected_at: now,
239
+ artifact_reject_reason: 'Missing required artifacts (PR or qa_bundle) after 24h grace period',
240
+ review_state: undefined,
241
+ reviewer_approved: undefined,
242
+ },
243
+ });
244
+ artifactRejectedIds.add(task.id);
245
+ escalated.delete(task.id);
246
+ logDryRun('artifact_rejected', `${task.id} — no artifacts after ${msToMinutes(ageInValidating)}m in validating`);
247
+ chatManager.sendMessage({
248
+ from: 'system',
249
+ channel: 'task-notifications',
250
+ content: `⚠️ Auto-rejected "${task.title}" (${task.id}) back to todo — missing required artifacts (PR or qa_bundle) after 24h in validating. @${task.assignee || 'unassigned'} please add artifacts and resubmit.`,
251
+ }).catch(() => { });
252
+ }
253
+ catch (err) {
254
+ logDryRun('artifact_reject_failed', `${task.id} — ${String(err)}`);
255
+ }
256
+ }
257
+ }
258
+ // Filter out artifact-rejected tasks from SLA checks
259
+ const slaValidating = remainingValidating.filter(t => !artifactRejectedIds.has(t.id));
260
+ for (const task of slaValidating) {
261
+ const meta = (task.metadata || {});
262
+ // Auto-close approved tasks still stuck in validating (drift repair)
263
+ // This catches chat approvals or any path that set reviewer_approved
264
+ // without transitioning status to done.
265
+ const reviewState = meta.review_state;
266
+ const reviewerApproved = meta.reviewer_approved === true;
267
+ if (reviewState === 'approved' || reviewerApproved) {
268
+ const dupeErr = getDuplicateClosureCanonicalRefError(meta);
269
+ if (dupeErr) {
270
+ try {
271
+ await taskManager.updateTask(task.id, {
272
+ status: 'todo',
273
+ metadata: {
274
+ ...meta,
275
+ auto_close_blocked: true,
276
+ auto_close_blocked_at: now,
277
+ auto_close_blocked_reason: dupeErr,
278
+ review_state: 'needs_author',
279
+ reviewer_approved: undefined,
280
+ reviewer_decision: undefined,
281
+ reviewer_notes: undefined,
282
+ },
283
+ });
284
+ chatManager.sendMessage({
285
+ from: 'system',
286
+ channel: 'task-notifications',
287
+ content: `⚠️ Drift-repair auto-close blocked for duplicate closure without canonical refs: ${task.id}. Requeued to todo.`,
288
+ }).catch(() => { });
289
+ }
290
+ catch { }
291
+ continue;
292
+ }
293
+ try {
294
+ await taskManager.updateTask(task.id, {
295
+ status: 'done',
296
+ metadata: {
297
+ ...meta,
298
+ auto_closed: true,
299
+ auto_closed_at: now,
300
+ auto_close_reason: 'sweeper_drift_repair_approved',
301
+ completed_at: now,
302
+ },
303
+ });
304
+ autoClosedIds.add(task.id);
305
+ escalated.delete(task.id);
306
+ logDryRun('drift_repair_auto_closed', `${task.id} — approved but stuck in validating, auto-closed`);
307
+ chatManager.sendMessage({
308
+ from: 'system',
309
+ channel: 'task-notifications',
310
+ content: `✅ Drift repair: auto-closed "${task.title}" (${task.id}) — was approved but stuck in validating. reviewer: @${task.reviewer || 'unknown'}`,
311
+ }).catch(() => { });
312
+ }
313
+ catch (err) {
314
+ logDryRun('drift_repair_auto_close_failed', `${task.id} — ${String(err)}`);
315
+ }
316
+ continue;
317
+ }
318
+ const enteredAt = meta.entered_validating_at || task.updatedAt;
319
+ const lastActivity = meta.review_last_activity_at || enteredAt;
320
+ const ageSinceActivity = now - lastActivity;
321
+ const ageMinutes = msToMinutes(ageSinceActivity);
322
+ // ── Persistent escalation state (survives restarts) ──────────────
323
+ // Read escalation history from task metadata, not just in-memory map
324
+ const persistedLevel = meta.sweeper_escalation_level;
325
+ const persistedAt = meta.sweeper_escalated_at;
326
+ const persistedCount = meta.sweeper_escalation_count || 0;
327
+ // Rehydrate in-memory map from metadata on first encounter
328
+ const prev = escalated.get(task.id);
329
+ if (!prev && persistedLevel && persistedAt) {
330
+ escalated.set(task.id, {
331
+ level: persistedLevel,
332
+ at: persistedAt,
333
+ });
334
+ }
335
+ const effective = escalated.get(task.id);
336
+ // Auto-reassign reviewer when a task sits in validating too long without activity.
337
+ // This mitigates "stuck in validating" states when the original reviewer is offline.
338
+ if (ageSinceActivity >= VALIDATING_REASSIGN_MS && meta.reviewer_auto_reassigned !== true) {
339
+ try {
340
+ const suggestion = suggestReviewer({
341
+ title: task.title,
342
+ assignee: task.assignee,
343
+ tags: meta.tags,
344
+ done_criteria: task.done_criteria,
345
+ }, tasksForScoring);
346
+ const currentReviewer = (task.reviewer || '').trim();
347
+ const nextReviewer = (suggestion.suggested || '').trim();
348
+ if (currentReviewer && nextReviewer && nextReviewer.toLowerCase() !== currentReviewer.toLowerCase()) {
349
+ await taskManager.updateTask(task.id, {
350
+ reviewer: nextReviewer,
351
+ metadata: {
352
+ ...meta,
353
+ review_state: 'queued',
354
+ review_last_activity_at: now,
355
+ reviewer_previous: currentReviewer,
356
+ reviewer_auto_reassigned: true,
357
+ reviewer_auto_reassigned_at: now,
358
+ reviewer_reassign_reason: 'validating_no_reviewer_activity',
359
+ reviewer_scores: suggestion.scores.slice(0, 3),
360
+ },
361
+ });
362
+ // Mutate local copy so subsequent alerts in this sweep use the new reviewer.
363
+ task.reviewer = nextReviewer;
364
+ logDryRun('reviewer_auto_reassigned', `${task.id} — ${currentReviewer} -> ${nextReviewer} after ${ageMinutes}m without reviewer activity`);
365
+ chatManager.sendMessage({
366
+ from: 'system',
367
+ channel: 'task-notifications',
368
+ content: `🔁 Auto-reassigned reviewer for "${task.title}" (${task.id}) after ${ageMinutes}m without reviewer activity: @${currentReviewer} → @${nextReviewer}.`,
369
+ }).catch(() => { });
370
+ }
371
+ }
372
+ catch (err) {
373
+ logDryRun('reviewer_auto_reassign_failed', `${task.id} — ${String(err)}`);
374
+ }
375
+ }
376
+ // Skip if max escalations reached (silenced)
377
+ if (persistedCount >= MAX_ESCALATION_COUNT) {
378
+ logDryRun('escalation_silenced', `${task.id} — ${persistedCount} escalations, max reached`);
379
+ continue;
380
+ }
381
+ // Skip if within cooldown window
382
+ const lastEscalatedAt = effective?.at || persistedAt || 0;
383
+ if (lastEscalatedAt && (now - lastEscalatedAt) < ESCALATION_COOLDOWN_MS) {
384
+ continue; // Still in cooldown
385
+ }
386
+ if (ageSinceActivity >= VALIDATING_CRITICAL_MS && effective?.level !== 'critical') {
387
+ const prUrl = extractPrUrl(meta);
388
+ const newCount = persistedCount + 1;
389
+ violations.push({
390
+ taskId: task.id,
391
+ title: task.title,
392
+ assignee: task.assignee,
393
+ reviewer: task.reviewer,
394
+ type: 'validating_critical',
395
+ age_minutes: ageMinutes,
396
+ message: `🚨 CRITICAL: "${task.title}" (${task.id}) stuck in validating for ${formatDuration(ageSinceActivity)}. @${task.reviewer || 'unassigned'} please review. @${task.assignee || 'unassigned'} — your PR is blocked.`,
397
+ remediation: generateRemediation({ taskId: task.id, issue: 'stale_validating', prUrl: prUrl || undefined, meta }),
398
+ });
399
+ escalated.set(task.id, { level: 'critical', at: now });
400
+ // Persist to task metadata so it survives restarts (lightweight, bypasses lifecycle gates)
401
+ taskManager.patchTaskMetadata(task.id, {
402
+ sweeper_escalation_level: 'critical',
403
+ sweeper_escalated_at: now,
404
+ sweeper_escalation_count: newCount,
405
+ });
406
+ logDryRun('validating_critical', `${task.id} — ${ageMinutes}m — reviewer:${task.reviewer} assignee:${task.assignee} count:${newCount}`);
407
+ }
408
+ else if (ageSinceActivity >= VALIDATING_SLA_MS && !effective) {
409
+ const prUrl = extractPrUrl(meta);
410
+ const newCount = persistedCount + 1;
411
+ violations.push({
412
+ taskId: task.id,
413
+ title: task.title,
414
+ assignee: task.assignee,
415
+ reviewer: task.reviewer,
416
+ type: 'validating_sla',
417
+ age_minutes: ageMinutes,
418
+ message: `⚠️ SLA breach: "${task.title}" (${task.id}) in validating ${formatDuration(ageSinceActivity)}. @${task.reviewer || 'unassigned'} — review needed. @${task.assignee || 'unassigned'} — ping if blocked.`,
419
+ remediation: generateRemediation({ taskId: task.id, issue: 'stale_validating', prUrl: prUrl || undefined, meta }),
420
+ });
421
+ escalated.set(task.id, { level: 'warning', at: now });
422
+ // Persist to task metadata so it survives restarts (lightweight, bypasses lifecycle gates)
423
+ taskManager.patchTaskMetadata(task.id, {
424
+ sweeper_escalation_level: 'warning',
425
+ sweeper_escalated_at: now,
426
+ sweeper_escalation_count: newCount,
427
+ });
428
+ logDryRun('validating_sla', `${task.id} — ${ageMinutes}m — reviewer:${task.reviewer} assignee:${task.assignee} count:${newCount}`);
429
+ }
430
+ }
431
+ // Clean up escalation tracking for tasks no longer validating
432
+ for (const [key] of escalated) {
433
+ // drift: prefix holds the real task ID — strip before resolving
434
+ const realTaskId = key.startsWith('drift:') ? key.slice(6) : key;
435
+ const lookup = taskManager.resolveTaskId(realTaskId);
436
+ if (!lookup.task || lookup.task.status !== 'validating') {
437
+ escalated.delete(key);
438
+ // Also clear persisted sweeper metadata when task leaves validating
439
+ if (lookup.task) {
440
+ const meta = (lookup.task.metadata || {});
441
+ if (meta.sweeper_escalation_level) {
442
+ taskManager.patchTaskMetadata(realTaskId, {
443
+ sweeper_escalation_level: undefined,
444
+ sweeper_escalated_at: undefined,
445
+ sweeper_escalation_count: undefined,
446
+ });
447
+ }
448
+ }
449
+ logDryRun('escalation_cleared', `${key} — no longer validating`);
450
+ }
451
+ }
452
+ // ── Orphan PR detection ──────────────────────────────────────────────
453
+ // Scan all tasks with PR URLs where the task is done/cancelled but the PR
454
+ // was linked — these represent potential orphan open PRs
455
+ const cancelledTasks = taskManager.listTasks({ status: 'cancelled' });
456
+ const doneAndCancelled = [...doneTasks, ...cancelledTasks];
457
+ for (const task of doneAndCancelled) {
458
+ const meta = (task.metadata || {});
459
+ const prUrl = extractPrUrl(meta);
460
+ if (!prUrl || flaggedOrphanPRs.has(prUrl))
461
+ continue;
462
+ // Check if this PR is also referenced by an active task — if so, it's not orphan
463
+ const activeTasks = [...doingTasks, ...validating, ...todoTasks];
464
+ const activeRef = activeTasks.find((t) => t.id !== task.id &&
465
+ extractPrUrl((t.metadata || {})) === prUrl);
466
+ if (activeRef)
467
+ continue;
468
+ // PR on a done task with no active task referencing it
469
+ // Check metadata flags that indicate the PR was merged/resolved
470
+ const prMerged = !!(meta.pr_merged);
471
+ const reviewerApproved = !!(meta.reviewer_approved);
472
+ const taskDone = task.status === 'done';
473
+ // If metadata says merged, skip
474
+ if (prMerged)
475
+ continue;
476
+ // Skip live PR checks during periodic sweep — execSync blocks the event loop.
477
+ // Orphan PR detection relies on metadata flags only; live checks available via /drift-report.
478
+ if (prMerged || reviewerApproved)
479
+ continue;
480
+ const completedAge = now - task.updatedAt;
481
+ if (completedAge >= ORPHAN_PR_THRESHOLD_MS) {
482
+ const assigneeMention = task.assignee ? `@${task.assignee}` : '@unassigned';
483
+ const reviewerMention = task.reviewer ? `@${task.reviewer}` : '@unassigned';
484
+ violations.push({
485
+ taskId: task.id,
486
+ title: task.title,
487
+ assignee: task.assignee,
488
+ reviewer: task.reviewer,
489
+ type: 'orphan_pr',
490
+ age_minutes: msToMinutes(completedAge),
491
+ message: `🔍 Orphan PR detected: ${prUrl} linked to done task "${task.title}" (${task.id}). PR may still be open — ${assigneeMention} close or merge it. ${reviewerMention} — confirm status.`,
492
+ remediation: generateRemediation({ taskId: task.id, issue: 'orphan_pr', prUrl }),
493
+ });
494
+ flaggedOrphanPRs.add(prUrl);
495
+ logDryRun('orphan_pr', `${prUrl} on ${task.id} — task done ${msToMinutes(completedAge)}m ago`);
496
+ }
497
+ }
498
+ // Also check validating tasks for PR drift (merged but not advanced)
499
+ for (const task of validating) {
500
+ const meta = (task.metadata || {});
501
+ if (meta.pr_merged && task.status === 'validating') {
502
+ const mergedAt = meta.pr_merged_at || task.updatedAt;
503
+ const driftAge = now - mergedAt;
504
+ if (driftAge >= ORPHAN_PR_THRESHOLD_MS && !escalated.has(`drift:${task.id}`)) {
505
+ violations.push({
506
+ taskId: task.id,
507
+ title: task.title,
508
+ assignee: task.assignee,
509
+ reviewer: task.reviewer,
510
+ type: 'pr_drift',
511
+ age_minutes: msToMinutes(driftAge),
512
+ message: `📦 PR merged ${msToMinutes(driftAge)}m ago but "${task.title}" (${task.id}) still in validating. @${task.reviewer || 'unassigned'} — approve or close. @${task.assignee || 'unassigned'} — ping if needed.`,
513
+ remediation: generateRemediation({ taskId: task.id, issue: 'pr_merged_not_closed', prUrl: extractPrUrl(meta) || undefined, meta }),
514
+ });
515
+ escalated.set(`drift:${task.id}`, { level: 'warning', at: now });
516
+ logDryRun('pr_drift', `${task.id} — PR merged ${msToMinutes(driftAge)}m ago, still validating`);
517
+ }
518
+ }
519
+ }
520
+ // ── Auto-merge processing ─────────────────────────────────────────────
521
+ // Attempt to auto-merge green+approved PRs and auto-close tasks
522
+ try {
523
+ const autoMergeResult = processAutoMerge([...validating, ...doingTasks, ...todoTasks]);
524
+ if (autoMergeResult.mergeAttempts > 0 || autoMergeResult.autoCloses > 0) {
525
+ logDryRun('auto_merge', `attempts=${autoMergeResult.mergeAttempts} successes=${autoMergeResult.mergeSuccesses} autoCloses=${autoMergeResult.autoCloses} skipped=${autoMergeResult.skipped}`);
526
+ }
527
+ }
528
+ catch (err) {
529
+ console.error('[Sweeper] Auto-merge processing failed:', err);
530
+ logDryRun('auto_merge_error', String(err));
531
+ }
532
+ const result = {
533
+ timestamp: now,
534
+ violations,
535
+ tasksScanned: totalScanned,
536
+ validatingCount: validating.length,
537
+ autoClosedCount: autoClosedIds.size,
538
+ artifactRejectedCount: artifactRejectedIds.size,
539
+ };
540
+ lastSweepAt = now;
541
+ lastSweepResults = result;
542
+ logDryRun('sweep_complete', `scanned=${totalScanned} validating=${validating.length} violations=${violations.length}`);
543
+ return result;
544
+ }
545
+ // ── Helper: Extract PR URL from task metadata ──────────────────────────────
546
+ function isValidPrUrl(url) {
547
+ // Filter out placeholder/invalid PR URLs (e.g. /pull/0, /pull/00)
548
+ const match = url.match(/\/pull\/(\d+)/);
549
+ if (!match)
550
+ return false;
551
+ const prNumber = parseInt(match[1], 10);
552
+ return prNumber > 0;
553
+ }
554
+ function extractPrUrl(meta) {
555
+ // Skip extraction entirely for doc-only or config-only tasks
556
+ const reviewHandoff = meta.review_handoff;
557
+ if (reviewHandoff?.doc_only || reviewHandoff?.config_only)
558
+ return null;
559
+ // Check multiple locations where PR URLs are stored
560
+ const candidates = [];
561
+ if (meta.pr_url && typeof meta.pr_url === 'string')
562
+ candidates.push(meta.pr_url);
563
+ const qaBundle = meta.qa_bundle;
564
+ if (qaBundle?.pr_link && typeof qaBundle.pr_link === 'string')
565
+ candidates.push(qaBundle.pr_link);
566
+ if (reviewHandoff?.pr_url && typeof reviewHandoff.pr_url === 'string')
567
+ candidates.push(reviewHandoff.pr_url);
568
+ const artifacts = meta.artifacts;
569
+ if (artifacts?.length) {
570
+ const ghPr = artifacts.find(a => typeof a === 'string' && a.includes('github.com') && a.includes('/pull/'));
571
+ if (ghPr)
572
+ candidates.push(ghPr);
573
+ }
574
+ // Return first valid PR URL, filtering out placeholders like /pull/0
575
+ for (const url of candidates) {
576
+ if (isValidPrUrl(url))
577
+ return url;
578
+ }
579
+ return null;
580
+ }
581
+ // ── Artifact Check ─────────────────────────────────────────────────────────
582
+ /**
583
+ * Check if a validating task has required artifacts (PR URL or qa_bundle).
584
+ * Doc-only and config-only tasks are exempt (they use review_handoff).
585
+ */
586
+ export function hasRequiredArtifacts(meta) {
587
+ // Doc-only / config-only tasks don't need code artifacts
588
+ const reviewHandoff = meta.review_handoff;
589
+ if (reviewHandoff?.doc_only || reviewHandoff?.config_only)
590
+ return true;
591
+ // Reconciled tasks (no code delta) are exempt
592
+ if (meta.reconciled === true)
593
+ return true;
594
+ // Check for PR URL in any known location
595
+ if (extractPrUrl(meta))
596
+ return true;
597
+ // Check for qa_bundle with meaningful evidence (not just review_packet structure)
598
+ const qaBundle = meta.qa_bundle;
599
+ if (qaBundle) {
600
+ // pr_link in qa_bundle counts as evidence
601
+ if (qaBundle.pr_link && typeof qaBundle.pr_link === 'string' && isValidPrUrl(qaBundle.pr_link))
602
+ return true;
603
+ // test results or deployment evidence count
604
+ if (qaBundle.test_results || qaBundle.deployment_url)
605
+ return true;
606
+ }
607
+ // Check artifacts array for any meaningful entry
608
+ const artifacts = meta.artifacts;
609
+ if (artifacts?.some(a => typeof a === 'string' && a.length > 0 && !a.startsWith('duplicate:')))
610
+ return true;
611
+ return false;
612
+ }
613
+ // ── Escalation ─────────────────────────────────────────────────────────────
614
+ async function escalateViolations(violations) {
615
+ if (violations.length === 0)
616
+ return;
617
+ // ── Batch violations into a single summary message ─────────────────
618
+ // Instead of spamming one message per violation, group them by type
619
+ // and post a single digest. Reduces noise from N messages to 1.
620
+ const critical = violations.filter(v => v.type === 'validating_critical');
621
+ const warnings = violations.filter(v => v.type === 'validating_sla');
622
+ const prIssues = violations.filter(v => v.type === 'orphan_pr' || v.type === 'pr_drift');
623
+ const lines = [`🔍 **Sweeper Digest** — ${violations.length} issue(s) found`];
624
+ if (critical.length > 0) {
625
+ lines.push('');
626
+ lines.push(`🚨 **Critical** (${critical.length}):`);
627
+ for (const v of critical) {
628
+ lines.push(` • ${v.title} (${v.taskId}) — ${v.age_minutes}m, reviewer: @${v.reviewer || 'unassigned'}`);
629
+ }
630
+ }
631
+ if (warnings.length > 0) {
632
+ lines.push('');
633
+ lines.push(`⚠️ **SLA Warning** (${warnings.length}):`);
634
+ for (const v of warnings) {
635
+ lines.push(` • ${v.title} (${v.taskId}) — ${v.age_minutes}m, reviewer: @${v.reviewer || 'unassigned'}`);
636
+ }
637
+ }
638
+ if (prIssues.length > 0) {
639
+ lines.push('');
640
+ lines.push(`📦 **PR Issues** (${prIssues.length}):`);
641
+ for (const v of prIssues) {
642
+ lines.push(` • ${v.title} (${v.taskId}) — ${v.age_minutes}m`);
643
+ }
644
+ }
645
+ try {
646
+ // Use first violation's taskId for preflight; digest is a summary alert
647
+ const firstTaskId = violations[0]?.taskId || 'unknown';
648
+ await sendAlertWithPreflight({
649
+ channel: 'general',
650
+ from: 'sweeper',
651
+ content: lines.join('\n'),
652
+ }, {
653
+ taskId: firstTaskId,
654
+ alertType: 'sweeper_digest',
655
+ agentId: violations[0]?.reviewer || violations[0]?.assignee,
656
+ });
657
+ }
658
+ catch {
659
+ console.warn(`[Sweeper] Could not post escalation digest`);
660
+ }
661
+ console.log(`[Sweeper] Escalated ${violations.length} violation(s) (batched):`, violations.map(v => `${v.type}:${v.taskId}`).join(', '));
662
+ }
663
+ // ── PR-State Drift Detection (webhook-triggered) ──────────────────────────
664
+ /**
665
+ * Check if a task's linked PR has been merged but the task is still in validating.
666
+ * Called externally when PR state changes are detected.
667
+ */
668
+ export function flagPrDrift(taskId, prState) {
669
+ const lookup = taskManager.resolveTaskId(taskId);
670
+ if (!lookup.task)
671
+ return null;
672
+ const task = lookup.task;
673
+ if (task.status === 'done')
674
+ return null; // Already done, no drift
675
+ if (prState === 'merged' && task.status === 'validating') {
676
+ logDryRun('pr_drift_webhook', `${taskId} — PR merged while task validating`);
677
+ return {
678
+ taskId: task.id,
679
+ title: task.title,
680
+ assignee: task.assignee,
681
+ reviewer: task.reviewer,
682
+ type: 'pr_drift',
683
+ age_minutes: 0,
684
+ message: `📦 PR merged but task "${task.title}" (${task.id}) still in validating. @${task.reviewer || 'unassigned'} — review or auto-advance. @${task.assignee || 'unassigned'} — confirm status.`,
685
+ };
686
+ }
687
+ if (prState === 'closed' && task.status !== 'blocked') {
688
+ logDryRun('pr_closed_webhook', `${taskId} — PR closed unmerged`);
689
+ return {
690
+ taskId: task.id,
691
+ title: task.title,
692
+ assignee: task.assignee,
693
+ reviewer: task.reviewer,
694
+ type: 'pr_drift',
695
+ age_minutes: 0,
696
+ message: `🔴 PR closed (not merged) for task "${task.title}" (${task.id}). @${task.assignee || 'unassigned'} — task should be blocked or have replacement PR. @${task.reviewer || 'unassigned'} — confirm action.`,
697
+ };
698
+ }
699
+ return null;
700
+ }
701
+ // ── Drift Report ───────────────────────────────────────────────────────────
702
+ /**
703
+ * Generate a comprehensive drift report showing all validating tasks,
704
+ * their PR status, and any orphan PRs or state drift.
705
+ */
706
+ export function generateDriftReport() {
707
+ const now = Date.now();
708
+ const validating = taskManager.listTasks({ status: 'validating' });
709
+ const validatingEntries = [];
710
+ const orphanEntries = [];
711
+ let staleCount = 0;
712
+ let driftCount = 0;
713
+ let cleanCount = 0;
714
+ // Analyze validating tasks
715
+ for (const task of validating) {
716
+ const meta = (task.metadata || {});
717
+ const enteredAt = meta.entered_validating_at || task.updatedAt;
718
+ const lastActivity = meta.review_last_activity_at || enteredAt;
719
+ const ageSinceActivity = now - lastActivity;
720
+ const ageMinutes = msToMinutes(ageSinceActivity);
721
+ const prUrl = extractPrUrl(meta);
722
+ const prMerged = !!(meta.pr_merged);
723
+ let issue = 'clean';
724
+ let detail = 'On track';
725
+ if (prMerged) {
726
+ issue = 'pr_merged_not_closed';
727
+ detail = `PR merged but task still validating (${ageMinutes}m since last activity)`;
728
+ driftCount++;
729
+ }
730
+ else if (!prUrl) {
731
+ issue = 'no_pr_linked';
732
+ detail = `No PR URL found in task metadata — cannot verify PR state`;
733
+ staleCount++;
734
+ }
735
+ else if (ageSinceActivity >= VALIDATING_CRITICAL_MS) {
736
+ issue = 'stale_validating';
737
+ detail = `${ageMinutes}m without reviewer activity (CRITICAL threshold: ${VALIDATING_CRITICAL_MS / 60_000}m)`;
738
+ staleCount++;
739
+ }
740
+ else if (ageSinceActivity >= VALIDATING_SLA_MS) {
741
+ issue = 'stale_validating';
742
+ detail = `${ageMinutes}m without reviewer activity (SLA threshold: ${VALIDATING_SLA_MS / 60_000}m)`;
743
+ staleCount++;
744
+ }
745
+ else {
746
+ cleanCount++;
747
+ }
748
+ validatingEntries.push({
749
+ taskId: task.id,
750
+ title: task.title,
751
+ status: task.status,
752
+ assignee: task.assignee,
753
+ reviewer: task.reviewer,
754
+ age_minutes: ageMinutes,
755
+ prUrl: prUrl || undefined,
756
+ prMerged,
757
+ issue,
758
+ detail,
759
+ remediation: issue !== 'clean' ? generateRemediation({ taskId: task.id, issue, prUrl: prUrl || undefined, meta }) : undefined,
760
+ });
761
+ }
762
+ // Collect all PR URLs from all tasks to find orphans
763
+ // Query only statuses that matter for orphan detection
764
+ const driftDone = taskManager.listTasks({ status: 'done' });
765
+ const driftDoing = taskManager.listTasks({ status: 'doing' });
766
+ const driftTodo = taskManager.listTasks({ status: 'todo' });
767
+ const driftBlocked = taskManager.listTasks({ status: 'blocked' });
768
+ const driftAll = [...validating, ...driftDone, ...driftDoing, ...driftTodo, ...driftBlocked];
769
+ const prToTasks = new Map();
770
+ for (const task of driftAll) {
771
+ const meta = (task.metadata || {});
772
+ const prUrl = extractPrUrl(meta);
773
+ if (!prUrl || prUrl.includes('workspace://'))
774
+ continue;
775
+ if (!prToTasks.has(prUrl))
776
+ prToTasks.set(prUrl, []);
777
+ prToTasks.get(prUrl).push({ taskId: task.id, status: task.status });
778
+ }
779
+ // Find orphan PRs: PR URLs only linked to done/cancelled tasks (not merged)
780
+ for (const [prUrl, tasks] of prToTasks) {
781
+ const hasActiveTask = tasks.some(t => ['doing', 'validating', 'todo', 'blocked'].includes(t.status));
782
+ if (hasActiveTask)
783
+ continue;
784
+ const doneTasks = tasks.filter(t => t.status === 'done' || t.status === 'cancelled');
785
+ if (doneTasks.length === 0)
786
+ continue;
787
+ // Check if any of the done tasks have pr_merged or reviewer_approved — if so, verify live
788
+ const anyMergedMeta = doneTasks.some(t => {
789
+ const task = driftAll.find(at => at.id === t.taskId);
790
+ if (!task)
791
+ return false;
792
+ const m = (task.metadata || {});
793
+ return !!(m.pr_merged);
794
+ });
795
+ if (anyMergedMeta)
796
+ continue;
797
+ // NOTE: Live PR checks removed — execSync blocks event loop.
798
+ // Rely on metadata flags; checkLivePrState() kept for on-demand use only.
799
+ const oldestDone = driftAll.find(t => t.id === doneTasks[0].taskId);
800
+ orphanEntries.push({
801
+ taskId: doneTasks[0].taskId,
802
+ title: oldestDone?.title || 'Unknown',
803
+ status: 'done',
804
+ assignee: oldestDone?.assignee,
805
+ reviewer: oldestDone?.reviewer,
806
+ age_minutes: oldestDone ? msToMinutes(now - oldestDone.updatedAt) : 0,
807
+ prUrl,
808
+ issue: 'orphan_pr',
809
+ detail: `PR linked to ${doneTasks.length} done task(s) but not marked as merged. May still be open.`,
810
+ remediation: generateRemediation({ taskId: doneTasks[0].taskId, issue: 'orphan_pr', prUrl }),
811
+ });
812
+ }
813
+ return {
814
+ timestamp: now,
815
+ validating: validatingEntries,
816
+ orphanPRs: orphanEntries,
817
+ summary: {
818
+ totalValidating: validating.length,
819
+ staleValidating: staleCount,
820
+ orphanPRCount: orphanEntries.length,
821
+ prDriftCount: driftCount,
822
+ cleanCount,
823
+ },
824
+ };
825
+ }
826
+ // ── Periodic Runner ────────────────────────────────────────────────────────
827
+ let sweepTimer = null;
828
+ export function startSweeper() {
829
+ if (sweepTimer)
830
+ return;
831
+ console.log(`[Sweeper] Starting execution sweeper (interval: ${SWEEP_INTERVAL_MS / 1000}s, SLA: ${VALIDATING_SLA_MS / 60_000}m, critical: ${VALIDATING_CRITICAL_MS / 60_000}m)`);
832
+ // Defer initial sweep to avoid blocking server startup
833
+ setTimeout(() => {
834
+ ;
835
+ (async () => {
836
+ const initial = await sweepValidatingQueue();
837
+ escalateViolations(initial.violations);
838
+ })().catch(err => {
839
+ console.error('[Sweeper] Initial sweep failed:', err);
840
+ });
841
+ }, 5000);
842
+ logDryRun('sweeper_started', `interval=${SWEEP_INTERVAL_MS / 1000}s SLA=${VALIDATING_SLA_MS / 60_000}m critical=${VALIDATING_CRITICAL_MS / 60_000}m`);
843
+ sweepTimer = setInterval(() => {
844
+ ;
845
+ (async () => {
846
+ const result = await sweepValidatingQueue();
847
+ escalateViolations(result.violations);
848
+ })().catch(err => {
849
+ console.error('[Sweeper] Sweep failed:', err);
850
+ logDryRun('sweep_error', String(err));
851
+ });
852
+ }, SWEEP_INTERVAL_MS);
853
+ sweepTimer.unref();
854
+ }
855
+ export function stopSweeper() {
856
+ if (sweepTimer) {
857
+ clearInterval(sweepTimer);
858
+ sweepTimer = null;
859
+ logDryRun('sweeper_stopped', 'Manual stop');
860
+ }
861
+ }
862
+ export function getSweeperStatus() {
863
+ return {
864
+ running: sweepTimer !== null,
865
+ lastSweepAt,
866
+ lastResults: lastSweepResults,
867
+ escalationTracking: Array.from(escalated.entries()).map(([taskId, e]) => ({
868
+ taskId,
869
+ level: e.level,
870
+ at: e.at,
871
+ })),
872
+ dryRunLog,
873
+ };
874
+ }
875
+ //# sourceMappingURL=executionSweeper.js.map