oh-my-codex 0.13.0 → 0.13.2
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 +40 -6
- package/crates/omx-explore/src/main.rs +221 -10
- package/dist/catalog/__tests__/generator.test.js +2 -0
- package/dist/catalog/__tests__/generator.test.js.map +1 -1
- package/dist/cli/__tests__/index.test.js +150 -1
- package/dist/cli/__tests__/index.test.js.map +1 -1
- package/dist/cli/__tests__/setup-skills-overwrite.test.js +41 -3
- package/dist/cli/__tests__/setup-skills-overwrite.test.js.map +1 -1
- package/dist/cli/__tests__/update.test.js +25 -1
- package/dist/cli/__tests__/update.test.js.map +1 -1
- package/dist/cli/index.d.ts +1 -0
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +73 -9
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/setup.d.ts.map +1 -1
- package/dist/cli/setup.js +15 -0
- package/dist/cli/setup.js.map +1 -1
- package/dist/cli/update.js +1 -1
- package/dist/cli/update.js.map +1 -1
- package/dist/hooks/__tests__/agents-overlay.test.js +20 -2
- package/dist/hooks/__tests__/agents-overlay.test.js.map +1 -1
- package/dist/hooks/__tests__/analyze-routing-contract.test.d.ts +2 -0
- package/dist/hooks/__tests__/analyze-routing-contract.test.d.ts.map +1 -0
- package/dist/hooks/__tests__/analyze-routing-contract.test.js +36 -0
- package/dist/hooks/__tests__/analyze-routing-contract.test.js.map +1 -0
- package/dist/hooks/__tests__/analyze-skill-contract.test.d.ts +2 -0
- package/dist/hooks/__tests__/analyze-skill-contract.test.d.ts.map +1 -0
- package/dist/hooks/__tests__/analyze-skill-contract.test.js +48 -0
- package/dist/hooks/__tests__/analyze-skill-contract.test.js.map +1 -0
- package/dist/hooks/__tests__/keyword-detector.test.js +32 -0
- package/dist/hooks/__tests__/keyword-detector.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-fallback-watcher.test.js +185 -8
- package/dist/hooks/__tests__/notify-fallback-watcher.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-hook-session-idle-dedupe.test.js +26 -0
- package/dist/hooks/__tests__/notify-hook-session-idle-dedupe.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-hook-session-scope.test.js +44 -0
- package/dist/hooks/__tests__/notify-hook-session-scope.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-hook-team-leader-nudge.test.js +126 -0
- package/dist/hooks/__tests__/notify-hook-team-leader-nudge.test.js.map +1 -1
- package/dist/hooks/__tests__/session.test.js +21 -0
- package/dist/hooks/__tests__/session.test.js.map +1 -1
- package/dist/hooks/agents-overlay.d.ts.map +1 -1
- package/dist/hooks/agents-overlay.js +9 -0
- package/dist/hooks/agents-overlay.js.map +1 -1
- package/dist/hooks/keyword-detector.d.ts.map +1 -1
- package/dist/hooks/keyword-detector.js +8 -1
- package/dist/hooks/keyword-detector.js.map +1 -1
- package/dist/hooks/session.d.ts.map +1 -1
- package/dist/hooks/session.js +9 -0
- package/dist/hooks/session.js.map +1 -1
- package/dist/hud/__tests__/state.test.js +55 -0
- package/dist/hud/__tests__/state.test.js.map +1 -1
- package/dist/hud/state.d.ts.map +1 -1
- package/dist/hud/state.js +23 -4
- package/dist/hud/state.js.map +1 -1
- package/dist/mcp/__tests__/bootstrap.test.js +38 -0
- package/dist/mcp/__tests__/bootstrap.test.js.map +1 -1
- package/dist/mcp/bootstrap.d.ts +1 -1
- package/dist/mcp/bootstrap.d.ts.map +1 -1
- package/dist/mcp/bootstrap.js +11 -3
- package/dist/mcp/bootstrap.js.map +1 -1
- package/dist/notifications/__tests__/reply-listener.test.js +34 -1
- package/dist/notifications/__tests__/reply-listener.test.js.map +1 -1
- package/dist/notifications/reply-listener.d.ts +1 -0
- package/dist/notifications/reply-listener.d.ts.map +1 -1
- package/dist/notifications/reply-listener.js +14 -2
- package/dist/notifications/reply-listener.js.map +1 -1
- package/dist/scripts/__tests__/codex-native-hook.test.js +248 -15
- package/dist/scripts/__tests__/codex-native-hook.test.js.map +1 -1
- package/dist/scripts/__tests__/generate-release-body.test.d.ts +2 -0
- package/dist/scripts/__tests__/generate-release-body.test.d.ts.map +1 -0
- package/dist/scripts/__tests__/generate-release-body.test.js +144 -0
- package/dist/scripts/__tests__/generate-release-body.test.js.map +1 -0
- package/dist/scripts/codex-native-hook.d.ts.map +1 -1
- package/dist/scripts/codex-native-hook.js +39 -49
- package/dist/scripts/codex-native-hook.js.map +1 -1
- package/dist/scripts/generate-release-body.d.ts +34 -0
- package/dist/scripts/generate-release-body.d.ts.map +1 -0
- package/dist/scripts/generate-release-body.js +249 -0
- package/dist/scripts/generate-release-body.js.map +1 -0
- package/dist/scripts/notify-fallback-watcher.js +43 -20
- package/dist/scripts/notify-fallback-watcher.js.map +1 -1
- package/dist/scripts/notify-hook/active-team.d.ts.map +1 -1
- package/dist/scripts/notify-hook/active-team.js +2 -1
- package/dist/scripts/notify-hook/active-team.js.map +1 -1
- package/dist/scripts/notify-hook/ralph-session-resume.d.ts.map +1 -1
- package/dist/scripts/notify-hook/ralph-session-resume.js +17 -2
- package/dist/scripts/notify-hook/ralph-session-resume.js.map +1 -1
- package/dist/scripts/notify-hook/state-io.d.ts.map +1 -1
- package/dist/scripts/notify-hook/state-io.js +16 -0
- package/dist/scripts/notify-hook/state-io.js.map +1 -1
- package/dist/scripts/notify-hook/team-leader-nudge.d.ts.map +1 -1
- package/dist/scripts/notify-hook/team-leader-nudge.js +26 -5
- package/dist/scripts/notify-hook/team-leader-nudge.js.map +1 -1
- package/dist/scripts/notify-hook.js +1 -7
- package/dist/scripts/notify-hook.js.map +1 -1
- package/dist/team/__tests__/model-contract.test.js +6 -0
- package/dist/team/__tests__/model-contract.test.js.map +1 -1
- package/dist/team/__tests__/tmux-session.test.js +1 -1
- package/dist/team/__tests__/tmux-session.test.js.map +1 -1
- package/dist/team/__tests__/worker-runtime-identity.test.d.ts +2 -0
- package/dist/team/__tests__/worker-runtime-identity.test.d.ts.map +1 -0
- package/dist/team/__tests__/worker-runtime-identity.test.js +250 -0
- package/dist/team/__tests__/worker-runtime-identity.test.js.map +1 -0
- package/dist/team/leader-activity.d.ts.map +1 -1
- package/dist/team/leader-activity.js +26 -15
- package/dist/team/leader-activity.js.map +1 -1
- package/dist/team/model-contract.d.ts.map +1 -1
- package/dist/team/model-contract.js.map +1 -1
- package/dist/team/runtime.d.ts.map +1 -1
- package/dist/team/runtime.js +9 -8
- package/dist/team/runtime.js.map +1 -1
- package/dist/team/scaling.d.ts.map +1 -1
- package/dist/team/scaling.js +10 -9
- package/dist/team/scaling.js.map +1 -1
- package/dist/team/tmux-session.d.ts.map +1 -1
- package/dist/team/tmux-session.js +3 -2
- package/dist/team/tmux-session.js.map +1 -1
- package/dist/verification/__tests__/explore-harness-release-workflow.test.js +3 -0
- package/dist/verification/__tests__/explore-harness-release-workflow.test.js.map +1 -1
- package/dist/wiki/__tests__/slug-nonascii.test.js +11 -5
- package/dist/wiki/__tests__/slug-nonascii.test.js.map +1 -1
- package/dist/wiki/storage.d.ts.map +1 -1
- package/dist/wiki/storage.js +2 -1
- package/dist/wiki/storage.js.map +1 -1
- package/package.json +3 -1
- package/skills/analyze/SKILL.md +101 -134
- package/src/scripts/__tests__/codex-native-hook.test.ts +297 -17
- package/src/scripts/__tests__/generate-release-body.test.ts +166 -0
- package/src/scripts/codex-native-hook.ts +99 -66
- package/src/scripts/generate-release-body.ts +295 -0
- package/src/scripts/notify-fallback-watcher.ts +44 -21
- package/src/scripts/notify-hook/active-team.ts +2 -1
- package/src/scripts/notify-hook/ralph-session-resume.ts +17 -2
- package/src/scripts/notify-hook/state-io.ts +16 -0
- package/src/scripts/notify-hook/team-leader-nudge.ts +24 -4
- package/src/scripts/notify-hook.ts +1 -6
- package/templates/AGENTS.md +1 -1
- package/templates/catalog-manifest.json +2 -4
|
@@ -34,6 +34,8 @@ import {
|
|
|
34
34
|
} from '../subagents/tracker.js';
|
|
35
35
|
import { listNotifyCanonicalActiveTeams } from './notify-hook/active-team.js';
|
|
36
36
|
import { sameFilePath } from '../utils/paths.js';
|
|
37
|
+
import { validateSessionId } from '../mcp/state-paths.js';
|
|
38
|
+
import { TEAM_NAME_SAFE_PATTERN } from '../team/contracts.js';
|
|
37
39
|
|
|
38
40
|
function argValue(name: string, fallback = ''): string {
|
|
39
41
|
const idx = process.argv.indexOf(name);
|
|
@@ -50,6 +52,21 @@ function safeString(v: unknown): string {
|
|
|
50
52
|
return typeof v === 'string' ? v : '';
|
|
51
53
|
}
|
|
52
54
|
|
|
55
|
+
function normalizeValidSessionId(value: unknown): string {
|
|
56
|
+
const trimmed = safeString(value).trim();
|
|
57
|
+
if (!trimmed) return '';
|
|
58
|
+
try {
|
|
59
|
+
return validateSessionId(trimmed) ?? '';
|
|
60
|
+
} catch {
|
|
61
|
+
return '';
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function normalizeValidTeamName(value: unknown): string {
|
|
66
|
+
const trimmed = safeString(value).trim();
|
|
67
|
+
return TEAM_NAME_SAFE_PATTERN.test(trimmed) ? trimmed : '';
|
|
68
|
+
}
|
|
69
|
+
|
|
53
70
|
function parsePositivePid(value: unknown): number | null {
|
|
54
71
|
const pid = Math.trunc(asNumber(value as string | number | undefined, 0));
|
|
55
72
|
return pid > 0 ? pid : null;
|
|
@@ -523,8 +540,8 @@ async function resolveActiveModeState(mode: string): Promise<ActiveModeResult> {
|
|
|
523
540
|
const session = await readSessionState(cwd);
|
|
524
541
|
if (session?.session_id) {
|
|
525
542
|
if (isSessionStateAuthoritativeForCwd(session, cwd)) {
|
|
526
|
-
currentSessionId =
|
|
527
|
-
currentSessionIsLive = !isSessionStale(session);
|
|
543
|
+
currentSessionId = normalizeValidSessionId(session.session_id);
|
|
544
|
+
currentSessionIsLive = currentSessionId !== '' && !isSessionStale(session);
|
|
528
545
|
}
|
|
529
546
|
if (currentSessionId && currentSessionIsLive) {
|
|
530
547
|
candidateDirs.push(join(stateDir, 'sessions', currentSessionId));
|
|
@@ -590,8 +607,8 @@ async function resolveActiveTeamState(): Promise<ActiveTeamResult> {
|
|
|
590
607
|
let currentSessionIsLive = false;
|
|
591
608
|
const session = await readSessionState(cwd);
|
|
592
609
|
if (session?.session_id) {
|
|
593
|
-
currentSessionId =
|
|
594
|
-
currentSessionIsLive = !isSessionStale(session);
|
|
610
|
+
currentSessionId = normalizeValidSessionId(session.session_id);
|
|
611
|
+
currentSessionIsLive = currentSessionId !== '' && !isSessionStale(session);
|
|
595
612
|
if (currentSessionId && currentSessionIsLive) {
|
|
596
613
|
candidateDirs.push(join(stateDir, 'sessions', currentSessionId));
|
|
597
614
|
}
|
|
@@ -610,7 +627,7 @@ async function resolveActiveTeamState(): Promise<ActiveTeamResult> {
|
|
|
610
627
|
.catch(() => null);
|
|
611
628
|
if (!parsed || typeof parsed !== 'object' || parsed.active !== true) continue;
|
|
612
629
|
|
|
613
|
-
const teamName =
|
|
630
|
+
const teamName = normalizeValidTeamName(parsed.team_name);
|
|
614
631
|
if (!teamName) continue;
|
|
615
632
|
|
|
616
633
|
const teamConfigDir = join(stateDir, 'team', teamName);
|
|
@@ -653,7 +670,9 @@ async function resolveActiveTeamState(): Promise<ActiveTeamResult> {
|
|
|
653
670
|
|
|
654
671
|
const canonicalFallbackTeams = await listNotifyCanonicalActiveTeams(cwd, currentSessionId).catch(() => []);
|
|
655
672
|
for (const team of canonicalFallbackTeams) {
|
|
656
|
-
const
|
|
673
|
+
const teamName = normalizeValidTeamName(team.teamName);
|
|
674
|
+
if (!teamName) continue;
|
|
675
|
+
const teamConfigDir = join(stateDir, 'team', teamName);
|
|
657
676
|
const manifestPath = join(teamConfigDir, 'manifest.v2.json');
|
|
658
677
|
const configPath = join(teamConfigDir, 'config.json');
|
|
659
678
|
const teamConfigPath = existsSync(manifestPath) ? manifestPath : configPath;
|
|
@@ -678,10 +697,10 @@ async function resolveActiveTeamState(): Promise<ActiveTeamResult> {
|
|
|
678
697
|
path: team.path,
|
|
679
698
|
state: {
|
|
680
699
|
active: true,
|
|
681
|
-
team_name:
|
|
700
|
+
team_name: teamName,
|
|
682
701
|
current_phase: team.phase,
|
|
683
702
|
},
|
|
684
|
-
team_name:
|
|
703
|
+
team_name: teamName,
|
|
685
704
|
pane_count: paneStatus.paneCount,
|
|
686
705
|
};
|
|
687
706
|
}
|
|
@@ -755,10 +774,13 @@ async function readRalphSteerLock(path: string): Promise<RalphSteerLockRecord |
|
|
|
755
774
|
}
|
|
756
775
|
}
|
|
757
776
|
|
|
777
|
+
const RALPH_STEER_LOCK_MAX_RETRIES = 5;
|
|
778
|
+
|
|
758
779
|
async function withRalphSteerLock<T>(task: () => Promise<T>): Promise<T | null> {
|
|
759
780
|
await mkdir(dirname(ralphSteerLockPath), { recursive: true }).catch(() => {});
|
|
760
781
|
|
|
761
|
-
|
|
782
|
+
let acquired = false;
|
|
783
|
+
for (let attempt = 0; attempt < RALPH_STEER_LOCK_MAX_RETRIES; attempt++) {
|
|
762
784
|
let handle;
|
|
763
785
|
try {
|
|
764
786
|
handle = await open(ralphSteerLockPath, 'wx');
|
|
@@ -767,6 +789,7 @@ async function withRalphSteerLock<T>(task: () => Promise<T>): Promise<T | null>
|
|
|
767
789
|
acquired_at: new Date().toISOString(),
|
|
768
790
|
};
|
|
769
791
|
await handle.writeFile(JSON.stringify(payload, null, 2));
|
|
792
|
+
acquired = true;
|
|
770
793
|
break;
|
|
771
794
|
} catch (error) {
|
|
772
795
|
const code = error !== null && typeof error === 'object' ? (error as NodeJS.ErrnoException).code : '';
|
|
@@ -786,6 +809,11 @@ async function withRalphSteerLock<T>(task: () => Promise<T>): Promise<T | null>
|
|
|
786
809
|
}
|
|
787
810
|
}
|
|
788
811
|
|
|
812
|
+
if (!acquired) {
|
|
813
|
+
lastRalphContinueSteer.last_reason = 'global_lock_exhausted';
|
|
814
|
+
return null;
|
|
815
|
+
}
|
|
816
|
+
|
|
789
817
|
try {
|
|
790
818
|
return await task();
|
|
791
819
|
} finally {
|
|
@@ -847,18 +875,18 @@ async function readRalphProgressGate(
|
|
|
847
875
|
return { allow: true, reason: 'progress_stale', progress_at: progressAt, subagent_session_id: subagentSessionId };
|
|
848
876
|
}
|
|
849
877
|
|
|
850
|
-
function shouldSkipRalphContinue(now: number, candidateIso: string
|
|
878
|
+
function shouldSkipRalphContinue(now: number, candidateIso: string): { skip: boolean; reason: string; anchorMs: number; anchorIso: string } {
|
|
851
879
|
const sharedMs = parseIsoMillis(candidateIso);
|
|
852
880
|
const localMs = parseIsoMillis(lastRalphContinueSteer.last_sent_at);
|
|
853
|
-
const startupAnchorIso = lastRalphContinueSteer.cooldown_anchor_at
|
|
881
|
+
const startupAnchorIso = lastRalphContinueSteer.cooldown_anchor_at;
|
|
854
882
|
const startupAnchorMs = parseIsoMillis(startupAnchorIso);
|
|
855
|
-
const startupCooldown = sharedMs === null && localMs === null;
|
|
856
|
-
const anchorMs = sharedMs ?? localMs ?? startupAnchorMs ??
|
|
883
|
+
const startupCooldown = sharedMs === null && localMs === null && startupAnchorMs !== null;
|
|
884
|
+
const anchorMs = sharedMs ?? localMs ?? startupAnchorMs ?? 0;
|
|
857
885
|
const anchorIso = sharedMs !== null
|
|
858
886
|
? candidateIso
|
|
859
887
|
: (localMs !== null ? lastRalphContinueSteer.last_sent_at : startupAnchorIso);
|
|
860
888
|
return {
|
|
861
|
-
skip: now - anchorMs < RALPH_CONTINUE_CADENCE_MS,
|
|
889
|
+
skip: anchorMs > 0 && now - anchorMs < RALPH_CONTINUE_CADENCE_MS,
|
|
862
890
|
reason: startupCooldown ? 'startup_cooldown' : (sharedMs !== null ? 'global_cooldown' : 'cooldown'),
|
|
863
891
|
anchorMs,
|
|
864
892
|
anchorIso,
|
|
@@ -1004,7 +1032,6 @@ async function writePidFileRecord(): Promise<void> {
|
|
|
1004
1032
|
async function runRalphContinueSteerTick(): Promise<void> {
|
|
1005
1033
|
const now = Date.now();
|
|
1006
1034
|
const nowIso = new Date(now).toISOString();
|
|
1007
|
-
const startupIso = new Date(startedAt).toISOString();
|
|
1008
1035
|
const activeRalph = await resolveActiveRalphState();
|
|
1009
1036
|
const activePaneId = safeString(activeRalph.state?.tmux_pane_id).trim();
|
|
1010
1037
|
lastRalphContinueSteer = {
|
|
@@ -1030,13 +1057,9 @@ async function runRalphContinueSteerTick(): Promise<void> {
|
|
|
1030
1057
|
return;
|
|
1031
1058
|
}
|
|
1032
1059
|
|
|
1033
|
-
if (parseIsoMillis(lastRalphContinueSteer.last_sent_at) === null && parseIsoMillis(lastRalphContinueSteer.cooldown_anchor_at) === null) {
|
|
1034
|
-
lastRalphContinueSteer.cooldown_anchor_at = startupIso;
|
|
1035
|
-
}
|
|
1036
|
-
|
|
1037
1060
|
const sharedBeforeLock = await readRalphSteerTimestamp();
|
|
1038
1061
|
lastRalphContinueSteer.shared_last_sent_at = sharedBeforeLock;
|
|
1039
|
-
const initialCooldown = shouldSkipRalphContinue(now, sharedBeforeLock
|
|
1062
|
+
const initialCooldown = shouldSkipRalphContinue(now, sharedBeforeLock);
|
|
1040
1063
|
if (initialCooldown.skip) {
|
|
1041
1064
|
lastRalphContinueSteer.last_reason = initialCooldown.reason;
|
|
1042
1065
|
if (!sharedBeforeLock && initialCooldown.reason === 'startup_cooldown') {
|
|
@@ -1048,7 +1071,7 @@ async function runRalphContinueSteerTick(): Promise<void> {
|
|
|
1048
1071
|
const outcome = await withRalphSteerLock(async () => {
|
|
1049
1072
|
const sharedLastSentAt = await readRalphSteerTimestamp();
|
|
1050
1073
|
lastRalphContinueSteer.shared_last_sent_at = sharedLastSentAt;
|
|
1051
|
-
const cooldown = shouldSkipRalphContinue(Date.now(), sharedLastSentAt
|
|
1074
|
+
const cooldown = shouldSkipRalphContinue(Date.now(), sharedLastSentAt);
|
|
1052
1075
|
if (cooldown.skip) {
|
|
1053
1076
|
lastRalphContinueSteer.last_reason = cooldown.reason;
|
|
1054
1077
|
if (!sharedLastSentAt && cooldown.reason === 'startup_cooldown') {
|
|
@@ -3,6 +3,7 @@ import { readdir } from 'fs/promises';
|
|
|
3
3
|
import { join } from 'path';
|
|
4
4
|
import { readTeamManifestV2, readTeamPhase } from '../../team/state.js';
|
|
5
5
|
import { resolveCanonicalTeamStateRoot } from '../../team/state-root.js';
|
|
6
|
+
import { TEAM_NAME_SAFE_PATTERN } from '../../team/contracts.js';
|
|
6
7
|
import { isTerminalPhase, safeString } from './utils.js';
|
|
7
8
|
|
|
8
9
|
export interface NotifyCanonicalActiveTeam {
|
|
@@ -28,7 +29,7 @@ export async function listNotifyCanonicalActiveTeams(
|
|
|
28
29
|
for (const entry of entries) {
|
|
29
30
|
if (!entry.isDirectory()) continue;
|
|
30
31
|
const teamName = entry.name.trim();
|
|
31
|
-
if (!teamName) continue;
|
|
32
|
+
if (!teamName || !TEAM_NAME_SAFE_PATTERN.test(teamName)) continue;
|
|
32
33
|
|
|
33
34
|
const [manifest, phaseState] = await Promise.all([
|
|
34
35
|
readTeamManifestV2(teamName, cwd),
|
|
@@ -128,7 +128,22 @@ function isActiveRalphCandidate(state: Record<string, unknown> | null): state is
|
|
|
128
128
|
return state.active === true && !isTerminalRalphPhase(state.current_phase);
|
|
129
129
|
}
|
|
130
130
|
|
|
131
|
-
|
|
131
|
+
function readSessionIdFromEnvironment(env: NodeJS.ProcessEnv = process.env): string {
|
|
132
|
+
const candidates = [env.OMX_SESSION_ID, env.CODEX_SESSION_ID, env.SESSION_ID];
|
|
133
|
+
for (const candidate of candidates) {
|
|
134
|
+
const sessionId = safeString(candidate).trim();
|
|
135
|
+
if (SESSION_ID_PATTERN.test(sessionId)) return sessionId;
|
|
136
|
+
}
|
|
137
|
+
return '';
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
async function readCurrentOmxSessionId(stateDir: string, env: NodeJS.ProcessEnv = process.env): Promise<string> {
|
|
141
|
+
const envSessionId = readSessionIdFromEnvironment(env);
|
|
142
|
+
if (envSessionId) {
|
|
143
|
+
const envScopedDir = join(stateDir, 'sessions', envSessionId);
|
|
144
|
+
if (existsSync(envScopedDir)) return envSessionId;
|
|
145
|
+
}
|
|
146
|
+
|
|
132
147
|
const session = await readUsableSessionState(resolve(stateDir, '..', '..'));
|
|
133
148
|
const sessionId = safeString(session?.session_id).trim();
|
|
134
149
|
return SESSION_ID_PATTERN.test(sessionId) ? sessionId : '';
|
|
@@ -194,7 +209,7 @@ export async function reconcileRalphSessionResume({
|
|
|
194
209
|
const lockedResult = await withRalphResumeLock(stateDir, async () => {
|
|
195
210
|
await hooks?.afterLockAcquired?.();
|
|
196
211
|
|
|
197
|
-
const currentOmxSessionId = await readCurrentOmxSessionId(stateDir);
|
|
212
|
+
const currentOmxSessionId = await readCurrentOmxSessionId(stateDir, env);
|
|
198
213
|
if (!currentOmxSessionId) {
|
|
199
214
|
return {
|
|
200
215
|
currentOmxSessionId: '',
|
|
@@ -25,7 +25,23 @@ function isSafeStateFileName(fileName: string): boolean {
|
|
|
25
25
|
&& !fileName.includes('\\');
|
|
26
26
|
}
|
|
27
27
|
|
|
28
|
+
function readSessionIdFromEnvironment(env: NodeJS.ProcessEnv = process.env): string | undefined {
|
|
29
|
+
const candidates = [env.OMX_SESSION_ID, env.CODEX_SESSION_ID, env.SESSION_ID];
|
|
30
|
+
for (const candidate of candidates) {
|
|
31
|
+
const sessionId = safeString(candidate).trim();
|
|
32
|
+
if (!SESSION_ID_PATTERN.test(sessionId)) continue;
|
|
33
|
+
return sessionId;
|
|
34
|
+
}
|
|
35
|
+
return undefined;
|
|
36
|
+
}
|
|
37
|
+
|
|
28
38
|
export async function readCurrentSessionId(baseStateDir: string): Promise<string | undefined> {
|
|
39
|
+
const envSessionId = readSessionIdFromEnvironment();
|
|
40
|
+
if (envSessionId) {
|
|
41
|
+
const envScopedDir = join(baseStateDir, 'sessions', envSessionId);
|
|
42
|
+
if (existsSync(envScopedDir)) return envSessionId;
|
|
43
|
+
}
|
|
44
|
+
|
|
29
45
|
const cwd = resolve(baseStateDir, '..', '..');
|
|
30
46
|
const session = await readUsableSessionState(cwd);
|
|
31
47
|
const sessionId = safeString(session?.session_id);
|
|
@@ -23,6 +23,8 @@ import { isLeaderRuntimeStale } from '../../team/leader-activity.js';
|
|
|
23
23
|
import { appendTeamDeliveryLog } from '../../team/delivery-log.js';
|
|
24
24
|
import { writeTeamLeaderAttention } from '../../team/state.js';
|
|
25
25
|
import { readLatestTeamProgressEvidenceMs } from '../../team/progress-evidence.js';
|
|
26
|
+
import { validateSessionId } from '../../mcp/state-paths.js';
|
|
27
|
+
import { TEAM_NAME_SAFE_PATTERN } from '../../team/contracts.js';
|
|
26
28
|
const LEADER_PANE_MISSING_NO_INJECTION_REASON = 'leader_pane_missing_no_injection';
|
|
27
29
|
const LEADER_PANE_SHELL_NO_INJECTION_REASON = 'leader_pane_shell_no_injection';
|
|
28
30
|
const LEADER_PANE_SAME_CLASSIFIED_STATE_SUPPRESSED_REASON = 'pane_already_shows_same_classified_state';
|
|
@@ -34,6 +36,21 @@ const ACK_LIKE_PATTERNS = [
|
|
|
34
36
|
/^(?:on it|will do|i(?:'|')ll do it|working on it)[.!]*$/i,
|
|
35
37
|
];
|
|
36
38
|
|
|
39
|
+
function normalizeValidSessionId(value) {
|
|
40
|
+
const trimmed = safeString(value).trim();
|
|
41
|
+
if (!trimmed) return '';
|
|
42
|
+
try {
|
|
43
|
+
return validateSessionId(trimmed) ?? '';
|
|
44
|
+
} catch {
|
|
45
|
+
return '';
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function normalizeValidTeamName(value) {
|
|
50
|
+
const trimmed = safeString(value).trim();
|
|
51
|
+
return TEAM_NAME_SAFE_PATTERN.test(trimmed) ? trimmed : '';
|
|
52
|
+
}
|
|
53
|
+
|
|
37
54
|
export function resolveLeaderNudgeIntervalMs() {
|
|
38
55
|
const raw = safeString(process.env.OMX_TEAM_LEADER_NUDGE_MS || '');
|
|
39
56
|
const parsed = asNumber(raw);
|
|
@@ -251,8 +268,9 @@ async function resolveCurrentSessionId(stateDir) {
|
|
|
251
268
|
|| process.env.SESSION_ID
|
|
252
269
|
|| '',
|
|
253
270
|
).trim();
|
|
254
|
-
|
|
255
|
-
|
|
271
|
+
const envSessionId = normalizeValidSessionId(fromEnv);
|
|
272
|
+
if (envSessionId) return envSessionId;
|
|
273
|
+
return normalizeValidSessionId((await readUsableSessionState(resolve(stateDir, '..', '..')))?.session_id);
|
|
256
274
|
}
|
|
257
275
|
|
|
258
276
|
async function readWorkerStatusSnapshot(stateDir, teamName, workerName) {
|
|
@@ -601,7 +619,7 @@ export async function maybeNudgeTeamLeader({
|
|
|
601
619
|
if (!existsSync(teamStatePath)) continue;
|
|
602
620
|
const parsed = JSON.parse(await readFile(teamStatePath, 'utf-8'));
|
|
603
621
|
if (!parsed) continue;
|
|
604
|
-
const teamName =
|
|
622
|
+
const teamName = normalizeValidTeamName(parsed.team_name || '');
|
|
605
623
|
if (!teamName) continue;
|
|
606
624
|
|
|
607
625
|
const phaseSnapshot = await readTeamPhaseSnapshot(stateDir, teamName, nowIso);
|
|
@@ -619,7 +637,9 @@ export async function maybeNudgeTeamLeader({
|
|
|
619
637
|
|
|
620
638
|
const canonicalFallbackTeams = await listNotifyCanonicalActiveTeams(cwd, currentSessionId).catch(() => []);
|
|
621
639
|
for (const team of canonicalFallbackTeams) {
|
|
622
|
-
|
|
640
|
+
const teamName = normalizeValidTeamName(team.teamName);
|
|
641
|
+
if (!teamName) continue;
|
|
642
|
+
candidateTeamNames.add(teamName);
|
|
623
643
|
}
|
|
624
644
|
|
|
625
645
|
// Use pre-computed staleness (captured before HUD state was updated this turn)
|
|
@@ -586,13 +586,8 @@ async function main() {
|
|
|
586
586
|
shouldSendSessionIdleHookEvent,
|
|
587
587
|
recordSessionIdleHookEventSent,
|
|
588
588
|
} = await import('../notifications/idle-cooldown.js');
|
|
589
|
-
const sessionJsonPath = join(stateDir, 'session.json');
|
|
590
589
|
const idleFingerprint = buildIdleNotificationFingerprint(payload);
|
|
591
|
-
|
|
592
|
-
try {
|
|
593
|
-
const sessionData = JSON.parse(await readFile(sessionJsonPath, 'utf-8'));
|
|
594
|
-
notifySessionId = safeString(sessionData && sessionData.session_id ? sessionData.session_id : '');
|
|
595
|
-
} catch { /* no session file */ }
|
|
590
|
+
const notifySessionId = getEffectiveSessionId();
|
|
596
591
|
|
|
597
592
|
const shouldNotifyLifecycle = notifySessionId
|
|
598
593
|
&& shouldSendIdleNotification(stateDir, notifySessionId, idleFingerprint);
|
package/templates/AGENTS.md
CHANGED
|
@@ -218,7 +218,7 @@ Runtime availability gate:
|
|
|
218
218
|
| "autopilot", "build me", "I want a" | `$autopilot` | Runtime-only: read `~/.codex/skills/autopilot/SKILL.md`, execute autonomous pipeline only inside OMX CLI/runtime |
|
|
219
219
|
| "ultrawork", "ulw", "parallel" | `$ultrawork` | Runtime-only: read `~/.codex/skills/ultrawork/SKILL.md`, execute parallel agents only inside OMX CLI/runtime |
|
|
220
220
|
| "ultraqa" | `$ultraqa` | Runtime-only: read `~/.codex/skills/ralph/SKILL.md`, run persistent completion and verification loop only inside OMX CLI/runtime (UltraQA compatibility alias) |
|
|
221
|
-
| "analyze", "investigate" | `$analyze` | Read `~/.codex/
|
|
221
|
+
| "analyze", "investigate" | `$analyze` | Read `~/.codex/skills/analyze/SKILL.md`, run read-only deep analysis with ranked synthesis, explicit confidence, and concrete file references |
|
|
222
222
|
| "plan this", "plan the", "let's plan" | `$plan` | Read `~/.codex/skills/plan/SKILL.md`, start planning workflow |
|
|
223
223
|
| "interview", "deep interview", "gather requirements", "interview me", "don't assume", "ouroboros" | `$deep-interview` | Read `~/.codex/skills/deep-interview/SKILL.md`, run Ouroboros-inspired Socratic ambiguity-gated interview workflow |
|
|
224
224
|
| "ralplan", "consensus plan" | `$ralplan` | Read `~/.codex/skills/ralplan/SKILL.md`, start consensus planning with RALPLAN-DR structured deliberation (short by default, `--deliberate` for high-risk) |
|
|
@@ -41,8 +41,7 @@
|
|
|
41
41
|
{
|
|
42
42
|
"name": "ultraqa",
|
|
43
43
|
"category": "execution",
|
|
44
|
-
"status": "
|
|
45
|
-
"canonical": "ralph",
|
|
44
|
+
"status": "active",
|
|
46
45
|
"core": false,
|
|
47
46
|
"internalRequired": false
|
|
48
47
|
},
|
|
@@ -79,8 +78,7 @@
|
|
|
79
78
|
{
|
|
80
79
|
"name": "analyze",
|
|
81
80
|
"category": "shortcut",
|
|
82
|
-
"status": "
|
|
83
|
-
"canonical": "debugger",
|
|
81
|
+
"status": "active",
|
|
84
82
|
"core": false,
|
|
85
83
|
"internalRequired": false
|
|
86
84
|
},
|