agent-embassy 1.5.0 → 1.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +29 -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 +43 -2
- package/dist/src/gateway/codex-app-server.js +539 -3
- 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 +13 -2
- package/dist/src/gateway/providers.js +370 -24
- 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 +122 -38
- 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
|
-
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";
|
|
5
|
+
import { certifiedCompatibilityVersions, compatibilityCoversWrites, compatibilityProbeNames, evaluateCompatibilityAttestation, isCompatibilityAttestation, isCompatibilityVersion, sharesCompatibilityMajor, UNKNOWN_COMPATIBILITY_VERSION, } from "./compatibility.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.");
|
|
@@ -1840,6 +1940,16 @@ function validateCodexFactory(factory, options = {}) {
|
|
|
1840
1940
|
throw new BridgeError("CODEX_FACTORY_ATTESTATION_INVALID", "The local Codex provider requires exact schema and write attestations.");
|
|
1841
1941
|
}
|
|
1842
1942
|
}
|
|
1943
|
+
function codexFactoryWritesEnabled(factory, attestation, writeProbePassed = false) {
|
|
1944
|
+
return ((attestation.tier === "certified" &&
|
|
1945
|
+
CODEX_APP_SERVER_WRITABLE_VERSIONS.includes(attestation.version) &&
|
|
1946
|
+
factory.writableReady &&
|
|
1947
|
+
factory.writeCompatibility !== null) ||
|
|
1948
|
+
(!factory.writableReady &&
|
|
1949
|
+
factory.writeCompatibility === null &&
|
|
1950
|
+
writeProbePassed &&
|
|
1951
|
+
compatibilityCoversWrites(attestation)));
|
|
1952
|
+
}
|
|
1843
1953
|
function codexRouteState(observation) {
|
|
1844
1954
|
if (observation.connection !== "ready")
|
|
1845
1955
|
return "stale";
|
|
@@ -1897,11 +2007,16 @@ export class LocalCodexGatewayProvider {
|
|
|
1897
2007
|
endpointActivationRetry;
|
|
1898
2008
|
endpointActivationRetryTimer;
|
|
1899
2009
|
callbackDrainFrozen = false;
|
|
2010
|
+
writeProbeObservationsByGeneration = new Map();
|
|
2011
|
+
writeProbeResults = new Map();
|
|
2012
|
+
writeProbeDecline;
|
|
2013
|
+
writeProbeOverflow;
|
|
1900
2014
|
compatibilityAttested;
|
|
1901
2015
|
compatibilityFailureCode;
|
|
1902
2016
|
endpointUnavailable = false;
|
|
1903
2017
|
pendingEndpointAttestation;
|
|
1904
2018
|
pendingEndpointRefreshEvent;
|
|
2019
|
+
pendingEndpointRefreshSelectorClaimed = false;
|
|
1905
2020
|
initialized = false;
|
|
1906
2021
|
closing = false;
|
|
1907
2022
|
closed = false;
|
|
@@ -1910,7 +2025,9 @@ export class LocalCodexGatewayProvider {
|
|
|
1910
2025
|
this.factory = options.factory;
|
|
1911
2026
|
this.refreshFactory = options.refreshFactory;
|
|
1912
2027
|
exactLocalHost(options.factory.hostId);
|
|
1913
|
-
this.compatibilityAttested =
|
|
2028
|
+
this.compatibilityAttested =
|
|
2029
|
+
options.factory.writableReady &&
|
|
2030
|
+
options.factory.writeCompatibility !== null;
|
|
1914
2031
|
this.maxCallbacks = positiveBounded(options.maxCallbackEvents, MAX_CODEX_CALLBACKS, 4_096);
|
|
1915
2032
|
this.maxRoutes = positiveBounded(options.maxRoutes, MAX_CODEX_ROUTES, 256);
|
|
1916
2033
|
this.maxReplyBytes = positiveBounded(options.maxReplyBytes, MAX_TRANSIENT_REPLY_BYTES, 1024 * 1024);
|
|
@@ -1961,6 +2078,7 @@ export class LocalCodexGatewayProvider {
|
|
|
1961
2078
|
if (attestation.tier === "incompatible") {
|
|
1962
2079
|
this.pendingEndpointAttestation = undefined;
|
|
1963
2080
|
this.pendingEndpointRefreshEvent = undefined;
|
|
2081
|
+
this.pendingEndpointRefreshSelectorClaimed = false;
|
|
1964
2082
|
this.clearEndpointActivationRetry();
|
|
1965
2083
|
this.compatibilityAttested = false;
|
|
1966
2084
|
this.endpointUnavailable = true;
|
|
@@ -1968,29 +2086,57 @@ export class LocalCodexGatewayProvider {
|
|
|
1968
2086
|
attestation.safeErrorCode ?? "CODEX_COMPATIBILITY_CHECK_FAILED";
|
|
1969
2087
|
return;
|
|
1970
2088
|
}
|
|
1971
|
-
const
|
|
1972
|
-
CODEX_APP_SERVER_WRITABLE_VERSIONS.includes(attestation.version) &&
|
|
1973
|
-
this.factory.writableReady &&
|
|
1974
|
-
this.factory.writeCompatibility !== null;
|
|
2089
|
+
const writesEnabled = codexFactoryWritesEnabled(this.factory, attestation, this.latestWriteCompatibilityProbeObservation(this.factory.endpointGeneration)?.outcome === "pass");
|
|
1975
2090
|
const schemaObserved = attestation.tier === "schema_attested" &&
|
|
1976
2091
|
!this.factory.writableReady &&
|
|
1977
2092
|
this.factory.writeCompatibility === null;
|
|
1978
|
-
if ((!
|
|
2093
|
+
if ((!writesEnabled && !schemaObserved) ||
|
|
1979
2094
|
(this.endpointUnavailable && pending === undefined) ||
|
|
1980
2095
|
!pendingMatches) {
|
|
1981
2096
|
throw new BridgeError("CODEX_COMPATIBILITY_ATTESTATION_MISMATCH", "The Codex compatibility attestation does not match this endpoint.");
|
|
1982
2097
|
}
|
|
1983
2098
|
this.pendingEndpointAttestation = undefined;
|
|
1984
2099
|
this.pendingEndpointRefreshEvent = undefined;
|
|
2100
|
+
this.pendingEndpointRefreshSelectorClaimed = false;
|
|
1985
2101
|
this.clearEndpointActivationRetry();
|
|
1986
|
-
this.compatibilityAttested =
|
|
1987
|
-
this.endpointUnavailable = !
|
|
2102
|
+
this.compatibilityAttested = writesEnabled;
|
|
2103
|
+
this.endpointUnavailable = !writesEnabled;
|
|
1988
2104
|
this.compatibilityFailureCode = undefined;
|
|
1989
2105
|
}
|
|
1990
|
-
|
|
1991
|
-
|
|
2106
|
+
releaseEndpointRefreshSelectorClaim(endpointGeneration) {
|
|
2107
|
+
const event = this.pendingEndpointRefreshEvent;
|
|
2108
|
+
if (event?.current.endpointGeneration === endpointGeneration &&
|
|
2109
|
+
this.pendingEndpointAttestation !== undefined) {
|
|
2110
|
+
this.pendingEndpointRefreshSelectorClaimed = false;
|
|
2111
|
+
}
|
|
2112
|
+
}
|
|
2113
|
+
rearmEndpointRefreshActivation(endpointGeneration) {
|
|
2114
|
+
const event = this.pendingEndpointRefreshEvent;
|
|
2115
|
+
if (event?.current.endpointGeneration === endpointGeneration &&
|
|
2116
|
+
this.pendingEndpointAttestation !== undefined &&
|
|
2117
|
+
!this.closing &&
|
|
2118
|
+
!this.closed) {
|
|
2119
|
+
this.publishEndpointRefresh(event);
|
|
2120
|
+
}
|
|
2121
|
+
}
|
|
2122
|
+
latestWriteCompatibilityProbeObservation(endpointGeneration) {
|
|
2123
|
+
return (this.writeProbeObservationsByGeneration.get(endpointGeneration) ??
|
|
2124
|
+
(this.writeProbeOverflow?.endpointGeneration === endpointGeneration
|
|
2125
|
+
? this.writeProbeOverflow.observation
|
|
2126
|
+
: undefined) ??
|
|
2127
|
+
(this.writeProbeDecline?.endpointGeneration === endpointGeneration
|
|
2128
|
+
? this.writeProbeDecline.observation
|
|
2129
|
+
: undefined));
|
|
2130
|
+
}
|
|
2131
|
+
async runCompatibilityProbes(context) {
|
|
2132
|
+
return await this.runCompatibilityProbesFor(this.factory, context === undefined
|
|
2133
|
+
? undefined
|
|
2134
|
+
: {
|
|
2135
|
+
stateRoot: path.resolve(context.stateRoot),
|
|
2136
|
+
forbiddenCodexThreadIds: [...context.forbiddenCodexThreadIds],
|
|
2137
|
+
});
|
|
1992
2138
|
}
|
|
1993
|
-
async runCompatibilityProbesFor(factory) {
|
|
2139
|
+
async runCompatibilityProbesFor(factory, context) {
|
|
1994
2140
|
const [installation, control, initialize, threadList] = compatibilityProbeNames.codex;
|
|
1995
2141
|
let transport;
|
|
1996
2142
|
let connector;
|
|
@@ -2000,6 +2146,9 @@ export class LocalCodexGatewayProvider {
|
|
|
2000
2146
|
stage = "initialize";
|
|
2001
2147
|
connector = await CodexAppServerConnector.connect({
|
|
2002
2148
|
compatibility: factory.schemaCompatibility,
|
|
2149
|
+
...(context === undefined
|
|
2150
|
+
? {}
|
|
2151
|
+
: { writeCompatibilityProbe: true }),
|
|
2003
2152
|
writesEnabled: false,
|
|
2004
2153
|
route: {
|
|
2005
2154
|
endpointGeneration: factory.endpointGeneration,
|
|
@@ -2013,16 +2162,25 @@ export class LocalCodexGatewayProvider {
|
|
|
2013
2162
|
});
|
|
2014
2163
|
stage = "thread_list";
|
|
2015
2164
|
await connector.observeLoadedThread(connector.guard());
|
|
2016
|
-
|
|
2165
|
+
const requiredProbes = [
|
|
2017
2166
|
passedProbe(installation),
|
|
2018
2167
|
passedProbe(control),
|
|
2019
2168
|
passedProbe(initialize),
|
|
2020
2169
|
passedProbe(threadList),
|
|
2021
2170
|
];
|
|
2171
|
+
if (context === undefined)
|
|
2172
|
+
return requiredProbes;
|
|
2173
|
+
const result = await this.runCodexWriteProbe(factory, connector, context);
|
|
2174
|
+
return result.outcome === "pass"
|
|
2175
|
+
? [...requiredProbes, passedProbe("write_attestation")]
|
|
2176
|
+
: requiredProbes;
|
|
2022
2177
|
}
|
|
2023
2178
|
catch (error) {
|
|
2024
|
-
if (this.isEndpointGenerationChanged(error)
|
|
2179
|
+
if (this.isEndpointGenerationChanged(error) ||
|
|
2180
|
+
(error instanceof BridgeError &&
|
|
2181
|
+
error.code === "CODEX_FACTORY_ATTESTATION_INVALID")) {
|
|
2025
2182
|
throw error;
|
|
2183
|
+
}
|
|
2026
2184
|
if (stage === "transport") {
|
|
2027
2185
|
const availabilityFailure = error instanceof LocalCodexTransportError &&
|
|
2028
2186
|
[
|
|
@@ -2066,6 +2224,164 @@ export class LocalCodexGatewayProvider {
|
|
|
2066
2224
|
}
|
|
2067
2225
|
}
|
|
2068
2226
|
}
|
|
2227
|
+
async runCodexWriteProbe(factory, connector, context) {
|
|
2228
|
+
const key = `${factory.appServerVersion}\0${factory.endpointGeneration}`;
|
|
2229
|
+
const cached = this.writeProbeResults.get(key);
|
|
2230
|
+
if (cached !== undefined)
|
|
2231
|
+
return await cached;
|
|
2232
|
+
if (this.writeProbeOverflow?.key === key) {
|
|
2233
|
+
if (this.writeProbeResults.size >= MAX_CODEX_WRITE_PROBE_ATTEMPTS) {
|
|
2234
|
+
return this.writeProbeOverflow.observation;
|
|
2235
|
+
}
|
|
2236
|
+
this.writeProbeOverflow = undefined;
|
|
2237
|
+
}
|
|
2238
|
+
if (this.writeProbeResults.size >= MAX_CODEX_WRITE_PROBE_ATTEMPTS) {
|
|
2239
|
+
const observation = Object.freeze(codexWriteProbeFailure("CODEX_WRITE_PROBE_CAPACITY_EXHAUSTED"));
|
|
2240
|
+
this.writeProbeOverflow = Object.freeze({
|
|
2241
|
+
endpointGeneration: factory.endpointGeneration,
|
|
2242
|
+
key,
|
|
2243
|
+
observation,
|
|
2244
|
+
});
|
|
2245
|
+
if (this.callbacks !== undefined) {
|
|
2246
|
+
invokeCallback(() => this.callbacks?.onProtocolNotice?.({
|
|
2247
|
+
code: observation.safeErrorCode,
|
|
2248
|
+
}));
|
|
2249
|
+
}
|
|
2250
|
+
return observation;
|
|
2251
|
+
}
|
|
2252
|
+
const pending = this.executeCodexWriteProbe(connector, context).then((result) => {
|
|
2253
|
+
const observation = Object.freeze({ ...result });
|
|
2254
|
+
if (observation.outcome === "fail" &&
|
|
2255
|
+
(observation.safeErrorCode ===
|
|
2256
|
+
"CODEX_WRITE_PROBE_MODEL_PIN_UNAVAILABLE" ||
|
|
2257
|
+
observation.safeErrorCode ===
|
|
2258
|
+
"CODEX_WRITE_PROBE_RATE_LIMIT_CONSTRAINED")) {
|
|
2259
|
+
this.writeProbeResults.delete(key);
|
|
2260
|
+
this.writeProbeDecline = Object.freeze({
|
|
2261
|
+
endpointGeneration: factory.endpointGeneration,
|
|
2262
|
+
observation,
|
|
2263
|
+
});
|
|
2264
|
+
}
|
|
2265
|
+
else {
|
|
2266
|
+
this.writeProbeObservationsByGeneration.set(factory.endpointGeneration, observation);
|
|
2267
|
+
}
|
|
2268
|
+
if (observation.outcome === "fail" && this.callbacks !== undefined) {
|
|
2269
|
+
invokeCallback(() => this.callbacks?.onProtocolNotice?.({
|
|
2270
|
+
code: observation.safeErrorCode,
|
|
2271
|
+
}));
|
|
2272
|
+
}
|
|
2273
|
+
return observation;
|
|
2274
|
+
});
|
|
2275
|
+
this.writeProbeResults.set(key, pending);
|
|
2276
|
+
return await pending;
|
|
2277
|
+
}
|
|
2278
|
+
async executeCodexWriteProbe(connector, context) {
|
|
2279
|
+
const stateRoot = path.resolve(context.stateRoot);
|
|
2280
|
+
const rootBefore = await attestCodexWriteProbeRoot(stateRoot);
|
|
2281
|
+
if (rootBefore === null) {
|
|
2282
|
+
return codexWriteProbeFailure("CODEX_WRITE_PROBE_THREAD_SETUP_FAILED");
|
|
2283
|
+
}
|
|
2284
|
+
const probeRoot = path.join(stateRoot, CODEX_WRITE_PROBE_ROOT);
|
|
2285
|
+
let probeRootBefore;
|
|
2286
|
+
try {
|
|
2287
|
+
let created = false;
|
|
2288
|
+
try {
|
|
2289
|
+
await mkdir(probeRoot, { mode: 0o700 });
|
|
2290
|
+
created = true;
|
|
2291
|
+
}
|
|
2292
|
+
catch (error) {
|
|
2293
|
+
if (systemErrorCode(error) !== "EEXIST")
|
|
2294
|
+
throw error;
|
|
2295
|
+
}
|
|
2296
|
+
if (created)
|
|
2297
|
+
await chmod(probeRoot, 0o700);
|
|
2298
|
+
const evidence = await attestCodexWriteProbeRoot(probeRoot);
|
|
2299
|
+
if (evidence === null) {
|
|
2300
|
+
return codexWriteProbeFailure("CODEX_WRITE_PROBE_THREAD_SETUP_FAILED");
|
|
2301
|
+
}
|
|
2302
|
+
probeRootBefore = evidence;
|
|
2303
|
+
}
|
|
2304
|
+
catch (error) {
|
|
2305
|
+
if (error instanceof BridgeError &&
|
|
2306
|
+
error.code === "CODEX_FACTORY_ATTESTATION_INVALID") {
|
|
2307
|
+
throw error;
|
|
2308
|
+
}
|
|
2309
|
+
if (transientCodexWriteProbeError(error)) {
|
|
2310
|
+
return codexWriteProbeFailure("CODEX_WRITE_PROBE_THREAD_SETUP_FAILED");
|
|
2311
|
+
}
|
|
2312
|
+
throw new BridgeError("CODEX_FACTORY_ATTESTATION_INVALID", "The controller-owned Codex probe root could not be established safely.");
|
|
2313
|
+
}
|
|
2314
|
+
let cwd;
|
|
2315
|
+
let before;
|
|
2316
|
+
let result = codexWriteProbeFailure("CODEX_WRITE_PROBE_THREAD_SETUP_FAILED");
|
|
2317
|
+
try {
|
|
2318
|
+
cwd = await mkdtemp(path.join(probeRoot, CODEX_WRITE_PROBE_DIRECTORY_PREFIX));
|
|
2319
|
+
if (path.dirname(cwd) !== probeRoot) {
|
|
2320
|
+
throw new Error("probe directory escaped its dedicated root");
|
|
2321
|
+
}
|
|
2322
|
+
await chmod(cwd, 0o700);
|
|
2323
|
+
before = await attestCodexWriteProbeDirectory(cwd);
|
|
2324
|
+
result = await connector.runWriteCompatibilityProbe({
|
|
2325
|
+
cwd,
|
|
2326
|
+
forbiddenThreadIds: [
|
|
2327
|
+
...new Set([
|
|
2328
|
+
COMPATIBILITY_PROBE_THREAD_ID,
|
|
2329
|
+
...context.forbiddenCodexThreadIds,
|
|
2330
|
+
...this.trackedRoutes,
|
|
2331
|
+
]),
|
|
2332
|
+
],
|
|
2333
|
+
});
|
|
2334
|
+
}
|
|
2335
|
+
catch (error) {
|
|
2336
|
+
if (!safeCodexWriteProbeFilesystemFailure(error)) {
|
|
2337
|
+
if (error instanceof BridgeError)
|
|
2338
|
+
throw error;
|
|
2339
|
+
throw new BridgeError("CODEX_FACTORY_ATTESTATION_INVALID", "The disposable Codex probe directory could not be established safely.");
|
|
2340
|
+
}
|
|
2341
|
+
result = codexWriteProbeFailure("CODEX_WRITE_PROBE_THREAD_SETUP_FAILED");
|
|
2342
|
+
}
|
|
2343
|
+
let unchanged = false;
|
|
2344
|
+
let removed = false;
|
|
2345
|
+
if (cwd !== undefined && before !== undefined) {
|
|
2346
|
+
try {
|
|
2347
|
+
const after = await attestCodexWriteProbeDirectory(cwd);
|
|
2348
|
+
unchanged = sameCodexWriteProbeDirectory(before, after);
|
|
2349
|
+
await rmdir(cwd);
|
|
2350
|
+
try {
|
|
2351
|
+
await lstat(cwd);
|
|
2352
|
+
}
|
|
2353
|
+
catch (error) {
|
|
2354
|
+
removed = systemErrorCode(error) === "ENOENT";
|
|
2355
|
+
}
|
|
2356
|
+
}
|
|
2357
|
+
catch (error) {
|
|
2358
|
+
if (!safeCodexWriteProbeFilesystemFailure(error)) {
|
|
2359
|
+
if (error instanceof BridgeError)
|
|
2360
|
+
throw error;
|
|
2361
|
+
throw new BridgeError("CODEX_FACTORY_ATTESTATION_INVALID", "The disposable Codex probe directory could not be re-attested safely.");
|
|
2362
|
+
}
|
|
2363
|
+
// Never remove a path after its exact ownership evidence changed.
|
|
2364
|
+
}
|
|
2365
|
+
}
|
|
2366
|
+
if (cwd !== undefined && !removed) {
|
|
2367
|
+
result = codexWriteProbeFailure("CODEX_WRITE_PROBE_CLEANUP_UNCONFIRMED", result);
|
|
2368
|
+
}
|
|
2369
|
+
else if (cwd !== undefined && !unchanged) {
|
|
2370
|
+
result = codexWriteProbeFailure("CODEX_WRITE_PROBE_TOOL_ACTIVITY_OBSERVED", result);
|
|
2371
|
+
}
|
|
2372
|
+
const [probeRootAfter, rootAfter] = await Promise.all([
|
|
2373
|
+
attestCodexWriteProbeRoot(probeRoot),
|
|
2374
|
+
attestCodexWriteProbeRoot(stateRoot),
|
|
2375
|
+
]);
|
|
2376
|
+
if (probeRootAfter === null || rootAfter === null) {
|
|
2377
|
+
return codexWriteProbeFailure("CODEX_WRITE_PROBE_THREAD_SETUP_FAILED", result);
|
|
2378
|
+
}
|
|
2379
|
+
if (!sameCodexWriteProbeRoot(probeRootBefore, probeRootAfter) ||
|
|
2380
|
+
!sameCodexWriteProbeRoot(rootBefore, rootAfter)) {
|
|
2381
|
+
throw new BridgeError("CODEX_FACTORY_ATTESTATION_INVALID", "A controller-owned Codex probe root changed while the disposable probe ran.");
|
|
2382
|
+
}
|
|
2383
|
+
return result;
|
|
2384
|
+
}
|
|
2069
2385
|
async initialize(callbacks) {
|
|
2070
2386
|
if (this.closed || this.initialized) {
|
|
2071
2387
|
throw new BridgeError("CODEX_PROVIDER_INITIALIZATION_REJECTED", "The local Codex provider cannot be initialized in this state.");
|
|
@@ -2079,7 +2395,7 @@ export class LocalCodexGatewayProvider {
|
|
|
2079
2395
|
safeErrorCode: this.compatibilityFailureCode,
|
|
2080
2396
|
};
|
|
2081
2397
|
}
|
|
2082
|
-
return this.
|
|
2398
|
+
return !this.compatibilityAttested
|
|
2083
2399
|
? {
|
|
2084
2400
|
health: "degraded",
|
|
2085
2401
|
compatibility: "compatible",
|
|
@@ -2124,9 +2440,13 @@ export class LocalCodexGatewayProvider {
|
|
|
2124
2440
|
throw selectionError;
|
|
2125
2441
|
}
|
|
2126
2442
|
};
|
|
2443
|
+
const retainedEndpointRefresh = this.retainedEndpointRefreshResult();
|
|
2127
2444
|
if (this.endpointRefresh !== undefined) {
|
|
2128
2445
|
route = await stageRefreshedRoute(await this.refreshEndpoint("selector"));
|
|
2129
2446
|
}
|
|
2447
|
+
else if (retainedEndpointRefresh !== undefined) {
|
|
2448
|
+
route = await stageRefreshedRoute(retainedEndpointRefresh);
|
|
2449
|
+
}
|
|
2130
2450
|
else if (this.endpointUnavailable &&
|
|
2131
2451
|
this.pendingEndpointAttestation === undefined) {
|
|
2132
2452
|
route = await stageRefreshedRoute(await this.refreshEndpoint("selector"));
|
|
@@ -2259,10 +2579,6 @@ export class LocalCodexGatewayProvider {
|
|
|
2259
2579
|
route.endpointGeneration !== this.factory.endpointGeneration) {
|
|
2260
2580
|
return { state: "failed", safeErrorCode: "CODEX_ROUTE_UNAVAILABLE" };
|
|
2261
2581
|
}
|
|
2262
|
-
if (!this.factory.writableReady ||
|
|
2263
|
-
this.factory.writeCompatibility === null) {
|
|
2264
|
-
return { state: "failed", safeErrorCode: "CODEX_WRITES_DISABLED" };
|
|
2265
|
-
}
|
|
2266
2582
|
const guard = route.connector.guard();
|
|
2267
2583
|
if (!guard.writableReady) {
|
|
2268
2584
|
return { state: "failed", safeErrorCode: "CODEX_WRITES_DISABLED" };
|
|
@@ -2385,6 +2701,7 @@ export class LocalCodexGatewayProvider {
|
|
|
2385
2701
|
this.routesRequiringRecovery.clear();
|
|
2386
2702
|
this.clearEndpointActivationRetry();
|
|
2387
2703
|
this.pendingEndpointRefreshEvent = undefined;
|
|
2704
|
+
this.pendingEndpointRefreshSelectorClaimed = false;
|
|
2388
2705
|
this.pendingEndpointAttestation = undefined;
|
|
2389
2706
|
const refreshResult = this.endpointRefresh === undefined
|
|
2390
2707
|
? []
|
|
@@ -2413,6 +2730,7 @@ export class LocalCodexGatewayProvider {
|
|
|
2413
2730
|
this.callbackQueue.length = 0;
|
|
2414
2731
|
this.pendingEndpointAttestation = undefined;
|
|
2415
2732
|
this.pendingEndpointRefreshEvent = undefined;
|
|
2733
|
+
this.pendingEndpointRefreshSelectorClaimed = false;
|
|
2416
2734
|
this.trackedRoutes.clear();
|
|
2417
2735
|
this.routeAliases.clear();
|
|
2418
2736
|
this.expectsReply.clear();
|
|
@@ -2462,7 +2780,9 @@ export class LocalCodexGatewayProvider {
|
|
|
2462
2780
|
}
|
|
2463
2781
|
const admittedFactory = this.factory;
|
|
2464
2782
|
const creation = (async () => {
|
|
2465
|
-
const route = await this.createRoute(threadId, admittedFactory, queueInitialObservation
|
|
2783
|
+
const route = await this.createRoute(threadId, admittedFactory, queueInitialObservation, this.pendingEndpointAttestation === undefined
|
|
2784
|
+
? this.compatibilityAttested
|
|
2785
|
+
: codexFactoryWritesEnabled(admittedFactory, this.pendingEndpointAttestation, this.latestWriteCompatibilityProbeObservation(admittedFactory.endpointGeneration)?.outcome === "pass"));
|
|
2466
2786
|
if (this.closing || this.closed) {
|
|
2467
2787
|
await this.closeRoute(route);
|
|
2468
2788
|
throw new BridgeError("CODEX_PROVIDER_UNAVAILABLE", "The local Codex provider closed during route creation.");
|
|
@@ -2486,13 +2806,12 @@ export class LocalCodexGatewayProvider {
|
|
|
2486
2806
|
this.routeCreations.delete(threadId);
|
|
2487
2807
|
}
|
|
2488
2808
|
}
|
|
2489
|
-
async createRoute(threadId, factory, queueInitialObservation) {
|
|
2809
|
+
async createRoute(threadId, factory, queueInitialObservation, writesEnabled) {
|
|
2490
2810
|
let transport;
|
|
2491
2811
|
let connector;
|
|
2492
2812
|
const generation = Symbol(threadId);
|
|
2493
2813
|
try {
|
|
2494
2814
|
transport = await factory.connectTransport();
|
|
2495
|
-
const writesEnabled = factory.writableReady && factory.writeCompatibility !== null;
|
|
2496
2815
|
connector = await CodexAppServerConnector.connect({
|
|
2497
2816
|
compatibility: factory.writeCompatibility ?? factory.schemaCompatibility,
|
|
2498
2817
|
writesEnabled,
|
|
@@ -2663,6 +2982,21 @@ export class LocalCodexGatewayProvider {
|
|
|
2663
2982
|
event.current.endpointGeneration === this.factory.endpointGeneration &&
|
|
2664
2983
|
event.routes.some((route) => route.routeHandle === routeHandle));
|
|
2665
2984
|
}
|
|
2985
|
+
retainedEndpointRefreshResult() {
|
|
2986
|
+
const event = this.pendingEndpointRefreshEvent;
|
|
2987
|
+
if (event === undefined ||
|
|
2988
|
+
this.pendingEndpointRefreshSelectorClaimed ||
|
|
2989
|
+
this.pendingEndpointAttestation === undefined ||
|
|
2990
|
+
event.current.endpointGeneration !== this.factory.endpointGeneration) {
|
|
2991
|
+
return undefined;
|
|
2992
|
+
}
|
|
2993
|
+
return {
|
|
2994
|
+
event,
|
|
2995
|
+
delivery: "selector",
|
|
2996
|
+
selectorClaimed: false,
|
|
2997
|
+
published: true,
|
|
2998
|
+
};
|
|
2999
|
+
}
|
|
2666
3000
|
async refreshEndpoint(delivery) {
|
|
2667
3001
|
if (this.refreshFactory === undefined || this.closing || this.closed) {
|
|
2668
3002
|
return undefined;
|
|
@@ -2707,6 +3041,7 @@ export class LocalCodexGatewayProvider {
|
|
|
2707
3041
|
this.compatibilityAttested = false;
|
|
2708
3042
|
this.pendingEndpointAttestation = undefined;
|
|
2709
3043
|
this.pendingEndpointRefreshEvent = undefined;
|
|
3044
|
+
this.pendingEndpointRefreshSelectorClaimed = false;
|
|
2710
3045
|
for (const routeHandle of this.trackedRoutes) {
|
|
2711
3046
|
this.routesRequiringRecovery.add(routeHandle);
|
|
2712
3047
|
}
|
|
@@ -2721,7 +3056,9 @@ export class LocalCodexGatewayProvider {
|
|
|
2721
3056
|
},
|
|
2722
3057
|
delivery: "callback",
|
|
2723
3058
|
selectorClaimed: true,
|
|
3059
|
+
published: false,
|
|
2724
3060
|
};
|
|
3061
|
+
result.published = true;
|
|
2725
3062
|
this.emitEndpointRefresh(result.event);
|
|
2726
3063
|
for (const routeHandle of this.routesRequiringRecovery) {
|
|
2727
3064
|
if (this.trackedRoutes.has(routeHandle)) {
|
|
@@ -2742,7 +3079,7 @@ export class LocalCodexGatewayProvider {
|
|
|
2742
3079
|
if (this.closing || this.closed)
|
|
2743
3080
|
break;
|
|
2744
3081
|
try {
|
|
2745
|
-
const route = await this.createRoute(routeHandle, replacementFactory, false);
|
|
3082
|
+
const route = await this.createRoute(routeHandle, replacementFactory, false, codexFactoryWritesEnabled(replacementFactory, attestation, this.latestWriteCompatibilityProbeObservation(replacementFactory.endpointGeneration)?.outcome === "pass"));
|
|
2746
3083
|
if (this.closing || this.closed) {
|
|
2747
3084
|
await this.closeRoute(route);
|
|
2748
3085
|
break;
|
|
@@ -2790,12 +3127,15 @@ export class LocalCodexGatewayProvider {
|
|
|
2790
3127
|
},
|
|
2791
3128
|
delivery: this.endpointRefreshDelivery ?? "callback",
|
|
2792
3129
|
selectorClaimed: false,
|
|
3130
|
+
published: false,
|
|
2793
3131
|
};
|
|
2794
3132
|
if (result.event.outcome === "compatible") {
|
|
2795
3133
|
this.pendingEndpointRefreshEvent = result.event;
|
|
3134
|
+
this.pendingEndpointRefreshSelectorClaimed = false;
|
|
2796
3135
|
}
|
|
2797
3136
|
if (result.delivery === "callback") {
|
|
2798
3137
|
result.selectorClaimed = true;
|
|
3138
|
+
result.published = true;
|
|
2799
3139
|
this.publishEndpointRefresh(result.event);
|
|
2800
3140
|
}
|
|
2801
3141
|
for (const routeHandle of this.routesRequiringRecovery) {
|
|
@@ -2914,11 +3254,16 @@ export class LocalCodexGatewayProvider {
|
|
|
2914
3254
|
return false;
|
|
2915
3255
|
}
|
|
2916
3256
|
result.selectorClaimed = true;
|
|
3257
|
+
if (this.pendingEndpointRefreshEvent === result.event) {
|
|
3258
|
+
this.pendingEndpointRefreshSelectorClaimed = true;
|
|
3259
|
+
}
|
|
2917
3260
|
return true;
|
|
2918
3261
|
}
|
|
2919
3262
|
emitSelectorRefreshFallback(result, staleRouteHandle) {
|
|
2920
3263
|
if (result.delivery !== "selector" || !result.selectorClaimed)
|
|
2921
3264
|
return;
|
|
3265
|
+
if (result.published)
|
|
3266
|
+
return;
|
|
2922
3267
|
let event = result.event;
|
|
2923
3268
|
if (staleRouteHandle !== undefined &&
|
|
2924
3269
|
event.outcome === "compatible" &&
|
|
@@ -2930,6 +3275,7 @@ export class LocalCodexGatewayProvider {
|
|
|
2930
3275
|
result.event = event;
|
|
2931
3276
|
this.pendingEndpointRefreshEvent = event;
|
|
2932
3277
|
}
|
|
3278
|
+
result.published = true;
|
|
2933
3279
|
this.publishEndpointRefresh(event);
|
|
2934
3280
|
}
|
|
2935
3281
|
publishEndpointRefresh(event) {
|