oh-my-codex 0.21.35 → 0.21.36

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.
package/Cargo.lock CHANGED
@@ -32,7 +32,7 @@ checksum = "cf8baf1c55e62ffcace7a9f06f4bd9cd3f0c4beb022d3b367256b91b87513d98"
32
32
 
33
33
  [[package]]
34
34
  name = "omx-api"
35
- version = "0.21.35"
35
+ version = "0.21.36"
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.21.35"
43
+ version = "0.21.36"
44
44
  dependencies = [
45
45
  "libc",
46
46
  ]
47
47
 
48
48
  [[package]]
49
49
  name = "omx-mux"
50
- version = "0.21.35"
50
+ version = "0.21.36"
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.21.35"
58
+ version = "0.21.36"
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.21.35"
70
+ version = "0.21.36"
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.21.35"
79
+ version = "0.21.36"
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.35"
14
+ version = "0.21.36"
15
15
 
16
16
  edition = "2021"
17
17
  rust-version = "1.73"
@@ -2796,11 +2796,185 @@ describe("codex native hook dispatch", { concurrency: false }, () => {
2796
2796
  assert.equal(output.stopReason, "native_stop_dispatch_failure");
2797
2797
  const logFiles = await readdir(join(cwd, ".omx", "logs"));
2798
2798
  assert.equal(logFiles.some((name) => /^native-hook-\d{4}-\d{2}-\d{2}\.jsonl$/.test(name)), true);
2799
+ const logName = logFiles.find((name) => /^native-hook-\d{4}-\d{2}-\d{2}\.jsonl$/.test(name));
2800
+ assert.notEqual(logName, undefined);
2801
+ const [record] = (await readFile(join(cwd, ".omx", "logs", logName ?? ""), "utf-8"))
2802
+ .trim()
2803
+ .split("\n")
2804
+ .map((line) => JSON.parse(line));
2805
+ assert.equal(typeof record?.timestamp, "string");
2806
+ assert.equal(record?.type, "native_hook_dispatch_error");
2807
+ assert.equal(record?.hook_event_name, "Stop");
2808
+ assert.equal(record?.session_id, "sess-cli-stop-dispatch-silent");
2809
+ assert.equal(record?.thread_id, "thread-cli-stop-dispatch-silent");
2810
+ assert.equal(record?.turn_id, "turn-cli-stop-dispatch-silent");
2811
+ assert.equal(record?.error, "test-induced Stop dispatch failure");
2799
2812
  }
2800
2813
  finally {
2801
2814
  await rm(cwd, { recursive: true, force: true });
2802
2815
  }
2803
2816
  });
2817
+ it("routes native-hook CLI error logs through OMX_ROOT", async () => {
2818
+ const project = await mkdtemp(join(tmpdir(), "omx-native-hook-cli-log-project-"));
2819
+ const runtimeRoot = await mkdtemp(join(tmpdir(), "omx-native-hook-cli-log-runtime-"));
2820
+ const projectLogsDir = join(project, ".omx", "logs");
2821
+ const sentinelPath = join(projectLogsDir, "sentinel.jsonl");
2822
+ const sentinel = "preserve this project log\n";
2823
+ try {
2824
+ await mkdir(projectLogsDir, { recursive: true });
2825
+ await writeFile(sentinelPath, sentinel);
2826
+ const env = {
2827
+ ...process.env,
2828
+ OMX_ROOT: runtimeRoot,
2829
+ NODE_ENV: "test",
2830
+ OMX_NATIVE_HOOK_TEST_THROW_DISPATCH: "1",
2831
+ };
2832
+ delete env.OMX_STATE_ROOT;
2833
+ delete env.OMX_TEAM_STATE_ROOT;
2834
+ delete env.OMX_TEAM_ROOT;
2835
+ delete env.OMX_PLUGIN_ROOT;
2836
+ delete env.OMX_PLUGIN_CACHE_PIN_PENDING;
2837
+ delete env.OMX_PLUGIN_CACHE_PIN_REQUIRED;
2838
+ const result = spawnSync(process.execPath, [nativeHookScriptPath()], {
2839
+ cwd: project,
2840
+ input: JSON.stringify({ hook_event_name: "Stop", cwd: project }),
2841
+ encoding: "utf-8",
2842
+ stdio: ["pipe", "pipe", "pipe"],
2843
+ env,
2844
+ });
2845
+ assert.equal(result.status, 0, result.stderr || result.stdout);
2846
+ assert.equal(result.stderr, "");
2847
+ const output = parseSingleJsonStdout(result.stdout);
2848
+ assert.equal(output.decision, "block");
2849
+ assert.equal(output.stopReason, "native_stop_dispatch_failure");
2850
+ assert.equal(existsSync(join(runtimeRoot, ".omx", "logs")), true);
2851
+ const runtimeLogsDir = join(runtimeRoot, ".omx", "logs");
2852
+ const logFiles = await readdir(runtimeLogsDir);
2853
+ assert.equal(logFiles.length, 1);
2854
+ assert.match(logFiles[0] ?? "", /^native-hook-\d{4}-\d{2}-\d{2}\.jsonl$/);
2855
+ const [record] = (await readFile(join(runtimeLogsDir, logFiles[0] ?? ""), "utf-8"))
2856
+ .trim()
2857
+ .split("\n")
2858
+ .map((line) => JSON.parse(line));
2859
+ assert.deepEqual({
2860
+ type: record?.type,
2861
+ hook_event_name: record?.hook_event_name,
2862
+ error: record?.error,
2863
+ }, {
2864
+ type: "native_hook_dispatch_error",
2865
+ hook_event_name: "Stop",
2866
+ error: "test-induced dispatch failure",
2867
+ });
2868
+ assert.equal(await readFile(sentinelPath, "utf-8"), sentinel);
2869
+ assert.deepEqual(await readdir(projectLogsDir), ["sentinel.jsonl"]);
2870
+ }
2871
+ finally {
2872
+ await rm(project, { recursive: true, force: true });
2873
+ await rm(runtimeRoot, { recursive: true, force: true });
2874
+ }
2875
+ });
2876
+ it("honors OMX_STATE_ROOT, OMX_ROOT precedence, and the cwd default for native-hook logs", async () => {
2877
+ const stateRoot = await mkdtemp(join(tmpdir(), "omx-native-hook-state-root-"));
2878
+ const unusedStateRoot = await mkdtemp(join(tmpdir(), "omx-native-hook-unused-state-root-"));
2879
+ const omxRoot = await mkdtemp(join(tmpdir(), "omx-native-hook-root-"));
2880
+ const baseEnv = {
2881
+ ...process.env,
2882
+ NODE_ENV: "test",
2883
+ OMX_NATIVE_HOOK_TEST_THROW_DISPATCH: "1",
2884
+ };
2885
+ delete baseEnv.OMX_ROOT;
2886
+ delete baseEnv.OMX_STATE_ROOT;
2887
+ delete baseEnv.OMX_TEAM_STATE_ROOT;
2888
+ delete baseEnv.OMX_TEAM_ROOT;
2889
+ delete baseEnv.OMX_PLUGIN_ROOT;
2890
+ delete baseEnv.OMX_PLUGIN_CACHE_PIN_PENDING;
2891
+ delete baseEnv.OMX_PLUGIN_CACHE_PIN_REQUIRED;
2892
+ const cases = [
2893
+ {
2894
+ name: "OMX_STATE_ROOT alone",
2895
+ expectedRoot: stateRoot,
2896
+ overrides: { OMX_STATE_ROOT: stateRoot },
2897
+ },
2898
+ {
2899
+ name: "OMX_ROOT precedence",
2900
+ expectedRoot: omxRoot,
2901
+ unusedRoot: unusedStateRoot,
2902
+ overrides: { OMX_ROOT: omxRoot, OMX_STATE_ROOT: unusedStateRoot },
2903
+ },
2904
+ { name: "cwd default", overrides: {} },
2905
+ ];
2906
+ try {
2907
+ for (const scenario of cases) {
2908
+ const project = await mkdtemp(join(tmpdir(), "omx-native-hook-root-contract-"));
2909
+ try {
2910
+ const result = spawnSync(process.execPath, [nativeHookScriptPath()], {
2911
+ cwd: project,
2912
+ input: JSON.stringify({ hook_event_name: "Stop", cwd: project }),
2913
+ encoding: "utf-8",
2914
+ stdio: ["pipe", "pipe", "pipe"],
2915
+ env: { ...baseEnv, ...scenario.overrides },
2916
+ });
2917
+ assert.equal(result.status, 0, `${scenario.name}: ${result.stderr || result.stdout}`);
2918
+ assert.equal(result.stderr, "", scenario.name);
2919
+ assert.equal(parseSingleJsonStdout(result.stdout).stopReason, "native_stop_dispatch_failure", scenario.name);
2920
+ const selectedRoot = scenario.expectedRoot ?? project;
2921
+ const selectedLogsDir = join(selectedRoot, ".omx", "logs");
2922
+ const logFiles = await readdir(selectedLogsDir);
2923
+ assert.equal(logFiles.some((name) => /^native-hook-\d{4}-\d{2}-\d{2}\.jsonl$/.test(name)), true, scenario.name);
2924
+ if (scenario.expectedRoot) {
2925
+ assert.equal(existsSync(join(project, ".omx", "logs")), false, scenario.name);
2926
+ }
2927
+ if (scenario.unusedRoot) {
2928
+ assert.equal(existsSync(join(scenario.unusedRoot, ".omx", "logs")), false, scenario.name);
2929
+ }
2930
+ }
2931
+ finally {
2932
+ await rm(project, { recursive: true, force: true });
2933
+ }
2934
+ }
2935
+ }
2936
+ finally {
2937
+ await rm(stateRoot, { recursive: true, force: true });
2938
+ await rm(unusedStateRoot, { recursive: true, force: true });
2939
+ await rm(omxRoot, { recursive: true, force: true });
2940
+ }
2941
+ });
2942
+ it("keeps CLI failure handling non-fatal when the selected native-hook log root is unwritable", async () => {
2943
+ const project = await mkdtemp(join(tmpdir(), "omx-native-hook-unwritable-project-"));
2944
+ const runtimeParent = await mkdtemp(join(tmpdir(), "omx-native-hook-unwritable-runtime-"));
2945
+ const runtimeRoot = join(runtimeParent, "runtime-root");
2946
+ try {
2947
+ await writeFile(runtimeRoot, "not a directory\n");
2948
+ const env = {
2949
+ ...process.env,
2950
+ OMX_ROOT: runtimeRoot,
2951
+ NODE_ENV: "test",
2952
+ OMX_NATIVE_HOOK_TEST_THROW_DISPATCH: "1",
2953
+ };
2954
+ delete env.OMX_STATE_ROOT;
2955
+ delete env.OMX_TEAM_STATE_ROOT;
2956
+ delete env.OMX_TEAM_ROOT;
2957
+ delete env.OMX_PLUGIN_ROOT;
2958
+ delete env.OMX_PLUGIN_CACHE_PIN_PENDING;
2959
+ delete env.OMX_PLUGIN_CACHE_PIN_REQUIRED;
2960
+ const result = spawnSync(process.execPath, [nativeHookScriptPath()], {
2961
+ cwd: project,
2962
+ input: JSON.stringify({ hook_event_name: "Stop", cwd: project }),
2963
+ encoding: "utf-8",
2964
+ stdio: ["pipe", "pipe", "pipe"],
2965
+ env,
2966
+ });
2967
+ assert.equal(result.status, 0, result.stderr || result.stdout);
2968
+ assert.equal(result.stderr, "");
2969
+ assert.equal(parseSingleJsonStdout(result.stdout).stopReason, "native_stop_dispatch_failure");
2970
+ assert.equal(existsSync(join(project, ".omx")), false);
2971
+ assert.equal(await readFile(runtimeRoot, "utf-8"), "not a directory\n");
2972
+ }
2973
+ finally {
2974
+ await rm(project, { recursive: true, force: true });
2975
+ await rm(runtimeParent, { recursive: true, force: true });
2976
+ }
2977
+ });
2804
2978
  it("keeps non-Stop dispatch failures fail-closed without foreground stderr noise", async () => {
2805
2979
  const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-cli-pretool-dispatch-silent-"));
2806
2980
  try {