feat(#2088): migrate Codex onto the Embeddable Orchestration System (ADR-1239)

Drive Codex install/uninstall through the descriptor-driven Host-Integration
Interface (declarative embedding adapter → engine surface dispatch) and fold
every positive `runtime === 'codex'` / `isCodex` projection into descriptor-driven
`runtime.hostBehaviors`. Install/uninstall output stays byte-parity-gated
(tests/fixtures/golden-install-parity/codex.json); no other runtime changes.

Three Context7-verified upgrades, each with a test on the user-reachable surface:
- Skill root → canonical $HOME/.agents/skills via a skills-kind `home` override,
  with pre-move migration cleanup (stale ~/.codex/skills/gsd-* removed on install
  and uninstall; user content preserved). Fixes getGlobalSkillsBase, writeManifest,
  and the skill-manifest inventory to honor the override so --skills-root /
  sync-skills / the manifest report the real location.
- Six new hooks.json lifecycle events (PreToolUse, PermissionRequest, PreCompact,
  PostCompact, SubagentStop, UserPromptSubmit) shared by install + uninstall;
  extendedHookEvents reconciled [] -> the schema-valid wired subset.
- Explicit `[agents] max_depth = 1` in the managed config.toml block, pinning the
  negotiated dispatch.maxDepth:1 axis. validateCodexConfigSchema now permits a
  known-scalar-only bare `[agents]` AgentsToml table (still rejects [[agents]] and
  unknown-key break-forms, #2760); mergeCodexConfig preserves the user's own
  AgentsToml scalars (max_threads etc.) instead of dropping them.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Tom Boucher
2026-07-08 21:42:11 -04:00
parent e5a1d7f5c4
commit 6e773d97df
24 changed files with 1252 additions and 211 deletions

View File

@@ -73,6 +73,10 @@ interface ArtifactKind {
/** For agents kind with a converter, accepts an optional AgentCtx as the second
* arg so cross-cutting can be applied pre-converter (ADR-1235 §1). */
stage: (resolvedProfile: ResolvedProfile, agentCtx?: AgentCtx) => string;
/** Resolved absolute alternate install root for this kind, if the descriptor
* specifies one (e.g. codex skills → $HOME/.agents). Undefined means the
* kind installs under the runtime's normal configDir. */
home?: string;
}
interface Layout {
@@ -419,6 +423,10 @@ interface ArtifactKindDescriptor {
nesting: 'flat' | 'nested';
recursive: boolean;
converter: string | null;
/** Optional alternate install home, relative to the user's home directory
* (e.g. ".agents" for codex skills → $HOME/.agents/skills). When absent,
* the kind installs under the runtime's normal configDir. */
home?: string;
}
interface ArtifactLayoutDescriptor {
@@ -445,18 +453,19 @@ function dispatchKindEntry(entry: ArtifactKindDescriptor, runtime: string, confi
const { kind, destSubpath, prefix, nesting, converter } = entry;
const nested = nesting === 'nested';
let result: ArtifactKind;
switch (kind) {
case 'commands':
if (converter == null) {
return commandsKind(destSubpath, prefix, configDir);
}
return convertedCommandsKind(destSubpath, prefix, converter, configDir);
result = converter == null
? commandsKind(destSubpath, prefix, configDir)
: convertedCommandsKind(destSubpath, prefix, converter, configDir);
break;
case 'agents':
if (converter == null) {
return agentsKind(destSubpath, prefix, configDir);
}
return convertedAgentsKind(destSubpath, prefix, converter, configDir, scope);
result = converter == null
? agentsKind(destSubpath, prefix, configDir)
: convertedAgentsKind(destSubpath, prefix, converter, configDir, scope);
break;
case 'skills':
if (converter == null) {
@@ -464,16 +473,24 @@ function dispatchKindEntry(entry: ArtifactKindDescriptor, runtime: string, confi
`resolveRuntimeArtifactLayout: skills entry for '${runtime}' has converter=null (converter is required for skills)`,
);
}
return skillsKind(destSubpath, prefix, converter, runtime, configDir, nested, scope);
result = skillsKind(destSubpath, prefix, converter, runtime, configDir, nested, scope);
break;
case 'kimi-agents':
return kimiAgentsKind(destSubpath, prefix, configDir);
result = kimiAgentsKind(destSubpath, prefix, configDir);
break;
default:
throw new TypeError(
`resolveRuntimeArtifactLayout: unknown kind '${kind}' in descriptor for runtime '${runtime}'`,
);
}
if (typeof entry.home === 'string' && entry.home !== '') {
result.home = path.join(os.homedir(), entry.home);
}
return result;
}
/**