oh-my-codex 0.12.4 → 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 (475) hide show
  1. package/Cargo.lock +5 -5
  2. package/Cargo.toml +1 -1
  3. package/README.md +27 -3
  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__/doctor-warning-copy.test.js +28 -0
  7. package/dist/cli/__tests__/doctor-warning-copy.test.js.map +1 -1
  8. package/dist/cli/__tests__/explore.test.js +95 -8
  9. package/dist/cli/__tests__/explore.test.js.map +1 -1
  10. package/dist/cli/__tests__/index.test.js +102 -4
  11. package/dist/cli/__tests__/index.test.js.map +1 -1
  12. package/dist/cli/__tests__/launch-fallback.test.js +169 -0
  13. package/dist/cli/__tests__/launch-fallback.test.js.map +1 -1
  14. package/dist/cli/__tests__/mcp-parity.test.js +31 -0
  15. package/dist/cli/__tests__/mcp-parity.test.js.map +1 -1
  16. package/dist/cli/__tests__/setup-agents-overwrite.test.js +66 -2
  17. package/dist/cli/__tests__/setup-agents-overwrite.test.js.map +1 -1
  18. package/dist/cli/__tests__/setup-refresh.test.js +51 -1
  19. package/dist/cli/__tests__/setup-refresh.test.js.map +1 -1
  20. package/dist/cli/__tests__/team.test.js +148 -3
  21. package/dist/cli/__tests__/team.test.js.map +1 -1
  22. package/dist/cli/__tests__/uninstall.test.js +14 -1
  23. package/dist/cli/__tests__/uninstall.test.js.map +1 -1
  24. package/dist/cli/cleanup.js +1 -1
  25. package/dist/cli/cleanup.js.map +1 -1
  26. package/dist/cli/constants.d.ts +1 -0
  27. package/dist/cli/constants.d.ts.map +1 -1
  28. package/dist/cli/constants.js +1 -0
  29. package/dist/cli/constants.js.map +1 -1
  30. package/dist/cli/doctor.d.ts.map +1 -1
  31. package/dist/cli/doctor.js +15 -0
  32. package/dist/cli/doctor.js.map +1 -1
  33. package/dist/cli/explore.d.ts +1 -0
  34. package/dist/cli/explore.d.ts.map +1 -1
  35. package/dist/cli/explore.js +49 -1
  36. package/dist/cli/explore.js.map +1 -1
  37. package/dist/cli/index.d.ts +2 -1
  38. package/dist/cli/index.d.ts.map +1 -1
  39. package/dist/cli/index.js +127 -14
  40. package/dist/cli/index.js.map +1 -1
  41. package/dist/cli/mcp-parity.d.ts +1 -1
  42. package/dist/cli/mcp-parity.d.ts.map +1 -1
  43. package/dist/cli/mcp-parity.js +24 -0
  44. package/dist/cli/mcp-parity.js.map +1 -1
  45. package/dist/cli/setup.d.ts.map +1 -1
  46. package/dist/cli/setup.js +17 -5
  47. package/dist/cli/setup.js.map +1 -1
  48. package/dist/cli/team.d.ts.map +1 -1
  49. package/dist/cli/team.js +80 -6
  50. package/dist/cli/team.js.map +1 -1
  51. package/dist/cli/uninstall.d.ts.map +1 -1
  52. package/dist/cli/uninstall.js +1 -0
  53. package/dist/cli/uninstall.js.map +1 -1
  54. package/dist/config/__tests__/generator-idempotent.test.js +60 -0
  55. package/dist/config/__tests__/generator-idempotent.test.js.map +1 -1
  56. package/dist/config/__tests__/mcp-registry.test.js +61 -0
  57. package/dist/config/__tests__/mcp-registry.test.js.map +1 -1
  58. package/dist/config/__tests__/wiki-config-contract.test.d.ts +2 -0
  59. package/dist/config/__tests__/wiki-config-contract.test.d.ts.map +1 -0
  60. package/dist/config/__tests__/wiki-config-contract.test.js +19 -0
  61. package/dist/config/__tests__/wiki-config-contract.test.js.map +1 -0
  62. package/dist/config/generator.d.ts +1 -0
  63. package/dist/config/generator.d.ts.map +1 -1
  64. package/dist/config/generator.js +88 -3
  65. package/dist/config/generator.js.map +1 -1
  66. package/dist/config/mcp-registry.d.ts +2 -0
  67. package/dist/config/mcp-registry.d.ts.map +1 -1
  68. package/dist/config/mcp-registry.js +12 -0
  69. package/dist/config/mcp-registry.js.map +1 -1
  70. package/dist/hooks/__tests__/agents-overlay.test.js +39 -0
  71. package/dist/hooks/__tests__/agents-overlay.test.js.map +1 -1
  72. package/dist/hooks/__tests__/keyword-detector.test.js +297 -4
  73. package/dist/hooks/__tests__/keyword-detector.test.js.map +1 -1
  74. package/dist/hooks/__tests__/notify-fallback-watcher.test.js +392 -22
  75. package/dist/hooks/__tests__/notify-fallback-watcher.test.js.map +1 -1
  76. package/dist/hooks/__tests__/notify-hook-auto-nudge.test.js +166 -67
  77. package/dist/hooks/__tests__/notify-hook-auto-nudge.test.js.map +1 -1
  78. package/dist/hooks/__tests__/notify-hook-managed-tmux.test.js +112 -2
  79. package/dist/hooks/__tests__/notify-hook-managed-tmux.test.js.map +1 -1
  80. package/dist/hooks/__tests__/notify-hook-modules.test.js +52 -12
  81. package/dist/hooks/__tests__/notify-hook-modules.test.js.map +1 -1
  82. package/dist/hooks/__tests__/notify-hook-regression-205.test.d.ts +2 -3
  83. package/dist/hooks/__tests__/notify-hook-regression-205.test.d.ts.map +1 -1
  84. package/dist/hooks/__tests__/notify-hook-regression-205.test.js +18 -23
  85. package/dist/hooks/__tests__/notify-hook-regression-205.test.js.map +1 -1
  86. package/dist/hooks/__tests__/notify-hook-session-scope.test.js +33 -0
  87. package/dist/hooks/__tests__/notify-hook-session-scope.test.js.map +1 -1
  88. package/dist/hooks/__tests__/notify-hook-team-dispatch.test.js +176 -1
  89. package/dist/hooks/__tests__/notify-hook-team-dispatch.test.js.map +1 -1
  90. package/dist/hooks/__tests__/notify-hook-team-leader-nudge.test.js +355 -7
  91. package/dist/hooks/__tests__/notify-hook-team-leader-nudge.test.js.map +1 -1
  92. package/dist/hooks/__tests__/notify-hook-tmux-heal.test.js +90 -2
  93. package/dist/hooks/__tests__/notify-hook-tmux-heal.test.js.map +1 -1
  94. package/dist/hooks/__tests__/session.test.js +142 -2
  95. package/dist/hooks/__tests__/session.test.js.map +1 -1
  96. package/dist/hooks/__tests__/wiki-docs-contract.test.d.ts +2 -0
  97. package/dist/hooks/__tests__/wiki-docs-contract.test.d.ts.map +1 -0
  98. package/dist/hooks/__tests__/wiki-docs-contract.test.js +34 -0
  99. package/dist/hooks/__tests__/wiki-docs-contract.test.js.map +1 -0
  100. package/dist/hooks/agents-overlay.d.ts.map +1 -1
  101. package/dist/hooks/agents-overlay.js +0 -1
  102. package/dist/hooks/agents-overlay.js.map +1 -1
  103. package/dist/hooks/extensibility/__tests__/dispatcher.test.js +32 -0
  104. package/dist/hooks/extensibility/__tests__/dispatcher.test.js.map +1 -1
  105. package/dist/hooks/extensibility/__tests__/runtime.test.js +31 -0
  106. package/dist/hooks/extensibility/__tests__/runtime.test.js.map +1 -1
  107. package/dist/hooks/extensibility/__tests__/sdk.test.js +33 -3
  108. package/dist/hooks/extensibility/__tests__/sdk.test.js.map +1 -1
  109. package/dist/hooks/extensibility/dispatcher.d.ts.map +1 -1
  110. package/dist/hooks/extensibility/dispatcher.js +41 -0
  111. package/dist/hooks/extensibility/dispatcher.js.map +1 -1
  112. package/dist/hooks/extensibility/sdk/runtime-state.d.ts.map +1 -1
  113. package/dist/hooks/extensibility/sdk/runtime-state.js +7 -1
  114. package/dist/hooks/extensibility/sdk/runtime-state.js.map +1 -1
  115. package/dist/hooks/extensibility/types.d.ts +1 -0
  116. package/dist/hooks/extensibility/types.d.ts.map +1 -1
  117. package/dist/hooks/keyword-detector.d.ts +6 -1
  118. package/dist/hooks/keyword-detector.d.ts.map +1 -1
  119. package/dist/hooks/keyword-detector.js +207 -10
  120. package/dist/hooks/keyword-detector.js.map +1 -1
  121. package/dist/hooks/keyword-registry.d.ts.map +1 -1
  122. package/dist/hooks/keyword-registry.js +3 -0
  123. package/dist/hooks/keyword-registry.js.map +1 -1
  124. package/dist/hooks/session.d.ts +14 -2
  125. package/dist/hooks/session.d.ts.map +1 -1
  126. package/dist/hooks/session.js +120 -16
  127. package/dist/hooks/session.js.map +1 -1
  128. package/dist/hud/__tests__/state.test.js +111 -2
  129. package/dist/hud/__tests__/state.test.js.map +1 -1
  130. package/dist/hud/state.d.ts.map +1 -1
  131. package/dist/hud/state.js +18 -21
  132. package/dist/hud/state.js.map +1 -1
  133. package/dist/mcp/__tests__/bootstrap.test.js +88 -1
  134. package/dist/mcp/__tests__/bootstrap.test.js.map +1 -1
  135. package/dist/mcp/__tests__/server-lifecycle.test.js +3 -0
  136. package/dist/mcp/__tests__/server-lifecycle.test.js.map +1 -1
  137. package/dist/mcp/__tests__/state-paths.test.js +30 -2
  138. package/dist/mcp/__tests__/state-paths.test.js.map +1 -1
  139. package/dist/mcp/__tests__/state-server.test.js +415 -0
  140. package/dist/mcp/__tests__/state-server.test.js.map +1 -1
  141. package/dist/mcp/__tests__/wiki-server.test.d.ts +2 -0
  142. package/dist/mcp/__tests__/wiki-server.test.d.ts.map +1 -0
  143. package/dist/mcp/__tests__/wiki-server.test.js +30 -0
  144. package/dist/mcp/__tests__/wiki-server.test.js.map +1 -0
  145. package/dist/mcp/bootstrap.d.ts +19 -1
  146. package/dist/mcp/bootstrap.d.ts.map +1 -1
  147. package/dist/mcp/bootstrap.js +185 -0
  148. package/dist/mcp/bootstrap.js.map +1 -1
  149. package/dist/mcp/state-paths.d.ts +5 -0
  150. package/dist/mcp/state-paths.d.ts.map +1 -1
  151. package/dist/mcp/state-paths.js +41 -11
  152. package/dist/mcp/state-paths.js.map +1 -1
  153. package/dist/mcp/state-server.d.ts +4 -4
  154. package/dist/mcp/state-server.d.ts.map +1 -1
  155. package/dist/mcp/state-server.js +49 -2
  156. package/dist/mcp/state-server.js.map +1 -1
  157. package/dist/mcp/wiki-server.d.ts +181 -0
  158. package/dist/mcp/wiki-server.d.ts.map +1 -0
  159. package/dist/mcp/wiki-server.js +235 -0
  160. package/dist/mcp/wiki-server.js.map +1 -0
  161. package/dist/modes/__tests__/base-autoresearch-contract.test.js +74 -2
  162. package/dist/modes/__tests__/base-autoresearch-contract.test.js.map +1 -1
  163. package/dist/modes/__tests__/base-multi-state-compat.test.d.ts +2 -0
  164. package/dist/modes/__tests__/base-multi-state-compat.test.d.ts.map +1 -0
  165. package/dist/modes/__tests__/base-multi-state-compat.test.js +38 -0
  166. package/dist/modes/__tests__/base-multi-state-compat.test.js.map +1 -0
  167. package/dist/modes/__tests__/base-tmux-pane.test.js +1 -1
  168. package/dist/modes/__tests__/base-tmux-pane.test.js.map +1 -1
  169. package/dist/modes/base.d.ts +2 -1
  170. package/dist/modes/base.d.ts.map +1 -1
  171. package/dist/modes/base.js +55 -31
  172. package/dist/modes/base.js.map +1 -1
  173. package/dist/notifications/__tests__/formatter.test.js +11 -0
  174. package/dist/notifications/__tests__/formatter.test.js.map +1 -1
  175. package/dist/notifications/__tests__/idle-cooldown.test.js +32 -1
  176. package/dist/notifications/__tests__/idle-cooldown.test.js.map +1 -1
  177. package/dist/notifications/__tests__/index.test.d.ts +2 -0
  178. package/dist/notifications/__tests__/index.test.d.ts.map +1 -0
  179. package/dist/notifications/__tests__/index.test.js +113 -0
  180. package/dist/notifications/__tests__/index.test.js.map +1 -0
  181. package/dist/notifications/__tests__/lifecycle-dedupe.test.d.ts +2 -0
  182. package/dist/notifications/__tests__/lifecycle-dedupe.test.d.ts.map +1 -0
  183. package/dist/notifications/__tests__/lifecycle-dedupe.test.js +86 -0
  184. package/dist/notifications/__tests__/lifecycle-dedupe.test.js.map +1 -0
  185. package/dist/notifications/__tests__/reply-listener.test.js +174 -0
  186. package/dist/notifications/__tests__/reply-listener.test.js.map +1 -1
  187. package/dist/notifications/__tests__/session-idle-tail-dedupe.test.d.ts +2 -0
  188. package/dist/notifications/__tests__/session-idle-tail-dedupe.test.d.ts.map +1 -0
  189. package/dist/notifications/__tests__/session-idle-tail-dedupe.test.js +93 -0
  190. package/dist/notifications/__tests__/session-idle-tail-dedupe.test.js.map +1 -0
  191. package/dist/notifications/__tests__/session-registry.test.js +48 -1
  192. package/dist/notifications/__tests__/session-registry.test.js.map +1 -1
  193. package/dist/notifications/__tests__/session-status.test.d.ts +2 -0
  194. package/dist/notifications/__tests__/session-status.test.d.ts.map +1 -0
  195. package/dist/notifications/__tests__/session-status.test.js +159 -0
  196. package/dist/notifications/__tests__/session-status.test.js.map +1 -0
  197. package/dist/notifications/__tests__/tmux.test.js +58 -1
  198. package/dist/notifications/__tests__/tmux.test.js.map +1 -1
  199. package/dist/notifications/idle-cooldown.d.ts +11 -0
  200. package/dist/notifications/idle-cooldown.d.ts.map +1 -1
  201. package/dist/notifications/idle-cooldown.js +42 -8
  202. package/dist/notifications/idle-cooldown.js.map +1 -1
  203. package/dist/notifications/index.d.ts +1 -1
  204. package/dist/notifications/index.d.ts.map +1 -1
  205. package/dist/notifications/index.js +41 -8
  206. package/dist/notifications/index.js.map +1 -1
  207. package/dist/notifications/lifecycle-dedupe.d.ts +8 -0
  208. package/dist/notifications/lifecycle-dedupe.d.ts.map +1 -0
  209. package/dist/notifications/lifecycle-dedupe.js +112 -0
  210. package/dist/notifications/lifecycle-dedupe.js.map +1 -0
  211. package/dist/notifications/reply-listener.d.ts +10 -1
  212. package/dist/notifications/reply-listener.d.ts.map +1 -1
  213. package/dist/notifications/reply-listener.js +49 -11
  214. package/dist/notifications/reply-listener.js.map +1 -1
  215. package/dist/notifications/session-registry.d.ts.map +1 -1
  216. package/dist/notifications/session-registry.js +7 -1
  217. package/dist/notifications/session-registry.js.map +1 -1
  218. package/dist/notifications/session-status.d.ts +23 -0
  219. package/dist/notifications/session-status.d.ts.map +1 -0
  220. package/dist/notifications/session-status.js +187 -0
  221. package/dist/notifications/session-status.js.map +1 -0
  222. package/dist/notifications/tmux.d.ts +10 -0
  223. package/dist/notifications/tmux.d.ts.map +1 -1
  224. package/dist/notifications/tmux.js +59 -5
  225. package/dist/notifications/tmux.js.map +1 -1
  226. package/dist/notifications/types.d.ts +2 -0
  227. package/dist/notifications/types.d.ts.map +1 -1
  228. package/dist/openclaw/__tests__/index.test.js +84 -0
  229. package/dist/openclaw/__tests__/index.test.js.map +1 -1
  230. package/dist/openclaw/index.d.ts.map +1 -1
  231. package/dist/openclaw/index.js +7 -14
  232. package/dist/openclaw/index.js.map +1 -1
  233. package/dist/openclaw/types.d.ts +2 -2
  234. package/dist/openclaw/types.d.ts.map +1 -1
  235. package/dist/scripts/__tests__/codex-native-hook.test.js +692 -40
  236. package/dist/scripts/__tests__/codex-native-hook.test.js.map +1 -1
  237. package/dist/scripts/__tests__/hook-derived-watcher.test.d.ts +2 -0
  238. package/dist/scripts/__tests__/hook-derived-watcher.test.d.ts.map +1 -0
  239. package/dist/scripts/__tests__/hook-derived-watcher.test.js +87 -0
  240. package/dist/scripts/__tests__/hook-derived-watcher.test.js.map +1 -0
  241. package/dist/scripts/codex-native-hook.d.ts.map +1 -1
  242. package/dist/scripts/codex-native-hook.js +309 -77
  243. package/dist/scripts/codex-native-hook.js.map +1 -1
  244. package/dist/scripts/hook-derived-watcher.js +43 -1
  245. package/dist/scripts/hook-derived-watcher.js.map +1 -1
  246. package/dist/scripts/notify-fallback-watcher.js +95 -21
  247. package/dist/scripts/notify-fallback-watcher.js.map +1 -1
  248. package/dist/scripts/notify-hook/active-team.d.ts +9 -0
  249. package/dist/scripts/notify-hook/active-team.d.ts.map +1 -0
  250. package/dist/scripts/notify-hook/active-team.js +44 -0
  251. package/dist/scripts/notify-hook/active-team.js.map +1 -0
  252. package/dist/scripts/notify-hook/auto-nudge.d.ts +5 -3
  253. package/dist/scripts/notify-hook/auto-nudge.d.ts.map +1 -1
  254. package/dist/scripts/notify-hook/auto-nudge.js +121 -78
  255. package/dist/scripts/notify-hook/auto-nudge.js.map +1 -1
  256. package/dist/scripts/notify-hook/managed-tmux.d.ts.map +1 -1
  257. package/dist/scripts/notify-hook/managed-tmux.js +18 -4
  258. package/dist/scripts/notify-hook/managed-tmux.js.map +1 -1
  259. package/dist/scripts/notify-hook/operational-events.d.ts.map +1 -1
  260. package/dist/scripts/notify-hook/operational-events.js +21 -0
  261. package/dist/scripts/notify-hook/operational-events.js.map +1 -1
  262. package/dist/scripts/notify-hook/ralph-session-resume.d.ts.map +1 -1
  263. package/dist/scripts/notify-hook/ralph-session-resume.js +3 -2
  264. package/dist/scripts/notify-hook/ralph-session-resume.js.map +1 -1
  265. package/dist/scripts/notify-hook/state-io.d.ts +10 -1
  266. package/dist/scripts/notify-hook/state-io.d.ts.map +1 -1
  267. package/dist/scripts/notify-hook/state-io.js +56 -12
  268. package/dist/scripts/notify-hook/state-io.js.map +1 -1
  269. package/dist/scripts/notify-hook/team-dispatch.d.ts.map +1 -1
  270. package/dist/scripts/notify-hook/team-dispatch.js +305 -167
  271. package/dist/scripts/notify-hook/team-dispatch.js.map +1 -1
  272. package/dist/scripts/notify-hook/team-leader-nudge.d.ts.map +1 -1
  273. package/dist/scripts/notify-hook/team-leader-nudge.js +87 -15
  274. package/dist/scripts/notify-hook/team-leader-nudge.js.map +1 -1
  275. package/dist/scripts/notify-hook/tmux-injection.d.ts.map +1 -1
  276. package/dist/scripts/notify-hook/tmux-injection.js +11 -2
  277. package/dist/scripts/notify-hook/tmux-injection.js.map +1 -1
  278. package/dist/scripts/notify-hook.js +26 -16
  279. package/dist/scripts/notify-hook.js.map +1 -1
  280. package/dist/scripts/run-provider-advisor.js +20 -2
  281. package/dist/scripts/run-provider-advisor.js.map +1 -1
  282. package/dist/scripts/smoke-packed-install.d.ts +1 -8
  283. package/dist/scripts/smoke-packed-install.d.ts.map +1 -1
  284. package/dist/scripts/smoke-packed-install.js +12 -68
  285. package/dist/scripts/smoke-packed-install.js.map +1 -1
  286. package/dist/state/__tests__/operations.test.js +113 -0
  287. package/dist/state/__tests__/operations.test.js.map +1 -1
  288. package/dist/state/__tests__/skill-active.test.js +35 -0
  289. package/dist/state/__tests__/skill-active.test.js.map +1 -1
  290. package/dist/state/__tests__/workflow-transition.test.d.ts +2 -0
  291. package/dist/state/__tests__/workflow-transition.test.d.ts.map +1 -0
  292. package/dist/state/__tests__/workflow-transition.test.js +56 -0
  293. package/dist/state/__tests__/workflow-transition.test.js.map +1 -0
  294. package/dist/state/operations.d.ts +1 -1
  295. package/dist/state/operations.d.ts.map +1 -1
  296. package/dist/state/operations.js +88 -2
  297. package/dist/state/operations.js.map +1 -1
  298. package/dist/state/skill-active.d.ts +2 -2
  299. package/dist/state/skill-active.d.ts.map +1 -1
  300. package/dist/state/skill-active.js +119 -33
  301. package/dist/state/skill-active.js.map +1 -1
  302. package/dist/state/workflow-transition-reconcile.d.ts +15 -0
  303. package/dist/state/workflow-transition-reconcile.d.ts.map +1 -0
  304. package/dist/state/workflow-transition-reconcile.js +100 -0
  305. package/dist/state/workflow-transition-reconcile.js.map +1 -0
  306. package/dist/state/workflow-transition.d.ts +22 -0
  307. package/dist/state/workflow-transition.d.ts.map +1 -0
  308. package/dist/state/workflow-transition.js +188 -0
  309. package/dist/state/workflow-transition.js.map +1 -0
  310. package/dist/team/__tests__/api-interop.test.js +90 -0
  311. package/dist/team/__tests__/api-interop.test.js.map +1 -1
  312. package/dist/team/__tests__/current-task-baseline.test.d.ts +2 -0
  313. package/dist/team/__tests__/current-task-baseline.test.d.ts.map +1 -0
  314. package/dist/team/__tests__/current-task-baseline.test.js +87 -0
  315. package/dist/team/__tests__/current-task-baseline.test.js.map +1 -0
  316. package/dist/team/__tests__/hardening-e2e.test.js +17 -0
  317. package/dist/team/__tests__/hardening-e2e.test.js.map +1 -1
  318. package/dist/team/__tests__/runtime.test.js +673 -65
  319. package/dist/team/__tests__/runtime.test.js.map +1 -1
  320. package/dist/team/__tests__/shutdown-fallback.test.js +11 -1
  321. package/dist/team/__tests__/shutdown-fallback.test.js.map +1 -1
  322. package/dist/team/__tests__/tmux-session.test.js +447 -4
  323. package/dist/team/__tests__/tmux-session.test.js.map +1 -1
  324. package/dist/team/api-interop.d.ts.map +1 -1
  325. package/dist/team/api-interop.js +10 -1
  326. package/dist/team/api-interop.js.map +1 -1
  327. package/dist/team/current-task-baseline.d.ts +32 -0
  328. package/dist/team/current-task-baseline.d.ts.map +1 -0
  329. package/dist/team/current-task-baseline.js +85 -0
  330. package/dist/team/current-task-baseline.js.map +1 -0
  331. package/dist/team/delivery-log.d.ts +1 -1
  332. package/dist/team/delivery-log.d.ts.map +1 -1
  333. package/dist/team/delivery-log.js.map +1 -1
  334. package/dist/team/leader-activity.d.ts +1 -0
  335. package/dist/team/leader-activity.d.ts.map +1 -1
  336. package/dist/team/leader-activity.js +4 -2
  337. package/dist/team/leader-activity.js.map +1 -1
  338. package/dist/team/progress-evidence.d.ts +2 -0
  339. package/dist/team/progress-evidence.d.ts.map +1 -0
  340. package/dist/team/progress-evidence.js +77 -0
  341. package/dist/team/progress-evidence.js.map +1 -0
  342. package/dist/team/runtime.d.ts.map +1 -1
  343. package/dist/team/runtime.js +269 -64
  344. package/dist/team/runtime.js.map +1 -1
  345. package/dist/team/scaling.d.ts.map +1 -1
  346. package/dist/team/scaling.js +1 -1
  347. package/dist/team/scaling.js.map +1 -1
  348. package/dist/team/state.d.ts.map +1 -1
  349. package/dist/team/state.js +2 -13
  350. package/dist/team/state.js.map +1 -1
  351. package/dist/team/tmux-session.d.ts +12 -3
  352. package/dist/team/tmux-session.d.ts.map +1 -1
  353. package/dist/team/tmux-session.js +174 -20
  354. package/dist/team/tmux-session.js.map +1 -1
  355. package/dist/team/worktree.d.ts +6 -1
  356. package/dist/team/worktree.d.ts.map +1 -1
  357. package/dist/team/worktree.js +28 -4
  358. package/dist/team/worktree.js.map +1 -1
  359. package/dist/utils/__tests__/agents-md.test.js +21 -1
  360. package/dist/utils/__tests__/agents-md.test.js.map +1 -1
  361. package/dist/utils/__tests__/repo-deps.test.d.ts +2 -0
  362. package/dist/utils/__tests__/repo-deps.test.d.ts.map +1 -0
  363. package/dist/utils/__tests__/repo-deps.test.js +71 -0
  364. package/dist/utils/__tests__/repo-deps.test.js.map +1 -0
  365. package/dist/utils/agents-md.d.ts +1 -0
  366. package/dist/utils/agents-md.d.ts.map +1 -1
  367. package/dist/utils/agents-md.js +7 -3
  368. package/dist/utils/agents-md.js.map +1 -1
  369. package/dist/utils/paths.d.ts +4 -0
  370. package/dist/utils/paths.d.ts.map +1 -1
  371. package/dist/utils/paths.js +20 -0
  372. package/dist/utils/paths.js.map +1 -1
  373. package/dist/utils/repo-deps.d.ts +20 -0
  374. package/dist/utils/repo-deps.d.ts.map +1 -0
  375. package/dist/utils/repo-deps.js +78 -0
  376. package/dist/utils/repo-deps.js.map +1 -0
  377. package/dist/verification/__tests__/dev-merge-issue-close-workflow.test.d.ts +2 -0
  378. package/dist/verification/__tests__/dev-merge-issue-close-workflow.test.d.ts.map +1 -0
  379. package/dist/verification/__tests__/dev-merge-issue-close-workflow.test.js +54 -0
  380. package/dist/verification/__tests__/dev-merge-issue-close-workflow.test.js.map +1 -0
  381. package/dist/wiki/__tests__/cjk-tokenize.test.d.ts +12 -0
  382. package/dist/wiki/__tests__/cjk-tokenize.test.d.ts.map +1 -0
  383. package/dist/wiki/__tests__/cjk-tokenize.test.js +139 -0
  384. package/dist/wiki/__tests__/cjk-tokenize.test.js.map +1 -0
  385. package/dist/wiki/__tests__/crlf-parse.test.d.ts +2 -0
  386. package/dist/wiki/__tests__/crlf-parse.test.d.ts.map +1 -0
  387. package/dist/wiki/__tests__/crlf-parse.test.js +24 -0
  388. package/dist/wiki/__tests__/crlf-parse.test.js.map +1 -0
  389. package/dist/wiki/__tests__/escape-newline.test.d.ts +2 -0
  390. package/dist/wiki/__tests__/escape-newline.test.d.ts.map +1 -0
  391. package/dist/wiki/__tests__/escape-newline.test.js +45 -0
  392. package/dist/wiki/__tests__/escape-newline.test.js.map +1 -0
  393. package/dist/wiki/__tests__/ingest.test.d.ts +5 -0
  394. package/dist/wiki/__tests__/ingest.test.d.ts.map +1 -0
  395. package/dist/wiki/__tests__/ingest.test.js +181 -0
  396. package/dist/wiki/__tests__/ingest.test.js.map +1 -0
  397. package/dist/wiki/__tests__/lint.test.d.ts +5 -0
  398. package/dist/wiki/__tests__/lint.test.d.ts.map +1 -0
  399. package/dist/wiki/__tests__/lint.test.js +163 -0
  400. package/dist/wiki/__tests__/lint.test.js.map +1 -0
  401. package/dist/wiki/__tests__/query.test.d.ts +5 -0
  402. package/dist/wiki/__tests__/query.test.d.ts.map +1 -0
  403. package/dist/wiki/__tests__/query.test.js +141 -0
  404. package/dist/wiki/__tests__/query.test.js.map +1 -0
  405. package/dist/wiki/__tests__/reserved-file-guard.test.d.ts +2 -0
  406. package/dist/wiki/__tests__/reserved-file-guard.test.d.ts.map +1 -0
  407. package/dist/wiki/__tests__/reserved-file-guard.test.js +44 -0
  408. package/dist/wiki/__tests__/reserved-file-guard.test.js.map +1 -0
  409. package/dist/wiki/__tests__/session-hooks.test.d.ts +5 -0
  410. package/dist/wiki/__tests__/session-hooks.test.d.ts.map +1 -0
  411. package/dist/wiki/__tests__/session-hooks.test.js +36 -0
  412. package/dist/wiki/__tests__/session-hooks.test.js.map +1 -0
  413. package/dist/wiki/__tests__/slug-nonascii.test.d.ts +2 -0
  414. package/dist/wiki/__tests__/slug-nonascii.test.d.ts.map +1 -0
  415. package/dist/wiki/__tests__/slug-nonascii.test.js +24 -0
  416. package/dist/wiki/__tests__/slug-nonascii.test.js.map +1 -0
  417. package/dist/wiki/__tests__/storage.test.d.ts +5 -0
  418. package/dist/wiki/__tests__/storage.test.d.ts.map +1 -0
  419. package/dist/wiki/__tests__/storage.test.js +278 -0
  420. package/dist/wiki/__tests__/storage.test.js.map +1 -0
  421. package/dist/wiki/__tests__/test-helpers.d.ts +31 -0
  422. package/dist/wiki/__tests__/test-helpers.d.ts.map +1 -0
  423. package/dist/wiki/__tests__/test-helpers.js +108 -0
  424. package/dist/wiki/__tests__/test-helpers.js.map +1 -0
  425. package/dist/wiki/index.d.ts +14 -0
  426. package/dist/wiki/index.d.ts.map +1 -0
  427. package/dist/wiki/index.js +17 -0
  428. package/dist/wiki/index.js.map +1 -0
  429. package/dist/wiki/ingest.d.ts +20 -0
  430. package/dist/wiki/ingest.d.ts.map +1 -0
  431. package/dist/wiki/ingest.js +115 -0
  432. package/dist/wiki/ingest.js.map +1 -0
  433. package/dist/wiki/lifecycle.d.ts +20 -0
  434. package/dist/wiki/lifecycle.d.ts.map +1 -0
  435. package/dist/wiki/lifecycle.js +212 -0
  436. package/dist/wiki/lifecycle.js.map +1 -0
  437. package/dist/wiki/lint.d.ts +25 -0
  438. package/dist/wiki/lint.d.ts.map +1 -0
  439. package/dist/wiki/lint.js +166 -0
  440. package/dist/wiki/lint.js.map +1 -0
  441. package/dist/wiki/query.d.ts +36 -0
  442. package/dist/wiki/query.d.ts.map +1 -0
  443. package/dist/wiki/query.js +138 -0
  444. package/dist/wiki/query.js.map +1 -0
  445. package/dist/wiki/storage.d.ts +33 -0
  446. package/dist/wiki/storage.d.ts.map +1 -0
  447. package/dist/wiki/storage.js +321 -0
  448. package/dist/wiki/storage.js.map +1 -0
  449. package/dist/wiki/types.d.ts +83 -0
  450. package/dist/wiki/types.d.ts.map +1 -0
  451. package/dist/wiki/types.js +15 -0
  452. package/dist/wiki/types.js.map +1 -0
  453. package/package.json +3 -1
  454. package/skills/configure-notifications/SKILL.md +1 -0
  455. package/skills/doctor/SKILL.md +11 -0
  456. package/skills/omx-setup/SKILL.md +1 -1
  457. package/skills/wiki/SKILL.md +57 -0
  458. package/src/scripts/__tests__/codex-native-hook.test.ts +920 -56
  459. package/src/scripts/__tests__/hook-derived-watcher.test.ts +111 -0
  460. package/src/scripts/codex-native-hook.ts +377 -83
  461. package/src/scripts/hook-derived-watcher.ts +43 -1
  462. package/src/scripts/notify-fallback-watcher.ts +99 -20
  463. package/src/scripts/notify-hook/active-team.ts +54 -0
  464. package/src/scripts/notify-hook/auto-nudge.ts +132 -79
  465. package/src/scripts/notify-hook/managed-tmux.ts +22 -4
  466. package/src/scripts/notify-hook/operational-events.ts +21 -0
  467. package/src/scripts/notify-hook/ralph-session-resume.ts +3 -2
  468. package/src/scripts/notify-hook/state-io.ts +89 -12
  469. package/src/scripts/notify-hook/team-dispatch.ts +326 -168
  470. package/src/scripts/notify-hook/team-leader-nudge.ts +91 -14
  471. package/src/scripts/notify-hook/tmux-injection.ts +11 -2
  472. package/src/scripts/notify-hook.ts +36 -22
  473. package/src/scripts/run-provider-advisor.ts +20 -2
  474. package/src/scripts/smoke-packed-install.ts +16 -83
  475. package/templates/AGENTS.md +3 -4
@@ -19,6 +19,79 @@ async function withTempWorkingDir(run) {
19
19
  async function writeJson(path, value) {
20
20
  await writeFile(path, JSON.stringify(value, null, 2));
21
21
  }
22
+ async function writeCanonicalTeamFixture(cwd, { teamName, sessionId, ownerSessionId, coarseState = 'missing', }) {
23
+ const stateDir = join(cwd, '.omx', 'state');
24
+ const teamDir = join(stateDir, 'team', teamName);
25
+ const workersDir = join(teamDir, 'workers');
26
+ const nowIso = new Date().toISOString();
27
+ await mkdir(join(cwd, '.omx', 'logs'), { recursive: true });
28
+ await mkdir(workersDir, { recursive: true });
29
+ await writeJson(join(stateDir, 'session.json'), { session_id: sessionId });
30
+ if (coarseState !== 'missing') {
31
+ await writeJson(join(stateDir, 'team-state.json'), {
32
+ active: coarseState === 'active',
33
+ team_name: teamName,
34
+ current_phase: 'team-exec',
35
+ });
36
+ }
37
+ await writeJson(join(stateDir, 'hud-state.json'), {
38
+ last_turn_at: nowIso,
39
+ turn_count: 1,
40
+ });
41
+ await writeJson(join(teamDir, 'manifest.v2.json'), {
42
+ schema_version: 2,
43
+ name: teamName,
44
+ task: 'canonical notify fallback repro',
45
+ leader: {
46
+ session_id: ownerSessionId,
47
+ worker_id: 'leader-fixed',
48
+ role: 'coordinator',
49
+ },
50
+ policy: {
51
+ worker_launch_mode: 'interactive',
52
+ display_mode: 'split_pane',
53
+ dispatch_mode: 'hook_preferred_with_fallback',
54
+ dispatch_ack_timeout_ms: 2000,
55
+ },
56
+ governance: {
57
+ delegation_only: false,
58
+ plan_approval_required: false,
59
+ nested_teams_allowed: false,
60
+ one_team_per_leader_session: true,
61
+ cleanup_requires_all_workers_inactive: true,
62
+ },
63
+ lifecycle_profile: 'default',
64
+ permissions_snapshot: {
65
+ approval_mode: 'never',
66
+ sandbox_mode: 'danger-full-access',
67
+ network_access: true,
68
+ },
69
+ tmux_session: `${teamName}:0`,
70
+ leader_pane_id: '%97',
71
+ hud_pane_id: null,
72
+ resize_hook_name: null,
73
+ resize_hook_target: null,
74
+ worker_count: 2,
75
+ next_task_id: 1,
76
+ workers: [
77
+ { name: 'worker-1', index: 1, pane_id: '%1', role: 'executor' },
78
+ { name: 'worker-2', index: 2, pane_id: '%2', role: 'executor' },
79
+ ],
80
+ created_at: nowIso,
81
+ });
82
+ await writeJson(join(teamDir, 'phase.json'), {
83
+ current_phase: 'team-exec',
84
+ updated_at: nowIso,
85
+ transitions: [],
86
+ });
87
+ for (const worker of ['worker-1', 'worker-2']) {
88
+ await mkdir(join(workersDir, worker), { recursive: true });
89
+ await writeJson(join(workersDir, worker, 'status.json'), {
90
+ state: 'idle',
91
+ updated_at: nowIso,
92
+ });
93
+ }
94
+ }
22
95
  async function readTeamDeliveryLog(cwd) {
23
96
  const path = join(cwd, '.omx', 'logs', `team-delivery-${new Date().toISOString().slice(0, 10)}.jsonl`);
24
97
  const raw = await readFile(path, 'utf-8').catch(() => '');
@@ -139,7 +212,9 @@ describe('notify-hook leader-side authority handoff', () => {
139
212
  });
140
213
  await writeFile(fakeTmuxPath, buildFakeTmux(tmuxLogPath));
141
214
  await chmod(fakeTmuxPath, 0o755);
142
- const result = runNotifyHook(cwd, fakeBinDir);
215
+ const result = runNotifyHook(cwd, fakeBinDir, {
216
+ OMX_SESSION_ID: 'sess-canonical-missing',
217
+ });
143
218
  assert.equal(result.status, 0, `notify-hook failed: ${result.stderr || result.stdout}`);
144
219
  const tmuxLog = await readFile(tmuxLogPath, 'utf-8').catch(() => '');
145
220
  assert.match(tmuxLog, /send-keys/, 'current implementation nudges the leader directly in this stale-leader path');
@@ -161,7 +236,9 @@ describe('notify-hook leader-side authority handoff', () => {
161
236
  worker_index: 1,
162
237
  trigger_message: 'dispatch ping',
163
238
  }, cwd);
164
- const result = runNotifyHook(cwd, fakeBinDir);
239
+ const result = runNotifyHook(cwd, fakeBinDir, {
240
+ OMX_SESSION_ID: 'sess-canonical-inactive',
241
+ });
165
242
  assert.equal(result.status, 0, `notify-hook failed: ${result.stderr || result.stdout}`);
166
243
  const request = await readDispatchRequest('handoff-dispatch', queued.request.request_id, cwd);
167
244
  assert.equal(request?.status, 'failed');
@@ -211,7 +288,9 @@ describe('notify-hook leader-side authority handoff', () => {
211
288
  });
212
289
  await writeFile(fakeTmuxPath, buildFakeTmuxWithListPanes(tmuxLogPath, ['%10 12345']));
213
290
  await chmod(fakeTmuxPath, 0o755);
214
- const result = runNotifyHook(cwd, fakeBinDir);
291
+ const result = runNotifyHook(cwd, fakeBinDir, {
292
+ OMX_SESSION_ID: 'sess-current',
293
+ });
215
294
  assert.equal(result.status, 0, `notify-hook failed: ${result.stderr || result.stdout}`);
216
295
  if (existsSync(tmuxLogPath)) {
217
296
  const tmuxLog = await readFile(tmuxLogPath, 'utf-8');
@@ -261,7 +340,9 @@ describe('notify-hook team leader nudge', () => {
261
340
  });
262
341
  await writeFile(fakeTmuxPath, buildFakeTmux(tmuxLogPath));
263
342
  await chmod(fakeTmuxPath, 0o755);
264
- const result = runNotifyHook(cwd, fakeBinDir);
343
+ const result = runNotifyHook(cwd, fakeBinDir, {
344
+ OMX_SESSION_ID: 'sess-canonical-missing',
345
+ });
265
346
  assert.equal(result.status, 0, `notify-hook failed: ${result.stderr || result.stdout}`);
266
347
  const tmuxLog = await readFile(tmuxLogPath, 'utf-8').catch(() => '');
267
348
  assert.doesNotMatch(tmuxLog, /send-keys -t %97 -l Team deep-interview-suppressed:/);
@@ -311,7 +392,9 @@ describe('notify-hook team leader nudge', () => {
311
392
  });
312
393
  await writeFile(fakeTmuxPath, buildFakeTmux(tmuxLogPath));
313
394
  await chmod(fakeTmuxPath, 0o755);
314
- const result = runNotifyHook(cwd, fakeBinDir);
395
+ const result = runNotifyHook(cwd, fakeBinDir, {
396
+ OMX_SESSION_ID: 'sess-canonical-inactive',
397
+ });
315
398
  assert.equal(result.status, 0, `notify-hook failed: ${result.stderr || result.stdout}`);
316
399
  const tmuxLog = await readFile(tmuxLogPath, 'utf-8');
317
400
  assert.match(tmuxLog, /send-keys/);
@@ -384,7 +467,9 @@ describe('notify-hook team leader nudge', () => {
384
467
  }
385
468
  await writeFile(fakeTmuxPath, buildFakeTmuxWithListPanes(tmuxLogPath, ['%10 12345', '%11 12346']));
386
469
  await chmod(fakeTmuxPath, 0o755);
387
- const result = runNotifyHook(cwd, fakeBinDir);
470
+ const result = runNotifyHook(cwd, fakeBinDir, {
471
+ OMX_SESSION_ID: 'sess-canonical-missing',
472
+ });
388
473
  assert.equal(result.status, 0, `notify-hook failed: ${result.stderr || result.stdout}`);
389
474
  const tmuxLog = await readFile(tmuxLogPath, 'utf-8');
390
475
  assert.match(tmuxLog, /\[OMX\] All 2 workers idle\./);
@@ -444,7 +529,9 @@ describe('notify-hook team leader nudge', () => {
444
529
  }
445
530
  await writeFile(fakeTmuxPath, buildFakeTmuxWithListPanes(tmuxLogPath, ['%10 12345', '%11 12346']));
446
531
  await chmod(fakeTmuxPath, 0o755);
447
- const result = runNotifyHook(cwd, fakeBinDir);
532
+ const result = runNotifyHook(cwd, fakeBinDir, {
533
+ OMX_SESSION_ID: 'sess-canonical-inactive',
534
+ });
448
535
  assert.equal(result.status, 0, `notify-hook failed: ${result.stderr || result.stdout}`);
449
536
  const tmuxLog = await readFile(tmuxLogPath, 'utf-8');
450
537
  assert.match(tmuxLog, /\[OMX\] All 2 workers idle/);
@@ -566,6 +653,53 @@ describe('notify-hook team leader nudge', () => {
566
653
  assert.match(tmuxLog, /\[OMX\] All 2 workers idle/, 'global team-state fallback should still fire idle nudge');
567
654
  });
568
655
  });
656
+ it('falls back to canonical team state when coarse team-state is missing', async () => {
657
+ await withTempWorkingDir(async (cwd) => {
658
+ const fakeBinDir = join(cwd, 'fake-bin');
659
+ const fakeTmuxPath = join(fakeBinDir, 'tmux');
660
+ const tmuxLogPath = join(cwd, 'tmux.log');
661
+ await mkdir(fakeBinDir, { recursive: true });
662
+ await writeCanonicalTeamFixture(cwd, {
663
+ teamName: 'canonical-missing',
664
+ sessionId: 'sess-canonical-missing',
665
+ ownerSessionId: 'sess-canonical-missing',
666
+ });
667
+ await writeFile(fakeTmuxPath, buildFakeTmux(tmuxLogPath));
668
+ await chmod(fakeTmuxPath, 0o755);
669
+ const result = runNotifyHook(cwd, fakeBinDir, {
670
+ OMX_SESSION_ID: 'sess-canonical-missing',
671
+ });
672
+ assert.equal(result.status, 0, `notify-hook failed: ${result.stderr || result.stdout}`);
673
+ const tmuxLog = await readFile(tmuxLogPath, 'utf-8');
674
+ assert.match(tmuxLog, /send-keys/);
675
+ assert.match(tmuxLog, /-t %97/, 'should target canonical leader pane');
676
+ assert.match(tmuxLog, /\[OMX\] All 2 workers idle/, 'canonical fallback should still fire idle nudge');
677
+ });
678
+ });
679
+ it('falls back to canonical team state when coarse team-state is inactive', async () => {
680
+ await withTempWorkingDir(async (cwd) => {
681
+ const fakeBinDir = join(cwd, 'fake-bin');
682
+ const fakeTmuxPath = join(fakeBinDir, 'tmux');
683
+ const tmuxLogPath = join(cwd, 'tmux.log');
684
+ await mkdir(fakeBinDir, { recursive: true });
685
+ await writeCanonicalTeamFixture(cwd, {
686
+ teamName: 'canonical-inactive',
687
+ sessionId: 'sess-canonical-inactive',
688
+ ownerSessionId: 'sess-canonical-inactive',
689
+ coarseState: 'inactive',
690
+ });
691
+ await writeFile(fakeTmuxPath, buildFakeTmux(tmuxLogPath));
692
+ await chmod(fakeTmuxPath, 0o755);
693
+ const result = runNotifyHook(cwd, fakeBinDir, {
694
+ OMX_SESSION_ID: 'sess-canonical-inactive',
695
+ });
696
+ assert.equal(result.status, 0, `notify-hook failed: ${result.stderr || result.stdout}`);
697
+ const tmuxLog = await readFile(tmuxLogPath, 'utf-8');
698
+ assert.match(tmuxLog, /send-keys/);
699
+ assert.match(tmuxLog, /-t %97/, 'should still target canonical leader pane');
700
+ assert.match(tmuxLog, /\[OMX\] All 2 workers idle/, 'inactive coarse state should still fall back canonically');
701
+ });
702
+ });
569
703
  it('nudges leader via tmux send-keys when team is active and mailbox has messages', async () => {
570
704
  await withTempWorkingDir(async (cwd) => {
571
705
  const omxDir = join(cwd, '.omx');
@@ -1158,6 +1292,103 @@ exit 0
1158
1292
  }
1159
1293
  });
1160
1294
  });
1295
+ it('suppresses duplicate visible leader injection when the pane already shows the same classified state', async () => {
1296
+ await withTempWorkingDir(async (cwd) => {
1297
+ const omxDir = join(cwd, '.omx');
1298
+ const stateDir = join(omxDir, 'state');
1299
+ const logsDir = join(omxDir, 'logs');
1300
+ const teamName = 'same-classified-state';
1301
+ const teamDir = join(stateDir, 'team', teamName);
1302
+ const mailboxDir = join(teamDir, 'mailbox');
1303
+ const fakeBinDir = join(cwd, 'fake-bin');
1304
+ const fakeTmuxPath = join(fakeBinDir, 'tmux');
1305
+ const tmuxLogPath = join(cwd, 'tmux.log');
1306
+ await mkdir(logsDir, { recursive: true });
1307
+ await mkdir(mailboxDir, { recursive: true });
1308
+ await mkdir(fakeBinDir, { recursive: true });
1309
+ await writeJson(join(stateDir, 'team-state.json'), {
1310
+ active: true,
1311
+ team_name: teamName,
1312
+ current_phase: 'team-exec',
1313
+ });
1314
+ await writeJson(join(teamDir, 'config.json'), {
1315
+ name: teamName,
1316
+ tmux_session: 'same-classified-state:0',
1317
+ leader_pane_id: '%75',
1318
+ });
1319
+ await writeJson(join(mailboxDir, 'leader-fixed.json'), {
1320
+ worker: 'leader-fixed',
1321
+ messages: [
1322
+ {
1323
+ message_id: 'same-classified-msg',
1324
+ from_worker: 'worker-1',
1325
+ to_worker: 'leader-fixed',
1326
+ body: 'please review latest output',
1327
+ created_at: '2026-03-12T00:00:00.000Z',
1328
+ },
1329
+ ],
1330
+ });
1331
+ const fakeTmux = `#!/usr/bin/env bash
1332
+ set -eu
1333
+ echo "$@" >> "${tmuxLogPath}"
1334
+ cmd="$1"
1335
+ shift || true
1336
+ if [[ "$cmd" == "display-message" ]]; then
1337
+ target=""
1338
+ format=""
1339
+ while (($#)); do
1340
+ case "$1" in
1341
+ -p) shift ;;
1342
+ -t) target="$2"; shift 2 ;;
1343
+ *) format="$1"; shift ;;
1344
+ esac
1345
+ done
1346
+ if [[ "$format" == "#{pane_in_mode}" && "$target" == "%75" ]]; then
1347
+ echo "0"
1348
+ exit 0
1349
+ fi
1350
+ if [[ "$format" == "#{pane_current_command}" && "$target" == "%75" ]]; then
1351
+ echo "codex"
1352
+ exit 0
1353
+ fi
1354
+ exit 0
1355
+ fi
1356
+ if [[ "$cmd" == "capture-pane" ]]; then
1357
+ cat <<'EOF'
1358
+ Team same-classified-state: 1 msg(s) for leader. Next: read messages; keep orchestrating; if done, gracefully shut down: omx team shutdown same-classified-state.
1359
+ EOF
1360
+ exit 0
1361
+ fi
1362
+ if [[ "$cmd" == "send-keys" ]]; then
1363
+ exit 0
1364
+ fi
1365
+ if [[ "$cmd" == "list-panes" ]]; then
1366
+ echo "%1 12345"
1367
+ exit 0
1368
+ fi
1369
+ exit 0
1370
+ `;
1371
+ await writeFile(fakeTmuxPath, fakeTmux);
1372
+ await chmod(fakeTmuxPath, 0o755);
1373
+ const result = runNotifyHook(cwd, fakeBinDir);
1374
+ assert.equal(result.status, 0, `notify-hook failed: ${result.stderr || result.stdout}`);
1375
+ const tmuxLog = await readFile(tmuxLogPath, 'utf-8');
1376
+ assert.match(tmuxLog, /capture-pane -t %75 -p -S -80/);
1377
+ assert.doesNotMatch(tmuxLog, /send-keys -t %75 -l Team same-classified-state: 1 msg\(s\) for leader\./, 'same visible classified state should not be reinjected');
1378
+ const eventsPath = join(teamDir, 'events', 'events.ndjson');
1379
+ const events = (await readFile(eventsPath, 'utf-8')).trim().split('\n').filter(Boolean).map((line) => JSON.parse(line));
1380
+ const nudgeEvent = events.find((entry) => entry.type === 'team_leader_nudge' && entry.reason === 'new_mailbox_message');
1381
+ assert.ok(nudgeEvent, 'suppressed visible sends should still emit leader nudge events');
1382
+ assert.equal(nudgeEvent.orchestration_intent, 'pending-mailbox-review');
1383
+ const deliveryLog = await readTeamDeliveryLog(cwd);
1384
+ assert.ok(deliveryLog.some((entry) => entry.event === 'nudge_triggered'
1385
+ && entry.team === teamName
1386
+ && entry.to_worker === 'leader-fixed'
1387
+ && entry.result === 'suppressed'
1388
+ && entry.reason === 'new_mailbox_message'
1389
+ && entry.suppression_reason === 'pane_already_shows_same_classified_state'));
1390
+ });
1391
+ });
1161
1392
  it('does not inject leader nudge while leader pane is in copy-mode', async () => {
1162
1393
  await withTempWorkingDir(async (cwd) => {
1163
1394
  const omxDir = join(cwd, '.omx');
@@ -1394,6 +1625,27 @@ exit 0
1394
1625
  }
1395
1626
  });
1396
1627
  });
1628
+ it('does not nudge a canonical-only team when owner session is blank', async () => {
1629
+ await withTempWorkingDir(async (cwd) => {
1630
+ const fakeBinDir = join(cwd, 'fake-bin');
1631
+ const fakeTmuxPath = join(fakeBinDir, 'tmux');
1632
+ const tmuxLogPath = join(cwd, 'tmux.log');
1633
+ await mkdir(fakeBinDir, { recursive: true });
1634
+ await writeCanonicalTeamFixture(cwd, {
1635
+ teamName: 'ownerless-team',
1636
+ sessionId: 'sess-current',
1637
+ ownerSessionId: '',
1638
+ });
1639
+ await writeFile(fakeTmuxPath, buildFakeTmux(tmuxLogPath));
1640
+ await chmod(fakeTmuxPath, 0o755);
1641
+ const result = runNotifyHook(cwd, fakeBinDir);
1642
+ assert.equal(result.status, 0, `notify-hook failed: ${result.stderr || result.stdout}`);
1643
+ if (existsSync(tmuxLogPath)) {
1644
+ const tmuxLog = await readFile(tmuxLogPath, 'utf-8');
1645
+ assert.doesNotMatch(tmuxLog, /send-keys/, 'ownerless canonical teams must not nudge');
1646
+ }
1647
+ });
1648
+ });
1397
1649
  it('nudges when worker panes are alive and leader is stale (no recent HUD turn)', async () => {
1398
1650
  await withTempWorkingDir(async (cwd) => {
1399
1651
  const omxDir = join(cwd, '.omx');
@@ -2130,6 +2382,102 @@ exit 0
2130
2382
  assert.match(finalLog, /Team stale-cadence: leader stale, \d+ worker pane\(s\) still active\./);
2131
2383
  });
2132
2384
  });
2385
+ it('suppresses stale leader follow-up when detached worktree progress is still fresh', async () => {
2386
+ await withTempWorkingDir(async (cwd) => {
2387
+ const omxDir = join(cwd, '.omx');
2388
+ const stateDir = join(omxDir, 'state');
2389
+ const logsDir = join(omxDir, 'logs');
2390
+ const teamName = 'fresh-detached-progress';
2391
+ const teamDir = join(stateDir, 'team', teamName);
2392
+ const workersDir = join(teamDir, 'workers');
2393
+ const tasksDir = join(teamDir, 'tasks');
2394
+ const workerWorktree = join(cwd, 'worktrees', 'worker-1');
2395
+ const fakeBinDir = join(cwd, 'fake-bin');
2396
+ const fakeTmuxPath = join(fakeBinDir, 'tmux');
2397
+ const tmuxLogPath = join(cwd, 'tmux.log');
2398
+ await mkdir(logsDir, { recursive: true });
2399
+ await mkdir(tasksDir, { recursive: true });
2400
+ await mkdir(join(workersDir, 'worker-1'), { recursive: true });
2401
+ await mkdir(join(workerWorktree, '.omx', 'state'), { recursive: true });
2402
+ await mkdir(fakeBinDir, { recursive: true });
2403
+ await writeJson(join(stateDir, 'team-state.json'), {
2404
+ active: true,
2405
+ team_name: teamName,
2406
+ current_phase: 'team-exec',
2407
+ });
2408
+ await writeJson(join(teamDir, 'config.json'), {
2409
+ name: teamName,
2410
+ tmux_session: 'omx-team-fresh-detached-progress',
2411
+ leader_pane_id: '%99',
2412
+ workers: [
2413
+ { name: 'worker-1', index: 1, pane_id: '%10', worktree_path: workerWorktree },
2414
+ ],
2415
+ });
2416
+ await writeJson(join(stateDir, 'hud-state.json'), {
2417
+ last_turn_at: new Date(Date.now() - 300_000).toISOString(),
2418
+ turn_count: 5,
2419
+ });
2420
+ await writeJson(join(tasksDir, 'task-1.json'), {
2421
+ id: '1',
2422
+ subject: 'Apply detached fix',
2423
+ description: 'keep going',
2424
+ status: 'in_progress',
2425
+ owner: 'worker-1',
2426
+ created_at: new Date(Date.now() - 300_000).toISOString(),
2427
+ });
2428
+ await writeJson(join(workersDir, 'worker-1', 'status.json'), {
2429
+ state: 'working',
2430
+ current_task_id: '1',
2431
+ updated_at: new Date(Date.now() - 300_000).toISOString(),
2432
+ });
2433
+ await writeJson(join(workersDir, 'worker-1', 'heartbeat.json'), {
2434
+ last_turn_at: new Date(Date.now() - 300_000).toISOString(),
2435
+ turn_count: 2,
2436
+ alive: true,
2437
+ });
2438
+ await writeJson(join(stateDir, 'team-leader-nudge.json'), {
2439
+ last_nudged_by_team: {
2440
+ [teamName]: {
2441
+ at: new Date(Date.now() - 60_000).toISOString(),
2442
+ last_message_id: '',
2443
+ reason: 'stale_leader_panes_alive',
2444
+ },
2445
+ },
2446
+ progress_by_team: {
2447
+ [teamName]: {
2448
+ signature: JSON.stringify({
2449
+ tasks: [{ id: '1', owner: 'worker-1', status: 'in_progress' }],
2450
+ workers: [{
2451
+ worker: 'worker-1',
2452
+ state: 'working',
2453
+ current_task_id: '1',
2454
+ status_missing: false,
2455
+ turn_count: 2,
2456
+ heartbeat_missing: false,
2457
+ }],
2458
+ }),
2459
+ last_progress_at: new Date(Date.now() - 300_000).toISOString(),
2460
+ },
2461
+ },
2462
+ });
2463
+ await writeJson(join(workerWorktree, '.omx', 'state', 'current-task-baseline.json'), {
2464
+ version: 1,
2465
+ tasks: [],
2466
+ });
2467
+ await writeFile(fakeTmuxPath, buildFakeTmuxWithListPanes(tmuxLogPath, ['%10 12345']));
2468
+ await chmod(fakeTmuxPath, 0o755);
2469
+ const result = runNotifyHook(cwd, fakeBinDir, {
2470
+ OMX_TEAM_PROGRESS_STALL_MS: '60000',
2471
+ OMX_TEAM_LEADER_NUDGE_MS: '30000',
2472
+ OMX_TEAM_LEADER_STALE_MS: '60000',
2473
+ });
2474
+ assert.equal(result.status, 0, `notify-hook failed: ${result.stderr || result.stdout}`);
2475
+ if (existsSync(tmuxLogPath)) {
2476
+ const tmuxLog = await readFile(tmuxLogPath, 'utf-8');
2477
+ assert.doesNotMatch(tmuxLog, /Team fresh-detached-progress: leader stale,/);
2478
+ }
2479
+ });
2480
+ });
2133
2481
  it('emits team_leader_nudge event to events.ndjson when nudge fires', async () => {
2134
2482
  await withTempWorkingDir(async (cwd) => {
2135
2483
  const omxDir = join(cwd, '.omx');