@the-open-engine/zeroshot 6.17.0 → 6.18.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 (76) hide show
  1. package/cli/index.js +9 -13
  2. package/lib/agent-cli-provider/adapters/claude.d.ts +2 -2
  3. package/lib/agent-cli-provider/adapters/claude.d.ts.map +1 -1
  4. package/lib/agent-cli-provider/adapters/claude.js +30 -0
  5. package/lib/agent-cli-provider/adapters/claude.js.map +1 -1
  6. package/lib/agent-cli-provider/adapters/codex.d.ts +2 -2
  7. package/lib/agent-cli-provider/adapters/codex.d.ts.map +1 -1
  8. package/lib/agent-cli-provider/adapters/codex.js +37 -0
  9. package/lib/agent-cli-provider/adapters/codex.js.map +1 -1
  10. package/lib/agent-cli-provider/adapters/gemini.d.ts +2 -2
  11. package/lib/agent-cli-provider/adapters/gemini.d.ts.map +1 -1
  12. package/lib/agent-cli-provider/adapters/gemini.js +115 -4
  13. package/lib/agent-cli-provider/adapters/gemini.js.map +1 -1
  14. package/lib/agent-cli-provider/adapters/opencode.d.ts +2 -2
  15. package/lib/agent-cli-provider/adapters/opencode.d.ts.map +1 -1
  16. package/lib/agent-cli-provider/adapters/opencode.js +108 -0
  17. package/lib/agent-cli-provider/adapters/opencode.js.map +1 -1
  18. package/lib/agent-cli-provider/contract-options.d.ts.map +1 -1
  19. package/lib/agent-cli-provider/contract-options.js +12 -0
  20. package/lib/agent-cli-provider/contract-options.js.map +1 -1
  21. package/lib/agent-cli-provider/index.d.ts +2 -2
  22. package/lib/agent-cli-provider/index.d.ts.map +1 -1
  23. package/lib/agent-cli-provider/index.js +2 -1
  24. package/lib/agent-cli-provider/index.js.map +1 -1
  25. package/lib/agent-cli-provider/provider-registry.d.ts +19 -7
  26. package/lib/agent-cli-provider/provider-registry.d.ts.map +1 -1
  27. package/lib/agent-cli-provider/provider-registry.js +4 -0
  28. package/lib/agent-cli-provider/provider-registry.js.map +1 -1
  29. package/lib/agent-cli-provider/single-agent-runtime.d.ts.map +1 -1
  30. package/lib/agent-cli-provider/single-agent-runtime.js +35 -4
  31. package/lib/agent-cli-provider/single-agent-runtime.js.map +1 -1
  32. package/lib/agent-cli-provider/types.d.ts +30 -3
  33. package/lib/agent-cli-provider/types.d.ts.map +1 -1
  34. package/lib/agent-cli-provider/types.js.map +1 -1
  35. package/lib/cluster/errors.cjs +4 -1
  36. package/lib/cluster/errors.d.ts +2 -0
  37. package/lib/cluster/errors.mjs +2 -0
  38. package/lib/cluster/index.cjs +8 -1
  39. package/lib/cluster/index.d.ts +4 -1
  40. package/lib/cluster/index.mjs +4 -1
  41. package/lib/cluster/json-source.cjs +61 -0
  42. package/lib/cluster/json-source.d.ts +4 -0
  43. package/lib/cluster/json-source.mjs +55 -0
  44. package/lib/cluster/payload-value.cjs +76 -0
  45. package/lib/cluster/payload-value.d.ts +7 -0
  46. package/lib/cluster/payload-value.mjs +72 -0
  47. package/lib/cluster/validators.cjs +21 -0
  48. package/lib/cluster/validators.d.ts +4 -1
  49. package/lib/cluster/validators.mjs +19 -1
  50. package/lib/provider-names.js +5 -0
  51. package/package.json +2 -2
  52. package/src/agent/agent-task-executor.js +194 -410
  53. package/src/agent/output-extraction.js +39 -14
  54. package/src/agent/output-reformatter.js +154 -111
  55. package/src/agent-cli-provider/adapters/claude.ts +39 -2
  56. package/src/agent-cli-provider/adapters/codex.ts +56 -2
  57. package/src/agent-cli-provider/adapters/gemini.ts +102 -6
  58. package/src/agent-cli-provider/adapters/opencode.ts +85 -2
  59. package/src/agent-cli-provider/contract-options.ts +16 -0
  60. package/src/agent-cli-provider/index.ts +4 -0
  61. package/src/agent-cli-provider/provider-registry.ts +23 -3
  62. package/src/agent-cli-provider/single-agent-runtime.ts +50 -4
  63. package/src/agent-cli-provider/types.ts +36 -2
  64. package/src/agent-wrapper.js +2 -2
  65. package/src/cluster/errors.ts +1 -0
  66. package/src/cluster/index.ts +4 -0
  67. package/src/cluster/json-source.ts +71 -0
  68. package/src/cluster/payload-value.ts +84 -0
  69. package/src/cluster/validators.ts +31 -2
  70. package/src/orchestrator.js +33 -13
  71. package/task-lib/attachable-watcher.js +1 -0
  72. package/task-lib/command-spec-cleanup.js +57 -11
  73. package/task-lib/commands/run.js +1 -0
  74. package/task-lib/provider-session-capture.js +8 -0
  75. package/task-lib/runner.js +9 -6
  76. package/task-lib/watcher.js +1 -0
@@ -1,3 +1,9 @@
1
+ import { randomUUID } from 'node:crypto';
2
+ import * as fs from 'node:fs';
3
+ import * as os from 'node:os';
4
+ import * as path from 'node:path';
5
+
6
+ import { UnsupportedProviderCapabilityError } from '../errors';
1
7
  import { appendJsonSchemaPrompt } from '../schema';
2
8
  import {
3
9
  getOrStringFromKeys,
@@ -16,7 +22,7 @@ import {
16
22
  type ModelCatalogEntry,
17
23
  type ModelLevel,
18
24
  type OutputEvent,
19
- type ProviderAdapter,
25
+ type StructuredOutputRecoveryAdapter,
20
26
  type ProviderParserState,
21
27
  type ResolvedModelSpec,
22
28
  type WarningMetadata,
@@ -52,6 +58,7 @@ function detectCliFeatures(helpText?: string | null): GeminiCliFeatures {
52
58
  supportsAutoApprove: unknown ? true : /--yolo\b/.test(help),
53
59
  supportsCwd: unknown ? true : /--cwd\b/.test(help),
54
60
  supportsModel: unknown ? true : /\s-m\b/.test(help) || /--model\b/.test(help),
61
+ supportsAdminPolicy: !unknown && /--admin-policy\b/.test(help),
55
62
  unknown,
56
63
  };
57
64
  }
@@ -109,6 +116,75 @@ function buildCommand(context: string, options: BuildProviderCommandOptions = {}
109
116
  });
110
117
  }
111
118
 
119
+ function standardAdminPolicyDirectory(): string {
120
+ if (process.platform === 'darwin') {
121
+ return '/Library/Application Support/GeminiCli/policies';
122
+ }
123
+ if (process.platform === 'win32') {
124
+ return path.join(process.env.ProgramData || 'C:\\ProgramData', 'gemini-cli', 'policies');
125
+ }
126
+ return '/etc/gemini-cli/policies';
127
+ }
128
+
129
+ function assertSupplementalAdminPolicyEffective(): void {
130
+ const directory = standardAdminPolicyDirectory();
131
+ let entries: fs.Dirent[];
132
+ try {
133
+ entries = fs.readdirSync(directory, { withFileTypes: true });
134
+ } catch (error) {
135
+ if (isRecord(error) && error.code === 'ENOENT') return;
136
+ throw new UnsupportedProviderCapabilityError(
137
+ 'gemini',
138
+ 'structuredOutputRecovery',
139
+ `Gemini structured-output recovery cannot inspect the standard admin-policy directory ${directory}; refuse a supplemental policy that may be ignored.`
140
+ );
141
+ }
142
+ if (!entries.some((entry) => entry.isFile() && entry.name.endsWith('.toml'))) return;
143
+ throw new UnsupportedProviderCapabilityError(
144
+ 'gemini',
145
+ 'structuredOutputRecovery',
146
+ `Gemini ignores --admin-policy when ${directory} contains TOML policies. Remove the conflict or use an administrator-managed deny-all policy before retrying.`
147
+ );
148
+ }
149
+
150
+ function writeRecoveryAdminPolicy(): string {
151
+ const directory = fs.mkdtempSync(path.join(os.tmpdir(), 'zeroshot-gemini-policy-'));
152
+ const policyPath = path.join(directory, `${randomUUID()}.toml`);
153
+ fs.writeFileSync(policyPath, '[[rule]]\ntoolName = "*"\ndecision = "deny"\npriority = 999\n', {
154
+ flag: 'wx',
155
+ mode: 0o600,
156
+ });
157
+ return policyPath;
158
+ }
159
+
160
+ function buildStructuredOutputRecoveryCommand(
161
+ context: string,
162
+ options: BuildProviderCommandOptions = {}
163
+ ): CommandSpec {
164
+ if (optionFeatures(options).supportsAdminPolicy !== true) {
165
+ throw new UnsupportedProviderCapabilityError(
166
+ 'gemini',
167
+ 'structuredOutputRecovery',
168
+ 'Gemini structured-output recovery requires CLI evidence for --admin-policy. Upgrade @google/gemini-cli before retrying.'
169
+ );
170
+ }
171
+ assertSupplementalAdminPolicyEffective();
172
+ const recoveryOptions = { ...options };
173
+ delete recoveryOptions.resumeSessionId;
174
+ delete recoveryOptions.continueSession;
175
+ const spec = buildCommand(context, { ...recoveryOptions, autoApprove: false });
176
+ const policyPath = writeRecoveryAdminPolicy();
177
+ return {
178
+ ...spec,
179
+ args: ['--admin-policy', policyPath, ...spec.args],
180
+ cleanup: [...(spec.cleanup || []), policyPath],
181
+ cleanupMetadata: [
182
+ ...spec.cleanupMetadata,
183
+ { kind: 'temp-file', provider: 'gemini', path: policyPath, reason: 'admin-policy' },
184
+ ],
185
+ };
186
+ }
187
+
112
188
  function normalizeMessageContent(content: unknown): string {
113
189
  if (typeof content === 'string') return content;
114
190
  if (Array.isArray(content)) {
@@ -148,6 +224,11 @@ function parseToolUseEvent(
148
224
  };
149
225
  }
150
226
 
227
+ function geminiErrorMessage(event: Record<string, unknown>, fallback: string): string {
228
+ const error = isRecord(event.error) ? event.error : null;
229
+ return (error && getString(error, 'message')) || getString(event, 'message') || fallback;
230
+ }
231
+
151
232
  function parseToolResultEvent(
152
233
  event: Record<string, unknown>,
153
234
  state: ProviderParserState
@@ -157,20 +238,32 @@ function parseToolResultEvent(
157
238
  ['tool_call_id', 'tool_id', 'id'],
158
239
  state.lastToolId
159
240
  );
241
+ const isError = getString(event, 'status') === 'error';
160
242
  return {
161
243
  type: 'tool_result',
162
244
  toolId,
163
- content: event.output ?? event.content ?? '',
164
- isError: event.success === false,
245
+ content: event.output ?? (isError ? geminiErrorMessage(event, 'Tool failed') : ''),
246
+ isError,
165
247
  };
166
248
  }
167
249
 
168
250
  function parseResultEvent(event: Record<string, unknown>): OutputEvent {
251
+ const success = getString(event, 'status') === 'success';
169
252
  return {
170
253
  type: 'result',
171
- success: event.success !== false,
254
+ success,
172
255
  result: event.result || '',
173
- error: event.success === false ? (getString(event, 'error') ?? 'Result failed') : null,
256
+ error: success ? null : geminiErrorMessage(event, 'Result failed'),
257
+ };
258
+ }
259
+
260
+ function parseErrorEvent(event: Record<string, unknown>): OutputEvent | null {
261
+ if (getString(event, 'severity') !== 'error') return null;
262
+ return {
263
+ type: 'result',
264
+ success: false,
265
+ result: '',
266
+ error: getString(event, 'message') || 'Gemini CLI error',
174
267
  };
175
268
  }
176
269
 
@@ -189,6 +282,8 @@ function parseEvent(line: string, state: ProviderParserState): OutputEvent | nul
189
282
  return parseToolResultEvent(event, state);
190
283
  case 'result':
191
284
  return parseResultEvent(event);
285
+ case 'error':
286
+ return parseErrorEvent(event);
192
287
  default:
193
288
  return null;
194
289
  }
@@ -229,7 +324,7 @@ function classifyError(error: unknown): ErrorClassification {
229
324
  );
230
325
  }
231
326
 
232
- export const geminiAdapter: ProviderAdapter = {
327
+ export const geminiAdapter: StructuredOutputRecoveryAdapter = {
233
328
  id: 'gemini',
234
329
  displayName: 'Gemini',
235
330
  binary: 'gemini',
@@ -242,6 +337,7 @@ export const geminiAdapter: ProviderAdapter = {
242
337
  defaultMinLevel: 'level1',
243
338
  detectCliFeatures,
244
339
  buildCommand,
340
+ buildStructuredOutputRecoveryCommand,
245
341
  parseEvent,
246
342
  createParserState: () => createParserState('gemini'),
247
343
  resolveModelSpec,
@@ -1,4 +1,8 @@
1
1
  import { contractError } from '../contract-errors';
2
+ import { randomUUID } from 'node:crypto';
3
+ import * as fs from 'node:fs';
4
+ import * as os from 'node:os';
5
+ import * as path from 'node:path';
2
6
  import { UnsupportedProviderCapabilityError } from '../errors';
3
7
  import { appendJsonSchemaPrompt } from '../schema';
4
8
  import {
@@ -15,7 +19,7 @@ import {
15
19
  type ErrorClassification,
16
20
  type OpencodeCliFeatures,
17
21
  type OutputEvent,
18
- type ProviderAdapter,
22
+ type StructuredOutputRecoveryAdapter,
19
23
  type WarningMetadata,
20
24
  } from '../types';
21
25
  import {
@@ -49,7 +53,9 @@ function detectCliFeatures(
49
53
  supportsCwd: unknown ? false : /--cwd\b/.test(help),
50
54
  supportsAutoApprove: false,
51
55
  supportsResume: !unknown && /--session\b/.test(help) && /--continue\b/.test(help),
56
+ supportsAgent: !unknown && /--agent\b/.test(help),
52
57
  supportsWebSearch: isCliVersionAtLeast(versionText, '1.0.137'),
58
+ supportsRecoveryIsolation: isCliVersionAtLeast(versionText, '1.17.20'),
53
59
  unknown,
54
60
  };
55
61
  }
@@ -162,6 +168,82 @@ function buildCommand(context: string, options: BuildProviderCommandOptions = {}
162
168
  });
163
169
  }
164
170
 
171
+ function buildStructuredOutputRecoveryCommand(
172
+ context: string,
173
+ options: BuildProviderCommandOptions = {}
174
+ ): CommandSpec {
175
+ const features = optionFeatures(options);
176
+ if (features.supportsAgent !== true || features.supportsRecoveryIsolation !== true) {
177
+ throw new UnsupportedProviderCapabilityError(
178
+ 'opencode',
179
+ 'structuredOutputRecovery',
180
+ 'OpenCode structured-output recovery requires CLI evidence for --agent and isolation controls available in OpenCode >= 1.17.20. Upgrade OpenCode before retrying.'
181
+ );
182
+ }
183
+ const agentName = `zeroshot-output-reformatter-${randomUUID()}`;
184
+ const profile = {
185
+ description: 'Convert supplied text to schema-valid JSON without external actions',
186
+ mode: 'primary',
187
+ permission: { '*': 'deny' },
188
+ tools: { '*': false },
189
+ };
190
+ const recoveryOptions = { ...options };
191
+ delete recoveryOptions.resumeSessionId;
192
+ delete recoveryOptions.continueSession;
193
+ const isolatedConfigHome = fs.mkdtempSync(path.join(os.tmpdir(), 'zeroshot-opencode-config-'));
194
+
195
+ try {
196
+ const spec = buildCommand(context, {
197
+ ...recoveryOptions,
198
+ agentName,
199
+ autoApprove: false,
200
+ webSearch: false,
201
+ });
202
+ const restrictedConfig = {
203
+ default_agent: agentName,
204
+ permission: { '*': 'deny' },
205
+ tools: { '*': false },
206
+ agent: { [agentName]: profile },
207
+ mode: { [agentName]: profile },
208
+ mcp: {},
209
+ instructions: [],
210
+ plugin: [],
211
+ command: {},
212
+ };
213
+ return {
214
+ ...spec,
215
+ env: {
216
+ ...spec.env,
217
+ ZEROSHOT_OPENCODE_AGENT: agentName,
218
+ XDG_CONFIG_HOME: isolatedConfigHome,
219
+ OPENCODE_DB: ':memory:',
220
+ OPENCODE_CONFIG: '',
221
+ OPENCODE_CONFIG_DIR: isolatedConfigHome,
222
+ OPENCODE_CONFIG_CONTENT: JSON.stringify(restrictedConfig),
223
+ OPENCODE_PERMISSION: JSON.stringify({ '*': 'deny' }),
224
+ OPENCODE_DISABLE_PROJECT_CONFIG: '1',
225
+ OPENCODE_PURE: '1',
226
+ OPENCODE_DISABLE_DEFAULT_PLUGINS: '1',
227
+ OPENCODE_DISABLE_EXTERNAL_SKILLS: '1',
228
+ OPENCODE_DISABLE_CLAUDE_CODE: '1',
229
+ },
230
+ cleanup: [...(spec.cleanup || []), isolatedConfigHome],
231
+ cleanupMetadata: [
232
+ ...(spec.cleanupMetadata || []),
233
+ {
234
+ kind: 'temp-directory',
235
+ provider: 'opencode',
236
+ path: isolatedConfigHome,
237
+ reason: 'isolated-config',
238
+ },
239
+ ],
240
+ };
241
+ } catch (error) {
242
+ fs.rmSync(isolatedConfigHome, { recursive: true, force: true });
243
+ throw error;
244
+ }
245
+ }
246
+
165
247
  function parseToolPart(part: Record<string, unknown>): OutputEvent | null {
166
248
  const state = getRecord(part, 'state') ?? {};
167
249
  const status = getString(state, 'status');
@@ -262,7 +344,7 @@ function classifyError(error: unknown): ErrorClassification {
262
344
  return classifyBaseProviderError(error, [], []);
263
345
  }
264
346
 
265
- export const opencodeAdapter: ProviderAdapter = {
347
+ export const opencodeAdapter: StructuredOutputRecoveryAdapter = {
266
348
  id: 'opencode',
267
349
  displayName: 'Opencode',
268
350
  binary: 'opencode',
@@ -281,6 +363,7 @@ export const opencodeAdapter: ProviderAdapter = {
281
363
  defaultMinLevel: 'level1',
282
364
  detectCliFeatures,
283
365
  buildCommand,
366
+ buildStructuredOutputRecoveryCommand,
284
367
  extractSessionId,
285
368
  parseEvent,
286
369
  createParserState: () => createParserState('opencode'),
@@ -24,6 +24,9 @@ const CLI_FEATURE_FIELDS = [
24
24
  'supportsModel',
25
25
  'supportsEffort',
26
26
  'supportsSettings',
27
+ 'supportsTools',
28
+ 'supportsStrictMcpConfig',
29
+ 'supportsNoSessionPersistence',
27
30
  'supportsJson',
28
31
  'supportsOutputSchema',
29
32
  'supportsDir',
@@ -31,8 +34,16 @@ const CLI_FEATURE_FIELDS = [
31
34
  'supportsConfigOverride',
32
35
  'supportsSkipGitRepoCheck',
33
36
  'supportsVariant',
37
+ 'supportsSandbox',
38
+ 'supportsEphemeral',
39
+ 'supportsIgnoreUserConfig',
40
+ 'supportsIgnoreRules',
41
+ 'supportsStrictConfig',
34
42
  'supportsJsonMode',
43
+ 'supportsAdminPolicy',
35
44
  'supportsNoSession',
45
+ 'supportsAgent',
46
+ 'supportsRecoveryIsolation',
36
47
  'supportsNoExtensions',
37
48
  'supportsNoSkills',
38
49
  'supportsNoPromptTemplates',
@@ -229,6 +240,11 @@ function normalizeBuildOptions(value: Record<string, unknown>): BuildProviderCom
229
240
  'strictSchema',
230
241
  optionalBooleanValue(value.strictSchema, 'options.strictSchema')
231
242
  );
243
+ addDefined(
244
+ result,
245
+ 'structuredOutputRecovery',
246
+ optionalBooleanValue(value.structuredOutputRecovery, 'options.structuredOutputRecovery')
247
+ );
232
248
  addDefined(
233
249
  result,
234
250
  'gateway',
@@ -53,6 +53,7 @@ export {
53
53
  providerRegistry,
54
54
  resolveProviderCommand,
55
55
  supportsProviderCapability,
56
+ supportsProviderOutputReformatting,
56
57
  } from './provider-registry';
57
58
 
58
59
  export type {
@@ -93,6 +94,9 @@ export type {
93
94
  ProviderCliFeatures,
94
95
  ProviderId,
95
96
  ProviderRegistryEntry,
97
+ StructuredOutputRecoveryAdapter,
98
+ StructuredOutputProviderRegistryEntry,
99
+ UnstructuredOutputProviderRegistryEntry,
96
100
  RedactionMetadata,
97
101
  ReasoningEffort,
98
102
  ResolvedGatewayBuildOptions,
@@ -8,7 +8,7 @@ import { opencodeAdapter } from './adapters/opencode';
8
8
  import { ompAdapter } from './adapters/omp';
9
9
  import { piAdapter } from './adapters/pi';
10
10
  import { resolveClaudeCommand } from './claude-command';
11
- import type { ModelLevel, ProviderAdapter } from './types';
11
+ import type { ModelLevel, ProviderAdapter, StructuredOutputRecoveryAdapter } from './types';
12
12
 
13
13
  export type ProviderCapabilityState = boolean | 'experimental';
14
14
 
@@ -69,7 +69,7 @@ export interface ProviderDockerMetadata {
69
69
  readonly install?: string;
70
70
  }
71
71
 
72
- export interface ProviderRegistryEntry {
72
+ interface ProviderRegistryEntryBase {
73
73
  readonly id: string;
74
74
  readonly aliases: readonly string[];
75
75
  readonly displayName: string;
@@ -84,7 +84,6 @@ export interface ProviderRegistryEntry {
84
84
  readonly settingsDefaults?: Readonly<Record<string, unknown>>;
85
85
  readonly settingsValidator?: (settings: Record<string, unknown>) => string | null;
86
86
  readonly availabilityProbe?: 'command' | 'help-or-version';
87
- readonly capabilities: ProviderCapabilities;
88
87
  readonly docs: ProviderDocsMetadata;
89
88
  readonly docker: ProviderDockerMetadata;
90
89
  readonly defaultLevels: Readonly<{
@@ -92,9 +91,26 @@ export interface ProviderRegistryEntry {
92
91
  readonly default: ModelLevel;
93
92
  readonly max: ModelLevel;
94
93
  }>;
94
+ }
95
+
96
+ export interface StructuredOutputProviderRegistryEntry extends ProviderRegistryEntryBase {
97
+ readonly capabilities: Omit<ProviderCapabilities, 'jsonSchema'> & {
98
+ readonly jsonSchema: true | 'experimental';
99
+ };
100
+ readonly adapter: StructuredOutputRecoveryAdapter;
101
+ }
102
+
103
+ export interface UnstructuredOutputProviderRegistryEntry extends ProviderRegistryEntryBase {
104
+ readonly capabilities: Omit<ProviderCapabilities, 'jsonSchema'> & {
105
+ readonly jsonSchema: false;
106
+ };
95
107
  readonly adapter: ProviderAdapter;
96
108
  }
97
109
 
110
+ export type ProviderRegistryEntry =
111
+ | StructuredOutputProviderRegistryEntry
112
+ | UnstructuredOutputProviderRegistryEntry;
113
+
98
114
  const STANDARD_CAPABILITIES: Readonly<
99
115
  Pick<
100
116
  ProviderCapabilities,
@@ -590,3 +606,7 @@ export function supportsProviderCapability(
590
606
  ): boolean {
591
607
  return getProviderRegistryEntry(name).capabilities[capability] === true;
592
608
  }
609
+
610
+ export function supportsProviderOutputReformatting(name: string): boolean {
611
+ return getProviderRegistryEntry(name).capabilities.jsonSchema !== false;
612
+ }
@@ -6,6 +6,7 @@ import {
6
6
  getProviderRegistryEntry,
7
7
  resolveProviderCommand,
8
8
  supportsProviderCapability,
9
+ supportsProviderOutputReformatting,
9
10
  } from './provider-registry';
10
11
  import type {
11
12
  BuildProviderCommandOptions,
@@ -16,6 +17,7 @@ import type {
16
17
  ModelLevel,
17
18
  ModelSpec,
18
19
  ProviderAdapter,
20
+ StructuredOutputRecoveryAdapter,
19
21
  ProviderCliFeatures,
20
22
  ProviderId,
21
23
  ResolvedGatewayBuildOptions,
@@ -106,7 +108,9 @@ export function prepareSingleAgentProviderCommand(
106
108
  adapter.id,
107
109
  baseOptions.cwd ?? process.cwd()
108
110
  );
109
- const requestedWebSearch = baseOptions.webSearch ?? providerSettings.webSearch;
111
+ const requestedWebSearch = baseOptions.structuredOutputRecovery
112
+ ? recoveryWebSearchOption(adapter)
113
+ : (baseOptions.webSearch ?? providerSettings.webSearch);
110
114
  assertWebSearchDeclared(adapter.id, requestedWebSearch);
111
115
  const cliFeatures = resolveRuntimeCliFeatures(
112
116
  adapter.id,
@@ -125,10 +129,37 @@ export function prepareSingleAgentProviderCommand(
125
129
  configuration: {
126
130
  webSearch: webSearchAttestation(options),
127
131
  },
128
- commandSpec: adapter.buildCommand(input.context, options),
132
+ commandSpec: buildRuntimeCommand(adapter, input.context, options),
129
133
  };
130
134
  }
131
135
 
136
+ function buildRuntimeCommand(
137
+ adapter: ProviderAdapter,
138
+ context: string,
139
+ options: BuildProviderCommandOptions
140
+ ): CommandSpec {
141
+ if (options.structuredOutputRecovery !== true) {
142
+ return adapter.buildCommand(context, options);
143
+ }
144
+ if (!supportsProviderOutputReformatting(adapter.id)) {
145
+ throw new UnsupportedProviderCapabilityError(
146
+ adapter.id,
147
+ 'structuredOutputRecovery',
148
+ `Provider ${adapter.id} does not advertise structured-output recovery.`
149
+ );
150
+ }
151
+ const recoveryAdapter = adapter as ProviderAdapter &
152
+ Partial<StructuredOutputRecoveryAdapter>;
153
+ if (typeof recoveryAdapter.buildStructuredOutputRecoveryCommand !== 'function') {
154
+ throw new UnsupportedProviderCapabilityError(
155
+ adapter.id,
156
+ 'structuredOutputRecovery',
157
+ `Provider ${adapter.id} advertises structured output without a recovery adapter. Upgrade Zeroshot before retrying.`
158
+ );
159
+ }
160
+ return recoveryAdapter.buildStructuredOutputRecoveryCommand(context, options);
161
+ }
162
+
132
163
  export function detectRuntimeProviderCliFeatures(provider: string): ProviderCliFeatures {
133
164
  return probeRuntimeProviderCli(provider).capabilities;
134
165
  }
@@ -262,15 +293,26 @@ function buildRuntimeOptions(
262
293
  providerSettings,
263
294
  modelSpec
264
295
  );
265
- const webSearch = baseOptions.webSearch ?? providerSettings.webSearch;
296
+ const webSearch = baseOptions.structuredOutputRecovery
297
+ ? recoveryWebSearchOption(adapter)
298
+ : (baseOptions.webSearch ?? providerSettings.webSearch);
266
299
  assertWebSearchDeclared(adapter.id, webSearch);
267
- const resolved = {
300
+ const baseResolved = {
268
301
  ...baseOptions,
269
302
  modelSpec,
270
303
  ...(gateway === undefined ? {} : { gateway }),
271
304
  ...(webSearch === undefined ? {} : { webSearch }),
272
305
  cliFeatures: runtime.cliFeatures,
273
306
  };
307
+ const resolved = { ...baseResolved };
308
+ if (baseOptions.structuredOutputRecovery) {
309
+ delete resolved.resumeSessionId;
310
+ delete resolved.continueSession;
311
+ delete resolved.mcpConfig;
312
+ resolved.autoApprove = false;
313
+ if (recoveryWebSearchOption(adapter) === undefined) delete resolved.webSearch;
314
+ else resolved.webSearch = false;
315
+ }
274
316
  if (baseOptions.jsonSchema && !supportsProviderCapability(adapter.id, 'jsonSchema')) {
275
317
  if (!shouldIncludeAuthEnv(baseOptions, runtime.authEnv)) {
276
318
  return { ...resolved, strictSchema: false };
@@ -281,6 +323,10 @@ function buildRuntimeOptions(
281
323
  return { ...resolved, authEnv: runtime.authEnv };
282
324
  }
283
325
 
326
+ function recoveryWebSearchOption(adapter: ProviderAdapter): false | undefined {
327
+ return getProviderRegistryEntry(adapter.id).capabilities.webSearch === false ? undefined : false;
328
+ }
329
+
284
330
  function resolveRuntimeGatewayOptions(
285
331
  provider: ProviderId,
286
332
  baseOptions: BuildProviderCommandOptions,
@@ -9,6 +9,8 @@ import type {
9
9
  ProviderDocsMetadata,
10
10
  ProviderInvokeSpec,
11
11
  ProviderRegistryEntry,
12
+ StructuredOutputProviderRegistryEntry,
13
+ UnstructuredOutputProviderRegistryEntry,
12
14
  } from './provider-registry';
13
15
 
14
16
  export type ProviderId = (typeof providerIds)[number];
@@ -27,8 +29,9 @@ export type {
27
29
  ProviderDocsMetadata,
28
30
  ProviderInvokeSpec,
29
31
  ProviderRegistryEntry,
32
+ StructuredOutputProviderRegistryEntry,
33
+ UnstructuredOutputProviderRegistryEntry,
30
34
  };
31
-
32
35
  export interface AgentCliProviderHelperMetadata {
33
36
  readonly packageName: '@the-open-engine/zeroshot';
34
37
  readonly buildOutputDir: 'lib/agent-cli-provider';
@@ -105,6 +108,9 @@ export interface ClaudeCliFeatures extends BaseCliFeatures {
105
108
  readonly supportsSettings: boolean;
106
109
  readonly supportsMcpConfig: boolean;
107
110
  readonly supportsResume: boolean;
111
+ readonly supportsTools: boolean;
112
+ readonly supportsStrictMcpConfig: boolean;
113
+ readonly supportsNoSessionPersistence: boolean;
108
114
  }
109
115
 
110
116
  export interface CodexCliFeatures extends BaseCliFeatures {
@@ -118,6 +124,11 @@ export interface CodexCliFeatures extends BaseCliFeatures {
118
124
  readonly supportsSkipGitRepoCheck: boolean;
119
125
  readonly supportsResume: boolean;
120
126
  readonly supportsWebSearch: boolean;
127
+ readonly supportsSandbox: boolean;
128
+ readonly supportsEphemeral: boolean;
129
+ readonly supportsIgnoreUserConfig: boolean;
130
+ readonly supportsIgnoreRules: boolean;
131
+ readonly supportsStrictConfig: boolean;
121
132
  }
122
133
 
123
134
  export interface GeminiCliFeatures extends BaseCliFeatures {
@@ -126,6 +137,7 @@ export interface GeminiCliFeatures extends BaseCliFeatures {
126
137
  readonly supportsAutoApprove: boolean;
127
138
  readonly supportsCwd: boolean;
128
139
  readonly supportsModel: boolean;
140
+ readonly supportsAdminPolicy: boolean;
129
141
  }
130
142
 
131
143
  export interface OpencodeCliFeatures extends BaseCliFeatures {
@@ -138,6 +150,8 @@ export interface OpencodeCliFeatures extends BaseCliFeatures {
138
150
  readonly supportsAutoApprove: false;
139
151
  readonly supportsResume: boolean;
140
152
  readonly supportsWebSearch: boolean;
153
+ readonly supportsAgent: boolean;
154
+ readonly supportsRecoveryIsolation: boolean;
141
155
  }
142
156
 
143
157
  export interface PiCliFeatures extends BaseCliFeatures {
@@ -215,6 +229,9 @@ export interface CliFeatureOverrides {
215
229
  readonly supportsIncludePartials?: boolean;
216
230
  readonly supportsVerbose?: boolean;
217
231
  readonly supportsModel?: boolean;
232
+ readonly supportsTools?: boolean;
233
+ readonly supportsStrictMcpConfig?: boolean;
234
+ readonly supportsNoSessionPersistence?: boolean;
218
235
  readonly supportsEffort?: boolean;
219
236
  readonly supportsSettings?: boolean;
220
237
  readonly supportsJson?: boolean;
@@ -223,12 +240,20 @@ export interface CliFeatureOverrides {
223
240
  readonly supportsCwd?: boolean;
224
241
  readonly supportsConfigOverride?: boolean;
225
242
  readonly supportsSkipGitRepoCheck?: boolean;
243
+ readonly supportsSandbox?: boolean;
244
+ readonly supportsEphemeral?: boolean;
245
+ readonly supportsIgnoreUserConfig?: boolean;
246
+ readonly supportsIgnoreRules?: boolean;
247
+ readonly supportsStrictConfig?: boolean;
226
248
  readonly supportsVariant?: boolean;
249
+ readonly supportsAdminPolicy?: boolean;
227
250
  readonly supportsJsonMode?: boolean;
228
251
  readonly supportsNoSession?: boolean;
229
252
  readonly supportsNoExtensions?: boolean;
230
253
  readonly supportsNoSkills?: boolean;
231
254
  readonly supportsNoPromptTemplates?: boolean;
255
+ readonly supportsAgent?: boolean;
256
+ readonly supportsRecoveryIsolation?: boolean;
232
257
  readonly supportsNoContextFiles?: boolean;
233
258
  readonly supportsNoApprove?: boolean;
234
259
  readonly supportsModeJson?: boolean;
@@ -262,7 +287,7 @@ export interface CleanupMetadata {
262
287
  readonly kind: 'temp-file' | 'temp-directory';
263
288
  readonly provider: ProviderId;
264
289
  readonly path: string;
265
- readonly reason: 'output-schema' | 'settings-overlay';
290
+ readonly reason: 'output-schema' | 'settings-overlay' | 'admin-policy' | 'isolated-config';
266
291
  }
267
292
 
268
293
  export interface WarningMetadata {
@@ -312,6 +337,8 @@ export interface BuildProviderCommandOptions {
312
337
  // one variadic `--mcp-config` followed by file paths; adapters such as Copilot accept repeated
313
338
  // flags with inline JSON so configs survive container path translation.
314
339
  readonly mcpConfig?: readonly string[];
340
+ /** Internal profile for provider-neutral structured-output correction turns. */
341
+ readonly structuredOutputRecovery?: boolean;
315
342
  }
316
343
 
317
344
  export interface TextEvent {
@@ -415,6 +442,13 @@ export interface ProviderAdapter {
415
442
  classifyError(error: unknown): ErrorClassification;
416
443
  }
417
444
 
445
+ export interface StructuredOutputRecoveryAdapter extends ProviderAdapter {
446
+ buildStructuredOutputRecoveryCommand(
447
+ context: string,
448
+ options?: BuildProviderCommandOptions
449
+ ): CommandSpec;
450
+ }
451
+
418
452
  export class InvalidProviderModelError extends Error {
419
453
  readonly permanent = true;
420
454
 
@@ -586,8 +586,8 @@ class AgentWrapper {
586
586
  * Falls back to reformatting if extraction fails
587
587
  * @private
588
588
  */
589
- _parseResultOutput(output) {
590
- return parseResultOutput(this, output);
589
+ _parseResultOutput(output, options = {}) {
590
+ return parseResultOutput(this, output, options);
591
591
  }
592
592
 
593
593
  /**
@@ -13,6 +13,7 @@ export class ClusterInternalError extends ClusterError {}
13
13
  export class ClusterProtocolError extends ClusterError {}
14
14
  export class ClusterTransportError extends ClusterError {}
15
15
  export class ClusterTimeoutError extends ClusterError {}
16
+ export class ClusterRequestError extends ClusterError {}
16
17
 
17
18
  export function requestAbortError(method: string): DOMException {
18
19
  return new DOMException(
@@ -5,11 +5,15 @@ export {
5
5
  ClusterError,
6
6
  ClusterInternalError,
7
7
  ClusterProtocolError,
8
+ ClusterRequestError,
8
9
  ClusterRpcError,
9
10
  ClusterStateError,
10
11
  ClusterTimeoutError,
11
12
  ClusterTransportError,
12
13
  } from './errors.js';
14
+ export { assertGraphProfile, assertGraphProfileSupported, assertGraphSpec } from './validators.js';
15
+ export * from './payload-value.js';
16
+ export * from './json-source.js';
13
17
  export { CONNECTION_TRANSITIONS, PROTOCOL_DIAGNOSTIC_CAPACITY, Connection } from './connection.js';
14
18
  export type {
15
19
  CallOptions,