@opengeni/api-router 3.0.1 → 3.0.2-canary.38894290271001
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/access-grant-rls.d.ts +8 -1
- package/dist/admission-trace.d.ts +8 -0
- package/dist/app.d.ts +50 -3
- package/dist/app.js +10 -1
- package/dist/artifact-catalog-cursor.d.ts +1 -0
- package/dist/attached-browser-viewer.d.ts +11 -0
- package/dist/auth/managed-auth-attempt-context.d.ts +2 -0
- package/dist/auth/managed-auth-rate-limits.d.ts +167 -0
- package/dist/auth/managed-auth-session-adapter.d.ts +9 -1
- package/dist/auth/managed-auth.d.ts +72 -5
- package/dist/auth/managed-sign-in-notifications.d.ts +4 -0
- package/dist/auth/new-signups-gate.d.ts +23 -0
- package/dist/auth/signup-funnel-metrics.d.ts +44 -0
- package/dist/azure-live.d.ts +28 -0
- package/dist/browser-action-fences.d.ts +13 -0
- package/dist/browser-controller-authority.d.ts +1 -61
- package/dist/browser-ephemeral.d.ts +5 -0
- package/dist/browser-lost-runtime-end.d.ts +24 -0
- package/dist/browser-state-authority.d.ts +1 -27
- package/dist/{chunk-AJ62T5QN.js → chunk-GBUJ2DYZ.js} +80507 -39878
- package/dist/chunk-GBUJ2DYZ.js.map +1 -0
- package/dist/chunk-OGQDXVXY.js +18 -0
- package/dist/chunk-OGQDXVXY.js.map +1 -0
- package/dist/claude-default-models.d.ts +19 -0
- package/dist/claude-subscription-core.d.ts +50 -0
- package/dist/claude-subscription-oauth.d.ts +36 -0
- package/dist/claude-subscription-profile.d.ts +9 -0
- package/dist/claude-subscription-usage.d.ts +6 -0
- package/dist/claude-workspace-connection.d.ts +15 -0
- package/dist/codemode.d.ts +40 -5
- package/dist/codex-realtime.d.ts +29 -3
- package/dist/codex-session-pointers.d.ts +19 -0
- package/dist/connection-ownership.d.ts +6 -3
- package/dist/controller-data-plane.d.ts +16 -0
- package/dist/credit-promotion-snapshot.d.ts +6 -0
- package/dist/editable-artifact-source-session.d.ts +3 -0
- package/dist/editable-artifact-websocket.d.ts +5 -3
- package/dist/fatal-process-boundary.d.ts +9 -1
- package/dist/github-access.d.ts +7 -1
- package/dist/github-browser-flow.d.ts +1 -1
- package/dist/http/acting-person.d.ts +36 -0
- package/dist/http/api-contract-compat.d.ts +19 -0
- package/dist/http/api-error.d.ts +50 -1
- package/dist/http/auth.d.ts +8 -1
- package/dist/http/cors.d.ts +12 -0
- package/dist/http/deprecation.d.ts +42 -0
- package/dist/http/keyed-admission.d.ts +12 -0
- package/dist/http/local-browser-boundary.d.ts +72 -0
- package/dist/http/metrics-listener.d.ts +22 -0
- package/dist/http/path-identifier.d.ts +17 -0
- package/dist/http/registered-route-label.d.ts +16 -0
- package/dist/http/rejection-telemetry.d.ts +34 -0
- package/dist/http/request-body.d.ts +27 -0
- package/dist/http/request-source.d.ts +41 -3
- package/dist/http/sse.d.ts +12 -1
- package/dist/http/unmatched-route.d.ts +18 -0
- package/dist/http/user-content.d.ts +36 -0
- package/dist/http/variable-sets-removed.d.ts +41 -0
- package/dist/http/websocket-upgrade-dispatch.d.ts +18 -0
- package/dist/index.js +1279 -108
- package/dist/index.js.map +1 -1
- package/dist/integration-connect-metrics.d.ts +35 -0
- package/dist/integration-endpoint-test.d.ts +20 -0
- package/dist/integrations/api-integrations.d.ts +2 -2
- package/dist/integrations/atlassian.d.ts +8 -3
- package/dist/integrations/fiken.d.ts +3 -1
- package/dist/integrations/github-lens-connect.d.ts +37 -66
- package/dist/integrations/google-drive.d.ts +2 -0
- package/dist/integrations/integration-commit-authority.d.ts +13 -0
- package/dist/integrations/marketplace-plugin.d.ts +43 -43
- package/dist/integrations/oauth-client.d.ts +171 -12
- package/dist/integrations/oauth-profiles.d.ts +42 -13
- package/dist/integrations/onepassword-vault-provider.d.ts +9 -0
- package/dist/integrations/slack-bot.d.ts +285 -7
- package/dist/integrations/slack-file-upload-flow.d.ts +62 -0
- package/dist/integrations/slack-interactions.d.ts +89 -10
- package/dist/integrations/slack-mrkdwn.d.ts +41 -0
- package/dist/integrations/slack-routing.d.ts +1 -1
- package/dist/integrations/slack-session-defaults.d.ts +58 -0
- package/dist/integrations/slack-task-file-upload.d.ts +36 -0
- package/dist/integrations/slack-tool-review.d.ts +5 -0
- package/dist/interaction-agent-access.d.ts +13 -8
- package/dist/interaction-agent-version.d.ts +21 -0
- package/dist/interaction-holder-heartbeat.d.ts +5 -0
- package/dist/interaction-metrics.d.ts +27 -2
- package/dist/interaction-route-stages.d.ts +25 -0
- package/dist/interaction-secrets-vault.d.ts +20 -0
- package/dist/interaction-secrets.d.ts +103 -0
- package/dist/mcp/action-catalog-types.d.ts +14 -0
- package/dist/mcp/action-catalog.gen.d.ts +2 -0
- package/dist/mcp/action-descriptions.d.ts +4 -0
- package/dist/mcp/action-schema-imports.gen.d.ts +1 -0
- package/dist/mcp/capability-account-readiness.d.ts +7 -0
- package/dist/mcp/child-read-evidence.d.ts +9 -0
- package/dist/mcp/company-profile-agent-admin.d.ts +12 -0
- package/dist/mcp/contract-input.d.ts +13 -0
- package/dist/mcp/documents.d.ts +2 -0
- package/dist/mcp/first-party-tool-permissions.d.ts +3 -606
- package/dist/mcp/notification-tools.d.ts +26 -0
- package/dist/mcp/orchestration-failure-diagnostic.d.ts +43 -0
- package/dist/mcp/scheduled-task-input.d.ts +1309 -0
- package/dist/mcp/scheduled-task-view.d.ts +217 -3
- package/dist/mcp/secret-tools.d.ts +14 -0
- package/dist/mcp/server.d.ts +108 -5
- package/dist/mcp/session-admin-access-tools.d.ts +33 -0
- package/dist/mcp/session-event-errors.d.ts +28 -0
- package/dist/mcp/session-event-view.d.ts +62 -0
- package/dist/mcp/session-view.d.ts +15 -0
- package/dist/mcp/session-wait.d.ts +18 -0
- package/dist/mcp/site-agent-clamp-tools.d.ts +38 -0
- package/dist/mcp/site-data-tools.d.ts +22 -0
- package/dist/mcp/site-publication-tools.d.ts +19 -0
- package/dist/mcp/site-sql-tools.d.ts +27 -0
- package/dist/mcp-oauth.d.ts +37 -4
- package/dist/model-calls/chat-completions.d.ts +87 -0
- package/dist/model-calls/pending-usage-settlements.d.ts +57 -0
- package/dist/model-calls/service.d.ts +36 -0
- package/dist/model-catalog.d.ts +9 -3
- package/dist/native-push-dispatch.d.ts +69 -0
- package/dist/observability.d.ts +3 -1
- package/dist/organization-mcp.d.ts +82 -0
- package/dist/prepared-mcp-actor.d.ts +77 -0
- package/dist/prepared-mcp-permissions.d.ts +8 -0
- package/dist/routes/analytics-consent.d.ts +26 -0
- package/dist/routes/api-integrations.d.ts +11 -0
- package/dist/routes/api-keys.d.ts +7 -7
- package/dist/routes/automations.d.ts +2 -0
- package/dist/routes/billing.d.ts +59 -0
- package/dist/routes/browser-sessions.d.ts +65 -2
- package/dist/routes/chat-completions.d.ts +11 -0
- package/dist/routes/claude-core-usage.d.ts +20 -0
- package/dist/routes/claude-subscription-accounts.d.ts +3 -0
- package/dist/routes/claude-subscription-oauth.d.ts +3 -0
- package/dist/routes/client-errors.d.ts +182 -0
- package/dist/routes/codex-core.d.ts +401 -0
- package/dist/routes/codex.d.ts +64 -2
- package/dist/routes/computer-sessions.d.ts +6 -1
- package/dist/routes/desktop-distribution.d.ts +32 -0
- package/dist/routes/github-browser-fonts.gen.d.ts +2 -0
- package/dist/routes/github-browser-pages.d.ts +27 -0
- package/dist/routes/inbox.d.ts +13 -0
- package/dist/routes/insights-response-cache.d.ts +10 -0
- package/dist/routes/insights-usage.d.ts +12 -0
- package/dist/routes/insights.d.ts +28 -0
- package/dist/routes/interaction-placement-message.d.ts +7 -0
- package/dist/routes/managed-sign-in-methods.d.ts +6 -0
- package/dist/routes/model-account-choice.d.ts +13 -0
- package/dist/routes/model-account-settings.d.ts +19 -0
- package/dist/routes/native-app-auth.d.ts +12 -0
- package/dist/routes/organization-integration-policy.d.ts +8 -0
- package/dist/routes/organization-integrations.d.ts +6 -0
- package/dist/routes/organization-mcp-connections.d.ts +3 -0
- package/dist/routes/organization-model-defaults.d.ts +9 -0
- package/dist/routes/organization-service-accounts.d.ts +3 -0
- package/dist/routes/personal-api-keys.d.ts +3 -0
- package/dist/routes/session-admin-access.d.ts +3 -0
- package/dist/routes/session-artifact-associations.d.ts +4 -0
- package/dist/routes/session-history-imports.d.ts +20 -0
- package/dist/routes/sessions.d.ts +38 -2
- package/dist/routes/site-data.d.ts +18 -0
- package/dist/routes/site-publishing.d.ts +5 -0
- package/dist/routes/site-runtime.d.ts +31 -0
- package/dist/routes/site-sql.d.ts +16 -0
- package/dist/routes/slack-bot-access.d.ts +3 -0
- package/dist/routes/subscription-account-pools.d.ts +21 -0
- package/dist/routes/subscription-core-http.d.ts +5 -0
- package/dist/routes/subscription-pool-access.d.ts +37 -0
- package/dist/routes/subscription-pool-core.d.ts +153 -0
- package/dist/routes/supergrok.d.ts +6 -6
- package/dist/routes/usage-allowances.d.ts +12 -0
- package/dist/routes/variable-sets-removed.d.ts +2 -0
- package/dist/routes/vault-providers.d.ts +34 -0
- package/dist/routes/vaults.d.ts +3 -0
- package/dist/routes/workspace-integrations.d.ts +35 -0
- package/dist/routes/workspace-model-providers.d.ts +4 -0
- package/dist/routes/workspaces.d.ts +11 -2
- package/dist/sandbox/agent-update.d.ts +64 -0
- package/dist/sandbox/auth-callout.d.ts +8 -0
- package/dist/sandbox/channel-a.d.ts +15 -1
- package/dist/sandbox/enrollment.d.ts +26 -4
- package/dist/sandbox/machine-audience.d.ts +29 -0
- package/dist/sandbox/machines.d.ts +15 -2
- package/dist/sandbox/metrics-ingestion.d.ts +6 -2
- package/dist/sandbox/screen-control.d.ts +85 -0
- package/dist/sandbox/viewer.d.ts +5 -1
- package/dist/sandbox-file-artifacts.d.ts +17 -1
- package/dist/scheduled-task-blockers.d.ts +39 -0
- package/dist/scheduled-task-deletion.d.ts +3 -4
- package/dist/scheduled-task-lifecycle.d.ts +14 -0
- package/dist/secret-answers.d.ts +40 -0
- package/dist/session-create-observability.d.ts +5 -0
- package/dist/session-event-view-36GO3IL7.js +528 -0
- package/dist/session-event-view-36GO3IL7.js.map +1 -0
- package/dist/signed-state-secret.d.ts +18 -0
- package/dist/site-agent-guard.d.ts +53 -0
- package/dist/site-data/operations.d.ts +103 -0
- package/dist/site-data/service.d.ts +203 -0
- package/dist/site-frame-guard.d.ts +32 -0
- package/dist/site-publishing/agent-data-tools.d.ts +84 -0
- package/dist/site-publishing/agent-model.d.ts +58 -0
- package/dist/site-publishing/agent-session.d.ts +16 -0
- package/dist/site-publishing/content-scanner.d.ts +31 -0
- package/dist/site-publishing/edge-sync.d.ts +48 -0
- package/dist/site-publishing/gateway-signature.d.ts +56 -0
- package/dist/site-publishing/limits.d.ts +63 -0
- package/dist/site-publishing/page-tool-authority.d.ts +30 -0
- package/dist/site-publishing/platform-usage.d.ts +113 -0
- package/dist/site-publishing/platform.d.ts +21 -0
- package/dist/site-publishing/readiness.d.ts +62 -0
- package/dist/site-publishing/runtime.d.ts +179 -0
- package/dist/site-publishing/service.d.ts +264 -0
- package/dist/site-publishing/session-events.d.ts +22 -0
- package/dist/site-publishing/session-views.d.ts +36 -0
- package/dist/site-publishing/viewer.d.ts +40 -0
- package/dist/site-session-origin.d.ts +31 -6
- package/dist/site-sql/format.d.ts +47 -0
- package/dist/site-sql/gateway.d.ts +50 -0
- package/dist/site-sql/service.d.ts +213 -0
- package/dist/temporal-schedule-sync.d.ts +26 -0
- package/dist/transcription/billing-refusal.d.ts +8 -0
- package/dist/transcription/normalize.d.ts +23 -0
- package/dist/transcription/providers/azure-mai.d.ts +12 -0
- package/dist/transcription/providers/azure-openai.d.ts +4 -0
- package/dist/transcription/providers/openai.d.ts +16 -0
- package/dist/transcription/providers/subscription-core.d.ts +39 -0
- package/dist/transcription/service.d.ts +10 -1
- package/dist/transcription/wav-duration.d.ts +8 -0
- package/dist/workspace-tool-gateway-attestations.d.ts +37 -0
- package/dist/workspace-tool-gateway-observability.d.ts +16 -0
- package/dist/workspace-tool-gateway-secrets.d.ts +27 -0
- package/dist/workspace-tool-gateway.d.ts +102 -6
- package/dist/workspace-tool-target-fence.d.ts +116 -0
- package/dist/workspace-tool-target.d.ts +225 -0
- package/dist/workspace-webhook-dispatch.d.ts +26 -0
- package/dist/xai-subscription-core.d.ts +60 -0
- package/package.json +25 -20
- package/src/access-grant-rls.ts +64 -14
- package/src/admission-trace.ts +14 -0
- package/src/app.ts +1684 -252
- package/src/artifact-catalog-cursor.ts +1 -0
- package/src/attached-browser-viewer.ts +26 -0
- package/src/auth/managed-auth-attempt-context.ts +9 -0
- package/src/auth/managed-auth-rate-limits.ts +252 -0
- package/src/auth/managed-auth-session-adapter.ts +64 -29
- package/src/auth/managed-auth.ts +303 -35
- package/src/auth/managed-email.ts +18 -1
- package/src/auth/managed-sign-in-notifications.ts +81 -0
- package/src/auth/new-signups-gate.ts +49 -0
- package/src/auth/organization-user-setup.ts +3 -3
- package/src/auth/signup-funnel-metrics.ts +164 -0
- package/src/azure-live.ts +103 -0
- package/src/browser-action-fences.ts +45 -0
- package/src/browser-controller-authority.ts +1 -186
- package/src/browser-ephemeral.ts +39 -0
- package/src/browser-lost-runtime-end.ts +71 -0
- package/src/browser-state-authority.ts +1 -236
- package/src/claude-default-models.ts +37 -0
- package/src/claude-subscription-core.ts +154 -0
- package/src/claude-subscription-oauth.ts +352 -0
- package/src/claude-subscription-profile.ts +73 -0
- package/src/claude-subscription-usage.ts +51 -0
- package/src/claude-workspace-connection.ts +82 -0
- package/src/codemode.ts +236 -39
- package/src/codex-realtime.ts +181 -53
- package/src/codex-session-pointers.ts +80 -0
- package/src/company-brain-okf.ts +1 -1
- package/src/connected-machine-computer-access.ts +2 -1
- package/src/connection-ownership.ts +19 -13
- package/src/controller-data-plane.ts +34 -0
- package/src/credit-promotion-snapshot.ts +36 -0
- package/src/editable-artifact-production.ts +10 -0
- package/src/editable-artifact-source-session.ts +123 -0
- package/src/editable-artifact-websocket.ts +16 -3
- package/src/fatal-process-boundary.ts +67 -3
- package/src/gateway-realtime.ts +12 -8
- package/src/github-access.ts +31 -5
- package/src/github-browser-flow.ts +1 -1
- package/src/http/acting-person.ts +105 -0
- package/src/http/api-contract-compat.ts +49 -0
- package/src/http/api-error.ts +210 -2
- package/src/http/auth.ts +68 -4
- package/src/http/cors.ts +37 -5
- package/src/http/deprecation.ts +118 -0
- package/src/http/keyed-admission.ts +29 -0
- package/src/http/local-browser-boundary.ts +343 -0
- package/src/http/metrics-listener.ts +57 -0
- package/src/http/path-identifier.ts +54 -0
- package/src/http/registered-route-label.ts +34 -0
- package/src/http/rejection-telemetry.ts +120 -0
- package/src/http/request-body.ts +127 -0
- package/src/http/request-source.ts +154 -13
- package/src/http/sse.ts +88 -28
- package/src/http/unmatched-route.ts +60 -0
- package/src/http/user-content.ts +158 -0
- package/src/http/variable-sets-removed.ts +122 -0
- package/src/http/websocket-upgrade-dispatch.ts +29 -0
- package/src/index.ts +107 -57
- package/src/integration-connect-metrics.ts +228 -0
- package/src/integration-endpoint-test.ts +291 -0
- package/src/integrations/api-integrations.ts +81 -7
- package/src/integrations/atlassian.ts +202 -83
- package/src/integrations/fiken.ts +66 -38
- package/src/integrations/github-app-connect.ts +64 -18
- package/src/integrations/github-lens-connect.ts +8 -29
- package/src/integrations/google-drive.ts +448 -152
- package/src/integrations/integration-commit-authority.ts +69 -0
- package/src/integrations/marketplace-plugin.ts +1 -1
- package/src/integrations/oauth-client.ts +733 -193
- package/src/integrations/oauth-profiles.ts +116 -45
- package/src/integrations/onepassword-vault-provider.ts +16 -0
- package/src/integrations/personal-github-repositories.ts +1 -1
- package/src/integrations/personal-github.ts +61 -35
- package/src/integrations/provider-oauth.ts +67 -85
- package/src/integrations/slack-app-home.ts +5 -5
- package/src/integrations/slack-bot.ts +893 -201
- package/src/integrations/slack-file-upload-flow.ts +147 -0
- package/src/integrations/slack-install.ts +7 -7
- package/src/integrations/slack-interactions.ts +1589 -299
- package/src/integrations/slack-mrkdwn.ts +193 -0
- package/src/integrations/slack-routing.ts +2 -2
- package/src/integrations/slack-session-defaults.ts +224 -0
- package/src/integrations/slack-task-file-upload.ts +323 -0
- package/src/integrations/slack-tool-review.ts +65 -0
- package/src/integrations/social-oauth.ts +90 -58
- package/src/interaction-agent-access.ts +40 -20
- package/src/interaction-agent-version.ts +85 -0
- package/src/interaction-holder-heartbeat.ts +24 -8
- package/src/interaction-metrics.ts +164 -2
- package/src/interaction-route-stages.ts +94 -0
- package/src/interaction-secrets-vault.ts +213 -0
- package/src/interaction-secrets.ts +426 -0
- package/src/mcp/action-catalog-types.ts +14 -0
- package/src/mcp/action-catalog.gen.ts +8014 -0
- package/src/mcp/action-descriptions.ts +299 -0
- package/src/mcp/action-schema-imports.gen.ts +304 -0
- package/src/mcp/capability-account-readiness.ts +17 -0
- package/src/mcp/child-read-evidence.ts +19 -0
- package/src/mcp/company-profile-agent-admin.ts +13 -0
- package/src/mcp/contract-input.ts +165 -0
- package/src/mcp/documents.ts +9 -2
- package/src/mcp/editable-artifacts.ts +29 -11
- package/src/mcp/files.ts +5 -1
- package/src/mcp/first-party-tool-permissions.ts +7 -285
- package/src/mcp/knowledge-entries.ts +108 -34
- package/src/mcp/notification-tools.ts +480 -0
- package/src/mcp/orchestration-failure-diagnostic.ts +43 -0
- package/src/mcp/scheduled-task-input.ts +89 -0
- package/src/mcp/scheduled-task-view.ts +63 -3
- package/src/mcp/secret-tools.ts +185 -0
- package/src/mcp/server.ts +2213 -1116
- package/src/mcp/session-admin-access-tools.ts +163 -0
- package/src/mcp/session-event-errors.ts +123 -0
- package/src/mcp/session-event-view.ts +395 -13
- package/src/mcp/session-view.ts +3 -0
- package/src/mcp/session-wait.ts +67 -7
- package/src/mcp/site-agent-clamp-tools.ts +140 -0
- package/src/mcp/site-data-tools.ts +339 -0
- package/src/mcp/site-publication-tools.ts +201 -0
- package/src/mcp/site-sql-tools.ts +427 -0
- package/src/mcp-oauth.ts +635 -45
- package/src/model-calls/chat-completions.ts +352 -0
- package/src/model-calls/pending-usage-settlements.ts +125 -0
- package/src/model-calls/service.ts +694 -0
- package/src/model-catalog.ts +68 -17
- package/src/native-push-dispatch.ts +457 -0
- package/src/observability.ts +15 -1
- package/src/organization-mcp.ts +485 -0
- package/src/prepared-mcp-actor.ts +154 -0
- package/src/prepared-mcp-permissions.ts +38 -0
- package/src/routes/analytics-consent.ts +123 -0
- package/src/routes/api-integrations.ts +268 -9
- package/src/routes/api-keys.ts +164 -25
- package/src/routes/artifact-catalog.ts +111 -0
- package/src/routes/automations.ts +163 -90
- package/src/routes/billing.ts +535 -59
- package/src/routes/browser-sessions.ts +966 -342
- package/src/routes/canonical-human-identities.ts +61 -0
- package/src/routes/capabilities.ts +112 -5
- package/src/routes/channels.ts +4 -3
- package/src/routes/chat-completions.ts +235 -0
- package/src/routes/claude-core-usage.ts +147 -0
- package/src/routes/claude-subscription-accounts.ts +233 -0
- package/src/routes/claude-subscription-oauth.ts +175 -0
- package/src/routes/client-errors.ts +394 -0
- package/src/routes/codex-core.ts +687 -0
- package/src/routes/codex.ts +1277 -1119
- package/src/routes/company-brain.ts +4 -0
- package/src/routes/computer-sessions.ts +319 -85
- package/src/routes/connect.ts +277 -175
- package/src/routes/connections.ts +690 -363
- package/src/routes/desktop-distribution.ts +217 -0
- package/src/routes/documents.ts +13 -7
- package/src/routes/editable-artifacts.ts +74 -4
- package/src/routes/enrollments.ts +110 -16
- package/src/routes/external-identity-links.ts +13 -12
- package/src/routes/files.ts +41 -16
- package/src/routes/github-browser-fonts.gen.ts +12 -0
- package/src/routes/github-browser-pages.ts +258 -0
- package/src/routes/github.ts +200 -75
- package/src/routes/inbox.ts +353 -0
- package/src/routes/insights-response-cache.ts +83 -0
- package/src/routes/insights-usage.ts +345 -0
- package/src/routes/insights.ts +103 -8
- package/src/routes/install.ts +51 -10
- package/src/routes/integration-facets.ts +70 -28
- package/src/routes/interaction-placement-message.ts +12 -0
- package/src/routes/knowledge.ts +136 -27
- package/src/routes/machines.ts +189 -135
- package/src/routes/managed-auth-session-sets.ts +43 -10
- package/src/routes/managed-onboarding.ts +39 -19
- package/src/routes/managed-sign-in-methods.ts +433 -0
- package/src/routes/memory-slack-publications.ts +3 -2
- package/src/routes/model-account-choice.ts +194 -0
- package/src/routes/model-account-settings.ts +235 -0
- package/src/routes/model-connection-access.ts +210 -48
- package/src/routes/native-app-auth.ts +296 -0
- package/src/routes/organization-integration-policy.ts +116 -0
- package/src/routes/organization-integrations.ts +326 -0
- package/src/routes/organization-mcp-connections.ts +183 -0
- package/src/routes/organization-memberships.ts +205 -19
- package/src/routes/organization-model-defaults.ts +110 -0
- package/src/routes/organization-model-providers.ts +74 -3
- package/src/routes/organization-service-accounts.ts +128 -0
- package/src/routes/organization-sessions.ts +21 -5
- package/src/routes/personal-api-keys.ts +184 -0
- package/src/routes/personal-github-git-broker.ts +2 -2
- package/src/routes/personal-github.ts +9 -4
- package/src/routes/plugins.ts +46 -7
- package/src/routes/pr-review-github.ts +105 -118
- package/src/routes/pr-review.ts +88 -82
- package/src/routes/preference-registry.ts +18 -0
- package/src/routes/rigs.ts +48 -13
- package/src/routes/scheduled-tasks.ts +347 -27
- package/src/routes/session-admin-access.ts +271 -0
- package/src/routes/session-artifact-associations.ts +164 -0
- package/src/routes/session-history-imports.ts +305 -0
- package/src/routes/sessions.ts +1682 -363
- package/src/routes/site-data.ts +340 -0
- package/src/routes/site-publishing.ts +584 -0
- package/src/routes/site-runtime.ts +1944 -0
- package/src/routes/site-sql.ts +263 -0
- package/src/routes/skill-content.ts +72 -7
- package/src/routes/skills.ts +5 -4
- package/src/routes/slack-bot-access.ts +108 -0
- package/src/routes/social.ts +27 -15
- package/src/routes/subscription-account-pools.ts +209 -0
- package/src/routes/subscription-core-http.ts +18 -0
- package/src/routes/subscription-pool-access.ts +180 -0
- package/src/routes/subscription-pool-core.ts +552 -0
- package/src/routes/supergrok.ts +174 -655
- package/src/routes/transcription-recordings.ts +71 -4
- package/src/routes/transcriptions.ts +26 -2
- package/src/routes/usage-allowances.ts +381 -0
- package/src/routes/variable-sets-removed.ts +19 -0
- package/src/routes/vault-providers.ts +782 -0
- package/src/routes/vaults.ts +421 -0
- package/src/routes/video-generation.ts +30 -12
- package/src/routes/workspace-artifacts.ts +165 -53
- package/src/routes/workspace-integrations.ts +576 -0
- package/src/routes/workspace-model-providers.ts +148 -0
- package/src/routes/workspace-state.ts +4 -0
- package/src/routes/workspaces.ts +625 -117
- package/src/sandbox/agent-update.ts +315 -0
- package/src/sandbox/auth-callout.ts +104 -21
- package/src/sandbox/channel-a.ts +150 -49
- package/src/sandbox/enrollment.ts +131 -22
- package/src/sandbox/machine-audience.ts +192 -0
- package/src/sandbox/machines.ts +75 -31
- package/src/sandbox/metrics-ingestion.ts +133 -2
- package/src/sandbox/rematerialize.ts +51 -21
- package/src/sandbox/screen-control.ts +289 -0
- package/src/sandbox/viewer.ts +33 -58
- package/src/sandbox-file-artifacts.ts +53 -27
- package/src/scheduled-task-blockers.ts +293 -0
- package/src/scheduled-task-deletion.ts +13 -3
- package/src/scheduled-task-lifecycle.ts +84 -0
- package/src/secret-answers.ts +119 -0
- package/src/session-create-observability.ts +46 -0
- package/src/session-realtime-context.ts +26 -4
- package/src/signed-state-secret.ts +58 -0
- package/src/site-agent-guard.ts +261 -0
- package/src/site-data/operations.ts +777 -0
- package/src/site-data/service.ts +1043 -0
- package/src/site-frame-guard.ts +107 -0
- package/src/site-publishing/agent-data-tools.ts +250 -0
- package/src/site-publishing/agent-model.ts +127 -0
- package/src/site-publishing/agent-session.ts +148 -0
- package/src/site-publishing/content-scanner.ts +93 -0
- package/src/site-publishing/edge-sync.ts +148 -0
- package/src/site-publishing/gateway-signature.ts +198 -0
- package/src/site-publishing/limits.ts +315 -0
- package/src/site-publishing/page-tool-authority.ts +111 -0
- package/src/site-publishing/platform-usage.ts +479 -0
- package/src/site-publishing/platform.ts +98 -0
- package/src/site-publishing/readiness.ts +330 -0
- package/src/site-publishing/runtime.ts +831 -0
- package/src/site-publishing/service.ts +1282 -0
- package/src/site-publishing/session-events.ts +262 -0
- package/src/site-publishing/session-views.ts +262 -0
- package/src/site-publishing/viewer.ts +75 -0
- package/src/site-session-origin.ts +91 -6
- package/src/site-sql/format.ts +134 -0
- package/src/site-sql/gateway.ts +168 -0
- package/src/site-sql/service.ts +1636 -0
- package/src/site-uploads.ts +27 -5
- package/src/temporal-schedule-sync.ts +74 -0
- package/src/transcription/billing-refusal.ts +14 -0
- package/src/transcription/normalize.ts +77 -0
- package/src/transcription/providers/azure-mai.ts +98 -0
- package/src/transcription/providers/azure-openai.ts +16 -7
- package/src/transcription/providers/codex-subscription.ts +190 -67
- package/src/transcription/providers/openai.ts +79 -5
- package/src/transcription/providers/subscription-core.ts +125 -0
- package/src/transcription/providers/xai-subscription.ts +81 -83
- package/src/transcription/segmenter.ts +7 -1
- package/src/transcription/service.ts +239 -10
- package/src/transcription/wav-duration.ts +39 -0
- package/src/workspace-deletion.ts +25 -1
- package/src/workspace-tool-gateway-attestations.ts +133 -0
- package/src/workspace-tool-gateway-observability.ts +64 -0
- package/src/workspace-tool-gateway-secrets.ts +104 -0
- package/src/workspace-tool-gateway.ts +921 -103
- package/src/workspace-tool-target-fence.ts +380 -0
- package/src/workspace-tool-target.ts +554 -0
- package/src/workspace-webhook-dispatch.ts +225 -0
- package/src/xai-realtime.ts +85 -81
- package/src/xai-subscription-core.ts +172 -0
- package/dist/browser-auth-broker.d.ts +0 -20
- package/dist/chunk-AJ62T5QN.js.map +0 -1
- package/dist/connection-authority-owner.d.ts +0 -59
- package/dist/routes/connection-authorities.d.ts +0 -3
- package/dist/routes/environments.d.ts +0 -5
- package/dist/routes/host-mcp-bindings.d.ts +0 -5
- package/dist/routes/host-mcp-resolvers.d.ts +0 -3
- package/dist/routes/packs.d.ts +0 -3
- package/dist/session-event-view-MU3HMNV3.js +0 -264
- package/dist/session-event-view-MU3HMNV3.js.map +0 -1
- package/dist/xai-subscription-auth.d.ts +0 -23
- package/src/browser-auth-broker.ts +0 -155
- package/src/connection-authority-owner.ts +0 -61
- package/src/routes/connection-authorities.ts +0 -139
- package/src/routes/environments.ts +0 -388
- package/src/routes/host-mcp-bindings.ts +0 -122
- package/src/routes/host-mcp-resolvers.ts +0 -41
- package/src/routes/packs.ts +0 -784
- package/src/xai-subscription-auth.ts +0 -139
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { knowledgeContextForGateway } from "@opengeni/core";
|
|
1
|
+
import { isVerifiedDelegatedHumanAuthorization, knowledgeContextForGateway } from "@opengeni/core";
|
|
2
2
|
import { createHash, randomBytes } from "node:crypto";
|
|
3
3
|
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
|
|
4
4
|
import { InMemoryTransport } from "@modelcontextprotocol/sdk/inMemory.js";
|
|
@@ -6,55 +6,70 @@ import { withSiteSessionOrigin } from "@opengeni/core";
|
|
|
6
6
|
import { resolveSiteSessionOrigin } from "./site-session-origin";
|
|
7
7
|
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
8
8
|
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
9
|
+
import { recordToolApproval } from "@opengeni/observability";
|
|
9
10
|
import {
|
|
10
11
|
CallToolRequestSchema,
|
|
11
12
|
ListToolsRequestSchema,
|
|
12
13
|
type CallToolResult,
|
|
13
14
|
} from "@modelcontextprotocol/sdk/types.js";
|
|
14
|
-
import { CODEX_CLIENT_VERSION } from "@opengeni/codex";
|
|
15
15
|
import { IntegrationInvocationError } from "@opengeni/capabilities";
|
|
16
|
+
import { CODEX_APPS_MCP_SERVER_ID } from "@opengeni/codex";
|
|
16
17
|
import type { Settings } from "@opengeni/config";
|
|
17
18
|
import {
|
|
18
19
|
FIRST_PARTY_MCP_TOOL_NAMES,
|
|
20
|
+
EDITABLE_ARTIFACT_MCP_CODEMODE_PATHS,
|
|
19
21
|
ToolGatewayCallRequest,
|
|
20
22
|
ToolGatewayCallResponse,
|
|
21
23
|
ToolGatewayApprovalRequest,
|
|
22
24
|
ToolGatewayApprovalResponse,
|
|
23
25
|
ToolGatewayDeclarationsResponse,
|
|
26
|
+
toolPolicyActionName,
|
|
27
|
+
isSecretReferenceError,
|
|
24
28
|
type AccessGrant,
|
|
25
|
-
type McpGatewayCredentialAuthority,
|
|
26
29
|
type ToolGatewayCatalog,
|
|
27
30
|
type ToolGatewayIdentity,
|
|
28
31
|
type ToolRef,
|
|
32
|
+
type ToolGatewayTarget,
|
|
29
33
|
} from "@opengeni/contracts";
|
|
30
34
|
import {
|
|
35
|
+
availableMcpAccountBindings,
|
|
31
36
|
buildApiIntegrationMcpServers,
|
|
37
|
+
expandApiIntegrationAccountRoutes,
|
|
38
|
+
expandMcpAccountRoutes,
|
|
32
39
|
hasPermission,
|
|
33
40
|
externalActorContinuationForAuthorization,
|
|
34
41
|
isVerifiedOrganizationServiceAuthorization,
|
|
35
42
|
externalContinuationCommitAuthorizer,
|
|
36
43
|
requireResolvedAccessGrantAuthorization,
|
|
37
|
-
|
|
44
|
+
codexAppsRequestAuthForDesignation,
|
|
45
|
+
resolveCodexAppsDesignationForRun,
|
|
38
46
|
resolveWorkspaceCatalogSettings,
|
|
39
47
|
settingsWithEnabledCapabilityMcpServers,
|
|
48
|
+
settingsWithEnabledCapabilityMcpServersForTargets,
|
|
49
|
+
visibleMcpAccountConnectionsByIds,
|
|
40
50
|
type AccessGrantAuthorization,
|
|
41
51
|
type ApiRouteDeps,
|
|
52
|
+
siteViewerForGrant,
|
|
42
53
|
} from "@opengeni/core";
|
|
43
54
|
import {
|
|
44
|
-
buildCodexTokenResolver,
|
|
45
55
|
buildConnectionTokenResolver,
|
|
46
|
-
|
|
47
|
-
|
|
56
|
+
buildSlackApiRateLimiter,
|
|
57
|
+
lockActiveExternalOrganizationKeyAuthority,
|
|
48
58
|
withAccountRls,
|
|
49
59
|
requireWorkspace,
|
|
50
|
-
withCodexAppsRequestAuthorization,
|
|
51
60
|
consumeToolGatewayApproval,
|
|
52
61
|
getWorkspaceArtifactContentRef,
|
|
53
62
|
issueToolGatewayApproval,
|
|
54
63
|
ToolGatewayApprovalOperationStartedError,
|
|
55
64
|
ToolGatewayApprovalRateLimitError,
|
|
56
65
|
WorkspaceArtifactNotFoundError,
|
|
66
|
+
listConnectorToolPermissionPolicies,
|
|
67
|
+
projectConnectorToolPermission,
|
|
68
|
+
resolveConnectorActionPolicy,
|
|
69
|
+
connectorActionPolicyDecision,
|
|
57
70
|
type ApiIntegrationRuntime,
|
|
71
|
+
type Database,
|
|
72
|
+
type EnabledMcpCapabilityServer,
|
|
58
73
|
} from "@opengeni/db";
|
|
59
74
|
import {
|
|
60
75
|
type LocalMcpServerRegistration,
|
|
@@ -62,14 +77,18 @@ import {
|
|
|
62
77
|
type ResolveConnectionCredentialInput,
|
|
63
78
|
type ResolveConnectionCredentialResult,
|
|
64
79
|
prepareWorkspaceToolGatewayTools,
|
|
80
|
+
sessionMcpApprovalConnectionId,
|
|
65
81
|
} from "@opengeni/runtime/workspace-tool-gateway";
|
|
66
82
|
import {
|
|
67
83
|
ToolGatewayApprovalRequiredError,
|
|
84
|
+
ToolGatewayBlockedError,
|
|
68
85
|
ToolGatewayCatalogStaleError,
|
|
69
86
|
ToolGatewayInputValidationError,
|
|
70
87
|
ToolGatewayToolNotFoundError,
|
|
71
88
|
digestCanonicalJson,
|
|
72
89
|
generateToolGatewayDeclarations,
|
|
90
|
+
safeNamespaceSegment,
|
|
91
|
+
projectToolGatewayPath,
|
|
73
92
|
type PreparedToolGatewayCall,
|
|
74
93
|
type ToolGatewayDefinition,
|
|
75
94
|
} from "@opengeni/tool-gateway";
|
|
@@ -80,7 +99,26 @@ import { buildDocumentsMcpServer } from "./mcp/documents";
|
|
|
80
99
|
import { buildFilesMcpServer } from "./mcp/files";
|
|
81
100
|
import { buildOpenGeniMcpServer } from "./mcp/server";
|
|
82
101
|
import { startWorkspaceToolGatewayObservation } from "./workspace-tool-gateway-observability";
|
|
102
|
+
import {
|
|
103
|
+
withSiteSecretContext,
|
|
104
|
+
workspaceGatewaySecretArguments,
|
|
105
|
+
} from "./workspace-tool-gateway-secrets";
|
|
106
|
+
import { vaultProviderRegistryFor } from "./routes/vault-providers";
|
|
107
|
+
import type { TargetAuthorityFence } from "./workspace-tool-target-fence";
|
|
108
|
+
import {
|
|
109
|
+
digestWorkspaceToolGatewayCatalogEntry,
|
|
110
|
+
type WorkspaceToolGatewayCatalogAttestations,
|
|
111
|
+
} from "./workspace-tool-gateway-attestations";
|
|
83
112
|
import type { Observability } from "@opengeni/observability";
|
|
113
|
+
import {
|
|
114
|
+
isPublishedSiteToolAuthorization,
|
|
115
|
+
publishedSiteToolAuthority,
|
|
116
|
+
type PublishedSiteToolAuthorization,
|
|
117
|
+
} from "./site-publishing/page-tool-authority";
|
|
118
|
+
|
|
119
|
+
export type WorkspaceToolGatewayAuthorization =
|
|
120
|
+
| AccessGrantAuthorization
|
|
121
|
+
| PublishedSiteToolAuthorization;
|
|
84
122
|
|
|
85
123
|
export type PreparedWorkspaceToolGateway = Pick<
|
|
86
124
|
PreparedWorkspaceToolGatewayTools,
|
|
@@ -121,8 +159,9 @@ export function requireWorkspaceToolGatewayGrant(grant: AccessGrant): void {
|
|
|
121
159
|
}
|
|
122
160
|
|
|
123
161
|
export function requireWorkspaceToolGatewayAuthorization(
|
|
124
|
-
authorization:
|
|
162
|
+
authorization: WorkspaceToolGatewayAuthorization,
|
|
125
163
|
): AccessGrant {
|
|
164
|
+
if (isPublishedSiteToolAuthorization(authorization)) return authorization.grant;
|
|
126
165
|
const grant = requireResolvedAccessGrantAuthorization(
|
|
127
166
|
authorization,
|
|
128
167
|
authorization.grant.workspaceId,
|
|
@@ -138,6 +177,7 @@ export function requireWorkspaceToolGatewayAuthorization(
|
|
|
138
177
|
if (
|
|
139
178
|
!authorization.canonicalManagedHumanSession &&
|
|
140
179
|
!authorization.canonicalLocalHumanSession &&
|
|
180
|
+
!isVerifiedDelegatedHumanAuthorization(authorization) &&
|
|
141
181
|
!externalActorContinuationForAuthorization(authorization)
|
|
142
182
|
) {
|
|
143
183
|
throw new HTTPException(403, { message: "current-human tool access required" });
|
|
@@ -147,9 +187,66 @@ export function requireWorkspaceToolGatewayAuthorization(
|
|
|
147
187
|
|
|
148
188
|
export async function prepareWorkspaceToolGateway(
|
|
149
189
|
routeDeps: ApiRouteDeps,
|
|
150
|
-
authorization:
|
|
190
|
+
authorization: WorkspaceToolGatewayAuthorization,
|
|
191
|
+
options: {
|
|
192
|
+
/**
|
|
193
|
+
* Prepare only these exact (account-qualified) identities. Servers outside
|
|
194
|
+
* them are never constructed, credentialed, connected, or listed; the
|
|
195
|
+
* resulting catalog and digest describe only this subset.
|
|
196
|
+
*/
|
|
197
|
+
allowedIdentities?: readonly ToolGatewayIdentity[];
|
|
198
|
+
} & WorkspaceToolGatewayPreparationOptions = {},
|
|
151
199
|
): Promise<PreparedWorkspaceToolGateway> {
|
|
152
200
|
const grant = requireWorkspaceToolGatewayAuthorization(authorization);
|
|
201
|
+
if (isPublishedSiteToolAuthorization(authorization)) {
|
|
202
|
+
const site = publishedSiteToolAuthority(authorization);
|
|
203
|
+
const allowed = site.requestedTools.filter(
|
|
204
|
+
(identity) =>
|
|
205
|
+
!options.allowedIdentities ||
|
|
206
|
+
options.allowedIdentities.some(
|
|
207
|
+
(item) => item.serverId === identity.serverId && item.toolName === identity.toolName,
|
|
208
|
+
),
|
|
209
|
+
);
|
|
210
|
+
// Targeted preparation replaces its allowlist with the target. It must be
|
|
211
|
+
// an exact declared identity first; paths are resolved by the Site adapter.
|
|
212
|
+
const target = options.target;
|
|
213
|
+
if (
|
|
214
|
+
target &&
|
|
215
|
+
(!("identity" in target) ||
|
|
216
|
+
!allowed.some(
|
|
217
|
+
(item) =>
|
|
218
|
+
item.serverId === target.identity.serverId &&
|
|
219
|
+
item.toolName === target.identity.toolName,
|
|
220
|
+
))
|
|
221
|
+
)
|
|
222
|
+
throw new HTTPException(403, { message: "site_tool_not_authorized" });
|
|
223
|
+
await site.reauthorize();
|
|
224
|
+
const prepared = await prepareWorkspaceToolGatewayForGrantInternal(
|
|
225
|
+
routeDeps,
|
|
226
|
+
grant,
|
|
227
|
+
allowed,
|
|
228
|
+
site.reauthorize,
|
|
229
|
+
options,
|
|
230
|
+
);
|
|
231
|
+
try {
|
|
232
|
+
await site.reauthorize();
|
|
233
|
+
} catch (error) {
|
|
234
|
+
await prepared.close().catch(() => undefined);
|
|
235
|
+
throw error;
|
|
236
|
+
}
|
|
237
|
+
return { ...prepared, reauthorize: site.reauthorize };
|
|
238
|
+
}
|
|
239
|
+
if (options.fence) {
|
|
240
|
+
// The fence owns live caller authority for a targeted request: admission
|
|
241
|
+
// ran before preparation, and each physical request evaluates it once.
|
|
242
|
+
return await prepareWorkspaceToolGatewayForGrantInternal(
|
|
243
|
+
routeDeps,
|
|
244
|
+
grant,
|
|
245
|
+
options.allowedIdentities,
|
|
246
|
+
undefined,
|
|
247
|
+
options,
|
|
248
|
+
);
|
|
249
|
+
}
|
|
153
250
|
const external = externalActorContinuationForAuthorization(authorization);
|
|
154
251
|
const reauthorizeExternal = externalContinuationCommitAuthorizer(authorization);
|
|
155
252
|
const service = isVerifiedOrganizationServiceAuthorization(authorization);
|
|
@@ -165,15 +262,18 @@ export async function prepareWorkspaceToolGateway(
|
|
|
165
262
|
await withAccountRls(routeDeps.db, scope.accountId, async (tx) => {
|
|
166
263
|
if (reauthorizeExternal) await reauthorizeExternal(tx);
|
|
167
264
|
else {
|
|
168
|
-
const live = await
|
|
265
|
+
const live = await lockActiveExternalOrganizationKeyAuthority(
|
|
169
266
|
tx,
|
|
170
267
|
scope.accountId,
|
|
171
268
|
scope.subjectId.slice("api_key:".length),
|
|
269
|
+
scope.workspaceId,
|
|
172
270
|
);
|
|
173
271
|
const workspace = await requireWorkspace(tx, scope.workspaceId);
|
|
174
272
|
if (
|
|
175
273
|
!live ||
|
|
176
|
-
permissions.some(
|
|
274
|
+
permissions.some(
|
|
275
|
+
(permission) => !hasPermission(live.permissions, permission, live.permissionMode),
|
|
276
|
+
) ||
|
|
177
277
|
workspace.accountId !== scope.accountId ||
|
|
178
278
|
workspace.kind !== "shared"
|
|
179
279
|
)
|
|
@@ -182,29 +282,233 @@ export async function prepareWorkspaceToolGateway(
|
|
|
182
282
|
});
|
|
183
283
|
}
|
|
184
284
|
: undefined;
|
|
185
|
-
|
|
285
|
+
const reauthorizeCaller = async () => {
|
|
286
|
+
options.signal?.throwIfAborted();
|
|
287
|
+
await options.reauthorize?.();
|
|
288
|
+
await reauthorize?.();
|
|
289
|
+
};
|
|
290
|
+
await reauthorizeCaller();
|
|
186
291
|
const prepared = await prepareWorkspaceToolGatewayForGrantInternal(
|
|
187
292
|
routeDeps,
|
|
188
293
|
grant,
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
authority: {
|
|
193
|
-
kind: external ? "external_user" : "organization_service",
|
|
194
|
-
subjectId: scope.subjectId,
|
|
195
|
-
permissions,
|
|
196
|
-
},
|
|
197
|
-
reauthorize,
|
|
198
|
-
}
|
|
199
|
-
: undefined,
|
|
294
|
+
options.allowedIdentities,
|
|
295
|
+
reauthorizeCaller,
|
|
296
|
+
options,
|
|
200
297
|
);
|
|
201
298
|
try {
|
|
202
|
-
await
|
|
299
|
+
await reauthorizeCaller();
|
|
203
300
|
} catch (error) {
|
|
204
|
-
await prepared.close();
|
|
301
|
+
await prepared.close().catch(() => undefined);
|
|
205
302
|
throw error;
|
|
206
303
|
}
|
|
207
|
-
return {
|
|
304
|
+
return {
|
|
305
|
+
...prepared,
|
|
306
|
+
reauthorize: async () => {
|
|
307
|
+
await reauthorizeCaller();
|
|
308
|
+
await prepared.reauthorize?.();
|
|
309
|
+
},
|
|
310
|
+
};
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
/**
|
|
314
|
+
* Bounded caller scope for catalog attestations. It binds the exact caller,
|
|
315
|
+
* actor class, and permission set; attestations still grant no authority.
|
|
316
|
+
*/
|
|
317
|
+
export function workspaceToolGatewayAttestationScope(
|
|
318
|
+
authorization: AccessGrantAuthorization,
|
|
319
|
+
): string {
|
|
320
|
+
const grant = authorization.grant;
|
|
321
|
+
const external = externalActorContinuationForAuthorization(authorization);
|
|
322
|
+
return digestCanonicalJson({
|
|
323
|
+
version: 1,
|
|
324
|
+
accountId: grant.accountId,
|
|
325
|
+
workspaceId: grant.workspaceId,
|
|
326
|
+
subjectId: grant.subjectId,
|
|
327
|
+
principalKind: grant.principalKind ?? null,
|
|
328
|
+
permissions: [...new Set(grant.permissions)].sort(),
|
|
329
|
+
actor: isVerifiedOrganizationServiceAuthorization(authorization)
|
|
330
|
+
? "service"
|
|
331
|
+
: external
|
|
332
|
+
? { external: digestCanonicalJson(external) }
|
|
333
|
+
: "human",
|
|
334
|
+
});
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
export type WorkspaceToolGatewayPreparationScope = "complete" | "target";
|
|
338
|
+
export type WorkspaceToolGatewayAttestationOutcome = "hit" | "miss" | "mismatch" | "bypass";
|
|
339
|
+
|
|
340
|
+
export type WorkspaceToolGatewayTiming = {
|
|
341
|
+
/** Wall time spent preparing gateways (all preparations, including fallback). */
|
|
342
|
+
prepareMs: number;
|
|
343
|
+
/** Wall time from the last successful preparation to the response. */
|
|
344
|
+
callMs: number;
|
|
345
|
+
scope: WorkspaceToolGatewayPreparationScope;
|
|
346
|
+
attestation: WorkspaceToolGatewayAttestationOutcome;
|
|
347
|
+
};
|
|
348
|
+
|
|
349
|
+
/** Prepare one complete current-caller catalog and remember its content-free attestation. */
|
|
350
|
+
export async function prepareAttestedWorkspaceToolGateway(
|
|
351
|
+
routeDeps: ApiRouteDeps,
|
|
352
|
+
authorization: AccessGrantAuthorization,
|
|
353
|
+
attestations: WorkspaceToolGatewayCatalogAttestations | undefined,
|
|
354
|
+
prepare: typeof prepareWorkspaceToolGateway = prepareWorkspaceToolGateway,
|
|
355
|
+
): Promise<PreparedWorkspaceToolGateway> {
|
|
356
|
+
const prepared = await prepare(routeDeps, authorization);
|
|
357
|
+
attestations?.record(
|
|
358
|
+
workspaceToolGatewayAttestationScope(authorization),
|
|
359
|
+
prepared.toolGatewayCatalog,
|
|
360
|
+
);
|
|
361
|
+
return prepared;
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
/**
|
|
365
|
+
* HTTP/SDK/Site call adapter. When the caller's `catalogDigest` names a
|
|
366
|
+
* complete catalog this process prepared for the same caller scope, only the
|
|
367
|
+
* target identity's connector is prepared live and its entry must match the
|
|
368
|
+
* attested entry exactly. Every other case (approval capability, unknown or
|
|
369
|
+
* expired digest, missing/changed entry) prepares the complete catalog and
|
|
370
|
+
* applies the unchanged stale-catalog contract.
|
|
371
|
+
*/
|
|
372
|
+
export async function callWorkspaceToolGatewayForCaller(
|
|
373
|
+
routeDeps: ApiRouteDeps,
|
|
374
|
+
authorization: AccessGrantAuthorization,
|
|
375
|
+
request: ToolGatewayCallRequest,
|
|
376
|
+
options: {
|
|
377
|
+
attestations?: WorkspaceToolGatewayCatalogAttestations;
|
|
378
|
+
observability?: Observability;
|
|
379
|
+
/** Must enforce current-caller gateway authorization before provider work. */
|
|
380
|
+
prepare?: typeof prepareWorkspaceToolGateway;
|
|
381
|
+
authorizeSiteTool?: AuthorizeWorkspaceSiteTool;
|
|
382
|
+
resolveOrigin?: typeof resolveSiteSessionOrigin;
|
|
383
|
+
/** Called once with the preparation split, whether the call succeeds or fails. */
|
|
384
|
+
onPreparation?: (
|
|
385
|
+
preparation: Pick<WorkspaceToolGatewayTiming, "prepareMs" | "scope" | "attestation">,
|
|
386
|
+
) => void;
|
|
387
|
+
} = {},
|
|
388
|
+
): Promise<{ response: ToolGatewayCallResponse; timing: WorkspaceToolGatewayTiming }> {
|
|
389
|
+
// `prepare` (prepareWorkspaceToolGateway in production) rejects any caller
|
|
390
|
+
// without live current-caller gateway authority before provider work. The
|
|
391
|
+
// attestation lookup below grants nothing and reveals nothing to a caller
|
|
392
|
+
// that preparation then rejects.
|
|
393
|
+
const grant = authorization.grant;
|
|
394
|
+
const prepare = options.prepare ?? prepareWorkspaceToolGateway;
|
|
395
|
+
const callGateway = (
|
|
396
|
+
prepared: PreparedWorkspaceToolGateway,
|
|
397
|
+
callRequest: ToolGatewayCallRequest,
|
|
398
|
+
) =>
|
|
399
|
+
callWorkspaceToolGateway(
|
|
400
|
+
prepared,
|
|
401
|
+
grant,
|
|
402
|
+
callRequest,
|
|
403
|
+
routeDeps.db,
|
|
404
|
+
undefined,
|
|
405
|
+
options.observability,
|
|
406
|
+
options.authorizeSiteTool,
|
|
407
|
+
options.resolveOrigin,
|
|
408
|
+
);
|
|
409
|
+
const scope = workspaceToolGatewayAttestationScope(authorization);
|
|
410
|
+
let prepareMs = 0;
|
|
411
|
+
const timedPrepare = async (allowedIdentities?: readonly ToolGatewayIdentity[]) => {
|
|
412
|
+
const startedAt = performance.now();
|
|
413
|
+
try {
|
|
414
|
+
return await prepare(
|
|
415
|
+
routeDeps,
|
|
416
|
+
authorization,
|
|
417
|
+
// A target-only call must behave exactly like the complete call: only
|
|
418
|
+
// provider construction narrows, never the settings first-party tools see.
|
|
419
|
+
allowedIdentities ? { allowedIdentities, firstPartySettings: "caller" } : {},
|
|
420
|
+
);
|
|
421
|
+
} finally {
|
|
422
|
+
prepareMs += performance.now() - startedAt;
|
|
423
|
+
}
|
|
424
|
+
};
|
|
425
|
+
const attestedEntry =
|
|
426
|
+
request.approvalToken === undefined
|
|
427
|
+
? options.attestations?.entryDigest(scope, request.catalogDigest, request.identity)
|
|
428
|
+
: undefined;
|
|
429
|
+
let attestation: WorkspaceToolGatewayAttestationOutcome =
|
|
430
|
+
request.approvalToken !== undefined || !options.attestations
|
|
431
|
+
? "bypass"
|
|
432
|
+
: attestedEntry
|
|
433
|
+
? "hit"
|
|
434
|
+
: "miss";
|
|
435
|
+
let preparedScope: WorkspaceToolGatewayPreparationScope = "complete";
|
|
436
|
+
try {
|
|
437
|
+
if (attestedEntry) {
|
|
438
|
+
preparedScope = "target";
|
|
439
|
+
const target = await timedPrepare([request.identity]);
|
|
440
|
+
try {
|
|
441
|
+
const entry = target.toolGatewayCatalog.entries.find(
|
|
442
|
+
(candidate) =>
|
|
443
|
+
candidate.identity.serverId === request.identity.serverId &&
|
|
444
|
+
candidate.identity.toolName === request.identity.toolName,
|
|
445
|
+
);
|
|
446
|
+
if (entry && digestWorkspaceToolGatewayCatalogEntry(entry) === attestedEntry) {
|
|
447
|
+
const callStartedAt = performance.now();
|
|
448
|
+
// The target-only gateway verifies its own digest; the caller's
|
|
449
|
+
// complete digest was verified through the exact attested entry.
|
|
450
|
+
const response = await callGateway(target, {
|
|
451
|
+
...request,
|
|
452
|
+
catalogDigest: target.toolGatewayCatalog.digest,
|
|
453
|
+
});
|
|
454
|
+
return {
|
|
455
|
+
response: ToolGatewayCallResponse.parse({
|
|
456
|
+
...response,
|
|
457
|
+
catalogDigest: request.catalogDigest,
|
|
458
|
+
}),
|
|
459
|
+
timing: {
|
|
460
|
+
prepareMs,
|
|
461
|
+
callMs: performance.now() - callStartedAt,
|
|
462
|
+
scope: "target",
|
|
463
|
+
attestation,
|
|
464
|
+
},
|
|
465
|
+
};
|
|
466
|
+
}
|
|
467
|
+
attestation = "mismatch";
|
|
468
|
+
preparedScope = "complete";
|
|
469
|
+
} finally {
|
|
470
|
+
await target.close();
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
const prepared = await timedPrepare();
|
|
474
|
+
try {
|
|
475
|
+
options.attestations?.record(scope, prepared.toolGatewayCatalog);
|
|
476
|
+
const callStartedAt = performance.now();
|
|
477
|
+
const response = await callGateway(prepared, request);
|
|
478
|
+
return {
|
|
479
|
+
response,
|
|
480
|
+
timing: {
|
|
481
|
+
prepareMs,
|
|
482
|
+
callMs: performance.now() - callStartedAt,
|
|
483
|
+
scope: "complete",
|
|
484
|
+
attestation,
|
|
485
|
+
},
|
|
486
|
+
};
|
|
487
|
+
} finally {
|
|
488
|
+
await prepared.close();
|
|
489
|
+
}
|
|
490
|
+
} finally {
|
|
491
|
+
try {
|
|
492
|
+
options.onPreparation?.({ prepareMs, scope: preparedScope, attestation });
|
|
493
|
+
} catch {
|
|
494
|
+
// Telemetry must never change gateway execution truth.
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
/** Content-free Server-Timing for gateway preparation versus execution. */
|
|
500
|
+
export function workspaceToolGatewayServerTiming(timing: {
|
|
501
|
+
prepareMs: number;
|
|
502
|
+
callMs?: number;
|
|
503
|
+
scope: WorkspaceToolGatewayPreparationScope;
|
|
504
|
+
attestation?: WorkspaceToolGatewayAttestationOutcome;
|
|
505
|
+
}): string {
|
|
506
|
+
const prepare = `gw-prepare;dur=${timing.prepareMs.toFixed(1)};desc="${timing.scope}${
|
|
507
|
+
timing.attestation ? `/${timing.attestation}` : ""
|
|
508
|
+
}"`;
|
|
509
|
+
return timing.callMs === undefined
|
|
510
|
+
? prepare
|
|
511
|
+
: `${prepare}, gw-call;dur=${timing.callMs.toFixed(1)}`;
|
|
208
512
|
}
|
|
209
513
|
|
|
210
514
|
export async function prepareMcpOAuthWorkspaceToolGateway(
|
|
@@ -222,26 +526,263 @@ export async function prepareWorkspaceToolGatewayForGrant(
|
|
|
222
526
|
routeDeps: ApiRouteDeps,
|
|
223
527
|
grant: AccessGrant,
|
|
224
528
|
allowedIdentities?: readonly { serverId: string; toolName: string }[],
|
|
529
|
+
options: WorkspaceToolGatewayPreparationOptions = {},
|
|
225
530
|
): Promise<PreparedWorkspaceToolGateway> {
|
|
226
|
-
return await prepareWorkspaceToolGatewayForGrantInternal(
|
|
531
|
+
return await prepareWorkspaceToolGatewayForGrantInternal(
|
|
532
|
+
routeDeps,
|
|
533
|
+
grant,
|
|
534
|
+
allowedIdentities,
|
|
535
|
+
undefined,
|
|
536
|
+
options,
|
|
537
|
+
);
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
export type WorkspaceToolGatewayPreparationOptions = {
|
|
541
|
+
target?: ToolGatewayTarget;
|
|
542
|
+
signal?: AbortSignal;
|
|
543
|
+
/** Trusted transport callback, never a request-body field. */
|
|
544
|
+
reauthorize?: () => Promise<void>;
|
|
545
|
+
/**
|
|
546
|
+
* Trusted per-request live fence of a targeted or manifest request (never a
|
|
547
|
+
* request-body field). Required with `target`; replaces `reauthorize`.
|
|
548
|
+
*/
|
|
549
|
+
fence?: TargetAuthorityFence;
|
|
550
|
+
/**
|
|
551
|
+
* Settings visible to first-party (opengeni/files/docs) tool handlers.
|
|
552
|
+
* `prepared` (default, MCP OAuth): the identity-narrowed server set.
|
|
553
|
+
* `caller`: the complete caller-authorized set, so a target-only call
|
|
554
|
+
* executes exactly as it would from the complete gateway.
|
|
555
|
+
*/
|
|
556
|
+
firstPartySettings?: "prepared" | "caller";
|
|
557
|
+
};
|
|
558
|
+
|
|
559
|
+
/** Keep live caller authority independent of native connection acquisition and refresh. */
|
|
560
|
+
export function withWorkspaceConnectionAuthorization(
|
|
561
|
+
resolve: ReturnType<typeof buildConnectionTokenResolver>,
|
|
562
|
+
reauthorize?: () => Promise<void>,
|
|
563
|
+
): ReturnType<typeof buildConnectionTokenResolver> {
|
|
564
|
+
if (!reauthorize) return resolve;
|
|
565
|
+
return async (input) => {
|
|
566
|
+
await reauthorize();
|
|
567
|
+
const result = await resolve(input);
|
|
568
|
+
await reauthorize();
|
|
569
|
+
if (result.status !== "ok") return result;
|
|
570
|
+
return {
|
|
571
|
+
...result,
|
|
572
|
+
authorizeProviderRequest: async () => {
|
|
573
|
+
try {
|
|
574
|
+
await reauthorize();
|
|
575
|
+
const allowed = result.authorizeProviderRequest
|
|
576
|
+
? await result.authorizeProviderRequest()
|
|
577
|
+
: true;
|
|
578
|
+
await reauthorize();
|
|
579
|
+
return allowed;
|
|
580
|
+
} catch {
|
|
581
|
+
return false;
|
|
582
|
+
}
|
|
583
|
+
},
|
|
584
|
+
};
|
|
585
|
+
};
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
/**
|
|
589
|
+
* Credential boundary of a fenced targeted request. Before an execution-mode
|
|
590
|
+
* resolve (which may refresh a token or record use) only the live caller is
|
|
591
|
+
* checked. The returned hook runs immediately before one physical provider
|
|
592
|
+
* request: the native hook, then (targeted) a non-refreshing native preflight
|
|
593
|
+
* that must still name the same connection version, then one full fence
|
|
594
|
+
* evaluation, the hook's last step. A hook retained past the request scope
|
|
595
|
+
* reads nothing.
|
|
596
|
+
*/
|
|
597
|
+
export function withTargetFenceConnectionAuthorization(
|
|
598
|
+
resolve: ReturnType<typeof buildConnectionTokenResolver>,
|
|
599
|
+
fence: Pick<TargetAuthorityFence, "caller" | "allows" | "assertOpen">,
|
|
600
|
+
options: { preflight: boolean },
|
|
601
|
+
): ReturnType<typeof buildConnectionTokenResolver> {
|
|
602
|
+
return async (input) => {
|
|
603
|
+
await fence.caller();
|
|
604
|
+
const result = await resolve(input);
|
|
605
|
+
if (result.status !== "ok") return result;
|
|
606
|
+
return {
|
|
607
|
+
...result,
|
|
608
|
+
authorizeProviderRequest: async () => {
|
|
609
|
+
try {
|
|
610
|
+
fence.assertOpen();
|
|
611
|
+
if (result.authorizeProviderRequest && !(await result.authorizeProviderRequest()))
|
|
612
|
+
return false;
|
|
613
|
+
if (options.preflight) {
|
|
614
|
+
const live = await resolve({
|
|
615
|
+
...input,
|
|
616
|
+
forceRefresh: false,
|
|
617
|
+
credentialResolutionMode: "preflight",
|
|
618
|
+
});
|
|
619
|
+
if (
|
|
620
|
+
live.status !== "ok" ||
|
|
621
|
+
live.connectionId !== result.connectionId ||
|
|
622
|
+
live.connectionVersion !== result.connectionVersion ||
|
|
623
|
+
(live.authorizeProviderRequest && !(await live.authorizeProviderRequest()))
|
|
624
|
+
)
|
|
625
|
+
return false;
|
|
626
|
+
}
|
|
627
|
+
} catch {
|
|
628
|
+
return false;
|
|
629
|
+
}
|
|
630
|
+
return await fence.allows();
|
|
631
|
+
},
|
|
632
|
+
};
|
|
633
|
+
};
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
/**
|
|
637
|
+
* The route part of a targeted fence. It reads only the rows that can produce
|
|
638
|
+
* the selected servers (their capability installations and explicit claimants,
|
|
639
|
+
* integrations with their runtime keys, the selected connections, and Codex
|
|
640
|
+
* only for a Codex target) and feeds them through the same projections as
|
|
641
|
+
* preparation. Any difference from the prepared route denies.
|
|
642
|
+
*/
|
|
643
|
+
function workspaceToolGatewayTargetRouteCheck(input: {
|
|
644
|
+
grant: AccessGrant;
|
|
645
|
+
staticSettings: Settings;
|
|
646
|
+
gatewaySettings: Settings;
|
|
647
|
+
integrations: readonly ApiIntegrationRuntime[];
|
|
648
|
+
capabilityServers: readonly EnabledMcpCapabilityServer[];
|
|
649
|
+
accountBindings: Awaited<ReturnType<typeof availableMcpAccountBindings>>;
|
|
650
|
+
bindingSource: Parameters<typeof availableMcpAccountBindings>[0]["source"];
|
|
651
|
+
}): (db: Database, stop: AbortSignal) => Promise<void> {
|
|
652
|
+
const { grant, gatewaySettings, bindingSource } = input;
|
|
653
|
+
const gatewayServerIds = new Set(gatewaySettings.mcpServers.map((server) => server.id));
|
|
654
|
+
const selectedBindings = input.accountBindings.filter((binding) =>
|
|
655
|
+
gatewayServerIds.has(binding.serverId),
|
|
656
|
+
);
|
|
657
|
+
// Canonical connector ids plus the account route ids themselves: any row
|
|
658
|
+
// that could produce, shadow or collide with the selected route is read.
|
|
659
|
+
const serverIds = [
|
|
660
|
+
...new Set(
|
|
661
|
+
gatewaySettings.mcpServers.flatMap((server) => [
|
|
662
|
+
server.id,
|
|
663
|
+
...selectedBindings
|
|
664
|
+
.filter((binding) => binding.serverId === server.id)
|
|
665
|
+
.map((binding) => binding.canonicalServerId),
|
|
666
|
+
]),
|
|
667
|
+
),
|
|
668
|
+
];
|
|
669
|
+
const capabilityIds = [
|
|
670
|
+
...new Set(
|
|
671
|
+
input.capabilityServers
|
|
672
|
+
.filter((server) => serverIds.includes(server.id))
|
|
673
|
+
.map((server) => server.capabilityId),
|
|
674
|
+
),
|
|
675
|
+
];
|
|
676
|
+
const connectionIds = (scope: "workspace" | "subject") =>
|
|
677
|
+
selectedBindings
|
|
678
|
+
.filter((binding) => binding.subjectScope === scope)
|
|
679
|
+
.map((binding) => binding.connectionId);
|
|
680
|
+
const integrationRoutes = (
|
|
681
|
+
integrations: readonly ApiIntegrationRuntime[],
|
|
682
|
+
bindings: typeof input.accountBindings,
|
|
683
|
+
) =>
|
|
684
|
+
expandApiIntegrationAccountRoutes({
|
|
685
|
+
integrations,
|
|
686
|
+
bindings,
|
|
687
|
+
tools: allGatewayToolRefs(gatewaySettings),
|
|
688
|
+
});
|
|
689
|
+
const expectedServers = digestCanonicalJson(gatewaySettings.mcpServers);
|
|
690
|
+
const expectedIntegrations = digestCanonicalJson(
|
|
691
|
+
integrationRoutes(input.integrations, input.accountBindings),
|
|
692
|
+
);
|
|
693
|
+
const changed = () => new HTTPException(403, { message: "tool_access_changed" });
|
|
694
|
+
return async (db, stop) => {
|
|
695
|
+
let currentIntegrations: readonly ApiIntegrationRuntime[] = [];
|
|
696
|
+
const currentSettings = await settingsWithEnabledCapabilityMcpServersForTargets(
|
|
697
|
+
db,
|
|
698
|
+
grant.workspaceId,
|
|
699
|
+
input.staticSettings,
|
|
700
|
+
{
|
|
701
|
+
subjectId: grant.subjectId,
|
|
702
|
+
accountId: grant.accountId,
|
|
703
|
+
serverIds,
|
|
704
|
+
capabilityIds,
|
|
705
|
+
onResolvedApiIntegrations: (items) => {
|
|
706
|
+
currentIntegrations = items;
|
|
707
|
+
},
|
|
708
|
+
signal: stop,
|
|
709
|
+
},
|
|
710
|
+
);
|
|
711
|
+
stop.throwIfAborted();
|
|
712
|
+
const connections = await visibleMcpAccountConnectionsByIds(db, {
|
|
713
|
+
accountId: grant.accountId,
|
|
714
|
+
workspaceId: grant.workspaceId,
|
|
715
|
+
source: bindingSource,
|
|
716
|
+
workspaceConnectionIds: connectionIds("workspace"),
|
|
717
|
+
personalConnectionIds: connectionIds("subject"),
|
|
718
|
+
});
|
|
719
|
+
// Reads nothing more: the selected connections are supplied.
|
|
720
|
+
const currentBindings = await availableMcpAccountBindings({
|
|
721
|
+
db,
|
|
722
|
+
accountId: grant.accountId,
|
|
723
|
+
workspaceId: grant.workspaceId,
|
|
724
|
+
settings: currentSettings,
|
|
725
|
+
tools: allGatewayToolRefs(currentSettings),
|
|
726
|
+
source: bindingSource,
|
|
727
|
+
connections,
|
|
728
|
+
});
|
|
729
|
+
let currentServers: string;
|
|
730
|
+
let currentIntegrationRoutes: string;
|
|
731
|
+
try {
|
|
732
|
+
const currentRoutes = expandMcpAccountRoutes({
|
|
733
|
+
settings: currentSettings,
|
|
734
|
+
tools: allGatewayToolRefs(currentSettings),
|
|
735
|
+
bindings: currentBindings,
|
|
736
|
+
});
|
|
737
|
+
currentServers = digestCanonicalJson(
|
|
738
|
+
workspaceToolGatewaySettingsForGrant(currentRoutes.settings, grant).mcpServers.filter(
|
|
739
|
+
(server) => gatewayServerIds.has(server.id),
|
|
740
|
+
),
|
|
741
|
+
);
|
|
742
|
+
currentIntegrationRoutes = digestCanonicalJson(
|
|
743
|
+
integrationRoutes(currentIntegrations, currentBindings),
|
|
744
|
+
);
|
|
745
|
+
} catch {
|
|
746
|
+
// A route that can no longer be expanded is a changed route.
|
|
747
|
+
throw changed();
|
|
748
|
+
}
|
|
749
|
+
if (currentServers !== expectedServers || currentIntegrationRoutes !== expectedIntegrations)
|
|
750
|
+
throw changed();
|
|
751
|
+
};
|
|
227
752
|
}
|
|
228
753
|
|
|
229
754
|
async function prepareWorkspaceToolGatewayForGrantInternal(
|
|
230
755
|
routeDeps: ApiRouteDeps,
|
|
231
756
|
grant: AccessGrant,
|
|
232
757
|
allowedIdentities?: readonly { serverId: string; toolName: string }[],
|
|
233
|
-
|
|
758
|
+
reauthorize?: () => Promise<void>,
|
|
759
|
+
options: WorkspaceToolGatewayPreparationOptions = {},
|
|
234
760
|
): Promise<PreparedWorkspaceToolGateway> {
|
|
235
761
|
const catalogSourceSettings = routeDeps.catalogSourceSettings ?? routeDeps.settings;
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
762
|
+
// Remote tools do not consume the model catalog. First-party handlers keep
|
|
763
|
+
// the complete caller model/settings context (not a narrowed provider view).
|
|
764
|
+
const targetNamespace =
|
|
765
|
+
options.target &&
|
|
766
|
+
("identity" in options.target ? options.target.identity.serverId : options.target.path[0]);
|
|
767
|
+
const needsModelContext =
|
|
768
|
+
!targetNamespace || ["opengeni", "docs", "files", "artifacts"].includes(targetNamespace);
|
|
769
|
+
const resolvedCatalog = needsModelContext
|
|
770
|
+
? await resolveWorkspaceCatalogSettings(routeDeps.db, catalogSourceSettings, {
|
|
771
|
+
accountId: grant.accountId,
|
|
772
|
+
workspaceId: grant.workspaceId,
|
|
773
|
+
})
|
|
774
|
+
: { settings: catalogSourceSettings };
|
|
244
775
|
let integrations: readonly ApiIntegrationRuntime[] = [];
|
|
776
|
+
let capabilityServers: readonly EnabledMcpCapabilityServer[] = [];
|
|
777
|
+
// One designation read serves the catalog overlay and the Apps request
|
|
778
|
+
// authentication below (every Apps request is rechecked by the database).
|
|
779
|
+
let codexAppsDesignationRead: ReturnType<typeof resolveCodexAppsDesignationForRun> | null = null;
|
|
780
|
+
const codexAppsDesignationForGrant = () =>
|
|
781
|
+
(codexAppsDesignationRead ??= resolveCodexAppsDesignationForRun(
|
|
782
|
+
routeDeps.db,
|
|
783
|
+
grant.workspaceId,
|
|
784
|
+
{ accountId: grant.accountId },
|
|
785
|
+
));
|
|
245
786
|
const settings = await settingsWithEnabledCapabilityMcpServers(
|
|
246
787
|
routeDeps.db,
|
|
247
788
|
grant.workspaceId,
|
|
@@ -251,29 +792,92 @@ async function prepareWorkspaceToolGatewayForGrantInternal(
|
|
|
251
792
|
onResolvedApiIntegrations: (resolved) => {
|
|
252
793
|
integrations = resolved;
|
|
253
794
|
},
|
|
795
|
+
onResolvedCapabilityServers: (resolved) => {
|
|
796
|
+
capabilityServers = resolved;
|
|
797
|
+
},
|
|
798
|
+
...(resolvedCatalog.settings.codexConnectedAppsEnabled
|
|
799
|
+
? { codexApps: codexAppsDesignationForGrant() }
|
|
800
|
+
: {}),
|
|
254
801
|
},
|
|
255
802
|
);
|
|
256
|
-
|
|
803
|
+
// Transport admission has already verified the current caller. A service
|
|
804
|
+
// receives workspace accounts only; human transports use their exact subject.
|
|
805
|
+
const bindingSource: Parameters<typeof availableMcpAccountBindings>[0]["source"] =
|
|
806
|
+
grant.principalKind === "service" || grant.principalKind === "api_key"
|
|
807
|
+
? { kind: "none" }
|
|
808
|
+
: { kind: "subject", accountId: grant.accountId, subjectId: grant.subjectId };
|
|
809
|
+
const accountBindings = await availableMcpAccountBindings({
|
|
810
|
+
db: routeDeps.db,
|
|
811
|
+
accountId: grant.accountId,
|
|
812
|
+
workspaceId: grant.workspaceId,
|
|
813
|
+
settings,
|
|
814
|
+
tools: allGatewayToolRefs(settings),
|
|
815
|
+
source: bindingSource,
|
|
816
|
+
});
|
|
817
|
+
const accountRoutes = expandMcpAccountRoutes({
|
|
818
|
+
settings,
|
|
819
|
+
tools: allGatewayToolRefs(settings),
|
|
820
|
+
bindings: accountBindings,
|
|
821
|
+
});
|
|
822
|
+
const callerSettings = workspaceToolGatewaySettingsForGrant(accountRoutes.settings, grant);
|
|
823
|
+
const targetServerId = options.target
|
|
824
|
+
? workspaceToolGatewayTargetServer(callerSettings, options.target)
|
|
825
|
+
: undefined;
|
|
826
|
+
if (options.target && "identity" in options.target) allowedIdentities = [options.target.identity];
|
|
827
|
+
// OAuth/Site identities are account-qualified. Intersect only after expansion;
|
|
828
|
+
// canonical connector IDs are never aliases for an account's execution route.
|
|
829
|
+
const gatewaySettings = workspaceToolGatewaySettingsForGrant(
|
|
830
|
+
targetServerId
|
|
831
|
+
? {
|
|
832
|
+
...accountRoutes.settings,
|
|
833
|
+
mcpServers: accountRoutes.settings.mcpServers.filter(
|
|
834
|
+
(server) => server.id === targetServerId,
|
|
835
|
+
),
|
|
836
|
+
}
|
|
837
|
+
: accountRoutes.settings,
|
|
838
|
+
grant,
|
|
839
|
+
allowedIdentities,
|
|
840
|
+
);
|
|
257
841
|
const gatewayServerIds = new Set(gatewaySettings.mcpServers.map((server) => server.id));
|
|
258
|
-
const
|
|
842
|
+
const fence = options.fence;
|
|
843
|
+
// Targeted preparation is always fenced: its live route and policy checks
|
|
844
|
+
// exist only as fence parts. Frozen attempt/Codemode decisions and the
|
|
845
|
+
// complete gateway retain their existing lifecycle semantics.
|
|
846
|
+
if (options.target && !fence) throw new Error("targeted tool preparation requires a live fence");
|
|
847
|
+
if (fence && options.target)
|
|
848
|
+
fence.setRoute(
|
|
849
|
+
workspaceToolGatewayTargetRouteCheck({
|
|
850
|
+
grant,
|
|
851
|
+
staticSettings: resolvedCatalog.settings,
|
|
852
|
+
gatewaySettings,
|
|
853
|
+
integrations,
|
|
854
|
+
capabilityServers,
|
|
855
|
+
accountBindings,
|
|
856
|
+
bindingSource,
|
|
857
|
+
}),
|
|
858
|
+
);
|
|
859
|
+
const firstPartySettings =
|
|
860
|
+
options.firstPartySettings === "caller" && (allowedIdentities || options.target)
|
|
861
|
+
? callerSettings
|
|
862
|
+
: gatewaySettings;
|
|
863
|
+
const deps = { ...routeDeps, catalogSourceSettings, settings: firstPartySettings };
|
|
259
864
|
const nativeResolveConnection = buildConnectionTokenResolver(routeDeps.db, gatewaySettings);
|
|
260
|
-
const
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
)
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
: await nativeResolveConnection(input);
|
|
865
|
+
const resolveConnection = fence
|
|
866
|
+
? withTargetFenceConnectionAuthorization(nativeResolveConnection, fence, {
|
|
867
|
+
preflight: options.target !== undefined,
|
|
868
|
+
})
|
|
869
|
+
: withWorkspaceConnectionAuthorization(nativeResolveConnection, reauthorize);
|
|
870
|
+
// The runtime's guarded transport runs this before every remote MCP request.
|
|
871
|
+
// Connection-broker requests are already fenced by their credential hook, so
|
|
872
|
+
// only Codex Apps and connectors without a connection reference (the
|
|
873
|
+
// runtime's own routing rule, widened to every Codex id) evaluate it here.
|
|
874
|
+
const authorizeTransportRequest = fence
|
|
875
|
+
? gatewaySettings.mcpServers.some(
|
|
876
|
+
(server) => server.id === CODEX_APPS_MCP_SERVER_ID || !server.connectionRef,
|
|
877
|
+
)
|
|
878
|
+
? async () => await fence.full()
|
|
879
|
+
: async () => fence.assertOpen()
|
|
880
|
+
: reauthorize;
|
|
277
881
|
const resolveCredential = async (
|
|
278
882
|
input: ResolveConnectionCredentialInput,
|
|
279
883
|
): Promise<ResolveConnectionCredentialResult> =>
|
|
@@ -299,6 +903,7 @@ async function prepareWorkspaceToolGatewayForGrantInternal(
|
|
|
299
903
|
routeDeps.getDocumentServices(),
|
|
300
904
|
{
|
|
301
905
|
knowledge: await knowledgeContextForGateway(routeDeps, grant),
|
|
906
|
+
settings: firstPartySettings,
|
|
302
907
|
},
|
|
303
908
|
),
|
|
304
909
|
),
|
|
@@ -307,7 +912,11 @@ async function prepareWorkspaceToolGatewayForGrantInternal(
|
|
|
307
912
|
]);
|
|
308
913
|
const apiIntegrationServers = buildApiIntegrationMcpServers({
|
|
309
914
|
settings: gatewaySettings,
|
|
310
|
-
integrations:
|
|
915
|
+
integrations: expandApiIntegrationAccountRoutes({
|
|
916
|
+
integrations,
|
|
917
|
+
bindings: accountBindings,
|
|
918
|
+
tools: allGatewayToolRefs(gatewaySettings),
|
|
919
|
+
}),
|
|
311
920
|
authority: {
|
|
312
921
|
accountId: grant.accountId,
|
|
313
922
|
workspaceId: grant.workspaceId,
|
|
@@ -319,37 +928,51 @@ async function prepareWorkspaceToolGatewayForGrantInternal(
|
|
|
319
928
|
...(input.connectionRef.subjectScope === "subject" ? { subjectId: grant.subjectId } : {}),
|
|
320
929
|
}),
|
|
321
930
|
});
|
|
322
|
-
|
|
323
|
-
|
|
931
|
+
// Legacy designation without a Codex cutover row; the core designation
|
|
932
|
+
// (rechecked by the database on every request) with an enabled cutover;
|
|
933
|
+
// none with a disabled cutover.
|
|
934
|
+
const codexAppsDesignation = gatewayServerIds.has("codex_apps")
|
|
935
|
+
? await codexAppsDesignationForGrant()
|
|
324
936
|
: null;
|
|
325
|
-
const codexAppsAuth =
|
|
326
|
-
? (
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
);
|
|
333
|
-
return {
|
|
334
|
-
clientVersion: CODEX_CLIENT_VERSION,
|
|
335
|
-
withAuthorization: async <T>(
|
|
336
|
-
use: (token: { accessToken: string; chatgptAccountId: string | null }) => Promise<T>,
|
|
337
|
-
): Promise<T> =>
|
|
338
|
-
await resolver.getToken().then(
|
|
339
|
-
async (token) =>
|
|
340
|
-
await withCodexAppsRequestAuthorization(
|
|
341
|
-
routeDeps.db,
|
|
342
|
-
{
|
|
343
|
-
workspaceId: grant.workspaceId,
|
|
344
|
-
credentialId: codexAppsCredentialId,
|
|
345
|
-
},
|
|
346
|
-
async () => await use(token),
|
|
347
|
-
),
|
|
348
|
-
),
|
|
349
|
-
};
|
|
350
|
-
})()
|
|
937
|
+
const codexAppsAuth = codexAppsDesignation
|
|
938
|
+
? codexAppsRequestAuthForDesignation(
|
|
939
|
+
routeDeps.db,
|
|
940
|
+
settings,
|
|
941
|
+
grant.workspaceId,
|
|
942
|
+
codexAppsDesignation,
|
|
943
|
+
)
|
|
351
944
|
: undefined;
|
|
352
945
|
const localMcpServers = [...firstPartyServers, ...apiIntegrationServers];
|
|
946
|
+
const policyTargets = new Map(
|
|
947
|
+
gatewaySettings.mcpServers.map((config) => {
|
|
948
|
+
const binding = accountBindings.find((candidate) => candidate.serverId === config.id);
|
|
949
|
+
return [
|
|
950
|
+
config.id,
|
|
951
|
+
{
|
|
952
|
+
connectionId:
|
|
953
|
+
binding?.connectionId ??
|
|
954
|
+
config.connectionRef?.connectionId ??
|
|
955
|
+
sessionMcpApprovalConnectionId(config.id, config.url),
|
|
956
|
+
serverId: binding?.canonicalServerId ?? config.id,
|
|
957
|
+
},
|
|
958
|
+
] as const;
|
|
959
|
+
}),
|
|
960
|
+
);
|
|
961
|
+
const policiesByServer = new Map(
|
|
962
|
+
await Promise.all(
|
|
963
|
+
[...policyTargets].map(
|
|
964
|
+
async ([id, target]) =>
|
|
965
|
+
[
|
|
966
|
+
id,
|
|
967
|
+
await listConnectorToolPermissionPolicies(routeDeps.db, {
|
|
968
|
+
...grant,
|
|
969
|
+
connectionId: target.connectionId,
|
|
970
|
+
}),
|
|
971
|
+
] as const,
|
|
972
|
+
),
|
|
973
|
+
),
|
|
974
|
+
);
|
|
975
|
+
const recommendations = new Map<string, "allow" | "ask">();
|
|
353
976
|
const prepared = await prepareWorkspaceToolGatewayTools(
|
|
354
977
|
gatewaySettings,
|
|
355
978
|
allGatewayToolRefs(gatewaySettings),
|
|
@@ -357,14 +980,114 @@ async function prepareWorkspaceToolGatewayForGrantInternal(
|
|
|
357
980
|
accountId: grant.accountId,
|
|
358
981
|
workspaceId: grant.workspaceId,
|
|
359
982
|
subjectId: grant.subjectId,
|
|
983
|
+
...(options.signal ? { requestSignal: options.signal } : {}),
|
|
984
|
+
...(authorizeTransportRequest ? { authorizeProviderRequest: authorizeTransportRequest } : {}),
|
|
360
985
|
credentialSubjectId: grant.subjectId,
|
|
986
|
+
mcpAccountLabels: accountRoutes.accountLabels,
|
|
361
987
|
resolveCredential,
|
|
988
|
+
slackRateLimit: buildSlackApiRateLimiter(routeDeps.db, gatewaySettings),
|
|
362
989
|
localMcpServers,
|
|
363
990
|
...(codexAppsAuth ? { codexAppsAuth } : {}),
|
|
364
991
|
workspaceToolGateway: {
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
992
|
+
resolveArguments: workspaceGatewaySecretArguments({
|
|
993
|
+
db: routeDeps.db,
|
|
994
|
+
settings: gatewaySettings,
|
|
995
|
+
grant,
|
|
996
|
+
providersForSubject: (subjectId) =>
|
|
997
|
+
vaultProviderRegistryFor(routeDeps, { workspaceId: grant.workspaceId, subjectId }),
|
|
998
|
+
}),
|
|
999
|
+
mapDefinition: (definition) => {
|
|
1000
|
+
// Raw/local providers have no native credential revision. The legacy
|
|
1001
|
+
// gateway fallback binds the whole public catalog, including Ask/Allow
|
|
1002
|
+
// presentation. Targeted approvals instead use the runtime's existing
|
|
1003
|
+
// executable authority plus the exact selected configuration already
|
|
1004
|
+
// fenced by the target route check. Never replace adapter authority.
|
|
1005
|
+
if (options.target && definition.approvalAuthorityDigest === undefined) {
|
|
1006
|
+
const config = gatewaySettings.mcpServers.find(
|
|
1007
|
+
(server) => server.id === definition.identity.serverId,
|
|
1008
|
+
);
|
|
1009
|
+
if (!config || !definition.effectAuthorityDigest)
|
|
1010
|
+
throw new Error("target approval authority unavailable");
|
|
1011
|
+
definition = {
|
|
1012
|
+
...definition,
|
|
1013
|
+
approvalAuthorityDigest: digestCanonicalJson({
|
|
1014
|
+
domain: "opengeni.target-tool-config-authority",
|
|
1015
|
+
version: 1,
|
|
1016
|
+
accountId: grant.accountId,
|
|
1017
|
+
workspaceId: grant.workspaceId,
|
|
1018
|
+
identity: definition.identity,
|
|
1019
|
+
effectAuthorityDigest: definition.effectAuthorityDigest,
|
|
1020
|
+
configuration: config,
|
|
1021
|
+
}),
|
|
1022
|
+
};
|
|
1023
|
+
}
|
|
1024
|
+
const recommendation = definition.approval === "human" ? "ask" : "allow";
|
|
1025
|
+
recommendations.set(workspaceToolGatewayIdentityKey(definition.identity), recommendation);
|
|
1026
|
+
const target = policyTargets.get(definition.identity.serverId);
|
|
1027
|
+
if (!target) return definition;
|
|
1028
|
+
const permission = projectConnectorToolPermission(
|
|
1029
|
+
policiesByServer.get(definition.identity.serverId) ?? [],
|
|
1030
|
+
{
|
|
1031
|
+
...target,
|
|
1032
|
+
toolName: definition.identity.toolName,
|
|
1033
|
+
defaultDecision: recommendation,
|
|
1034
|
+
},
|
|
1035
|
+
);
|
|
1036
|
+
// A transport without a verified approval round trip omits any tool
|
|
1037
|
+
// which could ask. Its execution still resolves the exact arguments.
|
|
1038
|
+
return { ...definition, approval: permission.approvalRequired ? "human" : "policy" };
|
|
1039
|
+
},
|
|
1040
|
+
resolveApproval: async ({ call, entry }) => {
|
|
1041
|
+
const defaultDecision =
|
|
1042
|
+
recommendations.get(workspaceToolGatewayIdentityKey(entry.identity)) ??
|
|
1043
|
+
(entry.approval === "human" ? "ask" : "allow");
|
|
1044
|
+
const target = policyTargets.get(entry.identity.serverId);
|
|
1045
|
+
if (!target) return defaultDecision;
|
|
1046
|
+
const resolveSelectedPolicy = async (db: Database = routeDeps.db) =>
|
|
1047
|
+
resolveConnectorActionPolicy(
|
|
1048
|
+
await listConnectorToolPermissionPolicies(db, {
|
|
1049
|
+
...grant,
|
|
1050
|
+
connectionId: target.connectionId,
|
|
1051
|
+
}),
|
|
1052
|
+
{
|
|
1053
|
+
...target,
|
|
1054
|
+
toolName: entry.identity.toolName,
|
|
1055
|
+
defaultDecision,
|
|
1056
|
+
actionName: toolPolicyActionName(
|
|
1057
|
+
entry.identity.toolName,
|
|
1058
|
+
entry.inputSchema,
|
|
1059
|
+
call.arguments,
|
|
1060
|
+
),
|
|
1061
|
+
},
|
|
1062
|
+
);
|
|
1063
|
+
const resolved = await resolveSelectedPolicy();
|
|
1064
|
+
const decision = !resolved.managed
|
|
1065
|
+
? defaultDecision
|
|
1066
|
+
: connectorActionPolicyDecision(resolved);
|
|
1067
|
+
recordToolApproval(decision, resolved.managed ? resolved.source : "default");
|
|
1068
|
+
// Only targeted current requests fence their decided action policy
|
|
1069
|
+
// live, at every later physical-request boundary of this call.
|
|
1070
|
+
if (fence && options.target)
|
|
1071
|
+
fence.setPolicy(async (db) => {
|
|
1072
|
+
const live = await resolveSelectedPolicy(db);
|
|
1073
|
+
const current = live.managed ? connectorActionPolicyDecision(live) : defaultDecision;
|
|
1074
|
+
if (current === "block") throw new ToolGatewayBlockedError();
|
|
1075
|
+
if (current === "ask" && decision !== "ask")
|
|
1076
|
+
throw new ToolGatewayApprovalRequiredError();
|
|
1077
|
+
});
|
|
1078
|
+
return decision;
|
|
1079
|
+
},
|
|
1080
|
+
filterDefinition: (definition) => {
|
|
1081
|
+
if (!workspaceToolGatewayDefinitionFilter(gatewaySettings, allowedIdentities)(definition))
|
|
1082
|
+
return false;
|
|
1083
|
+
const target = options.target;
|
|
1084
|
+
if (!target || "identity" in target) return true;
|
|
1085
|
+
const path = projectToolGatewayPath(definition);
|
|
1086
|
+
return (
|
|
1087
|
+
path.length === target.path.length &&
|
|
1088
|
+
path.every((part, index) => part === target.path[index])
|
|
1089
|
+
);
|
|
1090
|
+
},
|
|
368
1091
|
},
|
|
369
1092
|
},
|
|
370
1093
|
);
|
|
@@ -372,6 +1095,10 @@ async function prepareWorkspaceToolGatewayForGrantInternal(
|
|
|
372
1095
|
await prepared.close().catch(() => undefined);
|
|
373
1096
|
throw new Error("workspace tool gateway preparation did not produce a gateway");
|
|
374
1097
|
}
|
|
1098
|
+
if (options.signal?.aborted) {
|
|
1099
|
+
await prepared.close().catch(() => undefined);
|
|
1100
|
+
options.signal.throwIfAborted();
|
|
1101
|
+
}
|
|
375
1102
|
return {
|
|
376
1103
|
toolGateway: prepared.toolGateway,
|
|
377
1104
|
toolGatewayCatalog: prepared.toolGatewayCatalog,
|
|
@@ -379,6 +1106,34 @@ async function prepareWorkspaceToolGatewayForGrantInternal(
|
|
|
379
1106
|
};
|
|
380
1107
|
}
|
|
381
1108
|
|
|
1109
|
+
/** Select one authorized connector without constructing any provider. */
|
|
1110
|
+
export function workspaceToolGatewayTargetServer(
|
|
1111
|
+
settings: Pick<Settings, "mcpServers">,
|
|
1112
|
+
target: ToolGatewayTarget,
|
|
1113
|
+
): string {
|
|
1114
|
+
if ("identity" in target) {
|
|
1115
|
+
if (settings.mcpServers.some((server) => server.id === target.identity.serverId))
|
|
1116
|
+
return target.identity.serverId;
|
|
1117
|
+
} else {
|
|
1118
|
+
const candidates = new Set(
|
|
1119
|
+
settings.mcpServers
|
|
1120
|
+
.filter((server) => safeNamespaceSegment(server.id) === target.path[0])
|
|
1121
|
+
.map((server) => server.id),
|
|
1122
|
+
);
|
|
1123
|
+
if (
|
|
1124
|
+
Object.values(EDITABLE_ARTIFACT_MCP_CODEMODE_PATHS).some(
|
|
1125
|
+
(path) =>
|
|
1126
|
+
path.length === target.path.length &&
|
|
1127
|
+
path.every((part, index) => part === target.path[index]),
|
|
1128
|
+
) &&
|
|
1129
|
+
settings.mcpServers.some((server) => server.id === "opengeni")
|
|
1130
|
+
)
|
|
1131
|
+
candidates.add("opengeni");
|
|
1132
|
+
if (candidates.size === 1) return [...candidates][0]!;
|
|
1133
|
+
}
|
|
1134
|
+
throw new HTTPException(404, { message: "tool_unavailable" });
|
|
1135
|
+
}
|
|
1136
|
+
|
|
382
1137
|
export function workspaceToolGatewayDefinitionFilter(
|
|
383
1138
|
settings: Pick<Settings, "allowedFirstPartyMcpTools">,
|
|
384
1139
|
allowedIdentities?: readonly ToolGatewayIdentity[],
|
|
@@ -548,7 +1303,9 @@ export async function callWorkspaceToolGateway(
|
|
|
548
1303
|
);
|
|
549
1304
|
await prepared.reauthorize?.();
|
|
550
1305
|
let approvalConfirmed = false;
|
|
551
|
-
const approvalRequired =
|
|
1306
|
+
const approvalRequired =
|
|
1307
|
+
preparedCall.approvalDecision === "ask" ||
|
|
1308
|
+
(preparedCall.approvalDecision === undefined && preparedCall.entry.approval === "human");
|
|
552
1309
|
if (approvalRequired && request.approvalToken && db) {
|
|
553
1310
|
approvalConfirmed = await consumeApproval(db, {
|
|
554
1311
|
tokenHash: hashOpaqueValue(request.approvalToken),
|
|
@@ -568,17 +1325,16 @@ export async function callWorkspaceToolGateway(
|
|
|
568
1325
|
transportMeta.approvalConfirmed = approvalConfirmed;
|
|
569
1326
|
const origin =
|
|
570
1327
|
siteContext && db
|
|
571
|
-
? await resolveOrigin(
|
|
572
|
-
db,
|
|
573
|
-
grant.workspaceId,
|
|
574
|
-
siteContext.siteArtifactId,
|
|
575
|
-
siteContext.siteVersionId,
|
|
576
|
-
)
|
|
1328
|
+
? await resolveOrigin(db, grant, siteContext.siteArtifactId, siteContext.siteVersionId)
|
|
577
1329
|
: null;
|
|
578
1330
|
await prepared.reauthorize?.();
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
:
|
|
1331
|
+
// Secret references in a Site call resolve with the viewer's reach.
|
|
1332
|
+
const secretContext = siteContext
|
|
1333
|
+
? { siteId: siteContext.siteArtifactId, siteVersion: siteContext.siteVersionId }
|
|
1334
|
+
: null;
|
|
1335
|
+
const result = await withSiteSecretContext(secretContext, () =>
|
|
1336
|
+
origin ? withSiteSessionOrigin(origin, () => preparedCall.execute()) : preparedCall.execute(),
|
|
1337
|
+
);
|
|
582
1338
|
observation.end(result.isError ? "tool_error" : "ok");
|
|
583
1339
|
return ToolGatewayCallResponse.parse({
|
|
584
1340
|
operationId,
|
|
@@ -616,7 +1372,10 @@ export async function approveWorkspaceToolGatewayCall(
|
|
|
616
1372
|
} catch (error) {
|
|
617
1373
|
throwWorkspaceToolGatewayHttpError(error);
|
|
618
1374
|
}
|
|
619
|
-
if (
|
|
1375
|
+
if (
|
|
1376
|
+
preparedCall.approvalDecision !== "ask" &&
|
|
1377
|
+
!(preparedCall.approvalDecision === undefined && preparedCall.entry.approval === "human")
|
|
1378
|
+
) {
|
|
620
1379
|
throw new HTTPException(422, { message: "tool_does_not_require_human_approval" });
|
|
621
1380
|
}
|
|
622
1381
|
const observation = startWorkspaceToolGatewayObservation(observability, {
|
|
@@ -681,7 +1440,7 @@ function catalogStaleHttpError(): ApiHttpError {
|
|
|
681
1440
|
});
|
|
682
1441
|
}
|
|
683
1442
|
|
|
684
|
-
function throwWorkspaceToolGatewayHttpError(error: unknown): never {
|
|
1443
|
+
export function throwWorkspaceToolGatewayHttpError(error: unknown): never {
|
|
685
1444
|
if (error instanceof ToolGatewayCatalogStaleError) {
|
|
686
1445
|
throw catalogStaleHttpError();
|
|
687
1446
|
}
|
|
@@ -689,11 +1448,42 @@ function throwWorkspaceToolGatewayHttpError(error: unknown): never {
|
|
|
689
1448
|
throw new HTTPException(404, { message: error.code, cause: error });
|
|
690
1449
|
}
|
|
691
1450
|
if (error instanceof ToolGatewayInputValidationError) {
|
|
692
|
-
|
|
1451
|
+
// The same value-free summary the model sees: which properties are missing
|
|
1452
|
+
// or mistyped, never the submitted argument values.
|
|
1453
|
+
throw new ApiHttpError(422, {
|
|
1454
|
+
code: "validation_failed",
|
|
1455
|
+
message: error.message,
|
|
1456
|
+
retryable: false,
|
|
1457
|
+
details: {
|
|
1458
|
+
code: error.code,
|
|
1459
|
+
issues: error.issues,
|
|
1460
|
+
omittedIssueCount: error.omittedIssueCount,
|
|
1461
|
+
},
|
|
1462
|
+
});
|
|
693
1463
|
}
|
|
694
1464
|
if (error instanceof ToolGatewayApprovalRequiredError) {
|
|
695
1465
|
throw new HTTPException(409, { message: error.code, cause: error });
|
|
696
1466
|
}
|
|
1467
|
+
if (error instanceof ToolGatewayBlockedError) {
|
|
1468
|
+
throw new HTTPException(403, { message: error.code, cause: error });
|
|
1469
|
+
}
|
|
1470
|
+
if (isSecretReferenceError(error)) {
|
|
1471
|
+
// Nothing ran. The failures name references, never values.
|
|
1472
|
+
throw new ApiHttpError(422, {
|
|
1473
|
+
code: "validation_failed",
|
|
1474
|
+
message: error.message,
|
|
1475
|
+
retryable: false,
|
|
1476
|
+
details: {
|
|
1477
|
+
code: error.code,
|
|
1478
|
+
failures: error.failures.map((failure) => ({
|
|
1479
|
+
raw: failure.raw,
|
|
1480
|
+
code: failure.code,
|
|
1481
|
+
message: failure.message,
|
|
1482
|
+
...(failure.candidates ? { candidates: failure.candidates } : {}),
|
|
1483
|
+
})),
|
|
1484
|
+
},
|
|
1485
|
+
});
|
|
1486
|
+
}
|
|
697
1487
|
if (error instanceof IntegrationInvocationError && error.outcome === "unknown") {
|
|
698
1488
|
throw new ApiHttpError(502, {
|
|
699
1489
|
code: "upstream_unavailable",
|
|
@@ -707,16 +1497,44 @@ function throwWorkspaceToolGatewayHttpError(error: unknown): never {
|
|
|
707
1497
|
throw error;
|
|
708
1498
|
}
|
|
709
1499
|
|
|
1500
|
+
/** A saved Site version, only when the caller can see the Site. */
|
|
1501
|
+
export type WorkspaceSiteVersionResolver = (
|
|
1502
|
+
db: ApiRouteDeps["db"],
|
|
1503
|
+
grant: AccessGrant,
|
|
1504
|
+
artifactId: string,
|
|
1505
|
+
versionId: string,
|
|
1506
|
+
) => Promise<{
|
|
1507
|
+
status: Awaited<ReturnType<typeof getWorkspaceArtifactContentRef>>["status"];
|
|
1508
|
+
version: Pick<
|
|
1509
|
+
Awaited<ReturnType<typeof getWorkspaceArtifactContentRef>>["version"],
|
|
1510
|
+
"id" | "requestedTools"
|
|
1511
|
+
>;
|
|
1512
|
+
}>;
|
|
1513
|
+
|
|
1514
|
+
const resolveVisibleSiteVersion: WorkspaceSiteVersionResolver = async (
|
|
1515
|
+
db,
|
|
1516
|
+
grant,
|
|
1517
|
+
artifactId,
|
|
1518
|
+
versionId,
|
|
1519
|
+
) =>
|
|
1520
|
+
await getWorkspaceArtifactContentRef(
|
|
1521
|
+
db,
|
|
1522
|
+
grant.workspaceId,
|
|
1523
|
+
await siteViewerForGrant(db, grant),
|
|
1524
|
+
artifactId,
|
|
1525
|
+
versionId,
|
|
1526
|
+
);
|
|
1527
|
+
|
|
710
1528
|
export async function requireWorkspaceSiteToolAuthorization(
|
|
711
1529
|
db: ApiRouteDeps["db"],
|
|
712
1530
|
grant: AccessGrant,
|
|
713
1531
|
context: WorkspaceSiteToolContext,
|
|
714
|
-
resolveVersion =
|
|
1532
|
+
resolveVersion: WorkspaceSiteVersionResolver = resolveVisibleSiteVersion,
|
|
715
1533
|
): Promise<void> {
|
|
716
1534
|
try {
|
|
717
1535
|
const { status, version } = await resolveVersion(
|
|
718
1536
|
db,
|
|
719
|
-
grant
|
|
1537
|
+
grant,
|
|
720
1538
|
context.siteArtifactId,
|
|
721
1539
|
context.siteVersionId,
|
|
722
1540
|
);
|