oh-my-codex 0.18.15 → 0.18.16

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.
Files changed (42) hide show
  1. package/Cargo.lock +6 -6
  2. package/Cargo.toml +1 -1
  3. package/dist/cli/__tests__/doctor-artifact-ownership.test.js +63 -0
  4. package/dist/cli/__tests__/doctor-artifact-ownership.test.js.map +1 -1
  5. package/dist/cli/__tests__/session-search-help.test.js +3 -1
  6. package/dist/cli/__tests__/session-search-help.test.js.map +1 -1
  7. package/dist/cli/__tests__/session-search.test.js +55 -1
  8. package/dist/cli/__tests__/session-search.test.js.map +1 -1
  9. package/dist/cli/doctor.d.ts.map +1 -1
  10. package/dist/cli/doctor.js +6 -2
  11. package/dist/cli/doctor.js.map +1 -1
  12. package/dist/cli/index.d.ts +1 -1
  13. package/dist/cli/index.d.ts.map +1 -1
  14. package/dist/cli/index.js +1 -1
  15. package/dist/cli/session-search.d.ts +6 -0
  16. package/dist/cli/session-search.d.ts.map +1 -1
  17. package/dist/cli/session-search.js +104 -4
  18. package/dist/cli/session-search.js.map +1 -1
  19. package/dist/hud/__tests__/state.test.js +121 -0
  20. package/dist/hud/__tests__/state.test.js.map +1 -1
  21. package/dist/hud/state.d.ts.map +1 -1
  22. package/dist/hud/state.js +33 -2
  23. package/dist/hud/state.js.map +1 -1
  24. package/dist/hud/types.d.ts +1 -1
  25. package/dist/hud/types.d.ts.map +1 -1
  26. package/dist/scripts/__tests__/codex-native-hook.test.js +184 -0
  27. package/dist/scripts/__tests__/codex-native-hook.test.js.map +1 -1
  28. package/dist/scripts/codex-native-hook.d.ts.map +1 -1
  29. package/dist/scripts/codex-native-hook.js +159 -1
  30. package/dist/scripts/codex-native-hook.js.map +1 -1
  31. package/dist/session-history/__tests__/friction.test.d.ts +2 -0
  32. package/dist/session-history/__tests__/friction.test.d.ts.map +1 -0
  33. package/dist/session-history/__tests__/friction.test.js +107 -0
  34. package/dist/session-history/__tests__/friction.test.js.map +1 -0
  35. package/dist/session-history/friction.d.ts +68 -0
  36. package/dist/session-history/friction.d.ts.map +1 -0
  37. package/dist/session-history/friction.js +365 -0
  38. package/dist/session-history/friction.js.map +1 -0
  39. package/package.json +1 -1
  40. package/plugins/oh-my-codex/.codex-plugin/plugin.json +1 -1
  41. package/src/scripts/__tests__/codex-native-hook.test.ts +224 -0
  42. package/src/scripts/codex-native-hook.ts +175 -1
@@ -1 +1 @@
1
- {"version":3,"file":"codex-native-hook.d.ts","sourceRoot":"","sources":["../../src/scripts/codex-native-hook.ts"],"names":[],"mappings":"AA4CA,OAAO,EAIL,KAAK,gBAAgB,EACtB,MAAM,8BAA8B,CAAC;AAgCtC,OAAO,EAAE,2BAA2B,EAAE,MAAM,qBAAqB,CAAC;AA2ClE,KAAK,kBAAkB,GACnB,cAAc,GACd,YAAY,GACZ,aAAa,GACb,kBAAkB,GAClB,YAAY,GACZ,aAAa,GACb,MAAM,CAAC;AAEX,KAAK,gBAAgB,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAEhD,UAAU,yBAAyB;IACjC,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,6BAA6B,CAAC,EAAE,OAAO,2BAA2B,CAAC;CACpE;AAED,MAAM,WAAW,wBAAwB;IACvC,aAAa,EAAE,kBAAkB,GAAG,IAAI,CAAC;IACzC,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,UAAU,EAAE,gBAAgB,GAAG,IAAI,CAAC;IACpC,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;CAC5C;AA4aD,wBAAgB,2BAA2B,CACzC,aAAa,EAAE,kBAAkB,GAAG,IAAI,GACvC,MAAM,GAAG,IAAI,CAmBf;AAwoBD,wBAAgB,kCAAkC,CAChD,QAAQ,EAAE,MAAM,EAChB,OAAO,GAAE;IACP,aAAa,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,MAAM,GAAG,IAAI,CAAC;IAC/C,kBAAkB,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,MAAM,CAAC;CACzC,GACL,MAAM,GAAG,IAAI,CAuBf;AAwkCD,wBAAgB,6BAA6B,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAMnE;AAo6ED,wBAAsB,uBAAuB,CAC3C,OAAO,EAAE,gBAAgB,EACzB,OAAO,GAAE,yBAA8B,GACtC,OAAO,CAAC,wBAAwB,CAAC,CAuVnC;AA6BD,wBAAgB,2BAA2B,CACzC,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE,MAAM,GAAG,SAAS,GACxB,OAAO,CAGT;AA+MD,wBAAsB,qBAAqB,IAAI,OAAO,CAAC,IAAI,CAAC,CAyC3D"}
1
+ {"version":3,"file":"codex-native-hook.d.ts","sourceRoot":"","sources":["../../src/scripts/codex-native-hook.ts"],"names":[],"mappings":"AA4CA,OAAO,EAIL,KAAK,gBAAgB,EACtB,MAAM,8BAA8B,CAAC;AAgCtC,OAAO,EAAE,2BAA2B,EAAE,MAAM,qBAAqB,CAAC;AA2ClE,KAAK,kBAAkB,GACnB,cAAc,GACd,YAAY,GACZ,aAAa,GACb,kBAAkB,GAClB,YAAY,GACZ,aAAa,GACb,MAAM,CAAC;AAEX,KAAK,gBAAgB,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAEhD,UAAU,yBAAyB;IACjC,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,6BAA6B,CAAC,EAAE,OAAO,2BAA2B,CAAC;CACpE;AAED,MAAM,WAAW,wBAAwB;IACvC,aAAa,EAAE,kBAAkB,GAAG,IAAI,CAAC;IACzC,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,UAAU,EAAE,gBAAgB,GAAG,IAAI,CAAC;IACpC,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;CAC5C;AA2cD,wBAAgB,2BAA2B,CACzC,aAAa,EAAE,kBAAkB,GAAG,IAAI,GACvC,MAAM,GAAG,IAAI,CAmBf;AAsxBD,wBAAgB,kCAAkC,CAChD,QAAQ,EAAE,MAAM,EAChB,OAAO,GAAE;IACP,aAAa,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,MAAM,GAAG,IAAI,CAAC;IAC/C,kBAAkB,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,MAAM,CAAC;CACzC,GACL,MAAM,GAAG,IAAI,CAuBf;AAwkCD,wBAAgB,6BAA6B,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAMnE;AAq6ED,wBAAsB,uBAAuB,CAC3C,OAAO,EAAE,gBAAgB,EACzB,OAAO,GAAE,yBAA8B,GACtC,OAAO,CAAC,wBAAwB,CAAC,CAuVnC;AA6BD,wBAAgB,2BAA2B,CACzC,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE,MAAM,GAAG,SAAS,GACxB,OAAO,CAGT;AA+MD,wBAAsB,qBAAqB,IAAI,OAAO,CAAC,IAAI,CAAC,CAyC3D"}
@@ -72,6 +72,37 @@ const SHORT_FOLLOWUP_PRIORITY_PATTERNS = [
72
72
  /(?:按照|按|基于)(?:这个|上述|当前)?(?:plan|计划|方案)/u,
73
73
  /\b(?:follow up|latest request|this turn|current turn|newest request)\b/i,
74
74
  ];
75
+ const RALPH_CONTINUATION_INTENT_PATTERNS = [
76
+ /\b(?:continue|resume|keep going|carry on|proceed|finish|complete)\b/i,
77
+ /\b(?:same|current|active|that|this)\s+(?:ralph|task|work|job|workflow)\b/i,
78
+ /\b(?:ralph)\b.{0,40}\b(?:continue|resume|finish|complete)\b/i,
79
+ /\b(?:continue|resume|finish|complete)\b.{0,40}\b(?:ralph)\b/i,
80
+ ];
81
+ const RALPH_LIVE_RISK_PATTERNS = [
82
+ /\b(?:prod|production|live|customer|user\s+data|billing|payment|credential|secret|token|key)\b/i,
83
+ /\b(?:deploy|release|publish|merge|push|delete|remove|drop|destroy|migrate|migration)\b/i,
84
+ /\b(?:database|db|terraform|kubectl|kubernetes|aws|gcp|azure|external|destructive)\b/i,
85
+ /\b(?:telegram|vps|service|restart|send|notify|notification|notifications|cron)\b/i,
86
+ ];
87
+ const RALPH_TASK_TEXT_FIELDS = [
88
+ "task_description",
89
+ "taskDescription",
90
+ "objective",
91
+ "task",
92
+ "prompt",
93
+ "initial_prompt",
94
+ "initialPrompt",
95
+ "user_prompt",
96
+ "userPrompt",
97
+ "last_user_message",
98
+ "lastUserMessage",
99
+ "task_slug",
100
+ ];
101
+ const RALPH_INTENT_STOPWORDS = new Set([
102
+ "a", "an", "and", "are", "as", "at", "be", "by", "for", "from", "in", "is", "it",
103
+ "of", "on", "or", "that", "the", "this", "to", "with", "you", "your", "task", "work",
104
+ "ralph", "continue", "resume", "finish", "complete", "fix", "issue",
105
+ ]);
75
106
  const MAX_SESSION_META_LINE_BYTES = 256 * 1024;
76
107
  function safeString(value) {
77
108
  return typeof value === "string" ? value : "";
@@ -638,6 +669,123 @@ async function isStaleOrphanedRalphStartingState(state, path, nowMs = Date.now()
638
669
  function hasValue(values, value) {
639
670
  return value !== "" && values.some((candidate) => candidate === value);
640
671
  }
672
+ function hasPositiveRalphStopOwnerMatch(state, context) {
673
+ const ownerOmxSessionId = safeString(state.owner_omx_session_id).trim();
674
+ if (ownerOmxSessionId && ownerOmxSessionId === context.sessionId)
675
+ return true;
676
+ const stateSessionId = safeString(state.session_id).trim();
677
+ if (!ownerOmxSessionId && stateSessionId && stateSessionId === context.sessionId)
678
+ return true;
679
+ const codexOwnerSessionId = safeString(state.owner_codex_session_id).trim();
680
+ if (codexOwnerSessionId) {
681
+ const stopCodexSessionIds = [
682
+ context.payloadSessionId,
683
+ context.currentNativeSessionId,
684
+ context.sessionId,
685
+ ].filter(Boolean);
686
+ if (hasValue(stopCodexSessionIds, codexOwnerSessionId))
687
+ return true;
688
+ }
689
+ const stateThreadId = safeString(state.owner_codex_thread_id ?? state.thread_id).trim();
690
+ if (stateThreadId && context.threadId && stateThreadId === context.threadId)
691
+ return true;
692
+ const statePaneId = safeString(state.tmux_pane_id).trim();
693
+ return statePaneId !== "" && context.tmuxPaneId !== "" && statePaneId === context.tmuxPaneId;
694
+ }
695
+ function textMatchesAny(text, patterns) {
696
+ return patterns.some((pattern) => pattern.test(text));
697
+ }
698
+ function extractRalphTaskText(state) {
699
+ const values = [];
700
+ for (const field of RALPH_TASK_TEXT_FIELDS) {
701
+ const value = safeString(state[field]).trim();
702
+ if (value)
703
+ values.push(value);
704
+ }
705
+ const taskMetadata = state.task_metadata ?? state.taskMetadata;
706
+ if (taskMetadata && typeof taskMetadata === "object") {
707
+ const metadata = taskMetadata;
708
+ for (const field of RALPH_TASK_TEXT_FIELDS) {
709
+ const value = safeString(metadata[field]).trim();
710
+ if (value)
711
+ values.push(value);
712
+ }
713
+ }
714
+ return values.join("\n");
715
+ }
716
+ function extractRalphStopUserText(payload) {
717
+ if (!payload)
718
+ return "";
719
+ return [
720
+ payload.prompt,
721
+ payload.user_prompt,
722
+ payload.userPrompt,
723
+ payload.input,
724
+ payload.last_user_message,
725
+ payload.lastUserMessage,
726
+ ].map(safeString).filter(Boolean).join("\n");
727
+ }
728
+ function tokenizeRalphIntentText(text) {
729
+ const tokens = new Set();
730
+ for (const match of text.toLowerCase().matchAll(/[a-z0-9][a-z0-9_-]{2,}/g)) {
731
+ const token = match[0].replace(/^issue-/, "");
732
+ if (!token || RALPH_INTENT_STOPWORDS.has(token))
733
+ continue;
734
+ tokens.add(token);
735
+ }
736
+ return tokens;
737
+ }
738
+ function countTokenOverlap(left, right) {
739
+ let overlap = 0;
740
+ for (const token of left) {
741
+ if (right.has(token))
742
+ overlap += 1;
743
+ }
744
+ return overlap;
745
+ }
746
+ function hasCurrentRalphTaskOverlap(state, userText) {
747
+ const taskText = extractRalphTaskText(state);
748
+ const taskTokens = tokenizeRalphIntentText(taskText);
749
+ const userTokens = tokenizeRalphIntentText(userText);
750
+ if (taskTokens.size === 0)
751
+ return false;
752
+ if (userTokens.size === 0)
753
+ return false;
754
+ const overlap = countTokenOverlap(taskTokens, userTokens);
755
+ const smallerSide = Math.min(taskTokens.size, userTokens.size);
756
+ return overlap >= 2 || (smallerSide <= 2 && overlap >= 1);
757
+ }
758
+ function hasMeaningfulRalphTaskText(state) {
759
+ return tokenizeRalphIntentText(extractRalphTaskText(state)).size > 0;
760
+ }
761
+ function isRalphLiveRiskContinuation(state, userText) {
762
+ return textMatchesAny(`${extractRalphTaskText(state)}\n${userText}`, RALPH_LIVE_RISK_PATTERNS);
763
+ }
764
+ function shouldAllowGlobalRalphStopContinuation(state, context) {
765
+ const userText = extractRalphStopUserText(context.payload);
766
+ const hasContinuationIntent = textMatchesAny(userText, RALPH_CONTINUATION_INTENT_PATTERNS);
767
+ const hasTaskOverlap = hasCurrentRalphTaskOverlap(state, userText);
768
+ const hasTaskText = hasMeaningfulRalphTaskText(state);
769
+ const hasUserTaskText = tokenizeRalphIntentText(userText).size > 0;
770
+ const hasPositiveOwnerMatch = hasPositiveRalphStopOwnerMatch(state, context);
771
+ if (!activeRalphStateMatchesStopOwner(state, context))
772
+ return false;
773
+ if (!userText.trim()) {
774
+ if (isRalphLiveRiskContinuation(state, userText))
775
+ return false;
776
+ return hasPositiveOwnerMatch || !hasRalphOwnerHint(state);
777
+ }
778
+ if (isRalphLiveRiskContinuation(state, userText)) {
779
+ return hasContinuationIntent && (hasPositiveOwnerMatch || hasTaskOverlap);
780
+ }
781
+ if (hasPositiveOwnerMatch && hasContinuationIntent) {
782
+ return true;
783
+ }
784
+ if (hasTaskText && hasUserTaskText) {
785
+ return hasTaskOverlap;
786
+ }
787
+ return hasContinuationIntent || hasTaskOverlap;
788
+ }
641
789
  function activeRalphStateMatchesStopOwner(state, context) {
642
790
  const ownerOmxSessionId = safeString(state.owner_omx_session_id).trim();
643
791
  if (ownerOmxSessionId && ownerOmxSessionId !== context.sessionId) {
@@ -910,7 +1058,16 @@ async function readActiveRalphState(cwd, stateDir, preferredSessionId, ownerCont
910
1058
  return null;
911
1059
  const directPath = join(stateDir, "ralph-state.json");
912
1060
  const direct = await readJsonIfExists(directPath);
913
- if (direct?.active === true && shouldContinueRun(direct)) {
1061
+ if (direct?.active === true
1062
+ && shouldContinueRun(direct)
1063
+ && shouldAllowGlobalRalphStopContinuation(direct, {
1064
+ sessionId: safeString(ownerContext?.payloadSessionId).trim(),
1065
+ payloadSessionId: safeString(ownerContext?.payloadSessionId).trim(),
1066
+ threadId: safeString(ownerContext?.threadId).trim(),
1067
+ currentNativeSessionId,
1068
+ tmuxPaneId: safeString(ownerContext?.tmuxPaneId).trim(),
1069
+ payload: ownerContext?.payload,
1070
+ })) {
914
1071
  return { state: direct, path: directPath };
915
1072
  }
916
1073
  return null;
@@ -3716,6 +3873,7 @@ async function buildStopHookOutput(payload, cwd, stateDir, options = {}) {
3716
3873
  payloadSessionId: sessionId,
3717
3874
  threadId,
3718
3875
  tmuxPaneId: safeString(process.env.TMUX_PANE).trim(),
3876
+ payload,
3719
3877
  };
3720
3878
  const ralphCompletionAuditBlock = options.skipRalphStopBlock === true
3721
3879
  ? null