Add native Codex hooks integration (#2319)

* Add native Codex hooks integration

* Address Codex review feedback

* Use durable Codex marketplace root

* Address Codex file context review feedback

* Harden Codex installer review paths

* Report Codex legacy cleanup failures

* fix: keep MCP manifests in marketplace sync

* fix: bundle zod in MCP server

* fix: warn on Codex legacy cleanup failure

* Fix hook observation readiness timeouts

* Address Codex hook review notes

* Tighten Codex MCP file context matching

* Resolve final Codex review nits

* Add Codex marketplace version guidance

* Reset worker failure counter on API fallback

* Fix Codex cat flag file extraction
This commit is contained in:
Alex Newman
2026-05-06 01:55:27 -07:00
committed by GitHub
parent a5bb6b346a
commit 56db06811e
33 changed files with 1628 additions and 504 deletions
+86
View File
@@ -11,6 +11,22 @@ const installSourcePath = join(
'install.ts',
);
const installSource = readFileSync(installSourcePath, 'utf-8');
const codexInstallerSourcePath = join(
__dirname,
'..',
'src',
'services',
'integrations',
'CodexCliInstaller.ts',
);
const codexInstallerSource = readFileSync(codexInstallerSourcePath, 'utf-8');
const syncMarketplaceSourcePath = join(
__dirname,
'..',
'scripts',
'sync-marketplace.cjs',
);
const syncMarketplaceSource = readFileSync(syncMarketplaceSourcePath, 'utf-8');
describe('Install Non-TTY Support', () => {
describe('isInteractive flag', () => {
@@ -76,6 +92,76 @@ describe('Install Non-TTY Support', () => {
it('uses console.log for note/summary in non-interactive mode', () => {
expect(installSource).toContain("console.log(`\\n ${installStatus}`)");
});
it('copies Codex marketplace metadata to the durable marketplace directory', () => {
const copyRegion = installSource.slice(
installSource.indexOf('const allowedTopLevelEntries = ['),
installSource.indexOf('function copyPluginToCache'),
);
expect(copyRegion).toContain("'.agents'");
expect(copyRegion).toContain("'.codex-plugin'");
expect(copyRegion).toContain("'.mcp.json'");
});
it('does not exclude MCP manifests during local marketplace sync', () => {
const gitignoreExcludeRegion = syncMarketplaceSource.slice(
syncMarketplaceSource.indexOf('function getGitignoreExcludes'),
syncMarketplaceSource.indexOf('const branch = getCurrentBranch'),
);
expect(gitignoreExcludeRegion).toContain("'.mcp.json'");
expect(gitignoreExcludeRegion).toContain('syncManagedFiles.has(line)');
});
it('registers Codex against the durable marketplace directory', () => {
expect(installSource).toContain('installCodexCli(marketplaceDirectory())');
});
it('captures Codex CLI output for install failure reporting', () => {
const runCodexRegion = codexInstallerSource.slice(
codexInstallerSource.indexOf('function runCodex'),
codexInstallerSource.indexOf('function removeCodexAgentsMdContext'),
);
expect(runCodexRegion).toContain('spawnSync');
expect(runCodexRegion).not.toContain("stdio: 'inherit'");
});
it('checks Codex CLI marketplace version before registration', () => {
const installRegion = codexInstallerSource.slice(
codexInstallerSource.indexOf('export async function installCodexCli'),
codexInstallerSource.indexOf('export function uninstallCodexCli'),
);
expect(codexInstallerSource).toContain("const MIN_CODEX_MARKETPLACE_VERSION = '0.128.0'");
expect(codexInstallerSource).toContain("spawnSync('codex', ['--version']");
expect(installRegion.indexOf('assertCodexMarketplaceSupported()'))
.toBeLessThan(installRegion.indexOf("runCodex(['plugin', 'marketplace', 'add', marketplaceRoot])"));
});
it('removes legacy Codex AGENTS context only after marketplace registration succeeds', () => {
const installRegion = codexInstallerSource.slice(
codexInstallerSource.indexOf('export async function installCodexCli'),
codexInstallerSource.indexOf('export function uninstallCodexCli'),
);
expect(installRegion.indexOf("runCodex(['plugin', 'marketplace', 'add', marketplaceRoot])"))
.toBeLessThan(installRegion.indexOf('cleanupLegacyCodexAgentsMdContext()'));
});
it('reports legacy Codex AGENTS cleanup failures to callers', () => {
expect(codexInstallerSource).toContain('function removeCodexAgentsMdContext(): boolean');
expect(codexInstallerSource).toContain('if (!cleanupLegacyCodexAgentsMdContext())');
});
it('does not fail Codex install after marketplace registration when only AGENTS cleanup fails', () => {
const installRegion = codexInstallerSource.slice(
codexInstallerSource.indexOf('export async function installCodexCli'),
codexInstallerSource.indexOf('export function uninstallCodexCli'),
);
const cleanupFailureRegion = installRegion.slice(
installRegion.indexOf('if (!cleanupLegacyCodexAgentsMdContext())'),
installRegion.indexOf('Installation complete!'),
);
expect(cleanupFailureRegion).toContain('console.warn');
expect(cleanupFailureRegion).not.toContain('return 1');
});
});
describe('TaskDescriptor interface', () => {