agent-embassy 2.0.1 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +56 -0
- package/CONTRIBUTING.md +19 -34
- package/README.md +110 -228
- package/SECURITY.md +55 -89
- package/dist/src/errors.d.ts +10 -1
- package/dist/src/errors.js +3 -1
- package/dist/src/errors.js.map +1 -1
- package/dist/src/gateway/claude-helper-protocol.d.ts +8 -11
- package/dist/src/gateway/claude-helper-protocol.js +12 -11
- package/dist/src/gateway/claude-helper-protocol.js.map +1 -1
- package/dist/src/gateway/claude-helper-supervisor.d.ts +1 -5
- package/dist/src/gateway/claude-helper-supervisor.js +10 -9
- package/dist/src/gateway/claude-helper-supervisor.js.map +1 -1
- package/dist/src/gateway/claude-helper.js +6 -6
- package/dist/src/gateway/claude-helper.js.map +1 -1
- package/dist/src/gateway/claude-peer.d.ts +0 -3
- package/dist/src/gateway/claude-peer.js +6 -18
- package/dist/src/gateway/claude-peer.js.map +1 -1
- package/dist/src/gateway/cli.d.ts +17 -6
- package/dist/src/gateway/cli.js +912 -261
- package/dist/src/gateway/cli.js.map +1 -1
- package/dist/src/gateway/codex-socket-holder.d.ts +26 -0
- package/dist/src/gateway/codex-socket-holder.js +76 -0
- package/dist/src/gateway/codex-socket-holder.js.map +1 -0
- package/dist/src/gateway/codex-stateless-transport.js +1 -1
- package/dist/src/gateway/codex-stateless-transport.js.map +1 -1
- package/dist/src/gateway/config.d.ts +1 -10
- package/dist/src/gateway/config.js +4 -10
- package/dist/src/gateway/config.js.map +1 -1
- package/dist/src/gateway/control.d.ts +45 -77
- package/dist/src/gateway/control.js +56 -141
- package/dist/src/gateway/control.js.map +1 -1
- package/dist/src/gateway/federation-nodes.d.ts +29 -2
- package/dist/src/gateway/federation-nodes.js +177 -7
- package/dist/src/gateway/federation-nodes.js.map +1 -1
- package/dist/src/gateway/peer-client.d.ts +4 -3
- package/dist/src/gateway/peer-client.js +22 -13
- package/dist/src/gateway/peer-client.js.map +1 -1
- package/dist/src/gateway/peer-protocol.d.ts +8 -11
- package/dist/src/gateway/peer-protocol.js +5 -12
- package/dist/src/gateway/peer-protocol.js.map +1 -1
- package/dist/src/gateway/provenance-envelope.d.ts +0 -1
- package/dist/src/gateway/provenance-envelope.js +4 -19
- package/dist/src/gateway/provenance-envelope.js.map +1 -1
- package/dist/src/gateway/providers.d.ts +7 -4
- package/dist/src/gateway/providers.js +16 -20
- package/dist/src/gateway/providers.js.map +1 -1
- package/dist/src/gateway/server.d.ts +3 -12
- package/dist/src/gateway/server.js +31 -50
- package/dist/src/gateway/server.js.map +1 -1
- package/dist/src/gateway/service-agent.d.ts +187 -0
- package/dist/src/gateway/service-agent.js +758 -0
- package/dist/src/gateway/service-agent.js.map +1 -0
- package/dist/src/gateway/service.d.ts +114 -31
- package/dist/src/gateway/service.js +461 -566
- package/dist/src/gateway/service.js.map +1 -1
- package/dist/src/gateway/status-view.d.ts +167 -0
- package/dist/src/gateway/status-view.js +488 -0
- package/dist/src/gateway/status-view.js.map +1 -0
- package/dist/src/gateway/store.d.ts +103 -21
- package/dist/src/gateway/store.js +454 -529
- package/dist/src/gateway/store.js.map +1 -1
- package/dist/src/gateway/types.d.ts +48 -99
- package/dist/src/gateway/types.js +15 -52
- package/dist/src/gateway/types.js.map +1 -1
- package/docs/CONFIGURATION.md +173 -44
- package/docs/DELIVERY.md +11 -11
- package/docs/GATEWAY-ARCHITECTURE.md +277 -375
- package/package.json +4 -12
- package/skills/embassy-peer/SKILL.md +65 -90
- package/skills/embassy-peer/agents/openai.yaml +1 -1
- package/README.zh-CN.md +0 -275
- package/assets/live-dashboard/app.css +0 -1619
- package/assets/vendor/react/LICENSE +0 -21
- package/assets/vendor/react/react-dom.production.min.js +0 -267
- package/assets/vendor/react/react.production.min.js +0 -31
- package/dist/src/gateway/acp-client.d.ts +0 -110
- package/dist/src/gateway/acp-client.js +0 -407
- package/dist/src/gateway/acp-client.js.map +0 -1
- package/dist/src/gateway/acp-provider.d.ts +0 -66
- package/dist/src/gateway/acp-provider.js +0 -275
- package/dist/src/gateway/acp-provider.js.map +0 -1
- package/dist/src/gateway/cli-copy.d.ts +0 -8
- package/dist/src/gateway/cli-copy.en.d.ts +0 -22
- package/dist/src/gateway/cli-copy.en.js +0 -62
- package/dist/src/gateway/cli-copy.en.js.map +0 -1
- package/dist/src/gateway/cli-copy.js +0 -27
- package/dist/src/gateway/cli-copy.js.map +0 -1
- package/dist/src/gateway/cli-copy.zh-CN.d.ts +0 -22
- package/dist/src/gateway/cli-copy.zh-CN.js +0 -62
- package/dist/src/gateway/cli-copy.zh-CN.js.map +0 -1
- package/dist/src/gateway/codex-doctor.d.ts +0 -36
- package/dist/src/gateway/codex-doctor.js +0 -127
- package/dist/src/gateway/codex-doctor.js.map +0 -1
- package/dist/src/gateway/dashboard-copy.d.ts +0 -7
- package/dist/src/gateway/dashboard-copy.en.d.ts +0 -504
- package/dist/src/gateway/dashboard-copy.en.js +0 -505
- package/dist/src/gateway/dashboard-copy.en.js.map +0 -1
- package/dist/src/gateway/dashboard-copy.js +0 -514
- package/dist/src/gateway/dashboard-copy.js.map +0 -1
- package/dist/src/gateway/dashboard-copy.zh-CN.d.ts +0 -504
- package/dist/src/gateway/dashboard-copy.zh-CN.js +0 -505
- package/dist/src/gateway/dashboard-copy.zh-CN.js.map +0 -1
- package/dist/src/gateway/dashboard-model.d.ts +0 -343
- package/dist/src/gateway/dashboard-model.js +0 -1061
- package/dist/src/gateway/dashboard-model.js.map +0 -1
- package/dist/src/gateway/dashboard.d.ts +0 -20
- package/dist/src/gateway/dashboard.js +0 -874
- package/dist/src/gateway/dashboard.js.map +0 -1
- package/dist/src/gateway/deepseek-detect.d.ts +0 -14
- package/dist/src/gateway/deepseek-detect.js +0 -41
- package/dist/src/gateway/deepseek-detect.js.map +0 -1
- package/dist/src/gateway/live-dashboard-app/app.js +0 -2385
- package/dist/src/gateway/live-dashboard-assets.d.ts +0 -10
- package/dist/src/gateway/live-dashboard-assets.js +0 -74
- package/dist/src/gateway/live-dashboard-assets.js.map +0 -1
- package/dist/src/gateway/live-dashboard-command.d.ts +0 -60
- package/dist/src/gateway/live-dashboard-command.js +0 -334
- package/dist/src/gateway/live-dashboard-command.js.map +0 -1
- package/dist/src/gateway/live-dashboard-http.d.ts +0 -39
- package/dist/src/gateway/live-dashboard-http.js +0 -383
- package/dist/src/gateway/live-dashboard-http.js.map +0 -1
- package/dist/src/gateway/live-dashboard-protocol.d.ts +0 -34
- package/dist/src/gateway/live-dashboard-protocol.js +0 -114
- package/dist/src/gateway/live-dashboard-protocol.js.map +0 -1
- package/dist/src/gateway/live-dashboard-server.d.ts +0 -33
- package/dist/src/gateway/live-dashboard-server.js +0 -144
- package/dist/src/gateway/live-dashboard-server.js.map +0 -1
- package/dist/src/gateway/live-dashboard-stream.d.ts +0 -46
- package/dist/src/gateway/live-dashboard-stream.js +0 -234
- package/dist/src/gateway/live-dashboard-stream.js.map +0 -1
- package/dist/src/gateway/live-dashboard.d.ts +0 -28
- package/dist/src/gateway/live-dashboard.js +0 -154
- package/dist/src/gateway/live-dashboard.js.map +0 -1
- package/dist/src/gateway/locale.d.ts +0 -4
- package/dist/src/gateway/locale.js +0 -10
- package/dist/src/gateway/locale.js.map +0 -1
- package/dist/src/gateway/progress-watch-machine.d.ts +0 -45
- package/dist/src/gateway/progress-watch-machine.js +0 -70
- package/dist/src/gateway/progress-watch-machine.js.map +0 -1
- package/docs/CONFIGURATION.zh-CN.md +0 -97
- package/docs/DASHBOARD.md +0 -98
- package/docs/DASHBOARD.zh-CN.md +0 -49
- package/docs/DELIVERY.zh-CN.md +0 -55
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.js","sourceRoot":"","sources":["../../../src/gateway/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../../../src/gateway/config.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import type { GatewayPublicSnapshot } from "./types.js";
|
|
2
2
|
import { type PeerCatalogResult, type PeerHandoffParams, type PeerHandoffResult } from "./peer-protocol.js";
|
|
3
3
|
import { type PeerMailboxAwaitResult } from "./peer-mailbox.js";
|
|
4
|
-
export declare const GATEWAY_CONTROL_PROTOCOL_VERSION:
|
|
4
|
+
export declare const GATEWAY_CONTROL_PROTOCOL_VERSION: 3;
|
|
5
5
|
export declare const GATEWAY_CONTROL_MAX_FRAME_BYTES: number;
|
|
6
6
|
export declare const GATEWAY_CONTROL_MAX_RESPONSE_BYTES: number;
|
|
7
7
|
export declare const GATEWAY_CONTROL_MAX_MESSAGE_BYTES: number;
|
|
8
8
|
export declare const GATEWAY_CONTROL_DEFAULT_TIMEOUT_MS = 3000;
|
|
9
9
|
export declare const GATEWAY_CONTROL_MAX_CONNECTIONS = 32;
|
|
10
|
-
export declare const gatewayControlMethods: readonly ["health", "register_codex", "unregister_codex", "
|
|
10
|
+
export declare const gatewayControlMethods: readonly ["health", "register_codex", "unregister_codex", "list_snapshot", "observe_snapshot", "delivery_status", "send", "refresh_discovery", "peer_catalog", "peer_handoff", "register_peer", "unregister_peer", "await_peer", "peer_receipt"];
|
|
11
11
|
export type GatewayControlMethod = (typeof gatewayControlMethods)[number];
|
|
12
12
|
export type GatewayBusyPolicy = "queue";
|
|
13
13
|
export type RegisterCodexParams = {
|
|
@@ -28,39 +28,11 @@ export type UnregisterCodexParams = {
|
|
|
28
28
|
alias: string;
|
|
29
29
|
threadId: string;
|
|
30
30
|
};
|
|
31
|
-
export type RemoveCodexRegistrationParams = {
|
|
32
|
-
alias: string;
|
|
33
|
-
};
|
|
34
|
-
export type SelectClaudeParams = {
|
|
35
|
-
alias: string;
|
|
36
|
-
};
|
|
37
|
-
export type PairParams = {
|
|
38
|
-
aliases: readonly [string, string];
|
|
39
|
-
};
|
|
40
31
|
type SendBase = {
|
|
41
32
|
fromAlias: string;
|
|
42
|
-
toAlias: string;
|
|
43
33
|
text: string;
|
|
44
|
-
expectsReply?: boolean;
|
|
45
|
-
trackIdleMinutes?: number;
|
|
46
34
|
};
|
|
47
|
-
type
|
|
48
|
-
expectsReply: boolean;
|
|
49
|
-
};
|
|
50
|
-
export type SendParams = SendBase & ({
|
|
51
|
-
threadId: string;
|
|
52
|
-
replyAddress?: never;
|
|
53
|
-
peerToken?: never;
|
|
54
|
-
} | {
|
|
55
|
-
replyAddress: string;
|
|
56
|
-
threadId?: never;
|
|
57
|
-
peerToken?: never;
|
|
58
|
-
} | {
|
|
59
|
-
peerToken: string;
|
|
60
|
-
threadId?: never;
|
|
61
|
-
replyAddress?: never;
|
|
62
|
-
});
|
|
63
|
-
export type ValidatedSendParams = ValidatedSendBase & ({
|
|
35
|
+
type SendAuthority = {
|
|
64
36
|
threadId: string;
|
|
65
37
|
replyAddress?: never;
|
|
66
38
|
peerToken?: never;
|
|
@@ -72,29 +44,32 @@ export type ValidatedSendParams = ValidatedSendBase & ({
|
|
|
72
44
|
peerToken: string;
|
|
73
45
|
threadId?: never;
|
|
74
46
|
replyAddress?: never;
|
|
75
|
-
});
|
|
76
|
-
export type GatewayReplyCaller = {
|
|
77
|
-
kind: "codex";
|
|
78
|
-
alias: string;
|
|
79
|
-
threadId: string;
|
|
80
|
-
} | {
|
|
81
|
-
kind: "claude";
|
|
82
|
-
alias: string;
|
|
83
|
-
replyAddress?: string;
|
|
84
|
-
} | {
|
|
85
|
-
kind: "peer";
|
|
86
|
-
alias: string;
|
|
87
|
-
token: string;
|
|
88
47
|
};
|
|
89
|
-
|
|
48
|
+
/**
|
|
49
|
+
* A send addresses either a route by name or an open conversation by its
|
|
50
|
+
* token, never both. The conversation form is what `reply` used to be: the
|
|
51
|
+
* caller owns one end, the other end is the conversation's recorded binding,
|
|
52
|
+
* and the answer always expects a reply, so `expectsReply` is not accepted
|
|
53
|
+
* with it.
|
|
54
|
+
*/
|
|
55
|
+
export type SendParams = (SendBase & SendAuthority & {
|
|
56
|
+
toAlias: string;
|
|
57
|
+
conversationId?: never;
|
|
58
|
+
expectsReply?: boolean;
|
|
59
|
+
}) | (SendBase & SendAuthority & {
|
|
90
60
|
conversationId: string;
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
61
|
+
toAlias?: never;
|
|
62
|
+
expectsReply?: never;
|
|
63
|
+
});
|
|
64
|
+
export type ValidatedSendParams = (SendBase & SendAuthority & {
|
|
65
|
+
toAlias: string;
|
|
66
|
+
conversationId?: never;
|
|
67
|
+
expectsReply: boolean;
|
|
68
|
+
}) | (SendBase & SendAuthority & {
|
|
96
69
|
conversationId: string;
|
|
97
|
-
|
|
70
|
+
toAlias?: never;
|
|
71
|
+
expectsReply?: never;
|
|
72
|
+
});
|
|
98
73
|
export type DeliveryStatusParams = {
|
|
99
74
|
token: string;
|
|
100
75
|
};
|
|
@@ -108,7 +83,13 @@ export type PeerHandoffControlParams = {
|
|
|
108
83
|
export type RegisterPeerParams = {
|
|
109
84
|
alias: string;
|
|
110
85
|
token?: string;
|
|
86
|
+
ephemeral?: true;
|
|
87
|
+
ttlMs?: number;
|
|
111
88
|
};
|
|
89
|
+
/** Bounds on an ephemeral peer registration's lifetime, in milliseconds. */
|
|
90
|
+
export declare const GATEWAY_EPHEMERAL_PEER_TTL_DEFAULT_MS = 300000;
|
|
91
|
+
export declare const GATEWAY_EPHEMERAL_PEER_TTL_MIN_MS = 1000;
|
|
92
|
+
export declare const GATEWAY_EPHEMERAL_PEER_TTL_MAX_MS = 3600000;
|
|
112
93
|
export type PeerPrincipalParams = {
|
|
113
94
|
alias: string;
|
|
114
95
|
token: string;
|
|
@@ -120,18 +101,11 @@ type RequestParams = {
|
|
|
120
101
|
health: Record<string, never>;
|
|
121
102
|
register_codex: RegisterCodexParams;
|
|
122
103
|
unregister_codex: UnregisterCodexParams;
|
|
123
|
-
remove_codex_registration: RemoveCodexRegistrationParams;
|
|
124
|
-
select_claude: SelectClaudeParams;
|
|
125
|
-
unselect_claude: SelectClaudeParams;
|
|
126
|
-
pair: PairParams;
|
|
127
|
-
unpair: PairParams;
|
|
128
104
|
list_snapshot: Record<string, never>;
|
|
129
105
|
observe_snapshot: Record<string, never>;
|
|
130
106
|
delivery_status: DeliveryStatusParams;
|
|
131
|
-
untrack: UntrackParams;
|
|
132
107
|
send: SendParams;
|
|
133
|
-
|
|
134
|
-
refresh_dashboard: Record<string, never>;
|
|
108
|
+
refresh_discovery: Record<string, never>;
|
|
135
109
|
peer_catalog: PeerCatalogParams;
|
|
136
110
|
peer_handoff: PeerHandoffControlParams;
|
|
137
111
|
register_peer: RegisterPeerParams;
|
|
@@ -146,15 +120,22 @@ export type GatewayControlRequest = {
|
|
|
146
120
|
params: RequestParams[M];
|
|
147
121
|
};
|
|
148
122
|
}[GatewayControlMethod];
|
|
149
|
-
export type GatewayDecisionCode = "ok" | "not_found" | "conflict" | "
|
|
123
|
+
export type GatewayDecisionCode = "ok" | "not_found" | "conflict" | "route_mismatch" | "busy" | "unavailable" | "rejected";
|
|
150
124
|
export type GatewayDecision = {
|
|
151
125
|
accepted: true;
|
|
152
126
|
code: "ok";
|
|
153
127
|
} | {
|
|
154
128
|
accepted: false;
|
|
155
129
|
code: Exclude<GatewayDecisionCode, "ok">;
|
|
156
|
-
ownerHost?: string;
|
|
157
130
|
};
|
|
131
|
+
/**
|
|
132
|
+
* A refused send carries an optional `reason`: the safe error code
|
|
133
|
+
* behind the coarse decision. The decision code stays the contract clients
|
|
134
|
+
* branch on; `reason` exists so the CLI can name the one remedy that fits
|
|
135
|
+
* (rename a session, move a workspace, fix `--from`, rescan) instead of
|
|
136
|
+
* printing one generic line for six different failures. It is a safe code:
|
|
137
|
+
* bounded, allowlisted-shaped, and never a message, path, or identifier.
|
|
138
|
+
*/
|
|
158
139
|
export type GatewaySendResult = {
|
|
159
140
|
accepted: true;
|
|
160
141
|
code: "ok";
|
|
@@ -163,6 +144,7 @@ export type GatewaySendResult = {
|
|
|
163
144
|
} | {
|
|
164
145
|
accepted: false;
|
|
165
146
|
code: Exclude<GatewayDecisionCode, "ok">;
|
|
147
|
+
reason?: string;
|
|
166
148
|
};
|
|
167
149
|
export type GatewayHealthResult = {
|
|
168
150
|
status: "ok" | "degraded";
|
|
@@ -197,18 +179,11 @@ type ResultByMethod = {
|
|
|
197
179
|
health: GatewayHealthResult;
|
|
198
180
|
register_codex: GatewayDecision;
|
|
199
181
|
unregister_codex: GatewayDecision;
|
|
200
|
-
remove_codex_registration: GatewayDecision;
|
|
201
|
-
select_claude: GatewayDecision;
|
|
202
|
-
unselect_claude: GatewayDecision;
|
|
203
|
-
pair: GatewayDecision;
|
|
204
|
-
unpair: GatewayDecision;
|
|
205
182
|
list_snapshot: GatewaySnapshot;
|
|
206
183
|
observe_snapshot: GatewaySnapshotObservation;
|
|
207
184
|
delivery_status: GatewayDeliveryStatusResult;
|
|
208
|
-
untrack: GatewayDecision;
|
|
209
185
|
send: GatewaySendResult;
|
|
210
|
-
|
|
211
|
-
refresh_dashboard: GatewayRefreshResult;
|
|
186
|
+
refresh_discovery: GatewayRefreshResult;
|
|
212
187
|
peer_catalog: PeerCatalogResult;
|
|
213
188
|
peer_handoff: PeerHandoffResult;
|
|
214
189
|
register_peer: GatewayRegisterPeerResult;
|
|
@@ -221,18 +196,11 @@ export type GatewayControlHandlers = {
|
|
|
221
196
|
health: () => MaybePromise<GatewayHealthResult>;
|
|
222
197
|
registerCodex: (params: Readonly<ValidatedRegisterCodexParams>) => MaybePromise<GatewayDecision>;
|
|
223
198
|
unregisterCodex: (params: Readonly<UnregisterCodexParams>) => MaybePromise<GatewayDecision>;
|
|
224
|
-
removeCodexRegistration: (params: Readonly<RemoveCodexRegistrationParams>) => MaybePromise<GatewayDecision>;
|
|
225
|
-
selectClaude: (params: Readonly<SelectClaudeParams>) => MaybePromise<GatewayDecision>;
|
|
226
|
-
unselectClaude: (params: Readonly<SelectClaudeParams>) => MaybePromise<GatewayDecision>;
|
|
227
|
-
pair: (params: Readonly<PairParams>) => MaybePromise<GatewayDecision>;
|
|
228
|
-
unpair: (params: Readonly<PairParams>) => MaybePromise<GatewayDecision>;
|
|
229
199
|
listSnapshot: () => MaybePromise<GatewaySnapshot>;
|
|
230
200
|
observeSnapshot: () => MaybePromise<GatewaySnapshotObservation>;
|
|
231
201
|
deliveryStatus: (params: Readonly<DeliveryStatusParams>) => MaybePromise<GatewayDeliveryStatusResult>;
|
|
232
|
-
untrack: (params: Readonly<UntrackParams>) => MaybePromise<GatewayDecision>;
|
|
233
202
|
send: (params: Readonly<ValidatedSendParams>) => MaybePromise<GatewaySendResult>;
|
|
234
|
-
|
|
235
|
-
refreshDashboard: () => MaybePromise<GatewayRefreshResult>;
|
|
203
|
+
refreshDiscovery: () => MaybePromise<GatewayRefreshResult>;
|
|
236
204
|
peerCatalog?: (params: Readonly<PeerCatalogParams>) => MaybePromise<PeerCatalogResult>;
|
|
237
205
|
peerHandoff?: (params: Readonly<PeerHandoffControlParams>) => MaybePromise<PeerHandoffResult>;
|
|
238
206
|
registerPeer: (params: Readonly<RegisterPeerParams>) => MaybePromise<GatewayRegisterPeerResult>;
|
|
@@ -4,10 +4,10 @@ import { chmod, lstat, mkdtemp, realpath, rename, rmdir, unlink } from "node:fs/
|
|
|
4
4
|
import net from "node:net";
|
|
5
5
|
import path from "node:path";
|
|
6
6
|
import { TextDecoder } from "node:util";
|
|
7
|
-
import { CONNECTOR_OBSERVATION_STALE_AFTER_MS, GATEWAY_PUBLIC_SNAPSHOT_BYTE_BUDGET,
|
|
7
|
+
import { CONNECTOR_OBSERVATION_STALE_AFTER_MS, GATEWAY_PUBLIC_SNAPSHOT_BYTE_BUDGET, gatewayPublicSnapshotLimits, isGatewayProvider, isMessageDirection, isPublicRegistryObservationSnapshot, } from "./types.js";
|
|
8
8
|
import { decodePeerParams, decodePeerResult } from "./peer-protocol.js";
|
|
9
9
|
import { isPeerMailboxAwaitResult } from "./peer-mailbox.js";
|
|
10
|
-
export const GATEWAY_CONTROL_PROTOCOL_VERSION =
|
|
10
|
+
export const GATEWAY_CONTROL_PROTOCOL_VERSION = 3;
|
|
11
11
|
export const GATEWAY_CONTROL_MAX_FRAME_BYTES = 32 * 1024;
|
|
12
12
|
export const GATEWAY_CONTROL_MAX_RESPONSE_BYTES = 256 * 1024;
|
|
13
13
|
export const GATEWAY_CONTROL_MAX_MESSAGE_BYTES = 16 * 1024;
|
|
@@ -30,12 +30,15 @@ const MESSAGE_SUFFIX_PATTERN = /^[0-9a-f]{8}$/;
|
|
|
30
30
|
const CONVERSATION_SUFFIX_PATTERN = /^[A-Za-z0-9_-]{8}$/;
|
|
31
31
|
const ISO_TIMESTAMP_PATTERN = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d{3})?Z$/;
|
|
32
32
|
export const gatewayControlMethods = [
|
|
33
|
-
"health", "register_codex", "unregister_codex", "
|
|
34
|
-
"
|
|
35
|
-
"
|
|
36
|
-
"refresh_dashboard", "peer_catalog", "peer_handoff",
|
|
33
|
+
"health", "register_codex", "unregister_codex", "list_snapshot",
|
|
34
|
+
"observe_snapshot", "delivery_status", "send",
|
|
35
|
+
"refresh_discovery", "peer_catalog", "peer_handoff",
|
|
37
36
|
"register_peer", "unregister_peer", "await_peer", "peer_receipt",
|
|
38
37
|
];
|
|
38
|
+
/** Bounds on an ephemeral peer registration's lifetime, in milliseconds. */
|
|
39
|
+
export const GATEWAY_EPHEMERAL_PEER_TTL_DEFAULT_MS = 300_000;
|
|
40
|
+
export const GATEWAY_EPHEMERAL_PEER_TTL_MIN_MS = 1_000;
|
|
41
|
+
export const GATEWAY_EPHEMERAL_PEER_TTL_MAX_MS = 3_600_000;
|
|
39
42
|
const WIRE_ERROR_MESSAGES = {
|
|
40
43
|
INVALID_JSON: "The control frame is not valid JSON.", FRAME_TOO_LARGE: "The control frame exceeds the size limit.",
|
|
41
44
|
INVALID_REQUEST: "The control request is invalid.", UNSUPPORTED_VERSION: "The control protocol version is unsupported.",
|
|
@@ -129,12 +132,6 @@ function isNonNegativeInteger(value) {
|
|
|
129
132
|
value >= 0 &&
|
|
130
133
|
value <= MAX_REVISION);
|
|
131
134
|
}
|
|
132
|
-
function isTrackIdleMinutes(value) {
|
|
133
|
-
return (typeof value === "number" &&
|
|
134
|
-
Number.isSafeInteger(value) &&
|
|
135
|
-
value >= 1 &&
|
|
136
|
-
value <= 24 * 60);
|
|
137
|
-
}
|
|
138
135
|
function isIsoTimestamp(value) {
|
|
139
136
|
if (typeof value !== "string" || !ISO_TIMESTAMP_PATTERN.test(value)) {
|
|
140
137
|
return false;
|
|
@@ -159,7 +156,6 @@ function isMessageText(value) {
|
|
|
159
156
|
}
|
|
160
157
|
const exact = hasExactKeys;
|
|
161
158
|
const nonNegative = isNonNegativeInteger;
|
|
162
|
-
const trackMinutes = isTrackIdleMinutes;
|
|
163
159
|
const iso = isIsoTimestamp;
|
|
164
160
|
const uuid = isUuid;
|
|
165
161
|
const alias = isAlias;
|
|
@@ -200,9 +196,20 @@ function decodePeerHandoff(value) {
|
|
|
200
196
|
}
|
|
201
197
|
}
|
|
202
198
|
function decodeRegisterPeer(value) {
|
|
203
|
-
|
|
199
|
+
// `ephemeral` belongs only to a first registration: re-presenting a token
|
|
200
|
+
// proves an existing route, and an existing route's lifetime is already
|
|
201
|
+
// fixed. `ttlMs` without `ephemeral` would name a lifetime nothing honours.
|
|
202
|
+
if (!shape(value, { alias: peerAlias }, { token: peerToken, ephemeral: oneOf(true),
|
|
203
|
+
ttlMs: (item) => Number.isSafeInteger(item) &&
|
|
204
|
+
Number(item) >= GATEWAY_EPHEMERAL_PEER_TTL_MIN_MS &&
|
|
205
|
+
Number(item) <= GATEWAY_EPHEMERAL_PEER_TTL_MAX_MS }) ||
|
|
206
|
+
(value.ephemeral !== undefined && value.token !== undefined) ||
|
|
207
|
+
(value.ttlMs !== undefined && value.ephemeral === undefined))
|
|
204
208
|
invalid();
|
|
205
|
-
return { alias: value.alias,
|
|
209
|
+
return { alias: value.alias,
|
|
210
|
+
...(value.token === undefined ? {} : { token: value.token }),
|
|
211
|
+
...(value.ephemeral === undefined ? {} : { ephemeral: true }),
|
|
212
|
+
...(value.ttlMs === undefined ? {} : { ttlMs: value.ttlMs }) };
|
|
206
213
|
}
|
|
207
214
|
function decodePeerPrincipal(value) {
|
|
208
215
|
if (!shape(value, { alias: peerAlias, token: peerToken }))
|
|
@@ -251,79 +258,35 @@ function decodeUnregister(value) {
|
|
|
251
258
|
invalid();
|
|
252
259
|
return { alias: value.alias, threadId: value.threadId.toLowerCase() };
|
|
253
260
|
}
|
|
254
|
-
function decodeRemoval(value) {
|
|
255
|
-
if (!shape(value, { alias }) || !value.alias.startsWith("codex-"))
|
|
256
|
-
invalid();
|
|
257
|
-
return { alias: value.alias };
|
|
258
|
-
}
|
|
259
|
-
function decodeSelection(value) {
|
|
260
|
-
if (!isRecord(value) || !exact(value, ["alias"]) ||
|
|
261
|
-
typeof value.alias !== "string" || !isClaudeSessionSelector(value.alias))
|
|
262
|
-
invalid();
|
|
263
|
-
return { alias: UUID_PATTERN.test(value.alias) ? value.alias.toLowerCase() : value.alias };
|
|
264
|
-
}
|
|
265
|
-
function decodePair(value) {
|
|
266
|
-
if (!isRecord(value) || !exact(value, ["aliases"]) || !Array.isArray(value.aliases) ||
|
|
267
|
-
value.aliases.length !== 2 || !alias(value.aliases[0]) || !alias(value.aliases[1]) ||
|
|
268
|
-
value.aliases[0] === value.aliases[1])
|
|
269
|
-
invalid();
|
|
270
|
-
return { aliases: [value.aliases[0], value.aliases[1]] };
|
|
271
|
-
}
|
|
272
261
|
function decodeSend(value) {
|
|
273
|
-
if (!isRecord(value) || !exact(value, ["fromAlias", "toAlias", "
|
|
274
|
-
!alias(value.fromAlias) ||
|
|
275
|
-
|
|
262
|
+
if (!isRecord(value) || !exact(value, ["fromAlias", "text"], ["toAlias", "conversationId", "threadId", "replyAddress", "peerToken", "expectsReply"]) ||
|
|
263
|
+
!alias(value.fromAlias) || !messageText(value.text) ||
|
|
264
|
+
[value.toAlias, value.conversationId].filter((item) => item !== undefined).length !== 1 ||
|
|
265
|
+
(value.toAlias !== undefined && (typeof value.toAlias !== "string" || !isClaudeSessionSelector(value.toAlias))) ||
|
|
266
|
+
(value.conversationId !== undefined && (typeof value.conversationId !== "string" || !isGatewayConversationId(value.conversationId))) ||
|
|
267
|
+
[value.threadId, value.replyAddress, value.peerToken].filter((item) => item !== undefined).length !== 1 ||
|
|
276
268
|
(value.threadId !== undefined && !uuid(value.threadId)) ||
|
|
277
269
|
(value.replyAddress !== undefined && (typeof value.replyAddress !== "string" || !isGatewayReplyAddress(value.replyAddress))) ||
|
|
278
270
|
(value.peerToken !== undefined && !peerToken(value.peerToken)) ||
|
|
279
|
-
(value.expectsReply !== undefined &&
|
|
280
|
-
|
|
271
|
+
(value.expectsReply !== undefined &&
|
|
272
|
+
(typeof value.expectsReply !== "boolean" || value.conversationId !== undefined)))
|
|
281
273
|
invalid();
|
|
282
274
|
const authority = value.threadId !== undefined ? { threadId: value.threadId.toLowerCase() }
|
|
283
275
|
: value.replyAddress !== undefined ? { replyAddress: value.replyAddress }
|
|
284
276
|
: { peerToken: value.peerToken };
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
if (!isRecord(value))
|
|
291
|
-
invalid();
|
|
292
|
-
if (value.kind === "codex" && shape(value, { kind: oneOf("codex"), alias, threadId: uuid }))
|
|
293
|
-
return { kind: "codex", alias: value.alias,
|
|
294
|
-
threadId: value.threadId.toLowerCase() };
|
|
295
|
-
if (value.kind === "claude" && isRecord(value) && exact(value, ["kind", "alias"], ["replyAddress"]) &&
|
|
296
|
-
alias(value.alias) && (value.replyAddress === undefined ||
|
|
297
|
-
(typeof value.replyAddress === "string" && isGatewayReplyAddress(value.replyAddress))))
|
|
298
|
-
return { kind: "claude", alias: value.alias,
|
|
299
|
-
...(value.replyAddress === undefined ? {} : { replyAddress: value.replyAddress }) };
|
|
300
|
-
if (value.kind === "peer" && shape(value, { kind: oneOf("peer"), alias: peerAlias, token: peerToken }))
|
|
301
|
-
return { kind: "peer", alias: value.alias, token: value.token };
|
|
302
|
-
return invalid();
|
|
303
|
-
}
|
|
304
|
-
function decodeReply(value) {
|
|
305
|
-
if (!isRecord(value) || !exact(value, ["conversationId", "text", "caller"], ["trackIdleMinutes"]) ||
|
|
306
|
-
typeof value.conversationId !== "string" || !isGatewayConversationId(value.conversationId) ||
|
|
307
|
-
!messageText(value.text) ||
|
|
308
|
-
(value.trackIdleMinutes !== undefined && !trackMinutes(value.trackIdleMinutes)))
|
|
309
|
-
invalid();
|
|
310
|
-
return { conversationId: value.conversationId, text: value.text, caller: normalizeReplyCaller(value.caller),
|
|
311
|
-
...(value.trackIdleMinutes === undefined ? {} : { trackIdleMinutes: value.trackIdleMinutes }) };
|
|
277
|
+
const target = value.conversationId !== undefined
|
|
278
|
+
? { conversationId: value.conversationId }
|
|
279
|
+
: { toAlias: UUID_PATTERN.test(value.toAlias) ? value.toAlias.toLowerCase() : value.toAlias,
|
|
280
|
+
expectsReply: value.expectsReply === true };
|
|
281
|
+
return { fromAlias: value.fromAlias, text: value.text, ...target, ...authority };
|
|
312
282
|
}
|
|
313
283
|
function decodeDeliveryStatus(value) {
|
|
314
284
|
if (!shape(value, { token: (item) => typeof item === "string" && DELIVERY_TOKEN_PATTERN.test(item) }))
|
|
315
285
|
invalid();
|
|
316
286
|
return { token: value.token };
|
|
317
287
|
}
|
|
318
|
-
function decodeUntrack(value) {
|
|
319
|
-
if (!shape(value, { conversationId: (item) => typeof item === "string" && CONVERSATION_ID_PATTERN.test(item) }))
|
|
320
|
-
invalid();
|
|
321
|
-
return { conversationId: value.conversationId };
|
|
322
|
-
}
|
|
323
288
|
function isDecision(value) {
|
|
324
|
-
return shape(value, { accepted: (item) => typeof item === "boolean", code: oneOf("ok", "not_found", "conflict", "
|
|
325
|
-
? value.code === "ok" && value.ownerHost === undefined
|
|
326
|
-
: value.code !== "ok" && (value.ownerHost === undefined || value.code === "conflict"));
|
|
289
|
+
return shape(value, { accepted: (item) => typeof item === "boolean", code: oneOf("ok", "not_found", "conflict", "route_mismatch", "busy", "unavailable", "rejected") }) && (value.accepted === true ? value.code === "ok" : value.code !== "ok");
|
|
327
290
|
}
|
|
328
291
|
function isRegisterPeerResult(value) {
|
|
329
292
|
return isDecision(value) || shape(value, { accepted: oneOf(true), code: oneOf("ok"), token: peerToken });
|
|
@@ -332,14 +295,15 @@ function isHealthResult(value) {
|
|
|
332
295
|
return shape(value, { status: oneOf("ok", "degraded"), revision: nonNegative });
|
|
333
296
|
}
|
|
334
297
|
function isSendResult(value) {
|
|
335
|
-
if (!isRecord(value) || value.accepted !== true)
|
|
336
|
-
return
|
|
298
|
+
if (!isRecord(value) || value.accepted !== true) {
|
|
299
|
+
return shape(value, { accepted: oneOf(false), code: oneOf("not_found", "conflict", "route_mismatch", "busy", "unavailable", "rejected") }, { reason: safeCode });
|
|
300
|
+
}
|
|
337
301
|
return shape(value, { accepted: oneOf(true), code: oneOf("ok"),
|
|
338
302
|
conversationId: (item) => typeof item === "string" && CONVERSATION_ID_PATTERN.test(item),
|
|
339
303
|
deliveryToken: (item) => typeof item === "string" && DELIVERY_TOKEN_PATTERN.test(item) });
|
|
340
304
|
}
|
|
341
305
|
function isRefreshResult(value) {
|
|
342
|
-
return shape(value, { accepted: (item) => typeof item === "boolean", code: oneOf("ok", "not_found", "conflict", "
|
|
306
|
+
return shape(value, { accepted: (item) => typeof item === "boolean", code: oneOf("ok", "not_found", "conflict", "route_mismatch", "busy", "unavailable", "rejected"), revision: nonNegative }) && isDecision({ accepted: value.accepted, code: value.code });
|
|
343
307
|
}
|
|
344
308
|
const TERMINAL = new Set(["delivered", "unconfirmed", "expired", "failed", "ambiguous", "cancelled"]);
|
|
345
309
|
function isDeliveryStatusResult(value) {
|
|
@@ -359,13 +323,9 @@ function isConnectorSnapshot(value) {
|
|
|
359
323
|
if (!shape(value, { provider: isGatewayProvider, host, health: isConnectorHealth,
|
|
360
324
|
protocol: (item) => typeof item === "string" && PROTOCOL_PATTERN.test(item),
|
|
361
325
|
protocolVersion: (item) => typeof item === "string" && PROTOCOL_VERSION_PATTERN.test(item) }, { lastSeenAt: iso, observationAgeMs: nonNegative, safeErrorCode: safeCode,
|
|
362
|
-
registry: isPublicRegistryObservationSnapshot
|
|
363
|
-
conditions: (rows) => Array.isArray(rows) && rows.length > 0 && rows.length <= 2 &&
|
|
364
|
-
rows.every(oneOf("split_brain", "orphaned", "attached", "observation_stale", "managed_layout_missing", "unknown")),
|
|
365
|
-
}) }))
|
|
326
|
+
registry: isPublicRegistryObservationSnapshot }))
|
|
366
327
|
return false;
|
|
367
|
-
return
|
|
368
|
-
(value.registry === undefined || value.provider === "claude");
|
|
328
|
+
return value.registry === undefined || value.provider === "claude";
|
|
369
329
|
}
|
|
370
330
|
function isRouteSnapshot(value) {
|
|
371
331
|
if (!shape(value, { alias, provider: isGatewayProvider, host, enabled: (item) => typeof item === "boolean",
|
|
@@ -376,24 +336,10 @@ function isRouteSnapshot(value) {
|
|
|
376
336
|
return row.alias.endsWith(`@${row.host}`) &&
|
|
377
337
|
(row.oldestQueuedAt === undefined ? row.queueDepth === 0 : row.queueDepth > 0);
|
|
378
338
|
}
|
|
379
|
-
function compareConsentEndpoints(left, right) {
|
|
380
|
-
return gatewayProviders.indexOf(left.provider) -
|
|
381
|
-
gatewayProviders.indexOf(right.provider) || left.alias.localeCompare(right.alias);
|
|
382
|
-
}
|
|
383
|
-
function isConsentEdgeSnapshot(value) {
|
|
384
|
-
const endpoint = (item) => shape(item, { alias, provider: isGatewayProvider });
|
|
385
|
-
if (!shape(value, { endpoints: (rows) => Array.isArray(rows) && rows.length === 2 && rows.every(endpoint),
|
|
386
|
-
host, counters: isRouteCounters }, { mutable: (item) => typeof item === "boolean" }))
|
|
387
|
-
return false;
|
|
388
|
-
const [left, right] = value.endpoints;
|
|
389
|
-
const hosts = [left.alias.slice(left.alias.lastIndexOf("@") + 1), right.alias.slice(right.alias.lastIndexOf("@") + 1)];
|
|
390
|
-
return left.alias !== right.alias && compareConsentEndpoints(left, right) < 0 &&
|
|
391
|
-
(left.provider !== right.provider || hosts[0] !== hosts[1]) && value.host === [...hosts].sort()[0];
|
|
392
|
-
}
|
|
393
339
|
function isAvailablePeerSnapshot(value) {
|
|
394
340
|
return shape(value, { alias, provider: oneOf("claude"), host,
|
|
395
341
|
state: oneOf("idle", "busy", "awaiting_approval", "offline"),
|
|
396
|
-
validated: (item) => typeof item === "boolean",
|
|
342
|
+
validated: (item) => typeof item === "boolean", routed: (item) => typeof item === "boolean" }, { lastSeenAt: iso, safeErrorCode: safeCode }) &&
|
|
397
343
|
value.alias.endsWith(`@${String(value.host)}`);
|
|
398
344
|
}
|
|
399
345
|
function isNormalizedMessageEvent(value) {
|
|
@@ -415,40 +361,17 @@ function isAccounting(value) {
|
|
|
415
361
|
function isSafeAlert(value) {
|
|
416
362
|
return shape(value, { code: safeCode, severity: oneOf("info", "warning", "error"), timestamp: iso }, { provider: isGatewayProvider, host, alias });
|
|
417
363
|
}
|
|
418
|
-
function isProgressWatchSnapshot(value) {
|
|
419
|
-
return shape(value, {
|
|
420
|
-
conversationIdSuffix: (item) => typeof item === "string" && CONVERSATION_SUFFIX_PATTERN.test(item),
|
|
421
|
-
ownerAlias: alias, workerAlias: alias, lastActivityAt: iso, nextActionAt: iso,
|
|
422
|
-
nudgeCount: oneOf(0, 1, 2),
|
|
423
|
-
}) && value.ownerAlias !== value.workerAlias;
|
|
424
|
-
}
|
|
425
|
-
function isProgressWatchEventSnapshot(value) {
|
|
426
|
-
if (!shape(value, { sequence: nonNegative, timestamp: iso,
|
|
427
|
-
conversationIdSuffix: (item) => typeof item === "string" && CONVERSATION_SUFFIX_PATTERN.test(item),
|
|
428
|
-
ownerAlias: alias, workerAlias: alias, kind: oneOf("opened", "replaced", "settled"),
|
|
429
|
-
actor: oneOf("owner", "worker", "operator", "gateway", "unknown") }, { reason: oneOf("done", "untracked", "idle_timeout", "tracking_disabled", "endpoint_retired", "pair_removed") }))
|
|
430
|
-
return false;
|
|
431
|
-
if (value.kind === "opened")
|
|
432
|
-
return value.actor === "owner" && value.reason === undefined;
|
|
433
|
-
if (value.kind === "replaced")
|
|
434
|
-
return (value.actor === "owner" || value.actor === "unknown") && value.reason === undefined;
|
|
435
|
-
return (value.reason === "done" && (value.actor === "owner" || value.actor === "worker")) ||
|
|
436
|
-
(value.reason === "untracked" && value.actor === "operator") ||
|
|
437
|
-
((value.reason === "idle_timeout" || value.reason === "tracking_disabled") && value.actor === "gateway") ||
|
|
438
|
-
(value.reason === "endpoint_retired" && (value.actor === "gateway" || value.actor === "operator")) ||
|
|
439
|
-
(value.reason === "pair_removed" && value.actor === "operator");
|
|
440
|
-
}
|
|
441
364
|
function isGatewayActivityEvent(value) {
|
|
442
365
|
if (!shape(value, { sequence: positive, timestamp: iso,
|
|
443
|
-
kind: oneOf("discovery", "
|
|
444
|
-
action: oneOf("discovery_refreshed", "
|
|
366
|
+
kind: oneOf("discovery", "registration"),
|
|
367
|
+
action: oneOf("discovery_refreshed", "codex_registered", "codex_succeeded", "codex_unregistered", "claude_route_installed", "claude_route_retired"),
|
|
445
368
|
outcome: oneOf("accepted", "rejected"), aliases: (rows) => arrayOf(rows, 2, alias),
|
|
446
369
|
operatorAction: oneOf(true) }, { safeErrorCode: safeCode }))
|
|
447
370
|
return false;
|
|
448
371
|
const allowed = {
|
|
449
|
-
discovery: ["discovery_refreshed"],
|
|
450
|
-
registration: ["codex_registered", "codex_succeeded", "codex_unregistered"
|
|
451
|
-
|
|
372
|
+
discovery: ["discovery_refreshed"],
|
|
373
|
+
registration: ["codex_registered", "codex_succeeded", "codex_unregistered",
|
|
374
|
+
"claude_route_installed", "claude_route_retired"],
|
|
452
375
|
};
|
|
453
376
|
return new Set(value.aliases).size === value.aliases.length &&
|
|
454
377
|
(allowed[value.kind]?.includes(value.action) ?? false);
|
|
@@ -466,23 +389,20 @@ function isDeadlinePressure(value) {
|
|
|
466
389
|
rows.reduce((sum, row) => sum + Number(row.expired), 0) === value.expiredEvents;
|
|
467
390
|
}
|
|
468
391
|
function isSnapshotTruncation(value) {
|
|
469
|
-
const required = ["connectors", "availablePeers", "routes", "
|
|
470
|
-
const optional = ["
|
|
392
|
+
const required = ["connectors", "availablePeers", "routes", "messages", "alerts"];
|
|
393
|
+
const optional = ["activityEvents"];
|
|
471
394
|
return isRecord(value) && exact(value, required, optional) &&
|
|
472
395
|
[...required, ...optional].every((key) => value[key] === undefined || nonNegative(value[key]));
|
|
473
396
|
}
|
|
474
397
|
export function isGatewaySnapshot(value) {
|
|
475
|
-
if (!shape(value, { schemaVersion: oneOf(2), generatedAt: iso,
|
|
398
|
+
if (!shape(value, { schemaVersion: oneOf(2), generatedAt: iso,
|
|
476
399
|
health: isConnectorHealth,
|
|
477
400
|
connectors: (rows) => arrayOf(rows, gatewayPublicSnapshotLimits.connectors, isConnectorSnapshot),
|
|
478
401
|
availablePeers: (rows) => arrayOf(rows, gatewayPublicSnapshotLimits.availablePeers, isAvailablePeerSnapshot),
|
|
479
402
|
routes: (rows) => arrayOf(rows, gatewayPublicSnapshotLimits.routes, isRouteSnapshot),
|
|
480
|
-
consentEdges: (rows) => arrayOf(rows, gatewayPublicSnapshotLimits.consentEdges, isConsentEdgeSnapshot),
|
|
481
403
|
messages: (rows) => arrayOf(rows, gatewayPublicSnapshotLimits.messages, isNormalizedMessageEvent),
|
|
482
404
|
accounting: isAccounting, alerts: (rows) => arrayOf(rows, gatewayPublicSnapshotLimits.alerts, isSafeAlert),
|
|
483
|
-
truncation: isSnapshotTruncation }, {
|
|
484
|
-
progressWatchEvents: (rows) => arrayOf(rows, gatewayPublicSnapshotLimits.progressWatchEvents, isProgressWatchEventSnapshot),
|
|
485
|
-
activityEvents: (rows) => arrayOf(rows, gatewayPublicSnapshotLimits.activityEvents, isGatewayActivityEvent),
|
|
405
|
+
truncation: isSnapshotTruncation }, { activityEvents: (rows) => arrayOf(rows, gatewayPublicSnapshotLimits.activityEvents, isGatewayActivityEvent),
|
|
486
406
|
deadlinePressure: isDeadlinePressure }))
|
|
487
407
|
return false;
|
|
488
408
|
const snapshot = value;
|
|
@@ -491,16 +411,13 @@ export function isGatewaySnapshot(value) {
|
|
|
491
411
|
const connectorKeys = snapshot.connectors.map((row) => `${row.provider}@${row.host}`);
|
|
492
412
|
const aliases = snapshot.routes.map((row) => row.alias);
|
|
493
413
|
const peers = snapshot.availablePeers.map((row) => row.alias);
|
|
494
|
-
const edgeKeys = snapshot.consentEdges.map((row) => `${row.endpoints[0].alias}\0${row.endpoints[1].alias}`);
|
|
495
|
-
const routeByAlias = new Map(snapshot.routes.map((row) => [row.alias, row]));
|
|
496
414
|
const honest = snapshot.connectors.every((row) => {
|
|
497
415
|
const seen = row.lastSeenAt === undefined ? undefined : Date.parse(row.lastSeenAt);
|
|
498
416
|
const age = seen === undefined ? undefined : Math.min(MAX_REVISION, Math.max(0, generated - seen));
|
|
499
417
|
return (row.observationAgeMs === undefined || row.observationAgeMs === age) &&
|
|
500
418
|
(row.health !== "healthy" || (age !== undefined && age <= CONNECTOR_OBSERVATION_STALE_AFTER_MS));
|
|
501
419
|
});
|
|
502
|
-
return honest && unique(connectorKeys) && unique(aliases) && unique(peers) &&
|
|
503
|
-
snapshot.consentEdges.every((edge) => edge.endpoints.every((endpoint) => routeByAlias.get(endpoint.alias)?.provider === endpoint.provider)) &&
|
|
420
|
+
return honest && unique(connectorKeys) && unique(aliases) && unique(peers) &&
|
|
504
421
|
Buffer.byteLength(JSON.stringify(snapshot), "utf8") <= GATEWAY_PUBLIC_SNAPSHOT_BYTE_BUDGET;
|
|
505
422
|
}
|
|
506
423
|
function isSnapshotObservation(value) {
|
|
@@ -508,13 +425,11 @@ function isSnapshotObservation(value) {
|
|
|
508
425
|
}
|
|
509
426
|
const descriptors = {
|
|
510
427
|
health: { handler: "health", decode: emptyParams, result: isHealthResult, mutation: false }, register_codex: { handler: "registerCodex", decode: decodeRegister, result: isDecision, mutation: true },
|
|
511
|
-
unregister_codex: { handler: "unregisterCodex", decode: decodeUnregister, result: isDecision, mutation: true },
|
|
512
|
-
select_claude: { handler: "selectClaude", decode: decodeSelection, result: isDecision, mutation: true }, unselect_claude: { handler: "unselectClaude", decode: decodeSelection, result: isDecision, mutation: true },
|
|
513
|
-
pair: { handler: "pair", decode: decodePair, result: isDecision, mutation: true }, unpair: { handler: "unpair", decode: decodePair, result: isDecision, mutation: true },
|
|
428
|
+
unregister_codex: { handler: "unregisterCodex", decode: decodeUnregister, result: isDecision, mutation: true },
|
|
514
429
|
list_snapshot: { handler: "listSnapshot", decode: emptyParams, result: isGatewaySnapshot, mutation: false }, observe_snapshot: { handler: "observeSnapshot", decode: emptyParams, result: isSnapshotObservation, mutation: false },
|
|
515
|
-
delivery_status: { handler: "deliveryStatus", decode: decodeDeliveryStatus, result: isDeliveryStatusResult, mutation: false },
|
|
430
|
+
delivery_status: { handler: "deliveryStatus", decode: decodeDeliveryStatus, result: isDeliveryStatusResult, mutation: false },
|
|
516
431
|
send: { handler: "send", decode: decodeSend, result: isSendResult, mutation: true },
|
|
517
|
-
|
|
432
|
+
refresh_discovery: { handler: "refreshDiscovery", decode: emptyParams, result: isRefreshResult, mutation: false },
|
|
518
433
|
peer_catalog: { handler: "peerCatalog", decode: decodePeerCatalog, result: isPeerCatalog, mutation: false },
|
|
519
434
|
peer_handoff: { handler: "peerHandoff", decode: decodePeerHandoff, result: isPeerHandoffResult, mutation: true },
|
|
520
435
|
register_peer: { handler: "registerPeer", decode: decodeRegisterPeer, result: isRegisterPeerResult, mutation: true },
|