oh-my-codex 0.21.33 → 0.21.34

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.33"
35
+ version = "0.21.34"
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.33"
43
+ version = "0.21.34"
44
44
  dependencies = [
45
45
  "libc",
46
46
  ]
47
47
 
48
48
  [[package]]
49
49
  name = "omx-mux"
50
- version = "0.21.33"
50
+ version = "0.21.34"
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.33"
58
+ version = "0.21.34"
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.33"
70
+ version = "0.21.34"
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.33"
79
+ version = "0.21.34"
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.33"
14
+ version = "0.21.34"
15
15
 
16
16
  edition = "2021"
17
17
  rust-version = "1.73"
@@ -5813,6 +5813,44 @@ process.on('SIGTERM', () => {});
5813
5813
  await rm(cwd, { recursive: true, force: true });
5814
5814
  }
5815
5815
  });
5816
+ it('shutdownTeam clears terminated prompt-worker PIDs before aborting on a partial teardown failure', async () => {
5817
+ const cwd = await mkdtemp(join(tmpdir(), 'omx-runtime-prompt-partial-'));
5818
+ const binDir = join(cwd, 'bin');
5819
+ const fakeCodexPath = join(binDir, 'codex');
5820
+ const originalProcessKill = process.kill;
5821
+ let runtime = null;
5822
+ await mkdir(binDir, { recursive: true });
5823
+ await writeFakePromptWorkerBinary(fakeCodexPath, `
5824
+ process.stdin.resume();
5825
+ setInterval(() => {}, 1000);
5826
+ `);
5827
+ try {
5828
+ runtime = await withPromptModeCodexEnv(binDir, {}, () => withoutTeamWorkerEnv(() => startTeam('team-prompt-partial', 'partial prompt teardown test', 'executor', 2, [{ subject: 's', description: 'd', owner: 'worker-1' }], cwd)));
5829
+ const stuckPid = runtime.config.workers[0]?.pid ?? 0;
5830
+ const stoppedPid = runtime.config.workers[1]?.pid ?? 0;
5831
+ assert.ok(stuckPid > 0 && stoppedPid > 0);
5832
+ process.kill = ((pid, signal) => {
5833
+ if ((pid === -stuckPid || pid === stuckPid) && signal === 0) {
5834
+ const error = new Error('permission denied');
5835
+ error.code = 'EPERM';
5836
+ throw error;
5837
+ }
5838
+ return originalProcessKill(pid, signal);
5839
+ });
5840
+ await assert.rejects(() => shutdownTeam(runtime.teamName, cwd, { force: true }), /^Error: shutdown_prompt_teardown_failed:worker-1:still_alive_after_sigkill$/);
5841
+ process.kill = originalProcessKill;
5842
+ const retained = await readTeamConfig(runtime.teamName, cwd);
5843
+ assert.ok(retained);
5844
+ assert.equal(retained?.workers[0]?.pid, stuckPid, 'the worker that survived stays recorded for retry');
5845
+ assert.equal(retained?.workers[1]?.pid, undefined, 'a terminated worker PID is never signalled again on retry');
5846
+ }
5847
+ finally {
5848
+ process.kill = originalProcessKill;
5849
+ if (runtime)
5850
+ await shutdownTeam(runtime.teamName, cwd, { force: true }).catch(() => { });
5851
+ await rm(cwd, { recursive: true, force: true });
5852
+ }
5853
+ });
5816
5854
  it('shutdownTeam reaps detached prompt-worker descendants', async () => {
5817
5855
  const cwd = await mkdtemp(join(tmpdir(), 'omx-runtime-prompt-descendants-'));
5818
5856
  const binDir = join(cwd, 'bin');
@@ -8695,6 +8733,87 @@ esac
8695
8733
  await rm(cwd, { recursive: true, force: true });
8696
8734
  }
8697
8735
  });
8736
+ it('shutdownTeam persists killed worker panes before aborting on a partial pane-kill failure', async () => {
8737
+ const cwd = await mkdtemp(join(tmpdir(), 'omx-runtime-shutdown-partial-kill-'));
8738
+ const teamName = 'team-shutdown-partial-kill';
8739
+ try {
8740
+ await withMockTmuxFixture({
8741
+ dirPrefix: 'omx-runtime-shutdown-partial-kill-bin-',
8742
+ tmuxScript: (tmuxLogPath) => `#!/bin/sh
8743
+ set -eu
8744
+ printf '%s\\n' "$*" >> "${tmuxLogPath}"
8745
+ case "$1" in
8746
+ -V)
8747
+ echo "tmux 3.4"
8748
+ ;;
8749
+ list-panes)
8750
+ case "$*" in
8751
+ *"-t leader:0"*)
8752
+ printf '%%10\\tzsh\\tzsh\\n'
8753
+ if [ ! -f "${tmuxLogPath}.killed13" ]; then printf '%%13\\tcodex\\tenv OMX_TEAM_INTERNAL_WORKER=team-shutdown-partial-kill/worker-1 codex\\n'; fi
8754
+ printf '%%14\\tcodex\\tenv OMX_TEAM_INTERNAL_WORKER=team-shutdown-partial-kill/worker-2 codex\\n%%15\\tcodex\\tenv OMX_TEAM_INTERNAL_WORKER=team-shutdown-partial-kill/worker-3 codex\\n'
8755
+ ;;
8756
+ *"-a -F #{pane_id}"*)
8757
+ printf '%%10\t0\t2000000010\n'
8758
+ if [ ! -f "${tmuxLogPath}.killed13" ]; then printf '%%13\t0\t2000000013\n'; fi
8759
+ printf '%%14\t0\t2000000014\n%%15\t0\t2000000015\n'
8760
+ ;;
8761
+ *)
8762
+ exit 1
8763
+ ;;
8764
+ esac
8765
+ ;;
8766
+ show-option)
8767
+ printf 'owner-1\\n'
8768
+ ;;
8769
+ kill-pane)
8770
+ case "$*" in
8771
+ *"-t %13"*) : > "${tmuxLogPath}.killed13" ;;
8772
+ *) echo "kill pane failed" >&2; exit 1 ;;
8773
+ esac
8774
+ ;;
8775
+ *)
8776
+ exit 0
8777
+ ;;
8778
+ esac
8779
+ `,
8780
+ }, async ({ tmuxLogPath }) => {
8781
+ await initTeamState(teamName, 'shutdown partial kill test', 'executor', 3, cwd);
8782
+ const config = await readTeamConfig(teamName, cwd);
8783
+ assert.ok(config);
8784
+ if (!config)
8785
+ return;
8786
+ config.tmux_session = 'leader:0';
8787
+ config.leader_pane_id = '%10';
8788
+ config.leader_pane_pid = 2000000010;
8789
+ config.hud_pane_id = null;
8790
+ config.tmux_pane_owner_id = 'owner-1';
8791
+ config.workers[0].pane_id = '%13';
8792
+ config.workers[0].pid = 2000000013;
8793
+ config.workers[1].pane_id = '%14';
8794
+ config.workers[1].pid = 2000000014;
8795
+ config.workers[2].pane_id = '%15';
8796
+ config.workers[2].pid = 2000000015;
8797
+ await saveTeamConfig(config, cwd);
8798
+ await assert.rejects(() => shutdownTeam(teamName, cwd, { force: true }), /shutdown_pane_teardown_failed:%14/);
8799
+ const tmuxLog = await readFile(tmuxLogPath, 'utf-8');
8800
+ assert.match(tmuxLog, /kill-pane -t %13/);
8801
+ assert.match(tmuxLog, /kill-pane -t %14/);
8802
+ assert.doesNotMatch(tmuxLog, /kill-pane -t %15/, 'teardown stops at the first failed kill');
8803
+ const retained = await readTeamConfig(teamName, cwd);
8804
+ assert.ok(retained);
8805
+ assert.equal(retained?.workers[0]?.pane_id, undefined, 'the killed pane is not re-authorized on retry');
8806
+ assert.equal(retained?.workers[0]?.pid, undefined);
8807
+ assert.equal(retained?.workers[1]?.pane_id, '%14', 'the pane whose kill failed stays authorized for retry');
8808
+ assert.equal(retained?.workers[1]?.pid, 2000000014);
8809
+ assert.equal(retained?.workers[2]?.pane_id, '%15', 'a pane never attempted stays authorized for retry');
8810
+ assert.equal(retained?.workers[2]?.pid, 2000000015);
8811
+ });
8812
+ }
8813
+ finally {
8814
+ await rm(cwd, { recursive: true, force: true });
8815
+ }
8816
+ });
8698
8817
  it('shutdownTeam preserves unrelated non-worker panes during shared-session shutdown', async () => {
8699
8818
  const cwd = await mkdtemp(join(tmpdir(), 'omx-runtime-shutdown-unrelated-pane-'));
8700
8819
  const teamName = 'team-shutdown-unrelated-pane';