@opengeni/api-router 2.5.0 → 2.6.4

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 (59) hide show
  1. package/dist/app.js +1 -1
  2. package/dist/auth/managed-auth-attempt-context.d.ts +5 -1
  3. package/dist/auth/managed-auth.d.ts +24 -1
  4. package/dist/{chunk-QESX7HDK.js → chunk-XTLI3CBH.js} +2236 -387
  5. package/dist/chunk-XTLI3CBH.js.map +1 -0
  6. package/dist/http/sse.d.ts +9 -0
  7. package/dist/index.js +104 -4
  8. package/dist/index.js.map +1 -1
  9. package/dist/interaction-metrics.d.ts +2 -0
  10. package/dist/mcp/company-brain-governed-writes.d.ts +1 -1
  11. package/dist/mcp/company-profile-agent-admin.d.ts +6 -6
  12. package/dist/mcp/remember.d.ts +2 -2
  13. package/dist/mcp/server.d.ts +1 -0
  14. package/dist/mcp/session-view.d.ts +1 -0
  15. package/dist/mcp/session-wait.d.ts +19 -0
  16. package/dist/routes/api-keys.d.ts +2 -0
  17. package/dist/routes/browser-sessions.d.ts +1 -0
  18. package/dist/routes/computer-sessions.d.ts +12 -0
  19. package/dist/routes/managed-auth-session-sets.d.ts +7 -0
  20. package/dist/routes/workspaces.d.ts +1 -1
  21. package/dist/sandbox/metrics-ingestion.d.ts +16 -0
  22. package/dist/workspace-delete-observability.d.ts +10 -0
  23. package/package.json +15 -15
  24. package/src/app.ts +172 -20
  25. package/src/auth/managed-auth-attempt-context.ts +40 -3
  26. package/src/auth/managed-auth-session-adapter.ts +1 -0
  27. package/src/auth/managed-auth.ts +164 -4
  28. package/src/http/sse.ts +279 -45
  29. package/src/integrations/oauth-client.ts +8 -1
  30. package/src/integrations/provider-oauth.ts +12 -2
  31. package/src/interaction-metrics.ts +30 -0
  32. package/src/mcp/company-brain-governed-writes.ts +38 -23
  33. package/src/mcp/company-profile-agent-admin.ts +7 -7
  34. package/src/mcp/remember.ts +19 -8
  35. package/src/mcp/server.ts +318 -26
  36. package/src/mcp/session-wait.ts +56 -8
  37. package/src/routes/api-integrations.ts +2 -2
  38. package/src/routes/api-keys.ts +149 -7
  39. package/src/routes/browser-sessions.ts +10 -2
  40. package/src/routes/capabilities.ts +3 -3
  41. package/src/routes/codex.ts +483 -74
  42. package/src/routes/company-profile.ts +64 -0
  43. package/src/routes/computer-sessions.ts +103 -1
  44. package/src/routes/integration-facets.ts +8 -5
  45. package/src/routes/interaction-resources.ts +7 -1
  46. package/src/routes/managed-auth-session-sets.ts +199 -2
  47. package/src/routes/organization-memberships.ts +28 -8
  48. package/src/routes/packs.ts +5 -5
  49. package/src/routes/plugins.ts +2 -2
  50. package/src/routes/scheduled-tasks.ts +9 -0
  51. package/src/routes/sessions.ts +3 -6
  52. package/src/routes/skills.ts +3 -3
  53. package/src/routes/workspaces.ts +293 -54
  54. package/src/sandbox/channel-a.ts +10 -4
  55. package/src/sandbox/machines.ts +13 -6
  56. package/src/sandbox/metrics-ingestion.ts +157 -3
  57. package/src/sandbox/viewer.ts +20 -1
  58. package/src/workspace-delete-observability.ts +75 -0
  59. package/dist/chunk-QESX7HDK.js.map +0 -1
@@ -5,6 +5,8 @@ import type { Observability } from "@opengeni/observability";
5
5
  export declare const SSE_QUEUED_FRAME_MAX_COUNT = 1;
6
6
  export declare const SSE_WRITE_STALL_TIMEOUT_MS = 30000;
7
7
  export declare const SSE_HEARTBEAT_INTERVAL_MS = 15000;
8
+ export declare const HTTP1_BROWSER_SSE_BATCH_MAX_BYTES: number;
9
+ export declare const HTTP1_BROWSER_SSE_BATCH_CONTENT_TYPE = "application/vnd.opengeni.sse-batch";
8
10
  export type SseDeliveryBoundObservation = {
9
11
  reason: "desired_size_non_positive" | "stall_timeout" | "frame_too_large";
10
12
  desiredSize: number | null;
@@ -12,6 +14,7 @@ export type SseDeliveryBoundObservation = {
12
14
  queuedBytes: number;
13
15
  };
14
16
  export type ByteBoundedSseStreamOptions = {
17
+ connectionLifetimeMs?: number | undefined;
15
18
  maxQueuedBytes?: number;
16
19
  stallTimeoutMs?: number;
17
20
  onStop?: () => void;
@@ -76,6 +79,11 @@ export declare function sseWorkspaceLiveStream(db: Database, bus: EventBus, acco
76
79
  */
77
80
  export declare function sseWorkspaceInteractionRevisionStream(db: Database, accountId: string, workspaceId: string, after: number, signal: AbortSignal, options?: WorkspaceInteractionSseOptions): Promise<Response>;
78
81
  export type SseDeliveryOptions = {
82
+ connectionLifetimeMs?: number | undefined;
83
+ /** Return a known-length batch instead of a chunked response. HTTP/1 only. */
84
+ finiteResponseMaxBytes?: number | undefined;
85
+ /** Browser transport classification for a finite response. */
86
+ finiteResponseMediaType?: "event-stream" | "http1-browser-batch" | undefined;
79
87
  maxQueuedBytes?: number;
80
88
  stallTimeoutMs?: number;
81
89
  heartbeatIntervalMs?: number;
@@ -87,4 +95,5 @@ export type SseDeliveryOptions = {
87
95
  /** Exact selected actor emitted on the stream response for cross-tab fencing. */
88
96
  actorEpoch?: string | undefined;
89
97
  };
98
+ export declare function browserSseDeliveryOptions(transport: string | undefined): Pick<SseDeliveryOptions, "connectionLifetimeMs" | "finiteResponseMaxBytes" | "finiteResponseMediaType">;
90
99
  export type SessionSseDeliveryOptions = SseDeliveryOptions;
package/dist/index.js CHANGED
@@ -6,7 +6,7 @@ import {
6
6
  createOpenGeniSlackBotInteractionClient,
7
7
  startSlackInteractionPump,
8
8
  startTemporalScheduleCleanupPump
9
- } from "./chunk-QESX7HDK.js";
9
+ } from "./chunk-XTLI3CBH.js";
10
10
 
11
11
  // src/index.ts
12
12
  import {
@@ -233,6 +233,94 @@ import {
233
233
  } from "@opengeni/agent-proto";
234
234
  var AGENT_EVENTS_SUBJECT = "agent.*.*.connection.*.events";
235
235
  var AGENT_HELLO_SUBJECT = "agent.*.*.connection.*.hello";
236
+ function payloadIsHeartbeat(payload) {
237
+ try {
238
+ return AgentEvent.decode(payload).event?.$case === "heartbeat";
239
+ } catch {
240
+ return false;
241
+ }
242
+ }
243
+ function createAgentEventIngestionScheduler(handler, options = {}) {
244
+ const maxConcurrentSubjects = options.maxConcurrentSubjects ?? 32;
245
+ if (!Number.isSafeInteger(maxConcurrentSubjects) || maxConcurrentSubjects <= 0) {
246
+ throw new RangeError("maxConcurrentSubjects must be a positive safe integer");
247
+ }
248
+ const states = /* @__PURE__ */ new Map();
249
+ const readySubjects = [];
250
+ const idleWaiters = /* @__PURE__ */ new Set();
251
+ let accepting = true;
252
+ let activeSubjects = 0;
253
+ const resolveIdleWaiters = () => {
254
+ if (states.size !== 0) return;
255
+ for (const resolve of idleWaiters) resolve();
256
+ idleWaiters.clear();
257
+ };
258
+ async function drain(subject, state) {
259
+ while (true) {
260
+ const event = state.pending.shift();
261
+ if (!event) break;
262
+ try {
263
+ await handler(event.payload, event.subject);
264
+ } catch {
265
+ }
266
+ }
267
+ activeSubjects -= 1;
268
+ state.scheduled = false;
269
+ if (state.pending.length > 0) {
270
+ schedule(subject, state);
271
+ } else {
272
+ if (states.get(subject) === state) states.delete(subject);
273
+ resolveIdleWaiters();
274
+ }
275
+ pump();
276
+ }
277
+ function pump() {
278
+ while (activeSubjects < maxConcurrentSubjects) {
279
+ const next = readySubjects.shift();
280
+ if (!next) return;
281
+ if (states.get(next.subject) !== next.state || !next.state.scheduled) continue;
282
+ activeSubjects += 1;
283
+ void drain(next.subject, next.state);
284
+ }
285
+ }
286
+ function schedule(subject, state) {
287
+ if (state.scheduled) return;
288
+ state.scheduled = true;
289
+ readySubjects.push({ subject, state });
290
+ pump();
291
+ }
292
+ return {
293
+ enqueue(payload, subject) {
294
+ if (!accepting) return;
295
+ const heartbeat = payloadIsHeartbeat(payload);
296
+ let state = states.get(subject);
297
+ if (!state) {
298
+ state = { scheduled: false, pending: [] };
299
+ states.set(subject, state);
300
+ }
301
+ const queued = { payload, subject, heartbeat };
302
+ const lastIndex = state.pending.length - 1;
303
+ if (heartbeat && lastIndex >= 0 && state.pending[lastIndex].heartbeat) {
304
+ state.pending[lastIndex] = queued;
305
+ options.onHeartbeatCoalesced?.();
306
+ } else {
307
+ state.pending.push(queued);
308
+ }
309
+ schedule(subject, state);
310
+ },
311
+ async whenIdle() {
312
+ if (states.size === 0) return;
313
+ await new Promise((resolve) => idleWaiters.add(resolve));
314
+ },
315
+ close() {
316
+ accepting = false;
317
+ for (const state of states.values()) {
318
+ state.pending.length = 0;
319
+ }
320
+ resolveIdleWaiters();
321
+ }
322
+ };
323
+ }
236
324
  function parseAgentSubject(subject, tail) {
237
325
  const parts = subject.split(".");
238
326
  if (parts.length !== 6 || parts[0] !== "agent" || parts[3] !== "connection" || !parts[4] || parts[5] !== tail) {
@@ -545,10 +633,22 @@ async function handleAgentEventPayload(db, observability, payload, subject, bus)
545
633
  }
546
634
  }
547
635
  function startMetricsIngestion(deps) {
548
- return deps.bus.subscribeAgentEvents(
549
- AGENT_EVENTS_SUBJECT,
550
- (payload, subject) => handleAgentEventPayload(deps.db, deps.observability, payload, subject, deps.bus)
636
+ const scheduler = createAgentEventIngestionScheduler(
637
+ (payload, subject) => handleAgentEventPayload(deps.db, deps.observability, payload, subject, deps.bus),
638
+ {
639
+ onHeartbeatCoalesced: () => deps.observability?.incrementCounter({
640
+ name: "opengeni_machine_heartbeat_coalesced_total",
641
+ help: "Connected Machine heartbeats collapsed while an exact runner event queue was busy."
642
+ })
643
+ }
551
644
  );
645
+ const unsubscribe = deps.bus.subscribeAgentEvents(AGENT_EVENTS_SUBJECT, (payload, subject) => {
646
+ scheduler.enqueue(payload, subject);
647
+ });
648
+ return () => {
649
+ unsubscribe();
650
+ scheduler.close();
651
+ };
552
652
  }
553
653
  function helloReportsDisplay(hello) {
554
654
  const caps = hello.capabilities;