@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.
- package/lib/agent-cli-provider/adapters/common.js +1 -1
- package/lib/agent-cli-provider/adapters/common.js.map +1 -1
- package/lib/agent-cli-provider/adapters/opencode-models.d.ts +7 -0
- package/lib/agent-cli-provider/adapters/opencode-models.d.ts.map +1 -0
- package/lib/agent-cli-provider/adapters/opencode-models.js +87 -0
- package/lib/agent-cli-provider/adapters/opencode-models.js.map +1 -0
- package/lib/agent-cli-provider/adapters/opencode.d.ts.map +1 -1
- package/lib/agent-cli-provider/adapters/opencode.js +6 -58
- package/lib/agent-cli-provider/adapters/opencode.js.map +1 -1
- package/lib/agent-cli-provider/single-agent-runtime.d.ts.map +1 -1
- package/lib/agent-cli-provider/single-agent-runtime.js +27 -13
- package/lib/agent-cli-provider/single-agent-runtime.js.map +1 -1
- package/lib/agent-cli-provider/types.d.ts +1 -0
- package/lib/agent-cli-provider/types.d.ts.map +1 -1
- package/lib/agent-cli-provider/types.js.map +1 -1
- package/package.json +1 -1
- package/scripts/assert-release-published.js +46 -15
- package/src/agent/agent-task-executor.js +18 -9
- package/src/agent-cli-provider/adapters/common.ts +1 -1
- package/src/agent-cli-provider/adapters/opencode-models.ts +100 -0
- package/src/agent-cli-provider/adapters/opencode.ts +8 -65
- package/src/agent-cli-provider/single-agent-runtime.ts +51 -33
- package/src/agent-cli-provider/types.ts +1 -0
- package/src/agent-wrapper.js +14 -2
- package/src/claude-task-runner.js +121 -37
- package/src/config-validator.js +2 -2
- package/src/providers/index.js +5 -0
- package/src/task-run-model-args.js +155 -0
- 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
|
|
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
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
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 {
|
|
243
|
+
return {
|
|
244
|
+
source: 'direct',
|
|
245
|
+
modelSpec: { model, reasoningEffort },
|
|
246
|
+
};
|
|
217
247
|
}
|
|
218
248
|
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
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
|
|
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({
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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) => {
|
package/src/config-validator.js
CHANGED
|
@@ -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.
|
|
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(
|
package/src/providers/index.js
CHANGED
|
@@ -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
|
+
};
|
package/task-lib/runner.js
CHANGED
|
@@ -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 =
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
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,
|
|
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
|
-
...
|
|
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
|
|
108
|
-
|
|
109
|
-
|
|
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
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
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
|
+
}
|