@the-open-engine/zeroshot 6.7.2 → 6.8.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.
Files changed (29) hide show
  1. package/lib/agent-cli-provider/adapters/common.js +1 -1
  2. package/lib/agent-cli-provider/adapters/common.js.map +1 -1
  3. package/lib/agent-cli-provider/adapters/opencode-models.d.ts +7 -0
  4. package/lib/agent-cli-provider/adapters/opencode-models.d.ts.map +1 -0
  5. package/lib/agent-cli-provider/adapters/opencode-models.js +87 -0
  6. package/lib/agent-cli-provider/adapters/opencode-models.js.map +1 -0
  7. package/lib/agent-cli-provider/adapters/opencode.d.ts.map +1 -1
  8. package/lib/agent-cli-provider/adapters/opencode.js +6 -58
  9. package/lib/agent-cli-provider/adapters/opencode.js.map +1 -1
  10. package/lib/agent-cli-provider/single-agent-runtime.d.ts.map +1 -1
  11. package/lib/agent-cli-provider/single-agent-runtime.js +27 -13
  12. package/lib/agent-cli-provider/single-agent-runtime.js.map +1 -1
  13. package/lib/agent-cli-provider/types.d.ts +1 -0
  14. package/lib/agent-cli-provider/types.d.ts.map +1 -1
  15. package/lib/agent-cli-provider/types.js.map +1 -1
  16. package/package.json +1 -1
  17. package/scripts/assert-release-published.js +46 -15
  18. package/src/agent/agent-task-executor.js +18 -9
  19. package/src/agent-cli-provider/adapters/common.ts +1 -1
  20. package/src/agent-cli-provider/adapters/opencode-models.ts +100 -0
  21. package/src/agent-cli-provider/adapters/opencode.ts +8 -65
  22. package/src/agent-cli-provider/single-agent-runtime.ts +51 -33
  23. package/src/agent-cli-provider/types.ts +1 -0
  24. package/src/agent-wrapper.js +14 -2
  25. package/src/claude-task-runner.js +121 -37
  26. package/src/config-validator.js +2 -2
  27. package/src/providers/index.js +5 -0
  28. package/src/task-run-model-args.js +155 -0
  29. package/task-lib/runner.js +66 -26
@@ -13,6 +13,18 @@ const { normalizeProviderName } = require('../lib/provider-names');
13
13
  const { getProvider } = require('./providers');
14
14
  const { prependWorktreeToolBinToEnv } = require('./worktree-tooling-env');
15
15
  const { prepareClaudeConfigDir } = require('./worktree-claude-config');
16
+ const {
17
+ appendTaskRunModelArgs,
18
+ wrapTaskRunWithIsolatedSettings,
19
+ } = require('./task-run-model-args');
20
+
21
+ function rejectCallerSuppliedModelProvenance(options) {
22
+ if (Object.prototype.hasOwnProperty.call(options, 'modelSpecSource')) {
23
+ throw new Error(
24
+ 'modelSpecSource is derived from model versus modelLevel/default and cannot be supplied.'
25
+ );
26
+ }
27
+ }
16
28
 
17
29
  function runCommand(command, args, options = {}, callback = null) {
18
30
  const timeout = options.timeout ?? 30000;
@@ -112,10 +124,11 @@ class ClaudeTaskRunner extends TaskRunner {
112
124
  * Execute a task via zeroshot CLI
113
125
  *
114
126
  * @param {string} context - Full prompt/context
115
- * @param {{agentId?: string, model?: string, outputFormat?: string, jsonSchema?: any, strictSchema?: boolean, cwd?: string, worktreePath?: string|null, isolation?: any}} options - Execution options
127
+ * @param {{agentId?: string, model?: string, modelLevel?: string, modelSpec?: Object|null, reasoningEffort?: string, outputFormat?: string, jsonSchema?: any, strictSchema?: boolean, cwd?: string, worktreePath?: string|null, isolation?: any}} options - Execution options
116
128
  * @returns {Promise<{success: boolean, output: string, error: string|null, taskId?: string}>}
117
129
  */
118
130
  async run(context, options = {}) {
131
+ rejectCallerSuppliedModelProvenance(options);
119
132
  const {
120
133
  agentId = 'unknown',
121
134
  provider,
@@ -137,7 +150,7 @@ class ClaudeTaskRunner extends TaskRunner {
137
150
  providerName,
138
151
  settings
139
152
  );
140
- const resolvedModelSpec = this._resolveModelSpec({
153
+ const modelSelection = this._resolveModelSelection({
141
154
  explicitModelSpec,
142
155
  model,
143
156
  reasoningEffort,
@@ -146,13 +159,13 @@ class ClaudeTaskRunner extends TaskRunner {
146
159
  providerSettings,
147
160
  levelOverrides,
148
161
  });
162
+ const resolvedModelSpec = modelSelection.modelSpec;
149
163
 
150
164
  // Isolation mode delegates to separate method
151
165
  if (isolation?.enabled) {
152
166
  return this._runIsolated(context, {
153
167
  ...options,
154
168
  provider: providerName,
155
- modelSpec: resolvedModelSpec,
156
169
  });
157
170
  }
158
171
 
@@ -168,6 +181,7 @@ class ClaudeTaskRunner extends TaskRunner {
168
181
  providerName,
169
182
  runOutputFormat,
170
183
  resolvedModelSpec,
184
+ modelSpecSource: modelSelection.source,
171
185
  jsonSchema,
172
186
  });
173
187
 
@@ -204,25 +218,74 @@ class ClaudeTaskRunner extends TaskRunner {
204
218
  providerSettings,
205
219
  levelOverrides,
206
220
  }) {
207
- if (explicitModelSpec) {
208
- if (explicitModelSpec.model) {
209
- providerModule.validateModelId(explicitModelSpec.model);
210
- }
211
- return explicitModelSpec;
212
- }
221
+ return this._resolveModelSelection({
222
+ explicitModelSpec,
223
+ model,
224
+ reasoningEffort,
225
+ modelLevel,
226
+ providerModule,
227
+ providerSettings,
228
+ levelOverrides,
229
+ }).modelSpec;
230
+ }
213
231
 
232
+ _resolveModelSelection({
233
+ explicitModelSpec,
234
+ model,
235
+ reasoningEffort,
236
+ modelLevel,
237
+ providerModule,
238
+ providerSettings,
239
+ levelOverrides,
240
+ }) {
214
241
  if (model) {
215
242
  providerModule.validateModelId(model);
216
- return { model, reasoningEffort };
243
+ return {
244
+ source: 'direct',
245
+ modelSpec: { model, reasoningEffort },
246
+ };
217
247
  }
218
248
 
219
- const level = modelLevel || providerSettings.defaultLevel || providerModule.getDefaultLevel();
220
- let resolvedModelSpec = providerModule.resolveModelSpec(level, levelOverrides);
221
- if (reasoningEffort) {
222
- resolvedModelSpec = { ...resolvedModelSpec, reasoningEffort };
249
+ if (explicitModelSpec?.model !== undefined) {
250
+ providerModule.validateModelId(explicitModelSpec.model);
251
+ return {
252
+ source: 'direct',
253
+ modelSpec: explicitModelSpec,
254
+ };
223
255
  }
224
256
 
225
- return resolvedModelSpec;
257
+ return {
258
+ source: 'provider-level',
259
+ modelSpec: this._resolveProviderLevelModelSpec({
260
+ explicitModelSpec,
261
+ modelLevel,
262
+ reasoningEffort,
263
+ providerModule,
264
+ providerSettings,
265
+ levelOverrides,
266
+ }),
267
+ };
268
+ }
269
+
270
+ _resolveProviderLevelModelSpec({
271
+ explicitModelSpec,
272
+ modelLevel,
273
+ reasoningEffort,
274
+ providerModule,
275
+ providerSettings,
276
+ levelOverrides,
277
+ }) {
278
+ const level =
279
+ explicitModelSpec?.level ||
280
+ modelLevel ||
281
+ providerSettings.defaultLevel ||
282
+ providerModule.getDefaultLevel();
283
+ const resolvedModelSpec = providerModule.resolveModelSpec(level, levelOverrides);
284
+ const resolvedReasoningEffort = explicitModelSpec?.reasoningEffort || reasoningEffort;
285
+
286
+ return resolvedReasoningEffort
287
+ ? { ...resolvedModelSpec, reasoningEffort: resolvedReasoningEffort }
288
+ : resolvedModelSpec;
226
289
  }
227
290
 
228
291
  _resolveOutputFormat({ outputFormat, jsonSchema, strictSchema }) {
@@ -232,16 +295,16 @@ class ClaudeTaskRunner extends TaskRunner {
232
295
  return jsonSchema && outputFormat === 'json' && !strictSchema ? 'stream-json' : outputFormat;
233
296
  }
234
297
 
235
- _buildRunArgs({ context, providerName, runOutputFormat, resolvedModelSpec, jsonSchema }) {
298
+ _buildRunArgs({
299
+ context,
300
+ providerName,
301
+ runOutputFormat,
302
+ resolvedModelSpec,
303
+ modelSpecSource = 'direct',
304
+ jsonSchema,
305
+ }) {
236
306
  const args = ['task', 'run', '--output-format', runOutputFormat, '--provider', providerName];
237
-
238
- if (resolvedModelSpec?.model) {
239
- args.push('--model', resolvedModelSpec.model);
240
- }
241
-
242
- if (resolvedModelSpec?.reasoningEffort) {
243
- args.push('--reasoning-effort', resolvedModelSpec.reasoningEffort);
244
- }
307
+ appendTaskRunModelArgs(args, resolvedModelSpec, modelSpecSource);
245
308
 
246
309
  // Pass schema to CLI only when using json output (strictSchema=true or no conflict)
247
310
  if (jsonSchema && runOutputFormat === 'json') {
@@ -542,20 +605,40 @@ class ClaudeTaskRunner extends TaskRunner {
542
605
  /**
543
606
  * Run task in isolated Docker container
544
607
  * @param {string} context
545
- * @param {{agentId?: string, model?: string, outputFormat?: string, jsonSchema?: any, strictSchema?: boolean, isolation?: any}} options
608
+ * @param {{agentId?: string, provider?: string, model?: string, modelLevel?: string, modelSpec?: Object|null, reasoningEffort?: string, outputFormat?: string, jsonSchema?: any, strictSchema?: boolean, isolation?: any}} options
546
609
  * @returns {Promise<{success: boolean, output: string, error: string|null}>}
547
610
  */
548
611
  _runIsolated(context, options) {
612
+ rejectCallerSuppliedModelProvenance(options);
549
613
  const {
550
614
  agentId = 'unknown',
551
615
  provider = 'claude',
552
- modelSpec = null,
616
+ model = null,
617
+ modelLevel = null,
618
+ modelSpec: explicitModelSpec = null,
619
+ reasoningEffort = null,
553
620
  outputFormat = 'stream-json',
554
621
  jsonSchema = null,
555
622
  strictSchema = false,
556
623
  isolation,
557
624
  } = options;
558
625
  const { manager, clusterId } = isolation;
626
+ const settings = loadSettings();
627
+ const { providerModule, providerSettings, levelOverrides } = this._getProviderContext(
628
+ provider,
629
+ settings
630
+ );
631
+ const modelSelection = this._resolveModelSelection({
632
+ explicitModelSpec,
633
+ model,
634
+ reasoningEffort,
635
+ modelLevel,
636
+ providerModule,
637
+ providerSettings,
638
+ levelOverrides,
639
+ });
640
+ const modelSpec = modelSelection.modelSpec;
641
+ const modelSpecSource = modelSelection.source;
559
642
 
560
643
  this._log(`📦 [${agentId}]: Running task in isolated container...`);
561
644
 
@@ -565,7 +648,7 @@ class ClaudeTaskRunner extends TaskRunner {
565
648
  ? 'stream-json'
566
649
  : desiredOutputFormat;
567
650
 
568
- const command = [
651
+ let command = [
569
652
  'zeroshot',
570
653
  'task',
571
654
  'run',
@@ -575,13 +658,7 @@ class ClaudeTaskRunner extends TaskRunner {
575
658
  provider,
576
659
  ];
577
660
 
578
- if (modelSpec?.model) {
579
- command.push('--model', modelSpec.model);
580
- }
581
-
582
- if (modelSpec?.reasoningEffort) {
583
- command.push('--reasoning-effort', modelSpec.reasoningEffort);
584
- }
661
+ appendTaskRunModelArgs(command, modelSpec, modelSpecSource);
585
662
 
586
663
  if (jsonSchema && runOutputFormat === 'json') {
587
664
  command.push('--json-schema', JSON.stringify(jsonSchema));
@@ -597,16 +674,23 @@ class ClaudeTaskRunner extends TaskRunner {
597
674
  }
598
675
 
599
676
  command.push(finalContext);
677
+ command = wrapTaskRunWithIsolatedSettings(command, {
678
+ providerName: provider,
679
+ settings,
680
+ modelSpecSource,
681
+ modelSpec,
682
+ });
600
683
 
601
684
  return new Promise((resolve, reject) => {
602
685
  let output = '';
603
686
  let resolved = false;
604
687
 
605
688
  const proc = manager.spawnInContainer(clusterId, command, {
606
- env:
607
- provider === 'claude' && modelSpec?.model
689
+ env: {
690
+ ...(provider === 'claude' && modelSpec?.model
608
691
  ? { ANTHROPIC_MODEL: modelSpec.model, ZEROSHOT_BLOCK_ASK_USER: '1' }
609
- : {},
692
+ : {}),
693
+ },
610
694
  });
611
695
 
612
696
  proc.stdout.on('data', (/** @type {Buffer} */ data) => {
@@ -2053,8 +2053,8 @@ function validateProviderSettings(provider, providerSettings) {
2053
2053
  `Invalid model override (must be non-empty string) for provider "${provider}"`
2054
2054
  );
2055
2055
  }
2056
- if (override?.model) {
2057
- providerModule.validateModelId(override.model);
2056
+ if (override?.model || (provider === 'opencode' && override?.model === '')) {
2057
+ providerModule.resolveModelSpec(level, { [level]: override });
2058
2058
  }
2059
2059
  if (override?.reasoningEffort && !providerSupportsCapability(provider, 'reasoningEffort')) {
2060
2060
  throw new Error(
@@ -129,6 +129,11 @@ class RuntimeProvider extends BaseProvider {
129
129
  return this._adapter.validateModelId(modelId);
130
130
  }
131
131
 
132
+ validateConfiguredModelId(modelId) {
133
+ const validator = this._adapter.validateConfiguredModelId || this._adapter.validateModelId;
134
+ return validator(modelId);
135
+ }
136
+
132
137
  isRetryableError(err) {
133
138
  return helper.classifyProviderError(this.name, err).retryable;
134
139
  }
@@ -0,0 +1,155 @@
1
+ /**
2
+ * Append a resolved model selection to a nested `zeroshot task run` invocation.
3
+ *
4
+ * Direct requests use the public, catalog-strict `--model` channel.
5
+ * Provider-level selections carry only their level. The child must resolve the
6
+ * concrete model again from its effective provider settings.
7
+ *
8
+ * @param {string[]} args
9
+ * @param {Object|null|undefined} modelSpec
10
+ * @param {'direct'|'provider-level'} [modelSpecSource]
11
+ * @returns {string[]}
12
+ */
13
+ function appendTaskRunModelArgs(args, modelSpec, modelSpecSource = 'direct') {
14
+ if (modelSpecSource === 'provider-level') {
15
+ if (!modelSpec?.level) {
16
+ throw new Error('Provider-level task model selections require a model level');
17
+ }
18
+ args.push('--model-level', modelSpec.level);
19
+ } else if (modelSpec?.model) {
20
+ args.push('--model', modelSpec.model);
21
+ }
22
+
23
+ if (modelSpec?.reasoningEffort) {
24
+ args.push('--reasoning-effort', modelSpec.reasoningEffort);
25
+ }
26
+
27
+ return args;
28
+ }
29
+
30
+ const ISOLATED_SETTINGS_FILE_ENV = 'ZEROSHOT_SETTINGS_FILE';
31
+ const ISOLATED_SETTINGS_FILE_MARKER = 'ZEROSHOT_DOCKER_SETTINGS_FILE';
32
+ const LEGACY_ISOLATED_PROVIDER_SETTINGS_ENV = 'ZEROSHOT_ISOLATED_PROVIDER_SETTINGS_JSON';
33
+ const SETTINGS_BOOTSTRAP_SCRIPT = String.raw`
34
+ const childProcess = require('node:child_process');
35
+ const fs = require('node:fs');
36
+ const os = require('node:os');
37
+ const path = require('node:path');
38
+
39
+ const snapshot = process.argv[1];
40
+ const command = process.argv[2];
41
+ const args = process.argv.slice(3);
42
+ const directory = fs.mkdtempSync(path.join(os.tmpdir(), 'zeroshot-isolated-settings-'));
43
+ const settingsFile = path.join(directory, 'settings.json');
44
+
45
+ try {
46
+ fs.writeFileSync(settingsFile, snapshot, { encoding: 'utf8', flag: 'wx', mode: 0o600 });
47
+ const result = childProcess.spawnSync(command, args, {
48
+ stdio: 'inherit',
49
+ env: {
50
+ ...process.env,
51
+ ${ISOLATED_SETTINGS_FILE_ENV}: settingsFile,
52
+ ${ISOLATED_SETTINGS_FILE_MARKER}: '1',
53
+ },
54
+ });
55
+ if (result.error) throw result.error;
56
+ process.exitCode = result.status === null ? 1 : result.status;
57
+ } finally {
58
+ fs.rmSync(directory, { recursive: true, force: true });
59
+ }
60
+ `.trim();
61
+
62
+ /**
63
+ * Wrap an isolated task command with a Docker-only, temporary settings-file
64
+ * bootstrap. The snapshot is a closed projection containing only the selected
65
+ * OpenCode level and its model. It never contains arbitrary provider settings,
66
+ * credentials, or caller-owned keys.
67
+ *
68
+ * @param {string[]} command
69
+ * @param {{providerName: string, settings: Object, modelSpecSource: 'direct'|'provider-level', modelSpec: Object|null|undefined}} context
70
+ * @returns {string[]}
71
+ */
72
+ function wrapTaskRunWithIsolatedSettings(command, context) {
73
+ const { providerName, settings, modelSpecSource, modelSpec } = context;
74
+ if (providerName !== 'opencode' || modelSpecSource !== 'provider-level') return command;
75
+ const snapshot = buildIsolatedSettingsSnapshot(settings, modelSpec);
76
+ if (snapshot === null) return command;
77
+ return ['node', '-e', SETTINGS_BOOTSTRAP_SCRIPT, snapshot, ...command];
78
+ }
79
+
80
+ function buildIsolatedSettingsSnapshot(settings, modelSpec) {
81
+ const level = modelSpec?.level;
82
+ if (!['level1', 'level2', 'level3'].includes(level)) {
83
+ throw permanentError(
84
+ 'Provider-level isolated OpenCode selections require a valid model level.'
85
+ );
86
+ }
87
+
88
+ const providerSettings = ownRecordValue(settings, 'providerSettings', 'settings') ?? {};
89
+ const opencodeSettings = ownRecordValue(
90
+ providerSettings,
91
+ 'opencode',
92
+ 'settings.providerSettings'
93
+ );
94
+ const levelOverrides = ownRecordValue(
95
+ opencodeSettings,
96
+ 'levelOverrides',
97
+ 'settings.providerSettings.opencode'
98
+ );
99
+ const levelOverride = ownRecordValue(
100
+ levelOverrides,
101
+ level,
102
+ 'settings.providerSettings.opencode.levelOverrides'
103
+ );
104
+ const configuredModel =
105
+ levelOverride && Object.prototype.hasOwnProperty.call(levelOverride, 'model')
106
+ ? levelOverride.model
107
+ : null;
108
+ if (configuredModel !== null && typeof configuredModel !== 'string') {
109
+ throw permanentError(`Configured isolated OpenCode ${level} model must be a string or null.`);
110
+ }
111
+ if (modelSpec?.model !== configuredModel) {
112
+ throw permanentError(
113
+ `Provider-level model "${modelSpec?.model}" does not match the effective isolated ${modelSpec?.level} model "${configuredModel}".`
114
+ );
115
+ }
116
+ if (configuredModel === null) return null;
117
+
118
+ return JSON.stringify({
119
+ providerSettings: {
120
+ opencode: {
121
+ levelOverrides: {
122
+ [level]: { model: configuredModel },
123
+ },
124
+ },
125
+ },
126
+ });
127
+ }
128
+
129
+ function ownRecordValue(record, key, field) {
130
+ if (record === null || record === undefined) return undefined;
131
+ if (typeof record !== 'object' || Array.isArray(record)) {
132
+ throw permanentError(`${field} must be an object.`);
133
+ }
134
+ if (!Object.prototype.hasOwnProperty.call(record, key)) return undefined;
135
+ const value = record[key];
136
+ if (value === null || value === undefined) return undefined;
137
+ if (typeof value !== 'object' || Array.isArray(value)) {
138
+ throw permanentError(`${field}.${key} must be an object.`);
139
+ }
140
+ return value;
141
+ }
142
+
143
+ function permanentError(message) {
144
+ const error = new Error(message);
145
+ error.permanent = true;
146
+ return error;
147
+ }
148
+
149
+ module.exports = {
150
+ ISOLATED_SETTINGS_FILE_ENV,
151
+ ISOLATED_SETTINGS_FILE_MARKER,
152
+ LEGACY_ISOLATED_PROVIDER_SETTINGS_ENV,
153
+ appendTaskRunModelArgs,
154
+ wrapTaskRunWithIsolatedSettings,
155
+ };
@@ -7,6 +7,11 @@ import { createRequire } from 'module';
7
7
 
8
8
  const require = createRequire(import.meta.url);
9
9
  const { prepareSingleAgentProviderCommand } = require('./provider-helper-runtime.js');
10
+ const {
11
+ ISOLATED_SETTINGS_FILE_ENV,
12
+ ISOLATED_SETTINGS_FILE_MARKER,
13
+ LEGACY_ISOLATED_PROVIDER_SETTINGS_ENV,
14
+ } = require('../src/task-run-model-args.js');
10
15
  export {
11
16
  isOwnedProcessTreeRunning,
12
17
  isProcessRunning,
@@ -25,10 +30,10 @@ export function spawnTask(prompt, options = {}) {
25
30
 
26
31
  const outputFormat = resolveOutputFormat(options);
27
32
  const jsonSchema = resolveJsonSchema(options, outputFormat);
28
- const prepared = prepareSingleAgentProviderCommand({
29
- provider: options.provider || null,
30
- context: prompt,
31
- options: buildProviderOptions(options, outputFormat, jsonSchema, cwd),
33
+ const prepared = prepareTaskProviderCommandFromResolved(prompt, options, {
34
+ outputFormat,
35
+ jsonSchema,
36
+ cwd,
32
37
  });
33
38
  const providerName = prepared.adapter.id;
34
39
  const modelSpec = prepared.options.modelSpec;
@@ -72,6 +77,24 @@ export function spawnTask(prompt, options = {}) {
72
77
  return task;
73
78
  }
74
79
 
80
+ export function prepareTaskProviderCommand(prompt, options = {}) {
81
+ const outputFormat = resolveOutputFormat(options);
82
+ return prepareTaskProviderCommandFromResolved(prompt, options, {
83
+ outputFormat,
84
+ jsonSchema: resolveJsonSchema(options, outputFormat),
85
+ cwd: options.cwd || process.cwd(),
86
+ });
87
+ }
88
+
89
+ function prepareTaskProviderCommandFromResolved(prompt, options, runtime) {
90
+ const modelSelection = resolveRequestedModelSelection(options);
91
+ return prepareSingleAgentProviderCommand({
92
+ provider: options.provider || null,
93
+ context: prompt,
94
+ options: buildProviderOptions(options, runtime, modelSelection),
95
+ });
96
+ }
97
+
75
98
  function resolveOutputFormat(options) {
76
99
  return options.outputFormat || 'stream-json';
77
100
  }
@@ -85,13 +108,13 @@ function resolveJsonSchema(options, outputFormat) {
85
108
  return jsonSchema;
86
109
  }
87
110
 
88
- function buildProviderOptions(options, outputFormat, jsonSchema, cwd) {
111
+ function buildProviderOptions(options, runtime, modelSelection) {
89
112
  return {
90
- outputFormat,
91
- jsonSchema,
92
- cwd,
113
+ outputFormat: runtime.outputFormat,
114
+ jsonSchema: runtime.jsonSchema,
115
+ cwd: runtime.cwd,
93
116
  autoApprove: true,
94
- ...modelSpecOption(options),
117
+ ...(modelSelection === undefined ? {} : { modelSpec: modelSelection.modelSpec }),
95
118
  ...mcpConfigOption(options),
96
119
  ...(options.resume ? { resumeSessionId: options.resume } : {}),
97
120
  ...(options.continue ? { continueSession: true } : {}),
@@ -104,27 +127,32 @@ function mcpConfigOption(options) {
104
127
  return { mcpConfig: entries };
105
128
  }
106
129
 
107
- function modelSpecOption(options) {
108
- const modelSpec = resolveRequestedModelSpec(options);
109
- return modelSpec === undefined ? {} : { modelSpec };
110
- }
130
+ function resolveRequestedModelSelection(options) {
131
+ if (Object.prototype.hasOwnProperty.call(options, 'configuredModel')) {
132
+ throw new Error(
133
+ '--configured-model is not supported; configure providerSettings levelOverrides instead'
134
+ );
135
+ }
111
136
 
112
- function resolveRequestedModelSpec(options) {
113
137
  if (options.model) {
114
- return {
115
- model: options.model,
116
- ...(options.reasoningEffort ? { reasoningEffort: options.reasoningEffort } : {}),
117
- };
138
+ return directModelSelection(options);
118
139
  }
119
140
 
120
- if (options.reasoningEffort) {
121
- return {
122
- ...(options.modelLevel ? { level: options.modelLevel } : {}),
123
- reasoningEffort: options.reasoningEffort,
124
- };
125
- }
126
- if (options.modelLevel) return { level: options.modelLevel };
127
- return undefined;
141
+ return providerLevelSelection(options);
142
+ }
143
+
144
+ function directModelSelection(options) {
145
+ const modelSpec = { model: options.model };
146
+ if (options.reasoningEffort) modelSpec.reasoningEffort = options.reasoningEffort;
147
+ return { modelSpec };
148
+ }
149
+
150
+ function providerLevelSelection(options) {
151
+ if (!options.reasoningEffort && !options.modelLevel) return undefined;
152
+ const modelSpec = {};
153
+ if (options.modelLevel) modelSpec.level = options.modelLevel;
154
+ if (options.reasoningEffort) modelSpec.reasoningEffort = options.reasoningEffort;
155
+ return { modelSpec };
128
156
  }
129
157
 
130
158
  function buildTaskRecord({ id, prompt, cwd, options, logFile, providerName, modelSpec }) {
@@ -189,6 +217,7 @@ function resolveWatcherScript(options, providerName) {
189
217
  }
190
218
 
191
219
  function spawnWatcher({ watcherScript, id, cwd, logFile, finalArgs, watcherConfig }) {
220
+ const watcherEnv = buildWatcherEnv();
192
221
  const watcher = fork(
193
222
  watcherScript,
194
223
  [id, cwd, logFile, JSON.stringify(finalArgs), JSON.stringify(watcherConfig)],
@@ -196,9 +225,20 @@ function spawnWatcher({ watcherScript, id, cwd, logFile, finalArgs, watcherConfi
196
225
  detached: true,
197
226
  stdio: 'ignore',
198
227
  windowsHide: true,
228
+ env: watcherEnv,
199
229
  }
200
230
  );
201
231
 
202
232
  watcher.unref();
203
233
  watcher.disconnect(); // Close IPC channel so parent can exit
204
234
  }
235
+
236
+ export function buildWatcherEnv(sourceEnv = process.env) {
237
+ const watcherEnv = { ...sourceEnv };
238
+ delete watcherEnv[LEGACY_ISOLATED_PROVIDER_SETTINGS_ENV];
239
+ if (watcherEnv[ISOLATED_SETTINGS_FILE_MARKER] === '1') {
240
+ delete watcherEnv[ISOLATED_SETTINGS_FILE_ENV];
241
+ delete watcherEnv[ISOLATED_SETTINGS_FILE_MARKER];
242
+ }
243
+ return watcherEnv;
244
+ }