@opengeni/api-router 2.5.0 → 2.6.4-canary.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 (70) hide show
  1. package/dist/app.d.ts +2 -1
  2. package/dist/app.js +3 -1
  3. package/dist/auth/managed-auth-attempt-context.d.ts +5 -1
  4. package/dist/auth/managed-auth.d.ts +24 -1
  5. package/dist/{chunk-QESX7HDK.js → chunk-UOI7KAP3.js} +3240 -791
  6. package/dist/chunk-UOI7KAP3.js.map +1 -0
  7. package/dist/http/sse.d.ts +9 -0
  8. package/dist/index.js +117 -5
  9. package/dist/index.js.map +1 -1
  10. package/dist/interaction-metrics.d.ts +2 -0
  11. package/dist/mcp/company-brain-governed-writes.d.ts +1 -1
  12. package/dist/mcp/company-profile-agent-admin.d.ts +6 -6
  13. package/dist/mcp/remember.d.ts +2 -2
  14. package/dist/mcp/scheduled-task-view.d.ts +3 -3
  15. package/dist/mcp/server.d.ts +1 -0
  16. package/dist/mcp/session-view.d.ts +1 -0
  17. package/dist/mcp/session-wait.d.ts +19 -0
  18. package/dist/model-catalog.d.ts +5 -31
  19. package/dist/routes/api-keys.d.ts +2 -0
  20. package/dist/routes/browser-sessions.d.ts +1 -0
  21. package/dist/routes/computer-sessions.d.ts +12 -0
  22. package/dist/routes/managed-auth-session-sets.d.ts +7 -0
  23. package/dist/routes/workspaces.d.ts +1 -1
  24. package/dist/sandbox/metrics-ingestion.d.ts +16 -0
  25. package/dist/workspace-delete-observability.d.ts +10 -0
  26. package/package.json +19 -19
  27. package/src/app.ts +206 -29
  28. package/src/auth/managed-auth-attempt-context.ts +40 -3
  29. package/src/auth/managed-auth-session-adapter.ts +1 -0
  30. package/src/auth/managed-auth.ts +164 -4
  31. package/src/http/sse.ts +279 -45
  32. package/src/index.ts +13 -1
  33. package/src/integrations/oauth-client.ts +8 -1
  34. package/src/integrations/provider-oauth.ts +12 -2
  35. package/src/integrations/slack-interactions.ts +77 -37
  36. package/src/interaction-metrics.ts +30 -0
  37. package/src/mcp/company-brain-governed-writes.ts +38 -23
  38. package/src/mcp/company-profile-agent-admin.ts +7 -7
  39. package/src/mcp/remember.ts +19 -8
  40. package/src/mcp/server.ts +328 -27
  41. package/src/mcp/session-wait.ts +56 -8
  42. package/src/model-catalog.ts +45 -337
  43. package/src/routes/api-integrations.ts +2 -2
  44. package/src/routes/api-keys.ts +149 -7
  45. package/src/routes/automations.ts +186 -19
  46. package/src/routes/browser-sessions.ts +10 -2
  47. package/src/routes/capabilities.ts +3 -3
  48. package/src/routes/codex.ts +483 -74
  49. package/src/routes/company-profile.ts +64 -0
  50. package/src/routes/computer-sessions.ts +103 -1
  51. package/src/routes/connections.ts +271 -16
  52. package/src/routes/github.ts +0 -8
  53. package/src/routes/integration-facets.ts +8 -5
  54. package/src/routes/interaction-resources.ts +7 -1
  55. package/src/routes/managed-auth-session-sets.ts +199 -2
  56. package/src/routes/organization-memberships.ts +28 -8
  57. package/src/routes/packs.ts +5 -5
  58. package/src/routes/plugins.ts +2 -2
  59. package/src/routes/pr-review.ts +80 -4
  60. package/src/routes/scheduled-tasks.ts +47 -11
  61. package/src/routes/sessions.ts +75 -53
  62. package/src/routes/skills.ts +3 -3
  63. package/src/routes/supergrok.ts +3 -2
  64. package/src/routes/workspaces.ts +665 -56
  65. package/src/sandbox/channel-a.ts +10 -4
  66. package/src/sandbox/machines.ts +13 -6
  67. package/src/sandbox/metrics-ingestion.ts +157 -3
  68. package/src/sandbox/viewer.ts +20 -1
  69. package/src/workspace-delete-observability.ts +75 -0
  70. package/dist/chunk-QESX7HDK.js.map +0 -1
@@ -6,7 +6,7 @@ import {
6
6
  import { listScheduledTaskRuns, listScheduledTasks } from "@opengeni/db";
7
7
  import type { Hono } from "hono";
8
8
  import { HTTPException } from "hono/http-exception";
9
- import { requireAccessGrant } from "@opengeni/core";
9
+ import { requireAccessGrant, resolveWorkspaceCatalogSettings } from "@opengeni/core";
10
10
  import { recordWorkspaceUsage, requireLimit } from "@opengeni/core";
11
11
  import type { ApiRouteDeps } from "@opengeni/core";
12
12
  import {
@@ -22,6 +22,7 @@ import {
22
22
  syncCreatedScheduledTask,
23
23
  syncUpdatedScheduledTask,
24
24
  updateScheduledTaskForApi,
25
+ validateScheduledTaskMachineTarget,
25
26
  validateScheduledTaskTarget,
26
27
  validatedScheduledTaskUpdate,
27
28
  } from "@opengeni/core";
@@ -29,7 +30,7 @@ import { boundedLimit } from "../http/common";
29
30
  import { deleteScheduledTaskWithDurableCleanup } from "../scheduled-task-deletion";
30
31
 
31
32
  export function registerScheduledTaskRoutes(app: Hono, deps: ApiRouteDeps): void {
32
- const { settings, db, workflowClient, objectStorage } = deps;
33
+ const { db, workflowClient, objectStorage } = deps;
33
34
 
34
35
  app.post("/v1/workspaces/:workspaceId/scheduled-tasks", async (c) => {
35
36
  const workspaceId = c.req.param("workspaceId");
@@ -42,6 +43,12 @@ export function registerScheduledTaskRoutes(app: Hono, deps: ApiRouteDeps): void
42
43
  });
43
44
  }
44
45
  const payload = parsedPayload.data;
46
+ const catalogSettings = (
47
+ await resolveWorkspaceCatalogSettings(db, deps.settings, {
48
+ accountId: grant.accountId,
49
+ workspaceId,
50
+ })
51
+ ).settings;
45
52
  await requireLimit(deps, {
46
53
  accountId: grant.accountId,
47
54
  workspaceId,
@@ -49,7 +56,7 @@ export function registerScheduledTaskRoutes(app: Hono, deps: ApiRouteDeps): void
49
56
  quantity: 1,
50
57
  });
51
58
  const task = await createValidatedScheduledTask({
52
- settings,
59
+ settings: catalogSettings,
53
60
  db,
54
61
  objectStorage,
55
62
  grant,
@@ -90,8 +97,14 @@ export function registerScheduledTaskRoutes(app: Hono, deps: ApiRouteDeps): void
90
97
  });
91
98
  }
92
99
  const payload = parsedPayload.data;
100
+ const catalogSettings = (
101
+ await resolveWorkspaceCatalogSettings(db, deps.settings, {
102
+ accountId: grant.accountId,
103
+ workspaceId,
104
+ })
105
+ ).settings;
93
106
  const update = await validatedScheduledTaskUpdate({
94
- settings,
107
+ settings: catalogSettings,
95
108
  db,
96
109
  objectStorage,
97
110
  grant,
@@ -123,8 +136,14 @@ export function registerScheduledTaskRoutes(app: Hono, deps: ApiRouteDeps): void
123
136
  const grant = await requireAccessGrant(c, deps, workspaceId, "scheduled_tasks:manage");
124
137
  const existing = await requireScheduledTaskForApi(db, workspaceId, c.req.param("taskId"));
125
138
  const previous = await captureScheduledTaskRestoreState(db, existing);
139
+ const catalogSettings = (
140
+ await resolveWorkspaceCatalogSettings(db, deps.settings, {
141
+ accountId: grant.accountId,
142
+ workspaceId,
143
+ })
144
+ ).settings;
126
145
  const update = await validatedScheduledTaskUpdate({
127
- settings,
146
+ settings: catalogSettings,
128
147
  db,
129
148
  objectStorage,
130
149
  grant,
@@ -145,6 +164,12 @@ export function registerScheduledTaskRoutes(app: Hono, deps: ApiRouteDeps): void
145
164
  // recognised as codex-billed and skip the credit/cost gates at the edge.
146
165
  const task = await requireScheduledTaskForApi(db, workspaceId, c.req.param("taskId"));
147
166
  if (task.action.kind === "agent_turn") {
167
+ const catalogSettings = (
168
+ await resolveWorkspaceCatalogSettings(db, deps.settings, {
169
+ accountId: grant.accountId,
170
+ workspaceId,
171
+ })
172
+ ).settings;
148
173
  await validateScheduledTaskTarget({
149
174
  db,
150
175
  sessionAuthorization: deps.sessionAuthorization,
@@ -157,13 +182,24 @@ export function registerScheduledTaskRoutes(app: Hono, deps: ApiRouteDeps): void
157
182
  agentConfig: task.agentConfig,
158
183
  missingTargetStatus: 404,
159
184
  });
160
- await requireLimit(deps, {
161
- accountId: grant.accountId,
162
- workspaceId,
163
- action: "agent_run:create",
164
- quantity: 1,
165
- model: task.agentConfig.model ?? deps.settings.openaiModel,
185
+ await validateScheduledTaskMachineTarget({
186
+ settings: catalogSettings,
187
+ db,
188
+ grant,
189
+ runMode: task.runMode,
190
+ agentConfig: task.agentConfig,
191
+ requireOnline: true,
166
192
  });
193
+ await requireLimit(
194
+ { ...deps, settings: catalogSettings },
195
+ {
196
+ accountId: grant.accountId,
197
+ workspaceId,
198
+ action: "agent_run:create",
199
+ quantity: 1,
200
+ model: task.agentConfig.model ?? catalogSettings.openaiModel,
201
+ },
202
+ );
167
203
  }
168
204
  // Body is optional (a bare POST is still a valid trigger); only a present,
169
205
  // non-empty body must parse against the contract.
@@ -12,6 +12,7 @@ import {
12
12
  GatewayRealtimeConnectRequest,
13
13
  ClientSessionEvent,
14
14
  CompactSessionContextRequest,
15
+ CreateSessionRequest,
15
16
  DeleteSessionQueueItemRequest,
16
17
  EditSessionQueueItemRequest,
17
18
  EndSessionRealtimeRequest,
@@ -47,6 +48,7 @@ import {
47
48
  SessionEventSemanticClass,
48
49
  SessionEventType,
49
50
  SessionMcpServerId,
51
+ SaveNewSessionDraftRequest,
50
52
  compactSessionEventResult,
51
53
  sessionEventLatestClassToSemanticClass,
52
54
  SaveComposerDraftRequest,
@@ -231,6 +233,7 @@ import {
231
233
  requirePermission,
232
234
  requireSessionAuthorization,
233
235
  requireSessionAuthorizationListScope,
236
+ resolveWorkspaceCatalogSettings,
234
237
  withResolvedSessionAuthorization,
235
238
  SESSION_AUTHORIZATION_DEFAULT_REAUTHORIZE_MS,
236
239
  SessionAuthorizationDeniedError,
@@ -284,7 +287,7 @@ import {
284
287
  validateVariableSetAttachment,
285
288
  } from "@opengeni/core";
286
289
  import { assertSessionExists, boundedLimit } from "../http/common";
287
- import { sseSessionStream } from "../http/sse";
290
+ import { browserSseDeliveryOptions, sseSessionStream } from "../http/sse";
288
291
  import {
289
292
  serveWorkspaceCapture,
290
293
  serveWorkspaceCaptureFile,
@@ -549,6 +552,7 @@ export function registerSessionRoutes(app: Hono, deps: SessionRouteDeps): void {
549
552
  }
550
553
  let session: Session;
551
554
  try {
555
+ CreateSessionRequest.parse(payload);
552
556
  session = await createSessionForRequest(deps, grant, workspaceId, payload, authorization);
553
557
  } catch (error) {
554
558
  return sessionCreateErrorResponse(c, error);
@@ -562,7 +566,13 @@ export function registerSessionRoutes(app: Hono, deps: SessionRouteDeps): void {
562
566
  app.get("/v1/workspaces/:workspaceId/new-session-draft", async (c) => {
563
567
  const workspaceId = c.req.param("workspaceId");
564
568
  const grant = await requireAccessGrant(c, deps, workspaceId, "sessions:read");
565
- return c.json(await getActorNewSessionDraft({ settings, db }, grant, workspaceId));
569
+ const catalog = await resolveWorkspaceCatalogSettings(db, settings, {
570
+ accountId: grant.accountId,
571
+ workspaceId,
572
+ });
573
+ return c.json(
574
+ await getActorNewSessionDraft({ settings: catalog.settings, db }, grant, workspaceId),
575
+ );
566
576
  });
567
577
 
568
578
  app.put("/v1/workspaces/:workspaceId/new-session-draft", async (c) => {
@@ -587,9 +597,14 @@ export function registerSessionRoutes(app: Hono, deps: SessionRouteDeps): void {
587
597
  );
588
598
  }
589
599
  try {
600
+ SaveNewSessionDraftRequest.parse(payload);
601
+ const catalog = await resolveWorkspaceCatalogSettings(db, settings, {
602
+ accountId: grant.accountId,
603
+ workspaceId,
604
+ });
590
605
  return c.json(
591
606
  await saveActorNewSessionDraft(
592
- { settings, db, objectStorage },
607
+ { settings: catalog.settings, db, objectStorage },
593
608
  grant,
594
609
  workspaceId,
595
610
  payload,
@@ -2659,6 +2674,7 @@ export function registerSessionRoutes(app: Hono, deps: SessionRouteDeps): void {
2659
2674
  Number.isFinite(after) ? after : 0,
2660
2675
  c.req.raw.signal,
2661
2676
  {
2677
+ ...browserSseDeliveryOptions(c.req.query("transport")),
2662
2678
  observability: deps.observability,
2663
2679
  actorEpoch: getManagedAuthRequestActorEpoch(c.req.raw) ?? undefined,
2664
2680
  reauthorizeAfterMs:
@@ -2897,28 +2913,33 @@ export function registerSessionRoutes(app: Hono, deps: SessionRouteDeps): void {
2897
2913
  const sessionId = c.req.param("sessionId");
2898
2914
  await assertSessionExists(db, workspaceId, sessionId);
2899
2915
  const payload = parseSteerSessionAdmission(await c.req.json().catch(() => null));
2900
- const result = await acceptSessionUserMessage(deps, grant, workspaceId, sessionId, {
2901
- text: payload.text,
2902
- annotations: payload.annotations,
2903
- modelContext: payload.modelContext ?? null,
2904
- resources: payload.resources,
2905
- model: payload.model ?? null,
2906
- reasoningEffort: payload.reasoningEffort ?? null,
2907
- latencyMode: payload.latencyMode ?? null,
2908
- mcpCredentialUpdates: payload.mcpCredentialUpdates ?? [],
2909
- connectionAuthorities: payload.connectionAuthorities,
2910
- ...(payload.personalResourceAttachment
2911
- ? { personalResourceAttachment: payload.personalResourceAttachment }
2912
- : {}),
2913
- authorization,
2914
- delivery: "steer",
2915
- origin: "human",
2916
- ...(payload.controlEtag !== undefined ? { controlEtag: payload.controlEtag } : {}),
2917
- ...(payload.expectedDraftRevision !== undefined
2918
- ? { expectedDraftRevision: payload.expectedDraftRevision }
2919
- : {}),
2920
- ...(payload.clientEventId ? { clientEventId: payload.clientEventId } : {}),
2921
- });
2916
+ let result: Awaited<ReturnType<typeof acceptSessionUserMessage>>;
2917
+ try {
2918
+ result = await acceptSessionUserMessage(deps, grant, workspaceId, sessionId, {
2919
+ text: payload.text,
2920
+ annotations: payload.annotations,
2921
+ modelContext: payload.modelContext ?? null,
2922
+ resources: payload.resources,
2923
+ model: payload.model ?? null,
2924
+ reasoningEffort: payload.reasoningEffort ?? null,
2925
+ latencyMode: payload.latencyMode ?? null,
2926
+ mcpCredentialUpdates: payload.mcpCredentialUpdates ?? [],
2927
+ connectionAuthorities: payload.connectionAuthorities,
2928
+ ...(payload.personalResourceAttachment
2929
+ ? { personalResourceAttachment: payload.personalResourceAttachment }
2930
+ : {}),
2931
+ authorization,
2932
+ delivery: "steer",
2933
+ origin: "human",
2934
+ ...(payload.controlEtag !== undefined ? { controlEtag: payload.controlEtag } : {}),
2935
+ ...(payload.expectedDraftRevision !== undefined
2936
+ ? { expectedDraftRevision: payload.expectedDraftRevision }
2937
+ : {}),
2938
+ ...(payload.clientEventId ? { clientEventId: payload.clientEventId } : {}),
2939
+ });
2940
+ } catch (error) {
2941
+ return commandConflictResponse(c, error);
2942
+ }
2922
2943
  return c.json(result, 202);
2923
2944
  });
2924
2945
 
@@ -2974,29 +2995,34 @@ export function registerSessionRoutes(app: Hono, deps: SessionRouteDeps): void {
2974
2995
  }
2975
2996
  }
2976
2997
  if (event.type === "user.message") {
2977
- const { accepted } = await acceptSessionUserMessage(deps, grant, workspaceId, sessionId, {
2978
- text: event.payload.text,
2979
- annotations: event.payload.annotations,
2980
- modelContext: event.payload.modelContext ?? null,
2981
- resources: event.payload.resources ?? [],
2982
- model: event.payload.model ?? null,
2983
- reasoningEffort: event.payload.reasoningEffort ?? null,
2984
- latencyMode: event.payload.latencyMode ?? null,
2985
- mcpCredentialUpdates: event.payload.mcpCredentialUpdates ?? [],
2986
- connectionAuthorities: event.payload.connectionAuthorities,
2987
- ...(event.payload.personalResourceAttachment
2988
- ? { personalResourceAttachment: event.payload.personalResourceAttachment }
2989
- : {}),
2990
- authorization,
2991
- ...(event.payload.controlEtag !== undefined
2992
- ? { controlEtag: event.payload.controlEtag }
2993
- : {}),
2994
- ...(event.payload.expectedDraftRevision !== undefined
2995
- ? { expectedDraftRevision: event.payload.expectedDraftRevision }
2996
- : {}),
2997
- ...(event.clientEventId ? { clientEventId: event.clientEventId } : {}),
2998
- });
2999
- return c.json(accepted, 202);
2998
+ let result: Awaited<ReturnType<typeof acceptSessionUserMessage>>;
2999
+ try {
3000
+ result = await acceptSessionUserMessage(deps, grant, workspaceId, sessionId, {
3001
+ text: event.payload.text,
3002
+ annotations: event.payload.annotations,
3003
+ modelContext: event.payload.modelContext ?? null,
3004
+ resources: event.payload.resources ?? [],
3005
+ model: event.payload.model ?? null,
3006
+ reasoningEffort: event.payload.reasoningEffort ?? null,
3007
+ latencyMode: event.payload.latencyMode ?? null,
3008
+ mcpCredentialUpdates: event.payload.mcpCredentialUpdates ?? [],
3009
+ connectionAuthorities: event.payload.connectionAuthorities,
3010
+ ...(event.payload.personalResourceAttachment
3011
+ ? { personalResourceAttachment: event.payload.personalResourceAttachment }
3012
+ : {}),
3013
+ authorization,
3014
+ ...(event.payload.controlEtag !== undefined
3015
+ ? { controlEtag: event.payload.controlEtag }
3016
+ : {}),
3017
+ ...(event.payload.expectedDraftRevision !== undefined
3018
+ ? { expectedDraftRevision: event.payload.expectedDraftRevision }
3019
+ : {}),
3020
+ ...(event.clientEventId ? { clientEventId: event.clientEventId } : {}),
3021
+ });
3022
+ } catch (error) {
3023
+ return commandConflictResponse(c, error);
3024
+ }
3025
+ return c.json(result.accepted, 202);
3000
3026
  }
3001
3027
 
3002
3028
  if (event.type === "user.approvalDecision") {
@@ -3201,12 +3227,8 @@ export function registerSessionRoutes(app: Hono, deps: SessionRouteDeps): void {
3201
3227
  // Human take-control: when the desktop is available + this policy is on
3202
3228
  // (default), the cell is mode "interactive" — the noVNC viewer drives :0
3203
3229
  // (x11vnc runs without -viewonly). Off → mode "read-only" (client disables
3204
- // take-control). Independent of the agent's computerUseReadOnly.
3230
+ // take-control). Agent interaction uses managed ComputerSession tools.
3205
3231
  desktopInteractive: settings.sandboxDesktopInteractive,
3206
- // P4.3 computer-use: the agent drives the active display; availability
3207
- // tracks the desktop tier + a desktop-capable backend.
3208
- computerUseEnabled: settings.computerUseEnabled,
3209
- computerUseReadOnly: settings.computerUseReadOnly,
3210
3232
  // Graceful degrade when scoped stream credentials cannot be minted.
3211
3233
  streamTokenSecretAvailable: !streamTokenDegraded(settings),
3212
3234
  desktopAcknowledged: acknowledged,
@@ -55,7 +55,7 @@ export function registerSkillRoutes(
55
55
 
56
56
  app.post("/v1/workspaces/:workspaceId/skills/library/:libraryId/install", async (c) => {
57
57
  const workspaceId = c.req.param("workspaceId");
58
- const grant = await requireAccessGrant(c, deps, workspaceId, "workspace:admin");
58
+ const grant = await requireAccessGrant(c, deps, workspaceId, "capabilities:manage");
59
59
  const libraryId = decodeURIComponent(c.req.param("libraryId"));
60
60
  const payload = InstallLibrarySkillRequest.parse(await c.req.json());
61
61
  let loaded: ReturnType<typeof loadSkillLibrarySkill>;
@@ -136,7 +136,7 @@ export function registerSkillRoutes(
136
136
 
137
137
  app.post("/v1/workspaces/:workspaceId/skills/install", async (c) => {
138
138
  const workspaceId = c.req.param("workspaceId");
139
- const grant = await requireAccessGrant(c, deps, workspaceId, "workspace:admin");
139
+ const grant = await requireAccessGrant(c, deps, workspaceId, "capabilities:manage");
140
140
  const payload = InstallSkillRequest.parse(await c.req.json());
141
141
  const resolved = await resolveForRoute(payload.url, github);
142
142
  if (
@@ -204,7 +204,7 @@ export function registerSkillRoutes(
204
204
 
205
205
  app.delete("/v1/workspaces/:workspaceId/skills/:capabilityId", async (c) => {
206
206
  const workspaceId = c.req.param("workspaceId");
207
- const grant = await requireAccessGrant(c, deps, workspaceId, "workspace:admin");
207
+ const grant = await requireAccessGrant(c, deps, workspaceId, "capabilities:manage");
208
208
  const capabilityId = decodeURIComponent(c.req.param("capabilityId"));
209
209
  const payload = UninstallSkillRequest.parse(await c.req.json());
210
210
  try {
@@ -552,8 +552,9 @@ export function registerSuperGrokRoutes(app: Hono, deps: ApiRouteDeps): void {
552
552
  } catch {
553
553
  valid = false;
554
554
  }
555
- const catalog = valid
556
- ? configuredModels(withXaiSubscriptionCatalogProvider(deps.settings))
555
+ const catalogSettings = valid ? (await deps.resolveCatalogSettings()).settings : null;
556
+ const catalog = catalogSettings
557
+ ? configuredModels(withXaiSubscriptionCatalogProvider(catalogSettings))
557
558
  .filter(
558
559
  (model) =>
559
560
  model.credentialSource.kind === "connected_subscription" &&