agent-embassy 1.5.0 → 1.6.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 +18 -1
- package/CONTRIBUTING.md +11 -3
- package/README.md +4 -2
- package/README.zh-CN.md +4 -2
- package/SECURITY.md +30 -9
- package/dist/src/gateway/cli.d.ts +1 -1
- package/dist/src/gateway/cli.js +4 -1
- package/dist/src/gateway/cli.js.map +1 -1
- package/dist/src/gateway/codex-app-server.d.ts +42 -1
- package/dist/src/gateway/codex-app-server.js +536 -2
- package/dist/src/gateway/codex-app-server.js.map +1 -1
- package/dist/src/gateway/compatibility.d.ts +31 -3
- package/dist/src/gateway/compatibility.js +100 -29
- package/dist/src/gateway/compatibility.js.map +1 -1
- package/dist/src/gateway/dashboard-copy.d.ts +1 -1
- package/dist/src/gateway/dashboard-copy.en.d.ts +6 -0
- package/dist/src/gateway/dashboard-copy.en.js +6 -0
- package/dist/src/gateway/dashboard-copy.en.js.map +1 -1
- package/dist/src/gateway/dashboard-copy.js +6 -0
- package/dist/src/gateway/dashboard-copy.js.map +1 -1
- package/dist/src/gateway/dashboard-copy.zh-CN.d.ts +6 -0
- package/dist/src/gateway/dashboard-copy.zh-CN.js +6 -0
- package/dist/src/gateway/dashboard-copy.zh-CN.js.map +1 -1
- package/dist/src/gateway/dashboard-model.d.ts +27 -7
- package/dist/src/gateway/dashboard-model.js +78 -25
- package/dist/src/gateway/dashboard-model.js.map +1 -1
- package/dist/src/gateway/dashboard.d.ts +3 -0
- package/dist/src/gateway/dashboard.js +15 -3
- package/dist/src/gateway/dashboard.js.map +1 -1
- package/dist/src/gateway/deepseek-detect.d.ts +13 -0
- package/dist/src/gateway/deepseek-detect.js +125 -0
- package/dist/src/gateway/deepseek-detect.js.map +1 -0
- package/dist/src/gateway/live-dashboard-app/app.js +12 -11
- package/dist/src/gateway/providers.d.ts +12 -2
- package/dist/src/gateway/providers.js +326 -6
- package/dist/src/gateway/providers.js.map +1 -1
- package/dist/src/gateway/server.d.ts +2 -0
- package/dist/src/gateway/server.js +6 -0
- package/dist/src/gateway/server.js.map +1 -1
- package/dist/src/gateway/service.d.ts +21 -2
- package/dist/src/gateway/service.js +116 -37
- package/dist/src/gateway/service.js.map +1 -1
- package/dist/src/gateway/types.d.ts +4 -3
- package/dist/src/gateway/types.js +17 -14
- package/dist/src/gateway/types.js.map +1 -1
- package/docs/CONFIGURATION.md +2 -2
- package/docs/CONFIGURATION.zh-CN.md +2 -2
- package/docs/DASHBOARD.md +10 -4
- package/docs/DASHBOARD.zh-CN.md +1 -1
- package/docs/GATEWAY-ARCHITECTURE.md +40 -14
- package/package.json +1 -1
- package/skills/embassy-peer/SKILL.md +1 -1
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import path from "node:path";
|
|
2
|
+
import { chmod, lstat, mkdir, mkdtemp, readdir, realpath, rmdir, } from "node:fs/promises";
|
|
2
3
|
import { BridgeError } from "../errors.js";
|
|
3
4
|
import { CLAUDE_PEER_COMPATIBILITY, ClaudePeerAdapter, } from "./claude-peer.js";
|
|
4
5
|
import { certifiedCompatibilityVersions, compatibilityProbeNames, evaluateCompatibilityAttestation, isCompatibilityAttestation, isCompatibilityVersion, sharesCompatibilityMajor, UNKNOWN_COMPATIBILITY_VERSION, } from "./compatibility.js";
|
|
5
|
-
import { CODEX_APP_SERVER_WRITABLE_VERSIONS, CodexAppServerConnector, CodexConnectorError, } from "./codex-app-server.js";
|
|
6
|
+
import { CODEX_APP_SERVER_WRITABLE_VERSIONS, CodexAppServerConnector, CodexConnectorError, codexWriteProbeFailure, } from "./codex-app-server.js";
|
|
6
7
|
import { LocalCodexTransportError } from "./codex-local-transport.js";
|
|
7
8
|
import { ClaudeNativeHelperSupervisor, } from "./claude-helper-supervisor.js";
|
|
8
9
|
import { isDashboardLocale } from "./locale.js";
|
|
@@ -24,6 +25,12 @@ const DEFAULT_CODEX_RECOVERY_INITIAL_MS = 250;
|
|
|
24
25
|
const DEFAULT_CODEX_RECOVERY_MAX_MS = 5_000;
|
|
25
26
|
const MAX_CODEX_ENDPOINT_REFRESH_CANDIDATES = 3;
|
|
26
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
|
+
]);
|
|
27
34
|
const CLAUDE_CLEAN_PREWRITE_RETRY_CODES = new Set([
|
|
28
35
|
"CLAUDE_PEER_TARGET_UNKNOWN",
|
|
29
36
|
"CLAUDE_PEER_TARGET_STALE",
|
|
@@ -85,6 +92,99 @@ function passedProbe(name) {
|
|
|
85
92
|
function failedProbe(name, safeErrorCode) {
|
|
86
93
|
return { name, outcome: "fail", safeErrorCode };
|
|
87
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
|
+
}
|
|
88
188
|
function exactLocalHost(hostId) {
|
|
89
189
|
if ((hostId ?? LOCAL_HOST) !== LOCAL_HOST) {
|
|
90
190
|
throw new BridgeError("GATEWAY_REMOTE_PROVIDER_DISABLED", "This provider is restricted to the exact local host boundary.");
|
|
@@ -1897,11 +1997,15 @@ export class LocalCodexGatewayProvider {
|
|
|
1897
1997
|
endpointActivationRetry;
|
|
1898
1998
|
endpointActivationRetryTimer;
|
|
1899
1999
|
callbackDrainFrozen = false;
|
|
2000
|
+
writeProbeObservationsByGeneration = new Map();
|
|
2001
|
+
writeProbeResults = new Map();
|
|
2002
|
+
writeProbeOverflow;
|
|
1900
2003
|
compatibilityAttested;
|
|
1901
2004
|
compatibilityFailureCode;
|
|
1902
2005
|
endpointUnavailable = false;
|
|
1903
2006
|
pendingEndpointAttestation;
|
|
1904
2007
|
pendingEndpointRefreshEvent;
|
|
2008
|
+
pendingEndpointRefreshSelectorClaimed = false;
|
|
1905
2009
|
initialized = false;
|
|
1906
2010
|
closing = false;
|
|
1907
2011
|
closed = false;
|
|
@@ -1961,6 +2065,7 @@ export class LocalCodexGatewayProvider {
|
|
|
1961
2065
|
if (attestation.tier === "incompatible") {
|
|
1962
2066
|
this.pendingEndpointAttestation = undefined;
|
|
1963
2067
|
this.pendingEndpointRefreshEvent = undefined;
|
|
2068
|
+
this.pendingEndpointRefreshSelectorClaimed = false;
|
|
1964
2069
|
this.clearEndpointActivationRetry();
|
|
1965
2070
|
this.compatibilityAttested = false;
|
|
1966
2071
|
this.endpointUnavailable = true;
|
|
@@ -1982,15 +2087,43 @@ export class LocalCodexGatewayProvider {
|
|
|
1982
2087
|
}
|
|
1983
2088
|
this.pendingEndpointAttestation = undefined;
|
|
1984
2089
|
this.pendingEndpointRefreshEvent = undefined;
|
|
2090
|
+
this.pendingEndpointRefreshSelectorClaimed = false;
|
|
1985
2091
|
this.clearEndpointActivationRetry();
|
|
1986
2092
|
this.compatibilityAttested = writableCertified;
|
|
1987
2093
|
this.endpointUnavailable = !writableCertified;
|
|
1988
2094
|
this.compatibilityFailureCode = undefined;
|
|
1989
2095
|
}
|
|
1990
|
-
|
|
1991
|
-
|
|
2096
|
+
releaseEndpointRefreshSelectorClaim(endpointGeneration) {
|
|
2097
|
+
const event = this.pendingEndpointRefreshEvent;
|
|
2098
|
+
if (event?.current.endpointGeneration === endpointGeneration &&
|
|
2099
|
+
this.pendingEndpointAttestation !== undefined) {
|
|
2100
|
+
this.pendingEndpointRefreshSelectorClaimed = false;
|
|
2101
|
+
}
|
|
2102
|
+
}
|
|
2103
|
+
rearmEndpointRefreshActivation(endpointGeneration) {
|
|
2104
|
+
const event = this.pendingEndpointRefreshEvent;
|
|
2105
|
+
if (event?.current.endpointGeneration === endpointGeneration &&
|
|
2106
|
+
this.pendingEndpointAttestation !== undefined &&
|
|
2107
|
+
!this.closing &&
|
|
2108
|
+
!this.closed) {
|
|
2109
|
+
this.publishEndpointRefresh(event);
|
|
2110
|
+
}
|
|
2111
|
+
}
|
|
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
|
+
});
|
|
1992
2125
|
}
|
|
1993
|
-
async runCompatibilityProbesFor(factory) {
|
|
2126
|
+
async runCompatibilityProbesFor(factory, context) {
|
|
1994
2127
|
const [installation, control, initialize, threadList] = compatibilityProbeNames.codex;
|
|
1995
2128
|
let transport;
|
|
1996
2129
|
let connector;
|
|
@@ -2000,6 +2133,9 @@ export class LocalCodexGatewayProvider {
|
|
|
2000
2133
|
stage = "initialize";
|
|
2001
2134
|
connector = await CodexAppServerConnector.connect({
|
|
2002
2135
|
compatibility: factory.schemaCompatibility,
|
|
2136
|
+
...(context === undefined
|
|
2137
|
+
? {}
|
|
2138
|
+
: { writeCompatibilityProbe: true }),
|
|
2003
2139
|
writesEnabled: false,
|
|
2004
2140
|
route: {
|
|
2005
2141
|
endpointGeneration: factory.endpointGeneration,
|
|
@@ -2013,16 +2149,25 @@ export class LocalCodexGatewayProvider {
|
|
|
2013
2149
|
});
|
|
2014
2150
|
stage = "thread_list";
|
|
2015
2151
|
await connector.observeLoadedThread(connector.guard());
|
|
2016
|
-
|
|
2152
|
+
const requiredProbes = [
|
|
2017
2153
|
passedProbe(installation),
|
|
2018
2154
|
passedProbe(control),
|
|
2019
2155
|
passedProbe(initialize),
|
|
2020
2156
|
passedProbe(threadList),
|
|
2021
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;
|
|
2022
2164
|
}
|
|
2023
2165
|
catch (error) {
|
|
2024
|
-
if (this.isEndpointGenerationChanged(error)
|
|
2166
|
+
if (this.isEndpointGenerationChanged(error) ||
|
|
2167
|
+
(error instanceof BridgeError &&
|
|
2168
|
+
error.code === "CODEX_FACTORY_ATTESTATION_INVALID")) {
|
|
2025
2169
|
throw error;
|
|
2170
|
+
}
|
|
2026
2171
|
if (stage === "transport") {
|
|
2027
2172
|
const availabilityFailure = error instanceof LocalCodexTransportError &&
|
|
2028
2173
|
[
|
|
@@ -2066,6 +2211,148 @@ export class LocalCodexGatewayProvider {
|
|
|
2066
2211
|
}
|
|
2067
2212
|
}
|
|
2068
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
|
+
}
|
|
2069
2356
|
async initialize(callbacks) {
|
|
2070
2357
|
if (this.closed || this.initialized) {
|
|
2071
2358
|
throw new BridgeError("CODEX_PROVIDER_INITIALIZATION_REJECTED", "The local Codex provider cannot be initialized in this state.");
|
|
@@ -2124,9 +2411,13 @@ export class LocalCodexGatewayProvider {
|
|
|
2124
2411
|
throw selectionError;
|
|
2125
2412
|
}
|
|
2126
2413
|
};
|
|
2414
|
+
const retainedEndpointRefresh = this.retainedEndpointRefreshResult();
|
|
2127
2415
|
if (this.endpointRefresh !== undefined) {
|
|
2128
2416
|
route = await stageRefreshedRoute(await this.refreshEndpoint("selector"));
|
|
2129
2417
|
}
|
|
2418
|
+
else if (retainedEndpointRefresh !== undefined) {
|
|
2419
|
+
route = await stageRefreshedRoute(retainedEndpointRefresh);
|
|
2420
|
+
}
|
|
2130
2421
|
else if (this.endpointUnavailable &&
|
|
2131
2422
|
this.pendingEndpointAttestation === undefined) {
|
|
2132
2423
|
route = await stageRefreshedRoute(await this.refreshEndpoint("selector"));
|
|
@@ -2385,6 +2676,7 @@ export class LocalCodexGatewayProvider {
|
|
|
2385
2676
|
this.routesRequiringRecovery.clear();
|
|
2386
2677
|
this.clearEndpointActivationRetry();
|
|
2387
2678
|
this.pendingEndpointRefreshEvent = undefined;
|
|
2679
|
+
this.pendingEndpointRefreshSelectorClaimed = false;
|
|
2388
2680
|
this.pendingEndpointAttestation = undefined;
|
|
2389
2681
|
const refreshResult = this.endpointRefresh === undefined
|
|
2390
2682
|
? []
|
|
@@ -2413,6 +2705,7 @@ export class LocalCodexGatewayProvider {
|
|
|
2413
2705
|
this.callbackQueue.length = 0;
|
|
2414
2706
|
this.pendingEndpointAttestation = undefined;
|
|
2415
2707
|
this.pendingEndpointRefreshEvent = undefined;
|
|
2708
|
+
this.pendingEndpointRefreshSelectorClaimed = false;
|
|
2416
2709
|
this.trackedRoutes.clear();
|
|
2417
2710
|
this.routeAliases.clear();
|
|
2418
2711
|
this.expectsReply.clear();
|
|
@@ -2663,6 +2956,21 @@ export class LocalCodexGatewayProvider {
|
|
|
2663
2956
|
event.current.endpointGeneration === this.factory.endpointGeneration &&
|
|
2664
2957
|
event.routes.some((route) => route.routeHandle === routeHandle));
|
|
2665
2958
|
}
|
|
2959
|
+
retainedEndpointRefreshResult() {
|
|
2960
|
+
const event = this.pendingEndpointRefreshEvent;
|
|
2961
|
+
if (event === undefined ||
|
|
2962
|
+
this.pendingEndpointRefreshSelectorClaimed ||
|
|
2963
|
+
this.pendingEndpointAttestation === undefined ||
|
|
2964
|
+
event.current.endpointGeneration !== this.factory.endpointGeneration) {
|
|
2965
|
+
return undefined;
|
|
2966
|
+
}
|
|
2967
|
+
return {
|
|
2968
|
+
event,
|
|
2969
|
+
delivery: "selector",
|
|
2970
|
+
selectorClaimed: false,
|
|
2971
|
+
published: true,
|
|
2972
|
+
};
|
|
2973
|
+
}
|
|
2666
2974
|
async refreshEndpoint(delivery) {
|
|
2667
2975
|
if (this.refreshFactory === undefined || this.closing || this.closed) {
|
|
2668
2976
|
return undefined;
|
|
@@ -2707,6 +3015,7 @@ export class LocalCodexGatewayProvider {
|
|
|
2707
3015
|
this.compatibilityAttested = false;
|
|
2708
3016
|
this.pendingEndpointAttestation = undefined;
|
|
2709
3017
|
this.pendingEndpointRefreshEvent = undefined;
|
|
3018
|
+
this.pendingEndpointRefreshSelectorClaimed = false;
|
|
2710
3019
|
for (const routeHandle of this.trackedRoutes) {
|
|
2711
3020
|
this.routesRequiringRecovery.add(routeHandle);
|
|
2712
3021
|
}
|
|
@@ -2721,7 +3030,9 @@ export class LocalCodexGatewayProvider {
|
|
|
2721
3030
|
},
|
|
2722
3031
|
delivery: "callback",
|
|
2723
3032
|
selectorClaimed: true,
|
|
3033
|
+
published: false,
|
|
2724
3034
|
};
|
|
3035
|
+
result.published = true;
|
|
2725
3036
|
this.emitEndpointRefresh(result.event);
|
|
2726
3037
|
for (const routeHandle of this.routesRequiringRecovery) {
|
|
2727
3038
|
if (this.trackedRoutes.has(routeHandle)) {
|
|
@@ -2790,12 +3101,15 @@ export class LocalCodexGatewayProvider {
|
|
|
2790
3101
|
},
|
|
2791
3102
|
delivery: this.endpointRefreshDelivery ?? "callback",
|
|
2792
3103
|
selectorClaimed: false,
|
|
3104
|
+
published: false,
|
|
2793
3105
|
};
|
|
2794
3106
|
if (result.event.outcome === "compatible") {
|
|
2795
3107
|
this.pendingEndpointRefreshEvent = result.event;
|
|
3108
|
+
this.pendingEndpointRefreshSelectorClaimed = false;
|
|
2796
3109
|
}
|
|
2797
3110
|
if (result.delivery === "callback") {
|
|
2798
3111
|
result.selectorClaimed = true;
|
|
3112
|
+
result.published = true;
|
|
2799
3113
|
this.publishEndpointRefresh(result.event);
|
|
2800
3114
|
}
|
|
2801
3115
|
for (const routeHandle of this.routesRequiringRecovery) {
|
|
@@ -2914,11 +3228,16 @@ export class LocalCodexGatewayProvider {
|
|
|
2914
3228
|
return false;
|
|
2915
3229
|
}
|
|
2916
3230
|
result.selectorClaimed = true;
|
|
3231
|
+
if (this.pendingEndpointRefreshEvent === result.event) {
|
|
3232
|
+
this.pendingEndpointRefreshSelectorClaimed = true;
|
|
3233
|
+
}
|
|
2917
3234
|
return true;
|
|
2918
3235
|
}
|
|
2919
3236
|
emitSelectorRefreshFallback(result, staleRouteHandle) {
|
|
2920
3237
|
if (result.delivery !== "selector" || !result.selectorClaimed)
|
|
2921
3238
|
return;
|
|
3239
|
+
if (result.published)
|
|
3240
|
+
return;
|
|
2922
3241
|
let event = result.event;
|
|
2923
3242
|
if (staleRouteHandle !== undefined &&
|
|
2924
3243
|
event.outcome === "compatible" &&
|
|
@@ -2930,6 +3249,7 @@ export class LocalCodexGatewayProvider {
|
|
|
2930
3249
|
result.event = event;
|
|
2931
3250
|
this.pendingEndpointRefreshEvent = event;
|
|
2932
3251
|
}
|
|
3252
|
+
result.published = true;
|
|
2933
3253
|
this.publishEndpointRefresh(event);
|
|
2934
3254
|
}
|
|
2935
3255
|
publishEndpointRefresh(event) {
|