oh-my-codex 0.13.2 → 0.14.0
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 +5 -5
- package/Cargo.toml +1 -1
- package/dist/autoresearch/__tests__/skill-validation.test.d.ts +2 -0
- package/dist/autoresearch/__tests__/skill-validation.test.d.ts.map +1 -0
- package/dist/autoresearch/__tests__/skill-validation.test.js +91 -0
- package/dist/autoresearch/__tests__/skill-validation.test.js.map +1 -0
- package/dist/autoresearch/skill-validation.d.ts +13 -0
- package/dist/autoresearch/skill-validation.d.ts.map +1 -0
- package/dist/autoresearch/skill-validation.js +165 -0
- package/dist/autoresearch/skill-validation.js.map +1 -0
- package/dist/catalog/__tests__/schema.test.js +6 -0
- package/dist/catalog/__tests__/schema.test.js.map +1 -1
- package/dist/cli/__tests__/autoresearch-guided.test.js +236 -273
- package/dist/cli/__tests__/autoresearch-guided.test.js.map +1 -1
- package/dist/cli/__tests__/autoresearch.test.js +64 -653
- package/dist/cli/__tests__/autoresearch.test.js.map +1 -1
- package/dist/cli/__tests__/index.test.js +7 -0
- package/dist/cli/__tests__/index.test.js.map +1 -1
- package/dist/cli/__tests__/nested-help-routing.test.js +2 -1
- package/dist/cli/__tests__/nested-help-routing.test.js.map +1 -1
- package/dist/cli/__tests__/question.test.d.ts +2 -0
- package/dist/cli/__tests__/question.test.d.ts.map +1 -0
- package/dist/cli/__tests__/question.test.js +113 -0
- package/dist/cli/__tests__/question.test.js.map +1 -0
- package/dist/cli/__tests__/session-search-help.test.js +1 -1
- package/dist/cli/__tests__/session-search-help.test.js.map +1 -1
- package/dist/cli/__tests__/setup-skills-overwrite.test.js +2 -0
- package/dist/cli/__tests__/setup-skills-overwrite.test.js.map +1 -1
- package/dist/cli/autoresearch-guided.d.ts +24 -7
- package/dist/cli/autoresearch-guided.d.ts.map +1 -1
- package/dist/cli/autoresearch-guided.js +189 -130
- package/dist/cli/autoresearch-guided.js.map +1 -1
- package/dist/cli/autoresearch.d.ts +3 -2
- package/dist/cli/autoresearch.d.ts.map +1 -1
- package/dist/cli/autoresearch.js +29 -305
- package/dist/cli/autoresearch.js.map +1 -1
- package/dist/cli/doctor.d.ts.map +1 -1
- package/dist/cli/doctor.js +43 -0
- package/dist/cli/doctor.js.map +1 -1
- package/dist/cli/index.d.ts +1 -1
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +8 -1
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/question.d.ts +3 -0
- package/dist/cli/question.d.ts.map +1 -0
- package/dist/cli/question.js +182 -0
- package/dist/cli/question.js.map +1 -0
- package/dist/hooks/__tests__/analyze-routing-contract.test.js +22 -13
- package/dist/hooks/__tests__/analyze-routing-contract.test.js.map +1 -1
- package/dist/hooks/__tests__/anti-slop-workflow.test.js +3 -3
- package/dist/hooks/__tests__/anti-slop-workflow.test.js.map +1 -1
- package/dist/hooks/__tests__/debugger-log-recency-contract.test.js +2 -2
- package/dist/hooks/__tests__/debugger-log-recency-contract.test.js.map +1 -1
- package/dist/hooks/__tests__/deep-interview-contract.test.js +22 -5
- package/dist/hooks/__tests__/deep-interview-contract.test.js.map +1 -1
- package/dist/hooks/__tests__/explore-sparkshell-guidance-contract.test.js +2 -2
- package/dist/hooks/__tests__/explore-sparkshell-guidance-contract.test.js.map +1 -1
- package/dist/hooks/__tests__/keyword-detector.test.js +308 -17
- package/dist/hooks/__tests__/keyword-detector.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-fallback-watcher.test.js +570 -2
- package/dist/hooks/__tests__/notify-fallback-watcher.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-hook-auto-nudge.test.js +717 -16
- package/dist/hooks/__tests__/notify-hook-auto-nudge.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-hook-cross-worktree-heartbeat.test.js +25 -0
- package/dist/hooks/__tests__/notify-hook-cross-worktree-heartbeat.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-hook-managed-tmux.test.js +894 -1
- package/dist/hooks/__tests__/notify-hook-managed-tmux.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-hook-ralph-resume.test.js +34 -0
- package/dist/hooks/__tests__/notify-hook-ralph-resume.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-hook-tmux-heal.test.js +132 -0
- package/dist/hooks/__tests__/notify-hook-tmux-heal.test.js.map +1 -1
- package/dist/hooks/__tests__/prompt-guidance-contract.test.js +22 -4
- package/dist/hooks/__tests__/prompt-guidance-contract.test.js.map +1 -1
- package/dist/hooks/__tests__/prompt-guidance-fragments.test.js +4 -2
- package/dist/hooks/__tests__/prompt-guidance-fragments.test.js.map +1 -1
- package/dist/hooks/__tests__/prompt-guidance-test-helpers.d.ts +1 -0
- package/dist/hooks/__tests__/prompt-guidance-test-helpers.d.ts.map +1 -1
- package/dist/hooks/__tests__/prompt-guidance-test-helpers.js +4 -1
- package/dist/hooks/__tests__/prompt-guidance-test-helpers.js.map +1 -1
- package/dist/hooks/__tests__/prompt-guidance-wave-two.test.js +28 -0
- package/dist/hooks/__tests__/prompt-guidance-wave-two.test.js.map +1 -1
- package/dist/hooks/__tests__/prompt-orchestration-boundary.test.js +5 -4
- package/dist/hooks/__tests__/prompt-orchestration-boundary.test.js.map +1 -1
- package/dist/hooks/__tests__/prompt-team-routing.test.js +2 -2
- package/dist/hooks/__tests__/prompt-team-routing.test.js.map +1 -1
- package/dist/hooks/__tests__/triage-config.test.d.ts +2 -0
- package/dist/hooks/__tests__/triage-config.test.d.ts.map +1 -0
- package/dist/hooks/__tests__/triage-config.test.js +211 -0
- package/dist/hooks/__tests__/triage-config.test.js.map +1 -0
- package/dist/hooks/__tests__/triage-heuristic.test.d.ts +2 -0
- package/dist/hooks/__tests__/triage-heuristic.test.d.ts.map +1 -0
- package/dist/hooks/__tests__/triage-heuristic.test.js +230 -0
- package/dist/hooks/__tests__/triage-heuristic.test.js.map +1 -0
- package/dist/hooks/__tests__/triage-state.test.d.ts +2 -0
- package/dist/hooks/__tests__/triage-state.test.d.ts.map +1 -0
- package/dist/hooks/__tests__/triage-state.test.js +426 -0
- package/dist/hooks/__tests__/triage-state.test.js.map +1 -0
- package/dist/hooks/keyword-detector.d.ts +26 -7
- package/dist/hooks/keyword-detector.d.ts.map +1 -1
- package/dist/hooks/keyword-detector.js +97 -26
- package/dist/hooks/keyword-detector.js.map +1 -1
- package/dist/hooks/keyword-registry.d.ts.map +1 -1
- package/dist/hooks/keyword-registry.js +16 -9
- package/dist/hooks/keyword-registry.js.map +1 -1
- package/dist/hooks/prompt-guidance-contract.d.ts.map +1 -1
- package/dist/hooks/prompt-guidance-contract.js +28 -1
- package/dist/hooks/prompt-guidance-contract.js.map +1 -1
- package/dist/hooks/triage-config.d.ts +33 -0
- package/dist/hooks/triage-config.d.ts.map +1 -0
- package/dist/hooks/triage-config.js +87 -0
- package/dist/hooks/triage-config.js.map +1 -0
- package/dist/hooks/triage-heuristic.d.ts +20 -0
- package/dist/hooks/triage-heuristic.d.ts.map +1 -0
- package/dist/hooks/triage-heuristic.js +210 -0
- package/dist/hooks/triage-heuristic.js.map +1 -0
- package/dist/hooks/triage-state.d.ts +63 -0
- package/dist/hooks/triage-state.d.ts.map +1 -0
- package/dist/hooks/triage-state.js +138 -0
- package/dist/hooks/triage-state.js.map +1 -0
- package/dist/hud/__tests__/reconcile.test.js +20 -0
- package/dist/hud/__tests__/reconcile.test.js.map +1 -1
- package/dist/hud/reconcile.d.ts +1 -0
- package/dist/hud/reconcile.d.ts.map +1 -1
- package/dist/hud/reconcile.js +2 -1
- package/dist/hud/reconcile.js.map +1 -1
- package/dist/mcp/__tests__/state-server.test.js +1 -0
- package/dist/mcp/__tests__/state-server.test.js.map +1 -1
- package/dist/mcp/state-server.d.ts +8 -0
- package/dist/mcp/state-server.d.ts.map +1 -1
- package/dist/mcp/state-server.js +4 -0
- package/dist/mcp/state-server.js.map +1 -1
- package/dist/modes/__tests__/base-ralph-contract.test.js +15 -0
- package/dist/modes/__tests__/base-ralph-contract.test.js.map +1 -1
- package/dist/modes/base.d.ts +1 -0
- package/dist/modes/base.d.ts.map +1 -1
- package/dist/modes/base.js +22 -6
- package/dist/modes/base.js.map +1 -1
- package/dist/notifications/__tests__/index.test.js +78 -0
- package/dist/notifications/__tests__/index.test.js.map +1 -1
- package/dist/notifications/index.d.ts.map +1 -1
- package/dist/notifications/index.js +39 -22
- package/dist/notifications/index.js.map +1 -1
- package/dist/openclaw/index.d.ts +5 -3
- package/dist/openclaw/index.d.ts.map +1 -1
- package/dist/openclaw/index.js +5 -3
- package/dist/openclaw/index.js.map +1 -1
- package/dist/question/__tests__/client.test.d.ts +2 -0
- package/dist/question/__tests__/client.test.d.ts.map +1 -0
- package/dist/question/__tests__/client.test.js +70 -0
- package/dist/question/__tests__/client.test.js.map +1 -0
- package/dist/question/__tests__/deep-interview.test.d.ts +2 -0
- package/dist/question/__tests__/deep-interview.test.d.ts.map +1 -0
- package/dist/question/__tests__/deep-interview.test.js +108 -0
- package/dist/question/__tests__/deep-interview.test.js.map +1 -0
- package/dist/question/__tests__/policy.test.d.ts +2 -0
- package/dist/question/__tests__/policy.test.d.ts.map +1 -0
- package/dist/question/__tests__/policy.test.js +107 -0
- package/dist/question/__tests__/policy.test.js.map +1 -0
- package/dist/question/__tests__/renderer.test.d.ts +2 -0
- package/dist/question/__tests__/renderer.test.d.ts.map +1 -0
- package/dist/question/__tests__/renderer.test.js +88 -0
- package/dist/question/__tests__/renderer.test.js.map +1 -0
- package/dist/question/__tests__/state.test.d.ts +2 -0
- package/dist/question/__tests__/state.test.d.ts.map +1 -0
- package/dist/question/__tests__/state.test.js +55 -0
- package/dist/question/__tests__/state.test.js.map +1 -0
- package/dist/question/__tests__/types.test.d.ts +2 -0
- package/dist/question/__tests__/types.test.d.ts.map +1 -0
- package/dist/question/__tests__/types.test.js +44 -0
- package/dist/question/__tests__/types.test.js.map +1 -0
- package/dist/question/__tests__/ui.test.d.ts +2 -0
- package/dist/question/__tests__/ui.test.d.ts.map +1 -0
- package/dist/question/__tests__/ui.test.js +169 -0
- package/dist/question/__tests__/ui.test.js.map +1 -0
- package/dist/question/client.d.ts +54 -0
- package/dist/question/client.d.ts.map +1 -0
- package/dist/question/client.js +77 -0
- package/dist/question/client.js.map +1 -0
- package/dist/question/deep-interview.d.ts +27 -0
- package/dist/question/deep-interview.d.ts.map +1 -0
- package/dist/question/deep-interview.js +101 -0
- package/dist/question/deep-interview.js.map +1 -0
- package/dist/question/policy.d.ts +18 -0
- package/dist/question/policy.d.ts.map +1 -0
- package/dist/question/policy.js +77 -0
- package/dist/question/policy.js.map +1 -0
- package/dist/question/renderer.d.ts +18 -0
- package/dist/question/renderer.d.ts.map +1 -0
- package/dist/question/renderer.js +128 -0
- package/dist/question/renderer.js.map +1 -0
- package/dist/question/state.d.ts +19 -0
- package/dist/question/state.d.ts.map +1 -0
- package/dist/question/state.js +108 -0
- package/dist/question/state.js.map +1 -0
- package/dist/question/types.d.ts +66 -0
- package/dist/question/types.d.ts.map +1 -0
- package/dist/question/types.js +82 -0
- package/dist/question/types.js.map +1 -0
- package/dist/question/ui.d.ts +38 -0
- package/dist/question/ui.d.ts.map +1 -0
- package/dist/question/ui.js +321 -0
- package/dist/question/ui.js.map +1 -0
- package/dist/ralph/contract.d.ts +1 -1
- package/dist/ralph/contract.d.ts.map +1 -1
- package/dist/ralph/contract.js +4 -1
- package/dist/ralph/contract.js.map +1 -1
- package/dist/ralplan/runtime.js +1 -1
- package/dist/ralplan/runtime.js.map +1 -1
- package/dist/runtime/__tests__/run-loop.test.d.ts +2 -0
- package/dist/runtime/__tests__/run-loop.test.d.ts.map +1 -0
- package/dist/runtime/__tests__/run-loop.test.js +35 -0
- package/dist/runtime/__tests__/run-loop.test.js.map +1 -0
- package/dist/runtime/__tests__/run-outcome.test.d.ts +2 -0
- package/dist/runtime/__tests__/run-outcome.test.d.ts.map +1 -0
- package/dist/runtime/__tests__/run-outcome.test.js +64 -0
- package/dist/runtime/__tests__/run-outcome.test.js.map +1 -0
- package/dist/runtime/run-loop.d.ts +41 -0
- package/dist/runtime/run-loop.d.ts.map +1 -0
- package/dist/runtime/run-loop.js +46 -0
- package/dist/runtime/run-loop.js.map +1 -0
- package/dist/runtime/run-outcome.d.ts +28 -0
- package/dist/runtime/run-outcome.d.ts.map +1 -0
- package/dist/runtime/run-outcome.js +136 -0
- package/dist/runtime/run-outcome.js.map +1 -0
- package/dist/runtime/run-state.d.ts +36 -0
- package/dist/runtime/run-state.d.ts.map +1 -0
- package/dist/runtime/run-state.js +110 -0
- package/dist/runtime/run-state.js.map +1 -0
- package/dist/scripts/__tests__/codex-native-hook.test.js +1128 -85
- package/dist/scripts/__tests__/codex-native-hook.test.js.map +1 -1
- package/dist/scripts/codex-native-hook.d.ts +2 -0
- package/dist/scripts/codex-native-hook.d.ts.map +1 -1
- package/dist/scripts/codex-native-hook.js +199 -11
- package/dist/scripts/codex-native-hook.js.map +1 -1
- package/dist/scripts/notify-fallback-watcher.js +81 -2
- package/dist/scripts/notify-fallback-watcher.js.map +1 -1
- package/dist/scripts/notify-hook/auto-nudge.d.ts +27 -0
- package/dist/scripts/notify-hook/auto-nudge.d.ts.map +1 -1
- package/dist/scripts/notify-hook/auto-nudge.js +83 -20
- package/dist/scripts/notify-hook/auto-nudge.js.map +1 -1
- package/dist/scripts/notify-hook/managed-tmux.d.ts.map +1 -1
- package/dist/scripts/notify-hook/managed-tmux.js +64 -38
- package/dist/scripts/notify-hook/managed-tmux.js.map +1 -1
- package/dist/scripts/notify-hook/ralph-session-resume.js +1 -1
- package/dist/scripts/notify-hook/ralph-session-resume.js.map +1 -1
- package/dist/scripts/notify-hook.js +15 -5
- package/dist/scripts/notify-hook.js.map +1 -1
- package/dist/scripts/sync-prompt-guidance-fragments.js +5 -0
- package/dist/scripts/sync-prompt-guidance-fragments.js.map +1 -1
- package/dist/state/__tests__/operations-ralph-phase.test.js +21 -0
- package/dist/state/__tests__/operations-ralph-phase.test.js.map +1 -1
- package/dist/state/__tests__/workflow-transition.test.js +11 -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 +15 -0
- package/dist/state/operations.js.map +1 -1
- package/dist/state/workflow-transition-reconcile.d.ts.map +1 -1
- package/dist/state/workflow-transition-reconcile.js +14 -1
- package/dist/state/workflow-transition-reconcile.js.map +1 -1
- package/dist/state/workflow-transition.d.ts.map +1 -1
- package/dist/state/workflow-transition.js +3 -1
- package/dist/state/workflow-transition.js.map +1 -1
- package/dist/team/__tests__/followup-planner.test.js +15 -0
- package/dist/team/__tests__/followup-planner.test.js.map +1 -1
- package/dist/team/__tests__/role-router.test.js +41 -0
- package/dist/team/__tests__/role-router.test.js.map +1 -1
- package/dist/team/followup-planner.d.ts.map +1 -1
- package/dist/team/followup-planner.js +31 -9
- package/dist/team/followup-planner.js.map +1 -1
- package/dist/team/role-router.d.ts.map +1 -1
- package/dist/team/role-router.js +73 -0
- package/dist/team/role-router.js.map +1 -1
- package/package.json +3 -2
- package/prompts/dependency-expert.md +3 -0
- package/prompts/executor.md +5 -0
- package/prompts/explore.md +2 -0
- package/prompts/planner.md +5 -0
- package/prompts/product-analyst.md +8 -8
- package/prompts/researcher.md +78 -30
- package/prompts/verifier.md +4 -0
- package/skills/autoresearch/SKILL.md +68 -0
- package/skills/deep-interview/SKILL.md +10 -9
- package/skills/help/SKILL.md +3 -1
- package/skills/ralplan/SKILL.md +1 -0
- package/skills/team/SKILL.md +1 -0
- package/skills/ultrawork/SKILL.md +1 -0
- package/src/scripts/__tests__/codex-native-hook.test.ts +1495 -188
- package/src/scripts/codex-native-hook.ts +235 -19
- package/src/scripts/notify-fallback-watcher.ts +92 -2
- package/src/scripts/notify-hook/auto-nudge.ts +89 -20
- package/src/scripts/notify-hook/managed-tmux.ts +70 -31
- package/src/scripts/notify-hook/ralph-session-resume.ts +1 -1
- package/src/scripts/notify-hook.ts +23 -5
- package/src/scripts/sync-prompt-guidance-fragments.ts +4 -0
- package/templates/AGENTS.md +48 -37
- package/templates/catalog-manifest.json +7 -0
|
@@ -1,11 +1,39 @@
|
|
|
1
1
|
import { afterEach, describe, it } from 'node:test';
|
|
2
2
|
import assert from 'node:assert/strict';
|
|
3
|
+
import { readFileSync } from 'node:fs';
|
|
3
4
|
import { chmod, mkdir, mkdtemp, readFile, rm, symlink, writeFile } from 'node:fs/promises';
|
|
4
5
|
import { tmpdir } from 'node:os';
|
|
5
6
|
import { join } from 'node:path';
|
|
6
7
|
import { buildTmuxSessionName } from '../../cli/index.js';
|
|
7
|
-
import { resolveManagedSessionContext, verifyManagedPaneTarget } from '../../scripts/notify-hook/managed-tmux.js';
|
|
8
|
+
import { resolveManagedPaneFromAnchor, resolveManagedSessionContext, resolveManagedSessionPane, verifyManagedPaneTarget, } from '../../scripts/notify-hook/managed-tmux.js';
|
|
8
9
|
import { writeSessionStart } from '../session.js';
|
|
10
|
+
function readLinuxStartTicks(pid) {
|
|
11
|
+
try {
|
|
12
|
+
const stat = readFileSync(`/proc/${pid}/stat`, 'utf-8');
|
|
13
|
+
const commandEnd = stat.lastIndexOf(')');
|
|
14
|
+
if (commandEnd === -1)
|
|
15
|
+
return null;
|
|
16
|
+
const remainder = stat.slice(commandEnd + 1).trim();
|
|
17
|
+
const fields = remainder.split(/\s+/);
|
|
18
|
+
if (fields.length <= 19)
|
|
19
|
+
return null;
|
|
20
|
+
const startTicks = Number(fields[19]);
|
|
21
|
+
return Number.isFinite(startTicks) ? startTicks : null;
|
|
22
|
+
}
|
|
23
|
+
catch {
|
|
24
|
+
return null;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
function readLinuxCmdline(pid) {
|
|
28
|
+
try {
|
|
29
|
+
const raw = readFileSync(`/proc/${pid}/cmdline`);
|
|
30
|
+
const text = raw.toString('utf-8').replace(/\0+/g, ' ').trim();
|
|
31
|
+
return text.length > 0 ? text : null;
|
|
32
|
+
}
|
|
33
|
+
catch {
|
|
34
|
+
return null;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
9
37
|
describe('notify-hook managed tmux windows fallback', () => {
|
|
10
38
|
async function withFakeTmux(cwd, script, run) {
|
|
11
39
|
const fakeBinDir = join(cwd, 'fake-bin');
|
|
@@ -160,5 +188,870 @@ exit 1
|
|
|
160
188
|
await rm(cwd, { recursive: true, force: true });
|
|
161
189
|
}
|
|
162
190
|
});
|
|
191
|
+
it('keeps the verified anchor pane instead of rebinding to the active codex pane in the session', async () => {
|
|
192
|
+
const cwd = await mkdtemp(join(tmpdir(), 'omx-managed-anchor-pane-'));
|
|
193
|
+
const originalPath = process.env.PATH;
|
|
194
|
+
try {
|
|
195
|
+
const stateDir = join(cwd, '.omx', 'state');
|
|
196
|
+
const fakeBinDir = join(cwd, 'fake-bin');
|
|
197
|
+
const fakeTmuxPath = join(fakeBinDir, 'tmux');
|
|
198
|
+
const sessionId = 'omx-anchor-pane';
|
|
199
|
+
const managedSessionName = buildTmuxSessionName(cwd, sessionId);
|
|
200
|
+
await mkdir(stateDir, { recursive: true });
|
|
201
|
+
await mkdir(fakeBinDir, { recursive: true });
|
|
202
|
+
await writeFile(join(stateDir, 'session.json'), JSON.stringify({
|
|
203
|
+
session_id: sessionId,
|
|
204
|
+
started_at: new Date().toISOString(),
|
|
205
|
+
cwd,
|
|
206
|
+
pid: process.pid,
|
|
207
|
+
platform: process.platform,
|
|
208
|
+
pid_start_ticks: readLinuxStartTicks(process.pid),
|
|
209
|
+
pid_cmdline: readLinuxCmdline(process.pid),
|
|
210
|
+
}, null, 2));
|
|
211
|
+
const fakeTmux = `#!/usr/bin/env bash
|
|
212
|
+
set -eu
|
|
213
|
+
cmd="$1"
|
|
214
|
+
shift || true
|
|
215
|
+
if [[ "$cmd" == "display-message" ]]; then
|
|
216
|
+
target=""
|
|
217
|
+
format=""
|
|
218
|
+
while (($#)); do
|
|
219
|
+
case "$1" in
|
|
220
|
+
-p) shift ;;
|
|
221
|
+
-t) target="$2"; shift 2 ;;
|
|
222
|
+
*) format="$1"; shift ;;
|
|
223
|
+
esac
|
|
224
|
+
done
|
|
225
|
+
if [[ -z "$target" && "$format" == "#S" ]]; then
|
|
226
|
+
echo "${managedSessionName}"
|
|
227
|
+
exit 0
|
|
228
|
+
fi
|
|
229
|
+
if [[ "$format" == "#{pane_current_command}" && "$target" == "%42" ]]; then
|
|
230
|
+
echo "codex"
|
|
231
|
+
exit 0
|
|
232
|
+
fi
|
|
233
|
+
if [[ "$format" == "#{pane_start_command}" && "$target" == "%42" ]]; then
|
|
234
|
+
echo "codex"
|
|
235
|
+
exit 0
|
|
236
|
+
fi
|
|
237
|
+
if [[ "$format" == "#S" && "$target" == "%42" ]]; then
|
|
238
|
+
echo "${managedSessionName}"
|
|
239
|
+
exit 0
|
|
240
|
+
fi
|
|
241
|
+
echo "unsupported display target: $target / $format" >&2
|
|
242
|
+
exit 1
|
|
243
|
+
fi
|
|
244
|
+
if [[ "$cmd" == "list-panes" ]]; then
|
|
245
|
+
target=""
|
|
246
|
+
while (($#)); do
|
|
247
|
+
case "$1" in
|
|
248
|
+
-s) shift ;;
|
|
249
|
+
-t) target="$2"; shift 2 ;;
|
|
250
|
+
-F) shift 2 ;;
|
|
251
|
+
*) shift ;;
|
|
252
|
+
esac
|
|
253
|
+
done
|
|
254
|
+
if [[ "$target" == "${managedSessionName}" ]]; then
|
|
255
|
+
printf "%%42\\t0\\tcodex\\tcodex\\n%%55\\t1\\tcodex\\tcodex\\n"
|
|
256
|
+
exit 0
|
|
257
|
+
fi
|
|
258
|
+
echo "unexpected list-panes target: $target" >&2
|
|
259
|
+
exit 1
|
|
260
|
+
fi
|
|
261
|
+
echo "unsupported cmd: $cmd" >&2
|
|
262
|
+
exit 1
|
|
263
|
+
`;
|
|
264
|
+
await writeFile(fakeTmuxPath, fakeTmux);
|
|
265
|
+
await chmod(fakeTmuxPath, 0o755);
|
|
266
|
+
process.env.PATH = `${fakeBinDir}:${originalPath || ''}`;
|
|
267
|
+
process.env.TMUX = '1';
|
|
268
|
+
delete process.env.TMUX_PANE;
|
|
269
|
+
process.env.OMX_TEAM_WORKER = '';
|
|
270
|
+
const paneId = await resolveManagedPaneFromAnchor('%42', cwd, { session_id: sessionId }, { allowTeamWorker: false });
|
|
271
|
+
assert.equal(paneId, '%42');
|
|
272
|
+
}
|
|
273
|
+
finally {
|
|
274
|
+
process.env.PATH = originalPath;
|
|
275
|
+
await rm(cwd, { recursive: true, force: true });
|
|
276
|
+
}
|
|
277
|
+
});
|
|
278
|
+
it('rebinds a node shell anchor to the live codex pane in the managed session', async () => {
|
|
279
|
+
const cwd = await mkdtemp(join(tmpdir(), 'omx-managed-node-shell-anchor-'));
|
|
280
|
+
const originalPath = process.env.PATH;
|
|
281
|
+
try {
|
|
282
|
+
const stateDir = join(cwd, '.omx', 'state');
|
|
283
|
+
const fakeBinDir = join(cwd, 'fake-bin');
|
|
284
|
+
const fakeTmuxPath = join(fakeBinDir, 'tmux');
|
|
285
|
+
const sessionId = 'omx-node-shell-anchor';
|
|
286
|
+
const managedSessionName = buildTmuxSessionName(cwd, sessionId);
|
|
287
|
+
await mkdir(stateDir, { recursive: true });
|
|
288
|
+
await mkdir(fakeBinDir, { recursive: true });
|
|
289
|
+
await writeFile(join(stateDir, 'session.json'), JSON.stringify({
|
|
290
|
+
session_id: sessionId,
|
|
291
|
+
started_at: new Date().toISOString(),
|
|
292
|
+
cwd,
|
|
293
|
+
pid: process.pid,
|
|
294
|
+
platform: process.platform,
|
|
295
|
+
pid_start_ticks: readLinuxStartTicks(process.pid),
|
|
296
|
+
pid_cmdline: readLinuxCmdline(process.pid),
|
|
297
|
+
}, null, 2));
|
|
298
|
+
const fakeTmux = `#!/usr/bin/env bash
|
|
299
|
+
set -eu
|
|
300
|
+
cmd="$1"
|
|
301
|
+
shift || true
|
|
302
|
+
if [[ "$cmd" == "display-message" ]]; then
|
|
303
|
+
target=""
|
|
304
|
+
format=""
|
|
305
|
+
while (($#)); do
|
|
306
|
+
case "$1" in
|
|
307
|
+
-p) shift ;;
|
|
308
|
+
-t) target="$2"; shift 2 ;;
|
|
309
|
+
*) format="$1"; shift ;;
|
|
310
|
+
esac
|
|
311
|
+
done
|
|
312
|
+
if [[ -z "$target" && "$format" == "#S" ]]; then
|
|
313
|
+
echo "${managedSessionName}"
|
|
314
|
+
exit 0
|
|
315
|
+
fi
|
|
316
|
+
if [[ "$format" == "#{pane_current_command}" && "$target" == "%42" ]]; then
|
|
317
|
+
echo "node"
|
|
318
|
+
exit 0
|
|
319
|
+
fi
|
|
320
|
+
if [[ "$format" == "#{pane_start_command}" && "$target" == "%42" ]]; then
|
|
321
|
+
echo "bash"
|
|
322
|
+
exit 0
|
|
323
|
+
fi
|
|
324
|
+
if [[ "$format" == "#S" && "$target" == "%42" ]]; then
|
|
325
|
+
echo "${managedSessionName}"
|
|
326
|
+
exit 0
|
|
327
|
+
fi
|
|
328
|
+
echo "unsupported display target: $target / $format" >&2
|
|
329
|
+
exit 1
|
|
330
|
+
fi
|
|
331
|
+
if [[ "$cmd" == "list-panes" ]]; then
|
|
332
|
+
target=""
|
|
333
|
+
while (($#)); do
|
|
334
|
+
case "$1" in
|
|
335
|
+
-s) shift ;;
|
|
336
|
+
-t) target="$2"; shift 2 ;;
|
|
337
|
+
-F) shift 2 ;;
|
|
338
|
+
*) shift ;;
|
|
339
|
+
esac
|
|
340
|
+
done
|
|
341
|
+
if [[ "$target" == "${managedSessionName}" ]]; then
|
|
342
|
+
printf "%%42\\t0\\tnode\\tbash\\n%%55\\t1\\tcodex\\tcodex\\n"
|
|
343
|
+
exit 0
|
|
344
|
+
fi
|
|
345
|
+
echo "unexpected list-panes target: $target" >&2
|
|
346
|
+
exit 1
|
|
347
|
+
fi
|
|
348
|
+
echo "unsupported cmd: $cmd" >&2
|
|
349
|
+
exit 1
|
|
350
|
+
`;
|
|
351
|
+
await writeFile(fakeTmuxPath, fakeTmux);
|
|
352
|
+
await chmod(fakeTmuxPath, 0o755);
|
|
353
|
+
process.env.PATH = `${fakeBinDir}:${originalPath || ''}`;
|
|
354
|
+
process.env.TMUX = '1';
|
|
355
|
+
delete process.env.TMUX_PANE;
|
|
356
|
+
process.env.OMX_TEAM_WORKER = '';
|
|
357
|
+
const paneId = await resolveManagedPaneFromAnchor('%42', cwd, { session_id: sessionId }, { allowTeamWorker: false });
|
|
358
|
+
assert.equal(paneId, '%55');
|
|
359
|
+
}
|
|
360
|
+
finally {
|
|
361
|
+
process.env.PATH = originalPath;
|
|
362
|
+
await rm(cwd, { recursive: true, force: true });
|
|
363
|
+
}
|
|
364
|
+
});
|
|
365
|
+
it('fails closed for anchorless managed-session recovery when only a wrapper-launched node pane exists', async () => {
|
|
366
|
+
const cwd = await mkdtemp(join(tmpdir(), 'omx-managed-wrapper-node-session-pane-'));
|
|
367
|
+
const originalPath = process.env.PATH;
|
|
368
|
+
try {
|
|
369
|
+
const stateDir = join(cwd, '.omx', 'state');
|
|
370
|
+
const fakeBinDir = join(cwd, 'fake-bin');
|
|
371
|
+
const fakeTmuxPath = join(fakeBinDir, 'tmux');
|
|
372
|
+
const sessionId = 'omx-wrapper-node-session-pane';
|
|
373
|
+
const managedSessionName = buildTmuxSessionName(cwd, sessionId);
|
|
374
|
+
await mkdir(stateDir, { recursive: true });
|
|
375
|
+
await mkdir(fakeBinDir, { recursive: true });
|
|
376
|
+
await writeFile(join(stateDir, 'session.json'), JSON.stringify({
|
|
377
|
+
session_id: sessionId,
|
|
378
|
+
started_at: new Date().toISOString(),
|
|
379
|
+
cwd,
|
|
380
|
+
pid: process.pid,
|
|
381
|
+
platform: process.platform,
|
|
382
|
+
pid_start_ticks: readLinuxStartTicks(process.pid),
|
|
383
|
+
pid_cmdline: readLinuxCmdline(process.pid),
|
|
384
|
+
}, null, 2));
|
|
385
|
+
const fakeTmux = `#!/usr/bin/env bash
|
|
386
|
+
set -eu
|
|
387
|
+
cmd="$1"
|
|
388
|
+
shift || true
|
|
389
|
+
if [[ "$cmd" == "display-message" ]]; then
|
|
390
|
+
target=""
|
|
391
|
+
format=""
|
|
392
|
+
while (($#)); do
|
|
393
|
+
case "$1" in
|
|
394
|
+
-p) shift ;;
|
|
395
|
+
-t) target="$2"; shift 2 ;;
|
|
396
|
+
*) format="$1"; shift ;;
|
|
397
|
+
esac
|
|
398
|
+
done
|
|
399
|
+
if [[ -z "$target" && "$format" == "#S" ]]; then
|
|
400
|
+
echo "${managedSessionName}"
|
|
401
|
+
exit 0
|
|
402
|
+
fi
|
|
403
|
+
echo "unsupported display target: $target / $format" >&2
|
|
404
|
+
exit 1
|
|
405
|
+
fi
|
|
406
|
+
if [[ "$cmd" == "list-panes" ]]; then
|
|
407
|
+
target=""
|
|
408
|
+
while (($#)); do
|
|
409
|
+
case "$1" in
|
|
410
|
+
-s) shift ;;
|
|
411
|
+
-t) target="$2"; shift 2 ;;
|
|
412
|
+
-F) shift 2 ;;
|
|
413
|
+
*) shift ;;
|
|
414
|
+
esac
|
|
415
|
+
done
|
|
416
|
+
if [[ "$target" == "${managedSessionName}" ]]; then
|
|
417
|
+
printf "%%42\\t1\\tnode\\tbash\\n"
|
|
418
|
+
exit 0
|
|
419
|
+
fi
|
|
420
|
+
echo "unexpected list-panes target: $target" >&2
|
|
421
|
+
exit 1
|
|
422
|
+
fi
|
|
423
|
+
echo "unsupported cmd: $cmd" >&2
|
|
424
|
+
exit 1
|
|
425
|
+
`;
|
|
426
|
+
await writeFile(fakeTmuxPath, fakeTmux);
|
|
427
|
+
await chmod(fakeTmuxPath, 0o755);
|
|
428
|
+
process.env.PATH = `${fakeBinDir}:${originalPath || ''}`;
|
|
429
|
+
process.env.TMUX = '1';
|
|
430
|
+
delete process.env.TMUX_PANE;
|
|
431
|
+
process.env.OMX_TEAM_WORKER = '';
|
|
432
|
+
const paneId = await resolveManagedSessionPane(cwd, { session_id: sessionId });
|
|
433
|
+
assert.equal(paneId, '');
|
|
434
|
+
}
|
|
435
|
+
finally {
|
|
436
|
+
process.env.PATH = originalPath;
|
|
437
|
+
await rm(cwd, { recursive: true, force: true });
|
|
438
|
+
}
|
|
439
|
+
});
|
|
440
|
+
it('keeps a wrapper-launched node anchor when detached anchor fallback has no stricter codex candidate', async () => {
|
|
441
|
+
const cwd = await mkdtemp(join(tmpdir(), 'omx-managed-wrapper-node-anchor-'));
|
|
442
|
+
const originalPath = process.env.PATH;
|
|
443
|
+
try {
|
|
444
|
+
const stateDir = join(cwd, '.omx', 'state');
|
|
445
|
+
const fakeBinDir = join(cwd, 'fake-bin');
|
|
446
|
+
const fakeTmuxPath = join(fakeBinDir, 'tmux');
|
|
447
|
+
const sessionId = 'omx-wrapper-node-anchor';
|
|
448
|
+
const managedSessionName = buildTmuxSessionName(cwd, sessionId);
|
|
449
|
+
await mkdir(stateDir, { recursive: true });
|
|
450
|
+
await mkdir(fakeBinDir, { recursive: true });
|
|
451
|
+
await writeFile(join(stateDir, 'session.json'), JSON.stringify({
|
|
452
|
+
session_id: sessionId,
|
|
453
|
+
started_at: new Date().toISOString(),
|
|
454
|
+
cwd,
|
|
455
|
+
pid: process.pid,
|
|
456
|
+
platform: process.platform,
|
|
457
|
+
pid_start_ticks: readLinuxStartTicks(process.pid),
|
|
458
|
+
pid_cmdline: readLinuxCmdline(process.pid),
|
|
459
|
+
}, null, 2));
|
|
460
|
+
const fakeTmux = `#!/usr/bin/env bash
|
|
461
|
+
set -eu
|
|
462
|
+
cmd="$1"
|
|
463
|
+
shift || true
|
|
464
|
+
if [[ "$cmd" == "display-message" ]]; then
|
|
465
|
+
target=""
|
|
466
|
+
format=""
|
|
467
|
+
while (($#)); do
|
|
468
|
+
case "$1" in
|
|
469
|
+
-p) shift ;;
|
|
470
|
+
-t) target="$2"; shift 2 ;;
|
|
471
|
+
*) format="$1"; shift ;;
|
|
472
|
+
esac
|
|
473
|
+
done
|
|
474
|
+
if [[ -z "$target" && "$format" == "#S" ]]; then
|
|
475
|
+
echo "${managedSessionName}"
|
|
476
|
+
exit 0
|
|
477
|
+
fi
|
|
478
|
+
if [[ "$format" == "#{pane_current_command}" && "$target" == "%42" ]]; then
|
|
479
|
+
echo "node"
|
|
480
|
+
exit 0
|
|
481
|
+
fi
|
|
482
|
+
if [[ "$format" == "#{pane_start_command}" && "$target" == "%42" ]]; then
|
|
483
|
+
echo "bash"
|
|
484
|
+
exit 0
|
|
485
|
+
fi
|
|
486
|
+
if [[ "$format" == "#S" && "$target" == "%42" ]]; then
|
|
487
|
+
echo "${managedSessionName}"
|
|
488
|
+
exit 0
|
|
489
|
+
fi
|
|
490
|
+
echo "unsupported display target: $target / $format" >&2
|
|
491
|
+
exit 1
|
|
492
|
+
fi
|
|
493
|
+
if [[ "$cmd" == "list-panes" ]]; then
|
|
494
|
+
target=""
|
|
495
|
+
while (($#)); do
|
|
496
|
+
case "$1" in
|
|
497
|
+
-s) shift ;;
|
|
498
|
+
-t) target="$2"; shift 2 ;;
|
|
499
|
+
-F) shift 2 ;;
|
|
500
|
+
*) shift ;;
|
|
501
|
+
esac
|
|
502
|
+
done
|
|
503
|
+
if [[ "$target" == "${managedSessionName}" ]]; then
|
|
504
|
+
printf "%%42\\t1\\tnode\\tbash\\n"
|
|
505
|
+
exit 0
|
|
506
|
+
fi
|
|
507
|
+
echo "unexpected list-panes target: $target" >&2
|
|
508
|
+
exit 1
|
|
509
|
+
fi
|
|
510
|
+
echo "unsupported cmd: $cmd" >&2
|
|
511
|
+
exit 1
|
|
512
|
+
`;
|
|
513
|
+
await writeFile(fakeTmuxPath, fakeTmux);
|
|
514
|
+
await chmod(fakeTmuxPath, 0o755);
|
|
515
|
+
process.env.PATH = `${fakeBinDir}:${originalPath || ''}`;
|
|
516
|
+
process.env.TMUX = '1';
|
|
517
|
+
delete process.env.TMUX_PANE;
|
|
518
|
+
process.env.OMX_TEAM_WORKER = '';
|
|
519
|
+
const paneId = await resolveManagedPaneFromAnchor('%42', cwd, { session_id: sessionId }, { allowTeamWorker: false });
|
|
520
|
+
assert.equal(paneId, '%42');
|
|
521
|
+
}
|
|
522
|
+
finally {
|
|
523
|
+
process.env.PATH = originalPath;
|
|
524
|
+
await rm(cwd, { recursive: true, force: true });
|
|
525
|
+
}
|
|
526
|
+
});
|
|
527
|
+
it('fails closed for a shell-degraded codex anchor when only a detached wrapper fallback exists', async () => {
|
|
528
|
+
const cwd = await mkdtemp(join(tmpdir(), 'omx-managed-degraded-codex-wrapper-only-'));
|
|
529
|
+
const originalPath = process.env.PATH;
|
|
530
|
+
try {
|
|
531
|
+
const stateDir = join(cwd, '.omx', 'state');
|
|
532
|
+
const fakeBinDir = join(cwd, 'fake-bin');
|
|
533
|
+
const fakeTmuxPath = join(fakeBinDir, 'tmux');
|
|
534
|
+
const sessionId = 'omx-degraded-codex-wrapper-only';
|
|
535
|
+
const managedSessionName = buildTmuxSessionName(cwd, sessionId);
|
|
536
|
+
await mkdir(stateDir, { recursive: true });
|
|
537
|
+
await mkdir(fakeBinDir, { recursive: true });
|
|
538
|
+
await writeFile(join(stateDir, 'session.json'), JSON.stringify({
|
|
539
|
+
session_id: sessionId,
|
|
540
|
+
started_at: new Date().toISOString(),
|
|
541
|
+
cwd,
|
|
542
|
+
pid: process.pid,
|
|
543
|
+
platform: process.platform,
|
|
544
|
+
pid_start_ticks: readLinuxStartTicks(process.pid),
|
|
545
|
+
pid_cmdline: readLinuxCmdline(process.pid),
|
|
546
|
+
}, null, 2));
|
|
547
|
+
const fakeTmux = `#!/usr/bin/env bash
|
|
548
|
+
set -eu
|
|
549
|
+
cmd="$1"
|
|
550
|
+
shift || true
|
|
551
|
+
if [[ "$cmd" == "display-message" ]]; then
|
|
552
|
+
target=""
|
|
553
|
+
format=""
|
|
554
|
+
while (($#)); do
|
|
555
|
+
case "$1" in
|
|
556
|
+
-p) shift ;;
|
|
557
|
+
-t) target="$2"; shift 2 ;;
|
|
558
|
+
*) format="$1"; shift ;;
|
|
559
|
+
esac
|
|
560
|
+
done
|
|
561
|
+
if [[ -z "$target" && "$format" == "#S" ]]; then
|
|
562
|
+
echo "${managedSessionName}"
|
|
563
|
+
exit 0
|
|
564
|
+
fi
|
|
565
|
+
if [[ "$format" == "#{pane_current_command}" && "$target" == "%42" ]]; then
|
|
566
|
+
echo "bash"
|
|
567
|
+
exit 0
|
|
568
|
+
fi
|
|
569
|
+
if [[ "$format" == "#{pane_start_command}" && "$target" == "%42" ]]; then
|
|
570
|
+
echo "codex --model gpt-5"
|
|
571
|
+
exit 0
|
|
572
|
+
fi
|
|
573
|
+
if [[ "$format" == "#S" && "$target" == "%42" ]]; then
|
|
574
|
+
echo "${managedSessionName}"
|
|
575
|
+
exit 0
|
|
576
|
+
fi
|
|
577
|
+
echo "unsupported display target: $target / $format" >&2
|
|
578
|
+
exit 1
|
|
579
|
+
fi
|
|
580
|
+
if [[ "$cmd" == "list-panes" ]]; then
|
|
581
|
+
target=""
|
|
582
|
+
while (($#)); do
|
|
583
|
+
case "$1" in
|
|
584
|
+
-s) shift ;;
|
|
585
|
+
-t) target="$2"; shift 2 ;;
|
|
586
|
+
-F) shift 2 ;;
|
|
587
|
+
*) shift ;;
|
|
588
|
+
esac
|
|
589
|
+
done
|
|
590
|
+
if [[ "$target" == "${managedSessionName}" ]]; then
|
|
591
|
+
printf "%%42\\t0\\tbash\\tcodex --model gpt-5\\n%%55\\t1\\tnode\\tbash\\n"
|
|
592
|
+
exit 0
|
|
593
|
+
fi
|
|
594
|
+
echo "unexpected list-panes target: $target" >&2
|
|
595
|
+
exit 1
|
|
596
|
+
fi
|
|
597
|
+
echo "unsupported cmd: $cmd" >&2
|
|
598
|
+
exit 1
|
|
599
|
+
`;
|
|
600
|
+
await writeFile(fakeTmuxPath, fakeTmux);
|
|
601
|
+
await chmod(fakeTmuxPath, 0o755);
|
|
602
|
+
process.env.PATH = `${fakeBinDir}:${originalPath || ''}`;
|
|
603
|
+
process.env.TMUX = '1';
|
|
604
|
+
delete process.env.TMUX_PANE;
|
|
605
|
+
process.env.OMX_TEAM_WORKER = '';
|
|
606
|
+
const paneId = await resolveManagedPaneFromAnchor('%42', cwd, { session_id: sessionId }, { allowTeamWorker: false });
|
|
607
|
+
assert.equal(paneId, '');
|
|
608
|
+
}
|
|
609
|
+
finally {
|
|
610
|
+
process.env.PATH = originalPath;
|
|
611
|
+
await rm(cwd, { recursive: true, force: true });
|
|
612
|
+
}
|
|
613
|
+
});
|
|
614
|
+
it('rebinds a shell-degraded codex anchor to the live codex pane in the managed session', async () => {
|
|
615
|
+
const cwd = await mkdtemp(join(tmpdir(), 'omx-managed-degraded-codex-anchor-'));
|
|
616
|
+
const originalPath = process.env.PATH;
|
|
617
|
+
try {
|
|
618
|
+
const stateDir = join(cwd, '.omx', 'state');
|
|
619
|
+
const fakeBinDir = join(cwd, 'fake-bin');
|
|
620
|
+
const fakeTmuxPath = join(fakeBinDir, 'tmux');
|
|
621
|
+
const sessionId = 'omx-degraded-codex-anchor';
|
|
622
|
+
const managedSessionName = buildTmuxSessionName(cwd, sessionId);
|
|
623
|
+
await mkdir(stateDir, { recursive: true });
|
|
624
|
+
await mkdir(fakeBinDir, { recursive: true });
|
|
625
|
+
await writeFile(join(stateDir, 'session.json'), JSON.stringify({
|
|
626
|
+
session_id: sessionId,
|
|
627
|
+
started_at: new Date().toISOString(),
|
|
628
|
+
cwd,
|
|
629
|
+
pid: process.pid,
|
|
630
|
+
platform: process.platform,
|
|
631
|
+
pid_start_ticks: readLinuxStartTicks(process.pid),
|
|
632
|
+
pid_cmdline: readLinuxCmdline(process.pid),
|
|
633
|
+
}, null, 2));
|
|
634
|
+
const fakeTmux = `#!/usr/bin/env bash
|
|
635
|
+
set -eu
|
|
636
|
+
cmd="$1"
|
|
637
|
+
shift || true
|
|
638
|
+
if [[ "$cmd" == "display-message" ]]; then
|
|
639
|
+
target=""
|
|
640
|
+
format=""
|
|
641
|
+
while (($#)); do
|
|
642
|
+
case "$1" in
|
|
643
|
+
-p) shift ;;
|
|
644
|
+
-t) target="$2"; shift 2 ;;
|
|
645
|
+
*) format="$1"; shift ;;
|
|
646
|
+
esac
|
|
647
|
+
done
|
|
648
|
+
if [[ -z "$target" && "$format" == "#S" ]]; then
|
|
649
|
+
echo "${managedSessionName}"
|
|
650
|
+
exit 0
|
|
651
|
+
fi
|
|
652
|
+
if [[ "$format" == "#{pane_current_command}" && "$target" == "%42" ]]; then
|
|
653
|
+
echo "bash"
|
|
654
|
+
exit 0
|
|
655
|
+
fi
|
|
656
|
+
if [[ "$format" == "#{pane_start_command}" && "$target" == "%42" ]]; then
|
|
657
|
+
echo "codex --model gpt-5"
|
|
658
|
+
exit 0
|
|
659
|
+
fi
|
|
660
|
+
if [[ "$format" == "#S" && "$target" == "%42" ]]; then
|
|
661
|
+
echo "${managedSessionName}"
|
|
662
|
+
exit 0
|
|
663
|
+
fi
|
|
664
|
+
echo "unsupported display target: $target / $format" >&2
|
|
665
|
+
exit 1
|
|
666
|
+
fi
|
|
667
|
+
if [[ "$cmd" == "list-panes" ]]; then
|
|
668
|
+
target=""
|
|
669
|
+
while (($#)); do
|
|
670
|
+
case "$1" in
|
|
671
|
+
-s) shift ;;
|
|
672
|
+
-t) target="$2"; shift 2 ;;
|
|
673
|
+
-F) shift 2 ;;
|
|
674
|
+
*) shift ;;
|
|
675
|
+
esac
|
|
676
|
+
done
|
|
677
|
+
if [[ "$target" == "${managedSessionName}" ]]; then
|
|
678
|
+
printf "%%42\\t0\\tbash\\tcodex --model gpt-5\\n%%55\\t1\\tcodex\\tcodex\\n"
|
|
679
|
+
exit 0
|
|
680
|
+
fi
|
|
681
|
+
echo "unexpected list-panes target: $target" >&2
|
|
682
|
+
exit 1
|
|
683
|
+
fi
|
|
684
|
+
echo "unsupported cmd: $cmd" >&2
|
|
685
|
+
exit 1
|
|
686
|
+
`;
|
|
687
|
+
await writeFile(fakeTmuxPath, fakeTmux);
|
|
688
|
+
await chmod(fakeTmuxPath, 0o755);
|
|
689
|
+
process.env.PATH = `${fakeBinDir}:${originalPath || ''}`;
|
|
690
|
+
process.env.TMUX = '1';
|
|
691
|
+
delete process.env.TMUX_PANE;
|
|
692
|
+
process.env.OMX_TEAM_WORKER = '';
|
|
693
|
+
const paneId = await resolveManagedPaneFromAnchor('%42', cwd, { session_id: sessionId }, { allowTeamWorker: false });
|
|
694
|
+
assert.equal(paneId, '%55');
|
|
695
|
+
}
|
|
696
|
+
finally {
|
|
697
|
+
process.env.PATH = originalPath;
|
|
698
|
+
await rm(cwd, { recursive: true, force: true });
|
|
699
|
+
}
|
|
700
|
+
});
|
|
701
|
+
it('ignores an active shell-degraded codex pane when selecting the live managed replacement', async () => {
|
|
702
|
+
const cwd = await mkdtemp(join(tmpdir(), 'omx-managed-active-degraded-codex-anchor-'));
|
|
703
|
+
const originalPath = process.env.PATH;
|
|
704
|
+
try {
|
|
705
|
+
const stateDir = join(cwd, '.omx', 'state');
|
|
706
|
+
const fakeBinDir = join(cwd, 'fake-bin');
|
|
707
|
+
const fakeTmuxPath = join(fakeBinDir, 'tmux');
|
|
708
|
+
const sessionId = 'omx-active-degraded-codex-anchor';
|
|
709
|
+
const managedSessionName = buildTmuxSessionName(cwd, sessionId);
|
|
710
|
+
await mkdir(stateDir, { recursive: true });
|
|
711
|
+
await mkdir(fakeBinDir, { recursive: true });
|
|
712
|
+
await writeFile(join(stateDir, 'session.json'), JSON.stringify({
|
|
713
|
+
session_id: sessionId,
|
|
714
|
+
started_at: new Date().toISOString(),
|
|
715
|
+
cwd,
|
|
716
|
+
pid: process.pid,
|
|
717
|
+
platform: process.platform,
|
|
718
|
+
pid_start_ticks: readLinuxStartTicks(process.pid),
|
|
719
|
+
pid_cmdline: readLinuxCmdline(process.pid),
|
|
720
|
+
}, null, 2));
|
|
721
|
+
const fakeTmux = `#!/usr/bin/env bash
|
|
722
|
+
set -eu
|
|
723
|
+
cmd="$1"
|
|
724
|
+
shift || true
|
|
725
|
+
if [[ "$cmd" == "display-message" ]]; then
|
|
726
|
+
target=""
|
|
727
|
+
format=""
|
|
728
|
+
while (($#)); do
|
|
729
|
+
case "$1" in
|
|
730
|
+
-p) shift ;;
|
|
731
|
+
-t) target="$2"; shift 2 ;;
|
|
732
|
+
*) format="$1"; shift ;;
|
|
733
|
+
esac
|
|
734
|
+
done
|
|
735
|
+
if [[ -z "$target" && "$format" == "#S" ]]; then
|
|
736
|
+
echo "${managedSessionName}"
|
|
737
|
+
exit 0
|
|
738
|
+
fi
|
|
739
|
+
if [[ "$format" == "#{pane_current_command}" && "$target" == "%42" ]]; then
|
|
740
|
+
echo "zsh"
|
|
741
|
+
exit 0
|
|
742
|
+
fi
|
|
743
|
+
if [[ "$format" == "#{pane_start_command}" && "$target" == "%42" ]]; then
|
|
744
|
+
echo "codex --model gpt-5"
|
|
745
|
+
exit 0
|
|
746
|
+
fi
|
|
747
|
+
if [[ "$format" == "#S" && "$target" == "%42" ]]; then
|
|
748
|
+
echo "${managedSessionName}"
|
|
749
|
+
exit 0
|
|
750
|
+
fi
|
|
751
|
+
echo "unsupported display target: $target / $format" >&2
|
|
752
|
+
exit 1
|
|
753
|
+
fi
|
|
754
|
+
if [[ "$cmd" == "list-panes" ]]; then
|
|
755
|
+
target=""
|
|
756
|
+
while (($#)); do
|
|
757
|
+
case "$1" in
|
|
758
|
+
-s) shift ;;
|
|
759
|
+
-t) target="$2"; shift 2 ;;
|
|
760
|
+
-F) shift 2 ;;
|
|
761
|
+
*) shift ;;
|
|
762
|
+
esac
|
|
763
|
+
done
|
|
764
|
+
if [[ "$target" == "${managedSessionName}" ]]; then
|
|
765
|
+
printf "%%42\\t1\\tzsh\\tcodex --model gpt-5\\n%%55\\t0\\tcodex\\tcodex\\n"
|
|
766
|
+
exit 0
|
|
767
|
+
fi
|
|
768
|
+
echo "unexpected list-panes target: $target" >&2
|
|
769
|
+
exit 1
|
|
770
|
+
fi
|
|
771
|
+
echo "unsupported cmd: $cmd" >&2
|
|
772
|
+
exit 1
|
|
773
|
+
`;
|
|
774
|
+
await writeFile(fakeTmuxPath, fakeTmux);
|
|
775
|
+
await chmod(fakeTmuxPath, 0o755);
|
|
776
|
+
process.env.PATH = `${fakeBinDir}:${originalPath || ''}`;
|
|
777
|
+
process.env.TMUX = '1';
|
|
778
|
+
delete process.env.TMUX_PANE;
|
|
779
|
+
process.env.OMX_TEAM_WORKER = '';
|
|
780
|
+
const paneId = await resolveManagedPaneFromAnchor('%42', cwd, { session_id: sessionId }, { allowTeamWorker: false });
|
|
781
|
+
assert.equal(paneId, '%55');
|
|
782
|
+
}
|
|
783
|
+
finally {
|
|
784
|
+
process.env.PATH = originalPath;
|
|
785
|
+
await rm(cwd, { recursive: true, force: true });
|
|
786
|
+
}
|
|
787
|
+
});
|
|
788
|
+
it('rebinds a degraded anchor using the verified session name when a follow-up #S lookup would fail', async () => {
|
|
789
|
+
const cwd = await mkdtemp(join(tmpdir(), 'omx-managed-degraded-anchor-session-reuse-'));
|
|
790
|
+
const originalPath = process.env.PATH;
|
|
791
|
+
try {
|
|
792
|
+
const stateDir = join(cwd, '.omx', 'state');
|
|
793
|
+
const fakeBinDir = join(cwd, 'fake-bin');
|
|
794
|
+
const fakeTmuxPath = join(fakeBinDir, 'tmux');
|
|
795
|
+
const sessionId = 'omx-degraded-anchor-session-reuse';
|
|
796
|
+
const managedSessionName = buildTmuxSessionName(cwd, sessionId);
|
|
797
|
+
const sessionLookupCountPath = join(cwd, 'session-lookup-count');
|
|
798
|
+
await mkdir(stateDir, { recursive: true });
|
|
799
|
+
await mkdir(fakeBinDir, { recursive: true });
|
|
800
|
+
await writeFile(join(stateDir, 'session.json'), JSON.stringify({
|
|
801
|
+
session_id: sessionId,
|
|
802
|
+
started_at: new Date().toISOString(),
|
|
803
|
+
cwd,
|
|
804
|
+
pid: process.pid,
|
|
805
|
+
platform: process.platform,
|
|
806
|
+
pid_start_ticks: readLinuxStartTicks(process.pid),
|
|
807
|
+
pid_cmdline: readLinuxCmdline(process.pid),
|
|
808
|
+
}, null, 2));
|
|
809
|
+
const fakeTmux = `#!/usr/bin/env bash
|
|
810
|
+
set -eu
|
|
811
|
+
cmd="$1"
|
|
812
|
+
shift || true
|
|
813
|
+
if [[ "$cmd" == "display-message" ]]; then
|
|
814
|
+
target=""
|
|
815
|
+
format=""
|
|
816
|
+
while (($#)); do
|
|
817
|
+
case "$1" in
|
|
818
|
+
-p) shift ;;
|
|
819
|
+
-t) target="$2"; shift 2 ;;
|
|
820
|
+
*) format="$1"; shift ;;
|
|
821
|
+
esac
|
|
822
|
+
done
|
|
823
|
+
if [[ -z "$target" && "$format" == "#S" ]]; then
|
|
824
|
+
echo "${managedSessionName}"
|
|
825
|
+
exit 0
|
|
826
|
+
fi
|
|
827
|
+
if [[ "$format" == "#{pane_current_command}" && "$target" == "%42" ]]; then
|
|
828
|
+
echo "bash"
|
|
829
|
+
exit 0
|
|
830
|
+
fi
|
|
831
|
+
if [[ "$format" == "#{pane_start_command}" && "$target" == "%42" ]]; then
|
|
832
|
+
echo "codex --model gpt-5"
|
|
833
|
+
exit 0
|
|
834
|
+
fi
|
|
835
|
+
if [[ "$format" == "#S" && "$target" == "%42" ]]; then
|
|
836
|
+
count=0
|
|
837
|
+
if [[ -f "${sessionLookupCountPath}" ]]; then
|
|
838
|
+
count="$(cat "${sessionLookupCountPath}")"
|
|
839
|
+
fi
|
|
840
|
+
count=$((count + 1))
|
|
841
|
+
printf '%s' "$count" > "${sessionLookupCountPath}"
|
|
842
|
+
if [[ "$count" -gt 1 ]]; then
|
|
843
|
+
echo "session lookup should not repeat" >&2
|
|
844
|
+
exit 1
|
|
845
|
+
fi
|
|
846
|
+
echo "${managedSessionName}"
|
|
847
|
+
exit 0
|
|
848
|
+
fi
|
|
849
|
+
echo "unsupported display target: $target / $format" >&2
|
|
850
|
+
exit 1
|
|
851
|
+
fi
|
|
852
|
+
if [[ "$cmd" == "list-panes" ]]; then
|
|
853
|
+
target=""
|
|
854
|
+
while (($#)); do
|
|
855
|
+
case "$1" in
|
|
856
|
+
-s) shift ;;
|
|
857
|
+
-t) target="$2"; shift 2 ;;
|
|
858
|
+
-F) shift 2 ;;
|
|
859
|
+
*) shift ;;
|
|
860
|
+
esac
|
|
861
|
+
done
|
|
862
|
+
if [[ "$target" == "${managedSessionName}" ]]; then
|
|
863
|
+
printf "%%42\\t1\\tbash\\tcodex --model gpt-5\\n%%55\\t0\\tcodex\\tcodex\\n"
|
|
864
|
+
exit 0
|
|
865
|
+
fi
|
|
866
|
+
echo "unexpected list-panes target: $target" >&2
|
|
867
|
+
exit 1
|
|
868
|
+
fi
|
|
869
|
+
echo "unsupported cmd: $cmd" >&2
|
|
870
|
+
exit 1
|
|
871
|
+
`;
|
|
872
|
+
await writeFile(fakeTmuxPath, fakeTmux);
|
|
873
|
+
await chmod(fakeTmuxPath, 0o755);
|
|
874
|
+
process.env.PATH = `${fakeBinDir}:${originalPath || ''}`;
|
|
875
|
+
process.env.TMUX = '1';
|
|
876
|
+
delete process.env.TMUX_PANE;
|
|
877
|
+
process.env.OMX_TEAM_WORKER = '';
|
|
878
|
+
const paneId = await resolveManagedPaneFromAnchor('%42', cwd, { session_id: sessionId }, { allowTeamWorker: false });
|
|
879
|
+
assert.equal(paneId, '%55');
|
|
880
|
+
}
|
|
881
|
+
finally {
|
|
882
|
+
process.env.PATH = originalPath;
|
|
883
|
+
await rm(cwd, { recursive: true, force: true });
|
|
884
|
+
}
|
|
885
|
+
});
|
|
886
|
+
it('fails closed when a degraded anchor has no live codex sibling in the managed session', async () => {
|
|
887
|
+
const cwd = await mkdtemp(join(tmpdir(), 'omx-managed-degraded-anchor-no-live-sibling-'));
|
|
888
|
+
const originalPath = process.env.PATH;
|
|
889
|
+
try {
|
|
890
|
+
const stateDir = join(cwd, '.omx', 'state');
|
|
891
|
+
const fakeBinDir = join(cwd, 'fake-bin');
|
|
892
|
+
const fakeTmuxPath = join(fakeBinDir, 'tmux');
|
|
893
|
+
const sessionId = 'omx-degraded-anchor-no-live-sibling';
|
|
894
|
+
const managedSessionName = buildTmuxSessionName(cwd, sessionId);
|
|
895
|
+
await mkdir(stateDir, { recursive: true });
|
|
896
|
+
await mkdir(fakeBinDir, { recursive: true });
|
|
897
|
+
await writeFile(join(stateDir, 'session.json'), JSON.stringify({
|
|
898
|
+
session_id: sessionId,
|
|
899
|
+
started_at: new Date().toISOString(),
|
|
900
|
+
cwd,
|
|
901
|
+
pid: process.pid,
|
|
902
|
+
platform: process.platform,
|
|
903
|
+
pid_start_ticks: readLinuxStartTicks(process.pid),
|
|
904
|
+
pid_cmdline: readLinuxCmdline(process.pid),
|
|
905
|
+
}, null, 2));
|
|
906
|
+
const fakeTmux = `#!/usr/bin/env bash
|
|
907
|
+
set -eu
|
|
908
|
+
cmd="$1"
|
|
909
|
+
shift || true
|
|
910
|
+
if [[ "$cmd" == "display-message" ]]; then
|
|
911
|
+
target=""
|
|
912
|
+
format=""
|
|
913
|
+
while (($#)); do
|
|
914
|
+
case "$1" in
|
|
915
|
+
-p) shift ;;
|
|
916
|
+
-t) target="$2"; shift 2 ;;
|
|
917
|
+
*) format="$1"; shift ;;
|
|
918
|
+
esac
|
|
919
|
+
done
|
|
920
|
+
if [[ -z "$target" && "$format" == "#S" ]]; then
|
|
921
|
+
echo "${managedSessionName}"
|
|
922
|
+
exit 0
|
|
923
|
+
fi
|
|
924
|
+
if [[ "$format" == "#{pane_current_command}" && "$target" == "%42" ]]; then
|
|
925
|
+
echo "bash"
|
|
926
|
+
exit 0
|
|
927
|
+
fi
|
|
928
|
+
if [[ "$format" == "#{pane_start_command}" && "$target" == "%42" ]]; then
|
|
929
|
+
echo "codex --model gpt-5"
|
|
930
|
+
exit 0
|
|
931
|
+
fi
|
|
932
|
+
if [[ "$format" == "#S" && "$target" == "%42" ]]; then
|
|
933
|
+
echo "${managedSessionName}"
|
|
934
|
+
exit 0
|
|
935
|
+
fi
|
|
936
|
+
echo "unsupported display target: $target / $format" >&2
|
|
937
|
+
exit 1
|
|
938
|
+
fi
|
|
939
|
+
if [[ "$cmd" == "list-panes" ]]; then
|
|
940
|
+
target=""
|
|
941
|
+
while (($#)); do
|
|
942
|
+
case "$1" in
|
|
943
|
+
-s) shift ;;
|
|
944
|
+
-t) target="$2"; shift 2 ;;
|
|
945
|
+
-F) shift 2 ;;
|
|
946
|
+
*) shift ;;
|
|
947
|
+
esac
|
|
948
|
+
done
|
|
949
|
+
if [[ "$target" == "${managedSessionName}" ]]; then
|
|
950
|
+
printf "%%42\\t1\\tbash\\tcodex --model gpt-5\\n%%55\\t0\\tbash\\tbash\\n"
|
|
951
|
+
exit 0
|
|
952
|
+
fi
|
|
953
|
+
echo "unexpected list-panes target: $target" >&2
|
|
954
|
+
exit 1
|
|
955
|
+
fi
|
|
956
|
+
echo "unsupported cmd: $cmd" >&2
|
|
957
|
+
exit 1
|
|
958
|
+
`;
|
|
959
|
+
await writeFile(fakeTmuxPath, fakeTmux);
|
|
960
|
+
await chmod(fakeTmuxPath, 0o755);
|
|
961
|
+
process.env.PATH = `${fakeBinDir}:${originalPath || ''}`;
|
|
962
|
+
process.env.TMUX = '1';
|
|
963
|
+
delete process.env.TMUX_PANE;
|
|
964
|
+
process.env.OMX_TEAM_WORKER = '';
|
|
965
|
+
const paneId = await resolveManagedPaneFromAnchor('%42', cwd, { session_id: sessionId }, { allowTeamWorker: false });
|
|
966
|
+
assert.equal(paneId, '');
|
|
967
|
+
}
|
|
968
|
+
finally {
|
|
969
|
+
process.env.PATH = originalPath;
|
|
970
|
+
await rm(cwd, { recursive: true, force: true });
|
|
971
|
+
}
|
|
972
|
+
});
|
|
973
|
+
it('keeps a verified live anchor when command-state lookup fails and another codex pane is active', async () => {
|
|
974
|
+
const cwd = await mkdtemp(join(tmpdir(), 'omx-managed-anchor-lookup-failure-'));
|
|
975
|
+
const originalPath = process.env.PATH;
|
|
976
|
+
try {
|
|
977
|
+
const stateDir = join(cwd, '.omx', 'state');
|
|
978
|
+
const fakeBinDir = join(cwd, 'fake-bin');
|
|
979
|
+
const fakeTmuxPath = join(fakeBinDir, 'tmux');
|
|
980
|
+
const sessionId = 'omx-anchor-lookup-failure';
|
|
981
|
+
const managedSessionName = buildTmuxSessionName(cwd, sessionId);
|
|
982
|
+
await mkdir(stateDir, { recursive: true });
|
|
983
|
+
await mkdir(fakeBinDir, { recursive: true });
|
|
984
|
+
await writeFile(join(stateDir, 'session.json'), JSON.stringify({
|
|
985
|
+
session_id: sessionId,
|
|
986
|
+
started_at: new Date().toISOString(),
|
|
987
|
+
cwd,
|
|
988
|
+
pid: process.pid,
|
|
989
|
+
platform: process.platform,
|
|
990
|
+
pid_start_ticks: readLinuxStartTicks(process.pid),
|
|
991
|
+
pid_cmdline: readLinuxCmdline(process.pid),
|
|
992
|
+
}, null, 2));
|
|
993
|
+
const fakeTmux = `#!/usr/bin/env bash
|
|
994
|
+
set -eu
|
|
995
|
+
cmd="$1"
|
|
996
|
+
shift || true
|
|
997
|
+
if [[ "$cmd" == "display-message" ]]; then
|
|
998
|
+
target=""
|
|
999
|
+
format=""
|
|
1000
|
+
while (($#)); do
|
|
1001
|
+
case "$1" in
|
|
1002
|
+
-p) shift ;;
|
|
1003
|
+
-t) target="$2"; shift 2 ;;
|
|
1004
|
+
*) format="$1"; shift ;;
|
|
1005
|
+
esac
|
|
1006
|
+
done
|
|
1007
|
+
if [[ -z "$target" && "$format" == "#S" ]]; then
|
|
1008
|
+
echo "${managedSessionName}"
|
|
1009
|
+
exit 0
|
|
1010
|
+
fi
|
|
1011
|
+
if [[ "$format" == "#S" && "$target" == "%42" ]]; then
|
|
1012
|
+
echo "${managedSessionName}"
|
|
1013
|
+
exit 0
|
|
1014
|
+
fi
|
|
1015
|
+
if [[ "$target" == "%42" && ( "$format" == "#{pane_current_command}" || "$format" == "#{pane_start_command}" ) ]]; then
|
|
1016
|
+
echo "transient lookup failure" >&2
|
|
1017
|
+
exit 1
|
|
1018
|
+
fi
|
|
1019
|
+
echo "unsupported display target: $target / $format" >&2
|
|
1020
|
+
exit 1
|
|
1021
|
+
fi
|
|
1022
|
+
if [[ "$cmd" == "list-panes" ]]; then
|
|
1023
|
+
target=""
|
|
1024
|
+
while (($#)); do
|
|
1025
|
+
case "$1" in
|
|
1026
|
+
-s) shift ;;
|
|
1027
|
+
-t) target="$2"; shift 2 ;;
|
|
1028
|
+
-F) shift 2 ;;
|
|
1029
|
+
*) shift ;;
|
|
1030
|
+
esac
|
|
1031
|
+
done
|
|
1032
|
+
if [[ "$target" == "${managedSessionName}" ]]; then
|
|
1033
|
+
printf "%%42\\t0\\tcodex\\tcodex\\n%%55\\t1\\tcodex\\tcodex\\n"
|
|
1034
|
+
exit 0
|
|
1035
|
+
fi
|
|
1036
|
+
echo "unexpected list-panes target: $target" >&2
|
|
1037
|
+
exit 1
|
|
1038
|
+
fi
|
|
1039
|
+
echo "unsupported cmd: $cmd" >&2
|
|
1040
|
+
exit 1
|
|
1041
|
+
`;
|
|
1042
|
+
await writeFile(fakeTmuxPath, fakeTmux);
|
|
1043
|
+
await chmod(fakeTmuxPath, 0o755);
|
|
1044
|
+
process.env.PATH = `${fakeBinDir}:${originalPath || ''}`;
|
|
1045
|
+
process.env.TMUX = '1';
|
|
1046
|
+
delete process.env.TMUX_PANE;
|
|
1047
|
+
process.env.OMX_TEAM_WORKER = '';
|
|
1048
|
+
const paneId = await resolveManagedPaneFromAnchor('%42', cwd, { session_id: sessionId }, { allowTeamWorker: false });
|
|
1049
|
+
assert.equal(paneId, '%42');
|
|
1050
|
+
}
|
|
1051
|
+
finally {
|
|
1052
|
+
process.env.PATH = originalPath;
|
|
1053
|
+
await rm(cwd, { recursive: true, force: true });
|
|
1054
|
+
}
|
|
1055
|
+
});
|
|
163
1056
|
});
|
|
164
1057
|
//# sourceMappingURL=notify-hook-managed-tmux.test.js.map
|