@the-open-engine/zeroshot 6.7.2 → 6.8.1

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 (38) hide show
  1. package/lib/agent-cli-provider/adapters/claude.d.ts.map +1 -1
  2. package/lib/agent-cli-provider/adapters/claude.js +1 -0
  3. package/lib/agent-cli-provider/adapters/claude.js.map +1 -1
  4. package/lib/agent-cli-provider/adapters/codex.d.ts.map +1 -1
  5. package/lib/agent-cli-provider/adapters/codex.js +3 -0
  6. package/lib/agent-cli-provider/adapters/codex.js.map +1 -1
  7. package/lib/agent-cli-provider/adapters/common.js +1 -1
  8. package/lib/agent-cli-provider/adapters/common.js.map +1 -1
  9. package/lib/agent-cli-provider/adapters/opencode-models.d.ts +7 -0
  10. package/lib/agent-cli-provider/adapters/opencode-models.d.ts.map +1 -0
  11. package/lib/agent-cli-provider/adapters/opencode-models.js +87 -0
  12. package/lib/agent-cli-provider/adapters/opencode-models.js.map +1 -0
  13. package/lib/agent-cli-provider/adapters/opencode.d.ts.map +1 -1
  14. package/lib/agent-cli-provider/adapters/opencode.js +6 -58
  15. package/lib/agent-cli-provider/adapters/opencode.js.map +1 -1
  16. package/lib/agent-cli-provider/single-agent-runtime.d.ts.map +1 -1
  17. package/lib/agent-cli-provider/single-agent-runtime.js +27 -13
  18. package/lib/agent-cli-provider/single-agent-runtime.js.map +1 -1
  19. package/lib/agent-cli-provider/types.d.ts +1 -0
  20. package/lib/agent-cli-provider/types.d.ts.map +1 -1
  21. package/lib/agent-cli-provider/types.js.map +1 -1
  22. package/package.json +1 -1
  23. package/scripts/assert-release-published.js +145 -35
  24. package/src/agent/agent-task-executor.js +23 -10
  25. package/src/agent-cli-provider/adapters/claude.ts +1 -0
  26. package/src/agent-cli-provider/adapters/codex.ts +3 -0
  27. package/src/agent-cli-provider/adapters/common.ts +1 -1
  28. package/src/agent-cli-provider/adapters/opencode-models.ts +100 -0
  29. package/src/agent-cli-provider/adapters/opencode.ts +8 -65
  30. package/src/agent-cli-provider/single-agent-runtime.ts +51 -33
  31. package/src/agent-cli-provider/types.ts +1 -0
  32. package/src/agent-wrapper.js +14 -2
  33. package/src/claude-task-runner.js +121 -37
  34. package/src/config-validator.js +2 -2
  35. package/src/isolation-manager.js +4 -1
  36. package/src/providers/index.js +5 -0
  37. package/src/task-run-model-args.js +155 -0
  38. package/task-lib/runner.js +66 -26
@@ -34,6 +34,11 @@ interface RuntimeProviderSettings {
34
34
  readonly gateway?: GatewayBuildOptions;
35
35
  }
36
36
 
37
+ interface RuntimeCommandContext {
38
+ readonly cliFeatures: CliFeatureOverrides;
39
+ readonly authEnv: Readonly<Record<string, string>>;
40
+ }
41
+
37
42
  export interface SingleAgentProviderCommandInput {
38
43
  readonly provider?: string | null;
39
44
  readonly context: string;
@@ -62,6 +67,7 @@ type MutableModelSpec = {
62
67
 
63
68
  const MODEL_LEVELS: readonly ModelLevel[] = ['level1', 'level2', 'level3'];
64
69
  const REASONING_EFFORTS: readonly ReasoningEffort[] = ['low', 'medium', 'high', 'xhigh', 'max'];
70
+ const LEGACY_ISOLATED_PROVIDER_SETTINGS_ENV = 'ZEROSHOT_ISOLATED_PROVIDER_SETTINGS_JSON';
65
71
  const settingsModule: unknown = require('../../lib/settings');
66
72
  const providerDetectionModule: unknown = require('../../lib/provider-detection');
67
73
  const claudeAuthModule: unknown = require('../../lib/settings/claude-auth');
@@ -76,6 +82,7 @@ const resolveClaudeAuthFn = moduleFunction(claudeAuthModule, 'resolveClaudeAuth'
76
82
  export function prepareSingleAgentProviderCommand(
77
83
  input: SingleAgentProviderCommandInput
78
84
  ): PreparedSingleAgentProviderCommand {
85
+ rejectCallerSuppliedModelProvenance(input);
79
86
  const baseOptions = input.options ?? {};
80
87
  const settings = loadRuntimeSettings();
81
88
  const adapter = adapterForRuntimeInput(input.provider, settings);
@@ -86,7 +93,10 @@ export function prepareSingleAgentProviderCommand(
86
93
  );
87
94
  const cliFeatures = resolveRuntimeCliFeatures(adapter.id, baseOptions.cliFeatures);
88
95
  const authEnv = baseOptions.authEnv ?? resolveRuntimeAuthEnv(adapter.id, settings);
89
- const options = buildRuntimeOptions(baseOptions, adapter, providerSettings, cliFeatures, authEnv);
96
+ const options = buildRuntimeOptions(baseOptions, adapter, providerSettings, {
97
+ cliFeatures,
98
+ authEnv,
99
+ });
90
100
  return {
91
101
  adapter,
92
102
  options,
@@ -128,8 +138,7 @@ function mergeAcpFailClosedCliFeatures(
128
138
  ...detected,
129
139
  ...overrides,
130
140
  supportsAcpStdio: detected.supportsAcpStdio && overrides.supportsAcpStdio !== false,
131
- supportsPromptImages:
132
- detected.supportsPromptImages && overrides.supportsPromptImages !== false,
141
+ supportsPromptImages: detected.supportsPromptImages && overrides.supportsPromptImages !== false,
133
142
  supportsLoadSession: detected.supportsLoadSession && overrides.supportsLoadSession !== false,
134
143
  supportsSessionCancel:
135
144
  detected.supportsSessionCancel && overrides.supportsSessionCancel !== false,
@@ -162,7 +171,9 @@ export function probeRuntimeProviderCli(provider: string): RuntimeProviderProbe
162
171
  }
163
172
 
164
173
  const helpText = stringResult(getHelpOutputFn(helpCommand.command, helpCommand.args)).trim();
165
- const versionText = stringResult(getVersionOutputFn(helpCommand.command, helpCommand.args)).trim();
174
+ const versionText = stringResult(
175
+ getVersionOutputFn(helpCommand.command, helpCommand.args)
176
+ ).trim();
166
177
  const availabilityProbe = getProviderRegistryEntry(adapter.id).availabilityProbe ?? 'command';
167
178
 
168
179
  return {
@@ -177,8 +188,7 @@ function buildRuntimeOptions(
177
188
  baseOptions: BuildProviderCommandOptions,
178
189
  adapter: ProviderAdapter,
179
190
  providerSettings: RuntimeProviderSettings,
180
- cliFeatures: CliFeatureOverrides,
181
- authEnv: Readonly<Record<string, string>>
191
+ runtime: RuntimeCommandContext
182
192
  ): BuildProviderCommandOptions {
183
193
  const modelSpec = resolveRuntimeModelSpec(adapter, baseOptions.modelSpec, providerSettings);
184
194
  const gateway = resolveRuntimeGatewayOptions(
@@ -191,16 +201,16 @@ function buildRuntimeOptions(
191
201
  ...baseOptions,
192
202
  modelSpec,
193
203
  ...(gateway === undefined ? {} : { gateway }),
194
- cliFeatures,
204
+ cliFeatures: runtime.cliFeatures,
195
205
  };
196
206
  if (baseOptions.jsonSchema && !supportsProviderCapability(adapter.id, 'jsonSchema')) {
197
- if (!shouldIncludeAuthEnv(baseOptions, authEnv)) {
207
+ if (!shouldIncludeAuthEnv(baseOptions, runtime.authEnv)) {
198
208
  return { ...resolved, strictSchema: false };
199
209
  }
200
- return { ...resolved, authEnv, strictSchema: false };
210
+ return { ...resolved, authEnv: runtime.authEnv, strictSchema: false };
201
211
  }
202
- if (!shouldIncludeAuthEnv(baseOptions, authEnv)) return resolved;
203
- return { ...resolved, authEnv };
212
+ if (!shouldIncludeAuthEnv(baseOptions, runtime.authEnv)) return resolved;
213
+ return { ...resolved, authEnv: runtime.authEnv };
204
214
  }
205
215
 
206
216
  function resolveRuntimeGatewayOptions(
@@ -218,23 +228,19 @@ function resolveRuntimeGatewayOptions(
218
228
  ? settingsGateway.headers
219
229
  : { ...(settingsGateway.headers ?? {}), ...requestGateway.headers };
220
230
  const mergedGateway: GatewayBuildOptions = {
221
- ...(requestGateway.baseUrl ?? settingsGateway.baseUrl
231
+ ...((requestGateway.baseUrl ?? settingsGateway.baseUrl)
222
232
  ? { baseUrl: requestGateway.baseUrl ?? settingsGateway.baseUrl }
223
233
  : {}),
224
- ...(requestGateway.apiKey ?? settingsGateway.apiKey
234
+ ...((requestGateway.apiKey ?? settingsGateway.apiKey)
225
235
  ? { apiKey: requestGateway.apiKey ?? settingsGateway.apiKey }
226
236
  : {}),
227
237
  ...(mergedHeaders === undefined ? {} : { headers: mergedHeaders }),
228
238
  model: requestGateway.model ?? modelSpec.model ?? settingsGateway.model ?? null,
229
- ...(requestGateway.toolPolicy ?? settingsGateway.toolPolicy
239
+ ...((requestGateway.toolPolicy ?? settingsGateway.toolPolicy)
230
240
  ? { toolPolicy: requestGateway.toolPolicy ?? settingsGateway.toolPolicy }
231
241
  : {}),
232
242
  };
233
- return resolveGatewayConfiguration(
234
- mergedGateway,
235
- 'options.gateway',
236
- cwd
237
- );
243
+ return resolveGatewayConfiguration(mergedGateway, 'options.gateway', cwd);
238
244
  }
239
245
 
240
246
  function shouldIncludeAuthEnv(
@@ -286,7 +292,8 @@ function adapterForRuntimeInput(
286
292
  provider: string | null | undefined,
287
293
  settings: Record<string, unknown>
288
294
  ): ProviderAdapter {
289
- const configured = provider ?? optionalString(settings.defaultProvider, 'settings.defaultProvider');
295
+ const configured =
296
+ provider ?? optionalString(settings.defaultProvider, 'settings.defaultProvider');
290
297
  return getProviderAdapter(configured ?? 'claude');
291
298
  }
292
299
 
@@ -309,16 +316,14 @@ function runtimeProviderSettings(
309
316
  );
310
317
  const gateway =
311
318
  provider === 'gateway'
312
- ? normalizeGatewayBuildOptions(
313
- providerSettings,
314
- 'settings.providerSettings.gateway',
315
- cwd
316
- )
319
+ ? normalizeGatewayBuildOptions(providerSettings, 'settings.providerSettings.gateway', cwd)
317
320
  : undefined;
318
321
  if (defaultLevel === undefined) {
319
322
  return gateway === undefined ? { levelOverrides } : { levelOverrides, gateway };
320
323
  }
321
- return gateway === undefined ? { defaultLevel, levelOverrides } : { defaultLevel, levelOverrides, gateway };
324
+ return gateway === undefined
325
+ ? { defaultLevel, levelOverrides }
326
+ : { defaultLevel, levelOverrides, gateway };
322
327
  }
323
328
 
324
329
  function runtimeHelpCommand(provider: ProviderId): CommandParts {
@@ -333,7 +338,11 @@ function probeGatewayProvider(adapter: ProviderAdapter): RuntimeProviderProbe {
333
338
  try {
334
339
  const settings = loadRuntimeSettings();
335
340
  const providerSettings = runtimeProviderSettings(settings, 'gateway', process.cwd());
336
- resolveGatewayConfiguration(providerSettings.gateway, 'settings.providerSettings.gateway', process.cwd());
341
+ resolveGatewayConfiguration(
342
+ providerSettings.gateway,
343
+ 'settings.providerSettings.gateway',
344
+ process.cwd()
345
+ );
337
346
  return {
338
347
  available: true,
339
348
  helpText: 'Bundled gateway runner',
@@ -351,8 +360,20 @@ function probeGatewayProvider(adapter: ProviderAdapter): RuntimeProviderProbe {
351
360
  }
352
361
 
353
362
  function loadRuntimeSettings(): Record<string, unknown> {
354
- const settings = loadSettingsFn();
355
- return requiredRecord(settings, 'loadSettings');
363
+ if (Object.prototype.hasOwnProperty.call(process.env, LEGACY_ISOLATED_PROVIDER_SETTINGS_ENV)) {
364
+ throw new Error(
365
+ `${LEGACY_ISOLATED_PROVIDER_SETTINGS_ENV} is not a trusted settings channel; use the settings file.`
366
+ );
367
+ }
368
+ return requiredRecord(loadSettingsFn(), 'loadSettings');
369
+ }
370
+
371
+ function rejectCallerSuppliedModelProvenance(input: SingleAgentProviderCommandInput): void {
372
+ if (Object.prototype.hasOwnProperty.call(input, 'modelSpecSource')) {
373
+ throw new Error(
374
+ 'modelSpecSource is not accepted at the child provider boundary; use modelLevel and effective provider settings.'
375
+ );
376
+ }
356
377
  }
357
378
 
358
379
  function moduleFunction(moduleValue: unknown, field: string): UnknownFunction {
@@ -456,10 +477,7 @@ function requiredRecord(value: unknown, field: string): Record<string, unknown>
456
477
  throw new Error(`${field} must be an object.`);
457
478
  }
458
479
 
459
- function stringRecordFromUnknown(
460
- value: unknown,
461
- field: string
462
- ): Readonly<Record<string, string>> {
480
+ function stringRecordFromUnknown(value: unknown, field: string): Readonly<Record<string, string>> {
463
481
  if (value === undefined || value === null) return {};
464
482
  const record = requiredRecord(value, field);
465
483
  const result: Record<string, string> = {};
@@ -367,6 +367,7 @@ export interface ProviderAdapter {
367
367
  createParserState(): ProviderParserState;
368
368
  resolveModelSpec(level: ModelLevel, overrides?: LevelOverrides): ResolvedModelSpec;
369
369
  validateModelId(modelId: string | null | undefined): string | null | undefined;
370
+ validateConfiguredModelId?(modelId: string | null | undefined): string | null | undefined;
370
371
  classifyError(error: unknown): ErrorClassification;
371
372
  }
372
373
 
@@ -103,10 +103,11 @@ class AgentWrapper {
103
103
  const taskRunner = options.taskRunner;
104
104
  this.mockSpawnFn = (args, { context }) => {
105
105
  const spec = this._resolveModelSpec();
106
+ const source = this._resolveModelSpecSource();
106
107
  return taskRunner.run(context, {
107
108
  agentId: this.id,
108
- model: this._selectModel(),
109
- modelSpec: spec,
109
+ ...(source === 'direct' ? { model: spec.model } : { modelLevel: spec.level }),
110
+ ...(spec.reasoningEffort ? { reasoningEffort: spec.reasoningEffort } : {}),
110
111
  provider: this._resolveProvider(),
111
112
  cwd: this.config.cwd || process.cwd(),
112
113
  worktreePath: this.worktree?.path || null,
@@ -222,6 +223,17 @@ class AgentWrapper {
222
223
  return applyReasoningOverride({ ...spec, level }, this.config.reasoningEffort);
223
224
  }
224
225
 
226
+ _resolveModelSpecSource() {
227
+ if (this.modelConfig.type === 'rules') {
228
+ for (const rule of this.modelConfig.rules) {
229
+ if (this._matchesIterationRange(rule.iterations)) {
230
+ return rule.model ? 'direct' : 'provider-level';
231
+ }
232
+ }
233
+ }
234
+ return this.modelConfig.model ? 'direct' : 'provider-level';
235
+ }
236
+
225
237
  /**
226
238
  * Publish a message to the message bus, always including sender_model
227
239
  * @private
@@ -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(
@@ -727,7 +727,10 @@ class IsolationManager {
727
727
 
728
728
  args.push(containerId, ...command);
729
729
 
730
- return spawn('docker', args, {
730
+ // spawn() throws on null bytes in argv; strip them before they get there.
731
+ const safeArgs = args.map((arg) => (typeof arg === 'string' ? arg.replace(/\0/g, '') : arg));
732
+
733
+ return spawn('docker', safeArgs, {
731
734
  stdio: ['pipe', 'pipe', 'pipe'],
732
735
  ...options.spawnOptions,
733
736
  });
@@ -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
  }