oh-my-codex 0.17.3 → 0.18.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 +13 -5
- package/Cargo.toml +2 -1
- package/README.md +1 -0
- package/crates/omx-api/Cargo.toml +19 -0
- package/crates/omx-api/src/lib.rs +2940 -0
- package/crates/omx-api/src/main.rs +10 -0
- package/crates/omx-api/tests/cli.rs +558 -0
- package/crates/omx-explore/src/main.rs +4 -0
- package/crates/omx-sparkshell/src/codex_bridge.rs +437 -123
- package/crates/omx-sparkshell/src/exec.rs +4 -0
- package/crates/omx-sparkshell/src/main.rs +738 -29
- package/crates/omx-sparkshell/src/prompt.rs +25 -3
- package/crates/omx-sparkshell/src/redaction.rs +241 -0
- package/crates/omx-sparkshell/tests/execution.rs +479 -238
- package/dist/cli/__tests__/api.test.d.ts +2 -0
- package/dist/cli/__tests__/api.test.d.ts.map +1 -0
- package/dist/cli/__tests__/api.test.js +175 -0
- package/dist/cli/__tests__/api.test.js.map +1 -0
- package/dist/cli/__tests__/ask.test.js +72 -5
- package/dist/cli/__tests__/ask.test.js.map +1 -1
- package/dist/cli/__tests__/autoresearch-goal.test.js +14 -1
- package/dist/cli/__tests__/autoresearch-goal.test.js.map +1 -1
- package/dist/cli/__tests__/explore.test.js +23 -0
- package/dist/cli/__tests__/explore.test.js.map +1 -1
- package/dist/cli/__tests__/index.test.js +123 -5
- package/dist/cli/__tests__/index.test.js.map +1 -1
- package/dist/cli/__tests__/launch-fallback.test.js +76 -0
- package/dist/cli/__tests__/launch-fallback.test.js.map +1 -1
- package/dist/cli/__tests__/package-bin-contract.test.js +4 -3
- package/dist/cli/__tests__/package-bin-contract.test.js.map +1 -1
- package/dist/cli/__tests__/setup-install-mode.test.js +138 -0
- package/dist/cli/__tests__/setup-install-mode.test.js.map +1 -1
- package/dist/cli/__tests__/sparkshell-cli.test.js +5 -0
- package/dist/cli/__tests__/sparkshell-cli.test.js.map +1 -1
- package/dist/cli/__tests__/version-sync-contract.test.js +4 -0
- package/dist/cli/__tests__/version-sync-contract.test.js.map +1 -1
- package/dist/cli/__tests__/windows-popup-loop-contract.test.js +1 -1
- package/dist/cli/__tests__/windows-popup-loop-contract.test.js.map +1 -1
- package/dist/cli/api.d.ts +26 -0
- package/dist/cli/api.d.ts.map +1 -0
- package/dist/cli/api.js +153 -0
- package/dist/cli/api.js.map +1 -0
- package/dist/cli/explore.d.ts +2 -0
- package/dist/cli/explore.d.ts.map +1 -1
- package/dist/cli/explore.js +43 -1
- package/dist/cli/explore.js.map +1 -1
- package/dist/cli/index.d.ts +10 -4
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +128 -10
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/native-assets.d.ts +2 -1
- package/dist/cli/native-assets.d.ts.map +1 -1
- package/dist/cli/native-assets.js +1 -0
- package/dist/cli/native-assets.js.map +1 -1
- package/dist/cli/sparkshell.d.ts.map +1 -1
- package/dist/cli/sparkshell.js +20 -3
- package/dist/cli/sparkshell.js.map +1 -1
- package/dist/config/generator.d.ts.map +1 -1
- package/dist/config/generator.js +90 -0
- package/dist/config/generator.js.map +1 -1
- package/dist/hooks/__tests__/best-practice-research-skill.test.d.ts +2 -0
- package/dist/hooks/__tests__/best-practice-research-skill.test.d.ts.map +1 -0
- package/dist/hooks/__tests__/best-practice-research-skill.test.js +27 -0
- package/dist/hooks/__tests__/best-practice-research-skill.test.js.map +1 -0
- package/dist/hooks/__tests__/keyword-detector.test.js +11 -0
- package/dist/hooks/__tests__/keyword-detector.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-hook-team-leader-nudge.test.js +6 -0
- package/dist/hooks/__tests__/notify-hook-team-leader-nudge.test.js.map +1 -1
- package/dist/hooks/__tests__/prompt-guidance-wave-two.test.js +4 -0
- package/dist/hooks/__tests__/prompt-guidance-wave-two.test.js.map +1 -1
- package/dist/hooks/keyword-registry.d.ts.map +1 -1
- package/dist/hooks/keyword-registry.js +1 -0
- package/dist/hooks/keyword-registry.js.map +1 -1
- package/dist/hud/__tests__/reconcile.test.js +2 -2
- package/dist/hud/__tests__/reconcile.test.js.map +1 -1
- package/dist/hud/__tests__/tmux.test.js +23 -18
- package/dist/hud/__tests__/tmux.test.js.map +1 -1
- package/dist/hud/tmux.d.ts.map +1 -1
- package/dist/hud/tmux.js +7 -6
- package/dist/hud/tmux.js.map +1 -1
- package/dist/mcp/__tests__/bootstrap.test.js +75 -1
- package/dist/mcp/__tests__/bootstrap.test.js.map +1 -1
- package/dist/mcp/bootstrap.d.ts +3 -1
- package/dist/mcp/bootstrap.d.ts.map +1 -1
- package/dist/mcp/bootstrap.js +71 -2
- package/dist/mcp/bootstrap.js.map +1 -1
- package/dist/scripts/__tests__/codex-native-hook.test.js +737 -26
- package/dist/scripts/__tests__/codex-native-hook.test.js.map +1 -1
- package/dist/scripts/__tests__/notify-dispatcher.test.js +183 -1
- package/dist/scripts/__tests__/notify-dispatcher.test.js.map +1 -1
- package/dist/scripts/__tests__/smoke-packed-install.test.js +4 -1
- package/dist/scripts/__tests__/smoke-packed-install.test.js.map +1 -1
- package/dist/scripts/build-api.d.ts +2 -0
- package/dist/scripts/build-api.d.ts.map +1 -0
- package/dist/scripts/build-api.js +44 -0
- package/dist/scripts/build-api.js.map +1 -0
- package/dist/scripts/codex-native-hook.d.ts.map +1 -1
- package/dist/scripts/codex-native-hook.js +208 -8
- package/dist/scripts/codex-native-hook.js.map +1 -1
- package/dist/scripts/codex-native-pre-post.d.ts.map +1 -1
- package/dist/scripts/codex-native-pre-post.js +89 -24
- package/dist/scripts/codex-native-pre-post.js.map +1 -1
- package/dist/scripts/notify-dispatcher.js +88 -0
- package/dist/scripts/notify-dispatcher.js.map +1 -1
- package/dist/scripts/notify-hook/team-dispatch.d.ts.map +1 -1
- package/dist/scripts/notify-hook/team-dispatch.js +27 -9
- package/dist/scripts/notify-hook/team-dispatch.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 +26 -11
- package/dist/scripts/notify-hook/team-leader-nudge.js.map +1 -1
- package/dist/scripts/notify-hook/team-tmux-guard.d.ts +1 -0
- package/dist/scripts/notify-hook/team-tmux-guard.d.ts.map +1 -1
- package/dist/scripts/notify-hook/team-tmux-guard.js +38 -0
- package/dist/scripts/notify-hook/team-tmux-guard.js.map +1 -1
- package/dist/scripts/notify-hook/team-worker-stop.d.ts.map +1 -1
- package/dist/scripts/notify-hook/team-worker-stop.js +27 -14
- package/dist/scripts/notify-hook/team-worker-stop.js.map +1 -1
- package/dist/scripts/run-provider-advisor.js +9 -3
- package/dist/scripts/run-provider-advisor.js.map +1 -1
- package/dist/scripts/smoke-packed-install.d.ts +1 -1
- package/dist/scripts/smoke-packed-install.d.ts.map +1 -1
- package/dist/scripts/smoke-packed-install.js +2 -0
- package/dist/scripts/smoke-packed-install.js.map +1 -1
- package/dist/team/__tests__/runtime.test.js +2 -2
- package/dist/team/__tests__/runtime.test.js.map +1 -1
- package/dist/team/__tests__/tmux-session.test.js +96 -19
- package/dist/team/__tests__/tmux-session.test.js.map +1 -1
- package/dist/team/tmux-session.d.ts +1 -0
- package/dist/team/tmux-session.d.ts.map +1 -1
- package/dist/team/tmux-session.js +34 -10
- package/dist/team/tmux-session.js.map +1 -1
- package/dist/verification/__tests__/ci-rust-gates.test.js +85 -10
- package/dist/verification/__tests__/ci-rust-gates.test.js.map +1 -1
- package/dist/verification/__tests__/explore-harness-release-workflow.test.js +1 -0
- package/dist/verification/__tests__/explore-harness-release-workflow.test.js.map +1 -1
- package/package.json +4 -3
- package/plugins/oh-my-codex/.codex-plugin/plugin.json +1 -1
- package/plugins/oh-my-codex/skills/best-practice-research/SKILL.md +83 -0
- package/plugins/oh-my-codex/skills/deep-interview/SKILL.md +1 -0
- package/plugins/oh-my-codex/skills/ralplan/SKILL.md +1 -1
- package/prompts/researcher.md +15 -10
- package/skills/best-practice-research/SKILL.md +83 -0
- package/skills/deep-interview/SKILL.md +1 -0
- package/skills/ralplan/SKILL.md +1 -1
- package/src/scripts/__tests__/codex-native-hook.test.ts +810 -4
- package/src/scripts/__tests__/notify-dispatcher.test.ts +223 -1
- package/src/scripts/__tests__/smoke-packed-install.test.ts +8 -2
- package/src/scripts/build-api.ts +48 -0
- package/src/scripts/codex-native-hook.ts +262 -10
- package/src/scripts/codex-native-pre-post.ts +103 -24
- package/src/scripts/notify-dispatcher.ts +97 -0
- package/src/scripts/notify-hook/team-dispatch.ts +27 -8
- package/src/scripts/notify-hook/team-leader-nudge.ts +25 -11
- package/src/scripts/notify-hook/team-tmux-guard.ts +42 -0
- package/src/scripts/notify-hook/team-worker-stop.ts +24 -13
- package/src/scripts/run-provider-advisor.ts +11 -3
- package/src/scripts/smoke-packed-install.ts +2 -0
- package/templates/catalog-manifest.json +7 -0
package/dist/cli/index.js
CHANGED
|
@@ -24,6 +24,7 @@ import { stateCommand } from "./state.js";
|
|
|
24
24
|
import { cleanupCommand, cleanupOmxMcpProcesses, findLaunchSafeCleanupCandidates, } from "./cleanup.js";
|
|
25
25
|
import { exploreCommand } from "./explore.js";
|
|
26
26
|
import { sparkshellCommand } from "./sparkshell.js";
|
|
27
|
+
import { apiCommand } from "./api.js";
|
|
27
28
|
import { agentsInitCommand } from "./agents-init.js";
|
|
28
29
|
import { agentsCommand } from "./agents.js";
|
|
29
30
|
import { sessionCommand } from "./session-search.js";
|
|
@@ -51,7 +52,7 @@ import { cleanCodexModelAvailabilityNuxIfNeeded, extractSharedMcpRegistryServers
|
|
|
51
52
|
import { OMX_FIRST_PARTY_MCP_SERVER_NAMES } from "../config/omx-first-party-mcp.js";
|
|
52
53
|
import { HUD_TMUX_HEIGHT_LINES } from "../hud/constants.js";
|
|
53
54
|
import { OMX_TMUX_HUD_OWNER_ENV } from "../hud/reconcile.js";
|
|
54
|
-
import { createHudWatchPane as createSharedHudWatchPane, killTmuxPane as killSharedTmuxPane, listCurrentWindowHudPaneIds, parsePaneIdFromTmuxOutput, } from "../hud/tmux.js";
|
|
55
|
+
import { createHudWatchPane as createSharedHudWatchPane, killTmuxPane as killSharedTmuxPane, listCurrentWindowHudPaneIds, parsePaneIdFromTmuxOutput, registerHudResizeHook, } from "../hud/tmux.js";
|
|
55
56
|
export { parseTmuxPaneSnapshot, isHudWatchPane, findHudWatchPaneIds } from "../hud/tmux.js";
|
|
56
57
|
rememberOmxLaunchContext();
|
|
57
58
|
import { classifySpawnError, resolveTmuxBinaryForPlatform, spawnPlatformCommandSync, } from "../utils/platform-command.js";
|
|
@@ -98,6 +99,7 @@ Usage:
|
|
|
98
99
|
omx adapt Scaffold OMX-owned adapter foundations for persistent external targets
|
|
99
100
|
omx resume Resume a previous interactive Codex session
|
|
100
101
|
omx explore Default read-only exploration entrypoint (may adaptively use sparkshell backend)
|
|
102
|
+
omx api Run native omx-api localhost gateway commands (serve|status|stop|generate)
|
|
101
103
|
omx session Search prior local session transcripts and history artifacts
|
|
102
104
|
omx agents-init [path]
|
|
103
105
|
Bootstrap lightweight AGENTS.md files for a repo/subtree
|
|
@@ -250,6 +252,7 @@ const NESTED_HELP_COMMANDS = new Set([
|
|
|
250
252
|
"performance-goal",
|
|
251
253
|
"resume",
|
|
252
254
|
"session",
|
|
255
|
+
"api",
|
|
253
256
|
"sparkshell",
|
|
254
257
|
"team",
|
|
255
258
|
"tmux-hook",
|
|
@@ -843,6 +846,7 @@ export async function main(args) {
|
|
|
843
846
|
"autoresearch",
|
|
844
847
|
"autoresearch-goal",
|
|
845
848
|
"explore",
|
|
849
|
+
"api",
|
|
846
850
|
"sparkshell",
|
|
847
851
|
"team",
|
|
848
852
|
"ralph",
|
|
@@ -947,6 +951,9 @@ export async function main(args) {
|
|
|
947
951
|
case "explore":
|
|
948
952
|
await exploreCommand(args.slice(1));
|
|
949
953
|
break;
|
|
954
|
+
case "api":
|
|
955
|
+
await apiCommand(args.slice(1));
|
|
956
|
+
break;
|
|
950
957
|
case "exec":
|
|
951
958
|
if (launchArgs[0] === "inject") {
|
|
952
959
|
await execInjectCommand(launchArgs);
|
|
@@ -1833,10 +1840,16 @@ function withTmuxExtendedKeysLeaseLock(cwd, socketPath, run) {
|
|
|
1833
1840
|
}
|
|
1834
1841
|
throw new Error(`timed out waiting for tmux extended-keys lease lock: ${lockPath}`);
|
|
1835
1842
|
}
|
|
1836
|
-
function buildDetachedSessionLeaderCommand(cwd, sessionName, codexCmd, sessionId, codexHomeOverride, projectLocalCodexHomeForCleanup, runtimeCodexHomeForCleanup) {
|
|
1843
|
+
function buildDetachedSessionLeaderCommand(cwd, sessionName, codexCmd, sessionId, codexHomeOverride, projectLocalCodexHomeForCleanup, runtimeCodexHomeForCleanup, parentEnvFilePath) {
|
|
1837
1844
|
const detachedPostLaunchHelper = sessionId
|
|
1838
1845
|
? `${buildDetachedSessionPostLaunchHelperCommand(cwd, sessionId, codexHomeOverride, projectLocalCodexHomeForCleanup, runtimeCodexHomeForCleanup)} >/dev/null 2>&1 || true;`
|
|
1839
1846
|
: "";
|
|
1847
|
+
const parentEnvSource = parentEnvFilePath && parentEnvFilePath.trim()
|
|
1848
|
+
? `if [ -r ${quoteShellArg(parentEnvFilePath)} ]; then . ${quoteShellArg(parentEnvFilePath)}; rm -f ${quoteShellArg(parentEnvFilePath)}; fi;`
|
|
1849
|
+
: "";
|
|
1850
|
+
const parentEnvCleanup = parentEnvFilePath && parentEnvFilePath.trim()
|
|
1851
|
+
? `rm -f ${quoteShellArg(parentEnvFilePath)} 2>/dev/null || true;`
|
|
1852
|
+
: "";
|
|
1840
1853
|
const wrapped = [
|
|
1841
1854
|
buildTmuxExtendedKeysAcquireShellSnippet(cwd),
|
|
1842
1855
|
'exec 3<&0;',
|
|
@@ -1850,6 +1863,7 @@ function buildDetachedSessionLeaderCommand(cwd, sessionName, codexCmd, sessionId
|
|
|
1850
1863
|
"fi;",
|
|
1851
1864
|
'exec 3<&- 2>/dev/null || true;',
|
|
1852
1865
|
buildTmuxExtendedKeysReleaseShellSnippet(cwd),
|
|
1866
|
+
parentEnvCleanup,
|
|
1853
1867
|
detachedPostLaunchHelper,
|
|
1854
1868
|
'if [ "$status" -eq 0 ]; then',
|
|
1855
1869
|
`tmux kill-session -t "${escapeShellDoubleQuotedValue(sessionName)}" >/dev/null 2>&1 || true;`,
|
|
@@ -1857,6 +1871,7 @@ function buildDetachedSessionLeaderCommand(cwd, sessionName, codexCmd, sessionId
|
|
|
1857
1871
|
"exit $status;",
|
|
1858
1872
|
"};",
|
|
1859
1873
|
"trap omx_detached_session_cleanup 0 INT TERM HUP;",
|
|
1874
|
+
parentEnvSource,
|
|
1860
1875
|
"omx_codex_started_at=$(date +%s 2>/dev/null || printf 0);",
|
|
1861
1876
|
`${codexCmd} <&3 &`,
|
|
1862
1877
|
"omx_codex_pid=$!;",
|
|
@@ -1997,6 +2012,34 @@ function buildTmuxExtendedKeysAcquireShellSnippet(cwd) {
|
|
|
1997
2012
|
function buildTmuxExtendedKeysReleaseShellSnippet(cwd) {
|
|
1998
2013
|
return `if [ -n "\${OMX_TMUX_EXTENDED_KEYS_LEASE:-}" ]; then ${buildTmuxExtendedKeysHelperCommand(cwd, "release")} "\${OMX_TMUX_EXTENDED_KEYS_LEASE}" >/dev/null 2>&1 || true; fi;`;
|
|
1999
2014
|
}
|
|
2015
|
+
const SHELL_ENV_NAME_PATTERN = /^[A-Za-z_][A-Za-z0-9_]*$/;
|
|
2016
|
+
export function serializeDetachedSessionParentEnv(env) {
|
|
2017
|
+
const lines = [];
|
|
2018
|
+
for (const key of Object.keys(env).sort()) {
|
|
2019
|
+
if (!SHELL_ENV_NAME_PATTERN.test(key))
|
|
2020
|
+
continue;
|
|
2021
|
+
const value = env[key];
|
|
2022
|
+
if (typeof value !== "string")
|
|
2023
|
+
continue;
|
|
2024
|
+
if (value.includes("\0"))
|
|
2025
|
+
continue;
|
|
2026
|
+
lines.push(`export ${key}=${quoteShellArg(value)}`);
|
|
2027
|
+
}
|
|
2028
|
+
return `${lines.join("\n")}\n`;
|
|
2029
|
+
}
|
|
2030
|
+
export function detachedSessionParentEnvFilePath(cwd, sessionId) {
|
|
2031
|
+
const safeSessionId = sessionId.replace(/[^A-Za-z0-9_.-]/g, "_");
|
|
2032
|
+
return join(omxRoot(cwd), "runtime", "tmux-env", `${safeSessionId}.env`);
|
|
2033
|
+
}
|
|
2034
|
+
export function writeDetachedSessionParentEnvFile(cwd, sessionId, env) {
|
|
2035
|
+
const filePath = detachedSessionParentEnvFilePath(cwd, sessionId);
|
|
2036
|
+
mkdirSync(dirname(filePath), { recursive: true, mode: 0o700 });
|
|
2037
|
+
writeFileSync(filePath, serializeDetachedSessionParentEnv(env), {
|
|
2038
|
+
encoding: "utf-8",
|
|
2039
|
+
mode: 0o600,
|
|
2040
|
+
});
|
|
2041
|
+
return filePath;
|
|
2042
|
+
}
|
|
2000
2043
|
export function withTmuxExtendedKeys(cwd, run, execFileSyncImpl = (file, tmuxArgs) => execFileSync(file, tmuxArgs, {
|
|
2001
2044
|
encoding: "utf-8",
|
|
2002
2045
|
...(process.platform === "win32" ? { windowsHide: true } : {}),
|
|
@@ -2010,10 +2053,10 @@ export function withTmuxExtendedKeys(cwd, run, execFileSyncImpl = (file, tmuxArg
|
|
|
2010
2053
|
releaseTmuxExtendedKeysLease(cwd, leaseHandle, execFileSyncImpl);
|
|
2011
2054
|
}
|
|
2012
2055
|
}
|
|
2013
|
-
export function buildDetachedSessionBootstrapSteps(sessionName, cwd, codexCmd, hudCmd, workerLaunchArgs, codexHomeOverride, notifyTempContractRaw, nativeWindows = false, sessionId, projectLocalCodexHomeForCleanup, runtimeCodexHomeForCleanup, omxRootOverride, env = process.env, sqliteHomeOverride) {
|
|
2056
|
+
export function buildDetachedSessionBootstrapSteps(sessionName, cwd, codexCmd, hudCmd, workerLaunchArgs, codexHomeOverride, notifyTempContractRaw, nativeWindows = false, sessionId, projectLocalCodexHomeForCleanup, runtimeCodexHomeForCleanup, omxRootOverride, env = process.env, sqliteHomeOverride, parentEnvFilePath) {
|
|
2014
2057
|
const detachedLeaderCmd = nativeWindows
|
|
2015
2058
|
? "powershell.exe"
|
|
2016
|
-
: buildDetachedSessionLeaderCommand(cwd, sessionName, codexCmd, sessionId, codexHomeOverride, projectLocalCodexHomeForCleanup, runtimeCodexHomeForCleanup);
|
|
2059
|
+
: buildDetachedSessionLeaderCommand(cwd, sessionName, codexCmd, sessionId, codexHomeOverride, projectLocalCodexHomeForCleanup, runtimeCodexHomeForCleanup, parentEnvFilePath);
|
|
2017
2060
|
const newSessionArgs = [
|
|
2018
2061
|
"new-session",
|
|
2019
2062
|
"-d",
|
|
@@ -2249,6 +2292,26 @@ async function readPostLaunchModeStateFile(path, dependencies = {}) {
|
|
|
2249
2292
|
function cleanPostLaunchString(value) {
|
|
2250
2293
|
return typeof value === "string" ? value.trim() : "";
|
|
2251
2294
|
}
|
|
2295
|
+
function isAutopilotReviewPendingPostLaunchState(state) {
|
|
2296
|
+
if (!state || state.active !== true)
|
|
2297
|
+
return false;
|
|
2298
|
+
const mode = cleanPostLaunchString(state.mode).toLowerCase();
|
|
2299
|
+
if (mode && mode !== "autopilot")
|
|
2300
|
+
return false;
|
|
2301
|
+
const phase = cleanPostLaunchString(state.current_phase ?? state.currentPhase)
|
|
2302
|
+
.toLowerCase()
|
|
2303
|
+
.replace(/_/g, "-");
|
|
2304
|
+
if (phase === "code-review" || phase === "review" || phase === "reviewing" || phase === "review-pending") {
|
|
2305
|
+
return true;
|
|
2306
|
+
}
|
|
2307
|
+
const nestedState = state.state && typeof state.state === "object"
|
|
2308
|
+
? state.state
|
|
2309
|
+
: {};
|
|
2310
|
+
return state.review_pending === true
|
|
2311
|
+
|| state.reviewPending === true
|
|
2312
|
+
|| nestedState.review_pending === true
|
|
2313
|
+
|| nestedState.reviewPending === true;
|
|
2314
|
+
}
|
|
2252
2315
|
function postLaunchUniqueStrings(values) {
|
|
2253
2316
|
return [...new Set(values.map((value) => value.trim()).filter(Boolean))];
|
|
2254
2317
|
}
|
|
@@ -2336,8 +2399,16 @@ export async function cleanupPostLaunchModeStateFiles(cwd, sessionId, dependenci
|
|
|
2336
2399
|
const rootSkillActiveStateBeforeCleanup = sessionId
|
|
2337
2400
|
? await readSkillActiveState(getSkillActiveStatePathsForStateDir(rootStateDir).rootPath)
|
|
2338
2401
|
: null;
|
|
2402
|
+
let preserveSkillActiveForReviewPendingAutopilot = false;
|
|
2339
2403
|
for (const stateDir of scopedDirs) {
|
|
2340
2404
|
const files = await readdir(stateDir).catch(() => []);
|
|
2405
|
+
const autopilotPath = join(stateDir, "autopilot-state.json");
|
|
2406
|
+
const autopilotPrecheck = files.includes("autopilot-state.json")
|
|
2407
|
+
? await readPostLaunchModeStateFile(autopilotPath, dependencies)
|
|
2408
|
+
: null;
|
|
2409
|
+
const preserveReviewPendingAutopilot = autopilotPrecheck?.kind === "ok"
|
|
2410
|
+
&& isAutopilotReviewPendingPostLaunchState(autopilotPrecheck.state);
|
|
2411
|
+
preserveSkillActiveForReviewPendingAutopilot ||= preserveReviewPendingAutopilot;
|
|
2341
2412
|
for (const file of files) {
|
|
2342
2413
|
if (!file.endsWith("-state.json") || file === "session.json")
|
|
2343
2414
|
continue;
|
|
@@ -2395,6 +2466,10 @@ export async function cleanupPostLaunchModeStateFiles(cwd, sessionId, dependenci
|
|
|
2395
2466
|
}
|
|
2396
2467
|
continue;
|
|
2397
2468
|
}
|
|
2469
|
+
if (preserveReviewPendingAutopilot
|
|
2470
|
+
&& (mode === "autopilot" || mode === SKILL_ACTIVE_STATE_MODE)) {
|
|
2471
|
+
continue;
|
|
2472
|
+
}
|
|
2398
2473
|
try {
|
|
2399
2474
|
const completedAt = now().toISOString();
|
|
2400
2475
|
if (mode === SKILL_ACTIVE_STATE_MODE) {
|
|
@@ -2433,7 +2508,9 @@ export async function cleanupPostLaunchModeStateFiles(cwd, sessionId, dependenci
|
|
|
2433
2508
|
}
|
|
2434
2509
|
if (sessionId) {
|
|
2435
2510
|
try {
|
|
2436
|
-
|
|
2511
|
+
if (!preserveSkillActiveForReviewPendingAutopilot) {
|
|
2512
|
+
await scrubPostLaunchRootSkillActiveForSession(rootStateDir, sessionId, now().toISOString(), writeFile, rootSkillActiveStateBeforeCleanup);
|
|
2513
|
+
}
|
|
2437
2514
|
}
|
|
2438
2515
|
catch (err) {
|
|
2439
2516
|
writeWarn(`[omx] postLaunch: failed to reconcile root skill-active state: ${err instanceof Error ? err.message : err}`);
|
|
@@ -2610,6 +2687,9 @@ function runCodex(cwd, args, sessionId, workerDefaultModel, codexHomeOverride, s
|
|
|
2610
2687
|
let hudPaneId = null;
|
|
2611
2688
|
try {
|
|
2612
2689
|
hudPaneId = createHudWatchPane(cwd, hudCmd);
|
|
2690
|
+
if (hudPaneId && currentPaneId) {
|
|
2691
|
+
registerHudResizeHook(hudPaneId, currentPaneId, HUD_TMUX_HEIGHT_LINES);
|
|
2692
|
+
}
|
|
2613
2693
|
}
|
|
2614
2694
|
catch (err) {
|
|
2615
2695
|
logCliOperationFailure(err);
|
|
@@ -2678,8 +2758,18 @@ function runCodex(cwd, args, sessionId, workerDefaultModel, codexHomeOverride, s
|
|
|
2678
2758
|
let registeredHookTarget = null;
|
|
2679
2759
|
let registeredHookName = null;
|
|
2680
2760
|
let registeredClientAttachedHookName = null;
|
|
2761
|
+
let detachedParentEnvFilePath;
|
|
2681
2762
|
try {
|
|
2682
|
-
|
|
2763
|
+
// This path is the user-shell interactive launch: OMX creates a tmux
|
|
2764
|
+
// session and immediately attaches the user's terminal to it. If a tmux
|
|
2765
|
+
// server already exists, `new-session -e` only forwards explicit values,
|
|
2766
|
+
// so provider-specific parent-shell keys would disappear. Source a
|
|
2767
|
+
// private env file inside the leader shell instead of putting every
|
|
2768
|
+
// parent env value on the tmux command line or in logs.
|
|
2769
|
+
if (!nativeWindows) {
|
|
2770
|
+
detachedParentEnvFilePath = writeDetachedSessionParentEnvFile(cwd, sessionId, codexEnvWithNotify);
|
|
2771
|
+
}
|
|
2772
|
+
const bootstrapSteps = buildDetachedSessionBootstrapSteps(sessionName, cwd, codexCmd, hudCmd, workerLaunchArgs, codexHomeOverride, notifyTempContractRaw, nativeWindows, sessionId, projectLocalCodexHomeForCleanup, runtimeCodexHomeForCleanup, omxRootOverride, process.env, sqliteHomeOverride, detachedParentEnvFilePath);
|
|
2683
2773
|
for (const step of bootstrapSteps) {
|
|
2684
2774
|
const output = execTmuxFileSync(step.args, {
|
|
2685
2775
|
stdio: "pipe",
|
|
@@ -2748,6 +2838,9 @@ function runCodex(cwd, args, sessionId, workerDefaultModel, codexHomeOverride, s
|
|
|
2748
2838
|
}
|
|
2749
2839
|
catch (err) {
|
|
2750
2840
|
logCliOperationFailure(err);
|
|
2841
|
+
if (detachedParentEnvFilePath) {
|
|
2842
|
+
rmSync(detachedParentEnvFilePath, { force: true });
|
|
2843
|
+
}
|
|
2751
2844
|
if (createdDetachedSession) {
|
|
2752
2845
|
const rollbackSteps = buildDetachedSessionRollbackSteps(sessionName, registeredHookTarget, registeredHookName, registeredClientAttachedHookName);
|
|
2753
2846
|
for (const rollbackStep of rollbackSteps) {
|
|
@@ -3117,6 +3210,22 @@ function parseWatcherPidFile(content) {
|
|
|
3117
3210
|
return Number.isFinite(pid) && pid > 0 ? pid : null;
|
|
3118
3211
|
}
|
|
3119
3212
|
}
|
|
3213
|
+
const DEFAULT_NOTIFY_FALLBACK_REAP_GRACE_MS = 5000;
|
|
3214
|
+
function resolveNotifyFallbackReapGraceMs(env = process.env) {
|
|
3215
|
+
const parsed = Number.parseInt(env.OMX_NOTIFY_FALLBACK_REAP_GRACE_MS || "", 10);
|
|
3216
|
+
if (Number.isFinite(parsed) && parsed >= 0)
|
|
3217
|
+
return parsed;
|
|
3218
|
+
return DEFAULT_NOTIFY_FALLBACK_REAP_GRACE_MS;
|
|
3219
|
+
}
|
|
3220
|
+
function isWatcherRecordWithinReapGrace(record, nowMs = Date.now(), graceMs = resolveNotifyFallbackReapGraceMs()) {
|
|
3221
|
+
if (graceMs <= 0 || !record.startedAt)
|
|
3222
|
+
return false;
|
|
3223
|
+
const startedMs = Date.parse(record.startedAt);
|
|
3224
|
+
if (!Number.isFinite(startedMs))
|
|
3225
|
+
return false;
|
|
3226
|
+
const ageMs = nowMs - startedMs;
|
|
3227
|
+
return ageMs >= 0 && ageMs < graceMs;
|
|
3228
|
+
}
|
|
3120
3229
|
function parseWatcherPidRecord(content) {
|
|
3121
3230
|
const trimmed = content.trim();
|
|
3122
3231
|
if (!trimmed)
|
|
@@ -3159,7 +3268,7 @@ function isLikelyOmxWatcherProcess(pid, execFileSyncFn = execFileSync, platform
|
|
|
3159
3268
|
export async function reapStaleNotifyFallbackWatcher(pidPath, deps = {}) {
|
|
3160
3269
|
const exists = deps.exists ?? existsSync;
|
|
3161
3270
|
if (!exists(pidPath))
|
|
3162
|
-
return;
|
|
3271
|
+
return "missing";
|
|
3163
3272
|
const { readFile } = await import("fs/promises");
|
|
3164
3273
|
const readFileImpl = deps.readFile ?? readFile;
|
|
3165
3274
|
const tryKillPidImpl = deps.tryKillPid ?? tryKillPid;
|
|
@@ -3168,9 +3277,15 @@ export async function reapStaleNotifyFallbackWatcher(pidPath, deps = {}) {
|
|
|
3168
3277
|
const isWatcherProcessImpl = deps.isWatcherProcess ?? isLikelyOmxWatcherProcess;
|
|
3169
3278
|
try {
|
|
3170
3279
|
const record = parseWatcherPidRecord(await readFileImpl(pidPath, "utf-8"));
|
|
3171
|
-
if (record
|
|
3172
|
-
|
|
3280
|
+
if (!record)
|
|
3281
|
+
return "invalid";
|
|
3282
|
+
if (!isWatcherProcessImpl(record.pid))
|
|
3283
|
+
return "identity_mismatch";
|
|
3284
|
+
if (isWatcherRecordWithinReapGrace(record, deps.nowMs?.() ?? Date.now(), deps.reapGraceMs ?? resolveNotifyFallbackReapGraceMs())) {
|
|
3285
|
+
return "recent_active";
|
|
3173
3286
|
}
|
|
3287
|
+
tryKillPidImpl(record.pid, "SIGTERM");
|
|
3288
|
+
return "reaped";
|
|
3174
3289
|
}
|
|
3175
3290
|
catch (error) {
|
|
3176
3291
|
if (!hasErrnoCodeImpl(error, "ESRCH")) {
|
|
@@ -3179,6 +3294,7 @@ export async function reapStaleNotifyFallbackWatcher(pidPath, deps = {}) {
|
|
|
3179
3294
|
error: error instanceof Error ? error.message : String(error),
|
|
3180
3295
|
});
|
|
3181
3296
|
}
|
|
3297
|
+
return "failed";
|
|
3182
3298
|
}
|
|
3183
3299
|
}
|
|
3184
3300
|
function tryKillPid(pid, signal = "SIGTERM") {
|
|
@@ -3196,7 +3312,9 @@ function tryKillPid(pid, signal = "SIGTERM") {
|
|
|
3196
3312
|
async function startNotifyFallbackWatcher(cwd, options = {}) {
|
|
3197
3313
|
const { mkdir, writeFile } = await import("fs/promises");
|
|
3198
3314
|
const pidPath = notifyFallbackPidPath(cwd);
|
|
3199
|
-
await reapStaleNotifyFallbackWatcher(pidPath);
|
|
3315
|
+
const reapResult = await reapStaleNotifyFallbackWatcher(pidPath);
|
|
3316
|
+
if (reapResult === "recent_active")
|
|
3317
|
+
return;
|
|
3200
3318
|
if (!shouldEnableNotifyFallbackWatcher(process.env, process.platform))
|
|
3201
3319
|
return;
|
|
3202
3320
|
const pkgRoot = getPackageRoot();
|