agent-embassy 1.6.0 → 1.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +31 -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.en.d.ts +1 -1
- package/dist/src/gateway/cli-copy.en.js +2 -2
- package/dist/src/gateway/cli-copy.zh-CN.d.ts +1 -1
- package/dist/src/gateway/cli-copy.zh-CN.js +2 -2
- package/dist/src/gateway/cli.d.ts +1 -1
- package/dist/src/gateway/cli.js +26 -5
- 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 -597
- package/dist/src/gateway/codex-app-server.js.map +1 -1
- package/dist/src/gateway/codex-local-transport.d.ts +8 -26
- package/dist/src/gateway/codex-local-transport.js +26 -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 +89 -57
- 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 +14 -44
- package/dist/src/gateway/dashboard-copy.en.js +37 -67
- package/dist/src/gateway/dashboard-copy.en.js.map +1 -1
- package/dist/src/gateway/dashboard-copy.js +14 -44
- package/dist/src/gateway/dashboard-copy.js.map +1 -1
- package/dist/src/gateway/dashboard-copy.zh-CN.d.ts +14 -44
- package/dist/src/gateway/dashboard-copy.zh-CN.js +37 -67
- package/dist/src/gateway/dashboard-copy.zh-CN.js.map +1 -1
- package/dist/src/gateway/dashboard-model.d.ts +38 -76
- package/dist/src/gateway/dashboard-model.js +158 -256
- 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 +75 -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 +166 -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 +12 -84
- package/dist/src/gateway/providers.js +69 -806
- package/dist/src/gateway/providers.js.map +1 -1
- package/dist/src/gateway/server.d.ts +8 -9
- package/dist/src/gateway/server.js +51 -99
- package/dist/src/gateway/server.js.map +1 -1
- package/dist/src/gateway/service.d.ts +22 -49
- package/dist/src/gateway/service.js +196 -432
- package/dist/src/gateway/service.js.map +1 -1
- package/dist/src/gateway/store.d.ts +20 -29
- package/dist/src/gateway/store.js +281 -827
- package/dist/src/gateway/store.js.map +1 -1
- package/dist/src/gateway/types.d.ts +55 -55
- package/dist/src/gateway/types.js +49 -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}$/;
|
|
@@ -24,13 +23,6 @@ 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;
|
|
26
25
|
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
26
|
const CLAUDE_CLEAN_PREWRITE_RETRY_CODES = new Set([
|
|
35
27
|
"CLAUDE_PEER_TARGET_UNKNOWN",
|
|
36
28
|
"CLAUDE_PEER_TARGET_STALE",
|
|
@@ -86,105 +78,6 @@ function claudeCleanPrewriteResult(error) {
|
|
|
86
78
|
: "CLAUDE_DISPATCH_PREWRITE_FAILED",
|
|
87
79
|
};
|
|
88
80
|
}
|
|
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
81
|
function exactLocalHost(hostId) {
|
|
189
82
|
if ((hostId ?? LOCAL_HOST) !== LOCAL_HOST) {
|
|
190
83
|
throw new BridgeError("GATEWAY_REMOTE_PROVIDER_DISABLED", "This provider is restricted to the exact local host boundary.");
|
|
@@ -226,18 +119,6 @@ function invokeCallback(operation) {
|
|
|
226
119
|
}
|
|
227
120
|
}
|
|
228
121
|
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
122
|
validateClaudeRuntimePathEvidence(runtime);
|
|
242
123
|
}
|
|
243
124
|
function validateClaudeRuntimePathEvidence(runtime) {
|
|
@@ -286,135 +167,17 @@ function isClaudeRegistryAvailabilityError(error) {
|
|
|
286
167
|
error.code === "EPERM" ||
|
|
287
168
|
error.code === "UNSAFE_PEER_DIRECTORY");
|
|
288
169
|
}
|
|
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
170
|
export class LocalClaudeGatewayProvider {
|
|
408
171
|
identity;
|
|
409
172
|
protocol = "claude-peer";
|
|
410
173
|
protocolVersion = `${CLAUDE_PEER_COMPATIBILITY.peerProtocol}`;
|
|
411
174
|
peer;
|
|
412
|
-
runtimeVersion;
|
|
413
175
|
maxPending;
|
|
414
176
|
discoveryPollMs;
|
|
415
177
|
now;
|
|
416
178
|
selectedStateRoots = new Map();
|
|
417
179
|
nativeHelpers;
|
|
180
|
+
nativeHelperSourceProvider;
|
|
418
181
|
discovered = new Map();
|
|
419
182
|
selected = new Map();
|
|
420
183
|
/** Exact live same-UID sessions observed through native inbound frames. */
|
|
@@ -448,25 +211,12 @@ export class LocalClaudeGatewayProvider {
|
|
|
448
211
|
retiredNativeCodexListener;
|
|
449
212
|
/** Fences discovery callbacks and monitor restarts across listener rotation. */
|
|
450
213
|
nativeCodexSuccessionFreeze;
|
|
451
|
-
/**
|
|
452
|
-
|
|
453
|
-
compatibilityFailureCode;
|
|
214
|
+
/** External registry trust/availability failures degrade Claude only. */
|
|
215
|
+
unavailableCode;
|
|
454
216
|
initialized = false;
|
|
455
217
|
closed = false;
|
|
456
218
|
constructor(options) {
|
|
457
219
|
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
220
|
if (options.locale !== undefined && !isDashboardLocale(options.locale)) {
|
|
471
221
|
throw new BridgeError("DASHBOARD_LOCALE_UNSUPPORTED", "The Claude provider notice locale is unsupported.");
|
|
472
222
|
}
|
|
@@ -476,15 +226,12 @@ export class LocalClaudeGatewayProvider {
|
|
|
476
226
|
hostId,
|
|
477
227
|
endpointGeneration: STABLE_CLAUDE_ENDPOINT_GENERATION,
|
|
478
228
|
};
|
|
479
|
-
this.runtimeVersion = compatibilityVersion;
|
|
480
229
|
this.maxPending = positiveBounded(options.maxPendingMessages, MAX_CLAUDE_PENDING, 4_096);
|
|
481
230
|
this.discoveryPollMs = positiveBounded(options.discoveryPollMs, DEFAULT_CLAUDE_DISCOVERY_POLL_MS, 30_000);
|
|
482
231
|
this.now = options.now ?? Date.now;
|
|
483
232
|
const protocolRuntime = {
|
|
484
233
|
claudeExecutable: options.runtime.claudeExecutable,
|
|
485
|
-
claudeCodeVersion: options.runtime.claudeCodeVersion
|
|
486
|
-
? options.runtime.launcherVersionEvidence
|
|
487
|
-
: options.runtime.claudeCodeVersion,
|
|
234
|
+
claudeCodeVersion: options.runtime.claudeCodeVersion,
|
|
488
235
|
sessionsDir: options.runtime.sessionsDir,
|
|
489
236
|
socketDir: options.runtime.socketDir,
|
|
490
237
|
};
|
|
@@ -506,60 +253,19 @@ export class LocalClaudeGatewayProvider {
|
|
|
506
253
|
deliveryNotices: options.deliveryNotices ?? "merged",
|
|
507
254
|
maxPendingMessages: this.maxPending,
|
|
508
255
|
maxHelpers: positiveBounded(options.nativeHelpers.maxHelpers, 256, 128),
|
|
509
|
-
callbacks: () => this.
|
|
256
|
+
callbacks: () => this.callbacks,
|
|
510
257
|
...(options.nativeHelpers.factory === undefined
|
|
511
258
|
? {}
|
|
512
259
|
: { factory: options.nativeHelpers.factory }),
|
|
513
260
|
});
|
|
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
|
-
}
|
|
261
|
+
this.nativeHelperSourceProvider = options.nativeHelperSourceProvider;
|
|
547
262
|
}
|
|
548
263
|
latestRegistryObservation() {
|
|
549
264
|
return this.registryObservation;
|
|
550
265
|
}
|
|
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;
|
|
266
|
+
activateEndpointGeneration(endpointGeneration) {
|
|
267
|
+
if (endpointGeneration !== this.identity.endpointGeneration) {
|
|
268
|
+
throw new BridgeError("CLAUDE_ENDPOINT_ACTIVATION_MISMATCH", "Only the exact current Claude endpoint generation can be activated.");
|
|
563
269
|
}
|
|
564
270
|
}
|
|
565
271
|
async initialize(callbacks) {
|
|
@@ -570,17 +276,9 @@ export class LocalClaudeGatewayProvider {
|
|
|
570
276
|
throw new BridgeError("CLAUDE_PROVIDER_ALREADY_INITIALIZED", "The local Claude provider can be initialized only once.");
|
|
571
277
|
}
|
|
572
278
|
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
279
|
if (this.nativeHelpers !== undefined) {
|
|
582
280
|
this.initialized = true;
|
|
583
|
-
return { health: "healthy"
|
|
281
|
+
return { health: "healthy" };
|
|
584
282
|
}
|
|
585
283
|
try {
|
|
586
284
|
let listenerGeneration;
|
|
@@ -615,18 +313,16 @@ export class LocalClaudeGatewayProvider {
|
|
|
615
313
|
}
|
|
616
314
|
this.activeNativeCodexListener = listenerGeneration;
|
|
617
315
|
this.initialized = true;
|
|
618
|
-
return { health: "healthy"
|
|
316
|
+
return { health: "healthy" };
|
|
619
317
|
}
|
|
620
318
|
catch (error) {
|
|
621
319
|
if (error instanceof BridgeError &&
|
|
622
320
|
error.code === "UNSAFE_PEER_DIRECTORY") {
|
|
623
|
-
this.
|
|
624
|
-
this.compatibilityFailureCode = "CLAUDE_REGISTRY_UNAVAILABLE";
|
|
321
|
+
this.unavailableCode = "CLAUDE_REGISTRY_UNAVAILABLE";
|
|
625
322
|
this.registryObservation = unavailableClaudeRegistryObservation();
|
|
626
323
|
this.initialized = true;
|
|
627
324
|
return {
|
|
628
325
|
health: "degraded",
|
|
629
|
-
compatibility: "incompatible",
|
|
630
326
|
safeErrorCode: "CLAUDE_REGISTRY_UNAVAILABLE",
|
|
631
327
|
};
|
|
632
328
|
}
|
|
@@ -671,12 +367,16 @@ export class LocalClaudeGatewayProvider {
|
|
|
671
367
|
}
|
|
672
368
|
return { routeHandle: resolved.targetId };
|
|
673
369
|
}
|
|
674
|
-
async
|
|
370
|
+
async advertiseNativeSourcePeer(input) {
|
|
675
371
|
if (this.nativeHelpers !== undefined) {
|
|
676
372
|
this.assertReady();
|
|
677
373
|
await this.nativeHelpers.advertise(input);
|
|
678
374
|
return;
|
|
679
375
|
}
|
|
376
|
+
if (input.sourceProvider !== "codex" &&
|
|
377
|
+
input.sourceProvider !== this.nativeHelperSourceProvider) {
|
|
378
|
+
throw new BridgeError("CLAUDE_NATIVE_HELPER_UNAVAILABLE", "A non-Codex source requires a supervised native Claude helper.", true);
|
|
379
|
+
}
|
|
680
380
|
const registration = this.nativeCodexRegistration(input);
|
|
681
381
|
await this.serializeNativeCodexRegistration(async () => {
|
|
682
382
|
this.assertReady();
|
|
@@ -763,7 +463,7 @@ export class LocalClaudeGatewayProvider {
|
|
|
763
463
|
this.retiredNativeCodexListener !== undefined) {
|
|
764
464
|
throw new BridgeError("CODEX_PEER_SUCCESSION_CAPACITY", "Only one distinct prepared or retired Codex generation is allowed.");
|
|
765
465
|
}
|
|
766
|
-
const registration = this.nativeCodexRegistration(input);
|
|
466
|
+
const registration = this.nativeCodexRegistration({ ...input, sourceProvider: "codex" });
|
|
767
467
|
this.nativeCodexPreparationInFlight = input.generation;
|
|
768
468
|
let prepared;
|
|
769
469
|
try {
|
|
@@ -1027,7 +727,7 @@ export class LocalClaudeGatewayProvider {
|
|
|
1027
727
|
}
|
|
1028
728
|
return purged;
|
|
1029
729
|
}
|
|
1030
|
-
async
|
|
730
|
+
async unadvertiseNativeSourcePeer(alias) {
|
|
1031
731
|
if (this.nativeHelpers !== undefined) {
|
|
1032
732
|
await this.nativeHelpers.unadvertise(alias);
|
|
1033
733
|
return;
|
|
@@ -1043,7 +743,7 @@ export class LocalClaudeGatewayProvider {
|
|
|
1043
743
|
active.provisionalIngressForwarded = false;
|
|
1044
744
|
});
|
|
1045
745
|
}
|
|
1046
|
-
async
|
|
746
|
+
async updateNativeSourcePeerStatus(alias, status) {
|
|
1047
747
|
if (this.nativeHelpers !== undefined) {
|
|
1048
748
|
await this.nativeHelpers.updateStatus(alias, status);
|
|
1049
749
|
return;
|
|
@@ -1105,10 +805,10 @@ export class LocalClaudeGatewayProvider {
|
|
|
1105
805
|
active.inboundQuiesced = true;
|
|
1106
806
|
}
|
|
1107
807
|
async dispatch(input) {
|
|
1108
|
-
if (this.
|
|
808
|
+
if (this.unavailableCode !== undefined) {
|
|
1109
809
|
return {
|
|
1110
810
|
state: "failed",
|
|
1111
|
-
safeErrorCode: this.
|
|
811
|
+
safeErrorCode: this.unavailableCode,
|
|
1112
812
|
};
|
|
1113
813
|
}
|
|
1114
814
|
if (this.nativeHelpers !== undefined) {
|
|
@@ -1182,7 +882,8 @@ export class LocalClaudeGatewayProvider {
|
|
|
1182
882
|
return { state: "failed", safeErrorCode: "CLAUDE_ROUTE_UNAVAILABLE" };
|
|
1183
883
|
}
|
|
1184
884
|
}
|
|
1185
|
-
if (activeListener.registration?.alias !== input.sourceAlias
|
|
885
|
+
if (activeListener.registration?.alias !== input.sourceAlias ||
|
|
886
|
+
activeListener.registration.sourceProvider !== input.sourceProvider) {
|
|
1186
887
|
return { state: "failed", safeErrorCode: "CLAUDE_ROUTE_UNAVAILABLE" };
|
|
1187
888
|
}
|
|
1188
889
|
if (this.activeNativeCodexListener !== activeListener ||
|
|
@@ -1201,7 +902,8 @@ export class LocalClaudeGatewayProvider {
|
|
|
1201
902
|
let content;
|
|
1202
903
|
try {
|
|
1203
904
|
content = composeProvenanceEnvelope({
|
|
1204
|
-
|
|
905
|
+
sourceProvider: input.sourceProvider,
|
|
906
|
+
recipientProvider: this.identity.provider,
|
|
1205
907
|
sourceAlias: input.sourceAlias,
|
|
1206
908
|
targetAlias: input.targetAlias,
|
|
1207
909
|
conversationId: input.conversationId,
|
|
@@ -1392,7 +1094,7 @@ export class LocalClaudeGatewayProvider {
|
|
|
1392
1094
|
}
|
|
1393
1095
|
}
|
|
1394
1096
|
assertReady() {
|
|
1395
|
-
if (!this.initialized || this.closed || this.
|
|
1097
|
+
if (!this.initialized || this.closed || this.unavailableCode !== undefined) {
|
|
1396
1098
|
throw new BridgeError("CLAUDE_PROVIDER_UNAVAILABLE", "The local Claude provider is not available.", true);
|
|
1397
1099
|
}
|
|
1398
1100
|
}
|
|
@@ -1407,13 +1109,14 @@ export class LocalClaudeGatewayProvider {
|
|
|
1407
1109
|
throw new BridgeError("INVALID_CODEX_PEER_ALIAS", "The native Codex peer alias must target this host.");
|
|
1408
1110
|
}
|
|
1409
1111
|
const name = input.alias.slice(0, -suffix.length);
|
|
1410
|
-
|
|
1411
|
-
|
|
1112
|
+
const prefix = gatewayRegistrationIngressPrefixes[input.sourceProvider];
|
|
1113
|
+
if (prefix === undefined || !name.startsWith(prefix) || !NATIVE_CLAUDE_NAME.test(name)) {
|
|
1114
|
+
throw new BridgeError("INVALID_CODEX_PEER_ALIAS", "The native source peer alias must use its provider prefix.");
|
|
1412
1115
|
}
|
|
1413
1116
|
if (!path.isAbsolute(input.cwd) || input.cwd.includes("\0")) {
|
|
1414
1117
|
throw new BridgeError("INVALID_CODEX_PEER_CWD", "A native Codex peer requires an absolute working directory.");
|
|
1415
1118
|
}
|
|
1416
|
-
return { alias: input.alias, cwd: input.cwd, name };
|
|
1119
|
+
return { alias: input.alias, sourceProvider: input.sourceProvider, cwd: input.cwd, name };
|
|
1417
1120
|
}
|
|
1418
1121
|
requireActiveNativeCodexListener() {
|
|
1419
1122
|
this.assertReady();
|
|
@@ -1451,7 +1154,7 @@ export class LocalClaudeGatewayProvider {
|
|
|
1451
1154
|
await this.rejectNativeInboundReceipt(listenerGeneration, message.receiptHandle, diagnosticCode);
|
|
1452
1155
|
}
|
|
1453
1156
|
};
|
|
1454
|
-
if (this.
|
|
1157
|
+
if (this.unavailableCode !== undefined ||
|
|
1455
1158
|
this.closed ||
|
|
1456
1159
|
listenerGeneration.listener.closed ||
|
|
1457
1160
|
!this.ownsNativeCodexListener(listenerGeneration)) {
|
|
@@ -1801,7 +1504,6 @@ export class LocalClaudeGatewayProvider {
|
|
|
1801
1504
|
routeHandle: peer.targetId,
|
|
1802
1505
|
kind: "interactive",
|
|
1803
1506
|
state: claudeRouteState(peer),
|
|
1804
|
-
compatibility: "compatible",
|
|
1805
1507
|
};
|
|
1806
1508
|
this.discovered.set(peer.targetId, row);
|
|
1807
1509
|
rows.push(row);
|
|
@@ -1891,53 +1593,13 @@ export class LocalClaudeGatewayProvider {
|
|
|
1891
1593
|
export function createLocalClaudeGatewayProvider(options) {
|
|
1892
1594
|
return new LocalClaudeGatewayProvider(options);
|
|
1893
1595
|
}
|
|
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));
|
|
1596
|
+
function validateCodexFactory(factory) {
|
|
1933
1597
|
if (factory.hostId !== LOCAL_HOST ||
|
|
1934
1598
|
factory.protocol !== "codex-app-server" ||
|
|
1935
1599
|
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.");
|
|
1600
|
+
factory.appServerVersion.length === 0 ||
|
|
1601
|
+
factory.endpointGeneration.length === 0) {
|
|
1602
|
+
throw new BridgeError("CODEX_FACTORY_ATTESTATION_INVALID", "The local Codex provider requires exact managed endpoint identity.");
|
|
1941
1603
|
}
|
|
1942
1604
|
}
|
|
1943
1605
|
function codexRouteState(observation) {
|
|
@@ -1961,9 +1623,6 @@ function codexRouteSafeCode(observation) {
|
|
|
1961
1623
|
if (codexRouteState(observation) === "stale") {
|
|
1962
1624
|
return "CODEX_ROUTE_STALE";
|
|
1963
1625
|
}
|
|
1964
|
-
if (!observation.writableReady) {
|
|
1965
|
-
return "CODEX_WRITES_DISABLED";
|
|
1966
|
-
}
|
|
1967
1626
|
return undefined;
|
|
1968
1627
|
}
|
|
1969
1628
|
export class LocalCodexGatewayProvider {
|
|
@@ -1997,13 +1656,7 @@ export class LocalCodexGatewayProvider {
|
|
|
1997
1656
|
endpointActivationRetry;
|
|
1998
1657
|
endpointActivationRetryTimer;
|
|
1999
1658
|
callbackDrainFrozen = false;
|
|
2000
|
-
writeProbeObservationsByGeneration = new Map();
|
|
2001
|
-
writeProbeResults = new Map();
|
|
2002
|
-
writeProbeOverflow;
|
|
2003
|
-
compatibilityAttested;
|
|
2004
|
-
compatibilityFailureCode;
|
|
2005
1659
|
endpointUnavailable = false;
|
|
2006
|
-
pendingEndpointAttestation;
|
|
2007
1660
|
pendingEndpointRefreshEvent;
|
|
2008
1661
|
pendingEndpointRefreshSelectorClaimed = false;
|
|
2009
1662
|
initialized = false;
|
|
@@ -2014,7 +1667,6 @@ export class LocalCodexGatewayProvider {
|
|
|
2014
1667
|
this.factory = options.factory;
|
|
2015
1668
|
this.refreshFactory = options.refreshFactory;
|
|
2016
1669
|
exactLocalHost(options.factory.hostId);
|
|
2017
|
-
this.compatibilityAttested = CODEX_APP_SERVER_WRITABLE_VERSIONS.includes(options.factory.appServerVersion);
|
|
2018
1670
|
this.maxCallbacks = positiveBounded(options.maxCallbackEvents, MAX_CODEX_CALLBACKS, 4_096);
|
|
2019
1671
|
this.maxRoutes = positiveBounded(options.maxRoutes, MAX_CODEX_ROUTES, 256);
|
|
2020
1672
|
this.maxReplyBytes = positiveBounded(options.maxReplyBytes, MAX_TRANSIENT_REPLY_BYTES, 1024 * 1024);
|
|
@@ -2043,336 +1695,39 @@ export class LocalCodexGatewayProvider {
|
|
|
2043
1695
|
get protocolVersion() {
|
|
2044
1696
|
return this.factory.protocolVersion;
|
|
2045
1697
|
}
|
|
2046
|
-
|
|
2047
|
-
|
|
2048
|
-
|
|
2049
|
-
|
|
2050
|
-
|
|
2051
|
-
|
|
2052
|
-
(attestation.checkedAt === pending.checkedAt &&
|
|
2053
|
-
attestation.tier === pending.tier &&
|
|
2054
|
-
attestation.safeErrorCode === pending.safeErrorCode &&
|
|
2055
|
-
attestation.probes.length === pending.probes.length &&
|
|
2056
|
-
attestation.probes.every((probe, index) => probe.name === pending.probes[index]?.name &&
|
|
2057
|
-
probe.outcome === pending.probes[index]?.outcome &&
|
|
2058
|
-
probe.safeErrorCode === pending.probes[index]?.safeErrorCode));
|
|
2059
|
-
if (!isCompatibilityAttestation(attestation) ||
|
|
2060
|
-
attestation.surface !== "codex" ||
|
|
2061
|
-
attestation.version !== this.factory.appServerVersion ||
|
|
2062
|
-
!pendingMatches) {
|
|
2063
|
-
throw new BridgeError("CODEX_COMPATIBILITY_ATTESTATION_MISMATCH", "The Codex compatibility attestation does not match this endpoint.");
|
|
2064
|
-
}
|
|
2065
|
-
if (attestation.tier === "incompatible") {
|
|
2066
|
-
this.pendingEndpointAttestation = undefined;
|
|
2067
|
-
this.pendingEndpointRefreshEvent = undefined;
|
|
2068
|
-
this.pendingEndpointRefreshSelectorClaimed = false;
|
|
2069
|
-
this.clearEndpointActivationRetry();
|
|
2070
|
-
this.compatibilityAttested = false;
|
|
2071
|
-
this.endpointUnavailable = true;
|
|
2072
|
-
this.compatibilityFailureCode =
|
|
2073
|
-
attestation.safeErrorCode ?? "CODEX_COMPATIBILITY_CHECK_FAILED";
|
|
2074
|
-
return;
|
|
1698
|
+
activateEndpointGeneration(endpointGeneration) {
|
|
1699
|
+
const event = this.pendingEndpointRefreshEvent;
|
|
1700
|
+
if (event === undefined ||
|
|
1701
|
+
event.current.endpointGeneration !== endpointGeneration ||
|
|
1702
|
+
this.factory.endpointGeneration !== endpointGeneration) {
|
|
1703
|
+
throw new BridgeError("CODEX_ENDPOINT_ACTIVATION_MISMATCH", "Only the exact pending managed Codex generation can be activated.");
|
|
2075
1704
|
}
|
|
2076
|
-
const writableCertified = attestation.tier === "certified" &&
|
|
2077
|
-
CODEX_APP_SERVER_WRITABLE_VERSIONS.includes(attestation.version) &&
|
|
2078
|
-
this.factory.writableReady &&
|
|
2079
|
-
this.factory.writeCompatibility !== null;
|
|
2080
|
-
const schemaObserved = attestation.tier === "schema_attested" &&
|
|
2081
|
-
!this.factory.writableReady &&
|
|
2082
|
-
this.factory.writeCompatibility === null;
|
|
2083
|
-
if ((!writableCertified && !schemaObserved) ||
|
|
2084
|
-
(this.endpointUnavailable && pending === undefined) ||
|
|
2085
|
-
!pendingMatches) {
|
|
2086
|
-
throw new BridgeError("CODEX_COMPATIBILITY_ATTESTATION_MISMATCH", "The Codex compatibility attestation does not match this endpoint.");
|
|
2087
|
-
}
|
|
2088
|
-
this.pendingEndpointAttestation = undefined;
|
|
2089
1705
|
this.pendingEndpointRefreshEvent = undefined;
|
|
2090
1706
|
this.pendingEndpointRefreshSelectorClaimed = false;
|
|
2091
1707
|
this.clearEndpointActivationRetry();
|
|
2092
|
-
this.
|
|
2093
|
-
this.endpointUnavailable = !writableCertified;
|
|
2094
|
-
this.compatibilityFailureCode = undefined;
|
|
1708
|
+
this.endpointUnavailable = false;
|
|
2095
1709
|
}
|
|
2096
1710
|
releaseEndpointRefreshSelectorClaim(endpointGeneration) {
|
|
2097
1711
|
const event = this.pendingEndpointRefreshEvent;
|
|
2098
|
-
if (event?.current.endpointGeneration === endpointGeneration
|
|
2099
|
-
this.pendingEndpointAttestation !== undefined) {
|
|
1712
|
+
if (event?.current.endpointGeneration === endpointGeneration) {
|
|
2100
1713
|
this.pendingEndpointRefreshSelectorClaimed = false;
|
|
2101
1714
|
}
|
|
2102
1715
|
}
|
|
2103
1716
|
rearmEndpointRefreshActivation(endpointGeneration) {
|
|
2104
1717
|
const event = this.pendingEndpointRefreshEvent;
|
|
2105
1718
|
if (event?.current.endpointGeneration === endpointGeneration &&
|
|
2106
|
-
this.pendingEndpointAttestation !== undefined &&
|
|
2107
1719
|
!this.closing &&
|
|
2108
1720
|
!this.closed) {
|
|
2109
1721
|
this.publishEndpointRefresh(event);
|
|
2110
1722
|
}
|
|
2111
1723
|
}
|
|
2112
|
-
latestWriteCompatibilityProbeObservation(endpointGeneration) {
|
|
2113
|
-
return (this.writeProbeObservationsByGeneration.get(endpointGeneration) ??
|
|
2114
|
-
(this.writeProbeOverflow?.endpointGeneration === endpointGeneration
|
|
2115
|
-
? this.writeProbeOverflow.observation
|
|
2116
|
-
: undefined));
|
|
2117
|
-
}
|
|
2118
|
-
async runCompatibilityProbes(context) {
|
|
2119
|
-
return await this.runCompatibilityProbesFor(this.factory, context === undefined
|
|
2120
|
-
? undefined
|
|
2121
|
-
: {
|
|
2122
|
-
stateRoot: path.resolve(context.stateRoot),
|
|
2123
|
-
forbiddenCodexThreadIds: [...context.forbiddenCodexThreadIds],
|
|
2124
|
-
});
|
|
2125
|
-
}
|
|
2126
|
-
async runCompatibilityProbesFor(factory, context) {
|
|
2127
|
-
const [installation, control, initialize, threadList] = compatibilityProbeNames.codex;
|
|
2128
|
-
let transport;
|
|
2129
|
-
let connector;
|
|
2130
|
-
let stage = "transport";
|
|
2131
|
-
try {
|
|
2132
|
-
transport = await factory.connectTransport();
|
|
2133
|
-
stage = "initialize";
|
|
2134
|
-
connector = await CodexAppServerConnector.connect({
|
|
2135
|
-
compatibility: factory.schemaCompatibility,
|
|
2136
|
-
...(context === undefined
|
|
2137
|
-
? {}
|
|
2138
|
-
: { writeCompatibilityProbe: true }),
|
|
2139
|
-
writesEnabled: false,
|
|
2140
|
-
route: {
|
|
2141
|
-
endpointGeneration: factory.endpointGeneration,
|
|
2142
|
-
threadId: COMPATIBILITY_PROBE_THREAD_ID,
|
|
2143
|
-
},
|
|
2144
|
-
transport,
|
|
2145
|
-
maxReplyBytes: this.maxReplyBytes,
|
|
2146
|
-
now: this.now,
|
|
2147
|
-
onEvent: () => undefined,
|
|
2148
|
-
onTurnResult: () => undefined,
|
|
2149
|
-
});
|
|
2150
|
-
stage = "thread_list";
|
|
2151
|
-
await connector.observeLoadedThread(connector.guard());
|
|
2152
|
-
const requiredProbes = [
|
|
2153
|
-
passedProbe(installation),
|
|
2154
|
-
passedProbe(control),
|
|
2155
|
-
passedProbe(initialize),
|
|
2156
|
-
passedProbe(threadList),
|
|
2157
|
-
];
|
|
2158
|
-
if (context === undefined)
|
|
2159
|
-
return requiredProbes;
|
|
2160
|
-
const result = await this.runCodexWriteProbe(factory, connector, context);
|
|
2161
|
-
return result.outcome === "pass"
|
|
2162
|
-
? [...requiredProbes, passedProbe("write_attestation")]
|
|
2163
|
-
: requiredProbes;
|
|
2164
|
-
}
|
|
2165
|
-
catch (error) {
|
|
2166
|
-
if (this.isEndpointGenerationChanged(error) ||
|
|
2167
|
-
(error instanceof BridgeError &&
|
|
2168
|
-
error.code === "CODEX_FACTORY_ATTESTATION_INVALID")) {
|
|
2169
|
-
throw error;
|
|
2170
|
-
}
|
|
2171
|
-
if (stage === "transport") {
|
|
2172
|
-
const availabilityFailure = error instanceof LocalCodexTransportError &&
|
|
2173
|
-
[
|
|
2174
|
-
"MANAGED_CODEX_UNAVAILABLE",
|
|
2175
|
-
"LOCAL_APP_SERVER_NOT_RUNNING",
|
|
2176
|
-
"SPAWN_FAILED",
|
|
2177
|
-
"SPAWN_TIMEOUT",
|
|
2178
|
-
"PROXY_STDERR_LIMIT",
|
|
2179
|
-
"TRANSPORT_CONNECT_FAILED",
|
|
2180
|
-
].includes(error.code);
|
|
2181
|
-
if (!availabilityFailure)
|
|
2182
|
-
throw error;
|
|
2183
|
-
const installationUnavailable = error instanceof LocalCodexTransportError &&
|
|
2184
|
-
error.code === "MANAGED_CODEX_UNAVAILABLE";
|
|
2185
|
-
return [
|
|
2186
|
-
installationUnavailable
|
|
2187
|
-
? failedProbe(installation, "CODEX_INSTALLATION_INVALID")
|
|
2188
|
-
: passedProbe(installation),
|
|
2189
|
-
failedProbe(control, installationUnavailable
|
|
2190
|
-
? "CODEX_COMPAT_PROBE_BLOCKED"
|
|
2191
|
-
: "CODEX_CONTROL_SOCKET_UNAVAILABLE"),
|
|
2192
|
-
failedProbe(initialize, "CODEX_COMPAT_PROBE_BLOCKED"),
|
|
2193
|
-
failedProbe(threadList, "CODEX_COMPAT_PROBE_BLOCKED"),
|
|
2194
|
-
];
|
|
2195
|
-
}
|
|
2196
|
-
return [
|
|
2197
|
-
passedProbe(installation),
|
|
2198
|
-
passedProbe(control),
|
|
2199
|
-
stage === "initialize"
|
|
2200
|
-
? failedProbe(initialize, "CODEX_INITIALIZE_SCHEMA_REJECTED")
|
|
2201
|
-
: passedProbe(initialize),
|
|
2202
|
-
failedProbe(threadList, "CODEX_THREAD_LIST_SCHEMA_REJECTED"),
|
|
2203
|
-
];
|
|
2204
|
-
}
|
|
2205
|
-
finally {
|
|
2206
|
-
if (connector !== undefined) {
|
|
2207
|
-
await connector.close();
|
|
2208
|
-
}
|
|
2209
|
-
else if (transport !== undefined) {
|
|
2210
|
-
await transport.close();
|
|
2211
|
-
}
|
|
2212
|
-
}
|
|
2213
|
-
}
|
|
2214
|
-
async runCodexWriteProbe(factory, connector, context) {
|
|
2215
|
-
const key = `${factory.appServerVersion}\0${factory.endpointGeneration}`;
|
|
2216
|
-
const cached = this.writeProbeResults.get(key);
|
|
2217
|
-
if (cached !== undefined)
|
|
2218
|
-
return await cached;
|
|
2219
|
-
if (this.writeProbeOverflow?.key === key) {
|
|
2220
|
-
return this.writeProbeOverflow.observation;
|
|
2221
|
-
}
|
|
2222
|
-
if (this.writeProbeResults.size >= MAX_CODEX_WRITE_PROBE_ATTEMPTS) {
|
|
2223
|
-
const observation = Object.freeze(codexWriteProbeFailure("CODEX_WRITE_PROBE_THREAD_SETUP_FAILED"));
|
|
2224
|
-
this.writeProbeOverflow = Object.freeze({
|
|
2225
|
-
endpointGeneration: factory.endpointGeneration,
|
|
2226
|
-
key,
|
|
2227
|
-
observation,
|
|
2228
|
-
});
|
|
2229
|
-
if (this.callbacks !== undefined) {
|
|
2230
|
-
invokeCallback(() => this.callbacks?.onProtocolNotice?.({
|
|
2231
|
-
code: observation.safeErrorCode,
|
|
2232
|
-
}));
|
|
2233
|
-
}
|
|
2234
|
-
return observation;
|
|
2235
|
-
}
|
|
2236
|
-
const pending = this.executeCodexWriteProbe(connector, context).then((result) => {
|
|
2237
|
-
const observation = Object.freeze({ ...result });
|
|
2238
|
-
this.writeProbeObservationsByGeneration.set(factory.endpointGeneration, observation);
|
|
2239
|
-
if (observation.outcome === "fail" && this.callbacks !== undefined) {
|
|
2240
|
-
invokeCallback(() => this.callbacks?.onProtocolNotice?.({
|
|
2241
|
-
code: observation.safeErrorCode,
|
|
2242
|
-
}));
|
|
2243
|
-
}
|
|
2244
|
-
return observation;
|
|
2245
|
-
});
|
|
2246
|
-
this.writeProbeResults.set(key, pending);
|
|
2247
|
-
return await pending;
|
|
2248
|
-
}
|
|
2249
|
-
async executeCodexWriteProbe(connector, context) {
|
|
2250
|
-
const stateRoot = path.resolve(context.stateRoot);
|
|
2251
|
-
const rootBefore = await attestCodexWriteProbeRoot(stateRoot);
|
|
2252
|
-
if (rootBefore === null) {
|
|
2253
|
-
return codexWriteProbeFailure("CODEX_WRITE_PROBE_THREAD_SETUP_FAILED");
|
|
2254
|
-
}
|
|
2255
|
-
const probeRoot = path.join(stateRoot, CODEX_WRITE_PROBE_ROOT);
|
|
2256
|
-
let probeRootBefore;
|
|
2257
|
-
try {
|
|
2258
|
-
let created = false;
|
|
2259
|
-
try {
|
|
2260
|
-
await mkdir(probeRoot, { mode: 0o700 });
|
|
2261
|
-
created = true;
|
|
2262
|
-
}
|
|
2263
|
-
catch (error) {
|
|
2264
|
-
if (systemErrorCode(error) !== "EEXIST")
|
|
2265
|
-
throw error;
|
|
2266
|
-
}
|
|
2267
|
-
if (created)
|
|
2268
|
-
await chmod(probeRoot, 0o700);
|
|
2269
|
-
const evidence = await attestCodexWriteProbeRoot(probeRoot);
|
|
2270
|
-
if (evidence === null) {
|
|
2271
|
-
return codexWriteProbeFailure("CODEX_WRITE_PROBE_THREAD_SETUP_FAILED");
|
|
2272
|
-
}
|
|
2273
|
-
probeRootBefore = evidence;
|
|
2274
|
-
}
|
|
2275
|
-
catch (error) {
|
|
2276
|
-
if (error instanceof BridgeError &&
|
|
2277
|
-
error.code === "CODEX_FACTORY_ATTESTATION_INVALID") {
|
|
2278
|
-
throw error;
|
|
2279
|
-
}
|
|
2280
|
-
if (transientCodexWriteProbeError(error)) {
|
|
2281
|
-
return codexWriteProbeFailure("CODEX_WRITE_PROBE_THREAD_SETUP_FAILED");
|
|
2282
|
-
}
|
|
2283
|
-
throw new BridgeError("CODEX_FACTORY_ATTESTATION_INVALID", "The controller-owned Codex probe root could not be established safely.");
|
|
2284
|
-
}
|
|
2285
|
-
let cwd;
|
|
2286
|
-
let before;
|
|
2287
|
-
let result = codexWriteProbeFailure("CODEX_WRITE_PROBE_THREAD_SETUP_FAILED");
|
|
2288
|
-
try {
|
|
2289
|
-
cwd = await mkdtemp(path.join(probeRoot, CODEX_WRITE_PROBE_DIRECTORY_PREFIX));
|
|
2290
|
-
if (path.dirname(cwd) !== probeRoot) {
|
|
2291
|
-
throw new Error("probe directory escaped its dedicated root");
|
|
2292
|
-
}
|
|
2293
|
-
await chmod(cwd, 0o700);
|
|
2294
|
-
before = await attestCodexWriteProbeDirectory(cwd);
|
|
2295
|
-
result = await connector.runWriteCompatibilityProbe({
|
|
2296
|
-
cwd,
|
|
2297
|
-
forbiddenThreadIds: [
|
|
2298
|
-
...new Set([
|
|
2299
|
-
COMPATIBILITY_PROBE_THREAD_ID,
|
|
2300
|
-
...context.forbiddenCodexThreadIds,
|
|
2301
|
-
...this.trackedRoutes,
|
|
2302
|
-
]),
|
|
2303
|
-
],
|
|
2304
|
-
});
|
|
2305
|
-
}
|
|
2306
|
-
catch (error) {
|
|
2307
|
-
if (!safeCodexWriteProbeFilesystemFailure(error)) {
|
|
2308
|
-
if (error instanceof BridgeError)
|
|
2309
|
-
throw error;
|
|
2310
|
-
throw new BridgeError("CODEX_FACTORY_ATTESTATION_INVALID", "The disposable Codex probe directory could not be established safely.");
|
|
2311
|
-
}
|
|
2312
|
-
result = codexWriteProbeFailure("CODEX_WRITE_PROBE_THREAD_SETUP_FAILED");
|
|
2313
|
-
}
|
|
2314
|
-
let unchanged = false;
|
|
2315
|
-
let removed = false;
|
|
2316
|
-
if (cwd !== undefined && before !== undefined) {
|
|
2317
|
-
try {
|
|
2318
|
-
const after = await attestCodexWriteProbeDirectory(cwd);
|
|
2319
|
-
unchanged = sameCodexWriteProbeDirectory(before, after);
|
|
2320
|
-
await rmdir(cwd);
|
|
2321
|
-
try {
|
|
2322
|
-
await lstat(cwd);
|
|
2323
|
-
}
|
|
2324
|
-
catch (error) {
|
|
2325
|
-
removed = systemErrorCode(error) === "ENOENT";
|
|
2326
|
-
}
|
|
2327
|
-
}
|
|
2328
|
-
catch (error) {
|
|
2329
|
-
if (!safeCodexWriteProbeFilesystemFailure(error)) {
|
|
2330
|
-
if (error instanceof BridgeError)
|
|
2331
|
-
throw error;
|
|
2332
|
-
throw new BridgeError("CODEX_FACTORY_ATTESTATION_INVALID", "The disposable Codex probe directory could not be re-attested safely.");
|
|
2333
|
-
}
|
|
2334
|
-
// Never remove a path after its exact ownership evidence changed.
|
|
2335
|
-
}
|
|
2336
|
-
}
|
|
2337
|
-
if (cwd !== undefined && !removed) {
|
|
2338
|
-
result = codexWriteProbeFailure("CODEX_WRITE_PROBE_CLEANUP_UNCONFIRMED", result);
|
|
2339
|
-
}
|
|
2340
|
-
else if (cwd !== undefined && !unchanged) {
|
|
2341
|
-
result = codexWriteProbeFailure("CODEX_WRITE_PROBE_TOOL_ACTIVITY_OBSERVED", result);
|
|
2342
|
-
}
|
|
2343
|
-
const [probeRootAfter, rootAfter] = await Promise.all([
|
|
2344
|
-
attestCodexWriteProbeRoot(probeRoot),
|
|
2345
|
-
attestCodexWriteProbeRoot(stateRoot),
|
|
2346
|
-
]);
|
|
2347
|
-
if (probeRootAfter === null || rootAfter === null) {
|
|
2348
|
-
return codexWriteProbeFailure("CODEX_WRITE_PROBE_THREAD_SETUP_FAILED", result);
|
|
2349
|
-
}
|
|
2350
|
-
if (!sameCodexWriteProbeRoot(probeRootBefore, probeRootAfter) ||
|
|
2351
|
-
!sameCodexWriteProbeRoot(rootBefore, rootAfter)) {
|
|
2352
|
-
throw new BridgeError("CODEX_FACTORY_ATTESTATION_INVALID", "A controller-owned Codex probe root changed while the disposable probe ran.");
|
|
2353
|
-
}
|
|
2354
|
-
return result;
|
|
2355
|
-
}
|
|
2356
1724
|
async initialize(callbacks) {
|
|
2357
1725
|
if (this.closed || this.initialized) {
|
|
2358
1726
|
throw new BridgeError("CODEX_PROVIDER_INITIALIZATION_REJECTED", "The local Codex provider cannot be initialized in this state.");
|
|
2359
1727
|
}
|
|
2360
1728
|
this.callbacks = callbacks;
|
|
2361
1729
|
this.initialized = true;
|
|
2362
|
-
|
|
2363
|
-
return {
|
|
2364
|
-
health: "degraded",
|
|
2365
|
-
compatibility: "incompatible",
|
|
2366
|
-
safeErrorCode: this.compatibilityFailureCode,
|
|
2367
|
-
};
|
|
2368
|
-
}
|
|
2369
|
-
return this.factory.writeCompatibility === null
|
|
2370
|
-
? {
|
|
2371
|
-
health: "degraded",
|
|
2372
|
-
compatibility: "compatible",
|
|
2373
|
-
safeErrorCode: "CODEX_MONITOR_ONLY",
|
|
2374
|
-
}
|
|
2375
|
-
: { health: "healthy", compatibility: "compatible" };
|
|
1730
|
+
return { health: "healthy" };
|
|
2376
1731
|
}
|
|
2377
1732
|
async selectRoute(input) {
|
|
2378
1733
|
if (!this.initialized || this.closing || this.closed) {
|
|
@@ -2394,9 +1749,8 @@ export class LocalCodexGatewayProvider {
|
|
|
2394
1749
|
let selectorRefreshReserved = false;
|
|
2395
1750
|
let route;
|
|
2396
1751
|
const stageRefreshedRoute = async (result) => {
|
|
2397
|
-
if (result === undefined
|
|
2398
|
-
|
|
2399
|
-
throw new BridgeError("CODEX_ROUTE_SETUP_REJECTED", "The exact Codex endpoint replacement was not compatibility-attested.");
|
|
1752
|
+
if (result === undefined) {
|
|
1753
|
+
throw new BridgeError("CODEX_ROUTE_SETUP_REJECTED", "The exact Codex endpoint replacement could not be established.");
|
|
2400
1754
|
}
|
|
2401
1755
|
refreshResult = result;
|
|
2402
1756
|
selectorRefreshReserved = this.reserveSelectorRefresh(result);
|
|
@@ -2419,7 +1773,7 @@ export class LocalCodexGatewayProvider {
|
|
|
2419
1773
|
route = await stageRefreshedRoute(retainedEndpointRefresh);
|
|
2420
1774
|
}
|
|
2421
1775
|
else if (this.endpointUnavailable &&
|
|
2422
|
-
this.
|
|
1776
|
+
this.pendingEndpointRefreshEvent === undefined) {
|
|
2423
1777
|
route = await stageRefreshedRoute(await this.refreshEndpoint("selector"));
|
|
2424
1778
|
}
|
|
2425
1779
|
else {
|
|
@@ -2537,7 +1891,6 @@ export class LocalCodexGatewayProvider {
|
|
|
2537
1891
|
this.closed ||
|
|
2538
1892
|
this.endpointUnavailable ||
|
|
2539
1893
|
this.endpointRefresh !== undefined ||
|
|
2540
|
-
!this.compatibilityAttested ||
|
|
2541
1894
|
input.authorization !== "selected_route" ||
|
|
2542
1895
|
!sameEndpoint(input.binding, this.identity)) {
|
|
2543
1896
|
return { state: "failed", safeErrorCode: "CODEX_ROUTE_UNAVAILABLE" };
|
|
@@ -2550,14 +1903,7 @@ export class LocalCodexGatewayProvider {
|
|
|
2550
1903
|
route.endpointGeneration !== this.factory.endpointGeneration) {
|
|
2551
1904
|
return { state: "failed", safeErrorCode: "CODEX_ROUTE_UNAVAILABLE" };
|
|
2552
1905
|
}
|
|
2553
|
-
if (!this.factory.writableReady ||
|
|
2554
|
-
this.factory.writeCompatibility === null) {
|
|
2555
|
-
return { state: "failed", safeErrorCode: "CODEX_WRITES_DISABLED" };
|
|
2556
|
-
}
|
|
2557
1906
|
const guard = route.connector.guard();
|
|
2558
|
-
if (!guard.writableReady) {
|
|
2559
|
-
return { state: "failed", safeErrorCode: "CODEX_WRITES_DISABLED" };
|
|
2560
|
-
}
|
|
2561
1907
|
if (strictDeadline(input.deadlineAt, this.now().getTime()) === undefined) {
|
|
2562
1908
|
return { state: "failed", safeErrorCode: "MESSAGE_EXPIRED" };
|
|
2563
1909
|
}
|
|
@@ -2567,7 +1913,8 @@ export class LocalCodexGatewayProvider {
|
|
|
2567
1913
|
let content;
|
|
2568
1914
|
try {
|
|
2569
1915
|
content = composeProvenanceEnvelope({
|
|
2570
|
-
|
|
1916
|
+
sourceProvider: input.sourceProvider,
|
|
1917
|
+
recipientProvider: this.identity.provider,
|
|
2571
1918
|
sourceAlias: input.sourceAlias,
|
|
2572
1919
|
targetAlias: input.targetAlias,
|
|
2573
1920
|
conversationId: input.conversationId,
|
|
@@ -2677,7 +2024,6 @@ export class LocalCodexGatewayProvider {
|
|
|
2677
2024
|
this.clearEndpointActivationRetry();
|
|
2678
2025
|
this.pendingEndpointRefreshEvent = undefined;
|
|
2679
2026
|
this.pendingEndpointRefreshSelectorClaimed = false;
|
|
2680
|
-
this.pendingEndpointAttestation = undefined;
|
|
2681
2027
|
const refreshResult = this.endpointRefresh === undefined
|
|
2682
2028
|
? []
|
|
2683
2029
|
: await Promise.allSettled([this.endpointRefresh]);
|
|
@@ -2703,7 +2049,6 @@ export class LocalCodexGatewayProvider {
|
|
|
2703
2049
|
this.closed = true;
|
|
2704
2050
|
this.closing = false;
|
|
2705
2051
|
this.callbackQueue.length = 0;
|
|
2706
|
-
this.pendingEndpointAttestation = undefined;
|
|
2707
2052
|
this.pendingEndpointRefreshEvent = undefined;
|
|
2708
2053
|
this.pendingEndpointRefreshSelectorClaimed = false;
|
|
2709
2054
|
this.trackedRoutes.clear();
|
|
@@ -2716,13 +2061,12 @@ export class LocalCodexGatewayProvider {
|
|
|
2716
2061
|
this.closing ||
|
|
2717
2062
|
this.closed ||
|
|
2718
2063
|
this.endpointUnavailable ||
|
|
2719
|
-
this.endpointRefresh !== undefined
|
|
2720
|
-
!this.compatibilityAttested) {
|
|
2064
|
+
this.endpointRefresh !== undefined) {
|
|
2721
2065
|
throw new BridgeError("CODEX_PROVIDER_UNAVAILABLE", "The local Codex provider is unavailable.", true);
|
|
2722
2066
|
}
|
|
2723
2067
|
}
|
|
2724
|
-
async ensureRoute(threadId, queueInitialObservation = true,
|
|
2725
|
-
if (
|
|
2068
|
+
async ensureRoute(threadId, queueInitialObservation = true, allowPendingEndpointActivation = false) {
|
|
2069
|
+
if (allowPendingEndpointActivation) {
|
|
2726
2070
|
if (!this.initialized || this.closing || this.closed) {
|
|
2727
2071
|
throw new BridgeError("CODEX_PROVIDER_UNAVAILABLE", "The local Codex provider is unavailable.", true);
|
|
2728
2072
|
}
|
|
@@ -2733,7 +2077,7 @@ export class LocalCodexGatewayProvider {
|
|
|
2733
2077
|
const pendingRelease = this.routeReleases.get(threadId);
|
|
2734
2078
|
if (pendingRelease !== undefined) {
|
|
2735
2079
|
await pendingRelease;
|
|
2736
|
-
return await this.ensureRoute(threadId, queueInitialObservation,
|
|
2080
|
+
return await this.ensureRoute(threadId, queueInitialObservation, allowPendingEndpointActivation);
|
|
2737
2081
|
}
|
|
2738
2082
|
const existing = this.routes.get(threadId);
|
|
2739
2083
|
if (existing !== undefined) {
|
|
@@ -2745,7 +2089,7 @@ export class LocalCodexGatewayProvider {
|
|
|
2745
2089
|
// a fresh one. The identity-checked release lets automatic recovery and
|
|
2746
2090
|
// an explicit selector join the same cleanup safely.
|
|
2747
2091
|
await this.releaseRouteInternal(threadId);
|
|
2748
|
-
return await this.ensureRoute(threadId, queueInitialObservation,
|
|
2092
|
+
return await this.ensureRoute(threadId, queueInitialObservation, allowPendingEndpointActivation);
|
|
2749
2093
|
}
|
|
2750
2094
|
const pending = this.routeCreations.get(threadId);
|
|
2751
2095
|
if (pending !== undefined)
|
|
@@ -2760,7 +2104,7 @@ export class LocalCodexGatewayProvider {
|
|
|
2760
2104
|
await this.closeRoute(route);
|
|
2761
2105
|
throw new BridgeError("CODEX_PROVIDER_UNAVAILABLE", "The local Codex provider closed during route creation.");
|
|
2762
2106
|
}
|
|
2763
|
-
if (!
|
|
2107
|
+
if (!allowPendingEndpointActivation &&
|
|
2764
2108
|
(this.endpointUnavailable ||
|
|
2765
2109
|
this.endpointRefresh !== undefined ||
|
|
2766
2110
|
admittedFactory !== this.factory ||
|
|
@@ -2785,10 +2129,8 @@ export class LocalCodexGatewayProvider {
|
|
|
2785
2129
|
const generation = Symbol(threadId);
|
|
2786
2130
|
try {
|
|
2787
2131
|
transport = await factory.connectTransport();
|
|
2788
|
-
const writesEnabled = factory.writableReady && factory.writeCompatibility !== null;
|
|
2789
2132
|
connector = await CodexAppServerConnector.connect({
|
|
2790
|
-
compatibility:
|
|
2791
|
-
writesEnabled,
|
|
2133
|
+
compatibility: { endpointGeneration: factory.endpointGeneration },
|
|
2792
2134
|
route: {
|
|
2793
2135
|
endpointGeneration: factory.endpointGeneration,
|
|
2794
2136
|
threadId,
|
|
@@ -2893,12 +2235,11 @@ export class LocalCodexGatewayProvider {
|
|
|
2893
2235
|
}
|
|
2894
2236
|
try {
|
|
2895
2237
|
if (this.endpointUnavailable) {
|
|
2896
|
-
if (this.
|
|
2238
|
+
if (this.pendingEndpointRefreshEvent !== undefined) {
|
|
2897
2239
|
throw new BridgeError("CODEX_ENDPOINT_REFRESH_PENDING", "The replacement Codex endpoint is awaiting controller activation.", true);
|
|
2898
2240
|
}
|
|
2899
2241
|
const refreshed = await this.refreshEndpoint("callback");
|
|
2900
|
-
if (refreshed?.event.
|
|
2901
|
-
refreshed.event.routes.some((route) => route.routeHandle === threadId)) {
|
|
2242
|
+
if (refreshed?.event.routes.some((route) => route.routeHandle === threadId)) {
|
|
2902
2243
|
this.routesRequiringRecovery.delete(threadId);
|
|
2903
2244
|
this.routeRecoveryAttempts.delete(threadId);
|
|
2904
2245
|
return;
|
|
@@ -2928,8 +2269,7 @@ export class LocalCodexGatewayProvider {
|
|
|
2928
2269
|
if (this.isEndpointGenerationChanged(error)) {
|
|
2929
2270
|
this.endpointUnavailable = true;
|
|
2930
2271
|
const refreshed = await this.refreshEndpoint("callback").catch(() => undefined);
|
|
2931
|
-
if (refreshed?.event.
|
|
2932
|
-
refreshed.event.routes.some((route) => route.routeHandle === threadId)) {
|
|
2272
|
+
if (refreshed?.event.routes.some((route) => route.routeHandle === threadId)) {
|
|
2933
2273
|
this.routesRequiringRecovery.delete(threadId);
|
|
2934
2274
|
this.routeRecoveryAttempts.delete(threadId);
|
|
2935
2275
|
return;
|
|
@@ -2960,7 +2300,6 @@ export class LocalCodexGatewayProvider {
|
|
|
2960
2300
|
const event = this.pendingEndpointRefreshEvent;
|
|
2961
2301
|
if (event === undefined ||
|
|
2962
2302
|
this.pendingEndpointRefreshSelectorClaimed ||
|
|
2963
|
-
this.pendingEndpointAttestation === undefined ||
|
|
2964
2303
|
event.current.endpointGeneration !== this.factory.endpointGeneration) {
|
|
2965
2304
|
return undefined;
|
|
2966
2305
|
}
|
|
@@ -3007,40 +2346,7 @@ export class LocalCodexGatewayProvider {
|
|
|
3007
2346
|
try {
|
|
3008
2347
|
const resolved = await this.resolveEndpointRefreshCandidate(refreshFactory, previousFactory, previous);
|
|
3009
2348
|
candidate = resolved.factory;
|
|
3010
|
-
const { attestation, identity: candidateIdentity } = resolved;
|
|
3011
2349
|
await this.retireEndpointGeneration(previousFactory, previous.endpointGeneration);
|
|
3012
|
-
if (attestation.tier === "incompatible") {
|
|
3013
|
-
await candidate.close();
|
|
3014
|
-
candidate = undefined;
|
|
3015
|
-
this.compatibilityAttested = false;
|
|
3016
|
-
this.pendingEndpointAttestation = undefined;
|
|
3017
|
-
this.pendingEndpointRefreshEvent = undefined;
|
|
3018
|
-
this.pendingEndpointRefreshSelectorClaimed = false;
|
|
3019
|
-
for (const routeHandle of this.trackedRoutes) {
|
|
3020
|
-
this.routesRequiringRecovery.add(routeHandle);
|
|
3021
|
-
}
|
|
3022
|
-
if (this.closing || this.closed)
|
|
3023
|
-
return undefined;
|
|
3024
|
-
const result = {
|
|
3025
|
-
event: {
|
|
3026
|
-
outcome: "incompatible",
|
|
3027
|
-
previous,
|
|
3028
|
-
candidate: candidateIdentity,
|
|
3029
|
-
attestation,
|
|
3030
|
-
},
|
|
3031
|
-
delivery: "callback",
|
|
3032
|
-
selectorClaimed: true,
|
|
3033
|
-
published: false,
|
|
3034
|
-
};
|
|
3035
|
-
result.published = true;
|
|
3036
|
-
this.emitEndpointRefresh(result.event);
|
|
3037
|
-
for (const routeHandle of this.routesRequiringRecovery) {
|
|
3038
|
-
if (this.trackedRoutes.has(routeHandle)) {
|
|
3039
|
-
this.scheduleRouteRecovery(routeHandle);
|
|
3040
|
-
}
|
|
3041
|
-
}
|
|
3042
|
-
return result;
|
|
3043
|
-
}
|
|
3044
2350
|
if (this.closing || this.closed) {
|
|
3045
2351
|
await candidate.close();
|
|
3046
2352
|
candidate = undefined;
|
|
@@ -3079,9 +2385,7 @@ export class LocalCodexGatewayProvider {
|
|
|
3079
2385
|
this.factory = replacementFactory;
|
|
3080
2386
|
candidate = undefined;
|
|
3081
2387
|
this.retiredEndpointGeneration = undefined;
|
|
3082
|
-
this.compatibilityAttested = false;
|
|
3083
2388
|
this.endpointUnavailable = true;
|
|
3084
|
-
this.pendingEndpointAttestation = attestation;
|
|
3085
2389
|
for (const staged of stagedRoutes) {
|
|
3086
2390
|
this.routes.set(staged.routeHandle, staged.route);
|
|
3087
2391
|
this.routesRequiringRecovery.delete(staged.routeHandle);
|
|
@@ -3093,20 +2397,16 @@ export class LocalCodexGatewayProvider {
|
|
|
3093
2397
|
}
|
|
3094
2398
|
const result = {
|
|
3095
2399
|
event: {
|
|
3096
|
-
outcome: "compatible",
|
|
3097
2400
|
previous,
|
|
3098
2401
|
current: this.identity,
|
|
3099
|
-
attestation,
|
|
3100
2402
|
routes: refreshedRoutes,
|
|
3101
2403
|
},
|
|
3102
2404
|
delivery: this.endpointRefreshDelivery ?? "callback",
|
|
3103
2405
|
selectorClaimed: false,
|
|
3104
2406
|
published: false,
|
|
3105
2407
|
};
|
|
3106
|
-
|
|
3107
|
-
|
|
3108
|
-
this.pendingEndpointRefreshSelectorClaimed = false;
|
|
3109
|
-
}
|
|
2408
|
+
this.pendingEndpointRefreshEvent = result.event;
|
|
2409
|
+
this.pendingEndpointRefreshSelectorClaimed = false;
|
|
3110
2410
|
if (result.delivery === "callback") {
|
|
3111
2411
|
result.selectorClaimed = true;
|
|
3112
2412
|
result.published = true;
|
|
@@ -3137,10 +2437,7 @@ export class LocalCodexGatewayProvider {
|
|
|
3137
2437
|
let candidate;
|
|
3138
2438
|
try {
|
|
3139
2439
|
candidate = await refreshFactory();
|
|
3140
|
-
|
|
3141
|
-
validateCodexFactory(candidate, {
|
|
3142
|
-
allowUnsupportedVersion: versionFailure !== undefined,
|
|
3143
|
-
});
|
|
2440
|
+
validateCodexFactory(candidate);
|
|
3144
2441
|
if (candidate.hostId !== previous.hostId ||
|
|
3145
2442
|
candidate.protocol !== previousFactory.protocol) {
|
|
3146
2443
|
throw new BridgeError("CODEX_FACTORY_ATTESTATION_INVALID", "The replacement Codex factory moved outside its pinned boundary.");
|
|
@@ -3150,41 +2447,12 @@ export class LocalCodexGatewayProvider {
|
|
|
3150
2447
|
if (candidate !== previousFactory)
|
|
3151
2448
|
await candidate.close();
|
|
3152
2449
|
candidate = undefined;
|
|
3153
|
-
if (this.endpointUnavailable &&
|
|
3154
|
-
this.compatibilityFailureCode === undefined &&
|
|
3155
|
-
!this.factory.writableReady &&
|
|
3156
|
-
this.factory.writeCompatibility === null) {
|
|
3157
|
-
throw new BridgeError("CODEX_MONITOR_ONLY", "This Codex App Server remains monitor-only until a write-attested generation is available.");
|
|
3158
|
-
}
|
|
3159
2450
|
if (attempt + 1 < MAX_CODEX_ENDPOINT_REFRESH_CANDIDATES) {
|
|
3160
2451
|
continue;
|
|
3161
2452
|
}
|
|
3162
2453
|
throw new BridgeError("CODEX_ENDPOINT_GENERATION_CHURN", "The Codex endpoint did not stabilize within its bounded refresh window.", true);
|
|
3163
2454
|
}
|
|
3164
|
-
|
|
3165
|
-
// outside the tested major is classified from OS-attested factory
|
|
3166
|
-
// evidence alone; no connector or transport may touch that protocol.
|
|
3167
|
-
const probes = versionFailure === undefined
|
|
3168
|
-
? await this.runCompatibilityProbesFor(candidate)
|
|
3169
|
-
: compatibilityProbeNames.codex.map((name, index) => index === 0
|
|
3170
|
-
? passedProbe(name)
|
|
3171
|
-
: failedProbe(name, versionFailure));
|
|
3172
|
-
const attestation = evaluateCompatibilityAttestation({
|
|
3173
|
-
surface: "codex",
|
|
3174
|
-
version: candidate.appServerVersion,
|
|
3175
|
-
checkedAt: this.now().toISOString(),
|
|
3176
|
-
certifiedVersions: certifiedCompatibilityVersions.codex,
|
|
3177
|
-
probes,
|
|
3178
|
-
});
|
|
3179
|
-
return {
|
|
3180
|
-
factory: candidate,
|
|
3181
|
-
identity: {
|
|
3182
|
-
provider: "codex",
|
|
3183
|
-
hostId: previous.hostId,
|
|
3184
|
-
endpointGeneration: candidate.endpointGeneration,
|
|
3185
|
-
},
|
|
3186
|
-
attestation,
|
|
3187
|
-
};
|
|
2455
|
+
return { factory: candidate };
|
|
3188
2456
|
}
|
|
3189
2457
|
catch (error) {
|
|
3190
2458
|
if (candidate !== undefined && candidate !== previousFactory) {
|
|
@@ -3240,7 +2508,6 @@ export class LocalCodexGatewayProvider {
|
|
|
3240
2508
|
return;
|
|
3241
2509
|
let event = result.event;
|
|
3242
2510
|
if (staleRouteHandle !== undefined &&
|
|
3243
|
-
event.outcome === "compatible" &&
|
|
3244
2511
|
event.routes.some((route) => route.routeHandle === staleRouteHandle)) {
|
|
3245
2512
|
event = {
|
|
3246
2513
|
...event,
|
|
@@ -3254,9 +2521,7 @@ export class LocalCodexGatewayProvider {
|
|
|
3254
2521
|
}
|
|
3255
2522
|
publishEndpointRefresh(event) {
|
|
3256
2523
|
this.emitEndpointRefresh(event);
|
|
3257
|
-
|
|
3258
|
-
this.armEndpointActivationRetry(event);
|
|
3259
|
-
}
|
|
2524
|
+
this.armEndpointActivationRetry(event);
|
|
3260
2525
|
}
|
|
3261
2526
|
armEndpointActivationRetry(event) {
|
|
3262
2527
|
this.clearEndpointActivationRetry();
|
|
@@ -3269,8 +2534,7 @@ export class LocalCodexGatewayProvider {
|
|
|
3269
2534
|
this.closing ||
|
|
3270
2535
|
this.closed ||
|
|
3271
2536
|
pending.attempt >= 3 ||
|
|
3272
|
-
|
|
3273
|
-
this.pendingEndpointAttestation === undefined)) {
|
|
2537
|
+
this.pendingEndpointRefreshEvent !== pending.event) {
|
|
3274
2538
|
this.clearEndpointActivationRetry();
|
|
3275
2539
|
return;
|
|
3276
2540
|
}
|
|
@@ -3282,8 +2546,7 @@ export class LocalCodexGatewayProvider {
|
|
|
3282
2546
|
const current = this.endpointActivationRetry;
|
|
3283
2547
|
if (current === undefined || current.event !== pending.event)
|
|
3284
2548
|
return;
|
|
3285
|
-
if (current.event
|
|
3286
|
-
this.pendingEndpointAttestation === undefined) {
|
|
2549
|
+
if (this.pendingEndpointRefreshEvent !== current.event) {
|
|
3287
2550
|
this.clearEndpointActivationRetry();
|
|
3288
2551
|
return;
|
|
3289
2552
|
}
|