oh-my-codex 0.18.7 → 0.18.8
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 +6 -6
- package/Cargo.toml +1 -1
- package/README.md +5 -5
- package/crates/omx-sparkshell/tests/execution.rs +1 -1
- package/dist/agents/__tests__/native-config.test.js +42 -1
- package/dist/agents/__tests__/native-config.test.js.map +1 -1
- package/dist/agents/definitions.d.ts +8 -0
- package/dist/agents/definitions.d.ts.map +1 -1
- package/dist/agents/definitions.js +1 -0
- package/dist/agents/definitions.js.map +1 -1
- package/dist/agents/native-config.d.ts +5 -1
- package/dist/agents/native-config.d.ts.map +1 -1
- package/dist/agents/native-config.js +17 -2
- package/dist/agents/native-config.js.map +1 -1
- package/dist/cli/__tests__/codex-plugin-layout.test.js +512 -1
- package/dist/cli/__tests__/codex-plugin-layout.test.js.map +1 -1
- package/dist/cli/__tests__/doctor-warning-copy.test.js +39 -0
- package/dist/cli/__tests__/doctor-warning-copy.test.js.map +1 -1
- package/dist/cli/__tests__/index.test.js +61 -5
- package/dist/cli/__tests__/index.test.js.map +1 -1
- package/dist/cli/__tests__/package-bin-contract.test.js +8 -4
- package/dist/cli/__tests__/package-bin-contract.test.js.map +1 -1
- package/dist/cli/__tests__/ralph-goal-mode-contract.test.js +13 -0
- package/dist/cli/__tests__/ralph-goal-mode-contract.test.js.map +1 -1
- package/dist/cli/__tests__/ralph.test.js +14 -0
- package/dist/cli/__tests__/ralph.test.js.map +1 -1
- package/dist/cli/__tests__/setup-install-mode.test.js +89 -0
- package/dist/cli/__tests__/setup-install-mode.test.js.map +1 -1
- package/dist/cli/__tests__/setup-refresh.test.js +65 -0
- package/dist/cli/__tests__/setup-refresh.test.js.map +1 -1
- package/dist/cli/__tests__/state.test.js +21 -0
- package/dist/cli/__tests__/state.test.js.map +1 -1
- package/dist/cli/__tests__/team.test.js +2 -2
- package/dist/cli/__tests__/update.test.js +110 -2
- package/dist/cli/__tests__/update.test.js.map +1 -1
- package/dist/cli/doctor.d.ts.map +1 -1
- package/dist/cli/doctor.js +8 -1
- package/dist/cli/doctor.js.map +1 -1
- package/dist/cli/index.d.ts +11 -2
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +108 -15
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/plugin-marketplace.d.ts +14 -2
- package/dist/cli/plugin-marketplace.d.ts.map +1 -1
- package/dist/cli/plugin-marketplace.js +62 -15
- package/dist/cli/plugin-marketplace.js.map +1 -1
- package/dist/cli/ralph.d.ts.map +1 -1
- package/dist/cli/ralph.js +3 -1
- package/dist/cli/ralph.js.map +1 -1
- package/dist/cli/setup-preferences.d.ts +2 -0
- package/dist/cli/setup-preferences.d.ts.map +1 -1
- package/dist/cli/setup-preferences.js +4 -0
- package/dist/cli/setup-preferences.js.map +1 -1
- package/dist/cli/setup.d.ts +3 -0
- package/dist/cli/setup.d.ts.map +1 -1
- package/dist/cli/setup.js +166 -27
- package/dist/cli/setup.js.map +1 -1
- package/dist/cli/state.d.ts.map +1 -1
- package/dist/cli/state.js +8 -1
- package/dist/cli/state.js.map +1 -1
- package/dist/cli/tmux-hook.d.ts.map +1 -1
- package/dist/cli/tmux-hook.js +16 -0
- package/dist/cli/tmux-hook.js.map +1 -1
- package/dist/cli/update.d.ts +2 -0
- package/dist/cli/update.d.ts.map +1 -1
- package/dist/cli/update.js +47 -3
- package/dist/cli/update.js.map +1 -1
- package/dist/config/__tests__/generator-notify.test.js +1 -0
- package/dist/config/__tests__/generator-notify.test.js.map +1 -1
- package/dist/config/generator.d.ts +2 -2
- package/dist/config/generator.d.ts.map +1 -1
- package/dist/config/generator.js +2 -2
- package/dist/config/generator.js.map +1 -1
- package/dist/config/team-mode.d.ts +12 -0
- package/dist/config/team-mode.d.ts.map +1 -0
- package/dist/config/team-mode.js +91 -0
- package/dist/config/team-mode.js.map +1 -0
- package/dist/hooks/__tests__/agents-overlay.test.js +88 -0
- package/dist/hooks/__tests__/agents-overlay.test.js.map +1 -1
- package/dist/hooks/__tests__/code-review-skill-contract.test.js +8 -0
- package/dist/hooks/__tests__/code-review-skill-contract.test.js.map +1 -1
- package/dist/hooks/__tests__/keyword-detector.test.js +423 -3
- package/dist/hooks/__tests__/keyword-detector.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-fallback-watcher.test.js +1 -1
- package/dist/hooks/__tests__/notify-fallback-watcher.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-hook-auto-nudge.test.js +189 -0
- package/dist/hooks/__tests__/notify-hook-auto-nudge.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-hook-team-leader-nudge.test.js +35 -2
- package/dist/hooks/__tests__/notify-hook-team-leader-nudge.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-hook-tmux-heal.test.js +3 -3
- package/dist/hooks/__tests__/notify-hook-tmux-heal.test.js.map +1 -1
- package/dist/hooks/__tests__/skill-guidance-contract.test.js +21 -0
- package/dist/hooks/__tests__/skill-guidance-contract.test.js.map +1 -1
- package/dist/hooks/agents-overlay.d.ts.map +1 -1
- package/dist/hooks/agents-overlay.js +36 -50
- package/dist/hooks/agents-overlay.js.map +1 -1
- package/dist/hooks/extensibility/__tests__/plugin-runner.test.js +31 -0
- package/dist/hooks/extensibility/__tests__/plugin-runner.test.js.map +1 -1
- package/dist/hooks/extensibility/plugin-runner.js +17 -21
- package/dist/hooks/extensibility/plugin-runner.js.map +1 -1
- package/dist/hooks/keyword-detector.d.ts.map +1 -1
- package/dist/hooks/keyword-detector.js +258 -12
- package/dist/hooks/keyword-detector.js.map +1 -1
- package/dist/hooks/prompt-guidance-contract.d.ts.map +1 -1
- package/dist/hooks/prompt-guidance-contract.js +6 -0
- package/dist/hooks/prompt-guidance-contract.js.map +1 -1
- package/dist/hooks/session.d.ts +1 -0
- package/dist/hooks/session.d.ts.map +1 -1
- package/dist/hooks/session.js.map +1 -1
- package/dist/hud/__tests__/authority.test.js +435 -32
- package/dist/hud/__tests__/authority.test.js.map +1 -1
- package/dist/hud/__tests__/hud-tmux-injection.test.js +2 -1
- package/dist/hud/__tests__/hud-tmux-injection.test.js.map +1 -1
- package/dist/hud/__tests__/index.test.js +42 -0
- package/dist/hud/__tests__/index.test.js.map +1 -1
- package/dist/hud/__tests__/reconcile.test.js +521 -15
- package/dist/hud/__tests__/reconcile.test.js.map +1 -1
- package/dist/hud/__tests__/render.test.js +61 -0
- package/dist/hud/__tests__/render.test.js.map +1 -1
- package/dist/hud/__tests__/state.test.js +132 -4
- package/dist/hud/__tests__/state.test.js.map +1 -1
- package/dist/hud/__tests__/tmux.test.js +180 -21
- package/dist/hud/__tests__/tmux.test.js.map +1 -1
- package/dist/hud/authority.d.ts +5 -0
- package/dist/hud/authority.d.ts.map +1 -1
- package/dist/hud/authority.js +324 -28
- package/dist/hud/authority.js.map +1 -1
- package/dist/hud/index.d.ts +3 -2
- package/dist/hud/index.d.ts.map +1 -1
- package/dist/hud/index.js +42 -19
- package/dist/hud/index.js.map +1 -1
- package/dist/hud/reconcile.d.ts +3 -3
- package/dist/hud/reconcile.d.ts.map +1 -1
- package/dist/hud/reconcile.js +128 -19
- package/dist/hud/reconcile.js.map +1 -1
- package/dist/hud/render.d.ts.map +1 -1
- package/dist/hud/render.js +35 -0
- package/dist/hud/render.js.map +1 -1
- package/dist/hud/state.d.ts.map +1 -1
- package/dist/hud/state.js +61 -62
- package/dist/hud/state.js.map +1 -1
- package/dist/hud/tmux.d.ts +24 -6
- package/dist/hud/tmux.d.ts.map +1 -1
- package/dist/hud/tmux.js +136 -38
- package/dist/hud/tmux.js.map +1 -1
- package/dist/hud/types.d.ts +11 -0
- package/dist/hud/types.d.ts.map +1 -1
- package/dist/hud/types.js.map +1 -1
- package/dist/mcp/__tests__/state-paths.test.js +71 -1
- package/dist/mcp/__tests__/state-paths.test.js.map +1 -1
- package/dist/mcp/state-paths.d.ts +32 -0
- package/dist/mcp/state-paths.d.ts.map +1 -1
- package/dist/mcp/state-paths.js +113 -17
- package/dist/mcp/state-paths.js.map +1 -1
- package/dist/mcp/state-server.d.ts +4 -4
- package/dist/scripts/__tests__/codex-native-hook.test.js +593 -11
- package/dist/scripts/__tests__/codex-native-hook.test.js.map +1 -1
- package/dist/scripts/__tests__/notify-state-io.test.js +72 -1
- package/dist/scripts/__tests__/notify-state-io.test.js.map +1 -1
- package/dist/scripts/__tests__/notify-tmux-injection.test.d.ts +2 -0
- package/dist/scripts/__tests__/notify-tmux-injection.test.d.ts.map +1 -0
- package/dist/scripts/__tests__/notify-tmux-injection.test.js +57 -0
- package/dist/scripts/__tests__/notify-tmux-injection.test.js.map +1 -0
- package/dist/scripts/__tests__/run-test-files.test.js +74 -0
- package/dist/scripts/__tests__/run-test-files.test.js.map +1 -1
- package/dist/scripts/__tests__/verify-native-agents.test.js +65 -0
- package/dist/scripts/__tests__/verify-native-agents.test.js.map +1 -1
- package/dist/scripts/codex-native-hook.d.ts.map +1 -1
- package/dist/scripts/codex-native-hook.js +88 -31
- package/dist/scripts/codex-native-hook.js.map +1 -1
- package/dist/scripts/eval/eval-parity-smoke.js +1 -1
- package/dist/scripts/eval/eval-parity-smoke.js.map +1 -1
- package/dist/scripts/notify-hook/auto-nudge.d.ts.map +1 -1
- package/dist/scripts/notify-hook/auto-nudge.js +3 -1
- package/dist/scripts/notify-hook/auto-nudge.js.map +1 -1
- package/dist/scripts/notify-hook/ralph-session-resume.d.ts.map +1 -1
- package/dist/scripts/notify-hook/ralph-session-resume.js +3 -10
- package/dist/scripts/notify-hook/ralph-session-resume.js.map +1 -1
- package/dist/scripts/notify-hook/state-io.d.ts.map +1 -1
- package/dist/scripts/notify-hook/state-io.js +62 -38
- package/dist/scripts/notify-hook/state-io.js.map +1 -1
- package/dist/scripts/notify-hook/team-leader-nudge.d.ts.map +1 -1
- package/dist/scripts/notify-hook/team-leader-nudge.js +7 -0
- package/dist/scripts/notify-hook/team-leader-nudge.js.map +1 -1
- package/dist/scripts/notify-hook/tmux-injection.d.ts +7 -0
- package/dist/scripts/notify-hook/tmux-injection.d.ts.map +1 -1
- package/dist/scripts/notify-hook/tmux-injection.js +24 -18
- package/dist/scripts/notify-hook/tmux-injection.js.map +1 -1
- package/dist/scripts/notify-hook.js +75 -11
- package/dist/scripts/notify-hook.js.map +1 -1
- package/dist/scripts/run-test-files.js +193 -22
- package/dist/scripts/run-test-files.js.map +1 -1
- package/dist/scripts/sync-plugin-mirror.d.ts.map +1 -1
- package/dist/scripts/sync-plugin-mirror.js +61 -3
- package/dist/scripts/sync-plugin-mirror.js.map +1 -1
- package/dist/scripts/verify-native-agents.d.ts.map +1 -1
- package/dist/scripts/verify-native-agents.js +58 -1
- package/dist/scripts/verify-native-agents.js.map +1 -1
- package/dist/state/__tests__/operations.test.js +113 -0
- package/dist/state/__tests__/operations.test.js.map +1 -1
- package/dist/state/__tests__/skill-active.test.js +3 -16
- package/dist/state/__tests__/skill-active.test.js.map +1 -1
- package/dist/state/__tests__/workflow-transition.test.js +25 -0
- package/dist/state/__tests__/workflow-transition.test.js.map +1 -1
- package/dist/state/operations.d.ts.map +1 -1
- package/dist/state/operations.js +57 -2
- package/dist/state/operations.js.map +1 -1
- package/dist/state/skill-active.d.ts.map +1 -1
- package/dist/state/skill-active.js +7 -39
- package/dist/state/skill-active.js.map +1 -1
- package/dist/state/workflow-transition-reconcile.d.ts.map +1 -1
- package/dist/state/workflow-transition-reconcile.js +10 -14
- package/dist/state/workflow-transition-reconcile.js.map +1 -1
- package/dist/team/__tests__/runtime.test.js +1 -1
- package/dist/team/__tests__/runtime.test.js.map +1 -1
- package/dist/team/__tests__/scaling.test.js +9 -4
- package/dist/team/__tests__/scaling.test.js.map +1 -1
- package/dist/team/__tests__/tmux-session.test.js +195 -2
- package/dist/team/__tests__/tmux-session.test.js.map +1 -1
- package/dist/team/__tests__/worker-runtime-identity.test.js +4 -2
- package/dist/team/__tests__/worker-runtime-identity.test.js.map +1 -1
- package/dist/team/scaling.d.ts.map +1 -1
- package/dist/team/scaling.js +3 -2
- package/dist/team/scaling.js.map +1 -1
- package/dist/team/tmux-session.d.ts +2 -0
- package/dist/team/tmux-session.d.ts.map +1 -1
- package/dist/team/tmux-session.js +142 -12
- package/dist/team/tmux-session.js.map +1 -1
- package/dist/verification/__tests__/ci-rust-gates.test.js +81 -1
- package/dist/verification/__tests__/ci-rust-gates.test.js.map +1 -1
- package/package.json +8 -8
- package/plugins/oh-my-codex/.codex-plugin/plugin.json +1 -1
- package/plugins/oh-my-codex/hooks/codex-native-hook.mjs +334 -21
- package/plugins/oh-my-codex/hooks/hooks.json +1 -2
- package/plugins/oh-my-codex/skills/autopilot/SKILL.md +3 -1
- package/plugins/oh-my-codex/skills/code-review/SKILL.md +7 -7
- package/plugins/oh-my-codex/skills/ralph/SKILL.md +22 -22
- package/plugins/oh-my-codex/skills/ultraqa/SKILL.md +9 -0
- package/skills/autopilot/SKILL.md +3 -1
- package/skills/code-review/SKILL.md +7 -7
- package/skills/ralph/SKILL.md +22 -22
- package/skills/ultraqa/SKILL.md +9 -0
- package/src/scripts/__tests__/codex-native-hook.test.ts +686 -13
- package/src/scripts/__tests__/notify-state-io.test.ts +95 -0
- package/src/scripts/__tests__/notify-tmux-injection.test.ts +82 -0
- package/src/scripts/__tests__/run-test-files.test.ts +102 -0
- package/src/scripts/__tests__/verify-native-agents.test.ts +75 -0
- package/src/scripts/codex-native-hook.ts +105 -28
- package/src/scripts/demo-team-e2e.sh +10 -7
- package/src/scripts/eval/eval-parity-smoke.ts +1 -1
- package/src/scripts/notify-hook/auto-nudge.ts +3 -1
- package/src/scripts/notify-hook/ralph-session-resume.ts +2 -8
- package/src/scripts/notify-hook/state-io.ts +75 -37
- package/src/scripts/notify-hook/team-leader-nudge.ts +7 -0
- package/src/scripts/notify-hook/tmux-injection.ts +35 -19
- package/src/scripts/notify-hook.ts +91 -4
- package/src/scripts/run-test-files.ts +192 -22
- package/src/scripts/sync-plugin-mirror.ts +98 -9
- package/src/scripts/verify-native-agents.ts +65 -1
|
@@ -84,6 +84,139 @@ describe('reconcileHudForPromptSubmit', () => {
|
|
|
84
84
|
assert.equal(resized.length, 1);
|
|
85
85
|
assert.equal(resized[0]?.heightLines, HUD_TMUX_HEIGHT_LINES);
|
|
86
86
|
});
|
|
87
|
+
it('reaps orphaned same-session HUD panes whose leader pane was destroyed, then recreates a single HUD', async () => {
|
|
88
|
+
// Regression for the "team mode leaves only stacked HUD strips" bug: the leader
|
|
89
|
+
// pane (%21) was destroyed but its owner-tagged HUD panes remained, all pointing
|
|
90
|
+
// at the dead leader id. They match neither findHudWatchPaneIds (leader mismatch)
|
|
91
|
+
// nor findLegacyFocusedHudWatchPaneIds (they carry owner metadata), so each prompt
|
|
92
|
+
// submit previously appended a fresh HUD instead of reclaiming the orphans.
|
|
93
|
+
const killed = [];
|
|
94
|
+
const created = [];
|
|
95
|
+
const orphan = (paneId) => ({
|
|
96
|
+
paneId,
|
|
97
|
+
currentCommand: 'node',
|
|
98
|
+
startCommand: `exec env OMX_SESSION_ID='sess-a' OMX_TMUX_HUD_OWNER='1' ${OMX_TMUX_HUD_LEADER_PANE_ENV}='%21' node omx hud --watch --preset=focused`,
|
|
99
|
+
});
|
|
100
|
+
const result = await reconcileHudForPromptSubmit('/repo', {
|
|
101
|
+
env: { TMUX: '1', TMUX_PANE: '%33', OMX_SESSION_ID: 'sess-a', [OMX_TMUX_HUD_OWNER_ENV]: '1' },
|
|
102
|
+
listCurrentWindowPanes: () => [
|
|
103
|
+
// %33 is the current (live) leader pane; %21 is gone from the window.
|
|
104
|
+
{ paneId: '%33', currentCommand: 'codex', startCommand: 'codex' },
|
|
105
|
+
orphan('%34'),
|
|
106
|
+
orphan('%42'),
|
|
107
|
+
orphan('%47'),
|
|
108
|
+
],
|
|
109
|
+
killTmuxPane: (paneId) => {
|
|
110
|
+
killed.push(paneId);
|
|
111
|
+
return true;
|
|
112
|
+
},
|
|
113
|
+
resizeTmuxPane: () => true,
|
|
114
|
+
createHudWatchPane: (_cwd, cmd, options) => {
|
|
115
|
+
created.push({ cmd, options });
|
|
116
|
+
return '%50';
|
|
117
|
+
},
|
|
118
|
+
resolveOmxCliEntryPath: () => '/repo/dist/cli/omx.js',
|
|
119
|
+
});
|
|
120
|
+
// All three dead-leader orphans are reaped, then exactly one fresh HUD is created.
|
|
121
|
+
assert.deepEqual(killed.sort(), ['%34', '%42', '%47']);
|
|
122
|
+
assert.equal(result.status, 'recreated');
|
|
123
|
+
assert.equal(result.paneId, '%50');
|
|
124
|
+
assert.equal(created.length, 1);
|
|
125
|
+
assert.equal(created[0]?.options?.targetPaneId, '%33');
|
|
126
|
+
assert.match(created[0]?.cmd || '', new RegExp(`${OMX_TMUX_HUD_LEADER_PANE_ENV}='%33'`));
|
|
127
|
+
});
|
|
128
|
+
it('reaps orphaned HUD panes tagged with an equivalent native session id', async () => {
|
|
129
|
+
// #2684 lets HUD dedupe treat the OMX owner id and Codex native session id as
|
|
130
|
+
// equivalent. Orphan reaping must use the same identity set so a canonical
|
|
131
|
+
// owner reconcile still reclaims dead-leader HUDs tagged with the native id.
|
|
132
|
+
const killed = [];
|
|
133
|
+
const result = await reconcileHudForPromptSubmit('/repo', {
|
|
134
|
+
env: { TMUX: '1', TMUX_PANE: '%1', OMX_SESSION_ID: 'codex-native-uuid', [OMX_TMUX_HUD_OWNER_ENV]: '1' },
|
|
135
|
+
sessionId: 'omx-owner-abc',
|
|
136
|
+
sessionIds: ['omx-owner-abc', 'codex-native-uuid'],
|
|
137
|
+
listCurrentWindowPanes: () => [
|
|
138
|
+
{ paneId: '%1', currentCommand: 'codex', startCommand: 'codex' },
|
|
139
|
+
{
|
|
140
|
+
paneId: '%2',
|
|
141
|
+
currentCommand: 'node',
|
|
142
|
+
startCommand: `env OMX_SESSION_ID='codex-native-uuid' OMX_TMUX_HUD_OWNER='1' ${OMX_TMUX_HUD_LEADER_PANE_ENV}='%21' node omx hud --watch`,
|
|
143
|
+
},
|
|
144
|
+
],
|
|
145
|
+
killTmuxPane: (paneId) => {
|
|
146
|
+
killed.push(paneId);
|
|
147
|
+
return true;
|
|
148
|
+
},
|
|
149
|
+
resizeTmuxPane: () => true,
|
|
150
|
+
createHudWatchPane: () => '%9',
|
|
151
|
+
resolveOmxCliEntryPath: () => '/repo/dist/cli/omx.js',
|
|
152
|
+
});
|
|
153
|
+
assert.deepEqual(killed, ['%2']);
|
|
154
|
+
assert.equal(result.status, 'recreated');
|
|
155
|
+
assert.equal(result.paneId, '%9');
|
|
156
|
+
});
|
|
157
|
+
it('does not reap an orphaned HUD pane that belongs to a different session', async () => {
|
|
158
|
+
// A HUD owned by another session's leader (which may live in a different tmux
|
|
159
|
+
// window we cannot see here) must survive even when that leader is absent.
|
|
160
|
+
const killed = [];
|
|
161
|
+
const result = await reconcileHudForPromptSubmit('/repo', {
|
|
162
|
+
env: { TMUX: '1', TMUX_PANE: '%1', OMX_SESSION_ID: 'sess-a', [OMX_TMUX_HUD_OWNER_ENV]: '1' },
|
|
163
|
+
listCurrentWindowPanes: () => [
|
|
164
|
+
{ paneId: '%1', currentCommand: 'codex', startCommand: 'codex' },
|
|
165
|
+
{
|
|
166
|
+
paneId: '%4',
|
|
167
|
+
currentCommand: 'node',
|
|
168
|
+
startCommand: `env OMX_SESSION_ID='sess-b' OMX_TMUX_HUD_OWNER='1' ${OMX_TMUX_HUD_LEADER_PANE_ENV}='%5' node omx hud --watch`,
|
|
169
|
+
},
|
|
170
|
+
],
|
|
171
|
+
killTmuxPane: (paneId) => {
|
|
172
|
+
killed.push(paneId);
|
|
173
|
+
return true;
|
|
174
|
+
},
|
|
175
|
+
resizeTmuxPane: () => true,
|
|
176
|
+
createHudWatchPane: () => '%9',
|
|
177
|
+
resolveOmxCliEntryPath: () => '/repo/dist/cli/omx.js',
|
|
178
|
+
});
|
|
179
|
+
// sess-b orphan is left untouched; this session simply creates its own HUD.
|
|
180
|
+
assert.deepEqual(killed, []);
|
|
181
|
+
assert.equal(result.status, 'recreated');
|
|
182
|
+
assert.equal(result.paneId, '%9');
|
|
183
|
+
});
|
|
184
|
+
it('reaps a same-session orphan whose recorded leader is itself another HUD pane', async () => {
|
|
185
|
+
// Review follow-up (#2682): when a HUD pane was mistakenly used as a leader, an
|
|
186
|
+
// orphan can name another HUD pane as its leader. That referenced HUD must not
|
|
187
|
+
// count as a live leader, or the orphan survives while the referenced HUD is
|
|
188
|
+
// reaped — leaving a dangling strip that still never matches the real pane.
|
|
189
|
+
const killed = [];
|
|
190
|
+
const result = await reconcileHudForPromptSubmit('/repo', {
|
|
191
|
+
env: { TMUX: '1', TMUX_PANE: '%1', OMX_SESSION_ID: 'sess-a', [OMX_TMUX_HUD_OWNER_ENV]: '1' },
|
|
192
|
+
listCurrentWindowPanes: () => [
|
|
193
|
+
{ paneId: '%1', currentCommand: 'codex', startCommand: 'codex' },
|
|
194
|
+
{
|
|
195
|
+
// orphan whose recorded leader (%3) is itself another HUD pane
|
|
196
|
+
paneId: '%2',
|
|
197
|
+
currentCommand: 'node',
|
|
198
|
+
startCommand: `exec env OMX_SESSION_ID='sess-a' OMX_TMUX_HUD_OWNER='1' ${OMX_TMUX_HUD_LEADER_PANE_ENV}='%3' node omx hud --watch --preset=focused`,
|
|
199
|
+
},
|
|
200
|
+
{
|
|
201
|
+
// the referenced HUD %3, itself orphaned (its leader %21 is gone)
|
|
202
|
+
paneId: '%3',
|
|
203
|
+
currentCommand: 'node',
|
|
204
|
+
startCommand: `exec env OMX_SESSION_ID='sess-a' OMX_TMUX_HUD_OWNER='1' ${OMX_TMUX_HUD_LEADER_PANE_ENV}='%21' node omx hud --watch --preset=focused`,
|
|
205
|
+
},
|
|
206
|
+
],
|
|
207
|
+
killTmuxPane: (paneId) => {
|
|
208
|
+
killed.push(paneId);
|
|
209
|
+
return true;
|
|
210
|
+
},
|
|
211
|
+
resizeTmuxPane: () => true,
|
|
212
|
+
createHudWatchPane: () => '%9',
|
|
213
|
+
resolveOmxCliEntryPath: () => '/repo/dist/cli/omx.js',
|
|
214
|
+
});
|
|
215
|
+
// Both HUD-led and dead-leader orphans are reaped; a single fresh HUD is created.
|
|
216
|
+
assert.deepEqual(killed.sort(), ['%2', '%3']);
|
|
217
|
+
assert.equal(result.status, 'recreated');
|
|
218
|
+
assert.equal(result.paneId, '%9');
|
|
219
|
+
});
|
|
87
220
|
it('prefers an explicit session override when recreating HUD', async () => {
|
|
88
221
|
const created = [];
|
|
89
222
|
const result = await reconcileHudForPromptSubmit('/repo', {
|
|
@@ -128,6 +261,87 @@ describe('reconcileHudForPromptSubmit', () => {
|
|
|
128
261
|
assert.equal(created.length, 1);
|
|
129
262
|
assert.match(created[0]?.cmd || '', /^exec env OMX_SESSION_ID='sess boxed' OMX_TMUX_HUD_OWNER='1' OMX_TMUX_HUD_LEADER_PANE='%1' OMX_ROOT='\/tmp\/boxed root\/it'\\''s\/\$\(literal\)' '.*' '.*omx\.js' hud --watch/);
|
|
130
263
|
});
|
|
264
|
+
it('forwards OMX_STATE_ROOT when recreating HUD with shell-safe quoting', async () => {
|
|
265
|
+
const created = [];
|
|
266
|
+
const result = await reconcileHudForPromptSubmit('/repo', {
|
|
267
|
+
env: {
|
|
268
|
+
TMUX: '1',
|
|
269
|
+
TMUX_PANE: '%1',
|
|
270
|
+
OMX_SESSION_ID: 'sess-a',
|
|
271
|
+
OMX_STATE_ROOT: '/boxed state/root',
|
|
272
|
+
[OMX_TMUX_HUD_OWNER_ENV]: '1',
|
|
273
|
+
},
|
|
274
|
+
listCurrentWindowPanes: () => [{ paneId: '%1', currentCommand: 'codex', startCommand: 'codex' }],
|
|
275
|
+
createHudWatchPane: (_cwd, hudCmd) => {
|
|
276
|
+
created.push(hudCmd);
|
|
277
|
+
return '%9';
|
|
278
|
+
},
|
|
279
|
+
resolveOmxCliEntryPath: () => '/repo/dist/cli/omx.js',
|
|
280
|
+
resizeTmuxPane: () => true,
|
|
281
|
+
readHudConfig: async () => ({ preset: 'focused', git: { display: 'branch' }, statusLine: { preset: 'focused' } }),
|
|
282
|
+
readAllState: async () => ({
|
|
283
|
+
version: null,
|
|
284
|
+
gitBranch: null,
|
|
285
|
+
ralph: null,
|
|
286
|
+
ultragoal: null,
|
|
287
|
+
ultrawork: null,
|
|
288
|
+
autopilot: null,
|
|
289
|
+
ralplan: null,
|
|
290
|
+
deepInterview: null,
|
|
291
|
+
autoresearch: null,
|
|
292
|
+
ultraqa: null,
|
|
293
|
+
team: null,
|
|
294
|
+
metrics: null,
|
|
295
|
+
hudNotify: null,
|
|
296
|
+
session: null,
|
|
297
|
+
}),
|
|
298
|
+
});
|
|
299
|
+
assert.equal(result.status, 'recreated');
|
|
300
|
+
assert.match(created[0] ?? '', /OMX_STATE_ROOT='\/boxed state\/root'/);
|
|
301
|
+
assert.doesNotMatch(created[0] ?? '', /OMX_ROOT=/);
|
|
302
|
+
});
|
|
303
|
+
it('forwards OMX_TEAM_STATE_ROOT before boxed roots when recreating HUD', async () => {
|
|
304
|
+
const created = [];
|
|
305
|
+
const result = await reconcileHudForPromptSubmit('/repo', {
|
|
306
|
+
env: {
|
|
307
|
+
TMUX: '1',
|
|
308
|
+
TMUX_PANE: '%1',
|
|
309
|
+
OMX_SESSION_ID: 'sess-a',
|
|
310
|
+
OMX_ROOT: '/boxed-root',
|
|
311
|
+
OMX_STATE_ROOT: '/boxed-state-root',
|
|
312
|
+
OMX_TEAM_STATE_ROOT: '/team-state-root',
|
|
313
|
+
[OMX_TMUX_HUD_OWNER_ENV]: '1',
|
|
314
|
+
},
|
|
315
|
+
listCurrentWindowPanes: () => [{ paneId: '%1', currentCommand: 'codex', startCommand: 'codex' }],
|
|
316
|
+
createHudWatchPane: (_cwd, hudCmd) => {
|
|
317
|
+
created.push(hudCmd);
|
|
318
|
+
return '%9';
|
|
319
|
+
},
|
|
320
|
+
resolveOmxCliEntryPath: () => '/repo/dist/cli/omx.js',
|
|
321
|
+
resizeTmuxPane: () => true,
|
|
322
|
+
readHudConfig: async () => ({ preset: 'focused', git: { display: 'branch' }, statusLine: { preset: 'focused' } }),
|
|
323
|
+
readAllState: async () => ({
|
|
324
|
+
version: null,
|
|
325
|
+
gitBranch: null,
|
|
326
|
+
ralph: null,
|
|
327
|
+
ultragoal: null,
|
|
328
|
+
ultrawork: null,
|
|
329
|
+
autopilot: null,
|
|
330
|
+
ralplan: null,
|
|
331
|
+
deepInterview: null,
|
|
332
|
+
autoresearch: null,
|
|
333
|
+
ultraqa: null,
|
|
334
|
+
team: null,
|
|
335
|
+
metrics: null,
|
|
336
|
+
hudNotify: null,
|
|
337
|
+
session: null,
|
|
338
|
+
}),
|
|
339
|
+
});
|
|
340
|
+
assert.equal(result.status, 'recreated');
|
|
341
|
+
assert.match(created[0] ?? '', /OMX_TEAM_STATE_ROOT='\/team-state-root'/);
|
|
342
|
+
assert.doesNotMatch(created[0] ?? '', /OMX_ROOT=/);
|
|
343
|
+
assert.doesNotMatch(created[0] ?? '', /OMX_STATE_ROOT=/);
|
|
344
|
+
});
|
|
131
345
|
it('targets the emitting pane window when listing and creating HUD panes', async () => {
|
|
132
346
|
const listArgs = [];
|
|
133
347
|
const created = [];
|
|
@@ -147,9 +361,138 @@ describe('reconcileHudForPromptSubmit', () => {
|
|
|
147
361
|
resolveOmxCliEntryPath: () => '/repo/dist/cli/omx.js',
|
|
148
362
|
});
|
|
149
363
|
assert.equal(result.status, 'recreated');
|
|
150
|
-
assert.deepEqual(listArgs, ['%leader']);
|
|
364
|
+
assert.deepEqual(listArgs, ['%leader', '%leader']);
|
|
151
365
|
assert.equal(created[0]?.options?.targetPaneId, '%leader');
|
|
152
366
|
});
|
|
367
|
+
it('keeps prompt-submit HUD recreation scoped to the emitting pane in multi-pane windows', async () => {
|
|
368
|
+
const created = [];
|
|
369
|
+
const result = await reconcileHudForPromptSubmit('/repo', {
|
|
370
|
+
env: { TMUX: '1', TMUX_PANE: '%right', OMX_SESSION_ID: 'sess-right', [OMX_TMUX_HUD_OWNER_ENV]: '1' },
|
|
371
|
+
listCurrentWindowPanes: () => [
|
|
372
|
+
{ paneId: '%left', currentCommand: 'codex', startCommand: 'codex' },
|
|
373
|
+
{ paneId: '%right', currentCommand: 'codex', startCommand: 'codex' },
|
|
374
|
+
],
|
|
375
|
+
createHudWatchPane: (_cwd, _cmd, options) => {
|
|
376
|
+
created.push({ options });
|
|
377
|
+
return '%hud-right';
|
|
378
|
+
},
|
|
379
|
+
resizeTmuxPane: () => true,
|
|
380
|
+
resolveOmxCliEntryPath: () => '/repo/dist/cli/omx.js',
|
|
381
|
+
});
|
|
382
|
+
assert.equal(result.status, 'recreated');
|
|
383
|
+
assert.equal(created[0]?.options?.targetPaneId, '%right');
|
|
384
|
+
assert.equal(Object.hasOwn(created[0]?.options ?? {}, 'fullWidth'), false);
|
|
385
|
+
});
|
|
386
|
+
it('collapses same-owner HUD panes that appear during the create race window', async () => {
|
|
387
|
+
const killed = [];
|
|
388
|
+
const resized = [];
|
|
389
|
+
let listCount = 0;
|
|
390
|
+
const result = await reconcileHudForPromptSubmit('/repo', {
|
|
391
|
+
env: { TMUX: '1', TMUX_PANE: '%1', OMX_SESSION_ID: 'sess-race', [OMX_TMUX_HUD_OWNER_ENV]: '1' },
|
|
392
|
+
listCurrentWindowPanes: () => {
|
|
393
|
+
listCount += 1;
|
|
394
|
+
if (listCount === 1) {
|
|
395
|
+
return [
|
|
396
|
+
{ paneId: '%1', currentCommand: 'codex', startCommand: 'codex' },
|
|
397
|
+
];
|
|
398
|
+
}
|
|
399
|
+
return [
|
|
400
|
+
{ paneId: '%1', currentCommand: 'codex', startCommand: 'codex' },
|
|
401
|
+
{
|
|
402
|
+
paneId: '%8',
|
|
403
|
+
currentCommand: 'node',
|
|
404
|
+
startCommand: `exec env OMX_SESSION_ID='sess-race' ${OMX_TMUX_HUD_LEADER_PANE_ENV}='%1' /node /omx.js hud --watch`,
|
|
405
|
+
},
|
|
406
|
+
{
|
|
407
|
+
paneId: '%9',
|
|
408
|
+
currentCommand: 'node',
|
|
409
|
+
startCommand: `exec env OMX_SESSION_ID='sess-race' ${OMX_TMUX_HUD_LEADER_PANE_ENV}='%1' /node /omx.js hud --watch --preset=focused`,
|
|
410
|
+
},
|
|
411
|
+
];
|
|
412
|
+
},
|
|
413
|
+
createHudWatchPane: () => '%9',
|
|
414
|
+
killTmuxPane: (paneId) => {
|
|
415
|
+
killed.push(paneId);
|
|
416
|
+
return true;
|
|
417
|
+
},
|
|
418
|
+
resizeTmuxPane: (paneId, heightLines) => {
|
|
419
|
+
resized.push({ paneId, heightLines });
|
|
420
|
+
return true;
|
|
421
|
+
},
|
|
422
|
+
resolveOmxCliEntryPath: () => '/repo/dist/cli/omx.js',
|
|
423
|
+
});
|
|
424
|
+
assert.equal(result.status, 'replaced_duplicates');
|
|
425
|
+
assert.equal(result.paneId, '%9');
|
|
426
|
+
assert.equal(result.duplicateCount, 1);
|
|
427
|
+
assert.deepEqual(killed, ['%8']);
|
|
428
|
+
assert.deepEqual(resized, [{ paneId: '%9', heightLines: HUD_TMUX_HEIGHT_LINES }]);
|
|
429
|
+
});
|
|
430
|
+
it('keeps an observed same-owner HUD when the returned create pane is absent from the post-create scan', async () => {
|
|
431
|
+
const killed = [];
|
|
432
|
+
const resized = [];
|
|
433
|
+
let listCount = 0;
|
|
434
|
+
const result = await reconcileHudForPromptSubmit('/repo', {
|
|
435
|
+
env: { TMUX: '1', TMUX_PANE: '%1', OMX_SESSION_ID: 'sess-race', [OMX_TMUX_HUD_OWNER_ENV]: '1' },
|
|
436
|
+
listCurrentWindowPanes: () => {
|
|
437
|
+
listCount += 1;
|
|
438
|
+
if (listCount === 1)
|
|
439
|
+
return [{ paneId: '%1', currentCommand: 'codex', startCommand: 'codex' }];
|
|
440
|
+
return [
|
|
441
|
+
{ paneId: '%1', currentCommand: 'codex', startCommand: 'codex' },
|
|
442
|
+
{
|
|
443
|
+
paneId: '%8',
|
|
444
|
+
currentCommand: 'node',
|
|
445
|
+
startCommand: `exec env OMX_SESSION_ID='sess-race' ${OMX_TMUX_HUD_LEADER_PANE_ENV}='%1' /node /omx.js hud --watch`,
|
|
446
|
+
},
|
|
447
|
+
];
|
|
448
|
+
},
|
|
449
|
+
createHudWatchPane: () => '%9',
|
|
450
|
+
killTmuxPane: (paneId) => { killed.push(paneId); return true; },
|
|
451
|
+
resizeTmuxPane: (paneId, heightLines) => { resized.push({ paneId, heightLines }); return true; },
|
|
452
|
+
resolveOmxCliEntryPath: () => '/repo/dist/cli/omx.js',
|
|
453
|
+
});
|
|
454
|
+
assert.equal(result.status, 'recreated');
|
|
455
|
+
assert.equal(result.paneId, '%8');
|
|
456
|
+
assert.equal(result.duplicateCount, 0);
|
|
457
|
+
assert.deepEqual(killed, []);
|
|
458
|
+
assert.deepEqual(resized, [{ paneId: '%8', heightLines: HUD_TMUX_HEIGHT_LINES }]);
|
|
459
|
+
});
|
|
460
|
+
it('kills post-create duplicate HUD panes even when the keeper cannot be resized', async () => {
|
|
461
|
+
const killed = [];
|
|
462
|
+
const registered = [];
|
|
463
|
+
let listCount = 0;
|
|
464
|
+
const result = await reconcileHudForPromptSubmit('/repo', {
|
|
465
|
+
env: { TMUX: '1', TMUX_PANE: '%1', OMX_SESSION_ID: 'sess-race', [OMX_TMUX_HUD_OWNER_ENV]: '1' },
|
|
466
|
+
listCurrentWindowPanes: () => {
|
|
467
|
+
listCount += 1;
|
|
468
|
+
if (listCount === 1)
|
|
469
|
+
return [{ paneId: '%1', currentCommand: 'codex', startCommand: 'codex' }];
|
|
470
|
+
return [
|
|
471
|
+
{ paneId: '%1', currentCommand: 'codex', startCommand: 'codex' },
|
|
472
|
+
{
|
|
473
|
+
paneId: '%8',
|
|
474
|
+
currentCommand: 'node',
|
|
475
|
+
startCommand: `exec env OMX_SESSION_ID='sess-race' ${OMX_TMUX_HUD_LEADER_PANE_ENV}='%1' /node /omx.js hud --watch`,
|
|
476
|
+
},
|
|
477
|
+
{
|
|
478
|
+
paneId: '%9',
|
|
479
|
+
currentCommand: 'node',
|
|
480
|
+
startCommand: `exec env OMX_SESSION_ID='sess-race' ${OMX_TMUX_HUD_LEADER_PANE_ENV}='%1' /node /omx.js hud --watch`,
|
|
481
|
+
},
|
|
482
|
+
];
|
|
483
|
+
},
|
|
484
|
+
createHudWatchPane: () => '%9',
|
|
485
|
+
killTmuxPane: (paneId) => { killed.push(paneId); return true; },
|
|
486
|
+
resizeTmuxPane: () => false,
|
|
487
|
+
registerHudResizeHook: (paneId) => { registered.push(paneId); return true; },
|
|
488
|
+
resolveOmxCliEntryPath: () => '/repo/dist/cli/omx.js',
|
|
489
|
+
});
|
|
490
|
+
assert.equal(result.status, 'failed');
|
|
491
|
+
assert.equal(result.paneId, '%9');
|
|
492
|
+
assert.equal(result.duplicateCount, 1);
|
|
493
|
+
assert.deepEqual(killed, ['%8']);
|
|
494
|
+
assert.deepEqual(registered, []);
|
|
495
|
+
});
|
|
153
496
|
it('kills duplicate HUD panes and reuses one existing pane', async () => {
|
|
154
497
|
const killed = [];
|
|
155
498
|
const resized = [];
|
|
@@ -190,6 +533,164 @@ describe('reconcileHudForPromptSubmit', () => {
|
|
|
190
533
|
assert.deepEqual(resized, [{ paneId: '%2', heightLines: HUD_TMUX_HEIGHT_LINES }]);
|
|
191
534
|
assert.deepEqual(created, []);
|
|
192
535
|
});
|
|
536
|
+
it('deduplicates same-leader HUD panes tagged with equivalent owner and canonical session ids', async () => {
|
|
537
|
+
const killed = [];
|
|
538
|
+
const resized = [];
|
|
539
|
+
const created = [];
|
|
540
|
+
const result = await reconcileHudForPromptSubmit('/repo', {
|
|
541
|
+
env: { TMUX: '1', TMUX_PANE: '%1', OMX_SESSION_ID: 'codex-native-uuid', [OMX_TMUX_HUD_OWNER_ENV]: '1' },
|
|
542
|
+
sessionId: 'omx-owner-abc',
|
|
543
|
+
sessionIds: ['omx-owner-abc', 'codex-native-uuid'],
|
|
544
|
+
listCurrentWindowPanes: () => [
|
|
545
|
+
{ paneId: '%1', currentCommand: 'codex', startCommand: 'codex' },
|
|
546
|
+
{
|
|
547
|
+
paneId: '%2',
|
|
548
|
+
currentCommand: 'node',
|
|
549
|
+
startCommand: `env OMX_SESSION_ID='omx-owner-abc' ${OMX_TMUX_HUD_LEADER_PANE_ENV}='%1' node omx hud --watch`,
|
|
550
|
+
},
|
|
551
|
+
{
|
|
552
|
+
paneId: '%3',
|
|
553
|
+
currentCommand: 'node',
|
|
554
|
+
startCommand: `env OMX_SESSION_ID='codex-native-uuid' ${OMX_TMUX_HUD_LEADER_PANE_ENV}='%1' node omx hud --watch`,
|
|
555
|
+
},
|
|
556
|
+
{
|
|
557
|
+
// Same equivalent session, but its recorded leader is itself a HUD pane;
|
|
558
|
+
// the orphan reaper should remove it before normal same-leader dedupe.
|
|
559
|
+
paneId: '%4',
|
|
560
|
+
currentCommand: 'node',
|
|
561
|
+
startCommand: `env OMX_SESSION_ID='codex-native-uuid' ${OMX_TMUX_HUD_LEADER_PANE_ENV}='%4' node omx hud --watch`,
|
|
562
|
+
},
|
|
563
|
+
],
|
|
564
|
+
killTmuxPane: (paneId) => {
|
|
565
|
+
killed.push(paneId);
|
|
566
|
+
return true;
|
|
567
|
+
},
|
|
568
|
+
createHudWatchPane: (_cwd, cmd) => {
|
|
569
|
+
created.push({ cmd });
|
|
570
|
+
return '%9';
|
|
571
|
+
},
|
|
572
|
+
resizeTmuxPane: (paneId, heightLines) => {
|
|
573
|
+
resized.push({ paneId, heightLines });
|
|
574
|
+
return true;
|
|
575
|
+
},
|
|
576
|
+
resolveOmxCliEntryPath: () => '/repo/dist/cli/omx.js',
|
|
577
|
+
});
|
|
578
|
+
assert.equal(result.status, 'replaced_duplicates');
|
|
579
|
+
assert.equal(result.paneId, '%2');
|
|
580
|
+
assert.equal(result.duplicateCount, 1);
|
|
581
|
+
assert.deepEqual(killed, ['%4', '%3']);
|
|
582
|
+
assert.deepEqual(resized, [{ paneId: '%2', heightLines: HUD_TMUX_HEIGHT_LINES }]);
|
|
583
|
+
assert.deepEqual(created, []);
|
|
584
|
+
});
|
|
585
|
+
it('reuses and deduplicates legacy unowned focused HUD watch panes before recreating', async () => {
|
|
586
|
+
const killed = [];
|
|
587
|
+
const resized = [];
|
|
588
|
+
const created = [];
|
|
589
|
+
const result = await reconcileHudForPromptSubmit('/repo', {
|
|
590
|
+
env: { TMUX: '1', TMUX_PANE: '%1', OMX_SESSION_ID: 'sess-a', [OMX_TMUX_HUD_OWNER_ENV]: '1' },
|
|
591
|
+
listCurrentWindowPanes: () => [
|
|
592
|
+
{ paneId: '%1', currentCommand: 'codex', startCommand: 'codex' },
|
|
593
|
+
{ paneId: '%2', currentCommand: 'node', startCommand: 'node /tmp/bin/omx.js hud --watch --preset=focused' },
|
|
594
|
+
{ paneId: '%3', currentCommand: 'node', startCommand: 'node /tmp/bin/omx.js hud --watch --preset=focused' },
|
|
595
|
+
{ paneId: '%4', currentCommand: 'node', startCommand: 'node /tmp/bin/omx.js hud --watch --preset=minimal' },
|
|
596
|
+
{ paneId: '%5', currentCommand: 'node', startCommand: 'node /tmp/bin/omx.js hud --tmux --preset=focused' },
|
|
597
|
+
],
|
|
598
|
+
killTmuxPane: (paneId) => { killed.push(paneId); return true; },
|
|
599
|
+
createHudWatchPane: () => { created.push('create'); return '%9'; },
|
|
600
|
+
resizeTmuxPane: (paneId, heightLines) => { resized.push({ paneId, heightLines }); return true; },
|
|
601
|
+
resolveOmxCliEntryPath: () => '/repo/dist/cli/omx.js',
|
|
602
|
+
});
|
|
603
|
+
assert.equal(result.status, 'replaced_duplicates');
|
|
604
|
+
assert.equal(result.paneId, '%2');
|
|
605
|
+
assert.equal(result.duplicateCount, 1);
|
|
606
|
+
assert.deepEqual(killed, ['%3']);
|
|
607
|
+
assert.deepEqual(resized, [{ paneId: '%2', heightLines: HUD_TMUX_HEIGHT_LINES }]);
|
|
608
|
+
assert.deepEqual(created, []);
|
|
609
|
+
});
|
|
610
|
+
it('treats an extra legacy focused pane as stale when an owned HUD already exists', async () => {
|
|
611
|
+
const killed = [];
|
|
612
|
+
const resized = [];
|
|
613
|
+
const result = await reconcileHudForPromptSubmit('/repo', {
|
|
614
|
+
env: { TMUX: '1', TMUX_PANE: '%1', OMX_SESSION_ID: 'sess-a', [OMX_TMUX_HUD_OWNER_ENV]: '1' },
|
|
615
|
+
listCurrentWindowPanes: () => [
|
|
616
|
+
{ paneId: '%1', currentCommand: 'codex', startCommand: 'codex' },
|
|
617
|
+
{
|
|
618
|
+
paneId: '%2',
|
|
619
|
+
currentCommand: 'node',
|
|
620
|
+
startCommand: `env OMX_SESSION_ID='sess-a' ${OMX_TMUX_HUD_LEADER_PANE_ENV}='%1' node omx hud --watch --preset=focused`,
|
|
621
|
+
},
|
|
622
|
+
{ paneId: '%3', currentCommand: 'node', startCommand: 'node /tmp/bin/omx.js hud --watch --preset=focused' },
|
|
623
|
+
],
|
|
624
|
+
killTmuxPane: (paneId) => { killed.push(paneId); return true; },
|
|
625
|
+
resizeTmuxPane: (paneId, heightLines) => { resized.push({ paneId, heightLines }); return true; },
|
|
626
|
+
resolveOmxCliEntryPath: () => '/repo/dist/cli/omx.js',
|
|
627
|
+
});
|
|
628
|
+
assert.equal(result.status, 'replaced_duplicates');
|
|
629
|
+
assert.equal(result.paneId, '%2');
|
|
630
|
+
assert.equal(result.duplicateCount, 1);
|
|
631
|
+
assert.deepEqual(killed, ['%3']);
|
|
632
|
+
assert.deepEqual(resized, [{ paneId: '%2', heightLines: HUD_TMUX_HEIGHT_LINES }]);
|
|
633
|
+
});
|
|
634
|
+
it('deduplicates legacy focused panes that appear during the prompt-submit create race', async () => {
|
|
635
|
+
const killed = [];
|
|
636
|
+
const resized = [];
|
|
637
|
+
let listCount = 0;
|
|
638
|
+
const result = await reconcileHudForPromptSubmit('/repo', {
|
|
639
|
+
env: { TMUX: '1', TMUX_PANE: '%1', OMX_SESSION_ID: 'sess-race', [OMX_TMUX_HUD_OWNER_ENV]: '1' },
|
|
640
|
+
listCurrentWindowPanes: () => {
|
|
641
|
+
listCount += 1;
|
|
642
|
+
if (listCount === 1)
|
|
643
|
+
return [{ paneId: '%1', currentCommand: 'codex', startCommand: 'codex' }];
|
|
644
|
+
return [
|
|
645
|
+
{ paneId: '%1', currentCommand: 'codex', startCommand: 'codex' },
|
|
646
|
+
{ paneId: '%8', currentCommand: 'node', startCommand: 'node /tmp/bin/omx.js hud --watch --preset=focused' },
|
|
647
|
+
{
|
|
648
|
+
paneId: '%9',
|
|
649
|
+
currentCommand: 'node',
|
|
650
|
+
startCommand: `exec env OMX_SESSION_ID='sess-race' ${OMX_TMUX_HUD_LEADER_PANE_ENV}='%1' /node /omx.js hud --watch --preset=focused`,
|
|
651
|
+
},
|
|
652
|
+
];
|
|
653
|
+
},
|
|
654
|
+
createHudWatchPane: () => '%9',
|
|
655
|
+
killTmuxPane: (paneId) => { killed.push(paneId); return true; },
|
|
656
|
+
resizeTmuxPane: (paneId, heightLines) => { resized.push({ paneId, heightLines }); return true; },
|
|
657
|
+
resolveOmxCliEntryPath: () => '/repo/dist/cli/omx.js',
|
|
658
|
+
});
|
|
659
|
+
assert.equal(result.status, 'replaced_duplicates');
|
|
660
|
+
assert.equal(result.paneId, '%9');
|
|
661
|
+
assert.equal(result.duplicateCount, 1);
|
|
662
|
+
assert.deepEqual(killed, ['%8']);
|
|
663
|
+
assert.deepEqual(resized, [{ paneId: '%9', heightLines: HUD_TMUX_HEIGHT_LINES }]);
|
|
664
|
+
});
|
|
665
|
+
it('kills existing duplicate HUD panes even when the keeper cannot be resized', async () => {
|
|
666
|
+
const killed = [];
|
|
667
|
+
const registered = [];
|
|
668
|
+
const result = await reconcileHudForPromptSubmit('/repo', {
|
|
669
|
+
env: { TMUX: '1', TMUX_PANE: '%1', OMX_SESSION_ID: 'sess-a', [OMX_TMUX_HUD_OWNER_ENV]: '1' },
|
|
670
|
+
listCurrentWindowPanes: () => [
|
|
671
|
+
{ paneId: '%1', currentCommand: 'codex', startCommand: 'codex' },
|
|
672
|
+
{
|
|
673
|
+
paneId: '%8',
|
|
674
|
+
currentCommand: 'node',
|
|
675
|
+
startCommand: `env OMX_SESSION_ID='sess-a' ${OMX_TMUX_HUD_LEADER_PANE_ENV}='%1' node omx hud --watch`,
|
|
676
|
+
},
|
|
677
|
+
{
|
|
678
|
+
paneId: '%9',
|
|
679
|
+
currentCommand: 'node',
|
|
680
|
+
startCommand: `env OMX_SESSION_ID='sess-a' ${OMX_TMUX_HUD_LEADER_PANE_ENV}='%1' node omx hud --watch`,
|
|
681
|
+
},
|
|
682
|
+
],
|
|
683
|
+
killTmuxPane: (paneId) => { killed.push(paneId); return true; },
|
|
684
|
+
resizeTmuxPane: () => false,
|
|
685
|
+
registerHudResizeHook: (paneId) => { registered.push(paneId); return true; },
|
|
686
|
+
resolveOmxCliEntryPath: () => '/repo/dist/cli/omx.js',
|
|
687
|
+
});
|
|
688
|
+
assert.equal(result.status, 'failed');
|
|
689
|
+
assert.equal(result.paneId, '%8');
|
|
690
|
+
assert.equal(result.duplicateCount, 1);
|
|
691
|
+
assert.deepEqual(killed, ['%9']);
|
|
692
|
+
assert.deepEqual(registered, []);
|
|
693
|
+
});
|
|
193
694
|
it('does not resize, kill, or reuse another active leader session HUD in the same tmux window', async () => {
|
|
194
695
|
const killed = [];
|
|
195
696
|
const resized = [];
|
|
@@ -246,6 +747,11 @@ describe('reconcileHudForPromptSubmit', () => {
|
|
|
246
747
|
{
|
|
247
748
|
paneId: '%4',
|
|
248
749
|
currentCommand: 'node',
|
|
750
|
+
startCommand: `env OMX_SESSION_ID='sess-b' ${OMX_TMUX_HUD_LEADER_PANE_ENV}='%1' node omx hud --watch`,
|
|
751
|
+
},
|
|
752
|
+
{
|
|
753
|
+
paneId: '%5',
|
|
754
|
+
currentCommand: 'node',
|
|
249
755
|
startCommand: `env OMX_SESSION_ID='sess-b' ${OMX_TMUX_HUD_LEADER_PANE_ENV}='%5' node omx hud --watch`,
|
|
250
756
|
},
|
|
251
757
|
],
|
|
@@ -384,7 +890,7 @@ describe('reconcileHudForPromptSubmit', () => {
|
|
|
384
890
|
assert.equal(result.status, 'resized');
|
|
385
891
|
assert.deepEqual(resized, [{ paneId: '%2', heightLines: HUD_TMUX_ULTRAGOAL_HEIGHT_LINES }]);
|
|
386
892
|
});
|
|
387
|
-
it('
|
|
893
|
+
it('recreates instead of reusing a leader-only HUD pane when reviving with a canonical session id', async () => {
|
|
388
894
|
const resized = [];
|
|
389
895
|
const created = [];
|
|
390
896
|
const result = await reconcileHudForPromptSubmit('/repo', {
|
|
@@ -407,10 +913,10 @@ describe('reconcileHudForPromptSubmit', () => {
|
|
|
407
913
|
},
|
|
408
914
|
resolveOmxCliEntryPath: () => '/repo/dist/cli/omx.js',
|
|
409
915
|
});
|
|
410
|
-
assert.equal(result.status, '
|
|
411
|
-
assert.equal(result.paneId, '%
|
|
412
|
-
assert.deepEqual(created, []);
|
|
413
|
-
assert.deepEqual(resized, [{ paneId: '%
|
|
916
|
+
assert.equal(result.status, 'recreated');
|
|
917
|
+
assert.equal(result.paneId, '%9');
|
|
918
|
+
assert.deepEqual(created, ['create']);
|
|
919
|
+
assert.deepEqual(resized, [{ paneId: '%9', heightLines: HUD_TMUX_HEIGHT_LINES }]);
|
|
414
920
|
});
|
|
415
921
|
it('deduplicates same-leader HUD panes without creating a new pane when session id is unavailable', async () => {
|
|
416
922
|
const killed = [];
|
|
@@ -476,15 +982,15 @@ describe('reconcileHudForPromptSubmit', () => {
|
|
|
476
982
|
},
|
|
477
983
|
],
|
|
478
984
|
resizeTmuxPane: () => true,
|
|
479
|
-
registerHudResizeHook: (hudPaneId,
|
|
480
|
-
registered.push({ hudPaneId,
|
|
985
|
+
registerHudResizeHook: (hudPaneId, leaderPaneId, heightLines) => {
|
|
986
|
+
registered.push({ hudPaneId, leaderPaneId, heightLines });
|
|
481
987
|
return true;
|
|
482
988
|
},
|
|
483
989
|
resolveOmxCliEntryPath: () => '/repo/dist/cli/omx.js',
|
|
484
990
|
});
|
|
485
991
|
assert.equal(registered.length, 1);
|
|
486
992
|
assert.equal(registered[0]?.hudPaneId, '%2');
|
|
487
|
-
assert.equal(registered[0]?.
|
|
993
|
+
assert.equal(registered[0]?.leaderPaneId, '%1');
|
|
488
994
|
assert.equal(registered[0]?.heightLines, HUD_TMUX_HEIGHT_LINES);
|
|
489
995
|
});
|
|
490
996
|
it('registers client-resized hook scoped from the emitting pane after creating a new HUD pane', async () => {
|
|
@@ -496,15 +1002,15 @@ describe('reconcileHudForPromptSubmit', () => {
|
|
|
496
1002
|
],
|
|
497
1003
|
createHudWatchPane: () => '%9',
|
|
498
1004
|
resizeTmuxPane: () => true,
|
|
499
|
-
registerHudResizeHook: (hudPaneId,
|
|
500
|
-
registered.push({ hudPaneId,
|
|
1005
|
+
registerHudResizeHook: (hudPaneId, leaderPaneId, heightLines) => {
|
|
1006
|
+
registered.push({ hudPaneId, leaderPaneId, heightLines });
|
|
501
1007
|
return true;
|
|
502
1008
|
},
|
|
503
1009
|
resolveOmxCliEntryPath: () => '/repo/dist/cli/omx.js',
|
|
504
1010
|
});
|
|
505
1011
|
assert.equal(registered.length, 1);
|
|
506
1012
|
assert.equal(registered[0]?.hudPaneId, '%9');
|
|
507
|
-
assert.equal(registered[0]?.
|
|
1013
|
+
assert.equal(registered[0]?.leaderPaneId, '%1');
|
|
508
1014
|
assert.equal(registered[0]?.heightLines, HUD_TMUX_HEIGHT_LINES);
|
|
509
1015
|
});
|
|
510
1016
|
it('keeps the resize hook on the reused duplicate keeper pane', async () => {
|
|
@@ -520,14 +1026,14 @@ describe('reconcileHudForPromptSubmit', () => {
|
|
|
520
1026
|
killTmuxPane: () => true,
|
|
521
1027
|
createHudWatchPane: () => '%9',
|
|
522
1028
|
resizeTmuxPane: () => true,
|
|
523
|
-
unregisterHudResizeHook: (
|
|
524
|
-
registerHudResizeHook: (hudPaneId,
|
|
1029
|
+
unregisterHudResizeHook: (leaderPaneId) => { unregistered.push(leaderPaneId); return true; },
|
|
1030
|
+
registerHudResizeHook: (hudPaneId, leaderPaneId) => { registered.push({ hudPaneId, leaderPaneId }); return true; },
|
|
525
1031
|
resolveOmxCliEntryPath: () => '/repo/dist/cli/omx.js',
|
|
526
1032
|
});
|
|
527
1033
|
assert.deepEqual(unregistered, []);
|
|
528
1034
|
assert.equal(registered.length, 1);
|
|
529
1035
|
assert.equal(registered[0]?.hudPaneId, '%2');
|
|
530
|
-
assert.equal(registered[0]?.
|
|
1036
|
+
assert.equal(registered[0]?.leaderPaneId, '%1');
|
|
531
1037
|
});
|
|
532
1038
|
});
|
|
533
1039
|
//# sourceMappingURL=reconcile.test.js.map
|