@opengeni/api-router 0.23.1 → 0.30.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 (152) hide show
  1. package/dist/app.d.ts +3 -2
  2. package/dist/app.js +5 -3
  3. package/dist/auth/canonical-human-session-admission.d.ts +27 -0
  4. package/dist/browser-auth-broker.d.ts +20 -0
  5. package/dist/browser-controller-authority.d.ts +61 -0
  6. package/dist/browser-network-route.d.ts +8 -0
  7. package/dist/browser-state-authority.d.ts +27 -0
  8. package/dist/{chunk-T4T2PGU4.js → chunk-2PQMSRCB.js} +37891 -17463
  9. package/dist/chunk-2PQMSRCB.js.map +1 -0
  10. package/dist/codemode.d.ts +23 -0
  11. package/dist/controller-data-plane.d.ts +12 -0
  12. package/dist/editable-artifact-live-hints.d.ts +11 -0
  13. package/dist/editable-artifact-native-kernel.d.ts +37 -0
  14. package/dist/editable-artifact-office-import.d.ts +22 -0
  15. package/dist/editable-artifact-production.d.ts +29 -0
  16. package/dist/editable-artifact-websocket.d.ts +49 -0
  17. package/dist/editable-artifact-workspace-files.d.ts +23 -0
  18. package/dist/github-browser-flow.d.ts +6 -0
  19. package/dist/http/auth.d.ts +1 -1
  20. package/dist/http/cors.d.ts +1 -0
  21. package/dist/http/sse.d.ts +18 -1
  22. package/dist/index.d.ts +3 -2
  23. package/dist/index.js +1796 -22
  24. package/dist/index.js.map +1 -1
  25. package/dist/integrations/api-integrations.d.ts +24 -0
  26. package/dist/integrations/atlassian.d.ts +176 -0
  27. package/dist/integrations/fiken.d.ts +233 -0
  28. package/dist/integrations/github-skill-source.d.ts +5 -0
  29. package/dist/integrations/google-drive.d.ts +85 -0
  30. package/dist/integrations/oauth-client.d.ts +22 -1
  31. package/dist/integrations/provider-oauth.d.ts +17 -0
  32. package/dist/integrations/slack-bot.d.ts +175 -1
  33. package/dist/integrations/slack-interactions.d.ts +24 -6
  34. package/dist/integrations/social-api.d.ts +2 -1
  35. package/dist/interaction-holder-heartbeat.d.ts +17 -0
  36. package/dist/interaction-metrics.d.ts +11 -0
  37. package/dist/mcp/editable-artifact-query-schema.d.ts +4 -0
  38. package/dist/mcp/editable-artifacts.d.ts +13 -0
  39. package/dist/mcp/scheduled-task-view.d.ts +168 -0
  40. package/dist/mcp/server.d.ts +16 -4
  41. package/dist/memory-slack-delivery.d.ts +9 -0
  42. package/dist/model-catalog.d.ts +1 -0
  43. package/dist/routes/api-integrations.d.ts +8 -0
  44. package/dist/routes/browser-identities.d.ts +5 -0
  45. package/dist/routes/browser-sessions.d.ts +6 -0
  46. package/dist/routes/canonical-human-identities.d.ts +3 -0
  47. package/dist/routes/channels.d.ts +3 -0
  48. package/dist/routes/company-profile.d.ts +3 -0
  49. package/dist/routes/computer-sessions.d.ts +6 -0
  50. package/dist/routes/editable-artifacts.d.ts +44 -0
  51. package/dist/routes/integration-facets.d.ts +3 -0
  52. package/dist/routes/interaction-resources.d.ts +5 -0
  53. package/dist/routes/memory-slack-publications.d.ts +6 -0
  54. package/dist/routes/organization-memberships.d.ts +3 -0
  55. package/dist/routes/plugins.d.ts +8 -0
  56. package/dist/routes/sessions.d.ts +18 -2
  57. package/dist/routes/skills.d.ts +6 -0
  58. package/dist/routes/slack-task-policy.d.ts +3 -0
  59. package/dist/routes/supergrok.d.ts +3 -0
  60. package/dist/routes/video-generation.d.ts +3 -0
  61. package/dist/sandbox/channel-a.d.ts +62 -2
  62. package/dist/sandbox/machines.d.ts +2 -2
  63. package/dist/sandbox/metrics-ingestion.d.ts +6 -1
  64. package/dist/sandbox/viewer.d.ts +7 -3
  65. package/dist/slack-reaction-files.d.ts +27 -0
  66. package/dist/temporal-schedule-cleanup.d.ts +26 -0
  67. package/dist/transcription/providers/xai-subscription.d.ts +8 -0
  68. package/dist/xai-realtime.d.ts +26 -0
  69. package/dist/xai-subscription-auth.d.ts +23 -0
  70. package/package.json +20 -14
  71. package/src/app.ts +604 -64
  72. package/src/auth/canonical-human-session-admission.ts +116 -0
  73. package/src/auth/managed-auth.ts +101 -0
  74. package/src/browser-auth-broker.ts +155 -0
  75. package/src/browser-controller-authority.ts +186 -0
  76. package/src/browser-network-route.ts +34 -0
  77. package/src/browser-state-authority.ts +236 -0
  78. package/src/codemode.ts +186 -0
  79. package/src/controller-data-plane.ts +35 -0
  80. package/src/editable-artifact-live-hints.ts +64 -0
  81. package/src/editable-artifact-native-kernel.ts +659 -0
  82. package/src/editable-artifact-office-import.ts +230 -0
  83. package/src/editable-artifact-production.ts +419 -0
  84. package/src/editable-artifact-websocket.ts +311 -0
  85. package/src/editable-artifact-workspace-files.ts +186 -0
  86. package/src/github-browser-flow.ts +35 -6
  87. package/src/http/auth.ts +29 -9
  88. package/src/http/cors.ts +3 -0
  89. package/src/http/sse.ts +229 -5
  90. package/src/index.ts +119 -20
  91. package/src/integrations/api-integrations.ts +365 -0
  92. package/src/integrations/atlassian.ts +1621 -0
  93. package/src/integrations/fiken.ts +1230 -0
  94. package/src/integrations/github-skill-source.ts +142 -0
  95. package/src/integrations/google-drive.ts +1005 -64
  96. package/src/integrations/oauth-client.ts +123 -33
  97. package/src/integrations/provider-oauth.ts +756 -0
  98. package/src/integrations/slack-bot.ts +669 -11
  99. package/src/integrations/slack-interactions.ts +2063 -93
  100. package/src/integrations/social-api.ts +11 -0
  101. package/src/interaction-holder-heartbeat.ts +95 -0
  102. package/src/interaction-metrics.ts +159 -0
  103. package/src/mcp/documents.ts +146 -6
  104. package/src/mcp/editable-artifact-query-schema.ts +236 -0
  105. package/src/mcp/editable-artifacts.ts +448 -0
  106. package/src/mcp/scheduled-task-view.ts +34 -0
  107. package/src/mcp/server.ts +1756 -355
  108. package/src/memory-slack-delivery.ts +209 -0
  109. package/src/model-catalog.ts +19 -6
  110. package/src/routes/api-integrations.ts +410 -0
  111. package/src/routes/browser-identities.ts +160 -0
  112. package/src/routes/browser-sessions.ts +4482 -0
  113. package/src/routes/canonical-human-identities.ts +156 -0
  114. package/src/routes/channels.ts +90 -0
  115. package/src/routes/company-profile.ts +255 -0
  116. package/src/routes/computer-sessions.ts +1602 -0
  117. package/src/routes/connections.ts +421 -99
  118. package/src/routes/documents.ts +22 -3
  119. package/src/routes/editable-artifacts.ts +1159 -0
  120. package/src/routes/files.ts +106 -4
  121. package/src/routes/github.ts +18 -2
  122. package/src/routes/install.ts +7 -1
  123. package/src/routes/integration-facets.ts +258 -0
  124. package/src/routes/interaction-resources.ts +595 -0
  125. package/src/routes/memory-slack-publications.ts +216 -0
  126. package/src/routes/organization-memberships.ts +53 -0
  127. package/src/routes/packs.ts +437 -7
  128. package/src/routes/plugins.ts +752 -0
  129. package/src/routes/rigs.ts +77 -20
  130. package/src/routes/scheduled-tasks.ts +67 -40
  131. package/src/routes/sessions.ts +830 -278
  132. package/src/routes/skills.ts +256 -0
  133. package/src/routes/slack-task-policy.ts +115 -0
  134. package/src/routes/supergrok.ts +717 -0
  135. package/src/routes/transcription-recordings.ts +9 -2
  136. package/src/routes/transcriptions.ts +2 -1
  137. package/src/routes/video-generation.ts +162 -0
  138. package/src/routes/workspaces.ts +68 -28
  139. package/src/sandbox/channel-a.ts +750 -116
  140. package/src/sandbox/machines.ts +5 -5
  141. package/src/sandbox/metrics-ingestion.ts +121 -3
  142. package/src/sandbox/rematerialize.ts +35 -47
  143. package/src/sandbox/viewer.ts +39 -12
  144. package/src/slack-reaction-files.ts +181 -0
  145. package/src/temporal-schedule-cleanup.ts +135 -0
  146. package/src/transcription/providers/xai-subscription.ts +110 -0
  147. package/src/transcription/service.ts +17 -2
  148. package/src/xai-realtime.ts +216 -0
  149. package/src/xai-subscription-auth.ts +132 -0
  150. package/dist/chunk-T4T2PGU4.js.map +0 -1
  151. package/dist/mcp/toolspace.d.ts +0 -71
  152. package/src/mcp/toolspace.ts +0 -1190
package/src/app.ts CHANGED
@@ -3,15 +3,19 @@ import {
3
3
  configuredAllowedModels,
4
4
  configuredAllowedReasoningEfforts,
5
5
  configuredModels,
6
+ resolveFirstPartyMcpToolPolicy,
6
7
  withCodexCatalogProvider,
8
+ withXaiSubscriptionCatalogProvider,
7
9
  } from "@opengeni/config";
8
10
  import {
9
11
  ClientConfig,
12
+ CodemodeCallRequest,
10
13
  ErrorEnvelope,
11
14
  OPENGENI_API_CONTRACT_HEADER,
12
15
  OPENGENI_API_CONTRACT_REVISION,
13
16
  OPENGENI_CORRELATION_HEADER,
14
17
  resolveWorkspaceMemoryEnabled,
18
+ resolveWorkspaceMemoryPromptMode,
15
19
  VOICE_INPUT_ACCEPTED_MIME_TYPES,
16
20
  TRANSCRIPTION_RECORDING_PROVIDER_SEGMENT_SECONDS,
17
21
  type AccessGrant,
@@ -23,7 +27,18 @@ import {
23
27
  indexDocumentNow,
24
28
  type DocumentServices,
25
29
  } from "@opengeni/documents";
26
- import { dbSql, getWorkspace, rlsContextForWorkspace } from "@opengeni/db";
30
+ import {
31
+ CodemodeCallBudgetExceededError,
32
+ CodemodeOperationConflictError,
33
+ CodemodeOperationNotExecutableError,
34
+ CodemodePayloadTooLargeError,
35
+ CodemodeToolApprovalRequiredError,
36
+ CodemodeToolNotInCatalogError,
37
+ dbSql,
38
+ getWorkspace,
39
+ rlsContextForWorkspace,
40
+ withSessionRlsActorContext,
41
+ } from "@opengeni/db";
27
42
  import { createObservability } from "@opengeni/observability";
28
43
  import { createObjectStorage } from "@opengeni/storage";
29
44
  import { WebStandardStreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/webStandardStreamableHttp.js";
@@ -39,6 +54,7 @@ import {
39
54
  CodexCompactionV2ProviderLockedError,
40
55
  hasPermission,
41
56
  requireAccessGrant,
57
+ requireLiveAgentAttemptAuthorization,
42
58
  requirePermission,
43
59
  requireSessionAuthorization,
44
60
  SessionAuthorizationDeniedError,
@@ -48,39 +64,69 @@ import { createManagedAuth } from "./auth/managed-auth";
48
64
  import { createApiSandboxClient, makeResumeBoxById } from "./sandbox/access";
49
65
  import { requireLimit } from "@opengeni/core";
50
66
  import { buildOpenGeniMcpServer } from "./mcp/server";
51
- import { isToolspaceGrant, prepareToolspaceMcpSurface } from "./mcp/toolspace";
67
+ import {
68
+ CodemodeAuthorityError,
69
+ isCodemodeGrant,
70
+ readCodemodeOperation,
71
+ requireActiveCodemodeCatalog,
72
+ submitAndDispatchCodemodeCall,
73
+ } from "./codemode";
52
74
  import { boundedMcpRequest, McpPayloadTooLargeError } from "@opengeni/runtime/mcp-network";
75
+ import {
76
+ BrowserControlProtocolError,
77
+ BrowserControlTransportError,
78
+ } from "@opengeni/runtime/sandbox";
53
79
  import { requireAccessKey } from "./http/auth";
80
+ import { allowedCorsOrigin } from "./http/cors";
54
81
  import { registerCapabilityRoutes } from "./routes/capabilities";
55
82
  import { registerCatalogAssetRoutes } from "./routes/catalog-assets";
56
83
  import { registerCodexRoutes } from "./routes/codex";
84
+ import { registerSuperGrokRoutes } from "./routes/supergrok";
57
85
  import { registerConnectionRoutes } from "./routes/connections";
58
86
  import { registerDocumentRoutes } from "./routes/documents";
59
87
  import { registerEnrollmentRoutes } from "./routes/enrollments";
60
88
  import { registerMachineRoutes } from "./routes/machines";
89
+ import { registerMemorySlackPublicationRoutes } from "./routes/memory-slack-publications";
61
90
  import { registerEnvironmentRoutes } from "./routes/environments";
62
91
  import { registerFileRoutes } from "./routes/files";
63
92
  import { registerApiKeyRoutes } from "./routes/api-keys";
64
93
  import { registerBillingRoutes } from "./routes/billing";
94
+ import { registerBrowserIdentityRoutes } from "./routes/browser-identities";
95
+ import { registerBrowserSessionRoutes } from "./routes/browser-sessions";
96
+ import { registerComputerSessionRoutes } from "./routes/computer-sessions";
65
97
  import { registerGitHubRoutes } from "./routes/github";
66
98
  import { registerInstallRoutes } from "./routes/install";
99
+ import { registerApiIntegrationRoutes } from "./routes/api-integrations";
100
+ import { registerIntegrationFacetRoutes } from "./routes/integration-facets";
101
+ import { registerInteractionResourceRoutes } from "./routes/interaction-resources";
67
102
  import { registerPackRoutes } from "./routes/packs";
103
+ import { registerPluginRoutes } from "./routes/plugins";
104
+ import { registerSkillRoutes } from "./routes/skills";
105
+ import { registerChannelRoutes } from "./routes/channels";
68
106
  import { registerRigRoutes } from "./routes/rigs";
69
107
  import { registerScheduledTaskRoutes } from "./routes/scheduled-tasks";
70
108
  import { registerSessionRoutes } from "./routes/sessions";
71
109
  import { registerSocialRoutes } from "./routes/social";
72
110
  import { registerWorkspaceRoutes } from "./routes/workspaces";
73
111
  import { registerWorkspaceInstructionPolicyRoutes } from "./routes/workspace-instruction-policies";
112
+ import { registerCompanyProfileRoutes } from "./routes/company-profile";
113
+ import { registerSlackTaskPolicyRoutes } from "./routes/slack-task-policy";
74
114
  import { registerWorkspaceStateRoutes } from "./routes/workspace-state";
75
115
  import { registerWorkspaceArtifactRoutes } from "./routes/workspace-artifacts";
76
116
  import { registerPreferenceRegistryRoutes } from "./routes/preference-registry";
77
117
  import { registerInsightsRoutes } from "./routes/insights";
78
118
  import { registerTranscriptionRoutes } from "./routes/transcriptions";
119
+ import { registerEditableArtifactRoutes } from "./routes/editable-artifacts";
120
+ import { registerVideoGenerationRoutes } from "./routes/video-generation";
121
+ import { registerCanonicalHumanIdentityRoutes } from "./routes/canonical-human-identities";
122
+ import { registerOrganizationMembershipRoutes } from "./routes/organization-memberships";
79
123
  import { projectClientModel } from "./model-catalog";
80
124
  import { createTranscriptionService } from "./transcription/service";
81
125
  import { createFfmpegTranscriptionSegmenter } from "./transcription/segmenter";
82
126
  import { registerSlackInteractionRoutes } from "./integrations/slack-interactions";
83
127
 
128
+ export { allowedCorsOrigin } from "./http/cors";
129
+
84
130
  export type {
85
131
  ApiRouteDeps,
86
132
  AppDependencies,
@@ -102,7 +148,7 @@ export {
102
148
  export { workflowIdForSession } from "@opengeni/core";
103
149
  export { replaySessionEvents, sseSessionStream, sseWorkspaceControlStream } from "./http/sse";
104
150
 
105
- export const API_MAX_REQUEST_BODY_BYTES = 8 * 1024 * 1024;
151
+ export const API_MAX_REQUEST_BODY_BYTES = 32 * 1024 * 1024;
106
152
 
107
153
  /** Effective Hono bodyLimit — API JSON ceiling or voice multipart + multipart overhead. */
108
154
  export function apiRequestBodyLimitBytes(settings: {
@@ -249,12 +295,18 @@ export function createAppComposition(deps: AppDependencies): {
249
295
  "Accept",
250
296
  "Authorization",
251
297
  "Content-Type",
298
+ "Range",
252
299
  "X-OpenGeni-Access-Key",
253
300
  "X-OpenGeni-Api-Contract",
254
301
  "X-OpenGeni-Correlation-Id",
255
302
  "X-OpenGeni-Subject",
256
303
  ],
257
- exposeHeaders: ["X-OpenGeni-Api-Contract", "X-OpenGeni-Correlation-Id"],
304
+ exposeHeaders: [
305
+ "Accept-Ranges",
306
+ "Content-Range",
307
+ "X-OpenGeni-Api-Contract",
308
+ "X-OpenGeni-Correlation-Id",
309
+ ],
258
310
  };
259
311
  const publicApiCors = cors({
260
312
  ...corsHeaders,
@@ -425,9 +477,12 @@ export function createAppComposition(deps: AppDependencies): {
425
477
 
426
478
  app.get("/v1/config/client", async (c) => {
427
479
  c.header("cache-control", "no-store");
428
- const catalogSettings = deps.settings.codexSubscriptionEnabled
480
+ const codexCatalogSettings = deps.settings.codexSubscriptionEnabled
429
481
  ? withCodexCatalogProvider(deps.settings)
430
482
  : deps.settings;
483
+ const catalogSettings = deps.settings.supergrokSubscriptionEnabled
484
+ ? withXaiSubscriptionCatalogProvider(codexCatalogSettings)
485
+ : codexCatalogSettings;
431
486
  return c.json(
432
487
  ClientConfig.parse({
433
488
  deploymentRevision: deps.settings.deploymentRevision,
@@ -446,6 +501,7 @@ export function createAppComposition(deps: AppDependencies): {
446
501
  id: server.id,
447
502
  name: server.name ?? server.id,
448
503
  })),
504
+ firstPartyMcpTools: resolveFirstPartyMcpToolPolicy(deps.settings),
449
505
  fileUploads: {
450
506
  enabled: objectStorage !== null,
451
507
  maxSizeBytes: objectStorage?.maxSinglePutSizeBytes ?? 5_000_000_000,
@@ -485,6 +541,22 @@ export function createAppComposition(deps: AppDependencies): {
485
541
  );
486
542
  });
487
543
 
544
+ app.use("/v1/workspaces/:workspaceId/*", async (c, next) => {
545
+ const workspaceId = c.req.param("workspaceId");
546
+ if (workspaceRequestRequiresCodexAccountPrevalidation(c.req.raw)) {
547
+ const grant = await requireAccessGrant(c, routeDeps, workspaceId);
548
+ await validateCodexAccountTarget(c.req.raw);
549
+ await withAccessGrantSessionRlsContext(routeDeps, grant, next);
550
+ return;
551
+ }
552
+ if (workspaceActorContextExempt(c.req.method, new URL(c.req.url).pathname)) {
553
+ await next();
554
+ return;
555
+ }
556
+ const grant = await requireAccessGrant(c, routeDeps, workspaceId);
557
+ await withAccessGrantSessionRlsContext(routeDeps, grant, next);
558
+ });
559
+
488
560
  app.all("/v1/workspaces/:workspaceId/mcp", async (c) => {
489
561
  const workspaceId = c.req.param("workspaceId");
490
562
  let boundedRequest: Request;
@@ -499,89 +571,138 @@ export function createAppComposition(deps: AppDependencies): {
499
571
  throw error;
500
572
  }
501
573
  const grant = await requireMcpAccessGrant(c, routeDeps, workspaceId);
502
- const toolspaceGrant = isToolspaceGrant(routeDeps.settings, grant);
503
- const boundSessionId = grant.metadata?.sessionId;
504
- if (toolspaceGrant || typeof boundSessionId === "string") {
505
- if (typeof boundSessionId !== "string") {
506
- throw new HTTPException(404, { message: "session not found" });
507
- }
508
- try {
509
- await requireSessionAuthorization(routeDeps, grant, {
510
- sessionId: boundSessionId,
511
- operation: toolspaceGrant ? "session.toolspace.call" : "session.first_party_mcp.call",
512
- surface: toolspaceGrant ? "toolspace" : "first_party_mcp",
513
- });
514
- } catch (error) {
515
- if (error instanceof SessionAuthorizationDeniedError) {
516
- throw new HTTPException(404, { message: "session not found" });
517
- }
518
- if (error instanceof SessionAuthorizationUnavailableError) {
519
- throw new HTTPException(503, {
520
- message: "session authorization is unavailable",
574
+ return await withAccessGrantSessionRlsContext(routeDeps, grant, async () => {
575
+ const boundSessionId = grant.metadata?.sessionId;
576
+ if (typeof boundSessionId === "string") {
577
+ try {
578
+ await requireSessionAuthorization(routeDeps, grant, {
579
+ sessionId: boundSessionId,
580
+ operation: "session.first_party_mcp.call",
581
+ surface: "first_party_mcp",
521
582
  });
583
+ } catch (error) {
584
+ if (error instanceof SessionAuthorizationDeniedError) {
585
+ throw new HTTPException(404, { message: "session not found" });
586
+ }
587
+ if (error instanceof SessionAuthorizationUnavailableError) {
588
+ throw new HTTPException(503, {
589
+ message: "session authorization is unavailable",
590
+ });
591
+ }
592
+ throw error;
522
593
  }
523
- throw error;
524
594
  }
595
+ const workspace = await getWorkspace(routeDeps.db, workspaceId);
596
+ const workspaceMemoryEnabled = resolveWorkspaceMemoryEnabled(workspace?.settings);
597
+ const workspaceMemoryPromptMode = resolveWorkspaceMemoryPromptMode(workspace?.settings);
598
+ const transport = new WebStandardStreamableHTTPServerTransport({
599
+ enableJsonResponse: true,
600
+ });
601
+ const mcp = buildOpenGeniMcpServer(routeDeps, grant, {
602
+ requestOrigin: new URL(c.req.url).origin,
603
+ workspaceMemoryEnabled,
604
+ workspaceMemoryPromptMode,
605
+ });
606
+ await mcp.connect(transport);
607
+ return await transport.handleRequest(boundedRequest);
608
+ });
609
+ });
610
+
611
+ app.get("/v1/workspaces/:workspaceId/codemode/catalog", async (c) => {
612
+ const workspaceId = c.req.param("workspaceId");
613
+ const grant = await requireAccessGrant(c, routeDeps, workspaceId);
614
+ if (!isCodemodeGrant(grant)) {
615
+ throw new HTTPException(403, { message: "Codemode access denied" });
525
616
  }
526
- let toolspace: Awaited<ReturnType<typeof prepareToolspaceMcpSurface>> = null;
527
- if (toolspaceGrant) {
528
- try {
529
- toolspace = await prepareToolspaceMcpSurface({
530
- deps: routeDeps,
531
- grant,
532
- });
533
- } catch (error) {
534
- if (error instanceof McpPayloadTooLargeError) {
535
- throw new HTTPException(413, {
536
- message: "MCP tool list exceeds the safety limit",
537
- });
538
- }
539
- throw error;
540
- }
617
+ try {
618
+ return c.json((await requireActiveCodemodeCatalog(routeDeps, grant)).catalog);
619
+ } catch (error) {
620
+ throw codemodeHttpError(error);
621
+ }
622
+ });
623
+
624
+ app.post("/v1/workspaces/:workspaceId/codemode/calls", async (c) => {
625
+ const workspaceId = c.req.param("workspaceId");
626
+ const grant = await requireAccessGrant(c, routeDeps, workspaceId);
627
+ if (!isCodemodeGrant(grant)) {
628
+ throw new HTTPException(403, { message: "Codemode access denied" });
629
+ }
630
+ const parsed = CodemodeCallRequest.safeParse(await c.req.json().catch(() => null));
631
+ if (!parsed.success) {
632
+ throw new HTTPException(400, { message: "Invalid Codemode call" });
541
633
  }
542
- const workspace = await getWorkspace(routeDeps.db, workspaceId);
543
- const workspaceMemoryEnabled = resolveWorkspaceMemoryEnabled(workspace?.settings);
544
- const transport = new WebStandardStreamableHTTPServerTransport({
545
- enableJsonResponse: true,
546
- });
547
- const mcp = buildOpenGeniMcpServer(routeDeps, grant, {
548
- requestOrigin: new URL(c.req.url).origin,
549
- toolspace,
550
- workspaceMemoryEnabled,
551
- });
552
634
  try {
553
- await mcp.connect(transport);
554
- return await transport.handleRequest(boundedRequest);
555
- } finally {
556
- await toolspace?.close().catch(() => undefined);
635
+ const submission = await submitAndDispatchCodemodeCall(routeDeps, grant, parsed.data);
636
+ const terminal = ["completed", "failed", "outcome_unknown", "cancelled"].includes(
637
+ submission.operation.state,
638
+ );
639
+ return c.json(submission, terminal ? 200 : 202);
640
+ } catch (error) {
641
+ throw codemodeHttpError(error);
642
+ }
643
+ });
644
+
645
+ app.get("/v1/workspaces/:workspaceId/codemode/calls/:operationId", async (c) => {
646
+ const workspaceId = c.req.param("workspaceId");
647
+ const grant = await requireAccessGrant(c, routeDeps, workspaceId);
648
+ if (!isCodemodeGrant(grant)) {
649
+ throw new HTTPException(403, { message: "Codemode access denied" });
650
+ }
651
+ try {
652
+ const operation = await readCodemodeOperation(routeDeps, grant, c.req.param("operationId"));
653
+ if (!operation)
654
+ throw new HTTPException(404, {
655
+ message: "Codemode operation not found",
656
+ });
657
+ return c.json(operation);
658
+ } catch (error) {
659
+ if (error instanceof HTTPException) throw error;
660
+ throw codemodeHttpError(error);
557
661
  }
558
662
  });
559
663
 
560
664
  registerFileRoutes(app, routeDeps);
561
665
  registerApiKeyRoutes(app, routeDeps);
562
666
  registerBillingRoutes(app, routeDeps);
667
+ registerBrowserIdentityRoutes(app, routeDeps);
668
+ registerBrowserSessionRoutes(app, routeDeps);
669
+ registerComputerSessionRoutes(app, routeDeps);
563
670
  registerDocumentRoutes(app, routeDeps);
564
671
  registerGitHubRoutes(app, routeDeps);
565
672
  registerInstallRoutes(app, routeDeps);
673
+ registerInteractionResourceRoutes(app, routeDeps);
566
674
  registerWorkspaceRoutes(app, routeDeps);
567
675
  registerInsightsRoutes(app, routeDeps);
568
676
  registerWorkspaceInstructionPolicyRoutes(app, routeDeps);
677
+ registerCompanyProfileRoutes(app, routeDeps);
678
+ registerSlackTaskPolicyRoutes(app, routeDeps);
569
679
  registerWorkspaceStateRoutes(app, routeDeps);
680
+ registerMemorySlackPublicationRoutes(app, routeDeps);
570
681
  registerWorkspaceArtifactRoutes(app, routeDeps);
571
682
  registerPreferenceRegistryRoutes(app, routeDeps);
572
683
  registerSocialRoutes(app, routeDeps);
573
684
  registerConnectionRoutes(app, routeDeps);
574
685
  registerCapabilityRoutes(app, routeDeps);
686
+ registerApiIntegrationRoutes(app, routeDeps);
687
+ registerIntegrationFacetRoutes(app, routeDeps);
575
688
  registerCatalogAssetRoutes(app, routeDeps);
576
689
  registerEnrollmentRoutes(app, routeDeps);
577
690
  registerMachineRoutes(app, routeDeps);
578
691
  registerEnvironmentRoutes(app, routeDeps);
692
+ registerChannelRoutes(app, routeDeps);
579
693
  registerRigRoutes(app, routeDeps);
580
694
  registerPackRoutes(app, routeDeps);
695
+ registerPluginRoutes(app, routeDeps);
696
+ registerSkillRoutes(app, routeDeps);
581
697
  registerSessionRoutes(app, routeDeps);
582
698
  registerScheduledTaskRoutes(app, routeDeps);
583
699
  registerCodexRoutes(app, routeDeps);
700
+ registerSuperGrokRoutes(app, routeDeps);
584
701
  registerTranscriptionRoutes(app, routeDeps);
702
+ registerEditableArtifactRoutes(app, routeDeps);
703
+ registerVideoGenerationRoutes(app, routeDeps);
704
+ registerCanonicalHumanIdentityRoutes(app, routeDeps);
705
+ registerOrganizationMembershipRoutes(app, routeDeps);
585
706
  registerSlackInteractionRoutes(app, routeDeps);
586
707
 
587
708
  app.notFound((c) => {
@@ -623,6 +744,7 @@ export function createAppComposition(deps: AppDependencies): {
623
744
  ? (boundedPublicMessage(apiError.message) ?? "Request failed.")
624
745
  : publicErrorMessage(error, status),
625
746
  retryable: apiError?.retryable ?? retryableHttpStatus(status),
747
+ ...(mutationOutcomeUnknown(error, c.req.method) ? { outcomeUnknown: true } : {}),
626
748
  requestId,
627
749
  ...(apiError?.details ? { details: apiError.details } : {}),
628
750
  },
@@ -633,6 +755,14 @@ export function createAppComposition(deps: AppDependencies): {
633
755
  return { app, routeDeps };
634
756
  }
635
757
 
758
+ function mutationOutcomeUnknown(error: unknown, method: string): boolean {
759
+ if (method === "GET" || method === "HEAD" || method === "OPTIONS") return false;
760
+ const cause = error instanceof HTTPException ? error.cause : error;
761
+ return (
762
+ cause instanceof BrowserControlTransportError || cause instanceof BrowserControlProtocolError
763
+ );
764
+ }
765
+
636
766
  export function appendVary(current: string | null, value: string): string {
637
767
  const values = (current ?? "")
638
768
  .split(",")
@@ -644,6 +774,74 @@ export function appendVary(current: string | null, value: string): string {
644
774
  return values.join(", ");
645
775
  }
646
776
 
777
+ const publicWorkspaceBrowserRoutePatterns = [
778
+ /^\/v1\/workspaces\/[^/]+\/github\/connect$/,
779
+ /^\/v1\/workspaces\/[^/]+\/github\/installations\/[^/]+\/configure$/,
780
+ /^\/v1\/workspaces\/[^/]+\/github\/installations\/select$/,
781
+ ] as const;
782
+
783
+ export function workspaceActorContextExempt(method: string, pathname: string): boolean {
784
+ if (/^\/v1\/workspaces\/[^/]+\/mcp$/.test(pathname)) return true;
785
+ if (
786
+ method === "GET" &&
787
+ publicWorkspaceBrowserRoutePatterns.some((route) => route.test(pathname))
788
+ ) {
789
+ return true;
790
+ }
791
+ return method === "POST" && /^\/v1\/workspaces\/[^/]+\/github\/installations$/.test(pathname);
792
+ }
793
+
794
+ function workspaceRequestRequiresCodexAccountPrevalidation(request: Request): boolean {
795
+ return (
796
+ request.method === "POST" &&
797
+ /^\/v1\/workspaces\/[^/]+\/sessions\/[^/]+\/codex-account$/.test(new URL(request.url).pathname)
798
+ );
799
+ }
800
+
801
+ async function validateCodexAccountTarget(request: Request): Promise<void> {
802
+ const body = (await request
803
+ .clone()
804
+ .json()
805
+ .catch(() => null)) as { target?: unknown } | null;
806
+ if (typeof body?.target !== "string" || body.target.length === 0) {
807
+ throw new HTTPException(400, {
808
+ message: 'target is required ("auto" or an account id)',
809
+ });
810
+ }
811
+ }
812
+
813
+ async function withAccessGrantSessionRlsContext<T>(
814
+ deps: ApiRouteDeps,
815
+ grant: AccessGrant,
816
+ fn: () => Promise<T>,
817
+ ): Promise<T> {
818
+ if (grant.principalKind !== "agent_attempt") {
819
+ return await withSessionRlsActorContext({ subjectId: grant.subjectId }, fn);
820
+ }
821
+ const callerSessionId = grant.metadata?.sessionId;
822
+ if (typeof callerSessionId !== "string") {
823
+ throw new HTTPException(403, { message: "agent attempt authority is invalid" });
824
+ }
825
+ try {
826
+ const actor = await requireLiveAgentAttemptAuthorization(deps.db, grant, callerSessionId);
827
+ return await withSessionRlsActorContext(
828
+ {
829
+ subjectId: actor.subjectId,
830
+ initiatingHumanSubjectId: actor.initiatingHumanSubjectId,
831
+ },
832
+ fn,
833
+ );
834
+ } catch (error) {
835
+ if (error instanceof SessionAuthorizationDeniedError) {
836
+ throw new HTTPException(403, {
837
+ message: "agent attempt authority is invalid",
838
+ cause: error,
839
+ });
840
+ }
841
+ throw error;
842
+ }
843
+ }
844
+
647
845
  async function requireMcpAccessGrant(
648
846
  c: Parameters<typeof requireAccessGrant>[0],
649
847
  deps: ApiRouteDeps,
@@ -653,8 +851,8 @@ async function requireMcpAccessGrant(
653
851
  if (hasPermission(grant.permissions, "workspace:read")) {
654
852
  return grant;
655
853
  }
656
- if (isToolspaceGrant(deps.settings, grant)) {
657
- return grant;
854
+ if (isCodemodeGrant(grant)) {
855
+ requirePermission(grant, "workspace:read");
658
856
  }
659
857
  // A worker-signed session-bound grant is allowed to reach the transport
660
858
  // without inheriting broad workspace read access. The exact session
@@ -687,6 +885,48 @@ function clientAuthConfig(settings: AppDependencies["settings"]) {
687
885
  return { mode: "none" as const };
688
886
  }
689
887
 
888
+ function codemodeHttpError(error: unknown): HTTPException {
889
+ if (error instanceof SessionAuthorizationDeniedError) {
890
+ return new HTTPException(404, {
891
+ message: "session not found",
892
+ cause: error,
893
+ });
894
+ }
895
+ if (error instanceof SessionAuthorizationUnavailableError) {
896
+ return new HTTPException(503, {
897
+ message: "session authorization is unavailable",
898
+ cause: error,
899
+ });
900
+ }
901
+ if (
902
+ error instanceof CodemodeAuthorityError ||
903
+ error instanceof CodemodeOperationNotExecutableError
904
+ ) {
905
+ return new HTTPException(409, { message: error.message, cause: error });
906
+ }
907
+ if (error instanceof CodemodeOperationConflictError) {
908
+ return new HTTPException(409, { message: error.message, cause: error });
909
+ }
910
+ if (error instanceof CodemodeCallBudgetExceededError) {
911
+ return new HTTPException(429, { message: error.message, cause: error });
912
+ }
913
+ if (error instanceof CodemodeToolNotInCatalogError) {
914
+ return new HTTPException(404, { message: error.message, cause: error });
915
+ }
916
+ if (error instanceof CodemodeToolApprovalRequiredError) {
917
+ return new HTTPException(409, { message: error.message, cause: error });
918
+ }
919
+ if (error instanceof CodemodePayloadTooLargeError) {
920
+ return new HTTPException(413, { message: error.message, cause: error });
921
+ }
922
+ return error instanceof HTTPException
923
+ ? error
924
+ : new HTTPException(500, {
925
+ message: "Codemode request failed",
926
+ cause: error,
927
+ });
928
+ }
929
+
690
930
  function clientAnalyticsConfig(settings: AppDependencies["settings"]) {
691
931
  if (!settings.analyticsEnabled) {
692
932
  return { consentRequired: true, providers: {} };
@@ -721,10 +961,6 @@ function structuredServicesHint(backend: string): {
721
961
  return { fileSystem: hasBox, git: hasBox, terminalEvents: hasBox };
722
962
  }
723
963
 
724
- export function allowedCorsOrigin(pattern: string, origin: string): boolean {
725
- return new RegExp(`^(?:${pattern})$`).test(origin);
726
- }
727
-
728
964
  function codexCompactionV2ProviderLockedError(
729
965
  error: unknown,
730
966
  ): CodexCompactionV2ProviderLockedError | null {
@@ -875,7 +1111,10 @@ async function withTimeout<T>(promise: Promise<T>, timeoutMs: number): Promise<T
875
1111
  }
876
1112
  }
877
1113
 
878
- const routeLabelPatterns: Array<{ pattern: RegExp; label: string }> = [
1114
+ const routeLabelPatterns: Array<{
1115
+ pattern: RegExp;
1116
+ label: string | ((match: RegExpMatchArray) => string);
1117
+ }> = [
879
1118
  { pattern: /^\/healthz$/, label: "/healthz" },
880
1119
  { pattern: /^\/readyz$/, label: "/readyz" },
881
1120
  { pattern: /^\/traffic-readyz$/, label: "/traffic-readyz" },
@@ -891,6 +1130,22 @@ const routeLabelPatterns: Array<{ pattern: RegExp; label: string }> = [
891
1130
  pattern: /^\/v1\/workspaces\/[^/]+\/codex\/status$/,
892
1131
  label: "/v1/workspaces/:workspaceId/codex/status",
893
1132
  },
1133
+ {
1134
+ pattern: /^\/v1\/workspaces\/[^/]+\/supergrok\/connect\/(start|poll)$/,
1135
+ label: (match) => `/v1/workspaces/:workspaceId/supergrok/connect/${match[1]}`,
1136
+ },
1137
+ {
1138
+ pattern: /^\/v1\/workspaces\/[^/]+\/supergrok\/(status|accounts|settings)$/,
1139
+ label: (match) => `/v1/workspaces/:workspaceId/supergrok/${match[1]}`,
1140
+ },
1141
+ {
1142
+ pattern: /^\/v1\/workspaces\/[^/]+\/supergrok\/accounts\/[^/]+\/(activate|allocator)$/,
1143
+ label: (match) => `/v1/workspaces/:workspaceId/supergrok/accounts/:accountId/${match[1]}`,
1144
+ },
1145
+ {
1146
+ pattern: /^\/v1\/workspaces\/[^/]+\/supergrok\/accounts\/[^/]+$/,
1147
+ label: "/v1/workspaces/:workspaceId/supergrok/accounts/:accountId",
1148
+ },
894
1149
  {
895
1150
  pattern: /^\/v1\/workspaces\/[^/]+\/codex\/usage$/,
896
1151
  label: "/v1/workspaces/:workspaceId/codex/usage",
@@ -937,6 +1192,154 @@ const routeLabelPatterns: Array<{ pattern: RegExp; label: string }> = [
937
1192
  pattern: /^\/v1\/workspaces\/[^/]+\/control-events\/stream$/,
938
1193
  label: "/v1/workspaces/:workspaceId/control-events/stream",
939
1194
  },
1195
+ {
1196
+ pattern: /^\/v1\/workspaces\/[^/]+\/live-events\/stream$/,
1197
+ label: "/v1/workspaces/:workspaceId/live-events/stream",
1198
+ },
1199
+ {
1200
+ pattern: /^\/v1\/workspaces\/[^/]+\/interaction-events\/stream$/,
1201
+ label: "/v1/workspaces/:workspaceId/interaction-events/stream",
1202
+ },
1203
+ {
1204
+ pattern: /^\/v1\/workspaces\/[^/]+\/attached-browsers\/[^/]+$/,
1205
+ label: "/v1/workspaces/:workspaceId/attached-browsers/:deviceId",
1206
+ },
1207
+ {
1208
+ pattern: /^\/v1\/workspaces\/[^/]+\/attached-browsers$/,
1209
+ label: "/v1/workspaces/:workspaceId/attached-browsers",
1210
+ },
1211
+ {
1212
+ pattern: /^\/v1\/workspaces\/[^/]+\/browser-identities\/[^/]+\/revisions$/,
1213
+ label: "/v1/workspaces/:workspaceId/browser-identities/:identityId/revisions",
1214
+ },
1215
+ {
1216
+ pattern: /^\/v1\/workspaces\/[^/]+\/browser-identities\/[^/]+$/,
1217
+ label: "/v1/workspaces/:workspaceId/browser-identities/:identityId",
1218
+ },
1219
+ {
1220
+ pattern: /^\/v1\/workspaces\/[^/]+\/browser-identities$/,
1221
+ label: "/v1/workspaces/:workspaceId/browser-identities",
1222
+ },
1223
+ {
1224
+ pattern: /^\/v1\/workspaces\/[^/]+\/browser-sessions\/[^/]+\/downloads\/[^/]+\/save$/,
1225
+ label:
1226
+ "/v1/workspaces/:workspaceId/browser-sessions/:browserSessionId/downloads/:downloadId/save",
1227
+ },
1228
+ {
1229
+ pattern: /^\/v1\/workspaces\/[^/]+\/browser-sessions\/[^/]+\/downloads\/[^/]+$/,
1230
+ label: "/v1/workspaces/:workspaceId/browser-sessions/:browserSessionId/downloads/:downloadId",
1231
+ },
1232
+ {
1233
+ pattern: /^\/v1\/workspaces\/[^/]+\/browser-sessions\/[^/]+\/downloads$/,
1234
+ label: "/v1/workspaces/:workspaceId/browser-sessions/:browserSessionId/downloads",
1235
+ },
1236
+ {
1237
+ pattern:
1238
+ /^\/v1\/workspaces\/[^/]+\/browser-sessions\/[^/]+\/auth-runs\/[^/]+\/external-auth\/interactive$/,
1239
+ label:
1240
+ "/v1/workspaces/:workspaceId/browser-sessions/:browserSessionId/auth-runs/:authRunId/external-auth/interactive",
1241
+ },
1242
+ {
1243
+ pattern:
1244
+ /^\/v1\/workspaces\/[^/]+\/browser-sessions\/[^/]+\/auth-runs\/[^/]+\/(external-auth|protected-fill|report|verify)$/,
1245
+ label: (match) =>
1246
+ `/v1/workspaces/:workspaceId/browser-sessions/:browserSessionId/auth-runs/:authRunId/${match[1]}`,
1247
+ },
1248
+ {
1249
+ pattern: /^\/v1\/workspaces\/[^/]+\/browser-sessions\/[^/]+\/auth-runs\/[^/]+$/,
1250
+ label: "/v1/workspaces/:workspaceId/browser-sessions/:browserSessionId/auth-runs/:authRunId",
1251
+ },
1252
+ {
1253
+ pattern: /^\/v1\/workspaces\/[^/]+\/browser-sessions\/[^/]+\/auth-runs$/,
1254
+ label: "/v1/workspaces/:workspaceId/browser-sessions/:browserSessionId/auth-runs",
1255
+ },
1256
+ {
1257
+ pattern:
1258
+ /^\/v1\/workspaces\/[^/]+\/browser-sessions\/[^/]+\/targets\/[^/]+\/(diagnostics|observation|select)$/,
1259
+ label: (match) =>
1260
+ `/v1/workspaces/:workspaceId/browser-sessions/:browserSessionId/targets/:targetId/${match[1]}`,
1261
+ },
1262
+ {
1263
+ pattern: /^\/v1\/workspaces\/[^/]+\/browser-sessions\/[^/]+\/targets\/[^/]+$/,
1264
+ label: "/v1/workspaces/:workspaceId/browser-sessions/:browserSessionId/targets/:targetId",
1265
+ },
1266
+ {
1267
+ pattern: /^\/v1\/workspaces\/[^/]+\/browser-sessions\/[^/]+\/operations\/[^/]+$/,
1268
+ label: "/v1/workspaces/:workspaceId/browser-sessions/:browserSessionId/operations/:operationId",
1269
+ },
1270
+ {
1271
+ pattern:
1272
+ /^\/v1\/workspaces\/[^/]+\/browser-sessions\/[^/]+\/(actions|attachments|clipboard|end|heartbeat|resume|revisions|suspend|targets)$/,
1273
+ label: (match) => `/v1/workspaces/:workspaceId/browser-sessions/:browserSessionId/${match[1]}`,
1274
+ },
1275
+ {
1276
+ pattern: /^\/v1\/workspaces\/[^/]+\/browser-sessions\/[^/]+$/,
1277
+ label: "/v1/workspaces/:workspaceId/browser-sessions/:browserSessionId",
1278
+ },
1279
+ {
1280
+ pattern: /^\/v1\/workspaces\/[^/]+\/browser-sessions$/,
1281
+ label: "/v1/workspaces/:workspaceId/browser-sessions",
1282
+ },
1283
+ {
1284
+ pattern: /^\/v1\/workspaces\/[^/]+\/computer-sessions\/[^/]+\/targets\/[^/]+\/observation$/,
1285
+ label:
1286
+ "/v1/workspaces/:workspaceId/computer-sessions/:computerSessionId/targets/:targetId/observation",
1287
+ },
1288
+ {
1289
+ pattern: /^\/v1\/workspaces\/[^/]+\/computer-sessions\/[^/]+\/operations\/[^/]+$/,
1290
+ label:
1291
+ "/v1/workspaces/:workspaceId/computer-sessions/:computerSessionId/operations/:operationId",
1292
+ },
1293
+ {
1294
+ pattern:
1295
+ /^\/v1\/workspaces\/[^/]+\/computer-sessions\/[^/]+\/(actions|attachments|clipboard|end|heartbeat|targets)$/,
1296
+ label: (match) =>
1297
+ `/v1/workspaces/:workspaceId/computer-sessions/:computerSessionId/${match[1]}`,
1298
+ },
1299
+ {
1300
+ pattern: /^\/v1\/workspaces\/[^/]+\/computer-sessions\/[^/]+$/,
1301
+ label: "/v1/workspaces/:workspaceId/computer-sessions/:computerSessionId",
1302
+ },
1303
+ {
1304
+ pattern: /^\/v1\/workspaces\/[^/]+\/computer-sessions$/,
1305
+ label: "/v1/workspaces/:workspaceId/computer-sessions",
1306
+ },
1307
+ {
1308
+ pattern: /^\/v1\/workspaces\/[^/]+\/network-routes\/[^/]+$/,
1309
+ label: "/v1/workspaces/:workspaceId/network-routes/:networkRouteId",
1310
+ },
1311
+ {
1312
+ pattern: /^\/v1\/workspaces\/[^/]+\/network-routes$/,
1313
+ label: "/v1/workspaces/:workspaceId/network-routes",
1314
+ },
1315
+ {
1316
+ pattern: /^\/v1\/workspaces\/[^/]+\/site-auth-connections\/[^/]+$/,
1317
+ label: "/v1/workspaces/:workspaceId/site-auth-connections/:siteAuthConnectionId",
1318
+ },
1319
+ {
1320
+ pattern: /^\/v1\/workspaces\/[^/]+\/site-auth-connections$/,
1321
+ label: "/v1/workspaces/:workspaceId/site-auth-connections",
1322
+ },
1323
+ {
1324
+ pattern: /^\/v1\/workspaces\/[^/]+\/auth-runs\/[^/]+$/,
1325
+ label: "/v1/workspaces/:workspaceId/auth-runs/:authRunId",
1326
+ },
1327
+ {
1328
+ pattern: /^\/v1\/workspaces\/[^/]+\/auth-runs$/,
1329
+ label: "/v1/workspaces/:workspaceId/auth-runs",
1330
+ },
1331
+ {
1332
+ pattern: /^\/v1\/workspaces\/[^/]+\/interaction-interventions\/[^/]+\/resolve$/,
1333
+ label: "/v1/workspaces/:workspaceId/interaction-interventions/:interventionId/resolve",
1334
+ },
1335
+ {
1336
+ pattern: /^\/v1\/workspaces\/[^/]+\/interaction-interventions\/[^/]+$/,
1337
+ label: "/v1/workspaces/:workspaceId/interaction-interventions/:interventionId",
1338
+ },
1339
+ {
1340
+ pattern: /^\/v1\/workspaces\/[^/]+\/interaction-interventions$/,
1341
+ label: "/v1/workspaces/:workspaceId/interaction-interventions",
1342
+ },
940
1343
  {
941
1344
  pattern: /^\/v1\/workspaces\/[^/]+\/control-events$/,
942
1345
  label: "/v1/workspaces/:workspaceId/control-events",
@@ -945,6 +1348,23 @@ const routeLabelPatterns: Array<{ pattern: RegExp; label: string }> = [
945
1348
  pattern: /^\/v1\/workspaces\/[^/]+\/inference-control$/,
946
1349
  label: "/v1/workspaces/:workspaceId/inference-control",
947
1350
  },
1351
+ {
1352
+ pattern:
1353
+ /^\/v1\/workspaces\/[^/]+\/sessions\/[^/]+\/fs\/(list|list-batch|read|write|delete|move|mkdir)$/,
1354
+ label: "/v1/workspaces/:workspaceId/sessions/:id/fs/:operation",
1355
+ },
1356
+ {
1357
+ pattern: /^\/v1\/workspaces\/[^/]+\/sessions\/[^/]+\/git\/(status|diff|read-batch|log|show)$/,
1358
+ label: "/v1/workspaces/:workspaceId/sessions/:id/git/:operation",
1359
+ },
1360
+ {
1361
+ pattern: /^\/v1\/workspaces\/[^/]+\/sessions\/[^/]+\/terminal\/(exec|pty)$/,
1362
+ label: "/v1/workspaces/:workspaceId/sessions/:id/terminal/:operation",
1363
+ },
1364
+ {
1365
+ pattern: /^\/v1\/workspaces\/[^/]+\/sessions\/[^/]+\/terminal\/pty\/(write|resize|close)$/,
1366
+ label: "/v1/workspaces/:workspaceId/sessions/:id/terminal/pty/:action",
1367
+ },
948
1368
  {
949
1369
  pattern: /^\/v1\/workspaces\/[^/]+\/sessions\/[^/]+\/events\/stream$/,
950
1370
  label: "/v1/workspaces/:workspaceId/sessions/:id/events/stream",
@@ -1141,6 +1561,80 @@ const routeLabelPatterns: Array<{ pattern: RegExp; label: string }> = [
1141
1561
  pattern: /^\/v1\/workspaces\/[^/]+\/capabilities\/[^/]+\/disable$/,
1142
1562
  label: "/v1/workspaces/:workspaceId/capabilities/:id/disable",
1143
1563
  },
1564
+ {
1565
+ pattern: /^\/v1\/workspaces\/[^/]+\/integrations\/preview$/,
1566
+ label: "/v1/workspaces/:workspaceId/integrations/preview",
1567
+ },
1568
+ {
1569
+ pattern: /^\/v1\/workspaces\/[^/]+\/integrations\/install$/,
1570
+ label: "/v1/workspaces/:workspaceId/integrations/install",
1571
+ },
1572
+ {
1573
+ pattern: /^\/v1\/workspaces\/[^/]+\/integrations\/definitions$/,
1574
+ label: "/v1/workspaces/:workspaceId/integrations/definitions",
1575
+ },
1576
+ {
1577
+ pattern: /^\/v1\/workspaces\/[^/]+\/integrations\/[^/]+\/instances\/[^/]+\/uninstall-preview$/,
1578
+ label:
1579
+ "/v1/workspaces/:workspaceId/integrations/:capabilityId/instances/:instanceKey/uninstall-preview",
1580
+ },
1581
+ {
1582
+ pattern:
1583
+ /^\/v1\/workspaces\/[^/]+\/integrations\/[^/]+\/instances\/[^/]+\/facets\/[^/]+\/browse$/,
1584
+ label:
1585
+ "/v1/workspaces/:workspaceId/integrations/:capabilityId/instances/:instanceKey/facets/:facetKey/browse",
1586
+ },
1587
+ {
1588
+ pattern:
1589
+ /^\/v1\/workspaces\/[^/]+\/integrations\/[^/]+\/instances\/[^/]+\/facets\/[^/]+\/source$/,
1590
+ label:
1591
+ "/v1/workspaces/:workspaceId/integrations/:capabilityId/instances/:instanceKey/facets/:facetKey/source",
1592
+ },
1593
+ {
1594
+ pattern:
1595
+ /^\/v1\/workspaces\/[^/]+\/integrations\/[^/]+\/instances\/[^/]+\/facets\/[^/]+\/pause$/,
1596
+ label:
1597
+ "/v1/workspaces/:workspaceId/integrations/:capabilityId/instances/:instanceKey/facets/:facetKey/pause",
1598
+ },
1599
+ {
1600
+ pattern:
1601
+ /^\/v1\/workspaces\/[^/]+\/integrations\/[^/]+\/instances\/[^/]+\/facets\/[^/]+\/resume$/,
1602
+ label:
1603
+ "/v1/workspaces/:workspaceId/integrations/:capabilityId/instances/:instanceKey/facets/:facetKey/resume",
1604
+ },
1605
+ {
1606
+ pattern: /^\/v1\/workspaces\/[^/]+\/integrations\/[^/]+\/instances\/[^/]+\/facets\/[^/]+$/,
1607
+ label:
1608
+ "/v1/workspaces/:workspaceId/integrations/:capabilityId/instances/:instanceKey/facets/:facetKey",
1609
+ },
1610
+ {
1611
+ pattern: /^\/v1\/workspaces\/[^/]+\/integrations\/[^/]+\/instances\/[^/]+\/facets$/,
1612
+ label: "/v1/workspaces/:workspaceId/integrations/:capabilityId/instances/:instanceKey/facets",
1613
+ },
1614
+ {
1615
+ pattern: /^\/v1\/workspaces\/[^/]+\/integrations\/[^/]+\/instances\/[^/]+$/,
1616
+ label: "/v1/workspaces/:workspaceId/integrations/:capabilityId/instances/:instanceKey",
1617
+ },
1618
+ {
1619
+ pattern: /^\/v1\/workspaces\/[^/]+\/integrations$/,
1620
+ label: "/v1/workspaces/:workspaceId/integrations",
1621
+ },
1622
+ {
1623
+ pattern: /^\/v1\/workspaces\/[^/]+\/skills\/preview$/,
1624
+ label: "/v1/workspaces/:workspaceId/skills/preview",
1625
+ },
1626
+ {
1627
+ pattern: /^\/v1\/workspaces\/[^/]+\/skills\/install$/,
1628
+ label: "/v1/workspaces/:workspaceId/skills/install",
1629
+ },
1630
+ {
1631
+ pattern: /^\/v1\/workspaces\/[^/]+\/skills\/[^/]+\/uninstall-preview$/,
1632
+ label: "/v1/workspaces/:workspaceId/skills/:id/uninstall-preview",
1633
+ },
1634
+ {
1635
+ pattern: /^\/v1\/workspaces\/[^/]+\/skills\/[^/]+$/,
1636
+ label: "/v1/workspaces/:workspaceId/skills/:id",
1637
+ },
1144
1638
  {
1145
1639
  pattern: /^\/v1\/workspaces\/[^/]+\/environments$/,
1146
1640
  label: "/v1/workspaces/:workspaceId/environments",
@@ -1173,6 +1667,26 @@ const routeLabelPatterns: Array<{ pattern: RegExp; label: string }> = [
1173
1667
  pattern: /^\/v1\/workspaces\/[^/]+\/packs\/[^/]+$/,
1174
1668
  label: "/v1/workspaces/:workspaceId/packs/:id",
1175
1669
  },
1670
+ {
1671
+ pattern: /^\/v1\/workspaces\/[^/]+\/plugins\/preview$/,
1672
+ label: "/v1/workspaces/:workspaceId/plugins/preview",
1673
+ },
1674
+ {
1675
+ pattern: /^\/v1\/workspaces\/[^/]+\/plugins$/,
1676
+ label: "/v1/workspaces/:workspaceId/plugins",
1677
+ },
1678
+ {
1679
+ pattern: /^\/v1\/workspaces\/[^/]+\/plugins\/install$/,
1680
+ label: "/v1/workspaces/:workspaceId/plugins/install",
1681
+ },
1682
+ {
1683
+ pattern: /^\/v1\/workspaces\/[^/]+\/plugins\/[^/]+\/uninstall-preview$/,
1684
+ label: "/v1/workspaces/:workspaceId/plugins/:pluginKey/uninstall-preview",
1685
+ },
1686
+ {
1687
+ pattern: /^\/v1\/workspaces\/[^/]+\/plugins\/[^/]+$/,
1688
+ label: "/v1/workspaces/:workspaceId/plugins/:pluginKey",
1689
+ },
1176
1690
  {
1177
1691
  pattern: /^\/v1\/workspaces\/[^/]+\/social\/connections$/,
1178
1692
  label: "/v1/workspaces/:workspaceId/social/connections",
@@ -1189,10 +1703,26 @@ const routeLabelPatterns: Array<{ pattern: RegExp; label: string }> = [
1189
1703
  pattern: /^\/v1\/workspaces\/[^/]+\/connections\/oauth\/start$/,
1190
1704
  label: "/v1/workspaces/:workspaceId/connections/oauth/start",
1191
1705
  },
1706
+ {
1707
+ pattern: /^\/v1\/workspaces\/[^/]+\/integrations\/oauth\/start$/,
1708
+ label: "/v1/workspaces/:workspaceId/integrations/oauth/start",
1709
+ },
1192
1710
  {
1193
1711
  pattern: /^\/v1\/workspaces\/[^/]+\/connections\/slack-bot\/install$/,
1194
1712
  label: "/v1/workspaces/:workspaceId/connections/slack-bot/install",
1195
1713
  },
1714
+ {
1715
+ pattern: /^\/v1\/workspaces\/[^/]+\/connections\/fiken\/install$/,
1716
+ label: "/v1/workspaces/:workspaceId/connections/fiken/install",
1717
+ },
1718
+ {
1719
+ pattern: /^\/v1\/workspaces\/[^/]+\/connections\/fiken\/oauth\/start$/,
1720
+ label: "/v1/workspaces/:workspaceId/connections/fiken/oauth/start",
1721
+ },
1722
+ {
1723
+ pattern: /^\/v1\/integrations\/fiken\/callback$/,
1724
+ label: "/v1/integrations/fiken/callback",
1725
+ },
1196
1726
  {
1197
1727
  pattern: /^\/v1\/workspaces\/[^/]+\/connections\/[^/]+$/,
1198
1728
  label: "/v1/workspaces/:workspaceId/connections/:connectionId",
@@ -1202,6 +1732,14 @@ const routeLabelPatterns: Array<{ pattern: RegExp; label: string }> = [
1202
1732
  pattern: /^\/v1\/integrations\/oauth\/callback$/,
1203
1733
  label: "/v1/integrations/oauth/callback",
1204
1734
  },
1735
+ {
1736
+ pattern: /^\/v1\/integrations\/provider-oauth\/callback$/,
1737
+ label: "/v1/integrations/provider-oauth/callback",
1738
+ },
1739
+ {
1740
+ pattern: /^\/v1\/integrations\/google-drive\/callback$/,
1741
+ label: "/v1/integrations/google-drive/callback",
1742
+ },
1205
1743
  {
1206
1744
  pattern: /^\/v1\/integrations\/oauth\/client-metadata\.json$/,
1207
1745
  label: "/v1/integrations/oauth/client-metadata.json",
@@ -1258,9 +1796,11 @@ const routeLabelPatterns: Array<{ pattern: RegExp; label: string }> = [
1258
1796
  ];
1259
1797
 
1260
1798
  export function routeLabel(pathname: string): string {
1261
- const match = routeLabelPatterns.find(({ pattern }) => pattern.test(pathname));
1262
- if (match) {
1263
- return match.label;
1799
+ for (const candidate of routeLabelPatterns) {
1800
+ const match = pathname.match(candidate.pattern);
1801
+ if (match) {
1802
+ return typeof candidate.label === "string" ? candidate.label : candidate.label(match);
1803
+ }
1264
1804
  }
1265
1805
  return pathname.startsWith("/v1/") ? "/v1/unknown" : "/unknown";
1266
1806
  }