@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
@@ -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
+ }