oh-my-codex 0.12.5 → 0.12.6

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 (386) hide show
  1. package/Cargo.lock +5 -5
  2. package/Cargo.toml +1 -1
  3. package/README.md +17 -0
  4. package/dist/cli/__tests__/ask.test.js +26 -0
  5. package/dist/cli/__tests__/ask.test.js.map +1 -1
  6. package/dist/cli/__tests__/explore.test.js +95 -8
  7. package/dist/cli/__tests__/explore.test.js.map +1 -1
  8. package/dist/cli/__tests__/index.test.js +58 -0
  9. package/dist/cli/__tests__/index.test.js.map +1 -1
  10. package/dist/cli/__tests__/launch-fallback.test.js +169 -0
  11. package/dist/cli/__tests__/launch-fallback.test.js.map +1 -1
  12. package/dist/cli/__tests__/mcp-parity.test.js +31 -0
  13. package/dist/cli/__tests__/mcp-parity.test.js.map +1 -1
  14. package/dist/cli/__tests__/setup-agents-overwrite.test.js +66 -2
  15. package/dist/cli/__tests__/setup-agents-overwrite.test.js.map +1 -1
  16. package/dist/cli/__tests__/setup-refresh.test.js +11 -1
  17. package/dist/cli/__tests__/setup-refresh.test.js.map +1 -1
  18. package/dist/cli/__tests__/team.test.js +92 -0
  19. package/dist/cli/__tests__/team.test.js.map +1 -1
  20. package/dist/cli/__tests__/uninstall.test.js +14 -1
  21. package/dist/cli/__tests__/uninstall.test.js.map +1 -1
  22. package/dist/cli/cleanup.js +1 -1
  23. package/dist/cli/cleanup.js.map +1 -1
  24. package/dist/cli/constants.d.ts +1 -0
  25. package/dist/cli/constants.d.ts.map +1 -1
  26. package/dist/cli/constants.js +1 -0
  27. package/dist/cli/constants.js.map +1 -1
  28. package/dist/cli/explore.d.ts +1 -0
  29. package/dist/cli/explore.d.ts.map +1 -1
  30. package/dist/cli/explore.js +45 -1
  31. package/dist/cli/explore.js.map +1 -1
  32. package/dist/cli/index.d.ts +1 -1
  33. package/dist/cli/index.d.ts.map +1 -1
  34. package/dist/cli/index.js +103 -10
  35. package/dist/cli/index.js.map +1 -1
  36. package/dist/cli/mcp-parity.d.ts +1 -1
  37. package/dist/cli/mcp-parity.d.ts.map +1 -1
  38. package/dist/cli/mcp-parity.js +24 -0
  39. package/dist/cli/mcp-parity.js.map +1 -1
  40. package/dist/cli/setup.d.ts.map +1 -1
  41. package/dist/cli/setup.js +2 -2
  42. package/dist/cli/setup.js.map +1 -1
  43. package/dist/cli/team.d.ts.map +1 -1
  44. package/dist/cli/team.js +54 -14
  45. package/dist/cli/team.js.map +1 -1
  46. package/dist/cli/uninstall.d.ts.map +1 -1
  47. package/dist/cli/uninstall.js +1 -0
  48. package/dist/cli/uninstall.js.map +1 -1
  49. package/dist/config/__tests__/generator-idempotent.test.js +1 -0
  50. package/dist/config/__tests__/generator-idempotent.test.js.map +1 -1
  51. package/dist/config/__tests__/mcp-registry.test.js +61 -0
  52. package/dist/config/__tests__/mcp-registry.test.js.map +1 -1
  53. package/dist/config/__tests__/wiki-config-contract.test.d.ts +2 -0
  54. package/dist/config/__tests__/wiki-config-contract.test.d.ts.map +1 -0
  55. package/dist/config/__tests__/wiki-config-contract.test.js +19 -0
  56. package/dist/config/__tests__/wiki-config-contract.test.js.map +1 -0
  57. package/dist/config/generator.d.ts.map +1 -1
  58. package/dist/config/generator.js +8 -0
  59. package/dist/config/generator.js.map +1 -1
  60. package/dist/config/mcp-registry.d.ts +2 -0
  61. package/dist/config/mcp-registry.d.ts.map +1 -1
  62. package/dist/config/mcp-registry.js +12 -0
  63. package/dist/config/mcp-registry.js.map +1 -1
  64. package/dist/hooks/__tests__/agents-overlay.test.js +18 -0
  65. package/dist/hooks/__tests__/agents-overlay.test.js.map +1 -1
  66. package/dist/hooks/__tests__/keyword-detector.test.js +75 -0
  67. package/dist/hooks/__tests__/keyword-detector.test.js.map +1 -1
  68. package/dist/hooks/__tests__/notify-fallback-watcher.test.js +140 -1
  69. package/dist/hooks/__tests__/notify-fallback-watcher.test.js.map +1 -1
  70. package/dist/hooks/__tests__/notify-hook-auto-nudge.test.js +35 -0
  71. package/dist/hooks/__tests__/notify-hook-auto-nudge.test.js.map +1 -1
  72. package/dist/hooks/__tests__/notify-hook-managed-tmux.test.js +86 -2
  73. package/dist/hooks/__tests__/notify-hook-managed-tmux.test.js.map +1 -1
  74. package/dist/hooks/__tests__/notify-hook-team-dispatch.test.js +176 -1
  75. package/dist/hooks/__tests__/notify-hook-team-dispatch.test.js.map +1 -1
  76. package/dist/hooks/__tests__/notify-hook-team-leader-nudge.test.js +193 -0
  77. package/dist/hooks/__tests__/notify-hook-team-leader-nudge.test.js.map +1 -1
  78. package/dist/hooks/__tests__/notify-hook-tmux-heal.test.js +90 -2
  79. package/dist/hooks/__tests__/notify-hook-tmux-heal.test.js.map +1 -1
  80. package/dist/hooks/__tests__/session.test.js +41 -1
  81. package/dist/hooks/__tests__/session.test.js.map +1 -1
  82. package/dist/hooks/__tests__/wiki-docs-contract.test.d.ts +2 -0
  83. package/dist/hooks/__tests__/wiki-docs-contract.test.d.ts.map +1 -0
  84. package/dist/hooks/__tests__/wiki-docs-contract.test.js +34 -0
  85. package/dist/hooks/__tests__/wiki-docs-contract.test.js.map +1 -0
  86. package/dist/hooks/agents-overlay.d.ts.map +1 -1
  87. package/dist/hooks/agents-overlay.js +0 -1
  88. package/dist/hooks/agents-overlay.js.map +1 -1
  89. package/dist/hooks/extensibility/__tests__/dispatcher.test.js +32 -0
  90. package/dist/hooks/extensibility/__tests__/dispatcher.test.js.map +1 -1
  91. package/dist/hooks/extensibility/__tests__/sdk.test.js +33 -3
  92. package/dist/hooks/extensibility/__tests__/sdk.test.js.map +1 -1
  93. package/dist/hooks/extensibility/dispatcher.d.ts.map +1 -1
  94. package/dist/hooks/extensibility/dispatcher.js +41 -0
  95. package/dist/hooks/extensibility/dispatcher.js.map +1 -1
  96. package/dist/hooks/extensibility/sdk/runtime-state.d.ts.map +1 -1
  97. package/dist/hooks/extensibility/sdk/runtime-state.js +7 -1
  98. package/dist/hooks/extensibility/sdk/runtime-state.js.map +1 -1
  99. package/dist/hooks/keyword-detector.d.ts.map +1 -1
  100. package/dist/hooks/keyword-detector.js +42 -5
  101. package/dist/hooks/keyword-detector.js.map +1 -1
  102. package/dist/hooks/keyword-registry.d.ts.map +1 -1
  103. package/dist/hooks/keyword-registry.js +3 -0
  104. package/dist/hooks/keyword-registry.js.map +1 -1
  105. package/dist/hooks/session.d.ts.map +1 -1
  106. package/dist/hooks/session.js +26 -4
  107. package/dist/hooks/session.js.map +1 -1
  108. package/dist/hud/__tests__/state.test.js +27 -0
  109. package/dist/hud/__tests__/state.test.js.map +1 -1
  110. package/dist/hud/state.d.ts.map +1 -1
  111. package/dist/hud/state.js +3 -1
  112. package/dist/hud/state.js.map +1 -1
  113. package/dist/mcp/__tests__/bootstrap.test.js +65 -1
  114. package/dist/mcp/__tests__/bootstrap.test.js.map +1 -1
  115. package/dist/mcp/__tests__/state-server.test.js +31 -0
  116. package/dist/mcp/__tests__/state-server.test.js.map +1 -1
  117. package/dist/mcp/__tests__/wiki-server.test.d.ts +2 -0
  118. package/dist/mcp/__tests__/wiki-server.test.d.ts.map +1 -0
  119. package/dist/mcp/__tests__/wiki-server.test.js +30 -0
  120. package/dist/mcp/__tests__/wiki-server.test.js.map +1 -0
  121. package/dist/mcp/bootstrap.d.ts +18 -1
  122. package/dist/mcp/bootstrap.d.ts.map +1 -1
  123. package/dist/mcp/bootstrap.js +158 -0
  124. package/dist/mcp/bootstrap.js.map +1 -1
  125. package/dist/mcp/state-server.d.ts +4 -4
  126. package/dist/mcp/state-server.d.ts.map +1 -1
  127. package/dist/mcp/state-server.js +27 -22
  128. package/dist/mcp/state-server.js.map +1 -1
  129. package/dist/mcp/wiki-server.d.ts +181 -0
  130. package/dist/mcp/wiki-server.d.ts.map +1 -0
  131. package/dist/mcp/wiki-server.js +235 -0
  132. package/dist/mcp/wiki-server.js.map +1 -0
  133. package/dist/modes/__tests__/base-autoresearch-contract.test.js +22 -1
  134. package/dist/modes/__tests__/base-autoresearch-contract.test.js.map +1 -1
  135. package/dist/modes/base.d.ts.map +1 -1
  136. package/dist/modes/base.js +10 -0
  137. package/dist/modes/base.js.map +1 -1
  138. package/dist/notifications/__tests__/formatter.test.js +11 -0
  139. package/dist/notifications/__tests__/formatter.test.js.map +1 -1
  140. package/dist/notifications/__tests__/idle-cooldown.test.js +32 -1
  141. package/dist/notifications/__tests__/idle-cooldown.test.js.map +1 -1
  142. package/dist/notifications/__tests__/index.test.d.ts +2 -0
  143. package/dist/notifications/__tests__/index.test.d.ts.map +1 -0
  144. package/dist/notifications/__tests__/index.test.js +113 -0
  145. package/dist/notifications/__tests__/index.test.js.map +1 -0
  146. package/dist/notifications/__tests__/lifecycle-dedupe.test.d.ts +2 -0
  147. package/dist/notifications/__tests__/lifecycle-dedupe.test.d.ts.map +1 -0
  148. package/dist/notifications/__tests__/lifecycle-dedupe.test.js +86 -0
  149. package/dist/notifications/__tests__/lifecycle-dedupe.test.js.map +1 -0
  150. package/dist/notifications/__tests__/reply-listener.test.js +155 -0
  151. package/dist/notifications/__tests__/reply-listener.test.js.map +1 -1
  152. package/dist/notifications/__tests__/session-idle-tail-dedupe.test.d.ts +2 -0
  153. package/dist/notifications/__tests__/session-idle-tail-dedupe.test.d.ts.map +1 -0
  154. package/dist/notifications/__tests__/session-idle-tail-dedupe.test.js +93 -0
  155. package/dist/notifications/__tests__/session-idle-tail-dedupe.test.js.map +1 -0
  156. package/dist/notifications/__tests__/session-registry.test.js +48 -1
  157. package/dist/notifications/__tests__/session-registry.test.js.map +1 -1
  158. package/dist/notifications/__tests__/session-status.test.d.ts +2 -0
  159. package/dist/notifications/__tests__/session-status.test.d.ts.map +1 -0
  160. package/dist/notifications/__tests__/session-status.test.js +159 -0
  161. package/dist/notifications/__tests__/session-status.test.js.map +1 -0
  162. package/dist/notifications/__tests__/tmux.test.js +58 -1
  163. package/dist/notifications/__tests__/tmux.test.js.map +1 -1
  164. package/dist/notifications/idle-cooldown.d.ts +11 -0
  165. package/dist/notifications/idle-cooldown.d.ts.map +1 -1
  166. package/dist/notifications/idle-cooldown.js +42 -8
  167. package/dist/notifications/idle-cooldown.js.map +1 -1
  168. package/dist/notifications/index.d.ts +1 -1
  169. package/dist/notifications/index.d.ts.map +1 -1
  170. package/dist/notifications/index.js +41 -8
  171. package/dist/notifications/index.js.map +1 -1
  172. package/dist/notifications/lifecycle-dedupe.d.ts +8 -0
  173. package/dist/notifications/lifecycle-dedupe.d.ts.map +1 -0
  174. package/dist/notifications/lifecycle-dedupe.js +112 -0
  175. package/dist/notifications/lifecycle-dedupe.js.map +1 -0
  176. package/dist/notifications/reply-listener.d.ts +2 -0
  177. package/dist/notifications/reply-listener.d.ts.map +1 -1
  178. package/dist/notifications/reply-listener.js +42 -6
  179. package/dist/notifications/reply-listener.js.map +1 -1
  180. package/dist/notifications/session-registry.d.ts.map +1 -1
  181. package/dist/notifications/session-registry.js +7 -1
  182. package/dist/notifications/session-registry.js.map +1 -1
  183. package/dist/notifications/session-status.d.ts +23 -0
  184. package/dist/notifications/session-status.d.ts.map +1 -0
  185. package/dist/notifications/session-status.js +187 -0
  186. package/dist/notifications/session-status.js.map +1 -0
  187. package/dist/notifications/tmux.d.ts +10 -0
  188. package/dist/notifications/tmux.d.ts.map +1 -1
  189. package/dist/notifications/tmux.js +59 -5
  190. package/dist/notifications/tmux.js.map +1 -1
  191. package/dist/notifications/types.d.ts +2 -0
  192. package/dist/notifications/types.d.ts.map +1 -1
  193. package/dist/openclaw/__tests__/index.test.js +84 -0
  194. package/dist/openclaw/__tests__/index.test.js.map +1 -1
  195. package/dist/openclaw/index.d.ts.map +1 -1
  196. package/dist/openclaw/index.js +7 -14
  197. package/dist/openclaw/index.js.map +1 -1
  198. package/dist/openclaw/types.d.ts +2 -2
  199. package/dist/openclaw/types.d.ts.map +1 -1
  200. package/dist/scripts/__tests__/codex-native-hook.test.js +270 -1
  201. package/dist/scripts/__tests__/codex-native-hook.test.js.map +1 -1
  202. package/dist/scripts/__tests__/hook-derived-watcher.test.d.ts +2 -0
  203. package/dist/scripts/__tests__/hook-derived-watcher.test.d.ts.map +1 -0
  204. package/dist/scripts/__tests__/hook-derived-watcher.test.js +87 -0
  205. package/dist/scripts/__tests__/hook-derived-watcher.test.js.map +1 -0
  206. package/dist/scripts/codex-native-hook.d.ts.map +1 -1
  207. package/dist/scripts/codex-native-hook.js +134 -6
  208. package/dist/scripts/codex-native-hook.js.map +1 -1
  209. package/dist/scripts/hook-derived-watcher.js +43 -1
  210. package/dist/scripts/hook-derived-watcher.js.map +1 -1
  211. package/dist/scripts/notify-fallback-watcher.js +27 -2
  212. package/dist/scripts/notify-fallback-watcher.js.map +1 -1
  213. package/dist/scripts/notify-hook/auto-nudge.d.ts.map +1 -1
  214. package/dist/scripts/notify-hook/auto-nudge.js +17 -1
  215. package/dist/scripts/notify-hook/auto-nudge.js.map +1 -1
  216. package/dist/scripts/notify-hook/managed-tmux.d.ts.map +1 -1
  217. package/dist/scripts/notify-hook/managed-tmux.js +5 -3
  218. package/dist/scripts/notify-hook/managed-tmux.js.map +1 -1
  219. package/dist/scripts/notify-hook/team-dispatch.d.ts.map +1 -1
  220. package/dist/scripts/notify-hook/team-dispatch.js +305 -167
  221. package/dist/scripts/notify-hook/team-dispatch.js.map +1 -1
  222. package/dist/scripts/notify-hook/team-leader-nudge.d.ts.map +1 -1
  223. package/dist/scripts/notify-hook/team-leader-nudge.js +79 -3
  224. package/dist/scripts/notify-hook/team-leader-nudge.js.map +1 -1
  225. package/dist/scripts/notify-hook/tmux-injection.d.ts.map +1 -1
  226. package/dist/scripts/notify-hook/tmux-injection.js +2 -1
  227. package/dist/scripts/notify-hook/tmux-injection.js.map +1 -1
  228. package/dist/scripts/run-provider-advisor.js +20 -2
  229. package/dist/scripts/run-provider-advisor.js.map +1 -1
  230. package/dist/scripts/smoke-packed-install.d.ts +1 -8
  231. package/dist/scripts/smoke-packed-install.d.ts.map +1 -1
  232. package/dist/scripts/smoke-packed-install.js +12 -68
  233. package/dist/scripts/smoke-packed-install.js.map +1 -1
  234. package/dist/state/__tests__/operations.test.js +113 -0
  235. package/dist/state/__tests__/operations.test.js.map +1 -1
  236. package/dist/state/operations.d.ts +1 -1
  237. package/dist/state/operations.d.ts.map +1 -1
  238. package/dist/state/operations.js +88 -2
  239. package/dist/state/operations.js.map +1 -1
  240. package/dist/state/skill-active.d.ts +1 -1
  241. package/dist/state/skill-active.d.ts.map +1 -1
  242. package/dist/state/skill-active.js +1 -0
  243. package/dist/state/skill-active.js.map +1 -1
  244. package/dist/team/__tests__/api-interop.test.js +90 -0
  245. package/dist/team/__tests__/api-interop.test.js.map +1 -1
  246. package/dist/team/__tests__/hardening-e2e.test.js +17 -0
  247. package/dist/team/__tests__/hardening-e2e.test.js.map +1 -1
  248. package/dist/team/__tests__/runtime.test.js +23 -0
  249. package/dist/team/__tests__/runtime.test.js.map +1 -1
  250. package/dist/team/__tests__/tmux-session.test.js +210 -0
  251. package/dist/team/__tests__/tmux-session.test.js.map +1 -1
  252. package/dist/team/api-interop.d.ts.map +1 -1
  253. package/dist/team/api-interop.js +10 -1
  254. package/dist/team/api-interop.js.map +1 -1
  255. package/dist/team/delivery-log.d.ts +1 -1
  256. package/dist/team/delivery-log.d.ts.map +1 -1
  257. package/dist/team/delivery-log.js.map +1 -1
  258. package/dist/team/leader-activity.d.ts +1 -0
  259. package/dist/team/leader-activity.d.ts.map +1 -1
  260. package/dist/team/leader-activity.js +4 -2
  261. package/dist/team/leader-activity.js.map +1 -1
  262. package/dist/team/progress-evidence.d.ts +2 -0
  263. package/dist/team/progress-evidence.d.ts.map +1 -0
  264. package/dist/team/progress-evidence.js +77 -0
  265. package/dist/team/progress-evidence.js.map +1 -0
  266. package/dist/team/runtime.d.ts.map +1 -1
  267. package/dist/team/runtime.js +44 -0
  268. package/dist/team/runtime.js.map +1 -1
  269. package/dist/team/tmux-session.d.ts.map +1 -1
  270. package/dist/team/tmux-session.js +57 -8
  271. package/dist/team/tmux-session.js.map +1 -1
  272. package/dist/team/worktree.d.ts +6 -1
  273. package/dist/team/worktree.d.ts.map +1 -1
  274. package/dist/team/worktree.js +4 -2
  275. package/dist/team/worktree.js.map +1 -1
  276. package/dist/utils/__tests__/agents-md.test.js +21 -1
  277. package/dist/utils/__tests__/agents-md.test.js.map +1 -1
  278. package/dist/utils/__tests__/repo-deps.test.d.ts +2 -0
  279. package/dist/utils/__tests__/repo-deps.test.d.ts.map +1 -0
  280. package/dist/utils/__tests__/repo-deps.test.js +71 -0
  281. package/dist/utils/__tests__/repo-deps.test.js.map +1 -0
  282. package/dist/utils/agents-md.d.ts +1 -0
  283. package/dist/utils/agents-md.d.ts.map +1 -1
  284. package/dist/utils/agents-md.js +7 -3
  285. package/dist/utils/agents-md.js.map +1 -1
  286. package/dist/utils/paths.d.ts +4 -0
  287. package/dist/utils/paths.d.ts.map +1 -1
  288. package/dist/utils/paths.js +20 -0
  289. package/dist/utils/paths.js.map +1 -1
  290. package/dist/utils/repo-deps.d.ts +20 -0
  291. package/dist/utils/repo-deps.d.ts.map +1 -0
  292. package/dist/utils/repo-deps.js +78 -0
  293. package/dist/utils/repo-deps.js.map +1 -0
  294. package/dist/verification/__tests__/dev-merge-issue-close-workflow.test.d.ts +2 -0
  295. package/dist/verification/__tests__/dev-merge-issue-close-workflow.test.d.ts.map +1 -0
  296. package/dist/verification/__tests__/dev-merge-issue-close-workflow.test.js +54 -0
  297. package/dist/verification/__tests__/dev-merge-issue-close-workflow.test.js.map +1 -0
  298. package/dist/wiki/__tests__/cjk-tokenize.test.d.ts +12 -0
  299. package/dist/wiki/__tests__/cjk-tokenize.test.d.ts.map +1 -0
  300. package/dist/wiki/__tests__/cjk-tokenize.test.js +139 -0
  301. package/dist/wiki/__tests__/cjk-tokenize.test.js.map +1 -0
  302. package/dist/wiki/__tests__/crlf-parse.test.d.ts +2 -0
  303. package/dist/wiki/__tests__/crlf-parse.test.d.ts.map +1 -0
  304. package/dist/wiki/__tests__/crlf-parse.test.js +24 -0
  305. package/dist/wiki/__tests__/crlf-parse.test.js.map +1 -0
  306. package/dist/wiki/__tests__/escape-newline.test.d.ts +2 -0
  307. package/dist/wiki/__tests__/escape-newline.test.d.ts.map +1 -0
  308. package/dist/wiki/__tests__/escape-newline.test.js +45 -0
  309. package/dist/wiki/__tests__/escape-newline.test.js.map +1 -0
  310. package/dist/wiki/__tests__/ingest.test.d.ts +5 -0
  311. package/dist/wiki/__tests__/ingest.test.d.ts.map +1 -0
  312. package/dist/wiki/__tests__/ingest.test.js +181 -0
  313. package/dist/wiki/__tests__/ingest.test.js.map +1 -0
  314. package/dist/wiki/__tests__/lint.test.d.ts +5 -0
  315. package/dist/wiki/__tests__/lint.test.d.ts.map +1 -0
  316. package/dist/wiki/__tests__/lint.test.js +163 -0
  317. package/dist/wiki/__tests__/lint.test.js.map +1 -0
  318. package/dist/wiki/__tests__/query.test.d.ts +5 -0
  319. package/dist/wiki/__tests__/query.test.d.ts.map +1 -0
  320. package/dist/wiki/__tests__/query.test.js +141 -0
  321. package/dist/wiki/__tests__/query.test.js.map +1 -0
  322. package/dist/wiki/__tests__/reserved-file-guard.test.d.ts +2 -0
  323. package/dist/wiki/__tests__/reserved-file-guard.test.d.ts.map +1 -0
  324. package/dist/wiki/__tests__/reserved-file-guard.test.js +44 -0
  325. package/dist/wiki/__tests__/reserved-file-guard.test.js.map +1 -0
  326. package/dist/wiki/__tests__/session-hooks.test.d.ts +5 -0
  327. package/dist/wiki/__tests__/session-hooks.test.d.ts.map +1 -0
  328. package/dist/wiki/__tests__/session-hooks.test.js +36 -0
  329. package/dist/wiki/__tests__/session-hooks.test.js.map +1 -0
  330. package/dist/wiki/__tests__/slug-nonascii.test.d.ts +2 -0
  331. package/dist/wiki/__tests__/slug-nonascii.test.d.ts.map +1 -0
  332. package/dist/wiki/__tests__/slug-nonascii.test.js +24 -0
  333. package/dist/wiki/__tests__/slug-nonascii.test.js.map +1 -0
  334. package/dist/wiki/__tests__/storage.test.d.ts +5 -0
  335. package/dist/wiki/__tests__/storage.test.d.ts.map +1 -0
  336. package/dist/wiki/__tests__/storage.test.js +278 -0
  337. package/dist/wiki/__tests__/storage.test.js.map +1 -0
  338. package/dist/wiki/__tests__/test-helpers.d.ts +31 -0
  339. package/dist/wiki/__tests__/test-helpers.d.ts.map +1 -0
  340. package/dist/wiki/__tests__/test-helpers.js +108 -0
  341. package/dist/wiki/__tests__/test-helpers.js.map +1 -0
  342. package/dist/wiki/index.d.ts +14 -0
  343. package/dist/wiki/index.d.ts.map +1 -0
  344. package/dist/wiki/index.js +17 -0
  345. package/dist/wiki/index.js.map +1 -0
  346. package/dist/wiki/ingest.d.ts +20 -0
  347. package/dist/wiki/ingest.d.ts.map +1 -0
  348. package/dist/wiki/ingest.js +115 -0
  349. package/dist/wiki/ingest.js.map +1 -0
  350. package/dist/wiki/lifecycle.d.ts +20 -0
  351. package/dist/wiki/lifecycle.d.ts.map +1 -0
  352. package/dist/wiki/lifecycle.js +212 -0
  353. package/dist/wiki/lifecycle.js.map +1 -0
  354. package/dist/wiki/lint.d.ts +25 -0
  355. package/dist/wiki/lint.d.ts.map +1 -0
  356. package/dist/wiki/lint.js +166 -0
  357. package/dist/wiki/lint.js.map +1 -0
  358. package/dist/wiki/query.d.ts +36 -0
  359. package/dist/wiki/query.d.ts.map +1 -0
  360. package/dist/wiki/query.js +138 -0
  361. package/dist/wiki/query.js.map +1 -0
  362. package/dist/wiki/storage.d.ts +33 -0
  363. package/dist/wiki/storage.d.ts.map +1 -0
  364. package/dist/wiki/storage.js +321 -0
  365. package/dist/wiki/storage.js.map +1 -0
  366. package/dist/wiki/types.d.ts +83 -0
  367. package/dist/wiki/types.d.ts.map +1 -0
  368. package/dist/wiki/types.js +15 -0
  369. package/dist/wiki/types.js.map +1 -0
  370. package/package.json +3 -1
  371. package/skills/configure-notifications/SKILL.md +1 -0
  372. package/skills/doctor/SKILL.md +11 -0
  373. package/skills/omx-setup/SKILL.md +1 -1
  374. package/skills/wiki/SKILL.md +57 -0
  375. package/src/scripts/__tests__/codex-native-hook.test.ts +378 -0
  376. package/src/scripts/__tests__/hook-derived-watcher.test.ts +111 -0
  377. package/src/scripts/codex-native-hook.ts +187 -7
  378. package/src/scripts/hook-derived-watcher.ts +43 -1
  379. package/src/scripts/notify-fallback-watcher.ts +26 -2
  380. package/src/scripts/notify-hook/auto-nudge.ts +19 -1
  381. package/src/scripts/notify-hook/managed-tmux.ts +8 -3
  382. package/src/scripts/notify-hook/team-dispatch.ts +326 -168
  383. package/src/scripts/notify-hook/team-leader-nudge.ts +82 -3
  384. package/src/scripts/notify-hook/tmux-injection.ts +2 -1
  385. package/src/scripts/run-provider-advisor.ts +20 -2
  386. package/src/scripts/smoke-packed-install.ts +16 -83
@@ -91,6 +91,7 @@ async function writeJsonAtomic(path, value) {
91
91
  }
92
92
  // Keep stale-timeout semantics aligned with src/team/state.ts LOCK_STALE_MS.
93
93
  const DISPATCH_LOCK_STALE_MS = 5 * 60 * 1000;
94
+ const DISPATCH_REQUEST_LEASE_STALE_MS = 30 * 1000;
94
95
  const DEFAULT_ISSUE_DISPATCH_COOLDOWN_MS = 15 * 60 * 1000;
95
96
  const ISSUE_DISPATCH_COOLDOWN_ENV = 'OMX_TEAM_DISPATCH_ISSUE_COOLDOWN_MS';
96
97
  const DEFAULT_DISPATCH_TRIGGER_COOLDOWN_MS = 30 * 1000;
@@ -171,6 +172,21 @@ function parseTriggerCooldownEntry(entry) {
171
172
  lastRequestId: safeString(entry.last_request_id).trim(),
172
173
  };
173
174
  }
175
+ function reserveDispatchCooldowns({ issueCooldownMs, triggerCooldownMs, issueCooldownByIssue, triggerCooldownByKey, issueKey, triggerKey, requestId, reservedAt = Date.now(), }) {
176
+ let mutated = false;
177
+ if (issueKey && issueCooldownMs > 0) {
178
+ issueCooldownByIssue[issueKey] = reservedAt;
179
+ mutated = true;
180
+ }
181
+ if (triggerKey && triggerCooldownMs > 0) {
182
+ triggerCooldownByKey[triggerKey] = {
183
+ at: reservedAt,
184
+ last_request_id: safeString(requestId).trim(),
185
+ };
186
+ mutated = true;
187
+ }
188
+ return mutated;
189
+ }
174
190
  async function withLockDirectory(lockDir, timeoutError, fn) {
175
191
  const ownerPath = join(lockDir, 'owner');
176
192
  const ownerToken = `${process.pid}.${Date.now()}.${Math.random().toString(16).slice(2)}`;
@@ -227,6 +243,50 @@ async function withDispatchLock(teamDirPath, fn) {
227
243
  async function withMailboxLock(teamDirPath, workerName, fn) {
228
244
  return await withLockDirectory(join(teamDirPath, 'mailbox', `.lock-${workerName}`), `Timed out acquiring mailbox lock for ${teamDirPath}/${workerName}`, fn);
229
245
  }
246
+ function dispatchRequestLeaseDir(teamDirPath, requestId) {
247
+ return join(teamDirPath, 'dispatch', `.processing-${safeString(requestId).trim()}`);
248
+ }
249
+ async function tryAcquireDispatchRequestLease(teamDirPath, requestId) {
250
+ const lockDir = dispatchRequestLeaseDir(teamDirPath, requestId);
251
+ const ownerPath = join(lockDir, 'owner');
252
+ const ownerToken = `${process.pid}.${Date.now()}.${Math.random().toString(16).slice(2)}`;
253
+ await mkdir(dirname(lockDir), { recursive: true });
254
+ while (true) {
255
+ try {
256
+ await mkdir(lockDir, { recursive: false });
257
+ await writeFile(ownerPath, ownerToken, 'utf8');
258
+ return { lockDir, ownerPath, ownerToken, requestId };
259
+ }
260
+ catch (error) {
261
+ if (error?.code !== 'EEXIST')
262
+ throw error;
263
+ try {
264
+ const info = await stat(lockDir);
265
+ if (Date.now() - info.mtimeMs > DISPATCH_REQUEST_LEASE_STALE_MS) {
266
+ await rm(lockDir, { recursive: true, force: true });
267
+ continue;
268
+ }
269
+ }
270
+ catch {
271
+ // best effort
272
+ }
273
+ return null;
274
+ }
275
+ }
276
+ }
277
+ async function releaseDispatchRequestLease(lease) {
278
+ if (!lease?.lockDir || !lease?.ownerPath || !lease?.ownerToken)
279
+ return;
280
+ try {
281
+ const currentOwner = await readFile(lease.ownerPath, 'utf8');
282
+ if (currentOwner.trim() === lease.ownerToken) {
283
+ await rm(lease.lockDir, { recursive: true, force: true });
284
+ }
285
+ }
286
+ catch {
287
+ // best effort
288
+ }
289
+ }
230
290
  function resolveLeaderPaneId(config) {
231
291
  return safeString(config?.leader_pane_id).trim();
232
292
  }
@@ -307,6 +367,199 @@ async function appendLeaderNotificationDeferredEvent({ stateDir, teamName, reque
307
367
  await mkdir(eventsDir, { recursive: true }).catch(() => { });
308
368
  await appendFile(eventsPath, JSON.stringify(event) + '\n').catch(() => { });
309
369
  }
370
+ async function finalizeClaimedDispatchRequest({ claim, result, teamName, teamDirPath, config, cwd, stateDir, logsDir, issueCooldownMs, triggerCooldownMs, }) {
371
+ const requestsPath = join(teamDirPath, 'dispatch', 'requests.json');
372
+ const issueKey = extractIssueKey(claim.request.trigger_message);
373
+ const triggerKey = normalizeTriggerKey(claim.request.trigger_message);
374
+ let summary = { processed: 0, skipped: 0, failed: 0 };
375
+ await withDispatchLock(teamDirPath, async () => {
376
+ const bridgeRequests = await readBridgeDispatchRequests(stateDir, teamName);
377
+ const usingLegacyRequests = bridgeRequests === null;
378
+ const requests = usingLegacyRequests ? await readJson(requestsPath, []) : bridgeRequests;
379
+ if (!Array.isArray(requests))
380
+ return;
381
+ const index = requests.findIndex((entry) => safeString(entry?.request_id).trim() === claim.request.request_id);
382
+ if (index < 0)
383
+ return;
384
+ const request = requests[index];
385
+ if (!request || typeof request !== 'object' || shouldSkipRequest(request) || request.status !== 'pending')
386
+ return;
387
+ const issueCooldownState = await readIssueCooldownState(teamDirPath);
388
+ const triggerCooldownState = await readTriggerCooldownState(teamDirPath);
389
+ const issueCooldownByIssue = issueCooldownState.by_issue || {};
390
+ const triggerCooldownByKey = triggerCooldownState.by_trigger || {};
391
+ const nowIso = new Date().toISOString();
392
+ let mutated = false;
393
+ mutated = reserveDispatchCooldowns({
394
+ issueCooldownMs,
395
+ triggerCooldownMs,
396
+ issueCooldownByIssue,
397
+ triggerCooldownByKey,
398
+ issueKey,
399
+ triggerKey,
400
+ requestId: request.request_id,
401
+ }) || mutated;
402
+ request.attempt_count = Number.isFinite(request.attempt_count) ? Math.max(0, request.attempt_count + 1) : 1;
403
+ request.updated_at = nowIso;
404
+ if (result.ok) {
405
+ const MAX_UNCONFIRMED_ATTEMPTS = 3;
406
+ if (result.reason === 'tmux_send_keys_unconfirmed' && request.attempt_count < MAX_UNCONFIRMED_ATTEMPTS) {
407
+ request.last_reason = result.reason;
408
+ summary.skipped += 1;
409
+ mutated = true;
410
+ await appendDispatchLog(logsDir, {
411
+ type: 'dispatch_unconfirmed_retry',
412
+ team: teamName,
413
+ request_id: request.request_id,
414
+ worker: request.to_worker,
415
+ attempt: request.attempt_count,
416
+ reason: result.reason,
417
+ ...buildDispatchAttemptEvidence(result),
418
+ });
419
+ await appendDeliveryTelemetry(logsDir, {
420
+ event: 'dispatch_result',
421
+ team: teamName,
422
+ request_id: request.request_id,
423
+ message_id: request.message_id || null,
424
+ to_worker: request.to_worker,
425
+ transport: 'send-keys',
426
+ result: 'retry',
427
+ reason: result.reason,
428
+ });
429
+ await emitOperationalHookEvent(cwd, 'retry-needed', {
430
+ team: teamName,
431
+ worker: request.to_worker,
432
+ request_id: request.request_id,
433
+ attempt: request.attempt_count,
434
+ command: request.trigger_message,
435
+ reason: result.reason,
436
+ status: 'retry-needed',
437
+ });
438
+ }
439
+ else if (result.reason === 'tmux_send_keys_unconfirmed') {
440
+ request.status = 'failed';
441
+ request.failed_at = nowIso;
442
+ request.last_reason = 'unconfirmed_after_max_retries';
443
+ runtimeExec({ command: 'MarkFailed', request_id: request.request_id, reason: 'unconfirmed_after_max_retries' }, stateDir);
444
+ summary.processed += 1;
445
+ summary.failed += 1;
446
+ mutated = true;
447
+ await appendDispatchLog(logsDir, {
448
+ type: 'dispatch_failed',
449
+ team: teamName,
450
+ request_id: request.request_id,
451
+ worker: request.to_worker,
452
+ message_id: request.message_id || null,
453
+ reason: request.last_reason,
454
+ ...buildDispatchAttemptEvidence(result),
455
+ });
456
+ await appendDeliveryTelemetry(logsDir, {
457
+ event: 'dispatch_result',
458
+ team: teamName,
459
+ request_id: request.request_id,
460
+ message_id: request.message_id || null,
461
+ to_worker: request.to_worker,
462
+ transport: 'send-keys',
463
+ result: 'failed',
464
+ reason: request.last_reason,
465
+ });
466
+ await emitOperationalHookEvent(cwd, 'failed', {
467
+ team: teamName,
468
+ worker: request.to_worker,
469
+ request_id: request.request_id,
470
+ message_id: request.message_id || null,
471
+ command: request.trigger_message,
472
+ reason: request.last_reason,
473
+ error_summary: request.last_reason,
474
+ status: 'failed',
475
+ });
476
+ }
477
+ else {
478
+ request.status = 'notified';
479
+ request.notified_at = nowIso;
480
+ request.last_reason = result.reason;
481
+ runtimeExec({ command: 'MarkNotified', request_id: request.request_id, channel: 'tmux' }, stateDir);
482
+ if (request.kind === 'mailbox' && request.message_id) {
483
+ runtimeExec({ command: 'MarkMailboxNotified', message_id: request.message_id }, stateDir);
484
+ if (usingLegacyRequests) {
485
+ await updateMailboxNotified(stateDir, teamName, request.to_worker, request.message_id).catch(() => { });
486
+ }
487
+ }
488
+ summary.processed += 1;
489
+ mutated = true;
490
+ await appendDispatchLog(logsDir, {
491
+ type: 'dispatch_notified',
492
+ team: teamName,
493
+ request_id: request.request_id,
494
+ worker: request.to_worker,
495
+ message_id: request.message_id || null,
496
+ reason: result.reason,
497
+ ...buildDispatchAttemptEvidence(result),
498
+ });
499
+ await appendDeliveryTelemetry(logsDir, {
500
+ event: 'dispatch_result',
501
+ team: teamName,
502
+ request_id: request.request_id,
503
+ message_id: request.message_id || null,
504
+ to_worker: request.to_worker,
505
+ transport: 'send-keys',
506
+ result: 'notified',
507
+ reason: result.reason,
508
+ });
509
+ }
510
+ }
511
+ else {
512
+ request.status = 'failed';
513
+ request.failed_at = nowIso;
514
+ request.last_reason = result.reason;
515
+ runtimeExec({ command: 'MarkFailed', request_id: request.request_id, reason: result.reason }, stateDir);
516
+ summary.processed += 1;
517
+ summary.failed += 1;
518
+ mutated = true;
519
+ await appendDispatchLog(logsDir, {
520
+ type: 'dispatch_failed',
521
+ team: teamName,
522
+ request_id: request.request_id,
523
+ worker: request.to_worker,
524
+ message_id: request.message_id || null,
525
+ reason: result.reason,
526
+ ...buildDispatchAttemptEvidence(result),
527
+ });
528
+ await appendDeliveryTelemetry(logsDir, {
529
+ event: 'dispatch_result',
530
+ team: teamName,
531
+ request_id: request.request_id,
532
+ message_id: request.message_id || null,
533
+ to_worker: request.to_worker,
534
+ transport: 'send-keys',
535
+ result: 'failed',
536
+ reason: result.reason,
537
+ });
538
+ await emitOperationalHookEvent(cwd, result.reason === LEADER_PANE_MISSING_DEFERRED_REASON ? 'handoff-needed' : 'failed', {
539
+ team: teamName,
540
+ worker: request.to_worker,
541
+ request_id: request.request_id,
542
+ message_id: request.message_id || null,
543
+ command: request.trigger_message,
544
+ reason: result.reason,
545
+ ...(result.reason === LEADER_PANE_MISSING_DEFERRED_REASON
546
+ ? { status: 'handoff-needed' }
547
+ : { status: 'failed', error_summary: result.reason }),
548
+ });
549
+ }
550
+ if (!mutated)
551
+ return;
552
+ issueCooldownState.by_issue = issueCooldownByIssue;
553
+ await writeJsonAtomic(issueCooldownStatePath(teamDirPath), issueCooldownState);
554
+ triggerCooldownState.by_trigger = triggerCooldownByKey;
555
+ await writeJsonAtomic(triggerCooldownStatePath(teamDirPath), triggerCooldownState);
556
+ await writeJsonAtomic(requestsPath, requests);
557
+ if (!usingLegacyRequests) {
558
+ await writeBridgeDispatchCompat(stateDir, teamName, requests);
559
+ }
560
+ });
561
+ return summary;
562
+ }
310
563
  function resolveWorkerCliForRequest(request, config) {
311
564
  const workers = Array.isArray(config?.workers) ? config.workers : [];
312
565
  const idx = Number.isFinite(request?.worker_index) ? Number(request.worker_index) : null;
@@ -557,6 +810,7 @@ export async function drainPendingTeamDispatch({ cwd, stateDir = resolveBridgeSt
557
810
  const configPath = join(teamDirPath, 'config.json');
558
811
  const requestsPath = join(teamDirPath, 'dispatch', 'requests.json');
559
812
  const config = await readJson(existsSync(manifestPath) ? manifestPath : configPath, {});
813
+ const claims = [];
560
814
  await withDispatchLock(teamDirPath, async () => {
561
815
  const bridgeRequests = await readBridgeDispatchRequests(stateDir, teamName);
562
816
  const usingLegacyRequests = bridgeRequests === null;
@@ -570,7 +824,7 @@ export async function drainPendingTeamDispatch({ cwd, stateDir = resolveBridgeSt
570
824
  const nowMs = Date.now();
571
825
  let mutated = false;
572
826
  for (const request of requests) {
573
- if (processed >= maxPerTick)
827
+ if (processed + claims.length >= maxPerTick)
574
828
  break;
575
829
  if (!request || typeof request !== 'object')
576
830
  continue;
@@ -614,9 +868,6 @@ export async function drainPendingTeamDispatch({ cwd, stateDir = resolveBridgeSt
614
868
  result: 'deferred',
615
869
  reason: LEADER_PANE_MISSING_DEFERRED_REASON,
616
870
  });
617
- // On the legacy fallback lane, requests.json still carries the queue
618
- // state for this deferred request; this event stays a progress
619
- // artifact for hook/watcher readers.
620
871
  await appendLeaderNotificationDeferredEvent({
621
872
  stateDir,
622
873
  teamName,
@@ -648,170 +899,24 @@ export async function drainPendingTeamDispatch({ cwd, stateDir = resolveBridgeSt
648
899
  continue;
649
900
  }
650
901
  }
651
- const result = await injector(request, config, resolve(cwd), stateDir);
652
- if (issueKey && issueCooldownMs > 0) {
653
- issueCooldownByIssue[issueKey] = Date.now();
654
- mutated = true;
655
- }
656
- if (triggerKey && triggerCooldownMs > 0) {
657
- triggerCooldownByKey[triggerKey] = {
658
- at: Date.now(),
659
- last_request_id: safeString(request.request_id).trim(),
660
- };
661
- mutated = true;
662
- }
663
- const nowIso = new Date().toISOString();
664
- request.attempt_count = Number.isFinite(request.attempt_count) ? Math.max(0, request.attempt_count + 1) : 1;
665
- request.updated_at = nowIso;
666
- if (result.ok) {
667
- // Unconfirmed sends: trigger text was still visible after retry
668
- // rounds. Leave as pending for the next tick to retry (up to 3
669
- // total attempts) rather than marking notified. Fixes #391.
670
- const MAX_UNCONFIRMED_ATTEMPTS = 3;
671
- if (result.reason === 'tmux_send_keys_unconfirmed' && request.attempt_count < MAX_UNCONFIRMED_ATTEMPTS) {
672
- request.last_reason = result.reason;
673
- mutated = true;
674
- skipped += 1;
675
- await appendDispatchLog(logsDir, {
676
- type: 'dispatch_unconfirmed_retry',
677
- team: teamName,
678
- request_id: request.request_id,
679
- worker: request.to_worker,
680
- attempt: request.attempt_count,
681
- reason: result.reason,
682
- ...buildDispatchAttemptEvidence(result),
683
- });
684
- await appendDeliveryTelemetry(logsDir, {
685
- event: 'dispatch_result',
686
- team: teamName,
687
- request_id: request.request_id,
688
- message_id: request.message_id || null,
689
- to_worker: request.to_worker,
690
- transport: 'send-keys',
691
- result: 'retry',
692
- reason: result.reason,
693
- });
694
- await emitOperationalHookEvent(cwd, 'retry-needed', {
695
- team: teamName,
696
- worker: request.to_worker,
697
- request_id: request.request_id,
698
- attempt: request.attempt_count,
699
- command: request.trigger_message,
700
- reason: result.reason,
701
- status: 'retry-needed',
702
- });
703
- continue;
704
- }
705
- if (result.reason === 'tmux_send_keys_unconfirmed') {
706
- request.status = 'failed';
707
- request.failed_at = nowIso;
708
- request.last_reason = 'unconfirmed_after_max_retries';
709
- runtimeExec({ command: 'MarkFailed', request_id: request.request_id, reason: 'unconfirmed_after_max_retries' }, stateDir);
710
- processed += 1;
711
- failed += 1;
712
- mutated = true;
713
- await appendDispatchLog(logsDir, {
714
- type: 'dispatch_failed',
715
- team: teamName,
716
- request_id: request.request_id,
717
- worker: request.to_worker,
718
- message_id: request.message_id || null,
719
- reason: request.last_reason,
720
- ...buildDispatchAttemptEvidence(result),
721
- });
722
- await appendDeliveryTelemetry(logsDir, {
723
- event: 'dispatch_result',
724
- team: teamName,
725
- request_id: request.request_id,
726
- message_id: request.message_id || null,
727
- to_worker: request.to_worker,
728
- transport: 'send-keys',
729
- result: 'failed',
730
- reason: request.last_reason,
731
- });
732
- await emitOperationalHookEvent(cwd, 'failed', {
733
- team: teamName,
734
- worker: request.to_worker,
735
- request_id: request.request_id,
736
- message_id: request.message_id || null,
737
- command: request.trigger_message,
738
- reason: request.last_reason,
739
- error_summary: request.last_reason,
740
- status: 'failed',
741
- });
742
- continue;
743
- }
744
- request.status = 'notified';
745
- request.notified_at = nowIso;
746
- request.last_reason = result.reason;
747
- runtimeExec({ command: 'MarkNotified', request_id: request.request_id, channel: 'tmux' }, stateDir);
748
- if (request.kind === 'mailbox' && request.message_id) {
749
- runtimeExec({ command: 'MarkMailboxNotified', message_id: request.message_id }, stateDir);
750
- if (usingLegacyRequests) {
751
- await updateMailboxNotified(stateDir, teamName, request.to_worker, request.message_id).catch(() => { });
752
- }
753
- }
754
- processed += 1;
755
- mutated = true;
756
- await appendDispatchLog(logsDir, {
757
- type: 'dispatch_notified',
758
- team: teamName,
759
- request_id: request.request_id,
760
- worker: request.to_worker,
761
- message_id: request.message_id || null,
762
- reason: result.reason,
763
- ...buildDispatchAttemptEvidence(result),
764
- });
765
- await appendDeliveryTelemetry(logsDir, {
766
- event: 'dispatch_result',
767
- team: teamName,
768
- request_id: request.request_id,
769
- message_id: request.message_id || null,
770
- to_worker: request.to_worker,
771
- transport: 'send-keys',
772
- result: 'notified',
773
- reason: result.reason,
774
- });
775
- }
776
- else {
777
- request.status = 'failed';
778
- request.failed_at = nowIso;
779
- request.last_reason = result.reason;
780
- runtimeExec({ command: 'MarkFailed', request_id: request.request_id, reason: result.reason }, stateDir);
781
- processed += 1;
782
- failed += 1;
783
- mutated = true;
784
- await appendDispatchLog(logsDir, {
785
- type: 'dispatch_failed',
786
- team: teamName,
787
- request_id: request.request_id,
788
- worker: request.to_worker,
789
- message_id: request.message_id || null,
790
- reason: result.reason,
791
- ...buildDispatchAttemptEvidence(result),
792
- });
793
- await appendDeliveryTelemetry(logsDir, {
794
- event: 'dispatch_result',
795
- team: teamName,
796
- request_id: request.request_id,
797
- message_id: request.message_id || null,
798
- to_worker: request.to_worker,
799
- transport: 'send-keys',
800
- result: 'failed',
801
- reason: result.reason,
802
- });
803
- await emitOperationalHookEvent(cwd, result.reason === LEADER_PANE_MISSING_DEFERRED_REASON ? 'handoff-needed' : 'failed', {
804
- team: teamName,
805
- worker: request.to_worker,
806
- request_id: request.request_id,
807
- message_id: request.message_id || null,
808
- command: request.trigger_message,
809
- reason: result.reason,
810
- ...(result.reason === LEADER_PANE_MISSING_DEFERRED_REASON
811
- ? { status: 'handoff-needed' }
812
- : { status: 'failed', error_summary: result.reason }),
813
- });
902
+ const lease = await tryAcquireDispatchRequestLease(teamDirPath, request.request_id);
903
+ if (!lease) {
904
+ skipped += 1;
905
+ continue;
814
906
  }
907
+ mutated = reserveDispatchCooldowns({
908
+ issueCooldownMs,
909
+ triggerCooldownMs,
910
+ issueCooldownByIssue,
911
+ triggerCooldownByKey,
912
+ issueKey,
913
+ triggerKey,
914
+ requestId: request.request_id,
915
+ }) || mutated;
916
+ claims.push({
917
+ request: { ...request },
918
+ lease,
919
+ });
815
920
  }
816
921
  if (mutated) {
817
922
  issueCooldownState.by_issue = issueCooldownByIssue;
@@ -824,6 +929,39 @@ export async function drainPendingTeamDispatch({ cwd, stateDir = resolveBridgeSt
824
929
  }
825
930
  }
826
931
  });
932
+ try {
933
+ for (const claim of claims) {
934
+ try {
935
+ const result = await injector(claim.request, config, resolve(cwd), stateDir);
936
+ const delta = await finalizeClaimedDispatchRequest({
937
+ claim,
938
+ result,
939
+ teamName,
940
+ teamDirPath,
941
+ config,
942
+ cwd,
943
+ stateDir,
944
+ logsDir,
945
+ issueCooldownMs,
946
+ triggerCooldownMs,
947
+ });
948
+ processed += delta.processed;
949
+ skipped += delta.skipped;
950
+ failed += delta.failed;
951
+ }
952
+ finally {
953
+ claim.released = true;
954
+ await releaseDispatchRequestLease(claim.lease);
955
+ }
956
+ }
957
+ }
958
+ finally {
959
+ for (const claim of claims) {
960
+ if (claim.released)
961
+ continue;
962
+ await releaseDispatchRequestLease(claim.lease);
963
+ }
964
+ }
827
965
  }
828
966
  return { processed, skipped, failed };
829
967
  }