@the-open-engine/zeroshot 6.32.0 → 6.33.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 (51) hide show
  1. package/lib/agent-cli-provider/adapters/codex.d.ts.map +1 -1
  2. package/lib/agent-cli-provider/adapters/codex.js +14 -6
  3. package/lib/agent-cli-provider/adapters/codex.js.map +1 -1
  4. package/lib/agent-cli-provider/provider-registry.d.ts +4 -2
  5. package/lib/agent-cli-provider/provider-registry.d.ts.map +1 -1
  6. package/lib/agent-cli-provider/provider-registry.js +13 -3
  7. package/lib/agent-cli-provider/provider-registry.js.map +1 -1
  8. package/lib/agent-cli-provider/single-agent-runtime.d.ts.map +1 -1
  9. package/lib/agent-cli-provider/single-agent-runtime.js +7 -4
  10. package/lib/agent-cli-provider/single-agent-runtime.js.map +1 -1
  11. package/lib/agent-cli-provider/types.d.ts +2 -0
  12. package/lib/agent-cli-provider/types.d.ts.map +1 -1
  13. package/lib/agent-cli-provider/types.js.map +1 -1
  14. package/lib/detached-startup.js +4 -0
  15. package/lib/hosted-target/adapter-request.cjs +6 -3
  16. package/lib/hosted-target/adapter-request.mjs +6 -3
  17. package/lib/target/discovery-sections.cjs +35 -13
  18. package/lib/target/discovery-sections.d.cts +5 -1
  19. package/lib/target/discovery-sections.d.mts +5 -1
  20. package/lib/target/discovery-sections.d.ts +5 -1
  21. package/lib/target/discovery-sections.js +35 -13
  22. package/lib/target/discovery-sections.mjs +35 -13
  23. package/lib/target/discovery.cjs +49 -38
  24. package/lib/target/discovery.d.cts +3 -0
  25. package/lib/target/discovery.d.mts +3 -0
  26. package/lib/target/discovery.d.ts +3 -0
  27. package/lib/target/discovery.js +49 -38
  28. package/lib/target/discovery.mjs +49 -38
  29. package/lib/target/index.d.ts +1 -1
  30. package/lib/target/run-intent-discovery.cjs +30 -0
  31. package/lib/target/run-intent-discovery.d.cts +11 -0
  32. package/lib/target/run-intent-discovery.d.mts +11 -0
  33. package/lib/target/run-intent-discovery.d.ts +11 -0
  34. package/lib/target/run-intent-discovery.js +30 -0
  35. package/lib/target/run-intent-discovery.mjs +27 -0
  36. package/npm-shrinkwrap.json +2 -2
  37. package/package.json +2 -3
  38. package/src/agent/agent-lifecycle.js +11 -1
  39. package/src/agent/agent-task-executor.js +25 -7
  40. package/src/agent/structured-output-error.js +42 -0
  41. package/src/agent-cli-provider/adapters/codex.ts +15 -16
  42. package/src/agent-cli-provider/provider-registry.ts +15 -3
  43. package/src/agent-cli-provider/single-agent-runtime.ts +11 -11
  44. package/src/agent-cli-provider/types.ts +2 -0
  45. package/src/hosted-target/adapter-request.ts +8 -3
  46. package/src/isolation-manager.js +52 -0
  47. package/src/orchestrator.js +6 -0
  48. package/src/target/discovery-sections.ts +59 -41
  49. package/src/target/discovery.ts +71 -54
  50. package/src/target/index.ts +1 -0
  51. package/src/target/run-intent-discovery.ts +39 -0
@@ -33,52 +33,27 @@ const ROOT_FIELDS = [
33
33
  'transport',
34
34
  'extensions',
35
35
  ];
36
- async function readBoundedJson(response) {
37
- return (0, bounded_response_js_1.readBoundedResponseJson)(response, MAX_DISCOVERY_BYTES, (kind) => new discovery_errors_js_1.TargetDiscoveryError(kind === 'size' ? 'response exceeds the size limit' : 'response is not valid UTF-8 JSON'));
38
- }
39
- async function fetchDocument(http, url) {
40
- const response = await http.fetch(url, {
41
- method: 'GET',
42
- headers: { Accept: 'application/json' },
43
- redirect: 'error',
44
- });
45
- if (response.url && new URL(response.url).href !== url) {
46
- await response.body?.cancel().catch(() => undefined);
47
- throw new discovery_errors_js_1.TargetDiscoveryError('request changed target route or authority');
48
- }
49
- if (!response.ok) {
50
- await response.body?.cancel().catch(() => undefined);
51
- throw new discovery_errors_js_1.TargetDiscoveryError(`request failed with status ${response.status}`);
52
- }
53
- return (0, discovery_validation_js_1.record)(await readBoundedJson(response), 'response');
54
- }
55
- async function discoverTarget(targetUrl, http) {
56
- const target = new URL(targetUrl);
57
- const origin = target.origin;
58
- const discovery = await fetchDocument(http, new URL(DISCOVERY_PATH, target).href);
36
+ const FLAGS = [
37
+ 'capsule_allocate',
38
+ 'capsule_read',
39
+ 'capsule_terminate',
40
+ 'capsule_access',
41
+ 'connections_onboarding',
42
+ ];
43
+ const SIZE_ERROR = 'response exceeds the size limit';
44
+ const JSON_ERROR = 'response is not valid UTF-8 JSON';
45
+ function parseDiscoveryDocument(discovery, origin) {
59
46
  (0, discovery_validation_js_1.exact)(discovery.kind, 'openengine.hosted-target/v1', 'kind');
60
47
  const adapter = (0, discovery_sections_js_1.parseAdapter)(discovery);
61
48
  const endpoint = (0, discovery_sections_js_1.parseEndpoint)(discovery, origin);
62
49
  const capsule = (0, discovery_sections_js_1.parseCapsule)(discovery, origin);
63
50
  const oauth = (0, discovery_sections_js_1.parseOAuth)(discovery, origin);
64
51
  (0, discovery_validation_js_1.exact)(discovery.organization_binding, 'device_approval', 'organization_binding');
65
- const capabilityFlags = (0, discovery_validation_js_1.exactStringSet)(discovery.capability_flags, 'capability_flags', [
66
- 'capsule_allocate',
67
- 'capsule_read',
68
- 'capsule_terminate',
69
- 'capsule_access',
70
- 'connections_onboarding',
71
- ]);
52
+ const capabilityFlags = (0, discovery_validation_js_1.exactStringSet)(discovery.capability_flags, 'capability_flags', FLAGS);
72
53
  const sizes = (0, discovery_sections_js_1.parseSizes)(discovery);
73
54
  const session = (0, discovery_sections_js_1.parseSession)(discovery);
74
55
  const transport = (0, discovery_sections_js_1.parseTransport)(discovery);
75
- const credentialInstall = (0, discovery_sections_js_1.parseExtensions)(discovery, origin);
76
- const metadata = await fetchDocument(http, oauth.metadataUrl);
77
- (0, discovery_sections_js_1.validateOAuthMetadata)(metadata, origin, [
78
- oauth.deviceAuthorizationEndpoint,
79
- oauth.tokenEndpoint,
80
- oauth.revocationEndpoint,
81
- ]);
56
+ const extensions = (0, discovery_sections_js_1.parseExtensions)(discovery, origin);
82
57
  const additional = Object.freeze(Object.fromEntries(Object.entries(discovery).filter(([key]) => !ROOT_FIELDS.includes(key))));
83
58
  return Object.freeze({
84
59
  origin,
@@ -92,10 +67,24 @@ async function discoverTarget(targetUrl, http) {
92
67
  capsule,
93
68
  transport,
94
69
  capabilityFlags,
95
- credentialInstall,
70
+ credentialInstall: extensions.credentialInstall,
71
+ runIntent: extensions.runIntent,
96
72
  additional,
97
73
  });
98
74
  }
75
+ async function discoverTarget(targetUrl, http) {
76
+ const target = new URL(targetUrl);
77
+ const origin = target.origin;
78
+ const discovery = await fetchDocument(http, new URL(DISCOVERY_PATH, target).href);
79
+ const descriptor = parseDiscoveryDocument(discovery, origin);
80
+ const metadata = await fetchDocument(http, descriptor.oauth.metadataUrl);
81
+ (0, discovery_sections_js_1.validateOAuthMetadata)(metadata, origin, [
82
+ descriptor.oauth.deviceAuthorizationEndpoint,
83
+ descriptor.oauth.tokenEndpoint,
84
+ descriptor.oauth.revocationEndpoint,
85
+ ]);
86
+ return descriptor;
87
+ }
99
88
  async function discoverTargetSessionEndpoints(targetUrl, http) {
100
89
  const descriptor = await discoverTarget(targetUrl, http);
101
90
  return Object.freeze({
@@ -110,3 +99,25 @@ async function discoverTargetSessionEndpoints(targetUrl, http) {
110
99
  descriptor,
111
100
  });
112
101
  }
102
+ function boundedResponseError(kind) {
103
+ return new discovery_errors_js_1.TargetDiscoveryError(kind === 'size' ? SIZE_ERROR : JSON_ERROR);
104
+ }
105
+ async function readBoundedJson(response) {
106
+ return (0, bounded_response_js_1.readBoundedResponseJson)(response, MAX_DISCOVERY_BYTES, boundedResponseError);
107
+ }
108
+ async function fetchDocument(http, url) {
109
+ const response = await http.fetch(url, {
110
+ method: 'GET',
111
+ headers: { Accept: 'application/json' },
112
+ redirect: 'error',
113
+ });
114
+ if (response.url && new URL(response.url).href !== url) {
115
+ await response.body?.cancel().catch(() => undefined);
116
+ throw new discovery_errors_js_1.TargetDiscoveryError('request changed target route or authority');
117
+ }
118
+ if (!response.ok) {
119
+ await response.body?.cancel().catch(() => undefined);
120
+ throw new discovery_errors_js_1.TargetDiscoveryError(`request failed with status ${response.status}`);
121
+ }
122
+ return (0, discovery_validation_js_1.record)(await readBoundedJson(response), 'response');
123
+ }
@@ -26,52 +26,27 @@ const ROOT_FIELDS = [
26
26
  'transport',
27
27
  'extensions',
28
28
  ];
29
- async function readBoundedJson(response) {
30
- return readBoundedResponseJson(response, MAX_DISCOVERY_BYTES, (kind) => new TargetDiscoveryError(kind === 'size' ? 'response exceeds the size limit' : 'response is not valid UTF-8 JSON'));
31
- }
32
- async function fetchDocument(http, url) {
33
- const response = await http.fetch(url, {
34
- method: 'GET',
35
- headers: { Accept: 'application/json' },
36
- redirect: 'error',
37
- });
38
- if (response.url && new URL(response.url).href !== url) {
39
- await response.body?.cancel().catch(() => undefined);
40
- throw new TargetDiscoveryError('request changed target route or authority');
41
- }
42
- if (!response.ok) {
43
- await response.body?.cancel().catch(() => undefined);
44
- throw new TargetDiscoveryError(`request failed with status ${response.status}`);
45
- }
46
- return record(await readBoundedJson(response), 'response');
47
- }
48
- export async function discoverTarget(targetUrl, http) {
49
- const target = new URL(targetUrl);
50
- const origin = target.origin;
51
- const discovery = await fetchDocument(http, new URL(DISCOVERY_PATH, target).href);
29
+ const FLAGS = [
30
+ 'capsule_allocate',
31
+ 'capsule_read',
32
+ 'capsule_terminate',
33
+ 'capsule_access',
34
+ 'connections_onboarding',
35
+ ];
36
+ const SIZE_ERROR = 'response exceeds the size limit';
37
+ const JSON_ERROR = 'response is not valid UTF-8 JSON';
38
+ function parseDiscoveryDocument(discovery, origin) {
52
39
  exact(discovery.kind, 'openengine.hosted-target/v1', 'kind');
53
40
  const adapter = parseAdapter(discovery);
54
41
  const endpoint = parseEndpoint(discovery, origin);
55
42
  const capsule = parseCapsule(discovery, origin);
56
43
  const oauth = parseOAuth(discovery, origin);
57
44
  exact(discovery.organization_binding, 'device_approval', 'organization_binding');
58
- const capabilityFlags = exactStringSet(discovery.capability_flags, 'capability_flags', [
59
- 'capsule_allocate',
60
- 'capsule_read',
61
- 'capsule_terminate',
62
- 'capsule_access',
63
- 'connections_onboarding',
64
- ]);
45
+ const capabilityFlags = exactStringSet(discovery.capability_flags, 'capability_flags', FLAGS);
65
46
  const sizes = parseSizes(discovery);
66
47
  const session = parseSession(discovery);
67
48
  const transport = parseTransport(discovery);
68
- const credentialInstall = parseExtensions(discovery, origin);
69
- const metadata = await fetchDocument(http, oauth.metadataUrl);
70
- validateOAuthMetadata(metadata, origin, [
71
- oauth.deviceAuthorizationEndpoint,
72
- oauth.tokenEndpoint,
73
- oauth.revocationEndpoint,
74
- ]);
49
+ const extensions = parseExtensions(discovery, origin);
75
50
  const additional = Object.freeze(Object.fromEntries(Object.entries(discovery).filter(([key]) => !ROOT_FIELDS.includes(key))));
76
51
  return Object.freeze({
77
52
  origin,
@@ -85,10 +60,24 @@ export async function discoverTarget(targetUrl, http) {
85
60
  capsule,
86
61
  transport,
87
62
  capabilityFlags,
88
- credentialInstall,
63
+ credentialInstall: extensions.credentialInstall,
64
+ runIntent: extensions.runIntent,
89
65
  additional,
90
66
  });
91
67
  }
68
+ export async function discoverTarget(targetUrl, http) {
69
+ const target = new URL(targetUrl);
70
+ const origin = target.origin;
71
+ const discovery = await fetchDocument(http, new URL(DISCOVERY_PATH, target).href);
72
+ const descriptor = parseDiscoveryDocument(discovery, origin);
73
+ const metadata = await fetchDocument(http, descriptor.oauth.metadataUrl);
74
+ validateOAuthMetadata(metadata, origin, [
75
+ descriptor.oauth.deviceAuthorizationEndpoint,
76
+ descriptor.oauth.tokenEndpoint,
77
+ descriptor.oauth.revocationEndpoint,
78
+ ]);
79
+ return descriptor;
80
+ }
92
81
  export async function discoverTargetSessionEndpoints(targetUrl, http) {
93
82
  const descriptor = await discoverTarget(targetUrl, http);
94
83
  return Object.freeze({
@@ -103,3 +92,25 @@ export async function discoverTargetSessionEndpoints(targetUrl, http) {
103
92
  descriptor,
104
93
  });
105
94
  }
95
+ function boundedResponseError(kind) {
96
+ return new TargetDiscoveryError(kind === 'size' ? SIZE_ERROR : JSON_ERROR);
97
+ }
98
+ async function readBoundedJson(response) {
99
+ return readBoundedResponseJson(response, MAX_DISCOVERY_BYTES, boundedResponseError);
100
+ }
101
+ async function fetchDocument(http, url) {
102
+ const response = await http.fetch(url, {
103
+ method: 'GET',
104
+ headers: { Accept: 'application/json' },
105
+ redirect: 'error',
106
+ });
107
+ if (response.url && new URL(response.url).href !== url) {
108
+ await response.body?.cancel().catch(() => undefined);
109
+ throw new TargetDiscoveryError('request changed target route or authority');
110
+ }
111
+ if (!response.ok) {
112
+ await response.body?.cancel().catch(() => undefined);
113
+ throw new TargetDiscoveryError(`request failed with status ${response.status}`);
114
+ }
115
+ return record(await readBoundedJson(response), 'response');
116
+ }
@@ -3,4 +3,4 @@ export { acquireTargetLock } from './credential-lock.js';
3
3
  export { requestDeviceCode, pollForToken, parseTokenResponse, DeviceFlowDeniedError, DeviceFlowExpiredError, UnboundSessionError, type DeviceCodeResponse, type DeviceExchangeContext, type TokenResponse, type HttpTransport, type Clock, } from './device-flow.js';
4
4
  export { addTarget, removeTarget, getTarget, listTargets, updateTargetOrganization, validateTargetName, normalizeAndValidateUrl, TargetNameInvalidError, TargetNameExistsError, TargetNotFoundError, TargetUrlInvalidError, type TargetRecord, type SettingsPort, } from './target-registry.js';
5
5
  export { TargetSessionManager, LoginRequiredError, type BrowserOpener, type TargetSessionDeps, type TargetSessionManagerInit, } from './target-session.js';
6
- export { discoverTarget, discoverTargetSessionEndpoints, expandRoute, TargetDiscoveryError, type CredentialInstallDescriptor, type RouteTemplate, type TargetDiscoveryDescriptor, type TargetSessionEndpoints, } from './discovery.js';
6
+ export { discoverTarget, discoverTargetSessionEndpoints, expandRoute, TargetDiscoveryError, type CredentialInstallDescriptor, type RunIntentDescriptor, type RouteTemplate, type TargetDiscoveryDescriptor, type TargetSessionEndpoints, } from './discovery.js';
@@ -0,0 +1,30 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.parseRunIntent = parseRunIntent;
4
+ const route_template_js_1 = require("./route-template.cjs");
5
+ const discovery_validation_js_1 = require("./discovery-validation.cjs");
6
+ function parseRunIntent(value, origin) {
7
+ if (value === undefined || value === null)
8
+ return null;
9
+ const extension = (0, discovery_validation_js_1.closedRecord)(value, 'extensions.run_intent', [
10
+ 'kind',
11
+ 'base_url',
12
+ 'route_templates',
13
+ ]);
14
+ (0, discovery_validation_js_1.exact)(extension.kind, 'zeroshot.run-intent/v2', 'extensions.run_intent.kind');
15
+ const routes = (0, discovery_validation_js_1.closedRecord)(extension.route_templates, 'extensions.run_intent.route_templates', [
16
+ 'submit',
17
+ 'status',
18
+ 'cancel',
19
+ ]);
20
+ const route = (name, variables) => (0, route_template_js_1.routeTemplate)(routes[name], `extensions.run_intent.route_templates.${name}`, variables);
21
+ return Object.freeze({
22
+ kind: 'zeroshot.run-intent/v2',
23
+ baseUrl: (0, discovery_validation_js_1.sameOriginUrl)(extension.base_url, 'extensions.run_intent.base_url', origin),
24
+ routes: Object.freeze({
25
+ submit: route('submit', ['org_id']),
26
+ status: route('status', ['org_id', 'intent_id']),
27
+ cancel: route('cancel', ['org_id', 'intent_id']),
28
+ }),
29
+ });
30
+ }
@@ -0,0 +1,11 @@
1
+ import type { RouteTemplate } from './route-template.js';
2
+ export interface RunIntentDescriptor {
3
+ readonly kind: 'zeroshot.run-intent/v2';
4
+ readonly baseUrl: string;
5
+ readonly routes: {
6
+ readonly submit: RouteTemplate;
7
+ readonly status: RouteTemplate;
8
+ readonly cancel: RouteTemplate;
9
+ };
10
+ }
11
+ export declare function parseRunIntent(value: unknown, origin: string): RunIntentDescriptor | null;
@@ -0,0 +1,11 @@
1
+ import type { RouteTemplate } from './route-template.js';
2
+ export interface RunIntentDescriptor {
3
+ readonly kind: 'zeroshot.run-intent/v2';
4
+ readonly baseUrl: string;
5
+ readonly routes: {
6
+ readonly submit: RouteTemplate;
7
+ readonly status: RouteTemplate;
8
+ readonly cancel: RouteTemplate;
9
+ };
10
+ }
11
+ export declare function parseRunIntent(value: unknown, origin: string): RunIntentDescriptor | null;
@@ -0,0 +1,11 @@
1
+ import type { RouteTemplate } from './route-template.js';
2
+ export interface RunIntentDescriptor {
3
+ readonly kind: 'zeroshot.run-intent/v2';
4
+ readonly baseUrl: string;
5
+ readonly routes: {
6
+ readonly submit: RouteTemplate;
7
+ readonly status: RouteTemplate;
8
+ readonly cancel: RouteTemplate;
9
+ };
10
+ }
11
+ export declare function parseRunIntent(value: unknown, origin: string): RunIntentDescriptor | null;
@@ -0,0 +1,30 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.parseRunIntent = parseRunIntent;
4
+ const route_template_js_1 = require("./route-template.js");
5
+ const discovery_validation_js_1 = require("./discovery-validation.js");
6
+ function parseRunIntent(value, origin) {
7
+ if (value === undefined || value === null)
8
+ return null;
9
+ const extension = (0, discovery_validation_js_1.closedRecord)(value, 'extensions.run_intent', [
10
+ 'kind',
11
+ 'base_url',
12
+ 'route_templates',
13
+ ]);
14
+ (0, discovery_validation_js_1.exact)(extension.kind, 'zeroshot.run-intent/v2', 'extensions.run_intent.kind');
15
+ const routes = (0, discovery_validation_js_1.closedRecord)(extension.route_templates, 'extensions.run_intent.route_templates', [
16
+ 'submit',
17
+ 'status',
18
+ 'cancel',
19
+ ]);
20
+ const route = (name, variables) => (0, route_template_js_1.routeTemplate)(routes[name], `extensions.run_intent.route_templates.${name}`, variables);
21
+ return Object.freeze({
22
+ kind: 'zeroshot.run-intent/v2',
23
+ baseUrl: (0, discovery_validation_js_1.sameOriginUrl)(extension.base_url, 'extensions.run_intent.base_url', origin),
24
+ routes: Object.freeze({
25
+ submit: route('submit', ['org_id']),
26
+ status: route('status', ['org_id', 'intent_id']),
27
+ cancel: route('cancel', ['org_id', 'intent_id']),
28
+ }),
29
+ });
30
+ }
@@ -0,0 +1,27 @@
1
+ import { routeTemplate } from './route-template.mjs';
2
+ import { closedRecord, exact, sameOriginUrl } from './discovery-validation.mjs';
3
+ export function parseRunIntent(value, origin) {
4
+ if (value === undefined || value === null)
5
+ return null;
6
+ const extension = closedRecord(value, 'extensions.run_intent', [
7
+ 'kind',
8
+ 'base_url',
9
+ 'route_templates',
10
+ ]);
11
+ exact(extension.kind, 'zeroshot.run-intent/v2', 'extensions.run_intent.kind');
12
+ const routes = closedRecord(extension.route_templates, 'extensions.run_intent.route_templates', [
13
+ 'submit',
14
+ 'status',
15
+ 'cancel',
16
+ ]);
17
+ const route = (name, variables) => routeTemplate(routes[name], `extensions.run_intent.route_templates.${name}`, variables);
18
+ return Object.freeze({
19
+ kind: 'zeroshot.run-intent/v2',
20
+ baseUrl: sameOriginUrl(extension.base_url, 'extensions.run_intent.base_url', origin),
21
+ routes: Object.freeze({
22
+ submit: route('submit', ['org_id']),
23
+ status: route('status', ['org_id', 'intent_id']),
24
+ cancel: route('cancel', ['org_id', 'intent_id']),
25
+ }),
26
+ });
27
+ }
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@the-open-engine/zeroshot",
3
- "version": "6.32.0",
3
+ "version": "6.33.0",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "@the-open-engine/zeroshot",
9
- "version": "6.32.0",
9
+ "version": "6.33.0",
10
10
  "hasInstallScript": true,
11
11
  "license": "MIT",
12
12
  "dependencies": {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@the-open-engine/zeroshot",
3
- "version": "6.32.0",
3
+ "version": "6.33.0",
4
4
  "description": "Independent executor–verifier orchestration for software changes.",
5
5
  "main": "src/orchestrator.js",
6
6
  "bin": {
@@ -77,7 +77,6 @@
77
77
  "protocol:generate": "node scripts/generate-cluster-types.js",
78
78
  "protocol:check": "cargo run -p openengine-cluster-testkit --bin generate-cluster-protocol -- --check && node scripts/generate-cluster-types.js --check",
79
79
  "rust:distribution:check": "node scripts/rust-distribution.js check-repository",
80
- "hosted-oecp:image:check": "node scripts/hosted-oecp-image.js check",
81
80
  "rust:check": "cargo fmt --all -- --check && cargo clippy --workspace --all-targets -- -D warnings && cargo test --workspace",
82
81
  "deadcode": "ts-prune --skip node_modules",
83
82
  "deadcode:files": "unimported",
@@ -177,8 +176,8 @@
177
176
  "!scripts/hosted-oecp-ci-relevance.js",
178
177
  "!scripts/hosted-oecp-image.js",
179
178
  "!scripts/hosted-oecp-image-commands.js",
179
+ "!scripts/hosted-oecp-image-inspection.js",
180
180
  "!scripts/hosted-oecp-image-smoke.js",
181
- "!scripts/hosted-oecp-manifest.js",
182
181
  "!scripts/hosted-oecp-smoke-capability.js",
183
182
  "!scripts/hosted-oecp-smoke-client.js",
184
183
  "!scripts/hosted-oecp-smoke-codex.mjs",
@@ -24,6 +24,10 @@ const { findPlatformMismatchReason } = require('./validation-platform');
24
24
  const { calculateRateLimitDelay, isRateLimitError } = require('./rate-limit-backoff');
25
25
  const { updateAgentProviderSession } = require('./provider-session');
26
26
  const { rebuildProviderSessionAfterCommit } = require('./agent-task-executor');
27
+ const {
28
+ buildStructuredOutputClusterFailure,
29
+ isStructuredOutputInvalidError,
30
+ } = require('./structured-output-error');
27
31
  const {
28
32
  commitRecordedOwnership,
29
33
  markCleanupRequired,
@@ -735,6 +739,7 @@ async function handleFinalFailure(agent, triggeringMessage, error, maxRetries) {
735
739
  const failureAttempts = error?.terminationAttempts ?? maxRetries;
736
740
  const unsupportedCapability =
737
741
  error?.code === 'unsupported-capability' && error?.permanent === true;
742
+ const structuredOutputInvalid = isStructuredOutputInvalidError(error);
738
743
  console.error(`
739
744
  ${'='.repeat(80)}`);
740
745
  console.error(`🔴🔴🔴 MAX RETRIES EXHAUSTED - AGENT: ${agent.id} 🔴🔴🔴`);
@@ -810,6 +815,9 @@ ${'='.repeat(80)}`);
810
815
  });
811
816
  }
812
817
 
818
+ if (structuredOutputInvalid) {
819
+ agent._publish(buildStructuredOutputClusterFailure(agent, error));
820
+ }
813
821
  if (unsupportedCapability) {
814
822
  agent._publish({
815
823
  topic: 'CLUSTER_FAILED',
@@ -880,6 +888,7 @@ ${'='.repeat(80)}`);
880
888
  capability: error.capability,
881
889
  }
882
890
  : {}),
891
+ ...(structuredOutputInvalid ? { code: error.code, details: error.details ?? null } : {}),
883
892
  timestamp: Date.now(),
884
893
  };
885
894
 
@@ -910,6 +919,7 @@ ${'='.repeat(80)}`);
910
919
  capability: error.capability,
911
920
  }
912
921
  : {}),
922
+ ...(structuredOutputInvalid ? { code: error.code, details: error.details ?? null } : {}),
913
923
  hookFailureContext: error.message.includes('Hook uses result')
914
924
  ? {
915
925
  taskId: agent.currentTaskId || 'UNKNOWN',
@@ -936,7 +946,7 @@ ${'='.repeat(80)}`);
936
946
  orchestrator: agent.orchestrator,
937
947
  });
938
948
 
939
- if (!error?.terminationExhausted && !unsupportedCapability) {
949
+ if (!error?.terminationExhausted && !unsupportedCapability && !structuredOutputInvalid) {
940
950
  agent.state = 'idle';
941
951
  }
942
952
  }
@@ -52,6 +52,10 @@ const {
52
52
  validateCompletedResumeIdentity,
53
53
  } = require('./provider-session');
54
54
  const { extractClaudeVertexModelError } = require('./output-extraction');
55
+ const {
56
+ createStructuredOutputInvalidError,
57
+ isStructuredOutputInvalidError,
58
+ } = require('./structured-output-error');
55
59
  const TASK_TERMINAL_STATUSES = new Set(['completed', 'failed', 'killed', 'stale']);
56
60
  function runCommandWithTimeout(command, args, options = {}, callback = null) {
57
61
  const timeout = options.timeout ?? 30000;
@@ -1412,6 +1416,7 @@ async function evaluateStructuredSuccess({ agent, taskId, state, success, allowR
1412
1416
  return { success: true, error: null };
1413
1417
  } catch (error) {
1414
1418
  if (
1419
+ isStructuredOutputInvalidError(error) ||
1415
1420
  isNestedLifecycleError(error) ||
1416
1421
  error?.permanent === true ||
1417
1422
  error?.recoveryAbort === true
@@ -2858,7 +2863,10 @@ async function parseResultOutput(agent, output, { allowRecovery = true } = {}) {
2858
2863
  const schema = agent.config.jsonSchema;
2859
2864
  if (!schema) {
2860
2865
  if (parsed) return parsed;
2861
- throw new Error(`Agent ${agent.id} output missing required JSON block`);
2866
+ throw createStructuredOutputInvalidError(
2867
+ `Agent ${agent.id} output missing required JSON block`,
2868
+ 'missing_json'
2869
+ );
2862
2870
  }
2863
2871
 
2864
2872
  const { createStructuredOutputValidator, reformatOutput } = require('./output-reformatter');
@@ -2911,7 +2919,12 @@ async function parseResultOutput(agent, output, { allowRecovery = true } = {}) {
2911
2919
  throw new Error('Task execution failed - no output');
2912
2920
  }
2913
2921
  const recoveryDetail = recovery?.lastError ? ` Recovery exhausted: ${recovery.lastError}.` : '';
2914
- throw new Error(`Agent ${agent.id} output missing required JSON block.${recoveryDetail}`);
2922
+ throw createStructuredOutputInvalidError(
2923
+ `Agent ${agent.id} output missing required JSON block.${recoveryDetail}`,
2924
+ 'missing_json',
2925
+ directValidation,
2926
+ recovery
2927
+ );
2915
2928
  }
2916
2929
 
2917
2930
  if (!allowRecovery) {
@@ -2921,13 +2934,18 @@ async function parseResultOutput(agent, output, { allowRecovery = true } = {}) {
2921
2934
 
2922
2935
  const errorDetail = directValidation?.error || 'unknown schema error';
2923
2936
  const message = `Agent ${agent.id} output failed JSON schema validation: ${errorDetail}`;
2924
- if (agent.role === 'validator') {
2925
- throw new Error(
2926
- recovery?.status === 'exhausted'
2927
- ? `${message}. Recovery exhausted after ${recovery.attempts} attempts: ${recovery.lastError}`
2928
- : message
2937
+ const terminalStructuredRole = ['planner', 'conductor', 'validator'].includes(agent.role);
2938
+ if (recovery?.status === 'exhausted' && terminalStructuredRole) {
2939
+ throw createStructuredOutputInvalidError(
2940
+ `${message}. Recovery exhausted after ${recovery.attempts} attempts: ${recovery.lastError}`,
2941
+ 'schema_validation',
2942
+ directValidation,
2943
+ recovery
2929
2944
  );
2930
2945
  }
2946
+ if (agent.role === 'validator') {
2947
+ throw new Error(message);
2948
+ }
2931
2949
 
2932
2950
  console.warn(`⚠️ ${message}`);
2933
2951
  agent._publish({
@@ -0,0 +1,42 @@
1
+ const STRUCTURED_OUTPUT_INVALID_CODE = 'STRUCTURED_OUTPUT_INVALID';
2
+
3
+ function createStructuredOutputInvalidError(message, kind, validation = null, recovery = null) {
4
+ const error = new Error(message);
5
+ error.code = STRUCTURED_OUTPUT_INVALID_CODE;
6
+ error.details = {
7
+ kind,
8
+ validationError: validation?.error ?? null,
9
+ recoveryAttempts: recovery?.status === 'exhausted' ? recovery.attempts : 0,
10
+ recoveryError: recovery?.status === 'exhausted' ? recovery.lastError : null,
11
+ };
12
+ return error;
13
+ }
14
+
15
+ function isStructuredOutputInvalidError(error) {
16
+ return error?.code === STRUCTURED_OUTPUT_INVALID_CODE;
17
+ }
18
+
19
+ function buildStructuredOutputClusterFailure(agent, error) {
20
+ return {
21
+ topic: 'CLUSTER_FAILED',
22
+ receiver: 'broadcast',
23
+ content: {
24
+ text: `Cluster failed: structured output is invalid for ${agent.id} - ${error.message}`,
25
+ data: {
26
+ reason: 'structured_output_invalid',
27
+ agentId: agent.id,
28
+ role: agent.role,
29
+ code: error.code,
30
+ details: error.details ?? null,
31
+ error: error.message,
32
+ },
33
+ },
34
+ };
35
+ }
36
+
37
+ module.exports = {
38
+ STRUCTURED_OUTPUT_INVALID_CODE,
39
+ createStructuredOutputInvalidError,
40
+ isStructuredOutputInvalidError,
41
+ buildStructuredOutputClusterFailure,
42
+ };
@@ -55,11 +55,12 @@ function detectCliFeatures(
55
55
  const help = helpText ?? '';
56
56
  const unknown = !help;
57
57
  const supportsConfigOverride = supports(help, /--config\b/);
58
+ const supportsSandbox = !unknown && /--sandbox\b/.test(help);
58
59
  return {
59
60
  provider: 'codex',
60
61
  supportsJson: supports(help, /--json\b/),
61
62
  supportsOutputSchema: supports(help, /--output-schema\b/),
62
- supportsAutoApprove: supports(help, /--dangerously-bypass-approvals-and-sandbox\b/),
63
+ supportsAutoApprove: !unknown && supportsConfigOverride && supportsSandbox,
63
64
  supportsCwd: supports(help, /\s-C\b/) || supports(help, /--cwd\b/),
64
65
  supportsConfigOverride,
65
66
  supportsModel: supports(help, /\s-m\b/) || supports(help, /--model\b/),
@@ -67,7 +68,7 @@ function detectCliFeatures(
67
68
  supportsResume: supports(help, /\bresume\b/),
68
69
  supportsWebSearch:
69
70
  !unknown && supportsConfigOverride && isCliVersionAtLeast(versionText, '0.146.0'),
70
- supportsSandbox: !unknown && /--sandbox\b/.test(help),
71
+ supportsSandbox,
71
72
  supportsEphemeral: !unknown && /--ephemeral\b/.test(help),
72
73
  supportsIgnoreUserConfig: !unknown && /--ignore-user-config\b/.test(help),
73
74
  supportsIgnoreRules: !unknown && /--ignore-rules\b/.test(help),
@@ -113,7 +114,9 @@ function addCwdArgs(args: string[], options: BuildProviderCommandOptions): void
113
114
  function addAutoApproveArgs(args: string[], options: BuildProviderCommandOptions): void {
114
115
  const features = optionFeatures(options);
115
116
  if (options.autoApprove && features.supportsAutoApprove) {
116
- args.push('--dangerously-bypass-approvals-and-sandbox');
117
+ const sandboxMode =
118
+ options.executionContext === 'docker' ? 'danger-full-access' : 'workspace-write';
119
+ args.push('--sandbox', sandboxMode, '--config', 'approval_policy="never"');
117
120
  }
118
121
  }
119
122
 
@@ -166,7 +169,7 @@ function collectWarnings(options: BuildProviderCommandOptions): WarningMetadata[
166
169
  warning(
167
170
  'codex',
168
171
  'codex-auto-approve',
169
- 'Codex CLI does not support auto-approve; continuing without bypass flag.'
172
+ 'Codex CLI does not support unattended workspace-write mode; continuing without it.'
170
173
  )
171
174
  );
172
175
  }
@@ -255,7 +258,9 @@ function assertStructuredOutputRecoveryFeatures(options: BuildProviderCommandOpt
255
258
  const missing = [
256
259
  ['--sandbox', features.supportsSandbox],
257
260
  ['--ephemeral', features.supportsEphemeral],
258
- ['--ignore-user-config', features.supportsIgnoreUserConfig],
261
+ ...(options.trustIsolatedCodexProfile === true
262
+ ? []
263
+ : [['--ignore-user-config', features.supportsIgnoreUserConfig] as const]),
259
264
  ['--ignore-rules', features.supportsIgnoreRules],
260
265
  ['--strict-config', features.supportsStrictConfig],
261
266
  ['--config', features.supportsConfigOverride],
@@ -284,17 +289,11 @@ function buildStructuredOutputRecoveryCommand(
284
289
  const args = [...spec.args];
285
290
  const prompt = args.pop();
286
291
  if (prompt === undefined) throw new Error('Codex recovery command is missing its prompt');
287
- args.push(
288
- '--sandbox',
289
- 'read-only',
290
- '--ephemeral',
291
- '--ignore-user-config',
292
- '--ignore-rules',
293
- '--strict-config',
294
- '--config',
295
- 'web_search="disabled"',
296
- prompt
297
- );
292
+ args.push('--sandbox', 'read-only', '--ephemeral');
293
+ if (options.trustIsolatedCodexProfile !== true) {
294
+ args.push('--ignore-user-config');
295
+ }
296
+ args.push('--ignore-rules', '--strict-config', '--config', 'web_search="disabled"', prompt);
298
297
  return { ...spec, args };
299
298
  }
300
299