@the-open-engine/zeroshot 6.15.0 → 6.16.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 (64) hide show
  1. package/README.md +10 -7
  2. package/cli/commands/providers.js +1 -0
  3. package/cli/index.js +45 -4
  4. package/lib/agent-cli-provider/adapters/codex.d.ts.map +1 -1
  5. package/lib/agent-cli-provider/adapters/codex.js +14 -2
  6. package/lib/agent-cli-provider/adapters/codex.js.map +1 -1
  7. package/lib/agent-cli-provider/adapters/common.d.ts +1 -0
  8. package/lib/agent-cli-provider/adapters/common.d.ts.map +1 -1
  9. package/lib/agent-cli-provider/adapters/common.js +14 -0
  10. package/lib/agent-cli-provider/adapters/common.js.map +1 -1
  11. package/lib/agent-cli-provider/adapters/index.d.ts.map +1 -1
  12. package/lib/agent-cli-provider/adapters/index.js +6 -1
  13. package/lib/agent-cli-provider/adapters/index.js.map +1 -1
  14. package/lib/agent-cli-provider/adapters/opencode.d.ts.map +1 -1
  15. package/lib/agent-cli-provider/adapters/opencode.js +52 -3
  16. package/lib/agent-cli-provider/adapters/opencode.js.map +1 -1
  17. package/lib/agent-cli-provider/contract-actions.d.ts.map +1 -1
  18. package/lib/agent-cli-provider/contract-actions.js +19 -8
  19. package/lib/agent-cli-provider/contract-actions.js.map +1 -1
  20. package/lib/agent-cli-provider/contract-fallback.d.ts.map +1 -1
  21. package/lib/agent-cli-provider/contract-fallback.js +9 -0
  22. package/lib/agent-cli-provider/contract-fallback.js.map +1 -1
  23. package/lib/agent-cli-provider/contract-options.d.ts.map +1 -1
  24. package/lib/agent-cli-provider/contract-options.js +2 -0
  25. package/lib/agent-cli-provider/contract-options.js.map +1 -1
  26. package/lib/agent-cli-provider/errors.d.ts +7 -0
  27. package/lib/agent-cli-provider/errors.d.ts.map +1 -1
  28. package/lib/agent-cli-provider/errors.js +14 -0
  29. package/lib/agent-cli-provider/errors.js.map +1 -1
  30. package/lib/agent-cli-provider/index.d.ts +1 -1
  31. package/lib/agent-cli-provider/index.d.ts.map +1 -1
  32. package/lib/agent-cli-provider/index.js.map +1 -1
  33. package/lib/agent-cli-provider/provider-registry.d.ts +20 -3
  34. package/lib/agent-cli-provider/provider-registry.d.ts.map +1 -1
  35. package/lib/agent-cli-provider/provider-registry.js +15 -2
  36. package/lib/agent-cli-provider/provider-registry.js.map +1 -1
  37. package/lib/agent-cli-provider/single-agent-runtime.d.ts +14 -2
  38. package/lib/agent-cli-provider/single-agent-runtime.d.ts.map +1 -1
  39. package/lib/agent-cli-provider/single-agent-runtime.js +97 -22
  40. package/lib/agent-cli-provider/single-agent-runtime.js.map +1 -1
  41. package/lib/agent-cli-provider/types.d.ts +11 -1
  42. package/lib/agent-cli-provider/types.d.ts.map +1 -1
  43. package/lib/agent-cli-provider/types.js.map +1 -1
  44. package/package.json +1 -1
  45. package/src/agent/agent-lifecycle.js +52 -1
  46. package/src/agent/agent-task-executor.js +8 -3
  47. package/src/agent-cli-provider/adapters/codex.ts +23 -2
  48. package/src/agent-cli-provider/adapters/common.ts +14 -0
  49. package/src/agent-cli-provider/adapters/index.ts +16 -2
  50. package/src/agent-cli-provider/adapters/opencode.ts +60 -4
  51. package/src/agent-cli-provider/contract-actions.ts +20 -9
  52. package/src/agent-cli-provider/contract-fallback.ts +9 -0
  53. package/src/agent-cli-provider/contract-options.ts +2 -0
  54. package/src/agent-cli-provider/errors.ts +14 -0
  55. package/src/agent-cli-provider/index.ts +1 -0
  56. package/src/agent-cli-provider/provider-registry.ts +20 -2
  57. package/src/agent-cli-provider/single-agent-runtime.ts +144 -24
  58. package/src/agent-cli-provider/types.ts +12 -1
  59. package/src/claude-task-runner.js +11 -0
  60. package/src/orchestrator.js +8 -3
  61. package/src/providers/index.js +6 -0
  62. package/src/task-run-model-args.js +58 -40
  63. package/src/task-spawn-cleanup-ownership.js +3 -0
  64. package/src/task-startup-error.js +68 -0
@@ -89,6 +89,7 @@ export interface ResolvedGatewayBuildOptions {
89
89
  export interface BaseCliFeatures {
90
90
  readonly provider?: ProviderId;
91
91
  readonly unknown?: boolean;
92
+ readonly supportsWebSearch?: boolean;
92
93
  }
93
94
 
94
95
  export interface ClaudeCliFeatures extends BaseCliFeatures {
@@ -116,6 +117,7 @@ export interface CodexCliFeatures extends BaseCliFeatures {
116
117
  readonly supportsModel: boolean;
117
118
  readonly supportsSkipGitRepoCheck: boolean;
118
119
  readonly supportsResume: boolean;
120
+ readonly supportsWebSearch: boolean;
119
121
  }
120
122
 
121
123
  export interface GeminiCliFeatures extends BaseCliFeatures {
@@ -134,6 +136,8 @@ export interface OpencodeCliFeatures extends BaseCliFeatures {
134
136
  readonly supportsDir: boolean;
135
137
  readonly supportsCwd: boolean;
136
138
  readonly supportsAutoApprove: false;
139
+ readonly supportsResume: boolean;
140
+ readonly supportsWebSearch: boolean;
137
141
  }
138
142
 
139
143
  export interface PiCliFeatures extends BaseCliFeatures {
@@ -218,6 +222,7 @@ export interface CliFeatureOverrides {
218
222
  readonly supportsAddDir?: boolean;
219
223
  readonly supportsMcpConfig?: boolean;
220
224
  readonly supportsResume?: boolean;
225
+ readonly supportsWebSearch?: boolean;
221
226
  readonly supportsBundledRunner?: boolean;
222
227
  readonly supportsAcpStdio?: boolean;
223
228
  readonly supportsPromptImages?: boolean;
@@ -252,6 +257,11 @@ export interface RedactionMetadata {
252
257
  readonly source?: string;
253
258
  }
254
259
 
260
+ export interface WebSearchAttestation {
261
+ readonly requested: boolean;
262
+ readonly effective: boolean;
263
+ }
264
+
255
265
  export interface CommandSpec {
256
266
  readonly binary: string;
257
267
  readonly args: readonly string[];
@@ -272,6 +282,7 @@ export interface BuildProviderCommandOptions {
272
282
  readonly autoApprove?: boolean;
273
283
  readonly resumeSessionId?: string;
274
284
  readonly continueSession?: boolean;
285
+ readonly webSearch?: boolean;
275
286
  readonly claudeSettingsFile?: string;
276
287
  readonly cliFeatures?: CliFeatureOverrides;
277
288
  readonly authEnv?: Readonly<Record<string, string>>;
@@ -373,7 +384,7 @@ export interface ProviderAdapter {
373
384
  readonly defaultLevel: ModelLevel;
374
385
  readonly defaultMaxLevel: ModelLevel;
375
386
  readonly defaultMinLevel: ModelLevel;
376
- detectCliFeatures(helpText?: string | null): ProviderCliFeatures;
387
+ detectCliFeatures(helpText?: string | null, versionText?: string | null): ProviderCliFeatures;
377
388
  buildCommand(context: string, options?: BuildProviderCommandOptions): CommandSpec;
378
389
  extractSessionId?(line: string): string | null;
379
390
  parseEvent(line: string, state: ProviderParserState): ProviderParseResult;
@@ -34,6 +34,7 @@ const {
34
34
  appendTaskRunModelArgs,
35
35
  wrapTaskRunWithIsolatedSettings,
36
36
  } = require('./task-run-model-args');
37
+ const { parseTaskStartupError } = require('./task-startup-error');
37
38
 
38
39
  function rejectCallerSuppliedModelProvenance(options) {
39
40
  if (Object.prototype.hasOwnProperty.call(options, 'modelSpecSource')) {
@@ -98,6 +99,7 @@ function runCommand(command, args, options = {}, callback = null) {
98
99
  }
99
100
 
100
101
  const TASK_TERMINAL_STATUSES = new Set(['completed', 'failed', 'killed', 'stale', 'cancelled']);
102
+ const TASK_STARTUP_STDERR_MAX_CHARS = 500;
101
103
 
102
104
  async function cleanupPersistedTaskAfterLaunchFailure(ctPath, taskId) {
103
105
  let lastError = null;
@@ -808,6 +810,7 @@ class ClaudeTaskRunner extends TaskRunner {
808
810
  return new Promise((resolve, reject) => {
809
811
  let output = '';
810
812
  let resolved = false;
813
+ let stderr = '';
811
814
 
812
815
  const proc = manager.spawnInContainer(clusterId, command, {
813
816
  env: {
@@ -828,6 +831,7 @@ class ClaudeTaskRunner extends TaskRunner {
828
831
 
829
832
  proc.stderr.on('data', (/** @type {Buffer} */ data) => {
830
833
  const chunk = data.toString();
834
+ stderr = `${stderr}${chunk}`.slice(-TASK_STARTUP_STDERR_MAX_CHARS);
831
835
  if (!this.quiet) {
832
836
  console.error(`[${agentId}] stderr:`, chunk);
833
837
  }
@@ -836,6 +840,13 @@ class ClaudeTaskRunner extends TaskRunner {
836
840
  proc.on('close', (/** @type {number|null} */ code) => {
837
841
  if (resolved) return;
838
842
  resolved = true;
843
+ if (code !== 0) {
844
+ const startupError = parseTaskStartupError(stderr);
845
+ if (startupError) {
846
+ reject(startupError);
847
+ return;
848
+ }
849
+ }
839
850
 
840
851
  resolve({
841
852
  success: code === 0,
@@ -2716,12 +2716,17 @@ class Orchestrator {
2716
2716
  });
2717
2717
 
2718
2718
  for (const error of errors) {
2719
+ const errorData = error.content?.data || {};
2719
2720
  const failureInfo = {
2720
2721
  agentId: error.sender,
2721
- taskId: error.content?.data?.taskId || null,
2722
- iteration: error.content?.data?.iteration || 0,
2723
- error: error.content?.data?.error || error.content?.text,
2722
+ taskId: errorData.taskId || null,
2723
+ iteration: errorData.iteration || 0,
2724
+ error: errorData.error || error.content?.text,
2724
2725
  timestamp: error.timestamp,
2726
+ ...(errorData.code !== undefined ? { code: errorData.code } : {}),
2727
+ ...(errorData.permanent !== undefined ? { permanent: errorData.permanent } : {}),
2728
+ ...(errorData.provider !== undefined ? { provider: errorData.provider } : {}),
2729
+ ...(errorData.capability !== undefined ? { capability: errorData.capability } : {}),
2725
2730
  };
2726
2731
  if (this._hasDurableProgressAfterFailure(cluster, clusterId, failureInfo)) {
2727
2732
  this._log(
@@ -159,6 +159,12 @@ class RuntimeProvider extends BaseProvider {
159
159
  validateSettings(settings) {
160
160
  const baseError = super.validateSettings(settings);
161
161
  if (baseError) return baseError;
162
+ if (
163
+ Object.prototype.hasOwnProperty.call(settings, 'webSearch') &&
164
+ !this._metadata.settingsFields.includes('webSearch')
165
+ ) {
166
+ return `Unknown provider setting: providerSettings.${this.name}.webSearch`;
167
+ }
162
168
  if (this._metadata.settingsValidator) {
163
169
  const providerError = this._metadata.settingsValidator(settings);
164
170
  if (providerError) return providerError;
@@ -1,3 +1,5 @@
1
+ const { providerSupportsCapability } = require('../lib/provider-names');
2
+
1
3
  /**
2
4
  * Append a resolved model selection to a nested `zeroshot task run` invocation.
3
5
  *
@@ -61,9 +63,9 @@ try {
61
63
 
62
64
  /**
63
65
  * 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.
66
+ * bootstrap. The snapshot is a closed projection containing only an enabled
67
+ * native-search control and, when needed, the selected OpenCode level/model.
68
+ * It never contains credentials or caller-owned keys.
67
69
  *
68
70
  * @param {string[]} command
69
71
  * @param {{providerName: string, settings: Object, modelSpecSource: 'direct'|'provider-level', modelSpec: Object|null|undefined}} context
@@ -71,57 +73,73 @@ try {
71
73
  */
72
74
  function wrapTaskRunWithIsolatedSettings(command, context) {
73
75
  const { providerName, settings, modelSpecSource, modelSpec } = context;
74
- if (providerName !== 'opencode' || modelSpecSource !== 'provider-level') return command;
75
- const snapshot = buildIsolatedSettingsSnapshot(settings, modelSpec);
76
+ if (!providerSupportsCapability(providerName, 'webSearch')) return command;
77
+ const includesOpencodeModel =
78
+ providerName === 'opencode' && modelSpecSource === 'provider-level';
79
+ const snapshot = buildIsolatedSettingsSnapshot(
80
+ settings,
81
+ providerName,
82
+ includesOpencodeModel ? modelSpec : null
83
+ );
76
84
  if (snapshot === null) return command;
77
85
  return ['node', '-e', SETTINGS_BOOTSTRAP_SCRIPT, snapshot, ...command];
78
86
  }
79
87
 
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
+ function buildIsolatedSettingsSnapshot(settings, providerName, opencodeModelSpec) {
88
89
  const providerSettings = ownRecordValue(settings, 'providerSettings', 'settings') ?? {};
89
- const opencodeSettings = ownRecordValue(
90
+ const selectedSettings = ownRecordValue(
90
91
  providerSettings,
91
- 'opencode',
92
+ providerName,
92
93
  'settings.providerSettings'
93
94
  );
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.`);
95
+ const webSearch = selectedSettings?.webSearch;
96
+ if (webSearch !== undefined && typeof webSearch !== 'boolean') {
97
+ throw permanentError(`settings.providerSettings.${providerName}.webSearch must be a boolean.`);
110
98
  }
111
- if (modelSpec?.model !== configuredModel) {
112
- throw permanentError(
113
- `Provider-level model "${modelSpec?.model}" does not match the effective isolated ${modelSpec?.level} model "${configuredModel}".`
99
+
100
+ const snapshot = {};
101
+ if (webSearch === true) snapshot.webSearch = true;
102
+ if (opencodeModelSpec !== null) {
103
+ const level = opencodeModelSpec?.level;
104
+ if (!['level1', 'level2', 'level3'].includes(level)) {
105
+ throw permanentError(
106
+ 'Provider-level isolated OpenCode selections require a valid model level.'
107
+ );
108
+ }
109
+
110
+ const levelOverrides = ownRecordValue(
111
+ selectedSettings,
112
+ 'levelOverrides',
113
+ 'settings.providerSettings.opencode'
114
+ );
115
+ const levelOverride = ownRecordValue(
116
+ levelOverrides,
117
+ level,
118
+ 'settings.providerSettings.opencode.levelOverrides'
114
119
  );
120
+ const configuredModel =
121
+ levelOverride && Object.prototype.hasOwnProperty.call(levelOverride, 'model')
122
+ ? levelOverride.model
123
+ : null;
124
+ if (configuredModel !== null && typeof configuredModel !== 'string') {
125
+ throw permanentError(`Configured isolated OpenCode ${level} model must be a string or null.`);
126
+ }
127
+ if (opencodeModelSpec.model !== configuredModel) {
128
+ throw permanentError(
129
+ `Provider-level model "${opencodeModelSpec.model}" does not match the effective isolated ${level} model "${configuredModel}".`
130
+ );
131
+ }
132
+ if (configuredModel !== null) {
133
+ snapshot.levelOverrides = {
134
+ [level]: { model: configuredModel },
135
+ };
136
+ }
115
137
  }
116
- if (configuredModel === null) return null;
117
138
 
139
+ if (Object.keys(snapshot).length === 0) return null;
118
140
  return JSON.stringify({
119
141
  providerSettings: {
120
- opencode: {
121
- levelOverrides: {
122
- [level]: { model: configuredModel },
123
- },
124
- },
142
+ [providerName]: snapshot,
125
143
  },
126
144
  });
127
145
  }
@@ -1,4 +1,5 @@
1
1
  const { randomUUID } = require('node:crypto');
2
+ const { parseTaskStartupError } = require('./task-startup-error');
2
3
 
3
4
  const TASK_SPAWN_OWNERSHIP_TOKEN_ENV = 'ZEROSHOT_TASK_SPAWN_OWNERSHIP_TOKEN';
4
5
 
@@ -38,6 +39,8 @@ function requireTaskIdFromWrapperResult({
38
39
  persistedTaskId = null,
39
40
  }) {
40
41
  if (code !== 0) {
42
+ const startupError = parseTaskStartupError(stderr);
43
+ if (startupError) throw startupError;
41
44
  throw new Error(`zeroshot task run failed with code ${code}: ${stderr}`);
42
45
  }
43
46
  if (!persistedTaskId) {
@@ -0,0 +1,68 @@
1
+ const TASK_STARTUP_ERROR_PREFIX = 'ZEROSHOT_TASK_STARTUP_ERROR:';
2
+ const SUPPORTED_STARTUP_ERROR_CODES = new Set(['unsupported-capability']);
3
+
4
+ function createUnsupportedProviderCapabilityError(provider, capability, message) {
5
+ const error = new Error(message);
6
+ error.name = 'UnsupportedProviderCapabilityError';
7
+ error.code = 'unsupported-capability';
8
+ error.permanent = true;
9
+ error.provider = provider;
10
+ error.capability = capability;
11
+ return error;
12
+ }
13
+
14
+ function serializeTaskStartupError(error) {
15
+ if (
16
+ !SUPPORTED_STARTUP_ERROR_CODES.has(error?.code) ||
17
+ error?.permanent !== true
18
+ ) {
19
+ return null;
20
+ }
21
+ const payload = {
22
+ version: 1,
23
+ code: error.code,
24
+ permanent: true,
25
+ message: String(error.message || error.code),
26
+ };
27
+ if (typeof error.provider === 'string') payload.provider = error.provider;
28
+ if (typeof error.capability === 'string') payload.capability = error.capability;
29
+ return `${TASK_STARTUP_ERROR_PREFIX}${JSON.stringify(payload)}`;
30
+ }
31
+
32
+ function parseTaskStartupError(stderr) {
33
+ const lines = String(stderr || '').split(/\r?\n/);
34
+ let line = lines.pop();
35
+ while (line === '' && lines.length > 0) line = lines.pop();
36
+ if (!line?.startsWith(TASK_STARTUP_ERROR_PREFIX)) return null;
37
+
38
+ let parsed;
39
+ try {
40
+ parsed = JSON.parse(line.slice(TASK_STARTUP_ERROR_PREFIX.length));
41
+ } catch {
42
+ return null;
43
+ }
44
+ if (
45
+ parsed?.version !== 1 ||
46
+ !SUPPORTED_STARTUP_ERROR_CODES.has(parsed.code) ||
47
+ parsed.permanent !== true ||
48
+ typeof parsed.message !== 'string' ||
49
+ parsed.message.length === 0 ||
50
+ (parsed.provider !== undefined && typeof parsed.provider !== 'string') ||
51
+ (parsed.capability !== undefined && typeof parsed.capability !== 'string')
52
+ ) {
53
+ return null;
54
+ }
55
+ const error = new Error(parsed.message);
56
+ error.code = parsed.code;
57
+ error.permanent = true;
58
+ if (parsed.provider !== undefined) error.provider = parsed.provider;
59
+ if (parsed.capability !== undefined) error.capability = parsed.capability;
60
+ return error;
61
+ }
62
+
63
+ module.exports = {
64
+ TASK_STARTUP_ERROR_PREFIX,
65
+ createUnsupportedProviderCapabilityError,
66
+ parseTaskStartupError,
67
+ serializeTaskStartupError,
68
+ };