@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.
- package/README.md +10 -7
- package/cli/commands/providers.js +1 -0
- package/cli/index.js +45 -4
- package/lib/agent-cli-provider/adapters/codex.d.ts.map +1 -1
- package/lib/agent-cli-provider/adapters/codex.js +14 -2
- package/lib/agent-cli-provider/adapters/codex.js.map +1 -1
- package/lib/agent-cli-provider/adapters/common.d.ts +1 -0
- package/lib/agent-cli-provider/adapters/common.d.ts.map +1 -1
- package/lib/agent-cli-provider/adapters/common.js +14 -0
- package/lib/agent-cli-provider/adapters/common.js.map +1 -1
- package/lib/agent-cli-provider/adapters/index.d.ts.map +1 -1
- package/lib/agent-cli-provider/adapters/index.js +6 -1
- package/lib/agent-cli-provider/adapters/index.js.map +1 -1
- package/lib/agent-cli-provider/adapters/opencode.d.ts.map +1 -1
- package/lib/agent-cli-provider/adapters/opencode.js +52 -3
- package/lib/agent-cli-provider/adapters/opencode.js.map +1 -1
- package/lib/agent-cli-provider/contract-actions.d.ts.map +1 -1
- package/lib/agent-cli-provider/contract-actions.js +19 -8
- package/lib/agent-cli-provider/contract-actions.js.map +1 -1
- package/lib/agent-cli-provider/contract-fallback.d.ts.map +1 -1
- package/lib/agent-cli-provider/contract-fallback.js +9 -0
- package/lib/agent-cli-provider/contract-fallback.js.map +1 -1
- package/lib/agent-cli-provider/contract-options.d.ts.map +1 -1
- package/lib/agent-cli-provider/contract-options.js +2 -0
- package/lib/agent-cli-provider/contract-options.js.map +1 -1
- package/lib/agent-cli-provider/errors.d.ts +7 -0
- package/lib/agent-cli-provider/errors.d.ts.map +1 -1
- package/lib/agent-cli-provider/errors.js +14 -0
- package/lib/agent-cli-provider/errors.js.map +1 -1
- package/lib/agent-cli-provider/index.d.ts +1 -1
- package/lib/agent-cli-provider/index.d.ts.map +1 -1
- package/lib/agent-cli-provider/index.js.map +1 -1
- package/lib/agent-cli-provider/provider-registry.d.ts +20 -3
- package/lib/agent-cli-provider/provider-registry.d.ts.map +1 -1
- package/lib/agent-cli-provider/provider-registry.js +15 -2
- package/lib/agent-cli-provider/provider-registry.js.map +1 -1
- package/lib/agent-cli-provider/single-agent-runtime.d.ts +14 -2
- package/lib/agent-cli-provider/single-agent-runtime.d.ts.map +1 -1
- package/lib/agent-cli-provider/single-agent-runtime.js +97 -22
- package/lib/agent-cli-provider/single-agent-runtime.js.map +1 -1
- package/lib/agent-cli-provider/types.d.ts +11 -1
- 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/src/agent/agent-lifecycle.js +52 -1
- package/src/agent/agent-task-executor.js +8 -3
- package/src/agent-cli-provider/adapters/codex.ts +23 -2
- package/src/agent-cli-provider/adapters/common.ts +14 -0
- package/src/agent-cli-provider/adapters/index.ts +16 -2
- package/src/agent-cli-provider/adapters/opencode.ts +60 -4
- package/src/agent-cli-provider/contract-actions.ts +20 -9
- package/src/agent-cli-provider/contract-fallback.ts +9 -0
- package/src/agent-cli-provider/contract-options.ts +2 -0
- package/src/agent-cli-provider/errors.ts +14 -0
- package/src/agent-cli-provider/index.ts +1 -0
- package/src/agent-cli-provider/provider-registry.ts +20 -2
- package/src/agent-cli-provider/single-agent-runtime.ts +144 -24
- package/src/agent-cli-provider/types.ts +12 -1
- package/src/claude-task-runner.js +11 -0
- package/src/orchestrator.js +8 -3
- package/src/providers/index.js +6 -0
- package/src/task-run-model-args.js +58 -40
- package/src/task-spawn-cleanup-ownership.js +3 -0
- package/src/task-startup-error.js +68 -0
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
import { stripTimestampPrefix } from '../log-prefix';
|
|
2
|
-
import {
|
|
2
|
+
import { UnsupportedProviderCapabilityError } from '../errors';
|
|
3
|
+
import {
|
|
4
|
+
getProviderRegistryEntry,
|
|
5
|
+
normalizeProviderName,
|
|
6
|
+
providerIds,
|
|
7
|
+
supportsProviderCapability,
|
|
8
|
+
} from '../provider-registry';
|
|
3
9
|
import type {
|
|
4
10
|
BuildProviderCommandOptions,
|
|
5
11
|
CommandSpec,
|
|
@@ -52,7 +58,15 @@ export function buildProviderCommand(
|
|
|
52
58
|
context: string,
|
|
53
59
|
options?: BuildProviderCommandOptions
|
|
54
60
|
): CommandSpec {
|
|
55
|
-
|
|
61
|
+
const adapter = getProviderAdapter(providerName);
|
|
62
|
+
if (options?.webSearch !== undefined && !supportsProviderCapability(adapter.id, 'webSearch')) {
|
|
63
|
+
throw new UnsupportedProviderCapabilityError(
|
|
64
|
+
adapter.id,
|
|
65
|
+
'webSearch',
|
|
66
|
+
`Provider ${adapter.id} does not expose provider-controlled native web search; remove options.webSearch.`
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
return adapter.buildCommand(context, options);
|
|
56
70
|
}
|
|
57
71
|
|
|
58
72
|
export function parseProviderChunk(
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { contractError } from '../contract-errors';
|
|
2
|
+
import { UnsupportedProviderCapabilityError } from '../errors';
|
|
1
3
|
import { appendJsonSchemaPrompt } from '../schema';
|
|
2
4
|
import {
|
|
3
5
|
getNumber,
|
|
@@ -20,8 +22,8 @@ import {
|
|
|
20
22
|
classifyBaseProviderError,
|
|
21
23
|
commandSpec,
|
|
22
24
|
createParserState,
|
|
25
|
+
isCliVersionAtLeast,
|
|
23
26
|
optionFeatures,
|
|
24
|
-
unsupportedSessionControlWarnings,
|
|
25
27
|
warning,
|
|
26
28
|
} from './common';
|
|
27
29
|
import {
|
|
@@ -32,7 +34,10 @@ import {
|
|
|
32
34
|
validateModelId,
|
|
33
35
|
} from './opencode-models';
|
|
34
36
|
|
|
35
|
-
function detectCliFeatures(
|
|
37
|
+
function detectCliFeatures(
|
|
38
|
+
helpText?: string | null,
|
|
39
|
+
versionText?: string | null
|
|
40
|
+
): OpencodeCliFeatures {
|
|
36
41
|
const help = helpText ?? '';
|
|
37
42
|
const unknown = !help;
|
|
38
43
|
return {
|
|
@@ -43,6 +48,8 @@ function detectCliFeatures(helpText?: string | null): OpencodeCliFeatures {
|
|
|
43
48
|
supportsDir: unknown ? false : /--dir\b/.test(help),
|
|
44
49
|
supportsCwd: unknown ? false : /--cwd\b/.test(help),
|
|
45
50
|
supportsAutoApprove: false,
|
|
51
|
+
supportsResume: !unknown && /--session\b/.test(help) && /--continue\b/.test(help),
|
|
52
|
+
supportsWebSearch: isCliVersionAtLeast(versionText, '1.0.137'),
|
|
46
53
|
unknown,
|
|
47
54
|
};
|
|
48
55
|
}
|
|
@@ -76,7 +83,7 @@ function addOpencodeOptionalArgs(args: string[], options: BuildProviderCommandOp
|
|
|
76
83
|
|
|
77
84
|
function collectOpencodeWarnings(options: BuildProviderCommandOptions): WarningMetadata[] {
|
|
78
85
|
const features = optionFeatures(options);
|
|
79
|
-
const warnings: WarningMetadata[] =
|
|
86
|
+
const warnings: WarningMetadata[] = [];
|
|
80
87
|
if (options.modelSpec?.reasoningEffort && features.supportsVariant === false) {
|
|
81
88
|
warnings.push(
|
|
82
89
|
warning(
|
|
@@ -89,19 +96,67 @@ function collectOpencodeWarnings(options: BuildProviderCommandOptions): WarningM
|
|
|
89
96
|
return warnings;
|
|
90
97
|
}
|
|
91
98
|
|
|
99
|
+
function addSessionArgs(args: string[], options: BuildProviderCommandOptions): void {
|
|
100
|
+
const hasResumeSessionId = options.resumeSessionId !== undefined;
|
|
101
|
+
if (!hasResumeSessionId && !options.continueSession) return;
|
|
102
|
+
const field = hasResumeSessionId ? 'options.resumeSessionId' : 'options.continueSession';
|
|
103
|
+
if (hasResumeSessionId && options.resumeSessionId?.trim().length === 0) {
|
|
104
|
+
throw contractError({
|
|
105
|
+
code: 'invalid-field',
|
|
106
|
+
field,
|
|
107
|
+
exitCode: 2,
|
|
108
|
+
message: 'options.resumeSessionId must be a non-empty OpenCode session ID.',
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
if (optionFeatures(options).supportsResume === false) {
|
|
112
|
+
throw contractError({
|
|
113
|
+
code: 'invalid-field',
|
|
114
|
+
field,
|
|
115
|
+
exitCode: 2,
|
|
116
|
+
message:
|
|
117
|
+
'OpenCode CLI cannot safely run continuation context because this installation lacks run --session/--continue.',
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
if (options.resumeSessionId) {
|
|
121
|
+
args.push('--session', options.resumeSessionId);
|
|
122
|
+
} else {
|
|
123
|
+
args.push('--continue');
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
function webSearchEnv(options: BuildProviderCommandOptions): Readonly<Record<string, string>> {
|
|
128
|
+
if (options.webSearch !== true) return {};
|
|
129
|
+
if (optionFeatures(options).supportsWebSearch !== true) {
|
|
130
|
+
throw new UnsupportedProviderCapabilityError(
|
|
131
|
+
'opencode',
|
|
132
|
+
'webSearch',
|
|
133
|
+
'OpenCode web search requires a parseable OpenCode CLI version >= 1.0.137. Update OpenCode or set providerSettings.opencode.webSearch to false.'
|
|
134
|
+
);
|
|
135
|
+
}
|
|
136
|
+
return { OPENCODE_ENABLE_EXA: '1' };
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
function extractSessionId(line: string): string | null {
|
|
140
|
+
const event = tryParseJson(line.trim());
|
|
141
|
+
if (!isRecord(event)) return null;
|
|
142
|
+
const sessionId = getString(event, 'sessionID') ?? getString(event, 'sessionId');
|
|
143
|
+
return sessionId?.trim() || null;
|
|
144
|
+
}
|
|
145
|
+
|
|
92
146
|
function buildCommand(context: string, options: BuildProviderCommandOptions = {}): CommandSpec {
|
|
93
147
|
const finalContext = options.jsonSchema
|
|
94
148
|
? appendJsonSchemaPrompt(context, options.jsonSchema)
|
|
95
149
|
: context;
|
|
96
150
|
const args: string[] = ['run'];
|
|
97
151
|
addOpencodeOptionalArgs(args, options);
|
|
152
|
+
addSessionArgs(args, options);
|
|
98
153
|
|
|
99
154
|
args.push(finalContext);
|
|
100
155
|
|
|
101
156
|
return commandSpec({
|
|
102
157
|
binary: 'opencode',
|
|
103
158
|
args,
|
|
104
|
-
env:
|
|
159
|
+
env: webSearchEnv(options),
|
|
105
160
|
...(options.cwd === undefined ? {} : { cwd: options.cwd }),
|
|
106
161
|
warnings: collectOpencodeWarnings(options),
|
|
107
162
|
});
|
|
@@ -226,6 +281,7 @@ export const opencodeAdapter: ProviderAdapter = {
|
|
|
226
281
|
defaultMinLevel: 'level1',
|
|
227
282
|
detectCliFeatures,
|
|
228
283
|
buildCommand,
|
|
284
|
+
extractSessionId,
|
|
229
285
|
parseEvent,
|
|
230
286
|
createParserState: () => createParserState('opencode'),
|
|
231
287
|
resolveModelSpec,
|
|
@@ -23,6 +23,10 @@ import type { ProcessRunner } from './process-runner';
|
|
|
23
23
|
|
|
24
24
|
function runBuildCommand(request: RequestData): ContractEnvelope {
|
|
25
25
|
const { adapter, commandSpec, options } = buildCommandSpec(request);
|
|
26
|
+
const webSearch = {
|
|
27
|
+
requested: options.webSearch === true,
|
|
28
|
+
effective: options.webSearch === true && options.cliFeatures?.supportsWebSearch === true,
|
|
29
|
+
};
|
|
26
30
|
return successEnvelope({
|
|
27
31
|
command: request.command ?? 'build-command',
|
|
28
32
|
adapter,
|
|
@@ -31,11 +35,13 @@ function runBuildCommand(request: RequestData): ContractEnvelope {
|
|
|
31
35
|
evidence: {
|
|
32
36
|
outputFormat: options.outputFormat ?? null,
|
|
33
37
|
schemaMode: schemaMode(options),
|
|
38
|
+
configuration: { webSearch },
|
|
34
39
|
},
|
|
35
40
|
result: {
|
|
36
41
|
commandSpec,
|
|
37
42
|
outputFormat: options.outputFormat ?? null,
|
|
38
43
|
schemaMode: schemaMode(options),
|
|
44
|
+
configuration: { webSearch },
|
|
39
45
|
},
|
|
40
46
|
});
|
|
41
47
|
}
|
|
@@ -43,11 +49,15 @@ function runBuildCommand(request: RequestData): ContractEnvelope {
|
|
|
43
49
|
function runProbe(request: RequestData): ContractEnvelope {
|
|
44
50
|
const adapter = adapterForProvider(request.provider);
|
|
45
51
|
const helpText = typeof request.raw.helpText === 'string' ? request.raw.helpText : null;
|
|
46
|
-
const
|
|
47
|
-
const
|
|
48
|
-
|
|
49
|
-
? adapter.
|
|
50
|
-
:
|
|
52
|
+
const versionText = typeof request.raw.versionText === 'string' ? request.raw.versionText : '';
|
|
53
|
+
const runtimeProbe =
|
|
54
|
+
helpText === null
|
|
55
|
+
? probeRuntimeProviderCli(adapter.id)
|
|
56
|
+
: probeRuntimeProviderCli(adapter.id, {
|
|
57
|
+
available: true,
|
|
58
|
+
helpText,
|
|
59
|
+
versionText,
|
|
60
|
+
});
|
|
51
61
|
return successEnvelope({
|
|
52
62
|
command: request.command ?? 'probe',
|
|
53
63
|
adapter,
|
|
@@ -60,10 +70,11 @@ function runProbe(request: RequestData): ContractEnvelope {
|
|
|
60
70
|
},
|
|
61
71
|
contractVersion: providerExecutableSchemaVersion,
|
|
62
72
|
adapterVersion: adapter.adapterVersion,
|
|
63
|
-
available: runtimeProbe
|
|
64
|
-
helpText: runtimeProbe
|
|
65
|
-
versionText: runtimeProbe
|
|
66
|
-
capabilities,
|
|
73
|
+
available: runtimeProbe.available,
|
|
74
|
+
helpText: runtimeProbe.helpText,
|
|
75
|
+
versionText: runtimeProbe.versionText,
|
|
76
|
+
capabilities: runtimeProbe.capabilities,
|
|
77
|
+
configuration: runtimeProbe.configuration,
|
|
67
78
|
credentials: adapter.credentialEnvKeys.map((key) => ({
|
|
68
79
|
key,
|
|
69
80
|
present: Boolean(request.env[key] ?? process.env[key]),
|
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
type ContractErrorObject,
|
|
7
7
|
type ContractEnvelope,
|
|
8
8
|
} from './contract-envelope';
|
|
9
|
+
import { UnsupportedProviderCapabilityError } from './errors';
|
|
9
10
|
import { getString, isRecord, parseJson, unknownToMessage } from './json';
|
|
10
11
|
import { mergeRedactions, redactString } from './redaction';
|
|
11
12
|
import type { ProviderAdapter } from './types';
|
|
@@ -77,6 +78,14 @@ function errorObject(requestError: ContractRequestError): ContractErrorObject {
|
|
|
77
78
|
|
|
78
79
|
export function requestErrorFromUnknown(error: unknown): ContractRequestError {
|
|
79
80
|
if (error instanceof ContractRequestError) return error;
|
|
81
|
+
if (error instanceof UnsupportedProviderCapabilityError) {
|
|
82
|
+
return contractError({
|
|
83
|
+
code: error.code,
|
|
84
|
+
message: error.message,
|
|
85
|
+
exitCode: 4,
|
|
86
|
+
field: 'options.webSearch',
|
|
87
|
+
});
|
|
88
|
+
}
|
|
80
89
|
return contractError({
|
|
81
90
|
code: 'internal-error',
|
|
82
91
|
message: unknownToMessage(error),
|
|
@@ -44,6 +44,7 @@ const CLI_FEATURE_FIELDS = [
|
|
|
44
44
|
'supportsAddDir',
|
|
45
45
|
'supportsMcpConfig',
|
|
46
46
|
'supportsResume',
|
|
47
|
+
'supportsWebSearch',
|
|
47
48
|
'supportsBundledRunner',
|
|
48
49
|
'supportsAcpStdio',
|
|
49
50
|
'supportsPromptImages',
|
|
@@ -210,6 +211,7 @@ function normalizeBuildOptions(value: Record<string, unknown>): BuildProviderCom
|
|
|
210
211
|
'continueSession',
|
|
211
212
|
optionalBooleanValue(value.continueSession, 'options.continueSession')
|
|
212
213
|
);
|
|
214
|
+
addDefined(result, 'webSearch', optionalBooleanValue(value.webSearch, 'options.webSearch'));
|
|
213
215
|
addDefined(
|
|
214
216
|
result,
|
|
215
217
|
'claudeSettingsFile',
|
|
@@ -1,6 +1,20 @@
|
|
|
1
1
|
import { getNumber, getRecord, getString, isRecord, unknownToMessage } from './json';
|
|
2
2
|
import type { ErrorClassification } from './types';
|
|
3
3
|
|
|
4
|
+
export class UnsupportedProviderCapabilityError extends Error {
|
|
5
|
+
readonly code = 'unsupported-capability';
|
|
6
|
+
readonly permanent = true;
|
|
7
|
+
readonly provider: string;
|
|
8
|
+
readonly capability: string;
|
|
9
|
+
|
|
10
|
+
constructor(provider: string, capability: string, message: string) {
|
|
11
|
+
super(message);
|
|
12
|
+
this.name = 'UnsupportedProviderCapabilityError';
|
|
13
|
+
this.provider = provider;
|
|
14
|
+
this.capability = capability;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
4
18
|
const BASE_RETRYABLE_PATTERNS: readonly RegExp[] = [
|
|
5
19
|
/rate.?limit/i,
|
|
6
20
|
/\b429\b/i,
|
|
@@ -20,6 +20,7 @@ export interface ProviderCapabilities {
|
|
|
20
20
|
readonly thinkingMode: ProviderCapabilityState;
|
|
21
21
|
readonly reasoningEffort: ProviderCapabilityState;
|
|
22
22
|
readonly sessionResume: ProviderCapabilityState;
|
|
23
|
+
readonly webSearch: ProviderCapabilityState;
|
|
23
24
|
}
|
|
24
25
|
|
|
25
26
|
interface FixedProviderCommandSpec {
|
|
@@ -102,6 +103,7 @@ const STANDARD_CAPABILITIES: Readonly<
|
|
|
102
103
|
| 'streamJson'
|
|
103
104
|
| 'thinkingMode'
|
|
104
105
|
| 'sessionResume'
|
|
106
|
+
| 'webSearch'
|
|
105
107
|
>
|
|
106
108
|
> = {
|
|
107
109
|
dockerIsolation: true,
|
|
@@ -110,6 +112,7 @@ const STANDARD_CAPABILITIES: Readonly<
|
|
|
110
112
|
streamJson: true,
|
|
111
113
|
thinkingMode: true,
|
|
112
114
|
sessionResume: false,
|
|
115
|
+
webSearch: false,
|
|
113
116
|
};
|
|
114
117
|
|
|
115
118
|
const CLAUDE_DOCKER_ENV_PASSTHROUGH = [
|
|
@@ -203,12 +206,15 @@ export const providerRegistry = [
|
|
|
203
206
|
authInstructions: 'codex login',
|
|
204
207
|
credentialPaths: ['~/.config/codex', '~/.codex'],
|
|
205
208
|
credentialEnvKeys: codexAdapter.credentialEnvKeys,
|
|
206
|
-
settingsFields: [],
|
|
209
|
+
settingsFields: ['webSearch'],
|
|
210
|
+
settingsDefaults: { webSearch: false },
|
|
211
|
+
settingsValidator: (settings): string | null => validateWebSearchSettings('codex', settings),
|
|
207
212
|
capabilities: {
|
|
208
213
|
...STANDARD_CAPABILITIES,
|
|
209
214
|
jsonSchema: true,
|
|
210
215
|
reasoningEffort: true,
|
|
211
216
|
sessionResume: true,
|
|
217
|
+
webSearch: true,
|
|
212
218
|
},
|
|
213
219
|
docs: {
|
|
214
220
|
label: 'Codex',
|
|
@@ -326,11 +332,15 @@ export const providerRegistry = [
|
|
|
326
332
|
authInstructions: 'opencode auth login',
|
|
327
333
|
credentialPaths: ['~/.local/share/opencode'],
|
|
328
334
|
credentialEnvKeys: opencodeAdapter.credentialEnvKeys,
|
|
329
|
-
settingsFields: [],
|
|
335
|
+
settingsFields: ['webSearch'],
|
|
336
|
+
settingsDefaults: { webSearch: false },
|
|
337
|
+
settingsValidator: (settings): string | null => validateWebSearchSettings('opencode', settings),
|
|
330
338
|
capabilities: {
|
|
331
339
|
...STANDARD_CAPABILITIES,
|
|
332
340
|
jsonSchema: 'experimental',
|
|
333
341
|
reasoningEffort: true,
|
|
342
|
+
sessionResume: true,
|
|
343
|
+
webSearch: true,
|
|
334
344
|
},
|
|
335
345
|
docs: {
|
|
336
346
|
label: 'Opencode',
|
|
@@ -472,6 +482,14 @@ export const providerRegistry = [
|
|
|
472
482
|
},
|
|
473
483
|
] as const satisfies readonly ProviderRegistryEntry[];
|
|
474
484
|
|
|
485
|
+
function validateWebSearchSettings(
|
|
486
|
+
provider: 'codex' | 'opencode',
|
|
487
|
+
settings: Record<string, unknown>
|
|
488
|
+
): string | null {
|
|
489
|
+
if (settings.webSearch === undefined || typeof settings.webSearch === 'boolean') return null;
|
|
490
|
+
return `providerSettings.${provider}.webSearch must be a boolean`;
|
|
491
|
+
}
|
|
492
|
+
|
|
475
493
|
type RegistryProviderId = (typeof providerRegistry)[number]['id'];
|
|
476
494
|
type RegistryProviderAlias = (typeof providerRegistry)[number]['aliases'][number];
|
|
477
495
|
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { getProviderAdapter } from './adapters';
|
|
2
|
+
import { UnsupportedProviderCapabilityError } from './errors';
|
|
2
3
|
import { normalizeGatewayBuildOptions, resolveGatewayConfiguration } from './gateway-tools';
|
|
3
4
|
import { isRecord } from './json';
|
|
4
5
|
import {
|
|
@@ -19,6 +20,7 @@ import type {
|
|
|
19
20
|
ProviderId,
|
|
20
21
|
ResolvedGatewayBuildOptions,
|
|
21
22
|
ReasoningEffort,
|
|
23
|
+
WebSearchAttestation,
|
|
22
24
|
} from './types';
|
|
23
25
|
|
|
24
26
|
type UnknownFunction = (...args: readonly unknown[]) => unknown;
|
|
@@ -32,6 +34,7 @@ interface RuntimeProviderSettings {
|
|
|
32
34
|
readonly defaultLevel?: ModelLevel;
|
|
33
35
|
readonly levelOverrides: LevelOverrides;
|
|
34
36
|
readonly gateway?: GatewayBuildOptions;
|
|
37
|
+
readonly webSearch?: boolean;
|
|
35
38
|
}
|
|
36
39
|
|
|
37
40
|
interface RuntimeCommandContext {
|
|
@@ -50,6 +53,9 @@ export interface PreparedSingleAgentProviderCommand {
|
|
|
50
53
|
readonly commandSpec: CommandSpec;
|
|
51
54
|
readonly options: BuildProviderCommandOptions;
|
|
52
55
|
readonly cliFeatures: CliFeatureOverrides;
|
|
56
|
+
readonly configuration: {
|
|
57
|
+
readonly webSearch: WebSearchAttestation;
|
|
58
|
+
};
|
|
53
59
|
}
|
|
54
60
|
|
|
55
61
|
export interface RuntimeProviderProbe {
|
|
@@ -57,6 +63,15 @@ export interface RuntimeProviderProbe {
|
|
|
57
63
|
readonly helpText: string;
|
|
58
64
|
readonly versionText: string;
|
|
59
65
|
readonly capabilities: ProviderCliFeatures;
|
|
66
|
+
readonly configuration: {
|
|
67
|
+
readonly webSearch: WebSearchAttestation;
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
interface RuntimeProbeEvidence {
|
|
72
|
+
readonly available?: boolean;
|
|
73
|
+
readonly helpText: string;
|
|
74
|
+
readonly versionText: string;
|
|
60
75
|
}
|
|
61
76
|
|
|
62
77
|
type MutableModelSpec = {
|
|
@@ -91,7 +106,13 @@ export function prepareSingleAgentProviderCommand(
|
|
|
91
106
|
adapter.id,
|
|
92
107
|
baseOptions.cwd ?? process.cwd()
|
|
93
108
|
);
|
|
94
|
-
const
|
|
109
|
+
const requestedWebSearch = baseOptions.webSearch ?? providerSettings.webSearch;
|
|
110
|
+
assertWebSearchDeclared(adapter.id, requestedWebSearch);
|
|
111
|
+
const cliFeatures = resolveRuntimeCliFeatures(
|
|
112
|
+
adapter.id,
|
|
113
|
+
baseOptions.cliFeatures,
|
|
114
|
+
requestedWebSearch === true
|
|
115
|
+
);
|
|
95
116
|
const authEnv = baseOptions.authEnv ?? resolveRuntimeAuthEnv(adapter.id, settings);
|
|
96
117
|
const options = buildRuntimeOptions(baseOptions, adapter, providerSettings, {
|
|
97
118
|
cliFeatures,
|
|
@@ -101,6 +122,9 @@ export function prepareSingleAgentProviderCommand(
|
|
|
101
122
|
adapter,
|
|
102
123
|
options,
|
|
103
124
|
cliFeatures,
|
|
125
|
+
configuration: {
|
|
126
|
+
webSearch: webSearchAttestation(options),
|
|
127
|
+
},
|
|
104
128
|
commandSpec: adapter.buildCommand(input.context, options),
|
|
105
129
|
};
|
|
106
130
|
}
|
|
@@ -111,22 +135,35 @@ export function detectRuntimeProviderCliFeatures(provider: string): ProviderCliF
|
|
|
111
135
|
|
|
112
136
|
function resolveRuntimeCliFeatures(
|
|
113
137
|
provider: ProviderId,
|
|
114
|
-
overrides: CliFeatureOverrides | undefined
|
|
138
|
+
overrides: CliFeatureOverrides | undefined,
|
|
139
|
+
webSearchRequested: boolean
|
|
115
140
|
): CliFeatureOverrides {
|
|
116
141
|
if (provider === 'gateway') {
|
|
142
|
+
const detected = detectRuntimeProviderCliFeatures(provider);
|
|
117
143
|
return {
|
|
118
|
-
...
|
|
144
|
+
...detected,
|
|
119
145
|
...overrides,
|
|
120
146
|
supportsBundledRunner: true,
|
|
147
|
+
supportsWebSearch: false,
|
|
121
148
|
};
|
|
122
149
|
}
|
|
123
|
-
if (getProviderRegistryEntry(provider).invoke.lane
|
|
124
|
-
|
|
150
|
+
if (getProviderRegistryEntry(provider).invoke.lane === 'acp-stdio') {
|
|
151
|
+
const detected = detectRuntimeProviderCliFeatures(provider);
|
|
152
|
+
if (overrides === undefined) return detected;
|
|
153
|
+
return mergeAcpFailClosedCliFeatures(detected, overrides);
|
|
125
154
|
}
|
|
126
|
-
|
|
155
|
+
if (overrides === undefined) return detectRuntimeProviderCliFeatures(provider);
|
|
156
|
+
if (!webSearchRequested) return overrides;
|
|
127
157
|
const detected = detectRuntimeProviderCliFeatures(provider);
|
|
128
|
-
|
|
129
|
-
|
|
158
|
+
return {
|
|
159
|
+
...overrides,
|
|
160
|
+
supportsResume:
|
|
161
|
+
'supportsResume' in detected &&
|
|
162
|
+
detected.supportsResume === true &&
|
|
163
|
+
overrides.supportsResume !== false,
|
|
164
|
+
supportsWebSearch:
|
|
165
|
+
detected.supportsWebSearch === true && overrides.supportsWebSearch !== false,
|
|
166
|
+
};
|
|
130
167
|
}
|
|
131
168
|
|
|
132
169
|
function mergeAcpFailClosedCliFeatures(
|
|
@@ -154,33 +191,61 @@ function mergeAcpFailClosedCliFeatures(
|
|
|
154
191
|
};
|
|
155
192
|
}
|
|
156
193
|
|
|
157
|
-
export function probeRuntimeProviderCli(
|
|
194
|
+
export function probeRuntimeProviderCli(
|
|
195
|
+
provider: string,
|
|
196
|
+
evidence?: RuntimeProbeEvidence
|
|
197
|
+
): RuntimeProviderProbe {
|
|
158
198
|
const adapter = getProviderAdapter(provider);
|
|
159
199
|
if (adapter.id === 'gateway') {
|
|
160
200
|
return probeGatewayProvider(adapter);
|
|
161
201
|
}
|
|
202
|
+
const requested = getProviderRegistryEntry(adapter.id).settingsFields.includes('webSearch')
|
|
203
|
+
? runtimeProviderSettings(loadRuntimeSettings(), adapter.id, process.cwd()).webSearch === true
|
|
204
|
+
: false;
|
|
162
205
|
const helpCommand = runtimeHelpCommand(adapter.id);
|
|
163
|
-
const commandAvailable =
|
|
206
|
+
const commandAvailable =
|
|
207
|
+
evidence === undefined
|
|
208
|
+
? booleanResult(commandExistsFn(helpCommand.command))
|
|
209
|
+
: evidence.available !== false;
|
|
164
210
|
if (!commandAvailable) {
|
|
211
|
+
const capabilities = attestedCliFeatures(adapter, '', '');
|
|
165
212
|
return {
|
|
166
213
|
available: false,
|
|
167
214
|
helpText: '',
|
|
168
215
|
versionText: '',
|
|
169
|
-
capabilities
|
|
216
|
+
capabilities,
|
|
217
|
+
configuration: {
|
|
218
|
+
webSearch: webSearchAttestation({ webSearch: requested, cliFeatures: capabilities }),
|
|
219
|
+
},
|
|
170
220
|
};
|
|
171
221
|
}
|
|
172
222
|
|
|
173
|
-
const helpText =
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
223
|
+
const helpText =
|
|
224
|
+
evidence?.helpText ??
|
|
225
|
+
stringResult(getHelpOutputFn(helpCommand.command, helpCommand.args)).trim();
|
|
226
|
+
const versionText =
|
|
227
|
+
evidence?.versionText ??
|
|
228
|
+
stringResult(
|
|
229
|
+
getVersionOutputFn(
|
|
230
|
+
helpCommand.command,
|
|
231
|
+
getProviderRegistryEntry(adapter.id).capabilities.webSearch === true
|
|
232
|
+
? []
|
|
233
|
+
: helpCommand.args
|
|
234
|
+
)
|
|
235
|
+
).trim();
|
|
177
236
|
const availabilityProbe = getProviderRegistryEntry(adapter.id).availabilityProbe ?? 'command';
|
|
237
|
+
const capabilities = attestedCliFeatures(adapter, helpText, versionText);
|
|
178
238
|
|
|
179
239
|
return {
|
|
180
|
-
available:
|
|
240
|
+
available:
|
|
241
|
+
evidence?.available ??
|
|
242
|
+
(availabilityProbe === 'help-or-version' ? Boolean(helpText || versionText) : true),
|
|
181
243
|
helpText,
|
|
182
244
|
versionText,
|
|
183
|
-
capabilities
|
|
245
|
+
capabilities,
|
|
246
|
+
configuration: {
|
|
247
|
+
webSearch: webSearchAttestation({ webSearch: requested, cliFeatures: capabilities }),
|
|
248
|
+
},
|
|
184
249
|
};
|
|
185
250
|
}
|
|
186
251
|
|
|
@@ -197,10 +262,13 @@ function buildRuntimeOptions(
|
|
|
197
262
|
providerSettings,
|
|
198
263
|
modelSpec
|
|
199
264
|
);
|
|
265
|
+
const webSearch = baseOptions.webSearch ?? providerSettings.webSearch;
|
|
266
|
+
assertWebSearchDeclared(adapter.id, webSearch);
|
|
200
267
|
const resolved = {
|
|
201
268
|
...baseOptions,
|
|
202
269
|
modelSpec,
|
|
203
270
|
...(gateway === undefined ? {} : { gateway }),
|
|
271
|
+
...(webSearch === undefined ? {} : { webSearch }),
|
|
204
272
|
cliFeatures: runtime.cliFeatures,
|
|
205
273
|
};
|
|
206
274
|
if (baseOptions.jsonSchema && !supportsProviderCapability(adapter.id, 'jsonSchema')) {
|
|
@@ -320,16 +388,20 @@ function runtimeProviderSettings(
|
|
|
320
388
|
providerSettings.levelOverrides,
|
|
321
389
|
`settings.providerSettings.${provider}.levelOverrides`
|
|
322
390
|
);
|
|
391
|
+
const webSearch = optionalBoolean(
|
|
392
|
+
providerSettings.webSearch,
|
|
393
|
+
`settings.providerSettings.${provider}.webSearch`
|
|
394
|
+
);
|
|
323
395
|
const gateway =
|
|
324
396
|
provider === 'gateway'
|
|
325
397
|
? normalizeGatewayBuildOptions(providerSettings, 'settings.providerSettings.gateway', cwd)
|
|
326
398
|
: undefined;
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
399
|
+
const base = {
|
|
400
|
+
levelOverrides,
|
|
401
|
+
...(gateway === undefined ? {} : { gateway }),
|
|
402
|
+
...(webSearch === undefined ? {} : { webSearch }),
|
|
403
|
+
};
|
|
404
|
+
return defaultLevel === undefined ? base : { ...base, defaultLevel };
|
|
333
405
|
}
|
|
334
406
|
|
|
335
407
|
function runtimeHelpCommand(provider: ProviderId): CommandParts {
|
|
@@ -340,7 +412,7 @@ function runtimeHelpCommand(provider: ProviderId): CommandParts {
|
|
|
340
412
|
}
|
|
341
413
|
|
|
342
414
|
function probeGatewayProvider(adapter: ProviderAdapter): RuntimeProviderProbe {
|
|
343
|
-
const capabilities = adapter
|
|
415
|
+
const capabilities = attestedCliFeatures(adapter, '', '');
|
|
344
416
|
try {
|
|
345
417
|
const settings = loadRuntimeSettings();
|
|
346
418
|
const providerSettings = runtimeProviderSettings(settings, 'gateway', process.cwd());
|
|
@@ -354,6 +426,9 @@ function probeGatewayProvider(adapter: ProviderAdapter): RuntimeProviderProbe {
|
|
|
354
426
|
helpText: 'Bundled gateway runner',
|
|
355
427
|
versionText: process.version,
|
|
356
428
|
capabilities,
|
|
429
|
+
configuration: {
|
|
430
|
+
webSearch: { requested: false, effective: false },
|
|
431
|
+
},
|
|
357
432
|
};
|
|
358
433
|
} catch {
|
|
359
434
|
return {
|
|
@@ -361,10 +436,49 @@ function probeGatewayProvider(adapter: ProviderAdapter): RuntimeProviderProbe {
|
|
|
361
436
|
helpText: 'Bundled gateway runner',
|
|
362
437
|
versionText: process.version,
|
|
363
438
|
capabilities,
|
|
439
|
+
configuration: {
|
|
440
|
+
webSearch: { requested: false, effective: false },
|
|
441
|
+
},
|
|
364
442
|
};
|
|
365
443
|
}
|
|
366
444
|
}
|
|
367
445
|
|
|
446
|
+
function attestedCliFeatures(
|
|
447
|
+
adapter: ProviderAdapter,
|
|
448
|
+
helpText: string,
|
|
449
|
+
versionText: string
|
|
450
|
+
): ProviderCliFeatures {
|
|
451
|
+
const detected = adapter.detectCliFeatures(helpText, versionText);
|
|
452
|
+
return {
|
|
453
|
+
...detected,
|
|
454
|
+
supportsWebSearch:
|
|
455
|
+
getProviderRegistryEntry(adapter.id).capabilities.webSearch === true &&
|
|
456
|
+
detected.supportsWebSearch === true,
|
|
457
|
+
};
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
function webSearchAttestation(options: {
|
|
461
|
+
readonly webSearch?: boolean;
|
|
462
|
+
readonly cliFeatures?: CliFeatureOverrides;
|
|
463
|
+
}): WebSearchAttestation {
|
|
464
|
+
const requested = options.webSearch === true;
|
|
465
|
+
return {
|
|
466
|
+
requested,
|
|
467
|
+
effective: requested && options.cliFeatures?.supportsWebSearch === true,
|
|
468
|
+
};
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
function assertWebSearchDeclared(provider: ProviderId, requested: boolean | undefined): void {
|
|
472
|
+
if (requested === undefined || getProviderRegistryEntry(provider).capabilities.webSearch === true) {
|
|
473
|
+
return;
|
|
474
|
+
}
|
|
475
|
+
throw new UnsupportedProviderCapabilityError(
|
|
476
|
+
provider,
|
|
477
|
+
'webSearch',
|
|
478
|
+
`Provider ${provider} does not expose provider-controlled native web search; remove options.webSearch.`
|
|
479
|
+
);
|
|
480
|
+
}
|
|
481
|
+
|
|
368
482
|
function loadRuntimeSettings(): Record<string, unknown> {
|
|
369
483
|
if (Object.prototype.hasOwnProperty.call(process.env, LEGACY_ISOLATED_PROVIDER_SETTINGS_ENV)) {
|
|
370
484
|
throw new Error(
|
|
@@ -439,6 +553,12 @@ function addReasoningEffort(result: MutableModelSpec, value: unknown, field: str
|
|
|
439
553
|
if (effort !== undefined) result.reasoningEffort = effort;
|
|
440
554
|
}
|
|
441
555
|
|
|
556
|
+
function optionalBoolean(value: unknown, field: string): boolean | undefined {
|
|
557
|
+
if (value === undefined) return undefined;
|
|
558
|
+
if (typeof value === 'boolean') return value;
|
|
559
|
+
throw new Error(`${field} must be a boolean.`);
|
|
560
|
+
}
|
|
561
|
+
|
|
442
562
|
function optionalModelLevel(value: unknown, field: string): ModelLevel | undefined {
|
|
443
563
|
if (value === undefined) return undefined;
|
|
444
564
|
if (value === 'level1' || value === 'level2' || value === 'level3') return value;
|