oh-my-opencode 4.16.1 → 4.16.3

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 (151) hide show
  1. package/.agents/skills/pre-publish-review/SKILL.md +3 -0
  2. package/README.md +2 -2
  3. package/dist/agents/hephaestus/agent.d.ts +1 -1
  4. package/dist/agents/hephaestus/gpt-5-6.d.ts +2 -0
  5. package/dist/agents/momus-gpt-5-6.d.ts +1 -0
  6. package/dist/agents/types.d.ts +2 -0
  7. package/dist/cli/index.js +800 -536
  8. package/dist/cli-node/index.js +800 -536
  9. package/dist/features/background-agent/error-classifier.d.ts +5 -0
  10. package/dist/features/background-agent/parent-wake-dedupe.d.ts +1 -0
  11. package/dist/features/background-agent/parent-wake-flush-runner.d.ts +2 -0
  12. package/dist/features/background-agent/parent-wake-prompt-dispatch.d.ts +1 -0
  13. package/dist/features/builtin-commands/templates/refactor-sections/intro-and-analysis.d.ts +1 -1
  14. package/dist/features/builtin-commands/templates/remove-ai-slops.d.ts +1 -1
  15. package/dist/index.js +464 -54
  16. package/dist/shared/live-server-route.d.ts +1 -1
  17. package/dist/skills/review-work/SKILL.md +4 -2
  18. package/dist/skills/start-work/SKILL.md +2 -0
  19. package/dist/skills/ulw-plan/references/full-workflow.md +1 -1
  20. package/dist/skills/ulw-research/SKILL.md +5 -3
  21. package/dist/skills/visual-qa/SKILL.md +1 -1
  22. package/dist/tui.js +125 -13
  23. package/package.json +13 -13
  24. package/packages/omo-codex/plugin/.codex-plugin/plugin.json +1 -1
  25. package/packages/omo-codex/plugin/components/bootstrap/dist/cli.js +398 -246
  26. package/packages/omo-codex/plugin/components/bootstrap/hooks/hooks.json +1 -1
  27. package/packages/omo-codex/plugin/components/bootstrap/package.json +1 -1
  28. package/packages/omo-codex/plugin/components/codegraph/package.json +1 -1
  29. package/packages/omo-codex/plugin/components/comment-checker/hooks/hooks.json +1 -1
  30. package/packages/omo-codex/plugin/components/comment-checker/package.json +1 -1
  31. package/packages/omo-codex/plugin/components/git-bash/hooks/hooks.json +2 -2
  32. package/packages/omo-codex/plugin/components/git-bash/package.json +1 -1
  33. package/packages/omo-codex/plugin/components/lazycodex-executor-verify/hooks/hooks.json +1 -1
  34. package/packages/omo-codex/plugin/components/lazycodex-executor-verify/package.json +1 -1
  35. package/packages/omo-codex/plugin/components/lsp/hooks/hooks.json +2 -2
  36. package/packages/omo-codex/plugin/components/lsp/package.json +1 -1
  37. package/packages/omo-codex/plugin/components/rules/bundled-rules/{hephaestus.md → hephaestus/gpt-5.5.md} +1 -1
  38. package/packages/omo-codex/plugin/components/rules/bundled-rules/hephaestus/gpt-5.6.md +93 -0
  39. package/packages/omo-codex/plugin/components/rules/dist/cli.js +43 -24
  40. package/packages/omo-codex/plugin/components/rules/hooks/hooks.json +4 -4
  41. package/packages/omo-codex/plugin/components/rules/package.json +1 -1
  42. package/packages/omo-codex/plugin/components/rules/src/codex-hook.ts +2 -1
  43. package/packages/omo-codex/plugin/components/rules/src/dynamic-target-fingerprints.ts +15 -6
  44. package/packages/omo-codex/plugin/components/rules/src/rules-engine-factory.ts +7 -2
  45. package/packages/omo-codex/plugin/components/rules/src/static-injection.ts +2 -2
  46. package/packages/omo-codex/plugin/components/rules/test/codex-hook.test.ts +32 -0
  47. package/packages/omo-codex/plugin/components/rules/test/hephaestus-model-variant.test.ts +106 -0
  48. package/packages/omo-codex/plugin/components/start-work-continuation/directive.md +1 -1
  49. package/packages/omo-codex/plugin/components/start-work-continuation/hooks/hooks.json +2 -2
  50. package/packages/omo-codex/plugin/components/start-work-continuation/package.json +1 -1
  51. package/packages/omo-codex/plugin/components/start-work-continuation/test/codex-hook.test.ts +1 -1
  52. package/packages/omo-codex/plugin/components/teammode/AGENTS.md +12 -9
  53. package/packages/omo-codex/plugin/components/teammode/hooks/hooks.json +1 -1
  54. package/packages/omo-codex/plugin/components/teammode/package.json +1 -1
  55. package/packages/omo-codex/plugin/components/teammode/skills/teammode/SKILL.md +136 -76
  56. package/packages/omo-codex/plugin/components/teammode/skills/teammode/scripts/team-guide.mjs +94 -23
  57. package/packages/omo-codex/plugin/components/teammode/skills/teammode/scripts/team-state.mjs +94 -14
  58. package/packages/omo-codex/plugin/components/teammode/skills/teammode/scripts/team-transport.mjs +55 -0
  59. package/packages/omo-codex/plugin/components/teammode/skills/teammode/scripts/team.mjs +72 -18
  60. package/packages/omo-codex/plugin/components/teammode/test/thread-title-hook.test.ts +23 -0
  61. package/packages/omo-codex/plugin/components/telemetry/hooks/hooks.json +1 -1
  62. package/packages/omo-codex/plugin/components/telemetry/package.json +1 -1
  63. package/packages/omo-codex/plugin/components/ultrawork/agents/explorer.toml +24 -41
  64. package/packages/omo-codex/plugin/components/ultrawork/agents/lazycodex-clone-fidelity-reviewer.toml +1 -1
  65. package/packages/omo-codex/plugin/components/ultrawork/agents/lazycodex-code-reviewer.toml +1 -1
  66. package/packages/omo-codex/plugin/components/ultrawork/agents/lazycodex-executor.toml +2 -2
  67. package/packages/omo-codex/plugin/components/ultrawork/agents/lazycodex-gate-reviewer.toml +1 -1
  68. package/packages/omo-codex/plugin/components/ultrawork/agents/lazycodex-qa-executor.toml +1 -1
  69. package/packages/omo-codex/plugin/components/ultrawork/agents/librarian.toml +21 -49
  70. package/packages/omo-codex/plugin/components/ultrawork/agents/metis.toml +17 -29
  71. package/packages/omo-codex/plugin/components/ultrawork/agents/momus.toml +20 -50
  72. package/packages/omo-codex/plugin/components/ultrawork/agents/plan.toml +18 -39
  73. package/packages/omo-codex/plugin/components/ultrawork/directive.md +1 -0
  74. package/packages/omo-codex/plugin/components/ultrawork/hooks/hooks.json +1 -1
  75. package/packages/omo-codex/plugin/components/ultrawork/package.json +1 -1
  76. package/packages/omo-codex/plugin/components/ultrawork/skills/ultrawork/SKILL.md +1 -0
  77. package/packages/omo-codex/plugin/components/ultrawork/skills/ulw-plan/SKILL.md +2 -0
  78. package/packages/omo-codex/plugin/components/ultrawork/skills/ulw-plan/references/full-workflow.md +3 -1
  79. package/packages/omo-codex/plugin/components/ulw-loop/directive.md +1 -0
  80. package/packages/omo-codex/plugin/components/ulw-loop/hooks/hooks.json +2 -2
  81. package/packages/omo-codex/plugin/components/ulw-loop/package.json +1 -1
  82. package/packages/omo-codex/plugin/components/ulw-loop/skills/ulw-loop/SKILL.md +2 -0
  83. package/packages/omo-codex/plugin/components/ulw-loop/skills/ulw-loop/references/full-workflow.md +1 -1
  84. package/packages/omo-codex/plugin/components/ulw-loop/test/skill-contract.test.ts +1 -1
  85. package/packages/omo-codex/plugin/hooks/post-compact-resetting-git-bash-mcp-reminder.json +1 -1
  86. package/packages/omo-codex/plugin/hooks/post-compact-resetting-lsp-diagnostics-cache.json +1 -1
  87. package/packages/omo-codex/plugin/hooks/post-compact-resetting-project-rule-cache.json +1 -1
  88. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-codegraph-init-guidance.json +1 -1
  89. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-comments.json +1 -1
  90. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-lsp-diagnostics.json +1 -1
  91. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-thread-title-hygiene.json +1 -1
  92. package/packages/omo-codex/plugin/hooks/post-tool-use-matching-project-rules.json +1 -1
  93. package/packages/omo-codex/plugin/hooks/pre-tool-use-enforcing-unlimited-goal-budget.json +1 -1
  94. package/packages/omo-codex/plugin/hooks/pre-tool-use-recommending-git-bash-mcp.json +1 -1
  95. package/packages/omo-codex/plugin/hooks/session-start-checking-auto-update.json +1 -1
  96. package/packages/omo-codex/plugin/hooks/session-start-checking-bootstrap-provisioning.json +1 -1
  97. package/packages/omo-codex/plugin/hooks/session-start-checking-codegraph-bootstrap.json +1 -1
  98. package/packages/omo-codex/plugin/hooks/session-start-loading-project-rules.json +1 -1
  99. package/packages/omo-codex/plugin/hooks/session-start-recording-session-telemetry.json +1 -1
  100. package/packages/omo-codex/plugin/hooks/stop-checking-start-work-continuation.json +1 -1
  101. package/packages/omo-codex/plugin/hooks/subagent-stop-checking-start-work-continuation.json +1 -1
  102. package/packages/omo-codex/plugin/hooks/subagent-stop-verifying-lazycodex-executor-evidence.json +1 -1
  103. package/packages/omo-codex/plugin/hooks/user-prompt-submit-checking-ultrawork-trigger.json +1 -1
  104. package/packages/omo-codex/plugin/hooks/user-prompt-submit-checking-ulw-loop-steering.json +1 -1
  105. package/packages/omo-codex/plugin/hooks/user-prompt-submit-loading-project-rules.json +1 -1
  106. package/packages/omo-codex/plugin/package-lock.json +13 -13
  107. package/packages/omo-codex/plugin/package.json +1 -1
  108. package/packages/omo-codex/plugin/scripts/auto-update.mjs +73 -19
  109. package/packages/omo-codex/plugin/scripts/entry-guard.mjs +26 -0
  110. package/packages/omo-codex/plugin/scripts/materialize-shared-upstreams.mjs +3 -1
  111. package/packages/omo-codex/plugin/scripts/migrate-codex-config/multi-agent-v2-guard.mjs +221 -20
  112. package/packages/omo-codex/plugin/scripts/migrate-codex-config/subagent-limit-guard.mjs +71 -3
  113. package/packages/omo-codex/plugin/scripts/migrate-codex-config.mjs +35 -7
  114. package/packages/omo-codex/plugin/scripts/migrate-omo-sot.mjs +2 -2
  115. package/packages/omo-codex/plugin/scripts/sync-hook-status-messages.mjs +2 -2
  116. package/packages/omo-codex/plugin/scripts/sync-skills.mjs +7 -5
  117. package/packages/omo-codex/plugin/scripts/sync-version.mjs +4 -2
  118. package/packages/omo-codex/plugin/skills/init-deep/SKILL.md +2 -2
  119. package/packages/omo-codex/plugin/skills/refactor/SKILL.md +2 -2
  120. package/packages/omo-codex/plugin/skills/remove-ai-slops/SKILL.md +2 -2
  121. package/packages/omo-codex/plugin/skills/review-work/SKILL.md +2 -2
  122. package/packages/omo-codex/plugin/skills/start-work/SKILL.md +2 -2
  123. package/packages/omo-codex/plugin/skills/teammode/SKILL.md +136 -76
  124. package/packages/omo-codex/plugin/skills/teammode/scripts/team-guide.mjs +94 -23
  125. package/packages/omo-codex/plugin/skills/teammode/scripts/team-state.mjs +94 -14
  126. package/packages/omo-codex/plugin/skills/teammode/scripts/team-transport.mjs +55 -0
  127. package/packages/omo-codex/plugin/skills/teammode/scripts/team.mjs +72 -18
  128. package/packages/omo-codex/plugin/skills/ultrawork/SKILL.md +1 -0
  129. package/packages/omo-codex/plugin/skills/ulw-loop/SKILL.md +2 -0
  130. package/packages/omo-codex/plugin/skills/ulw-loop/references/full-workflow.md +1 -1
  131. package/packages/omo-codex/plugin/skills/ulw-plan/SKILL.md +2 -0
  132. package/packages/omo-codex/plugin/skills/ulw-plan/references/full-workflow.md +3 -1
  133. package/packages/omo-codex/plugin/skills/ulw-research/SKILL.md +3 -3
  134. package/packages/omo-codex/plugin/skills/visual-qa/SKILL.md +3 -3
  135. package/packages/omo-codex/plugin/test/aggregate-agents.test.mjs +47 -1
  136. package/packages/omo-codex/plugin/test/aggregate-plugin-fixture.mjs +1 -1
  137. package/packages/omo-codex/plugin/test/aggregate-skills.test.mjs +4 -2
  138. package/packages/omo-codex/plugin/test/auto-update-restart-notice.test.mjs +33 -0
  139. package/packages/omo-codex/plugin/test/auto-update.test.mjs +35 -1
  140. package/packages/omo-codex/plugin/test/migrate-codex-config.test.mjs +412 -19
  141. package/packages/omo-codex/plugin/test/multi-agent-v2-regression.test.mjs +71 -0
  142. package/packages/omo-codex/plugin/test/subagent-limit-migration.test.mjs +65 -0
  143. package/packages/omo-codex/plugin/test/sync-skills-orchestration.test.mjs +13 -4
  144. package/packages/omo-codex/plugin/test/teammode-transport.test.mjs +345 -0
  145. package/packages/omo-codex/scripts/install-dist/install-local.mjs +436 -284
  146. package/packages/omo-codex/scripts/install-generated-bundle.test.mjs +111 -1
  147. package/packages/shared-skills/skills/review-work/SKILL.md +4 -2
  148. package/packages/shared-skills/skills/start-work/SKILL.md +2 -0
  149. package/packages/shared-skills/skills/ulw-plan/references/full-workflow.md +1 -1
  150. package/packages/shared-skills/skills/ulw-research/SKILL.md +5 -3
  151. package/packages/shared-skills/skills/visual-qa/SKILL.md +1 -1
@@ -440,7 +440,7 @@ async function readOptionalFile(path) {
440
440
  // components/bootstrap/src/worker.ts
441
441
  import { appendFile as appendFile2, mkdir as mkdir8, readFile as readFile13 } from "node:fs/promises";
442
442
  import { homedir as homedir4 } from "node:os";
443
- import { dirname as dirname8, join as join20, resolve as resolve7 } from "node:path";
443
+ import { dirname as dirname9, join as join22, resolve as resolve7 } from "node:path";
444
444
  import { fileURLToPath as fileURLToPath2 } from "node:url";
445
445
 
446
446
  // components/bootstrap/src/provision.ts
@@ -864,211 +864,129 @@ async function defaultVersionProbe2(binaryPath) {
864
864
  }
865
865
 
866
866
  // components/bootstrap/src/setup.ts
867
- import { copyFile as copyFile2, mkdir as mkdir7, readdir as readdir4, rm as rm10, stat as stat5 } from "node:fs/promises";
868
- import { join as join19 } from "node:path";
867
+ import { copyFile as copyFile2, mkdir as mkdir7, readdir as readdir5, rm as rm10, stat as stat5 } from "node:fs/promises";
868
+ import { join as join21 } from "node:path";
869
869
 
870
870
  // ../src/install/link-cached-plugin-agents.ts
871
- import { copyFile, lstat as lstat2, mkdir as mkdir4, readFile as readFile5, readdir, rm as rm6, writeFile as writeFile3 } from "node:fs/promises";
872
- import { basename as basename3, join as join9 } from "node:path";
871
+ import { copyFile, lstat as lstat3, mkdir as mkdir4, readdir as readdir2, rm as rm6, writeFile as writeFile4 } from "node:fs/promises";
872
+ import { basename as basename3, join as join10 } from "node:path";
873
873
 
874
- // ../src/install/retired-managed-agent-purge.ts
875
- import { lstat, readFile as readFile4, rm as rm5 } from "node:fs/promises";
874
+ // ../src/install/preserved-agent-settings.ts
875
+ import { lstat, readFile as readFile4, readdir, writeFile as writeFile3 } from "node:fs/promises";
876
876
  import { join as join8 } from "node:path";
877
- var RETIRED_MANAGED_AGENT_FILES = [
878
- {
879
- fileName: "codex-ultrawork-reviewer.toml",
880
- requiredMarkers: [
881
- 'name = "codex-ultrawork-reviewer"',
882
- 'description = "Strict ultrawork verification reviewer.',
883
- 'developer_instructions = """You are the ultrawork verification reviewer.'
884
- ]
885
- }
886
- ];
887
- async function purgeRetiredManagedAgentFiles(input) {
888
- const agentsDir = join8(input.codexHome, "agents");
889
- if (!await exists(agentsDir))
890
- return;
891
- for (const retiredAgent of RETIRED_MANAGED_AGENT_FILES) {
892
- const agentPath = join8(agentsDir, retiredAgent.fileName);
893
- if (!await exists(agentPath))
894
- continue;
895
- const agentStat = await lstat(agentPath);
896
- if (agentStat.isDirectory() && !agentStat.isSymbolicLink())
897
- continue;
898
- const content = await readTextIfExists(agentPath);
899
- if (content === null || !hasRequiredMarkers(content, retiredAgent.requiredMarkers))
900
- continue;
901
- await rm5(agentPath, { force: true });
902
- }
903
- }
904
- function hasRequiredMarkers(content, markers) {
905
- return markers.every((marker) => content.includes(marker));
906
- }
907
- async function readTextIfExists(path) {
908
- try {
909
- return await readFile4(path, "utf8");
910
- } catch (error) {
911
- if (nodeErrorCode(error) === "ENOENT")
912
- return null;
913
- throw error;
914
- }
915
- }
916
- async function exists(path) {
917
- try {
918
- await lstat(path);
919
- return true;
920
- } catch (error) {
921
- if (nodeErrorCode(error) !== "ENOENT")
922
- throw error;
923
- return false;
877
+
878
+ // ../src/install/managed-agent-reasoning-defaults.ts
879
+ var MANAGED_REASONING_DEFAULT_UPGRADES = new Map([
880
+ [
881
+ "explorer",
882
+ {
883
+ previous: { model: "gpt-5.4-mini", effort: "low" },
884
+ current: { model: "gpt-5.6-terra", effort: "medium" }
885
+ }
886
+ ],
887
+ [
888
+ "librarian",
889
+ {
890
+ previous: { model: "gpt-5.4-mini", effort: "low" },
891
+ current: { model: "gpt-5.6-terra", effort: "medium" }
892
+ }
893
+ ],
894
+ [
895
+ "momus",
896
+ {
897
+ previous: { model: "gpt-5.5", effort: "xhigh" },
898
+ current: { model: "gpt-5.6-sol", effort: "ultra" }
899
+ }
900
+ ]
901
+ ]);
902
+ function resolveManagedAgentReasoning(input) {
903
+ const upgrade = MANAGED_REASONING_DEFAULT_UPGRADES.get(input.agentName);
904
+ if (upgrade !== undefined && input.preserved.model === upgrade.previous.model && input.preserved.effort === upgrade.previous.effort && input.bundledModel === upgrade.current.model && input.bundledEffort === upgrade.current.effort) {
905
+ return upgrade.current.effort;
924
906
  }
925
- }
926
- function nodeErrorCode(error) {
927
- if (!(error instanceof Error) || !("code" in error))
928
- return null;
929
- return typeof error.code === "string" ? error.code : null;
907
+ return input.preserved.effort;
930
908
  }
931
909
 
932
- // ../src/install/link-cached-plugin-agents.ts
933
- var MANIFEST_FILE = ".installed-agents.json";
910
+ // ../src/install/preserved-agent-settings.ts
934
911
  async function capturePreservedAgentReasoning(input) {
935
- const agentsDir = join9(input.codexHome, "agents");
936
- if (!await exists2(agentsDir))
912
+ const agentsDir = join8(input.codexHome, "agents");
913
+ if (!await exists(agentsDir))
937
914
  return new Map;
938
915
  const preserved = new Map;
939
916
  const agentEntries = await readdir(agentsDir, { withFileTypes: true });
940
917
  for (const entry of agentEntries) {
941
918
  if (!entry.name.endsWith(".toml"))
942
919
  continue;
943
- const content = await readTextIfExists2(join9(agentsDir, entry.name));
920
+ const content = await readTextIfExists(join8(agentsDir, entry.name));
944
921
  if (content === null)
945
922
  continue;
946
923
  const effort = extractReasoningEffort(content);
947
- if (effort !== null)
948
- preserved.set(agentNameFromToml(entry.name), effort);
924
+ if (effort !== null) {
925
+ preserved.set(agentNameFromToml(entry.name), {
926
+ model: extractModel(content),
927
+ effort
928
+ });
929
+ }
949
930
  }
950
931
  return preserved;
951
932
  }
952
933
  async function capturePreservedAgentServiceTier(input) {
953
- const agentsDir = join9(input.codexHome, "agents");
954
- if (!await exists2(agentsDir))
934
+ const agentsDir = join8(input.codexHome, "agents");
935
+ if (!await exists(agentsDir))
955
936
  return new Map;
956
937
  const preserved = new Map;
957
938
  const agentEntries = await readdir(agentsDir, { withFileTypes: true });
958
939
  for (const entry of agentEntries) {
959
940
  if (!entry.name.endsWith(".toml"))
960
941
  continue;
961
- const content = await readTextIfExists2(join9(agentsDir, entry.name));
942
+ const content = await readTextIfExists(join8(agentsDir, entry.name));
962
943
  if (content === null)
963
944
  continue;
964
945
  preserved.set(agentNameFromToml(entry.name), extractServiceTier(content));
965
946
  }
966
947
  return preserved;
967
948
  }
968
- async function linkCachedPluginAgents(input) {
969
- const bundledAgents = await discoverBundledAgents(input.pluginRoot);
970
- await purgeRetiredManagedAgentFiles({ codexHome: input.codexHome });
971
- if (bundledAgents.length === 0) {
972
- await writeManifest(input.pluginRoot, []);
973
- return [];
974
- }
975
- const agentsDir = join9(input.codexHome, "agents");
976
- await mkdir4(agentsDir, { recursive: true });
977
- const linked = [];
978
- for (const agentPath of bundledAgents) {
979
- const agentFileName = basename3(agentPath);
980
- const agentName = agentNameFromToml(agentFileName);
981
- const linkPath = join9(agentsDir, agentFileName);
982
- await replaceWithCopy(linkPath, agentPath);
983
- await restorePreservedReasoning({
984
- agentName,
985
- linkPath,
986
- target: agentPath,
987
- value: input.preservedReasoning?.get(agentName)
988
- });
989
- await restorePreservedServiceTier({
990
- linkPath,
991
- preserved: input.preservedServiceTier?.has(agentName) ?? false,
992
- value: input.preservedServiceTier?.get(agentName) ?? null
993
- });
994
- linked.push({ name: agentFileName, path: linkPath, target: agentPath });
995
- }
996
- await writeManifest(input.pluginRoot, linked.map((entry) => entry.path));
997
- return linked;
998
- }
999
- async function restorePreservedServiceTier(input) {
1000
- if (!input.preserved)
949
+ async function restorePreservedReasoning(input) {
950
+ if (input.value === undefined)
1001
951
  return;
1002
- const content = await readFile5(input.linkPath, "utf8");
1003
- if (extractServiceTier(content) === input.value)
952
+ const content = await readFile4(input.target, "utf8");
953
+ const bundledEffort = extractReasoningEffort(content);
954
+ const effort = resolveManagedAgentReasoning({
955
+ agentName: input.agentName,
956
+ bundledModel: extractModel(content),
957
+ bundledEffort,
958
+ preserved: input.value
959
+ });
960
+ if (bundledEffort === effort)
1004
961
  return;
1005
- const replacement = replaceServiceTier(content, input.value);
962
+ const replacement = replaceTopLevelStringSetting(content, "model_reasoning_effort", effort, { insertIfMissing: false });
1006
963
  if (!replacement.replaced)
1007
964
  return;
1008
965
  await writeFile3(input.linkPath, replacement.content);
1009
966
  }
1010
- async function discoverBundledAgents(pluginRoot) {
1011
- const componentsRoot = join9(pluginRoot, "components");
1012
- if (!await exists2(componentsRoot))
1013
- return [];
1014
- const componentEntries = await readdir(componentsRoot, { withFileTypes: true });
1015
- const agents = [];
1016
- for (const entry of componentEntries) {
1017
- if (!entry.isDirectory())
1018
- continue;
1019
- const agentsRoot = join9(componentsRoot, entry.name, "agents");
1020
- if (!await exists2(agentsRoot))
1021
- continue;
1022
- const agentEntries = await readdir(agentsRoot, { withFileTypes: true });
1023
- for (const file of agentEntries) {
1024
- if (!file.isFile() || !file.name.endsWith(".toml"))
1025
- continue;
1026
- agents.push(join9(agentsRoot, file.name));
1027
- }
1028
- }
1029
- agents.sort();
1030
- return agents;
1031
- }
1032
- async function replaceWithCopy(linkPath, target) {
1033
- await prepareReplacement(linkPath);
1034
- await copyFile(target, linkPath);
1035
- }
1036
- async function prepareReplacement(linkPath) {
1037
- if (!await exists2(linkPath))
1038
- return;
1039
- const entryStat = await lstat2(linkPath);
1040
- if (entryStat.isDirectory() && !entryStat.isSymbolicLink()) {
1041
- throw new Error(`${linkPath} already exists and is a directory; refusing to replace`);
1042
- }
1043
- await rm6(linkPath, { force: true });
1044
- }
1045
- async function writeManifest(pluginRoot, agentPaths) {
1046
- const manifestPath = join9(pluginRoot, MANIFEST_FILE);
1047
- const payload = { agents: [...agentPaths].sort() };
1048
- await writeFile3(manifestPath, `${JSON.stringify(payload, null, "\t")}
1049
- `);
1050
- }
1051
- async function restorePreservedReasoning(input) {
1052
- if (input.value === undefined)
967
+ async function restorePreservedServiceTier(input) {
968
+ if (!input.preserved)
1053
969
  return;
1054
- const content = await readFile5(input.target, "utf8");
1055
- const bundledEffort = extractReasoningEffort(content);
1056
- if (bundledEffort === input.value)
970
+ const content = await readFile4(input.linkPath, "utf8");
971
+ if (extractServiceTier(content) === input.value)
1057
972
  return;
1058
- const replacement = replaceReasoningEffort(content, input.value);
973
+ const replacement = replaceTopLevelStringSetting(content, "service_tier", input.value, { insertIfMissing: true });
1059
974
  if (!replacement.replaced)
1060
975
  return;
1061
976
  await writeFile3(input.linkPath, replacement.content);
1062
977
  }
1063
- async function readTextIfExists2(path) {
978
+ async function readTextIfExists(path) {
1064
979
  try {
1065
- return await readFile5(path, "utf8");
980
+ return await readFile4(path, "utf8");
1066
981
  } catch (error) {
1067
- if (nodeErrorCode2(error) === "ENOENT")
982
+ if (nodeErrorCode(error) === "ENOENT")
1068
983
  return null;
1069
984
  throw error;
1070
985
  }
1071
986
  }
987
+ function extractModel(content) {
988
+ return extractTopLevelStringSetting(content, "model");
989
+ }
1072
990
  function extractReasoningEffort(content) {
1073
991
  return extractTopLevelStringSetting(content, "model_reasoning_effort");
1074
992
  }
@@ -1088,12 +1006,6 @@ function extractTopLevelStringSetting(content, key) {
1088
1006
  }
1089
1007
  return null;
1090
1008
  }
1091
- function replaceReasoningEffort(content, value) {
1092
- return replaceTopLevelStringSetting(content, "model_reasoning_effort", value, { insertIfMissing: false });
1093
- }
1094
- function replaceServiceTier(content, value) {
1095
- return replaceTopLevelStringSetting(content, "service_tier", value, { insertIfMissing: true });
1096
- }
1097
1009
  function replaceTopLevelStringSetting(content, key, value, options) {
1098
1010
  const lines = content.split(/\n/);
1099
1011
  for (let index = 0;index < lines.length; index += 1) {
@@ -1153,6 +1065,64 @@ function parseJsonString(value) {
1153
1065
  return null;
1154
1066
  }
1155
1067
  }
1068
+ async function exists(path) {
1069
+ try {
1070
+ await lstat(path);
1071
+ return true;
1072
+ } catch (error) {
1073
+ if (nodeErrorCode(error) !== "ENOENT")
1074
+ throw error;
1075
+ return false;
1076
+ }
1077
+ }
1078
+ function nodeErrorCode(error) {
1079
+ if (!(error instanceof Error) || !("code" in error))
1080
+ return null;
1081
+ return typeof error.code === "string" ? error.code : null;
1082
+ }
1083
+
1084
+ // ../src/install/retired-managed-agent-purge.ts
1085
+ import { lstat as lstat2, readFile as readFile5, rm as rm5 } from "node:fs/promises";
1086
+ import { join as join9 } from "node:path";
1087
+ var RETIRED_MANAGED_AGENT_FILES = [
1088
+ {
1089
+ fileName: "codex-ultrawork-reviewer.toml",
1090
+ requiredMarkers: [
1091
+ 'name = "codex-ultrawork-reviewer"',
1092
+ 'description = "Strict ultrawork verification reviewer.',
1093
+ 'developer_instructions = """You are the ultrawork verification reviewer.'
1094
+ ]
1095
+ }
1096
+ ];
1097
+ async function purgeRetiredManagedAgentFiles(input) {
1098
+ const agentsDir = join9(input.codexHome, "agents");
1099
+ if (!await exists2(agentsDir))
1100
+ return;
1101
+ for (const retiredAgent of RETIRED_MANAGED_AGENT_FILES) {
1102
+ const agentPath = join9(agentsDir, retiredAgent.fileName);
1103
+ if (!await exists2(agentPath))
1104
+ continue;
1105
+ const agentStat = await lstat2(agentPath);
1106
+ if (agentStat.isDirectory() && !agentStat.isSymbolicLink())
1107
+ continue;
1108
+ const content = await readTextIfExists2(agentPath);
1109
+ if (content === null || !hasRequiredMarkers(content, retiredAgent.requiredMarkers))
1110
+ continue;
1111
+ await rm5(agentPath, { force: true });
1112
+ }
1113
+ }
1114
+ function hasRequiredMarkers(content, markers) {
1115
+ return markers.every((marker) => content.includes(marker));
1116
+ }
1117
+ async function readTextIfExists2(path) {
1118
+ try {
1119
+ return await readFile5(path, "utf8");
1120
+ } catch (error) {
1121
+ if (nodeErrorCode2(error) === "ENOENT")
1122
+ return null;
1123
+ throw error;
1124
+ }
1125
+ }
1156
1126
  async function exists2(path) {
1157
1127
  try {
1158
1128
  await lstat2(path);
@@ -1169,9 +1139,102 @@ function nodeErrorCode2(error) {
1169
1139
  return typeof error.code === "string" ? error.code : null;
1170
1140
  }
1171
1141
 
1142
+ // ../src/install/link-cached-plugin-agents.ts
1143
+ var MANIFEST_FILE = ".installed-agents.json";
1144
+ async function linkCachedPluginAgents(input) {
1145
+ const bundledAgents = await discoverBundledAgents(input.pluginRoot);
1146
+ await purgeRetiredManagedAgentFiles({ codexHome: input.codexHome });
1147
+ if (bundledAgents.length === 0) {
1148
+ await writeManifest(input.pluginRoot, []);
1149
+ return [];
1150
+ }
1151
+ const agentsDir = join10(input.codexHome, "agents");
1152
+ await mkdir4(agentsDir, { recursive: true });
1153
+ const linked = [];
1154
+ for (const agentPath of bundledAgents) {
1155
+ const agentFileName = basename3(agentPath);
1156
+ const agentName = agentNameFromToml2(agentFileName);
1157
+ const linkPath = join10(agentsDir, agentFileName);
1158
+ await replaceWithCopy(linkPath, agentPath);
1159
+ await restorePreservedReasoning({
1160
+ agentName,
1161
+ linkPath,
1162
+ target: agentPath,
1163
+ value: input.preservedReasoning?.get(agentName)
1164
+ });
1165
+ await restorePreservedServiceTier({
1166
+ linkPath,
1167
+ preserved: input.preservedServiceTier?.has(agentName) ?? false,
1168
+ value: input.preservedServiceTier?.get(agentName) ?? null
1169
+ });
1170
+ linked.push({ name: agentFileName, path: linkPath, target: agentPath });
1171
+ }
1172
+ await writeManifest(input.pluginRoot, linked.map((entry) => entry.path));
1173
+ return linked;
1174
+ }
1175
+ async function discoverBundledAgents(pluginRoot) {
1176
+ const componentsRoot = join10(pluginRoot, "components");
1177
+ if (!await exists3(componentsRoot))
1178
+ return [];
1179
+ const componentEntries = await readdir2(componentsRoot, { withFileTypes: true });
1180
+ const agents = [];
1181
+ for (const entry of componentEntries) {
1182
+ if (!entry.isDirectory())
1183
+ continue;
1184
+ const agentsRoot = join10(componentsRoot, entry.name, "agents");
1185
+ if (!await exists3(agentsRoot))
1186
+ continue;
1187
+ const agentEntries = await readdir2(agentsRoot, { withFileTypes: true });
1188
+ for (const file of agentEntries) {
1189
+ if (!file.isFile() || !file.name.endsWith(".toml"))
1190
+ continue;
1191
+ agents.push(join10(agentsRoot, file.name));
1192
+ }
1193
+ }
1194
+ agents.sort();
1195
+ return agents;
1196
+ }
1197
+ async function replaceWithCopy(linkPath, target) {
1198
+ await prepareReplacement(linkPath);
1199
+ await copyFile(target, linkPath);
1200
+ }
1201
+ async function prepareReplacement(linkPath) {
1202
+ if (!await exists3(linkPath))
1203
+ return;
1204
+ const entryStat = await lstat3(linkPath);
1205
+ if (entryStat.isDirectory() && !entryStat.isSymbolicLink()) {
1206
+ throw new Error(`${linkPath} already exists and is a directory; refusing to replace`);
1207
+ }
1208
+ await rm6(linkPath, { force: true });
1209
+ }
1210
+ async function writeManifest(pluginRoot, agentPaths) {
1211
+ const manifestPath = join10(pluginRoot, MANIFEST_FILE);
1212
+ const payload = { agents: [...agentPaths].sort() };
1213
+ await writeFile4(manifestPath, `${JSON.stringify(payload, null, "\t")}
1214
+ `);
1215
+ }
1216
+ function agentNameFromToml2(fileName) {
1217
+ return fileName.endsWith(".toml") ? fileName.slice(0, -".toml".length) : fileName;
1218
+ }
1219
+ async function exists3(path) {
1220
+ try {
1221
+ await lstat3(path);
1222
+ return true;
1223
+ } catch (error) {
1224
+ if (nodeErrorCode3(error) !== "ENOENT")
1225
+ throw error;
1226
+ return false;
1227
+ }
1228
+ }
1229
+ function nodeErrorCode3(error) {
1230
+ if (!(error instanceof Error) || !("code" in error))
1231
+ return null;
1232
+ return typeof error.code === "string" ? error.code : null;
1233
+ }
1234
+
1172
1235
  // ../src/install/codex-cache-bins.ts
1173
- import { chmod as chmod2, lstat as lstat6, mkdir as mkdir5, readFile as readFile8, readdir as readdir3, readlink as readlink3, rm as rm9, stat as stat4, symlink, writeFile as writeFile4 } from "node:fs/promises";
1174
- import { basename as basename4, isAbsolute as isAbsolute3, join as join13, relative as relative2, resolve as resolve4, sep } from "node:path";
1236
+ import { chmod as chmod2, lstat as lstat7, mkdir as mkdir5, readFile as readFile8, readdir as readdir4, readlink as readlink3, rm as rm9, stat as stat4, symlink, writeFile as writeFile5 } from "node:fs/promises";
1237
+ import { basename as basename4, isAbsolute as isAbsolute3, join as join14, relative as relative2, resolve as resolve4, sep } from "node:path";
1175
1238
 
1176
1239
  // ../src/install/codex-cache-command-shim.ts
1177
1240
  var COMMAND_SHIM_MARKER = ":: generated by oh-my-openagent Codex installer";
@@ -1215,14 +1278,14 @@ function windowsCommandShim(targetPath) {
1215
1278
  }
1216
1279
 
1217
1280
  // ../src/install/codex-cache-dangling-bins.ts
1218
- import { lstat as lstat4, readFile as readFile6, readdir as readdir2, readlink, rm as rm7, stat as stat3 } from "node:fs/promises";
1219
- import { dirname as dirname5, isAbsolute as isAbsolute2, join as join10, resolve as resolve3 } from "node:path";
1281
+ import { lstat as lstat5, readFile as readFile6, readdir as readdir3, readlink, rm as rm7, stat as stat3 } from "node:fs/promises";
1282
+ import { dirname as dirname5, isAbsolute as isAbsolute2, join as join11, resolve as resolve3 } from "node:path";
1220
1283
 
1221
1284
  // ../src/install/codex-cache-fs.ts
1222
- import { lstat as lstat3 } from "node:fs/promises";
1285
+ import { lstat as lstat4 } from "node:fs/promises";
1223
1286
  async function fileExistsStrict(path) {
1224
1287
  try {
1225
- await lstat3(path);
1288
+ await lstat4(path);
1226
1289
  return true;
1227
1290
  } catch (error) {
1228
1291
  if (isNodeErrorWithCode(error) && error.code === "ENOENT")
@@ -1239,12 +1302,12 @@ function isNodeErrorWithCode(error) {
1239
1302
 
1240
1303
  // ../src/install/codex-cache-dangling-bins.ts
1241
1304
  async function removeDanglingManagedComponentBins(binDir, platform, managedBinNames) {
1242
- const entries = await readdir2(binDir, { withFileTypes: true });
1305
+ const entries = await readdir3(binDir, { withFileTypes: true });
1243
1306
  for (const entry of entries) {
1244
1307
  const binName = managedBinNameForEntry(entry.name, platform);
1245
1308
  if (binName === null || !managedBinNames.has(binName))
1246
1309
  continue;
1247
- const linkPath = join10(binDir, entry.name);
1310
+ const linkPath = join11(binDir, entry.name);
1248
1311
  if (platform === "win32") {
1249
1312
  await removeDanglingGeneratedCommandShim(linkPath);
1250
1313
  continue;
@@ -1259,7 +1322,7 @@ function managedBinNameForEntry(name, platform) {
1259
1322
  }
1260
1323
  async function removeDanglingManagedSymlink(linkPath) {
1261
1324
  try {
1262
- const linkStat = await lstat4(linkPath);
1325
+ const linkStat = await lstat5(linkPath);
1263
1326
  if (!linkStat.isSymbolicLink())
1264
1327
  return;
1265
1328
  const linkTarget = await readlink(linkPath);
@@ -1274,7 +1337,7 @@ async function removeDanglingManagedSymlink(linkPath) {
1274
1337
  }
1275
1338
  async function removeDanglingGeneratedCommandShim(linkPath) {
1276
1339
  try {
1277
- const linkStat = await lstat4(linkPath);
1340
+ const linkStat = await lstat5(linkPath);
1278
1341
  if (!linkStat.isFile())
1279
1342
  return;
1280
1343
  const content = await readFile6(linkPath, "utf8");
@@ -1325,8 +1388,8 @@ function hasOmoCodexPluginPrefix(parts, endExclusive) {
1325
1388
  }
1326
1389
 
1327
1390
  // ../src/install/codex-cache-legacy-bins.ts
1328
- import { lstat as lstat5, readFile as readFile7, readlink as readlink2, rm as rm8 } from "node:fs/promises";
1329
- import { join as join11 } from "node:path";
1391
+ import { lstat as lstat6, readFile as readFile7, readlink as readlink2, rm as rm8 } from "node:fs/promises";
1392
+ import { join as join12 } from "node:path";
1330
1393
  var LEGACY_CODEX_COMPONENT_BINS = [
1331
1394
  { name: "omo", component: "ulw-loop" },
1332
1395
  { name: "codex-comment-checker", component: "comment-checker" },
@@ -1338,13 +1401,13 @@ var LEGACY_CODEX_COMPONENT_BINS = [
1338
1401
  ];
1339
1402
  async function removeLegacyCodexComponentBins(binDir, platform) {
1340
1403
  for (const entry of LEGACY_CODEX_COMPONENT_BINS) {
1341
- const linkPath = join11(binDir, platform === "win32" ? `${entry.name}.cmd` : entry.name);
1404
+ const linkPath = join12(binDir, platform === "win32" ? `${entry.name}.cmd` : entry.name);
1342
1405
  await removeLegacyCodexComponentBin(linkPath, entry.component, platform);
1343
1406
  }
1344
1407
  }
1345
1408
  async function removeLegacyCodexComponentBin(linkPath, component, platform) {
1346
1409
  try {
1347
- const stat4 = await lstat5(linkPath);
1410
+ const stat4 = await lstat6(linkPath);
1348
1411
  if (platform !== "win32") {
1349
1412
  if (!stat4.isSymbolicLink())
1350
1413
  return;
@@ -1389,10 +1452,10 @@ function isNodeErrorWithCode2(error) {
1389
1452
  }
1390
1453
 
1391
1454
  // ../src/install/codex-cache-runtime-wrapper.ts
1392
- import { join as join12 } from "node:path";
1455
+ import { join as join13 } from "node:path";
1393
1456
  var RUNTIME_WRAPPER_MARKER = "OMO_GENERATED_RUNTIME_WRAPPER";
1394
1457
  function posixRuntimeWrapper(cliPath, codexHome, binDir, nodeCliPath) {
1395
- const ulwLoopBin = toPosixPath(join12(binDir, "omo-ulw-loop"));
1458
+ const ulwLoopBin = toPosixPath(join13(binDir, "omo-ulw-loop"));
1396
1459
  const nodeCli = escapePosixDoubleQuoted(toPosixPath(nodeCliPath));
1397
1460
  const escapedCliPath = escapePosixDoubleQuoted(toPosixPath(cliPath));
1398
1461
  const escapedCodexHome = escapePosixDoubleQuoted(toPosixPath(codexHome));
@@ -1437,7 +1500,7 @@ function posixRuntimeWrapper(cliPath, codexHome, binDir, nodeCliPath) {
1437
1500
  `);
1438
1501
  }
1439
1502
  function windowsRuntimeWrapper(cliPath, codexHome, binDir, nodeCliPath) {
1440
- const ulwLoopBin = join12(binDir, "omo-ulw-loop.cmd");
1503
+ const ulwLoopBin = join13(binDir, "omo-ulw-loop.cmd");
1441
1504
  return [
1442
1505
  "@echo off",
1443
1506
  `rem ${RUNTIME_WRAPPER_MARKER}`,
@@ -1494,29 +1557,29 @@ async function linkCachedPluginBins(input) {
1494
1557
  return linked;
1495
1558
  }
1496
1559
  async function linkRootRuntimeBin(input) {
1497
- const cliPath = join13(input.repoRoot, "dist", "cli", "index.js");
1560
+ const cliPath = join14(input.repoRoot, "dist", "cli", "index.js");
1498
1561
  if (!await isFile2(cliPath))
1499
1562
  return null;
1500
- const nodeCliPath = join13(input.repoRoot, "dist", "cli-node", "index.js");
1563
+ const nodeCliPath = join14(input.repoRoot, "dist", "cli-node", "index.js");
1501
1564
  const platform = input.platform ?? process.platform;
1502
1565
  await mkdir5(input.binDir, { recursive: true });
1503
1566
  if (platform === "win32") {
1504
- const linkPath2 = join13(input.binDir, "omo.cmd");
1567
+ const linkPath2 = join14(input.binDir, "omo.cmd");
1505
1568
  await replaceRuntimeWrapper(linkPath2, windowsRuntimeWrapper(cliPath, input.codexHome, input.binDir, nodeCliPath));
1506
1569
  return { name: "omo", path: linkPath2, target: cliPath };
1507
1570
  }
1508
- const linkPath = join13(input.binDir, "omo");
1571
+ const linkPath = join14(input.binDir, "omo");
1509
1572
  await replaceRuntimeWrapper(linkPath, posixRuntimeWrapper(cliPath, input.codexHome, input.binDir, nodeCliPath));
1510
1573
  await chmod2(linkPath, 493);
1511
1574
  return { name: "omo", path: linkPath, target: cliPath };
1512
1575
  }
1513
1576
  async function linkCachedPluginBin(binDir, link, platform) {
1514
1577
  if (platform === "win32") {
1515
- const linkPath2 = join13(binDir, `${link.name}.cmd`);
1578
+ const linkPath2 = join14(binDir, `${link.name}.cmd`);
1516
1579
  await replaceCommandShim(linkPath2, link.target);
1517
1580
  return linkPath2;
1518
1581
  }
1519
- const linkPath = join13(binDir, link.name);
1582
+ const linkPath = join14(binDir, link.name);
1520
1583
  await replaceSymlink(linkPath, link.target);
1521
1584
  return linkPath;
1522
1585
  }
@@ -1535,16 +1598,16 @@ async function discoverPackageBins(root) {
1535
1598
  return links;
1536
1599
  }
1537
1600
  async function collectPackageBins(directory, root, links) {
1538
- const entries = await readdir3(directory, { withFileTypes: true });
1601
+ const entries = await readdir4(directory, { withFileTypes: true });
1539
1602
  if (entries.some((entry) => entry.isFile() && entry.name === "package.json")) {
1540
- await appendPackageBinLinks(join13(directory, "package.json"), directory, root, links);
1603
+ await appendPackageBinLinks(join14(directory, "package.json"), directory, root, links);
1541
1604
  }
1542
1605
  for (const entry of entries) {
1543
1606
  if (!entry.isDirectory())
1544
1607
  continue;
1545
1608
  if (entry.name === "node_modules" || entry.name === ".git" || entry.name === "dist")
1546
1609
  continue;
1547
- const childPath = join13(directory, entry.name);
1610
+ const childPath = join14(directory, entry.name);
1548
1611
  if (!childPath.startsWith(root))
1549
1612
  continue;
1550
1613
  await collectPackageBins(childPath, root, links);
@@ -1603,17 +1666,17 @@ async function replaceSymlink(linkPath, targetPath) {
1603
1666
  async function replaceCommandShim(linkPath, targetPath) {
1604
1667
  if (await existingNonShim(linkPath))
1605
1668
  throw new Error(`${linkPath} already exists and is not a command shim`);
1606
- await writeFile4(linkPath, windowsCommandShim(targetPath));
1669
+ await writeFile5(linkPath, windowsCommandShim(targetPath));
1607
1670
  }
1608
1671
  async function replaceRuntimeWrapper(linkPath, content) {
1609
1672
  if (await existingNonRuntimeWrapper(linkPath))
1610
1673
  throw new Error(`${linkPath} already exists and is not a generated OMO runtime wrapper`);
1611
1674
  await rm9(linkPath, { force: true });
1612
- await writeFile4(linkPath, content);
1675
+ await writeFile5(linkPath, content);
1613
1676
  }
1614
1677
  async function existingNonRuntimeWrapper(path) {
1615
1678
  try {
1616
- const stat5 = await lstat6(path);
1679
+ const stat5 = await lstat7(path);
1617
1680
  if (stat5.isSymbolicLink())
1618
1681
  return false;
1619
1682
  if (!stat5.isFile())
@@ -1628,7 +1691,7 @@ async function existingNonRuntimeWrapper(path) {
1628
1691
  }
1629
1692
  async function existingNonShim(path) {
1630
1693
  try {
1631
- const stat5 = await lstat6(path);
1694
+ const stat5 = await lstat7(path);
1632
1695
  if (!stat5.isFile())
1633
1696
  return true;
1634
1697
  const content = await readFile8(path, "utf8");
@@ -1643,7 +1706,7 @@ async function existingNonShim(path) {
1643
1706
  }
1644
1707
  async function existingNonSymlink(path) {
1645
1708
  try {
1646
- const stat5 = await lstat6(path);
1709
+ const stat5 = await lstat7(path);
1647
1710
  if (!stat5.isSymbolicLink())
1648
1711
  return true;
1649
1712
  await readlink3(path);
@@ -1657,7 +1720,7 @@ async function existingNonSymlink(path) {
1657
1720
 
1658
1721
  // ../src/install/codex-config-toml.ts
1659
1722
  import { mkdir as mkdir6, readFile as readFile10 } from "node:fs/promises";
1660
- import { dirname as dirname7 } from "node:path";
1723
+ import { dirname as dirname8 } from "node:path";
1661
1724
 
1662
1725
  // ../src/install/toml-section-editor.ts
1663
1726
  function findTomlSection(config, header) {
@@ -2015,14 +2078,14 @@ function tomlKeySegment(value) {
2015
2078
  }
2016
2079
 
2017
2080
  // ../src/install/codex-config-atomic-write.ts
2018
- import { lstat as lstat7, readlink as readlink4, realpath, rename as rename3, unlink, writeFile as writeFile5 } from "node:fs/promises";
2019
- import { basename as basename5, dirname as dirname6, isAbsolute as isAbsolute4, join as join14, resolve as resolve5 } from "node:path";
2081
+ import { lstat as lstat8, readlink as readlink4, realpath, rename as rename3, unlink, writeFile as writeFile6 } from "node:fs/promises";
2082
+ import { basename as basename5, dirname as dirname6, isAbsolute as isAbsolute4, join as join15, resolve as resolve5 } from "node:path";
2020
2083
  var RENAME_RETRY_DELAYS_MS = [10, 25, 50];
2021
2084
  var RETRIABLE_RENAME_CODES = new Set(["EPERM", "EBUSY"]);
2022
2085
  async function writeFileAtomic(targetPath, data) {
2023
2086
  const writeTarget = await resolveSymlinkTarget(targetPath);
2024
- const temporaryPath = join14(dirname6(writeTarget), `.tmp-${basename5(writeTarget)}-${process.pid}-${Date.now()}`);
2025
- await writeFile5(temporaryPath, data);
2087
+ const temporaryPath = join15(dirname6(writeTarget), `.tmp-${basename5(writeTarget)}-${process.pid}-${Date.now()}`);
2088
+ await writeFile6(temporaryPath, data);
2026
2089
  try {
2027
2090
  await renameWithRetry(temporaryPath, writeTarget);
2028
2091
  } catch (error) {
@@ -2036,7 +2099,7 @@ async function writeFileAtomic(targetPath, data) {
2036
2099
  }
2037
2100
  async function resolveSymlinkTarget(targetPath) {
2038
2101
  try {
2039
- const linkStats = await lstat7(targetPath);
2102
+ const linkStats = await lstat8(targetPath);
2040
2103
  if (!linkStats.isSymbolicLink())
2041
2104
  return targetPath;
2042
2105
  } catch (error) {
@@ -2392,7 +2455,7 @@ function isRootSetting(line, key) {
2392
2455
 
2393
2456
  // ../src/install/codex-model-catalog.ts
2394
2457
  import { readFile as readFile9 } from "node:fs/promises";
2395
- import { join as join15 } from "node:path";
2458
+ import { join as join16 } from "node:path";
2396
2459
  var FALLBACK_CODEX_MODEL_CATALOG = {
2397
2460
  current: {
2398
2461
  model: "gpt-5.5",
@@ -2411,7 +2474,7 @@ var FALLBACK_CODEX_MODEL_CATALOG = {
2411
2474
  ]
2412
2475
  };
2413
2476
  async function readCodexModelCatalog(codexPackageRoot) {
2414
- const catalogPath = join15(codexPackageRoot, "plugin", "model-catalog.json");
2477
+ const catalogPath = join16(codexPackageRoot, "plugin", "model-catalog.json");
2415
2478
  try {
2416
2479
  const parsed = JSON.parse(await readFile9(catalogPath, "utf8"));
2417
2480
  return parseCodexModelCatalog(parsed) ?? FALLBACK_CODEX_MODEL_CATALOG;
@@ -2494,30 +2557,87 @@ function isRootSetting2(line, key) {
2494
2557
  }
2495
2558
 
2496
2559
  // ../src/install/codex-multi-agent-v2-config.ts
2560
+ import { readFileSync } from "node:fs";
2561
+ import { dirname as dirname7, isAbsolute as isAbsolute5, join as join17 } from "node:path";
2497
2562
  var CODEX_AGENTS_HEADER = "agents";
2498
2563
  var CODEX_MULTI_AGENT_V2_HEADER = "features.multi_agent_v2";
2499
2564
  var CODEX_SUBAGENT_THREAD_LIMIT = 1000;
2500
- function ensureCodexMultiAgentV2Config(config) {
2565
+ function ensureCodexMultiAgentV2Config(config, options = {}) {
2501
2566
  const featureFlag = removeFeatureFlagSetting(config, "multi_agent_v2");
2502
- const agentsConfig = ensureAgentsMaxThreads(featureFlag.config);
2503
- const section = findTomlSection(agentsConfig, CODEX_MULTI_AGENT_V2_HEADER);
2567
+ const v2Preferred = options.multiAgentVersion === "v2";
2568
+ const modelKnown = options.multiAgentVersion != null || readRootModel(featureFlag.config) !== null;
2569
+ const agentsConfig = v2Preferred ? removeAgentsMaxThreads(featureFlag.config) : modelKnown ? ensureAgentsMaxThreads(featureFlag.config) : raiseExistingAgentsMaxThreads(featureFlag.config);
2504
2570
  const maxThreadsValue = CODEX_SUBAGENT_THREAD_LIMIT.toString();
2571
+ const preserveDisable = featureFlag.value === false && !v2Preferred;
2572
+ const featureConfig = preserveDisable ? setMultiAgentV2Disable(agentsConfig) : v2Preferred ? removeMultiAgentV2Disable(agentsConfig) : agentsConfig;
2573
+ const section = findTomlSection(featureConfig, CODEX_MULTI_AGENT_V2_HEADER);
2505
2574
  if (!section) {
2506
- const enabledSetting = featureFlag.value === false ? `enabled = false
2575
+ const enabledSetting = preserveDisable ? `enabled = false
2507
2576
  ` : "";
2508
- return appendBlock(agentsConfig, `[${CODEX_MULTI_AGENT_V2_HEADER}]
2577
+ return appendBlock(featureConfig, `[${CODEX_MULTI_AGENT_V2_HEADER}]
2509
2578
  ${enabledSetting}max_concurrent_threads_per_session = ${maxThreadsValue}
2510
2579
  `);
2511
2580
  }
2512
- const withPreservedDisable = featureFlag.value === false ? replaceOrInsertSetting(agentsConfig, section, "enabled", "false") : agentsConfig;
2513
- const updatedSection = featureFlag.value === false ? findTomlSection(withPreservedDisable, CODEX_MULTI_AGENT_V2_HEADER) : section;
2514
- if (!updatedSection) {
2515
- return appendBlock(withPreservedDisable, `[${CODEX_MULTI_AGENT_V2_HEADER}]
2516
- enabled = false
2517
- max_concurrent_threads_per_session = ${maxThreadsValue}
2518
- `);
2581
+ return replaceOrInsertSetting(featureConfig, section, "max_concurrent_threads_per_session", maxThreadsValue);
2582
+ }
2583
+ function resolveCodexMultiAgentVersion(config, configPath) {
2584
+ const model = readRootModel(config);
2585
+ if (model === null)
2586
+ return null;
2587
+ const catalogPath = resolveCatalogPath(readRootModelCatalogPath(config), configPath);
2588
+ const catalogVersion = readCatalogMultiAgentVersion(model, catalogPath);
2589
+ if (catalogVersion !== null)
2590
+ return catalogVersion;
2591
+ return /^gpt-5\.6\b/i.test(model) ? "v2" : null;
2592
+ }
2593
+ function resolveCatalogPath(configuredPath, configPath) {
2594
+ if (configuredPath === null)
2595
+ return join17(dirname7(configPath), "models_cache.json");
2596
+ return isAbsolute5(configuredPath) ? configuredPath : join17(dirname7(configPath), configuredPath);
2597
+ }
2598
+ function readCatalogMultiAgentVersion(model, cachePath) {
2599
+ let raw;
2600
+ try {
2601
+ raw = readFileSync(cachePath, "utf8");
2602
+ } catch {
2603
+ return null;
2604
+ }
2605
+ let cache;
2606
+ try {
2607
+ cache = JSON.parse(raw);
2608
+ } catch {
2609
+ return null;
2519
2610
  }
2520
- return replaceOrInsertSetting(withPreservedDisable, updatedSection, "max_concurrent_threads_per_session", maxThreadsValue);
2611
+ if (!isRecord2(cache) || !Array.isArray(cache.models))
2612
+ return null;
2613
+ for (const entry of cache.models) {
2614
+ if (!isRecord2(entry))
2615
+ continue;
2616
+ if (entry.slug !== model && entry.id !== model)
2617
+ continue;
2618
+ const version = entry.multi_agent_version;
2619
+ if (version === "v1" || version === "v2")
2620
+ return version;
2621
+ return null;
2622
+ }
2623
+ return null;
2624
+ }
2625
+ function readRootModel(config) {
2626
+ const double = config.match(/^\s*model\s*=\s*"([^"]+)"/m);
2627
+ if (double !== null)
2628
+ return double[1] ?? null;
2629
+ const single = config.match(/^\s*model\s*=\s*'([^']+)'/m);
2630
+ return single?.[1] ?? null;
2631
+ }
2632
+ function readRootModelCatalogPath(config) {
2633
+ const double = config.match(/^\s*model_catalog_json\s*=\s*"([^"]+)"/m);
2634
+ if (double !== null)
2635
+ return double[1] ?? null;
2636
+ const single = config.match(/^\s*model_catalog_json\s*=\s*'([^']+)'/m);
2637
+ return single?.[1] ?? null;
2638
+ }
2639
+ function isRecord2(value) {
2640
+ return typeof value === "object" && value !== null && !Array.isArray(value);
2521
2641
  }
2522
2642
  function removeFeatureFlagSetting(config, featureName) {
2523
2643
  const section = findTomlSection(config, "features");
@@ -2538,6 +2658,36 @@ max_threads = ${maxThreadsValue}
2538
2658
  }
2539
2659
  return replaceOrInsertSetting(config, section, "max_threads", maxThreadsValue);
2540
2660
  }
2661
+ function removeAgentsMaxThreads(config) {
2662
+ const section = findTomlSection(config, CODEX_AGENTS_HEADER);
2663
+ if (!section)
2664
+ return config;
2665
+ if (!/^\s*max_threads\s*=/m.test(section.text))
2666
+ return config;
2667
+ return removeSetting(config, section, "max_threads");
2668
+ }
2669
+ function removeMultiAgentV2Disable(config) {
2670
+ const section = findTomlSection(config, CODEX_MULTI_AGENT_V2_HEADER);
2671
+ if (!section)
2672
+ return config;
2673
+ if (!/^\s*enabled\s*=\s*false(?:\s*#.*)?$/m.test(section.text))
2674
+ return config;
2675
+ return removeSetting(config, section, "enabled");
2676
+ }
2677
+ function setMultiAgentV2Disable(config) {
2678
+ const section = findTomlSection(config, CODEX_MULTI_AGENT_V2_HEADER);
2679
+ if (!section)
2680
+ return config;
2681
+ return replaceOrInsertSetting(config, section, "enabled", "false");
2682
+ }
2683
+ function raiseExistingAgentsMaxThreads(config) {
2684
+ const section = findTomlSection(config, CODEX_AGENTS_HEADER);
2685
+ if (!section)
2686
+ return config;
2687
+ if (!/^\s*max_threads\s*=/m.test(section.text))
2688
+ return config;
2689
+ return replaceOrInsertSetting(config, section, "max_threads", CODEX_SUBAGENT_THREAD_LIMIT.toString());
2690
+ }
2541
2691
  function readBooleanSetting(sectionText, key) {
2542
2692
  const match = new RegExp(`^\\s*${escapeRegExp(key)}\\s*=\\s*(true|false)\\s*(?:#.*)?$`, "m").exec(sectionText);
2543
2693
  if (!match)
@@ -2547,7 +2697,7 @@ function readBooleanSetting(sectionText, key) {
2547
2697
 
2548
2698
  // ../src/install/codex-config-toml.ts
2549
2699
  async function updateCodexConfig(input) {
2550
- await mkdir6(dirname7(input.configPath), { recursive: true });
2700
+ await mkdir6(dirname8(input.configPath), { recursive: true });
2551
2701
  let config;
2552
2702
  try {
2553
2703
  config = await readFile10(input.configPath, "utf8");
@@ -2570,7 +2720,9 @@ async function updateCodexConfig(input) {
2570
2720
  config = ensureFeatureEnabled(config, "multi_agent");
2571
2721
  config = removeUnsupportedCodexMultiAgentModeConfig(config);
2572
2722
  config = ensureCodexReasoningConfig(config, await readCodexModelCatalog(input.repoRoot));
2573
- config = ensureCodexMultiAgentV2Config(config);
2723
+ config = ensureCodexMultiAgentV2Config(config, {
2724
+ multiAgentVersion: resolveCodexMultiAgentVersion(config, input.configPath)
2725
+ });
2574
2726
  if (input.autonomousPermissions === true)
2575
2727
  config = ensureAutonomousPermissions(config);
2576
2728
  if (!(input.preserveMarketplaceSource === true && hasMarketplaceBlock(config, input.marketplaceName))) {
@@ -2594,12 +2746,12 @@ function isMissingFileError(error) {
2594
2746
  }
2595
2747
 
2596
2748
  // ../src/install/codex-git-bash-mcp-env.ts
2597
- import { readFile as readFile11, writeFile as writeFile6 } from "node:fs/promises";
2598
- import { join as join16 } from "node:path";
2749
+ import { readFile as readFile11, writeFile as writeFile7 } from "node:fs/promises";
2750
+ import { join as join18 } from "node:path";
2599
2751
  var GIT_BASH_ENV_KEY = "OMO_CODEX_GIT_BASH_PATH";
2600
2752
  var CODEGRAPH_RELATIVE_ARGS = new Set(["components/codegraph/dist/serve.js", "./components/codegraph/dist/serve.js"]);
2601
2753
  async function stampGitBashMcpEnv(input) {
2602
- const manifestPath = join16(input.pluginRoot, ".mcp.json");
2754
+ const manifestPath = join18(input.pluginRoot, ".mcp.json");
2603
2755
  if (!await fileExistsStrict(manifestPath))
2604
2756
  return false;
2605
2757
  const parsed = JSON.parse(await readFile11(manifestPath, "utf8"));
@@ -2620,7 +2772,7 @@ async function stampGitBashMcpEnv(input) {
2620
2772
  }
2621
2773
  if (!changed)
2622
2774
  return false;
2623
- await writeFile6(manifestPath, `${JSON.stringify(parsed, null, "\t")}
2775
+ await writeFile7(manifestPath, `${JSON.stringify(parsed, null, "\t")}
2624
2776
  `);
2625
2777
  return true;
2626
2778
  }
@@ -2632,14 +2784,14 @@ function stampCodegraphMcpPath(mcpServers, pluginRoot) {
2632
2784
  const entrypoint = args[0];
2633
2785
  if (typeof entrypoint !== "string" || !CODEGRAPH_RELATIVE_ARGS.has(entrypoint))
2634
2786
  return false;
2635
- codegraphServer["args"] = [join16(pluginRoot, "components", "codegraph", "dist", "serve.js"), ...args.slice(1)];
2787
+ codegraphServer["args"] = [join18(pluginRoot, "components", "codegraph", "dist", "serve.js"), ...args.slice(1)];
2636
2788
  return true;
2637
2789
  }
2638
2790
 
2639
2791
  // ../src/install/codex-hook-trust.ts
2640
2792
  import { createHash as createHash3 } from "node:crypto";
2641
2793
  import { readFile as readFile12 } from "node:fs/promises";
2642
- import { join as join17 } from "node:path";
2794
+ import { join as join19 } from "node:path";
2643
2795
  var EVENT_LABELS = new Map([
2644
2796
  ["PreToolUse", "pre_tool_use"],
2645
2797
  ["PermissionRequest", "permission_request"],
@@ -2653,16 +2805,16 @@ var EVENT_LABELS = new Map([
2653
2805
  ["Stop", "stop"]
2654
2806
  ]);
2655
2807
  async function trustedHookStatesForPlugin(input) {
2656
- const manifestPath = join17(input.pluginRoot, ".codex-plugin", "plugin.json");
2657
- if (!await exists3(manifestPath))
2808
+ const manifestPath = join19(input.pluginRoot, ".codex-plugin", "plugin.json");
2809
+ if (!await exists4(manifestPath))
2658
2810
  return [];
2659
2811
  const manifest = JSON.parse(await readFile12(manifestPath, "utf8"));
2660
2812
  if (!isPlainRecord(manifest))
2661
2813
  return [];
2662
2814
  const states = [];
2663
2815
  for (const hookPath of hookManifestPaths(manifest.hooks)) {
2664
- const hooksPath = join17(input.pluginRoot, hookPath);
2665
- if (!await exists3(hooksPath))
2816
+ const hooksPath = join19(input.pluginRoot, hookPath);
2817
+ if (!await exists4(hooksPath))
2666
2818
  continue;
2667
2819
  const parsed = JSON.parse(await readFile12(hooksPath, "utf8"));
2668
2820
  if (!isPlainRecord(parsed) || !isPlainRecord(parsed.hooks))
@@ -2745,7 +2897,7 @@ function canonicalJson(value) {
2745
2897
  function stripDotSlash(value) {
2746
2898
  return value.startsWith("./") ? value.slice(2) : value;
2747
2899
  }
2748
- async function exists3(path) {
2900
+ async function exists4(path) {
2749
2901
  try {
2750
2902
  await readFile12(path, "utf8");
2751
2903
  return true;
@@ -2758,7 +2910,7 @@ async function exists3(path) {
2758
2910
 
2759
2911
  // ../src/install/codex-installer-bin-dir.ts
2760
2912
  import { homedir as homedir3 } from "node:os";
2761
- import { join as join18, resolve as resolve6 } from "node:path";
2913
+ import { join as join20, resolve as resolve6 } from "node:path";
2762
2914
  function resolveCodexInstallerBinDir(input) {
2763
2915
  const explicitBinDir = input.binDir ?? input.env?.CODEX_LOCAL_BIN_DIR;
2764
2916
  if (explicitBinDir !== undefined && explicitBinDir.trim().length > 0)
@@ -2767,7 +2919,7 @@ function resolveCodexInstallerBinDir(input) {
2767
2919
  const defaultCodexHome = resolve6(homeDir, ".codex");
2768
2920
  const resolvedCodexHome = resolve6(input.codexHome);
2769
2921
  if (resolvedCodexHome !== defaultCodexHome)
2770
- return join18(resolvedCodexHome, "bin");
2922
+ return join20(resolvedCodexHome, "bin");
2771
2923
  return resolve6(homeDir, ".local", "bin");
2772
2924
  }
2773
2925
 
@@ -2923,9 +3075,9 @@ async function resolveGitBashStep(options, degraded) {
2923
3075
  return false;
2924
3076
  }
2925
3077
  async function linkBundledAgentsStep(options) {
2926
- const agentsTarget = join19(options.codexHome, "agents");
3078
+ const agentsTarget = join21(options.codexHome, "agents");
2927
3079
  try {
2928
- const stageRoot = join19(options.pluginData, "bootstrap", "agents-stage");
3080
+ const stageRoot = join21(options.pluginData, "bootstrap", "agents-stage");
2929
3081
  await stageBundledAgents(options.pluginRoot, stageRoot);
2930
3082
  const preservedReasoning = await capturePreservedAgentReasoning({ codexHome: options.codexHome });
2931
3083
  const preservedServiceTier = await capturePreservedAgentServiceTier({ codexHome: options.codexHome });
@@ -2935,7 +3087,7 @@ async function linkBundledAgentsStep(options) {
2935
3087
  preservedReasoning,
2936
3088
  preservedServiceTier
2937
3089
  });
2938
- const agentConfigs = linked.map((link) => ({ configFile: `./agents/${link.name}`, name: agentNameFromToml2(link.name) })).sort((left, right) => left.name.localeCompare(right.name));
3090
+ const agentConfigs = linked.map((link) => ({ configFile: `./agents/${link.name}`, name: agentNameFromToml3(link.name) })).sort((left, right) => left.name.localeCompare(right.name));
2939
3091
  return { agentConfigs, degraded: [] };
2940
3092
  } catch (error) {
2941
3093
  return {
@@ -2953,21 +3105,21 @@ async function linkBundledAgentsStep(options) {
2953
3105
  async function stageBundledAgents(pluginRoot, stageRoot) {
2954
3106
  await rm10(stageRoot, { force: true, recursive: true });
2955
3107
  await mkdir7(stageRoot, { recursive: true });
2956
- const componentsRoot = join19(pluginRoot, "components");
3108
+ const componentsRoot = join21(pluginRoot, "components");
2957
3109
  for (const componentName of await directoryNames(componentsRoot)) {
2958
- const agentsDir = join19(componentsRoot, componentName, "agents");
3110
+ const agentsDir = join21(componentsRoot, componentName, "agents");
2959
3111
  const agentFiles = (await fileNames(agentsDir)).filter((name) => name.endsWith(".toml"));
2960
3112
  if (agentFiles.length === 0)
2961
3113
  continue;
2962
- const stagedAgentsDir = join19(stageRoot, "components", componentName, "agents");
3114
+ const stagedAgentsDir = join21(stageRoot, "components", componentName, "agents");
2963
3115
  await mkdir7(stagedAgentsDir, { recursive: true });
2964
3116
  for (const agentFile of agentFiles) {
2965
- await copyFile2(join19(agentsDir, agentFile), join19(stagedAgentsDir, agentFile));
3117
+ await copyFile2(join21(agentsDir, agentFile), join21(stagedAgentsDir, agentFile));
2966
3118
  }
2967
3119
  }
2968
3120
  }
2969
3121
  async function updateConfigStep(options, inputs, degraded) {
2970
- const configPath = join19(options.codexHome, "config.toml");
3122
+ const configPath = join21(options.codexHome, "config.toml");
2971
3123
  try {
2972
3124
  await assertWritableConfigIfPresent(configPath);
2973
3125
  const trustedHookStates = await trustedHookStatesForPlugin({
@@ -3023,7 +3175,7 @@ async function linkComponentBinsStep(options, degraded) {
3023
3175
  await linkRuntimeWrapperStep(options, binDir, degraded);
3024
3176
  }
3025
3177
  async function linkRuntimeWrapperStep(options, binDir, degraded) {
3026
- const cliPath = join19(options.pluginRoot, "dist", "cli", "index.js");
3178
+ const cliPath = join21(options.pluginRoot, "dist", "cli", "index.js");
3027
3179
  try {
3028
3180
  const linked = await linkRootRuntimeBin({
3029
3181
  binDir,
@@ -3056,7 +3208,7 @@ async function stampGitBashEnvStep(options, degraded) {
3056
3208
  degraded.push({
3057
3209
  component: "git-bash-env",
3058
3210
  hint: BOOTSTRAP_DOCTOR_HINT,
3059
- reason: `failed to stamp ${join19(options.pluginRoot, ".mcp.json")}: ${errorMessage(error)}`
3211
+ reason: `failed to stamp ${join21(options.pluginRoot, ".mcp.json")}: ${errorMessage(error)}`
3060
3212
  });
3061
3213
  }
3062
3214
  }
@@ -3068,7 +3220,7 @@ async function fileNames(root) {
3068
3220
  }
3069
3221
  async function entryNames(root, keep) {
3070
3222
  try {
3071
- const entries = await readdir4(root, { withFileTypes: true });
3223
+ const entries = await readdir5(root, { withFileTypes: true });
3072
3224
  return entries.filter((entry) => keep(entry)).map((entry) => entry.name).sort();
3073
3225
  } catch (error) {
3074
3226
  if (error instanceof Error && "code" in error && error.code === "ENOENT")
@@ -3076,7 +3228,7 @@ async function entryNames(root, keep) {
3076
3228
  throw error;
3077
3229
  }
3078
3230
  }
3079
- function agentNameFromToml2(fileName) {
3231
+ function agentNameFromToml3(fileName) {
3080
3232
  return fileName.endsWith(".toml") ? fileName.slice(0, -".toml".length) : fileName;
3081
3233
  }
3082
3234
  function errorMessage(error) {
@@ -3124,11 +3276,11 @@ function resolvePluginDataRoot(env) {
3124
3276
  const fromEnv = env["PLUGIN_DATA"]?.trim();
3125
3277
  if (fromEnv !== undefined && fromEnv.length > 0)
3126
3278
  return fromEnv;
3127
- return join20(homedir4(), ".local", "share", "lazycodex");
3279
+ return join22(homedir4(), ".local", "share", "lazycodex");
3128
3280
  }
3129
3281
  async function readPluginVersion(pluginRoot) {
3130
3282
  try {
3131
- const parsed = JSON.parse(await readFile13(join20(pluginRoot, ".codex-plugin", "plugin.json"), "utf8"));
3283
+ const parsed = JSON.parse(await readFile13(join22(pluginRoot, ".codex-plugin", "plugin.json"), "utf8"));
3132
3284
  if (typeof parsed !== "object" || parsed === null)
3133
3285
  return;
3134
3286
  const version = parsed["version"];
@@ -3195,7 +3347,7 @@ async function runBootstrapWorker(options = {}) {
3195
3347
  degraded.push({
3196
3348
  component: "bootstrap",
3197
3349
  hint: BOOTSTRAP_DOCTOR_HINT,
3198
- reason: `plugin version unresolved from ${join20(pluginRoot, ".codex-plugin", "plugin.json")}`
3350
+ reason: `plugin version unresolved from ${join22(pluginRoot, ".codex-plugin", "plugin.json")}`
3199
3351
  });
3200
3352
  }
3201
3353
  for (const step of steps) {
@@ -3234,12 +3386,12 @@ function resolvePluginRoot(env) {
3234
3386
  const fromEnv = env["PLUGIN_ROOT"]?.trim();
3235
3387
  if (fromEnv !== undefined && fromEnv.length > 0)
3236
3388
  return fromEnv;
3237
- return resolve7(dirname8(fileURLToPath2(import.meta.url)), "..", "..", "..");
3389
+ return resolve7(dirname9(fileURLToPath2(import.meta.url)), "..", "..", "..");
3238
3390
  }
3239
3391
  async function appendBootstrapLog(pluginData, now, event, details) {
3240
3392
  try {
3241
- const logPath = join20(pluginData, "bootstrap", "bootstrap.log");
3242
- await mkdir8(dirname8(logPath), { recursive: true });
3393
+ const logPath = join22(pluginData, "bootstrap", "bootstrap.log");
3394
+ await mkdir8(dirname9(logPath), { recursive: true });
3243
3395
  await appendFile2(logPath, `${JSON.stringify({ timestamp: new Date(now).toISOString(), event, ...details })}
3244
3396
  `);
3245
3397
  } catch {}