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
@@ -1,20 +1,31 @@
1
1
  /**
2
- * Runtime migration: force `[features.multi_agent_v2]` to `enabled = false`.
2
+ * Runtime migration for `[features.multi_agent_v2]`.
3
3
  *
4
- * Runs on every Codex SessionStart (via auto-update's config migration) so
5
- * multi-agent V2 stays off regardless of how it was turned on: an
6
- * installer-forced `enabled = true`, a missing `enabled` key the runtime
7
- * would resolve per model, or the `[features]` boolean shorthand
8
- * `multi_agent_v2 = true` (removed here because a boolean key and a
9
- * `[features.multi_agent_v2]` table for the same name are conflicting TOML).
4
+ * Historical behavior (openai/codex#26753): force `enabled = false` on every
5
+ * SessionStart because enabling V2 made every turn 400 with encrypted
6
+ * spawn_agent parameters on models that were not configured for encrypted
7
+ * tool use. OpenAI closed that as NOT_PLANNED (V2 under development).
10
8
  *
11
- * Upstream basis: openai/codex#26753 with the flag on, EVERY turn fails
12
- * with a 400 ("spawn_agent declares encrypted parameters but is not
13
- * configured for encrypted tool use by this model"), even on prompts that
14
- * never touch subagents. OpenAI closed it NOT_PLANNED stating V2 is under
15
- * development, not recommended, and bug reports are not accepted. Same
16
- * failure class still being reported (openai/codex#27205).
9
+ * GPT-5.6 models that declare `multi_agent_version: "v2"` in the Codex model
10
+ * catalog invert that failure mode: forcing `enabled = false` makes every
11
+ * turn 400 with a reserved `collaboration.spawn_agent` schema mismatch
12
+ * (lazycodex#118 / oh-my-openagent#6002 / openai/codex#31097), and
13
+ * `hide_spawn_agent_metadata = false` (written by OMO installers <= 4.15.x)
14
+ * mismatches the same reserved schema by re-adding agent_type/model
15
+ * properties to spawn_agent. For those models this guard clears the managed
16
+ * disable and the stale metadata override, leaving V2 unset so Codex can
17
+ * follow model metadata.
18
+ *
19
+ * When the selected model is unknown or declares V1, keep the #26753
20
+ * force-disable path.
21
+ *
22
+ * Opt out of the whole migration with LAZYCODEX_CONFIG_MIGRATION_DISABLED=1
23
+ * (or OMO_CODEX_CONFIG_MIGRATION_DISABLED=1).
17
24
  */
25
+ import { readFileSync } from "node:fs";
26
+ import { homedir } from "node:os";
27
+ import { dirname, isAbsolute, join } from "node:path";
28
+
18
29
  const MANAGED_COMMENT_MARKER = "openai/codex#26753";
19
30
  const MANAGED_DISABLE_COMMENT = [
20
31
  "# Managed by LazyCodex: multi_agent_v2 is re-disabled on every Codex session start",
@@ -23,26 +34,192 @@ const MANAGED_DISABLE_COMMENT = [
23
34
  "",
24
35
  ].join("\n");
25
36
 
26
- export function forceDisableMultiAgentV2(config) {
27
- let result = removeFeaturesShorthand(config);
37
+ /**
38
+ * @param {string} config
39
+ * @param {{
40
+ * multiAgentVersion?: string | null,
41
+ * sessionModel?: string | null,
42
+ * requireSessionModel?: boolean,
43
+ * env?: NodeJS.ProcessEnv,
44
+ * modelsCachePath?: string,
45
+ * configPath?: string,
46
+ * }} [options]
47
+ */
48
+ export function forceDisableMultiAgentV2(config, options = {}) {
49
+ // Always normalize the legacy `[features]` boolean shorthand first: leaving
50
+ // `multi_agent_v2 = true|false` in place while a later guard appends the
51
+ // `[features.multi_agent_v2]` table would define the same name as both a
52
+ // scalar and a table, which Codex rejects as invalid TOML.
53
+ const normalized = removeFeaturesShorthand(config);
54
+ const sessionModel = normalizeModel(options.sessionModel);
55
+ const multiAgentVersion =
56
+ options.multiAgentVersion !== undefined
57
+ ? options.multiAgentVersion
58
+ : resolveMultiAgentVersionFromConfig(normalized, options);
59
+ const effectiveModel = sessionModel || readRootModel(normalized);
60
+
61
+ if (prefersMultiAgentV2(multiAgentVersion, effectiveModel)) {
62
+ return clearMultiAgentV2DisableForReservedSchema(normalized);
63
+ }
64
+
65
+ // SessionStart can run with an override model (`codex -m gpt-5.6-terra`) while
66
+ // config.toml still lists a different default. If we cannot see the effective
67
+ // session model, do not force-disable — writing enabled=false would break a
68
+ // GPT-5.6 reserved collaboration.spawn_agent session.
69
+ if (options.requireSessionModel === true && !sessionModel) {
70
+ return normalized;
71
+ }
72
+
73
+ // No model evidence at all (no session model AND no root `model` in
74
+ // config.toml — Codex Desktop selects the model in the UI): config alone
75
+ // cannot prove the session is not a GPT-5.6 reserved-schema model, and
76
+ // writing `enabled = false` would 400 every turn on those sessions
77
+ // (#6002). Leave the enable state untouched.
78
+ if (multiAgentVersion == null && !sessionModel && !readRootModel(normalized)) {
79
+ return normalized;
80
+ }
81
+
82
+ // Unknown catalog entry for an explicit session model: skip force-disable
83
+ // rather than assume the legacy encrypted-V2 failure mode.
84
+ if (sessionModel && multiAgentVersion == null) {
85
+ return normalized;
86
+ }
87
+
88
+ return forceDisableLegacyEncryptedV2(normalized);
89
+ }
90
+
91
+ /**
92
+ * True when the effective model should run MultiAgentV2: the catalog says
93
+ * "v2", or the catalog is unavailable but the model is a GPT-5.6 family
94
+ * model (which reserves the collaboration.spawn_agent schema).
95
+ * @param {"v1" | "v2" | null | undefined} multiAgentVersion
96
+ * @param {string | null | undefined} sessionModel
97
+ */
98
+ export function prefersMultiAgentV2(multiAgentVersion, sessionModel) {
99
+ return multiAgentVersion === "v2" || (multiAgentVersion == null && isGpt56Family(normalizeModel(sessionModel)));
100
+ }
101
+
102
+ /**
103
+ * Resolve the effective model against Codex `models_cache.json`.
104
+ * Prefers SessionStart `model` over the root `model` in config.toml.
105
+ * @param {string} config
106
+ * @param {{ sessionModel?: string | null, env?: NodeJS.ProcessEnv, modelsCachePath?: string, configPath?: string }} [options]
107
+ * @returns {"v1" | "v2" | null}
108
+ */
109
+ export function resolveMultiAgentVersionFromConfig(config, options = {}) {
110
+ const model = normalizeModel(options.sessionModel) || readRootModel(config);
111
+ if (!model) return null;
112
+ const version = resolveMultiAgentVersionForModel(model, {
113
+ ...options,
114
+ modelsCachePath: options.modelsCachePath?.trim() || resolveModelCatalogPath(readRootModelCatalogPath(config), options) || undefined,
115
+ });
116
+ return version ?? (isGpt56Family(model) ? "v2" : null);
117
+ }
118
+
119
+ /**
120
+ * @param {string} model
121
+ * @param {{ env?: NodeJS.ProcessEnv, modelsCachePath?: string }} [options]
122
+ * @returns {"v1" | "v2" | null}
123
+ */
124
+ export function resolveMultiAgentVersionForModel(model, options = {}) {
125
+ const cachePath =
126
+ options.modelsCachePath?.trim() ||
127
+ join(options.env?.CODEX_HOME?.trim() || join(homedir(), ".codex"), "models_cache.json");
128
+
129
+ try {
130
+ const cache = JSON.parse(readFileSync(cachePath, "utf8"));
131
+ const models = Array.isArray(cache?.models) ? cache.models : [];
132
+ const entry = models.find((item) => item?.slug === model || item?.id === model);
133
+ const version = entry?.multi_agent_version;
134
+ if (version === "v1" || version === "v2") return version;
135
+ return null;
136
+ } catch {
137
+ return null;
138
+ }
139
+ }
140
+
141
+ export function readRootModel(config) {
142
+ const double = config.match(/^\s*model\s*=\s*"([^"]+)"/m);
143
+ if (double) return double[1];
144
+ const single = config.match(/^\s*model\s*=\s*'([^']+)'/m);
145
+ return single?.[1] ?? null;
146
+ }
147
+
148
+ // Codex documents `model_catalog_json` as a COMPLETE replacement for the
149
+ // fetched models_cache.json (codex-rs/core/src/config/mod.rs load_model_catalog
150
+ // -> load_catalog_json -> ModelsResponse). When set, Codex resolves the model
151
+ // only from that file, so the guard must too — otherwise Codex and the guard
152
+ // disagree on the multi-agent version (lazycodex#120).
153
+ export function readRootModelCatalogPath(config) {
154
+ const double = config.match(/^\s*model_catalog_json\s*=\s*"([^"]+)"/m);
155
+ if (double) return double[1];
156
+ const single = config.match(/^\s*model_catalog_json\s*=\s*'([^']+)'/m);
157
+ return single?.[1] ?? null;
158
+ }
159
+
160
+ function resolveModelCatalogPath(configuredPath, options) {
161
+ const trimmed = normalizeModel(configuredPath);
162
+ if (!trimmed) return null;
163
+ if (isAbsolute(trimmed)) return trimmed;
164
+ const baseDir = options.configPath ? dirname(options.configPath) : options.env?.CODEX_HOME?.trim() || join(homedir(), ".codex");
165
+ return join(baseDir, trimmed);
166
+ }
167
+
168
+ function normalizeModel(value) {
169
+ if (typeof value !== "string") return null;
170
+ const trimmed = value.trim();
171
+ return trimmed.length > 0 ? trimmed : null;
172
+ }
173
+
174
+ function isGpt56Family(model) {
175
+ return typeof model === "string" && /^gpt-5\.6\b/i.test(model);
176
+ }
177
+
178
+ function clearMultiAgentV2DisableForReservedSchema(config) {
179
+ // `config` arrives shorthand-normalized from forceDisableMultiAgentV2.
180
+ const result = removeManagedDisableComments(config);
181
+
28
182
  const section = findSection(result, "[features.multi_agent_v2]");
183
+ if (!section) return result;
184
+
185
+ // Two settings poison the reserved collaboration.spawn_agent schema on V2
186
+ // models (verified against codex-cli 0.144.1 + gpt-5.6-sol):
187
+ // - `enabled = false` forces the legacy V1 tool surface on some Codex
188
+ // versions (#6002's failure shape);
189
+ // - `hide_spawn_agent_metadata = false` (written by OMO installers
190
+ // <= 4.15.x to expose agent_type) re-adds agent_type/model/... to
191
+ // spawn_agent, mismatching the reserved schema -> HTTP 400 every turn.
192
+ // Remove both; `hide_spawn_agent_metadata = true` matches the Codex
193
+ // default and is left alone.
194
+ const cleared = section.text
195
+ .replace(/^\s*enabled\s*=\s*false[ \t]*(?:#[^\n]*)?\n?/gm, "")
196
+ .replace(/^\s*hide_spawn_agent_metadata\s*=\s*false[ \t]*(?:#[^\n]*)?\n?/gm, "");
197
+ if (cleared === section.text) return result;
198
+ return result.slice(0, section.start) + cleared + result.slice(section.end);
199
+ }
200
+
201
+ // `config` arrives shorthand-normalized from forceDisableMultiAgentV2.
202
+ function forceDisableLegacyEncryptedV2(config) {
203
+ const section = findSection(config, "[features.multi_agent_v2]");
29
204
 
30
205
  if (!section) {
31
- return ensureManagedComment(appendDisabledSection(result));
206
+ return ensureManagedComment(appendDisabledSection(config));
32
207
  }
33
208
 
34
209
  const enabledTruePattern = /^(\s*)enabled\s*=\s*true[ \t]*(#[^\n]*)?$/m;
35
210
  if (enabledTruePattern.test(section.text)) {
36
- const patched = section.text.replace(enabledTruePattern, (_match, indent, comment) => comment ? `${indent}enabled = false ${comment}` : `${indent}enabled = false`);
37
- return ensureManagedComment(result.slice(0, section.start) + patched + result.slice(section.end));
211
+ const patched = section.text.replace(enabledTruePattern, (_match, indent, comment) =>
212
+ comment ? `${indent}enabled = false ${comment}` : `${indent}enabled = false`,
213
+ );
214
+ return ensureManagedComment(config.slice(0, section.start) + patched + config.slice(section.end));
38
215
  }
39
216
 
40
- if (/^\s*enabled\s*=\s*false[ \t]*(?:#[^\n]*)?$/m.test(section.text)) return result;
217
+ if (/^\s*enabled\s*=\s*false[ \t]*(?:#[^\n]*)?$/m.test(section.text)) return config;
41
218
 
42
219
  const headerEnd = section.text.indexOf("\n");
43
220
  const insertAt = headerEnd === -1 ? section.text.length : headerEnd + 1;
44
221
  const patched = `${section.text.slice(0, insertAt)}${headerEnd === -1 ? "\n" : ""}enabled = false\n${section.text.slice(insertAt)}`;
45
- return ensureManagedComment(result.slice(0, section.start) + patched + result.slice(section.end));
222
+ return ensureManagedComment(config.slice(0, section.start) + patched + config.slice(section.end));
46
223
  }
47
224
 
48
225
  function ensureManagedComment(config) {
@@ -52,6 +229,30 @@ function ensureManagedComment(config) {
52
229
  return config.slice(0, section.start) + MANAGED_DISABLE_COMMENT + config.slice(section.start);
53
230
  }
54
231
 
232
+ function removeManagedDisableComments(config) {
233
+ if (!config.includes(MANAGED_COMMENT_MARKER) && !config.includes("Managed by LazyCodex: multi_agent_v2")) {
234
+ return config;
235
+ }
236
+
237
+ const lines = config.split("\n");
238
+ const kept = [];
239
+ for (const line of lines) {
240
+ const trimmed = line.trim();
241
+ if (
242
+ trimmed.startsWith("#") &&
243
+ (trimmed.includes(MANAGED_COMMENT_MARKER) ||
244
+ trimmed.includes("Managed by LazyCodex: multi_agent_v2") ||
245
+ trimmed.includes("because enabling it fails every turn with HTTP 400") ||
246
+ trimmed.includes("LAZYCODEX_CONFIG_MIGRATION_DISABLED=1") ||
247
+ trimmed.includes("OMO_CODEX_CONFIG_MIGRATION_DISABLED=1"))
248
+ ) {
249
+ continue;
250
+ }
251
+ kept.push(line);
252
+ }
253
+ return kept.join("\n").replace(/\n{3,}/g, "\n\n");
254
+ }
255
+
55
256
  function removeFeaturesShorthand(config) {
56
257
  const section = findSection(config, "[features]");
57
258
  if (!section) return config;
@@ -1,9 +1,59 @@
1
+ import { prefersMultiAgentV2, readRootModel, resolveMultiAgentVersionFromConfig } from "./multi-agent-v2-guard.mjs";
2
+
1
3
  const CODEX_AGENTS_HEADER = "[agents]";
2
4
  const CODEX_MULTI_AGENT_V2_HEADER = "[features.multi_agent_v2]";
3
5
  const CODEX_SUBAGENT_THREAD_LIMIT = "1000";
4
6
 
5
- export function ensureSubagentConcurrencyLimit(config) {
6
- return ensureMultiAgentV2ThreadLimit(ensureAgentsMaxThreads(config));
7
+ /**
8
+ * Ensure subagent concurrency limits without writing settings that conflict
9
+ * with MultiAgentV2. When the selected model prefers V2 (catalog `v2`, or a
10
+ * GPT-5.6 family session model with the catalog unavailable) or V2 is already
11
+ * enabled in config, skip `agents.max_threads` because Codex rejects that key
12
+ * while features.multi_agent_v2 is enabled.
13
+ *
14
+ * When no model is resolvable at all (no session model and no root `model`
15
+ * in config.toml — Codex Desktop selects the model in the UI), never
16
+ * introduce `agents.max_threads`: it hard-fails thread/start on
17
+ * MultiAgentV2 sessions. An existing cap is still raised in place so the
18
+ * legacy low-cap repair keeps working and a hand-removed key stays removed.
19
+ *
20
+ * @param {string} config
21
+ * @param {{ multiAgentVersion?: string | null, sessionModel?: string | null, env?: NodeJS.ProcessEnv, modelsCachePath?: string }} [options]
22
+ */
23
+ export function ensureSubagentConcurrencyLimit(config, options = {}) {
24
+ const multiAgentVersion =
25
+ options.multiAgentVersion !== undefined
26
+ ? options.multiAgentVersion
27
+ : resolveMultiAgentVersionFromConfig(config, options);
28
+ const v2Preferred = prefersMultiAgentV2(multiAgentVersion, options.sessionModel) || isMultiAgentV2Enabled(config);
29
+
30
+ let result = config;
31
+ if (v2Preferred) {
32
+ result = removeAgentsMaxThreads(result);
33
+ } else if (multiAgentVersion == null && !hasModelEvidence(config, options)) {
34
+ result = raiseExistingAgentsMaxThreads(result);
35
+ } else {
36
+ result = ensureAgentsMaxThreads(result);
37
+ }
38
+ return ensureMultiAgentV2ThreadLimit(result);
39
+ }
40
+
41
+ function hasModelEvidence(config, options) {
42
+ const sessionModel = typeof options.sessionModel === "string" ? options.sessionModel.trim() : "";
43
+ return sessionModel.length > 0 || readRootModel(config) !== null;
44
+ }
45
+
46
+ function isMultiAgentV2Enabled(config) {
47
+ const section = findSection(config, CODEX_MULTI_AGENT_V2_HEADER);
48
+ if (!section) return false;
49
+ return /^\s*enabled\s*=\s*true[ \t]*(?:#[^\n]*)?$/m.test(section.text);
50
+ }
51
+
52
+ function raiseExistingAgentsMaxThreads(config) {
53
+ const section = findSection(config, CODEX_AGENTS_HEADER);
54
+ if (!section) return config;
55
+ if (!/^\s*max_threads\s*=/m.test(section.text)) return config;
56
+ return replaceOrInsertSetting(config, section, "max_threads", CODEX_SUBAGENT_THREAD_LIMIT);
7
57
  }
8
58
 
9
59
  function ensureAgentsMaxThreads(config) {
@@ -12,6 +62,22 @@ function ensureAgentsMaxThreads(config) {
12
62
  return replaceOrInsertSetting(config, section, "max_threads", CODEX_SUBAGENT_THREAD_LIMIT);
13
63
  }
14
64
 
65
+ function removeAgentsMaxThreads(config) {
66
+ const section = findSection(config, CODEX_AGENTS_HEADER);
67
+ if (!section) return config;
68
+ if (!/^\s*max_threads\s*=/m.test(section.text)) return config;
69
+
70
+ const patched = section.text.replace(/^\s*max_threads\s*=\s*[^\n]*\n?/m, "");
71
+ const bodyLines = patched
72
+ .split("\n")
73
+ .slice(1)
74
+ .filter((line) => line.trim() !== "");
75
+ if (bodyLines.length === 0) {
76
+ return config.slice(0, section.start) + config.slice(section.end).replace(/^\n+/, "");
77
+ }
78
+ return config.slice(0, section.start) + patched + config.slice(section.end);
79
+ }
80
+
15
81
  function ensureMultiAgentV2ThreadLimit(config) {
16
82
  const section = findSection(config, CODEX_MULTI_AGENT_V2_HEADER);
17
83
  if (!section) {
@@ -26,7 +92,9 @@ function ensureMultiAgentV2ThreadLimit(config) {
26
92
  function replaceOrInsertSetting(config, section, key, value) {
27
93
  const pattern = new RegExp(`^(\\s*)${escapeRegExp(key)}\\s*=\\s*[^\\n#]*(#[^\\n]*)?$`, "m");
28
94
  if (pattern.test(section.text)) {
29
- const patched = section.text.replace(pattern, (_match, indent, comment) => comment ? `${indent}${key} = ${value} ${comment}` : `${indent}${key} = ${value}`);
95
+ const patched = section.text.replace(pattern, (_match, indent, comment) =>
96
+ comment ? `${indent}${key} = ${value} ${comment}` : `${indent}${key} = ${value}`,
97
+ );
30
98
  return config.slice(0, section.start) + patched + config.slice(section.end);
31
99
  }
32
100
 
@@ -2,13 +2,16 @@
2
2
 
3
3
  import { mkdir, readFile, writeFile } from "node:fs/promises";
4
4
  import { dirname } from "node:path";
5
- import { pathToFileURL } from "node:url";
6
5
 
6
+ import { isCliEntry } from "./entry-guard.mjs";
7
7
  import { FALLBACK_CATALOG, readModelCatalog } from "./migrate-codex-config/catalog.mjs";
8
8
  import { configPaths } from "./migrate-codex-config/config-paths.mjs";
9
9
  import { removeStaleContext7PlaceholderMcpServer } from "./migrate-codex-config/context7-placeholder-guard.mjs";
10
10
  import { removeUnsupportedRootMultiAgentMode } from "./migrate-codex-config/multi-agent-mode-guard.mjs";
11
- import { forceDisableMultiAgentV2 } from "./migrate-codex-config/multi-agent-v2-guard.mjs";
11
+ import {
12
+ forceDisableMultiAgentV2,
13
+ resolveMultiAgentVersionFromConfig,
14
+ } from "./migrate-codex-config/multi-agent-v2-guard.mjs";
12
15
  import { ensureCodexReasoningConfig as applyReasoningProfile, readRootSettings } from "./migrate-codex-config/root-settings.mjs";
13
16
  import { readState, resolveStatePath, writeState } from "./migrate-codex-config/state.mjs";
14
17
  import { ensureSubagentConcurrencyLimit } from "./migrate-codex-config/subagent-limit-guard.mjs";
@@ -19,7 +22,12 @@ export function ensureCodexReasoningConfig(config, profile = FALLBACK_CATALOG.cu
19
22
  return applyReasoningProfile(config, profile);
20
23
  }
21
24
 
22
- export async function migrateCodexConfig({ env = process.env, cwd = process.cwd() } = {}) {
25
+ export async function migrateCodexConfig({
26
+ env = process.env,
27
+ cwd = process.cwd(),
28
+ sessionModel = null,
29
+ requireSessionModel = false,
30
+ } = {}) {
23
31
  const catalog = await readModelCatalog(env);
24
32
  const statePath = resolveStatePath(env);
25
33
  const state = await readState(statePath);
@@ -31,6 +39,9 @@ export async function migrateCodexConfig({ env = process.env, cwd = process.cwd(
31
39
  const result = await migrateConfigFile(configPath, {
32
40
  catalog,
33
41
  previousState: state.files?.[configPath],
42
+ env,
43
+ sessionModel,
44
+ requireSessionModel,
34
45
  });
35
46
  if (result.changed) changed.push(configPath);
36
47
  if (result.multiAgentModeChanged) modeChanged.push(configPath);
@@ -44,7 +55,16 @@ export async function migrateCodexConfig({ env = process.env, cwd = process.cwd(
44
55
  return { changed, modeChanged };
45
56
  }
46
57
 
47
- export async function migrateConfigFile(configPath, { catalog = FALLBACK_CATALOG, previousState } = {}) {
58
+ export async function migrateConfigFile(
59
+ configPath,
60
+ {
61
+ catalog = FALLBACK_CATALOG,
62
+ previousState,
63
+ env = process.env,
64
+ sessionModel = null,
65
+ requireSessionModel = false,
66
+ } = {},
67
+ ) {
48
68
  const before = await readConfig(configPath);
49
69
  const decision = shouldApplyCatalog(before, catalog, previousState);
50
70
 
@@ -56,7 +76,12 @@ export async function migrateConfigFile(configPath, { catalog = FALLBACK_CATALOG
56
76
  reasoningApplied = config !== before;
57
77
  }
58
78
 
59
- const afterMultiAgentGuard = forceDisableMultiAgentV2(config);
79
+ const multiAgentOptions = { env, sessionModel, requireSessionModel, configPath };
80
+ const multiAgentVersion = resolveMultiAgentVersionFromConfig(config, multiAgentOptions);
81
+ const afterMultiAgentGuard = forceDisableMultiAgentV2(config, {
82
+ ...multiAgentOptions,
83
+ multiAgentVersion,
84
+ });
60
85
  const multiAgentChanged = afterMultiAgentGuard !== config;
61
86
  if (multiAgentChanged) config = afterMultiAgentGuard;
62
87
 
@@ -68,7 +93,10 @@ export async function migrateConfigFile(configPath, { catalog = FALLBACK_CATALOG
68
93
  const context7PlaceholderChanged = afterContext7PlaceholderGuard !== config;
69
94
  if (context7PlaceholderChanged) config = afterContext7PlaceholderGuard;
70
95
 
71
- const afterSubagentLimit = ensureSubagentConcurrencyLimit(config);
96
+ const afterSubagentLimit = ensureSubagentConcurrencyLimit(config, {
97
+ ...multiAgentOptions,
98
+ multiAgentVersion,
99
+ });
72
100
  const subagentLimitChanged = afterSubagentLimit !== config;
73
101
  if (subagentLimitChanged) config = afterSubagentLimit;
74
102
 
@@ -117,7 +145,7 @@ async function readConfig(configPath) {
117
145
  }
118
146
  }
119
147
 
120
- if (process.argv[1] !== undefined && import.meta.url === pathToFileURL(process.argv[1]).href) {
148
+ if (isCliEntry(import.meta.url)) {
121
149
  migrateCodexConfig().catch((error) => {
122
150
  if (!(error instanceof Error)) throw error;
123
151
  process.exit(0);
@@ -3,8 +3,8 @@
3
3
  import { access, mkdir, readFile, writeFile } from "node:fs/promises";
4
4
  import { homedir } from "node:os";
5
5
  import { dirname, join } from "node:path";
6
- import { pathToFileURL } from "node:url";
7
6
 
7
+ import { isCliEntry } from "./entry-guard.mjs";
8
8
  import { addCodexCodegraphValues, hasOwn, isRecord, recordAt } from "./migrate-omo-sot/editor.mjs";
9
9
  import { parseJsonc } from "./migrate-omo-sot/jsonc.mjs";
10
10
  import { SCAFFOLD } from "./migrate-omo-sot/scaffold.mjs";
@@ -109,7 +109,7 @@ function isValidCodegraphValue(key, value) {
109
109
  return typeof value === "boolean";
110
110
  }
111
111
 
112
- if (process.argv[1] !== undefined && import.meta.url === pathToFileURL(process.argv[1]).href) {
112
+ if (isCliEntry(import.meta.url)) {
113
113
  migrateOmoSotConfig({ seed: process.argv.includes("--seed") })
114
114
  .then((result) => {
115
115
  for (const warning of result.warnings) process.stderr.write(`${warning}\n`);
@@ -1,9 +1,9 @@
1
1
  #!/usr/bin/env node
2
2
  import { access, readdir, readFile, writeFile } from "node:fs/promises";
3
3
  import { dirname, join } from "node:path";
4
- import { pathToFileURL } from "node:url";
5
4
  import { fileURLToPath } from "node:url";
6
5
 
6
+ import { isCliEntry } from "./entry-guard.mjs";
7
7
  import { formatLazyCodexHookStatusMessage, normalizeLazyCodexHookStatusLabel } from "./hook-status-message.mjs";
8
8
 
9
9
  const defaultRoot = dirname(dirname(fileURLToPath(import.meta.url)));
@@ -102,6 +102,6 @@ export async function syncHookStatusMessages(root = defaultRoot, options = {}) {
102
102
  }
103
103
  }
104
104
 
105
- if (process.argv[1] !== undefined && import.meta.url === pathToFileURL(process.argv[1]).href) {
105
+ if (isCliEntry(import.meta.url)) {
106
106
  await syncHookStatusMessages();
107
107
  }
@@ -1,7 +1,9 @@
1
1
  #!/usr/bin/env node
2
2
  import { cp, mkdir, readdir, readFile, rm, writeFile } from "node:fs/promises";
3
3
  import { dirname, join } from "node:path";
4
- import { fileURLToPath, pathToFileURL } from "node:url";
4
+ import { fileURLToPath } from "node:url";
5
+
6
+ import { isCliEntry } from "./entry-guard.mjs";
5
7
  import { sharedSkillsRootPath } from "@oh-my-opencode/shared-skills";
6
8
 
7
9
  const root = dirname(dirname(fileURLToPath(import.meta.url)));
@@ -36,7 +38,7 @@ function shouldCopySkillSource(source) {
36
38
 
37
39
  const opencodeOnlyOrchestrationPattern = /\b(?:call_omo_agent|background_output|team_[a-z_]+|task)\s*\(/;
38
40
 
39
- const codexHarnessToolCompatibility = `## Codex Harness Tool Compatibility
41
+ export const codexHarnessToolCompatibility = `## Codex Harness Tool Compatibility
40
42
 
41
43
  This skill may include examples copied from the OpenCode harness. In Codex, do not call OpenCode-only tools such as \`call_omo_agent(...)\`, \`task(...)\`, \`background_output(...)\`, or \`team_*(...)\` literally. Translate those examples to Codex native tools:
42
44
 
@@ -48,11 +50,11 @@ This skill may include examples copied from the OpenCode harness. In Codex, do n
48
50
  | \`task(subagent_type="oracle", ...)\` for final verification | \`multi_agent_v1.spawn_agent({"message":"TASK: act as a rigorous reviewer. ...","agent_type":"lazycodex-gate-reviewer","fork_context":false})\` |
49
51
  | \`task(category="...", ...)\` for implementation or QA | \`multi_agent_v1.spawn_agent({"message":"TASK: act as an implementation or QA worker. ...","fork_context":false})\` |
50
52
  | \`background_output(task_id="...")\` | \`multi_agent_v1.wait_agent(...)\` for mailbox signals |
51
- | \`team_*(...)\` | Use Codex native subagents via \`multi_agent_v1.spawn_agent\`, \`multi_agent_v1.send_input\`, \`multi_agent_v1.wait_agent\`, and \`multi_agent_v1.close_agent\` |
53
+ | \`team_*(...)\` | Use Codex native subagents via \`multi_agent_v1.spawn_agent\` and \`multi_agent_v1.wait_agent\`; use \`multi_agent_v1.send_input\` and \`multi_agent_v1.close_agent\` only when exposed in the active tools list |
52
54
 
53
55
  Role-specific behavior must be described in a self-contained \`message\`. Use \`fork_context: false\` to start the child with only the initial prompt (no parent history); use \`fork_context: true\` only when full parent history is truly required. Include any required conversation context, files, diffs, constraints, and requested skill names directly in the spawned agent's \`message\`. OMO installs these selectable agent roles into \`~/.codex/agents/\`: \`explorer\`, \`librarian\`, \`plan\`, \`momus\`, \`metis\`, \`lazycodex-code-reviewer\`, \`lazycodex-qa-executor\`, and \`lazycodex-gate-reviewer\` - pass the matching name as \`agent_type\` so the child gets that role's model and instructions. If the spawn tool exposes no \`agent_type\` parameter, omit it and describe the role inside \`message\`. If a code block below conflicts with this section, this section wins.
54
56
 
55
- On \`multi_agent_v2\` sessions the same \`agent_type\` applies (the OMO installer exposes it) with \`fork_turns\` instead of \`fork_context\`. If a code block below conflicts with this section, this section wins.
57
+ Codex exposes ONE of two subagent tool surfaces per session; check your own tool list and route accordingly. If \`multi_agent_v1.*\` tools exist, use the table above as written. If instead a flat \`spawn_agent\` with a required \`task_name\` exists (\`multi_agent_v2\`), rewrite every \`multi_agent_v1.*\` example: \`multi_agent_v1.spawn_agent({...,"fork_context":false})\` becomes \`spawn_agent({"task_name":"<lowercase_digits_underscores>","message":...,"agent_type":...,"fork_turns":"none"})\` (\`"all"\` only when full parent history is truly required); \`send_input\` becomes \`send_message\`; do not call \`close_agent\`/\`resume_agent\` (finished agents end on their own; \`followup_task\` re-tasks one, \`interrupt_agent\` stops one); \`wait_agent\` takes only \`timeout_ms\` and returns on any child mailbox activity. \`agent_type\` works the same on both surfaces. If a code block below conflicts with this section, this section wins.
56
58
 
57
59
  When translating \`load_skills=[...]\`, include the requested skill names in the spawned agent's \`message\`. If a code block below conflicts with this section, this section wins.
58
60
 
@@ -263,6 +265,6 @@ async function syncSkills() {
263
265
  }
264
266
  }
265
267
 
266
- if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) {
268
+ if (isCliEntry(import.meta.url)) {
267
269
  await syncSkills();
268
270
  }
@@ -1,7 +1,9 @@
1
1
  #!/usr/bin/env node
2
2
  import { readdir, readFile, writeFile } from "node:fs/promises";
3
3
  import { dirname, join } from "node:path";
4
- import { fileURLToPath, pathToFileURL } from "node:url";
4
+ import { fileURLToPath } from "node:url";
5
+
6
+ import { isCliEntry } from "./entry-guard.mjs";
5
7
 
6
8
  const scriptDir = dirname(fileURLToPath(import.meta.url));
7
9
  const defaultPluginRoot = dirname(scriptDir);
@@ -88,7 +90,7 @@ export async function syncVersion(options = {}) {
88
90
  return { version, targets, changed };
89
91
  }
90
92
 
91
- if (process.argv[1] !== undefined && import.meta.url === pathToFileURL(process.argv[1]).href) {
93
+ if (isCliEntry(import.meta.url)) {
92
94
  const result = await syncVersion();
93
95
  console.log(`Synced OMO Codex manifests to version ${result.version} (${result.changed.length} updated)`);
94
96
  }
@@ -14,11 +14,11 @@ This skill may include examples copied from the OpenCode harness. In Codex, do n
14
14
  | `task(subagent_type="oracle", ...)` for final verification | `multi_agent_v1.spawn_agent({"message":"TASK: act as a rigorous reviewer. ...","agent_type":"lazycodex-gate-reviewer","fork_context":false})` |
15
15
  | `task(category="...", ...)` for implementation or QA | `multi_agent_v1.spawn_agent({"message":"TASK: act as an implementation or QA worker. ...","fork_context":false})` |
16
16
  | `background_output(task_id="...")` | `multi_agent_v1.wait_agent(...)` for mailbox signals |
17
- | `team_*(...)` | Use Codex native subagents via `multi_agent_v1.spawn_agent`, `multi_agent_v1.send_input`, `multi_agent_v1.wait_agent`, and `multi_agent_v1.close_agent` |
17
+ | `team_*(...)` | Use Codex native subagents via `multi_agent_v1.spawn_agent` and `multi_agent_v1.wait_agent`; use `multi_agent_v1.send_input` and `multi_agent_v1.close_agent` only when exposed in the active tools list |
18
18
 
19
19
  Role-specific behavior must be described in a self-contained `message`. Use `fork_context: false` to start the child with only the initial prompt (no parent history); use `fork_context: true` only when full parent history is truly required. Include any required conversation context, files, diffs, constraints, and requested skill names directly in the spawned agent's `message`. OMO installs these selectable agent roles into `~/.codex/agents/`: `explorer`, `librarian`, `plan`, `momus`, `metis`, `lazycodex-code-reviewer`, `lazycodex-qa-executor`, and `lazycodex-gate-reviewer` - pass the matching name as `agent_type` so the child gets that role's model and instructions. If the spawn tool exposes no `agent_type` parameter, omit it and describe the role inside `message`. If a code block below conflicts with this section, this section wins.
20
20
 
21
- On `multi_agent_v2` sessions the same `agent_type` applies (the OMO installer exposes it) with `fork_turns` instead of `fork_context`. If a code block below conflicts with this section, this section wins.
21
+ Codex exposes ONE of two subagent tool surfaces per session; check your own tool list and route accordingly. If `multi_agent_v1.*` tools exist, use the table above as written. If instead a flat `spawn_agent` with a required `task_name` exists (`multi_agent_v2`), rewrite every `multi_agent_v1.*` example: `multi_agent_v1.spawn_agent({...,"fork_context":false})` becomes `spawn_agent({"task_name":"<lowercase_digits_underscores>","message":...,"agent_type":...,"fork_turns":"none"})` (`"all"` only when full parent history is truly required); `send_input` becomes `send_message`; do not call `close_agent`/`resume_agent` (finished agents end on their own; `followup_task` re-tasks one, `interrupt_agent` stops one); `wait_agent` takes only `timeout_ms` and returns on any child mailbox activity. `agent_type` works the same on both surfaces. If a code block below conflicts with this section, this section wins.
22
22
 
23
23
  When translating `load_skills=[...]`, include the requested skill names in the spawned agent's `message`. If a code block below conflicts with this section, this section wins.
24
24
 
@@ -15,11 +15,11 @@ This skill may include examples copied from the OpenCode harness. In Codex, do n
15
15
  | `task(subagent_type="oracle", ...)` for final verification | `multi_agent_v1.spawn_agent({"message":"TASK: act as a rigorous reviewer. ...","agent_type":"lazycodex-gate-reviewer","fork_context":false})` |
16
16
  | `task(category="...", ...)` for implementation or QA | `multi_agent_v1.spawn_agent({"message":"TASK: act as an implementation or QA worker. ...","fork_context":false})` |
17
17
  | `background_output(task_id="...")` | `multi_agent_v1.wait_agent(...)` for mailbox signals |
18
- | `team_*(...)` | Use Codex native subagents via `multi_agent_v1.spawn_agent`, `multi_agent_v1.send_input`, `multi_agent_v1.wait_agent`, and `multi_agent_v1.close_agent` |
18
+ | `team_*(...)` | Use Codex native subagents via `multi_agent_v1.spawn_agent` and `multi_agent_v1.wait_agent`; use `multi_agent_v1.send_input` and `multi_agent_v1.close_agent` only when exposed in the active tools list |
19
19
 
20
20
  Role-specific behavior must be described in a self-contained `message`. Use `fork_context: false` to start the child with only the initial prompt (no parent history); use `fork_context: true` only when full parent history is truly required. Include any required conversation context, files, diffs, constraints, and requested skill names directly in the spawned agent's `message`. OMO installs these selectable agent roles into `~/.codex/agents/`: `explorer`, `librarian`, `plan`, `momus`, `metis`, `lazycodex-code-reviewer`, `lazycodex-qa-executor`, and `lazycodex-gate-reviewer` - pass the matching name as `agent_type` so the child gets that role's model and instructions. If the spawn tool exposes no `agent_type` parameter, omit it and describe the role inside `message`. If a code block below conflicts with this section, this section wins.
21
21
 
22
- On `multi_agent_v2` sessions the same `agent_type` applies (the OMO installer exposes it) with `fork_turns` instead of `fork_context`. If a code block below conflicts with this section, this section wins.
22
+ Codex exposes ONE of two subagent tool surfaces per session; check your own tool list and route accordingly. If `multi_agent_v1.*` tools exist, use the table above as written. If instead a flat `spawn_agent` with a required `task_name` exists (`multi_agent_v2`), rewrite every `multi_agent_v1.*` example: `multi_agent_v1.spawn_agent({...,"fork_context":false})` becomes `spawn_agent({"task_name":"<lowercase_digits_underscores>","message":...,"agent_type":...,"fork_turns":"none"})` (`"all"` only when full parent history is truly required); `send_input` becomes `send_message`; do not call `close_agent`/`resume_agent` (finished agents end on their own; `followup_task` re-tasks one, `interrupt_agent` stops one); `wait_agent` takes only `timeout_ms` and returns on any child mailbox activity. `agent_type` works the same on both surfaces. If a code block below conflicts with this section, this section wins.
23
23
 
24
24
  When translating `load_skills=[...]`, include the requested skill names in the spawned agent's `message`. If a code block below conflicts with this section, this section wins.
25
25
 
@@ -15,11 +15,11 @@ This skill may include examples copied from the OpenCode harness. In Codex, do n
15
15
  | `task(subagent_type="oracle", ...)` for final verification | `multi_agent_v1.spawn_agent({"message":"TASK: act as a rigorous reviewer. ...","agent_type":"lazycodex-gate-reviewer","fork_context":false})` |
16
16
  | `task(category="...", ...)` for implementation or QA | `multi_agent_v1.spawn_agent({"message":"TASK: act as an implementation or QA worker. ...","fork_context":false})` |
17
17
  | `background_output(task_id="...")` | `multi_agent_v1.wait_agent(...)` for mailbox signals |
18
- | `team_*(...)` | Use Codex native subagents via `multi_agent_v1.spawn_agent`, `multi_agent_v1.send_input`, `multi_agent_v1.wait_agent`, and `multi_agent_v1.close_agent` |
18
+ | `team_*(...)` | Use Codex native subagents via `multi_agent_v1.spawn_agent` and `multi_agent_v1.wait_agent`; use `multi_agent_v1.send_input` and `multi_agent_v1.close_agent` only when exposed in the active tools list |
19
19
 
20
20
  Role-specific behavior must be described in a self-contained `message`. Use `fork_context: false` to start the child with only the initial prompt (no parent history); use `fork_context: true` only when full parent history is truly required. Include any required conversation context, files, diffs, constraints, and requested skill names directly in the spawned agent's `message`. OMO installs these selectable agent roles into `~/.codex/agents/`: `explorer`, `librarian`, `plan`, `momus`, `metis`, `lazycodex-code-reviewer`, `lazycodex-qa-executor`, and `lazycodex-gate-reviewer` - pass the matching name as `agent_type` so the child gets that role's model and instructions. If the spawn tool exposes no `agent_type` parameter, omit it and describe the role inside `message`. If a code block below conflicts with this section, this section wins.
21
21
 
22
- On `multi_agent_v2` sessions the same `agent_type` applies (the OMO installer exposes it) with `fork_turns` instead of `fork_context`. If a code block below conflicts with this section, this section wins.
22
+ Codex exposes ONE of two subagent tool surfaces per session; check your own tool list and route accordingly. If `multi_agent_v1.*` tools exist, use the table above as written. If instead a flat `spawn_agent` with a required `task_name` exists (`multi_agent_v2`), rewrite every `multi_agent_v1.*` example: `multi_agent_v1.spawn_agent({...,"fork_context":false})` becomes `spawn_agent({"task_name":"<lowercase_digits_underscores>","message":...,"agent_type":...,"fork_turns":"none"})` (`"all"` only when full parent history is truly required); `send_input` becomes `send_message`; do not call `close_agent`/`resume_agent` (finished agents end on their own; `followup_task` re-tasks one, `interrupt_agent` stops one); `wait_agent` takes only `timeout_ms` and returns on any child mailbox activity. `agent_type` works the same on both surfaces. If a code block below conflicts with this section, this section wins.
23
23
 
24
24
  When translating `load_skills=[...]`, include the requested skill names in the spawned agent's `message`. If a code block below conflicts with this section, this section wins.
25
25