agent-embassy 1.6.1 → 1.7.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +33 -0
- package/README.md +9 -9
- package/README.zh-CN.md +9 -9
- package/SECURITY.md +34 -77
- package/dist/src/gateway/acp-client.d.ts +117 -0
- package/dist/src/gateway/acp-client.js +382 -0
- package/dist/src/gateway/acp-client.js.map +1 -0
- package/dist/src/gateway/acp-provider.d.ts +65 -0
- package/dist/src/gateway/acp-provider.js +163 -0
- package/dist/src/gateway/acp-provider.js.map +1 -0
- package/dist/src/gateway/claude-helper-protocol.d.ts +3 -1
- package/dist/src/gateway/claude-helper-protocol.js +15 -5
- package/dist/src/gateway/claude-helper-protocol.js.map +1 -1
- package/dist/src/gateway/claude-helper-supervisor.d.ts +3 -1
- package/dist/src/gateway/claude-helper-supervisor.js +16 -2
- package/dist/src/gateway/claude-helper-supervisor.js.map +1 -1
- package/dist/src/gateway/claude-helper.js +4 -3
- package/dist/src/gateway/claude-helper.js.map +1 -1
- package/dist/src/gateway/claude-peer.d.ts +1 -1
- package/dist/src/gateway/claude-peer.js +11 -13
- package/dist/src/gateway/claude-peer.js.map +1 -1
- package/dist/src/gateway/claude-runtime.d.ts +0 -20
- package/dist/src/gateway/claude-runtime.js +6 -127
- package/dist/src/gateway/claude-runtime.js.map +1 -1
- package/dist/src/gateway/cli-copy.d.ts +1 -1
- package/dist/src/gateway/cli-copy.en.d.ts +3 -1
- package/dist/src/gateway/cli-copy.en.js +5 -2
- package/dist/src/gateway/cli-copy.en.js.map +1 -1
- package/dist/src/gateway/cli-copy.js +2 -0
- package/dist/src/gateway/cli-copy.js.map +1 -1
- package/dist/src/gateway/cli-copy.zh-CN.d.ts +3 -1
- package/dist/src/gateway/cli-copy.zh-CN.js +5 -2
- package/dist/src/gateway/cli-copy.zh-CN.js.map +1 -1
- package/dist/src/gateway/cli.d.ts +2 -2
- package/dist/src/gateway/cli.js +73 -6
- package/dist/src/gateway/cli.js.map +1 -1
- package/dist/src/gateway/codex-app-server.d.ts +5 -74
- package/dist/src/gateway/codex-app-server.js +9 -599
- package/dist/src/gateway/codex-app-server.js.map +1 -1
- package/dist/src/gateway/codex-doctor.d.ts +33 -0
- package/dist/src/gateway/codex-doctor.js +148 -0
- package/dist/src/gateway/codex-doctor.js.map +1 -0
- package/dist/src/gateway/codex-local-transport.d.ts +13 -26
- package/dist/src/gateway/codex-local-transport.js +31 -76
- package/dist/src/gateway/codex-local-transport.js.map +1 -1
- package/dist/src/gateway/compatibility.d.ts +6 -66
- package/dist/src/gateway/compatibility.js +14 -296
- package/dist/src/gateway/compatibility.js.map +1 -1
- package/dist/src/gateway/config.d.ts +9 -0
- package/dist/src/gateway/config.js +6 -2
- package/dist/src/gateway/config.js.map +1 -1
- package/dist/src/gateway/control.d.ts +16 -1
- package/dist/src/gateway/control.js +128 -59
- package/dist/src/gateway/control.js.map +1 -1
- package/dist/src/gateway/dashboard-copy.d.ts +1 -1
- package/dist/src/gateway/dashboard-copy.en.d.ts +19 -44
- package/dist/src/gateway/dashboard-copy.en.js +42 -67
- package/dist/src/gateway/dashboard-copy.en.js.map +1 -1
- package/dist/src/gateway/dashboard-copy.js +19 -44
- package/dist/src/gateway/dashboard-copy.js.map +1 -1
- package/dist/src/gateway/dashboard-copy.zh-CN.d.ts +19 -44
- package/dist/src/gateway/dashboard-copy.zh-CN.js +42 -67
- package/dist/src/gateway/dashboard-copy.zh-CN.js.map +1 -1
- package/dist/src/gateway/dashboard-model.d.ts +41 -76
- package/dist/src/gateway/dashboard-model.js +167 -257
- package/dist/src/gateway/dashboard-model.js.map +1 -1
- package/dist/src/gateway/dashboard.d.ts +0 -3
- package/dist/src/gateway/dashboard.js +77 -76
- package/dist/src/gateway/dashboard.js.map +1 -1
- package/dist/src/gateway/deepseek-detect.d.ts +7 -6
- package/dist/src/gateway/deepseek-detect.js +31 -115
- package/dist/src/gateway/deepseek-detect.js.map +1 -1
- package/dist/src/gateway/live-dashboard-app/app.js +175 -621
- package/dist/src/gateway/live-dashboard-command.js +2 -4
- package/dist/src/gateway/live-dashboard-command.js.map +1 -1
- package/dist/src/gateway/live-dashboard-http.d.ts +1 -2
- package/dist/src/gateway/live-dashboard-http.js +14 -15
- package/dist/src/gateway/live-dashboard-http.js.map +1 -1
- package/dist/src/gateway/progress-watch-machine.d.ts +1 -4
- package/dist/src/gateway/progress-watch-machine.js.map +1 -1
- package/dist/src/gateway/provenance-envelope.d.ts +3 -2
- package/dist/src/gateway/provenance-envelope.js +44 -11
- package/dist/src/gateway/provenance-envelope.js.map +1 -1
- package/dist/src/gateway/providers.d.ts +19 -85
- package/dist/src/gateway/providers.js +132 -835
- package/dist/src/gateway/providers.js.map +1 -1
- package/dist/src/gateway/server.d.ts +8 -9
- package/dist/src/gateway/server.js +63 -99
- package/dist/src/gateway/server.js.map +1 -1
- package/dist/src/gateway/service.d.ts +48 -51
- package/dist/src/gateway/service.js +748 -540
- package/dist/src/gateway/service.js.map +1 -1
- package/dist/src/gateway/store.d.ts +20 -29
- package/dist/src/gateway/store.js +306 -838
- package/dist/src/gateway/store.js.map +1 -1
- package/dist/src/gateway/types.d.ts +63 -54
- package/dist/src/gateway/types.js +51 -91
- package/dist/src/gateway/types.js.map +1 -1
- package/docs/CONFIGURATION.md +11 -10
- package/docs/CONFIGURATION.zh-CN.md +10 -9
- package/docs/DASHBOARD.md +4 -38
- package/docs/DASHBOARD.zh-CN.md +4 -2
- package/docs/GATEWAY-ARCHITECTURE.md +61 -119
- package/package.json +1 -1
- package/skills/embassy-peer/SKILL.md +6 -6
|
@@ -1,10 +1,9 @@
|
|
|
1
|
-
import { arePublicAvailablePeerSnapshots,
|
|
2
|
-
import { compatibilitySurfaceDefinitions, } from "./compatibility.js";
|
|
1
|
+
import { arePublicAvailablePeerSnapshots, gatewayProviders, isPublicRegistryObservationSnapshot, parseDirection, } from "./types.js";
|
|
3
2
|
export const DASHBOARD_MODEL_LIMITS = Object.freeze({
|
|
4
3
|
connectors: 16,
|
|
5
4
|
availablePeers: 64,
|
|
6
5
|
routes: 128,
|
|
7
|
-
|
|
6
|
+
consentEdges: 64,
|
|
8
7
|
messages: 50,
|
|
9
8
|
messageEvents: 60,
|
|
10
9
|
progressWatches: 64,
|
|
@@ -35,7 +34,6 @@ const statePresentation = {
|
|
|
35
34
|
busy: { tone: "info", chip: "active" },
|
|
36
35
|
awaiting_approval: { tone: "warning", chip: "warning" },
|
|
37
36
|
offline: { tone: "danger", chip: "failure" },
|
|
38
|
-
incompatible: { tone: "danger", chip: "failure" },
|
|
39
37
|
disabled: { tone: "quiet", chip: "inert" },
|
|
40
38
|
}),
|
|
41
39
|
peer: presentation({
|
|
@@ -43,20 +41,12 @@ const statePresentation = {
|
|
|
43
41
|
busy: { tone: "info", chip: "active" },
|
|
44
42
|
awaiting_approval: { tone: "warning", chip: "warning" },
|
|
45
43
|
offline: { tone: "danger", chip: "inert" },
|
|
46
|
-
incompatible: { tone: "danger", chip: "failure" },
|
|
47
44
|
}),
|
|
48
45
|
health: presentation({
|
|
49
46
|
healthy: { tone: "good", chip: "positive" },
|
|
50
47
|
connecting: { tone: "info", chip: "progress" },
|
|
51
48
|
degraded: { tone: "warning", chip: "warning" },
|
|
52
49
|
offline: { tone: "danger", chip: "failure" },
|
|
53
|
-
incompatible: { tone: "danger", chip: "failure" },
|
|
54
|
-
}),
|
|
55
|
-
compatibility: presentation({
|
|
56
|
-
compatible: { tone: "good", chip: "positive" },
|
|
57
|
-
unknown: { tone: "quiet", chip: "inert" },
|
|
58
|
-
expired: { tone: "warning", chip: "warning" },
|
|
59
|
-
incompatible: { tone: "danger", chip: "failure" },
|
|
60
50
|
}),
|
|
61
51
|
overall: presentation({
|
|
62
52
|
ready: { tone: "good", chip: "positive" },
|
|
@@ -104,7 +94,6 @@ const guidanceCopyKeys = {
|
|
|
104
94
|
progress_watch: "progressWatch",
|
|
105
95
|
registry_empty: "registryEmpty",
|
|
106
96
|
registry_rejected: "registryRejected",
|
|
107
|
-
provider_incompatible: "providerIncompatible",
|
|
108
97
|
generic: "generic",
|
|
109
98
|
};
|
|
110
99
|
const nextActionCopyKeys = {
|
|
@@ -115,7 +104,6 @@ const nextActionCopyKeys = {
|
|
|
115
104
|
repair_claude_inventory: "next.repairClaude",
|
|
116
105
|
register_codex: "next.registerCodex",
|
|
117
106
|
restore_codex: "next.restoreCodex",
|
|
118
|
-
review_compatibility: "next.reviewCompatibility",
|
|
119
107
|
none: "next.none",
|
|
120
108
|
};
|
|
121
109
|
const attentionCommands = {
|
|
@@ -137,7 +125,6 @@ const attentionCommands = {
|
|
|
137
125
|
progress_watch: "embassy status",
|
|
138
126
|
registry_empty: "embassy refresh-dashboard",
|
|
139
127
|
registry_rejected: "embassy status",
|
|
140
|
-
provider_incompatible: "embassy refresh-dashboard",
|
|
141
128
|
generic: "embassy status",
|
|
142
129
|
};
|
|
143
130
|
const deliveryMeaningKeys = {
|
|
@@ -158,8 +145,8 @@ const deliveryMeaningKeys = {
|
|
|
158
145
|
/** JSON-safe semantics embedded once in the live dashboard boot payload. */
|
|
159
146
|
export const DASHBOARD_SEMANTICS = Object.freeze({
|
|
160
147
|
statePresentation,
|
|
161
|
-
|
|
162
|
-
delivered: {
|
|
148
|
+
deliveryChipByTargetProvider: {
|
|
149
|
+
delivered: { claude: "qualified" },
|
|
163
150
|
},
|
|
164
151
|
deliveryChipBySafeErrorCode: {
|
|
165
152
|
rejected: { SENDER_NOT_PAIRED: "inert" },
|
|
@@ -175,9 +162,11 @@ export const DASHBOARD_SEMANTICS = Object.freeze({
|
|
|
175
162
|
codex_succession_recovery: "<old>",
|
|
176
163
|
},
|
|
177
164
|
deliveryMeaningKeys,
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
165
|
+
deliveryMeaningByTargetProvider: {
|
|
166
|
+
claude: "activity.meaning.delivered.toClaude",
|
|
167
|
+
codex: "activity.meaning.delivered.toCodex",
|
|
168
|
+
deepseek: "activity.meaning.delivered.toDeepSeek",
|
|
169
|
+
grok: "activity.meaning.delivered.toGrok",
|
|
181
170
|
},
|
|
182
171
|
deliveryMeaningBySafeErrorCode: {
|
|
183
172
|
SENDER_NOT_PAIRED: "activity.meaning.senderNotPaired",
|
|
@@ -196,14 +185,15 @@ function semanticPresentation(domain, state) {
|
|
|
196
185
|
export function dashboardChipKind(domain, state, direction, safeErrorCode) {
|
|
197
186
|
if (domain === "delivery") {
|
|
198
187
|
const byCode = DASHBOARD_SEMANTICS.deliveryChipBySafeErrorCode;
|
|
199
|
-
const
|
|
188
|
+
const byTarget = DASHBOARD_SEMANTICS.deliveryChipByTargetProvider;
|
|
200
189
|
if (safeErrorCode !== undefined) {
|
|
201
190
|
const override = byCode[state]?.[safeErrorCode];
|
|
202
191
|
if (override !== undefined)
|
|
203
192
|
return override;
|
|
204
193
|
}
|
|
205
194
|
if (direction !== undefined) {
|
|
206
|
-
const
|
|
195
|
+
const target = parseDirection(direction)?.targetProvider;
|
|
196
|
+
const override = target === undefined ? undefined : byTarget[state]?.[target];
|
|
207
197
|
if (override !== undefined)
|
|
208
198
|
return override;
|
|
209
199
|
}
|
|
@@ -224,7 +214,10 @@ export function deliveryMeaningKey(state, direction, safeErrorCode) {
|
|
|
224
214
|
return DASHBOARD_SEMANTICS.deliveryMeaningBySafeErrorCode.SENDER_NOT_PAIRED;
|
|
225
215
|
}
|
|
226
216
|
if (state === "delivered" && direction !== undefined) {
|
|
227
|
-
|
|
217
|
+
const target = parseDirection(direction)?.targetProvider;
|
|
218
|
+
if (target !== undefined) {
|
|
219
|
+
return DASHBOARD_SEMANTICS.deliveryMeaningByTargetProvider[target];
|
|
220
|
+
}
|
|
228
221
|
}
|
|
229
222
|
if (state === "abandoned" && safeErrorCode !== undefined) {
|
|
230
223
|
const byCode = DASHBOARD_SEMANTICS.deliveryMeaningBySafeErrorCode;
|
|
@@ -300,16 +293,14 @@ function normalizedCounters(counters) {
|
|
|
300
293
|
bytesAccepted: normalizedInteger(counters?.bytesAccepted) ?? 0,
|
|
301
294
|
};
|
|
302
295
|
}
|
|
303
|
-
export function buildDashboardViewModel(snapshot
|
|
304
|
-
return buildProjectedDashboardViewModel(snapshot, false
|
|
296
|
+
export function buildDashboardViewModel(snapshot) {
|
|
297
|
+
return buildProjectedDashboardViewModel(snapshot, false);
|
|
305
298
|
}
|
|
306
|
-
export function buildLiveDashboardViewModel(snapshot
|
|
307
|
-
return buildProjectedDashboardViewModel(snapshot, true
|
|
299
|
+
export function buildLiveDashboardViewModel(snapshot) {
|
|
300
|
+
return buildProjectedDashboardViewModel(snapshot, true);
|
|
308
301
|
}
|
|
309
302
|
function routeIsReady(route) {
|
|
310
303
|
return (route.enabled &&
|
|
311
|
-
route.safeErrorCode !== "CODEX_WRITES_DISABLED" &&
|
|
312
|
-
route.compatibility === "compatible" &&
|
|
313
304
|
(route.state === "idle" || route.state === "busy"));
|
|
314
305
|
}
|
|
315
306
|
function peerSelectionGuidance(peer) {
|
|
@@ -322,11 +313,6 @@ function peerSelectionGuidance(peer) {
|
|
|
322
313
|
}
|
|
323
314
|
if (peer.state === "offline")
|
|
324
315
|
return "offline";
|
|
325
|
-
if (peer.state === "incompatible" ||
|
|
326
|
-
peer.compatibility !== "compatible" ||
|
|
327
|
-
peer.safeErrorCode !== undefined) {
|
|
328
|
-
return "incompatible";
|
|
329
|
-
}
|
|
330
316
|
return undefined;
|
|
331
317
|
}
|
|
332
318
|
function peerIsSelectable(peer) {
|
|
@@ -343,10 +329,10 @@ function partyStatus(routes, ready) {
|
|
|
343
329
|
return routes.some((route) => route.state === "busy") ? "busy" : "ready";
|
|
344
330
|
}
|
|
345
331
|
function routePriority(route) {
|
|
346
|
-
if (!route.enabled || route.state === "offline"
|
|
332
|
+
if (!route.enabled || route.state === "offline") {
|
|
347
333
|
return 0;
|
|
348
334
|
}
|
|
349
|
-
if (route.state === "stale"
|
|
335
|
+
if (route.state === "stale")
|
|
350
336
|
return 1;
|
|
351
337
|
if (route.state === "awaiting_approval")
|
|
352
338
|
return 2;
|
|
@@ -358,7 +344,8 @@ const CODEX_APP_RECONNECT_GUIDANCE_AFTER_MS = 2_000;
|
|
|
358
344
|
function needsCodexAppReconnect(route, connectors, generatedAt) {
|
|
359
345
|
if (route.provider !== "codex" ||
|
|
360
346
|
route.state !== "stale" ||
|
|
361
|
-
route.safeErrorCode !== "CODEX_ROUTE_STALE"
|
|
347
|
+
(route.safeErrorCode !== "CODEX_ROUTE_STALE" &&
|
|
348
|
+
route.safeErrorCode !== "ENDPOINT_GENERATION_CHANGED") ||
|
|
362
349
|
route.lastSeenAt === undefined ||
|
|
363
350
|
generatedAt === undefined ||
|
|
364
351
|
Date.parse(generatedAt) - Date.parse(route.lastSeenAt) <
|
|
@@ -367,8 +354,7 @@ function needsCodexAppReconnect(route, connectors, generatedAt) {
|
|
|
367
354
|
}
|
|
368
355
|
return connectors.some((connector) => connector.provider === "codex" &&
|
|
369
356
|
connector.host === route.host &&
|
|
370
|
-
connector.health === "healthy"
|
|
371
|
-
connector.compatibility === "compatible");
|
|
357
|
+
connector.health === "healthy");
|
|
372
358
|
}
|
|
373
359
|
function alertPriority(severity) {
|
|
374
360
|
return severity === "error" ? 0 : severity === "warning" ? 1 : 2;
|
|
@@ -405,14 +391,6 @@ function guidanceFor(code, provider) {
|
|
|
405
391
|
case "ADAPTER_DEGRADED":
|
|
406
392
|
case "ROUTE_DEGRADED":
|
|
407
393
|
return "degraded";
|
|
408
|
-
case "CLAUDE_PEER_VERSION_UNSUPPORTED":
|
|
409
|
-
case "CLAUDE_VERSION_UNPARSEABLE":
|
|
410
|
-
case "CLAUDE_VERSION_CHECK_FAILED":
|
|
411
|
-
case "CLAUDE_VERSION_EVIDENCE_CONFLICT":
|
|
412
|
-
case "CLAUDE_VERSION_EVIDENCE_TOO_LARGE":
|
|
413
|
-
case "CODEX_APP_SERVER_VERSION_UNSUPPORTED":
|
|
414
|
-
case "CODEX_APP_SERVER_VERSION_UNPARSEABLE":
|
|
415
|
-
return "provider_incompatible";
|
|
416
394
|
default:
|
|
417
395
|
return "generic";
|
|
418
396
|
}
|
|
@@ -574,35 +552,17 @@ function buildMessageGroups(messages, includeBodies) {
|
|
|
574
552
|
activeGroups: all.filter((message) => ["queued", "dispatching", "transport_written", "held"].includes(message.state)).length,
|
|
575
553
|
};
|
|
576
554
|
}
|
|
577
|
-
function buildProjectedDashboardViewModel(snapshot, includeBodies
|
|
578
|
-
const surfaceDefinitions = options.compatibilitySurfaceDefinitions ?? compatibilitySurfaceDefinitions;
|
|
555
|
+
function buildProjectedDashboardViewModel(snapshot, includeBodies) {
|
|
579
556
|
const generatedAt = normalizedTimestamp(snapshot.generatedAt);
|
|
580
557
|
const inboundMode = snapshot.inboundMode === "open" ? "open" : "paired";
|
|
581
558
|
const validPeers = arePublicAvailablePeerSnapshots(snapshot.availablePeers)
|
|
582
559
|
? snapshot.availablePeers
|
|
583
560
|
: [];
|
|
584
|
-
const writesCoveredProviders = new Set((Array.isArray(snapshot.compatibilityChecks)
|
|
585
|
-
? snapshot.compatibilityChecks.filter(isPublicCompatibilityCheckSnapshot)
|
|
586
|
-
: []).filter((check) => check.writesCovered).map((check) => check.surface));
|
|
587
|
-
const quarantinedProviders = new Set([
|
|
588
|
-
...snapshot.connectors
|
|
589
|
-
.filter((connector) => connector.compatibility === "incompatible")
|
|
590
|
-
.map((connector) => connector.provider),
|
|
591
|
-
...(Array.isArray(snapshot.compatibilityChecks)
|
|
592
|
-
? snapshot.compatibilityChecks
|
|
593
|
-
.filter(isPublicCompatibilityCheckSnapshot)
|
|
594
|
-
.filter((check) => check.tier === "incompatible")
|
|
595
|
-
.flatMap((check) => check.surface === "claude" || check.surface === "codex"
|
|
596
|
-
? [check.surface]
|
|
597
|
-
: [])
|
|
598
|
-
: []),
|
|
599
|
-
]);
|
|
600
561
|
const peers = validPeers
|
|
601
562
|
.map((peer) => ({
|
|
602
563
|
alias: boundedText(peer.alias),
|
|
603
564
|
host: boundedText(peer.host),
|
|
604
565
|
state: peer.state,
|
|
605
|
-
compatibility: peer.compatibility,
|
|
606
566
|
validated: peer.validated,
|
|
607
567
|
selected: peer.selected,
|
|
608
568
|
selectable: peerIsSelectable(peer),
|
|
@@ -654,7 +614,6 @@ function buildProjectedDashboardViewModel(snapshot, includeBodies, options) {
|
|
|
654
614
|
host: boundedText(route.host),
|
|
655
615
|
enabled: Boolean(route.enabled),
|
|
656
616
|
state: route.state,
|
|
657
|
-
compatibility: route.compatibility,
|
|
658
617
|
queueDepth: depth,
|
|
659
618
|
...(oldestQueuedAt === undefined ? {} : { oldestQueuedAt }),
|
|
660
619
|
...(age === undefined ? {} : { queueAgeMs: age }),
|
|
@@ -671,33 +630,32 @@ function buildProjectedDashboardViewModel(snapshot, includeBodies, options) {
|
|
|
671
630
|
.sort((left, right) => routePriority(left) - routePriority(right) ||
|
|
672
631
|
compareText(left.alias, right.alias))
|
|
673
632
|
.slice(0, DASHBOARD_MODEL_LIMITS.routes);
|
|
674
|
-
const
|
|
675
|
-
const
|
|
676
|
-
.map((
|
|
677
|
-
const
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
const
|
|
633
|
+
const routeByEndpoint = new Map(allRoutes.map((route) => [`${route.provider}\0${route.alias}`, route]));
|
|
634
|
+
const allConsentEdges = snapshot.consentEdges
|
|
635
|
+
.map((edge) => {
|
|
636
|
+
const endpoints = edge.endpoints.map((endpoint) => ({
|
|
637
|
+
alias: boundedText(endpoint.alias),
|
|
638
|
+
provider: endpoint.provider,
|
|
639
|
+
}));
|
|
640
|
+
const routes = endpoints.map((endpoint) => routeByEndpoint.get(`${endpoint.provider}\0${endpoint.alias}`));
|
|
641
|
+
const bothPresent = routes.every((route) => route !== undefined);
|
|
682
642
|
return {
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
host: boundedText(pair.host),
|
|
643
|
+
endpoints,
|
|
644
|
+
host: boundedText(edge.host),
|
|
686
645
|
state: bothPresent &&
|
|
687
|
-
|
|
688
|
-
!quarantinedProviders.has("codex") &&
|
|
689
|
-
routeIsReady(claudeRoute) &&
|
|
690
|
-
routeIsReady(codexRoute)
|
|
646
|
+
routes.every((route) => route !== undefined && routeIsReady(route))
|
|
691
647
|
? "ready"
|
|
692
648
|
: bothPresent
|
|
693
649
|
? "degraded"
|
|
694
650
|
: "unavailable",
|
|
695
|
-
counters: normalizedCounters(
|
|
651
|
+
counters: normalizedCounters(edge.counters),
|
|
696
652
|
};
|
|
697
653
|
})
|
|
698
|
-
.sort((left, right) => compareText(left.
|
|
699
|
-
compareText(left.
|
|
700
|
-
|
|
654
|
+
.sort((left, right) => compareText(left.endpoints[0].provider, right.endpoints[0].provider) ||
|
|
655
|
+
compareText(left.endpoints[0].alias, right.endpoints[0].alias) ||
|
|
656
|
+
compareText(left.endpoints[1].provider, right.endpoints[1].provider) ||
|
|
657
|
+
compareText(left.endpoints[1].alias, right.endpoints[1].alias));
|
|
658
|
+
const consentEdges = allConsentEdges.slice(0, DASHBOARD_MODEL_LIMITS.consentEdges);
|
|
701
659
|
const allWatches = (snapshot.progressWatches ?? [])
|
|
702
660
|
.flatMap((watch) => {
|
|
703
661
|
const lastActivityAt = normalizedTimestamp(watch.lastActivityAt);
|
|
@@ -763,7 +721,6 @@ function buildProjectedDashboardViewModel(snapshot, includeBodies, options) {
|
|
|
763
721
|
provider: connector.provider,
|
|
764
722
|
host: boundedText(connector.host),
|
|
765
723
|
health: connector.health,
|
|
766
|
-
compatibility: connector.compatibility,
|
|
767
724
|
...(safeProtocol(connector.protocol) === undefined
|
|
768
725
|
? {}
|
|
769
726
|
: { protocol: safeProtocol(connector.protocol) }),
|
|
@@ -773,6 +730,13 @@ function buildProjectedDashboardViewModel(snapshot, includeBodies, options) {
|
|
|
773
730
|
...(normalizedTimestamp(connector.lastSeenAt) === undefined
|
|
774
731
|
? {}
|
|
775
732
|
: { lastSeenAt: normalizedTimestamp(connector.lastSeenAt) }),
|
|
733
|
+
...(connector.codexDoctor === undefined
|
|
734
|
+
? {}
|
|
735
|
+
: {
|
|
736
|
+
codexDoctor: {
|
|
737
|
+
conditions: [...connector.codexDoctor.conditions],
|
|
738
|
+
},
|
|
739
|
+
}),
|
|
776
740
|
...(safeCode(connector.safeErrorCode) === undefined
|
|
777
741
|
? {}
|
|
778
742
|
: { safeErrorCode: safeCode(connector.safeErrorCode) }),
|
|
@@ -791,38 +755,6 @@ function buildProjectedDashboardViewModel(snapshot, includeBodies, options) {
|
|
|
791
755
|
})
|
|
792
756
|
.sort((left, right) => compareText(`${left.provider}\0${left.host}`, `${right.provider}\0${right.host}`))
|
|
793
757
|
.slice(0, DASHBOARD_MODEL_LIMITS.connectors);
|
|
794
|
-
const detectedCompatibilityChecks = (Array.isArray(snapshot.compatibilityChecks)
|
|
795
|
-
? snapshot.compatibilityChecks.filter(isPublicCompatibilityCheckSnapshot)
|
|
796
|
-
: [])
|
|
797
|
-
.map((attestation) => {
|
|
798
|
-
const failed = attestation.probes.find((probe) => probe.outcome === "fail");
|
|
799
|
-
const issue = safeCode(attestation.safeErrorCode);
|
|
800
|
-
return {
|
|
801
|
-
surface: attestation.surface,
|
|
802
|
-
version: boundedText(attestation.version),
|
|
803
|
-
...(attestation.testedVersion === undefined
|
|
804
|
-
? {}
|
|
805
|
-
: { testedVersion: boundedText(attestation.testedVersion) }),
|
|
806
|
-
...(attestation.supportedMajor === undefined
|
|
807
|
-
? {}
|
|
808
|
-
: { supportedMajor: boundedText(attestation.supportedMajor) }),
|
|
809
|
-
tier: attestation.tier,
|
|
810
|
-
writesCovered: attestation.writesCovered,
|
|
811
|
-
checkedAt: attestation.checkedAt,
|
|
812
|
-
...(failed === undefined ? {} : { failure: failed.name }),
|
|
813
|
-
...(issue === undefined ? {} : { safeErrorCode: issue }),
|
|
814
|
-
};
|
|
815
|
-
})
|
|
816
|
-
.sort((left, right) => compareText(left.surface, right.surface));
|
|
817
|
-
const detectedSurfaces = new Set(detectedCompatibilityChecks.map(({ surface }) => surface));
|
|
818
|
-
const compatibilityChecks = [
|
|
819
|
-
...detectedCompatibilityChecks,
|
|
820
|
-
...surfaceDefinitions
|
|
821
|
-
.filter(({ required, surface }) => !required && !detectedSurfaces.has(surface))
|
|
822
|
-
.map(({ surface }) => ({ surface, notDetected: true })),
|
|
823
|
-
]
|
|
824
|
-
.sort((left, right) => compareText(left.surface, right.surface))
|
|
825
|
-
.slice(0, surfaceDefinitions.length);
|
|
826
758
|
const messages = buildMessageGroups(snapshot.messages, includeBodies);
|
|
827
759
|
const queuedMessages = allRoutes.reduce((total, route) => boundedAdd(total, route.queueDepth), 0);
|
|
828
760
|
const queuedWithAge = allRoutes
|
|
@@ -830,69 +762,109 @@ function buildProjectedDashboardViewModel(snapshot, includeBodies, options) {
|
|
|
830
762
|
.sort((left, right) => right.queueAgeMs - left.queueAgeMs);
|
|
831
763
|
const oldest = queuedWithAge[0];
|
|
832
764
|
const activeDeliveries = messages.activeGroups;
|
|
833
|
-
const
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
const
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
const
|
|
842
|
-
const
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
765
|
+
const routesByProvider = Object.fromEntries(gatewayProviders.map((provider) => [
|
|
766
|
+
provider,
|
|
767
|
+
allRoutes.filter((route) => route.provider === provider),
|
|
768
|
+
]));
|
|
769
|
+
const readyByProvider = Object.fromEntries(gatewayProviders.map((provider) => [
|
|
770
|
+
provider,
|
|
771
|
+
routesByProvider[provider].filter(routeIsReady).length,
|
|
772
|
+
]));
|
|
773
|
+
const pairedAliasesByProvider = Object.fromEntries(gatewayProviders.map((provider) => [provider, new Set()]));
|
|
774
|
+
for (const edge of allConsentEdges) {
|
|
775
|
+
for (const endpoint of edge.endpoints) {
|
|
776
|
+
pairedAliasesByProvider[endpoint.provider].add(endpoint.alias);
|
|
777
|
+
}
|
|
778
|
+
}
|
|
779
|
+
const readyConsentEdges = allConsentEdges.filter((edge) => edge.state === "ready");
|
|
780
|
+
const consentEdgeCountIsLowerBound = (normalizedInteger(snapshot.truncation.consentEdges) ?? 0) > 0;
|
|
781
|
+
const hasConsentEvidence = allConsentEdges.length > 0 || consentEdgeCountIsLowerBound;
|
|
846
782
|
const graph = {
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
783
|
+
consentEdgeCount: allConsentEdges.length,
|
|
784
|
+
readyConsentEdgeCount: readyConsentEdges.length,
|
|
785
|
+
consentEdgeCountIsLowerBound,
|
|
786
|
+
unpairedReadyByProvider: Object.fromEntries(gatewayProviders.map((provider) => [
|
|
787
|
+
provider,
|
|
788
|
+
routesByProvider[provider].filter((route) => routeIsReady(route) &&
|
|
789
|
+
!pairedAliasesByProvider[provider].has(route.alias)).length,
|
|
790
|
+
])),
|
|
852
791
|
};
|
|
853
792
|
const selectedPeers = validPeers.filter((peer) => peer.selected && peerIsSelectable(peer));
|
|
854
793
|
const selectablePeers = validPeers.filter(peerIsSelectable);
|
|
855
|
-
const selectedClaudeCount =
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
(
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
const codexNextAction =
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
794
|
+
const selectedClaudeCount = Math.max(selectedPeers.filter((peer) => peer.provider === "claude").length, readyByProvider.claude);
|
|
795
|
+
const readyPairedByProvider = Object.fromEntries(gatewayProviders.map((provider) => [
|
|
796
|
+
provider,
|
|
797
|
+
routesByProvider[provider].some((route) => routeIsReady(route) &&
|
|
798
|
+
pairedAliasesByProvider[provider].has(route.alias)),
|
|
799
|
+
]));
|
|
800
|
+
const claudeNextAction = selectedClaudeCount > 0 &&
|
|
801
|
+
(readyPairedByProvider.claude ||
|
|
802
|
+
(consentEdgeCountIsLowerBound && readyByProvider.claude > 0))
|
|
803
|
+
? "none"
|
|
804
|
+
: readyByProvider.claude > 0
|
|
805
|
+
? "pair_routes"
|
|
806
|
+
: validPeers.length === 0
|
|
807
|
+
? "discover_claude"
|
|
808
|
+
: selectablePeers.length > 0
|
|
809
|
+
? !hasConsentEvidence
|
|
810
|
+
? "pair_routes"
|
|
811
|
+
: "restore_claude"
|
|
812
|
+
: selectedPeers.length > 0
|
|
813
|
+
? "restore_claude"
|
|
814
|
+
: "repair_claude_inventory";
|
|
815
|
+
const codexNextAction = readyPairedByProvider.codex ||
|
|
816
|
+
(consentEdgeCountIsLowerBound && readyByProvider.codex > 0)
|
|
817
|
+
? "none"
|
|
818
|
+
: readyByProvider.codex > 0
|
|
819
|
+
? "pair_routes"
|
|
820
|
+
: routesByProvider.codex.length === 0
|
|
821
|
+
? "register_codex"
|
|
822
|
+
: "restore_codex";
|
|
823
|
+
const parties = gatewayProviders.map((provider) => {
|
|
824
|
+
const providerPeers = validPeers.filter((peer) => peer.provider === provider);
|
|
825
|
+
const providerRoutes = routesByProvider[provider];
|
|
826
|
+
const ready = provider === "claude"
|
|
827
|
+
? selectedClaudeCount
|
|
828
|
+
: readyByProvider[provider];
|
|
829
|
+
const nextAction = provider === "claude"
|
|
830
|
+
? claudeNextAction
|
|
831
|
+
: provider === "codex"
|
|
832
|
+
? codexNextAction
|
|
833
|
+
: readyPairedByProvider[provider]
|
|
834
|
+
? "none"
|
|
835
|
+
: ready > 0
|
|
836
|
+
? "pair_routes"
|
|
837
|
+
: "none";
|
|
838
|
+
return {
|
|
839
|
+
kind: provider,
|
|
840
|
+
status: provider === "claude" && ready > 0 && partyStatus(providerRoutes, ready) === "missing"
|
|
841
|
+
? "attention"
|
|
842
|
+
: partyStatus(providerRoutes, ready),
|
|
843
|
+
total: provider === "claude" ? providerPeers.length : providerRoutes.length,
|
|
844
|
+
ready,
|
|
845
|
+
...(provider === "claude"
|
|
846
|
+
? { selectable: selectablePeers.filter((peer) => peer.provider === provider).length }
|
|
847
|
+
: {}),
|
|
848
|
+
countIsLowerBound: (normalizedInteger(snapshot.truncation.routes) ?? 0) > 0 ||
|
|
849
|
+
(provider === "claude" &&
|
|
850
|
+
(normalizedInteger(snapshot.truncation.availablePeers) ?? 0) > 0),
|
|
851
|
+
...(providerPeers.find((peer) => peer.selected)?.alias === undefined
|
|
852
|
+
? providerRoutes[0]?.alias === undefined
|
|
853
|
+
? {}
|
|
854
|
+
: { primaryAlias: boundedText(providerRoutes[0].alias) }
|
|
855
|
+
: { primaryAlias: boundedText(providerPeers.find((peer) => peer.selected).alias) }),
|
|
856
|
+
nextAction,
|
|
857
|
+
};
|
|
858
|
+
});
|
|
885
859
|
const explicitAlerts = coalesceCodexReactivationAlerts(snapshot.alerts
|
|
886
|
-
.filter((alert) => alert.code !== "COMPATIBILITY_CERTIFICATION_FAILED")
|
|
887
860
|
.map((alert) => {
|
|
888
861
|
const code = safeCode(alert.code);
|
|
889
|
-
const
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
? routeByAlias.get(alert.alias)
|
|
862
|
+
const route = code === "QUEUE_STALLED" && typeof alert.alias === "string" &&
|
|
863
|
+
alert.provider !== undefined
|
|
864
|
+
? routeByEndpoint.get(`${alert.provider}\0${alert.alias}`)
|
|
893
865
|
: undefined;
|
|
894
|
-
const alertRoute = typeof alert.alias === "string"
|
|
895
|
-
?
|
|
866
|
+
const alertRoute = typeof alert.alias === "string" && alert.provider !== undefined
|
|
867
|
+
? routeByEndpoint.get(`${alert.provider}\0${alert.alias}`)
|
|
896
868
|
: undefined;
|
|
897
869
|
return {
|
|
898
870
|
kind: "alert",
|
|
@@ -905,9 +877,7 @@ function buildProjectedDashboardViewModel(snapshot, includeBodies, options) {
|
|
|
905
877
|
...(normalizedTimestamp(alert.timestamp) === undefined
|
|
906
878
|
? {}
|
|
907
879
|
: { timestamp: normalizedTimestamp(alert.timestamp) }),
|
|
908
|
-
...(alert.provider ===
|
|
909
|
-
? { provider: alert.provider }
|
|
910
|
-
: {}),
|
|
880
|
+
...(alert.provider === undefined ? {} : { provider: alert.provider }),
|
|
911
881
|
...(typeof alert.alias === "string"
|
|
912
882
|
? { alias: boundedText(alert.alias) }
|
|
913
883
|
: {}),
|
|
@@ -926,29 +896,19 @@ function buildProjectedDashboardViewModel(snapshot, includeBodies, options) {
|
|
|
926
896
|
guidance: alertRoute !== undefined &&
|
|
927
897
|
needsCodexAppReconnect(alertRoute, connectors, generatedAt)
|
|
928
898
|
? "codex_app_reconnect_required"
|
|
929
|
-
:
|
|
930
|
-
alert.provider !== undefined &&
|
|
931
|
-
quarantinedProviders.has(alert.provider)
|
|
932
|
-
? "provider_incompatible"
|
|
933
|
-
: providerWritesCovered &&
|
|
934
|
-
guidanceFor(code, alert.provider) === "provider_incompatible"
|
|
935
|
-
? "degraded"
|
|
936
|
-
: guidanceFor(code, alert.provider),
|
|
899
|
+
: guidanceFor(code, alert.provider),
|
|
937
900
|
};
|
|
938
901
|
}), allRoutes).sort((left, right) => alertPriority(left.severity) - alertPriority(right.severity) ||
|
|
939
902
|
compareText(right.timestamp ?? "", left.timestamp ?? ""));
|
|
940
903
|
const attentionCandidates = [...explicitAlerts];
|
|
941
904
|
const unavailableConsentEdgeScopes = new Set();
|
|
942
|
-
for (const
|
|
943
|
-
if (
|
|
905
|
+
for (const edge of allConsentEdges) {
|
|
906
|
+
if (edge.state !== "unavailable")
|
|
944
907
|
continue;
|
|
945
|
-
for (const endpoint of
|
|
946
|
-
{
|
|
947
|
-
{ provider: "codex", alias: pair.codexAlias },
|
|
948
|
-
]) {
|
|
949
|
-
if (routeByAlias.has(endpoint.alias))
|
|
908
|
+
for (const endpoint of edge.endpoints) {
|
|
909
|
+
if (routeByEndpoint.has(`${endpoint.provider}\0${endpoint.alias}`))
|
|
950
910
|
continue;
|
|
951
|
-
const scope = `${endpoint.provider}\0${endpoint.alias}\0${
|
|
911
|
+
const scope = `${endpoint.provider}\0${endpoint.alias}\0${edge.host}`;
|
|
952
912
|
if (unavailableConsentEdgeScopes.has(scope))
|
|
953
913
|
continue;
|
|
954
914
|
unavailableConsentEdgeScopes.add(scope);
|
|
@@ -957,13 +917,13 @@ function buildProjectedDashboardViewModel(snapshot, includeBodies, options) {
|
|
|
957
917
|
severity: "warning",
|
|
958
918
|
provider: endpoint.provider,
|
|
959
919
|
alias: endpoint.alias,
|
|
960
|
-
host:
|
|
920
|
+
host: edge.host,
|
|
961
921
|
guidance: "consent_edge_unavailable",
|
|
962
922
|
});
|
|
963
923
|
}
|
|
964
924
|
}
|
|
965
925
|
for (const route of allRoutes) {
|
|
966
|
-
const writes = messages.groups.filter(({ direction, targetAlias, state, timestamp }) => direction === "
|
|
926
|
+
const writes = messages.groups.filter(({ direction, targetAlias, state, timestamp }) => parseDirection(direction)?.targetProvider === "claude" &&
|
|
967
927
|
targetAlias === route.alias && state === "delivered" &&
|
|
968
928
|
timestamp !== undefined);
|
|
969
929
|
// Groups arrive newest-first. The oldest unconsumed write owns the notice:
|
|
@@ -1008,9 +968,7 @@ function buildProjectedDashboardViewModel(snapshot, includeBodies, options) {
|
|
|
1008
968
|
}
|
|
1009
969
|
const representedScopes = new Set(attentionCandidates.map((item) => `${item.provider ?? ""}\0${item.alias ?? ""}\0${item.host ?? ""}`));
|
|
1010
970
|
for (const route of allRoutes) {
|
|
1011
|
-
if (routeIsReady(route)
|
|
1012
|
-
quarantinedProviders.has(route.provider) ||
|
|
1013
|
-
route.safeErrorCode === "CODEX_WRITES_DISABLED") {
|
|
971
|
+
if (routeIsReady(route)) {
|
|
1014
972
|
continue;
|
|
1015
973
|
}
|
|
1016
974
|
const scope = `${route.provider}\0${route.alias}\0${route.host}`;
|
|
@@ -1019,9 +977,7 @@ function buildProjectedDashboardViewModel(snapshot, includeBodies, options) {
|
|
|
1019
977
|
attentionCandidates.push({
|
|
1020
978
|
kind: "route",
|
|
1021
979
|
...(route.safeErrorCode === undefined ? {} : { code: route.safeErrorCode }),
|
|
1022
|
-
severity: route.state === "offline"
|
|
1023
|
-
? "error"
|
|
1024
|
-
: "warning",
|
|
980
|
+
severity: route.state === "offline" ? "error" : "warning",
|
|
1025
981
|
...(route.lastSeenAt === undefined ? {} : { timestamp: route.lastSeenAt }),
|
|
1026
982
|
provider: route.provider,
|
|
1027
983
|
alias: route.alias,
|
|
@@ -1039,10 +995,8 @@ function buildProjectedDashboardViewModel(snapshot, includeBodies, options) {
|
|
|
1039
995
|
representedScopes.add(scope);
|
|
1040
996
|
}
|
|
1041
997
|
for (const connector of connectors) {
|
|
1042
|
-
if (
|
|
1043
|
-
connector.compatibility === "compatible")) {
|
|
998
|
+
if (connector.health === "healthy")
|
|
1044
999
|
continue;
|
|
1045
|
-
}
|
|
1046
1000
|
const scope = `${connector.provider}\0\0${connector.host}`;
|
|
1047
1001
|
if (representedScopes.has(scope))
|
|
1048
1002
|
continue;
|
|
@@ -1059,17 +1013,7 @@ function buildProjectedDashboardViewModel(snapshot, includeBodies, options) {
|
|
|
1059
1013
|
host: connector.host,
|
|
1060
1014
|
guidance: connector.health === "offline"
|
|
1061
1015
|
? "connector_offline"
|
|
1062
|
-
:
|
|
1063
|
-
(quarantinedProviders.has(connector.provider) ||
|
|
1064
|
-
connector.safeErrorCode === "CLAUDE_PEER_VERSION_UNSUPPORTED" ||
|
|
1065
|
-
connector.safeErrorCode === "CLAUDE_VERSION_UNPARSEABLE" ||
|
|
1066
|
-
connector.safeErrorCode === "CLAUDE_VERSION_CHECK_FAILED" ||
|
|
1067
|
-
connector.safeErrorCode === "CLAUDE_VERSION_EVIDENCE_CONFLICT" ||
|
|
1068
|
-
connector.safeErrorCode === "CLAUDE_VERSION_EVIDENCE_TOO_LARGE" ||
|
|
1069
|
-
connector.safeErrorCode === "CODEX_APP_SERVER_VERSION_UNSUPPORTED" ||
|
|
1070
|
-
connector.safeErrorCode === "CODEX_APP_SERVER_VERSION_UNPARSEABLE")
|
|
1071
|
-
? "provider_incompatible"
|
|
1072
|
-
: "degraded",
|
|
1016
|
+
: "degraded",
|
|
1073
1017
|
});
|
|
1074
1018
|
representedScopes.add(scope);
|
|
1075
1019
|
}
|
|
@@ -1112,10 +1056,7 @@ function buildProjectedDashboardViewModel(snapshot, includeBodies, options) {
|
|
|
1112
1056
|
guidance: "registry_empty",
|
|
1113
1057
|
});
|
|
1114
1058
|
}
|
|
1115
|
-
if ((snapshot.health === "degraded" ||
|
|
1116
|
-
snapshot.health === "offline" ||
|
|
1117
|
-
snapshot.health === "incompatible") &&
|
|
1118
|
-
!(snapshot.health === "degraded" && quarantinedProviders.size > 0) &&
|
|
1059
|
+
if ((snapshot.health === "degraded" || snapshot.health === "offline") &&
|
|
1119
1060
|
!attentionCandidates.some((item) => item.kind === "broker")) {
|
|
1120
1061
|
attentionCandidates.push({
|
|
1121
1062
|
kind: "broker",
|
|
@@ -1124,11 +1065,11 @@ function buildProjectedDashboardViewModel(snapshot, includeBodies, options) {
|
|
|
1124
1065
|
});
|
|
1125
1066
|
}
|
|
1126
1067
|
const attention = attentionCandidates.slice(0, DASHBOARD_MODEL_LIMITS.alerts);
|
|
1127
|
-
const setupComplete =
|
|
1128
|
-
const
|
|
1068
|
+
const setupComplete = hasConsentEvidence;
|
|
1069
|
+
const hasDegradedConsentEdge = allConsentEdges.some((edge) => edge.state !== "ready");
|
|
1129
1070
|
const overall = attentionCandidates.length > 0 ||
|
|
1130
1071
|
(normalizedInteger(snapshot.truncation.alerts) ?? 0) > 0 ||
|
|
1131
|
-
|
|
1072
|
+
hasDegradedConsentEdge
|
|
1132
1073
|
? "attention"
|
|
1133
1074
|
: setupComplete
|
|
1134
1075
|
? "ready"
|
|
@@ -1137,7 +1078,7 @@ function buildProjectedDashboardViewModel(snapshot, includeBodies, options) {
|
|
|
1137
1078
|
connectors: boundedAdd(normalizedInteger(snapshot.truncation.connectors) ?? 0, Math.max(0, snapshot.connectors.length - DASHBOARD_MODEL_LIMITS.connectors)),
|
|
1138
1079
|
availablePeers: boundedAdd(normalizedInteger(snapshot.truncation.availablePeers) ?? 0, Math.max(0, validPeers.length - DASHBOARD_MODEL_LIMITS.availablePeers)),
|
|
1139
1080
|
routes: boundedAdd(normalizedInteger(snapshot.truncation.routes) ?? 0, Math.max(0, allRoutes.length - DASHBOARD_MODEL_LIMITS.routes)),
|
|
1140
|
-
|
|
1081
|
+
consentEdges: boundedAdd(normalizedInteger(snapshot.truncation.consentEdges) ?? 0, Math.max(0, allConsentEdges.length - DASHBOARD_MODEL_LIMITS.consentEdges)),
|
|
1141
1082
|
progressWatches: boundedAdd(normalizedInteger(snapshot.truncation.progressWatches) ?? 0, Math.max(0, allWatches.length - DASHBOARD_MODEL_LIMITS.progressWatches)),
|
|
1142
1083
|
upstreamProgressWatchEvents: normalizedInteger(snapshot.truncation.progressWatchEvents) ?? 0,
|
|
1143
1084
|
progressWatchEvents: Math.max(0, allWatchEvents.length - DASHBOARD_MODEL_LIMITS.progressWatchEvents),
|
|
@@ -1150,43 +1091,13 @@ function buildProjectedDashboardViewModel(snapshot, includeBodies, options) {
|
|
|
1150
1091
|
activityEvents: Math.max(0, allBrokerActivity.length - DASHBOARD_MODEL_LIMITS.activityEvents),
|
|
1151
1092
|
};
|
|
1152
1093
|
return {
|
|
1153
|
-
schemaVersion:
|
|
1094
|
+
schemaVersion: 2,
|
|
1154
1095
|
...(generatedAt === undefined ? {} : { generatedAt }),
|
|
1155
1096
|
inboundMode,
|
|
1156
1097
|
health: snapshot.health,
|
|
1157
1098
|
overall,
|
|
1158
1099
|
exchange: {
|
|
1159
|
-
|
|
1160
|
-
kind: "claude",
|
|
1161
|
-
status: selectedClaudeCount > 0 && claudeStatus === "missing"
|
|
1162
|
-
? "attention"
|
|
1163
|
-
: claudeStatus,
|
|
1164
|
-
total: validPeers.length,
|
|
1165
|
-
ready: selectedClaudeCount,
|
|
1166
|
-
selectable: selectablePeers.length,
|
|
1167
|
-
countIsLowerBound: (normalizedInteger(snapshot.truncation.availablePeers) ?? 0) > 0 ||
|
|
1168
|
-
(normalizedInteger(snapshot.truncation.routes) ?? 0) > 0,
|
|
1169
|
-
...(selectedPeers[0]?.alias === undefined
|
|
1170
|
-
? claudeRoutes[0]?.alias === undefined
|
|
1171
|
-
? {}
|
|
1172
|
-
: { primaryAlias: boundedText(claudeRoutes[0].alias) }
|
|
1173
|
-
: { primaryAlias: boundedText(selectedPeers[0].alias) }),
|
|
1174
|
-
nextAction: claudeNextAction,
|
|
1175
|
-
},
|
|
1176
|
-
codex: {
|
|
1177
|
-
kind: "codex",
|
|
1178
|
-
status: codexStatus,
|
|
1179
|
-
total: codexRoutes.length,
|
|
1180
|
-
ready: readyCodex,
|
|
1181
|
-
...(monitorOnlyCodexRoutes.length === 0
|
|
1182
|
-
? {}
|
|
1183
|
-
: { monitorOnly: monitorOnlyCodexRoutes.length }),
|
|
1184
|
-
countIsLowerBound: (normalizedInteger(snapshot.truncation.routes) ?? 0) > 0,
|
|
1185
|
-
...(codexRoutes[0]?.alias === undefined
|
|
1186
|
-
? {}
|
|
1187
|
-
: { primaryAlias: codexRoutes[0].alias }),
|
|
1188
|
-
nextAction: codexNextAction,
|
|
1189
|
-
},
|
|
1100
|
+
parties,
|
|
1190
1101
|
queuedMessages,
|
|
1191
1102
|
queueCountIsLowerBound: (normalizedInteger(snapshot.truncation.routes) ?? 0) > 0,
|
|
1192
1103
|
...(oldest === undefined
|
|
@@ -1213,12 +1124,11 @@ function buildProjectedDashboardViewModel(snapshot, includeBodies, options) {
|
|
|
1213
1124
|
brokerActivity,
|
|
1214
1125
|
peers,
|
|
1215
1126
|
routes,
|
|
1216
|
-
|
|
1127
|
+
consentEdges,
|
|
1217
1128
|
watches,
|
|
1218
1129
|
watchEvents,
|
|
1219
1130
|
graph,
|
|
1220
1131
|
connectors,
|
|
1221
|
-
compatibilityChecks,
|
|
1222
1132
|
accounting: {
|
|
1223
1133
|
accepted: normalizedInteger(snapshot.accounting.accepted) ?? 0,
|
|
1224
1134
|
duplicates: normalizedInteger(snapshot.accounting.duplicates) ?? 0,
|