oh-my-codex 0.11.11 → 0.11.13
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/README.de.md +12 -6
- package/README.el.md +223 -0
- package/README.es.md +12 -6
- package/README.fr.md +11 -5
- package/README.it.md +12 -6
- package/README.ja.md +12 -6
- package/README.ko.md +12 -6
- package/README.md +56 -28
- package/README.pl.md +216 -0
- package/README.pt.md +12 -6
- package/README.ru.md +12 -6
- package/README.tr.md +12 -6
- package/README.vi.md +148 -183
- package/README.zh-TW.md +14 -17
- package/README.zh.md +12 -6
- package/crates/omx-runtime-core/src/engine.rs +122 -4
- package/crates/omx-runtime-core/src/lib.rs +17 -0
- package/dist/autoresearch/contracts.d.ts.map +1 -1
- package/dist/autoresearch/contracts.js +1 -0
- package/dist/autoresearch/contracts.js.map +1 -1
- package/dist/autoresearch/runtime.d.ts.map +1 -1
- package/dist/autoresearch/runtime.js +7 -1
- package/dist/autoresearch/runtime.js.map +1 -1
- package/dist/cli/__tests__/agents.test.js +24 -1
- package/dist/cli/__tests__/agents.test.js.map +1 -1
- package/dist/cli/__tests__/autoresearch.test.js +11 -0
- package/dist/cli/__tests__/autoresearch.test.js.map +1 -1
- package/dist/cli/__tests__/cleanup.test.js +117 -4
- package/dist/cli/__tests__/cleanup.test.js.map +1 -1
- package/dist/cli/__tests__/doctor-warning-copy.test.js +33 -3
- package/dist/cli/__tests__/doctor-warning-copy.test.js.map +1 -1
- package/dist/cli/__tests__/error-handling-warnings.test.js +13 -0
- package/dist/cli/__tests__/error-handling-warnings.test.js.map +1 -1
- package/dist/cli/__tests__/exec.test.js +6 -0
- package/dist/cli/__tests__/exec.test.js.map +1 -1
- package/dist/cli/__tests__/index.test.js +101 -1
- package/dist/cli/__tests__/index.test.js.map +1 -1
- package/dist/cli/__tests__/launch-fallback.test.js +3 -0
- package/dist/cli/__tests__/launch-fallback.test.js.map +1 -1
- package/dist/cli/__tests__/package-bin-contract.test.js +10 -0
- package/dist/cli/__tests__/package-bin-contract.test.js.map +1 -1
- package/dist/cli/__tests__/packaged-script-resolution.test.js +4 -3
- package/dist/cli/__tests__/packaged-script-resolution.test.js.map +1 -1
- package/dist/cli/__tests__/resume.test.js +6 -0
- package/dist/cli/__tests__/resume.test.js.map +1 -1
- package/dist/cli/__tests__/setup-refresh.test.js +29 -12
- package/dist/cli/__tests__/setup-refresh.test.js.map +1 -1
- package/dist/cli/__tests__/setup-scope.test.js +1 -1
- package/dist/cli/__tests__/setup-scope.test.js.map +1 -1
- package/dist/cli/__tests__/star-prompt.test.js +16 -0
- package/dist/cli/__tests__/star-prompt.test.js.map +1 -1
- package/dist/cli/__tests__/uninstall.test.js +112 -1
- package/dist/cli/__tests__/uninstall.test.js.map +1 -1
- package/dist/cli/__tests__/windows-popup-loop-contract.test.d.ts +2 -0
- package/dist/cli/__tests__/windows-popup-loop-contract.test.d.ts.map +1 -0
- package/dist/cli/__tests__/windows-popup-loop-contract.test.js +30 -0
- package/dist/cli/__tests__/windows-popup-loop-contract.test.js.map +1 -0
- package/dist/cli/agents.d.ts.map +1 -1
- package/dist/cli/agents.js +9 -3
- package/dist/cli/agents.js.map +1 -1
- package/dist/cli/autoresearch-guided.d.ts.map +1 -1
- package/dist/cli/autoresearch-guided.js +9 -3
- package/dist/cli/autoresearch-guided.js.map +1 -1
- package/dist/cli/autoresearch.d.ts.map +1 -1
- package/dist/cli/autoresearch.js +8 -2
- package/dist/cli/autoresearch.js.map +1 -1
- package/dist/cli/cleanup.d.ts +2 -0
- package/dist/cli/cleanup.d.ts.map +1 -1
- package/dist/cli/cleanup.js +27 -1
- package/dist/cli/cleanup.js.map +1 -1
- package/dist/cli/doctor.js +7 -0
- package/dist/cli/doctor.js.map +1 -1
- package/dist/cli/index.d.ts +9 -1
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +171 -55
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/setup.d.ts.map +1 -1
- package/dist/cli/setup.js +18 -15
- package/dist/cli/setup.js.map +1 -1
- package/dist/cli/star-prompt.d.ts.map +1 -1
- package/dist/cli/star-prompt.js +2 -0
- package/dist/cli/star-prompt.js.map +1 -1
- package/dist/cli/team.d.ts.map +1 -1
- package/dist/cli/team.js +5 -1
- package/dist/cli/team.js.map +1 -1
- package/dist/cli/tmux-hook.d.ts.map +1 -1
- package/dist/cli/tmux-hook.js +4 -1
- package/dist/cli/tmux-hook.js.map +1 -1
- package/dist/cli/uninstall.d.ts.map +1 -1
- package/dist/cli/uninstall.js +26 -0
- package/dist/cli/uninstall.js.map +1 -1
- package/dist/cli/update.d.ts.map +1 -1
- package/dist/cli/update.js +1 -0
- package/dist/cli/update.js.map +1 -1
- package/dist/compat/__tests__/rust-runtime-compat.test.js +84 -1
- package/dist/compat/__tests__/rust-runtime-compat.test.js.map +1 -1
- package/dist/config/__tests__/generator-idempotent.test.js +4 -4
- package/dist/config/__tests__/generator-idempotent.test.js.map +1 -1
- package/dist/config/__tests__/mcp-registry.test.js +13 -16
- package/dist/config/__tests__/mcp-registry.test.js.map +1 -1
- package/dist/config/mcp-registry.d.ts +1 -0
- package/dist/config/mcp-registry.d.ts.map +1 -1
- package/dist/config/mcp-registry.js +4 -4
- package/dist/config/mcp-registry.js.map +1 -1
- package/dist/config/models.d.ts +1 -0
- package/dist/config/models.d.ts.map +1 -1
- package/dist/config/models.js +39 -1
- package/dist/config/models.js.map +1 -1
- package/dist/hooks/__tests__/keyword-detector.test.js +12 -1
- package/dist/hooks/__tests__/keyword-detector.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-fallback-watcher.test.js +554 -18
- package/dist/hooks/__tests__/notify-fallback-watcher.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-hook-auto-nudge.test.js +347 -16
- package/dist/hooks/__tests__/notify-hook-auto-nudge.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-hook-modules.test.js +5 -0
- package/dist/hooks/__tests__/notify-hook-modules.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-hook-ralph-resume.test.d.ts +2 -0
- package/dist/hooks/__tests__/notify-hook-ralph-resume.test.d.ts.map +1 -0
- package/dist/hooks/__tests__/notify-hook-ralph-resume.test.js +597 -0
- package/dist/hooks/__tests__/notify-hook-ralph-resume.test.js.map +1 -0
- package/dist/hooks/__tests__/notify-hook-regression-205.test.js +19 -1
- package/dist/hooks/__tests__/notify-hook-regression-205.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-hook-session-scope.test.js +73 -53
- package/dist/hooks/__tests__/notify-hook-session-scope.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-hook-team-dispatch.test.js +193 -2
- package/dist/hooks/__tests__/notify-hook-team-dispatch.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-hook-team-leader-nudge.test.js +183 -0
- package/dist/hooks/__tests__/notify-hook-team-leader-nudge.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-hook-tmux-heal.test.js +255 -97
- package/dist/hooks/__tests__/notify-hook-tmux-heal.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-hook-tmux-scrollback.test.js +0 -0
- package/dist/hooks/__tests__/notify-hook-tmux-scrollback.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-hook-worker-idle.test.js +46 -0
- package/dist/hooks/__tests__/notify-hook-worker-idle.test.js.map +1 -1
- package/dist/hooks/__tests__/prompt-team-routing.test.js +34 -0
- package/dist/hooks/__tests__/prompt-team-routing.test.js.map +1 -1
- package/dist/hooks/__tests__/tmux-hook-engine.test.js +32 -1
- package/dist/hooks/__tests__/tmux-hook-engine.test.js.map +1 -1
- package/dist/hooks/code-simplifier/index.d.ts.map +1 -1
- package/dist/hooks/code-simplifier/index.js +1 -0
- package/dist/hooks/code-simplifier/index.js.map +1 -1
- package/dist/hooks/codebase-map.d.ts.map +1 -1
- package/dist/hooks/codebase-map.js +1 -0
- package/dist/hooks/codebase-map.js.map +1 -1
- package/dist/hooks/extensibility/sdk/tmux.d.ts.map +1 -1
- package/dist/hooks/extensibility/sdk/tmux.js +3 -1
- package/dist/hooks/extensibility/sdk/tmux.js.map +1 -1
- package/dist/hooks/keyword-detector.d.ts +1 -0
- package/dist/hooks/keyword-detector.d.ts.map +1 -1
- package/dist/hooks/keyword-detector.js +48 -0
- 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.map +1 -1
- package/dist/hooks/session.js +1 -0
- package/dist/hooks/session.js.map +1 -1
- package/dist/hud/__tests__/state.test.js +70 -1
- package/dist/hud/__tests__/state.test.js.map +1 -1
- package/dist/hud/authority.d.ts.map +1 -1
- package/dist/hud/authority.js +1 -0
- package/dist/hud/authority.js.map +1 -1
- package/dist/hud/state.d.ts.map +1 -1
- package/dist/hud/state.js +52 -0
- package/dist/hud/state.js.map +1 -1
- package/dist/mcp/state-server.d.ts.map +1 -1
- package/dist/mcp/state-server.js +5 -0
- package/dist/mcp/state-server.js.map +1 -1
- package/dist/modes/__tests__/base-session-scope.test.js +46 -0
- package/dist/modes/__tests__/base-session-scope.test.js.map +1 -1
- package/dist/modes/base.d.ts.map +1 -1
- package/dist/modes/base.js +4 -0
- package/dist/modes/base.js.map +1 -1
- package/dist/notifications/__tests__/custom-alias-enablement.test.d.ts +2 -0
- package/dist/notifications/__tests__/custom-alias-enablement.test.d.ts.map +1 -0
- package/dist/notifications/__tests__/custom-alias-enablement.test.js +84 -0
- package/dist/notifications/__tests__/custom-alias-enablement.test.js.map +1 -0
- package/dist/notifications/__tests__/idle-cooldown.test.js +55 -0
- package/dist/notifications/__tests__/idle-cooldown.test.js.map +1 -1
- package/dist/notifications/idle-cooldown.d.ts +8 -6
- package/dist/notifications/idle-cooldown.d.ts.map +1 -1
- package/dist/notifications/idle-cooldown.js +53 -22
- package/dist/notifications/idle-cooldown.js.map +1 -1
- package/dist/notifications/notifier.js +1 -1
- package/dist/notifications/notifier.js.map +1 -1
- package/dist/notifications/reply-listener.d.ts.map +1 -1
- package/dist/notifications/reply-listener.js +1 -0
- package/dist/notifications/reply-listener.js.map +1 -1
- package/dist/notifications/tmux.d.ts.map +1 -1
- package/dist/notifications/tmux.js +4 -0
- package/dist/notifications/tmux.js.map +1 -1
- package/dist/openclaw/config.js +2 -2
- package/dist/openclaw/config.js.map +1 -1
- package/dist/runtime/bridge.d.ts +2 -0
- package/dist/runtime/bridge.d.ts.map +1 -1
- package/dist/runtime/bridge.js +8 -0
- package/dist/runtime/bridge.js.map +1 -1
- package/dist/scripts/notify-fallback-watcher.js +103 -53
- package/dist/scripts/notify-fallback-watcher.js.map +1 -1
- package/dist/scripts/notify-hook/auto-nudge.d.ts +2 -1
- package/dist/scripts/notify-hook/auto-nudge.d.ts.map +1 -1
- package/dist/scripts/notify-hook/auto-nudge.js +90 -104
- package/dist/scripts/notify-hook/auto-nudge.js.map +1 -1
- package/dist/scripts/notify-hook/managed-tmux.d.ts +19 -0
- package/dist/scripts/notify-hook/managed-tmux.d.ts.map +1 -0
- package/dist/scripts/notify-hook/managed-tmux.js +320 -0
- package/dist/scripts/notify-hook/managed-tmux.js.map +1 -0
- package/dist/scripts/notify-hook/operational-events.d.ts.map +1 -1
- package/dist/scripts/notify-hook/operational-events.js +2 -0
- package/dist/scripts/notify-hook/operational-events.js.map +1 -1
- package/dist/scripts/notify-hook/ralph-session-resume.d.ts +22 -0
- package/dist/scripts/notify-hook/ralph-session-resume.d.ts.map +1 -0
- package/dist/scripts/notify-hook/ralph-session-resume.js +277 -0
- package/dist/scripts/notify-hook/ralph-session-resume.js.map +1 -0
- package/dist/scripts/notify-hook/state-io.d.ts +1 -1
- package/dist/scripts/notify-hook/state-io.d.ts.map +1 -1
- package/dist/scripts/notify-hook/state-io.js +2 -10
- package/dist/scripts/notify-hook/state-io.js.map +1 -1
- package/dist/scripts/notify-hook/team-dispatch.d.ts +1 -1
- package/dist/scripts/notify-hook/team-dispatch.d.ts.map +1 -1
- package/dist/scripts/notify-hook/team-dispatch.js +123 -72
- package/dist/scripts/notify-hook/team-dispatch.js.map +1 -1
- package/dist/scripts/notify-hook/team-leader-nudge.d.ts +2 -1
- package/dist/scripts/notify-hook/team-leader-nudge.d.ts.map +1 -1
- package/dist/scripts/notify-hook/team-leader-nudge.js +13 -5
- package/dist/scripts/notify-hook/team-leader-nudge.js.map +1 -1
- package/dist/scripts/notify-hook/team-tmux-guard.d.ts.map +1 -1
- package/dist/scripts/notify-hook/team-tmux-guard.js +1 -19
- package/dist/scripts/notify-hook/team-tmux-guard.js.map +1 -1
- package/dist/scripts/notify-hook/team-worker.js +4 -4
- package/dist/scripts/notify-hook/team-worker.js.map +1 -1
- package/dist/scripts/notify-hook/tmux-injection.d.ts +1 -1
- package/dist/scripts/notify-hook/tmux-injection.d.ts.map +1 -1
- package/dist/scripts/notify-hook/tmux-injection.js +102 -35
- package/dist/scripts/notify-hook/tmux-injection.js.map +1 -1
- package/dist/scripts/notify-hook.js +144 -20
- package/dist/scripts/notify-hook.js.map +1 -1
- package/dist/scripts/run-provider-advisor.js +2 -0
- package/dist/scripts/run-provider-advisor.js.map +1 -1
- package/dist/scripts/run-test-files.d.ts +2 -0
- package/dist/scripts/run-test-files.d.ts.map +1 -0
- package/dist/scripts/run-test-files.js +41 -0
- package/dist/scripts/run-test-files.js.map +1 -0
- package/dist/scripts/tmux-hook-engine.d.ts +2 -0
- package/dist/scripts/tmux-hook-engine.d.ts.map +1 -1
- package/dist/scripts/tmux-hook-engine.js +15 -0
- package/dist/scripts/tmux-hook-engine.js.map +1 -1
- package/dist/team/__tests__/api-interop.test.js +136 -4
- package/dist/team/__tests__/api-interop.test.js.map +1 -1
- package/dist/team/__tests__/leader-activity.test.js +107 -2
- package/dist/team/__tests__/leader-activity.test.js.map +1 -1
- package/dist/team/__tests__/runtime-cli.test.js +32 -0
- package/dist/team/__tests__/runtime-cli.test.js.map +1 -1
- package/dist/team/__tests__/runtime.test.js +148 -0
- package/dist/team/__tests__/runtime.test.js.map +1 -1
- package/dist/team/__tests__/shutdown-fallback.test.js +13 -0
- package/dist/team/__tests__/shutdown-fallback.test.js.map +1 -1
- package/dist/team/__tests__/state-root.test.js +11 -1
- package/dist/team/__tests__/state-root.test.js.map +1 -1
- package/dist/team/__tests__/state.test.js +237 -0
- package/dist/team/__tests__/state.test.js.map +1 -1
- package/dist/team/__tests__/tmux-session.test.js +521 -2
- package/dist/team/__tests__/tmux-session.test.js.map +1 -1
- package/dist/team/api-interop.d.ts.map +1 -1
- package/dist/team/api-interop.js +41 -31
- package/dist/team/api-interop.js.map +1 -1
- package/dist/team/commit-hygiene.d.ts +60 -0
- package/dist/team/commit-hygiene.d.ts.map +1 -0
- package/dist/team/commit-hygiene.js +232 -0
- package/dist/team/commit-hygiene.js.map +1 -0
- package/dist/team/leader-activity.d.ts.map +1 -1
- package/dist/team/leader-activity.js +56 -4
- package/dist/team/leader-activity.js.map +1 -1
- package/dist/team/runtime-cli.d.ts +9 -1
- package/dist/team/runtime-cli.d.ts.map +1 -1
- package/dist/team/runtime-cli.js +15 -6
- package/dist/team/runtime-cli.js.map +1 -1
- package/dist/team/runtime.d.ts +7 -2
- package/dist/team/runtime.d.ts.map +1 -1
- package/dist/team/runtime.js +392 -171
- package/dist/team/runtime.js.map +1 -1
- package/dist/team/scaling.d.ts.map +1 -1
- package/dist/team/scaling.js +6 -2
- package/dist/team/scaling.js.map +1 -1
- package/dist/team/state/dispatch.d.ts +2 -0
- package/dist/team/state/dispatch.d.ts.map +1 -1
- package/dist/team/state/dispatch.js +86 -40
- package/dist/team/state/dispatch.js.map +1 -1
- package/dist/team/state/mailbox.d.ts +3 -0
- package/dist/team/state/mailbox.d.ts.map +1 -1
- package/dist/team/state/mailbox.js +93 -19
- package/dist/team/state/mailbox.js.map +1 -1
- package/dist/team/state-root.d.ts +1 -1
- package/dist/team/state-root.d.ts.map +1 -1
- package/dist/team/state-root.js +8 -3
- package/dist/team/state-root.js.map +1 -1
- package/dist/team/state.d.ts.map +1 -1
- package/dist/team/state.js +96 -2
- package/dist/team/state.js.map +1 -1
- package/dist/team/tmux-session.d.ts.map +1 -1
- package/dist/team/tmux-session.js +81 -29
- package/dist/team/tmux-session.js.map +1 -1
- package/dist/team/worker-bootstrap.d.ts.map +1 -1
- package/dist/team/worker-bootstrap.js +4 -0
- package/dist/team/worker-bootstrap.js.map +1 -1
- package/dist/team/worktree.d.ts.map +1 -1
- package/dist/team/worktree.js +9 -0
- package/dist/team/worktree.js.map +1 -1
- package/dist/utils/__tests__/paths.test.js +98 -11
- package/dist/utils/__tests__/paths.test.js.map +1 -1
- package/dist/utils/__tests__/platform-command.test.js +101 -2
- package/dist/utils/__tests__/platform-command.test.js.map +1 -1
- package/dist/utils/git-layout.d.ts +8 -0
- package/dist/utils/git-layout.d.ts.map +1 -0
- package/dist/utils/git-layout.js +58 -0
- package/dist/utils/git-layout.js.map +1 -0
- package/dist/utils/paths.d.ts +3 -0
- package/dist/utils/paths.d.ts.map +1 -1
- package/dist/utils/paths.js +14 -4
- package/dist/utils/paths.js.map +1 -1
- package/dist/utils/platform-command.d.ts.map +1 -1
- package/dist/utils/platform-command.js +35 -3
- package/dist/utils/platform-command.js.map +1 -1
- package/package.json +9 -5
- package/src/scripts/notify-fallback-watcher.ts +103 -53
- package/src/scripts/notify-hook/auto-nudge.ts +97 -103
- package/src/scripts/notify-hook/managed-tmux.ts +324 -0
- package/src/scripts/notify-hook/operational-events.ts +2 -0
- package/src/scripts/notify-hook/ralph-session-resume.ts +337 -0
- package/src/scripts/notify-hook/state-io.ts +2 -10
- package/src/scripts/notify-hook/team-dispatch.ts +131 -66
- package/src/scripts/notify-hook/team-leader-nudge.ts +19 -5
- package/src/scripts/notify-hook/team-tmux-guard.ts +0 -20
- package/src/scripts/notify-hook/team-worker.ts +4 -4
- package/src/scripts/notify-hook/tmux-injection.ts +103 -33
- package/src/scripts/notify-hook.ts +150 -21
- package/src/scripts/run-provider-advisor.ts +4 -2
- package/src/scripts/run-test-files.ts +48 -0
- package/src/scripts/tmux-hook-engine.ts +16 -0
- package/templates/AGENTS.md +51 -43
|
@@ -5,7 +5,6 @@
|
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
import { readFile, writeFile } from 'fs/promises';
|
|
8
|
-
import { execFileSync } from 'child_process';
|
|
9
8
|
import { join } from 'path';
|
|
10
9
|
import { homedir } from 'os';
|
|
11
10
|
import { asNumber, safeString } from './utils.js';
|
|
@@ -13,7 +12,15 @@ import { readJsonIfExists, getScopedStateDirsForCurrentSession, readdir } from '
|
|
|
13
12
|
import { runProcess } from './process-runner.js';
|
|
14
13
|
import { logTmuxHookEvent } from './log.js';
|
|
15
14
|
import { evaluatePaneInjectionReadiness, mapPaneInjectionReadinessReason, sendPaneInput } from './team-tmux-guard.js';
|
|
16
|
-
import { buildCapturePaneArgv, DEFAULT_MARKER } from '../tmux-hook-engine.js';
|
|
15
|
+
import { buildCapturePaneArgv, DEFAULT_MARKER, tmuxHookExplicitlyDisablesInjection } from '../tmux-hook-engine.js';
|
|
16
|
+
import {
|
|
17
|
+
isManagedOmxSession,
|
|
18
|
+
resolveManagedCurrentPane,
|
|
19
|
+
resolveManagedPaneFromAnchor,
|
|
20
|
+
resolveManagedSessionPane,
|
|
21
|
+
resolveInvocationSessionId,
|
|
22
|
+
verifyManagedPaneTarget,
|
|
23
|
+
} from './managed-tmux.js';
|
|
17
24
|
|
|
18
25
|
export const SKILL_ACTIVE_STATE_FILE = 'skill-active-state.json';
|
|
19
26
|
export const DEEP_INTERVIEW_BLOCKED_APPROVAL_INPUTS = ['yes', 'y', 'proceed', 'continue', 'ok', 'sure', 'go ahead', 'next i should'];
|
|
@@ -53,22 +60,29 @@ export function normalizeBlockedAutoApprovalInput(text) {
|
|
|
53
60
|
.trim();
|
|
54
61
|
}
|
|
55
62
|
|
|
63
|
+
function buildBlockedAutoApprovalMatcher(blockedInputs) {
|
|
64
|
+
const normalizedBlockedInputs = blockedInputs.map((entry) => normalizeBlockedAutoApprovalInput(entry)).filter(Boolean);
|
|
65
|
+
return {
|
|
66
|
+
exactMatches: new Set(normalizedBlockedInputs),
|
|
67
|
+
prefixedMatches: normalizedBlockedInputs.filter((entry) => DEEP_INTERVIEW_BLOCKED_APPROVAL_PREFIXES.has(entry)),
|
|
68
|
+
blockedTokenSet: new Set(normalizedBlockedInputs.flatMap((entry) => entry.split(/\s+/).filter(Boolean))),
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
|
|
56
72
|
export function isBlockedAutoApprovalInput(text, blockedInputs = DEEP_INTERVIEW_BLOCKED_APPROVAL_INPUTS) {
|
|
57
73
|
const normalized = normalizeBlockedAutoApprovalInput(text);
|
|
58
74
|
if (!normalized) return false;
|
|
59
|
-
|
|
60
|
-
if (
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
.some((prefix) => normalized.startsWith(`${prefix} `))
|
|
65
|
-
) return true;
|
|
75
|
+
const normalizedBlockedInputs = blockedInputs.map((entry) => normalizeBlockedAutoApprovalInput(entry)).filter(Boolean);
|
|
76
|
+
if (normalizedBlockedInputs.includes(normalized)) return true;
|
|
77
|
+
|
|
78
|
+
const blockedPrefixes = normalizedBlockedInputs.filter((entry) => DEEP_INTERVIEW_BLOCKED_APPROVAL_PREFIXES.has(entry));
|
|
79
|
+
if (blockedPrefixes.some((prefix) => normalized.startsWith(`${prefix} `))) return true;
|
|
66
80
|
|
|
67
81
|
const tokens = normalized.split(/\s+/).filter(Boolean);
|
|
68
82
|
if (tokens.length === 0) return false;
|
|
69
83
|
|
|
70
84
|
const blockedTokenSet = new Set(
|
|
71
|
-
|
|
85
|
+
normalizedBlockedInputs.flatMap((entry) => entry.split(/\s+/).filter(Boolean)),
|
|
72
86
|
);
|
|
73
87
|
return tokens.every((token) => blockedTokenSet.has(token));
|
|
74
88
|
}
|
|
@@ -177,6 +191,11 @@ export async function isDeepInterviewStateActive(stateDir) {
|
|
|
177
191
|
return Boolean(modeState && modeState.active === true);
|
|
178
192
|
}
|
|
179
193
|
|
|
194
|
+
export async function isDeepInterviewInputLockActive(stateDir) {
|
|
195
|
+
const skillState = await loadSkillActiveState(stateDir);
|
|
196
|
+
return isDeepInterviewAutoApprovalLocked(skillState);
|
|
197
|
+
}
|
|
198
|
+
|
|
180
199
|
export async function resolveAutoNudgeSignature(stateDir, payload, lastMessage = '') {
|
|
181
200
|
const normalizedMessage = normalizeAutoNudgeSignatureText(lastMessage);
|
|
182
201
|
const hudState = await readJsonIfExists(join(stateDir, 'hud-state.json'), null);
|
|
@@ -352,6 +371,13 @@ export async function loadAutoNudgeConfig() {
|
|
|
352
371
|
return normalizeAutoNudgeConfig(raw.autoNudge);
|
|
353
372
|
}
|
|
354
373
|
|
|
374
|
+
async function localTmuxInjectionDisabled(cwd) {
|
|
375
|
+
const normalizedCwd = safeString(cwd).trim();
|
|
376
|
+
if (!normalizedCwd) return false;
|
|
377
|
+
const raw = await readJsonIfExists(join(normalizedCwd, '.omx', 'tmux-hook.json'), null);
|
|
378
|
+
return tmuxHookExplicitlyDisablesInjection(raw);
|
|
379
|
+
}
|
|
380
|
+
|
|
355
381
|
export function detectStallPattern(text, patterns) {
|
|
356
382
|
if (!text || typeof text !== 'string') return false;
|
|
357
383
|
const normalized = normalizeStallDetectionText(text);
|
|
@@ -373,105 +399,59 @@ export async function capturePane(paneId, lines = 10) {
|
|
|
373
399
|
}
|
|
374
400
|
}
|
|
375
401
|
|
|
376
|
-
function
|
|
377
|
-
const
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
.
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
// Fall back to empty when tmux scan is unavailable.
|
|
400
|
-
}
|
|
401
|
-
|
|
402
|
-
return '';
|
|
403
|
-
}
|
|
404
|
-
|
|
405
|
-
async function resolveCodexPaneFromAnchor(anchorPane) {
|
|
406
|
-
const paneId = safeString(anchorPane).trim();
|
|
407
|
-
if (!paneId) return '';
|
|
408
|
-
|
|
409
|
-
try {
|
|
410
|
-
const sessionResult = await runProcess('tmux', ['display-message', '-t', paneId, '-p', '#S'], 2000);
|
|
411
|
-
const sessionName = safeString(sessionResult.stdout).trim();
|
|
412
|
-
if (!sessionName) return '';
|
|
413
|
-
|
|
414
|
-
const panesResult = await runProcess(
|
|
415
|
-
'tmux',
|
|
416
|
-
['list-panes', '-s', '-t', sessionName, '-F', '#{pane_id}\t#{pane_current_command}\t#{pane_start_command}'],
|
|
417
|
-
2000,
|
|
418
|
-
);
|
|
419
|
-
const panes = safeString(panesResult.stdout).trim().split('\n').filter(Boolean);
|
|
420
|
-
for (const line of panes) {
|
|
421
|
-
const [candidatePaneId, , rawStartCommand = ''] = line.split('\t');
|
|
422
|
-
const startCommand = safeString(rawStartCommand).toLowerCase();
|
|
423
|
-
if (!candidatePaneId) continue;
|
|
424
|
-
if (/\bomx\b.*\bhud\b.*--watch/i.test(startCommand)) continue;
|
|
425
|
-
if (startCommand.includes('codex')) return candidatePaneId;
|
|
426
|
-
}
|
|
427
|
-
} catch {
|
|
428
|
-
// Fall back to the anchored pane when session scanning is unavailable.
|
|
429
|
-
}
|
|
430
|
-
|
|
431
|
-
return '';
|
|
432
|
-
}
|
|
433
|
-
|
|
434
|
-
export async function resolveNudgePaneTarget(stateDir: any, cwd = '') {
|
|
435
|
-
// Use canonical codex pane resolver — validates pane is running an agent, not a shell
|
|
436
|
-
const { resolveCodexPane } = await import('../tmux-hook-engine.js');
|
|
437
|
-
const codexPane = resolveCodexPane();
|
|
438
|
-
if (codexPane) return codexPane;
|
|
439
|
-
|
|
440
|
-
let fallbackPane = '';
|
|
441
|
-
|
|
442
|
-
try {
|
|
443
|
-
const scopedDirs = await getScopedStateDirsForCurrentSession(stateDir);
|
|
444
|
-
for (const dir of scopedDirs) {
|
|
445
|
-
const files = await readdir(dir).catch(() => []);
|
|
446
|
-
for (const f of files) {
|
|
447
|
-
if (!f.endsWith('-state.json')) continue;
|
|
448
|
-
const path = join(dir, f);
|
|
449
|
-
try {
|
|
450
|
-
const state = JSON.parse(await readFile(path, 'utf-8'));
|
|
451
|
-
if (state && state.active && state.tmux_pane_id) {
|
|
452
|
-
const anchoredPane = safeString(state.tmux_pane_id).trim();
|
|
453
|
-
if (!anchoredPane) continue;
|
|
454
|
-
const upgradedPane = await resolveCodexPaneFromAnchor(anchoredPane);
|
|
455
|
-
if (upgradedPane) return upgradedPane;
|
|
456
|
-
if (!fallbackPane) fallbackPane = anchoredPane;
|
|
457
|
-
}
|
|
458
|
-
} catch {
|
|
459
|
-
// skip malformed state
|
|
460
|
-
}
|
|
402
|
+
export async function resolveNudgePaneTarget(stateDir: any, cwd = '', payload: any = undefined) {
|
|
403
|
+
const allowTeamWorker = safeString(process.env.OMX_TEAM_WORKER || '').trim() !== '';
|
|
404
|
+
const managedCurrentPane = await resolveManagedCurrentPane(cwd, payload, { allowTeamWorker });
|
|
405
|
+
if (managedCurrentPane) return managedCurrentPane;
|
|
406
|
+
|
|
407
|
+
const invocationSessionId = resolveInvocationSessionId(payload);
|
|
408
|
+
const scopedDirs = await getScopedStateDirsForCurrentSession(stateDir, invocationSessionId).catch(() => []);
|
|
409
|
+
for (const dir of scopedDirs) {
|
|
410
|
+
const files = await readdir(dir).catch(() => []);
|
|
411
|
+
for (const f of files) {
|
|
412
|
+
if (!f.endsWith('-state.json')) continue;
|
|
413
|
+
const path = join(dir, f);
|
|
414
|
+
try {
|
|
415
|
+
const state = JSON.parse(await readFile(path, 'utf-8'));
|
|
416
|
+
if (!state || !state.active || !state.tmux_pane_id) continue;
|
|
417
|
+
const anchoredPane = safeString(state.tmux_pane_id).trim();
|
|
418
|
+
if (!anchoredPane) continue;
|
|
419
|
+
const managedPane = await resolveManagedPaneFromAnchor(anchoredPane, cwd, payload, { allowTeamWorker });
|
|
420
|
+
if (managedPane) return managedPane;
|
|
421
|
+
const verdict = await verifyManagedPaneTarget(anchoredPane, cwd, payload, { allowTeamWorker });
|
|
422
|
+
if (verdict.ok) return anchoredPane;
|
|
423
|
+
} catch {
|
|
424
|
+
// skip malformed state
|
|
461
425
|
}
|
|
462
426
|
}
|
|
463
|
-
} catch {
|
|
464
|
-
// Non-critical
|
|
465
427
|
}
|
|
466
428
|
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
return resolveCodexPaneByCwdFallback(cwd);
|
|
429
|
+
return await resolveManagedSessionPane(cwd, payload);
|
|
470
430
|
}
|
|
471
431
|
|
|
472
432
|
export async function maybeAutoNudge({ cwd, stateDir, logsDir, payload }) {
|
|
473
433
|
const config = await loadAutoNudgeConfig();
|
|
474
434
|
if (!config.enabled) return;
|
|
435
|
+
if (await localTmuxInjectionDisabled(cwd)) {
|
|
436
|
+
await logTmuxHookEvent(logsDir, {
|
|
437
|
+
timestamp: new Date().toISOString(),
|
|
438
|
+
type: 'auto_nudge_skipped',
|
|
439
|
+
reason: 'tmux_hook_disabled',
|
|
440
|
+
}).catch(() => {});
|
|
441
|
+
return;
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
const sourceName = safeString(payload?.source || '');
|
|
445
|
+
const managedSession = await isManagedOmxSession(cwd, payload, { allowTeamWorker: true });
|
|
446
|
+
if (!managedSession) {
|
|
447
|
+
if (sourceName === 'notify-fallback-watcher-stall') return;
|
|
448
|
+
await logTmuxHookEvent(logsDir, {
|
|
449
|
+
timestamp: new Date().toISOString(),
|
|
450
|
+
type: 'auto_nudge_skipped',
|
|
451
|
+
reason: 'unmanaged_session',
|
|
452
|
+
}).catch(() => {});
|
|
453
|
+
return;
|
|
454
|
+
}
|
|
475
455
|
|
|
476
456
|
const lastMessage = safeString(payload['last-assistant-message'] || payload.last_assistant_message || '');
|
|
477
457
|
const latestUserInput = latestUserInputFromPayload(payload);
|
|
@@ -493,7 +473,7 @@ export async function maybeAutoNudge({ cwd, stateDir, logsDir, payload }) {
|
|
|
493
473
|
if (!nudgeState || typeof nudgeState !== 'object') {
|
|
494
474
|
nudgeState = { nudgeCount: 0, lastNudgeAt: '', lastSignature: '', lastSemanticSignature: '' };
|
|
495
475
|
}
|
|
496
|
-
const paneId = await resolveNudgePaneTarget(stateDir, cwd);
|
|
476
|
+
const paneId = await resolveNudgePaneTarget(stateDir, cwd, payload);
|
|
497
477
|
|
|
498
478
|
let detected = detectStallPattern(lastMessage, config.patterns);
|
|
499
479
|
let source = 'payload';
|
|
@@ -512,6 +492,18 @@ export async function maybeAutoNudge({ cwd, stateDir, logsDir, payload }) {
|
|
|
512
492
|
const signature = await resolveAutoNudgeSignature(stateDir, payload, signatureSourceText);
|
|
513
493
|
const semanticSignature = normalizeAutoNudgeSignatureText(signatureSourceText);
|
|
514
494
|
|
|
495
|
+
if (signature && safeString(nudgeState.lastSignature) === signature) {
|
|
496
|
+
await logTmuxHookEvent(logsDir, {
|
|
497
|
+
timestamp: new Date().toISOString(),
|
|
498
|
+
type: 'auto_nudge_skipped',
|
|
499
|
+
reason: 'already_nudged_for_signature',
|
|
500
|
+
source,
|
|
501
|
+
signature,
|
|
502
|
+
semantic_signature: semanticSignature,
|
|
503
|
+
}).catch(() => {});
|
|
504
|
+
return;
|
|
505
|
+
}
|
|
506
|
+
|
|
515
507
|
const lastNudgeAtMs = Date.parse(safeString(nudgeState.lastNudgeAt));
|
|
516
508
|
if (
|
|
517
509
|
semanticSignature
|
|
@@ -532,7 +524,6 @@ export async function maybeAutoNudge({ cwd, stateDir, logsDir, payload }) {
|
|
|
532
524
|
return;
|
|
533
525
|
}
|
|
534
526
|
|
|
535
|
-
const sourceName = safeString(payload?.source || '');
|
|
536
527
|
const isFallbackWatcherSource = sourceName === 'notify-fallback-watcher-stall';
|
|
537
528
|
if (!isFallbackWatcherSource && config.stallMs > 0) {
|
|
538
529
|
nudgeState.pendingSignature = signature;
|
|
@@ -564,7 +555,7 @@ export async function maybeAutoNudge({ cwd, stateDir, logsDir, payload }) {
|
|
|
564
555
|
}
|
|
565
556
|
|
|
566
557
|
const deepInterviewLockActive = isDeepInterviewAutoApprovalLocked(skillState) && !releaseReason;
|
|
567
|
-
if (deepInterviewLockActive
|
|
558
|
+
if (deepInterviewLockActive) {
|
|
568
559
|
const blockedMessage = skillState.input_lock?.message || DEEP_INTERVIEW_INPUT_LOCK_MESSAGE;
|
|
569
560
|
await logTmuxHookEvent(logsDir, {
|
|
570
561
|
timestamp: new Date().toISOString(),
|
|
@@ -573,6 +564,9 @@ export async function maybeAutoNudge({ cwd, stateDir, logsDir, payload }) {
|
|
|
573
564
|
response: config.response,
|
|
574
565
|
source,
|
|
575
566
|
blocked_by: 'deep-interview-lock',
|
|
567
|
+
block_kind: isBlockedAutoApprovalInput(config.response, skillState.input_lock?.blocked_inputs)
|
|
568
|
+
? 'blocked-auto-approval'
|
|
569
|
+
: 'input-lock-active',
|
|
576
570
|
message: blockedMessage,
|
|
577
571
|
suppressed: true,
|
|
578
572
|
}).catch(() => {});
|
|
@@ -0,0 +1,324 @@
|
|
|
1
|
+
import { execFileSync } from 'child_process';
|
|
2
|
+
import { readFileSync } from 'fs';
|
|
3
|
+
import { basename, dirname, resolve as resolvePath } from 'path';
|
|
4
|
+
import { readSessionState, isSessionStale } from '../../hooks/session.js';
|
|
5
|
+
import { runProcess } from './process-runner.js';
|
|
6
|
+
import { safeString } from './utils.js';
|
|
7
|
+
|
|
8
|
+
function sanitizeTmuxToken(value: string): string {
|
|
9
|
+
const cleaned = safeString(value)
|
|
10
|
+
.toLowerCase()
|
|
11
|
+
.replace(/[^a-z0-9]+/g, '-')
|
|
12
|
+
.replace(/^-+|-+$/g, '');
|
|
13
|
+
return cleaned || 'unknown';
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function buildExpectedManagedTmuxSessionName(cwd: string, sessionId: string): string {
|
|
17
|
+
const parentPath = dirname(cwd);
|
|
18
|
+
const parentDir = basename(parentPath);
|
|
19
|
+
const dirName = basename(cwd);
|
|
20
|
+
const grandparentPath = dirname(parentPath);
|
|
21
|
+
const grandparentDir = basename(grandparentPath);
|
|
22
|
+
const repoDir = parentDir.endsWith('.omx-worktrees')
|
|
23
|
+
? parentDir.slice(0, -'.omx-worktrees'.length)
|
|
24
|
+
: parentDir === 'worktrees' && grandparentDir === '.omx'
|
|
25
|
+
? basename(dirname(grandparentPath))
|
|
26
|
+
: null;
|
|
27
|
+
const dirToken = repoDir
|
|
28
|
+
? sanitizeTmuxToken(`${repoDir}-${dirName}`)
|
|
29
|
+
: sanitizeTmuxToken(dirName);
|
|
30
|
+
let branchToken = 'detached';
|
|
31
|
+
try {
|
|
32
|
+
const branch = execFileSync('git', ['rev-parse', '--abbrev-ref', 'HEAD'], {
|
|
33
|
+
cwd,
|
|
34
|
+
encoding: 'utf-8',
|
|
35
|
+
stdio: ['ignore', 'pipe', 'ignore'],
|
|
36
|
+
timeout: 2000,
|
|
37
|
+
windowsHide: true,
|
|
38
|
+
}).trim();
|
|
39
|
+
if (branch) branchToken = sanitizeTmuxToken(branch);
|
|
40
|
+
} catch {
|
|
41
|
+
// best effort only
|
|
42
|
+
}
|
|
43
|
+
const sessionToken = sanitizeTmuxToken(sessionId.replace(/^omx-/, ''));
|
|
44
|
+
const name = `omx-${dirToken}-${branchToken}-${sessionToken}`;
|
|
45
|
+
return name.length > 120 ? name.slice(0, 120) : name;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export function resolveInvocationSessionId(payload: any): string {
|
|
49
|
+
return safeString(
|
|
50
|
+
payload?.session_id
|
|
51
|
+
|| payload?.['session-id']
|
|
52
|
+
|| process.env.OMX_SESSION_ID
|
|
53
|
+
|| process.env.CODEX_SESSION_ID
|
|
54
|
+
|| process.env.SESSION_ID
|
|
55
|
+
|| '',
|
|
56
|
+
).trim();
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function readCurrentTmuxSessionName(): string {
|
|
60
|
+
if (!process.env.TMUX) return '';
|
|
61
|
+
try {
|
|
62
|
+
return execFileSync('tmux', ['display-message', '-p', '#S'], {
|
|
63
|
+
encoding: 'utf-8',
|
|
64
|
+
stdio: ['ignore', 'pipe', 'ignore'],
|
|
65
|
+
timeout: 2000,
|
|
66
|
+
}).trim();
|
|
67
|
+
} catch {
|
|
68
|
+
return '';
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function readParentPid(pid: number): number | null {
|
|
73
|
+
if (!Number.isInteger(pid) || pid <= 1) return null;
|
|
74
|
+
try {
|
|
75
|
+
if (process.platform === 'linux') {
|
|
76
|
+
const stat = readFileSync(`/proc/${pid}/stat`, 'utf-8');
|
|
77
|
+
const commandEnd = stat.lastIndexOf(')');
|
|
78
|
+
if (commandEnd === -1) return null;
|
|
79
|
+
const remainder = stat.slice(commandEnd + 1).trim();
|
|
80
|
+
const fields = remainder.split(/\s+/);
|
|
81
|
+
if (fields.length === 0) return null;
|
|
82
|
+
const ppid = Number(fields[1]);
|
|
83
|
+
return Number.isFinite(ppid) && ppid > 0 ? ppid : null;
|
|
84
|
+
}
|
|
85
|
+
const raw = execFileSync('ps', ['-o', 'ppid=', '-p', String(pid)], {
|
|
86
|
+
encoding: 'utf-8',
|
|
87
|
+
timeout: 2000,
|
|
88
|
+
}).trim();
|
|
89
|
+
const ppid = Number(raw);
|
|
90
|
+
return Number.isFinite(ppid) && ppid > 0 ? ppid : null;
|
|
91
|
+
} catch {
|
|
92
|
+
return null;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function processHasAncestorPid(targetPid: number, currentPid = process.pid): boolean {
|
|
97
|
+
if (!Number.isInteger(targetPid) || targetPid <= 1) return false;
|
|
98
|
+
let pid = Number.isInteger(currentPid) && currentPid > 1 ? currentPid : process.pid;
|
|
99
|
+
for (let depth = 0; depth < 64 && pid > 1; depth += 1) {
|
|
100
|
+
if (pid === targetPid) return true;
|
|
101
|
+
const parent = readParentPid(pid);
|
|
102
|
+
if (!parent || parent === pid) break;
|
|
103
|
+
pid = parent;
|
|
104
|
+
}
|
|
105
|
+
return false;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
export async function resolveManagedSessionContext(cwd: string, payload: any, { allowTeamWorker = true } = {}): Promise<any> {
|
|
109
|
+
if (allowTeamWorker && safeString(process.env.OMX_TEAM_WORKER || '').trim() !== '') {
|
|
110
|
+
return {
|
|
111
|
+
managed: true,
|
|
112
|
+
reason: 'team_worker',
|
|
113
|
+
invocationSessionId: '',
|
|
114
|
+
sessionState: null,
|
|
115
|
+
expectedTmuxSessionName: '',
|
|
116
|
+
currentTmuxSessionName: '',
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
const invocationSessionId = resolveInvocationSessionId(payload);
|
|
121
|
+
if (!invocationSessionId) {
|
|
122
|
+
return {
|
|
123
|
+
managed: false,
|
|
124
|
+
reason: 'missing_session_id',
|
|
125
|
+
invocationSessionId: '',
|
|
126
|
+
sessionState: null,
|
|
127
|
+
expectedTmuxSessionName: '',
|
|
128
|
+
currentTmuxSessionName: '',
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
try {
|
|
133
|
+
const sessionState = await readSessionState(cwd);
|
|
134
|
+
if (!sessionState) {
|
|
135
|
+
return { managed: false, reason: 'missing_session_state', invocationSessionId, sessionState: null, expectedTmuxSessionName: '', currentTmuxSessionName: '' };
|
|
136
|
+
}
|
|
137
|
+
if (resolvePath(safeString(sessionState.cwd || cwd)) !== resolvePath(cwd)) {
|
|
138
|
+
return { managed: false, reason: 'cwd_mismatch', invocationSessionId, sessionState, expectedTmuxSessionName: '', currentTmuxSessionName: '' };
|
|
139
|
+
}
|
|
140
|
+
if (safeString(sessionState.session_id).trim() !== invocationSessionId) {
|
|
141
|
+
return { managed: false, reason: 'session_id_mismatch', invocationSessionId, sessionState, expectedTmuxSessionName: '', currentTmuxSessionName: '' };
|
|
142
|
+
}
|
|
143
|
+
if (isSessionStale(sessionState)) {
|
|
144
|
+
return { managed: false, reason: 'stale_session', invocationSessionId, sessionState, expectedTmuxSessionName: '', currentTmuxSessionName: '' };
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
const expectedTmuxSessionName = buildExpectedManagedTmuxSessionName(cwd, invocationSessionId);
|
|
148
|
+
const currentTmuxSessionName = readCurrentTmuxSessionName();
|
|
149
|
+
if (currentTmuxSessionName && currentTmuxSessionName === expectedTmuxSessionName) {
|
|
150
|
+
return {
|
|
151
|
+
managed: true,
|
|
152
|
+
reason: 'tmux_session_match',
|
|
153
|
+
invocationSessionId,
|
|
154
|
+
sessionState,
|
|
155
|
+
expectedTmuxSessionName,
|
|
156
|
+
currentTmuxSessionName,
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
if (processHasAncestorPid(sessionState.pid)) {
|
|
161
|
+
return {
|
|
162
|
+
managed: true,
|
|
163
|
+
reason: currentTmuxSessionName ? 'pid_ancestry_match_tmux_mismatch' : 'pid_ancestry_match',
|
|
164
|
+
invocationSessionId,
|
|
165
|
+
sessionState,
|
|
166
|
+
expectedTmuxSessionName,
|
|
167
|
+
currentTmuxSessionName: '',
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
return {
|
|
172
|
+
managed: false,
|
|
173
|
+
reason: currentTmuxSessionName ? 'tmux_session_mismatch' : 'pid_ancestry_mismatch',
|
|
174
|
+
invocationSessionId,
|
|
175
|
+
sessionState,
|
|
176
|
+
expectedTmuxSessionName,
|
|
177
|
+
currentTmuxSessionName,
|
|
178
|
+
};
|
|
179
|
+
} catch {
|
|
180
|
+
return {
|
|
181
|
+
managed: false,
|
|
182
|
+
reason: 'session_check_failed',
|
|
183
|
+
invocationSessionId,
|
|
184
|
+
sessionState: null,
|
|
185
|
+
expectedTmuxSessionName: '',
|
|
186
|
+
currentTmuxSessionName: '',
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
export async function isManagedOmxSession(cwd: string, payload: any, options: { allowTeamWorker?: boolean } = {}): Promise<boolean> {
|
|
192
|
+
const context = await resolveManagedSessionContext(cwd, payload, options);
|
|
193
|
+
return context.managed === true;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
export async function verifyManagedPaneTarget(paneId: string, cwd: string, payload: any, { allowTeamWorker = true } = {}): Promise<any> {
|
|
197
|
+
const paneTarget = safeString(paneId).trim();
|
|
198
|
+
if (!paneTarget) {
|
|
199
|
+
return { ok: false, reason: 'missing_pane_target', paneTarget: '' };
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
const managedContext = await resolveManagedSessionContext(cwd, payload, { allowTeamWorker });
|
|
203
|
+
if (!managedContext.managed) {
|
|
204
|
+
return { ok: false, reason: managedContext.reason || 'unmanaged_session', paneTarget, managedContext };
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
if (managedContext.reason === 'team_worker') {
|
|
208
|
+
return { ok: true, reason: 'ok', paneTarget, managedContext };
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
const expectedSession = safeString(managedContext.expectedTmuxSessionName).trim();
|
|
212
|
+
if (!expectedSession) {
|
|
213
|
+
return { ok: false, reason: 'missing_expected_tmux_session', paneTarget, managedContext };
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
try {
|
|
217
|
+
const sessionResult = await runProcess('tmux', ['display-message', '-p', '-t', paneTarget, '#S'], 2000);
|
|
218
|
+
const paneSessionName = safeString(sessionResult.stdout).trim();
|
|
219
|
+
if (!paneSessionName) {
|
|
220
|
+
return { ok: false, reason: 'pane_session_missing', paneTarget, managedContext };
|
|
221
|
+
}
|
|
222
|
+
if (paneSessionName !== expectedSession) {
|
|
223
|
+
return { ok: false, reason: 'pane_not_managed_session', paneTarget, paneSessionName, managedContext };
|
|
224
|
+
}
|
|
225
|
+
return { ok: true, reason: 'ok', paneTarget, paneSessionName, managedContext };
|
|
226
|
+
} catch {
|
|
227
|
+
return { ok: false, reason: 'pane_session_lookup_failed', paneTarget, managedContext };
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
|
|
232
|
+
async function readManagedPaneCommandState(paneTarget: string): Promise<{ currentCommand: string; startCommand: string }> {
|
|
233
|
+
try {
|
|
234
|
+
const [currentResult, startResult] = await Promise.all([
|
|
235
|
+
runProcess('tmux', ['display-message', '-p', '-t', paneTarget, '#{pane_current_command}'], 2000),
|
|
236
|
+
runProcess('tmux', ['display-message', '-p', '-t', paneTarget, '#{pane_start_command}'], 2000),
|
|
237
|
+
]);
|
|
238
|
+
return {
|
|
239
|
+
currentCommand: safeString(currentResult.stdout).trim().toLowerCase(),
|
|
240
|
+
startCommand: safeString(startResult.stdout).trim().toLowerCase(),
|
|
241
|
+
};
|
|
242
|
+
} catch {
|
|
243
|
+
return { currentCommand: '', startCommand: '' };
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
function paneLooksLikeManagedAgent({ currentCommand, startCommand }: { currentCommand: string; startCommand: string }): boolean {
|
|
248
|
+
if (/\bomx\b.*\bhud\b.*--watch/i.test(startCommand)) return false;
|
|
249
|
+
if (startCommand.includes('codex')) return true;
|
|
250
|
+
return currentCommand === 'codex' || currentCommand === 'node' || currentCommand === 'npx';
|
|
251
|
+
}
|
|
252
|
+
export async function resolveManagedCurrentPane(cwd: string, payload: any, { allowTeamWorker = false } = {}): Promise<string> {
|
|
253
|
+
const paneTarget = safeString(process.env.TMUX_PANE || '').trim();
|
|
254
|
+
if (!paneTarget) return '';
|
|
255
|
+
const verdict = await verifyManagedPaneTarget(paneTarget, cwd, payload, { allowTeamWorker });
|
|
256
|
+
if (!verdict.ok) return '';
|
|
257
|
+
const commandState = await readManagedPaneCommandState(paneTarget);
|
|
258
|
+
return paneLooksLikeManagedAgent(commandState) ? paneTarget : '';
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
export async function resolveManagedSessionPane(cwd: string, payload: any): Promise<string> {
|
|
262
|
+
const managedContext = await resolveManagedSessionContext(cwd, payload, { allowTeamWorker: false });
|
|
263
|
+
if (!managedContext.managed) return '';
|
|
264
|
+
const expectedSession = safeString(managedContext.expectedTmuxSessionName).trim();
|
|
265
|
+
if (!expectedSession) return '';
|
|
266
|
+
|
|
267
|
+
try {
|
|
268
|
+
const panesResult = await runProcess(
|
|
269
|
+
'tmux',
|
|
270
|
+
['list-panes', '-s', '-t', expectedSession, '-F', '#{pane_id}\t#{pane_current_command}\t#{pane_start_command}'],
|
|
271
|
+
2000,
|
|
272
|
+
);
|
|
273
|
+
const panes = safeString(panesResult.stdout)
|
|
274
|
+
.trim()
|
|
275
|
+
.split('\n')
|
|
276
|
+
.filter(Boolean);
|
|
277
|
+
for (const line of panes) {
|
|
278
|
+
const [candidatePaneId, rawCurrentCommand = '', rawStartCommand = ''] = line.split('\t');
|
|
279
|
+
const startCommand = safeString(rawStartCommand).toLowerCase();
|
|
280
|
+
const currentCommand = safeString(rawCurrentCommand).trim().toLowerCase();
|
|
281
|
+
if (!candidatePaneId) continue;
|
|
282
|
+
if (/\bomx\b.*\bhud\b.*--watch/i.test(startCommand)) continue;
|
|
283
|
+
if (startCommand.includes('codex')) return candidatePaneId;
|
|
284
|
+
if (currentCommand === 'codex') return candidatePaneId;
|
|
285
|
+
}
|
|
286
|
+
} catch {
|
|
287
|
+
// best effort only
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
return '';
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
export async function resolveManagedPaneFromAnchor(anchorPane: string, cwd: string, payload: any, { allowTeamWorker = false } = {}): Promise<string> {
|
|
294
|
+
const paneTarget = safeString(anchorPane).trim();
|
|
295
|
+
if (!paneTarget) return '';
|
|
296
|
+
const verdict = await verifyManagedPaneTarget(paneTarget, cwd, payload, { allowTeamWorker });
|
|
297
|
+
if (!verdict.ok) return '';
|
|
298
|
+
|
|
299
|
+
try {
|
|
300
|
+
const sessionResult = await runProcess('tmux', ['display-message', '-p', '-t', paneTarget, '#S'], 2000);
|
|
301
|
+
const sessionName = safeString(sessionResult.stdout).trim();
|
|
302
|
+
if (!sessionName) return paneTarget;
|
|
303
|
+
|
|
304
|
+
const panesResult = await runProcess(
|
|
305
|
+
'tmux',
|
|
306
|
+
['list-panes', '-s', '-t', sessionName, '-F', '#{pane_id}\t#{pane_current_command}\t#{pane_start_command}'],
|
|
307
|
+
2000,
|
|
308
|
+
);
|
|
309
|
+
const panes = safeString(panesResult.stdout).trim().split('\n').filter(Boolean);
|
|
310
|
+
for (const line of panes) {
|
|
311
|
+
const [candidatePaneId, rawCurrentCommand = '', rawStartCommand = ''] = line.split('\t');
|
|
312
|
+
const startCommand = safeString(rawStartCommand).toLowerCase();
|
|
313
|
+
const currentCommand = safeString(rawCurrentCommand).trim().toLowerCase();
|
|
314
|
+
if (!candidatePaneId) continue;
|
|
315
|
+
if (/\bomx\b.*\bhud\b.*--watch/i.test(startCommand)) continue;
|
|
316
|
+
if (startCommand.includes('codex')) return candidatePaneId;
|
|
317
|
+
if (currentCommand === 'codex') return candidatePaneId;
|
|
318
|
+
}
|
|
319
|
+
} catch {
|
|
320
|
+
// best effort only
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
return paneTarget;
|
|
324
|
+
}
|
|
@@ -39,6 +39,7 @@ function gitValue(cwd: any, args: string[]): string {
|
|
|
39
39
|
encoding: 'utf-8',
|
|
40
40
|
stdio: ['ignore', 'pipe', 'ignore'],
|
|
41
41
|
timeout: 2000,
|
|
42
|
+
windowsHide: true,
|
|
42
43
|
}).trim();
|
|
43
44
|
} catch {
|
|
44
45
|
return '';
|
|
@@ -83,6 +84,7 @@ export function resolveOperationalSessionName(cwd: any, sessionId = '', sessionN
|
|
|
83
84
|
encoding: 'utf-8',
|
|
84
85
|
stdio: ['ignore', 'pipe', 'ignore'],
|
|
85
86
|
timeout: 2000,
|
|
87
|
+
windowsHide: true,
|
|
86
88
|
}).trim();
|
|
87
89
|
if (tmuxSession) return tmuxSession;
|
|
88
90
|
} catch {
|