oh-my-codex 0.14.0 → 0.14.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 +14 -8
- package/crates/omx-explore/src/main.rs +94 -1
- package/crates/omx-sparkshell/src/codex_bridge.rs +59 -12
- package/crates/omx-sparkshell/tests/execution.rs +48 -0
- package/dist/cli/__tests__/explore.test.js +33 -1
- package/dist/cli/__tests__/explore.test.js.map +1 -1
- package/dist/cli/__tests__/index.test.js +11 -2
- package/dist/cli/__tests__/index.test.js.map +1 -1
- package/dist/cli/__tests__/package-bin-contract.test.js +5 -0
- package/dist/cli/__tests__/package-bin-contract.test.js.map +1 -1
- package/dist/cli/__tests__/question.test.js +139 -25
- package/dist/cli/__tests__/question.test.js.map +1 -1
- package/dist/cli/__tests__/session-scoped-runtime.test.js +30 -0
- package/dist/cli/__tests__/session-scoped-runtime.test.js.map +1 -1
- package/dist/cli/__tests__/setup-agents-overwrite.test.js +32 -7
- package/dist/cli/__tests__/setup-agents-overwrite.test.js.map +1 -1
- package/dist/cli/__tests__/setup-refresh.test.js +8 -6
- package/dist/cli/__tests__/setup-refresh.test.js.map +1 -1
- package/dist/cli/__tests__/sparkshell-cli.test.js +23 -0
- package/dist/cli/__tests__/sparkshell-cli.test.js.map +1 -1
- package/dist/cli/__tests__/uninstall.test.js +65 -5
- package/dist/cli/__tests__/uninstall.test.js.map +1 -1
- package/dist/cli/__tests__/update.test.js +360 -26
- package/dist/cli/__tests__/update.test.js.map +1 -1
- package/dist/cli/explore.d.ts.map +1 -1
- package/dist/cli/explore.js +18 -3
- package/dist/cli/explore.js.map +1 -1
- package/dist/cli/index.d.ts +2 -1
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +7 -2
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/setup.d.ts.map +1 -1
- package/dist/cli/setup.js +25 -3
- package/dist/cli/setup.js.map +1 -1
- package/dist/cli/sparkshell.d.ts.map +1 -1
- package/dist/cli/sparkshell.js +11 -1
- package/dist/cli/sparkshell.js.map +1 -1
- package/dist/cli/team.d.ts.map +1 -1
- package/dist/cli/team.js +159 -394
- package/dist/cli/team.js.map +1 -1
- package/dist/cli/uninstall.d.ts.map +1 -1
- package/dist/cli/uninstall.js +3 -1
- package/dist/cli/uninstall.js.map +1 -1
- package/dist/cli/update.d.ts +37 -9
- package/dist/cli/update.d.ts.map +1 -1
- package/dist/cli/update.js +204 -26
- package/dist/cli/update.js.map +1 -1
- package/dist/config/__tests__/generator-idempotent.test.js +51 -14
- package/dist/config/__tests__/generator-idempotent.test.js.map +1 -1
- package/dist/config/__tests__/generator-notify.test.js +35 -10
- package/dist/config/__tests__/generator-notify.test.js.map +1 -1
- package/dist/config/generator.d.ts +1 -0
- package/dist/config/generator.d.ts.map +1 -1
- package/dist/config/generator.js +61 -7
- package/dist/config/generator.js.map +1 -1
- package/dist/hooks/__tests__/code-review-skill-contract.test.d.ts +2 -0
- package/dist/hooks/__tests__/code-review-skill-contract.test.d.ts.map +1 -0
- package/dist/hooks/__tests__/code-review-skill-contract.test.js +56 -0
- package/dist/hooks/__tests__/code-review-skill-contract.test.js.map +1 -0
- package/dist/hooks/__tests__/deep-interview-contract.test.js +31 -0
- package/dist/hooks/__tests__/deep-interview-contract.test.js.map +1 -1
- package/dist/hooks/__tests__/explicit-terminal-stop-docs-contract.test.d.ts +2 -0
- package/dist/hooks/__tests__/explicit-terminal-stop-docs-contract.test.d.ts.map +1 -0
- package/dist/hooks/__tests__/explicit-terminal-stop-docs-contract.test.js +43 -0
- package/dist/hooks/__tests__/explicit-terminal-stop-docs-contract.test.js.map +1 -0
- package/dist/hooks/__tests__/explicit-terminal-stop-model-docs-contract.test.d.ts +2 -0
- package/dist/hooks/__tests__/explicit-terminal-stop-model-docs-contract.test.d.ts.map +1 -0
- package/dist/hooks/__tests__/explicit-terminal-stop-model-docs-contract.test.js +38 -0
- package/dist/hooks/__tests__/explicit-terminal-stop-model-docs-contract.test.js.map +1 -0
- package/dist/hooks/__tests__/keyword-detector.test.js +108 -0
- package/dist/hooks/__tests__/keyword-detector.test.js.map +1 -1
- package/dist/hooks/__tests__/prompt-guidance-test-helpers.d.ts.map +1 -1
- package/dist/hooks/__tests__/prompt-guidance-test-helpers.js +16 -1
- package/dist/hooks/__tests__/prompt-guidance-test-helpers.js.map +1 -1
- package/dist/hooks/keyword-detector.d.ts.map +1 -1
- package/dist/hooks/keyword-detector.js +34 -8
- package/dist/hooks/keyword-detector.js.map +1 -1
- package/dist/mcp/__tests__/bootstrap.test.js +7 -25
- package/dist/mcp/__tests__/bootstrap.test.js.map +1 -1
- package/dist/mcp/__tests__/server-lifecycle.test.js +60 -0
- package/dist/mcp/__tests__/server-lifecycle.test.js.map +1 -1
- package/dist/mcp/__tests__/state-server.test.js +177 -0
- package/dist/mcp/__tests__/state-server.test.js.map +1 -1
- package/dist/mcp/bootstrap.d.ts.map +1 -1
- package/dist/mcp/bootstrap.js +36 -18
- package/dist/mcp/bootstrap.js.map +1 -1
- package/dist/mcp/state-server.d.ts +17 -0
- package/dist/mcp/state-server.d.ts.map +1 -1
- package/dist/mcp/state-server.js +55 -1
- package/dist/mcp/state-server.js.map +1 -1
- package/dist/notifications/__tests__/index.test.js +0 -3
- package/dist/notifications/__tests__/index.test.js.map +1 -1
- package/dist/notifications/__tests__/session-status.test.js +90 -0
- package/dist/notifications/__tests__/session-status.test.js.map +1 -1
- package/dist/notifications/session-status.d.ts +2 -0
- package/dist/notifications/session-status.d.ts.map +1 -1
- package/dist/notifications/session-status.js +19 -4
- package/dist/notifications/session-status.js.map +1 -1
- package/dist/question/__tests__/deep-interview.test.js +44 -0
- package/dist/question/__tests__/deep-interview.test.js.map +1 -1
- package/dist/question/__tests__/renderer.test.js +192 -12
- package/dist/question/__tests__/renderer.test.js.map +1 -1
- package/dist/question/__tests__/state.test.js +21 -1
- package/dist/question/__tests__/state.test.js.map +1 -1
- package/dist/question/deep-interview.d.ts +3 -0
- package/dist/question/deep-interview.d.ts.map +1 -1
- package/dist/question/deep-interview.js +18 -1
- package/dist/question/deep-interview.js.map +1 -1
- package/dist/question/renderer.d.ts +4 -2
- package/dist/question/renderer.d.ts.map +1 -1
- package/dist/question/renderer.js +87 -18
- package/dist/question/renderer.js.map +1 -1
- package/dist/runtime/__tests__/run-outcome.test.js +38 -0
- package/dist/runtime/__tests__/run-outcome.test.js.map +1 -1
- package/dist/runtime/__tests__/run-state.test.d.ts +2 -0
- package/dist/runtime/__tests__/run-state.test.d.ts.map +1 -0
- package/dist/runtime/__tests__/run-state.test.js +37 -0
- package/dist/runtime/__tests__/run-state.test.js.map +1 -0
- package/dist/runtime/run-loop.d.ts +5 -1
- package/dist/runtime/run-loop.d.ts.map +1 -1
- package/dist/runtime/run-loop.js +8 -3
- package/dist/runtime/run-loop.js.map +1 -1
- package/dist/runtime/run-outcome.d.ts +18 -0
- package/dist/runtime/run-outcome.d.ts.map +1 -1
- package/dist/runtime/run-outcome.js +156 -7
- package/dist/runtime/run-outcome.js.map +1 -1
- package/dist/runtime/run-state.d.ts +5 -1
- package/dist/runtime/run-state.d.ts.map +1 -1
- package/dist/runtime/run-state.js +13 -3
- package/dist/runtime/run-state.js.map +1 -1
- package/dist/runtime/terminal-lifecycle.d.ts +11 -0
- package/dist/runtime/terminal-lifecycle.d.ts.map +1 -0
- package/dist/runtime/terminal-lifecycle.js +52 -0
- package/dist/runtime/terminal-lifecycle.js.map +1 -0
- package/dist/scripts/__tests__/codex-native-hook.test.js +370 -56
- package/dist/scripts/__tests__/codex-native-hook.test.js.map +1 -1
- package/dist/scripts/__tests__/postinstall.test.d.ts +2 -0
- package/dist/scripts/__tests__/postinstall.test.d.ts.map +1 -0
- package/dist/scripts/__tests__/postinstall.test.js +178 -0
- package/dist/scripts/__tests__/postinstall.test.js.map +1 -0
- package/dist/scripts/codex-native-hook.d.ts +1 -0
- package/dist/scripts/codex-native-hook.d.ts.map +1 -1
- package/dist/scripts/codex-native-hook.js +115 -56
- package/dist/scripts/codex-native-hook.js.map +1 -1
- package/dist/scripts/postinstall.d.ts +22 -0
- package/dist/scripts/postinstall.d.ts.map +1 -0
- package/dist/scripts/postinstall.js +105 -0
- package/dist/scripts/postinstall.js.map +1 -0
- package/dist/state/__tests__/operations.test.js +60 -0
- package/dist/state/__tests__/operations.test.js.map +1 -1
- package/dist/state/operations.d.ts.map +1 -1
- package/dist/state/operations.js +18 -1
- package/dist/state/operations.js.map +1 -1
- package/dist/team/__tests__/role-router.test.js +6 -0
- package/dist/team/__tests__/role-router.test.js.map +1 -1
- package/dist/team/__tests__/runtime.test.js +108 -2
- package/dist/team/__tests__/runtime.test.js.map +1 -1
- package/dist/team/runtime.d.ts.map +1 -1
- package/dist/team/runtime.js +18 -4
- package/dist/team/runtime.js.map +1 -1
- package/dist/utils/__tests__/dep-versions.test.js +25 -8
- package/dist/utils/__tests__/dep-versions.test.js.map +1 -1
- package/dist/utils/__tests__/paths.test.js +45 -0
- package/dist/utils/__tests__/paths.test.js.map +1 -1
- package/dist/utils/paths.d.ts +2 -0
- package/dist/utils/paths.d.ts.map +1 -1
- package/dist/utils/paths.js +22 -7
- package/dist/utils/paths.js.map +1 -1
- package/dist/verification/__tests__/ci-rust-gates.test.js +1 -1
- package/dist/verification/__tests__/ci-rust-gates.test.js.map +1 -1
- package/package.json +3 -2
- package/prompts/architect.md +4 -0
- package/prompts/code-reviewer.md +3 -0
- package/skills/code-review/SKILL.md +94 -28
- package/skills/deep-interview/SKILL.md +91 -0
- package/src/scripts/__tests__/codex-native-hook.test.ts +468 -64
- package/src/scripts/__tests__/postinstall.test.ts +210 -0
- package/src/scripts/codex-native-hook.ts +136 -53
- package/src/scripts/postinstall-bootstrap.js +23 -0
- package/src/scripts/postinstall.ts +161 -0
- package/templates/AGENTS.md +1 -1
- package/templates/model-instructions/explore-lightweight-AGENTS.md +11 -0
- package/templates/model-instructions/sparkshell-lightweight-AGENTS.md +10 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"run-state.js","sourceRoot":"","sources":["../../src/runtime/run-state.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAE9E,OAAO,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAC5E,OAAO,EACL,kBAAkB,EAClB,oBAAoB,EACpB,mBAAmB,
|
|
1
|
+
{"version":3,"file":"run-state.js","sourceRoot":"","sources":["../../src/runtime/run-state.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { TERMINAL_LIFECYCLE_OUTCOMES, compatibilityRunOutcomeFromTerminalLifecycleOutcome, type RunOutcome, type TerminalLifecycleOutcome, type TerminalLifecycleOutcomeNormalizationResult } from './run-outcome.js';
|
|
2
|
+
export { TERMINAL_LIFECYCLE_OUTCOMES, compatibilityRunOutcomeFromTerminalLifecycleOutcome, };
|
|
3
|
+
export type { TerminalLifecycleOutcome };
|
|
4
|
+
export type TerminalLifecycleNormalizationResult = TerminalLifecycleOutcomeNormalizationResult;
|
|
5
|
+
export declare function normalizeTerminalLifecycleOutcome(value: unknown): TerminalLifecycleNormalizationResult;
|
|
6
|
+
export declare function inferTerminalLifecycleOutcome(candidate: {
|
|
7
|
+
lifecycle_outcome?: unknown;
|
|
8
|
+
run_outcome?: unknown;
|
|
9
|
+
}): TerminalLifecycleNormalizationResult;
|
|
10
|
+
export declare function preferredRunOutcomeForLifecycleOutcome(outcome: TerminalLifecycleOutcome): RunOutcome;
|
|
11
|
+
//# sourceMappingURL=terminal-lifecycle.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"terminal-lifecycle.d.ts","sourceRoot":"","sources":["../../src/runtime/terminal-lifecycle.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,2BAA2B,EAC3B,mDAAmD,EAInD,KAAK,UAAU,EACf,KAAK,wBAAwB,EAC7B,KAAK,2CAA2C,EACjD,MAAM,kBAAkB,CAAC;AAE1B,OAAO,EACL,2BAA2B,EAC3B,mDAAmD,GACpD,CAAC;AACF,YAAY,EAAE,wBAAwB,EAAE,CAAC;AACzC,MAAM,MAAM,oCAAoC,GAAG,2CAA2C,CAAC;AAU/F,wBAAgB,iCAAiC,CAAC,KAAK,EAAE,OAAO,GAAG,oCAAoC,CAatG;AAED,wBAAgB,6BAA6B,CAAC,SAAS,EAAE;IACvD,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB,GAAG,oCAAoC,CAyBvC;AAED,wBAAgB,sCAAsC,CACpD,OAAO,EAAE,wBAAwB,GAChC,UAAU,CAEZ"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { TERMINAL_LIFECYCLE_OUTCOMES, compatibilityRunOutcomeFromTerminalLifecycleOutcome, normalizeRunOutcome, normalizeTerminalLifecycleOutcome as normalizeTerminalLifecycleOutcomeContract, terminalLifecycleOutcomeFromRunOutcome, } from './run-outcome.js';
|
|
2
|
+
export { TERMINAL_LIFECYCLE_OUTCOMES, compatibilityRunOutcomeFromTerminalLifecycleOutcome, };
|
|
3
|
+
function rewriteWarning(warning) {
|
|
4
|
+
return warning?.replace('legacy terminal lifecycle outcome', 'legacy lifecycle outcome');
|
|
5
|
+
}
|
|
6
|
+
function isCanonicalTerminalLifecycleOutcome(value) {
|
|
7
|
+
return typeof value === 'string' && TERMINAL_LIFECYCLE_OUTCOMES.includes(value.trim());
|
|
8
|
+
}
|
|
9
|
+
export function normalizeTerminalLifecycleOutcome(value) {
|
|
10
|
+
if (isCanonicalTerminalLifecycleOutcome(value)) {
|
|
11
|
+
return { outcome: value.trim() };
|
|
12
|
+
}
|
|
13
|
+
const result = normalizeTerminalLifecycleOutcomeContract(value, {
|
|
14
|
+
blockedOnUserStrategy: 'blocked',
|
|
15
|
+
});
|
|
16
|
+
return {
|
|
17
|
+
...(result.outcome ? { outcome: result.outcome } : {}),
|
|
18
|
+
...(result.warning ? { warning: rewriteWarning(result.warning) } : {}),
|
|
19
|
+
...(result.error ? { error: result.error } : {}),
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
export function inferTerminalLifecycleOutcome(candidate) {
|
|
23
|
+
const explicit = normalizeTerminalLifecycleOutcome(candidate.lifecycle_outcome);
|
|
24
|
+
if (explicit.outcome || explicit.error)
|
|
25
|
+
return explicit;
|
|
26
|
+
const runOutcome = normalizeRunOutcome(candidate.run_outcome);
|
|
27
|
+
if (runOutcome.error)
|
|
28
|
+
return { error: runOutcome.error };
|
|
29
|
+
switch (runOutcome.outcome) {
|
|
30
|
+
case 'finish':
|
|
31
|
+
case 'blocked_on_user':
|
|
32
|
+
case 'failed':
|
|
33
|
+
return {
|
|
34
|
+
outcome: terminalLifecycleOutcomeFromRunOutcome(runOutcome.outcome, {
|
|
35
|
+
blockedOnUserStrategy: 'blocked',
|
|
36
|
+
}),
|
|
37
|
+
};
|
|
38
|
+
case 'cancelled':
|
|
39
|
+
return {
|
|
40
|
+
outcome: terminalLifecycleOutcomeFromRunOutcome('cancelled', {
|
|
41
|
+
blockedOnUserStrategy: 'blocked',
|
|
42
|
+
}),
|
|
43
|
+
warning: 'normalized legacy run outcome "cancelled" -> "userinterlude"',
|
|
44
|
+
};
|
|
45
|
+
default:
|
|
46
|
+
return {};
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
export function preferredRunOutcomeForLifecycleOutcome(outcome) {
|
|
50
|
+
return compatibilityRunOutcomeFromTerminalLifecycleOutcome(outcome);
|
|
51
|
+
}
|
|
52
|
+
//# sourceMappingURL=terminal-lifecycle.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"terminal-lifecycle.js","sourceRoot":"","sources":["../../src/runtime/terminal-lifecycle.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,2BAA2B,EAC3B,mDAAmD,EACnD,mBAAmB,EACnB,iCAAiC,IAAI,yCAAyC,EAC9E,sCAAsC,GAIvC,MAAM,kBAAkB,CAAC;AAE1B,OAAO,EACL,2BAA2B,EAC3B,mDAAmD,GACpD,CAAC;AAIF,SAAS,cAAc,CAAC,OAA2B;IACjD,OAAO,OAAO,EAAE,OAAO,CAAC,mCAAmC,EAAE,0BAA0B,CAAC,CAAC;AAC3F,CAAC;AAED,SAAS,mCAAmC,CAAC,KAAc;IACzD,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAK,2BAAiD,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC;AAChH,CAAC;AAED,MAAM,UAAU,iCAAiC,CAAC,KAAc;IAC9D,IAAI,mCAAmC,CAAC,KAAK,CAAC,EAAE,CAAC;QAC/C,OAAO,EAAE,OAAO,EAAE,KAAK,CAAC,IAAI,EAA8B,EAAE,CAAC;IAC/D,CAAC;IAED,MAAM,MAAM,GAAG,yCAAyC,CAAC,KAAK,EAAE;QAC9D,qBAAqB,EAAE,SAAS;KACjC,CAAC,CAAC;IACH,OAAO;QACL,GAAG,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,MAAM,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACtD,GAAG,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,cAAc,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACtE,GAAG,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,MAAM,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KACjD,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,6BAA6B,CAAC,SAG7C;IACC,MAAM,QAAQ,GAAG,iCAAiC,CAAC,SAAS,CAAC,iBAAiB,CAAC,CAAC;IAChF,IAAI,QAAQ,CAAC,OAAO,IAAI,QAAQ,CAAC,KAAK;QAAE,OAAO,QAAQ,CAAC;IAExD,MAAM,UAAU,GAAG,mBAAmB,CAAC,SAAS,CAAC,WAAW,CAAC,CAAC;IAC9D,IAAI,UAAU,CAAC,KAAK;QAAE,OAAO,EAAE,KAAK,EAAE,UAAU,CAAC,KAAK,EAAE,CAAC;IACzD,QAAQ,UAAU,CAAC,OAAO,EAAE,CAAC;QAC3B,KAAK,QAAQ,CAAC;QACd,KAAK,iBAAiB,CAAC;QACvB,KAAK,QAAQ;YACX,OAAO;gBACL,OAAO,EAAE,sCAAsC,CAAC,UAAU,CAAC,OAAO,EAAE;oBAClE,qBAAqB,EAAE,SAAS;iBACjC,CAAC;aACH,CAAC;QACJ,KAAK,WAAW;YACd,OAAO;gBACL,OAAO,EAAE,sCAAsC,CAAC,WAAW,EAAE;oBAC3D,qBAAqB,EAAE,SAAS;iBACjC,CAAC;gBACF,OAAO,EAAE,8DAA8D;aACxE,CAAC;QACJ;YACE,OAAO,EAAE,CAAC;IACd,CAAC;AACH,CAAC;AAED,MAAM,UAAU,sCAAsC,CACpD,OAAiC;IAEjC,OAAO,mDAAmD,CAAC,OAAO,CAAC,CAAC;AACtE,CAAC"}
|
|
@@ -4,16 +4,36 @@ import { existsSync } from "node:fs";
|
|
|
4
4
|
import { chmod, mkdir, mkdtemp, readFile, rm, writeFile } from "node:fs/promises";
|
|
5
5
|
import { tmpdir } from "node:os";
|
|
6
6
|
import { dirname, join } from "node:path";
|
|
7
|
+
import { pathToFileURL } from "node:url";
|
|
7
8
|
import { afterEach, beforeEach, describe, it } from "node:test";
|
|
8
9
|
import { buildManagedCodexHooksConfig } from "../../config/codex-hooks.js";
|
|
9
10
|
import { initTeamState, readTeamLeaderAttention, readTeamPhase, writeTeamLeaderAttention, } from "../../team/state.js";
|
|
10
|
-
import { dispatchCodexNativeHook, mapCodexHookEventToOmxEvent, resolveSessionOwnerPidFromAncestry, } from "../codex-native-hook.js";
|
|
11
|
+
import { dispatchCodexNativeHook, isCodexNativeHookMainModule, mapCodexHookEventToOmxEvent, resolveSessionOwnerPidFromAncestry, } from "../codex-native-hook.js";
|
|
11
12
|
import { writeSessionStart } from "../../hooks/session.js";
|
|
12
13
|
import { resetTriageConfigCache } from "../../hooks/triage-config.js";
|
|
13
14
|
async function writeJson(path, value) {
|
|
14
15
|
await mkdir(dirname(path), { recursive: true }).catch(() => { });
|
|
15
16
|
await writeFile(path, JSON.stringify(value, null, 2));
|
|
16
17
|
}
|
|
18
|
+
async function writeHookCounterPlugin(cwd) {
|
|
19
|
+
const markerPath = join(cwd, ".omx", "stop-hook-counter.json");
|
|
20
|
+
await mkdir(join(cwd, ".omx", "hooks"), { recursive: true });
|
|
21
|
+
await writeFile(join(cwd, ".omx", "hooks", "count-stop-hook.mjs"), `import { mkdir, readFile, writeFile } from "node:fs/promises";
|
|
22
|
+
import { dirname, join } from "node:path";
|
|
23
|
+
|
|
24
|
+
export async function onHookEvent(event) {
|
|
25
|
+
if (event.event !== "stop") return;
|
|
26
|
+
const outPath = join(process.cwd(), ".omx", "stop-hook-counter.json");
|
|
27
|
+
await mkdir(dirname(outPath), { recursive: true });
|
|
28
|
+
let count = 0;
|
|
29
|
+
try {
|
|
30
|
+
count = JSON.parse(await readFile(outPath, "utf-8")).count || 0;
|
|
31
|
+
} catch {}
|
|
32
|
+
await writeFile(outPath, JSON.stringify({ count: count + 1 }, null, 2));
|
|
33
|
+
}
|
|
34
|
+
`, "utf-8");
|
|
35
|
+
return markerPath;
|
|
36
|
+
}
|
|
17
37
|
async function writeReleaseReadinessLeaderAttention(teamName, sessionId, cwd, options) {
|
|
18
38
|
await writeTeamLeaderAttention(teamName, {
|
|
19
39
|
team_name: teamName,
|
|
@@ -91,6 +111,13 @@ describe("codex native hook config", () => {
|
|
|
91
111
|
});
|
|
92
112
|
});
|
|
93
113
|
describe("codex native hook dispatch", () => {
|
|
114
|
+
it("treats space-containing argv entry paths as the main module", () => {
|
|
115
|
+
const entryPath = "/tmp/omx native/codex-native-hook.js";
|
|
116
|
+
assert.equal(isCodexNativeHookMainModule(pathToFileURL(entryPath).href, entryPath), true);
|
|
117
|
+
});
|
|
118
|
+
it("does not treat a different module url as the main module", () => {
|
|
119
|
+
assert.equal(isCodexNativeHookMainModule(pathToFileURL("/tmp/omx native/other-script.js").href, "/tmp/omx native/codex-native-hook.js"), false);
|
|
120
|
+
});
|
|
94
121
|
it("emits deterministic JSON stdout when CLI stdin is malformed", () => {
|
|
95
122
|
const stdout = execFileSync(process.execPath, [join(process.cwd(), "dist", "scripts", "codex-native-hook.js")], {
|
|
96
123
|
cwd: process.cwd(),
|
|
@@ -209,7 +236,7 @@ describe("codex native hook dispatch", () => {
|
|
|
209
236
|
await rm(cwd, { recursive: true, force: true });
|
|
210
237
|
}
|
|
211
238
|
});
|
|
212
|
-
it("
|
|
239
|
+
it("adds .omx/ to git info/exclude during SessionStart instead of mutating repo .gitignore", async () => {
|
|
213
240
|
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-session-gitignore-"));
|
|
214
241
|
try {
|
|
215
242
|
await writeFile(join(cwd, ".gitignore"), "node_modules/\n");
|
|
@@ -221,8 +248,55 @@ describe("codex native hook dispatch", () => {
|
|
|
221
248
|
}, { cwd, sessionOwnerPid: 43210 });
|
|
222
249
|
assert.equal(result.omxEventName, "session-start");
|
|
223
250
|
const gitignore = await readFile(join(cwd, ".gitignore"), "utf-8");
|
|
224
|
-
assert.
|
|
225
|
-
|
|
251
|
+
assert.equal(gitignore, "node_modules/\n");
|
|
252
|
+
const exclude = await readFile(join(cwd, ".git", "info", "exclude"), "utf-8");
|
|
253
|
+
assert.match(exclude, /(?:^|\n)\.omx\/\n/);
|
|
254
|
+
assert.match(JSON.stringify(result.outputJson), /Added \.omx\/ to .*\.git[\/]info[\/]exclude/);
|
|
255
|
+
}
|
|
256
|
+
finally {
|
|
257
|
+
await rm(cwd, { recursive: true, force: true });
|
|
258
|
+
}
|
|
259
|
+
});
|
|
260
|
+
it("keeps SessionStart quiet when .omx/ is already ignored by repo-level gitignore", async () => {
|
|
261
|
+
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-session-existing-ignore-"));
|
|
262
|
+
try {
|
|
263
|
+
await writeFile(join(cwd, ".gitignore"), "node_modules/\n.omx/\n");
|
|
264
|
+
execFileSync("git", ["init"], { cwd, stdio: "pipe" });
|
|
265
|
+
const result = await dispatchCodexNativeHook({
|
|
266
|
+
hook_event_name: "SessionStart",
|
|
267
|
+
cwd,
|
|
268
|
+
session_id: "sess-gitignore-existing",
|
|
269
|
+
}, { cwd, sessionOwnerPid: 43210 });
|
|
270
|
+
assert.equal(result.omxEventName, "session-start");
|
|
271
|
+
const gitignore = await readFile(join(cwd, ".gitignore"), "utf-8");
|
|
272
|
+
assert.equal(gitignore, "node_modules/\n.omx/\n");
|
|
273
|
+
const exclude = await readFile(join(cwd, ".git", "info", "exclude"), "utf-8");
|
|
274
|
+
assert.doesNotMatch(exclude, /(?:^|\n)\.omx\/\n/);
|
|
275
|
+
assert.doesNotMatch(JSON.stringify(result.outputJson), /Added \.omx\//);
|
|
276
|
+
}
|
|
277
|
+
finally {
|
|
278
|
+
await rm(cwd, { recursive: true, force: true });
|
|
279
|
+
}
|
|
280
|
+
});
|
|
281
|
+
it("respects existing Git ignore resolution before writing local excludes", async () => {
|
|
282
|
+
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-session-global-ignore-"));
|
|
283
|
+
const excludesFile = join(cwd, "global-ignore");
|
|
284
|
+
try {
|
|
285
|
+
await writeFile(join(cwd, ".gitignore"), "node_modules/\n");
|
|
286
|
+
await writeFile(excludesFile, ".omx/\n");
|
|
287
|
+
execFileSync("git", ["init"], { cwd, stdio: "pipe" });
|
|
288
|
+
execFileSync("git", ["config", "core.excludesfile", excludesFile], { cwd, stdio: "pipe" });
|
|
289
|
+
const result = await dispatchCodexNativeHook({
|
|
290
|
+
hook_event_name: "SessionStart",
|
|
291
|
+
cwd,
|
|
292
|
+
session_id: "sess-gitignore-global",
|
|
293
|
+
}, { cwd, sessionOwnerPid: 43210 });
|
|
294
|
+
assert.equal(result.omxEventName, "session-start");
|
|
295
|
+
const gitignore = await readFile(join(cwd, ".gitignore"), "utf-8");
|
|
296
|
+
assert.equal(gitignore, "node_modules/\n");
|
|
297
|
+
const exclude = await readFile(join(cwd, ".git", "info", "exclude"), "utf-8");
|
|
298
|
+
assert.doesNotMatch(exclude, /(?:^|\n)\.omx\/\n/);
|
|
299
|
+
assert.doesNotMatch(JSON.stringify(result.outputJson), /Added \.omx\//);
|
|
226
300
|
}
|
|
227
301
|
finally {
|
|
228
302
|
await rm(cwd, { recursive: true, force: true });
|
|
@@ -373,6 +447,29 @@ describe("codex native hook dispatch", () => {
|
|
|
373
447
|
await rm(cwd, { recursive: true, force: true });
|
|
374
448
|
}
|
|
375
449
|
});
|
|
450
|
+
it("normalizes the Korean keyboard typo for ulw during UserPromptSubmit activation", async () => {
|
|
451
|
+
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-ulw-ko-"));
|
|
452
|
+
try {
|
|
453
|
+
await mkdir(join(cwd, ".omx", "state"), { recursive: true });
|
|
454
|
+
const result = await dispatchCodexNativeHook({
|
|
455
|
+
hook_event_name: "UserPromptSubmit",
|
|
456
|
+
cwd,
|
|
457
|
+
session_id: "sess-ulw-ko",
|
|
458
|
+
thread_id: "thread-ulw-ko",
|
|
459
|
+
turn_id: "turn-ulw-ko",
|
|
460
|
+
prompt: "ㅕㅣㅈ로 병렬 처리해줘",
|
|
461
|
+
}, { cwd });
|
|
462
|
+
assert.equal(result.omxEventName, "keyword-detector");
|
|
463
|
+
assert.equal(result.skillState?.skill, "ultrawork");
|
|
464
|
+
assert.equal(result.skillState?.keyword, "ulw");
|
|
465
|
+
const additionalContext = String(result.outputJson?.hookSpecificOutput?.additionalContext || "");
|
|
466
|
+
assert.match(additionalContext, /workflow keyword \"ulw\" -> ultrawork/);
|
|
467
|
+
assert.equal(existsSync(join(cwd, ".omx", "state", "sessions", "sess-ulw-ko", "ultrawork-state.json")), true);
|
|
468
|
+
}
|
|
469
|
+
finally {
|
|
470
|
+
await rm(cwd, { recursive: true, force: true });
|
|
471
|
+
}
|
|
472
|
+
});
|
|
376
473
|
it("does not activate Ralph workflow state from a plain conversational mention", async () => {
|
|
377
474
|
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-ralph-plain-text-"));
|
|
378
475
|
try {
|
|
@@ -538,6 +635,9 @@ describe("codex native hook dispatch", () => {
|
|
|
538
635
|
assert.match(message, /skill: deep-interview activated and initial state initialized at \.omx\/state\/sessions\/sess-deep-interview-msg\/deep-interview-state\.json; write subsequent updates via omx_state MCP\./);
|
|
539
636
|
assert.match(message, /Deep-interview must ask each interview round via `omx question`/);
|
|
540
637
|
assert.match(message, /do not fall back to `request_user_input` or plain-text questioning/i);
|
|
638
|
+
assert.match(message, /After starting `omx question` in a background terminal, wait for that terminal to finish and read the JSON answer before continuing the interview\./);
|
|
639
|
+
assert.match(message, /If bare `omx question` is unavailable in this reused session, use the current-session CLI bridge command:/);
|
|
640
|
+
assert.match(message, /`'.+' '.+dist\/cli\/omx\.js' question`/);
|
|
541
641
|
assert.match(message, /Stop remains blocked while a deep-interview question obligation is pending\./);
|
|
542
642
|
}
|
|
543
643
|
finally {
|
|
@@ -2470,6 +2570,55 @@ esac
|
|
|
2470
2570
|
await rm(cwd, { recursive: true, force: true });
|
|
2471
2571
|
}
|
|
2472
2572
|
});
|
|
2573
|
+
it("blocks Stop when a same-session deep-interview question obligation is pending even after the mode marked itself inactive", async () => {
|
|
2574
|
+
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-deep-interview-question-inactive-"));
|
|
2575
|
+
try {
|
|
2576
|
+
const stateDir = join(cwd, ".omx", "state");
|
|
2577
|
+
await mkdir(join(stateDir, "sessions", "sess-stop-deep-interview-question-inactive"), { recursive: true });
|
|
2578
|
+
await writeJson(join(stateDir, "session.json"), { session_id: "sess-stop-deep-interview-question-inactive" });
|
|
2579
|
+
await writeJson(join(stateDir, "sessions", "sess-stop-deep-interview-question-inactive", "skill-active-state.json"), {
|
|
2580
|
+
version: 1,
|
|
2581
|
+
active: true,
|
|
2582
|
+
skill: "deep-interview",
|
|
2583
|
+
phase: "planning",
|
|
2584
|
+
session_id: "sess-stop-deep-interview-question-inactive",
|
|
2585
|
+
thread_id: "thread-stop-deep-interview-question-inactive",
|
|
2586
|
+
});
|
|
2587
|
+
await writeJson(join(stateDir, "sessions", "sess-stop-deep-interview-question-inactive", "deep-interview-state.json"), {
|
|
2588
|
+
active: false,
|
|
2589
|
+
mode: "deep-interview",
|
|
2590
|
+
current_phase: "intent-first",
|
|
2591
|
+
lifecycle_outcome: "askuserQuestion",
|
|
2592
|
+
run_outcome: "blocked_on_user",
|
|
2593
|
+
completed_at: "2026-04-19T03:20:30.000Z",
|
|
2594
|
+
session_id: "sess-stop-deep-interview-question-inactive",
|
|
2595
|
+
thread_id: "thread-stop-deep-interview-question-inactive",
|
|
2596
|
+
question_enforcement: {
|
|
2597
|
+
obligation_id: "obligation-inactive",
|
|
2598
|
+
source: "omx-question",
|
|
2599
|
+
status: "pending",
|
|
2600
|
+
lifecycle_outcome: "askuserQuestion",
|
|
2601
|
+
requested_at: "2026-04-19T03:20:00.000Z",
|
|
2602
|
+
},
|
|
2603
|
+
});
|
|
2604
|
+
const result = await dispatchCodexNativeHook({
|
|
2605
|
+
hook_event_name: "Stop",
|
|
2606
|
+
cwd,
|
|
2607
|
+
session_id: "sess-stop-deep-interview-question-inactive",
|
|
2608
|
+
thread_id: "thread-stop-deep-interview-question-inactive",
|
|
2609
|
+
}, { cwd });
|
|
2610
|
+
assert.equal(result.omxEventName, "stop");
|
|
2611
|
+
assert.deepEqual(result.outputJson, {
|
|
2612
|
+
decision: "block",
|
|
2613
|
+
reason: "Deep interview is still active (phase: intent-first) and has a pending structured question obligation; use `omx question` before stopping.",
|
|
2614
|
+
stopReason: "deep_interview_question_required",
|
|
2615
|
+
systemMessage: "OMX deep-interview is still active (phase: intent-first) and requires a structured question via omx question before stopping.",
|
|
2616
|
+
});
|
|
2617
|
+
}
|
|
2618
|
+
finally {
|
|
2619
|
+
await rm(cwd, { recursive: true, force: true });
|
|
2620
|
+
}
|
|
2621
|
+
});
|
|
2473
2622
|
it("keeps blocking pending deep-interview question Stop replays until the obligation changes", async () => {
|
|
2474
2623
|
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-deep-interview-question-replay-"));
|
|
2475
2624
|
try {
|
|
@@ -2738,6 +2887,50 @@ esac
|
|
|
2738
2887
|
await rm(cwd, { recursive: true, force: true });
|
|
2739
2888
|
}
|
|
2740
2889
|
});
|
|
2890
|
+
it("does not block Stop from stale current-session Ralph state when session.json points to a dead owner", async () => {
|
|
2891
|
+
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-stale-current-session-ralph-"));
|
|
2892
|
+
try {
|
|
2893
|
+
const stateDir = join(cwd, ".omx", "state");
|
|
2894
|
+
await mkdir(join(stateDir, "sessions", "sess-dead"), { recursive: true });
|
|
2895
|
+
await writeJson(join(stateDir, "session.json"), {
|
|
2896
|
+
session_id: "sess-dead",
|
|
2897
|
+
cwd,
|
|
2898
|
+
pid: Number.MAX_SAFE_INTEGER,
|
|
2899
|
+
started_at: "2026-01-01T00:00:00.000Z",
|
|
2900
|
+
});
|
|
2901
|
+
await writeJson(join(stateDir, "sessions", "sess-dead", "ralph-state.json"), {
|
|
2902
|
+
active: true,
|
|
2903
|
+
current_phase: "verifying",
|
|
2904
|
+
session_id: "sess-dead",
|
|
2905
|
+
});
|
|
2906
|
+
await writeJson(join(stateDir, "skill-active-state.json"), {
|
|
2907
|
+
active: true,
|
|
2908
|
+
skill: "team",
|
|
2909
|
+
phase: "team-exec",
|
|
2910
|
+
active_skills: [{ skill: "team", phase: "team-exec", active: true, session_id: "sess-dead" }],
|
|
2911
|
+
});
|
|
2912
|
+
await writeJson(join(stateDir, "native-stop-state.json"), {
|
|
2913
|
+
sessions: {
|
|
2914
|
+
"sess-dead": {
|
|
2915
|
+
last_signature: "ralph-stop|sess-dead|thread-1|no-message|verifying",
|
|
2916
|
+
updated_at: "2026-04-20T21:00:00.000Z",
|
|
2917
|
+
},
|
|
2918
|
+
},
|
|
2919
|
+
});
|
|
2920
|
+
const result = await dispatchCodexNativeHook({
|
|
2921
|
+
hook_event_name: "Stop",
|
|
2922
|
+
cwd,
|
|
2923
|
+
session_id: "sess-dead",
|
|
2924
|
+
thread_id: "thread-1",
|
|
2925
|
+
stop_hook_active: true,
|
|
2926
|
+
}, { cwd });
|
|
2927
|
+
assert.equal(result.omxEventName, "stop");
|
|
2928
|
+
assert.equal(result.outputJson, null);
|
|
2929
|
+
}
|
|
2930
|
+
finally {
|
|
2931
|
+
await rm(cwd, { recursive: true, force: true });
|
|
2932
|
+
}
|
|
2933
|
+
});
|
|
2741
2934
|
it("does not block Stop from another session-scoped Ralph state when an explicit session_id has no active Ralph state", async () => {
|
|
2742
2935
|
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-explicit-session-ralph-"));
|
|
2743
2936
|
try {
|
|
@@ -2875,6 +3068,91 @@ esac
|
|
|
2875
3068
|
await rm(cwd, { recursive: true, force: true });
|
|
2876
3069
|
}
|
|
2877
3070
|
});
|
|
3071
|
+
it("lets dispatcher dedupe identical native stop hook replays after Stop payload normalization", async () => {
|
|
3072
|
+
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-ralph-hook-dedupe-"));
|
|
3073
|
+
const previousOmxSessionId = process.env.OMX_SESSION_ID;
|
|
3074
|
+
try {
|
|
3075
|
+
const stateDir = join(cwd, ".omx", "state");
|
|
3076
|
+
await mkdir(join(stateDir, "sessions", "sess-stop-ralph-hook-dedupe"), { recursive: true });
|
|
3077
|
+
await writeHookCounterPlugin(cwd);
|
|
3078
|
+
await writeFile(join(stateDir, "sessions", "sess-stop-ralph-hook-dedupe", "ralph-state.json"), JSON.stringify({
|
|
3079
|
+
active: true,
|
|
3080
|
+
current_phase: "executing",
|
|
3081
|
+
session_id: "sess-stop-ralph-hook-dedupe",
|
|
3082
|
+
}));
|
|
3083
|
+
process.env.OMX_SESSION_ID = "sess-stop-ralph-hook-dedupe";
|
|
3084
|
+
const payload = {
|
|
3085
|
+
hook_event_name: "Stop",
|
|
3086
|
+
cwd,
|
|
3087
|
+
session_id: "sess-stop-ralph-hook-dedupe",
|
|
3088
|
+
thread_id: "thread-stop-ralph-hook-dedupe",
|
|
3089
|
+
turn_id: "turn-stop-ralph-hook-dedupe-1",
|
|
3090
|
+
last_assistant_message: "Next active targets:\n\n1. scheduler integration\n\nI am continuing.",
|
|
3091
|
+
};
|
|
3092
|
+
await dispatchCodexNativeHook(payload, { cwd });
|
|
3093
|
+
await dispatchCodexNativeHook({
|
|
3094
|
+
...payload,
|
|
3095
|
+
stop_hook_active: true,
|
|
3096
|
+
}, { cwd });
|
|
3097
|
+
const marker = JSON.parse(await readFile(join(cwd, ".omx", "stop-hook-counter.json"), "utf-8"));
|
|
3098
|
+
assert.equal(marker.count, 1);
|
|
3099
|
+
}
|
|
3100
|
+
finally {
|
|
3101
|
+
if (typeof previousOmxSessionId === "string")
|
|
3102
|
+
process.env.OMX_SESSION_ID = previousOmxSessionId;
|
|
3103
|
+
else
|
|
3104
|
+
delete process.env.OMX_SESSION_ID;
|
|
3105
|
+
await rm(cwd, { recursive: true, force: true });
|
|
3106
|
+
}
|
|
3107
|
+
});
|
|
3108
|
+
it("preserves per-turn native stop hook delivery even when stop_hook_active remains true", async () => {
|
|
3109
|
+
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-ralph-hook-refire-"));
|
|
3110
|
+
const previousOmxSessionId = process.env.OMX_SESSION_ID;
|
|
3111
|
+
try {
|
|
3112
|
+
const stateDir = join(cwd, ".omx", "state");
|
|
3113
|
+
await mkdir(join(stateDir, "sessions", "sess-stop-ralph-hook-refire"), { recursive: true });
|
|
3114
|
+
await writeHookCounterPlugin(cwd);
|
|
3115
|
+
await writeFile(join(stateDir, "sessions", "sess-stop-ralph-hook-refire", "ralph-state.json"), JSON.stringify({
|
|
3116
|
+
active: true,
|
|
3117
|
+
current_phase: "executing",
|
|
3118
|
+
session_id: "sess-stop-ralph-hook-refire",
|
|
3119
|
+
}));
|
|
3120
|
+
process.env.OMX_SESSION_ID = "sess-stop-ralph-hook-refire";
|
|
3121
|
+
const payload = {
|
|
3122
|
+
hook_event_name: "Stop",
|
|
3123
|
+
cwd,
|
|
3124
|
+
session_id: "sess-stop-ralph-hook-refire",
|
|
3125
|
+
thread_id: "thread-stop-ralph-hook-refire",
|
|
3126
|
+
turn_id: "turn-stop-ralph-hook-refire-1",
|
|
3127
|
+
last_assistant_message: "Continuing current task.",
|
|
3128
|
+
};
|
|
3129
|
+
await dispatchCodexNativeHook(payload, { cwd });
|
|
3130
|
+
await dispatchCodexNativeHook({
|
|
3131
|
+
...payload,
|
|
3132
|
+
turn_id: "turn-stop-ralph-hook-refire-2",
|
|
3133
|
+
stop_hook_active: true,
|
|
3134
|
+
}, { cwd });
|
|
3135
|
+
await writeFile(join(stateDir, "sessions", "sess-stop-ralph-hook-refire", "ralph-state.json"), JSON.stringify({
|
|
3136
|
+
active: true,
|
|
3137
|
+
current_phase: "executing",
|
|
3138
|
+
session_id: "sess-stop-ralph-hook-refire",
|
|
3139
|
+
}));
|
|
3140
|
+
await dispatchCodexNativeHook({
|
|
3141
|
+
...payload,
|
|
3142
|
+
turn_id: "turn-stop-ralph-hook-refire-3",
|
|
3143
|
+
stop_hook_active: true,
|
|
3144
|
+
}, { cwd });
|
|
3145
|
+
const marker = JSON.parse(await readFile(join(cwd, ".omx", "stop-hook-counter.json"), "utf-8"));
|
|
3146
|
+
assert.equal(marker.count, 3);
|
|
3147
|
+
}
|
|
3148
|
+
finally {
|
|
3149
|
+
if (typeof previousOmxSessionId === "string")
|
|
3150
|
+
process.env.OMX_SESSION_ID = previousOmxSessionId;
|
|
3151
|
+
else
|
|
3152
|
+
delete process.env.OMX_SESSION_ID;
|
|
3153
|
+
await rm(cwd, { recursive: true, force: true });
|
|
3154
|
+
}
|
|
3155
|
+
});
|
|
2878
3156
|
it("returns Stop continuation output for native auto-nudge stall prompts", async () => {
|
|
2879
3157
|
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-auto-nudge-"));
|
|
2880
3158
|
try {
|
|
@@ -2975,6 +3253,51 @@ esac
|
|
|
2975
3253
|
await rm(cwd, { recursive: true, force: true });
|
|
2976
3254
|
}
|
|
2977
3255
|
});
|
|
3256
|
+
it("dedupes native stop hook replay across owner launch SessionStart reconciliation drift", async () => {
|
|
3257
|
+
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-dispatch-session-drift-"));
|
|
3258
|
+
try {
|
|
3259
|
+
const stateDir = join(cwd, ".omx", "state");
|
|
3260
|
+
await mkdir(join(stateDir, "sessions", "omx-canonical"), { recursive: true });
|
|
3261
|
+
await writeHookCounterPlugin(cwd);
|
|
3262
|
+
process.env.OMX_SESSION_ID = "omx-canonical";
|
|
3263
|
+
await writeSessionStart(cwd, "omx-canonical");
|
|
3264
|
+
await writeJson(join(stateDir, "sessions", "omx-canonical", "ralph-state.json"), {
|
|
3265
|
+
active: true,
|
|
3266
|
+
current_phase: "executing",
|
|
3267
|
+
session_id: "omx-canonical",
|
|
3268
|
+
});
|
|
3269
|
+
await dispatchCodexNativeHook({
|
|
3270
|
+
hook_event_name: "SessionStart",
|
|
3271
|
+
cwd,
|
|
3272
|
+
session_id: "codex-native-new",
|
|
3273
|
+
}, { cwd, sessionOwnerPid: process.pid });
|
|
3274
|
+
await dispatchCodexNativeHook({
|
|
3275
|
+
hook_event_name: "Stop",
|
|
3276
|
+
cwd,
|
|
3277
|
+
session_id: "codex-native-new",
|
|
3278
|
+
thread_id: "thread-stop-hook-drift",
|
|
3279
|
+
turn_id: "turn-stop-hook-drift-1",
|
|
3280
|
+
last_assistant_message: "Keep going and finish the cleanup.",
|
|
3281
|
+
}, { cwd });
|
|
3282
|
+
await dispatchCodexNativeHook({
|
|
3283
|
+
hook_event_name: "Stop",
|
|
3284
|
+
cwd,
|
|
3285
|
+
session_id: "omx-canonical",
|
|
3286
|
+
thread_id: "thread-stop-hook-drift",
|
|
3287
|
+
turn_id: "turn-stop-hook-drift-1",
|
|
3288
|
+
stop_hook_active: true,
|
|
3289
|
+
last_assistant_message: "Keep going and finish the cleanup.",
|
|
3290
|
+
}, { cwd });
|
|
3291
|
+
const marker = JSON.parse(await readFile(join(cwd, ".omx", "stop-hook-counter.json"), "utf-8"));
|
|
3292
|
+
assert.equal(marker.count, 1);
|
|
3293
|
+
const sessionState = JSON.parse(await readFile(join(stateDir, "session.json"), "utf-8"));
|
|
3294
|
+
assert.equal(sessionState.session_id, "omx-canonical");
|
|
3295
|
+
assert.equal(sessionState.native_session_id, "codex-native-new");
|
|
3296
|
+
}
|
|
3297
|
+
finally {
|
|
3298
|
+
await rm(cwd, { recursive: true, force: true });
|
|
3299
|
+
}
|
|
3300
|
+
});
|
|
2978
3301
|
it("re-fires native auto-nudge for a later fresh Stop reply even when stop_hook_active is true", async () => {
|
|
2979
3302
|
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-auto-nudge-refire-"));
|
|
2980
3303
|
try {
|
|
@@ -3432,59 +3755,50 @@ esac
|
|
|
3432
3755
|
await rm(cwd, { recursive: true, force: true });
|
|
3433
3756
|
}
|
|
3434
3757
|
});
|
|
3435
|
-
it("
|
|
3436
|
-
const
|
|
3437
|
-
|
|
3438
|
-
|
|
3439
|
-
|
|
3440
|
-
|
|
3441
|
-
|
|
3442
|
-
|
|
3443
|
-
|
|
3444
|
-
|
|
3758
|
+
it("suppresses duplicate ultrawork Stop replays while stop_hook_active stays true", async () => {
|
|
3759
|
+
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-ultrawork-repeat-"));
|
|
3760
|
+
try {
|
|
3761
|
+
const stateDir = join(cwd, ".omx", "state");
|
|
3762
|
+
await mkdir(stateDir, { recursive: true });
|
|
3763
|
+
await writeJson(join(stateDir, "ultrawork-state.json"), {
|
|
3764
|
+
active: true,
|
|
3765
|
+
current_phase: "executing",
|
|
3766
|
+
});
|
|
3767
|
+
const first = await dispatchCodexNativeHook({
|
|
3768
|
+
hook_event_name: "Stop",
|
|
3769
|
+
cwd,
|
|
3770
|
+
session_id: "sess-stop-ultrawork-repeat",
|
|
3771
|
+
thread_id: "thread-stop-ultrawork-repeat",
|
|
3772
|
+
turn_id: "turn-stop-ultrawork-repeat-1",
|
|
3773
|
+
}, { cwd });
|
|
3774
|
+
const repeated = await dispatchCodexNativeHook({
|
|
3775
|
+
hook_event_name: "Stop",
|
|
3776
|
+
cwd,
|
|
3777
|
+
session_id: "sess-stop-ultrawork-repeat",
|
|
3778
|
+
thread_id: "thread-stop-ultrawork-repeat",
|
|
3779
|
+
turn_id: "turn-stop-ultrawork-repeat-1",
|
|
3780
|
+
stop_hook_active: true,
|
|
3781
|
+
}, { cwd });
|
|
3782
|
+
const fresh = await dispatchCodexNativeHook({
|
|
3783
|
+
hook_event_name: "Stop",
|
|
3784
|
+
cwd,
|
|
3785
|
+
session_id: "sess-stop-ultrawork-repeat",
|
|
3786
|
+
thread_id: "thread-stop-ultrawork-repeat",
|
|
3787
|
+
turn_id: "turn-stop-ultrawork-repeat-2",
|
|
3788
|
+
stop_hook_active: true,
|
|
3789
|
+
}, { cwd });
|
|
3790
|
+
assert.equal(first.omxEventName, "stop");
|
|
3791
|
+
assert.deepEqual(repeated.outputJson, null);
|
|
3792
|
+
assert.equal(fresh.omxEventName, "stop");
|
|
3793
|
+
assert.deepEqual(fresh.outputJson, {
|
|
3794
|
+
decision: "block",
|
|
3445
3795
|
reason: "OMX ultrawork is still active (phase: executing); continue the task and gather fresh verification evidence before stopping.",
|
|
3446
|
-
|
|
3447
|
-
|
|
3448
|
-
|
|
3449
|
-
|
|
3450
|
-
|
|
3451
|
-
}
|
|
3452
|
-
];
|
|
3453
|
-
for (const testCase of cases) {
|
|
3454
|
-
const cwd = await mkdtemp(join(tmpdir(), `omx-native-hook-stop-${testCase.mode}-repeat-`));
|
|
3455
|
-
try {
|
|
3456
|
-
const stateDir = join(cwd, ".omx", "state");
|
|
3457
|
-
await mkdir(stateDir, { recursive: true });
|
|
3458
|
-
await writeJson(join(stateDir, `${testCase.mode}-state.json`), {
|
|
3459
|
-
active: true,
|
|
3460
|
-
current_phase: testCase.phase,
|
|
3461
|
-
});
|
|
3462
|
-
await dispatchCodexNativeHook({
|
|
3463
|
-
hook_event_name: "Stop",
|
|
3464
|
-
cwd,
|
|
3465
|
-
session_id: `sess-stop-${testCase.mode}-repeat`,
|
|
3466
|
-
thread_id: `thread-stop-${testCase.mode}-repeat`,
|
|
3467
|
-
turn_id: `turn-stop-${testCase.mode}-repeat-1`,
|
|
3468
|
-
}, { cwd });
|
|
3469
|
-
const repeated = await dispatchCodexNativeHook({
|
|
3470
|
-
hook_event_name: "Stop",
|
|
3471
|
-
cwd,
|
|
3472
|
-
session_id: `sess-stop-${testCase.mode}-repeat`,
|
|
3473
|
-
thread_id: `thread-stop-${testCase.mode}-repeat`,
|
|
3474
|
-
turn_id: `turn-stop-${testCase.mode}-repeat-1`,
|
|
3475
|
-
stop_hook_active: true,
|
|
3476
|
-
}, { cwd });
|
|
3477
|
-
assert.equal(repeated.omxEventName, "stop");
|
|
3478
|
-
assert.deepEqual(repeated.outputJson, {
|
|
3479
|
-
decision: "block",
|
|
3480
|
-
reason: testCase.reason,
|
|
3481
|
-
stopReason: `${testCase.mode}_${testCase.phase}`,
|
|
3482
|
-
systemMessage: `OMX ${testCase.mode} is still active (phase: ${testCase.phase}).`,
|
|
3483
|
-
});
|
|
3484
|
-
}
|
|
3485
|
-
finally {
|
|
3486
|
-
await rm(cwd, { recursive: true, force: true });
|
|
3487
|
-
}
|
|
3796
|
+
stopReason: "ultrawork_executing",
|
|
3797
|
+
systemMessage: "OMX ultrawork is still active (phase: executing).",
|
|
3798
|
+
});
|
|
3799
|
+
}
|
|
3800
|
+
finally {
|
|
3801
|
+
await rm(cwd, { recursive: true, force: true });
|
|
3488
3802
|
}
|
|
3489
3803
|
});
|
|
3490
3804
|
it("re-blocks active ralplan skill state on repeated Stop hooks", async () => {
|