oh-my-codex 0.17.2 → 0.18.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Cargo.lock +13 -5
- package/Cargo.toml +2 -1
- package/README.md +1 -0
- package/crates/omx-api/Cargo.toml +19 -0
- package/crates/omx-api/src/lib.rs +2940 -0
- package/crates/omx-api/src/main.rs +10 -0
- package/crates/omx-api/tests/cli.rs +558 -0
- package/crates/omx-explore/src/main.rs +4 -0
- package/crates/omx-sparkshell/src/codex_bridge.rs +437 -123
- package/crates/omx-sparkshell/src/exec.rs +4 -0
- package/crates/omx-sparkshell/src/main.rs +738 -29
- package/crates/omx-sparkshell/src/prompt.rs +25 -3
- package/crates/omx-sparkshell/src/redaction.rs +241 -0
- package/crates/omx-sparkshell/tests/execution.rs +479 -238
- package/dist/cli/__tests__/api.test.d.ts +2 -0
- package/dist/cli/__tests__/api.test.d.ts.map +1 -0
- package/dist/cli/__tests__/api.test.js +175 -0
- package/dist/cli/__tests__/api.test.js.map +1 -0
- package/dist/cli/__tests__/ask.test.js +72 -5
- package/dist/cli/__tests__/ask.test.js.map +1 -1
- package/dist/cli/__tests__/autoresearch-goal.test.js +14 -1
- package/dist/cli/__tests__/autoresearch-goal.test.js.map +1 -1
- package/dist/cli/__tests__/doctor-warning-copy.test.js +51 -0
- package/dist/cli/__tests__/doctor-warning-copy.test.js.map +1 -1
- package/dist/cli/__tests__/explore.test.js +23 -0
- package/dist/cli/__tests__/explore.test.js.map +1 -1
- package/dist/cli/__tests__/index.test.js +123 -5
- package/dist/cli/__tests__/index.test.js.map +1 -1
- package/dist/cli/__tests__/launch-fallback.test.js +76 -0
- package/dist/cli/__tests__/launch-fallback.test.js.map +1 -1
- package/dist/cli/__tests__/package-bin-contract.test.js +4 -3
- package/dist/cli/__tests__/package-bin-contract.test.js.map +1 -1
- package/dist/cli/__tests__/question.test.js +45 -22
- package/dist/cli/__tests__/question.test.js.map +1 -1
- package/dist/cli/__tests__/setup-agents-overwrite.test.js +2 -0
- package/dist/cli/__tests__/setup-agents-overwrite.test.js.map +1 -1
- package/dist/cli/__tests__/setup-install-mode.test.js +138 -0
- package/dist/cli/__tests__/setup-install-mode.test.js.map +1 -1
- package/dist/cli/__tests__/setup-scope.test.js +8 -2
- package/dist/cli/__tests__/setup-scope.test.js.map +1 -1
- package/dist/cli/__tests__/sparkshell-cli.test.js +5 -0
- package/dist/cli/__tests__/sparkshell-cli.test.js.map +1 -1
- package/dist/cli/__tests__/version-sync-contract.test.js +4 -0
- package/dist/cli/__tests__/version-sync-contract.test.js.map +1 -1
- package/dist/cli/__tests__/windows-popup-loop-contract.test.js +1 -1
- package/dist/cli/__tests__/windows-popup-loop-contract.test.js.map +1 -1
- package/dist/cli/api.d.ts +26 -0
- package/dist/cli/api.d.ts.map +1 -0
- package/dist/cli/api.js +153 -0
- package/dist/cli/api.js.map +1 -0
- package/dist/cli/doctor.d.ts.map +1 -1
- package/dist/cli/doctor.js +39 -4
- package/dist/cli/doctor.js.map +1 -1
- package/dist/cli/explore.d.ts +2 -0
- package/dist/cli/explore.d.ts.map +1 -1
- package/dist/cli/explore.js +43 -1
- package/dist/cli/explore.js.map +1 -1
- package/dist/cli/index.d.ts +10 -4
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +128 -10
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/native-assets.d.ts +2 -1
- package/dist/cli/native-assets.d.ts.map +1 -1
- package/dist/cli/native-assets.js +1 -0
- package/dist/cli/native-assets.js.map +1 -1
- package/dist/cli/setup.d.ts.map +1 -1
- package/dist/cli/setup.js +6 -1
- package/dist/cli/setup.js.map +1 -1
- package/dist/cli/sparkshell.d.ts.map +1 -1
- package/dist/cli/sparkshell.js +20 -3
- package/dist/cli/sparkshell.js.map +1 -1
- package/dist/config/generator.d.ts.map +1 -1
- package/dist/config/generator.js +90 -0
- package/dist/config/generator.js.map +1 -1
- package/dist/hooks/__tests__/best-practice-research-skill.test.d.ts +2 -0
- package/dist/hooks/__tests__/best-practice-research-skill.test.d.ts.map +1 -0
- package/dist/hooks/__tests__/best-practice-research-skill.test.js +27 -0
- package/dist/hooks/__tests__/best-practice-research-skill.test.js.map +1 -0
- package/dist/hooks/__tests__/keyword-detector.test.js +11 -0
- package/dist/hooks/__tests__/keyword-detector.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-hook-team-leader-nudge.test.js +6 -0
- package/dist/hooks/__tests__/notify-hook-team-leader-nudge.test.js.map +1 -1
- package/dist/hooks/__tests__/prompt-guidance-wave-two.test.js +4 -0
- package/dist/hooks/__tests__/prompt-guidance-wave-two.test.js.map +1 -1
- package/dist/hooks/keyword-registry.d.ts.map +1 -1
- package/dist/hooks/keyword-registry.js +1 -0
- package/dist/hooks/keyword-registry.js.map +1 -1
- package/dist/hud/__tests__/reconcile.test.js +2 -2
- package/dist/hud/__tests__/reconcile.test.js.map +1 -1
- package/dist/hud/__tests__/tmux.test.js +23 -18
- package/dist/hud/__tests__/tmux.test.js.map +1 -1
- package/dist/hud/tmux.d.ts.map +1 -1
- package/dist/hud/tmux.js +7 -6
- package/dist/hud/tmux.js.map +1 -1
- package/dist/mcp/__tests__/bootstrap.test.js +75 -1
- package/dist/mcp/__tests__/bootstrap.test.js.map +1 -1
- package/dist/mcp/bootstrap.d.ts +3 -1
- package/dist/mcp/bootstrap.d.ts.map +1 -1
- package/dist/mcp/bootstrap.js +71 -2
- package/dist/mcp/bootstrap.js.map +1 -1
- package/dist/scripts/__tests__/codex-native-hook.test.js +737 -26
- package/dist/scripts/__tests__/codex-native-hook.test.js.map +1 -1
- package/dist/scripts/__tests__/notify-dispatcher.test.js +183 -1
- package/dist/scripts/__tests__/notify-dispatcher.test.js.map +1 -1
- package/dist/scripts/__tests__/smoke-packed-install.test.js +4 -1
- package/dist/scripts/__tests__/smoke-packed-install.test.js.map +1 -1
- package/dist/scripts/build-api.d.ts +2 -0
- package/dist/scripts/build-api.d.ts.map +1 -0
- package/dist/scripts/build-api.js +44 -0
- package/dist/scripts/build-api.js.map +1 -0
- package/dist/scripts/codex-native-hook.d.ts.map +1 -1
- package/dist/scripts/codex-native-hook.js +208 -8
- package/dist/scripts/codex-native-hook.js.map +1 -1
- package/dist/scripts/codex-native-pre-post.d.ts.map +1 -1
- package/dist/scripts/codex-native-pre-post.js +89 -24
- package/dist/scripts/codex-native-pre-post.js.map +1 -1
- package/dist/scripts/notify-dispatcher.js +88 -0
- package/dist/scripts/notify-dispatcher.js.map +1 -1
- package/dist/scripts/notify-hook/team-dispatch.d.ts.map +1 -1
- package/dist/scripts/notify-hook/team-dispatch.js +27 -9
- package/dist/scripts/notify-hook/team-dispatch.js.map +1 -1
- package/dist/scripts/notify-hook/team-leader-nudge.d.ts.map +1 -1
- package/dist/scripts/notify-hook/team-leader-nudge.js +26 -11
- package/dist/scripts/notify-hook/team-leader-nudge.js.map +1 -1
- package/dist/scripts/notify-hook/team-tmux-guard.d.ts +1 -0
- package/dist/scripts/notify-hook/team-tmux-guard.d.ts.map +1 -1
- package/dist/scripts/notify-hook/team-tmux-guard.js +38 -0
- package/dist/scripts/notify-hook/team-tmux-guard.js.map +1 -1
- package/dist/scripts/notify-hook/team-worker-stop.d.ts.map +1 -1
- package/dist/scripts/notify-hook/team-worker-stop.js +27 -14
- package/dist/scripts/notify-hook/team-worker-stop.js.map +1 -1
- package/dist/scripts/run-provider-advisor.js +9 -3
- package/dist/scripts/run-provider-advisor.js.map +1 -1
- package/dist/scripts/smoke-packed-install.d.ts +1 -1
- package/dist/scripts/smoke-packed-install.d.ts.map +1 -1
- package/dist/scripts/smoke-packed-install.js +2 -0
- package/dist/scripts/smoke-packed-install.js.map +1 -1
- package/dist/team/__tests__/runtime.test.js +2 -2
- package/dist/team/__tests__/runtime.test.js.map +1 -1
- package/dist/team/__tests__/tmux-session.test.js +153 -25
- package/dist/team/__tests__/tmux-session.test.js.map +1 -1
- package/dist/team/tmux-session.d.ts +1 -0
- package/dist/team/tmux-session.d.ts.map +1 -1
- package/dist/team/tmux-session.js +55 -10
- package/dist/team/tmux-session.js.map +1 -1
- package/dist/utils/__tests__/agents-md.test.js +45 -1
- package/dist/utils/__tests__/agents-md.test.js.map +1 -1
- package/dist/utils/agents-md.d.ts +2 -0
- package/dist/utils/agents-md.d.ts.map +1 -1
- package/dist/utils/agents-md.js +19 -0
- package/dist/utils/agents-md.js.map +1 -1
- package/dist/verification/__tests__/ci-rust-gates.test.js +85 -10
- package/dist/verification/__tests__/ci-rust-gates.test.js.map +1 -1
- package/dist/verification/__tests__/explore-harness-release-workflow.test.js +1 -0
- package/dist/verification/__tests__/explore-harness-release-workflow.test.js.map +1 -1
- package/package.json +4 -3
- package/plugins/oh-my-codex/.codex-plugin/plugin.json +1 -1
- package/plugins/oh-my-codex/skills/best-practice-research/SKILL.md +83 -0
- package/plugins/oh-my-codex/skills/deep-interview/SKILL.md +1 -0
- package/plugins/oh-my-codex/skills/ralplan/SKILL.md +1 -1
- package/prompts/researcher.md +15 -10
- package/skills/best-practice-research/SKILL.md +83 -0
- package/skills/deep-interview/SKILL.md +1 -0
- package/skills/ralplan/SKILL.md +1 -1
- package/src/scripts/__tests__/codex-native-hook.test.ts +810 -4
- package/src/scripts/__tests__/notify-dispatcher.test.ts +223 -1
- package/src/scripts/__tests__/smoke-packed-install.test.ts +8 -2
- package/src/scripts/build-api.ts +48 -0
- package/src/scripts/codex-native-hook.ts +262 -10
- package/src/scripts/codex-native-pre-post.ts +103 -24
- package/src/scripts/notify-dispatcher.ts +97 -0
- package/src/scripts/notify-hook/team-dispatch.ts +27 -8
- package/src/scripts/notify-hook/team-leader-nudge.ts +25 -11
- package/src/scripts/notify-hook/team-tmux-guard.ts +42 -0
- package/src/scripts/notify-hook/team-worker-stop.ts +24 -13
- package/src/scripts/run-provider-advisor.ts +11 -3
- package/src/scripts/smoke-packed-install.ts +2 -0
- package/templates/catalog-manifest.json +7 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { execFileSync } from "child_process";
|
|
2
2
|
import { closeSync, existsSync, openSync, readFileSync, readSync } from "fs";
|
|
3
|
-
import { appendFile, mkdir, readFile, readdir, writeFile } from "fs/promises";
|
|
3
|
+
import { appendFile, mkdir, readFile, readdir, stat, writeFile } from "fs/promises";
|
|
4
4
|
import { extname, join, relative, resolve } from "path";
|
|
5
5
|
import { pathToFileURL } from "url";
|
|
6
6
|
import { readModeState, readModeStateForActiveDecision, readModeStateForSession, updateModeState } from "../modes/base.js";
|
|
@@ -38,6 +38,7 @@ const TEAM_STOP_BLOCKING_TASK_STATUSES = new Set(["pending", "in_progress", "blo
|
|
|
38
38
|
const TEAM_WORKER_TERMINAL_RUN_STATES = new Set(["done", "complete", "completed", "failed", "stopped", "cancelled"]);
|
|
39
39
|
const NATIVE_STOP_STATE_FILE = "native-stop-state.json";
|
|
40
40
|
const ORDINARY_STOP_NO_PROGRESS_DEFAULT_MAX_REPEATS = 8;
|
|
41
|
+
const RALPH_ORPHANED_STARTING_STALE_MS = 15 * 60_000;
|
|
41
42
|
const ORDINARY_STOP_NO_PROGRESS_DEFAULT_IDLE_MS = 10 * 60_000;
|
|
42
43
|
const ORDINARY_STOP_NO_PROGRESS_MAX_MESSAGE_LENGTH = 240;
|
|
43
44
|
const STABLE_FINAL_RECOMMENDATION_PATTERNS = [
|
|
@@ -344,6 +345,47 @@ async function readActiveAutoresearchState(cwd, sessionId) {
|
|
|
344
345
|
function isRalphStartingPhase(state) {
|
|
345
346
|
return safeString(state.current_phase ?? state.currentPhase).trim().toLowerCase() === "starting";
|
|
346
347
|
}
|
|
348
|
+
function parseTimestampMs(value) {
|
|
349
|
+
const text = safeString(value).trim();
|
|
350
|
+
if (!text)
|
|
351
|
+
return null;
|
|
352
|
+
const ms = Date.parse(text);
|
|
353
|
+
return Number.isFinite(ms) ? ms : null;
|
|
354
|
+
}
|
|
355
|
+
function numericValue(value) {
|
|
356
|
+
if (typeof value === "number" && Number.isFinite(value))
|
|
357
|
+
return value;
|
|
358
|
+
if (typeof value === "string" && value.trim()) {
|
|
359
|
+
const parsed = Number(value);
|
|
360
|
+
return Number.isFinite(parsed) ? parsed : null;
|
|
361
|
+
}
|
|
362
|
+
return null;
|
|
363
|
+
}
|
|
364
|
+
function hasRalphOwnerHint(state) {
|
|
365
|
+
return [
|
|
366
|
+
state.owner_omx_session_id,
|
|
367
|
+
state.owner_codex_session_id,
|
|
368
|
+
state.owner_codex_thread_id,
|
|
369
|
+
state.thread_id,
|
|
370
|
+
state.tmux_pane_id,
|
|
371
|
+
state.task_slug,
|
|
372
|
+
].some((value) => safeString(value).trim() !== "");
|
|
373
|
+
}
|
|
374
|
+
async function isStaleOrphanedRalphStartingState(state, path, nowMs = Date.now()) {
|
|
375
|
+
if (!isRalphStartingPhase(state))
|
|
376
|
+
return false;
|
|
377
|
+
if (numericValue(state.iteration) !== 0)
|
|
378
|
+
return false;
|
|
379
|
+
if (hasRalphOwnerHint(state))
|
|
380
|
+
return false;
|
|
381
|
+
const timestampMs = parseTimestampMs(state.updated_at)
|
|
382
|
+
?? parseTimestampMs(state.started_at)
|
|
383
|
+
?? parseTimestampMs(state.created_at)
|
|
384
|
+
?? await stat(path).then((info) => info.mtimeMs, () => null);
|
|
385
|
+
if (timestampMs === null)
|
|
386
|
+
return false;
|
|
387
|
+
return nowMs - timestampMs > RALPH_ORPHANED_STARTING_STALE_MS;
|
|
388
|
+
}
|
|
347
389
|
function hasValue(values, value) {
|
|
348
390
|
return value !== "" && values.some((candidate) => candidate === value);
|
|
349
391
|
}
|
|
@@ -410,6 +452,89 @@ async function hasConsistentRalphSkillActivation(stateDir, sessionId) {
|
|
|
410
452
|
return false;
|
|
411
453
|
return true;
|
|
412
454
|
}
|
|
455
|
+
function isShadowableRalphStartingSeed(state) {
|
|
456
|
+
if (state.active !== true)
|
|
457
|
+
return false;
|
|
458
|
+
if (!isRalphStartingPhase(state))
|
|
459
|
+
return false;
|
|
460
|
+
if (state.completion_audit || state.completionAudit)
|
|
461
|
+
return false;
|
|
462
|
+
const iteration = numericValue(state.iteration);
|
|
463
|
+
return iteration === null || iteration <= 0;
|
|
464
|
+
}
|
|
465
|
+
function hasPassingCompletedRalphAudit(state, cwd) {
|
|
466
|
+
if (!state)
|
|
467
|
+
return false;
|
|
468
|
+
if (state.mode && safeString(state.mode) !== "ralph")
|
|
469
|
+
return false;
|
|
470
|
+
if (!isRalphCompletePhase(state.current_phase ?? state.currentPhase))
|
|
471
|
+
return false;
|
|
472
|
+
if (state.active === true)
|
|
473
|
+
return false;
|
|
474
|
+
return evaluateRalphCompletionAuditEvidence(state, cwd).complete === true;
|
|
475
|
+
}
|
|
476
|
+
function shouldRetireShadowedRalphStartingSeed(seedState, completedState, cwd, ownerContext) {
|
|
477
|
+
if (!isShadowableRalphStartingSeed(seedState))
|
|
478
|
+
return false;
|
|
479
|
+
if (!hasPassingCompletedRalphAudit(completedState, cwd))
|
|
480
|
+
return false;
|
|
481
|
+
if (!completedState)
|
|
482
|
+
return false;
|
|
483
|
+
const completedSessionId = safeString(ownerContext?.completedSessionId ?? completedState.session_id).trim();
|
|
484
|
+
if (completedSessionId
|
|
485
|
+
&& !activeRalphStateMatchesStopOwner(completedState, {
|
|
486
|
+
sessionId: completedSessionId,
|
|
487
|
+
payloadSessionId: safeString(ownerContext?.payloadSessionId).trim(),
|
|
488
|
+
threadId: safeString(ownerContext?.threadId).trim(),
|
|
489
|
+
currentNativeSessionId: safeString(ownerContext?.currentNativeSessionId).trim(),
|
|
490
|
+
tmuxPaneId: safeString(ownerContext?.tmuxPaneId).trim(),
|
|
491
|
+
})) {
|
|
492
|
+
return false;
|
|
493
|
+
}
|
|
494
|
+
const seedThreadId = safeString(seedState.owner_codex_thread_id ?? seedState.thread_id).trim();
|
|
495
|
+
const completedThreadId = safeString(completedState?.owner_codex_thread_id ?? completedState?.thread_id).trim();
|
|
496
|
+
const stopThreadId = safeString(ownerContext?.threadId).trim();
|
|
497
|
+
if (seedThreadId && completedThreadId && seedThreadId !== completedThreadId)
|
|
498
|
+
return false;
|
|
499
|
+
if (seedThreadId && stopThreadId && seedThreadId !== stopThreadId)
|
|
500
|
+
return false;
|
|
501
|
+
if (completedThreadId && stopThreadId && completedThreadId !== stopThreadId)
|
|
502
|
+
return false;
|
|
503
|
+
const seedPaneId = safeString(seedState.tmux_pane_id).trim();
|
|
504
|
+
const completedPaneId = safeString(completedState?.tmux_pane_id).trim();
|
|
505
|
+
const stopPaneId = safeString(ownerContext?.tmuxPaneId).trim();
|
|
506
|
+
if (seedPaneId && completedPaneId && seedPaneId !== completedPaneId)
|
|
507
|
+
return false;
|
|
508
|
+
if (seedPaneId && stopPaneId && seedPaneId !== stopPaneId)
|
|
509
|
+
return false;
|
|
510
|
+
if (completedPaneId && stopPaneId && completedPaneId !== stopPaneId)
|
|
511
|
+
return false;
|
|
512
|
+
const seedStartedAt = parseTimestampMs(seedState.started_at ?? seedState.startedAt);
|
|
513
|
+
const completedAt = parseTimestampMs(completedState?.completed_at ?? completedState?.completedAt);
|
|
514
|
+
if (completedAt === null)
|
|
515
|
+
return false;
|
|
516
|
+
if (seedStartedAt !== null && seedStartedAt > completedAt)
|
|
517
|
+
return false;
|
|
518
|
+
return true;
|
|
519
|
+
}
|
|
520
|
+
async function retireShadowedRalphStartingSeed(path, seedState, completedSessionId, completedPath, completedState) {
|
|
521
|
+
const nowIso = new Date().toISOString();
|
|
522
|
+
const completedAt = safeString(completedState.completed_at ?? completedState.completedAt).trim() || nowIso;
|
|
523
|
+
const next = {
|
|
524
|
+
...seedState,
|
|
525
|
+
active: false,
|
|
526
|
+
current_phase: "complete",
|
|
527
|
+
completed_at: completedAt,
|
|
528
|
+
stop_reason: "shadowed_by_completed_canonical_ralph",
|
|
529
|
+
shadowed_by_completed_canonical_ralph: {
|
|
530
|
+
session_id: completedSessionId,
|
|
531
|
+
state_path: completedPath,
|
|
532
|
+
completed_at: completedAt,
|
|
533
|
+
reconciled_at: nowIso,
|
|
534
|
+
},
|
|
535
|
+
};
|
|
536
|
+
await writeFile(path, JSON.stringify(next, null, 2));
|
|
537
|
+
}
|
|
413
538
|
async function readRalphCompletionAuditBlockState(cwd, stateDir, preferredSessionId, ownerContext) {
|
|
414
539
|
const [rawSessionInfo, usableSessionInfo] = await Promise.all([
|
|
415
540
|
readSessionState(cwd),
|
|
@@ -480,6 +605,12 @@ async function readActiveRalphState(cwd, stateDir, preferredSessionId, ownerCont
|
|
|
480
605
|
safeString(preferredSessionId).trim(),
|
|
481
606
|
currentOmxSessionId,
|
|
482
607
|
].filter(Boolean))];
|
|
608
|
+
const completedCanonicalPath = currentOmxSessionId
|
|
609
|
+
? getStateFilePath("ralph-state.json", cwd, currentOmxSessionId)
|
|
610
|
+
: "";
|
|
611
|
+
const completedCanonicalState = completedCanonicalPath
|
|
612
|
+
? await readJsonIfExists(completedCanonicalPath)
|
|
613
|
+
: null;
|
|
483
614
|
// Ralph Stop stays authoritative-scope-only once the Stop payload is session-bound.
|
|
484
615
|
// That is intentionally stricter than generic state MCP reads: do not scan sibling
|
|
485
616
|
// session scopes or fall back to root when a current/explicit session is in play.
|
|
@@ -492,10 +623,27 @@ async function readActiveRalphState(cwd, stateDir, preferredSessionId, ownerCont
|
|
|
492
623
|
}
|
|
493
624
|
const sessionScopedPath = getStateFilePath("ralph-state.json", cwd, sessionId);
|
|
494
625
|
const sessionScoped = await readJsonIfExists(sessionScopedPath);
|
|
495
|
-
if (sessionScoped?.active === true
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
626
|
+
if (sessionScoped?.active === true) {
|
|
627
|
+
if (currentOmxSessionId
|
|
628
|
+
&& sessionId !== currentOmxSessionId
|
|
629
|
+
&& completedCanonicalState
|
|
630
|
+
&& shouldRetireShadowedRalphStartingSeed(sessionScoped, completedCanonicalState, cwd, {
|
|
631
|
+
completedSessionId: currentOmxSessionId,
|
|
632
|
+
payloadSessionId: safeString(ownerContext?.payloadSessionId).trim(),
|
|
633
|
+
threadId: safeString(ownerContext?.threadId).trim(),
|
|
634
|
+
currentNativeSessionId,
|
|
635
|
+
tmuxPaneId: safeString(ownerContext?.tmuxPaneId).trim(),
|
|
636
|
+
})) {
|
|
637
|
+
await retireShadowedRalphStartingSeed(sessionScopedPath, sessionScoped, currentOmxSessionId, completedCanonicalPath, completedCanonicalState);
|
|
638
|
+
continue;
|
|
639
|
+
}
|
|
640
|
+
if (await isStaleOrphanedRalphStartingState(sessionScoped, sessionScopedPath)) {
|
|
641
|
+
continue;
|
|
642
|
+
}
|
|
643
|
+
if (isRalphStartingPhase(sessionScoped)
|
|
644
|
+
&& !(await isVisibleRalphActiveForSession(stateDir, sessionId))) {
|
|
645
|
+
continue;
|
|
646
|
+
}
|
|
499
647
|
}
|
|
500
648
|
if (sessionScoped?.active === true
|
|
501
649
|
&& shouldContinueRun(sessionScoped)
|
|
@@ -1345,11 +1493,14 @@ async function buildModeBasedStopOutput(mode, cwd, sessionId) {
|
|
|
1345
1493
|
if (!state || !shouldContinueRun(state))
|
|
1346
1494
|
return null;
|
|
1347
1495
|
const phase = formatPhase(state.current_phase);
|
|
1496
|
+
const systemMessage = mode === "autopilot" && phase.toLowerCase().replace(/_/g, "-") === "code-review"
|
|
1497
|
+
? "OMX autopilot is still active (phase: code-review). Run the required $code-review step before completing or clearing Autopilot state."
|
|
1498
|
+
: `OMX ${mode} is still active (phase: ${phase}).`;
|
|
1348
1499
|
return {
|
|
1349
1500
|
decision: "block",
|
|
1350
1501
|
reason: `OMX ${mode} is still active (phase: ${phase}); continue the task and gather fresh verification evidence before stopping.`,
|
|
1351
1502
|
stopReason: `${mode}_${phase}`,
|
|
1352
|
-
systemMessage
|
|
1503
|
+
systemMessage,
|
|
1353
1504
|
};
|
|
1354
1505
|
}
|
|
1355
1506
|
export function looksLikeGoalCompletionPrompt(text) {
|
|
@@ -1359,6 +1510,11 @@ export function looksLikeGoalCompletionPrompt(text) {
|
|
|
1359
1510
|
|| /\b(?:ultragoal|performance[-\s]goal|autoresearch[-\s]goal)\b.{0,80}\b(?:complete|checkpoint|finish|close|mark)\b/i.test(text)
|
|
1360
1511
|
|| /(?:^|[.!?]\s+)(?:the\s+)?goal\s+(?:is\s+|now\s+|has\s+been\s+)?(?:complete|completed|finished|closed)(?:\s*(?:[.!?]|$)|\s*[:;]\s*\S|\s*[—–-]\s*\S)/i.test(text);
|
|
1361
1512
|
}
|
|
1513
|
+
function reportsAutoresearchGoalObjectiveMismatch(text) {
|
|
1514
|
+
return /\bautoresearch[-\s]goal\b/i.test(text)
|
|
1515
|
+
&& /\b(?:complete|completion|reconciliation)\b/i.test(text)
|
|
1516
|
+
&& /objective mismatch/i.test(text);
|
|
1517
|
+
}
|
|
1362
1518
|
async function findActiveGoalWorkflowReconciliationRequirement(cwd) {
|
|
1363
1519
|
const ultragoal = await readJsonIfExists(join(cwd, ".omx", "ultragoal", "goals.json"));
|
|
1364
1520
|
const aggregateCompletion = safeObject(ultragoal?.aggregateCompletion);
|
|
@@ -1402,10 +1558,17 @@ async function findActiveGoalWorkflowReconciliationRequirement(cwd) {
|
|
|
1402
1558
|
const completion = await readJsonIfExists(join(autoresearchRoot, entry.name, "completion.json"));
|
|
1403
1559
|
const completionVerdict = safeString(completion?.verdict);
|
|
1404
1560
|
const completionPassed = completion?.passed === true || completionVerdict === "pass";
|
|
1405
|
-
if (mission?.workflow === "autoresearch-goal"
|
|
1561
|
+
if (mission?.workflow === "autoresearch-goal"
|
|
1562
|
+
&& status
|
|
1563
|
+
&& status !== "complete"
|
|
1564
|
+
&& completionPassed) {
|
|
1406
1565
|
return {
|
|
1407
1566
|
workflow: "autoresearch-goal",
|
|
1408
1567
|
command: `omx autoresearch-goal complete --slug ${safeString(mission.slug) || entry.name} --codex-goal-json '<get_goal JSON or path>'`,
|
|
1568
|
+
remediation: [
|
|
1569
|
+
"If that command fails with a Codex goal objective mismatch after a fresh get_goal snapshot, do not repeat the same complete command blindly in this thread.",
|
|
1570
|
+
"Either retry with a correct fresh snapshot or record an explicit blocked verdict for this autoresearch-goal and continue it from a fresh Codex thread.",
|
|
1571
|
+
].join(" "),
|
|
1409
1572
|
};
|
|
1410
1573
|
}
|
|
1411
1574
|
}
|
|
@@ -1431,6 +1594,9 @@ async function buildGoalWorkflowReconciliationStopOutput(payload, cwd) {
|
|
|
1431
1594
|
const requirement = await findActiveGoalWorkflowReconciliationRequirement(cwd);
|
|
1432
1595
|
if (!requirement)
|
|
1433
1596
|
return null;
|
|
1597
|
+
if (requirement.workflow === "autoresearch-goal" && reportsAutoresearchGoalObjectiveMismatch(lastAssistantMessage)) {
|
|
1598
|
+
return null;
|
|
1599
|
+
}
|
|
1434
1600
|
const systemMessage = [
|
|
1435
1601
|
`OMX ${requirement.workflow} requires get_goal snapshot reconciliation before completion; call get_goal and pass --codex-goal-json to ${requirement.command}.`,
|
|
1436
1602
|
requirement.remediation,
|
|
@@ -1533,7 +1699,7 @@ function readPayloadSessionId(payload) {
|
|
|
1533
1699
|
return safeString(payload.session_id ?? payload.sessionId).trim();
|
|
1534
1700
|
}
|
|
1535
1701
|
function readPayloadThreadId(payload) {
|
|
1536
|
-
return safeString(payload.thread_id ?? payload.threadId).trim();
|
|
1702
|
+
return safeString(payload.owner_codex_thread_id ?? payload.thread_id ?? payload.threadId).trim();
|
|
1537
1703
|
}
|
|
1538
1704
|
function readPayloadTurnId(payload) {
|
|
1539
1705
|
return safeString(payload.turn_id ?? payload.turnId).trim();
|
|
@@ -1608,6 +1774,8 @@ async function readBlockingSkillForStop(cwd, stateDir, sessionId, threadId, requ
|
|
|
1608
1774
|
const modeSnapshot = getRunContinuationSnapshot(modeState);
|
|
1609
1775
|
if (modeSnapshot?.terminal === true)
|
|
1610
1776
|
continue;
|
|
1777
|
+
if (await shouldIgnoreSessionSkillBlockerForCanonicalInactiveRoot(cwd, stateDir, skill, sessionId, threadId))
|
|
1778
|
+
continue;
|
|
1611
1779
|
const phase = formatPhase(modeState.current_phase, formatPhase(visibleEntries.find((entry) => entry.skill === skill)?.phase, "planning"));
|
|
1612
1780
|
if (TERMINAL_MODE_PHASES.has(phase.toLowerCase()) || phase === "completing") {
|
|
1613
1781
|
continue;
|
|
@@ -1648,6 +1816,38 @@ function isTerminalOrInactiveModeState(state) {
|
|
|
1648
1816
|
const phase = safeString(state.current_phase ?? state.currentPhase).trim().toLowerCase();
|
|
1649
1817
|
return phase !== "" && TERMINAL_MODE_PHASES.has(phase);
|
|
1650
1818
|
}
|
|
1819
|
+
function rootSkillStateHasNoActiveSkillForStopContext(rootState, skill, sessionId, threadId) {
|
|
1820
|
+
if (!rootState)
|
|
1821
|
+
return false;
|
|
1822
|
+
return !listActiveSkills(rootState).some((entry) => (entry.skill === skill
|
|
1823
|
+
&& matchesSkillStopContext(entry, rootState, sessionId, threadId)));
|
|
1824
|
+
}
|
|
1825
|
+
function rootModeStateIsCanonicalForStopContext(state, cwd, sessionId, threadId) {
|
|
1826
|
+
if (!modeStateMatchesSkillStopContext(state, cwd, sessionId))
|
|
1827
|
+
return false;
|
|
1828
|
+
const stateSessionId = safeString(state.owner_omx_session_id
|
|
1829
|
+
?? state.session_id
|
|
1830
|
+
?? state.codex_session_id
|
|
1831
|
+
?? state.owner_codex_session_id).trim();
|
|
1832
|
+
if (sessionId && stateSessionId !== sessionId)
|
|
1833
|
+
return false;
|
|
1834
|
+
const stateThreadId = safeString(state.owner_codex_thread_id ?? state.thread_id).trim();
|
|
1835
|
+
if (threadId && stateThreadId && stateThreadId !== threadId)
|
|
1836
|
+
return false;
|
|
1837
|
+
return true;
|
|
1838
|
+
}
|
|
1839
|
+
async function shouldIgnoreSessionSkillBlockerForCanonicalInactiveRoot(cwd, stateDir, skill, sessionId, threadId) {
|
|
1840
|
+
const rootModeState = await readJsonIfExists(join(stateDir, `${skill}-state.json`));
|
|
1841
|
+
if (!rootModeState)
|
|
1842
|
+
return false;
|
|
1843
|
+
if (!rootModeStateIsCanonicalForStopContext(rootModeState, cwd, sessionId, threadId))
|
|
1844
|
+
return false;
|
|
1845
|
+
if (!isTerminalOrInactiveModeState(rootModeState))
|
|
1846
|
+
return false;
|
|
1847
|
+
const { rootPath } = getSkillActiveStatePathsForStateDir(stateDir);
|
|
1848
|
+
const rootSkillState = await readSkillActiveState(rootPath);
|
|
1849
|
+
return rootSkillStateHasNoActiveSkillForStopContext(rootSkillState, skill, sessionId, threadId);
|
|
1850
|
+
}
|
|
1651
1851
|
async function readSessionScopedModeStateForRootSkill(cwd, stateDir, skill, sessionIds) {
|
|
1652
1852
|
for (const sessionId of sessionIds) {
|
|
1653
1853
|
const state = await readStopSessionPinnedState(`${skill}-state.json`, cwd, sessionId, stateDir);
|