oh-my-codex 0.21.4 → 0.21.5

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 (240) hide show
  1. package/Cargo.lock +6 -6
  2. package/Cargo.toml +1 -1
  3. package/README.md +4 -0
  4. package/crates/omx-runtime/src/lease_mutex.rs +379 -0
  5. package/crates/omx-runtime/src/main.rs +4 -0
  6. package/crates/omx-runtime/tests/execution.rs +152 -0
  7. package/dist/cli/__tests__/doctor-process-identity-readiness.test.js +1 -1
  8. package/dist/cli/__tests__/doctor-process-identity-readiness.test.js.map +1 -1
  9. package/dist/cli/__tests__/doctor-state-root-binding.test.js +36 -0
  10. package/dist/cli/__tests__/doctor-state-root-binding.test.js.map +1 -1
  11. package/dist/cli/__tests__/doctor-warning-copy.test.js +100 -4
  12. package/dist/cli/__tests__/doctor-warning-copy.test.js.map +1 -1
  13. package/dist/cli/__tests__/index.test.js +87 -7
  14. package/dist/cli/__tests__/index.test.js.map +1 -1
  15. package/dist/cli/__tests__/package-bin-contract.test.js +1 -1
  16. package/dist/cli/__tests__/package-bin-contract.test.js.map +1 -1
  17. package/dist/cli/__tests__/plugin-cache-symlink-provenance-3552.test.js +17 -13
  18. package/dist/cli/__tests__/plugin-cache-symlink-provenance-3552.test.js.map +1 -1
  19. package/dist/cli/__tests__/setup-agents-overwrite.test.js +11 -9
  20. package/dist/cli/__tests__/setup-agents-overwrite.test.js.map +1 -1
  21. package/dist/cli/__tests__/setup-install-mode.test.js +66 -0
  22. package/dist/cli/__tests__/setup-install-mode.test.js.map +1 -1
  23. package/dist/cli/__tests__/setup-reasoning-preserve.test.d.ts +2 -0
  24. package/dist/cli/__tests__/setup-reasoning-preserve.test.d.ts.map +1 -0
  25. package/dist/cli/__tests__/setup-reasoning-preserve.test.js +198 -0
  26. package/dist/cli/__tests__/setup-reasoning-preserve.test.js.map +1 -0
  27. package/dist/cli/__tests__/team.test.js +67 -1
  28. package/dist/cli/__tests__/team.test.js.map +1 -1
  29. package/dist/cli/__tests__/update.test.js +47 -0
  30. package/dist/cli/__tests__/update.test.js.map +1 -1
  31. package/dist/cli/codex-feature-probe.d.ts +2 -1
  32. package/dist/cli/codex-feature-probe.d.ts.map +1 -1
  33. package/dist/cli/codex-feature-probe.js +4 -2
  34. package/dist/cli/codex-feature-probe.js.map +1 -1
  35. package/dist/cli/codex-home.d.ts +14 -0
  36. package/dist/cli/codex-home.d.ts.map +1 -1
  37. package/dist/cli/codex-home.js +21 -0
  38. package/dist/cli/codex-home.js.map +1 -1
  39. package/dist/cli/doctor.d.ts +2 -0
  40. package/dist/cli/doctor.d.ts.map +1 -1
  41. package/dist/cli/doctor.js +122 -9
  42. package/dist/cli/doctor.js.map +1 -1
  43. package/dist/cli/index.d.ts.map +1 -1
  44. package/dist/cli/index.js +27 -6
  45. package/dist/cli/index.js.map +1 -1
  46. package/dist/cli/launch-credential-provenance.d.ts +49 -0
  47. package/dist/cli/launch-credential-provenance.d.ts.map +1 -0
  48. package/dist/cli/launch-credential-provenance.js +163 -0
  49. package/dist/cli/launch-credential-provenance.js.map +1 -0
  50. package/dist/cli/setup.d.ts.map +1 -1
  51. package/dist/cli/setup.js +11 -3
  52. package/dist/cli/setup.js.map +1 -1
  53. package/dist/cli/team.d.ts.map +1 -1
  54. package/dist/cli/team.js +23 -7
  55. package/dist/cli/team.js.map +1 -1
  56. package/dist/cli/uninstall.js +1 -1
  57. package/dist/cli/uninstall.js.map +1 -1
  58. package/dist/cli/update.d.ts.map +1 -1
  59. package/dist/cli/update.js +7 -0
  60. package/dist/cli/update.js.map +1 -1
  61. package/dist/compat/__tests__/doctor-contract.test.js +5 -1
  62. package/dist/compat/__tests__/doctor-contract.test.js.map +1 -1
  63. package/dist/config/__tests__/codex-feature-flags.test.js +10 -1
  64. package/dist/config/__tests__/codex-feature-flags.test.js.map +1 -1
  65. package/dist/config/__tests__/generator-notify.test.js +9 -0
  66. package/dist/config/__tests__/generator-notify.test.js.map +1 -1
  67. package/dist/config/codex-feature-flags.d.ts +4 -0
  68. package/dist/config/codex-feature-flags.d.ts.map +1 -1
  69. package/dist/config/codex-feature-flags.js +17 -1
  70. package/dist/config/codex-feature-flags.js.map +1 -1
  71. package/dist/config/generator.d.ts +8 -1
  72. package/dist/config/generator.d.ts.map +1 -1
  73. package/dist/config/generator.js +23 -9
  74. package/dist/config/generator.js.map +1 -1
  75. package/dist/hooks/__tests__/anti-slop-workflow.test.js +2 -2
  76. package/dist/hooks/__tests__/anti-slop-workflow.test.js.map +1 -1
  77. package/dist/hooks/__tests__/code-review-skill-contract.test.js +3 -3
  78. package/dist/hooks/__tests__/code-review-skill-contract.test.js.map +1 -1
  79. package/dist/hooks/__tests__/deep-interview-contract.test.js +6 -7
  80. package/dist/hooks/__tests__/deep-interview-contract.test.js.map +1 -1
  81. package/dist/hooks/__tests__/notify-hook-auto-nudge.test.js +16 -33
  82. package/dist/hooks/__tests__/notify-hook-auto-nudge.test.js.map +1 -1
  83. package/dist/hooks/__tests__/notify-hook-tmux-heal.test.js +9 -29
  84. package/dist/hooks/__tests__/notify-hook-tmux-heal.test.js.map +1 -1
  85. package/dist/hooks/__tests__/notify-hook-tmux-scrollback.test.js +9 -29
  86. package/dist/hooks/__tests__/notify-hook-tmux-scrollback.test.js.map +1 -1
  87. package/dist/hooks/__tests__/prompt-guidance-contract.test.js +10 -7
  88. package/dist/hooks/__tests__/prompt-guidance-contract.test.js.map +1 -1
  89. package/dist/hooks/__tests__/session.test.js +41 -6
  90. package/dist/hooks/__tests__/session.test.js.map +1 -1
  91. package/dist/hooks/__tests__/sunset-routing-contract.test.js +23 -0
  92. package/dist/hooks/__tests__/sunset-routing-contract.test.js.map +1 -1
  93. package/dist/hooks/__tests__/visual-ralph-skill.test.js +3 -3
  94. package/dist/hooks/__tests__/visual-ralph-skill.test.js.map +1 -1
  95. package/dist/hooks/session.d.ts +1 -1
  96. package/dist/hooks/session.d.ts.map +1 -1
  97. package/dist/hooks/session.js +85 -29
  98. package/dist/hooks/session.js.map +1 -1
  99. package/dist/hud/__tests__/index.test.js +130 -2
  100. package/dist/hud/__tests__/index.test.js.map +1 -1
  101. package/dist/hud/__tests__/reconcile.test.js +76 -0
  102. package/dist/hud/__tests__/reconcile.test.js.map +1 -1
  103. package/dist/hud/__tests__/render.test.js +155 -4
  104. package/dist/hud/__tests__/render.test.js.map +1 -1
  105. package/dist/hud/__tests__/roster-height-realtmux.test.d.ts +2 -0
  106. package/dist/hud/__tests__/roster-height-realtmux.test.d.ts.map +1 -0
  107. package/dist/hud/__tests__/roster-height-realtmux.test.js +70 -0
  108. package/dist/hud/__tests__/roster-height-realtmux.test.js.map +1 -0
  109. package/dist/hud/__tests__/state.test.js +92 -1
  110. package/dist/hud/__tests__/state.test.js.map +1 -1
  111. package/dist/hud/__tests__/tmux.test.js +24 -1
  112. package/dist/hud/__tests__/tmux.test.js.map +1 -1
  113. package/dist/hud/index.d.ts +3 -0
  114. package/dist/hud/index.d.ts.map +1 -1
  115. package/dist/hud/index.js +40 -18
  116. package/dist/hud/index.js.map +1 -1
  117. package/dist/hud/reconcile.d.ts +1 -0
  118. package/dist/hud/reconcile.d.ts.map +1 -1
  119. package/dist/hud/reconcile.js +9 -3
  120. package/dist/hud/reconcile.js.map +1 -1
  121. package/dist/hud/render.d.ts +1 -1
  122. package/dist/hud/render.d.ts.map +1 -1
  123. package/dist/hud/render.js +81 -7
  124. package/dist/hud/render.js.map +1 -1
  125. package/dist/hud/state.d.ts.map +1 -1
  126. package/dist/hud/state.js +38 -3
  127. package/dist/hud/state.js.map +1 -1
  128. package/dist/hud/tmux.d.ts +4 -0
  129. package/dist/hud/tmux.d.ts.map +1 -1
  130. package/dist/hud/tmux.js +66 -12
  131. package/dist/hud/tmux.js.map +1 -1
  132. package/dist/hud/types.d.ts +9 -0
  133. package/dist/hud/types.d.ts.map +1 -1
  134. package/dist/hud/types.js.map +1 -1
  135. package/dist/leader/__tests__/contract.test.js +9 -2
  136. package/dist/leader/__tests__/contract.test.js.map +1 -1
  137. package/dist/leader/contract.d.ts +2 -2
  138. package/dist/leader/contract.d.ts.map +1 -1
  139. package/dist/leader/contract.js +2 -2
  140. package/dist/leader/contract.js.map +1 -1
  141. package/dist/ralplan/__tests__/advisory-activation.test.js +3 -3
  142. package/dist/ralplan/__tests__/advisory-activation.test.js.map +1 -1
  143. package/dist/runtime/__tests__/bridge.test.js +91 -23
  144. package/dist/runtime/__tests__/bridge.test.js.map +1 -1
  145. package/dist/runtime/bridge.d.ts.map +1 -1
  146. package/dist/runtime/bridge.js +7 -1
  147. package/dist/runtime/bridge.js.map +1 -1
  148. package/dist/scripts/__tests__/codex-native-hook.test.js +10 -5
  149. package/dist/scripts/__tests__/codex-native-hook.test.js.map +1 -1
  150. package/dist/scripts/__tests__/prompt-inventory.test.js +83 -2
  151. package/dist/scripts/__tests__/prompt-inventory.test.js.map +1 -1
  152. package/dist/scripts/__tests__/smoke-packed-install.test.js +75 -23
  153. package/dist/scripts/__tests__/smoke-packed-install.test.js.map +1 -1
  154. package/dist/scripts/prompt-inventory.d.ts +17 -0
  155. package/dist/scripts/prompt-inventory.d.ts.map +1 -1
  156. package/dist/scripts/prompt-inventory.js +64 -11
  157. package/dist/scripts/prompt-inventory.js.map +1 -1
  158. package/dist/scripts/run-test-files.js +1 -0
  159. package/dist/scripts/run-test-files.js.map +1 -1
  160. package/dist/scripts/smoke-packed-install.d.ts +1 -1
  161. package/dist/scripts/smoke-packed-install.d.ts.map +1 -1
  162. package/dist/scripts/smoke-packed-install.js +46 -17
  163. package/dist/scripts/smoke-packed-install.js.map +1 -1
  164. package/dist/state/__tests__/mode-binding-lease.test.js +58 -2
  165. package/dist/state/__tests__/mode-binding-lease.test.js.map +1 -1
  166. package/dist/state/mode-binding-lease-helper.js +9 -41
  167. package/dist/state/mode-binding-lease-helper.js.map +1 -1
  168. package/dist/state/mode-binding-lease.d.ts.map +1 -1
  169. package/dist/state/mode-binding-lease.js +32 -10
  170. package/dist/state/mode-binding-lease.js.map +1 -1
  171. package/dist/team/__tests__/runtime.test.js +14 -1
  172. package/dist/team/__tests__/runtime.test.js.map +1 -1
  173. package/dist/team/__tests__/scaling.test.js +5 -1
  174. package/dist/team/__tests__/scaling.test.js.map +1 -1
  175. package/dist/team/__tests__/tmux-session.test.js +60 -0
  176. package/dist/team/__tests__/tmux-session.test.js.map +1 -1
  177. package/dist/team/__tests__/tmux-source-authority-realtmux.test.js +2 -2
  178. package/dist/team/__tests__/tmux-source-authority-realtmux.test.js.map +1 -1
  179. package/dist/team/runtime-cli.js +4 -2
  180. package/dist/team/runtime-cli.js.map +1 -1
  181. package/dist/team/runtime.d.ts +11 -0
  182. package/dist/team/runtime.d.ts.map +1 -1
  183. package/dist/team/runtime.js +24 -3
  184. package/dist/team/runtime.js.map +1 -1
  185. package/dist/team/scaling.d.ts.map +1 -1
  186. package/dist/team/scaling.js +11 -5
  187. package/dist/team/scaling.js.map +1 -1
  188. package/dist/team/state.d.ts +1 -1
  189. package/dist/team/state.d.ts.map +1 -1
  190. package/dist/team/state.js +5 -3
  191. package/dist/team/state.js.map +1 -1
  192. package/dist/team/team-identity.d.ts.map +1 -1
  193. package/dist/team/team-identity.js +8 -14
  194. package/dist/team/team-identity.js.map +1 -1
  195. package/dist/team/tmux-session.d.ts.map +1 -1
  196. package/dist/team/tmux-session.js +17 -8
  197. package/dist/team/tmux-session.js.map +1 -1
  198. package/dist/ultragoal/__tests__/artifacts.test.js +43 -0
  199. package/dist/ultragoal/__tests__/artifacts.test.js.map +1 -1
  200. package/dist/ultragoal/artifacts.d.ts +1 -0
  201. package/dist/ultragoal/artifacts.d.ts.map +1 -1
  202. package/dist/ultragoal/artifacts.js +9 -5
  203. package/dist/ultragoal/artifacts.js.map +1 -1
  204. package/dist/utils/__tests__/platform-command.test.js +10 -0
  205. package/dist/utils/__tests__/platform-command.test.js.map +1 -1
  206. package/dist/utils/platform-command.d.ts.map +1 -1
  207. package/dist/utils/platform-command.js +10 -4
  208. package/dist/utils/platform-command.js.map +1 -1
  209. package/dist/utils/toml.d.ts.map +1 -1
  210. package/dist/utils/toml.js +4 -3
  211. package/dist/utils/toml.js.map +1 -1
  212. package/dist/verification/__tests__/ci-rust-gates.test.js +11 -0
  213. package/dist/verification/__tests__/ci-rust-gates.test.js.map +1 -1
  214. package/package.json +2 -2
  215. package/plugins/oh-my-codex/.codex-plugin/plugin.json +1 -1
  216. package/plugins/oh-my-codex/skills/ai-slop-cleaner/SKILL.md +2 -3
  217. package/plugins/oh-my-codex/skills/best-practice-research/SKILL.md +1 -1
  218. package/plugins/oh-my-codex/skills/code-review/SKILL.md +1 -1
  219. package/plugins/oh-my-codex/skills/deep-interview/SKILL.md +13 -22
  220. package/plugins/oh-my-codex/skills/design/SKILL.md +1 -1
  221. package/plugins/oh-my-codex/skills/hud/SKILL.md +1 -1
  222. package/plugins/oh-my-codex/skills/plan/SKILL.md +1 -1
  223. package/plugins/oh-my-codex/skills/ralplan/SKILL.md +9 -74
  224. package/plugins/oh-my-codex/skills/visual-ralph/SKILL.md +7 -7
  225. package/skills/ai-slop-cleaner/SKILL.md +2 -3
  226. package/skills/best-practice-research/SKILL.md +1 -1
  227. package/skills/code-review/SKILL.md +1 -1
  228. package/skills/deep-interview/SKILL.md +13 -22
  229. package/skills/design/SKILL.md +1 -1
  230. package/skills/hud/SKILL.md +1 -1
  231. package/skills/plan/SKILL.md +1 -1
  232. package/skills/ralplan/SKILL.md +9 -74
  233. package/skills/visual-ralph/SKILL.md +7 -7
  234. package/src/scripts/__tests__/codex-native-hook.test.ts +9 -5
  235. package/src/scripts/__tests__/prompt-inventory.test.ts +84 -1
  236. package/src/scripts/__tests__/smoke-packed-install.test.ts +75 -23
  237. package/src/scripts/prompt-inventory.ts +71 -12
  238. package/src/scripts/run-test-files.ts +1 -0
  239. package/src/scripts/smoke-packed-install.ts +56 -17
  240. package/templates/AGENTS.md +4 -7
package/Cargo.lock CHANGED
@@ -32,7 +32,7 @@ checksum = "6b947ae49db0d222b1dbc6b113ce7248a3fc3a6ca21b696717bfc000ba4484d8"
32
32
 
33
33
  [[package]]
34
34
  name = "omx-api"
35
- version = "0.20.5"
35
+ version = "0.21.5"
36
36
  dependencies = [
37
37
  "serde",
38
38
  "serde_json",
@@ -40,14 +40,14 @@ dependencies = [
40
40
 
41
41
  [[package]]
42
42
  name = "omx-explore-harness"
43
- version = "0.20.5"
43
+ version = "0.21.5"
44
44
  dependencies = [
45
45
  "libc",
46
46
  ]
47
47
 
48
48
  [[package]]
49
49
  name = "omx-mux"
50
- version = "0.20.5"
50
+ version = "0.21.5"
51
51
  dependencies = [
52
52
  "serde",
53
53
  "serde_json",
@@ -55,7 +55,7 @@ dependencies = [
55
55
 
56
56
  [[package]]
57
57
  name = "omx-runtime"
58
- version = "0.20.5"
58
+ version = "0.21.5"
59
59
  dependencies = [
60
60
  "fs2",
61
61
  "libc",
@@ -67,7 +67,7 @@ dependencies = [
67
67
 
68
68
  [[package]]
69
69
  name = "omx-runtime-core"
70
- version = "0.20.5"
70
+ version = "0.21.5"
71
71
  dependencies = [
72
72
  "fs2",
73
73
  "serde",
@@ -76,7 +76,7 @@ dependencies = [
76
76
 
77
77
  [[package]]
78
78
  name = "omx-sparkshell"
79
- version = "0.20.5"
79
+ version = "0.21.5"
80
80
  dependencies = [
81
81
  "omx-mux",
82
82
  ]
package/Cargo.toml CHANGED
@@ -11,7 +11,7 @@ resolver = "2"
11
11
 
12
12
  [workspace.package]
13
13
 
14
- version = "0.21.4"
14
+ version = "0.21.5"
15
15
 
16
16
  edition = "2021"
17
17
  rust-version = "1.73"
package/README.md CHANGED
@@ -379,8 +379,12 @@ These are operator/support surfaces:
379
379
  - `.omx-config.json` model/env routing is documented in [the model/env routing reference](./docs/reference/omx-config-schema-routing.md); only edit keys supported by your installed OMX version
380
380
  - `omx doctor` verifies the install when something seems wrong; it does not prove that the active Codex profile can make an authenticated model call
381
381
  - `omx hud --watch` is a monitoring/status surface, not the primary user workflow
382
+ - Active session teams show their name and worker count beside the OMX version (for example, `[OMX#0.21.4] team:checkout (3 workers) | repo/branch`) in every HUD preset, before long repository labels. When ultragoal is active, the combined ultragoal/team summary also takes priority over repository labels and other modes. Team discovery remains session-scoped; this does not show teams from other sessions or change when team startup publishes active state.
383
+ - Below the summary, each team agent gets a live-refreshing row with its name, last reported state (`working`, `idle`, `blocked`, `done`, `failed`, `draining`, or `unknown`), current task ID, role, tmux pane ID, and update age when available. Columns stay aligned across different name lengths and missing fields; `working` and `done` are green, while `blocked` and `failed` are yellow. The HUD grows to fit the roster and shrinks after the team stops. Missing or malformed status files display `unknown`; a reported `working` state is not a process-liveness check. Membership and status are read without modifying team files.
382
384
  - GitGuardex finish progress is opt-in. Add `"guardex": { "enabled": true }` to the project-local `.omx/hud-config.json` to show `gx:<step>/<total> <phase>`; running review/autofix phases animate in the HUD. OMX does not read Guardex state when this flag is absent or false.
383
385
 
386
+ Tmux roster height is bounded by live window and leader/HUD geometry, reserving at least half of their shared rows for the leader. Workers that do not fit are represented by a `+N workers` overflow row. The watch loop remeasures after resizing; unknown geometry keeps the HUD compact rather than expanding it unchecked.
387
+
384
388
  For non-team sessions, native Codex hooks are now the canonical lifecycle surface:
385
389
  - `plugins/oh-my-codex/hooks/hooks.json` = official plugin-scoped hook registrations for plugin installs
386
390
  - `.codex/hooks.json` = legacy/fallback native Codex hook registrations preserved for legacy installs and older Codex versions
@@ -0,0 +1,379 @@
1
+ use fs2::FileExt;
2
+ use serde_json::Value;
3
+ use std::io::{self, BufRead, Write};
4
+ #[cfg(unix)]
5
+ use std::os::unix::ffi::OsStrExt;
6
+ use std::path::{Path, PathBuf};
7
+ use std::time::{Duration, Instant};
8
+
9
+ const LOCK_TIMEOUT: Duration = Duration::from_secs(34);
10
+ const SAFE_LEAF_SUFFIX: &str = ".mutex";
11
+
12
+ pub fn run(args: &[String]) -> Result<(), String> {
13
+ if args.len() != 4 {
14
+ return Err("lease-mutex requires exactly <absolute-canonical-dir> <expected-dev> <expected-ino> <safe-leaf>".to_string());
15
+ }
16
+ let leaf = validate_safe_leaf(&args[3])?;
17
+ #[cfg(unix)]
18
+ {
19
+ let dir = PathBuf::from(&args[0]);
20
+ if !dir.is_absolute() {
21
+ return emit_startup_error("lease-mutex directory must be absolute".to_string());
22
+ }
23
+ let expected_dev = match parse_identity(&args[1], "expected-dev") {
24
+ Ok(value) => value,
25
+ Err(error) => return emit_startup_error(error),
26
+ };
27
+ let expected_ino = match parse_identity(&args[2], "expected-ino") {
28
+ Ok(value) => value,
29
+ Err(error) => return emit_startup_error(error),
30
+ };
31
+ let lease = match UnixLease::open(&dir, expected_dev, expected_ino, leaf) {
32
+ Ok(lease) => lease,
33
+ Err(error) => return emit_startup_error(error),
34
+ };
35
+ run_protocol(lease)
36
+ }
37
+ #[cfg(not(unix))]
38
+ {
39
+ let _ = args;
40
+ Err("lease-mutex unsupported on this platform; native no-follow identity semantics are unavailable".to_string())
41
+ }
42
+ }
43
+
44
+ fn validate_safe_leaf(raw: &str) -> Result<String, String> {
45
+ let valid_hex = raw.strip_suffix(SAFE_LEAF_SUFFIX).is_some_and(|key| {
46
+ key.len() == 64
47
+ && key
48
+ .bytes()
49
+ .all(|b| b.is_ascii_digit() || (b'a'..=b'f').contains(&b))
50
+ });
51
+ if !valid_hex {
52
+ return Err("lease-mutex safe-leaf must match ^[a-f0-9]{64}\\.mutex$".to_string());
53
+ }
54
+ Ok(raw.to_string())
55
+ }
56
+
57
+ fn parse_identity(raw: &str, name: &str) -> Result<u64, String> {
58
+ raw.parse::<u64>()
59
+ .map_err(|_| format!("{name} must be a non-negative decimal integer"))
60
+ }
61
+
62
+ fn run_protocol<L: Lease>(mut lease: L) -> Result<(), String> {
63
+ let mut stdout = io::BufWriter::new(io::stdout().lock());
64
+ write_json(
65
+ &mut stdout,
66
+ serde_json::json!({"id":0,"ok":true,"ready":true}),
67
+ )?;
68
+ let stdin = io::stdin();
69
+ for line in stdin.lock().lines() {
70
+ let line = line.map_err(|e| format!("stdin read failed: {e}"))?;
71
+ if line.trim().is_empty() {
72
+ continue;
73
+ }
74
+ let request: Value = match serde_json::from_str(&line) {
75
+ Ok(value) => value,
76
+ Err(error) => {
77
+ write_json(
78
+ &mut stdout,
79
+ serde_json::json!({"id":null,"ok":false,"error":format!("invalid JSON request: {error}")}),
80
+ )?;
81
+ continue;
82
+ }
83
+ };
84
+ let id = request.get("id").cloned().unwrap_or(Value::Null);
85
+ let op = request.get("op").and_then(Value::as_str);
86
+ match op {
87
+ Some("assert") => match lease.verify() {
88
+ Ok(()) => write_json(&mut stdout, serde_json::json!({"id":id,"ok":true}))?,
89
+ Err(error) => write_json(
90
+ &mut stdout,
91
+ serde_json::json!({"id":id,"ok":false,"error":error}),
92
+ )?,
93
+ },
94
+ Some("close") => match lease.verify() {
95
+ Ok(()) => {
96
+ lease.release()?;
97
+ write_json(&mut stdout, serde_json::json!({"id":id,"ok":true}))?;
98
+ return Ok(());
99
+ }
100
+ Err(error) => {
101
+ let release_error = lease.release().err();
102
+ let message = match release_error {
103
+ Some(release_error) => format!("{error}; {release_error}"),
104
+ None => error,
105
+ };
106
+ write_json(
107
+ &mut stdout,
108
+ serde_json::json!({"id":id,"ok":false,"error":message}),
109
+ )?;
110
+ return Ok(());
111
+ }
112
+ },
113
+ Some(other) => write_json(
114
+ &mut stdout,
115
+ serde_json::json!({"id":id,"ok":false,"error":format!("unsupported lease-mutex operation: {other}")}),
116
+ )?,
117
+ None => write_json(
118
+ &mut stdout,
119
+ serde_json::json!({"id":id,"ok":false,"error":"request op must be assert or close"}),
120
+ )?,
121
+ }
122
+ }
123
+ Ok(())
124
+ }
125
+
126
+ fn write_json(out: &mut impl Write, value: Value) -> Result<(), String> {
127
+ serde_json::to_writer(&mut *out, &value)
128
+ .map_err(|e| format!("stdout JSON write failed: {e}"))?;
129
+ out.write_all(b"\n")
130
+ .map_err(|e| format!("stdout write failed: {e}"))?;
131
+ out.flush().map_err(|e| format!("stdout flush failed: {e}"))
132
+ }
133
+
134
+ fn emit_startup_error(error: String) -> Result<(), String> {
135
+ let mut stdout = io::stdout().lock();
136
+ let _ = write_json(
137
+ &mut stdout,
138
+ serde_json::json!({"id":0,"ok":false,"error":error}),
139
+ );
140
+ Err(error)
141
+ }
142
+
143
+ trait Lease {
144
+ fn verify(&self) -> Result<(), String>;
145
+ fn release(&mut self) -> Result<(), String>;
146
+ }
147
+
148
+ #[cfg(unix)]
149
+ struct UnixLease {
150
+ dir: std::fs::File,
151
+ mutex: std::fs::File,
152
+ dir_path: PathBuf,
153
+ expected_dev: u64,
154
+ expected_ino: u64,
155
+ mutex_dev: u64,
156
+ mutex_ino: u64,
157
+ leaf: String,
158
+ held: bool,
159
+ }
160
+
161
+ #[cfg(unix)]
162
+ impl UnixLease {
163
+ fn open(
164
+ path: &Path,
165
+ expected_dev: u64,
166
+ expected_ino: u64,
167
+ leaf: String,
168
+ ) -> Result<Self, String> {
169
+ std::fs::canonicalize(path)
170
+ .map_err(|e| format!("lease-mutex cannot canonicalize directory: {e}"))?;
171
+ let dir = open_directory(path)?;
172
+ let dir_identity = stat_fd(dir.as_raw_fd(), "pinned directory")?;
173
+ if !dir_identity.is_dir {
174
+ return Err("lease-mutex path is not a directory".to_string());
175
+ }
176
+ if dir_identity.dev != expected_dev || dir_identity.ino != expected_ino {
177
+ return Err(format!("lease-mutex directory identity mismatch: expected dev={expected_dev} ino={expected_ino}, got dev={} ino={}", dir_identity.dev, dir_identity.ino));
178
+ }
179
+ let mutex = open_mutex(dir.as_raw_fd(), &leaf)?;
180
+ let mutex_identity = stat_fd(mutex.as_raw_fd(), "mutex file")?;
181
+ if !mutex_identity.is_regular || mutex_identity.nlink != 1 {
182
+ return Err(
183
+ "lease-mutex mutex leaf must be a regular file with link count 1".to_string(),
184
+ );
185
+ }
186
+ try_lock(&mutex)?;
187
+ let lease = Self {
188
+ dir,
189
+ mutex,
190
+ dir_path: path.to_path_buf(),
191
+ expected_dev,
192
+ expected_ino,
193
+ mutex_dev: mutex_identity.dev,
194
+ mutex_ino: mutex_identity.ino,
195
+ leaf,
196
+ held: true,
197
+ };
198
+ lease.verify()?;
199
+ Ok(lease)
200
+ }
201
+ }
202
+
203
+ #[cfg(unix)]
204
+ impl Lease for UnixLease {
205
+ fn verify(&self) -> Result<(), String> {
206
+ if !self.held {
207
+ return Err("lease-mutex lock is no longer held".to_string());
208
+ }
209
+ let pinned = stat_fd(self.dir.as_raw_fd(), "pinned directory")?;
210
+ if !pinned.is_dir || pinned.dev != self.expected_dev || pinned.ino != self.expected_ino {
211
+ return Err("lease-mutex pinned directory identity changed".to_string());
212
+ }
213
+ let visible = stat_at(self.dir.as_raw_fd(), &self.leaf)?;
214
+ if !visible.is_regular || visible.nlink != 1 {
215
+ return Err("lease-mutex visible mutex leaf is not a regular nlink-1 file".to_string());
216
+ }
217
+ if visible.dev != self.mutex_dev || visible.ino != self.mutex_ino {
218
+ return Err("lease-mutex visible mutex identity changed".to_string());
219
+ }
220
+ let opened = stat_fd(self.mutex.as_raw_fd(), "opened mutex")?;
221
+ if !opened.is_regular
222
+ || opened.nlink != 1
223
+ || opened.dev != self.mutex_dev
224
+ || opened.ino != self.mutex_ino
225
+ {
226
+ return Err("lease-mutex opened mutex identity changed".to_string());
227
+ }
228
+ let path_identity = stat_path(&self.dir_path)?;
229
+ if path_identity.dev != self.expected_dev || path_identity.ino != self.expected_ino {
230
+ return Err("lease-mutex directory path identity changed".to_string());
231
+ }
232
+ Ok(())
233
+ }
234
+
235
+ fn release(&mut self) -> Result<(), String> {
236
+ if self.held {
237
+ FileExt::unlock(&self.mutex).map_err(|e| format!("lease-mutex unlock failed: {e}"))?;
238
+ self.held = false;
239
+ }
240
+ Ok(())
241
+ }
242
+ }
243
+
244
+ #[cfg(unix)]
245
+ use std::os::fd::{AsRawFd, FromRawFd};
246
+
247
+ #[cfg(unix)]
248
+ #[derive(Clone, Copy)]
249
+ struct Identity {
250
+ dev: u64,
251
+ ino: u64,
252
+ nlink: u64,
253
+ is_dir: bool,
254
+ is_regular: bool,
255
+ }
256
+
257
+ #[cfg(unix)]
258
+ fn open_directory(path: &Path) -> Result<std::fs::File, String> {
259
+ let c = cstring(path, "directory")?;
260
+ let fd = unsafe {
261
+ libc::open(
262
+ c.as_ptr(),
263
+ libc::O_RDONLY | libc::O_DIRECTORY | libc::O_CLOEXEC | libc::O_NOFOLLOW,
264
+ )
265
+ };
266
+ if fd < 0 {
267
+ return Err(format!(
268
+ "lease-mutex cannot open directory without following symlinks: {}",
269
+ io::Error::last_os_error()
270
+ ));
271
+ }
272
+ Ok(unsafe { std::fs::File::from_raw_fd(fd) })
273
+ }
274
+
275
+ #[cfg(unix)]
276
+ fn open_mutex(dir_fd: i32, leaf_name: &str) -> Result<std::fs::File, String> {
277
+ let leaf = std::ffi::CString::new(leaf_name)
278
+ .map_err(|_| "lease-mutex safe-leaf contains NUL".to_string())?;
279
+ let flags = libc::O_RDWR | libc::O_CLOEXEC | libc::O_NOFOLLOW;
280
+ let mut fd = unsafe { libc::openat(dir_fd, leaf.as_ptr(), flags) };
281
+ if fd < 0 && io::Error::last_os_error().raw_os_error() == Some(libc::ENOENT) {
282
+ fd = unsafe {
283
+ libc::openat(
284
+ dir_fd,
285
+ leaf.as_ptr(),
286
+ flags | libc::O_CREAT | libc::O_EXCL,
287
+ 0o600,
288
+ )
289
+ };
290
+ if fd < 0 && io::Error::last_os_error().raw_os_error() == Some(libc::EEXIST) {
291
+ fd = unsafe { libc::openat(dir_fd, leaf.as_ptr(), flags) };
292
+ }
293
+ }
294
+ if fd < 0 {
295
+ return Err(format!(
296
+ "lease-mutex cannot open permanent mutex leaf without following symlinks: {}",
297
+ io::Error::last_os_error()
298
+ ));
299
+ }
300
+ Ok(unsafe { std::fs::File::from_raw_fd(fd) })
301
+ }
302
+
303
+ #[cfg(unix)]
304
+ fn try_lock(file: &std::fs::File) -> Result<(), String> {
305
+ let start = Instant::now();
306
+ loop {
307
+ match file.try_lock_exclusive() {
308
+ Ok(()) => return Ok(()),
309
+ Err(error)
310
+ if error.kind() == io::ErrorKind::WouldBlock && start.elapsed() < LOCK_TIMEOUT =>
311
+ {
312
+ std::thread::sleep(Duration::from_millis(20))
313
+ }
314
+ Err(error) if error.kind() == io::ErrorKind::WouldBlock => {
315
+ return Err(format!(
316
+ "lease-mutex lock acquisition timed out after {} ms",
317
+ LOCK_TIMEOUT.as_millis()
318
+ ))
319
+ }
320
+ Err(error) => return Err(format!("lease-mutex lock acquisition failed: {error}")),
321
+ }
322
+ }
323
+ }
324
+
325
+ #[cfg(unix)]
326
+ fn cstring(path: &Path, label: &str) -> Result<std::ffi::CString, String> {
327
+ std::ffi::CString::new(path.as_os_str().as_bytes())
328
+ .map_err(|_| format!("lease-mutex {label} path contains an embedded NUL byte"))
329
+ }
330
+
331
+ #[cfg(unix)]
332
+ fn stat_path(path: &Path) -> Result<Identity, String> {
333
+ let c = cstring(path, "directory")?;
334
+ let mut st = unsafe { std::mem::zeroed() };
335
+ if unsafe { libc::lstat(c.as_ptr(), &mut st) } != 0 {
336
+ return Err(format!(
337
+ "lease-mutex cannot verify directory path: {}",
338
+ io::Error::last_os_error()
339
+ ));
340
+ }
341
+ Ok(identity_from_stat(&st))
342
+ }
343
+
344
+ #[cfg(unix)]
345
+ fn stat_at(dir_fd: i32, leaf: &str) -> Result<Identity, String> {
346
+ let c = std::ffi::CString::new(leaf).unwrap();
347
+ let mut st = unsafe { std::mem::zeroed() };
348
+ if unsafe { libc::fstatat(dir_fd, c.as_ptr(), &mut st, libc::AT_SYMLINK_NOFOLLOW) } != 0 {
349
+ return Err(format!(
350
+ "lease-mutex cannot verify visible mutex leaf: {}",
351
+ io::Error::last_os_error()
352
+ ));
353
+ }
354
+ Ok(identity_from_stat(&st))
355
+ }
356
+
357
+ #[cfg(unix)]
358
+ fn stat_fd(fd: i32, label: &str) -> Result<Identity, String> {
359
+ let mut st = unsafe { std::mem::zeroed() };
360
+ if unsafe { libc::fstat(fd, &mut st) } != 0 {
361
+ return Err(format!(
362
+ "lease-mutex cannot stat {label}: {}",
363
+ io::Error::last_os_error()
364
+ ));
365
+ }
366
+ Ok(identity_from_stat(&st))
367
+ }
368
+
369
+ #[cfg(unix)]
370
+ #[allow(clippy::unnecessary_cast)]
371
+ fn identity_from_stat(st: &libc::stat) -> Identity {
372
+ Identity {
373
+ dev: st.st_dev as u64,
374
+ ino: st.st_ino as u64,
375
+ nlink: st.st_nlink as u64,
376
+ is_dir: (st.st_mode & libc::S_IFMT) == libc::S_IFDIR,
377
+ is_regular: (st.st_mode & libc::S_IFMT) == libc::S_IFREG,
378
+ }
379
+ }
@@ -1,3 +1,5 @@
1
+ mod lease_mutex;
2
+
1
3
  use fs2::FileExt;
2
4
  use omx_mux::{canonical_contract_summary, MuxAdapter, MuxOperation, MuxTarget, TmuxAdapter};
3
5
  use omx_runtime_core::{runtime_contract_summary, RuntimeCommand, RuntimeEngine};
@@ -123,6 +125,7 @@ fn run() -> Result<(), String> {
123
125
  );
124
126
  Ok(())
125
127
  }
128
+ Some("lease-mutex") => lease_mutex::run(&args[1..]),
126
129
  Some("fs-rename-no-replace") => run_fs_rename_no_replace(&args[1..]),
127
130
  Some("process-identity") => run_process_identity(&args[1..]),
128
131
  Some("init") => {
@@ -533,6 +536,7 @@ fn print_usage() {
533
536
  " snapshot [--json] [--state-dir=DIR] print a runtime snapshot\n",
534
537
  " mux-contract print the mux boundary summary\n",
535
538
  " exec <json> [--state-dir=DIR] process a runtime command from JSON\n",
539
+ " lease-mutex <absolute-canonical-dir> <expected-dev> <expected-ino> <safe-leaf> hold native lease mutex over stdin JSON protocol\n",
536
540
  " init <state-dir> initialize a fresh state directory\n",
537
541
  ));
538
542
  }
@@ -1,3 +1,154 @@
1
+ #[cfg(unix)]
2
+ mod lease_mutex_tests {
3
+ use super::*;
4
+ use std::io::{BufRead, BufReader, Write};
5
+ use std::os::unix::fs::MetadataExt;
6
+ use std::process::{Child, ChildStdout, Stdio};
7
+ use std::sync::mpsc;
8
+ use std::thread;
9
+ use std::time::{Duration, SystemTime, UNIX_EPOCH};
10
+ const MUTEX_LEAF: &str =
11
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.mutex";
12
+
13
+ fn temp_dir() -> std::path::PathBuf {
14
+ let dir = std::env::temp_dir().join(format!(
15
+ "omx-lease-mutex-{}-{}",
16
+ std::process::id(),
17
+ SystemTime::now()
18
+ .duration_since(UNIX_EPOCH)
19
+ .unwrap()
20
+ .as_nanos()
21
+ ));
22
+ std::fs::create_dir_all(&dir).unwrap();
23
+ dir
24
+ }
25
+
26
+ fn spawn_lease(dir: &std::path::Path) -> (Child, BufReader<ChildStdout>) {
27
+ let metadata = std::fs::metadata(dir).unwrap();
28
+ let mut child = Command::new(env!("CARGO_BIN_EXE_omx-runtime"))
29
+ .args([
30
+ "lease-mutex",
31
+ dir.to_str().unwrap(),
32
+ &metadata.dev().to_string(),
33
+ &metadata.ino().to_string(),
34
+ MUTEX_LEAF,
35
+ ])
36
+ .stdin(Stdio::piped())
37
+ .stdout(Stdio::piped())
38
+ .stderr(Stdio::piped())
39
+ .spawn()
40
+ .unwrap();
41
+ let stdout = BufReader::new(child.stdout.take().unwrap());
42
+ (child, stdout)
43
+ }
44
+
45
+ fn read_line(reader: &mut BufReader<ChildStdout>) -> serde_json::Value {
46
+ let mut line = String::new();
47
+ reader.read_line(&mut line).unwrap();
48
+ serde_json::from_str(&line).unwrap()
49
+ }
50
+
51
+ #[test]
52
+ fn lease_mutex_serializes_contenders() {
53
+ let dir = temp_dir();
54
+ let (mut first, mut first_out) = spawn_lease(&dir);
55
+ assert_eq!(read_line(&mut first_out)["ready"], true);
56
+ let (mut second, second_out) = spawn_lease(&dir);
57
+ let (tx, rx) = mpsc::channel();
58
+ thread::spawn(move || {
59
+ let mut reader = second_out;
60
+ let mut line = String::new();
61
+ let result = reader.read_line(&mut line).map(|_| line);
62
+ let _ = tx.send(result);
63
+ });
64
+ assert!(rx.recv_timeout(Duration::from_millis(150)).is_err());
65
+ first
66
+ .stdin
67
+ .as_mut()
68
+ .unwrap()
69
+ .write_all(b"{\"id\":1,\"op\":\"close\"}\n")
70
+ .unwrap();
71
+ assert!(first.wait().unwrap().success());
72
+ let line = rx.recv_timeout(Duration::from_secs(3)).unwrap().unwrap();
73
+ assert_eq!(
74
+ serde_json::from_str::<serde_json::Value>(&line).unwrap()["ready"],
75
+ true
76
+ );
77
+ let _ = second.kill();
78
+ let _ = second.wait();
79
+ let _ = std::fs::remove_dir_all(dir);
80
+ }
81
+
82
+ #[test]
83
+ fn lease_mutex_releases_on_process_death() {
84
+ let dir = temp_dir();
85
+ let (mut first, mut first_out) = spawn_lease(&dir);
86
+ assert_eq!(read_line(&mut first_out)["ready"], true);
87
+ first.kill().unwrap();
88
+ first.wait().unwrap();
89
+ let (mut second, mut second_out) = spawn_lease(&dir);
90
+ assert_eq!(read_line(&mut second_out)["ready"], true);
91
+ second.kill().unwrap();
92
+ second.wait().unwrap();
93
+ let _ = std::fs::remove_dir_all(dir);
94
+ }
95
+
96
+ #[test]
97
+ fn lease_mutex_rejects_symlink_and_directory_identity_mismatch() {
98
+ use std::os::unix::fs::symlink;
99
+ let dir = temp_dir();
100
+ let target = dir.join("target");
101
+ std::fs::create_dir(&target).unwrap();
102
+ symlink(&target, dir.join("link")).unwrap();
103
+ let output = Command::new(env!("CARGO_BIN_EXE_omx-runtime"))
104
+ .args([
105
+ "lease-mutex",
106
+ dir.join("link").to_str().unwrap(),
107
+ "0",
108
+ "0",
109
+ MUTEX_LEAF,
110
+ ])
111
+ .output()
112
+ .unwrap();
113
+ assert!(!output.status.success());
114
+ let metadata = std::fs::metadata(&dir).unwrap();
115
+ let output = Command::new(env!("CARGO_BIN_EXE_omx-runtime"))
116
+ .args([
117
+ "lease-mutex",
118
+ target.to_str().unwrap(),
119
+ &metadata.dev().to_string(),
120
+ &metadata.ino().to_string(),
121
+ MUTEX_LEAF,
122
+ ])
123
+ .output()
124
+ .unwrap();
125
+ assert!(!output.status.success());
126
+ let _ = std::fs::remove_dir_all(dir);
127
+ }
128
+
129
+ #[test]
130
+ fn lease_mutex_assert_fails_after_directory_path_replacement() {
131
+ let dir = temp_dir();
132
+ let replacement = dir.with_extension("replacement");
133
+ let (mut child, mut output) = spawn_lease(&dir);
134
+ assert_eq!(read_line(&mut output)["ready"], true);
135
+ std::fs::rename(&dir, &replacement).unwrap();
136
+ std::fs::create_dir(&dir).unwrap();
137
+ child
138
+ .stdin
139
+ .as_mut()
140
+ .unwrap()
141
+ .write_all(b"{\"id\":7,\"op\":\"assert\"}\n")
142
+ .unwrap();
143
+ let response = read_line(&mut output);
144
+ assert_eq!(response["ok"], false);
145
+ assert!(response["error"].as_str().unwrap().contains("identity"));
146
+ let _ = child.kill();
147
+ let _ = child.wait();
148
+ let _ = std::fs::remove_dir_all(dir);
149
+ let _ = std::fs::remove_dir_all(replacement);
150
+ }
151
+ }
1
152
  use std::process::Command;
2
153
 
3
154
  #[test]
@@ -439,6 +590,7 @@ fn concurrent_exec_queue_accepts_exactly_one_request_id() {
439
590
  // ---------------------------------------------------------------------------
440
591
 
441
592
  /// Helper: run `fs-rename-no-replace <from> <to>` and return (exit_ok, stdout, stderr).
593
+ #[cfg(target_os = "linux")]
442
594
  fn run_rename_no_replace(from: &str, to: &str) -> (bool, String, String) {
443
595
  let output = Command::new(env!("CARGO_BIN_EXE_omx-runtime"))
444
596
  .args(["fs-rename-no-replace", from, to])
@@ -54,7 +54,7 @@ describe('doctor native process identity readiness', () => {
54
54
  provider: providerReturning({ kind: 'unsupported' }),
55
55
  });
56
56
  assert.equal(check?.status, 'fail');
57
- assert.equal(check?.message, 'native process identity is unavailable (provider unavailable); reinstall or update OMX and rerun doctor');
57
+ assert.equal(check?.message, 'native process identity is unavailable (provider unavailable); run `omx update` (repairs the native runtime cache) or `omx setup`, then rerun doctor; session pointer recovery works without the native runtime');
58
58
  });
59
59
  it('fails safely for ambiguous current-process observations', () => {
60
60
  for (const observation of [{ kind: 'gone' }, { kind: 'denied' }]) {
@@ -1 +1 @@
1
- {"version":3,"file":"doctor-process-identity-readiness.test.js","sourceRoot":"","sources":["../../../src/cli/__tests__/doctor-process-identity-readiness.test.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"doctor-process-identity-readiness.test.js","sourceRoot":"","sources":["../../../src/cli/__tests__/doctor-process-identity-readiness.test.ts"],"names":[],"mappings":"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"}