agent-embassy 2.0.1 → 3.1.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 (147) hide show
  1. package/CHANGELOG.md +77 -0
  2. package/CONTRIBUTING.md +23 -36
  3. package/README.md +123 -222
  4. package/SECURITY.md +72 -93
  5. package/dist/src/errors.d.ts +10 -1
  6. package/dist/src/errors.js +3 -1
  7. package/dist/src/errors.js.map +1 -1
  8. package/dist/src/gateway/claude-helper-protocol.d.ts +8 -11
  9. package/dist/src/gateway/claude-helper-protocol.js +12 -11
  10. package/dist/src/gateway/claude-helper-protocol.js.map +1 -1
  11. package/dist/src/gateway/claude-helper-supervisor.d.ts +1 -5
  12. package/dist/src/gateway/claude-helper-supervisor.js +45 -17
  13. package/dist/src/gateway/claude-helper-supervisor.js.map +1 -1
  14. package/dist/src/gateway/claude-helper.js +6 -6
  15. package/dist/src/gateway/claude-helper.js.map +1 -1
  16. package/dist/src/gateway/claude-peer.d.ts +0 -3
  17. package/dist/src/gateway/claude-peer.js +7 -19
  18. package/dist/src/gateway/claude-peer.js.map +1 -1
  19. package/dist/src/gateway/cli.d.ts +17 -7
  20. package/dist/src/gateway/cli.js +945 -265
  21. package/dist/src/gateway/cli.js.map +1 -1
  22. package/dist/src/gateway/codex-socket-holder.d.ts +26 -0
  23. package/dist/src/gateway/codex-socket-holder.js +76 -0
  24. package/dist/src/gateway/codex-socket-holder.js.map +1 -0
  25. package/dist/src/gateway/codex-stateless-transport.js +1 -1
  26. package/dist/src/gateway/codex-stateless-transport.js.map +1 -1
  27. package/dist/src/gateway/config.d.ts +1 -10
  28. package/dist/src/gateway/config.js +4 -10
  29. package/dist/src/gateway/config.js.map +1 -1
  30. package/dist/src/gateway/control.d.ts +66 -80
  31. package/dist/src/gateway/control.js +92 -147
  32. package/dist/src/gateway/control.js.map +1 -1
  33. package/dist/src/gateway/federation-nodes.d.ts +29 -2
  34. package/dist/src/gateway/federation-nodes.js +177 -7
  35. package/dist/src/gateway/federation-nodes.js.map +1 -1
  36. package/dist/src/gateway/peer-client.d.ts +4 -3
  37. package/dist/src/gateway/peer-client.js +22 -13
  38. package/dist/src/gateway/peer-client.js.map +1 -1
  39. package/dist/src/gateway/peer-protocol.d.ts +13 -11
  40. package/dist/src/gateway/peer-protocol.js +14 -12
  41. package/dist/src/gateway/peer-protocol.js.map +1 -1
  42. package/dist/src/gateway/provenance-envelope.d.ts +0 -1
  43. package/dist/src/gateway/provenance-envelope.js +4 -19
  44. package/dist/src/gateway/provenance-envelope.js.map +1 -1
  45. package/dist/src/gateway/providers.d.ts +11 -7
  46. package/dist/src/gateway/providers.js +49 -50
  47. package/dist/src/gateway/providers.js.map +1 -1
  48. package/dist/src/gateway/server.d.ts +4 -14
  49. package/dist/src/gateway/server.js +32 -54
  50. package/dist/src/gateway/server.js.map +1 -1
  51. package/dist/src/gateway/service-agent.d.ts +187 -0
  52. package/dist/src/gateway/service-agent.js +758 -0
  53. package/dist/src/gateway/service-agent.js.map +1 -0
  54. package/dist/src/gateway/service.d.ts +119 -38
  55. package/dist/src/gateway/service.js +754 -798
  56. package/dist/src/gateway/service.js.map +1 -1
  57. package/dist/src/gateway/status-view.d.ts +119 -0
  58. package/dist/src/gateway/status-view.js +484 -0
  59. package/dist/src/gateway/status-view.js.map +1 -0
  60. package/dist/src/gateway/store.d.ts +106 -21
  61. package/dist/src/gateway/store.js +463 -537
  62. package/dist/src/gateway/store.js.map +1 -1
  63. package/dist/src/gateway/types.d.ts +64 -102
  64. package/dist/src/gateway/types.js +40 -86
  65. package/dist/src/gateway/types.js.map +1 -1
  66. package/docs/CONFIGURATION.md +174 -45
  67. package/docs/DELIVERY.md +11 -11
  68. package/docs/GATEWAY-ARCHITECTURE.md +292 -384
  69. package/package.json +4 -12
  70. package/skills/embassy-peer/SKILL.md +69 -92
  71. package/skills/embassy-peer/agents/openai.yaml +1 -1
  72. package/README.zh-CN.md +0 -275
  73. package/assets/live-dashboard/app.css +0 -1619
  74. package/assets/vendor/react/LICENSE +0 -21
  75. package/assets/vendor/react/react-dom.production.min.js +0 -267
  76. package/assets/vendor/react/react.production.min.js +0 -31
  77. package/dist/src/gateway/acp-client.d.ts +0 -110
  78. package/dist/src/gateway/acp-client.js +0 -407
  79. package/dist/src/gateway/acp-client.js.map +0 -1
  80. package/dist/src/gateway/acp-provider.d.ts +0 -66
  81. package/dist/src/gateway/acp-provider.js +0 -275
  82. package/dist/src/gateway/acp-provider.js.map +0 -1
  83. package/dist/src/gateway/claude-helper-client.d.ts +0 -2
  84. package/dist/src/gateway/claude-helper-client.js +0 -2
  85. package/dist/src/gateway/claude-helper-client.js.map +0 -1
  86. package/dist/src/gateway/cli-copy.d.ts +0 -8
  87. package/dist/src/gateway/cli-copy.en.d.ts +0 -22
  88. package/dist/src/gateway/cli-copy.en.js +0 -62
  89. package/dist/src/gateway/cli-copy.en.js.map +0 -1
  90. package/dist/src/gateway/cli-copy.js +0 -27
  91. package/dist/src/gateway/cli-copy.js.map +0 -1
  92. package/dist/src/gateway/cli-copy.zh-CN.d.ts +0 -22
  93. package/dist/src/gateway/cli-copy.zh-CN.js +0 -62
  94. package/dist/src/gateway/cli-copy.zh-CN.js.map +0 -1
  95. package/dist/src/gateway/codex-doctor.d.ts +0 -36
  96. package/dist/src/gateway/codex-doctor.js +0 -127
  97. package/dist/src/gateway/codex-doctor.js.map +0 -1
  98. package/dist/src/gateway/dashboard-copy.d.ts +0 -7
  99. package/dist/src/gateway/dashboard-copy.en.d.ts +0 -504
  100. package/dist/src/gateway/dashboard-copy.en.js +0 -505
  101. package/dist/src/gateway/dashboard-copy.en.js.map +0 -1
  102. package/dist/src/gateway/dashboard-copy.js +0 -514
  103. package/dist/src/gateway/dashboard-copy.js.map +0 -1
  104. package/dist/src/gateway/dashboard-copy.zh-CN.d.ts +0 -504
  105. package/dist/src/gateway/dashboard-copy.zh-CN.js +0 -505
  106. package/dist/src/gateway/dashboard-copy.zh-CN.js.map +0 -1
  107. package/dist/src/gateway/dashboard-model.d.ts +0 -343
  108. package/dist/src/gateway/dashboard-model.js +0 -1061
  109. package/dist/src/gateway/dashboard-model.js.map +0 -1
  110. package/dist/src/gateway/dashboard.d.ts +0 -20
  111. package/dist/src/gateway/dashboard.js +0 -874
  112. package/dist/src/gateway/dashboard.js.map +0 -1
  113. package/dist/src/gateway/deepseek-detect.d.ts +0 -14
  114. package/dist/src/gateway/deepseek-detect.js +0 -41
  115. package/dist/src/gateway/deepseek-detect.js.map +0 -1
  116. package/dist/src/gateway/live-dashboard-app/app.js +0 -2385
  117. package/dist/src/gateway/live-dashboard-assets.d.ts +0 -10
  118. package/dist/src/gateway/live-dashboard-assets.js +0 -74
  119. package/dist/src/gateway/live-dashboard-assets.js.map +0 -1
  120. package/dist/src/gateway/live-dashboard-command.d.ts +0 -60
  121. package/dist/src/gateway/live-dashboard-command.js +0 -334
  122. package/dist/src/gateway/live-dashboard-command.js.map +0 -1
  123. package/dist/src/gateway/live-dashboard-http.d.ts +0 -39
  124. package/dist/src/gateway/live-dashboard-http.js +0 -383
  125. package/dist/src/gateway/live-dashboard-http.js.map +0 -1
  126. package/dist/src/gateway/live-dashboard-protocol.d.ts +0 -34
  127. package/dist/src/gateway/live-dashboard-protocol.js +0 -114
  128. package/dist/src/gateway/live-dashboard-protocol.js.map +0 -1
  129. package/dist/src/gateway/live-dashboard-server.d.ts +0 -33
  130. package/dist/src/gateway/live-dashboard-server.js +0 -144
  131. package/dist/src/gateway/live-dashboard-server.js.map +0 -1
  132. package/dist/src/gateway/live-dashboard-stream.d.ts +0 -46
  133. package/dist/src/gateway/live-dashboard-stream.js +0 -234
  134. package/dist/src/gateway/live-dashboard-stream.js.map +0 -1
  135. package/dist/src/gateway/live-dashboard.d.ts +0 -28
  136. package/dist/src/gateway/live-dashboard.js +0 -154
  137. package/dist/src/gateway/live-dashboard.js.map +0 -1
  138. package/dist/src/gateway/locale.d.ts +0 -4
  139. package/dist/src/gateway/locale.js +0 -10
  140. package/dist/src/gateway/locale.js.map +0 -1
  141. package/dist/src/gateway/progress-watch-machine.d.ts +0 -45
  142. package/dist/src/gateway/progress-watch-machine.js +0 -70
  143. package/dist/src/gateway/progress-watch-machine.js.map +0 -1
  144. package/docs/CONFIGURATION.zh-CN.md +0 -97
  145. package/docs/DASHBOARD.md +0 -98
  146. package/docs/DASHBOARD.zh-CN.md +0 -49
  147. package/docs/DELIVERY.zh-CN.md +0 -55
@@ -1,14 +1,10 @@
1
- export declare const gatewayProviders: readonly ["claude", "codex", "deepseek", "grok", "peer"];
1
+ export declare const gatewayProviders: readonly ["claude", "codex", "peer"];
2
2
  export type GatewayProvider = (typeof gatewayProviders)[number];
3
3
  export declare const gatewayRegistrationIngressPrefixes: Readonly<{
4
4
  claude: undefined;
5
5
  codex: string;
6
- deepseek: string;
7
- grok: string;
8
6
  peer: string;
9
7
  }>;
10
- export declare const gatewayInboundModes: readonly ["paired", "open"];
11
- export type GatewayInboundMode = (typeof gatewayInboundModes)[number];
12
8
  export declare const connectorHealthStates: readonly ["offline", "connecting", "healthy", "degraded"];
13
9
  export type ConnectorHealth = (typeof connectorHealthStates)[number];
14
10
  export declare const routeStates: readonly ["stale", "idle", "busy", "awaiting_approval", "offline", "disabled"];
@@ -22,7 +18,6 @@ export type ParsedMessageDirection = Readonly<{
22
18
  }>;
23
19
  export declare const isGatewayProvider: (value: unknown) => value is GatewayProvider;
24
20
  export declare function directionId(sourceProvider: GatewayProvider, targetProvider: GatewayProvider): MessageDirection;
25
- export declare const messageDirections: readonly MessageDirection[];
26
21
  export declare function parseDirection(value: unknown): ParsedMessageDirection | undefined;
27
22
  export declare const isMessageDirection: (value: unknown) => value is MessageDirection;
28
23
  export declare const deliveryStates: readonly ["queued", "duplicate", "dispatching", "transport_written", "held", "delivered", "unconfirmed", "failed", "ambiguous", "expired", "cancelled", "abandoned", "rejected"];
@@ -36,9 +31,6 @@ export declare const gatewayPublicSnapshotLimits: Readonly<{
36
31
  readonly registryRejectionCodes: 32;
37
32
  readonly availablePeers: 256;
38
33
  readonly routes: 256;
39
- readonly consentEdges: 256;
40
- readonly progressWatches: 64;
41
- readonly progressWatchEvents: 256;
42
34
  readonly activityEvents: 256;
43
35
  readonly messages: 1024;
44
36
  readonly alerts: 256;
@@ -76,22 +68,6 @@ export type GatewayRouteRecord = {
76
68
  updatedAt: string;
77
69
  counters: RouteCounters;
78
70
  };
79
- /**
80
- * Durable bidirectional consent between two exact route owners. Aliases are
81
- * display coordinates; the private registration identities prevent an alias from silently
82
- * retargeting an existing permission edge.
83
- */
84
- export type GatewayConsentEndpoint = Readonly<{
85
- alias: string;
86
- provider: GatewayProvider;
87
- registrationId: string;
88
- }>;
89
- export type GatewayConsentEdgeRecord = {
90
- endpoints: readonly [GatewayConsentEndpoint, GatewayConsentEndpoint];
91
- createdAt: string;
92
- updatedAt: string;
93
- counters: RouteCounters;
94
- };
95
71
  /**
96
72
  * Controller-internal route view used to prove ownership across a restart.
97
73
  * This type must never cross the control protocol or enter a public snapshot.
@@ -113,8 +89,6 @@ export type QueuedMessageMetadata = {
113
89
  deadlineAt: string;
114
90
  bytes: number;
115
91
  body?: string;
116
- pair?: true;
117
- transientTarget?: true;
118
92
  steer?: true;
119
93
  };
120
94
  export type DedupeRecord = {
@@ -124,7 +98,6 @@ export type DedupeRecord = {
124
98
  sourceAlias: string;
125
99
  targetAlias: string;
126
100
  direction: MessageDirection;
127
- pair?: true;
128
101
  firstSeenAt: string;
129
102
  expiresAt: string;
130
103
  };
@@ -163,7 +136,7 @@ export type GatewayAccounting = {
163
136
  queuedBytes: number;
164
137
  };
165
138
  export type GatewayPreparedWriteEvidence = {
166
- kind: "claude_mailbox" | "codex_turn_start" | "codex_turn_steer" | "acp_prompt" | "peer_mailbox" | "peer_handoff";
139
+ kind: "claude_mailbox" | "codex_turn_start" | "codex_turn_steer" | "peer_mailbox" | "peer_handoff";
167
140
  bodyBytes: number;
168
141
  bodySha256: string;
169
142
  frameBytes: number;
@@ -173,8 +146,7 @@ export type GatewayMessageAttemptAuthority = {
173
146
  attemptId: string;
174
147
  attemptCount: number;
175
148
  targetRegistrationId: string;
176
- sourceRegistrationId: string | null;
177
- consentEdge: readonly [GatewayConsentEndpoint, GatewayConsentEndpoint] | null;
149
+ sourceRegistrationId: string;
178
150
  };
179
151
  export type GatewayMessageState = {
180
152
  phase: "queued";
@@ -201,9 +173,8 @@ export type GatewayMessageState = {
201
173
  export type GatewayMessageRecord = QueuedMessageMetadata & {
202
174
  sequence: number;
203
175
  deliveryToken?: string;
204
- sourceRegistrationId: string | null;
176
+ sourceRegistrationId: string;
205
177
  targetRegistrationId: string | null;
206
- consentEdge: readonly [GatewayConsentEndpoint, GatewayConsentEndpoint] | null;
207
178
  state: GatewayMessageState;
208
179
  };
209
180
  export type GatewayMessageActivity = {
@@ -216,7 +187,7 @@ export type GatewayRuntimeActivity = {
216
187
  };
217
188
  export type GatewayStateActivity = GatewayMessageActivity | GatewayRuntimeActivity;
218
189
  export type GatewayPersistedState = {
219
- schemaVersion: 4;
190
+ schemaVersion: 5;
220
191
  commit: {
221
192
  sequence: number;
222
193
  id: string;
@@ -225,7 +196,6 @@ export type GatewayPersistedState = {
225
196
  updatedAt: string;
226
197
  eventSequence: number;
227
198
  routes: GatewayRouteRecord[];
228
- consentEdges: GatewayConsentEdgeRecord[];
229
199
  messages: GatewayMessageRecord[];
230
200
  dedupe: DedupeRecord[];
231
201
  rateBuckets: RateBucket[];
@@ -246,16 +216,6 @@ export type PublicRouteSnapshot = {
246
216
  safeErrorCode?: string;
247
217
  mutable?: boolean;
248
218
  };
249
- export type PublicConsentEndpointSnapshot = Readonly<{
250
- alias: string;
251
- provider: GatewayProvider;
252
- }>;
253
- export type PublicConsentEdgeSnapshot = {
254
- endpoints: readonly [PublicConsentEndpointSnapshot, PublicConsentEndpointSnapshot];
255
- host: string;
256
- counters: RouteCounters;
257
- mutable?: boolean;
258
- };
259
219
  export type PublicConnectorSnapshot = {
260
220
  provider: GatewayProvider;
261
221
  host: string;
@@ -264,15 +224,26 @@ export type PublicConnectorSnapshot = {
264
224
  protocolVersion: string;
265
225
  lastSeenAt?: string;
266
226
  observationAgeMs?: number;
267
- codexDoctor?: PublicCodexDoctorSnapshot;
268
227
  safeErrorCode?: string;
269
228
  registry?: PublicRegistryObservationSnapshot;
270
229
  };
271
- export type PublicCodexDoctorCondition = "split_brain" | "orphaned" | "attached" | "observation_stale" | "managed_layout_missing" | "unknown";
272
- export type PublicCodexDoctorSnapshot = Readonly<{
273
- conditions: readonly PublicCodexDoctorCondition[];
274
- }>;
275
230
  export declare const CONNECTOR_OBSERVATION_STALE_AFTER_MS = 35000;
231
+ /** The words a connector is allowed to say. Never a sentence, never a number. */
232
+ export type StatusWord = "ok" | "stale" | "degraded" | "offline";
233
+ /**
234
+ * Connector words. A connector reporting `CONNECTOR_OBSERVATION_STALE` has
235
+ * nothing wrong with it: no route of that provider was observed inside the
236
+ * broker's window. That is `stale`, not `degraded`, and the difference is the
237
+ * point — an idle Mac with no registered Codex task must not read as broken.
238
+ */
239
+ export declare function connectorWord(connector: PublicConnectorSnapshot): StatusWord;
240
+ /**
241
+ * The overall word is informational and derived from the two connectors a
242
+ * message actually travels through. A shell peer never contributes: its
243
+ * mailbox is pull-only, so "nobody is awaiting it" is a fact about the
244
+ * operator's other terminal, not about the broker.
245
+ */
246
+ export declare function gatewayHealthWord(connectors: readonly PublicConnectorSnapshot[]): StatusWord;
276
247
  /**
277
248
  * Bounded, native-ID-free evidence attached to one Claude connector. Counts
278
249
  * describe only the latest pass; the monotonic flag distinguishes an ordinary
@@ -295,7 +266,10 @@ export type PublicAvailablePeerState = (typeof publicAvailablePeerStates)[number
295
266
  * A transient, metadata-only discovery row. Native provider IDs, process
296
267
  * identifiers, registry/socket paths, and generations are deliberately absent.
297
268
  * Version 1 uses this for genuine Claude peers; Codex inventory remains the
298
- * explicit PublicRouteSnapshot list.
269
+ * explicit PublicRouteSnapshot list. `routed` means the session currently has
270
+ * an installed logical route — it is a fact about the route table, never a
271
+ * permission: any discovered session is addressable, and its route installs on
272
+ * first use.
299
273
  */
300
274
  export type PublicAvailablePeerSnapshot = {
301
275
  alias: string;
@@ -303,13 +277,18 @@ export type PublicAvailablePeerSnapshot = {
303
277
  host: string;
304
278
  state: PublicAvailablePeerState;
305
279
  validated: boolean;
306
- selected: boolean;
280
+ routed: boolean;
307
281
  lastSeenAt?: string;
308
282
  safeErrorCode?: string;
309
283
  };
310
- export declare const gatewayActivityKinds: readonly ["discovery", "selection", "registration", "pairing", "watch"];
284
+ export declare const gatewayActivityKinds: readonly ["discovery", "registration"];
311
285
  export type GatewayActivityKind = (typeof gatewayActivityKinds)[number];
312
- export declare const gatewayActivityActions: readonly ["discovery_refreshed", "claude_selected", "claude_unselected", "codex_registered", "codex_succeeded", "codex_unregistered", "routes_paired", "routes_unpaired", "watch_ended"];
286
+ /**
287
+ * `claude_route_installed` and `claude_route_retired` are journaled by the
288
+ * store when a send installs, renames, or displaces a discovered Claude
289
+ * session's route; there is no explicit Claude registration command.
290
+ */
291
+ export declare const gatewayActivityActions: readonly ["discovery_refreshed", "codex_registered", "codex_succeeded", "codex_unregistered", "claude_route_installed", "claude_route_retired", "route_retired"];
313
292
  export type GatewayActivityAction = (typeof gatewayActivityActions)[number];
314
293
  export type PublicGatewayActivityEvent = {
315
294
  sequence: number;
@@ -335,8 +314,6 @@ export type DeadlinePressureSnapshot = {
335
314
  expiredEvents: number;
336
315
  buckets: DeadlinePressureBucket[];
337
316
  };
338
- export declare function isPublicAvailablePeerSnapshot(value: unknown): value is PublicAvailablePeerSnapshot;
339
- export declare function arePublicAvailablePeerSnapshots(value: unknown, maximumRows?: number): value is PublicAvailablePeerSnapshot[];
340
317
  export declare function isPublicRegistryObservationSnapshot(value: unknown): value is PublicRegistryObservationSnapshot;
341
318
  export type SafeGatewayAlert = {
342
319
  code: string;
@@ -346,35 +323,13 @@ export type SafeGatewayAlert = {
346
323
  host?: string;
347
324
  alias?: string;
348
325
  };
349
- export type PublicProgressWatchSnapshot = {
350
- conversationIdSuffix: string;
351
- ownerAlias: string;
352
- workerAlias: string;
353
- lastActivityAt: string;
354
- nextActionAt: string;
355
- nudgeCount: 0 | 1 | 2;
356
- };
357
- export type PublicProgressWatchEventSnapshot = {
358
- sequence: number;
359
- timestamp: string;
360
- conversationIdSuffix: string;
361
- ownerAlias: string;
362
- workerAlias: string;
363
- kind: "opened" | "replaced" | "settled";
364
- actor: "owner" | "worker" | "operator" | "gateway" | "unknown";
365
- reason?: "done" | "untracked" | "idle_timeout" | "pair_removed" | "endpoint_retired" | "tracking_disabled";
366
- };
367
326
  export type GatewayPublicSnapshot = {
368
327
  schemaVersion: 2;
369
328
  generatedAt: string;
370
- inboundMode: GatewayInboundMode;
371
329
  health: ConnectorHealth;
372
330
  connectors: PublicConnectorSnapshot[];
373
331
  availablePeers: PublicAvailablePeerSnapshot[];
374
332
  routes: PublicRouteSnapshot[];
375
- consentEdges: PublicConsentEdgeSnapshot[];
376
- progressWatches?: PublicProgressWatchSnapshot[];
377
- progressWatchEvents?: PublicProgressWatchEventSnapshot[];
378
333
  activityEvents?: PublicGatewayActivityEvent[];
379
334
  deadlinePressure?: DeadlinePressureSnapshot;
380
335
  messages: NormalizedMessageEvent[];
@@ -386,9 +341,6 @@ export type GatewaySnapshotTruncation = {
386
341
  connectors: number;
387
342
  availablePeers: number;
388
343
  routes: number;
389
- consentEdges: number;
390
- progressWatches?: number;
391
- progressWatchEvents?: number;
392
344
  activityEvents?: number;
393
345
  messages: number;
394
346
  alerts: number;
@@ -397,13 +349,23 @@ export type GatewaySnapshotTruncation = {
397
349
  * Deterministically projects a canonical snapshot under the local control
398
350
  * byte budget. Content is never shortened: complete metadata rows are either
399
351
  * retained or counted as omitted. Registry rejection-code detail is shed
400
- * before consent edges or selected routes; connector inventory remains last.
352
+ * before routes; connector inventory remains last.
401
353
  */
402
354
  export declare function projectGatewayPublicSnapshot(snapshot: GatewayPublicSnapshot, maximumBytes?: number): GatewayPublicSnapshot;
403
355
  export type RegisterRouteInput = {
404
356
  alias: string;
405
357
  binding: LogicalRouteBinding;
406
358
  registrationMode: RouteRegistrationMode;
359
+ /**
360
+ * An ephemeral registration is real in memory — it routes, queues, and
361
+ * settles exactly like any other — but it is projected out of the durable
362
+ * state document along with every row that names it, so it cannot survive
363
+ * a restart and cannot be published to a federation peer; and when it is
364
+ * removed, by `unregister-peer` or by its own expiry, those rows leave the
365
+ * live state with it, so nothing of it is written afterwards either.
366
+ * `embassy check` uses one so a self-test leaves nothing behind.
367
+ */
368
+ ephemeral?: true;
407
369
  };
408
370
  export type GatewayReservedAttempt = Readonly<{
409
371
  messageId: string;
@@ -415,9 +377,8 @@ export type GatewayReservedAttempt = Readonly<{
415
377
  sourceAlias: string;
416
378
  targetAlias: string;
417
379
  conversationIdSuffix?: string;
418
- sourceRegistrationId: string | null;
380
+ sourceRegistrationId: string;
419
381
  targetRegistrationId: string;
420
- consentEdge: readonly [GatewayConsentEndpoint, GatewayConsentEndpoint] | null;
421
382
  bytes: number;
422
383
  steer?: true;
423
384
  }>;
@@ -442,7 +403,7 @@ type SettledAttemptResult = Readonly<{
442
403
  settlement: TerminalMessageSettlement;
443
404
  }>;
444
405
  export type AuthorizeMessageInput = AttemptInput & Readonly<{
445
- sourceRegistrationId: string | null;
406
+ sourceRegistrationId: string;
446
407
  targetRegistrationId: string;
447
408
  prepared: GatewayPreparedWriteEvidence;
448
409
  }>;
@@ -450,7 +411,7 @@ export type AuthorizeMessageResult = Readonly<{
450
411
  status: "authorized";
451
412
  }> | Readonly<{
452
413
  status: "stale";
453
- reason: "not_reserved" | "attempt_mismatch" | "registration_changed" | "consent_removed";
414
+ reason: "not_reserved" | "attempt_mismatch" | "registration_changed";
454
415
  }> | Readonly<{
455
416
  status: "terminal";
456
417
  reason: "expired" | "fenced";
@@ -500,6 +461,14 @@ export type ReplaceCodexRegistrationAtomicResult = Readonly<{
500
461
  idempotent: boolean;
501
462
  settlements: readonly TerminalMessageSettlement[];
502
463
  }>;
464
+ /**
465
+ * Result of installing a discovered Claude session's route on first send.
466
+ * `settlements` carries the work of any route the install displaced.
467
+ */
468
+ export type InstallClaudeRouteResult = Readonly<{
469
+ installed: boolean;
470
+ settlements: readonly TerminalMessageSettlement[];
471
+ }>;
503
472
  export type EnqueueMessageInput = {
504
473
  sourceAlias: string;
505
474
  targetAlias: string;
@@ -512,26 +481,18 @@ export type EnqueueMessageInput = {
512
481
  steer?: true;
513
482
  };
514
483
  /**
515
- * Controller-internal proof of one currently observed native Claude peer.
516
- * The binding may contain a native session identifier and must remain an
517
- * in-memory call argument. Store metadata retains only the public alias.
484
+ * The native Claude sender of one inbound message: its installed route's
485
+ * alias and exact binding. The binding carries a native session identifier
486
+ * and must remain an in-memory call argument.
518
487
  */
519
- export type TransientNativeClaudePeer = {
488
+ export type NativeClaudeSender = {
520
489
  alias: string;
521
490
  binding: LogicalRouteBinding;
522
491
  };
523
492
  export type EnqueueNativeIngressInput = Omit<EnqueueMessageInput, "sourceAlias" | "targetAlias" | "expectedSourceRegistrationId" | "expectedTargetRegistrationId"> & {
524
- source: TransientNativeClaudePeer;
493
+ source: NativeClaudeSender;
525
494
  targetAlias: string;
526
495
  expectedTargetRegistrationId: string;
527
- authorizedPairTeardownReply?: true;
528
- };
529
- export type EnqueueNativeReplyInput = Omit<EnqueueMessageInput, "sourceAlias" | "targetAlias" | "expectedSourceRegistrationId" | "expectedTargetRegistrationId"> & {
530
- sourceAlias: string;
531
- expectedSourceRegistrationId: string;
532
- target: TransientNativeClaudePeer;
533
- pair?: true;
534
- exposeDeliveryToken?: true;
535
496
  };
536
497
  export type EnqueueMessageResult = {
537
498
  accepted: boolean;
@@ -539,7 +500,6 @@ export type EnqueueMessageResult = {
539
500
  messageId?: string;
540
501
  messageIdSuffix: string;
541
502
  deliveryToken?: string;
542
- pair?: true;
543
503
  supersededSettlement?: TerminalMessageSettlement;
544
504
  };
545
505
  /**
@@ -554,8 +514,6 @@ export type TerminalMessageSettlement = {
554
514
  };
555
515
  export type GatewayStoreLimits = {
556
516
  maxRoutes: number;
557
- maxConsentEdges: number;
558
- maxWatches?: number;
559
517
  eventCapacity: number;
560
518
  eventTtlMs: number;
561
519
  dedupeCapacity: number;
@@ -575,5 +533,9 @@ export type GatewayStoreDependencies = {
575
533
  randomId?: () => string;
576
534
  renameStateFile?: (source: string, target: string) => Promise<void>;
577
535
  afterStateFileRename?: () => void | Promise<void>;
536
+ /** Controller-lock liveness probe; the default is `process.kill(pid, 0)`. */
537
+ isProcessAlive?: (pid: number) => boolean;
538
+ /** This machine's name as the controller lock records it; the default is `os.hostname()`. */
539
+ hostname?: () => string;
578
540
  };
579
541
  export {};
@@ -1,6 +1,5 @@
1
- export const gatewayProviders = ["claude", "codex", "deepseek", "grok", "peer"];
2
- export const gatewayRegistrationIngressPrefixes = Object.freeze({ claude: undefined, codex: "codex-", deepseek: "dsh-", grok: "grok-", peer: "peer-" });
3
- export const gatewayInboundModes = ["paired", "open"];
1
+ export const gatewayProviders = ["claude", "codex", "peer"];
2
+ export const gatewayRegistrationIngressPrefixes = Object.freeze({ claude: undefined, codex: "codex-", peer: "peer-" });
4
3
  export const connectorHealthStates = ["offline", "connecting", "healthy", "degraded"];
5
4
  export const routeStates = ["stale", "idle", "busy", "awaiting_approval", "offline", "disabled"];
6
5
  export const routeRegistrationModes = ["explicit_opt_in", "selected_live_peer", "federated_peer"];
@@ -8,7 +7,6 @@ export const isGatewayProvider = (value) => typeof value === "string" && gateway
8
7
  export function directionId(sourceProvider, targetProvider) {
9
8
  return `${sourceProvider}_to_${targetProvider}`;
10
9
  }
11
- export const messageDirections = Object.freeze(gatewayProviders.flatMap((sourceProvider) => gatewayProviders.map((targetProvider) => directionId(sourceProvider, targetProvider))));
12
10
  export function parseDirection(value) {
13
11
  if (typeof value !== "string")
14
12
  return undefined;
@@ -31,12 +29,37 @@ export const alertSeverities = ["info", "warning", "error"];
31
29
  export const gatewayPublicSnapshotLimits = Object.freeze({
32
30
  connectors: 64, registryRejectionCodes: 32,
33
31
  availablePeers: 256, routes: 256,
34
- consentEdges: 256, progressWatches: 64,
35
- progressWatchEvents: 256, activityEvents: 256,
32
+ activityEvents: 256,
36
33
  messages: 1_024, alerts: 256,
37
34
  });
38
35
  export const GATEWAY_PUBLIC_SNAPSHOT_BYTE_BUDGET = 240 * 1024;
39
36
  export const CONNECTOR_OBSERVATION_STALE_AFTER_MS = 35_000;
37
+ /**
38
+ * Connector words. A connector reporting `CONNECTOR_OBSERVATION_STALE` has
39
+ * nothing wrong with it: no route of that provider was observed inside the
40
+ * broker's window. That is `stale`, not `degraded`, and the difference is the
41
+ * point — an idle Mac with no registered Codex task must not read as broken.
42
+ */
43
+ export function connectorWord(connector) {
44
+ if (connector.health === "healthy" || connector.health === "connecting")
45
+ return "ok";
46
+ if (connector.health === "offline")
47
+ return "offline";
48
+ return connector.safeErrorCode === "CONNECTOR_OBSERVATION_STALE" ? "stale" : "degraded";
49
+ }
50
+ const severityRank = (word) => word === "degraded" || word === "offline" ? 0 : word === "stale" ? 1 : 2;
51
+ /**
52
+ * The overall word is informational and derived from the two connectors a
53
+ * message actually travels through. A shell peer never contributes: its
54
+ * mailbox is pull-only, so "nobody is awaiting it" is a fact about the
55
+ * operator's other terminal, not about the broker.
56
+ */
57
+ export function gatewayHealthWord(connectors) {
58
+ const routed = connectors.filter((connector) => connector.provider === "claude" || connector.provider === "codex");
59
+ if (routed.length === 0)
60
+ return "offline";
61
+ return routed.map(connectorWord).reduce((worst, word) => severityRank(word) < severityRank(worst) ? word : worst, "ok");
62
+ }
40
63
  export const publicAvailablePeerStates = [
41
64
  "idle",
42
65
  "busy",
@@ -45,21 +68,21 @@ export const publicAvailablePeerStates = [
45
68
  ];
46
69
  export const gatewayActivityKinds = [
47
70
  "discovery",
48
- "selection",
49
71
  "registration",
50
- "pairing",
51
- "watch",
52
72
  ];
73
+ /**
74
+ * `claude_route_installed` and `claude_route_retired` are journaled by the
75
+ * store when a send installs, renames, or displaces a discovered Claude
76
+ * session's route; there is no explicit Claude registration command.
77
+ */
53
78
  export const gatewayActivityActions = [
54
79
  "discovery_refreshed",
55
- "claude_selected",
56
- "claude_unselected",
57
80
  "codex_registered",
58
81
  "codex_succeeded",
59
82
  "codex_unregistered",
60
- "routes_paired",
61
- "routes_unpaired",
62
- "watch_ended",
83
+ "claude_route_installed",
84
+ "claude_route_retired",
85
+ "route_retired",
63
86
  ];
64
87
  export const deadlinePressureBucketNames = [
65
88
  "under_1m",
@@ -68,8 +91,6 @@ export const deadlinePressureBucketNames = [
68
91
  "15m_to_60m",
69
92
  "over_60m",
70
93
  ];
71
- const PUBLIC_ALIAS_PATTERN = /^[a-z][a-z0-9_-]{0,31}@[a-z0-9](?:[a-z0-9.-]{0,61}[a-z0-9])?$/;
72
- const PUBLIC_HOST_PATTERN = /^[a-z0-9](?:[a-z0-9.-]{0,61}[a-z0-9])?$/;
73
94
  const PUBLIC_SAFE_CODE_PATTERN = /^[A-Z][A-Z0-9_]{0,63}$/;
74
95
  function publicRecord(value) {
75
96
  return value !== null && typeof value === "object" && !Array.isArray(value)
@@ -80,42 +101,9 @@ function exactPublicKeys(value, required, optional = []) {
80
101
  return required.every((key) => Object.hasOwn(value, key)) &&
81
102
  keys.every((key) => required.includes(key) || optional.includes(key));
82
103
  }
83
- function publicTimestamp(value) {
84
- return typeof value === "string" && value.length >= 20 && value.length <= 35 && Number.isFinite(Date.parse(value));
85
- }
86
104
  function nonNegative(value) {
87
105
  return Number.isSafeInteger(value) && Number(value) >= 0;
88
106
  }
89
- export function isPublicAvailablePeerSnapshot(value) {
90
- const candidate = publicRecord(value);
91
- if (candidate === undefined ||
92
- !exactPublicKeys(candidate, ["alias", "provider", "host", "state", "validated", "selected"], ["lastSeenAt", "safeErrorCode"]) ||
93
- typeof candidate.alias !== "string" ||
94
- !PUBLIC_ALIAS_PATTERN.test(candidate.alias) ||
95
- typeof candidate.host !== "string" ||
96
- !PUBLIC_HOST_PATTERN.test(candidate.host) ||
97
- !candidate.alias.endsWith(`@${candidate.host}`) ||
98
- candidate.provider !== "claude" ||
99
- typeof candidate.state !== "string" ||
100
- !publicAvailablePeerStates.includes(candidate.state) ||
101
- typeof candidate.validated !== "boolean" ||
102
- typeof candidate.selected !== "boolean")
103
- return false;
104
- return (candidate.lastSeenAt === undefined || publicTimestamp(candidate.lastSeenAt)) &&
105
- (candidate.safeErrorCode === undefined ||
106
- (typeof candidate.safeErrorCode === "string" && PUBLIC_SAFE_CODE_PATTERN.test(candidate.safeErrorCode)));
107
- }
108
- export function arePublicAvailablePeerSnapshots(value, maximumRows = gatewayPublicSnapshotLimits.availablePeers) {
109
- if (!Array.isArray(value) ||
110
- !Number.isSafeInteger(maximumRows) ||
111
- maximumRows < 0 ||
112
- value.length > maximumRows ||
113
- !value.every(isPublicAvailablePeerSnapshot)) {
114
- return false;
115
- }
116
- const aliases = value.map((peer) => `${peer.provider}\0${peer.alias}`);
117
- return new Set(aliases).size === aliases.length;
118
- }
119
107
  export function isPublicRegistryObservationSnapshot(value) {
120
108
  const candidate = publicRecord(value);
121
109
  if (candidate === undefined ||
@@ -180,7 +168,7 @@ function snapshotBytes(snapshot) {
180
168
  * Deterministically projects a canonical snapshot under the local control
181
169
  * byte budget. Content is never shortened: complete metadata rows are either
182
170
  * retained or counted as omitted. Registry rejection-code detail is shed
183
- * before consent edges or selected routes; connector inventory remains last.
171
+ * before routes; connector inventory remains last.
184
172
  */
185
173
  export function projectGatewayPublicSnapshot(snapshot, maximumBytes = GATEWAY_PUBLIC_SNAPSHOT_BYTE_BUDGET) {
186
174
  if (!Number.isSafeInteger(maximumBytes) ||
@@ -193,18 +181,9 @@ export function projectGatewayPublicSnapshot(snapshot, maximumBytes = GATEWAY_PU
193
181
  connectors: snapshot.truncation.connectors + omitted(snapshot.connectors.length, gatewayPublicSnapshotLimits.connectors),
194
182
  availablePeers: snapshot.truncation.availablePeers + omitted(snapshot.availablePeers.length, gatewayPublicSnapshotLimits.availablePeers),
195
183
  routes: snapshot.truncation.routes + omitted(snapshot.routes.length, gatewayPublicSnapshotLimits.routes),
196
- consentEdges: snapshot.truncation.consentEdges + omitted(snapshot.consentEdges.length, gatewayPublicSnapshotLimits.consentEdges),
197
184
  messages: snapshot.truncation.messages + omitted(snapshot.messages.length, gatewayPublicSnapshotLimits.messages),
198
185
  alerts: snapshot.truncation.alerts + omitted(snapshot.alerts.length, gatewayPublicSnapshotLimits.alerts),
199
186
  };
200
- if (snapshot.progressWatches !== undefined) {
201
- truncation.progressWatches = (snapshot.truncation.progressWatches ?? 0) +
202
- omitted(snapshot.progressWatches.length, gatewayPublicSnapshotLimits.progressWatches);
203
- }
204
- if (snapshot.progressWatchEvents !== undefined) {
205
- truncation.progressWatchEvents = (snapshot.truncation.progressWatchEvents ?? 0) +
206
- omitted(snapshot.progressWatchEvents.length, gatewayPublicSnapshotLimits.progressWatchEvents);
207
- }
208
187
  if (snapshot.activityEvents !== undefined) {
209
188
  truncation.activityEvents = (snapshot.truncation.activityEvents ?? 0) +
210
189
  omitted(snapshot.activityEvents.length, gatewayPublicSnapshotLimits.activityEvents);
@@ -214,13 +193,6 @@ export function projectGatewayPublicSnapshot(snapshot, maximumBytes = GATEWAY_PU
214
193
  connectors: structuredClone(snapshot.connectors.slice(0, gatewayPublicSnapshotLimits.connectors)),
215
194
  availablePeers: snapshot.availablePeers.slice(0, gatewayPublicSnapshotLimits.availablePeers),
216
195
  routes: snapshot.routes.slice(0, gatewayPublicSnapshotLimits.routes),
217
- consentEdges: snapshot.consentEdges.slice(0, gatewayPublicSnapshotLimits.consentEdges),
218
- ...(snapshot.progressWatches === undefined ? {} : {
219
- progressWatches: snapshot.progressWatches.slice(0, gatewayPublicSnapshotLimits.progressWatches),
220
- }),
221
- ...(snapshot.progressWatchEvents === undefined ? {} : {
222
- progressWatchEvents: snapshot.progressWatchEvents.slice(-gatewayPublicSnapshotLimits.progressWatchEvents),
223
- }),
224
196
  ...(snapshot.activityEvents === undefined ? {} : {
225
197
  activityEvents: snapshot.activityEvents.slice(-gatewayPublicSnapshotLimits.activityEvents),
226
198
  }),
@@ -256,13 +228,6 @@ export function projectGatewayPublicSnapshot(snapshot, maximumBytes = GATEWAY_PU
256
228
  const retained = count === 0 ? [] : newest ? rows.slice(-count) : rows.slice(0, count);
257
229
  apply(retained, priorOmissions + rows.length - count);
258
230
  });
259
- const watchEvents = projected.progressWatchEvents ?? [];
260
- if (watchEvents.length > 0 &&
261
- retainRows(watchEvents, projected.truncation.progressWatchEvents ?? 0, true, (rows, omissions) => {
262
- projected.progressWatchEvents = rows;
263
- projected.truncation.progressWatchEvents = omissions;
264
- }))
265
- return projected;
266
231
  const activityEvents = projected.activityEvents ?? [];
267
232
  if (activityEvents.length > 0 &&
268
233
  retainRows(activityEvents, projected.truncation.activityEvents ?? 0, true, (rows, omissions) => {
@@ -277,8 +242,8 @@ export function projectGatewayPublicSnapshot(snapshot, maximumBytes = GATEWAY_PU
277
242
  }))
278
243
  return projected;
279
244
  const peers = [...projected.availablePeers].sort((left, right) => {
280
- if (left.selected !== right.selected)
281
- return left.selected ? -1 : 1;
245
+ if (left.routed !== right.routed)
246
+ return left.routed ? -1 : 1;
282
247
  const byState = peerPriority(left.state) - peerPriority(right.state);
283
248
  if (byState !== 0)
284
249
  return byState;
@@ -335,17 +300,6 @@ export function projectGatewayPublicSnapshot(snapshot, maximumBytes = GATEWAY_PU
335
300
  return byState;
336
301
  return left.alias.localeCompare(right.alias);
337
302
  });
338
- const consentEdges = [...projected.consentEdges].sort((left, right) => left.endpoints
339
- .map((endpoint) => `${endpoint.provider}\0${endpoint.alias}`)
340
- .join("\0")
341
- .localeCompare(right.endpoints
342
- .map((endpoint) => `${endpoint.provider}\0${endpoint.alias}`)
343
- .join("\0")));
344
- if (retainRows(consentEdges, projected.truncation.consentEdges, false, (rows, omissions) => {
345
- projected.consentEdges = rows;
346
- projected.truncation.consentEdges = omissions;
347
- }))
348
- return projected;
349
303
  if (retainRows(routes, projected.truncation.routes, false, (rows, omissions) => {
350
304
  projected.routes = rows;
351
305
  projected.truncation.routes = omissions;