@the-open-engine/zeroshot 6.15.0 → 6.17.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 (69) 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/omp.d.ts +3 -0
  15. package/lib/agent-cli-provider/adapters/omp.d.ts.map +1 -0
  16. package/lib/agent-cli-provider/adapters/omp.js +356 -0
  17. package/lib/agent-cli-provider/adapters/omp.js.map +1 -0
  18. package/lib/agent-cli-provider/adapters/opencode.d.ts.map +1 -1
  19. package/lib/agent-cli-provider/adapters/opencode.js +52 -3
  20. package/lib/agent-cli-provider/adapters/opencode.js.map +1 -1
  21. package/lib/agent-cli-provider/contract-actions.d.ts.map +1 -1
  22. package/lib/agent-cli-provider/contract-actions.js +19 -8
  23. package/lib/agent-cli-provider/contract-actions.js.map +1 -1
  24. package/lib/agent-cli-provider/contract-fallback.d.ts.map +1 -1
  25. package/lib/agent-cli-provider/contract-fallback.js +9 -0
  26. package/lib/agent-cli-provider/contract-fallback.js.map +1 -1
  27. package/lib/agent-cli-provider/contract-options.d.ts.map +1 -1
  28. package/lib/agent-cli-provider/contract-options.js +7 -0
  29. package/lib/agent-cli-provider/contract-options.js.map +1 -1
  30. package/lib/agent-cli-provider/errors.d.ts +7 -0
  31. package/lib/agent-cli-provider/errors.d.ts.map +1 -1
  32. package/lib/agent-cli-provider/errors.js +14 -0
  33. package/lib/agent-cli-provider/errors.js.map +1 -1
  34. package/lib/agent-cli-provider/index.d.ts +1 -1
  35. package/lib/agent-cli-provider/index.d.ts.map +1 -1
  36. package/lib/agent-cli-provider/index.js.map +1 -1
  37. package/lib/agent-cli-provider/provider-registry.d.ts +66 -3
  38. package/lib/agent-cli-provider/provider-registry.d.ts.map +1 -1
  39. package/lib/agent-cli-provider/provider-registry.js +54 -2
  40. package/lib/agent-cli-provider/provider-registry.js.map +1 -1
  41. package/lib/agent-cli-provider/single-agent-runtime.d.ts +14 -2
  42. package/lib/agent-cli-provider/single-agent-runtime.d.ts.map +1 -1
  43. package/lib/agent-cli-provider/single-agent-runtime.js +97 -22
  44. package/lib/agent-cli-provider/single-agent-runtime.js.map +1 -1
  45. package/lib/agent-cli-provider/types.d.ts +30 -2
  46. package/lib/agent-cli-provider/types.d.ts.map +1 -1
  47. package/lib/agent-cli-provider/types.js.map +1 -1
  48. package/package.json +1 -1
  49. package/src/agent/agent-lifecycle.js +52 -1
  50. package/src/agent/agent-task-executor.js +8 -3
  51. package/src/agent-cli-provider/adapters/codex.ts +23 -2
  52. package/src/agent-cli-provider/adapters/common.ts +14 -0
  53. package/src/agent-cli-provider/adapters/index.ts +16 -2
  54. package/src/agent-cli-provider/adapters/omp.ts +448 -0
  55. package/src/agent-cli-provider/adapters/opencode.ts +60 -4
  56. package/src/agent-cli-provider/contract-actions.ts +20 -9
  57. package/src/agent-cli-provider/contract-fallback.ts +9 -0
  58. package/src/agent-cli-provider/contract-options.ts +7 -0
  59. package/src/agent-cli-provider/errors.ts +14 -0
  60. package/src/agent-cli-provider/index.ts +2 -0
  61. package/src/agent-cli-provider/provider-registry.ts +59 -2
  62. package/src/agent-cli-provider/single-agent-runtime.ts +144 -24
  63. package/src/agent-cli-provider/types.ts +32 -1
  64. package/src/claude-task-runner.js +11 -0
  65. package/src/orchestrator.js +8 -3
  66. package/src/providers/index.js +6 -0
  67. package/src/task-run-model-args.js +58 -40
  68. package/src/task-spawn-cleanup-ownership.js +3 -0
  69. package/src/task-startup-error.js +68 -0
@@ -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
+ };