agentloom 0.1.12 → 0.1.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -202,13 +202,24 @@ claude:
202
202
  model: sonnet
203
203
  codex:
204
204
  model: gpt-5-codex
205
- reasoningEffort: medium
206
- webSearch: true
205
+ model_reasoning_effort: medium
206
+ web_search: true
207
207
  ---
208
208
 
209
209
  You are a strict reviewer...
210
210
  ```
211
211
 
212
+ For Codex, provider-native config keys are passed through to the generated
213
+ `.codex/agents/<role>.toml` as a thin layer. Prefer Codex's native snake_case
214
+ keys such as `approval_policy`, `approvals_reviewer`, or `web_search`. Existing
215
+ camelCase aliases like `reasoningEffort` are still normalized for
216
+ backward-compatibility.
217
+
218
+ Current `codex-cli 0.121.0` exposes spawn-time controls such as `fork_context` and
219
+ `fork_turns` only on the `spawn_agent` tool path, not in static role/config
220
+ files, so Codex itself will warn or ignore them if you place them under
221
+ `codex:` in canonical frontmatter today.
222
+
212
223
  ## Command schema
213
224
 
214
225
  Canonical commands are markdown files. Frontmatter is optional. When present,
@@ -0,0 +1,3 @@
1
+ export declare function normalizeCodexConfigForToml(providerConfig: Record<string, unknown>): Record<string, unknown>;
2
+ export declare function cloneCodexProviderConfig(providerConfig: Record<string, unknown>): Record<string, unknown>;
3
+ export declare function stripManagedCodexInstructionKeys(providerConfig: Record<string, unknown>): Record<string, unknown>;
@@ -0,0 +1,72 @@
1
+ import { isObject } from "./fs.js";
2
+ const MANAGED_INSTRUCTION_KEYS = new Set([
3
+ "developerInstructions",
4
+ "developer_instructions",
5
+ "modelInstructionsFile",
6
+ "model_instructions_file",
7
+ ]);
8
+ const LEGACY_CODEX_ALIASES = {
9
+ approvalPolicy: "approval_policy",
10
+ reasoningEffort: "model_reasoning_effort",
11
+ reasoningSummary: "model_reasoning_summary",
12
+ sandboxMode: "sandbox_mode",
13
+ verbosity: "model_verbosity",
14
+ webSearch: "web_search",
15
+ };
16
+ export function normalizeCodexConfigForToml(providerConfig) {
17
+ const normalized = normalizeCodexValueForToml(providerConfig);
18
+ if (!isObject(normalized)) {
19
+ return {};
20
+ }
21
+ for (const key of MANAGED_INSTRUCTION_KEYS) {
22
+ delete normalized[key];
23
+ }
24
+ return normalized;
25
+ }
26
+ export function cloneCodexProviderConfig(providerConfig) {
27
+ const cloned = cloneCodexValue(providerConfig);
28
+ if (!isObject(cloned)) {
29
+ return {};
30
+ }
31
+ return cloned;
32
+ }
33
+ export function stripManagedCodexInstructionKeys(providerConfig) {
34
+ const cleaned = cloneCodexProviderConfig(providerConfig);
35
+ for (const key of MANAGED_INSTRUCTION_KEYS) {
36
+ delete cleaned[key];
37
+ }
38
+ return cleaned;
39
+ }
40
+ function normalizeCodexValueForToml(value) {
41
+ if (Array.isArray(value)) {
42
+ return value.map((entry) => normalizeCodexValueForToml(entry));
43
+ }
44
+ if (!isObject(value)) {
45
+ return value;
46
+ }
47
+ return Object.fromEntries(Object.entries(value).map(([key, entry]) => [
48
+ toSnakeCase(key),
49
+ normalizeCodexValueForToml(entry),
50
+ ]));
51
+ }
52
+ function cloneCodexValue(value) {
53
+ if (Array.isArray(value)) {
54
+ return value.map((entry) => cloneCodexValue(entry));
55
+ }
56
+ if (!isObject(value)) {
57
+ return value;
58
+ }
59
+ return Object.fromEntries(Object.entries(value).map(([key, entry]) => [key, cloneCodexValue(entry)]));
60
+ }
61
+ function toSnakeCase(key) {
62
+ if (Object.prototype.hasOwnProperty.call(LEGACY_CODEX_ALIASES, key)) {
63
+ return LEGACY_CODEX_ALIASES[key];
64
+ }
65
+ if (!/[A-Z]/.test(key)) {
66
+ return key;
67
+ }
68
+ return key
69
+ .replace(/([A-Z]+)([A-Z][a-z])/g, "$1_$2")
70
+ .replace(/([a-z0-9])([A-Z])/g, "$1_$2")
71
+ .toLowerCase();
72
+ }
@@ -6,6 +6,7 @@ import TOML from "@iarna/toml";
6
6
  import matter from "gray-matter";
7
7
  import YAML from "yaml";
8
8
  import { buildAgentMarkdown, parseAgentsDir } from "./agents.js";
9
+ import { cloneCodexProviderConfig, stripManagedCodexInstructionKeys, } from "./codex.js";
9
10
  import { normalizeCommandArgumentsForCanonical, parseCommandsDir, } from "./commands.js";
10
11
  import { ensureDir, isObject, readJsonIfExists, slugify } from "./fs.js";
11
12
  import { readLockfile, writeLockfile } from "./lockfile.js";
@@ -394,33 +395,7 @@ function readCodexProviderAgents(paths) {
394
395
  const description = isObject(roleEntry) && typeof roleEntry.description === "string"
395
396
  ? roleEntry.description.trim()
396
397
  : roleName;
397
- const providerConfig = {};
398
- if (typeof roleToml.model === "string") {
399
- providerConfig.model = roleToml.model;
400
- }
401
- if (typeof roleToml.model_reasoning_effort === "string") {
402
- providerConfig.reasoningEffort = roleToml.model_reasoning_effort;
403
- }
404
- if (typeof roleToml.model_reasoning_summary === "string") {
405
- providerConfig.reasoningSummary = roleToml.model_reasoning_summary;
406
- }
407
- if (typeof roleToml.model_verbosity === "string") {
408
- providerConfig.verbosity = roleToml.model_verbosity;
409
- }
410
- if (typeof roleToml.approval_policy === "string") {
411
- providerConfig.approvalPolicy = roleToml.approval_policy;
412
- }
413
- if (typeof roleToml.sandbox_mode === "string") {
414
- providerConfig.sandboxMode = roleToml.sandbox_mode;
415
- }
416
- if (typeof roleToml.web_search === "boolean") {
417
- providerConfig.webSearch = roleToml.web_search;
418
- }
419
- if (typeof providerConfig.webSearch !== "boolean" &&
420
- isObject(roleToml.tools) &&
421
- typeof roleToml.tools.web_search === "boolean") {
422
- providerConfig.webSearch = roleToml.tools.web_search;
423
- }
398
+ const providerConfig = stripManagedCodexInstructionKeys(cloneCodexProviderConfig(roleToml));
424
399
  records.push({
425
400
  provider: "codex",
426
401
  sourcePath: roleTomlPath,
@@ -6,6 +6,7 @@ import YAML from "yaml";
6
6
  import { ALL_PROVIDERS } from "../types.js";
7
7
  import { getProviderConfig, isProviderEnabled, parseAgentsDir, } from "../core/agents.js";
8
8
  import { parseCommandsDir, renderCommandForProvider, } from "../core/commands.js";
9
+ import { normalizeCodexConfigForToml } from "../core/codex.js";
9
10
  import { parseRulesDir, renderRuleForCursor, upsertManagedRuleBlocks, } from "../core/rules.js";
10
11
  import { ensureDir, isObject, readJsonIfExists, readTextIfExists, relativePosix, removeFileIfExists, slugify, toPosixPath, writeJsonAtomic, writeTextAtomic, } from "../core/fs.js";
11
12
  import { readManifest, writeManifest } from "../core/manifest.js";
@@ -869,33 +870,15 @@ function resolveCodexDeveloperInstructions(agentBody, providerConfig) {
869
870
  providerConfig.developerInstructions.trim() !== "") {
870
871
  return providerConfig.developerInstructions.trim();
871
872
  }
873
+ if (typeof providerConfig.developer_instructions === "string" &&
874
+ providerConfig.developer_instructions.trim() !== "") {
875
+ return providerConfig.developer_instructions.trim();
876
+ }
872
877
  return agentBody.trimStart().trimEnd();
873
878
  }
874
879
  function buildCodexRoleToml(developerInstructions, providerConfig) {
875
- const roleToml = {
876
- developer_instructions: developerInstructions,
877
- };
878
- if (typeof providerConfig.model === "string") {
879
- roleToml.model = providerConfig.model;
880
- }
881
- if (typeof providerConfig.reasoningEffort === "string") {
882
- roleToml.model_reasoning_effort = providerConfig.reasoningEffort;
883
- }
884
- if (typeof providerConfig.reasoningSummary === "string") {
885
- roleToml.model_reasoning_summary = providerConfig.reasoningSummary;
886
- }
887
- if (typeof providerConfig.verbosity === "string") {
888
- roleToml.model_verbosity = providerConfig.verbosity;
889
- }
890
- if (typeof providerConfig.approvalPolicy === "string") {
891
- roleToml.approval_policy = providerConfig.approvalPolicy;
892
- }
893
- if (typeof providerConfig.sandboxMode === "string") {
894
- roleToml.sandbox_mode = providerConfig.sandboxMode;
895
- }
896
- if (typeof providerConfig.webSearch === "boolean") {
897
- roleToml.web_search = providerConfig.webSearch;
898
- }
880
+ const roleToml = normalizeCodexConfigForToml(providerConfig);
881
+ roleToml.developer_instructions = developerInstructions;
899
882
  return roleToml;
900
883
  }
901
884
  function mapMcpServers(servers, allowedKeys) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agentloom",
3
- "version": "0.1.12",
3
+ "version": "0.1.13",
4
4
  "description": "Unified agent and MCP sync CLI for multi-provider AI tooling",
5
5
  "type": "module",
6
6
  "bin": {