agent-embassy 1.6.1 → 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.
Files changed (97) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/README.md +9 -9
  3. package/README.zh-CN.md +9 -9
  4. package/SECURITY.md +34 -77
  5. package/dist/src/gateway/acp-client.d.ts +117 -0
  6. package/dist/src/gateway/acp-client.js +382 -0
  7. package/dist/src/gateway/acp-client.js.map +1 -0
  8. package/dist/src/gateway/acp-provider.d.ts +65 -0
  9. package/dist/src/gateway/acp-provider.js +163 -0
  10. package/dist/src/gateway/acp-provider.js.map +1 -0
  11. package/dist/src/gateway/claude-helper-protocol.d.ts +3 -1
  12. package/dist/src/gateway/claude-helper-protocol.js +15 -5
  13. package/dist/src/gateway/claude-helper-protocol.js.map +1 -1
  14. package/dist/src/gateway/claude-helper-supervisor.d.ts +3 -1
  15. package/dist/src/gateway/claude-helper-supervisor.js +16 -2
  16. package/dist/src/gateway/claude-helper-supervisor.js.map +1 -1
  17. package/dist/src/gateway/claude-helper.js +4 -3
  18. package/dist/src/gateway/claude-helper.js.map +1 -1
  19. package/dist/src/gateway/claude-peer.d.ts +1 -1
  20. package/dist/src/gateway/claude-peer.js +11 -13
  21. package/dist/src/gateway/claude-peer.js.map +1 -1
  22. package/dist/src/gateway/claude-runtime.d.ts +0 -20
  23. package/dist/src/gateway/claude-runtime.js +6 -127
  24. package/dist/src/gateway/claude-runtime.js.map +1 -1
  25. package/dist/src/gateway/cli-copy.en.d.ts +1 -1
  26. package/dist/src/gateway/cli-copy.en.js +2 -2
  27. package/dist/src/gateway/cli-copy.zh-CN.d.ts +1 -1
  28. package/dist/src/gateway/cli-copy.zh-CN.js +2 -2
  29. package/dist/src/gateway/cli.d.ts +1 -1
  30. package/dist/src/gateway/cli.js +26 -5
  31. package/dist/src/gateway/cli.js.map +1 -1
  32. package/dist/src/gateway/codex-app-server.d.ts +5 -74
  33. package/dist/src/gateway/codex-app-server.js +9 -599
  34. package/dist/src/gateway/codex-app-server.js.map +1 -1
  35. package/dist/src/gateway/codex-local-transport.d.ts +8 -26
  36. package/dist/src/gateway/codex-local-transport.js +26 -76
  37. package/dist/src/gateway/codex-local-transport.js.map +1 -1
  38. package/dist/src/gateway/compatibility.d.ts +6 -66
  39. package/dist/src/gateway/compatibility.js +14 -296
  40. package/dist/src/gateway/compatibility.js.map +1 -1
  41. package/dist/src/gateway/config.d.ts +9 -0
  42. package/dist/src/gateway/config.js +6 -2
  43. package/dist/src/gateway/config.js.map +1 -1
  44. package/dist/src/gateway/control.d.ts +16 -1
  45. package/dist/src/gateway/control.js +89 -57
  46. package/dist/src/gateway/control.js.map +1 -1
  47. package/dist/src/gateway/dashboard-copy.d.ts +1 -1
  48. package/dist/src/gateway/dashboard-copy.en.d.ts +14 -44
  49. package/dist/src/gateway/dashboard-copy.en.js +37 -67
  50. package/dist/src/gateway/dashboard-copy.en.js.map +1 -1
  51. package/dist/src/gateway/dashboard-copy.js +14 -44
  52. package/dist/src/gateway/dashboard-copy.js.map +1 -1
  53. package/dist/src/gateway/dashboard-copy.zh-CN.d.ts +14 -44
  54. package/dist/src/gateway/dashboard-copy.zh-CN.js +37 -67
  55. package/dist/src/gateway/dashboard-copy.zh-CN.js.map +1 -1
  56. package/dist/src/gateway/dashboard-model.d.ts +38 -76
  57. package/dist/src/gateway/dashboard-model.js +158 -256
  58. package/dist/src/gateway/dashboard-model.js.map +1 -1
  59. package/dist/src/gateway/dashboard.d.ts +0 -3
  60. package/dist/src/gateway/dashboard.js +75 -76
  61. package/dist/src/gateway/dashboard.js.map +1 -1
  62. package/dist/src/gateway/deepseek-detect.d.ts +7 -6
  63. package/dist/src/gateway/deepseek-detect.js +31 -115
  64. package/dist/src/gateway/deepseek-detect.js.map +1 -1
  65. package/dist/src/gateway/live-dashboard-app/app.js +166 -621
  66. package/dist/src/gateway/live-dashboard-command.js +2 -4
  67. package/dist/src/gateway/live-dashboard-command.js.map +1 -1
  68. package/dist/src/gateway/live-dashboard-http.d.ts +1 -2
  69. package/dist/src/gateway/live-dashboard-http.js +14 -15
  70. package/dist/src/gateway/live-dashboard-http.js.map +1 -1
  71. package/dist/src/gateway/progress-watch-machine.d.ts +1 -4
  72. package/dist/src/gateway/progress-watch-machine.js.map +1 -1
  73. package/dist/src/gateway/provenance-envelope.d.ts +3 -2
  74. package/dist/src/gateway/provenance-envelope.js +44 -11
  75. package/dist/src/gateway/provenance-envelope.js.map +1 -1
  76. package/dist/src/gateway/providers.d.ts +12 -85
  77. package/dist/src/gateway/providers.js +72 -835
  78. package/dist/src/gateway/providers.js.map +1 -1
  79. package/dist/src/gateway/server.d.ts +8 -9
  80. package/dist/src/gateway/server.js +51 -99
  81. package/dist/src/gateway/server.js.map +1 -1
  82. package/dist/src/gateway/service.d.ts +22 -49
  83. package/dist/src/gateway/service.js +196 -437
  84. package/dist/src/gateway/service.js.map +1 -1
  85. package/dist/src/gateway/store.d.ts +20 -29
  86. package/dist/src/gateway/store.js +281 -827
  87. package/dist/src/gateway/store.js.map +1 -1
  88. package/dist/src/gateway/types.d.ts +55 -55
  89. package/dist/src/gateway/types.js +49 -91
  90. package/dist/src/gateway/types.js.map +1 -1
  91. package/docs/CONFIGURATION.md +11 -10
  92. package/docs/CONFIGURATION.zh-CN.md +10 -9
  93. package/docs/DASHBOARD.md +4 -38
  94. package/docs/DASHBOARD.zh-CN.md +4 -2
  95. package/docs/GATEWAY-ARCHITECTURE.md +61 -119
  96. package/package.json +1 -1
  97. package/skills/embassy-peer/SKILL.md +6 -6
@@ -1,15 +1,14 @@
1
1
  import { createHash, randomBytes } from "node:crypto";
2
2
  import { BridgeError } from "../errors.js";
3
3
  import { KeyedMutex } from "../mutex.js";
4
- import { certifiedCompatibilityVersions, compatibilityCoversWrites, compatibilitySurfaceDefinitions, evaluateCompatibilityAttestation, isCompatibilityAttestation, } from "./compatibility.js";
5
- import { createGatewayConversationId, isGatewaySnapshot, startGatewayControlServer, } from "./control.js";
4
+ import { createGatewayConversationId, isGatewaySnapshot, startGatewayControlServer, pairParamAliases, pairParamThreadAttestation, } from "./control.js";
6
5
  import { publishGatewayDashboard } from "./dashboard.js";
7
6
  import { createDeliveryMachine, isTerminalDeliveryMachine, projectDelivery, projectDeliveryWakeups, transitionDelivery, } from "./delivery-machine.js";
8
7
  import { createCodexRegistrationSuccession, transitionCodexRegistrationSuccession, } from "./codex-registration-succession.js";
9
8
  import { createCodexRegistrationGeneration, isCodexRegistrationGeneration, } from "./codex-registration-generation.js";
10
9
  import { PROGRESS_WATCH_DEFAULT_IDLE_MS } from "./progress-watch-machine.js";
11
10
  import { GatewayStore, } from "./store.js";
12
- import { GATEWAY_PUBLIC_SNAPSHOT_BYTE_BUDGET, gatewayPublicSnapshotLimits, arePublicAvailablePeerSnapshots, projectPublicCompatibilityCheck, projectGatewayPublicSnapshot, } from "./types.js";
11
+ import { GATEWAY_PUBLIC_SNAPSHOT_BYTE_BUDGET, gatewayPublicSnapshotLimits, gatewayRegistrationIngressPrefixes, arePublicAvailablePeerSnapshots, directionId, parseDirection, projectGatewayPublicSnapshot, } from "./types.js";
13
12
  const PUBLIC_ALIAS = /^[a-z][a-z0-9_-]{0,31}@[a-z0-9](?:[a-z0-9.-]{0,61}[a-z0-9])?$/;
14
13
  const CLAUDE_SESSION_ID = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
15
14
  const SAFE_CODE = /^[A-Z][A-Z0-9_]{0,63}$/;
@@ -77,39 +76,13 @@ function bindingKey(binding) {
77
76
  ].join("\0");
78
77
  }
79
78
  function endpointRefreshCallbackKey(event) {
80
- const attestation = {
81
- schemaVersion: event.attestation.schemaVersion,
82
- surface: event.attestation.surface,
83
- version: event.attestation.version,
84
- tier: event.attestation.tier,
85
- checkedAt: event.attestation.checkedAt,
86
- probes: event.attestation.probes.map((probe) => ({
87
- name: probe.name,
88
- outcome: probe.outcome,
89
- ...(probe.safeErrorCode === undefined
90
- ? {}
91
- : { safeErrorCode: probe.safeErrorCode }),
92
- })),
93
- ...(event.attestation.safeErrorCode === undefined
94
- ? {}
95
- : { safeErrorCode: event.attestation.safeErrorCode }),
96
- };
97
- return event.outcome === "compatible"
98
- ? JSON.stringify({
99
- outcome: event.outcome,
100
- previous: event.previous,
101
- current: event.current,
102
- attestation,
103
- routes: [...event.routes]
104
- .map((route) => ({ ...route }))
105
- .sort((left, right) => left.routeHandle.localeCompare(right.routeHandle)),
106
- })
107
- : JSON.stringify({
108
- outcome: event.outcome,
109
- previous: event.previous,
110
- candidate: event.candidate,
111
- attestation,
112
- });
79
+ return JSON.stringify({
80
+ previous: event.previous,
81
+ current: event.current,
82
+ routes: [...event.routes]
83
+ .map((route) => ({ ...route }))
84
+ .sort((left, right) => left.routeHandle.localeCompare(right.routeHandle)),
85
+ });
113
86
  }
114
87
  function stableLease(provider, value) {
115
88
  return `lease_${createHash("sha256").update(provider).update("\0").update(value).digest("base64url")}`;
@@ -149,8 +122,6 @@ export class GatewayService {
149
122
  config;
150
123
  store;
151
124
  adapters;
152
- compatibilityObservers;
153
- compatibilitySurfaceDefinitions;
154
125
  publishDashboard;
155
126
  now;
156
127
  timers;
@@ -176,9 +147,6 @@ export class GatewayService {
176
147
  deliveryTokens = new Map();
177
148
  runtimeAlerts = [];
178
149
  activityEvents = [];
179
- /** Startup probe failures that must preflight cross-provider mutations. */
180
- incompatibleProviderSurfaces = new Map();
181
- activeCodexIncompatibility;
182
150
  activitySequence = 0;
183
151
  detachedReceiptWrites = new Set();
184
152
  nativeIngressByConversation = new Map();
@@ -202,8 +170,6 @@ export class GatewayService {
202
170
  durablyAppliedEndpointRefreshes = new Map();
203
171
  /** Selector transitions staged until their exact registration owns a helper. */
204
172
  pendingSelectorEndpointActivations = new Map();
205
- /** Last incompatible transition already projected per allowed Codex host. */
206
- handledIncompatibleEndpointRefreshes = new Map();
207
173
  /** Process-local dedupe for automatic public activity rows across retries. */
208
174
  endpointRefreshActivityKeys = new Set();
209
175
  callbackWorker;
@@ -247,9 +213,6 @@ export class GatewayService {
247
213
  constructor(options) {
248
214
  this.config = options.config;
249
215
  this.adapters = [...(options.adapters ?? [])];
250
- this.compatibilityObservers = [...(options.compatibilityObservers ?? [])];
251
- this.compatibilitySurfaceDefinitions =
252
- options.compatibilitySurfaceDefinitions ?? compatibilitySurfaceDefinitions;
253
216
  this.publishDashboard = options.publishDashboard ?? publishGatewayDashboard;
254
217
  this.now = options.now ?? (() => new Date());
255
218
  this.store =
@@ -297,28 +260,41 @@ export class GatewayService {
297
260
  }
298
261
  seen.add(key);
299
262
  }
300
- if (this.adapters.length > 0) {
301
- await this.runAutomaticCompatibilityProbesLocked();
302
- assertStartActive();
303
- }
304
263
  for (const adapter of this.adapters) {
305
264
  const observation = await adapter.initialize(this.callbacksFor(adapter));
306
265
  assertStartActive();
266
+ this.captureRegistryObservation(adapter, adapter.latestRegistryObservation?.());
307
267
  await this.store.observeConnector({
308
268
  identity: adapter.identity,
309
269
  health: observation.health,
310
- compatibility: observation.compatibility,
311
270
  protocol: adapter.protocol,
312
271
  protocolVersion: adapter.protocolVersion,
313
272
  ...(observation.safeErrorCode === undefined
314
273
  ? {}
315
274
  : { safeErrorCode: safeCode(observation.safeErrorCode, "ADAPTER_DEGRADED") }),
316
275
  });
276
+ if (observation.ownedRoute !== undefined) {
277
+ const route = observation.ownedRoute;
278
+ const prefix = gatewayRegistrationIngressPrefixes[adapter.identity.provider];
279
+ if (prefix === undefined || !route.alias.startsWith(prefix) ||
280
+ !route.alias.endsWith(`@${adapter.identity.hostId}`) ||
281
+ !PUBLIC_ALIAS.test(route.alias) || !PRIVATE_HANDLE.test(route.routeHandle) ||
282
+ route.state !== "idle") {
283
+ throw new BridgeError("GATEWAY_OWNED_ROUTE_INVALID", "A config-declared provider route has invalid identity evidence.");
284
+ }
285
+ const binding = {
286
+ ...adapter.identity,
287
+ routeHandle: route.routeHandle,
288
+ ownerLease: stableLease(adapter.identity.provider, `${adapter.identity.hostId}\0${route.alias}`),
289
+ };
290
+ await this.registerOrRebind(route.alias, binding, "endpoint_reobserved", route.state, binding.ownerLease, "explicit_opt_in");
291
+ this.rememberBinding(route.alias, binding, route.state);
292
+ bootReactivatedIdleAliases.push(route.alias);
293
+ }
317
294
  assertStartActive();
318
295
  }
319
296
  await this.recoverCodexSuccessionAfterRestartLocked();
320
- bootReactivatedIdleAliases =
321
- await this.reactivateRetainedCodexRoutesAfterRestartLocked();
297
+ bootReactivatedIdleAliases.push(...(await this.reactivateRetainedCodexRoutesAfterRestartLocked()));
322
298
  assertStartActive();
323
299
  const control = await startGatewayControlServer({
324
300
  stateDir: this.store.rootDir,
@@ -460,7 +436,6 @@ export class GatewayService {
460
436
  this.deliveryTrackers.clear();
461
437
  this.deliveryTokens.clear();
462
438
  this.runtimeAlerts.length = 0;
463
- this.incompatibleProviderSurfaces.clear();
464
439
  this.registryEvidenceByConnector.clear();
465
440
  this.registryAdaptersWithParseableRecords.clear();
466
441
  this.detachedReceiptWrites.clear();
@@ -476,7 +451,6 @@ export class GatewayService {
476
451
  this.activatedEndpointRefreshes.clear();
477
452
  this.durablyAppliedEndpointRefreshes.clear();
478
453
  this.pendingSelectorEndpointActivations.clear();
479
- this.handledIncompatibleEndpointRefreshes.clear();
480
454
  this.endpointRefreshActivityKeys.clear();
481
455
  this.codexOrphanCleanupPending.clear();
482
456
  this.candidates.clear();
@@ -536,12 +510,12 @@ export class GatewayService {
536
510
  pair: async (params) => await this.exclusiveDecision(async () => this.pairRoutes(params), {
537
511
  kind: "pairing",
538
512
  action: "routes_paired",
539
- aliases: [params.claudeAlias, params.codexAlias],
513
+ aliases: [...pairParamAliases(params)],
540
514
  }),
541
515
  unpair: async (params) => await this.exclusiveDecision(async () => this.unpairRoutes(params), {
542
516
  kind: "pairing",
543
517
  action: "routes_unpaired",
544
- aliases: [params.claudeAlias, params.codexAlias],
518
+ aliases: [...pairParamAliases(params)],
545
519
  }),
546
520
  listSnapshot: async () => (await this.observeSnapshot()).snapshot,
547
521
  observeSnapshot: async () => await this.observeSnapshot(),
@@ -572,91 +546,6 @@ export class GatewayService {
572
546
  async snapshot() {
573
547
  return (await this.observeSnapshot()).snapshot;
574
548
  }
575
- async runAutomaticCompatibilityProbesLocked() {
576
- const entries = [
577
- ...this.adapters.map((adapter) => {
578
- if (adapter.compatibilitySurface === undefined ||
579
- adapter.runCompatibilityProbes === undefined) {
580
- throw new BridgeError("COMPAT_PROVIDER_UNAVAILABLE", "Every local provider must expose the bounded compatibility probe contract.");
581
- }
582
- return {
583
- observer: adapter,
584
- adapter,
585
- };
586
- }),
587
- ...this.compatibilityObservers.map((observer) => ({ observer })),
588
- ];
589
- const observedEntries = entries.map((entry) => ({
590
- ...entry,
591
- observation: entry.observer.compatibilitySurface(),
592
- }));
593
- const bySurface = new Map(observedEntries.map((entry) => [entry.observation.surface, entry]));
594
- if (bySurface.size !== entries.length) {
595
- throw new BridgeError("COMPAT_PROVIDER_UNAVAILABLE", "Each compatibility observer must expose one distinct surface.");
596
- }
597
- const declared = new Set(this.compatibilitySurfaceDefinitions.map(({ surface }) => surface));
598
- if ([...bySurface.keys()].some((surface) => !declared.has(surface))) {
599
- throw new BridgeError("COMPAT_SURFACE_UNDECLARED", "Every compatibility observer must use a declared surface.");
600
- }
601
- const forbiddenCodexThreadIds = (await this.store.inspectPrivateCodexRoutes()).map((route) => route.binding.routeHandle);
602
- const probeContext = {
603
- stateRoot: this.store.rootDir,
604
- forbiddenCodexThreadIds,
605
- };
606
- const attestations = [];
607
- for (const definition of this.compatibilitySurfaceDefinitions) {
608
- const { surface } = definition;
609
- const entry = bySurface.get(surface);
610
- if (entry === undefined) {
611
- if (!definition.required)
612
- continue;
613
- throw new BridgeError("COMPAT_PROVIDER_UNAVAILABLE", "A required compatibility surface is unavailable.");
614
- }
615
- const probes = entry.adapter === undefined
616
- ? await entry.observer.runCompatibilityProbes()
617
- : await entry.adapter.runCompatibilityProbes(probeContext);
618
- let writeProbePassed = false;
619
- if (entry.adapter !== undefined) {
620
- const writeProbe = entry.adapter.latestWriteCompatibilityProbeObservation?.(entry.adapter.identity.endpointGeneration);
621
- writeProbePassed = writeProbe?.outcome === "pass";
622
- if (writeProbe?.outcome === "fail") {
623
- this.addRuntimeAlert(writeProbe.safeErrorCode, writeProbe.safeErrorCode === "CODEX_WRITE_PROBE_TOOL_ACTIVITY_OBSERVED" ||
624
- writeProbe.safeErrorCode === "CODEX_WRITE_PROBE_CLEANUP_UNCONFIRMED"
625
- ? "error"
626
- : "warning", { provider: "codex", host: entry.adapter.identity.hostId });
627
- }
628
- this.captureRegistryObservation(entry.adapter, entry.adapter.latestRegistryObservation?.());
629
- }
630
- const attestation = evaluateCompatibilityAttestation({
631
- surface,
632
- version: entry.observation.version,
633
- checkedAt: this.now().toISOString(),
634
- certifiedVersions: certifiedCompatibilityVersions[surface],
635
- probes,
636
- });
637
- await this.store.recordCompatibilityAttestation(attestation);
638
- entry.observer.acceptCompatibilityAttestation?.(attestation);
639
- if (attestation.tier === "incompatible" && entry.adapter !== undefined) {
640
- const code = attestation.safeErrorCode ?? "COMPATIBILITY_CHECK_FAILED";
641
- this.incompatibleProviderSurfaces.set(this.providerSurfaceKey(entry.adapter.identity.provider, entry.adapter.identity.hostId), code);
642
- if (surface === "codex") {
643
- this.activeCodexIncompatibility = structuredClone(attestation);
644
- }
645
- }
646
- else if (surface === "codex" &&
647
- attestation.tier === "schema_attested" &&
648
- !(compatibilityCoversWrites(attestation) && writeProbePassed) &&
649
- entry.adapter !== undefined) {
650
- // A live but untested App Server schema is useful for observation only.
651
- // Fence retained boot reactivation before it can trigger endpoint
652
- // refresh churn; an explicit registration may still re-probe a later,
653
- // certified generation through the provider's bounded refresh path.
654
- this.incompatibleProviderSurfaces.set(this.providerSurfaceKey("codex", entry.adapter.identity.hostId), "CODEX_MONITOR_ONLY");
655
- }
656
- attestations.push(attestation);
657
- }
658
- return attestations;
659
- }
660
549
  captureRegistryObservation(adapter, observation) {
661
550
  if (adapter.identity.provider !== "claude" ||
662
551
  observation === undefined ||
@@ -884,50 +773,30 @@ export class GatewayService {
884
773
  });
885
774
  },
886
775
  onEndpointRefresh: (event) => {
776
+ if (!Array.isArray(event.routes))
777
+ return;
778
+ const routeHandles = event.routes.map((route) => route.routeHandle);
887
779
  if (!this.running ||
888
780
  adapter.identity.provider !== "codex" ||
889
781
  event.previous.provider !== "codex" ||
890
782
  event.previous.hostId !== adapter.identity.hostId ||
891
- !isCompatibilityAttestation(event.attestation) ||
892
- event.attestation.surface !== "codex") {
893
- return;
894
- }
895
- if (event.outcome === "compatible") {
896
- const routeHandles = event.routes.map((route) => route.routeHandle);
897
- if (event.current.provider !== "codex" ||
898
- event.current.hostId !== event.previous.hostId ||
899
- event.current.endpointGeneration !==
900
- adapter.identity.endpointGeneration ||
901
- event.attestation.version !== adapter.protocolVersion ||
902
- event.attestation.tier === "incompatible" ||
903
- routeHandles.length > this.config.limits.maxRoutes ||
904
- new Set(routeHandles).size !== routeHandles.length ||
905
- routeHandles.some((routeHandle) => !PRIVATE_HANDLE.test(routeHandle))) {
906
- return;
907
- }
908
- }
909
- else if (event.candidate.provider !== "codex" ||
910
- event.candidate.hostId !== event.previous.hostId ||
911
- event.previous.endpointGeneration !==
783
+ event.current.provider !== "codex" ||
784
+ event.current.hostId !== event.previous.hostId ||
785
+ event.current.endpointGeneration !==
912
786
  adapter.identity.endpointGeneration ||
913
- event.attestation.tier !== "incompatible") {
787
+ routeHandles.length > this.config.limits.maxRoutes ||
788
+ new Set(routeHandles).size !== routeHandles.length ||
789
+ routeHandles.some((routeHandle) => !PRIVATE_HANDLE.test(routeHandle))) {
914
790
  return;
915
791
  }
916
792
  this.enqueueCallback({
917
793
  type: "endpoint_refresh",
918
794
  adapter,
919
- value: event.outcome === "compatible"
920
- ? {
921
- ...event,
922
- previous: { ...event.previous },
923
- current: { ...event.current },
924
- routes: event.routes.map((route) => ({ ...route })),
925
- }
926
- : {
927
- ...event,
928
- previous: { ...event.previous },
929
- candidate: { ...event.candidate },
930
- },
795
+ value: {
796
+ previous: { ...event.previous },
797
+ current: { ...event.current },
798
+ routes: event.routes.map((route) => ({ ...route })),
799
+ },
931
800
  });
932
801
  },
933
802
  };
@@ -1074,7 +943,7 @@ export class GatewayService {
1074
943
  await this.onDelivery(event.source, event.value, event.receivedAt);
1075
944
  }
1076
945
  else if (event.type === "route") {
1077
- if (pendingRefresh?.outcome === "compatible" &&
946
+ if (pendingRefresh !== undefined &&
1078
947
  event.source.provider === "codex" &&
1079
948
  event.source.hostId === pendingRefresh.current.hostId &&
1080
949
  event.source.endpointGeneration ===
@@ -1204,12 +1073,7 @@ export class GatewayService {
1204
1073
  await this.drainDetachedReceiptWritesLocked();
1205
1074
  }
1206
1075
  async failClosedEndpointRefreshConflict(boundary) {
1207
- const identities = [
1208
- boundary.value.previous,
1209
- ...(boundary.value.outcome === "compatible"
1210
- ? [boundary.value.current]
1211
- : []),
1212
- ];
1076
+ const identities = [boundary.value.previous, boundary.value.current];
1213
1077
  for (const identity of identities) {
1214
1078
  const routes = (await this.store.inspectPrivateCodexRoutes()).filter((route) => route.binding.hostId === identity.hostId &&
1215
1079
  route.binding.endpointGeneration === identity.endpointGeneration);
@@ -1311,23 +1175,6 @@ export class GatewayService {
1311
1175
  throw new BridgeError("PROVIDER_UNAVAILABLE", "The selected provider is unavailable.", true);
1312
1176
  return adapter;
1313
1177
  }
1314
- providerSurfaceKey(provider, host) {
1315
- return `${provider}@${host}`;
1316
- }
1317
- incompatibleProviderCode(provider, host) {
1318
- return this.incompatibleProviderSurfaces.get(this.providerSurfaceKey(provider, host));
1319
- }
1320
- /** Reject before either half of a cross-provider mutation can acquire state. */
1321
- assertCrossProviderMutationCompatible(host, options = { allowCodexRefresh: false }) {
1322
- const claudeCode = this.incompatibleProviderCode("claude", host);
1323
- if (claudeCode !== undefined) {
1324
- throw new BridgeError(claudeCode, "The Claude provider is observation-only until Embassy restarts with a compatible runtime.");
1325
- }
1326
- const codexCode = this.incompatibleProviderCode("codex", host);
1327
- if (codexCode !== undefined && !options.allowCodexRefresh) {
1328
- throw new BridgeError(codexCode, "The Codex provider is observation-only until a compatible endpoint is re-observed.");
1329
- }
1330
- }
1331
1178
  async assertClaudeWorkspaceDisjoint(adapter, routeHandle) {
1332
1179
  if (adapter.identity.provider !== "claude") {
1333
1180
  throw new BridgeError("CLAUDE_PROVIDER_MISMATCH", "The selected workspace guard does not belong to Claude.");
@@ -1385,7 +1232,6 @@ export class GatewayService {
1385
1232
  for (const route of retained) {
1386
1233
  if (!route.enabled ||
1387
1234
  route.state !== "stale" ||
1388
- route.compatibility !== "expired" ||
1389
1235
  this.pendingCodexSuccessionRecovery?.journal.new.alias === route.alias ||
1390
1236
  this.codexSuccessionPoisoned.has(route.alias)) {
1391
1237
  continue;
@@ -1398,13 +1244,6 @@ export class GatewayService {
1398
1244
  this.addRuntimeAlert("CODEX_BOOT_REACTIVATION_SKIPPED", "warning", { provider: "codex", host: route.binding.hostId, alias: route.alias });
1399
1245
  continue;
1400
1246
  }
1401
- if (this.incompatibleProviderCode("codex", route.binding.hostId) !==
1402
- undefined ||
1403
- this.incompatibleProviderCode("claude", route.binding.hostId) !==
1404
- undefined) {
1405
- this.addRuntimeAlert("CODEX_BOOT_REACTIVATION_SKIPPED", "warning", { provider: "codex", host: route.binding.hostId, alias: route.alias });
1406
- continue;
1407
- }
1408
1247
  let routeSelected = false;
1409
1248
  let advertiseAttempted = false;
1410
1249
  let storeReactivated = false;
@@ -1442,19 +1281,20 @@ export class GatewayService {
1442
1281
  storeReactivated =
1443
1282
  afterStaging !== undefined &&
1444
1283
  afterStaging.enabled &&
1445
- afterStaging.compatibility === "compatible" &&
1284
+ afterStaging.state !== "stale" &&
1446
1285
  bindingKey(afterStaging.binding) === bindingKey(binding);
1447
1286
  }
1448
1287
  advertiseAttempted = true;
1449
- const advertise = claudeAdapter.advertiseNativeCodexPeer;
1288
+ const advertise = claudeAdapter.advertiseNativeSourcePeer;
1450
1289
  if (advertise === undefined) {
1451
1290
  throw new BridgeError("CODEX_BOOT_REACTIVATION_UNAVAILABLE", "The retained Codex task cannot rebuild its native advertisement.");
1452
1291
  }
1453
1292
  await advertise.call(claudeAdapter, {
1454
1293
  alias: route.alias,
1294
+ sourceProvider: "codex",
1455
1295
  cwd: this.nativePeerCwd,
1456
1296
  });
1457
- await claudeAdapter.updateNativeCodexPeerStatus?.(route.alias, selected.state === "idle"
1297
+ await claudeAdapter.updateNativeSourcePeerStatus?.(route.alias, selected.state === "idle"
1458
1298
  ? "idle"
1459
1299
  : selected.state === "awaiting_approval"
1460
1300
  ? "waiting"
@@ -1498,7 +1338,7 @@ export class GatewayService {
1498
1338
  let cleanupFailed = false;
1499
1339
  if (advertiseAttempted) {
1500
1340
  await claudeAdapter
1501
- .unadvertiseNativeCodexPeer?.(route.alias)
1341
+ .unadvertiseNativeSourcePeer?.(route.alias)
1502
1342
  .catch(() => {
1503
1343
  cleanupFailed = true;
1504
1344
  });
@@ -1630,7 +1470,7 @@ export class GatewayService {
1630
1470
  oldInspection.binding.ownerLease !==
1631
1471
  stableLease("codex", `${lock.hostId}\0${lock.threadId}`) ||
1632
1472
  !oldInspection.enabled ||
1633
- oldInspection.compatibility !== "compatible") {
1473
+ oldInspection.state === "stale") {
1634
1474
  throw new BridgeError("CODEX_SUCCESSION_OWNER_MISMATCH", "The active Codex registration is no longer exact and compatible.");
1635
1475
  }
1636
1476
  const claudeAdapter = this.adapter("claude", params.hostId);
@@ -1939,7 +1779,7 @@ export class GatewayService {
1939
1779
  this.rememberBinding(effect.registration.alias, execution.newBinding, state);
1940
1780
  this.codexRegistrationLocks.delete(this.successionOldRegistration(execution).alias);
1941
1781
  this.codexRegistrationLocks.set(effect.registration.alias, Object.freeze({ ...effect.registration }));
1942
- await claudeAdapter.updateNativeCodexPeerStatus?.(effect.registration.alias, state === "idle"
1782
+ await claudeAdapter.updateNativeSourcePeerStatus?.(effect.registration.alias, state === "idle"
1943
1783
  ? "idle"
1944
1784
  : state === "awaiting_approval"
1945
1785
  ? "waiting"
@@ -2084,8 +1924,8 @@ export class GatewayService {
2084
1924
  throw new BridgeError("CODEX_SUCCESSION_DRAIN_INCOMPLETE", "Interrupted generations cannot close before pending work and receipts are drained. Restart the gateway with embassy serve.");
2085
1925
  }
2086
1926
  const results = await Promise.allSettled([
2087
- claudeAdapter.unadvertiseNativeCodexPeer?.(execution.oldRegistration.alias),
2088
- claudeAdapter.unadvertiseNativeCodexPeer?.(execution.newRegistration.alias),
1927
+ claudeAdapter.unadvertiseNativeSourcePeer?.(execution.oldRegistration.alias),
1928
+ claudeAdapter.unadvertiseNativeSourcePeer?.(execution.newRegistration.alias),
2089
1929
  codexAdapter.releaseRoute?.(execution.oldRegistration.threadId),
2090
1930
  codexAdapter.releaseRoute?.(execution.newRegistration.threadId),
2091
1931
  ]);
@@ -2310,9 +2150,6 @@ export class GatewayService {
2310
2150
  if (!params.alias.startsWith("codex-")) {
2311
2151
  throw new BridgeError("INVALID_CODEX_PEER_ALIAS", "A registered Codex alias must start with codex-.");
2312
2152
  }
2313
- this.assertCrossProviderMutationCompatible(params.hostId, {
2314
- allowCodexRefresh: params.succeedsAlias === undefined,
2315
- });
2316
2153
  if (this.codexSuccessionPoisoned.has(params.alias) ||
2317
2154
  this.codexOrphanCleanupPending.has(params.alias) ||
2318
2155
  (params.succeedsAlias !== undefined &&
@@ -2383,7 +2220,7 @@ export class GatewayService {
2383
2220
  const afterRefresh = await this.store.inspectPrivateRoute(params.alias);
2384
2221
  const alreadyInstalledExactly = afterRefresh !== undefined &&
2385
2222
  afterRefresh.enabled &&
2386
- afterRefresh.compatibility === "compatible" &&
2223
+ afterRefresh.state !== "stale" &&
2387
2224
  bindingKey(afterRefresh.binding) === bindingKey(binding);
2388
2225
  if (!alreadyInstalledExactly || !deferredEndpointRefresh) {
2389
2226
  await this.registerOrRebind(params.alias, binding, "endpoint_reobserved", registered.state);
@@ -2398,15 +2235,16 @@ export class GatewayService {
2398
2235
  try {
2399
2236
  this.rememberBinding(params.alias, binding, registered.state);
2400
2237
  const claudeAdapter = this.adapter("claude", params.hostId);
2401
- const advertise = claudeAdapter.advertiseNativeCodexPeer;
2238
+ const advertise = claudeAdapter.advertiseNativeSourcePeer;
2402
2239
  if (advertise !== undefined) {
2403
2240
  advertiseAttempted = true;
2404
2241
  await advertise.call(claudeAdapter, {
2405
2242
  alias: params.alias,
2243
+ sourceProvider: "codex",
2406
2244
  cwd: this.nativePeerCwd,
2407
2245
  });
2408
2246
  }
2409
- await claudeAdapter.updateNativeCodexPeerStatus?.(params.alias, registered.state === "idle"
2247
+ await claudeAdapter.updateNativeSourcePeerStatus?.(params.alias, registered.state === "idle"
2410
2248
  ? "idle"
2411
2249
  : registered.state === "awaiting_approval"
2412
2250
  ? "waiting"
@@ -2428,8 +2266,7 @@ export class GatewayService {
2428
2266
  }
2429
2267
  catch (error) {
2430
2268
  if (routeBeforeRegistration !== undefined) {
2431
- if (deferredEndpointRefresh &&
2432
- registered.endpointRefresh?.outcome === "compatible") {
2269
+ if (deferredEndpointRefresh && registered.endpointRefresh !== undefined) {
2433
2270
  try {
2434
2271
  await this.store.markConnectorOffline(registered.endpointRefresh.current, "CODEX_ENDPOINT_ACTIVATION_FAILED", [], { preserveQueued: true });
2435
2272
  this.routeStates.set(params.alias, "stale");
@@ -2511,7 +2348,7 @@ export class GatewayService {
2511
2348
  });
2512
2349
  if (advertiseAttempted) {
2513
2350
  await this.adapter("claude", params.hostId)
2514
- .unadvertiseNativeCodexPeer?.(params.alias)
2351
+ .unadvertiseNativeSourcePeer?.(params.alias)
2515
2352
  .catch(() => {
2516
2353
  cleanupFailed = true;
2517
2354
  });
@@ -2538,7 +2375,7 @@ export class GatewayService {
2538
2375
  }
2539
2376
  this.forgetBinding(params.alias);
2540
2377
  await this.adapter("claude", host)
2541
- .unadvertiseNativeCodexPeer?.(params.alias)
2378
+ .unadvertiseNativeSourcePeer?.(params.alias)
2542
2379
  .catch(() => {
2543
2380
  this.dashboardHealthy = false;
2544
2381
  });
@@ -2709,7 +2546,7 @@ export class GatewayService {
2709
2546
  }
2710
2547
  await this.requireUnchangedStaleCodexOrphan(alias, binding);
2711
2548
  if (nativeGeneration !== undefined) {
2712
- const unadvertise = claudeAdapter.unadvertiseNativeCodexPeer;
2549
+ const unadvertise = claudeAdapter.unadvertiseNativeSourcePeer;
2713
2550
  if (unadvertise === undefined) {
2714
2551
  throw new BridgeError("CODEX_ORPHAN_NATIVE_IDENTITY_UNPROVEN", "The native Codex listener cannot be removed exactly.");
2715
2552
  }
@@ -2899,7 +2736,6 @@ export class GatewayService {
2899
2736
  for (const route of persisted) {
2900
2737
  if (!route.enabled ||
2901
2738
  route.state !== "stale" ||
2902
- route.compatibility !== "expired" ||
2903
2739
  !CLAUDE_SESSION_ID.test(route.binding.routeHandle) ||
2904
2740
  route.binding.ownerLease !==
2905
2741
  stableLease("claude", route.binding.routeHandle)) {
@@ -2923,8 +2759,7 @@ export class GatewayService {
2923
2759
  alias,
2924
2760
  provider: "claude",
2925
2761
  host: row.adapter.identity.hostId,
2926
- state: "incompatible",
2927
- compatibility: "incompatible",
2762
+ state: "offline",
2928
2763
  validated: false,
2929
2764
  selected: false,
2930
2765
  safeErrorCode: row.safeErrorCode,
@@ -2942,7 +2777,6 @@ export class GatewayService {
2942
2777
  provider: "claude",
2943
2778
  host: row.adapter.identity.hostId,
2944
2779
  state: row.peer.state,
2945
- compatibility: row.peer.compatibility,
2946
2780
  validated: true,
2947
2781
  selected,
2948
2782
  });
@@ -2951,7 +2785,6 @@ export class GatewayService {
2951
2785
  await this.store.observeRoute({
2952
2786
  binding: selectedBinding,
2953
2787
  state: row.peer.state,
2954
- compatibility: "compatible",
2955
2788
  });
2956
2789
  }
2957
2790
  }
@@ -2966,7 +2799,7 @@ export class GatewayService {
2966
2799
  const inspection = await this.store.inspectPrivateRoute(alias);
2967
2800
  if (inspection !== undefined &&
2968
2801
  inspection.enabled &&
2969
- inspection.compatibility === "compatible") {
2802
+ inspection.state !== "stale") {
2970
2803
  await this.drainPreDeadlineDeliveryCallbacksLocked();
2971
2804
  const inFlightSettlements = await this.planRouteInFlightSettlementsLocked(alias, {
2972
2805
  unwrittenOutcome: "failed",
@@ -3043,7 +2876,6 @@ export class GatewayService {
3043
2876
  const expectedLease = stableLease("claude", candidate.routeHandle);
3044
2877
  if (!persisted.enabled ||
3045
2878
  persisted.state !== "stale" ||
3046
- persisted.compatibility !== "expired" ||
3047
2879
  persisted.binding.provider !== "claude" ||
3048
2880
  candidate.adapter.identity.provider !== "claude" ||
3049
2881
  persisted.binding.hostId !== candidate.adapter.identity.hostId ||
@@ -3109,7 +2941,6 @@ export class GatewayService {
3109
2941
  binding,
3110
2942
  registrationMode: "selected_live_peer",
3111
2943
  state: selected.state,
3112
- compatibility: "compatible",
3113
2944
  });
3114
2945
  this.rememberBinding(candidate.alias, binding, selected.state);
3115
2946
  }
@@ -3161,7 +2992,6 @@ export class GatewayService {
3161
2992
  binding,
3162
2993
  registrationMode: "selected_live_peer",
3163
2994
  state: selectedState,
3164
- compatibility: "compatible",
3165
2995
  },
3166
2996
  inFlightSettlements,
3167
2997
  });
@@ -3234,47 +3064,83 @@ export class GatewayService {
3234
3064
  }
3235
3065
  return candidate.alias;
3236
3066
  }
3237
- assertCodexPairCaller(params) {
3238
- const binding = this.routeBindings.get(params.codexAlias);
3067
+ assertPairThreadAttestation(params) {
3068
+ const attestation = pairParamThreadAttestation(params);
3069
+ const legacyAlias = "codexAlias" in params ? params.codexAlias : undefined;
3070
+ if (attestation === undefined && legacyAlias === undefined)
3071
+ return;
3072
+ const binding = this.routeBindings.get(attestation?.alias ?? legacyAlias);
3239
3073
  if (binding?.provider !== "codex" ||
3240
- (params.codexThreadId !== undefined &&
3241
- binding.routeHandle !== params.codexThreadId)) {
3074
+ (attestation !== undefined && binding.routeHandle !== attestation.threadId)) {
3242
3075
  throw new BridgeError("CODEX_CALLER_MISMATCH", "The inherited Codex task does not own the requested pair endpoint.");
3243
3076
  }
3244
3077
  }
3245
3078
  async pairRoutes(params) {
3246
- this.assertCodexPairCaller(params);
3247
- await this.selectAndPairClaude(params.claudeAlias, params.codexAlias);
3079
+ this.assertPairThreadAttestation(params);
3080
+ if ("claudeAlias" in params) {
3081
+ await this.selectAndPairClaude(params.claudeAlias, params.codexAlias);
3082
+ return;
3083
+ }
3084
+ await this.assertLiveConsentEndpoints(params.aliases);
3085
+ const added = await this.store.addConsentEdge({ aliases: params.aliases });
3086
+ if (added.created)
3087
+ await this.changed();
3088
+ else
3089
+ await this.publish();
3248
3090
  }
3249
3091
  async unpairRoutes(params) {
3250
- this.assertCodexPairCaller(params);
3251
- const selected = await this.selectedClaudeRoute(params.claudeAlias);
3252
- await this.unpairClaudeRoute(selected, params.codexAlias);
3092
+ this.assertPairThreadAttestation(params);
3093
+ const aliases = pairParamAliases(params);
3094
+ if (!("claudeAlias" in params))
3095
+ await this.assertLiveConsentEndpoints(aliases);
3096
+ await this.removeConsentEdge(aliases);
3097
+ }
3098
+ async assertLiveConsentEndpoints(aliases) {
3099
+ const routes = await Promise.all(aliases.map((alias) => this.store.inspectPrivateRoute(alias)));
3100
+ if (routes[0] === undefined || routes[1] === undefined) {
3101
+ throw new BridgeError("CONSENT_EDGE_ROUTE_NOT_FOUND", "Both consent endpoints must be registered.");
3102
+ }
3103
+ for (const route of routes) {
3104
+ const live = this.routeBindings.get(route.alias);
3105
+ if (!route.enabled ||
3106
+ route.state === "stale" ||
3107
+ live === undefined ||
3108
+ bindingKey(live) !== bindingKey(route.binding) ||
3109
+ this.adapter(route.binding.provider, route.binding.hostId).identity.endpointGeneration !==
3110
+ route.binding.endpointGeneration) {
3111
+ throw new BridgeError("ROUTE_UNAVAILABLE", "Both consent endpoints must be exact positively observed live routes.");
3112
+ }
3113
+ }
3114
+ if (routes[0].binding.provider === routes[1].binding.provider || routes[0].binding.hostId !== routes[1].binding.hostId) {
3115
+ throw new BridgeError("INVALID_CONSENT_EDGE", "A consent edge must connect distinct providers on the same host.");
3116
+ }
3117
+ return routes;
3253
3118
  }
3254
- async unpairClaudeRoute(selected, codexAlias) {
3119
+ async removeConsentEdge(aliases) {
3255
3120
  await this.drainPreDeadlineDeliveryCallbacksLocked();
3256
- const pair = {
3257
- claudeAlias: selected.alias,
3258
- codexAlias,
3259
- };
3260
- const inFlightSettlements = await this.planPairInFlightSettlementsLocked(pair, {
3121
+ const routes = await Promise.all(aliases.map((alias) => this.store.inspectPrivateRoute(alias)));
3122
+ const edge = { aliases };
3123
+ const inFlightSettlements = await this.planConsentEdgeInFlightSettlementsLocked(edge, {
3261
3124
  unwrittenOutcome: "cancelled",
3262
3125
  safeErrorCode: "PAIR_REMOVED",
3263
3126
  });
3264
- const result = await this.store.unpairRoutes({
3265
- ...pair,
3127
+ const result = await this.store.removeConsentEdge({
3128
+ ...edge,
3266
3129
  inFlightSettlements,
3267
3130
  });
3268
3131
  for (const settlement of result.settlements) {
3269
3132
  await this.applyTerminalSettlementLocked(settlement);
3270
3133
  }
3271
- this.purgePairCapabilitiesLocked(pair);
3272
- if (result.claudeRouteUnreferenced) {
3273
- const settlements = await this.store.unregisterRoute(selected.alias, selected.binding.ownerLease);
3134
+ this.purgeConsentEdgeCapabilitiesLocked(aliases);
3135
+ for (const alias of result.unreferencedAliases) {
3136
+ const selected = routes.find((route) => route?.alias === alias);
3137
+ if (selected?.binding.provider !== "claude" || selected.registrationMode !== "selected_live_peer")
3138
+ continue;
3139
+ const settlements = await this.store.unregisterRoute(alias, selected.binding.ownerLease);
3274
3140
  for (const settlement of settlements) {
3275
3141
  await this.applyTerminalSettlementLocked(settlement);
3276
3142
  }
3277
- this.forgetBinding(selected.alias);
3143
+ this.forgetBinding(alias);
3278
3144
  await this.adapters
3279
3145
  .find((adapter) => adapter.identity.provider === "claude" &&
3280
3146
  adapter.identity.hostId === selected.binding.hostId)
@@ -3282,7 +3148,7 @@ export class GatewayService {
3282
3148
  .catch(() => {
3283
3149
  this.dashboardHealthy = false;
3284
3150
  });
3285
- this.availablePeers = this.availablePeers.map((peer) => peer.alias === selected.alias ? { ...peer, selected: false } : peer);
3151
+ this.availablePeers = this.availablePeers.map((peer) => peer.alias === alias ? { ...peer, selected: false } : peer);
3286
3152
  }
3287
3153
  await this.changed();
3288
3154
  }
@@ -3291,17 +3157,6 @@ export class GatewayService {
3291
3157
  }
3292
3158
  async selectAndPairClaude(selector, codexAlias) {
3293
3159
  const codexBinding = this.routeBindings.get(codexAlias);
3294
- const retainedCodexRoute = codexBinding?.provider === "codex"
3295
- ? undefined
3296
- : await this.store.inspectPrivateRoute(codexAlias);
3297
- const codexHost = codexBinding?.provider === "codex"
3298
- ? codexBinding.hostId
3299
- : retainedCodexRoute?.binding.provider === "codex"
3300
- ? retainedCodexRoute.binding.hostId
3301
- : undefined;
3302
- if (codexHost !== undefined) {
3303
- this.assertCrossProviderMutationCompatible(codexHost);
3304
- }
3305
3160
  const codexAdapter = codexBinding?.provider === "codex"
3306
3161
  ? this.adapters.find((adapter) => adapter.identity.provider === "codex" &&
3307
3162
  adapter.identity.hostId === codexBinding.hostId)
@@ -3341,10 +3196,7 @@ export class GatewayService {
3341
3196
  }
3342
3197
  let paired;
3343
3198
  try {
3344
- paired = await this.store.pairRoutes({
3345
- claudeAlias: candidate.alias,
3346
- codexAlias,
3347
- });
3199
+ paired = await this.store.addConsentEdge({ aliases: [candidate.alias, codexAlias] });
3348
3200
  }
3349
3201
  catch (error) {
3350
3202
  if (!alreadyLive &&
@@ -3384,7 +3236,7 @@ export class GatewayService {
3384
3236
  async unselectClaude(params) {
3385
3237
  const codexAlias = await this.inferCodexAlias(params.codexThreadId);
3386
3238
  const selected = await this.selectedClaudeRoute(params.alias);
3387
- await this.unpairClaudeRoute(selected, codexAlias);
3239
+ await this.removeConsentEdge([selected.alias, codexAlias]);
3388
3240
  }
3389
3241
  async selectedClaudeRoute(selector) {
3390
3242
  const persisted = await this.store.inspectPrivateClaudeRoutes();
@@ -3396,14 +3248,13 @@ export class GatewayService {
3396
3248
  throw new BridgeError("PEER_NOT_FOUND", "No selected Claude session matches that selector.");
3397
3249
  return selected;
3398
3250
  }
3399
- async registerOrRebind(alias, binding, reason, state, currentOwnerLease = binding.ownerLease) {
3251
+ async registerOrRebind(alias, binding, reason, state, currentOwnerLease = binding.ownerLease, registrationMode = binding.provider === "codex" ? "explicit_opt_in" : "selected_live_peer") {
3400
3252
  try {
3401
3253
  await this.store.registerRoute({
3402
3254
  alias,
3403
3255
  binding,
3404
- registrationMode: binding.provider === "codex" ? "explicit_opt_in" : "selected_live_peer",
3256
+ registrationMode,
3405
3257
  state,
3406
- compatibility: "compatible",
3407
3258
  });
3408
3259
  }
3409
3260
  catch (error) {
@@ -3473,14 +3324,12 @@ export class GatewayService {
3473
3324
  await this.store.observeConnector({
3474
3325
  identity: adapter.identity,
3475
3326
  health: "healthy",
3476
- compatibility: "compatible",
3477
3327
  protocol: adapter.protocol,
3478
3328
  protocolVersion: adapter.protocolVersion,
3479
3329
  });
3480
3330
  await this.store.observeRoute({
3481
3331
  binding,
3482
3332
  state,
3483
- compatibility: "compatible",
3484
3333
  });
3485
3334
  return binding;
3486
3335
  }
@@ -3796,8 +3645,8 @@ export class GatewayService {
3796
3645
  };
3797
3646
  });
3798
3647
  }
3799
- async planPairInFlightSettlementsLocked(pair, input) {
3800
- const affected = await this.store.inspectAffectedPairInFlightMessages(pair);
3648
+ async planConsentEdgeInFlightSettlementsLocked(edge, input) {
3649
+ const affected = await this.store.inspectAffectedConsentEdgeInFlightMessages(edge);
3801
3650
  const observedAt = this.now().getTime();
3802
3651
  return affected.map(({ messageId }) => {
3803
3652
  const tracker = this.deliveryTrackers.get(messageId);
@@ -3828,9 +3677,8 @@ export class GatewayService {
3828
3677
  };
3829
3678
  });
3830
3679
  }
3831
- purgePairCapabilitiesLocked(pair) {
3832
- const matches = (sourceAlias, targetAlias) => (sourceAlias === pair.claudeAlias && targetAlias === pair.codexAlias) ||
3833
- (sourceAlias === pair.codexAlias && targetAlias === pair.claudeAlias);
3680
+ purgeConsentEdgeCapabilitiesLocked(aliases) {
3681
+ const matches = (sourceAlias, targetAlias) => aliases.includes(sourceAlias) && aliases.includes(targetAlias);
3834
3682
  const conversationIds = new Set();
3835
3683
  for (const [conversationId, conversation] of this.conversations) {
3836
3684
  if (conversation.pair !== true ||
@@ -4614,12 +4462,13 @@ export class GatewayService {
4614
4462
  * native-ingress boundary; this preserves the exact connector generation
4615
4463
  * without recreating route authority or accepting a new unsolicited send.
4616
4464
  */
4617
- async enqueueObservedClaudeReplyAfterRouteTeardownLocked(conversation, sourceBinding, text) {
4465
+ async enqueueObservedClaudeReplyAfterRouteTeardownLocked(conversation, sourceBinding, expectedTargetBinding, text) {
4618
4466
  const target = await this.store.resolveRoute(conversation.sourceAlias);
4619
4467
  if (sourceBinding.provider !== "claude" ||
4620
- target.provider !== "codex" ||
4468
+ target.provider === "claude" ||
4469
+ target.provider !== expectedTargetBinding.provider || target.ownerLease !== expectedTargetBinding.ownerLease ||
4621
4470
  sourceBinding.hostId !== target.hostId) {
4622
- throw new BridgeError("RECOVERED_REPLY_ROUTE_MISMATCH", "The retained provider reply no longer matches an exact same-host Codex target.");
4471
+ throw new BridgeError("RECOVERED_REPLY_ROUTE_MISMATCH", "The retained provider reply no longer matches an exact same-host non-Claude target.");
4623
4472
  }
4624
4473
  const sequence = conversation.nextSequence;
4625
4474
  const enqueuedAt = this.now().getTime();
@@ -4650,6 +4499,7 @@ export class GatewayService {
4650
4499
  expectsReply: false,
4651
4500
  sequence,
4652
4501
  targetBindingKey: bindingKey(target),
4502
+ sourceBinding: { ...sourceBinding },
4653
4503
  authorization: "selected_route",
4654
4504
  targetAlias: conversation.sourceAlias,
4655
4505
  deadlineAt,
@@ -4662,7 +4512,7 @@ export class GatewayService {
4662
4512
  deadlineAt,
4663
4513
  });
4664
4514
  await this.changed();
4665
- await this.setNativeCodexStatus(conversation.sourceAlias, "waiting");
4515
+ await this.setNativeSourceStatus(conversation.sourceAlias, "waiting");
4666
4516
  this.scheduleDispatch(conversation.sourceAlias);
4667
4517
  }
4668
4518
  pruneTransient() {
@@ -4882,7 +4732,6 @@ export class GatewayService {
4882
4732
  !dispatchable ||
4883
4733
  inspection === undefined ||
4884
4734
  !inspection.enabled ||
4885
- inspection.compatibility !== "compatible" ||
4886
4735
  !storedDispatchable ||
4887
4736
  bindingKey(inspection.binding) !== bindingKey(binding) ||
4888
4737
  bindingKey(selectedBinding) !== bindingKey(binding)) {
@@ -4972,10 +4821,26 @@ export class GatewayService {
4972
4821
  })).markerActive
4973
4822
  : false;
4974
4823
  }
4824
+ const sourceBinding = context.sourceBinding ??
4825
+ (await this.store.inspectPrivateRoute(item.sourceAlias))?.binding;
4826
+ const parsedDirection = parseDirection(item.direction);
4827
+ if (sourceBinding === undefined ||
4828
+ parsedDirection?.sourceProvider !== sourceBinding.provider ||
4829
+ parsedDirection.targetProvider !== binding.provider ||
4830
+ item.direction !== directionId(sourceBinding.provider, binding.provider)) {
4831
+ await this.finishDelivery({
4832
+ messageId: item.messageId,
4833
+ state: "failed",
4834
+ safeErrorCode: "MESSAGE_DIRECTION_BINDING_MISMATCH",
4835
+ });
4836
+ return;
4837
+ }
4838
+ context.sourceBinding ??= { ...sourceBinding };
4975
4839
  let result;
4976
4840
  try {
4977
4841
  result = await this.awaitDispatchWithNativeHeldLocked(tracker, this.adapter(binding.provider, binding.hostId).dispatch({
4978
4842
  sourceAlias: item.sourceAlias,
4843
+ sourceProvider: sourceBinding.provider,
4979
4844
  targetAlias: item.targetAlias,
4980
4845
  conversationId: context.conversationId,
4981
4846
  binding,
@@ -5068,13 +4933,12 @@ export class GatewayService {
5068
4933
  await this.store.observeRoute({
5069
4934
  binding,
5070
4935
  state: "idle",
5071
- compatibility: "compatible",
5072
4936
  });
5073
4937
  }
5074
4938
  else if (binding.provider === "codex") {
5075
4939
  this.scheduleHeldRedispatch(currentTargetAlias);
5076
4940
  }
5077
- await this.setNativeCodexStatus(currentTargetAlias, "waiting");
4941
+ await this.setNativeSourceStatus(currentTargetAlias, "waiting");
5078
4942
  }
5079
4943
  }
5080
4944
  else if (requeued.status === "settled") {
@@ -5285,13 +5149,12 @@ export class GatewayService {
5285
5149
  .then(() => true)
5286
5150
  .catch(() => false);
5287
5151
  const pairStillActive = conversation.pair !== true ||
5288
- (await this.store.hasPair({
5289
- claudeAlias: conversation.targetAlias,
5290
- codexAlias: conversation.sourceAlias,
5152
+ (await this.store.hasConsentEdge({
5153
+ aliases: [conversation.targetAlias, conversation.sourceAlias],
5291
5154
  }));
5292
5155
  if ((!sourceStillRoutable || !pairStillActive) &&
5293
- sourceBinding?.provider === "claude") {
5294
- await this.enqueueObservedClaudeReplyAfterRouteTeardownLocked(conversation, sourceBinding, replyText);
5156
+ sourceBinding?.provider === "claude" && context.sourceBinding !== undefined) {
5157
+ await this.enqueueObservedClaudeReplyAfterRouteTeardownLocked(conversation, sourceBinding, context.sourceBinding, replyText);
5295
5158
  }
5296
5159
  else {
5297
5160
  await this.enqueue(conversation.targetAlias, conversation.sourceAlias, replyText, false, {
@@ -5384,7 +5247,7 @@ export class GatewayService {
5384
5247
  };
5385
5248
  const collision = this.routeBindings.get(event.sourceAlias);
5386
5249
  if (target === undefined ||
5387
- target.provider !== "codex" ||
5250
+ target.provider === "claude" ||
5388
5251
  target.hostId !== event.endpoint.hostId ||
5389
5252
  (collision !== undefined &&
5390
5253
  bindingKey(collision) !== bindingKey(sourceBinding))) {
@@ -5426,6 +5289,7 @@ export class GatewayService {
5426
5289
  expectsReply: !steer,
5427
5290
  sequence: 0,
5428
5291
  targetBindingKey: bindingKey(target),
5292
+ sourceBinding: { ...sourceBinding },
5429
5293
  authorization: "selected_route",
5430
5294
  targetAlias: event.targetAlias,
5431
5295
  deadlineAt,
@@ -5451,7 +5315,7 @@ export class GatewayService {
5451
5315
  deadlineAt,
5452
5316
  });
5453
5317
  await this.changed();
5454
- await this.setNativeCodexStatus(event.targetAlias, "waiting");
5318
+ await this.setNativeSourceStatus(event.targetAlias, "waiting");
5455
5319
  // Native ingress gets one immediate dispatch opportunity. Only an
5456
5320
  // actual queued/waiting result earns a `held` acknowledgement; an idle
5457
5321
  // fast path can therefore settle with one terminal acknowledgement.
@@ -5521,7 +5385,7 @@ export class GatewayService {
5521
5385
  return activationDeferred;
5522
5386
  }
5523
5387
  finally {
5524
- if (!succeeded && event.outcome === "compatible") {
5388
+ if (!succeeded) {
5525
5389
  adapter.releaseEndpointRefreshSelectorClaim?.(event.current.endpointGeneration);
5526
5390
  }
5527
5391
  if (ownsActiveTransition &&
@@ -5540,56 +5404,29 @@ export class GatewayService {
5540
5404
  }
5541
5405
  }
5542
5406
  async applyCodexEndpointRefresh(adapter, event, transitionKey, options) {
5407
+ const routeHandles = Array.isArray(event.routes)
5408
+ ? event.routes.map((route) => route.routeHandle)
5409
+ : [];
5543
5410
  if (this.closing ||
5544
- (event.outcome !== "compatible" && event.outcome !== "incompatible") ||
5545
- !isCompatibilityAttestation(event.attestation) ||
5546
- event.attestation.surface !== "codex" ||
5547
5411
  event.previous.provider !== "codex" ||
5548
5412
  !PRIVATE_HANDLE.test(event.previous.endpointGeneration) ||
5549
5413
  adapter.identity.provider !== "codex" ||
5550
5414
  adapter.identity.hostId !== event.previous.hostId ||
5551
- !this.config.allowedHosts.includes(event.previous.hostId)) {
5415
+ !this.config.allowedHosts.includes(event.previous.hostId) ||
5416
+ event.current.provider !== "codex" ||
5417
+ event.current.hostId !== event.previous.hostId ||
5418
+ !PRIVATE_HANDLE.test(event.current.endpointGeneration) ||
5419
+ event.current.endpointGeneration === event.previous.endpointGeneration ||
5420
+ adapter.identity.endpointGeneration !== event.current.endpointGeneration ||
5421
+ !Array.isArray(event.routes) ||
5422
+ event.routes.length > this.config.limits.maxRoutes ||
5423
+ new Set(routeHandles).size !== routeHandles.length ||
5424
+ event.routes.some((route) => !PRIVATE_HANDLE.test(route.routeHandle) ||
5425
+ !["idle", "busy", "awaiting_approval"].includes(route.state))) {
5552
5426
  throw new BridgeError("CODEX_ENDPOINT_REFRESH_MISMATCH", "The Codex endpoint transition failed closed runtime validation.");
5553
5427
  }
5554
- if (event.outcome === "compatible") {
5555
- const routeHandles = Array.isArray(event.routes)
5556
- ? event.routes.map((route) => route.routeHandle)
5557
- : [];
5558
- if (event.current.provider !== "codex" ||
5559
- event.current.hostId !== event.previous.hostId ||
5560
- !PRIVATE_HANDLE.test(event.current.endpointGeneration) ||
5561
- event.current.endpointGeneration ===
5562
- event.previous.endpointGeneration ||
5563
- adapter.identity.endpointGeneration !==
5564
- event.current.endpointGeneration ||
5565
- event.attestation.version !== adapter.protocolVersion ||
5566
- event.attestation.tier === "incompatible" ||
5567
- !Array.isArray(event.routes) ||
5568
- event.routes.length > this.config.limits.maxRoutes ||
5569
- new Set(routeHandles).size !== routeHandles.length ||
5570
- event.routes.some((route) => !PRIVATE_HANDLE.test(route.routeHandle) ||
5571
- !["idle", "busy", "awaiting_approval"].includes(route.state))) {
5572
- throw new BridgeError("CODEX_ENDPOINT_REFRESH_MISMATCH", "The refreshed Codex provider identity changed before activation.");
5573
- }
5574
- if (this.activatedEndpointRefreshes.get(event.current.hostId) ===
5575
- transitionKey) {
5576
- return false;
5577
- }
5578
- }
5579
- else {
5580
- if (event.candidate.provider !== "codex" ||
5581
- event.candidate.hostId !== event.previous.hostId ||
5582
- !PRIVATE_HANDLE.test(event.candidate.endpointGeneration) ||
5583
- event.candidate.endpointGeneration ===
5584
- event.previous.endpointGeneration ||
5585
- adapter.identity.endpointGeneration !==
5586
- event.previous.endpointGeneration ||
5587
- event.attestation.tier !== "incompatible") {
5588
- throw new BridgeError("CODEX_ENDPOINT_REFRESH_MISMATCH", "The incompatible Codex candidate no longer matches the active endpoint.");
5589
- }
5590
- if (this.handledIncompatibleEndpointRefreshes.get(event.previous.hostId) === transitionKey) {
5591
- return false;
5592
- }
5428
+ if (this.activatedEndpointRefreshes.get(event.current.hostId) === transitionKey) {
5429
+ return false;
5593
5430
  }
5594
5431
  let inventory = await this.store.inspectPrivateCodexRoutes();
5595
5432
  const previousRoutes = inventory.filter((route) => route.binding.hostId === event.previous.hostId &&
@@ -5607,11 +5444,10 @@ export class GatewayService {
5607
5444
  settlements = await this.store.markConnectorOffline(event.previous, "ENDPOINT_GENERATION_CHANGED", inFlightSettlements, { preserveQueued: true });
5608
5445
  }
5609
5446
  catch (error) {
5610
- const alreadyInvalidatedCompatibleTransition = event.outcome === "compatible" &&
5611
- error instanceof BridgeError &&
5447
+ const alreadyInvalidatedTransition = error instanceof BridgeError &&
5612
5448
  error.code === "CONNECTOR_NOT_FOUND" &&
5613
- previousRoutes.every((route) => route.state === "stale" && route.compatibility === "expired");
5614
- if (!alreadyInvalidatedCompatibleTransition)
5449
+ previousRoutes.every((route) => route.state === "stale");
5450
+ if (!alreadyInvalidatedTransition)
5615
5451
  throw error;
5616
5452
  }
5617
5453
  for (const settlement of settlements) {
@@ -5620,47 +5456,9 @@ export class GatewayService {
5620
5456
  for (const route of previousRoutes) {
5621
5457
  this.routeStates.set(route.alias, "stale");
5622
5458
  }
5623
- await this.store.recordCompatibilityAttestation(event.attestation);
5624
- if (event.outcome === "incompatible") {
5625
- await this.requireEndpointRefreshUnconflicted();
5626
- this.activeCodexIncompatibility = structuredClone(event.attestation);
5627
- this.incompatibleProviderSurfaces.set(this.providerSurfaceKey("codex", event.previous.hostId), event.attestation.safeErrorCode ?? "CODEX_ENDPOINT_INCOMPATIBLE");
5628
- this.addRuntimeAlert(event.attestation.safeErrorCode ?? "CODEX_ENDPOINT_INCOMPATIBLE", "error", { provider: "codex", host: event.previous.hostId });
5629
- this.handledIncompatibleEndpointRefreshes.set(event.previous.hostId, transitionKey);
5630
- await this.changed();
5631
- return false;
5632
- }
5633
- if (event.attestation.tier === "schema_attested" &&
5634
- !(compatibilityCoversWrites(event.attestation) &&
5635
- adapter.latestWriteCompatibilityProbeObservation?.(event.current.endpointGeneration)?.outcome === "pass")) {
5636
- // A passing untested-version schema is useful observation, but it is not
5637
- // durable route or write authority. Settle the provider's pending probe
5638
- // so a later generation can be examined, while leaving every G1 route
5639
- // stale and refusing selector/registration activation.
5640
- await this.requireEndpointRefreshUnconflicted();
5641
- await this.store.observeConnector({
5642
- identity: event.current,
5643
- health: "degraded",
5644
- compatibility: "unknown",
5645
- protocol: adapter.protocol,
5646
- protocolVersion: adapter.protocolVersion,
5647
- safeErrorCode: "CODEX_MONITOR_ONLY",
5648
- });
5649
- adapter.acceptCompatibilityAttestation(event.attestation);
5650
- this.activeCodexIncompatibility = undefined;
5651
- this.incompatibleProviderSurfaces.set(this.providerSurfaceKey("codex", event.current.hostId), "CODEX_MONITOR_ONLY");
5652
- this.handledIncompatibleEndpointRefreshes.delete(event.current.hostId);
5653
- await this.changed();
5654
- if (options.deferActivation === true ||
5655
- options.requiredRoute !== undefined) {
5656
- throw new BridgeError("CODEX_MONITOR_ONLY", "This Codex App Server passed read-only schema probes but cannot authorize a task route or message write.");
5657
- }
5658
- return false;
5659
- }
5660
5459
  await this.store.observeConnector({
5661
5460
  identity: event.current,
5662
5461
  health: "healthy",
5663
- compatibility: "compatible",
5664
5462
  protocol: adapter.protocol,
5665
5463
  protocolVersion: adapter.protocolVersion,
5666
5464
  });
@@ -5715,7 +5513,7 @@ export class GatewayService {
5715
5513
  route.binding.routeHandle === expected.threadId &&
5716
5514
  route.binding.ownerLease === expected.ownerLease &&
5717
5515
  route.enabled &&
5718
- route.compatibility === "compatible");
5516
+ route.state !== "stale");
5719
5517
  return matches.length === 1;
5720
5518
  });
5721
5519
  if (committedExactly) {
@@ -5748,8 +5546,7 @@ export class GatewayService {
5748
5546
  route.binding.hostId !== event.current.hostId ||
5749
5547
  route.binding.endpointGeneration !==
5750
5548
  event.current.endpointGeneration ||
5751
- route.state !== "stale" ||
5752
- route.compatibility !== "expired") {
5549
+ route.state !== "stale") {
5753
5550
  continue;
5754
5551
  }
5755
5552
  await this.registerOrRebind(route.alias, route.binding, "endpoint_reobserved", state, route.binding.ownerLease);
@@ -5764,7 +5561,7 @@ export class GatewayService {
5764
5561
  event.current.endpointGeneration &&
5765
5562
  route.binding.routeHandle === observed.routeHandle &&
5766
5563
  route.enabled &&
5767
- route.compatibility === "compatible");
5564
+ route.state !== "stale");
5768
5565
  if (matches.length > 1) {
5769
5566
  throw new BridgeError("CODEX_ENDPOINT_REFRESH_MISMATCH", "A refreshed Codex task has multiple durable owners.");
5770
5567
  }
@@ -5804,7 +5601,7 @@ export class GatewayService {
5804
5601
  event.current.endpointGeneration &&
5805
5602
  route.binding.routeHandle === requiredRoute.threadId &&
5806
5603
  route.enabled &&
5807
- route.compatibility === "compatible");
5604
+ route.state !== "stale");
5808
5605
  const match = matches[0];
5809
5606
  if (matches.length !== 1 || match === undefined) {
5810
5607
  throw new BridgeError("CODEX_ENDPOINT_REFRESH_MISMATCH", "The registering Codex task was not durably installed exactly.");
@@ -5858,7 +5655,7 @@ export class GatewayService {
5858
5655
  }
5859
5656
  for (const route of activatedRoutes) {
5860
5657
  requireNativeContinuity(route);
5861
- await this.setNativeCodexStatus(route.alias, route.state === "idle"
5658
+ await this.setNativeSourceStatus(route.alias, route.state === "idle"
5862
5659
  ? "idle"
5863
5660
  : route.state === "awaiting_approval"
5864
5661
  ? "waiting"
@@ -5898,10 +5695,7 @@ export class GatewayService {
5898
5695
  for (const route of activatedRoutes) {
5899
5696
  requireNativeContinuity(route);
5900
5697
  }
5901
- // Provider writes remain closed until every durable and native transition
5902
- // above is complete. This acknowledgement is deliberately the final
5903
- // fallible activation step.
5904
- adapter.acceptCompatibilityAttestation(event.attestation);
5698
+ adapter.activateEndpointGeneration(event.current.endpointGeneration);
5905
5699
  }
5906
5700
  catch (error) {
5907
5701
  try {
@@ -5921,9 +5715,6 @@ export class GatewayService {
5921
5715
  ?.transitionKey === transitionKey) {
5922
5716
  this.pendingSelectorEndpointActivations.delete(event.current.hostId);
5923
5717
  }
5924
- this.handledIncompatibleEndpointRefreshes.delete(event.current.hostId);
5925
- this.activeCodexIncompatibility = undefined;
5926
- this.incompatibleProviderSurfaces.delete(this.providerSurfaceKey("codex", event.current.hostId));
5927
5718
  return false;
5928
5719
  }
5929
5720
  async onRouteState(source, event) {
@@ -5945,7 +5736,6 @@ export class GatewayService {
5945
5736
  await this.store.observeConnector({
5946
5737
  identity: source,
5947
5738
  health: event.state === "stale" ? "degraded" : "healthy",
5948
- compatibility: event.state === "stale" ? "expired" : "compatible",
5949
5739
  protocol: adapter.protocol,
5950
5740
  protocolVersion: adapter.protocolVersion,
5951
5741
  ...(event.safeErrorCode === undefined
@@ -5961,7 +5751,6 @@ export class GatewayService {
5961
5751
  await this.store.observeRoute({
5962
5752
  binding,
5963
5753
  state: "stale",
5964
- compatibility: "expired",
5965
5754
  safeErrorCode: staleSafeCode,
5966
5755
  });
5967
5756
  }
@@ -5969,7 +5758,6 @@ export class GatewayService {
5969
5758
  await this.store.observeRoute({
5970
5759
  binding,
5971
5760
  state: event.state,
5972
- compatibility: "compatible",
5973
5761
  ...(event.safeErrorCode === undefined
5974
5762
  ? {}
5975
5763
  : {
@@ -5979,15 +5767,15 @@ export class GatewayService {
5979
5767
  }
5980
5768
  await this.store.touchProgressWatchesForAlias(alias);
5981
5769
  await this.changed();
5982
- if (binding.provider === "codex") {
5983
- await this.setNativeCodexStatus(alias, event.state === "idle"
5770
+ if (binding.provider !== "claude") {
5771
+ await this.setNativeSourceStatus(alias, event.state === "idle"
5984
5772
  ? "idle"
5985
5773
  : event.state === "awaiting_approval"
5986
5774
  ? "waiting"
5987
5775
  : "busy");
5988
- if (previousState === "stale" && event.state !== "stale") {
5989
- adapter.rearmEndpointRefreshActivation?.(binding.endpointGeneration);
5990
- }
5776
+ }
5777
+ if (binding.provider === "codex" && previousState === "stale" && event.state !== "stale") {
5778
+ adapter.rearmEndpointRefreshActivation?.(binding.endpointGeneration);
5991
5779
  }
5992
5780
  if (event.state === "idle" || event.state === "stale") {
5993
5781
  const heldTimer = this.heldRedispatchTimers.get(alias);
@@ -5998,12 +5786,12 @@ export class GatewayService {
5998
5786
  this.scheduleDispatch(alias);
5999
5787
  }
6000
5788
  }
6001
- async setNativeCodexStatus(alias, status, requireSuccess = false) {
5789
+ async setNativeSourceStatus(alias, status, requireSuccess = false) {
6002
5790
  const binding = this.routeBindings.get(alias);
6003
- if (binding?.provider !== "codex")
5791
+ if (binding === undefined || binding.provider === "claude")
6004
5792
  return;
6005
5793
  const adapter = this.adapter("claude", binding.hostId);
6006
- const update = adapter.updateNativeCodexPeerStatus;
5794
+ const update = adapter.updateNativeSourcePeerStatus;
6007
5795
  if (update === undefined) {
6008
5796
  if (requireSuccess) {
6009
5797
  throw new BridgeError("CODEX_ENDPOINT_REFRESH_NATIVE_STATUS_FAILED", "The native listener does not support refreshed Codex status activation.", true);
@@ -6102,30 +5890,6 @@ export class GatewayService {
6102
5890
  }
6103
5891
  async publicSnapshotLocked() {
6104
5892
  const base = await this.store.publicSnapshot();
6105
- const compatibilityChecks = [];
6106
- for (const { surface } of this.compatibilitySurfaceDefinitions) {
6107
- if (surface === "codex" && this.activeCodexIncompatibility !== undefined) {
6108
- compatibilityChecks.push(structuredClone(this.activeCodexIncompatibility));
6109
- continue;
6110
- }
6111
- const currentVersions = [
6112
- ...new Set([...this.adapters, ...this.compatibilityObservers].flatMap((observer) => {
6113
- const observation = observer.compatibilitySurface?.();
6114
- return observation?.surface === surface ? [observation.version] : [];
6115
- })),
6116
- ].sort((left, right) => left.localeCompare(right));
6117
- const currentAttestations = (await Promise.all(currentVersions.map(async (version) => await this.store.inspectCompatibilityAttestation(surface, version))))
6118
- .filter((attestation) => attestation !== undefined)
6119
- .sort((left, right) => {
6120
- const checkedAt = right.checkedAt.localeCompare(left.checkedAt);
6121
- if (checkedAt !== 0)
6122
- return checkedAt;
6123
- return left.version.localeCompare(right.version);
6124
- });
6125
- const selected = currentAttestations[0];
6126
- if (selected !== undefined)
6127
- compatibilityChecks.push(selected);
6128
- }
6129
5893
  const peers = this.availablePeers.map((peer) => ({ ...peer }));
6130
5894
  if (!arePublicAvailablePeerSnapshots(peers)) {
6131
5895
  throw new BridgeError("INVALID_TRANSIENT_INVENTORY", "The transient peer inventory is unsafe.");
@@ -6154,11 +5918,6 @@ export class GatewayService {
6154
5918
  return projectGatewayPublicSnapshot({
6155
5919
  ...base,
6156
5920
  connectors: this.connectorsWithRegistry(base.connectors),
6157
- ...(compatibilityChecks.length === 0
6158
- ? {}
6159
- : {
6160
- compatibilityChecks: compatibilityChecks.map(projectPublicCompatibilityCheck),
6161
- }),
6162
5921
  availablePeers: peers,
6163
5922
  activityEvents: this.activityEvents.map((event) => ({
6164
5923
  ...event,