oh-my-codex 0.11.12 → 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.md +23 -0
- package/README.vi.md +144 -185
- package/crates/omx-runtime-core/src/engine.rs +122 -4
- package/crates/omx-runtime-core/src/lib.rs +17 -0
- 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__/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 +94 -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__/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/cleanup.d.ts +2 -0
- package/dist/cli/cleanup.d.ts.map +1 -1
- package/dist/cli/cleanup.js +26 -1
- package/dist/cli/cleanup.js.map +1 -1
- package/dist/cli/index.d.ts +7 -0
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +161 -50
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/setup.d.ts.map +1 -1
- package/dist/cli/setup.js +15 -14
- package/dist/cli/setup.js.map +1 -1
- package/dist/cli/star-prompt.d.ts.map +1 -1
- package/dist/cli/star-prompt.js +1 -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/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/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 +499 -17
- package/dist/hooks/__tests__/notify-fallback-watcher.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-hook-auto-nudge.test.js +140 -14
- 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 +15 -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/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/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/state.d.ts.map +1 -1
- package/dist/hud/state.js +10 -37
- 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/openclaw/config.js +2 -2
- package/dist/openclaw/config.js.map +1 -1
- package/dist/runtime/bridge.d.ts +1 -0
- package/dist/runtime/bridge.d.ts.map +1 -1
- package/dist/runtime/bridge.js +2 -6
- package/dist/runtime/bridge.js.map +1 -1
- package/dist/scripts/notify-fallback-watcher.js +97 -59
- 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 +72 -238
- 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/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.map +1 -1
- package/dist/scripts/notify-hook/team-dispatch.js +60 -59
- 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/tmux-hook-engine.d.ts +1 -0
- package/dist/scripts/tmux-hook-engine.d.ts.map +1 -1
- package/dist/scripts/tmux-hook-engine.js +3 -0
- package/dist/scripts/tmux-hook-engine.js.map +1 -1
- package/dist/team/__tests__/api-interop.test.js +96 -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 +16 -5
- package/dist/team/__tests__/state.test.js.map +1 -1
- package/dist/team/__tests__/tmux-session.test.js +460 -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 +34 -7
- 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 +17 -35
- 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 +391 -63
- package/dist/team/runtime.js.map +1 -1
- package/dist/team/state/dispatch.js +1 -1
- package/dist/team/state/dispatch.js.map +1 -1
- package/dist/team/state/mailbox.d.ts +1 -0
- package/dist/team/state/mailbox.d.ts.map +1 -1
- package/dist/team/state/mailbox.js +54 -8
- 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 +66 -3
- package/dist/team/state.js.map +1 -1
- package/dist/team/tmux-session.d.ts.map +1 -1
- package/dist/team/tmux-session.js +69 -27
- package/dist/team/tmux-session.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/platform-command.d.ts.map +1 -1
- package/dist/utils/platform-command.js +32 -1
- package/dist/utils/platform-command.js.map +1 -1
- package/package.json +6 -6
- package/src/scripts/notify-fallback-watcher.ts +96 -58
- package/src/scripts/notify-hook/auto-nudge.ts +75 -230
- package/src/scripts/notify-hook/managed-tmux.ts +324 -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 +70 -54
- 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/tmux-hook-engine.ts +4 -0
|
@@ -5,17 +5,22 @@
|
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
import { readFile, writeFile } from 'fs/promises';
|
|
8
|
-
import {
|
|
9
|
-
import { execFileSync } from 'child_process';
|
|
10
|
-
import { basename, dirname, join, resolve as resolvePath } from 'path';
|
|
8
|
+
import { join } from 'path';
|
|
11
9
|
import { homedir } from 'os';
|
|
12
10
|
import { asNumber, safeString } from './utils.js';
|
|
13
11
|
import { readJsonIfExists, getScopedStateDirsForCurrentSession, readdir } from './state-io.js';
|
|
14
12
|
import { runProcess } from './process-runner.js';
|
|
15
13
|
import { logTmuxHookEvent } from './log.js';
|
|
16
14
|
import { evaluatePaneInjectionReadiness, mapPaneInjectionReadinessReason, sendPaneInput } from './team-tmux-guard.js';
|
|
17
|
-
import { buildCapturePaneArgv, DEFAULT_MARKER } from '../tmux-hook-engine.js';
|
|
18
|
-
import {
|
|
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';
|
|
19
24
|
|
|
20
25
|
export const SKILL_ACTIVE_STATE_FILE = 'skill-active-state.json';
|
|
21
26
|
export const DEEP_INTERVIEW_BLOCKED_APPROVAL_INPUTS = ['yes', 'y', 'proceed', 'continue', 'ok', 'sure', 'go ahead', 'next i should'];
|
|
@@ -55,22 +60,29 @@ export function normalizeBlockedAutoApprovalInput(text) {
|
|
|
55
60
|
.trim();
|
|
56
61
|
}
|
|
57
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
|
+
|
|
58
72
|
export function isBlockedAutoApprovalInput(text, blockedInputs = DEEP_INTERVIEW_BLOCKED_APPROVAL_INPUTS) {
|
|
59
73
|
const normalized = normalizeBlockedAutoApprovalInput(text);
|
|
60
74
|
if (!normalized) return false;
|
|
61
|
-
|
|
62
|
-
if (
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
.some((prefix) => normalized.startsWith(`${prefix} `))
|
|
67
|
-
) 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;
|
|
68
80
|
|
|
69
81
|
const tokens = normalized.split(/\s+/).filter(Boolean);
|
|
70
82
|
if (tokens.length === 0) return false;
|
|
71
83
|
|
|
72
84
|
const blockedTokenSet = new Set(
|
|
73
|
-
|
|
85
|
+
normalizedBlockedInputs.flatMap((entry) => entry.split(/\s+/).filter(Boolean)),
|
|
74
86
|
);
|
|
75
87
|
return tokens.every((token) => blockedTokenSet.has(token));
|
|
76
88
|
}
|
|
@@ -179,6 +191,11 @@ export async function isDeepInterviewStateActive(stateDir) {
|
|
|
179
191
|
return Boolean(modeState && modeState.active === true);
|
|
180
192
|
}
|
|
181
193
|
|
|
194
|
+
export async function isDeepInterviewInputLockActive(stateDir) {
|
|
195
|
+
const skillState = await loadSkillActiveState(stateDir);
|
|
196
|
+
return isDeepInterviewAutoApprovalLocked(skillState);
|
|
197
|
+
}
|
|
198
|
+
|
|
182
199
|
export async function resolveAutoNudgeSignature(stateDir, payload, lastMessage = '') {
|
|
183
200
|
const normalizedMessage = normalizeAutoNudgeSignatureText(lastMessage);
|
|
184
201
|
const hudState = await readJsonIfExists(join(stateDir, 'hud-state.json'), null);
|
|
@@ -354,6 +371,13 @@ export async function loadAutoNudgeConfig() {
|
|
|
354
371
|
return normalizeAutoNudgeConfig(raw.autoNudge);
|
|
355
372
|
}
|
|
356
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
|
+
|
|
357
381
|
export function detectStallPattern(text, patterns) {
|
|
358
382
|
if (!text || typeof text !== 'string') return false;
|
|
359
383
|
const normalized = normalizeStallDetectionText(text);
|
|
@@ -375,233 +399,52 @@ export async function capturePane(paneId, lines = 10) {
|
|
|
375
399
|
}
|
|
376
400
|
}
|
|
377
401
|
|
|
378
|
-
function
|
|
379
|
-
const
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
try {
|
|
383
|
-
const panes = execFileSync('tmux', [
|
|
384
|
-
'list-panes', '-a', '-F', '#{pane_id} #{pane_current_path} #{pane_current_command} #{pane_start_command}',
|
|
385
|
-
], { encoding: 'utf-8', timeout: 2000, windowsHide: true })
|
|
386
|
-
.trim()
|
|
387
|
-
.split('\n')
|
|
388
|
-
.filter(Boolean);
|
|
389
|
-
|
|
390
|
-
for (const line of panes) {
|
|
391
|
-
const [paneId, panePath = '', paneCommand = '', startCommand = ''] = line.split('\t');
|
|
392
|
-
const normalizedPanePath = safeString(panePath).trim();
|
|
393
|
-
const normalizedStart = safeString(startCommand).toLowerCase();
|
|
394
|
-
const normalizedCommand = safeString(paneCommand).trim().toLowerCase();
|
|
395
|
-
if (!paneId || normalizedPanePath !== normalizedCwd) continue;
|
|
396
|
-
if (/\bomx\b.*\bhud\b.*--watch/i.test(normalizedStart)) continue;
|
|
397
|
-
if (normalizedStart.includes('codex')) return paneId;
|
|
398
|
-
if (normalizedCommand === 'codex' || normalizedCommand === 'node' || normalizedCommand === 'npx') return paneId;
|
|
399
|
-
}
|
|
400
|
-
} catch {
|
|
401
|
-
// Fall back to empty when tmux scan is unavailable.
|
|
402
|
-
}
|
|
403
|
-
|
|
404
|
-
return '';
|
|
405
|
-
}
|
|
406
|
-
|
|
407
|
-
async function resolveCodexPaneFromAnchor(anchorPane) {
|
|
408
|
-
const paneId = safeString(anchorPane).trim();
|
|
409
|
-
if (!paneId) return '';
|
|
410
|
-
|
|
411
|
-
try {
|
|
412
|
-
const sessionResult = await runProcess('tmux', ['display-message', '-t', paneId, '-p', '#S'], 2000);
|
|
413
|
-
const sessionName = safeString(sessionResult.stdout).trim();
|
|
414
|
-
if (!sessionName) return '';
|
|
415
|
-
|
|
416
|
-
const panesResult = await runProcess(
|
|
417
|
-
'tmux',
|
|
418
|
-
['list-panes', '-s', '-t', sessionName, '-F', '#{pane_id}\t#{pane_current_command}\t#{pane_start_command}'],
|
|
419
|
-
2000,
|
|
420
|
-
);
|
|
421
|
-
const panes = safeString(panesResult.stdout).trim().split('\n').filter(Boolean);
|
|
422
|
-
for (const line of panes) {
|
|
423
|
-
const [candidatePaneId, , rawStartCommand = ''] = line.split('\t');
|
|
424
|
-
const startCommand = safeString(rawStartCommand).toLowerCase();
|
|
425
|
-
if (!candidatePaneId) continue;
|
|
426
|
-
if (/\bomx\b.*\bhud\b.*--watch/i.test(startCommand)) continue;
|
|
427
|
-
if (startCommand.includes('codex')) return candidatePaneId;
|
|
428
|
-
}
|
|
429
|
-
} catch {
|
|
430
|
-
// Fall back to the anchored pane when session scanning is unavailable.
|
|
431
|
-
}
|
|
432
|
-
|
|
433
|
-
return '';
|
|
434
|
-
}
|
|
435
|
-
|
|
436
|
-
function resolveInvocationSessionId(payload) {
|
|
437
|
-
return safeString(
|
|
438
|
-
payload?.session_id
|
|
439
|
-
|| payload?.['session-id']
|
|
440
|
-
|| process.env.OMX_SESSION_ID
|
|
441
|
-
|| process.env.CODEX_SESSION_ID
|
|
442
|
-
|| process.env.SESSION_ID
|
|
443
|
-
|| '',
|
|
444
|
-
).trim();
|
|
445
|
-
}
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
function sanitizeTmuxToken(value) {
|
|
449
|
-
const cleaned = safeString(value)
|
|
450
|
-
.toLowerCase()
|
|
451
|
-
.replace(/[^a-z0-9]+/g, '-')
|
|
452
|
-
.replace(/^-+|-+$/g, '');
|
|
453
|
-
return cleaned || 'unknown';
|
|
454
|
-
}
|
|
455
|
-
|
|
456
|
-
function buildExpectedManagedTmuxSessionName(cwd, sessionId) {
|
|
457
|
-
const parentPath = dirname(cwd);
|
|
458
|
-
const parentDir = basename(parentPath);
|
|
459
|
-
const dirName = basename(cwd);
|
|
460
|
-
const grandparentPath = dirname(parentPath);
|
|
461
|
-
const grandparentDir = basename(grandparentPath);
|
|
462
|
-
const repoDir = parentDir.endsWith('.omx-worktrees')
|
|
463
|
-
? parentDir.slice(0, -'.omx-worktrees'.length)
|
|
464
|
-
: parentDir === 'worktrees' && grandparentDir === '.omx'
|
|
465
|
-
? basename(dirname(grandparentPath))
|
|
466
|
-
: null;
|
|
467
|
-
const dirToken = repoDir
|
|
468
|
-
? sanitizeTmuxToken(`${repoDir}-${dirName}`)
|
|
469
|
-
: sanitizeTmuxToken(dirName);
|
|
470
|
-
let branchToken = 'detached';
|
|
471
|
-
try {
|
|
472
|
-
const branch = execFileSync('git', ['rev-parse', '--abbrev-ref', 'HEAD'], {
|
|
473
|
-
cwd,
|
|
474
|
-
encoding: 'utf-8',
|
|
475
|
-
stdio: ['ignore', 'pipe', 'ignore'],
|
|
476
|
-
timeout: 2000,
|
|
477
|
-
}).trim();
|
|
478
|
-
if (branch) branchToken = sanitizeTmuxToken(branch);
|
|
479
|
-
} catch {
|
|
480
|
-
// best effort only
|
|
481
|
-
}
|
|
482
|
-
const sessionToken = sanitizeTmuxToken(safeString(sessionId).replace(/^omx-/, ''));
|
|
483
|
-
const name = `omx-${dirToken}-${branchToken}-${sessionToken}`;
|
|
484
|
-
return name.length > 120 ? name.slice(0, 120) : name;
|
|
485
|
-
}
|
|
486
|
-
|
|
487
|
-
function readCurrentTmuxSessionName() {
|
|
488
|
-
if (!process.env.TMUX) return '';
|
|
489
|
-
try {
|
|
490
|
-
return execFileSync('tmux', ['display-message', '-p', '#S'], {
|
|
491
|
-
encoding: 'utf-8',
|
|
492
|
-
stdio: ['ignore', 'pipe', 'ignore'],
|
|
493
|
-
timeout: 2000,
|
|
494
|
-
}).trim();
|
|
495
|
-
} catch {
|
|
496
|
-
return '';
|
|
497
|
-
}
|
|
498
|
-
}
|
|
499
|
-
|
|
500
|
-
function readParentPid(pid) {
|
|
501
|
-
if (!Number.isInteger(pid) || pid <= 1) return null;
|
|
502
|
-
try {
|
|
503
|
-
if (process.platform === 'linux') {
|
|
504
|
-
const stat = readFileSync(`/proc/${pid}/stat`, 'utf-8');
|
|
505
|
-
const commandEnd = stat.lastIndexOf(')');
|
|
506
|
-
if (commandEnd === -1) return null;
|
|
507
|
-
const remainder = stat.slice(commandEnd + 1).trim();
|
|
508
|
-
const fields = remainder.split(/\s+/);
|
|
509
|
-
if (fields.length === 0) return null;
|
|
510
|
-
const ppid = Number(fields[1]);
|
|
511
|
-
return Number.isFinite(ppid) && ppid > 0 ? ppid : null;
|
|
512
|
-
}
|
|
513
|
-
const raw = execFileSync('ps', ['-o', 'ppid=', '-p', String(pid)], {
|
|
514
|
-
encoding: 'utf-8',
|
|
515
|
-
timeout: 2000,
|
|
516
|
-
}).trim();
|
|
517
|
-
const ppid = Number(raw);
|
|
518
|
-
return Number.isFinite(ppid) && ppid > 0 ? ppid : null;
|
|
519
|
-
} catch {
|
|
520
|
-
return null;
|
|
521
|
-
}
|
|
522
|
-
}
|
|
523
|
-
|
|
524
|
-
function processHasAncestorPid(targetPid, currentPid = process.pid) {
|
|
525
|
-
if (!Number.isInteger(targetPid) || targetPid <= 1) return false;
|
|
526
|
-
let pid = Number.isInteger(currentPid) && currentPid > 1 ? currentPid : process.pid;
|
|
527
|
-
for (let depth = 0; depth < 64 && pid > 1; depth += 1) {
|
|
528
|
-
if (pid === targetPid) return true;
|
|
529
|
-
const parent = readParentPid(pid);
|
|
530
|
-
if (!parent || parent === pid) break;
|
|
531
|
-
pid = parent;
|
|
532
|
-
}
|
|
533
|
-
return false;
|
|
534
|
-
}
|
|
535
|
-
|
|
536
|
-
async function isManagedOmxSessionForAutoNudge(cwd, payload) {
|
|
537
|
-
if (safeString(process.env.OMX_TEAM_WORKER || '').trim() !== '') return true;
|
|
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;
|
|
538
406
|
|
|
539
407
|
const invocationSessionId = resolveInvocationSessionId(payload);
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
const
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
return false;
|
|
558
|
-
}
|
|
559
|
-
}
|
|
560
|
-
|
|
561
|
-
export async function resolveNudgePaneTarget(stateDir: any, cwd = '') {
|
|
562
|
-
// Use canonical codex pane resolver — validates pane is running an agent, not a shell
|
|
563
|
-
const { resolveCodexPane } = await import('../tmux-hook-engine.js');
|
|
564
|
-
const codexPane = resolveCodexPane();
|
|
565
|
-
if (codexPane) return codexPane;
|
|
566
|
-
|
|
567
|
-
let fallbackPane = '';
|
|
568
|
-
|
|
569
|
-
try {
|
|
570
|
-
const scopedDirs = await getScopedStateDirsForCurrentSession(stateDir);
|
|
571
|
-
for (const dir of scopedDirs) {
|
|
572
|
-
const files = await readdir(dir).catch(() => []);
|
|
573
|
-
for (const f of files) {
|
|
574
|
-
if (!f.endsWith('-state.json')) continue;
|
|
575
|
-
const path = join(dir, f);
|
|
576
|
-
try {
|
|
577
|
-
const state = JSON.parse(await readFile(path, 'utf-8'));
|
|
578
|
-
if (state && state.active && state.tmux_pane_id) {
|
|
579
|
-
const anchoredPane = safeString(state.tmux_pane_id).trim();
|
|
580
|
-
if (!anchoredPane) continue;
|
|
581
|
-
const upgradedPane = await resolveCodexPaneFromAnchor(anchoredPane);
|
|
582
|
-
if (upgradedPane) return upgradedPane;
|
|
583
|
-
if (!fallbackPane) fallbackPane = anchoredPane;
|
|
584
|
-
}
|
|
585
|
-
} catch {
|
|
586
|
-
// skip malformed state
|
|
587
|
-
}
|
|
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
|
|
588
425
|
}
|
|
589
426
|
}
|
|
590
|
-
} catch {
|
|
591
|
-
// Non-critical
|
|
592
427
|
}
|
|
593
428
|
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
return resolveCodexPaneByCwdFallback(cwd);
|
|
429
|
+
return await resolveManagedSessionPane(cwd, payload);
|
|
597
430
|
}
|
|
598
431
|
|
|
599
432
|
export async function maybeAutoNudge({ cwd, stateDir, logsDir, payload }) {
|
|
600
433
|
const config = await loadAutoNudgeConfig();
|
|
601
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
|
+
}
|
|
602
443
|
|
|
603
|
-
const
|
|
444
|
+
const sourceName = safeString(payload?.source || '');
|
|
445
|
+
const managedSession = await isManagedOmxSession(cwd, payload, { allowTeamWorker: true });
|
|
604
446
|
if (!managedSession) {
|
|
447
|
+
if (sourceName === 'notify-fallback-watcher-stall') return;
|
|
605
448
|
await logTmuxHookEvent(logsDir, {
|
|
606
449
|
timestamp: new Date().toISOString(),
|
|
607
450
|
type: 'auto_nudge_skipped',
|
|
@@ -630,7 +473,7 @@ export async function maybeAutoNudge({ cwd, stateDir, logsDir, payload }) {
|
|
|
630
473
|
if (!nudgeState || typeof nudgeState !== 'object') {
|
|
631
474
|
nudgeState = { nudgeCount: 0, lastNudgeAt: '', lastSignature: '', lastSemanticSignature: '' };
|
|
632
475
|
}
|
|
633
|
-
const paneId = await resolveNudgePaneTarget(stateDir, cwd);
|
|
476
|
+
const paneId = await resolveNudgePaneTarget(stateDir, cwd, payload);
|
|
634
477
|
|
|
635
478
|
let detected = detectStallPattern(lastMessage, config.patterns);
|
|
636
479
|
let source = 'payload';
|
|
@@ -681,7 +524,6 @@ export async function maybeAutoNudge({ cwd, stateDir, logsDir, payload }) {
|
|
|
681
524
|
return;
|
|
682
525
|
}
|
|
683
526
|
|
|
684
|
-
const sourceName = safeString(payload?.source || '');
|
|
685
527
|
const isFallbackWatcherSource = sourceName === 'notify-fallback-watcher-stall';
|
|
686
528
|
if (!isFallbackWatcherSource && config.stallMs > 0) {
|
|
687
529
|
nudgeState.pendingSignature = signature;
|
|
@@ -713,7 +555,7 @@ export async function maybeAutoNudge({ cwd, stateDir, logsDir, payload }) {
|
|
|
713
555
|
}
|
|
714
556
|
|
|
715
557
|
const deepInterviewLockActive = isDeepInterviewAutoApprovalLocked(skillState) && !releaseReason;
|
|
716
|
-
if (deepInterviewLockActive
|
|
558
|
+
if (deepInterviewLockActive) {
|
|
717
559
|
const blockedMessage = skillState.input_lock?.message || DEEP_INTERVIEW_INPUT_LOCK_MESSAGE;
|
|
718
560
|
await logTmuxHookEvent(logsDir, {
|
|
719
561
|
timestamp: new Date().toISOString(),
|
|
@@ -722,6 +564,9 @@ export async function maybeAutoNudge({ cwd, stateDir, logsDir, payload }) {
|
|
|
722
564
|
response: config.response,
|
|
723
565
|
source,
|
|
724
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',
|
|
725
570
|
message: blockedMessage,
|
|
726
571
|
suppressed: true,
|
|
727
572
|
}).catch(() => {});
|