agent-embassy 1.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.
Files changed (145) hide show
  1. package/CHANGELOG.md +61 -0
  2. package/CONTRIBUTING.md +122 -0
  3. package/LICENSE +21 -0
  4. package/README.md +185 -0
  5. package/README.zh-CN.md +185 -0
  6. package/SECURITY.md +224 -0
  7. package/assets/live-dashboard/app.css +1610 -0
  8. package/assets/mark-seal.svg +7 -0
  9. package/assets/mark.svg +7 -0
  10. package/assets/social-preview.png +0 -0
  11. package/assets/vendor/react/LICENSE +21 -0
  12. package/assets/vendor/react/react-dom.production.min.js +267 -0
  13. package/assets/vendor/react/react.production.min.js +31 -0
  14. package/dist/src/errors.d.ts +5 -0
  15. package/dist/src/errors.js +11 -0
  16. package/dist/src/errors.js.map +1 -0
  17. package/dist/src/gateway/claude-compatibility-scratch.d.ts +18 -0
  18. package/dist/src/gateway/claude-compatibility-scratch.js +158 -0
  19. package/dist/src/gateway/claude-compatibility-scratch.js.map +1 -0
  20. package/dist/src/gateway/claude-helper-client.d.ts +43 -0
  21. package/dist/src/gateway/claude-helper-client.js +212 -0
  22. package/dist/src/gateway/claude-helper-client.js.map +1 -0
  23. package/dist/src/gateway/claude-helper-protocol.d.ts +172 -0
  24. package/dist/src/gateway/claude-helper-protocol.js +373 -0
  25. package/dist/src/gateway/claude-helper-protocol.js.map +1 -0
  26. package/dist/src/gateway/claude-helper-supervisor.d.ts +74 -0
  27. package/dist/src/gateway/claude-helper-supervisor.js +497 -0
  28. package/dist/src/gateway/claude-helper-supervisor.js.map +1 -0
  29. package/dist/src/gateway/claude-helper.d.ts +1 -0
  30. package/dist/src/gateway/claude-helper.js +269 -0
  31. package/dist/src/gateway/claude-helper.js.map +1 -0
  32. package/dist/src/gateway/claude-peer.d.ts +273 -0
  33. package/dist/src/gateway/claude-peer.js +2359 -0
  34. package/dist/src/gateway/claude-peer.js.map +1 -0
  35. package/dist/src/gateway/claude-runtime.d.ts +40 -0
  36. package/dist/src/gateway/claude-runtime.js +302 -0
  37. package/dist/src/gateway/claude-runtime.js.map +1 -0
  38. package/dist/src/gateway/cli-copy.d.ts +8 -0
  39. package/dist/src/gateway/cli-copy.en.d.ts +13 -0
  40. package/dist/src/gateway/cli-copy.en.js +43 -0
  41. package/dist/src/gateway/cli-copy.en.js.map +1 -0
  42. package/dist/src/gateway/cli-copy.js +19 -0
  43. package/dist/src/gateway/cli-copy.js.map +1 -0
  44. package/dist/src/gateway/cli-copy.zh-CN.d.ts +13 -0
  45. package/dist/src/gateway/cli-copy.zh-CN.js +43 -0
  46. package/dist/src/gateway/cli-copy.zh-CN.js.map +1 -0
  47. package/dist/src/gateway/cli.d.ts +44 -0
  48. package/dist/src/gateway/cli.js +969 -0
  49. package/dist/src/gateway/cli.js.map +1 -0
  50. package/dist/src/gateway/codex-app-server.d.ts +310 -0
  51. package/dist/src/gateway/codex-app-server.js +1746 -0
  52. package/dist/src/gateway/codex-app-server.js.map +1 -0
  53. package/dist/src/gateway/codex-local-transport.d.ts +81 -0
  54. package/dist/src/gateway/codex-local-transport.js +617 -0
  55. package/dist/src/gateway/codex-local-transport.js.map +1 -0
  56. package/dist/src/gateway/codex-registration-generation.d.ts +5 -0
  57. package/dist/src/gateway/codex-registration-generation.js +19 -0
  58. package/dist/src/gateway/codex-registration-generation.js.map +1 -0
  59. package/dist/src/gateway/codex-registration-succession.d.ts +209 -0
  60. package/dist/src/gateway/codex-registration-succession.js +588 -0
  61. package/dist/src/gateway/codex-registration-succession.js.map +1 -0
  62. package/dist/src/gateway/compatibility.d.ts +70 -0
  63. package/dist/src/gateway/compatibility.js +252 -0
  64. package/dist/src/gateway/compatibility.js.map +1 -0
  65. package/dist/src/gateway/config.d.ts +24 -0
  66. package/dist/src/gateway/config.js +130 -0
  67. package/dist/src/gateway/config.js.map +1 -0
  68. package/dist/src/gateway/control.d.ts +308 -0
  69. package/dist/src/gateway/control.js +1573 -0
  70. package/dist/src/gateway/control.js.map +1 -0
  71. package/dist/src/gateway/dashboard-copy.d.ts +7 -0
  72. package/dist/src/gateway/dashboard-copy.en.d.ts +506 -0
  73. package/dist/src/gateway/dashboard-copy.en.js +507 -0
  74. package/dist/src/gateway/dashboard-copy.en.js.map +1 -0
  75. package/dist/src/gateway/dashboard-copy.js +516 -0
  76. package/dist/src/gateway/dashboard-copy.js.map +1 -0
  77. package/dist/src/gateway/dashboard-copy.zh-CN.d.ts +506 -0
  78. package/dist/src/gateway/dashboard-copy.zh-CN.js +507 -0
  79. package/dist/src/gateway/dashboard-copy.zh-CN.js.map +1 -0
  80. package/dist/src/gateway/dashboard-model.d.ts +207 -0
  81. package/dist/src/gateway/dashboard-model.js +720 -0
  82. package/dist/src/gateway/dashboard-model.js.map +1 -0
  83. package/dist/src/gateway/dashboard.d.ts +26 -0
  84. package/dist/src/gateway/dashboard.js +941 -0
  85. package/dist/src/gateway/dashboard.js.map +1 -0
  86. package/dist/src/gateway/delivery-machine.d.ts +235 -0
  87. package/dist/src/gateway/delivery-machine.js +540 -0
  88. package/dist/src/gateway/delivery-machine.js.map +1 -0
  89. package/dist/src/gateway/instance-lease.d.ts +21 -0
  90. package/dist/src/gateway/instance-lease.js +549 -0
  91. package/dist/src/gateway/instance-lease.js.map +1 -0
  92. package/dist/src/gateway/live-dashboard-app/app.js +2914 -0
  93. package/dist/src/gateway/live-dashboard-assets.d.ts +10 -0
  94. package/dist/src/gateway/live-dashboard-assets.js +73 -0
  95. package/dist/src/gateway/live-dashboard-assets.js.map +1 -0
  96. package/dist/src/gateway/live-dashboard-bootstrap.d.ts +36 -0
  97. package/dist/src/gateway/live-dashboard-bootstrap.js +176 -0
  98. package/dist/src/gateway/live-dashboard-bootstrap.js.map +1 -0
  99. package/dist/src/gateway/live-dashboard-command.d.ts +54 -0
  100. package/dist/src/gateway/live-dashboard-command.js +324 -0
  101. package/dist/src/gateway/live-dashboard-command.js.map +1 -0
  102. package/dist/src/gateway/live-dashboard-http.d.ts +41 -0
  103. package/dist/src/gateway/live-dashboard-http.js +445 -0
  104. package/dist/src/gateway/live-dashboard-http.js.map +1 -0
  105. package/dist/src/gateway/live-dashboard-protocol.d.ts +38 -0
  106. package/dist/src/gateway/live-dashboard-protocol.js +156 -0
  107. package/dist/src/gateway/live-dashboard-protocol.js.map +1 -0
  108. package/dist/src/gateway/live-dashboard-server.d.ts +33 -0
  109. package/dist/src/gateway/live-dashboard-server.js +130 -0
  110. package/dist/src/gateway/live-dashboard-server.js.map +1 -0
  111. package/dist/src/gateway/live-dashboard-stream.d.ts +46 -0
  112. package/dist/src/gateway/live-dashboard-stream.js +233 -0
  113. package/dist/src/gateway/live-dashboard-stream.js.map +1 -0
  114. package/dist/src/gateway/live-dashboard.d.ts +31 -0
  115. package/dist/src/gateway/live-dashboard.js +190 -0
  116. package/dist/src/gateway/live-dashboard.js.map +1 -0
  117. package/dist/src/gateway/locale.d.ts +4 -0
  118. package/dist/src/gateway/locale.js +10 -0
  119. package/dist/src/gateway/locale.js.map +1 -0
  120. package/dist/src/gateway/progress-watch-machine.d.ts +97 -0
  121. package/dist/src/gateway/progress-watch-machine.js +172 -0
  122. package/dist/src/gateway/progress-watch-machine.js.map +1 -0
  123. package/dist/src/gateway/providers.d.ts +287 -0
  124. package/dist/src/gateway/providers.js +2451 -0
  125. package/dist/src/gateway/providers.js.map +1 -0
  126. package/dist/src/gateway/server.d.ts +57 -0
  127. package/dist/src/gateway/server.js +264 -0
  128. package/dist/src/gateway/server.js.map +1 -0
  129. package/dist/src/gateway/service.d.ts +449 -0
  130. package/dist/src/gateway/service.js +4831 -0
  131. package/dist/src/gateway/service.js.map +1 -0
  132. package/dist/src/gateway/store.d.ts +340 -0
  133. package/dist/src/gateway/store.js +4047 -0
  134. package/dist/src/gateway/store.js.map +1 -0
  135. package/dist/src/gateway/types.d.ts +514 -0
  136. package/dist/src/gateway/types.js +414 -0
  137. package/dist/src/gateway/types.js.map +1 -0
  138. package/dist/src/mutex.d.ts +4 -0
  139. package/dist/src/mutex.js +23 -0
  140. package/dist/src/mutex.js.map +1 -0
  141. package/docs/DESIGN.md +134 -0
  142. package/docs/GATEWAY-ARCHITECTURE.md +858 -0
  143. package/package.json +80 -0
  144. package/skills/embassy-peer/SKILL.md +198 -0
  145. package/skills/embassy-peer/agents/openai.yaml +4 -0
@@ -0,0 +1,720 @@
1
+ import { arePublicAvailablePeerSnapshots } from "./types.js";
2
+ import { isCompatibilityAttestation, } from "./compatibility.js";
3
+ export const DASHBOARD_MODEL_LIMITS = Object.freeze({
4
+ connectors: 16,
5
+ availablePeers: 64,
6
+ routes: 128,
7
+ pairs: 64,
8
+ messages: 50,
9
+ messageEvents: 60,
10
+ progressWatches: 64,
11
+ progressWatchEvents: 64,
12
+ activityEvents: 64,
13
+ alerts: 32,
14
+ });
15
+ const SAFE_CODE_PATTERN = /^[A-Z][A-Z0-9_]{0,63}$/;
16
+ const SAFE_PROTOCOL_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._+:/-]{0,47}$/;
17
+ const OPAQUE_SUFFIX_PATTERN = /^[a-f0-9]{6,12}$/i;
18
+ const CONVERSATION_SUFFIX_PATTERN = /^[A-Za-z0-9_-]{8}$/;
19
+ function boundedText(value, maximumCharacters = 96) {
20
+ if (typeof value !== "string")
21
+ return "";
22
+ const characters = Array.from(value);
23
+ return characters.length <= maximumCharacters
24
+ ? value
25
+ : `${characters.slice(0, maximumCharacters - 1).join("")}\u2026`;
26
+ }
27
+ function safeCode(value) {
28
+ return typeof value === "string" && SAFE_CODE_PATTERN.test(value)
29
+ ? value
30
+ : undefined;
31
+ }
32
+ function safeProtocol(value) {
33
+ return typeof value === "string" && SAFE_PROTOCOL_PATTERN.test(value)
34
+ ? value
35
+ : undefined;
36
+ }
37
+ function normalizedInteger(value) {
38
+ return typeof value === "number" &&
39
+ Number.isSafeInteger(value) &&
40
+ value >= 0
41
+ ? value
42
+ : undefined;
43
+ }
44
+ function normalizedTimestamp(value) {
45
+ if (typeof value !== "string")
46
+ return undefined;
47
+ const milliseconds = Date.parse(value);
48
+ return Number.isFinite(milliseconds)
49
+ ? new Date(milliseconds).toISOString()
50
+ : undefined;
51
+ }
52
+ function compareText(left, right) {
53
+ return left < right ? -1 : left > right ? 1 : 0;
54
+ }
55
+ function boundedAdd(left, right) {
56
+ return Math.min(Number.MAX_SAFE_INTEGER, left + right);
57
+ }
58
+ function normalizedCounters(counters) {
59
+ return {
60
+ accepted: normalizedInteger(counters?.accepted) ?? 0,
61
+ delivered: normalizedInteger(counters?.delivered) ?? 0,
62
+ unconfirmed: normalizedInteger(counters?.unconfirmed) ?? 0,
63
+ failed: normalizedInteger(counters?.failed) ?? 0,
64
+ ambiguous: normalizedInteger(counters?.ambiguous) ?? 0,
65
+ expired: normalizedInteger(counters?.expired) ?? 0,
66
+ cancelled: normalizedInteger(counters?.cancelled) ?? 0,
67
+ abandoned: normalizedInteger(counters?.abandoned) ?? 0,
68
+ rejected: normalizedInteger(counters?.rejected) ?? 0,
69
+ bytesAccepted: normalizedInteger(counters?.bytesAccepted) ?? 0,
70
+ };
71
+ }
72
+ function routeIsReady(route) {
73
+ return (route.enabled &&
74
+ route.compatibility === "compatible" &&
75
+ (route.state === "idle" || route.state === "busy"));
76
+ }
77
+ function peerSelectionGuidance(peer) {
78
+ if (peer.safeErrorCode === "PEER_ALIAS_COLLISION")
79
+ return "alias_collision";
80
+ if (peer.safeErrorCode === "PEER_SESSION_COLLISION")
81
+ return "session_collision";
82
+ if (peer.safeErrorCode === "PEER_DISCOVERY_INCOMPLETE") {
83
+ return "discovery_incomplete";
84
+ }
85
+ if (peer.state === "offline")
86
+ return "offline";
87
+ if (peer.state === "incompatible" ||
88
+ peer.compatibility !== "compatible" ||
89
+ peer.safeErrorCode !== undefined) {
90
+ return "incompatible";
91
+ }
92
+ return undefined;
93
+ }
94
+ function peerIsSelectable(peer) {
95
+ return peer.validated && peerSelectionGuidance(peer) === undefined;
96
+ }
97
+ function partyStatus(routes, ready) {
98
+ if (routes.length === 0)
99
+ return "missing";
100
+ if (ready === 0) {
101
+ return routes.some((route) => route.state === "awaiting_approval")
102
+ ? "waiting"
103
+ : "attention";
104
+ }
105
+ return routes.some((route) => route.state === "busy") ? "busy" : "ready";
106
+ }
107
+ function routePriority(route) {
108
+ if (!route.enabled || route.state === "offline" || route.state === "incompatible") {
109
+ return 0;
110
+ }
111
+ if (route.state === "stale" || route.compatibility !== "compatible")
112
+ return 1;
113
+ if (route.state === "awaiting_approval")
114
+ return 2;
115
+ if (route.state === "busy")
116
+ return 3;
117
+ return 4;
118
+ }
119
+ function alertPriority(severity) {
120
+ return severity === "error" ? 0 : severity === "warning" ? 1 : 2;
121
+ }
122
+ function guidanceFor(code, provider) {
123
+ if (code === "CODEX_SUCCESSION_BARRIER_BUSY") {
124
+ return "codex_succession_busy";
125
+ }
126
+ if (code?.startsWith("CODEX_SUCCESSION_") === true) {
127
+ return "codex_succession_recovery";
128
+ }
129
+ switch (code) {
130
+ case "REOBSERVATION_REQUIRED":
131
+ return provider === "claude" ? "reobserve_claude" : "reobserve_codex";
132
+ case "PEER_NOT_OBSERVED":
133
+ case "CLAUDE_PEER_NOT_OBSERVED":
134
+ return "claude_not_observed";
135
+ case "CODEX_ROUTE_STALE":
136
+ return "codex_stale";
137
+ case "CONNECTOR_OFFLINE":
138
+ return "connector_offline";
139
+ case "ROUTE_STALE":
140
+ return "route_stale";
141
+ case "QUEUE_STALLED":
142
+ return "queue_stalled";
143
+ case "CLAUDE_NATIVE_ACK_UNAVAILABLE":
144
+ case "CLAUDE_RECEIPT_UNCONFIRMED":
145
+ return "unconfirmed";
146
+ case "ADAPTER_DEGRADED":
147
+ case "ROUTE_DEGRADED":
148
+ return "degraded";
149
+ default:
150
+ return "generic";
151
+ }
152
+ }
153
+ function queueAge(depth, oldestQueuedAt, generatedAt) {
154
+ if (depth === 0 || oldestQueuedAt === undefined || generatedAt === undefined) {
155
+ return undefined;
156
+ }
157
+ const elapsed = Date.parse(generatedAt) - Date.parse(oldestQueuedAt);
158
+ return Number.isSafeInteger(elapsed) && elapsed >= 0 ? elapsed : undefined;
159
+ }
160
+ function messageKey(message) {
161
+ return [
162
+ message.direction,
163
+ boundedText(message.sourceAlias),
164
+ boundedText(message.targetAlias),
165
+ typeof message.messageIdSuffix === "string" ? message.messageIdSuffix : "",
166
+ ].join("\0");
167
+ }
168
+ function buildMessageGroups(messages) {
169
+ const grouped = new Map();
170
+ for (const message of messages) {
171
+ const key = messageKey(message);
172
+ const events = grouped.get(key);
173
+ if (events === undefined)
174
+ grouped.set(key, [message]);
175
+ else
176
+ events.push(message);
177
+ }
178
+ const all = [...grouped.values()]
179
+ .map((events) => {
180
+ const ordered = [...events].sort((left, right) => {
181
+ const byTime = compareText(normalizedTimestamp(left.timestamp) ?? "", normalizedTimestamp(right.timestamp) ?? "");
182
+ return byTime ||
183
+ (normalizedInteger(left.sequence) ?? 0) -
184
+ (normalizedInteger(right.sequence) ?? 0);
185
+ });
186
+ const latest = ordered[ordered.length - 1];
187
+ const allEvents = ordered.map((event) => ({
188
+ sequence: normalizedInteger(event.sequence) ?? 0,
189
+ ...(normalizedTimestamp(event.timestamp) === undefined
190
+ ? {}
191
+ : { timestamp: normalizedTimestamp(event.timestamp) }),
192
+ state: event.state,
193
+ ...(normalizedInteger(event.latencyMs) === undefined
194
+ ? {}
195
+ : { latencyMs: normalizedInteger(event.latencyMs) }),
196
+ ...(safeCode(event.safeErrorCode) === undefined
197
+ ? {}
198
+ : { safeErrorCode: safeCode(event.safeErrorCode) }),
199
+ ...(typeof event.conversationIdSuffix === "string" &&
200
+ CONVERSATION_SUFFIX_PATTERN.test(event.conversationIdSuffix)
201
+ ? { conversationIdSuffix: event.conversationIdSuffix }
202
+ : {}),
203
+ ...(event.steer === true ? { steer: true } : {}),
204
+ }));
205
+ return {
206
+ direction: latest.direction,
207
+ sourceAlias: boundedText(latest.sourceAlias),
208
+ targetAlias: boundedText(latest.targetAlias),
209
+ ...(typeof latest.messageIdSuffix === "string" &&
210
+ OPAQUE_SUFFIX_PATTERN.test(latest.messageIdSuffix)
211
+ ? { messageIdSuffix: latest.messageIdSuffix.toLowerCase() }
212
+ : {}),
213
+ ...(typeof latest.conversationIdSuffix === "string" &&
214
+ CONVERSATION_SUFFIX_PATTERN.test(latest.conversationIdSuffix)
215
+ ? { conversationIdSuffix: latest.conversationIdSuffix }
216
+ : {}),
217
+ state: latest.state,
218
+ ...(normalizedTimestamp(latest.timestamp) === undefined
219
+ ? {}
220
+ : { timestamp: normalizedTimestamp(latest.timestamp) }),
221
+ ...(normalizedInteger(latest.latencyMs) === undefined
222
+ ? {}
223
+ : { latencyMs: normalizedInteger(latest.latencyMs) }),
224
+ bytes: normalizedInteger(latest.bytes) ?? 0,
225
+ ...(safeCode(latest.safeErrorCode) === undefined
226
+ ? {}
227
+ : { safeErrorCode: safeCode(latest.safeErrorCode) }),
228
+ ...(latest.steer === true ? { steer: true } : {}),
229
+ events: allEvents,
230
+ };
231
+ })
232
+ .sort((left, right) => compareText(right.timestamp ?? "", left.timestamp ?? "") ||
233
+ compareText(left.sourceAlias, right.sourceAlias));
234
+ let eventBudget = DASHBOARD_MODEL_LIMITS.messageEvents;
235
+ let omittedEvents = 0;
236
+ const displayedGroups = all.slice(0, DASHBOARD_MODEL_LIMITS.messages);
237
+ const visibleGroups = displayedGroups.map((group, index) => {
238
+ const groupsAfterThis = displayedGroups.length - index - 1;
239
+ const availableForThis = Math.max(1, eventBudget - groupsAfterThis);
240
+ const retained = Math.min(availableForThis, group.events.length);
241
+ eventBudget -= retained;
242
+ omittedEvents += group.events.length - retained;
243
+ return {
244
+ ...group,
245
+ events: retained === 0 ? [] : group.events.slice(-retained),
246
+ };
247
+ });
248
+ return {
249
+ groups: visibleGroups,
250
+ omittedGroups: Math.max(0, all.length - DASHBOARD_MODEL_LIMITS.messages),
251
+ omittedEvents,
252
+ activeGroups: all.filter((message) => ["queued", "dispatching", "transport_written", "held"].includes(message.state)).length,
253
+ };
254
+ }
255
+ export function buildDashboardViewModel(snapshot) {
256
+ const generatedAt = normalizedTimestamp(snapshot.generatedAt);
257
+ const inboundMode = snapshot.inboundMode === "open" ? "open" : "paired";
258
+ const validPeers = arePublicAvailablePeerSnapshots(snapshot.availablePeers)
259
+ ? snapshot.availablePeers
260
+ : [];
261
+ const peers = validPeers
262
+ .map((peer) => ({
263
+ alias: boundedText(peer.alias),
264
+ host: boundedText(peer.host),
265
+ state: peer.state,
266
+ compatibility: peer.compatibility,
267
+ validated: peer.validated,
268
+ selected: peer.selected,
269
+ selectable: peerIsSelectable(peer),
270
+ ...(peerSelectionGuidance(peer) === undefined
271
+ ? {}
272
+ : { selectionGuidance: peerSelectionGuidance(peer) }),
273
+ ...(normalizedTimestamp(peer.lastSeenAt) === undefined
274
+ ? {}
275
+ : { lastSeenAt: normalizedTimestamp(peer.lastSeenAt) }),
276
+ ...(safeCode(peer.safeErrorCode) === undefined
277
+ ? {}
278
+ : { safeErrorCode: safeCode(peer.safeErrorCode) }),
279
+ }))
280
+ .sort((left, right) => {
281
+ if (left.selected !== right.selected)
282
+ return left.selected ? -1 : 1;
283
+ return compareText(left.alias, right.alias);
284
+ })
285
+ .slice(0, DASHBOARD_MODEL_LIMITS.availablePeers);
286
+ const allBrokerActivity = (snapshot.activityEvents ?? []).flatMap((event) => {
287
+ const timestamp = normalizedTimestamp(event.timestamp);
288
+ const sequence = normalizedInteger(event.sequence);
289
+ if (timestamp === undefined || sequence === undefined || sequence < 1) {
290
+ return [];
291
+ }
292
+ return [
293
+ {
294
+ sequence,
295
+ timestamp,
296
+ kind: event.kind,
297
+ action: event.action,
298
+ outcome: event.outcome,
299
+ aliases: event.aliases.map((alias) => boundedText(alias)).slice(0, 2),
300
+ ...(safeCode(event.safeErrorCode) === undefined
301
+ ? {}
302
+ : { safeErrorCode: safeCode(event.safeErrorCode) }),
303
+ },
304
+ ];
305
+ });
306
+ const brokerActivity = allBrokerActivity.slice(-DASHBOARD_MODEL_LIMITS.activityEvents);
307
+ const allRoutes = snapshot.routes.map((route) => {
308
+ const depth = normalizedInteger(route.queueDepth) ?? 0;
309
+ const oldestQueuedAt = normalizedTimestamp(route.oldestQueuedAt);
310
+ const age = queueAge(depth, oldestQueuedAt, generatedAt);
311
+ return {
312
+ alias: boundedText(route.alias),
313
+ provider: route.provider,
314
+ host: boundedText(route.host),
315
+ enabled: Boolean(route.enabled),
316
+ state: route.state,
317
+ compatibility: route.compatibility,
318
+ queueDepth: depth,
319
+ ...(oldestQueuedAt === undefined ? {} : { oldestQueuedAt }),
320
+ ...(age === undefined ? {} : { queueAgeMs: age }),
321
+ counters: normalizedCounters(route.counters),
322
+ ...(normalizedTimestamp(route.lastSeenAt) === undefined
323
+ ? {}
324
+ : { lastSeenAt: normalizedTimestamp(route.lastSeenAt) }),
325
+ ...(safeCode(route.safeErrorCode) === undefined
326
+ ? {}
327
+ : { safeErrorCode: safeCode(route.safeErrorCode) }),
328
+ };
329
+ });
330
+ const routes = [...allRoutes]
331
+ .sort((left, right) => routePriority(left) - routePriority(right) ||
332
+ compareText(left.alias, right.alias))
333
+ .slice(0, DASHBOARD_MODEL_LIMITS.routes);
334
+ const routeByAlias = new Map(allRoutes.map((route) => [route.alias, route]));
335
+ const allPairs = snapshot.pairs
336
+ .map((pair) => {
337
+ const claudeAlias = boundedText(pair.claudeAlias);
338
+ const codexAlias = boundedText(pair.codexAlias);
339
+ const claudeRoute = routeByAlias.get(claudeAlias);
340
+ const codexRoute = routeByAlias.get(codexAlias);
341
+ const bothPresent = claudeRoute !== undefined && codexRoute !== undefined;
342
+ return {
343
+ claudeAlias,
344
+ codexAlias,
345
+ host: boundedText(pair.host),
346
+ state: bothPresent && routeIsReady(claudeRoute) && routeIsReady(codexRoute)
347
+ ? "ready"
348
+ : bothPresent
349
+ ? "degraded"
350
+ : "unavailable",
351
+ counters: normalizedCounters(pair.counters),
352
+ };
353
+ })
354
+ .sort((left, right) => compareText(left.claudeAlias, right.claudeAlias) ||
355
+ compareText(left.codexAlias, right.codexAlias));
356
+ const pairs = allPairs.slice(0, DASHBOARD_MODEL_LIMITS.pairs);
357
+ const allWatches = (snapshot.progressWatches ?? [])
358
+ .flatMap((watch) => {
359
+ const lastActivityAt = normalizedTimestamp(watch.lastActivityAt);
360
+ const nextActionAt = normalizedTimestamp(watch.nextActionAt);
361
+ const generatedAtMs = generatedAt === undefined ? undefined : Date.parse(generatedAt);
362
+ if (!CONVERSATION_SUFFIX_PATTERN.test(watch.conversationIdSuffix) ||
363
+ lastActivityAt === undefined ||
364
+ nextActionAt === undefined ||
365
+ generatedAtMs === undefined) {
366
+ return [];
367
+ }
368
+ const idleMs = normalizedInteger(watch.idleMs);
369
+ if (idleMs === undefined ||
370
+ (watch.nudgeCount !== 0 && watch.nudgeCount !== 1 && watch.nudgeCount !== 2)) {
371
+ return [];
372
+ }
373
+ return [
374
+ {
375
+ conversationIdSuffix: watch.conversationIdSuffix,
376
+ ownerAlias: boundedText(watch.ownerAlias),
377
+ workerAlias: boundedText(watch.workerAlias),
378
+ phase: watch.phase,
379
+ capability: watch.capability,
380
+ lastActivityAt,
381
+ nextActionAt,
382
+ idleMs,
383
+ idleForMs: Math.max(0, generatedAtMs - Date.parse(lastActivityAt)),
384
+ dueInMs: Math.max(0, Date.parse(nextActionAt) - generatedAtMs),
385
+ nudgeCount: watch.nudgeCount,
386
+ workerReportedComplete: Boolean(watch.workerReportedComplete),
387
+ },
388
+ ];
389
+ })
390
+ .sort((left, right) => compareText(left.nextActionAt, right.nextActionAt) ||
391
+ compareText(left.ownerAlias, right.ownerAlias) ||
392
+ compareText(left.workerAlias, right.workerAlias));
393
+ const watches = allWatches.slice(0, DASHBOARD_MODEL_LIMITS.progressWatches);
394
+ const allWatchEvents = (snapshot.progressWatchEvents ?? [])
395
+ .flatMap((event) => {
396
+ const timestamp = normalizedTimestamp(event.timestamp);
397
+ if (timestamp === undefined ||
398
+ !CONVERSATION_SUFFIX_PATTERN.test(event.conversationIdSuffix) ||
399
+ normalizedInteger(event.sequence) === undefined) {
400
+ return [];
401
+ }
402
+ return [
403
+ {
404
+ sequence: event.sequence,
405
+ timestamp,
406
+ conversationIdSuffix: event.conversationIdSuffix,
407
+ ownerAlias: boundedText(event.ownerAlias),
408
+ workerAlias: boundedText(event.workerAlias),
409
+ kind: event.kind,
410
+ ...(event.nudgeNumber === undefined
411
+ ? {}
412
+ : { nudgeNumber: event.nudgeNumber }),
413
+ },
414
+ ];
415
+ })
416
+ .sort((left, right) => right.sequence - left.sequence);
417
+ const watchEvents = allWatchEvents.slice(0, DASHBOARD_MODEL_LIMITS.progressWatchEvents);
418
+ const connectors = snapshot.connectors
419
+ .map((connector) => ({
420
+ provider: connector.provider,
421
+ host: boundedText(connector.host),
422
+ health: connector.health,
423
+ compatibility: connector.compatibility,
424
+ ...(safeProtocol(connector.protocol) === undefined
425
+ ? {}
426
+ : { protocol: safeProtocol(connector.protocol) }),
427
+ ...(safeProtocol(connector.protocolVersion) === undefined
428
+ ? {}
429
+ : { protocolVersion: safeProtocol(connector.protocolVersion) }),
430
+ ...(normalizedTimestamp(connector.lastSeenAt) === undefined
431
+ ? {}
432
+ : { lastSeenAt: normalizedTimestamp(connector.lastSeenAt) }),
433
+ ...(safeCode(connector.safeErrorCode) === undefined
434
+ ? {}
435
+ : { safeErrorCode: safeCode(connector.safeErrorCode) }),
436
+ }))
437
+ .sort((left, right) => compareText(`${left.provider}\0${left.host}`, `${right.provider}\0${right.host}`))
438
+ .slice(0, DASHBOARD_MODEL_LIMITS.connectors);
439
+ const compatibilityChecks = (Array.isArray(snapshot.compatibilityChecks)
440
+ ? snapshot.compatibilityChecks.filter(isCompatibilityAttestation)
441
+ : [])
442
+ .map((attestation) => {
443
+ const failed = attestation.probes.find((probe) => probe.outcome === "fail");
444
+ return {
445
+ surface: attestation.surface,
446
+ version: boundedText(attestation.version),
447
+ tier: attestation.tier,
448
+ checkedAt: attestation.checkedAt,
449
+ ...(attestation.certification === undefined
450
+ ? {}
451
+ : {
452
+ certificationDepth: attestation.certification.depth,
453
+ certificationOutcome: attestation.certification.outcome,
454
+ certifiedAt: attestation.certification.certifiedAt,
455
+ ...(safeCode(attestation.certification.safeErrorCode) === undefined
456
+ ? {}
457
+ : {
458
+ certificationSafeErrorCode: safeCode(attestation.certification.safeErrorCode),
459
+ }),
460
+ }),
461
+ ...(failed === undefined ? {} : { failedProbe: failed.name }),
462
+ ...(safeCode(attestation.safeErrorCode) === undefined
463
+ ? {}
464
+ : { safeErrorCode: safeCode(attestation.safeErrorCode) }),
465
+ };
466
+ })
467
+ .sort((left, right) => compareText(left.surface, right.surface))
468
+ .slice(0, 2);
469
+ const messages = buildMessageGroups(snapshot.messages);
470
+ const queuedMessages = allRoutes.reduce((total, route) => boundedAdd(total, route.queueDepth), 0);
471
+ const queuedWithAge = allRoutes
472
+ .filter((route) => route.oldestQueuedAt !== undefined && route.queueAgeMs !== undefined)
473
+ .sort((left, right) => right.queueAgeMs - left.queueAgeMs);
474
+ const oldest = queuedWithAge[0];
475
+ const activeDeliveries = messages.activeGroups;
476
+ const claudeRoutes = allRoutes.filter((route) => route.provider === "claude");
477
+ const codexRoutes = allRoutes.filter((route) => route.provider === "codex");
478
+ const readyClaude = claudeRoutes.filter(routeIsReady).length;
479
+ const readyCodex = codexRoutes.filter(routeIsReady).length;
480
+ const readyPairs = allPairs.filter((pair) => pair.state === "ready");
481
+ const pairedReadyClaude = new Set(readyPairs.map((pair) => pair.claudeAlias));
482
+ const pairedReadyCodex = new Set(readyPairs.map((pair) => pair.codexAlias));
483
+ const graph = {
484
+ pairCount: allPairs.length,
485
+ readyPairCount: readyPairs.length,
486
+ pairCountIsLowerBound: (normalizedInteger(snapshot.truncation.pairs) ?? 0) > 0,
487
+ unpairedReadyClaude: claudeRoutes.filter((route) => routeIsReady(route) && !pairedReadyClaude.has(route.alias)).length,
488
+ unpairedReadyCodex: codexRoutes.filter((route) => routeIsReady(route) && !pairedReadyCodex.has(route.alias)).length,
489
+ };
490
+ const selectedPeers = validPeers.filter((peer) => peer.selected && peerIsSelectable(peer));
491
+ const selectablePeers = validPeers.filter(peerIsSelectable);
492
+ const selectedClaudeCount = Math.max(selectedPeers.length, readyClaude);
493
+ const claudeStatus = partyStatus(claudeRoutes, readyClaude);
494
+ const codexStatus = partyStatus(codexRoutes, readyCodex);
495
+ const claudeNextAction = graph.readyPairCount > 0 && selectedClaudeCount > 0 && readyClaude > 0
496
+ ? "none"
497
+ : validPeers.length === 0
498
+ ? "discover_claude"
499
+ : selectablePeers.length > 0
500
+ ? "pair_routes"
501
+ : selectedPeers.length > 0
502
+ ? "restore_claude"
503
+ : "repair_claude_inventory";
504
+ const codexNextAction = codexRoutes.length === 0
505
+ ? "register_codex"
506
+ : readyCodex === 0
507
+ ? "restore_codex"
508
+ : graph.readyPairCount === 0
509
+ ? "pair_routes"
510
+ : "none";
511
+ const explicitAlerts = snapshot.alerts
512
+ .map((alert) => {
513
+ const code = safeCode(alert.code);
514
+ return {
515
+ kind: "alert",
516
+ ...(code === undefined ? {} : { code }),
517
+ severity: alert.severity === "error" ||
518
+ alert.severity === "warning" ||
519
+ alert.severity === "info"
520
+ ? alert.severity
521
+ : "warning",
522
+ ...(normalizedTimestamp(alert.timestamp) === undefined
523
+ ? {}
524
+ : { timestamp: normalizedTimestamp(alert.timestamp) }),
525
+ ...(alert.provider === "claude" || alert.provider === "codex"
526
+ ? { provider: alert.provider }
527
+ : {}),
528
+ ...(typeof alert.alias === "string"
529
+ ? { alias: boundedText(alert.alias) }
530
+ : {}),
531
+ ...(typeof alert.host === "string" ? { host: boundedText(alert.host) } : {}),
532
+ guidance: guidanceFor(code, alert.provider),
533
+ };
534
+ })
535
+ .sort((left, right) => alertPriority(left.severity) - alertPriority(right.severity) ||
536
+ compareText(right.timestamp ?? "", left.timestamp ?? ""));
537
+ const attentionCandidates = [...explicitAlerts];
538
+ for (const watch of allWatches) {
539
+ if (watch.phase !== "episode" && watch.capability !== "route")
540
+ continue;
541
+ attentionCandidates.push({
542
+ kind: "watch",
543
+ code: watch.capability === "route"
544
+ ? "PROGRESS_WATCH_CAPABILITY_DEGRADED"
545
+ : "PROGRESS_WATCH_QUIET",
546
+ severity: "warning",
547
+ timestamp: watch.lastActivityAt,
548
+ alias: watch.workerAlias,
549
+ guidance: "progress_watch",
550
+ });
551
+ }
552
+ const representedScopes = new Set(attentionCandidates.map((item) => `${item.provider ?? ""}\0${item.alias ?? ""}\0${item.host ?? ""}`));
553
+ for (const route of allRoutes) {
554
+ if (routeIsReady(route)) {
555
+ continue;
556
+ }
557
+ const scope = `${route.provider}\0${route.alias}\0${route.host}`;
558
+ if (representedScopes.has(scope))
559
+ continue;
560
+ attentionCandidates.push({
561
+ kind: "route",
562
+ ...(route.safeErrorCode === undefined ? {} : { code: route.safeErrorCode }),
563
+ severity: route.state === "offline" || route.state === "incompatible"
564
+ ? "error"
565
+ : "warning",
566
+ ...(route.lastSeenAt === undefined ? {} : { timestamp: route.lastSeenAt }),
567
+ provider: route.provider,
568
+ alias: route.alias,
569
+ host: route.host,
570
+ guidance: route.provider === "codex" && route.state === "stale"
571
+ ? "codex_stale"
572
+ : "route_stale",
573
+ });
574
+ representedScopes.add(scope);
575
+ }
576
+ for (const connector of connectors) {
577
+ if ((connector.health === "healthy" &&
578
+ connector.compatibility === "compatible")) {
579
+ continue;
580
+ }
581
+ const scope = `${connector.provider}\0\0${connector.host}`;
582
+ if (representedScopes.has(scope))
583
+ continue;
584
+ attentionCandidates.push({
585
+ kind: "connector",
586
+ ...(connector.safeErrorCode === undefined
587
+ ? {}
588
+ : { code: connector.safeErrorCode }),
589
+ severity: connector.health === "degraded" ? "warning" : "error",
590
+ ...(connector.lastSeenAt === undefined
591
+ ? {}
592
+ : { timestamp: connector.lastSeenAt }),
593
+ provider: connector.provider,
594
+ host: connector.host,
595
+ guidance: connector.health === "offline" ? "connector_offline" : "degraded",
596
+ });
597
+ representedScopes.add(scope);
598
+ }
599
+ if ((snapshot.health === "degraded" ||
600
+ snapshot.health === "offline" ||
601
+ snapshot.health === "incompatible") &&
602
+ !attentionCandidates.some((item) => item.kind === "broker")) {
603
+ attentionCandidates.push({
604
+ kind: "broker",
605
+ severity: snapshot.health === "degraded" ? "warning" : "error",
606
+ guidance: snapshot.health === "offline" ? "connector_offline" : "degraded",
607
+ });
608
+ }
609
+ const attention = attentionCandidates.slice(0, DASHBOARD_MODEL_LIMITS.alerts);
610
+ const setupComplete = graph.readyPairCount > 0;
611
+ const overall = attentionCandidates.length > 0 ||
612
+ (normalizedInteger(snapshot.truncation.alerts) ?? 0) > 0
613
+ ? "attention"
614
+ : setupComplete
615
+ ? "ready"
616
+ : "setup";
617
+ const omissions = {
618
+ connectors: boundedAdd(normalizedInteger(snapshot.truncation.connectors) ?? 0, Math.max(0, snapshot.connectors.length - DASHBOARD_MODEL_LIMITS.connectors)),
619
+ availablePeers: boundedAdd(normalizedInteger(snapshot.truncation.availablePeers) ?? 0, Math.max(0, validPeers.length - DASHBOARD_MODEL_LIMITS.availablePeers)),
620
+ routes: boundedAdd(normalizedInteger(snapshot.truncation.routes) ?? 0, Math.max(0, allRoutes.length - DASHBOARD_MODEL_LIMITS.routes)),
621
+ pairs: boundedAdd(normalizedInteger(snapshot.truncation.pairs) ?? 0, Math.max(0, allPairs.length - DASHBOARD_MODEL_LIMITS.pairs)),
622
+ progressWatches: boundedAdd(normalizedInteger(snapshot.truncation.progressWatches) ?? 0, Math.max(0, allWatches.length - DASHBOARD_MODEL_LIMITS.progressWatches)),
623
+ upstreamProgressWatchEvents: normalizedInteger(snapshot.truncation.progressWatchEvents) ?? 0,
624
+ progressWatchEvents: Math.max(0, allWatchEvents.length - DASHBOARD_MODEL_LIMITS.progressWatchEvents),
625
+ upstreamMessageEvents: normalizedInteger(snapshot.truncation.messages) ?? 0,
626
+ messageGroups: messages.omittedGroups,
627
+ messageEvents: messages.omittedEvents,
628
+ upstreamAlerts: normalizedInteger(snapshot.truncation.alerts) ?? 0,
629
+ attentionItems: Math.max(0, attentionCandidates.length - DASHBOARD_MODEL_LIMITS.alerts),
630
+ upstreamActivityEvents: normalizedInteger(snapshot.truncation.activityEvents) ?? 0,
631
+ activityEvents: Math.max(0, allBrokerActivity.length - DASHBOARD_MODEL_LIMITS.activityEvents),
632
+ };
633
+ return {
634
+ schemaVersion: 1,
635
+ ...(generatedAt === undefined ? {} : { generatedAt }),
636
+ inboundMode,
637
+ health: snapshot.health,
638
+ overall,
639
+ exchange: {
640
+ claude: {
641
+ kind: "claude",
642
+ status: selectedClaudeCount > 0 && claudeStatus === "missing"
643
+ ? "attention"
644
+ : claudeStatus,
645
+ total: validPeers.length,
646
+ ready: selectedClaudeCount,
647
+ selectable: selectablePeers.length,
648
+ countIsLowerBound: (normalizedInteger(snapshot.truncation.availablePeers) ?? 0) > 0 ||
649
+ (normalizedInteger(snapshot.truncation.routes) ?? 0) > 0,
650
+ ...(selectedPeers[0]?.alias === undefined
651
+ ? claudeRoutes[0]?.alias === undefined
652
+ ? {}
653
+ : { primaryAlias: boundedText(claudeRoutes[0].alias) }
654
+ : { primaryAlias: boundedText(selectedPeers[0].alias) }),
655
+ nextAction: claudeNextAction,
656
+ },
657
+ codex: {
658
+ kind: "codex",
659
+ status: codexStatus,
660
+ total: codexRoutes.length,
661
+ ready: readyCodex,
662
+ countIsLowerBound: (normalizedInteger(snapshot.truncation.routes) ?? 0) > 0,
663
+ ...(codexRoutes[0]?.alias === undefined
664
+ ? {}
665
+ : { primaryAlias: codexRoutes[0].alias }),
666
+ nextAction: codexNextAction,
667
+ },
668
+ queuedMessages,
669
+ queueCountIsLowerBound: (normalizedInteger(snapshot.truncation.routes) ?? 0) > 0,
670
+ ...(oldest === undefined
671
+ ? {}
672
+ : {
673
+ oldestQueueAgeMs: oldest.queueAgeMs,
674
+ oldestQueuedAt: oldest.oldestQueuedAt,
675
+ }),
676
+ },
677
+ attention,
678
+ transit: {
679
+ queuedMessages,
680
+ activeDeliveries,
681
+ queueCountIsLowerBound: (normalizedInteger(snapshot.truncation.routes) ?? 0) > 0,
682
+ activeCountIsLowerBound: (normalizedInteger(snapshot.truncation.messages) ?? 0) > 0,
683
+ ...(oldest === undefined
684
+ ? {}
685
+ : {
686
+ oldestQueueAgeMs: oldest.queueAgeMs,
687
+ oldestQueuedAt: oldest.oldestQueuedAt,
688
+ }),
689
+ },
690
+ activity: messages.groups,
691
+ brokerActivity,
692
+ peers,
693
+ routes,
694
+ pairs,
695
+ watches,
696
+ watchEvents,
697
+ graph,
698
+ connectors,
699
+ compatibilityChecks,
700
+ accounting: {
701
+ accepted: normalizedInteger(snapshot.accounting.accepted) ?? 0,
702
+ duplicates: normalizedInteger(snapshot.accounting.duplicates) ?? 0,
703
+ delivered: normalizedInteger(snapshot.accounting.delivered) ?? 0,
704
+ unconfirmed: normalizedInteger(snapshot.accounting.unconfirmed) ?? 0,
705
+ failed: normalizedInteger(snapshot.accounting.failed) ?? 0,
706
+ ambiguous: normalizedInteger(snapshot.accounting.ambiguous) ?? 0,
707
+ expired: normalizedInteger(snapshot.accounting.expired) ?? 0,
708
+ cancelled: normalizedInteger(snapshot.accounting.cancelled) ?? 0,
709
+ abandoned: normalizedInteger(snapshot.accounting.abandoned) ?? 0,
710
+ rejected: normalizedInteger(snapshot.accounting.rejected) ?? 0,
711
+ bytesAccepted: normalizedInteger(snapshot.accounting.bytesAccepted) ?? 0,
712
+ queuedBytes: normalizedInteger(snapshot.accounting.queuedBytes) ?? 0,
713
+ },
714
+ ...(snapshot.deadlinePressure === undefined
715
+ ? {}
716
+ : { deadlinePressure: snapshot.deadlinePressure }),
717
+ omissions,
718
+ };
719
+ }
720
+ //# sourceMappingURL=dashboard-model.js.map