@parall/codex-agent 1.41.0 → 1.42.0

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/dist/config.d.ts CHANGED
@@ -42,10 +42,18 @@ export declare function resolveCodexAgentConfig(env?: NodeJS.ProcessEnv): CodexA
42
42
  */
43
43
  export declare function normalizeSandbox(value: string): string;
44
44
  /**
45
- * Normalise an approval policy string to the kebab-case form the app-server
46
- * JSON-RPC enum expects (`never` / `on-request` / `on-failure` /
47
- * `unless-trusted`). Mirrors the sandbox enum serde rename_all kebab-case
48
- * applies across app-server protocol enums.
45
+ * Normalise an approval policy string to the enum form the app-server
46
+ * JSON-RPC surface accepts. Empirically verified against both pinned CLI
47
+ * generations: 0.144.x accepts `untrusted` / `on-request` / `granular` /
48
+ * `never`; 0.133.0 additionally accepted `on-failure`. Two legacy inputs are
49
+ * translated rather than passed through, because the server rejects the whole
50
+ * thread/start with "unknown variant" otherwise:
51
+ *
52
+ * - `on-failure` → `on-request` (removed from the enum in 0.144; the closest
53
+ * surviving escalation policy).
54
+ * - `unless-trusted` → `untrusted` (the wire constant was never
55
+ * `unless-trusted` on either generation — accepting-but-forwarding it made
56
+ * every thread/start fail for configs that set it).
49
57
  */
50
58
  export declare function normalizeApprovalPolicy(value: string): string;
51
59
  export declare function resolveWsUrl(apiUrl: string, explicitWsUrl?: string, swimlaneName?: string): string;
@@ -1 +1 @@
1
- {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAIA,MAAM,MAAM,gBAAgB,GAAG;IAC7B,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;IACrB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB;;;;;;;OAOG;IACH,OAAO,EAAE,MAAM,CAAC;IAChB;;;;;OAKG;IACH,cAAc,EAAE,MAAM,CAAC;IACvB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB,CAAC;AAcF,wBAAgB,uBAAuB,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,gBAAgB,CAyC9F;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAUtD;AAED;;;;;GAKG;AACH,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAW7D;AAED,wBAAgB,YAAY,CAC1B,MAAM,EAAE,MAAM,EACd,aAAa,CAAC,EAAE,MAAM,EACtB,YAAY,CAAC,EAAE,MAAM,GACpB,MAAM,CAMR;AAED,wBAAgB,oBAAoB,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAEhE;AAED,wBAAgB,8BAA8B,CAAC,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,MAAM,CAG3F;AAED,wBAAgB,yBAAyB,CAAC,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,MAAM,CAGtF;AAED;;;;;;GAMG;AACH,wBAAgB,sBAAsB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAE/D;AAED,wBAAgB,wBAAwB,CAAC,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,MAAM,CAGrF"}
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAIA,MAAM,MAAM,gBAAgB,GAAG;IAC7B,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;IACrB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB;;;;;;;OAOG;IACH,OAAO,EAAE,MAAM,CAAC;IAChB;;;;;OAKG;IACH,cAAc,EAAE,MAAM,CAAC;IACvB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB,CAAC;AAcF,wBAAgB,uBAAuB,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,gBAAgB,CAyC9F;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAUtD;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAa7D;AAED,wBAAgB,YAAY,CAC1B,MAAM,EAAE,MAAM,EACd,aAAa,CAAC,EAAE,MAAM,EACtB,YAAY,CAAC,EAAE,MAAM,GACpB,MAAM,CAMR;AAED,wBAAgB,oBAAoB,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAEhE;AAED,wBAAgB,8BAA8B,CAAC,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,MAAM,CAG3F;AAED,wBAAgB,yBAAyB,CAAC,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,MAAM,CAGtF;AAED;;;;;;GAMG;AACH,wBAAgB,sBAAsB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAE/D;AAED,wBAAgB,wBAAwB,CAAC,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,MAAM,CAGrF"}
package/dist/config.js CHANGED
@@ -69,20 +69,30 @@ export function normalizeSandbox(value) {
69
69
  return map[value.toLowerCase()] ?? value;
70
70
  }
71
71
  /**
72
- * Normalise an approval policy string to the kebab-case form the app-server
73
- * JSON-RPC enum expects (`never` / `on-request` / `on-failure` /
74
- * `unless-trusted`). Mirrors the sandbox enum serde rename_all kebab-case
75
- * applies across app-server protocol enums.
72
+ * Normalise an approval policy string to the enum form the app-server
73
+ * JSON-RPC surface accepts. Empirically verified against both pinned CLI
74
+ * generations: 0.144.x accepts `untrusted` / `on-request` / `granular` /
75
+ * `never`; 0.133.0 additionally accepted `on-failure`. Two legacy inputs are
76
+ * translated rather than passed through, because the server rejects the whole
77
+ * thread/start with "unknown variant" otherwise:
78
+ *
79
+ * - `on-failure` → `on-request` (removed from the enum in 0.144; the closest
80
+ * surviving escalation policy).
81
+ * - `unless-trusted` → `untrusted` (the wire constant was never
82
+ * `unless-trusted` on either generation — accepting-but-forwarding it made
83
+ * every thread/start fail for configs that set it).
76
84
  */
77
85
  export function normalizeApprovalPolicy(value) {
78
86
  const map = {
79
87
  never: 'never',
80
88
  'on-request': 'on-request',
81
89
  onrequest: 'on-request',
82
- 'unless-trusted': 'unless-trusted',
83
- unlesstrusted: 'unless-trusted',
84
- 'on-failure': 'on-failure',
85
- onfailure: 'on-failure',
90
+ untrusted: 'untrusted',
91
+ 'unless-trusted': 'untrusted',
92
+ unlesstrusted: 'untrusted',
93
+ granular: 'granular',
94
+ 'on-failure': 'on-request',
95
+ onfailure: 'on-request',
86
96
  };
87
97
  return map[value.toLowerCase()] ?? value;
88
98
  }
@@ -1 +1 @@
1
- {"version":3,"file":"dispatch.d.ts","sourceRoot":"","sources":["../src/dispatch.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EACV,eAAe,EACf,eAAe,EACf,YAAY,EACZ,QAAQ,EACR,iBAAiB,EACjB,aAAa,EACb,YAAY,EACb,MAAM,oBAAoB,CAAC;AAC5B,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAIpD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,sBAAsB,CAAC;AAEhE,KAAK,4BAA4B,GAAG,IAAI,CACtC,gBAAgB,EACd,gBAAgB,GAChB,UAAU,GACV,WAAW,GACX,OAAO,GACP,iBAAiB,GACjB,SAAS,GACT,cAAc,CACjB,GAAG;IACF,cAAc,EAAE,mBAAmB,CAAC;IACpC,GAAG,CAAC,EAAE,aAAa,CAAC;IACpB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,iBAAiB,CAAC,EAAE,OAAO,CAAC;CAC7B,CAAC;AAqCF,qBAAa,qBAAsB,YAAW,eAAe;IA+B/C,OAAO,CAAC,QAAQ,CAAC,IAAI;IA9BjC,OAAO,CAAC,MAAM,CAAmC;IACjD,OAAO,CAAC,IAAI,CAA+C;IAC3D,OAAO,CAAC,WAAW,CAAS;IAC5B,OAAO,CAAC,YAAY,CAA8B;IAClD,OAAO,CAAC,QAAQ,CAAC,WAAW,CAA+B;IAC3D,OAAO,CAAC,QAAQ,CAAC,aAAa,CAA6B;IAC3D,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAA6B;IAC/D,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAqB;IACtD,OAAO,CAAC,QAAQ,CAAS;IAEzB;;;;;;;OAOG;IACH,OAAO,CAAC,aAAa;gBAYQ,IAAI,EAAE,4BAA4B;IAE/D,YAAY,CAAC,MAAM,EAAE;QAAE,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,eAAe,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,GAAG,IAAI;IAMhF,qBAAqB,CAAC,UAAU,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAqB/E,aAAa,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI;IAUvC,oBAAoB,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO;IAI1C,QAAQ,CAAC,EACd,KAAK,EACL,YAAY,EACZ,UAAU,EACV,OAAO,GACR,EAAE,YAAY,GAAG,aAAa,CAAC,YAAY,CAAC;IA0O7C,cAAc,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;IAMjD,WAAW,CAAC,EAAE,UAAU,EAAE,gBAAgB,EAAE,EAAE,QAAQ,GAAG,OAAO,CAAC,iBAAiB,GAAG,IAAI,CAAC;IA+BhG,WAAW,CAAC,EAAE,IAAI,EAAE,EAAE,eAAe;IAIrC,OAAO,CAAC,cAAc;IAKhB,IAAI;YAiBI,aAAa;YA2Bb,OAAO;IAuFrB,OAAO,CAAC,qBAAqB;YAmCf,UAAU;IA2CxB,OAAO,CAAC,iBAAiB;CA2B1B"}
1
+ {"version":3,"file":"dispatch.d.ts","sourceRoot":"","sources":["../src/dispatch.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EACV,eAAe,EACf,eAAe,EACf,YAAY,EACZ,QAAQ,EACR,iBAAiB,EACjB,aAAa,EACb,YAAY,EACb,MAAM,oBAAoB,CAAC;AAC5B,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAIpD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,sBAAsB,CAAC;AAEhE,KAAK,4BAA4B,GAAG,IAAI,CACtC,gBAAgB,EACd,gBAAgB,GAChB,UAAU,GACV,WAAW,GACX,OAAO,GACP,iBAAiB,GACjB,SAAS,GACT,cAAc,CACjB,GAAG;IACF,cAAc,EAAE,mBAAmB,CAAC;IACpC,GAAG,CAAC,EAAE,aAAa,CAAC;IACpB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,iBAAiB,CAAC,EAAE,OAAO,CAAC;CAC7B,CAAC;AAqCF,qBAAa,qBAAsB,YAAW,eAAe;IA+B/C,OAAO,CAAC,QAAQ,CAAC,IAAI;IA9BjC,OAAO,CAAC,MAAM,CAAmC;IACjD,OAAO,CAAC,IAAI,CAA+C;IAC3D,OAAO,CAAC,WAAW,CAAS;IAC5B,OAAO,CAAC,YAAY,CAA8B;IAClD,OAAO,CAAC,QAAQ,CAAC,WAAW,CAA+B;IAC3D,OAAO,CAAC,QAAQ,CAAC,aAAa,CAA6B;IAC3D,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAA6B;IAC/D,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAqB;IACtD,OAAO,CAAC,QAAQ,CAAS;IAEzB;;;;;;;OAOG;IACH,OAAO,CAAC,aAAa;gBAYQ,IAAI,EAAE,4BAA4B;IAE/D,YAAY,CAAC,MAAM,EAAE;QAAE,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,eAAe,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,GAAG,IAAI;IAMhF,qBAAqB,CAAC,UAAU,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAqB/E,aAAa,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI;IAUvC,oBAAoB,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO;IAI1C,QAAQ,CAAC,EACd,KAAK,EACL,YAAY,EACZ,UAAU,EACV,OAAO,GACR,EAAE,YAAY,GAAG,aAAa,CAAC,YAAY,CAAC;IA6P7C,cAAc,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;IAMjD,WAAW,CAAC,EAAE,UAAU,EAAE,gBAAgB,EAAE,EAAE,QAAQ,GAAG,OAAO,CAAC,iBAAiB,GAAG,IAAI,CAAC;IAgChG,WAAW,CAAC,EAAE,IAAI,EAAE,EAAE,eAAe;IAIrC,OAAO,CAAC,cAAc;IAKhB,IAAI;YAiBI,aAAa;YA2Bb,OAAO;IAuFrB,OAAO,CAAC,qBAAqB;YAmCf,UAAU;IA6CxB,OAAO,CAAC,iBAAiB;CA2B1B"}
package/dist/dispatch.js CHANGED
@@ -215,10 +215,32 @@ export class CodexAppServerAdapter {
215
215
  // this contract, we want a hard error surfaced to the operator, not a
216
216
  // silent fallback that hides real protocol drift.
217
217
  const turnInput = buildTurnInput(preparedBody, preparedImages);
218
- const startTurn = (targetThreadId) => client.sendRequest('turn/start', {
219
- threadId: targetThreadId,
220
- input: turnInput,
221
- });
218
+ // Per-turn overrides, first-class on both supported CLI generations
219
+ // (0.133 / 0.144) with "for this turn and subsequent turns" semantics.
220
+ // Sending the CURRENT config on every turn is what makes
221
+ // agent_config.update (model / thinking-effort hot updates) take
222
+ // effect on the next turn: thread-level config only applies at
223
+ // thread/start-resume, which an already-open thread never re-runs,
224
+ // and a prior turn's override would otherwise stay live for the rest
225
+ // of the subprocess lifetime.
226
+ //
227
+ // The fields are only sent when a value is configured: on the wire
228
+ // they are plain serde Options, so JSON null is indistinguishable
229
+ // from omission — there is no "reset to default" encoding. CLEARING
230
+ // a previously-set pin therefore keeps the old sticky value until
231
+ // the subprocess restarts (pod restart / redeploy / idle reap);
232
+ // an explicit restart-on-clear path is a tracked follow-up.
233
+ const startTurn = (targetThreadId) => {
234
+ const turnParams = {
235
+ threadId: targetThreadId,
236
+ input: turnInput,
237
+ };
238
+ if (this.opts.model)
239
+ turnParams.model = this.opts.model;
240
+ if (this.opts.reasoningEffort)
241
+ turnParams.effort = this.opts.reasoningEffort;
242
+ return client.sendRequest('turn/start', turnParams);
243
+ };
222
244
  // Start the turn. If the stored threadId is dead, we retry once with a fresh thread.
223
245
  let turnStartResult;
224
246
  try {
@@ -360,7 +382,8 @@ export class CodexAppServerAdapter {
360
382
  if (this.opts.model)
361
383
  forkParams.model = this.opts.model;
362
384
  if (this.opts.reasoningEffort) {
363
- forkParams.config = { modelReasoningEffort: this.opts.reasoningEffort };
385
+ // Raw config.toml key see openThread for the snake_case rationale.
386
+ forkParams.config = { model_reasoning_effort: this.opts.reasoningEffort };
364
387
  }
365
388
  const result = await client.sendRequest('thread/fork', forkParams);
366
389
  const forkedThreadId = extractThreadId(result);
@@ -555,12 +578,14 @@ export class CodexAppServerAdapter {
555
578
  if (this.opts.model)
556
579
  commonParams.model = this.opts.model;
557
580
  if (this.opts.reasoningEffort) {
558
- // The app-server JSON-RPC surface uses camelCase for overrides
559
- // `modelReasoningEffort` parallels `approvalPolicy` / `sandbox` on the
560
- // top-level params. The corresponding config.toml key is
561
- // `model_reasoning_effort` (snake_case), but the nested `config` on
562
- // `thread/start` / `thread/resume` takes the camelCase form.
563
- commonParams.config = { modelReasoningEffort: this.opts.reasoningEffort };
581
+ // The nested `config` object is raw config.toml overrides and keeps the
582
+ // TOML key names `model_reasoning_effort` (snake_case) — unlike the
583
+ // typed top-level params (`approvalPolicy` / `sandbox`), which are
584
+ // camelCase. Empirically verified against both pinned CLI generations
585
+ // (0.133.0 and 0.144.1): the camelCase key is silently ignored
586
+ // (thread/start echoes reasoningEffort: null), the snake_case key is
587
+ // applied and echoed back.
588
+ commonParams.config = { model_reasoning_effort: this.opts.reasoningEffort };
564
589
  }
565
590
  const method = opts.resumeId ? 'thread/resume' : 'thread/start';
566
591
  const params = opts.resumeId
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@parall/codex-agent",
3
- "version": "1.41.0",
3
+ "version": "1.42.0",
4
4
  "description": "Codex CLI bridge runtime for self-hosted Parall agents",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -26,9 +26,9 @@
26
26
  ],
27
27
  "dependencies": {
28
28
  "smol-toml": "^1.6.1",
29
- "@parall/agent-core": "1.41.0",
30
- "@parall/sdk": "1.41.0",
31
- "@parall/cli": "1.41.0"
29
+ "@parall/agent-core": "1.42.0",
30
+ "@parall/cli": "1.42.0",
31
+ "@parall/sdk": "1.42.0"
32
32
  },
33
33
  "devDependencies": {
34
34
  "@types/node": "^22.0.0",
package/src/config.ts CHANGED
@@ -112,20 +112,30 @@ export function normalizeSandbox(value: string): string {
112
112
  }
113
113
 
114
114
  /**
115
- * Normalise an approval policy string to the kebab-case form the app-server
116
- * JSON-RPC enum expects (`never` / `on-request` / `on-failure` /
117
- * `unless-trusted`). Mirrors the sandbox enum serde rename_all kebab-case
118
- * applies across app-server protocol enums.
115
+ * Normalise an approval policy string to the enum form the app-server
116
+ * JSON-RPC surface accepts. Empirically verified against both pinned CLI
117
+ * generations: 0.144.x accepts `untrusted` / `on-request` / `granular` /
118
+ * `never`; 0.133.0 additionally accepted `on-failure`. Two legacy inputs are
119
+ * translated rather than passed through, because the server rejects the whole
120
+ * thread/start with "unknown variant" otherwise:
121
+ *
122
+ * - `on-failure` → `on-request` (removed from the enum in 0.144; the closest
123
+ * surviving escalation policy).
124
+ * - `unless-trusted` → `untrusted` (the wire constant was never
125
+ * `unless-trusted` on either generation — accepting-but-forwarding it made
126
+ * every thread/start fail for configs that set it).
119
127
  */
120
128
  export function normalizeApprovalPolicy(value: string): string {
121
129
  const map: Record<string, string> = {
122
130
  never: 'never',
123
131
  'on-request': 'on-request',
124
132
  onrequest: 'on-request',
125
- 'unless-trusted': 'unless-trusted',
126
- unlesstrusted: 'unless-trusted',
127
- 'on-failure': 'on-failure',
128
- onfailure: 'on-failure',
133
+ untrusted: 'untrusted',
134
+ 'unless-trusted': 'untrusted',
135
+ unlesstrusted: 'untrusted',
136
+ granular: 'granular',
137
+ 'on-failure': 'on-request',
138
+ onfailure: 'on-request',
129
139
  };
130
140
  return map[value.toLowerCase()] ?? value;
131
141
  }
package/src/dispatch.ts CHANGED
@@ -264,11 +264,30 @@ export class CodexAppServerAdapter implements DispatchAdapter {
264
264
  // this contract, we want a hard error surfaced to the operator, not a
265
265
  // silent fallback that hides real protocol drift.
266
266
  const turnInput = buildTurnInput(preparedBody, preparedImages);
267
- const startTurn = (targetThreadId: string): Promise<unknown> =>
268
- client.sendRequest('turn/start', {
267
+ // Per-turn overrides, first-class on both supported CLI generations
268
+ // (0.133 / 0.144) with "for this turn and subsequent turns" semantics.
269
+ // Sending the CURRENT config on every turn is what makes
270
+ // agent_config.update (model / thinking-effort hot updates) take
271
+ // effect on the next turn: thread-level config only applies at
272
+ // thread/start-resume, which an already-open thread never re-runs,
273
+ // and a prior turn's override would otherwise stay live for the rest
274
+ // of the subprocess lifetime.
275
+ //
276
+ // The fields are only sent when a value is configured: on the wire
277
+ // they are plain serde Options, so JSON null is indistinguishable
278
+ // from omission — there is no "reset to default" encoding. CLEARING
279
+ // a previously-set pin therefore keeps the old sticky value until
280
+ // the subprocess restarts (pod restart / redeploy / idle reap);
281
+ // an explicit restart-on-clear path is a tracked follow-up.
282
+ const startTurn = (targetThreadId: string): Promise<unknown> => {
283
+ const turnParams: Record<string, unknown> = {
269
284
  threadId: targetThreadId,
270
285
  input: turnInput,
271
- });
286
+ };
287
+ if (this.opts.model) turnParams.model = this.opts.model;
288
+ if (this.opts.reasoningEffort) turnParams.effort = this.opts.reasoningEffort;
289
+ return client.sendRequest('turn/start', turnParams);
290
+ };
272
291
 
273
292
  // Start the turn. If the stored threadId is dead, we retry once with a fresh thread.
274
293
  let turnStartResult: unknown;
@@ -409,7 +428,8 @@ export class CodexAppServerAdapter implements DispatchAdapter {
409
428
  }
410
429
  if (this.opts.model) forkParams.model = this.opts.model;
411
430
  if (this.opts.reasoningEffort) {
412
- forkParams.config = { modelReasoningEffort: this.opts.reasoningEffort };
431
+ // Raw config.toml key see openThread for the snake_case rationale.
432
+ forkParams.config = { model_reasoning_effort: this.opts.reasoningEffort };
413
433
  }
414
434
  const result = await client.sendRequest('thread/fork', forkParams);
415
435
  const forkedThreadId = extractThreadId(result);
@@ -621,12 +641,14 @@ export class CodexAppServerAdapter implements DispatchAdapter {
621
641
  commonParams.sandbox = normalizeSandbox(this.opts.sandbox);
622
642
  if (this.opts.model) commonParams.model = this.opts.model;
623
643
  if (this.opts.reasoningEffort) {
624
- // The app-server JSON-RPC surface uses camelCase for overrides
625
- // `modelReasoningEffort` parallels `approvalPolicy` / `sandbox` on the
626
- // top-level params. The corresponding config.toml key is
627
- // `model_reasoning_effort` (snake_case), but the nested `config` on
628
- // `thread/start` / `thread/resume` takes the camelCase form.
629
- commonParams.config = { modelReasoningEffort: this.opts.reasoningEffort };
644
+ // The nested `config` object is raw config.toml overrides and keeps the
645
+ // TOML key names `model_reasoning_effort` (snake_case) — unlike the
646
+ // typed top-level params (`approvalPolicy` / `sandbox`), which are
647
+ // camelCase. Empirically verified against both pinned CLI generations
648
+ // (0.133.0 and 0.144.1): the camelCase key is silently ignored
649
+ // (thread/start echoes reasoningEffort: null), the snake_case key is
650
+ // applied and echoed back.
651
+ commonParams.config = { model_reasoning_effort: this.opts.reasoningEffort };
630
652
  }
631
653
 
632
654
  const method = opts.resumeId ? 'thread/resume' : 'thread/start';