agent-embassy 1.2.0 → 1.4.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 +41 -0
- package/CONTRIBUTING.md +50 -16
- package/README.md +57 -25
- package/README.zh-CN.md +45 -23
- package/SECURITY.md +68 -48
- package/dist/src/gateway/claude-helper-protocol.d.ts +1 -0
- package/dist/src/gateway/claude-helper-protocol.js +4 -2
- package/dist/src/gateway/claude-helper-protocol.js.map +1 -1
- package/dist/src/gateway/claude-helper-supervisor.d.ts +1 -0
- package/dist/src/gateway/claude-helper-supervisor.js +3 -1
- package/dist/src/gateway/claude-helper-supervisor.js.map +1 -1
- package/dist/src/gateway/claude-runtime.d.ts +0 -2
- package/dist/src/gateway/claude-runtime.js +6 -17
- package/dist/src/gateway/claude-runtime.js.map +1 -1
- package/dist/src/gateway/cli-copy.d.ts +1 -1
- package/dist/src/gateway/cli-copy.en.d.ts +4 -1
- package/dist/src/gateway/cli-copy.en.js +7 -3
- package/dist/src/gateway/cli-copy.en.js.map +1 -1
- package/dist/src/gateway/cli-copy.js +3 -0
- package/dist/src/gateway/cli-copy.js.map +1 -1
- package/dist/src/gateway/cli-copy.zh-CN.d.ts +4 -1
- package/dist/src/gateway/cli-copy.zh-CN.js +7 -3
- package/dist/src/gateway/cli-copy.zh-CN.js.map +1 -1
- package/dist/src/gateway/cli.d.ts +2 -5
- package/dist/src/gateway/cli.js +41 -67
- package/dist/src/gateway/cli.js.map +1 -1
- package/dist/src/gateway/codex-app-server.d.ts +2 -8
- package/dist/src/gateway/codex-app-server.js +36 -62
- package/dist/src/gateway/codex-app-server.js.map +1 -1
- package/dist/src/gateway/codex-local-transport.d.ts +4 -10
- package/dist/src/gateway/codex-local-transport.js +13 -16
- package/dist/src/gateway/codex-local-transport.js.map +1 -1
- package/dist/src/gateway/compatibility.d.ts +0 -27
- package/dist/src/gateway/compatibility.js +1 -101
- package/dist/src/gateway/compatibility.js.map +1 -1
- package/dist/src/gateway/config.d.ts +1 -4
- package/dist/src/gateway/config.js +0 -10
- package/dist/src/gateway/config.js.map +1 -1
- package/dist/src/gateway/control.d.ts +2 -21
- package/dist/src/gateway/control.js +7 -36
- 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 +21 -8
- package/dist/src/gateway/dashboard-copy.en.js +40 -27
- package/dist/src/gateway/dashboard-copy.en.js.map +1 -1
- package/dist/src/gateway/dashboard-copy.js +21 -8
- package/dist/src/gateway/dashboard-copy.js.map +1 -1
- package/dist/src/gateway/dashboard-copy.zh-CN.d.ts +21 -8
- package/dist/src/gateway/dashboard-copy.zh-CN.js +40 -27
- package/dist/src/gateway/dashboard-copy.zh-CN.js.map +1 -1
- package/dist/src/gateway/dashboard-model.d.ts +4 -9
- package/dist/src/gateway/dashboard-model.js +159 -46
- package/dist/src/gateway/dashboard-model.js.map +1 -1
- package/dist/src/gateway/dashboard.js +27 -16
- package/dist/src/gateway/dashboard.js.map +1 -1
- package/dist/src/gateway/instance-lease.d.ts +2 -3
- package/dist/src/gateway/instance-lease.js +17 -140
- package/dist/src/gateway/instance-lease.js.map +1 -1
- package/dist/src/gateway/live-dashboard-app/app.js +62 -63
- package/dist/src/gateway/live-dashboard-command.d.ts +5 -1
- package/dist/src/gateway/live-dashboard-command.js +8 -6
- package/dist/src/gateway/live-dashboard-command.js.map +1 -1
- package/dist/src/gateway/live-dashboard-http.d.ts +0 -4
- package/dist/src/gateway/live-dashboard-http.js +20 -94
- package/dist/src/gateway/live-dashboard-http.js.map +1 -1
- package/dist/src/gateway/live-dashboard-protocol.d.ts +1 -5
- package/dist/src/gateway/live-dashboard-protocol.js +0 -42
- package/dist/src/gateway/live-dashboard-protocol.js.map +1 -1
- package/dist/src/gateway/live-dashboard-server.d.ts +2 -2
- package/dist/src/gateway/live-dashboard-server.js +18 -4
- package/dist/src/gateway/live-dashboard-server.js.map +1 -1
- package/dist/src/gateway/live-dashboard.d.ts +3 -6
- package/dist/src/gateway/live-dashboard.js +6 -42
- package/dist/src/gateway/live-dashboard.js.map +1 -1
- package/dist/src/gateway/progress-watch-machine.d.ts +6 -5
- package/dist/src/gateway/progress-watch-machine.js +6 -6
- package/dist/src/gateway/progress-watch-machine.js.map +1 -1
- package/dist/src/gateway/provenance-envelope.d.ts +1 -0
- package/dist/src/gateway/provenance-envelope.js +11 -4
- package/dist/src/gateway/provenance-envelope.js.map +1 -1
- package/dist/src/gateway/providers.d.ts +2 -21
- package/dist/src/gateway/providers.js +21 -261
- package/dist/src/gateway/providers.js.map +1 -1
- package/dist/src/gateway/server.js +0 -3
- package/dist/src/gateway/server.js.map +1 -1
- package/dist/src/gateway/service.d.ts +5 -15
- package/dist/src/gateway/service.js +101 -265
- package/dist/src/gateway/service.js.map +1 -1
- package/dist/src/gateway/store.d.ts +9 -21
- package/dist/src/gateway/store.js +360 -140
- package/dist/src/gateway/store.js.map +1 -1
- package/dist/src/gateway/types.d.ts +1 -6
- package/dist/src/gateway/types.js.map +1 -1
- package/docs/CONFIGURATION.md +102 -0
- package/docs/CONFIGURATION.zh-CN.md +72 -0
- package/docs/DASHBOARD.md +103 -0
- package/docs/DASHBOARD.zh-CN.md +47 -0
- package/docs/DELIVERY.md +84 -0
- package/docs/DELIVERY.zh-CN.md +53 -0
- package/docs/GATEWAY-ARCHITECTURE.md +249 -153
- package/package.json +8 -4
- package/skills/embassy-peer/SKILL.md +25 -10
- package/dist/src/gateway/claude-compatibility-scratch.d.ts +0 -18
- package/dist/src/gateway/claude-compatibility-scratch.js +0 -158
- package/dist/src/gateway/claude-compatibility-scratch.js.map +0 -1
- package/dist/src/gateway/live-dashboard-bootstrap.d.ts +0 -36
- package/dist/src/gateway/live-dashboard-bootstrap.js +0 -176
- package/dist/src/gateway/live-dashboard-bootstrap.js.map +0 -1
- package/docs/DESIGN.md +0 -134
|
@@ -1,8 +1,7 @@
|
|
|
1
|
-
import { createHash
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import { BridgeError } from "../errors.js";
|
|
4
4
|
import { CLAUDE_PEER_COMPATIBILITY, ClaudePeerAdapter, } from "./claude-peer.js";
|
|
5
|
-
import { startClaudeCompatibilityScratch, } from "./claude-compatibility-scratch.js";
|
|
6
5
|
import { certifiedCompatibilityVersions, compatibilityProbeNames, evaluateCompatibilityAttestation, sharesCompatibilityMajor, } from "./compatibility.js";
|
|
7
6
|
import { CODEX_APP_SERVER_WRITABLE_VERSIONS, CodexAppServerConnector, CodexConnectorError, } from "./codex-app-server.js";
|
|
8
7
|
import { LocalCodexTransportError } from "./codex-local-transport.js";
|
|
@@ -25,9 +24,6 @@ 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
26
|
const COMPATIBILITY_PROBE_THREAD_ID = "00000000-0000-7000-8000-000000000000";
|
|
28
|
-
const COMPATIBILITY_TURN_TIMEOUT_MS = 60_000;
|
|
29
|
-
const CLAUDE_CERTIFICATION_TIMEOUT_MS = 10_000;
|
|
30
|
-
const CLAUDE_CERTIFICATION_DISCOVERY_POLL_MS = 100;
|
|
31
27
|
const CLAUDE_CLEAN_PREWRITE_RETRY_CODES = new Set([
|
|
32
28
|
"CLAUDE_PEER_TARGET_UNKNOWN",
|
|
33
29
|
"CLAUDE_PEER_TARGET_STALE",
|
|
@@ -109,7 +105,8 @@ function invokeCallback(operation) {
|
|
|
109
105
|
}
|
|
110
106
|
}
|
|
111
107
|
function validateAttestedClaudeRuntime(runtime) {
|
|
112
|
-
if (
|
|
108
|
+
if (runtime.claudeCodeVersion !==
|
|
109
|
+
CLAUDE_PEER_COMPATIBILITY.claudeCodeVersion ||
|
|
113
110
|
[
|
|
114
111
|
runtime.claudeExecutable,
|
|
115
112
|
runtime.sessionsDir,
|
|
@@ -146,7 +143,6 @@ export class LocalClaudeGatewayProvider {
|
|
|
146
143
|
now;
|
|
147
144
|
selectedStateRoots = new Map();
|
|
148
145
|
nativeHelpers;
|
|
149
|
-
compatibilityScratchFactory;
|
|
150
146
|
discovered = new Map();
|
|
151
147
|
selected = new Map();
|
|
152
148
|
/** Exact live same-UID sessions observed through native inbound frames. */
|
|
@@ -179,7 +175,6 @@ export class LocalClaudeGatewayProvider {
|
|
|
179
175
|
retiredNativeCodexListener;
|
|
180
176
|
/** Fences discovery callbacks and monitor restarts across listener rotation. */
|
|
181
177
|
nativeCodexSuccessionFreeze;
|
|
182
|
-
compatibilityCertificationInFlight = false;
|
|
183
178
|
initialized = false;
|
|
184
179
|
closed = false;
|
|
185
180
|
constructor(options) {
|
|
@@ -205,9 +200,6 @@ export class LocalClaudeGatewayProvider {
|
|
|
205
200
|
locale,
|
|
206
201
|
deliveryNotices,
|
|
207
202
|
})))(options.runtime, options.locale ?? "en", options.deliveryNotices ?? "merged");
|
|
208
|
-
this.compatibilityScratchFactory =
|
|
209
|
-
options.compatibilityScratchFactory ??
|
|
210
|
-
(() => startClaudeCompatibilityScratch(options.runtime));
|
|
211
203
|
this.nativeHelpers =
|
|
212
204
|
options.nativeHelpers === undefined
|
|
213
205
|
? undefined
|
|
@@ -258,90 +250,6 @@ export class LocalClaudeGatewayProvider {
|
|
|
258
250
|
];
|
|
259
251
|
}
|
|
260
252
|
}
|
|
261
|
-
async runCompatibilityCertification(input) {
|
|
262
|
-
void input.routeHandle;
|
|
263
|
-
void input.withTurn;
|
|
264
|
-
const depth = "wire";
|
|
265
|
-
let scratch;
|
|
266
|
-
let listener;
|
|
267
|
-
if (this.compatibilityCertificationInFlight) {
|
|
268
|
-
return {
|
|
269
|
-
depth,
|
|
270
|
-
outcome: "fail",
|
|
271
|
-
certifiedAt: new Date(this.now()).toISOString(),
|
|
272
|
-
safeErrorCode: "CLAUDE_CERTIFICATION_BUSY",
|
|
273
|
-
};
|
|
274
|
-
}
|
|
275
|
-
this.compatibilityCertificationInFlight = true;
|
|
276
|
-
try {
|
|
277
|
-
this.assertReady();
|
|
278
|
-
scratch = await this.compatibilityScratchFactory();
|
|
279
|
-
const deadlineAt = this.now() + CLAUDE_CERTIFICATION_TIMEOUT_MS;
|
|
280
|
-
let targetId;
|
|
281
|
-
while (this.now() < deadlineAt) {
|
|
282
|
-
scratch.assertRunning();
|
|
283
|
-
const discovery = await this.peer.discover();
|
|
284
|
-
if (discovery.truncated) {
|
|
285
|
-
throw new BridgeError("CLAUDE_CERTIFICATION_DISCOVERY_INCOMPLETE", "The scratch session could not be proven from a complete registry scan.", true);
|
|
286
|
-
}
|
|
287
|
-
const matches = discovery.peers.filter((peer) => peer.alias === scratch?.name && peer.targetId === scratch?.sessionId);
|
|
288
|
-
if (matches.length > 1) {
|
|
289
|
-
throw new BridgeError("CLAUDE_CERTIFICATION_TARGET_COLLISION", "The scratch session identity was not unique.");
|
|
290
|
-
}
|
|
291
|
-
if (matches.length === 1) {
|
|
292
|
-
targetId = matches[0]?.targetId;
|
|
293
|
-
break;
|
|
294
|
-
}
|
|
295
|
-
await new Promise((resolve) => {
|
|
296
|
-
const timer = setTimeout(resolve, CLAUDE_CERTIFICATION_DISCOVERY_POLL_MS);
|
|
297
|
-
timer.unref();
|
|
298
|
-
});
|
|
299
|
-
}
|
|
300
|
-
if (targetId === undefined) {
|
|
301
|
-
throw new BridgeError("CLAUDE_CERTIFICATION_TARGET_UNAVAILABLE", "The bounded scratch session did not publish a discoverable peer record.", true);
|
|
302
|
-
}
|
|
303
|
-
await this.peer.assertTargetWorkspaceDisjoint(targetId, input.controllerStateRoot);
|
|
304
|
-
listener = await this.peer.listen({
|
|
305
|
-
onMessage: () => undefined,
|
|
306
|
-
onReceipt: () => undefined,
|
|
307
|
-
});
|
|
308
|
-
const sent = await this.peer.send(targetId, "[embassy compat-certify] ignore", {
|
|
309
|
-
listener,
|
|
310
|
-
receiptDeadlineAt: deadlineAt,
|
|
311
|
-
});
|
|
312
|
-
if (sent.transportStatus !== "transport_written" ||
|
|
313
|
-
sent.receiptStatus !== "pending") {
|
|
314
|
-
throw new BridgeError("CLAUDE_CERTIFICATION_WRITE_UNCONFIRMED", "The scratch diagnostic frame was not accepted for receipt tracking.", true);
|
|
315
|
-
}
|
|
316
|
-
// A direct `crossSessionInbound: accept` write emits no native
|
|
317
|
-
// peer_message_status frame. That status family describes the optional
|
|
318
|
-
// approval lifecycle, not universal delivery acknowledgement. For this
|
|
319
|
-
// wire-depth certification, the strongest portable evidence is the
|
|
320
|
-
// confirmed write to the exact, uniquely discovered scratch session.
|
|
321
|
-
return {
|
|
322
|
-
depth,
|
|
323
|
-
outcome: "pass",
|
|
324
|
-
certifiedAt: new Date(this.now()).toISOString(),
|
|
325
|
-
};
|
|
326
|
-
}
|
|
327
|
-
catch (error) {
|
|
328
|
-
const observedCode = error instanceof BridgeError && /^[A-Z][A-Z0-9_]{0,63}$/.test(error.code)
|
|
329
|
-
? error.code
|
|
330
|
-
: "CLAUDE_CERTIFICATION_FAILED";
|
|
331
|
-
return {
|
|
332
|
-
depth,
|
|
333
|
-
outcome: "fail",
|
|
334
|
-
certifiedAt: new Date(this.now()).toISOString(),
|
|
335
|
-
safeErrorCode: observedCode,
|
|
336
|
-
};
|
|
337
|
-
}
|
|
338
|
-
finally {
|
|
339
|
-
await listener?.close().catch(() => undefined);
|
|
340
|
-
await scratch?.close().catch(() => undefined);
|
|
341
|
-
await this.peer.discover().catch(() => undefined);
|
|
342
|
-
this.compatibilityCertificationInFlight = false;
|
|
343
|
-
}
|
|
344
|
-
}
|
|
345
253
|
async initialize(callbacks) {
|
|
346
254
|
if (this.closed) {
|
|
347
255
|
throw new BridgeError("CLAUDE_PROVIDER_CLOSED", "The local Claude provider is closed.");
|
|
@@ -871,9 +779,7 @@ export class LocalClaudeGatewayProvider {
|
|
|
871
779
|
const selectedAlias = this.selected.get(input.binding.routeHandle);
|
|
872
780
|
const stateRoot = this.selectedStateRoots.get(input.binding.routeHandle);
|
|
873
781
|
if (input.authorization === "selected_route" &&
|
|
874
|
-
(selectedAlias === undefined ||
|
|
875
|
-
selectedAlias !== input.targetAlias ||
|
|
876
|
-
stateRoot === undefined)) {
|
|
782
|
+
(selectedAlias === undefined || stateRoot === undefined)) {
|
|
877
783
|
return { state: "failed", safeErrorCode: "CLAUDE_ROUTE_UNAVAILABLE" };
|
|
878
784
|
}
|
|
879
785
|
if (input.authorization === "selected_route") {
|
|
@@ -919,29 +825,17 @@ export class LocalClaudeGatewayProvider {
|
|
|
919
825
|
return { state: "failed", safeErrorCode: "CLAUDE_ROUTE_UNAVAILABLE" };
|
|
920
826
|
}
|
|
921
827
|
if (input.authorization === "native_reply") {
|
|
922
|
-
try {
|
|
923
|
-
await this.refreshClaudeDiscovery();
|
|
924
|
-
}
|
|
925
|
-
catch {
|
|
926
|
-
return { state: "failed", safeErrorCode: "CLAUDE_ROUTE_UNAVAILABLE" };
|
|
927
|
-
}
|
|
928
828
|
const observedRoute = this.nativeInbound.get(input.binding.routeHandle);
|
|
929
|
-
const current = this.discovered.get(input.binding.routeHandle);
|
|
930
829
|
if (observedRoute === undefined ||
|
|
931
|
-
observedRoute.listenerGeneration !== activeListener.generation
|
|
932
|
-
current?.alias !== observedRoute.alias) {
|
|
830
|
+
observedRoute.listenerGeneration !== activeListener.generation) {
|
|
933
831
|
this.nativeInbound.delete(input.binding.routeHandle);
|
|
934
832
|
return { state: "failed", safeErrorCode: "CLAUDE_NATIVE_REPLY_STALE" };
|
|
935
833
|
}
|
|
936
|
-
if (observedRoute.alias !== input.targetAlias) {
|
|
937
|
-
return { state: "failed", safeErrorCode: "CLAUDE_ROUTE_UNAVAILABLE" };
|
|
938
|
-
}
|
|
939
834
|
}
|
|
940
835
|
else {
|
|
941
836
|
const selectedAlias = this.selected.get(input.binding.routeHandle);
|
|
942
837
|
const stateRoot = this.selectedStateRoots.get(input.binding.routeHandle);
|
|
943
838
|
if (selectedAlias === undefined ||
|
|
944
|
-
selectedAlias !== input.targetAlias ||
|
|
945
839
|
stateRoot === undefined) {
|
|
946
840
|
return { state: "failed", safeErrorCode: "CLAUDE_ROUTE_UNAVAILABLE" };
|
|
947
841
|
}
|
|
@@ -970,6 +864,9 @@ export class LocalClaudeGatewayProvider {
|
|
|
970
864
|
targetAlias: input.targetAlias,
|
|
971
865
|
conversationId: input.conversationId,
|
|
972
866
|
body: input.text,
|
|
867
|
+
...(input.progressWatchActive === true
|
|
868
|
+
? { progressWatchActive: true }
|
|
869
|
+
: {}),
|
|
973
870
|
});
|
|
974
871
|
}
|
|
975
872
|
catch (error) {
|
|
@@ -987,8 +884,8 @@ export class LocalClaudeGatewayProvider {
|
|
|
987
884
|
}
|
|
988
885
|
this.selectedDispatchEpoch.set(input.binding.routeHandle, (this.selectedDispatchEpoch.get(input.binding.routeHandle) ?? 0) + 1);
|
|
989
886
|
// The exact workspace attestation is refreshed before every send. A
|
|
990
|
-
// clean failure
|
|
991
|
-
//
|
|
887
|
+
// clean prewrite failure remains retryable within the message deadline;
|
|
888
|
+
// retry timing does not depend on the model turn's observed state.
|
|
992
889
|
this.selectedObservationDirty.add(input.binding.routeHandle);
|
|
993
890
|
this.emitClaudeRouteObservation(input.binding.routeHandle, "busy");
|
|
994
891
|
try {
|
|
@@ -1558,29 +1455,16 @@ export class LocalClaudeGatewayProvider {
|
|
|
1558
1455
|
this.discovered.set(peer.targetId, row);
|
|
1559
1456
|
rows.push(row);
|
|
1560
1457
|
}
|
|
1561
|
-
for (const [routeHandle, route] of this.nativeInbound) {
|
|
1562
|
-
const current = this.discovered.get(routeHandle);
|
|
1563
|
-
if (current !== undefined) {
|
|
1564
|
-
// The stable session UUID owns native reply authority. A rename only
|
|
1565
|
-
// updates its current public coordinate.
|
|
1566
|
-
this.nativeInbound.set(routeHandle, {
|
|
1567
|
-
alias: current.alias,
|
|
1568
|
-
listenerGeneration: route.listenerGeneration,
|
|
1569
|
-
});
|
|
1570
|
-
}
|
|
1571
|
-
else if (!discovery.truncated) {
|
|
1572
|
-
this.nativeInbound.delete(routeHandle);
|
|
1573
|
-
}
|
|
1574
|
-
}
|
|
1575
1458
|
for (const [routeHandle, selectedAlias] of this.selected) {
|
|
1576
1459
|
const row = this.discovered.get(routeHandle);
|
|
1577
1460
|
if (row === undefined) {
|
|
1578
1461
|
this.emitClaudeRouteObservation(routeHandle, "busy", "CLAUDE_PEER_NOT_OBSERVED", true);
|
|
1579
1462
|
}
|
|
1580
1463
|
else {
|
|
1581
|
-
if (row.alias !== selectedAlias) {
|
|
1464
|
+
if (!discovery.truncated && row.alias !== selectedAlias) {
|
|
1582
1465
|
// A Claude rename changes only the live name index. The native
|
|
1583
|
-
// session UUID remains the selected logical route.
|
|
1466
|
+
// session UUID remains the selected logical route. An incomplete
|
|
1467
|
+
// scan is display evidence only and cannot rename dispatch authority.
|
|
1584
1468
|
this.selected.set(routeHandle, row.alias);
|
|
1585
1469
|
}
|
|
1586
1470
|
if (row.state === "idle" &&
|
|
@@ -1665,7 +1549,8 @@ function validateCodexFactory(factory) {
|
|
|
1665
1549
|
if (factory.hostId !== LOCAL_HOST ||
|
|
1666
1550
|
factory.protocol !== "codex-app-server" ||
|
|
1667
1551
|
factory.protocolVersion !== factory.appServerVersion ||
|
|
1668
|
-
(!certified &&
|
|
1552
|
+
(!certified &&
|
|
1553
|
+
(!schemaCandidate || write !== null || factory.writableReady)) ||
|
|
1669
1554
|
schema.appServerVersion !== factory.appServerVersion ||
|
|
1670
1555
|
schema.endpointGeneration !== factory.endpointGeneration ||
|
|
1671
1556
|
schema.protocol !== "app-server-v2-stable" ||
|
|
@@ -1714,7 +1599,6 @@ function codexRouteSafeCode(observation) {
|
|
|
1714
1599
|
export class LocalCodexGatewayProvider {
|
|
1715
1600
|
factory;
|
|
1716
1601
|
refreshFactory;
|
|
1717
|
-
compatibilityPolicy;
|
|
1718
1602
|
maxCallbacks;
|
|
1719
1603
|
maxRoutes;
|
|
1720
1604
|
maxReplyBytes;
|
|
@@ -1735,8 +1619,6 @@ export class LocalCodexGatewayProvider {
|
|
|
1735
1619
|
trackedRoutes = new Set();
|
|
1736
1620
|
retiredEndpointGeneration;
|
|
1737
1621
|
expectsReply = new Map();
|
|
1738
|
-
compatibilityCertifyingRoutes = new Set();
|
|
1739
|
-
compatibilityTurns = new Map();
|
|
1740
1622
|
callbackQueue = [];
|
|
1741
1623
|
callbackScheduled = false;
|
|
1742
1624
|
callbacks;
|
|
@@ -1756,8 +1638,6 @@ export class LocalCodexGatewayProvider {
|
|
|
1756
1638
|
validateCodexFactory(options.factory);
|
|
1757
1639
|
this.factory = options.factory;
|
|
1758
1640
|
this.refreshFactory = options.refreshFactory;
|
|
1759
|
-
this.compatibilityPolicy =
|
|
1760
|
-
options.compatibilityPolicy ?? options.factory.compatibilityPolicy;
|
|
1761
1641
|
exactLocalHost(options.factory.hostId);
|
|
1762
1642
|
this.compatibilityAttested = CODEX_APP_SERVER_WRITABLE_VERSIONS.includes(options.factory.appServerVersion);
|
|
1763
1643
|
this.maxCallbacks = positiveBounded(options.maxCallbackEvents, MAX_CODEX_CALLBACKS, 4_096);
|
|
@@ -1795,7 +1675,8 @@ export class LocalCodexGatewayProvider {
|
|
|
1795
1675
|
const pending = this.pendingEndpointAttestation;
|
|
1796
1676
|
if (attestation.surface !== "codex" ||
|
|
1797
1677
|
attestation.version !== this.factory.appServerVersion ||
|
|
1798
|
-
attestation.tier
|
|
1678
|
+
attestation.tier !== "certified" ||
|
|
1679
|
+
!CODEX_APP_SERVER_WRITABLE_VERSIONS.includes(attestation.version) ||
|
|
1799
1680
|
(this.endpointUnavailable && pending === undefined) ||
|
|
1800
1681
|
(pending !== undefined &&
|
|
1801
1682
|
(attestation.checkedAt !== pending.checkedAt ||
|
|
@@ -1885,107 +1766,6 @@ export class LocalCodexGatewayProvider {
|
|
|
1885
1766
|
}
|
|
1886
1767
|
}
|
|
1887
1768
|
}
|
|
1888
|
-
async runCompatibilityCertification(input) {
|
|
1889
|
-
void input.controllerStateRoot;
|
|
1890
|
-
const certifiedAt = this.now().toISOString();
|
|
1891
|
-
const depth = input.withTurn ? "turn" : "thread_ops";
|
|
1892
|
-
const routeHandle = input.routeHandle;
|
|
1893
|
-
if (routeHandle === undefined || this.compatibilityCertifyingRoutes.has(routeHandle)) {
|
|
1894
|
-
return {
|
|
1895
|
-
depth,
|
|
1896
|
-
outcome: "fail",
|
|
1897
|
-
certifiedAt,
|
|
1898
|
-
safeErrorCode: "CODEX_CERTIFICATION_ROUTE_REQUIRED",
|
|
1899
|
-
};
|
|
1900
|
-
}
|
|
1901
|
-
this.assertReady();
|
|
1902
|
-
const route = this.routes.get(routeHandle);
|
|
1903
|
-
if (route === undefined || !this.observeRouteSuccessionBarrier(routeHandle).clean) {
|
|
1904
|
-
return {
|
|
1905
|
-
depth,
|
|
1906
|
-
outcome: "fail",
|
|
1907
|
-
certifiedAt,
|
|
1908
|
-
safeErrorCode: "CODEX_CERTIFICATION_ROUTE_BUSY",
|
|
1909
|
-
};
|
|
1910
|
-
}
|
|
1911
|
-
this.compatibilityCertifyingRoutes.add(routeHandle);
|
|
1912
|
-
try {
|
|
1913
|
-
const loaded = await route.connector.observeLoadedThread(route.connector.guard());
|
|
1914
|
-
if (!loaded.selectedThreadLoaded) {
|
|
1915
|
-
return {
|
|
1916
|
-
depth,
|
|
1917
|
-
outcome: "fail",
|
|
1918
|
-
certifiedAt: this.now().toISOString(),
|
|
1919
|
-
safeErrorCode: "CODEX_CERTIFICATION_THREAD_UNAVAILABLE",
|
|
1920
|
-
};
|
|
1921
|
-
}
|
|
1922
|
-
await route.connector.certifyThreadOperations(route.connector.guard());
|
|
1923
|
-
if (!input.withTurn) {
|
|
1924
|
-
return {
|
|
1925
|
-
depth,
|
|
1926
|
-
outcome: "pass",
|
|
1927
|
-
certifiedAt: this.now().toISOString(),
|
|
1928
|
-
};
|
|
1929
|
-
}
|
|
1930
|
-
const messageId = `compat_${randomBytes(12).toString("hex")}`;
|
|
1931
|
-
const result = new Promise((resolve) => {
|
|
1932
|
-
const timer = setTimeout(() => {
|
|
1933
|
-
this.compatibilityTurns.delete(messageId);
|
|
1934
|
-
resolve({
|
|
1935
|
-
messageId,
|
|
1936
|
-
outcome: "expired",
|
|
1937
|
-
replyCode: "REPLY_UNAVAILABLE",
|
|
1938
|
-
text: null,
|
|
1939
|
-
});
|
|
1940
|
-
}, COMPATIBILITY_TURN_TIMEOUT_MS);
|
|
1941
|
-
timer.unref();
|
|
1942
|
-
this.compatibilityTurns.set(messageId, { resolve, timer });
|
|
1943
|
-
});
|
|
1944
|
-
try {
|
|
1945
|
-
const disposition = await route.connector.submitMessage(route.connector.guard(), {
|
|
1946
|
-
deadlineAt: new Date(this.now().getTime() + COMPATIBILITY_TURN_TIMEOUT_MS).toISOString(),
|
|
1947
|
-
messageId,
|
|
1948
|
-
text: "reply OK",
|
|
1949
|
-
});
|
|
1950
|
-
if (disposition.disposition !== "started") {
|
|
1951
|
-
throw new BridgeError("CODEX_CERTIFICATION_TURN_NOT_STARTED", "The bounded certification turn did not start on the exact idle route.");
|
|
1952
|
-
}
|
|
1953
|
-
const settled = await result;
|
|
1954
|
-
if (settled.outcome !== "completed" ||
|
|
1955
|
-
settled.text?.trim() !== "OK") {
|
|
1956
|
-
return {
|
|
1957
|
-
depth,
|
|
1958
|
-
outcome: "fail",
|
|
1959
|
-
certifiedAt: this.now().toISOString(),
|
|
1960
|
-
safeErrorCode: "CODEX_CERTIFICATION_TURN_FAILED",
|
|
1961
|
-
};
|
|
1962
|
-
}
|
|
1963
|
-
return {
|
|
1964
|
-
depth,
|
|
1965
|
-
outcome: "pass",
|
|
1966
|
-
certifiedAt: this.now().toISOString(),
|
|
1967
|
-
};
|
|
1968
|
-
}
|
|
1969
|
-
finally {
|
|
1970
|
-
const pending = this.compatibilityTurns.get(messageId);
|
|
1971
|
-
if (pending !== undefined) {
|
|
1972
|
-
clearTimeout(pending.timer);
|
|
1973
|
-
this.compatibilityTurns.delete(messageId);
|
|
1974
|
-
}
|
|
1975
|
-
}
|
|
1976
|
-
}
|
|
1977
|
-
catch {
|
|
1978
|
-
return {
|
|
1979
|
-
depth,
|
|
1980
|
-
outcome: "fail",
|
|
1981
|
-
certifiedAt: this.now().toISOString(),
|
|
1982
|
-
safeErrorCode: "CODEX_CERTIFICATION_FAILED",
|
|
1983
|
-
};
|
|
1984
|
-
}
|
|
1985
|
-
finally {
|
|
1986
|
-
this.compatibilityCertifyingRoutes.delete(routeHandle);
|
|
1987
|
-
}
|
|
1988
|
-
}
|
|
1989
1769
|
async initialize(callbacks) {
|
|
1990
1770
|
if (this.closed || this.initialized) {
|
|
1991
1771
|
throw new BridgeError("CODEX_PROVIDER_INITIALIZATION_REJECTED", "The local Codex provider cannot be initialized in this state.");
|
|
@@ -2172,9 +1952,6 @@ export class LocalCodexGatewayProvider {
|
|
|
2172
1952
|
route.endpointGeneration !== this.factory.endpointGeneration) {
|
|
2173
1953
|
return { state: "failed", safeErrorCode: "CODEX_ROUTE_UNAVAILABLE" };
|
|
2174
1954
|
}
|
|
2175
|
-
if (this.compatibilityCertifyingRoutes.has(input.binding.routeHandle)) {
|
|
2176
|
-
return { state: "deferred", safeErrorCode: "CODEX_ROUTE_HELD" };
|
|
2177
|
-
}
|
|
2178
1955
|
if (!this.factory.writableReady ||
|
|
2179
1956
|
this.factory.writeCompatibility === null) {
|
|
2180
1957
|
return { state: "failed", safeErrorCode: "CODEX_WRITES_DISABLED" };
|
|
@@ -2197,6 +1974,9 @@ export class LocalCodexGatewayProvider {
|
|
|
2197
1974
|
targetAlias: input.targetAlias,
|
|
2198
1975
|
conversationId: input.conversationId,
|
|
2199
1976
|
body: input.text,
|
|
1977
|
+
...(input.progressWatchActive === true
|
|
1978
|
+
? { progressWatchActive: true }
|
|
1979
|
+
: {}),
|
|
2200
1980
|
});
|
|
2201
1981
|
}
|
|
2202
1982
|
catch (error) {
|
|
@@ -2321,17 +2101,6 @@ export class LocalCodexGatewayProvider {
|
|
|
2321
2101
|
this.closed = true;
|
|
2322
2102
|
this.closing = false;
|
|
2323
2103
|
this.callbackQueue.length = 0;
|
|
2324
|
-
for (const [messageId, pending] of this.compatibilityTurns) {
|
|
2325
|
-
clearTimeout(pending.timer);
|
|
2326
|
-
pending.resolve({
|
|
2327
|
-
messageId,
|
|
2328
|
-
outcome: "abandoned",
|
|
2329
|
-
replyCode: "REPLY_UNAVAILABLE",
|
|
2330
|
-
text: null,
|
|
2331
|
-
});
|
|
2332
|
-
}
|
|
2333
|
-
this.compatibilityTurns.clear();
|
|
2334
|
-
this.compatibilityCertifyingRoutes.clear();
|
|
2335
2104
|
this.pendingEndpointAttestation = undefined;
|
|
2336
2105
|
this.pendingEndpointRefreshEvent = undefined;
|
|
2337
2106
|
this.trackedRoutes.clear();
|
|
@@ -2723,8 +2492,7 @@ export class LocalCodexGatewayProvider {
|
|
|
2723
2492
|
candidate = await refreshFactory();
|
|
2724
2493
|
validateCodexFactory(candidate);
|
|
2725
2494
|
if (candidate.hostId !== previous.hostId ||
|
|
2726
|
-
candidate.protocol !== previousFactory.protocol
|
|
2727
|
-
candidate.compatibilityPolicy !== this.compatibilityPolicy) {
|
|
2495
|
+
candidate.protocol !== previousFactory.protocol) {
|
|
2728
2496
|
throw new BridgeError("CODEX_FACTORY_ATTESTATION_INVALID", "The replacement Codex factory moved outside its pinned boundary.");
|
|
2729
2497
|
}
|
|
2730
2498
|
if (candidate === previousFactory ||
|
|
@@ -2744,7 +2512,6 @@ export class LocalCodexGatewayProvider {
|
|
|
2744
2512
|
surface: "codex",
|
|
2745
2513
|
version: candidate.appServerVersion,
|
|
2746
2514
|
checkedAt: this.now().toISOString(),
|
|
2747
|
-
policy: this.compatibilityPolicy,
|
|
2748
2515
|
certifiedVersions: certifiedCompatibilityVersions.codex,
|
|
2749
2516
|
probes,
|
|
2750
2517
|
});
|
|
@@ -2881,13 +2648,6 @@ export class LocalCodexGatewayProvider {
|
|
|
2881
2648
|
invokeCallback(() => callbacks.onEndpointRefresh?.(event));
|
|
2882
2649
|
}
|
|
2883
2650
|
onTurnResult(endpointGeneration, result) {
|
|
2884
|
-
const certification = this.compatibilityTurns.get(result.messageId);
|
|
2885
|
-
if (certification !== undefined) {
|
|
2886
|
-
clearTimeout(certification.timer);
|
|
2887
|
-
this.compatibilityTurns.delete(result.messageId);
|
|
2888
|
-
certification.resolve(result);
|
|
2889
|
-
return;
|
|
2890
|
-
}
|
|
2891
2651
|
const wantsReply = this.expectsReply.get(result.messageId) === true;
|
|
2892
2652
|
this.expectsReply.delete(result.messageId);
|
|
2893
2653
|
let replyText;
|