@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.
- package/lib/agent-cli-provider/adapters/claude.d.ts.map +1 -1
- package/lib/agent-cli-provider/adapters/claude.js +1 -0
- package/lib/agent-cli-provider/adapters/claude.js.map +1 -1
- package/lib/agent-cli-provider/adapters/codex.d.ts.map +1 -1
- package/lib/agent-cli-provider/adapters/codex.js +3 -0
- package/lib/agent-cli-provider/adapters/codex.js.map +1 -1
- 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 +145 -35
- package/src/agent/agent-task-executor.js +23 -10
- package/src/agent-cli-provider/adapters/claude.ts +1 -0
- package/src/agent-cli-provider/adapters/codex.ts +3 -0
- 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/isolation-manager.js +4 -1
- package/src/providers/index.js +5 -0
- package/src/task-run-model-args.js +155 -0
- package/task-lib/runner.js +66 -26
|
@@ -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
|
+
}
|