agent-embassy 1.6.1 → 1.7.1
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/CHANGELOG.md +33 -0
- package/README.md +9 -9
- package/README.zh-CN.md +9 -9
- package/SECURITY.md +34 -77
- package/dist/src/gateway/acp-client.d.ts +117 -0
- package/dist/src/gateway/acp-client.js +382 -0
- package/dist/src/gateway/acp-client.js.map +1 -0
- package/dist/src/gateway/acp-provider.d.ts +65 -0
- package/dist/src/gateway/acp-provider.js +163 -0
- package/dist/src/gateway/acp-provider.js.map +1 -0
- package/dist/src/gateway/claude-helper-protocol.d.ts +3 -1
- package/dist/src/gateway/claude-helper-protocol.js +15 -5
- package/dist/src/gateway/claude-helper-protocol.js.map +1 -1
- package/dist/src/gateway/claude-helper-supervisor.d.ts +3 -1
- package/dist/src/gateway/claude-helper-supervisor.js +16 -2
- package/dist/src/gateway/claude-helper-supervisor.js.map +1 -1
- package/dist/src/gateway/claude-helper.js +4 -3
- package/dist/src/gateway/claude-helper.js.map +1 -1
- package/dist/src/gateway/claude-peer.d.ts +1 -1
- package/dist/src/gateway/claude-peer.js +11 -13
- package/dist/src/gateway/claude-peer.js.map +1 -1
- package/dist/src/gateway/claude-runtime.d.ts +0 -20
- package/dist/src/gateway/claude-runtime.js +6 -127
- package/dist/src/gateway/claude-runtime.js.map +1 -1
- package/dist/src/gateway/cli-copy.d.ts +1 -1
- package/dist/src/gateway/cli-copy.en.d.ts +3 -1
- package/dist/src/gateway/cli-copy.en.js +5 -2
- package/dist/src/gateway/cli-copy.en.js.map +1 -1
- package/dist/src/gateway/cli-copy.js +2 -0
- package/dist/src/gateway/cli-copy.js.map +1 -1
- package/dist/src/gateway/cli-copy.zh-CN.d.ts +3 -1
- package/dist/src/gateway/cli-copy.zh-CN.js +5 -2
- package/dist/src/gateway/cli-copy.zh-CN.js.map +1 -1
- package/dist/src/gateway/cli.d.ts +2 -2
- package/dist/src/gateway/cli.js +73 -6
- package/dist/src/gateway/cli.js.map +1 -1
- package/dist/src/gateway/codex-app-server.d.ts +5 -74
- package/dist/src/gateway/codex-app-server.js +9 -599
- package/dist/src/gateway/codex-app-server.js.map +1 -1
- package/dist/src/gateway/codex-doctor.d.ts +33 -0
- package/dist/src/gateway/codex-doctor.js +148 -0
- package/dist/src/gateway/codex-doctor.js.map +1 -0
- package/dist/src/gateway/codex-local-transport.d.ts +13 -26
- package/dist/src/gateway/codex-local-transport.js +31 -76
- package/dist/src/gateway/codex-local-transport.js.map +1 -1
- package/dist/src/gateway/compatibility.d.ts +6 -66
- package/dist/src/gateway/compatibility.js +14 -296
- package/dist/src/gateway/compatibility.js.map +1 -1
- package/dist/src/gateway/config.d.ts +9 -0
- package/dist/src/gateway/config.js +6 -2
- package/dist/src/gateway/config.js.map +1 -1
- package/dist/src/gateway/control.d.ts +16 -1
- package/dist/src/gateway/control.js +128 -59
- package/dist/src/gateway/control.js.map +1 -1
- package/dist/src/gateway/dashboard-copy.d.ts +1 -1
- package/dist/src/gateway/dashboard-copy.en.d.ts +19 -44
- package/dist/src/gateway/dashboard-copy.en.js +42 -67
- package/dist/src/gateway/dashboard-copy.en.js.map +1 -1
- package/dist/src/gateway/dashboard-copy.js +19 -44
- package/dist/src/gateway/dashboard-copy.js.map +1 -1
- package/dist/src/gateway/dashboard-copy.zh-CN.d.ts +19 -44
- package/dist/src/gateway/dashboard-copy.zh-CN.js +42 -67
- package/dist/src/gateway/dashboard-copy.zh-CN.js.map +1 -1
- package/dist/src/gateway/dashboard-model.d.ts +41 -76
- package/dist/src/gateway/dashboard-model.js +167 -257
- package/dist/src/gateway/dashboard-model.js.map +1 -1
- package/dist/src/gateway/dashboard.d.ts +0 -3
- package/dist/src/gateway/dashboard.js +77 -76
- package/dist/src/gateway/dashboard.js.map +1 -1
- package/dist/src/gateway/deepseek-detect.d.ts +7 -6
- package/dist/src/gateway/deepseek-detect.js +31 -115
- package/dist/src/gateway/deepseek-detect.js.map +1 -1
- package/dist/src/gateway/live-dashboard-app/app.js +175 -621
- package/dist/src/gateway/live-dashboard-command.js +2 -4
- package/dist/src/gateway/live-dashboard-command.js.map +1 -1
- package/dist/src/gateway/live-dashboard-http.d.ts +1 -2
- package/dist/src/gateway/live-dashboard-http.js +14 -15
- package/dist/src/gateway/live-dashboard-http.js.map +1 -1
- package/dist/src/gateway/progress-watch-machine.d.ts +1 -4
- package/dist/src/gateway/progress-watch-machine.js.map +1 -1
- package/dist/src/gateway/provenance-envelope.d.ts +3 -2
- package/dist/src/gateway/provenance-envelope.js +44 -11
- package/dist/src/gateway/provenance-envelope.js.map +1 -1
- package/dist/src/gateway/providers.d.ts +19 -85
- package/dist/src/gateway/providers.js +132 -835
- package/dist/src/gateway/providers.js.map +1 -1
- package/dist/src/gateway/server.d.ts +8 -9
- package/dist/src/gateway/server.js +63 -99
- package/dist/src/gateway/server.js.map +1 -1
- package/dist/src/gateway/service.d.ts +48 -51
- package/dist/src/gateway/service.js +748 -540
- package/dist/src/gateway/service.js.map +1 -1
- package/dist/src/gateway/store.d.ts +20 -29
- package/dist/src/gateway/store.js +306 -838
- package/dist/src/gateway/store.js.map +1 -1
- package/dist/src/gateway/types.d.ts +63 -54
- package/dist/src/gateway/types.js +51 -91
- package/dist/src/gateway/types.js.map +1 -1
- package/docs/CONFIGURATION.md +11 -10
- package/docs/CONFIGURATION.zh-CN.md +10 -9
- package/docs/DASHBOARD.md +4 -38
- package/docs/DASHBOARD.zh-CN.md +4 -2
- package/docs/GATEWAY-ARCHITECTURE.md +61 -119
- package/package.json +1 -1
- package/skills/embassy-peer/SKILL.md +6 -6
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
import path from "node:path";
|
|
2
|
-
import { chmod, lstat, mkdir, mkdtemp, readdir, realpath, rmdir, } from "node:fs/promises";
|
|
3
2
|
import { BridgeError } from "../errors.js";
|
|
4
3
|
import { CLAUDE_PEER_COMPATIBILITY, ClaudePeerAdapter, } from "./claude-peer.js";
|
|
5
|
-
import {
|
|
6
|
-
import { CODEX_APP_SERVER_WRITABLE_VERSIONS, CodexAppServerConnector, CodexConnectorError, codexWriteProbeFailure, } from "./codex-app-server.js";
|
|
4
|
+
import { CodexAppServerConnector, CodexConnectorError, } from "./codex-app-server.js";
|
|
7
5
|
import { LocalCodexTransportError } from "./codex-local-transport.js";
|
|
8
6
|
import { ClaudeNativeHelperSupervisor, } from "./claude-helper-supervisor.js";
|
|
9
7
|
import { isDashboardLocale } from "./locale.js";
|
|
10
8
|
import { composeProvenanceEnvelope } from "./provenance-envelope.js";
|
|
9
|
+
import { gatewayRegistrationIngressPrefixes } from "./types.js";
|
|
11
10
|
const LOCAL_HOST = "this-mac";
|
|
12
11
|
const STABLE_CLAUDE_ENDPOINT_GENERATION = "claude_local_endpoint";
|
|
13
12
|
const NATIVE_CLAUDE_NAME = /^[a-z][a-z0-9_-]{0,31}$/;
|
|
@@ -23,14 +22,8 @@ const DEFAULT_CODEX_CLEANUP_TIMEOUT_MS = 5_000;
|
|
|
23
22
|
const DEFAULT_CODEX_CLEANUP_POLL_MS = 25;
|
|
24
23
|
const DEFAULT_CODEX_RECOVERY_INITIAL_MS = 250;
|
|
25
24
|
const DEFAULT_CODEX_RECOVERY_MAX_MS = 5_000;
|
|
25
|
+
const DEFAULT_CODEX_OBSERVATION_POLL_MS = 15_000;
|
|
26
26
|
const MAX_CODEX_ENDPOINT_REFRESH_CANDIDATES = 3;
|
|
27
|
-
const COMPATIBILITY_PROBE_THREAD_ID = "00000000-0000-7000-8000-000000000000";
|
|
28
|
-
const CODEX_WRITE_PROBE_DIRECTORY_PREFIX = ".codex-write-probe-";
|
|
29
|
-
const CODEX_WRITE_PROBE_ROOT = "write-probes";
|
|
30
|
-
const MAX_CODEX_WRITE_PROBE_ATTEMPTS = 16;
|
|
31
|
-
const CODEX_WRITE_PROBE_TRANSIENT_ERRNOS = new Set([
|
|
32
|
-
"EAGAIN", "EIO", "EMFILE", "ENFILE",
|
|
33
|
-
]);
|
|
34
27
|
const CLAUDE_CLEAN_PREWRITE_RETRY_CODES = new Set([
|
|
35
28
|
"CLAUDE_PEER_TARGET_UNKNOWN",
|
|
36
29
|
"CLAUDE_PEER_TARGET_STALE",
|
|
@@ -86,105 +79,6 @@ function claudeCleanPrewriteResult(error) {
|
|
|
86
79
|
: "CLAUDE_DISPATCH_PREWRITE_FAILED",
|
|
87
80
|
};
|
|
88
81
|
}
|
|
89
|
-
function passedProbe(name) {
|
|
90
|
-
return { name, outcome: "pass" };
|
|
91
|
-
}
|
|
92
|
-
function failedProbe(name, safeErrorCode) {
|
|
93
|
-
return { name, outcome: "fail", safeErrorCode };
|
|
94
|
-
}
|
|
95
|
-
function systemErrorCode(error) {
|
|
96
|
-
return typeof error === "object" &&
|
|
97
|
-
error !== null &&
|
|
98
|
-
"code" in error &&
|
|
99
|
-
typeof error.code === "string"
|
|
100
|
-
? error.code
|
|
101
|
-
: undefined;
|
|
102
|
-
}
|
|
103
|
-
function transientCodexWriteProbeError(error) {
|
|
104
|
-
return CODEX_WRITE_PROBE_TRANSIENT_ERRNOS.has(systemErrorCode(error) ?? "");
|
|
105
|
-
}
|
|
106
|
-
function safeCodexWriteProbeFilesystemFailure(error) {
|
|
107
|
-
return (transientCodexWriteProbeError(error) ||
|
|
108
|
-
systemErrorCode(error) === "ENOTEMPTY" ||
|
|
109
|
-
(error instanceof BridgeError &&
|
|
110
|
-
error.code === "CODEX_WRITE_PROBE_THREAD_SETUP_FAILED"));
|
|
111
|
-
}
|
|
112
|
-
async function attestCodexWriteProbeDirectory(directory) {
|
|
113
|
-
try {
|
|
114
|
-
const resolved = path.resolve(directory);
|
|
115
|
-
const [metadata, canonical, entries] = await Promise.all([
|
|
116
|
-
lstat(resolved, { bigint: true }),
|
|
117
|
-
realpath(resolved),
|
|
118
|
-
readdir(resolved),
|
|
119
|
-
]);
|
|
120
|
-
const uid = process.getuid?.();
|
|
121
|
-
if (uid === undefined ||
|
|
122
|
-
!metadata.isDirectory() ||
|
|
123
|
-
canonical !== resolved ||
|
|
124
|
-
metadata.uid !== BigInt(uid) ||
|
|
125
|
-
(metadata.mode & 511n) !== 448n) {
|
|
126
|
-
throw new BridgeError("CODEX_FACTORY_ATTESTATION_INVALID", "The disposable Codex probe directory crossed its ownership, mode, or canonical-path boundary.");
|
|
127
|
-
}
|
|
128
|
-
if (entries.length !== 0) {
|
|
129
|
-
throw new BridgeError("CODEX_WRITE_PROBE_THREAD_SETUP_FAILED", "The disposable Codex probe directory was not empty.");
|
|
130
|
-
}
|
|
131
|
-
return {
|
|
132
|
-
dev: metadata.dev,
|
|
133
|
-
ino: metadata.ino,
|
|
134
|
-
mode: metadata.mode & 511n,
|
|
135
|
-
mtimeNs: metadata.mtimeNs,
|
|
136
|
-
uid: metadata.uid,
|
|
137
|
-
};
|
|
138
|
-
}
|
|
139
|
-
catch (error) {
|
|
140
|
-
if (error instanceof BridgeError)
|
|
141
|
-
throw error;
|
|
142
|
-
if (transientCodexWriteProbeError(error)) {
|
|
143
|
-
throw new BridgeError("CODEX_WRITE_PROBE_THREAD_SETUP_FAILED", "A transient filesystem error prevented disposable probe attestation.");
|
|
144
|
-
}
|
|
145
|
-
throw new BridgeError("CODEX_FACTORY_ATTESTATION_INVALID", "The disposable Codex probe directory could not be attested safely.");
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
async function attestCodexWriteProbeRoot(root) {
|
|
149
|
-
try {
|
|
150
|
-
const resolved = path.resolve(root);
|
|
151
|
-
const [metadata, canonical] = await Promise.all([
|
|
152
|
-
lstat(resolved, { bigint: true }),
|
|
153
|
-
realpath(resolved),
|
|
154
|
-
]);
|
|
155
|
-
const uid = process.getuid?.();
|
|
156
|
-
if (uid === undefined ||
|
|
157
|
-
!metadata.isDirectory() ||
|
|
158
|
-
canonical !== resolved ||
|
|
159
|
-
metadata.uid !== BigInt(uid) ||
|
|
160
|
-
(metadata.mode & 511n) !== 448n) {
|
|
161
|
-
throw new BridgeError("CODEX_FACTORY_ATTESTATION_INVALID", "A controller-owned Codex probe root changed outside its exact owned-directory boundary.");
|
|
162
|
-
}
|
|
163
|
-
return {
|
|
164
|
-
dev: metadata.dev,
|
|
165
|
-
ino: metadata.ino,
|
|
166
|
-
mode: metadata.mode & 511n,
|
|
167
|
-
uid: metadata.uid,
|
|
168
|
-
};
|
|
169
|
-
}
|
|
170
|
-
catch (error) {
|
|
171
|
-
if (error instanceof BridgeError)
|
|
172
|
-
throw error;
|
|
173
|
-
if (transientCodexWriteProbeError(error)) {
|
|
174
|
-
return null;
|
|
175
|
-
}
|
|
176
|
-
throw new BridgeError("CODEX_FACTORY_ATTESTATION_INVALID", "A controller-owned Codex probe root could not be attested.");
|
|
177
|
-
}
|
|
178
|
-
}
|
|
179
|
-
function sameCodexWriteProbeDirectory(before, after) {
|
|
180
|
-
return sameCodexWriteProbeRoot(before, after) && before.mtimeNs === after.mtimeNs;
|
|
181
|
-
}
|
|
182
|
-
function sameCodexWriteProbeRoot(before, after) {
|
|
183
|
-
return (before.dev === after.dev &&
|
|
184
|
-
before.ino === after.ino &&
|
|
185
|
-
before.mode === after.mode &&
|
|
186
|
-
before.uid === after.uid);
|
|
187
|
-
}
|
|
188
82
|
function exactLocalHost(hostId) {
|
|
189
83
|
if ((hostId ?? LOCAL_HOST) !== LOCAL_HOST) {
|
|
190
84
|
throw new BridgeError("GATEWAY_REMOTE_PROVIDER_DISABLED", "This provider is restricted to the exact local host boundary.");
|
|
@@ -226,18 +120,6 @@ function invokeCallback(operation) {
|
|
|
226
120
|
}
|
|
227
121
|
}
|
|
228
122
|
function validateAttestedClaudeRuntime(runtime) {
|
|
229
|
-
if (runtime.versionEvidenceFailure !== undefined) {
|
|
230
|
-
throw new BridgeError(runtime.versionEvidenceFailure, "The local Claude provider requires bounded, internally consistent runtime version evidence.");
|
|
231
|
-
}
|
|
232
|
-
const majorEvidence = runtime.claudeCodeVersion === UNKNOWN_COMPATIBILITY_VERSION
|
|
233
|
-
? runtime.launcherVersionEvidence
|
|
234
|
-
: runtime.claudeCodeVersion;
|
|
235
|
-
if (majorEvidence === undefined ||
|
|
236
|
-
!sharesCompatibilityMajor(majorEvidence, CLAUDE_PEER_COMPATIBILITY.claudeCodeVersion)) {
|
|
237
|
-
throw new BridgeError(majorEvidence === undefined
|
|
238
|
-
? "CLAUDE_VERSION_UNPARSEABLE"
|
|
239
|
-
: "CLAUDE_PEER_VERSION_UNSUPPORTED", "The local Claude provider requires a positively observed supported runtime major.");
|
|
240
|
-
}
|
|
241
123
|
validateClaudeRuntimePathEvidence(runtime);
|
|
242
124
|
}
|
|
243
125
|
function validateClaudeRuntimePathEvidence(runtime) {
|
|
@@ -286,135 +168,17 @@ function isClaudeRegistryAvailabilityError(error) {
|
|
|
286
168
|
error.code === "EPERM" ||
|
|
287
169
|
error.code === "UNSAFE_PEER_DIRECTORY");
|
|
288
170
|
}
|
|
289
|
-
/**
|
|
290
|
-
* A bounded observation-only surface for an installed provider generation
|
|
291
|
-
* whose protocol cannot be used safely. It deliberately performs no provider
|
|
292
|
-
* discovery, listener construction, route selection, or message write.
|
|
293
|
-
*/
|
|
294
|
-
export class IncompatibleGatewayProvider {
|
|
295
|
-
identity;
|
|
296
|
-
protocol = "not-attested";
|
|
297
|
-
protocolVersion = "unknown";
|
|
298
|
-
version;
|
|
299
|
-
probes;
|
|
300
|
-
safeErrorCode;
|
|
301
|
-
initialized = false;
|
|
302
|
-
closed = false;
|
|
303
|
-
constructor(options) {
|
|
304
|
-
const attestation = evaluateCompatibilityAttestation({
|
|
305
|
-
surface: options.identity.provider,
|
|
306
|
-
version: options.version,
|
|
307
|
-
checkedAt: "2000-01-01T00:00:00.000Z",
|
|
308
|
-
certifiedVersions: certifiedCompatibilityVersions[options.identity.provider],
|
|
309
|
-
probes: options.probes,
|
|
310
|
-
});
|
|
311
|
-
if (attestation.tier !== "incompatible" ||
|
|
312
|
-
attestation.safeErrorCode !== options.safeErrorCode) {
|
|
313
|
-
throw new BridgeError("INCOMPATIBLE_PROVIDER_EVIDENCE_INVALID", "A disabled provider requires exact bounded incompatibility evidence.");
|
|
314
|
-
}
|
|
315
|
-
exactLocalHost(options.identity.hostId);
|
|
316
|
-
this.identity = Object.freeze({ ...options.identity });
|
|
317
|
-
this.version = options.version;
|
|
318
|
-
this.probes = Object.freeze(options.probes.map((probe) => Object.freeze({ ...probe })));
|
|
319
|
-
this.safeErrorCode = options.safeErrorCode;
|
|
320
|
-
}
|
|
321
|
-
compatibilitySurface() {
|
|
322
|
-
return { surface: this.identity.provider, version: this.version };
|
|
323
|
-
}
|
|
324
|
-
async runCompatibilityProbes() {
|
|
325
|
-
return this.probes;
|
|
326
|
-
}
|
|
327
|
-
acceptCompatibilityAttestation(attestation) {
|
|
328
|
-
if (!isCompatibilityAttestation(attestation) ||
|
|
329
|
-
attestation.surface !== this.identity.provider ||
|
|
330
|
-
attestation.version !== this.version ||
|
|
331
|
-
attestation.tier !== "incompatible" ||
|
|
332
|
-
attestation.safeErrorCode !== this.safeErrorCode ||
|
|
333
|
-
attestation.probes.length !== this.probes.length ||
|
|
334
|
-
attestation.probes.some((probe, index) => probe.name !== this.probes[index]?.name ||
|
|
335
|
-
probe.outcome !== this.probes[index]?.outcome ||
|
|
336
|
-
probe.safeErrorCode !== this.probes[index]?.safeErrorCode)) {
|
|
337
|
-
throw new BridgeError("INCOMPATIBLE_PROVIDER_ATTESTATION_MISMATCH", "The compatibility attestation does not match this disabled provider surface.");
|
|
338
|
-
}
|
|
339
|
-
}
|
|
340
|
-
async initialize(_callbacks) {
|
|
341
|
-
if (this.closed || this.initialized) {
|
|
342
|
-
throw new BridgeError("INCOMPATIBLE_PROVIDER_UNAVAILABLE", "The disabled provider surface cannot be initialized again.");
|
|
343
|
-
}
|
|
344
|
-
this.initialized = true;
|
|
345
|
-
return {
|
|
346
|
-
health: "degraded",
|
|
347
|
-
compatibility: "incompatible",
|
|
348
|
-
safeErrorCode: this.safeErrorCode,
|
|
349
|
-
};
|
|
350
|
-
}
|
|
351
|
-
async discoverClaudePeers() {
|
|
352
|
-
throw this.unavailable();
|
|
353
|
-
}
|
|
354
|
-
async selectRoute(_input) {
|
|
355
|
-
throw this.unavailable();
|
|
356
|
-
}
|
|
357
|
-
async assertWorkspaceDisjoint(_routeHandle, _stateRoot) {
|
|
358
|
-
throw this.unavailable();
|
|
359
|
-
}
|
|
360
|
-
async resolveReplyAddress(_address) {
|
|
361
|
-
throw this.unavailable();
|
|
362
|
-
}
|
|
363
|
-
async advertiseNativeCodexPeer(_input) {
|
|
364
|
-
throw this.unavailable();
|
|
365
|
-
}
|
|
366
|
-
async dispatch(_input) {
|
|
367
|
-
throw this.unavailable();
|
|
368
|
-
}
|
|
369
|
-
async releaseRoute(_routeHandle) { }
|
|
370
|
-
async close() {
|
|
371
|
-
this.closed = true;
|
|
372
|
-
}
|
|
373
|
-
unavailable() {
|
|
374
|
-
return new BridgeError(this.safeErrorCode, "This provider surface is observation-only because its compatibility boundary did not pass.");
|
|
375
|
-
}
|
|
376
|
-
}
|
|
377
|
-
export function createIncompatibleClaudeGatewayProvider(options) {
|
|
378
|
-
validateClaudeRuntimePathEvidence(options.runtime);
|
|
379
|
-
const [launcher, version, ...unobserved] = compatibilityProbeNames.claude;
|
|
380
|
-
return new IncompatibleGatewayProvider({
|
|
381
|
-
identity: {
|
|
382
|
-
provider: "claude",
|
|
383
|
-
hostId: LOCAL_HOST,
|
|
384
|
-
endpointGeneration: STABLE_CLAUDE_ENDPOINT_GENERATION,
|
|
385
|
-
},
|
|
386
|
-
version: options.version,
|
|
387
|
-
probes: [
|
|
388
|
-
passedProbe(launcher),
|
|
389
|
-
failedProbe(version, options.safeErrorCode),
|
|
390
|
-
...unobserved.map((name) => failedProbe(name, options.safeErrorCode)),
|
|
391
|
-
],
|
|
392
|
-
safeErrorCode: options.safeErrorCode,
|
|
393
|
-
});
|
|
394
|
-
}
|
|
395
|
-
export function createIncompatibleCodexGatewayProvider(options) {
|
|
396
|
-
const [installation, ...unobserved] = compatibilityProbeNames.codex;
|
|
397
|
-
return new IncompatibleGatewayProvider({
|
|
398
|
-
identity: options.identity,
|
|
399
|
-
version: options.version,
|
|
400
|
-
probes: [
|
|
401
|
-
passedProbe(installation),
|
|
402
|
-
...unobserved.map((name) => failedProbe(name, options.safeErrorCode)),
|
|
403
|
-
],
|
|
404
|
-
safeErrorCode: options.safeErrorCode,
|
|
405
|
-
});
|
|
406
|
-
}
|
|
407
171
|
export class LocalClaudeGatewayProvider {
|
|
408
172
|
identity;
|
|
409
173
|
protocol = "claude-peer";
|
|
410
174
|
protocolVersion = `${CLAUDE_PEER_COMPATIBILITY.peerProtocol}`;
|
|
411
175
|
peer;
|
|
412
|
-
runtimeVersion;
|
|
413
176
|
maxPending;
|
|
414
177
|
discoveryPollMs;
|
|
415
178
|
now;
|
|
416
179
|
selectedStateRoots = new Map();
|
|
417
180
|
nativeHelpers;
|
|
181
|
+
nativeHelperSourceProvider;
|
|
418
182
|
discovered = new Map();
|
|
419
183
|
selected = new Map();
|
|
420
184
|
/** Exact live same-UID sessions observed through native inbound frames. */
|
|
@@ -448,25 +212,12 @@ export class LocalClaudeGatewayProvider {
|
|
|
448
212
|
retiredNativeCodexListener;
|
|
449
213
|
/** Fences discovery callbacks and monitor restarts across listener rotation. */
|
|
450
214
|
nativeCodexSuccessionFreeze;
|
|
451
|
-
/**
|
|
452
|
-
|
|
453
|
-
compatibilityFailureCode;
|
|
215
|
+
/** External registry trust/availability failures degrade Claude only. */
|
|
216
|
+
unavailableCode;
|
|
454
217
|
initialized = false;
|
|
455
218
|
closed = false;
|
|
456
219
|
constructor(options) {
|
|
457
220
|
validateAttestedClaudeRuntime(options.runtime);
|
|
458
|
-
const compatibilityVersion = options.compatibilityVersion ??
|
|
459
|
-
(options.runtime.claudeCodeVersion === UNKNOWN_COMPATIBILITY_VERSION
|
|
460
|
-
? options.runtime.launcherVersionEvidence
|
|
461
|
-
: options.runtime.claudeCodeVersion);
|
|
462
|
-
if (compatibilityVersion === undefined ||
|
|
463
|
-
!isCompatibilityVersion(compatibilityVersion) ||
|
|
464
|
-
(compatibilityVersion !== options.runtime.claudeCodeVersion &&
|
|
465
|
-
!(options.runtime.claudeCodeVersion ===
|
|
466
|
-
UNKNOWN_COMPATIBILITY_VERSION &&
|
|
467
|
-
options.runtime.launcherVersionEvidence === compatibilityVersion))) {
|
|
468
|
-
throw new BridgeError("CLAUDE_RUNTIME_ATTESTATION_INVALID", "Claude compatibility classification requires exact bounded launcher evidence.");
|
|
469
|
-
}
|
|
470
221
|
if (options.locale !== undefined && !isDashboardLocale(options.locale)) {
|
|
471
222
|
throw new BridgeError("DASHBOARD_LOCALE_UNSUPPORTED", "The Claude provider notice locale is unsupported.");
|
|
472
223
|
}
|
|
@@ -476,15 +227,12 @@ export class LocalClaudeGatewayProvider {
|
|
|
476
227
|
hostId,
|
|
477
228
|
endpointGeneration: STABLE_CLAUDE_ENDPOINT_GENERATION,
|
|
478
229
|
};
|
|
479
|
-
this.runtimeVersion = compatibilityVersion;
|
|
480
230
|
this.maxPending = positiveBounded(options.maxPendingMessages, MAX_CLAUDE_PENDING, 4_096);
|
|
481
231
|
this.discoveryPollMs = positiveBounded(options.discoveryPollMs, DEFAULT_CLAUDE_DISCOVERY_POLL_MS, 30_000);
|
|
482
232
|
this.now = options.now ?? Date.now;
|
|
483
233
|
const protocolRuntime = {
|
|
484
234
|
claudeExecutable: options.runtime.claudeExecutable,
|
|
485
|
-
claudeCodeVersion: options.runtime.claudeCodeVersion
|
|
486
|
-
? options.runtime.launcherVersionEvidence
|
|
487
|
-
: options.runtime.claudeCodeVersion,
|
|
235
|
+
claudeCodeVersion: options.runtime.claudeCodeVersion,
|
|
488
236
|
sessionsDir: options.runtime.sessionsDir,
|
|
489
237
|
socketDir: options.runtime.socketDir,
|
|
490
238
|
};
|
|
@@ -506,60 +254,19 @@ export class LocalClaudeGatewayProvider {
|
|
|
506
254
|
deliveryNotices: options.deliveryNotices ?? "merged",
|
|
507
255
|
maxPendingMessages: this.maxPending,
|
|
508
256
|
maxHelpers: positiveBounded(options.nativeHelpers.maxHelpers, 256, 128),
|
|
509
|
-
callbacks: () => this.
|
|
257
|
+
callbacks: () => this.callbacks,
|
|
510
258
|
...(options.nativeHelpers.factory === undefined
|
|
511
259
|
? {}
|
|
512
260
|
: { factory: options.nativeHelpers.factory }),
|
|
513
261
|
});
|
|
514
|
-
|
|
515
|
-
compatibilitySurface() {
|
|
516
|
-
return { surface: "claude", version: this.runtimeVersion };
|
|
517
|
-
}
|
|
518
|
-
async runCompatibilityProbes() {
|
|
519
|
-
const [launcher, version, registry, socket, protocol] = compatibilityProbeNames.claude;
|
|
520
|
-
try {
|
|
521
|
-
const discovery = await this.peer.discover();
|
|
522
|
-
this.registryObservation = claudeRegistryObservation(discovery);
|
|
523
|
-
return [
|
|
524
|
-
passedProbe(launcher),
|
|
525
|
-
passedProbe(version),
|
|
526
|
-
passedProbe(registry),
|
|
527
|
-
passedProbe(socket),
|
|
528
|
-
passedProbe(protocol),
|
|
529
|
-
];
|
|
530
|
-
}
|
|
531
|
-
catch (error) {
|
|
532
|
-
if (!isClaudeRegistryAvailabilityError(error))
|
|
533
|
-
throw error;
|
|
534
|
-
this.registryObservation = unavailableClaudeRegistryObservation();
|
|
535
|
-
// A missing or unreadable registry is provider-local incompatibility:
|
|
536
|
-
// keep the broker and the other surface alive, but fence this provider
|
|
537
|
-
// before listener construction can turn the same missing path into a
|
|
538
|
-
// broker-wide startup failure.
|
|
539
|
-
return [
|
|
540
|
-
passedProbe(launcher),
|
|
541
|
-
passedProbe(version),
|
|
542
|
-
failedProbe(registry, "CLAUDE_REGISTRY_UNAVAILABLE"),
|
|
543
|
-
failedProbe(socket, "CLAUDE_REGISTRY_UNAVAILABLE"),
|
|
544
|
-
passedProbe(protocol),
|
|
545
|
-
];
|
|
546
|
-
}
|
|
262
|
+
this.nativeHelperSourceProvider = options.nativeHelperSourceProvider;
|
|
547
263
|
}
|
|
548
264
|
latestRegistryObservation() {
|
|
549
265
|
return this.registryObservation;
|
|
550
266
|
}
|
|
551
|
-
|
|
552
|
-
if (
|
|
553
|
-
|
|
554
|
-
attestation.version !== this.runtimeVersion) {
|
|
555
|
-
throw new BridgeError("CLAUDE_COMPATIBILITY_ATTESTATION_MISMATCH", "The Claude compatibility attestation does not match this runtime.");
|
|
556
|
-
}
|
|
557
|
-
this.compatibilityBlocked = attestation.tier === "incompatible";
|
|
558
|
-
this.compatibilityFailureCode = attestation.safeErrorCode;
|
|
559
|
-
if (this.compatibilityBlocked) {
|
|
560
|
-
if (this.monitorTimer !== undefined)
|
|
561
|
-
clearTimeout(this.monitorTimer);
|
|
562
|
-
this.monitorTimer = undefined;
|
|
267
|
+
activateEndpointGeneration(endpointGeneration) {
|
|
268
|
+
if (endpointGeneration !== this.identity.endpointGeneration) {
|
|
269
|
+
throw new BridgeError("CLAUDE_ENDPOINT_ACTIVATION_MISMATCH", "Only the exact current Claude endpoint generation can be activated.");
|
|
563
270
|
}
|
|
564
271
|
}
|
|
565
272
|
async initialize(callbacks) {
|
|
@@ -570,17 +277,9 @@ export class LocalClaudeGatewayProvider {
|
|
|
570
277
|
throw new BridgeError("CLAUDE_PROVIDER_ALREADY_INITIALIZED", "The local Claude provider can be initialized only once.");
|
|
571
278
|
}
|
|
572
279
|
this.callbacks = callbacks;
|
|
573
|
-
if (this.compatibilityBlocked) {
|
|
574
|
-
this.initialized = true;
|
|
575
|
-
return {
|
|
576
|
-
health: "degraded",
|
|
577
|
-
compatibility: "incompatible",
|
|
578
|
-
safeErrorCode: this.compatibilityFailureCode ?? "CLAUDE_COMPATIBILITY_CHECK_FAILED",
|
|
579
|
-
};
|
|
580
|
-
}
|
|
581
280
|
if (this.nativeHelpers !== undefined) {
|
|
582
281
|
this.initialized = true;
|
|
583
|
-
return { health: "healthy"
|
|
282
|
+
return { health: "healthy" };
|
|
584
283
|
}
|
|
585
284
|
try {
|
|
586
285
|
let listenerGeneration;
|
|
@@ -615,18 +314,16 @@ export class LocalClaudeGatewayProvider {
|
|
|
615
314
|
}
|
|
616
315
|
this.activeNativeCodexListener = listenerGeneration;
|
|
617
316
|
this.initialized = true;
|
|
618
|
-
return { health: "healthy"
|
|
317
|
+
return { health: "healthy" };
|
|
619
318
|
}
|
|
620
319
|
catch (error) {
|
|
621
320
|
if (error instanceof BridgeError &&
|
|
622
321
|
error.code === "UNSAFE_PEER_DIRECTORY") {
|
|
623
|
-
this.
|
|
624
|
-
this.compatibilityFailureCode = "CLAUDE_REGISTRY_UNAVAILABLE";
|
|
322
|
+
this.unavailableCode = "CLAUDE_REGISTRY_UNAVAILABLE";
|
|
625
323
|
this.registryObservation = unavailableClaudeRegistryObservation();
|
|
626
324
|
this.initialized = true;
|
|
627
325
|
return {
|
|
628
326
|
health: "degraded",
|
|
629
|
-
compatibility: "incompatible",
|
|
630
327
|
safeErrorCode: "CLAUDE_REGISTRY_UNAVAILABLE",
|
|
631
328
|
};
|
|
632
329
|
}
|
|
@@ -671,12 +368,16 @@ export class LocalClaudeGatewayProvider {
|
|
|
671
368
|
}
|
|
672
369
|
return { routeHandle: resolved.targetId };
|
|
673
370
|
}
|
|
674
|
-
async
|
|
371
|
+
async advertiseNativeSourcePeer(input) {
|
|
675
372
|
if (this.nativeHelpers !== undefined) {
|
|
676
373
|
this.assertReady();
|
|
677
374
|
await this.nativeHelpers.advertise(input);
|
|
678
375
|
return;
|
|
679
376
|
}
|
|
377
|
+
if (input.sourceProvider !== "codex" &&
|
|
378
|
+
input.sourceProvider !== this.nativeHelperSourceProvider) {
|
|
379
|
+
throw new BridgeError("CLAUDE_NATIVE_HELPER_UNAVAILABLE", "A non-Codex source requires a supervised native Claude helper.", true);
|
|
380
|
+
}
|
|
680
381
|
const registration = this.nativeCodexRegistration(input);
|
|
681
382
|
await this.serializeNativeCodexRegistration(async () => {
|
|
682
383
|
this.assertReady();
|
|
@@ -763,7 +464,7 @@ export class LocalClaudeGatewayProvider {
|
|
|
763
464
|
this.retiredNativeCodexListener !== undefined) {
|
|
764
465
|
throw new BridgeError("CODEX_PEER_SUCCESSION_CAPACITY", "Only one distinct prepared or retired Codex generation is allowed.");
|
|
765
466
|
}
|
|
766
|
-
const registration = this.nativeCodexRegistration(input);
|
|
467
|
+
const registration = this.nativeCodexRegistration({ ...input, sourceProvider: "codex" });
|
|
767
468
|
this.nativeCodexPreparationInFlight = input.generation;
|
|
768
469
|
let prepared;
|
|
769
470
|
try {
|
|
@@ -1027,7 +728,7 @@ export class LocalClaudeGatewayProvider {
|
|
|
1027
728
|
}
|
|
1028
729
|
return purged;
|
|
1029
730
|
}
|
|
1030
|
-
async
|
|
731
|
+
async unadvertiseNativeSourcePeer(alias) {
|
|
1031
732
|
if (this.nativeHelpers !== undefined) {
|
|
1032
733
|
await this.nativeHelpers.unadvertise(alias);
|
|
1033
734
|
return;
|
|
@@ -1043,7 +744,7 @@ export class LocalClaudeGatewayProvider {
|
|
|
1043
744
|
active.provisionalIngressForwarded = false;
|
|
1044
745
|
});
|
|
1045
746
|
}
|
|
1046
|
-
async
|
|
747
|
+
async updateNativeSourcePeerStatus(alias, status) {
|
|
1047
748
|
if (this.nativeHelpers !== undefined) {
|
|
1048
749
|
await this.nativeHelpers.updateStatus(alias, status);
|
|
1049
750
|
return;
|
|
@@ -1105,10 +806,10 @@ export class LocalClaudeGatewayProvider {
|
|
|
1105
806
|
active.inboundQuiesced = true;
|
|
1106
807
|
}
|
|
1107
808
|
async dispatch(input) {
|
|
1108
|
-
if (this.
|
|
809
|
+
if (this.unavailableCode !== undefined) {
|
|
1109
810
|
return {
|
|
1110
811
|
state: "failed",
|
|
1111
|
-
safeErrorCode: this.
|
|
812
|
+
safeErrorCode: this.unavailableCode,
|
|
1112
813
|
};
|
|
1113
814
|
}
|
|
1114
815
|
if (this.nativeHelpers !== undefined) {
|
|
@@ -1182,7 +883,8 @@ export class LocalClaudeGatewayProvider {
|
|
|
1182
883
|
return { state: "failed", safeErrorCode: "CLAUDE_ROUTE_UNAVAILABLE" };
|
|
1183
884
|
}
|
|
1184
885
|
}
|
|
1185
|
-
if (activeListener.registration?.alias !== input.sourceAlias
|
|
886
|
+
if (activeListener.registration?.alias !== input.sourceAlias ||
|
|
887
|
+
activeListener.registration.sourceProvider !== input.sourceProvider) {
|
|
1186
888
|
return { state: "failed", safeErrorCode: "CLAUDE_ROUTE_UNAVAILABLE" };
|
|
1187
889
|
}
|
|
1188
890
|
if (this.activeNativeCodexListener !== activeListener ||
|
|
@@ -1201,7 +903,8 @@ export class LocalClaudeGatewayProvider {
|
|
|
1201
903
|
let content;
|
|
1202
904
|
try {
|
|
1203
905
|
content = composeProvenanceEnvelope({
|
|
1204
|
-
|
|
906
|
+
sourceProvider: input.sourceProvider,
|
|
907
|
+
recipientProvider: this.identity.provider,
|
|
1205
908
|
sourceAlias: input.sourceAlias,
|
|
1206
909
|
targetAlias: input.targetAlias,
|
|
1207
910
|
conversationId: input.conversationId,
|
|
@@ -1392,7 +1095,7 @@ export class LocalClaudeGatewayProvider {
|
|
|
1392
1095
|
}
|
|
1393
1096
|
}
|
|
1394
1097
|
assertReady() {
|
|
1395
|
-
if (!this.initialized || this.closed || this.
|
|
1098
|
+
if (!this.initialized || this.closed || this.unavailableCode !== undefined) {
|
|
1396
1099
|
throw new BridgeError("CLAUDE_PROVIDER_UNAVAILABLE", "The local Claude provider is not available.", true);
|
|
1397
1100
|
}
|
|
1398
1101
|
}
|
|
@@ -1407,13 +1110,14 @@ export class LocalClaudeGatewayProvider {
|
|
|
1407
1110
|
throw new BridgeError("INVALID_CODEX_PEER_ALIAS", "The native Codex peer alias must target this host.");
|
|
1408
1111
|
}
|
|
1409
1112
|
const name = input.alias.slice(0, -suffix.length);
|
|
1410
|
-
|
|
1411
|
-
|
|
1113
|
+
const prefix = gatewayRegistrationIngressPrefixes[input.sourceProvider];
|
|
1114
|
+
if (prefix === undefined || !name.startsWith(prefix) || !NATIVE_CLAUDE_NAME.test(name)) {
|
|
1115
|
+
throw new BridgeError("INVALID_CODEX_PEER_ALIAS", "The native source peer alias must use its provider prefix.");
|
|
1412
1116
|
}
|
|
1413
1117
|
if (!path.isAbsolute(input.cwd) || input.cwd.includes("\0")) {
|
|
1414
1118
|
throw new BridgeError("INVALID_CODEX_PEER_CWD", "A native Codex peer requires an absolute working directory.");
|
|
1415
1119
|
}
|
|
1416
|
-
return { alias: input.alias, cwd: input.cwd, name };
|
|
1120
|
+
return { alias: input.alias, sourceProvider: input.sourceProvider, cwd: input.cwd, name };
|
|
1417
1121
|
}
|
|
1418
1122
|
requireActiveNativeCodexListener() {
|
|
1419
1123
|
this.assertReady();
|
|
@@ -1451,7 +1155,7 @@ export class LocalClaudeGatewayProvider {
|
|
|
1451
1155
|
await this.rejectNativeInboundReceipt(listenerGeneration, message.receiptHandle, diagnosticCode);
|
|
1452
1156
|
}
|
|
1453
1157
|
};
|
|
1454
|
-
if (this.
|
|
1158
|
+
if (this.unavailableCode !== undefined ||
|
|
1455
1159
|
this.closed ||
|
|
1456
1160
|
listenerGeneration.listener.closed ||
|
|
1457
1161
|
!this.ownsNativeCodexListener(listenerGeneration)) {
|
|
@@ -1801,7 +1505,6 @@ export class LocalClaudeGatewayProvider {
|
|
|
1801
1505
|
routeHandle: peer.targetId,
|
|
1802
1506
|
kind: "interactive",
|
|
1803
1507
|
state: claudeRouteState(peer),
|
|
1804
|
-
compatibility: "compatible",
|
|
1805
1508
|
};
|
|
1806
1509
|
this.discovered.set(peer.targetId, row);
|
|
1807
1510
|
rows.push(row);
|
|
@@ -1891,65 +1594,15 @@ export class LocalClaudeGatewayProvider {
|
|
|
1891
1594
|
export function createLocalClaudeGatewayProvider(options) {
|
|
1892
1595
|
return new LocalClaudeGatewayProvider(options);
|
|
1893
1596
|
}
|
|
1894
|
-
function
|
|
1895
|
-
if (version === UNKNOWN_COMPATIBILITY_VERSION) {
|
|
1896
|
-
return "CODEX_APP_SERVER_VERSION_UNPARSEABLE";
|
|
1897
|
-
}
|
|
1898
|
-
if (!isCompatibilityVersion(version)) {
|
|
1899
|
-
throw new BridgeError("CODEX_FACTORY_ATTESTATION_INVALID", "The local Codex factory version evidence is malformed.");
|
|
1900
|
-
}
|
|
1901
|
-
return CODEX_APP_SERVER_WRITABLE_VERSIONS.some((certifiedVersion) => sharesCompatibilityMajor(certifiedVersion, version))
|
|
1902
|
-
? undefined
|
|
1903
|
-
: "CODEX_APP_SERVER_VERSION_UNSUPPORTED";
|
|
1904
|
-
}
|
|
1905
|
-
function validateCodexFactory(factory, options = {}) {
|
|
1906
|
-
const schema = factory.schemaCompatibility;
|
|
1907
|
-
const write = factory.writeCompatibility;
|
|
1908
|
-
const versionFailure = codexVersionFailureCode(factory.appServerVersion);
|
|
1909
|
-
const certified = CODEX_APP_SERVER_WRITABLE_VERSIONS.includes(factory.appServerVersion);
|
|
1910
|
-
const schemaCandidate = schema.observedSchemaCandidate === true &&
|
|
1911
|
-
CODEX_APP_SERVER_WRITABLE_VERSIONS.some((version) => sharesCompatibilityMajor(version, factory.appServerVersion));
|
|
1912
|
-
const unsupportedVersionInvalid = versionFailure !== undefined &&
|
|
1913
|
-
(options.allowUnsupportedVersion !== true ||
|
|
1914
|
-
write !== null ||
|
|
1915
|
-
factory.writableReady);
|
|
1916
|
-
const supportedSchemaInvalid = versionFailure === undefined &&
|
|
1917
|
-
((!certified &&
|
|
1918
|
-
(!schemaCandidate || write !== null || factory.writableReady)) ||
|
|
1919
|
-
schema.protocol !== "app-server-v2-stable" ||
|
|
1920
|
-
schema.steering?.method !== "turn/steer" ||
|
|
1921
|
-
schema.steering.requestSchema !== "expected-turn-id-text-v1" ||
|
|
1922
|
-
schema.steering.deliveryBoundary !== "next-tool-call-boundary" ||
|
|
1923
|
-
(write !== null &&
|
|
1924
|
-
(write.appServerVersion !== schema.appServerVersion ||
|
|
1925
|
-
write.endpointGeneration !== schema.endpointGeneration ||
|
|
1926
|
-
write.protocol !== schema.protocol ||
|
|
1927
|
-
write.observedSchemaCandidate !== schema.observedSchemaCandidate ||
|
|
1928
|
-
write.steering?.method !== schema.steering.method ||
|
|
1929
|
-
write.steering.requestSchema !== schema.steering.requestSchema ||
|
|
1930
|
-
write.steering.deliveryBoundary !==
|
|
1931
|
-
schema.steering.deliveryBoundary)) ||
|
|
1932
|
-
factory.writableReady !== (write !== null));
|
|
1597
|
+
function validateCodexFactory(factory) {
|
|
1933
1598
|
if (factory.hostId !== LOCAL_HOST ||
|
|
1934
1599
|
factory.protocol !== "codex-app-server" ||
|
|
1935
1600
|
factory.protocolVersion !== factory.appServerVersion ||
|
|
1936
|
-
|
|
1937
|
-
|
|
1938
|
-
|
|
1939
|
-
supportedSchemaInvalid) {
|
|
1940
|
-
throw new BridgeError("CODEX_FACTORY_ATTESTATION_INVALID", "The local Codex provider requires exact schema and write attestations.");
|
|
1601
|
+
factory.appServerVersion.length === 0 ||
|
|
1602
|
+
factory.endpointGeneration.length === 0) {
|
|
1603
|
+
throw new BridgeError("CODEX_FACTORY_ATTESTATION_INVALID", "The local Codex provider requires exact managed endpoint identity.");
|
|
1941
1604
|
}
|
|
1942
1605
|
}
|
|
1943
|
-
function codexFactoryWritesEnabled(factory, attestation, writeProbePassed = false) {
|
|
1944
|
-
return ((attestation.tier === "certified" &&
|
|
1945
|
-
CODEX_APP_SERVER_WRITABLE_VERSIONS.includes(attestation.version) &&
|
|
1946
|
-
factory.writableReady &&
|
|
1947
|
-
factory.writeCompatibility !== null) ||
|
|
1948
|
-
(!factory.writableReady &&
|
|
1949
|
-
factory.writeCompatibility === null &&
|
|
1950
|
-
writeProbePassed &&
|
|
1951
|
-
compatibilityCoversWrites(attestation)));
|
|
1952
|
-
}
|
|
1953
1606
|
function codexRouteState(observation) {
|
|
1954
1607
|
if (observation.connection !== "ready")
|
|
1955
1608
|
return "stale";
|
|
@@ -1971,9 +1624,6 @@ function codexRouteSafeCode(observation) {
|
|
|
1971
1624
|
if (codexRouteState(observation) === "stale") {
|
|
1972
1625
|
return "CODEX_ROUTE_STALE";
|
|
1973
1626
|
}
|
|
1974
|
-
if (!observation.writableReady) {
|
|
1975
|
-
return "CODEX_WRITES_DISABLED";
|
|
1976
|
-
}
|
|
1977
1627
|
return undefined;
|
|
1978
1628
|
}
|
|
1979
1629
|
export class LocalCodexGatewayProvider {
|
|
@@ -1986,6 +1636,7 @@ export class LocalCodexGatewayProvider {
|
|
|
1986
1636
|
cleanupTimeoutMs;
|
|
1987
1637
|
recoveryInitialMs;
|
|
1988
1638
|
recoveryMaxMs;
|
|
1639
|
+
observationPollMs;
|
|
1989
1640
|
now;
|
|
1990
1641
|
routes = new Map();
|
|
1991
1642
|
/** Exact public alias authority retained across internal route recovery. */
|
|
@@ -1997,6 +1648,8 @@ export class LocalCodexGatewayProvider {
|
|
|
1997
1648
|
routeRecoveries = new Map();
|
|
1998
1649
|
routesRequiringRecovery = new Set();
|
|
1999
1650
|
trackedRoutes = new Set();
|
|
1651
|
+
observationTimer;
|
|
1652
|
+
observationInFlight;
|
|
2000
1653
|
retiredEndpointGeneration;
|
|
2001
1654
|
expectsReply = new Map();
|
|
2002
1655
|
callbackQueue = [];
|
|
@@ -2007,14 +1660,7 @@ export class LocalCodexGatewayProvider {
|
|
|
2007
1660
|
endpointActivationRetry;
|
|
2008
1661
|
endpointActivationRetryTimer;
|
|
2009
1662
|
callbackDrainFrozen = false;
|
|
2010
|
-
writeProbeObservationsByGeneration = new Map();
|
|
2011
|
-
writeProbeResults = new Map();
|
|
2012
|
-
writeProbeDecline;
|
|
2013
|
-
writeProbeOverflow;
|
|
2014
|
-
compatibilityAttested;
|
|
2015
|
-
compatibilityFailureCode;
|
|
2016
1663
|
endpointUnavailable = false;
|
|
2017
|
-
pendingEndpointAttestation;
|
|
2018
1664
|
pendingEndpointRefreshEvent;
|
|
2019
1665
|
pendingEndpointRefreshSelectorClaimed = false;
|
|
2020
1666
|
initialized = false;
|
|
@@ -2025,9 +1671,6 @@ export class LocalCodexGatewayProvider {
|
|
|
2025
1671
|
this.factory = options.factory;
|
|
2026
1672
|
this.refreshFactory = options.refreshFactory;
|
|
2027
1673
|
exactLocalHost(options.factory.hostId);
|
|
2028
|
-
this.compatibilityAttested =
|
|
2029
|
-
options.factory.writableReady &&
|
|
2030
|
-
options.factory.writeCompatibility !== null;
|
|
2031
1674
|
this.maxCallbacks = positiveBounded(options.maxCallbackEvents, MAX_CODEX_CALLBACKS, 4_096);
|
|
2032
1675
|
this.maxRoutes = positiveBounded(options.maxRoutes, MAX_CODEX_ROUTES, 256);
|
|
2033
1676
|
this.maxReplyBytes = positiveBounded(options.maxReplyBytes, MAX_TRANSIENT_REPLY_BYTES, 1024 * 1024);
|
|
@@ -2041,6 +1684,7 @@ export class LocalCodexGatewayProvider {
|
|
|
2041
1684
|
if (this.recoveryInitialMs > this.recoveryMaxMs) {
|
|
2042
1685
|
throw new BridgeError("INVALID_GATEWAY_PROVIDER_CONFIGURATION", "The Codex recovery initial delay cannot exceed its capped delay.");
|
|
2043
1686
|
}
|
|
1687
|
+
this.observationPollMs = positiveBounded(options.observationPollMs, DEFAULT_CODEX_OBSERVATION_POLL_MS, 60_000);
|
|
2044
1688
|
this.now = options.now ?? (() => new Date());
|
|
2045
1689
|
}
|
|
2046
1690
|
get identity() {
|
|
@@ -2056,352 +1700,39 @@ export class LocalCodexGatewayProvider {
|
|
|
2056
1700
|
get protocolVersion() {
|
|
2057
1701
|
return this.factory.protocolVersion;
|
|
2058
1702
|
}
|
|
2059
|
-
|
|
2060
|
-
|
|
2061
|
-
|
|
2062
|
-
|
|
2063
|
-
|
|
2064
|
-
|
|
2065
|
-
(attestation.checkedAt === pending.checkedAt &&
|
|
2066
|
-
attestation.tier === pending.tier &&
|
|
2067
|
-
attestation.safeErrorCode === pending.safeErrorCode &&
|
|
2068
|
-
attestation.probes.length === pending.probes.length &&
|
|
2069
|
-
attestation.probes.every((probe, index) => probe.name === pending.probes[index]?.name &&
|
|
2070
|
-
probe.outcome === pending.probes[index]?.outcome &&
|
|
2071
|
-
probe.safeErrorCode === pending.probes[index]?.safeErrorCode));
|
|
2072
|
-
if (!isCompatibilityAttestation(attestation) ||
|
|
2073
|
-
attestation.surface !== "codex" ||
|
|
2074
|
-
attestation.version !== this.factory.appServerVersion ||
|
|
2075
|
-
!pendingMatches) {
|
|
2076
|
-
throw new BridgeError("CODEX_COMPATIBILITY_ATTESTATION_MISMATCH", "The Codex compatibility attestation does not match this endpoint.");
|
|
2077
|
-
}
|
|
2078
|
-
if (attestation.tier === "incompatible") {
|
|
2079
|
-
this.pendingEndpointAttestation = undefined;
|
|
2080
|
-
this.pendingEndpointRefreshEvent = undefined;
|
|
2081
|
-
this.pendingEndpointRefreshSelectorClaimed = false;
|
|
2082
|
-
this.clearEndpointActivationRetry();
|
|
2083
|
-
this.compatibilityAttested = false;
|
|
2084
|
-
this.endpointUnavailable = true;
|
|
2085
|
-
this.compatibilityFailureCode =
|
|
2086
|
-
attestation.safeErrorCode ?? "CODEX_COMPATIBILITY_CHECK_FAILED";
|
|
2087
|
-
return;
|
|
2088
|
-
}
|
|
2089
|
-
const writesEnabled = codexFactoryWritesEnabled(this.factory, attestation, this.latestWriteCompatibilityProbeObservation(this.factory.endpointGeneration)?.outcome === "pass");
|
|
2090
|
-
const schemaObserved = attestation.tier === "schema_attested" &&
|
|
2091
|
-
!this.factory.writableReady &&
|
|
2092
|
-
this.factory.writeCompatibility === null;
|
|
2093
|
-
if ((!writesEnabled && !schemaObserved) ||
|
|
2094
|
-
(this.endpointUnavailable && pending === undefined) ||
|
|
2095
|
-
!pendingMatches) {
|
|
2096
|
-
throw new BridgeError("CODEX_COMPATIBILITY_ATTESTATION_MISMATCH", "The Codex compatibility attestation does not match this endpoint.");
|
|
1703
|
+
activateEndpointGeneration(endpointGeneration) {
|
|
1704
|
+
const event = this.pendingEndpointRefreshEvent;
|
|
1705
|
+
if (event === undefined ||
|
|
1706
|
+
event.current.endpointGeneration !== endpointGeneration ||
|
|
1707
|
+
this.factory.endpointGeneration !== endpointGeneration) {
|
|
1708
|
+
throw new BridgeError("CODEX_ENDPOINT_ACTIVATION_MISMATCH", "Only the exact pending managed Codex generation can be activated.");
|
|
2097
1709
|
}
|
|
2098
|
-
this.pendingEndpointAttestation = undefined;
|
|
2099
1710
|
this.pendingEndpointRefreshEvent = undefined;
|
|
2100
1711
|
this.pendingEndpointRefreshSelectorClaimed = false;
|
|
2101
1712
|
this.clearEndpointActivationRetry();
|
|
2102
|
-
this.
|
|
2103
|
-
this.endpointUnavailable = !writesEnabled;
|
|
2104
|
-
this.compatibilityFailureCode = undefined;
|
|
1713
|
+
this.endpointUnavailable = false;
|
|
2105
1714
|
}
|
|
2106
1715
|
releaseEndpointRefreshSelectorClaim(endpointGeneration) {
|
|
2107
1716
|
const event = this.pendingEndpointRefreshEvent;
|
|
2108
|
-
if (event?.current.endpointGeneration === endpointGeneration
|
|
2109
|
-
this.pendingEndpointAttestation !== undefined) {
|
|
1717
|
+
if (event?.current.endpointGeneration === endpointGeneration) {
|
|
2110
1718
|
this.pendingEndpointRefreshSelectorClaimed = false;
|
|
2111
1719
|
}
|
|
2112
1720
|
}
|
|
2113
1721
|
rearmEndpointRefreshActivation(endpointGeneration) {
|
|
2114
1722
|
const event = this.pendingEndpointRefreshEvent;
|
|
2115
1723
|
if (event?.current.endpointGeneration === endpointGeneration &&
|
|
2116
|
-
this.pendingEndpointAttestation !== undefined &&
|
|
2117
1724
|
!this.closing &&
|
|
2118
1725
|
!this.closed) {
|
|
2119
1726
|
this.publishEndpointRefresh(event);
|
|
2120
1727
|
}
|
|
2121
1728
|
}
|
|
2122
|
-
latestWriteCompatibilityProbeObservation(endpointGeneration) {
|
|
2123
|
-
return (this.writeProbeObservationsByGeneration.get(endpointGeneration) ??
|
|
2124
|
-
(this.writeProbeOverflow?.endpointGeneration === endpointGeneration
|
|
2125
|
-
? this.writeProbeOverflow.observation
|
|
2126
|
-
: undefined) ??
|
|
2127
|
-
(this.writeProbeDecline?.endpointGeneration === endpointGeneration
|
|
2128
|
-
? this.writeProbeDecline.observation
|
|
2129
|
-
: undefined));
|
|
2130
|
-
}
|
|
2131
|
-
async runCompatibilityProbes(context) {
|
|
2132
|
-
return await this.runCompatibilityProbesFor(this.factory, context === undefined
|
|
2133
|
-
? undefined
|
|
2134
|
-
: {
|
|
2135
|
-
stateRoot: path.resolve(context.stateRoot),
|
|
2136
|
-
forbiddenCodexThreadIds: [...context.forbiddenCodexThreadIds],
|
|
2137
|
-
});
|
|
2138
|
-
}
|
|
2139
|
-
async runCompatibilityProbesFor(factory, context) {
|
|
2140
|
-
const [installation, control, initialize, threadList] = compatibilityProbeNames.codex;
|
|
2141
|
-
let transport;
|
|
2142
|
-
let connector;
|
|
2143
|
-
let stage = "transport";
|
|
2144
|
-
try {
|
|
2145
|
-
transport = await factory.connectTransport();
|
|
2146
|
-
stage = "initialize";
|
|
2147
|
-
connector = await CodexAppServerConnector.connect({
|
|
2148
|
-
compatibility: factory.schemaCompatibility,
|
|
2149
|
-
...(context === undefined
|
|
2150
|
-
? {}
|
|
2151
|
-
: { writeCompatibilityProbe: true }),
|
|
2152
|
-
writesEnabled: false,
|
|
2153
|
-
route: {
|
|
2154
|
-
endpointGeneration: factory.endpointGeneration,
|
|
2155
|
-
threadId: COMPATIBILITY_PROBE_THREAD_ID,
|
|
2156
|
-
},
|
|
2157
|
-
transport,
|
|
2158
|
-
maxReplyBytes: this.maxReplyBytes,
|
|
2159
|
-
now: this.now,
|
|
2160
|
-
onEvent: () => undefined,
|
|
2161
|
-
onTurnResult: () => undefined,
|
|
2162
|
-
});
|
|
2163
|
-
stage = "thread_list";
|
|
2164
|
-
await connector.observeLoadedThread(connector.guard());
|
|
2165
|
-
const requiredProbes = [
|
|
2166
|
-
passedProbe(installation),
|
|
2167
|
-
passedProbe(control),
|
|
2168
|
-
passedProbe(initialize),
|
|
2169
|
-
passedProbe(threadList),
|
|
2170
|
-
];
|
|
2171
|
-
if (context === undefined)
|
|
2172
|
-
return requiredProbes;
|
|
2173
|
-
const result = await this.runCodexWriteProbe(factory, connector, context);
|
|
2174
|
-
return result.outcome === "pass"
|
|
2175
|
-
? [...requiredProbes, passedProbe("write_attestation")]
|
|
2176
|
-
: requiredProbes;
|
|
2177
|
-
}
|
|
2178
|
-
catch (error) {
|
|
2179
|
-
if (this.isEndpointGenerationChanged(error) ||
|
|
2180
|
-
(error instanceof BridgeError &&
|
|
2181
|
-
error.code === "CODEX_FACTORY_ATTESTATION_INVALID")) {
|
|
2182
|
-
throw error;
|
|
2183
|
-
}
|
|
2184
|
-
if (stage === "transport") {
|
|
2185
|
-
const availabilityFailure = error instanceof LocalCodexTransportError &&
|
|
2186
|
-
[
|
|
2187
|
-
"MANAGED_CODEX_UNAVAILABLE",
|
|
2188
|
-
"LOCAL_APP_SERVER_NOT_RUNNING",
|
|
2189
|
-
"SPAWN_FAILED",
|
|
2190
|
-
"SPAWN_TIMEOUT",
|
|
2191
|
-
"PROXY_STDERR_LIMIT",
|
|
2192
|
-
"TRANSPORT_CONNECT_FAILED",
|
|
2193
|
-
].includes(error.code);
|
|
2194
|
-
if (!availabilityFailure)
|
|
2195
|
-
throw error;
|
|
2196
|
-
const installationUnavailable = error instanceof LocalCodexTransportError &&
|
|
2197
|
-
error.code === "MANAGED_CODEX_UNAVAILABLE";
|
|
2198
|
-
return [
|
|
2199
|
-
installationUnavailable
|
|
2200
|
-
? failedProbe(installation, "CODEX_INSTALLATION_INVALID")
|
|
2201
|
-
: passedProbe(installation),
|
|
2202
|
-
failedProbe(control, installationUnavailable
|
|
2203
|
-
? "CODEX_COMPAT_PROBE_BLOCKED"
|
|
2204
|
-
: "CODEX_CONTROL_SOCKET_UNAVAILABLE"),
|
|
2205
|
-
failedProbe(initialize, "CODEX_COMPAT_PROBE_BLOCKED"),
|
|
2206
|
-
failedProbe(threadList, "CODEX_COMPAT_PROBE_BLOCKED"),
|
|
2207
|
-
];
|
|
2208
|
-
}
|
|
2209
|
-
return [
|
|
2210
|
-
passedProbe(installation),
|
|
2211
|
-
passedProbe(control),
|
|
2212
|
-
stage === "initialize"
|
|
2213
|
-
? failedProbe(initialize, "CODEX_INITIALIZE_SCHEMA_REJECTED")
|
|
2214
|
-
: passedProbe(initialize),
|
|
2215
|
-
failedProbe(threadList, "CODEX_THREAD_LIST_SCHEMA_REJECTED"),
|
|
2216
|
-
];
|
|
2217
|
-
}
|
|
2218
|
-
finally {
|
|
2219
|
-
if (connector !== undefined) {
|
|
2220
|
-
await connector.close();
|
|
2221
|
-
}
|
|
2222
|
-
else if (transport !== undefined) {
|
|
2223
|
-
await transport.close();
|
|
2224
|
-
}
|
|
2225
|
-
}
|
|
2226
|
-
}
|
|
2227
|
-
async runCodexWriteProbe(factory, connector, context) {
|
|
2228
|
-
const key = `${factory.appServerVersion}\0${factory.endpointGeneration}`;
|
|
2229
|
-
const cached = this.writeProbeResults.get(key);
|
|
2230
|
-
if (cached !== undefined)
|
|
2231
|
-
return await cached;
|
|
2232
|
-
if (this.writeProbeOverflow?.key === key) {
|
|
2233
|
-
if (this.writeProbeResults.size >= MAX_CODEX_WRITE_PROBE_ATTEMPTS) {
|
|
2234
|
-
return this.writeProbeOverflow.observation;
|
|
2235
|
-
}
|
|
2236
|
-
this.writeProbeOverflow = undefined;
|
|
2237
|
-
}
|
|
2238
|
-
if (this.writeProbeResults.size >= MAX_CODEX_WRITE_PROBE_ATTEMPTS) {
|
|
2239
|
-
const observation = Object.freeze(codexWriteProbeFailure("CODEX_WRITE_PROBE_CAPACITY_EXHAUSTED"));
|
|
2240
|
-
this.writeProbeOverflow = Object.freeze({
|
|
2241
|
-
endpointGeneration: factory.endpointGeneration,
|
|
2242
|
-
key,
|
|
2243
|
-
observation,
|
|
2244
|
-
});
|
|
2245
|
-
if (this.callbacks !== undefined) {
|
|
2246
|
-
invokeCallback(() => this.callbacks?.onProtocolNotice?.({
|
|
2247
|
-
code: observation.safeErrorCode,
|
|
2248
|
-
}));
|
|
2249
|
-
}
|
|
2250
|
-
return observation;
|
|
2251
|
-
}
|
|
2252
|
-
const pending = this.executeCodexWriteProbe(connector, context).then((result) => {
|
|
2253
|
-
const observation = Object.freeze({ ...result });
|
|
2254
|
-
if (observation.outcome === "fail" &&
|
|
2255
|
-
(observation.safeErrorCode ===
|
|
2256
|
-
"CODEX_WRITE_PROBE_MODEL_PIN_UNAVAILABLE" ||
|
|
2257
|
-
observation.safeErrorCode ===
|
|
2258
|
-
"CODEX_WRITE_PROBE_RATE_LIMIT_CONSTRAINED")) {
|
|
2259
|
-
this.writeProbeResults.delete(key);
|
|
2260
|
-
this.writeProbeDecline = Object.freeze({
|
|
2261
|
-
endpointGeneration: factory.endpointGeneration,
|
|
2262
|
-
observation,
|
|
2263
|
-
});
|
|
2264
|
-
}
|
|
2265
|
-
else {
|
|
2266
|
-
this.writeProbeObservationsByGeneration.set(factory.endpointGeneration, observation);
|
|
2267
|
-
}
|
|
2268
|
-
if (observation.outcome === "fail" && this.callbacks !== undefined) {
|
|
2269
|
-
invokeCallback(() => this.callbacks?.onProtocolNotice?.({
|
|
2270
|
-
code: observation.safeErrorCode,
|
|
2271
|
-
}));
|
|
2272
|
-
}
|
|
2273
|
-
return observation;
|
|
2274
|
-
});
|
|
2275
|
-
this.writeProbeResults.set(key, pending);
|
|
2276
|
-
return await pending;
|
|
2277
|
-
}
|
|
2278
|
-
async executeCodexWriteProbe(connector, context) {
|
|
2279
|
-
const stateRoot = path.resolve(context.stateRoot);
|
|
2280
|
-
const rootBefore = await attestCodexWriteProbeRoot(stateRoot);
|
|
2281
|
-
if (rootBefore === null) {
|
|
2282
|
-
return codexWriteProbeFailure("CODEX_WRITE_PROBE_THREAD_SETUP_FAILED");
|
|
2283
|
-
}
|
|
2284
|
-
const probeRoot = path.join(stateRoot, CODEX_WRITE_PROBE_ROOT);
|
|
2285
|
-
let probeRootBefore;
|
|
2286
|
-
try {
|
|
2287
|
-
let created = false;
|
|
2288
|
-
try {
|
|
2289
|
-
await mkdir(probeRoot, { mode: 0o700 });
|
|
2290
|
-
created = true;
|
|
2291
|
-
}
|
|
2292
|
-
catch (error) {
|
|
2293
|
-
if (systemErrorCode(error) !== "EEXIST")
|
|
2294
|
-
throw error;
|
|
2295
|
-
}
|
|
2296
|
-
if (created)
|
|
2297
|
-
await chmod(probeRoot, 0o700);
|
|
2298
|
-
const evidence = await attestCodexWriteProbeRoot(probeRoot);
|
|
2299
|
-
if (evidence === null) {
|
|
2300
|
-
return codexWriteProbeFailure("CODEX_WRITE_PROBE_THREAD_SETUP_FAILED");
|
|
2301
|
-
}
|
|
2302
|
-
probeRootBefore = evidence;
|
|
2303
|
-
}
|
|
2304
|
-
catch (error) {
|
|
2305
|
-
if (error instanceof BridgeError &&
|
|
2306
|
-
error.code === "CODEX_FACTORY_ATTESTATION_INVALID") {
|
|
2307
|
-
throw error;
|
|
2308
|
-
}
|
|
2309
|
-
if (transientCodexWriteProbeError(error)) {
|
|
2310
|
-
return codexWriteProbeFailure("CODEX_WRITE_PROBE_THREAD_SETUP_FAILED");
|
|
2311
|
-
}
|
|
2312
|
-
throw new BridgeError("CODEX_FACTORY_ATTESTATION_INVALID", "The controller-owned Codex probe root could not be established safely.");
|
|
2313
|
-
}
|
|
2314
|
-
let cwd;
|
|
2315
|
-
let before;
|
|
2316
|
-
let result = codexWriteProbeFailure("CODEX_WRITE_PROBE_THREAD_SETUP_FAILED");
|
|
2317
|
-
try {
|
|
2318
|
-
cwd = await mkdtemp(path.join(probeRoot, CODEX_WRITE_PROBE_DIRECTORY_PREFIX));
|
|
2319
|
-
if (path.dirname(cwd) !== probeRoot) {
|
|
2320
|
-
throw new Error("probe directory escaped its dedicated root");
|
|
2321
|
-
}
|
|
2322
|
-
await chmod(cwd, 0o700);
|
|
2323
|
-
before = await attestCodexWriteProbeDirectory(cwd);
|
|
2324
|
-
result = await connector.runWriteCompatibilityProbe({
|
|
2325
|
-
cwd,
|
|
2326
|
-
forbiddenThreadIds: [
|
|
2327
|
-
...new Set([
|
|
2328
|
-
COMPATIBILITY_PROBE_THREAD_ID,
|
|
2329
|
-
...context.forbiddenCodexThreadIds,
|
|
2330
|
-
...this.trackedRoutes,
|
|
2331
|
-
]),
|
|
2332
|
-
],
|
|
2333
|
-
});
|
|
2334
|
-
}
|
|
2335
|
-
catch (error) {
|
|
2336
|
-
if (!safeCodexWriteProbeFilesystemFailure(error)) {
|
|
2337
|
-
if (error instanceof BridgeError)
|
|
2338
|
-
throw error;
|
|
2339
|
-
throw new BridgeError("CODEX_FACTORY_ATTESTATION_INVALID", "The disposable Codex probe directory could not be established safely.");
|
|
2340
|
-
}
|
|
2341
|
-
result = codexWriteProbeFailure("CODEX_WRITE_PROBE_THREAD_SETUP_FAILED");
|
|
2342
|
-
}
|
|
2343
|
-
let unchanged = false;
|
|
2344
|
-
let removed = false;
|
|
2345
|
-
if (cwd !== undefined && before !== undefined) {
|
|
2346
|
-
try {
|
|
2347
|
-
const after = await attestCodexWriteProbeDirectory(cwd);
|
|
2348
|
-
unchanged = sameCodexWriteProbeDirectory(before, after);
|
|
2349
|
-
await rmdir(cwd);
|
|
2350
|
-
try {
|
|
2351
|
-
await lstat(cwd);
|
|
2352
|
-
}
|
|
2353
|
-
catch (error) {
|
|
2354
|
-
removed = systemErrorCode(error) === "ENOENT";
|
|
2355
|
-
}
|
|
2356
|
-
}
|
|
2357
|
-
catch (error) {
|
|
2358
|
-
if (!safeCodexWriteProbeFilesystemFailure(error)) {
|
|
2359
|
-
if (error instanceof BridgeError)
|
|
2360
|
-
throw error;
|
|
2361
|
-
throw new BridgeError("CODEX_FACTORY_ATTESTATION_INVALID", "The disposable Codex probe directory could not be re-attested safely.");
|
|
2362
|
-
}
|
|
2363
|
-
// Never remove a path after its exact ownership evidence changed.
|
|
2364
|
-
}
|
|
2365
|
-
}
|
|
2366
|
-
if (cwd !== undefined && !removed) {
|
|
2367
|
-
result = codexWriteProbeFailure("CODEX_WRITE_PROBE_CLEANUP_UNCONFIRMED", result);
|
|
2368
|
-
}
|
|
2369
|
-
else if (cwd !== undefined && !unchanged) {
|
|
2370
|
-
result = codexWriteProbeFailure("CODEX_WRITE_PROBE_TOOL_ACTIVITY_OBSERVED", result);
|
|
2371
|
-
}
|
|
2372
|
-
const [probeRootAfter, rootAfter] = await Promise.all([
|
|
2373
|
-
attestCodexWriteProbeRoot(probeRoot),
|
|
2374
|
-
attestCodexWriteProbeRoot(stateRoot),
|
|
2375
|
-
]);
|
|
2376
|
-
if (probeRootAfter === null || rootAfter === null) {
|
|
2377
|
-
return codexWriteProbeFailure("CODEX_WRITE_PROBE_THREAD_SETUP_FAILED", result);
|
|
2378
|
-
}
|
|
2379
|
-
if (!sameCodexWriteProbeRoot(probeRootBefore, probeRootAfter) ||
|
|
2380
|
-
!sameCodexWriteProbeRoot(rootBefore, rootAfter)) {
|
|
2381
|
-
throw new BridgeError("CODEX_FACTORY_ATTESTATION_INVALID", "A controller-owned Codex probe root changed while the disposable probe ran.");
|
|
2382
|
-
}
|
|
2383
|
-
return result;
|
|
2384
|
-
}
|
|
2385
1729
|
async initialize(callbacks) {
|
|
2386
1730
|
if (this.closed || this.initialized) {
|
|
2387
1731
|
throw new BridgeError("CODEX_PROVIDER_INITIALIZATION_REJECTED", "The local Codex provider cannot be initialized in this state.");
|
|
2388
1732
|
}
|
|
2389
1733
|
this.callbacks = callbacks;
|
|
2390
1734
|
this.initialized = true;
|
|
2391
|
-
|
|
2392
|
-
return {
|
|
2393
|
-
health: "degraded",
|
|
2394
|
-
compatibility: "incompatible",
|
|
2395
|
-
safeErrorCode: this.compatibilityFailureCode,
|
|
2396
|
-
};
|
|
2397
|
-
}
|
|
2398
|
-
return !this.compatibilityAttested
|
|
2399
|
-
? {
|
|
2400
|
-
health: "degraded",
|
|
2401
|
-
compatibility: "compatible",
|
|
2402
|
-
safeErrorCode: "CODEX_MONITOR_ONLY",
|
|
2403
|
-
}
|
|
2404
|
-
: { health: "healthy", compatibility: "compatible" };
|
|
1735
|
+
return { health: "healthy" };
|
|
2405
1736
|
}
|
|
2406
1737
|
async selectRoute(input) {
|
|
2407
1738
|
if (!this.initialized || this.closing || this.closed) {
|
|
@@ -2423,9 +1754,8 @@ export class LocalCodexGatewayProvider {
|
|
|
2423
1754
|
let selectorRefreshReserved = false;
|
|
2424
1755
|
let route;
|
|
2425
1756
|
const stageRefreshedRoute = async (result) => {
|
|
2426
|
-
if (result === undefined
|
|
2427
|
-
|
|
2428
|
-
throw new BridgeError("CODEX_ROUTE_SETUP_REJECTED", "The exact Codex endpoint replacement was not compatibility-attested.");
|
|
1757
|
+
if (result === undefined) {
|
|
1758
|
+
throw new BridgeError("CODEX_ROUTE_SETUP_REJECTED", "The exact Codex endpoint replacement could not be established.");
|
|
2429
1759
|
}
|
|
2430
1760
|
refreshResult = result;
|
|
2431
1761
|
selectorRefreshReserved = this.reserveSelectorRefresh(result);
|
|
@@ -2448,7 +1778,7 @@ export class LocalCodexGatewayProvider {
|
|
|
2448
1778
|
route = await stageRefreshedRoute(retainedEndpointRefresh);
|
|
2449
1779
|
}
|
|
2450
1780
|
else if (this.endpointUnavailable &&
|
|
2451
|
-
this.
|
|
1781
|
+
this.pendingEndpointRefreshEvent === undefined) {
|
|
2452
1782
|
route = await stageRefreshedRoute(await this.refreshEndpoint("selector"));
|
|
2453
1783
|
}
|
|
2454
1784
|
else {
|
|
@@ -2502,6 +1832,7 @@ export class LocalCodexGatewayProvider {
|
|
|
2502
1832
|
throw new BridgeError("CODEX_ROUTE_SETUP_REJECTED", "The exact Codex task connection closed during route selection.");
|
|
2503
1833
|
}
|
|
2504
1834
|
this.trackedRoutes.add(input.routeHandle);
|
|
1835
|
+
this.scheduleCodexObservation();
|
|
2505
1836
|
this.routeAliases.set(input.routeHandle, input.alias);
|
|
2506
1837
|
if (refreshResult === undefined) {
|
|
2507
1838
|
this.queueRouteObservation(route, observation);
|
|
@@ -2566,7 +1897,6 @@ export class LocalCodexGatewayProvider {
|
|
|
2566
1897
|
this.closed ||
|
|
2567
1898
|
this.endpointUnavailable ||
|
|
2568
1899
|
this.endpointRefresh !== undefined ||
|
|
2569
|
-
!this.compatibilityAttested ||
|
|
2570
1900
|
input.authorization !== "selected_route" ||
|
|
2571
1901
|
!sameEndpoint(input.binding, this.identity)) {
|
|
2572
1902
|
return { state: "failed", safeErrorCode: "CODEX_ROUTE_UNAVAILABLE" };
|
|
@@ -2580,9 +1910,6 @@ export class LocalCodexGatewayProvider {
|
|
|
2580
1910
|
return { state: "failed", safeErrorCode: "CODEX_ROUTE_UNAVAILABLE" };
|
|
2581
1911
|
}
|
|
2582
1912
|
const guard = route.connector.guard();
|
|
2583
|
-
if (!guard.writableReady) {
|
|
2584
|
-
return { state: "failed", safeErrorCode: "CODEX_WRITES_DISABLED" };
|
|
2585
|
-
}
|
|
2586
1913
|
if (strictDeadline(input.deadlineAt, this.now().getTime()) === undefined) {
|
|
2587
1914
|
return { state: "failed", safeErrorCode: "MESSAGE_EXPIRED" };
|
|
2588
1915
|
}
|
|
@@ -2592,7 +1919,8 @@ export class LocalCodexGatewayProvider {
|
|
|
2592
1919
|
let content;
|
|
2593
1920
|
try {
|
|
2594
1921
|
content = composeProvenanceEnvelope({
|
|
2595
|
-
|
|
1922
|
+
sourceProvider: input.sourceProvider,
|
|
1923
|
+
recipientProvider: this.identity.provider,
|
|
2596
1924
|
sourceAlias: input.sourceAlias,
|
|
2597
1925
|
targetAlias: input.targetAlias,
|
|
2598
1926
|
conversationId: input.conversationId,
|
|
@@ -2658,6 +1986,10 @@ export class LocalCodexGatewayProvider {
|
|
|
2658
1986
|
throw new BridgeError("CODEX_ENDPOINT_ACTIVATION_PENDING", "The exact Codex route still belongs to an unaccepted endpoint transition.", true);
|
|
2659
1987
|
}
|
|
2660
1988
|
this.trackedRoutes.delete(routeHandle);
|
|
1989
|
+
if (this.trackedRoutes.size === 0 && this.observationTimer !== undefined) {
|
|
1990
|
+
clearTimeout(this.observationTimer);
|
|
1991
|
+
this.observationTimer = undefined;
|
|
1992
|
+
}
|
|
2661
1993
|
this.cancelRouteRecovery(routeHandle);
|
|
2662
1994
|
const recovery = this.routeRecoveries.get(routeHandle);
|
|
2663
1995
|
if (recovery !== undefined)
|
|
@@ -2695,6 +2027,10 @@ export class LocalCodexGatewayProvider {
|
|
|
2695
2027
|
if (this.closed || this.closing)
|
|
2696
2028
|
return;
|
|
2697
2029
|
this.closing = true;
|
|
2030
|
+
if (this.observationTimer !== undefined) {
|
|
2031
|
+
clearTimeout(this.observationTimer);
|
|
2032
|
+
this.observationTimer = undefined;
|
|
2033
|
+
}
|
|
2698
2034
|
for (const timer of this.routeRecoveryTimers.values())
|
|
2699
2035
|
clearTimeout(timer);
|
|
2700
2036
|
this.routeRecoveryTimers.clear();
|
|
@@ -2702,7 +2038,6 @@ export class LocalCodexGatewayProvider {
|
|
|
2702
2038
|
this.clearEndpointActivationRetry();
|
|
2703
2039
|
this.pendingEndpointRefreshEvent = undefined;
|
|
2704
2040
|
this.pendingEndpointRefreshSelectorClaimed = false;
|
|
2705
|
-
this.pendingEndpointAttestation = undefined;
|
|
2706
2041
|
const refreshResult = this.endpointRefresh === undefined
|
|
2707
2042
|
? []
|
|
2708
2043
|
: await Promise.allSettled([this.endpointRefresh]);
|
|
@@ -2711,6 +2046,9 @@ export class LocalCodexGatewayProvider {
|
|
|
2711
2046
|
await Promise.allSettled([
|
|
2712
2047
|
...this.routeRecoveries.values(),
|
|
2713
2048
|
...this.routeCreations.values(),
|
|
2049
|
+
...(this.observationInFlight === undefined
|
|
2050
|
+
? []
|
|
2051
|
+
: [this.observationInFlight]),
|
|
2714
2052
|
]);
|
|
2715
2053
|
this.drainCallbackQueue();
|
|
2716
2054
|
if (refreshResult.some((result) => result.status === "rejected") ||
|
|
@@ -2728,7 +2066,6 @@ export class LocalCodexGatewayProvider {
|
|
|
2728
2066
|
this.closed = true;
|
|
2729
2067
|
this.closing = false;
|
|
2730
2068
|
this.callbackQueue.length = 0;
|
|
2731
|
-
this.pendingEndpointAttestation = undefined;
|
|
2732
2069
|
this.pendingEndpointRefreshEvent = undefined;
|
|
2733
2070
|
this.pendingEndpointRefreshSelectorClaimed = false;
|
|
2734
2071
|
this.trackedRoutes.clear();
|
|
@@ -2741,13 +2078,12 @@ export class LocalCodexGatewayProvider {
|
|
|
2741
2078
|
this.closing ||
|
|
2742
2079
|
this.closed ||
|
|
2743
2080
|
this.endpointUnavailable ||
|
|
2744
|
-
this.endpointRefresh !== undefined
|
|
2745
|
-
!this.compatibilityAttested) {
|
|
2081
|
+
this.endpointRefresh !== undefined) {
|
|
2746
2082
|
throw new BridgeError("CODEX_PROVIDER_UNAVAILABLE", "The local Codex provider is unavailable.", true);
|
|
2747
2083
|
}
|
|
2748
2084
|
}
|
|
2749
|
-
async ensureRoute(threadId, queueInitialObservation = true,
|
|
2750
|
-
if (
|
|
2085
|
+
async ensureRoute(threadId, queueInitialObservation = true, allowPendingEndpointActivation = false) {
|
|
2086
|
+
if (allowPendingEndpointActivation) {
|
|
2751
2087
|
if (!this.initialized || this.closing || this.closed) {
|
|
2752
2088
|
throw new BridgeError("CODEX_PROVIDER_UNAVAILABLE", "The local Codex provider is unavailable.", true);
|
|
2753
2089
|
}
|
|
@@ -2758,7 +2094,7 @@ export class LocalCodexGatewayProvider {
|
|
|
2758
2094
|
const pendingRelease = this.routeReleases.get(threadId);
|
|
2759
2095
|
if (pendingRelease !== undefined) {
|
|
2760
2096
|
await pendingRelease;
|
|
2761
|
-
return await this.ensureRoute(threadId, queueInitialObservation,
|
|
2097
|
+
return await this.ensureRoute(threadId, queueInitialObservation, allowPendingEndpointActivation);
|
|
2762
2098
|
}
|
|
2763
2099
|
const existing = this.routes.get(threadId);
|
|
2764
2100
|
if (existing !== undefined) {
|
|
@@ -2770,7 +2106,7 @@ export class LocalCodexGatewayProvider {
|
|
|
2770
2106
|
// a fresh one. The identity-checked release lets automatic recovery and
|
|
2771
2107
|
// an explicit selector join the same cleanup safely.
|
|
2772
2108
|
await this.releaseRouteInternal(threadId);
|
|
2773
|
-
return await this.ensureRoute(threadId, queueInitialObservation,
|
|
2109
|
+
return await this.ensureRoute(threadId, queueInitialObservation, allowPendingEndpointActivation);
|
|
2774
2110
|
}
|
|
2775
2111
|
const pending = this.routeCreations.get(threadId);
|
|
2776
2112
|
if (pending !== undefined)
|
|
@@ -2780,14 +2116,12 @@ export class LocalCodexGatewayProvider {
|
|
|
2780
2116
|
}
|
|
2781
2117
|
const admittedFactory = this.factory;
|
|
2782
2118
|
const creation = (async () => {
|
|
2783
|
-
const route = await this.createRoute(threadId, admittedFactory, queueInitialObservation
|
|
2784
|
-
? this.compatibilityAttested
|
|
2785
|
-
: codexFactoryWritesEnabled(admittedFactory, this.pendingEndpointAttestation, this.latestWriteCompatibilityProbeObservation(admittedFactory.endpointGeneration)?.outcome === "pass"));
|
|
2119
|
+
const route = await this.createRoute(threadId, admittedFactory, queueInitialObservation);
|
|
2786
2120
|
if (this.closing || this.closed) {
|
|
2787
2121
|
await this.closeRoute(route);
|
|
2788
2122
|
throw new BridgeError("CODEX_PROVIDER_UNAVAILABLE", "The local Codex provider closed during route creation.");
|
|
2789
2123
|
}
|
|
2790
|
-
if (!
|
|
2124
|
+
if (!allowPendingEndpointActivation &&
|
|
2791
2125
|
(this.endpointUnavailable ||
|
|
2792
2126
|
this.endpointRefresh !== undefined ||
|
|
2793
2127
|
admittedFactory !== this.factory ||
|
|
@@ -2806,15 +2140,14 @@ export class LocalCodexGatewayProvider {
|
|
|
2806
2140
|
this.routeCreations.delete(threadId);
|
|
2807
2141
|
}
|
|
2808
2142
|
}
|
|
2809
|
-
async createRoute(threadId, factory, queueInitialObservation
|
|
2143
|
+
async createRoute(threadId, factory, queueInitialObservation) {
|
|
2810
2144
|
let transport;
|
|
2811
2145
|
let connector;
|
|
2812
2146
|
const generation = Symbol(threadId);
|
|
2813
2147
|
try {
|
|
2814
2148
|
transport = await factory.connectTransport();
|
|
2815
2149
|
connector = await CodexAppServerConnector.connect({
|
|
2816
|
-
compatibility:
|
|
2817
|
-
writesEnabled,
|
|
2150
|
+
compatibility: { endpointGeneration: factory.endpointGeneration },
|
|
2818
2151
|
route: {
|
|
2819
2152
|
endpointGeneration: factory.endpointGeneration,
|
|
2820
2153
|
threadId,
|
|
@@ -2885,6 +2218,49 @@ export class LocalCodexGatewayProvider {
|
|
|
2885
2218
|
this.scheduleRouteRecovery(threadId);
|
|
2886
2219
|
}
|
|
2887
2220
|
}
|
|
2221
|
+
scheduleCodexObservation() {
|
|
2222
|
+
if (this.closed ||
|
|
2223
|
+
this.closing ||
|
|
2224
|
+
this.trackedRoutes.size === 0 ||
|
|
2225
|
+
this.observationTimer !== undefined ||
|
|
2226
|
+
this.observationInFlight !== undefined) {
|
|
2227
|
+
return;
|
|
2228
|
+
}
|
|
2229
|
+
const timer = setTimeout(() => {
|
|
2230
|
+
if (this.observationTimer !== timer)
|
|
2231
|
+
return;
|
|
2232
|
+
this.observationTimer = undefined;
|
|
2233
|
+
const observation = this.refreshCodexObservations().catch(() => {
|
|
2234
|
+
// Unexpected provider-local observation failures must not kill re-arm.
|
|
2235
|
+
});
|
|
2236
|
+
this.observationInFlight = observation;
|
|
2237
|
+
void observation.finally(() => {
|
|
2238
|
+
if (this.observationInFlight === observation) {
|
|
2239
|
+
this.observationInFlight = undefined;
|
|
2240
|
+
}
|
|
2241
|
+
this.scheduleCodexObservation();
|
|
2242
|
+
});
|
|
2243
|
+
}, this.observationPollMs);
|
|
2244
|
+
timer.unref();
|
|
2245
|
+
this.observationTimer = timer;
|
|
2246
|
+
}
|
|
2247
|
+
async refreshCodexObservations() {
|
|
2248
|
+
for (const [threadId, route] of [...this.routes]) {
|
|
2249
|
+
if (this.closed ||
|
|
2250
|
+
this.closing ||
|
|
2251
|
+
!this.trackedRoutes.has(threadId) ||
|
|
2252
|
+
route.connector.observation().requestInFlight) {
|
|
2253
|
+
continue;
|
|
2254
|
+
}
|
|
2255
|
+
try {
|
|
2256
|
+
await route.connector.observeLoadedThread(route.connector.guard());
|
|
2257
|
+
}
|
|
2258
|
+
catch {
|
|
2259
|
+
// Connector events carry the provider-local failure evidence. One
|
|
2260
|
+
// route's failed observation must not stop later routes or the timer.
|
|
2261
|
+
}
|
|
2262
|
+
}
|
|
2263
|
+
}
|
|
2888
2264
|
scheduleRouteRecovery(threadId) {
|
|
2889
2265
|
if (this.closed ||
|
|
2890
2266
|
this.closing ||
|
|
@@ -2919,12 +2295,11 @@ export class LocalCodexGatewayProvider {
|
|
|
2919
2295
|
}
|
|
2920
2296
|
try {
|
|
2921
2297
|
if (this.endpointUnavailable) {
|
|
2922
|
-
if (this.
|
|
2298
|
+
if (this.pendingEndpointRefreshEvent !== undefined) {
|
|
2923
2299
|
throw new BridgeError("CODEX_ENDPOINT_REFRESH_PENDING", "The replacement Codex endpoint is awaiting controller activation.", true);
|
|
2924
2300
|
}
|
|
2925
2301
|
const refreshed = await this.refreshEndpoint("callback");
|
|
2926
|
-
if (refreshed?.event.
|
|
2927
|
-
refreshed.event.routes.some((route) => route.routeHandle === threadId)) {
|
|
2302
|
+
if (refreshed?.event.routes.some((route) => route.routeHandle === threadId)) {
|
|
2928
2303
|
this.routesRequiringRecovery.delete(threadId);
|
|
2929
2304
|
this.routeRecoveryAttempts.delete(threadId);
|
|
2930
2305
|
return;
|
|
@@ -2954,8 +2329,7 @@ export class LocalCodexGatewayProvider {
|
|
|
2954
2329
|
if (this.isEndpointGenerationChanged(error)) {
|
|
2955
2330
|
this.endpointUnavailable = true;
|
|
2956
2331
|
const refreshed = await this.refreshEndpoint("callback").catch(() => undefined);
|
|
2957
|
-
if (refreshed?.event.
|
|
2958
|
-
refreshed.event.routes.some((route) => route.routeHandle === threadId)) {
|
|
2332
|
+
if (refreshed?.event.routes.some((route) => route.routeHandle === threadId)) {
|
|
2959
2333
|
this.routesRequiringRecovery.delete(threadId);
|
|
2960
2334
|
this.routeRecoveryAttempts.delete(threadId);
|
|
2961
2335
|
return;
|
|
@@ -2986,7 +2360,6 @@ export class LocalCodexGatewayProvider {
|
|
|
2986
2360
|
const event = this.pendingEndpointRefreshEvent;
|
|
2987
2361
|
if (event === undefined ||
|
|
2988
2362
|
this.pendingEndpointRefreshSelectorClaimed ||
|
|
2989
|
-
this.pendingEndpointAttestation === undefined ||
|
|
2990
2363
|
event.current.endpointGeneration !== this.factory.endpointGeneration) {
|
|
2991
2364
|
return undefined;
|
|
2992
2365
|
}
|
|
@@ -3033,40 +2406,7 @@ export class LocalCodexGatewayProvider {
|
|
|
3033
2406
|
try {
|
|
3034
2407
|
const resolved = await this.resolveEndpointRefreshCandidate(refreshFactory, previousFactory, previous);
|
|
3035
2408
|
candidate = resolved.factory;
|
|
3036
|
-
const { attestation, identity: candidateIdentity } = resolved;
|
|
3037
2409
|
await this.retireEndpointGeneration(previousFactory, previous.endpointGeneration);
|
|
3038
|
-
if (attestation.tier === "incompatible") {
|
|
3039
|
-
await candidate.close();
|
|
3040
|
-
candidate = undefined;
|
|
3041
|
-
this.compatibilityAttested = false;
|
|
3042
|
-
this.pendingEndpointAttestation = undefined;
|
|
3043
|
-
this.pendingEndpointRefreshEvent = undefined;
|
|
3044
|
-
this.pendingEndpointRefreshSelectorClaimed = false;
|
|
3045
|
-
for (const routeHandle of this.trackedRoutes) {
|
|
3046
|
-
this.routesRequiringRecovery.add(routeHandle);
|
|
3047
|
-
}
|
|
3048
|
-
if (this.closing || this.closed)
|
|
3049
|
-
return undefined;
|
|
3050
|
-
const result = {
|
|
3051
|
-
event: {
|
|
3052
|
-
outcome: "incompatible",
|
|
3053
|
-
previous,
|
|
3054
|
-
candidate: candidateIdentity,
|
|
3055
|
-
attestation,
|
|
3056
|
-
},
|
|
3057
|
-
delivery: "callback",
|
|
3058
|
-
selectorClaimed: true,
|
|
3059
|
-
published: false,
|
|
3060
|
-
};
|
|
3061
|
-
result.published = true;
|
|
3062
|
-
this.emitEndpointRefresh(result.event);
|
|
3063
|
-
for (const routeHandle of this.routesRequiringRecovery) {
|
|
3064
|
-
if (this.trackedRoutes.has(routeHandle)) {
|
|
3065
|
-
this.scheduleRouteRecovery(routeHandle);
|
|
3066
|
-
}
|
|
3067
|
-
}
|
|
3068
|
-
return result;
|
|
3069
|
-
}
|
|
3070
2410
|
if (this.closing || this.closed) {
|
|
3071
2411
|
await candidate.close();
|
|
3072
2412
|
candidate = undefined;
|
|
@@ -3079,7 +2419,7 @@ export class LocalCodexGatewayProvider {
|
|
|
3079
2419
|
if (this.closing || this.closed)
|
|
3080
2420
|
break;
|
|
3081
2421
|
try {
|
|
3082
|
-
const route = await this.createRoute(routeHandle, replacementFactory, false
|
|
2422
|
+
const route = await this.createRoute(routeHandle, replacementFactory, false);
|
|
3083
2423
|
if (this.closing || this.closed) {
|
|
3084
2424
|
await this.closeRoute(route);
|
|
3085
2425
|
break;
|
|
@@ -3105,9 +2445,7 @@ export class LocalCodexGatewayProvider {
|
|
|
3105
2445
|
this.factory = replacementFactory;
|
|
3106
2446
|
candidate = undefined;
|
|
3107
2447
|
this.retiredEndpointGeneration = undefined;
|
|
3108
|
-
this.compatibilityAttested = false;
|
|
3109
2448
|
this.endpointUnavailable = true;
|
|
3110
|
-
this.pendingEndpointAttestation = attestation;
|
|
3111
2449
|
for (const staged of stagedRoutes) {
|
|
3112
2450
|
this.routes.set(staged.routeHandle, staged.route);
|
|
3113
2451
|
this.routesRequiringRecovery.delete(staged.routeHandle);
|
|
@@ -3119,20 +2457,16 @@ export class LocalCodexGatewayProvider {
|
|
|
3119
2457
|
}
|
|
3120
2458
|
const result = {
|
|
3121
2459
|
event: {
|
|
3122
|
-
outcome: "compatible",
|
|
3123
2460
|
previous,
|
|
3124
2461
|
current: this.identity,
|
|
3125
|
-
attestation,
|
|
3126
2462
|
routes: refreshedRoutes,
|
|
3127
2463
|
},
|
|
3128
2464
|
delivery: this.endpointRefreshDelivery ?? "callback",
|
|
3129
2465
|
selectorClaimed: false,
|
|
3130
2466
|
published: false,
|
|
3131
2467
|
};
|
|
3132
|
-
|
|
3133
|
-
|
|
3134
|
-
this.pendingEndpointRefreshSelectorClaimed = false;
|
|
3135
|
-
}
|
|
2468
|
+
this.pendingEndpointRefreshEvent = result.event;
|
|
2469
|
+
this.pendingEndpointRefreshSelectorClaimed = false;
|
|
3136
2470
|
if (result.delivery === "callback") {
|
|
3137
2471
|
result.selectorClaimed = true;
|
|
3138
2472
|
result.published = true;
|
|
@@ -3163,10 +2497,7 @@ export class LocalCodexGatewayProvider {
|
|
|
3163
2497
|
let candidate;
|
|
3164
2498
|
try {
|
|
3165
2499
|
candidate = await refreshFactory();
|
|
3166
|
-
|
|
3167
|
-
validateCodexFactory(candidate, {
|
|
3168
|
-
allowUnsupportedVersion: versionFailure !== undefined,
|
|
3169
|
-
});
|
|
2500
|
+
validateCodexFactory(candidate);
|
|
3170
2501
|
if (candidate.hostId !== previous.hostId ||
|
|
3171
2502
|
candidate.protocol !== previousFactory.protocol) {
|
|
3172
2503
|
throw new BridgeError("CODEX_FACTORY_ATTESTATION_INVALID", "The replacement Codex factory moved outside its pinned boundary.");
|
|
@@ -3176,41 +2507,12 @@ export class LocalCodexGatewayProvider {
|
|
|
3176
2507
|
if (candidate !== previousFactory)
|
|
3177
2508
|
await candidate.close();
|
|
3178
2509
|
candidate = undefined;
|
|
3179
|
-
if (this.endpointUnavailable &&
|
|
3180
|
-
this.compatibilityFailureCode === undefined &&
|
|
3181
|
-
!this.factory.writableReady &&
|
|
3182
|
-
this.factory.writeCompatibility === null) {
|
|
3183
|
-
throw new BridgeError("CODEX_MONITOR_ONLY", "This Codex App Server remains monitor-only until a write-attested generation is available.");
|
|
3184
|
-
}
|
|
3185
2510
|
if (attempt + 1 < MAX_CODEX_ENDPOINT_REFRESH_CANDIDATES) {
|
|
3186
2511
|
continue;
|
|
3187
2512
|
}
|
|
3188
2513
|
throw new BridgeError("CODEX_ENDPOINT_GENERATION_CHURN", "The Codex endpoint did not stabilize within its bounded refresh window.", true);
|
|
3189
2514
|
}
|
|
3190
|
-
|
|
3191
|
-
// outside the tested major is classified from OS-attested factory
|
|
3192
|
-
// evidence alone; no connector or transport may touch that protocol.
|
|
3193
|
-
const probes = versionFailure === undefined
|
|
3194
|
-
? await this.runCompatibilityProbesFor(candidate)
|
|
3195
|
-
: compatibilityProbeNames.codex.map((name, index) => index === 0
|
|
3196
|
-
? passedProbe(name)
|
|
3197
|
-
: failedProbe(name, versionFailure));
|
|
3198
|
-
const attestation = evaluateCompatibilityAttestation({
|
|
3199
|
-
surface: "codex",
|
|
3200
|
-
version: candidate.appServerVersion,
|
|
3201
|
-
checkedAt: this.now().toISOString(),
|
|
3202
|
-
certifiedVersions: certifiedCompatibilityVersions.codex,
|
|
3203
|
-
probes,
|
|
3204
|
-
});
|
|
3205
|
-
return {
|
|
3206
|
-
factory: candidate,
|
|
3207
|
-
identity: {
|
|
3208
|
-
provider: "codex",
|
|
3209
|
-
hostId: previous.hostId,
|
|
3210
|
-
endpointGeneration: candidate.endpointGeneration,
|
|
3211
|
-
},
|
|
3212
|
-
attestation,
|
|
3213
|
-
};
|
|
2515
|
+
return { factory: candidate };
|
|
3214
2516
|
}
|
|
3215
2517
|
catch (error) {
|
|
3216
2518
|
if (candidate !== undefined && candidate !== previousFactory) {
|
|
@@ -3266,7 +2568,6 @@ export class LocalCodexGatewayProvider {
|
|
|
3266
2568
|
return;
|
|
3267
2569
|
let event = result.event;
|
|
3268
2570
|
if (staleRouteHandle !== undefined &&
|
|
3269
|
-
event.outcome === "compatible" &&
|
|
3270
2571
|
event.routes.some((route) => route.routeHandle === staleRouteHandle)) {
|
|
3271
2572
|
event = {
|
|
3272
2573
|
...event,
|
|
@@ -3280,9 +2581,7 @@ export class LocalCodexGatewayProvider {
|
|
|
3280
2581
|
}
|
|
3281
2582
|
publishEndpointRefresh(event) {
|
|
3282
2583
|
this.emitEndpointRefresh(event);
|
|
3283
|
-
|
|
3284
|
-
this.armEndpointActivationRetry(event);
|
|
3285
|
-
}
|
|
2584
|
+
this.armEndpointActivationRetry(event);
|
|
3286
2585
|
}
|
|
3287
2586
|
armEndpointActivationRetry(event) {
|
|
3288
2587
|
this.clearEndpointActivationRetry();
|
|
@@ -3295,8 +2594,7 @@ export class LocalCodexGatewayProvider {
|
|
|
3295
2594
|
this.closing ||
|
|
3296
2595
|
this.closed ||
|
|
3297
2596
|
pending.attempt >= 3 ||
|
|
3298
|
-
|
|
3299
|
-
this.pendingEndpointAttestation === undefined)) {
|
|
2597
|
+
this.pendingEndpointRefreshEvent !== pending.event) {
|
|
3300
2598
|
this.clearEndpointActivationRetry();
|
|
3301
2599
|
return;
|
|
3302
2600
|
}
|
|
@@ -3308,8 +2606,7 @@ export class LocalCodexGatewayProvider {
|
|
|
3308
2606
|
const current = this.endpointActivationRetry;
|
|
3309
2607
|
if (current === undefined || current.event !== pending.event)
|
|
3310
2608
|
return;
|
|
3311
|
-
if (current.event
|
|
3312
|
-
this.pendingEndpointAttestation === undefined) {
|
|
2609
|
+
if (this.pendingEndpointRefreshEvent !== current.event) {
|
|
3313
2610
|
this.clearEndpointActivationRetry();
|
|
3314
2611
|
return;
|
|
3315
2612
|
}
|