@the-open-engine/zeroshot 6.33.0 → 6.34.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 (47) hide show
  1. package/lib/agent-cli-provider/adapters/codex.d.ts.map +1 -1
  2. package/lib/agent-cli-provider/adapters/codex.js +3 -1
  3. package/lib/agent-cli-provider/adapters/codex.js.map +1 -1
  4. package/lib/hosted-target/adapter-request.cjs +55 -0
  5. package/lib/hosted-target/adapter-request.d.cts +9 -0
  6. package/lib/hosted-target/adapter-request.d.mts +9 -0
  7. package/lib/hosted-target/adapter-request.d.ts +9 -0
  8. package/lib/hosted-target/adapter-request.mjs +55 -1
  9. package/lib/hosted-target/adapter-types.d.cts +1 -0
  10. package/lib/hosted-target/adapter-types.d.mts +1 -0
  11. package/lib/hosted-target/adapter-types.d.ts +1 -0
  12. package/lib/hosted-target/response-status.cjs +18 -0
  13. package/lib/hosted-target/response-status.d.cts +2 -0
  14. package/lib/hosted-target/response-status.d.mts +2 -0
  15. package/lib/hosted-target/response-status.d.ts +2 -0
  16. package/lib/hosted-target/response-status.mjs +15 -0
  17. package/lib/hosted-target/retry-executor.d.cts +1 -1
  18. package/lib/hosted-target/retry-executor.d.mts +1 -1
  19. package/lib/hosted-target/retry-executor.d.ts +1 -1
  20. package/lib/hosted-target/runtime-install.cjs +45 -0
  21. package/lib/hosted-target/runtime-install.d.cts +14 -0
  22. package/lib/hosted-target/runtime-install.d.mts +14 -0
  23. package/lib/hosted-target/runtime-install.d.ts +14 -0
  24. package/lib/hosted-target/runtime-install.mjs +42 -0
  25. package/lib/hosted-target/zero-cloud-v1-adapter.cjs +35 -66
  26. package/lib/hosted-target/zero-cloud-v1-adapter.d.cts +1 -0
  27. package/lib/hosted-target/zero-cloud-v1-adapter.d.mts +1 -0
  28. package/lib/hosted-target/zero-cloud-v1-adapter.d.ts +1 -0
  29. package/lib/hosted-target/zero-cloud-v1-adapter.mjs +37 -68
  30. package/lib/target/discovery-validation.cjs +11 -18
  31. package/lib/target/discovery-validation.d.cts +1 -12
  32. package/lib/target/discovery-validation.d.mts +1 -12
  33. package/lib/target/discovery-validation.d.ts +1 -12
  34. package/lib/target/discovery-validation.js +11 -18
  35. package/lib/target/discovery-validation.mjs +11 -18
  36. package/npm-shrinkwrap.json +2 -2
  37. package/package.json +2 -1
  38. package/src/agent/agent-config.js +3 -5
  39. package/src/agent-cli-provider/adapters/codex.ts +3 -1
  40. package/src/hosted-target/adapter-request.ts +75 -1
  41. package/src/hosted-target/adapter-types.ts +6 -0
  42. package/src/hosted-target/response-status.ts +21 -0
  43. package/src/hosted-target/retry-executor.ts +8 -1
  44. package/src/hosted-target/runtime-install.ts +83 -0
  45. package/src/hosted-target/zero-cloud-v1-adapter.ts +65 -100
  46. package/src/target/discovery-validation.ts +26 -28
  47. package/task-lib/runner.js +14 -1
@@ -0,0 +1,83 @@
1
+ import type { CredentialInstallDescriptor } from '../target/discovery.js';
2
+ import { readBoundedResponseJson } from '../target/bounded-response.js';
3
+ import { MAX_RESPONSE_BYTES } from './bounds.js';
4
+ import { TargetProtocolError } from './errors.js';
5
+ import { assertCapsuleResponseStatus } from './response-status.js';
6
+ import { withTargetRetry } from './retry-executor.js';
7
+ import type { Clock, RetryPolicy } from './types.js';
8
+
9
+ type RuntimeInstallOptions = {
10
+ readonly capsuleId: string;
11
+ readonly runtime: unknown;
12
+ readonly accessToken: string;
13
+ readonly descriptor: CredentialInstallDescriptor;
14
+ readonly signal?: AbortSignal;
15
+ readonly clock: Clock;
16
+ readonly retryPolicy: RetryPolicy;
17
+ readonly request: (
18
+ method: string,
19
+ path: string,
20
+ signal: AbortSignal | undefined,
21
+ body: string,
22
+ accessToken: string
23
+ ) => Promise<Response>;
24
+ };
25
+
26
+ function validOpaque(value: string, field: string): void {
27
+ if (value.length === 0 || value.length > 1024) {
28
+ throw new TargetProtocolError(`${field} is invalid`);
29
+ }
30
+ }
31
+
32
+ function runtimeBody(runtime: unknown, maximum: number): string {
33
+ let body: string | undefined;
34
+ try {
35
+ body = JSON.stringify(runtime);
36
+ } catch {
37
+ throw new TargetProtocolError('Runtime bundle is not serializable');
38
+ }
39
+ if (body === undefined || Buffer.byteLength(body) > maximum) {
40
+ throw new TargetProtocolError('Runtime bundle exceeds the advertised size bound');
41
+ }
42
+ return body;
43
+ }
44
+
45
+ export async function installRuntime(options: RuntimeInstallOptions): Promise<void> {
46
+ validOpaque(options.capsuleId, 'capsule id');
47
+ validOpaque(options.accessToken, 'capsule access token');
48
+ let path: string;
49
+ try {
50
+ path = options.descriptor.install.routeTemplate.expand({
51
+ capsule_id: options.capsuleId,
52
+ });
53
+ } catch {
54
+ throw new TargetProtocolError('Runtime install route expansion is unsafe');
55
+ }
56
+ const body = runtimeBody(options.runtime, options.descriptor.maxBodyBytes);
57
+ await withTargetRetry(
58
+ 'installRuntime',
59
+ async () => {
60
+ const response = await options.request(
61
+ options.descriptor.install.method,
62
+ path,
63
+ options.signal,
64
+ body,
65
+ options.accessToken
66
+ );
67
+ await assertCapsuleResponseStatus(
68
+ response,
69
+ 204,
70
+ (errorResponse) =>
71
+ readBoundedResponseJson(
72
+ errorResponse,
73
+ MAX_RESPONSE_BYTES,
74
+ () => new TargetProtocolError('Capsule error response is malformed')
75
+ ),
76
+ options.clock
77
+ );
78
+ await response.body?.cancel().catch(() => undefined);
79
+ },
80
+ options.signal,
81
+ { clock: options.clock, policy: options.retryPolicy }
82
+ );
83
+ }
@@ -1,10 +1,5 @@
1
1
  import { IDEMPOTENCY_KEY_PATTERN, MAX_RESPONSE_BYTES } from './bounds.js';
2
- import {
3
- TargetAdapterError,
4
- TargetAuthError,
5
- TargetProtocolError,
6
- TargetTransportError,
7
- } from './errors.js';
2
+ import { TargetProtocolError } from './errors.js';
8
3
  import { DefaultRetryPolicy } from './retry.js';
9
4
  import {
10
5
  assertCapsule,
@@ -20,34 +15,23 @@ import type {
20
15
  CapsuleLimits,
21
16
  CapsuleListPage,
22
17
  Clock,
23
- HttpTransport,
24
18
  ListRequest,
25
19
  RetryPolicy,
26
- TargetAccessTokenProvider,
27
20
  } from './types.js';
28
21
  import type { TargetDiscoveryDescriptor } from '../target/discovery.js';
29
22
  import { readBoundedResponseJson } from '../target/bounded-response.js';
30
- import { throwCapsuleServerError } from './capsule-error-response.js';
31
23
  import { validateAccessUrl } from './access-url.js';
32
24
  import { withTargetRetry } from './retry-executor.js';
25
+ import { installRuntime as installOpaqueRuntime } from './runtime-install.js';
26
+ import { assertCapsuleResponseStatus } from './response-status.js';
33
27
  import {
34
- requestUrl,
35
- type AdapterRequest,
28
+ createAdapterRequester,
29
+ type AdapterRequester,
36
30
  type ExecuteArguments,
37
31
  } from './adapter-request.js';
38
32
 
39
- const DEFAULT_TRANSPORT: HttpTransport = {
40
- fetch(url, init) {
41
- return globalThis.fetch(url, init);
42
- },
43
- };
44
33
  const DEFAULT_CLOCK: Clock = { now: () => Date.now() };
45
34
 
46
- function throwIfAborted(signal?: AbortSignal): void {
47
- if (signal?.aborted)
48
- throw signal.reason ?? new globalThis.DOMException('The operation was aborted', 'AbortError');
49
- }
50
-
51
35
  function validOpaque(value: string, field: string): void {
52
36
  if (value.length === 0 || value.length > 1024)
53
37
  throw new TargetProtocolError(`${field} is invalid`);
@@ -57,12 +41,10 @@ function jsonRequest(body: unknown): string {
57
41
  return JSON.stringify(body);
58
42
  }
59
43
 
60
-
61
44
  export class ZeroCloudV1TargetAdapter implements TargetAdapter {
62
45
  readonly #descriptor: TargetDiscoveryDescriptor;
63
46
  readonly #organizationId: string;
64
- readonly #tokenProvider: TargetAccessTokenProvider;
65
- readonly #transport: HttpTransport;
47
+ readonly #request: AdapterRequester;
66
48
  readonly #clock: Clock;
67
49
  readonly #retryPolicy: RetryPolicy;
68
50
 
@@ -70,8 +52,11 @@ export class ZeroCloudV1TargetAdapter implements TargetAdapter {
70
52
  this.#descriptor = options.descriptor;
71
53
  this.#organizationId = options.organization.id;
72
54
  validOpaque(this.#organizationId, 'organization id');
73
- this.#tokenProvider = options.tokenProvider;
74
- this.#transport = options.transport ?? DEFAULT_TRANSPORT;
55
+ this.#request = createAdapterRequester(
56
+ options.descriptor,
57
+ options.tokenProvider,
58
+ options.transport
59
+ );
75
60
  this.#clock = options.clock ?? DEFAULT_CLOCK;
76
61
  this.#retryPolicy = options.retryPolicy ?? new DefaultRetryPolicy();
77
62
  }
@@ -199,21 +184,42 @@ export class ZeroCloudV1TargetAdapter implements TargetAdapter {
199
184
  return result;
200
185
  }
201
186
 
187
+ async installRuntime(
188
+ capsuleId: string,
189
+ runtime: unknown,
190
+ accessToken: string,
191
+ signal?: AbortSignal
192
+ ): Promise<void> {
193
+ const descriptor = this.#descriptor.credentialInstall;
194
+ if (descriptor === null) {
195
+ throw new TargetProtocolError('Target does not advertise runtime installation');
196
+ }
197
+ await installOpaqueRuntime({
198
+ capsuleId,
199
+ runtime,
200
+ accessToken,
201
+ descriptor,
202
+ ...(signal === undefined ? {} : { signal }),
203
+ clock: this.#clock,
204
+ retryPolicy: this.#retryPolicy,
205
+ request: (method, path, requestSignal, body, token) =>
206
+ this.#request({
207
+ method,
208
+ path,
209
+ signal: requestSignal,
210
+ request: { body },
211
+ accessToken: token,
212
+ }),
213
+ });
214
+ }
215
+
202
216
  #execute<T>(...args: ExecuteArguments<T>): Promise<T> {
203
217
  return Promise.resolve().then(() => this.#executeExpanded(args));
204
218
  }
205
219
 
206
220
  #executeExpanded<T>(args: ExecuteArguments<T>): Promise<T> {
207
- const [
208
- operation,
209
- method,
210
- template,
211
- values,
212
- expectedStatus,
213
- validate,
214
- signal,
215
- request = {},
216
- ] = args;
221
+ const [operation, method, template, values, expectedStatus, validate, signal, request = {}] =
222
+ args;
217
223
  let path: string;
218
224
  try {
219
225
  path = template.expand(values);
@@ -223,77 +229,36 @@ export class ZeroCloudV1TargetAdapter implements TargetAdapter {
223
229
  return withTargetRetry(
224
230
  operation,
225
231
  async () => {
226
- const response = await this.#request(method, path, signal, request);
227
- if (response.status >= 300 && response.status < 400) {
228
- await response.body?.cancel().catch(() => undefined);
229
- throw new TargetProtocolError('Capsule redirects are forbidden');
230
- }
231
- if (response.status !== expectedStatus) {
232
- if (response.status >= 200 && response.status < 300) {
233
- await response.body?.cancel().catch(() => undefined);
234
- throw new TargetProtocolError('Target returned an unexpected success status');
235
- }
236
- await throwCapsuleServerError(
237
- response,
238
- (errorResponse) => this.#readJson(errorResponse),
239
- this.#clock,
240
- );
241
- }
232
+ const response = await this.#request({
233
+ method,
234
+ path,
235
+ signal,
236
+ request,
237
+ accessToken: undefined,
238
+ });
239
+ await assertCapsuleResponseStatus(
240
+ response,
241
+ expectedStatus,
242
+ (errorResponse) => this.#readJson(errorResponse),
243
+ this.#clock
244
+ );
242
245
  return validate(await this.#readJson(response));
243
246
  },
244
247
  signal,
245
- { clock: this.#clock, policy: this.#retryPolicy },
248
+ { clock: this.#clock, policy: this.#retryPolicy }
246
249
  );
247
250
  }
248
251
 
249
- async #request(
250
- method: string,
251
- path: string,
252
- signal: AbortSignal | undefined,
253
- request: AdapterRequest,
254
- ): Promise<Response> {
255
- throwIfAborted(signal);
256
- const url = requestUrl(path, this.#descriptor);
257
- let token: string;
258
- try {
259
- token = await this.#tokenProvider.getAccessToken(signal);
260
- } catch {
261
- throw new TargetAuthError('Target access authorization failed');
262
- }
263
- const init: RequestInit & { redirect: 'manual' } = {
264
- method,
265
- headers: {
266
- Accept: 'application/json',
267
- Authorization: `Bearer ${token}`,
268
- ...(request.body === undefined ? {} : { 'Content-Type': 'application/json' }),
269
- ...request.headers,
270
- },
271
- redirect: 'manual',
272
- };
273
- if (request.body !== undefined) init.body = request.body;
274
- if (signal !== undefined) init.signal = signal;
275
- try {
276
- const response = await this.#transport.fetch(url.href, init);
277
- if (response.url && new globalThis.URL(response.url).href !== url.href) {
278
- await response.body?.cancel().catch(() => undefined);
279
- throw new TargetProtocolError('Capsule response changed target route');
280
- }
281
- return response;
282
- } catch (error) {
283
- if (error instanceof TargetAdapterError) throw error;
284
- throwIfAborted(signal);
285
- throw new TargetTransportError('Capsule transport failed');
286
- }
287
- }
288
-
289
-
290
252
  #readJson(response: Response): Promise<unknown> {
291
- return readBoundedResponseJson(response, MAX_RESPONSE_BYTES, (kind) =>
292
- new TargetProtocolError(
293
- kind === 'size'
294
- ? 'Capsule response exceeds the size limit'
295
- : 'Capsule response is not valid UTF-8 JSON',
296
- ),
253
+ return readBoundedResponseJson(
254
+ response,
255
+ MAX_RESPONSE_BYTES,
256
+ (kind) =>
257
+ new TargetProtocolError(
258
+ kind === 'size'
259
+ ? 'Capsule response exceeds the size limit'
260
+ : 'Capsule response is not valid UTF-8 JSON'
261
+ )
297
262
  );
298
263
  }
299
264
  }
@@ -3,16 +3,8 @@ import { routeTemplate, type RouteTemplate } from './route-template.js';
3
3
 
4
4
  export interface CredentialInstallDescriptor {
5
5
  readonly kind: 'openengine.capsule-credential-install/v1';
6
- readonly grant: { readonly routeTemplate: RouteTemplate; readonly method: 'POST' };
7
6
  readonly install: { readonly routeTemplate: RouteTemplate; readonly method: 'PUT' };
8
- readonly uploadUrlOrigin: 'same_origin';
9
- readonly sealedEnvelopeAlgorithms: readonly ['RSA-OAEP-3072-SHA256'];
10
- readonly bounds: {
11
- readonly maxEnvelopeBytes: number;
12
- readonly maxBodyBytes: number;
13
- readonly grantTtlSeconds: number;
14
- readonly maxClockSkewSeconds: number;
15
- };
7
+ readonly maxBodyBytes: number;
16
8
  }
17
9
 
18
10
  export function record(value: unknown, field: string): Record<string, unknown> {
@@ -116,29 +108,35 @@ export function sameOriginUrl(
116
108
  export function parseCredentialInstall(value: unknown): CredentialInstallDescriptor | null {
117
109
  if (value === undefined || value === null) return null;
118
110
  const extension = closedRecord(value, 'extensions.credential_install', [
119
- 'kind', 'grant', 'install', 'upload_url_origin', 'sealed_envelope_algorithms', 'bounds',
111
+ 'kind',
112
+ 'install',
113
+ 'max_body_bytes',
120
114
  ]);
121
- exact(extension.kind, 'openengine.capsule-credential-install/v1', 'extensions.credential_install.kind');
122
- const grant = closedRecord(extension.grant, 'extensions.credential_install.grant', ['route_template', 'method']);
123
- const install = closedRecord(extension.install, 'extensions.credential_install.install', ['route_template', 'method']);
124
- exact(grant.method, 'POST', 'extensions.credential_install.grant.method');
125
- exact(install.method, 'PUT', 'extensions.credential_install.install.method');
126
- exact(extension.upload_url_origin, 'same_origin', 'extensions.credential_install.upload_url_origin');
127
- exactStringSet(extension.sealed_envelope_algorithms, 'extensions.credential_install.sealed_envelope_algorithms', ['RSA-OAEP-3072-SHA256']);
128
- const bounds = closedRecord(extension.bounds, 'extensions.credential_install.bounds', [
129
- 'max_envelope_bytes', 'max_body_bytes', 'grant_ttl_seconds', 'max_clock_skew_seconds',
115
+ exact(
116
+ extension.kind,
117
+ 'openengine.capsule-credential-install/v1',
118
+ 'extensions.credential_install.kind'
119
+ );
120
+ const install = closedRecord(extension.install, 'extensions.credential_install.install', [
121
+ 'route_template',
122
+ 'method',
130
123
  ]);
124
+ exact(install.method, 'PUT', 'extensions.credential_install.install.method');
131
125
  return Object.freeze({
132
126
  kind: 'openengine.capsule-credential-install/v1' as const,
133
- grant: Object.freeze({ routeTemplate: routeTemplate(grant.route_template, 'extensions.credential_install.grant.route_template', ['capsule_id']), method: 'POST' as const }),
134
- install: Object.freeze({ routeTemplate: routeTemplate(install.route_template, 'extensions.credential_install.install.route_template', ['capsule_id']), method: 'PUT' as const }),
135
- uploadUrlOrigin: 'same_origin' as const,
136
- sealedEnvelopeAlgorithms: Object.freeze(['RSA-OAEP-3072-SHA256'] as const),
137
- bounds: Object.freeze({
138
- maxEnvelopeBytes: integer(bounds.max_envelope_bytes, 'extensions.credential_install.bounds.max_envelope_bytes', 1, 1_048_576),
139
- maxBodyBytes: integer(bounds.max_body_bytes, 'extensions.credential_install.bounds.max_body_bytes', 1, 1_048_576),
140
- grantTtlSeconds: integer(bounds.grant_ttl_seconds, 'extensions.credential_install.bounds.grant_ttl_seconds', 1, 3_600),
141
- maxClockSkewSeconds: integer(bounds.max_clock_skew_seconds, 'extensions.credential_install.bounds.max_clock_skew_seconds', 0, 300),
127
+ install: Object.freeze({
128
+ routeTemplate: routeTemplate(
129
+ install.route_template,
130
+ 'extensions.credential_install.install.route_template',
131
+ ['capsule_id']
132
+ ),
133
+ method: 'PUT' as const,
142
134
  }),
135
+ maxBodyBytes: integer(
136
+ extension.max_body_bytes,
137
+ 'extensions.credential_install.max_body_bytes',
138
+ 1,
139
+ 4 * 1024 * 1024
140
+ ),
143
141
  });
144
142
  }
@@ -43,6 +43,8 @@ const {
43
43
  partitionPathFor,
44
44
  createOmpSessionPartitionDirectory,
45
45
  } = require('../src/omp-session-partition');
46
+ const TASK_EXECUTION_CONTEXT_ENV = 'ZEROSHOT_TASK_EXECUTION_CONTEXT';
47
+ const TASK_EXECUTION_CONTEXTS = new Set(['host', 'detached', 'docker', 'benchmark']);
46
48
  export {
47
49
  isOwnedProcessTreeRunning,
48
50
  isProcessRunning,
@@ -413,7 +415,7 @@ function buildProviderOptions(options, runtime, modelSelection) {
413
415
  outputFormat: runtime.outputFormat,
414
416
  jsonSchema: runtime.jsonSchema,
415
417
  cwd: runtime.cwd,
416
- executionContext: 'detached',
418
+ executionContext: resolveTaskExecutionContext(),
417
419
  autoApprove: !structuredOutputRecovery,
418
420
  ...(modelSelection === undefined ? {} : { modelSpec: modelSelection.modelSpec }),
419
421
  ...(structuredOutputRecovery ? {} : mcpConfigOption(options)),
@@ -428,6 +430,17 @@ function buildProviderOptions(options, runtime, modelSelection) {
428
430
  };
429
431
  }
430
432
 
433
+ export function resolveTaskExecutionContext(environment = process.env) {
434
+ const context = environment[TASK_EXECUTION_CONTEXT_ENV];
435
+ if (context === undefined) return 'detached';
436
+ if (!TASK_EXECUTION_CONTEXTS.has(context)) {
437
+ throw new Error(
438
+ `${TASK_EXECUTION_CONTEXT_ENV} must be one of: ${[...TASK_EXECUTION_CONTEXTS].join(', ')}.`
439
+ );
440
+ }
441
+ return context;
442
+ }
443
+
431
444
  function claudeSettingsFileOption() {
432
445
  const settingsPath = process.env[CLAUDE_SETTINGS_ENV]?.trim();
433
446
  return settingsPath ? { claudeSettingsFile: settingsPath } : {};