@opengeni/api-router 0.30.1 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/access-grant-rls.d.ts +3 -0
- package/dist/admission-trace.d.ts +8 -0
- package/dist/api-websocket.d.ts +18 -0
- package/dist/app.d.ts +50 -2
- package/dist/app.js +12 -1
- package/dist/artifact-catalog-cursor.d.ts +27 -0
- package/dist/auth/managed-auth-attempt-context.d.ts +10 -0
- package/dist/auth/managed-auth-rate-limits.d.ts +167 -0
- package/dist/auth/managed-auth-session-adapter.d.ts +6 -0
- package/dist/auth/managed-auth.d.ts +102 -3
- package/dist/auth/managed-email.d.ts +29 -0
- package/dist/auth/managed-sign-in-notifications.d.ts +4 -0
- package/dist/auth/new-signups-gate.d.ts +23 -0
- package/dist/auth/organization-user-setup.d.ts +80 -0
- package/dist/auth/signup-funnel-metrics.d.ts +44 -0
- package/dist/browser-controller-authority.d.ts +1 -61
- package/dist/browser-ephemeral.d.ts +5 -0
- package/dist/browser-state-authority.d.ts +1 -27
- package/dist/chunk-6TZ5LG3O.js +123948 -0
- package/dist/chunk-6TZ5LG3O.js.map +1 -0
- package/dist/chunk-OGQDXVXY.js +18 -0
- package/dist/chunk-OGQDXVXY.js.map +1 -0
- package/dist/claude-subscription-account-conflicts.d.ts +1 -0
- package/dist/claude-subscription-account-usage.d.ts +39 -0
- package/dist/claude-subscription-oauth.d.ts +25 -0
- package/dist/claude-subscription-profile.d.ts +9 -0
- package/dist/claude-subscription-usage.d.ts +44 -0
- package/dist/claude-workspace-connection.d.ts +15 -0
- package/dist/codemode.d.ts +32 -1
- package/dist/company-brain-okf.d.ts +24 -0
- package/dist/connected-machine-computer-access.d.ts +11 -0
- package/dist/connection-ownership.d.ts +54 -0
- package/dist/controller-data-plane.d.ts +32 -0
- package/dist/editable-artifact-office-import.d.ts +7 -4
- package/dist/editable-artifact-source-session.d.ts +3 -0
- package/dist/editable-artifact-websocket.d.ts +10 -18
- package/dist/editable-artifact-workspace-files.d.ts +1 -1
- package/dist/fatal-process-boundary.d.ts +25 -0
- package/dist/github-access.d.ts +31 -2
- package/dist/http/acting-person.d.ts +24 -0
- package/dist/http/api-contract-compat.d.ts +19 -0
- package/dist/http/api-error.d.ts +27 -1
- package/dist/http/cors.d.ts +17 -0
- package/dist/http/deprecation.d.ts +42 -0
- package/dist/http/interaction-control-error.d.ts +13 -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/request-body.d.ts +27 -0
- package/dist/http/request-source.d.ts +52 -0
- package/dist/http/sse.d.ts +17 -6
- package/dist/http/unmatched-route.d.ts +18 -0
- package/dist/http/user-content.d.ts +36 -0
- package/dist/http/websocket-upgrade-dispatch.d.ts +18 -0
- package/dist/index.d.ts +10 -3
- package/dist/index.js +1129 -166
- package/dist/index.js.map +1 -1
- package/dist/integration-endpoint-test.d.ts +20 -0
- package/dist/integrations/api-integrations.d.ts +2 -2
- package/dist/integrations/atlassian.d.ts +29 -3
- package/dist/integrations/connect-authority.d.ts +1 -0
- package/dist/integrations/connect-callback-return.d.ts +6 -0
- package/dist/integrations/fiken.d.ts +16 -2
- package/dist/integrations/github-app-connect.d.ts +47 -0
- package/dist/integrations/github-installation-proof.d.ts +3 -0
- package/dist/integrations/github-lens-connect.d.ts +116 -0
- package/dist/integrations/github-skill-source.d.ts +1 -5
- package/dist/integrations/google-drive.d.ts +25 -0
- package/dist/integrations/integration-commit-authority.d.ts +13 -0
- package/dist/integrations/marketplace-plugin.d.ts +2691 -0
- package/dist/integrations/oauth-client.d.ts +178 -18
- package/dist/integrations/oauth-profiles.d.ts +222 -0
- package/dist/integrations/oauth-return-path.d.ts +1 -0
- package/dist/integrations/personal-github-repositories.d.ts +84 -0
- package/dist/integrations/personal-github.d.ts +42 -0
- package/dist/integrations/pr-review-provider.d.ts +22 -0
- package/dist/integrations/provider-oauth.d.ts +19 -1
- package/dist/integrations/slack-app-home.d.ts +24 -0
- package/dist/integrations/slack-bot.d.ts +367 -12
- package/dist/integrations/slack-file-upload-flow.d.ts +62 -0
- package/dist/integrations/slack-install.d.ts +20 -0
- package/dist/integrations/slack-interactions.d.ts +113 -10
- package/dist/integrations/slack-mrkdwn.d.ts +41 -0
- package/dist/integrations/slack-routing.d.ts +120 -0
- package/dist/integrations/slack-session-defaults.d.ts +58 -0
- package/dist/integrations/slack-task-file-upload.d.ts +36 -0
- package/dist/integrations/social-oauth.d.ts +7 -0
- package/dist/interaction-agent-access.d.ts +15 -0
- package/dist/interaction-frame-proxy.d.ts +68 -0
- package/dist/interaction-metrics.d.ts +2 -0
- package/dist/mcp/action-catalog-types.d.ts +9 -0
- package/dist/mcp/action-catalog.gen.d.ts +2 -0
- package/dist/mcp/capability-account-readiness.d.ts +7 -0
- package/dist/mcp/child-read-evidence.d.ts +9 -0
- package/dist/mcp/command-read.d.ts +24 -0
- package/dist/mcp/company-profile-agent-admin.d.ts +44 -0
- package/dist/mcp/contract-input.d.ts +11 -0
- package/dist/mcp/documents.d.ts +7 -5
- package/dist/mcp/files.d.ts +2 -2
- package/dist/mcp/first-party-tool-permissions.d.ts +650 -0
- package/dist/mcp/knowledge-entries.d.ts +5 -0
- package/dist/mcp/orchestration-failure-diagnostic.d.ts +43 -0
- package/dist/mcp/receipts.d.ts +9 -0
- package/dist/mcp/request-abort.d.ts +19 -0
- package/dist/mcp/scheduled-task-input.d.ts +1189 -0
- package/dist/mcp/scheduled-task-view.d.ts +22 -4
- package/dist/mcp/server.d.ts +255 -3
- package/dist/mcp/session-event-view.d.ts +94 -0
- package/dist/mcp/session-view.d.ts +66 -2
- package/dist/mcp/session-wait.d.ts +174 -0
- package/dist/mcp-oauth.d.ts +52 -0
- package/dist/model-catalog.d.ts +11 -31
- package/dist/observability.d.ts +3 -1
- package/dist/organization-mcp.d.ts +52 -0
- package/dist/organization-recovery-notifications.d.ts +41 -0
- package/dist/routes/analytics-consent.d.ts +26 -0
- package/dist/routes/api-integrations.d.ts +25 -0
- package/dist/routes/api-keys.d.ts +17 -0
- package/dist/routes/artifact-catalog.d.ts +6 -0
- package/dist/routes/automations.d.ts +19 -0
- package/dist/routes/billing.d.ts +49 -0
- package/dist/routes/browser-sessions.d.ts +71 -3
- 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 +146 -0
- package/dist/routes/codex.d.ts +76 -2
- package/dist/routes/company-brain.d.ts +3 -0
- package/dist/routes/company-profile.d.ts +2 -1
- package/dist/routes/computer-sessions.d.ts +14 -0
- package/dist/routes/connect.d.ts +5 -0
- package/dist/routes/editable-artifacts.d.ts +2 -0
- package/dist/routes/external-identity-links.d.ts +6 -0
- package/dist/routes/feedback.d.ts +3 -0
- package/dist/routes/files.d.ts +4 -0
- package/dist/routes/github-browser-pages.d.ts +12 -0
- package/dist/routes/github.d.ts +2 -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 +30 -1
- package/dist/routes/knowledge.d.ts +3 -0
- package/dist/routes/managed-auth-session-sets.d.ts +26 -0
- package/dist/routes/managed-onboarding.d.ts +29 -0
- package/dist/routes/managed-sign-in-methods.d.ts +6 -0
- package/dist/routes/model-connection-access.d.ts +3 -0
- package/dist/routes/organization-integration-policy.d.ts +3 -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-providers.d.ts +3 -0
- package/dist/routes/organization-recovery.d.ts +19 -0
- package/dist/routes/organization-service-accounts.d.ts +3 -0
- package/dist/routes/organization-sessions.d.ts +26 -0
- package/dist/routes/personal-github-git-broker.d.ts +29 -0
- package/dist/routes/personal-github.d.ts +3 -0
- package/dist/routes/pr-review-github.d.ts +3 -0
- package/dist/routes/pr-review.d.ts +3 -0
- package/dist/routes/session-artifact-associations.d.ts +4 -0
- package/dist/routes/session-history-imports.d.ts +9 -0
- package/dist/routes/sessions.d.ts +44 -6
- package/dist/routes/skill-content.d.ts +4 -0
- package/dist/routes/skill-install-authority.d.ts +6 -0
- package/dist/routes/subscription-account-pools.d.ts +34 -0
- package/dist/routes/subscription-pool-access.d.ts +16 -0
- package/dist/routes/supergrok.d.ts +6 -1
- package/dist/routes/usage-allowances.d.ts +12 -0
- package/dist/routes/user-resource-authorities.d.ts +3 -0
- package/dist/routes/workspace-artifacts.d.ts +2 -1
- package/dist/routes/workspace-capture.d.ts +10 -2
- package/dist/routes/workspace-integrations.d.ts +35 -0
- package/dist/routes/workspace-learning.d.ts +3 -0
- package/dist/routes/workspace-model-providers.d.ts +4 -0
- package/dist/routes/workspaces.d.ts +29 -0
- package/dist/sandbox/auth-callout.d.ts +8 -0
- package/dist/sandbox/channel-a.d.ts +19 -1
- package/dist/sandbox/connection-authority.d.ts +3 -0
- package/dist/sandbox/enrollment.d.ts +9 -1
- package/dist/sandbox/machines.d.ts +5 -2
- package/dist/sandbox/metrics-ingestion.d.ts +28 -20
- package/dist/sandbox/rematerialize.d.ts +2 -0
- package/dist/sandbox/session-attach-variable-sets.d.ts +30 -0
- package/dist/sandbox/viewer.d.ts +26 -1
- package/dist/sandbox-file-artifacts.d.ts +50 -0
- package/dist/scheduled-task-deletion.d.ts +13 -0
- package/dist/session-create-observability.d.ts +5 -0
- package/dist/session-event-view-MPS2OZD3.js +292 -0
- package/dist/session-event-view-MPS2OZD3.js.map +1 -0
- package/dist/site-session-origin.d.ts +13 -0
- package/dist/site-uploads.d.ts +41 -0
- package/dist/slack-reaction-files.d.ts +1 -1
- package/dist/temporal-schedule-cleanup.d.ts +1 -0
- package/dist/temporal-schedule-sync.d.ts +26 -0
- package/dist/transcription/providers/xai-subscription.d.ts +1 -0
- package/dist/transcription/service.d.ts +2 -1
- package/dist/work-discovery-observability.d.ts +33 -0
- package/dist/workspace-artifact-content.d.ts +28 -0
- package/dist/workspace-artifact-provenance.d.ts +7 -0
- package/dist/workspace-delete-observability.d.ts +10 -0
- package/dist/workspace-deletion.d.ts +6 -0
- package/dist/workspace-state-projection.d.ts +3 -5
- package/dist/workspace-tool-gateway-observability.d.ts +17 -0
- package/dist/workspace-tool-gateway.d.ts +123 -0
- package/dist/workspace-webhook-dispatch.d.ts +26 -0
- package/package.json +23 -19
- package/src/access-grant-rls.ts +75 -0
- package/src/admission-trace.ts +14 -0
- package/src/api-websocket.ts +23 -0
- package/src/app.ts +1974 -256
- package/src/artifact-catalog-cursor.ts +60 -0
- package/src/auth/managed-auth-attempt-context.ts +70 -0
- package/src/auth/managed-auth-rate-limits.ts +252 -0
- package/src/auth/managed-auth-session-adapter.ts +222 -0
- package/src/auth/managed-auth.ts +536 -60
- package/src/auth/managed-email.ts +174 -0
- 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 +307 -0
- package/src/auth/signup-funnel-metrics.ts +164 -0
- package/src/browser-controller-authority.ts +1 -186
- package/src/browser-ephemeral.ts +39 -0
- package/src/browser-state-authority.ts +1 -236
- package/src/claude-subscription-account-conflicts.ts +12 -0
- package/src/claude-subscription-account-usage.ts +48 -0
- package/src/claude-subscription-oauth.ts +362 -0
- package/src/claude-subscription-profile.ts +73 -0
- package/src/claude-subscription-usage.ts +97 -0
- package/src/claude-workspace-connection.ts +82 -0
- package/src/codemode.ts +194 -30
- package/src/codex-realtime.ts +8 -2
- package/src/company-brain-okf.ts +340 -0
- package/src/connected-machine-computer-access.ts +33 -0
- package/src/connection-ownership.ts +199 -0
- package/src/controller-data-plane.ts +81 -0
- package/src/editable-artifact-native-kernel.ts +26 -15
- package/src/editable-artifact-office-import.ts +77 -9
- package/src/editable-artifact-production.ts +23 -7
- package/src/editable-artifact-source-session.ts +123 -0
- package/src/editable-artifact-websocket.ts +27 -21
- package/src/editable-artifact-workspace-files.ts +31 -6
- package/src/fatal-process-boundary.ts +246 -0
- package/src/gateway-realtime.ts +1 -1
- package/src/github-access.ts +147 -5
- package/src/http/acting-person.ts +71 -0
- package/src/http/api-contract-compat.ts +49 -0
- package/src/http/api-error.ts +115 -1
- package/src/http/auth.ts +48 -0
- package/src/http/cors.ts +67 -0
- package/src/http/deprecation.ts +118 -0
- package/src/http/interaction-control-error.ts +164 -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/request-body.ts +127 -0
- package/src/http/request-source.ts +178 -0
- package/src/http/sse.ts +486 -132
- package/src/http/unmatched-route.ts +60 -0
- package/src/http/user-content.ts +158 -0
- package/src/http/websocket-upgrade-dispatch.ts +29 -0
- package/src/index.ts +157 -52
- package/src/integration-endpoint-test.ts +291 -0
- package/src/integrations/api-integrations.ts +98 -11
- package/src/integrations/atlassian.ts +474 -136
- package/src/integrations/connect-authority.ts +2 -0
- package/src/integrations/connect-callback-return.ts +86 -0
- package/src/integrations/fiken.ts +354 -64
- package/src/integrations/github-app-connect.ts +435 -0
- package/src/integrations/github-installation-proof.ts +60 -0
- package/src/integrations/github-lens-connect.ts +76 -0
- package/src/integrations/github-skill-source.ts +2 -142
- package/src/integrations/google-drive.ts +820 -222
- package/src/integrations/integration-commit-authority.ts +69 -0
- package/src/integrations/marketplace-plugin.ts +48 -0
- package/src/integrations/oauth-client.ts +1356 -459
- package/src/integrations/oauth-profiles.ts +537 -0
- package/src/integrations/oauth-return-path.ts +18 -0
- package/src/integrations/personal-github-repositories.ts +674 -0
- package/src/integrations/personal-github.ts +825 -0
- package/src/integrations/pr-review-provider.ts +246 -0
- package/src/integrations/provider-oauth.ts +250 -22
- package/src/integrations/slack-app-home.ts +300 -0
- package/src/integrations/slack-bot.ts +963 -239
- package/src/integrations/slack-file-upload-flow.ts +147 -0
- package/src/integrations/slack-install.ts +83 -0
- package/src/integrations/slack-interactions.ts +3170 -474
- package/src/integrations/slack-mrkdwn.ts +193 -0
- package/src/integrations/slack-routing.ts +337 -0
- package/src/integrations/slack-session-defaults.ts +224 -0
- package/src/integrations/slack-task-file-upload.ts +323 -0
- package/src/integrations/social-oauth.ts +213 -15
- package/src/interaction-agent-access.ts +48 -0
- package/src/interaction-frame-proxy.ts +409 -0
- package/src/interaction-metrics.ts +30 -0
- package/src/mcp/action-catalog-types.ts +9 -0
- package/src/mcp/action-catalog.gen.ts +6696 -0
- package/src/mcp/capability-account-readiness.ts +17 -0
- package/src/mcp/child-read-evidence.ts +19 -0
- package/src/mcp/command-read.ts +109 -0
- package/src/mcp/company-profile-agent-admin.ts +198 -0
- package/src/mcp/contract-input.ts +141 -0
- package/src/mcp/documents.ts +65 -377
- package/src/mcp/editable-artifacts.ts +12 -6
- package/src/mcp/files.ts +80 -38
- package/src/mcp/first-party-tool-permissions.ts +305 -0
- package/src/mcp/knowledge-entries.ts +322 -0
- package/src/mcp/orchestration-failure-diagnostic.ts +43 -0
- package/src/mcp/receipts.ts +34 -0
- package/src/mcp/request-abort.ts +44 -0
- package/src/mcp/scheduled-task-input.ts +76 -0
- package/src/mcp/scheduled-task-view.ts +44 -3
- package/src/mcp/server.ts +3030 -1267
- package/src/mcp/session-event-view.ts +383 -0
- package/src/mcp/session-view.ts +84 -3
- package/src/mcp/session-wait.ts +665 -0
- package/src/mcp-oauth.ts +1123 -0
- package/src/model-catalog.ts +91 -333
- package/src/observability.ts +15 -1
- package/src/organization-mcp.ts +397 -0
- package/src/organization-recovery-notifications.ts +103 -0
- package/src/routes/analytics-consent.ts +123 -0
- package/src/routes/api-integrations.ts +364 -74
- package/src/routes/api-keys.ts +336 -10
- package/src/routes/artifact-catalog.ts +279 -0
- package/src/routes/automations.ts +768 -0
- package/src/routes/billing.ts +461 -46
- package/src/routes/browser-sessions.ts +839 -131
- package/src/routes/canonical-human-identities.ts +90 -14
- package/src/routes/capabilities.ts +107 -6
- package/src/routes/channels.ts +20 -3
- package/src/routes/claude-subscription-accounts.ts +334 -0
- package/src/routes/claude-subscription-oauth.ts +142 -0
- package/src/routes/client-errors.ts +362 -0
- package/src/routes/codex.ts +833 -116
- package/src/routes/company-brain.ts +322 -0
- package/src/routes/company-profile.ts +65 -1
- package/src/routes/computer-sessions.ts +557 -106
- package/src/routes/connect.ts +1938 -0
- package/src/routes/connections.ts +1040 -349
- package/src/routes/documents.ts +488 -287
- package/src/routes/editable-artifacts.ts +79 -9
- package/src/routes/enrollments.ts +135 -39
- package/src/routes/environments.ts +154 -58
- package/src/routes/external-identity-links.ts +201 -0
- package/src/routes/feedback.ts +113 -0
- package/src/routes/files.ts +337 -39
- package/src/routes/github-browser-pages.ts +162 -0
- package/src/routes/github.ts +446 -144
- package/src/routes/insights-response-cache.ts +83 -0
- package/src/routes/insights-usage.ts +345 -0
- package/src/routes/insights.ts +153 -19
- package/src/routes/install.ts +133 -38
- package/src/routes/integration-facets.ts +107 -33
- package/src/routes/interaction-resources.ts +30 -3
- package/src/routes/knowledge.ts +488 -0
- package/src/routes/machines.ts +313 -15
- package/src/routes/managed-auth-session-sets.ts +1218 -0
- package/src/routes/managed-onboarding.ts +337 -0
- package/src/routes/managed-sign-in-methods.ts +433 -0
- package/src/routes/memory-slack-publications.ts +3 -2
- package/src/routes/model-connection-access.ts +208 -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 +178 -0
- package/src/routes/organization-memberships.ts +1019 -27
- package/src/routes/organization-model-providers.ts +293 -0
- package/src/routes/organization-recovery.ts +325 -0
- package/src/routes/organization-service-accounts.ts +128 -0
- package/src/routes/organization-sessions.ts +350 -0
- package/src/routes/personal-github-git-broker.ts +785 -0
- package/src/routes/personal-github.ts +370 -0
- package/src/routes/plugins.ts +251 -22
- package/src/routes/pr-review-github.ts +881 -0
- package/src/routes/pr-review.ts +622 -0
- package/src/routes/preference-registry.ts +12 -93
- package/src/routes/rigs.ts +167 -50
- package/src/routes/scheduled-tasks.ts +346 -41
- package/src/routes/session-artifact-associations.ts +87 -0
- package/src/routes/session-history-imports.ts +280 -0
- package/src/routes/sessions.ts +2306 -264
- package/src/routes/skill-content.ts +297 -0
- package/src/routes/skill-install-authority.ts +64 -0
- package/src/routes/skills.ts +62 -9
- package/src/routes/social.ts +46 -18
- package/src/routes/subscription-account-pools.ts +436 -0
- package/src/routes/subscription-pool-access.ts +126 -0
- package/src/routes/supergrok.ts +219 -303
- package/src/routes/transcription-recordings.ts +38 -3
- package/src/routes/transcriptions.ts +4 -0
- package/src/routes/usage-allowances.ts +378 -0
- package/src/routes/user-resource-authorities.ts +138 -0
- package/src/routes/video-generation.ts +14 -10
- package/src/routes/workspace-artifacts.ts +275 -69
- package/src/routes/workspace-capture.ts +21 -0
- package/src/routes/workspace-integrations.ts +576 -0
- package/src/routes/workspace-learning.ts +159 -0
- package/src/routes/workspace-model-providers.ts +148 -0
- package/src/routes/workspace-state.ts +38 -25
- package/src/routes/workspaces.ts +1004 -127
- package/src/sandbox/auth-callout.ts +96 -19
- package/src/sandbox/channel-a.ts +323 -24
- package/src/sandbox/connection-authority.ts +3 -0
- package/src/sandbox/enrollment.ts +81 -6
- package/src/sandbox/machines.ts +218 -76
- package/src/sandbox/metrics-ingestion.ts +411 -62
- package/src/sandbox/rematerialize.ts +121 -19
- package/src/sandbox/session-attach-variable-sets.ts +131 -0
- package/src/sandbox/viewer.ts +280 -25
- package/src/sandbox-file-artifacts.ts +413 -0
- package/src/scheduled-task-deletion.ts +138 -0
- package/src/session-create-observability.ts +46 -0
- package/src/session-realtime-context.ts +6 -3
- package/src/site-session-origin.ts +48 -0
- package/src/site-uploads.ts +224 -0
- package/src/slack-reaction-files.ts +16 -5
- package/src/temporal-schedule-cleanup.ts +13 -0
- package/src/temporal-schedule-sync.ts +74 -0
- package/src/transcription/providers/codex-subscription.ts +2 -0
- package/src/transcription/providers/openai.ts +1 -0
- package/src/transcription/providers/xai-subscription.ts +49 -3
- package/src/transcription/segmenter.ts +8 -2
- package/src/transcription/service.ts +43 -6
- package/src/work-discovery-observability.ts +121 -0
- package/src/workspace-artifact-content.ts +144 -0
- package/src/workspace-artifact-provenance.ts +104 -0
- package/src/workspace-delete-observability.ts +75 -0
- package/src/workspace-deletion.ts +73 -0
- package/src/workspace-state-projection.ts +18 -148
- package/src/workspace-tool-gateway-observability.ts +100 -0
- package/src/workspace-tool-gateway.ts +870 -0
- package/src/workspace-webhook-dispatch.ts +190 -0
- package/src/xai-subscription-auth.ts +9 -2
- package/dist/chunk-2PQMSRCB.js +0 -49759
- package/dist/chunk-2PQMSRCB.js.map +0 -1
- package/dist/routes/packs.d.ts +0 -3
- package/src/routes/packs.ts +0 -729
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Markdown to Slack mrkdwn for model-authored text OpenGeni posts to Slack.
|
|
3
|
+
*
|
|
4
|
+
* Agents write ordinary Markdown because the same reply renders as Markdown in
|
|
5
|
+
* the OpenGeni console. Slack message text does not render Markdown headings,
|
|
6
|
+
* `**bold**`, or `[label](url)` links, so a reply that reads well in the
|
|
7
|
+
* console arrives in Slack full of literal syntax. This rewrites only that
|
|
8
|
+
* syntax, only for the bytes handed to Slack: stored session events, model
|
|
9
|
+
* history, and every other surface keep the model's exact text. It is
|
|
10
|
+
* formatting at a third-party sink, not content classification or redaction.
|
|
11
|
+
*
|
|
12
|
+
* Code is never rewritten: fenced code blocks keep every line between their
|
|
13
|
+
* fences, and inline code spans are copied as they are.
|
|
14
|
+
*
|
|
15
|
+
* The output is part of the Slack post and update ledgers' request digest, so a
|
|
16
|
+
* change to it changes the bytes an in-flight operation is bound to. Delivery
|
|
17
|
+
* (`deliverSlackModelText` in `slack-interactions.ts`) falls back only to the
|
|
18
|
+
* unformatted text; a later output change must keep the previous rendering
|
|
19
|
+
* reachable the same way, or operations started before the deploy conflict on
|
|
20
|
+
* every retry.
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* A private provider citation handle: U+E200 `cite`, one or more U+E202
|
|
25
|
+
* separated references, then U+E201. Codex web search can emit these without
|
|
26
|
+
* the annotation table that would make them resolvable, so they carry nothing a
|
|
27
|
+
* Slack reader can use. The OpenGeni timeline hides the same handles
|
|
28
|
+
* (`stripOpaqueCitationTokens` in `packages/react/src/timeline/projection.ts`).
|
|
29
|
+
*/
|
|
30
|
+
const PROVIDER_CITATION_TOKEN =
|
|
31
|
+
/[ \t]*\u{E200}(?:cite|filecite)(?:\u{E202}[^\u{E200}-\u{E202}]*)*\u{E201}/gu;
|
|
32
|
+
/** Stray citation delimiters, for example from a token cut short upstream. */
|
|
33
|
+
const PROVIDER_CITATION_DELIMITER = /[\u{E200}-\u{E202}]/gu;
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* A fence at any indentation. Agents nest code under list items (a `10.` item
|
|
37
|
+
* or a nested bullet puts the fence four or more spaces in), and Slack has no
|
|
38
|
+
* indented code block, so a fence line is a fence wherever it starts.
|
|
39
|
+
*/
|
|
40
|
+
const FENCE_OPEN = /^[ \t]*(`{3,}|~{3,})(.*)$/u;
|
|
41
|
+
const ATX_HEADING = /^ {0,3}#{1,6}[ \t]+(.*?)(?:[ \t]+#+)?[ \t]*$/u;
|
|
42
|
+
const BULLET = /^([ \t]*)[-*+][ \t]+(?=\S)/u;
|
|
43
|
+
const THEMATIC_BREAK = /^ {0,3}([-*_])(?:[ \t]*\1){2,}[ \t]*$/u;
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Spans that must reach Slack exactly as written and are set aside before any
|
|
47
|
+
* link is read: inline code, and Slack's own `<...>` tokens (mentions, channel
|
|
48
|
+
* links, and links already in Slack form).
|
|
49
|
+
*/
|
|
50
|
+
const CODE_SPAN_OR_SLACK_TOKEN =
|
|
51
|
+
/(`+)(?!`)[^\n]*?(?<!`)\1(?!`)|<(?:[@#!]|https?:|mailto:)[^<>\n]*>/gu;
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* A Markdown link or image whose target is a web or mail address. Other
|
|
55
|
+
* targets, such as OpenGeni's own `artifact:` and `sandbox:` links, mean
|
|
56
|
+
* nothing in Slack and are left as written rather than turned into dead links.
|
|
57
|
+
* One level of balanced parentheses is allowed in the URL (as in Wikipedia
|
|
58
|
+
* links), and an optional link title is dropped.
|
|
59
|
+
*/
|
|
60
|
+
const MARKDOWN_LINK =
|
|
61
|
+
/!?\[((?:[^[\]\n]|\[[^[\]\n]*\])+)\]\(\s*((?:https?:\/\/|mailto:)(?:[^\s()<>|]|\([^\s()<>|]*\))+)(?:\s+(?:"[^"\n]*"|'[^'\n]*'|\([^()\n]*\)))?\s*\)/gu;
|
|
62
|
+
|
|
63
|
+
/** Bare URLs are set aside after links: they may legitimately contain `__` or `**`. */
|
|
64
|
+
const BARE_URL = /\bhttps?:\/\/[^\s<>]+/gu;
|
|
65
|
+
|
|
66
|
+
const BOLD_ITALIC_ASTERISK = /\*\*\*(?=[^\s*])((?:[^*\n]|\*(?!\*))+?)(?<=[^\s*])\*\*\*/gu;
|
|
67
|
+
const BOLD_ASTERISK = /\*\*(?=[^\s*])((?:[^*\n]|\*(?!\*))+?)(?<=[^\s*])\*\*/gu;
|
|
68
|
+
const BOLD_UNDERSCORE =
|
|
69
|
+
/(?<![\p{L}\p{N}_])__(?=[^\s_])((?:[^_\n]|_(?!_))+?)(?<=[^\s_])__(?![\p{L}\p{N}_])/gu;
|
|
70
|
+
const STRIKETHROUGH = /~~(?=[^\s~])((?:[^~\n]|~(?!~))+?)(?<=[^\s~])~~/gu;
|
|
71
|
+
const PLACEHOLDER = /\u0000(\d+)\u0000/gu;
|
|
72
|
+
|
|
73
|
+
/** Remove provider citation handles and any stray citation delimiters. */
|
|
74
|
+
export function stripProviderCitationMarkers(text: string): string {
|
|
75
|
+
return text.replace(PROVIDER_CITATION_TOKEN, "").replace(PROVIDER_CITATION_DELIMITER, "");
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Rewrite common Markdown as Slack mrkdwn.
|
|
80
|
+
*
|
|
81
|
+
* - `# Heading` (any level) becomes a bold line.
|
|
82
|
+
* - `**bold**` and `__bold__` become `*bold*`, `***both***` becomes `*_both_*`,
|
|
83
|
+
* and `~~struck~~` becomes `~struck~`.
|
|
84
|
+
* - `[label](https://...)` becomes `<https://...|label>`.
|
|
85
|
+
* - `-`, `*`, and `+` bullets become Slack's `•` bullet, keeping indentation;
|
|
86
|
+
* numbered lists and block quotes are already Slack syntax and stay as they are.
|
|
87
|
+
* - Fenced code blocks, at any indentation, keep their contents byte for byte.
|
|
88
|
+
* The opening fence loses its language tag, which Slack would otherwise print
|
|
89
|
+
* as the first code line, and an unclosed block is closed so Slack still
|
|
90
|
+
* renders it as code.
|
|
91
|
+
* - Provider citation handles are removed.
|
|
92
|
+
*
|
|
93
|
+
* Text that uses none of this syntax comes back unchanged.
|
|
94
|
+
*/
|
|
95
|
+
export function slackMrkdwnFromMarkdown(markdown: string): string {
|
|
96
|
+
const lines = stripProviderCitationMarkers(markdown).split("\n");
|
|
97
|
+
const output: string[] = [];
|
|
98
|
+
let fence: { character: string; length: number } | null = null;
|
|
99
|
+
for (const line of lines) {
|
|
100
|
+
if (fence) {
|
|
101
|
+
if (closesFence(line, fence)) {
|
|
102
|
+
output.push("```");
|
|
103
|
+
fence = null;
|
|
104
|
+
} else {
|
|
105
|
+
output.push(line);
|
|
106
|
+
}
|
|
107
|
+
continue;
|
|
108
|
+
}
|
|
109
|
+
const opening = FENCE_OPEN.exec(line);
|
|
110
|
+
// A backtick fence's info string cannot contain a backtick; such a line is
|
|
111
|
+
// inline code, not a fence.
|
|
112
|
+
if (opening && !(opening[1]!.startsWith("`") && opening[2]!.includes("`"))) {
|
|
113
|
+
fence = { character: opening[1]![0]!, length: opening[1]!.length };
|
|
114
|
+
output.push("```");
|
|
115
|
+
continue;
|
|
116
|
+
}
|
|
117
|
+
output.push(slackLine(line));
|
|
118
|
+
}
|
|
119
|
+
if (fence) output.push("```");
|
|
120
|
+
return output.join("\n");
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function closesFence(line: string, fence: { character: string; length: number }): boolean {
|
|
124
|
+
const trimmed = line.trim();
|
|
125
|
+
return (
|
|
126
|
+
trimmed.length >= fence.length &&
|
|
127
|
+
[...trimmed].every((character) => character === fence.character)
|
|
128
|
+
);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
function slackLine(line: string): string {
|
|
132
|
+
if (THEMATIC_BREAK.test(line)) return line;
|
|
133
|
+
const heading = ATX_HEADING.exec(line);
|
|
134
|
+
if (heading) {
|
|
135
|
+
const content = slackInline(heading[1]!, { heading: true }).trim();
|
|
136
|
+
return content ? `*${content}*` : line;
|
|
137
|
+
}
|
|
138
|
+
const bullet = BULLET.exec(line);
|
|
139
|
+
if (bullet) {
|
|
140
|
+
return `${bullet[1]}• ${slackInline(line.slice(bullet[0].length), { heading: false })}`;
|
|
141
|
+
}
|
|
142
|
+
return slackInline(line, { heading: false });
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
function slackInline(text: string, options: { heading: boolean }): string {
|
|
146
|
+
const kept: string[] = [];
|
|
147
|
+
const keep = (value: string) => {
|
|
148
|
+
kept.push(value);
|
|
149
|
+
return `\u0000${kept.length - 1}\u0000`;
|
|
150
|
+
};
|
|
151
|
+
// The placeholders are NUL-delimited, so a NUL in the model text itself
|
|
152
|
+
// could collide with one. Slack does not render NUL; drop it.
|
|
153
|
+
let working = text.replace(/\u0000/gu, "");
|
|
154
|
+
working = working.replace(CODE_SPAN_OR_SLACK_TOKEN, keep);
|
|
155
|
+
working = working.replace(MARKDOWN_LINK, (_match, label: string, url: string) =>
|
|
156
|
+
keep(slackLink(restore(label, kept), url)),
|
|
157
|
+
);
|
|
158
|
+
working = working.replace(BARE_URL, keep);
|
|
159
|
+
if (options.heading) {
|
|
160
|
+
// The whole heading becomes one bold span, so emphasis inside it would
|
|
161
|
+
// nest and break Slack's parser. Drop the emphasis markers instead.
|
|
162
|
+
working = working
|
|
163
|
+
.replace(BOLD_ASTERISK, "$1")
|
|
164
|
+
.replace(BOLD_UNDERSCORE, "$1")
|
|
165
|
+
.replace(/\*/gu, "");
|
|
166
|
+
} else {
|
|
167
|
+
working = working
|
|
168
|
+
.replace(BOLD_ITALIC_ASTERISK, "*_$1_*")
|
|
169
|
+
.replace(BOLD_ASTERISK, "*$1*")
|
|
170
|
+
.replace(BOLD_UNDERSCORE, "*$1*");
|
|
171
|
+
}
|
|
172
|
+
working = working.replace(STRIKETHROUGH, "~$1~");
|
|
173
|
+
return restore(working, kept);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
function restore(text: string, kept: readonly string[]): string {
|
|
177
|
+
return text.replace(PLACEHOLDER, (_match, index: string) => kept[Number(index)] ?? "");
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
function slackLink(label: string, url: string): string {
|
|
181
|
+
const text = label
|
|
182
|
+
.replace(BOLD_ASTERISK, "$1")
|
|
183
|
+
.replace(BOLD_UNDERSCORE, "$1")
|
|
184
|
+
.replace(/`/gu, "")
|
|
185
|
+
.trim();
|
|
186
|
+
if (!text || text === url) return `<${url}>`;
|
|
187
|
+
return `<${url}|${escapeSlackLinkLabel(text)}>`;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/** Slack reads `&`, `<`, and `>` as control characters inside a link. */
|
|
191
|
+
function escapeSlackLinkLabel(label: string): string {
|
|
192
|
+
return label.replace(/&/gu, "&").replace(/</gu, "<").replace(/>/gu, ">");
|
|
193
|
+
}
|
|
@@ -0,0 +1,337 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Per-channel and per-DM Slack workspace routing.
|
|
3
|
+
*
|
|
4
|
+
* Routing is decided BEFORE any agent runs, from durable facts only, so it can
|
|
5
|
+
* never be a model judgement. The resolver is pure with respect to the database
|
|
6
|
+
* reads it is handed: it returns a decision, and the caller persists and acts on
|
|
7
|
+
* it.
|
|
8
|
+
*
|
|
9
|
+
* Two rules are load-bearing and easy to break by accident:
|
|
10
|
+
*
|
|
11
|
+
* A mapped thread keeps the workspace it was created in, unconditionally,
|
|
12
|
+
* including after the channel has been re-pointed. Otherwise a live
|
|
13
|
+
* conversation would silently change tenants mid-thread.
|
|
14
|
+
*
|
|
15
|
+
* A subject who lacks access to the routed workspace is NEVER quietly served
|
|
16
|
+
* from another workspace they happen to belong to. Every failure is explicit
|
|
17
|
+
* and creates no session.
|
|
18
|
+
*/
|
|
19
|
+
import type { AccessGrant } from "@opengeni/contracts";
|
|
20
|
+
import type {
|
|
21
|
+
SlackChannelRoute,
|
|
22
|
+
SlackInteractionInboxEntry,
|
|
23
|
+
SlackRoutableWorkspace,
|
|
24
|
+
SlackUserDmRoute,
|
|
25
|
+
} from "@opengeni/db";
|
|
26
|
+
|
|
27
|
+
export type SlackRouteTenancy = { accountId: string; workspaceId: string };
|
|
28
|
+
|
|
29
|
+
export type SlackRouteResolution =
|
|
30
|
+
| (SlackRouteTenancy & {
|
|
31
|
+
kind: "resolved";
|
|
32
|
+
label: string | null;
|
|
33
|
+
source:
|
|
34
|
+
| "thread"
|
|
35
|
+
| "prefix"
|
|
36
|
+
| "channel"
|
|
37
|
+
| "dm_route"
|
|
38
|
+
| "dm_personal"
|
|
39
|
+
| "sole_candidate"
|
|
40
|
+
| "installation";
|
|
41
|
+
})
|
|
42
|
+
| { kind: "ask"; candidates: readonly SlackRoutableWorkspace[] }
|
|
43
|
+
| {
|
|
44
|
+
kind: "denied";
|
|
45
|
+
reason: "no_access_to_named" | "no_access_to_route" | "no_candidates";
|
|
46
|
+
requested: string | null;
|
|
47
|
+
candidates: readonly SlackRoutableWorkspace[];
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* A Slack direct message to the bot, by any trigger that can carry one.
|
|
52
|
+
*
|
|
53
|
+
* `isDirectMessageShortcut` is deliberately narrower: it means "a message
|
|
54
|
+
* shortcut invoked inside a DM", which is a private-handoff concern. Routing
|
|
55
|
+
* cares about the broader question of whether this conversation is one human's
|
|
56
|
+
* private channel with the bot, which is also true of an ordinary `dm` event.
|
|
57
|
+
*/
|
|
58
|
+
export function isSlackDirectMessageConversation(
|
|
59
|
+
entry: Pick<SlackInteractionInboxEntry, "triggerKind" | "slackChannelId">,
|
|
60
|
+
): boolean {
|
|
61
|
+
return entry.triggerKind === "dm" || entry.slackChannelId.startsWith("D");
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const PREFIX = "in ";
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Trigger kinds whose text the invoking human wrote themselves. A message
|
|
68
|
+
* shortcut and a reaction both act on another person's message.
|
|
69
|
+
*/
|
|
70
|
+
const AUTHORED_BY_INVOKER: ReadonlySet<string> = new Set([
|
|
71
|
+
"app_mention",
|
|
72
|
+
"dm",
|
|
73
|
+
"slash_command",
|
|
74
|
+
"thread_reply",
|
|
75
|
+
]);
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Split a leading bot mention off the message text.
|
|
79
|
+
*
|
|
80
|
+
* Slack delivers an `app_mention` with the mention still in the text
|
|
81
|
+
* (`<@U123> do the thing`), and OpenGeni stores it verbatim. The workspace
|
|
82
|
+
* prefix is only an override when it is the first thing the person typed, so it
|
|
83
|
+
* is parsed after the mention rather than at byte 0 of the raw text. Everything
|
|
84
|
+
* split off here is put back, so the message the model sees is unchanged apart
|
|
85
|
+
* from the addressing the person used to route it.
|
|
86
|
+
*/
|
|
87
|
+
export function splitSlackLeadingMention(
|
|
88
|
+
text: string,
|
|
89
|
+
botUserId: string | null,
|
|
90
|
+
): { lead: string; rest: string } {
|
|
91
|
+
if (!botUserId || !/^[UWB][A-Z0-9]{1,63}$/u.test(botUserId)) return { lead: "", rest: text };
|
|
92
|
+
const mention = `<@${botUserId}>`;
|
|
93
|
+
if (!text.startsWith(mention)) return { lead: "", rest: text };
|
|
94
|
+
const remainder = text.slice(mention.length);
|
|
95
|
+
const trimmed = remainder.replace(/^[ \t]+/u, "");
|
|
96
|
+
return { lead: text.slice(0, text.length - trimmed.length), rest: trimmed };
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* The strict `in <workspace>: ...` override.
|
|
101
|
+
*
|
|
102
|
+
* Parsed only at byte 0, so ordinary prose that happens to contain the word
|
|
103
|
+
* cannot trigger it, and matched case-insensitively against the exact label of a
|
|
104
|
+
* workspace the subject can already start work in. A prefix that names nothing
|
|
105
|
+
* recognizable is NOT a suggestion that falls through: it is a refusal, because
|
|
106
|
+
* silently ignoring an explicit override is how a message lands somewhere the
|
|
107
|
+
* person did not intend.
|
|
108
|
+
*/
|
|
109
|
+
export function parseSlackWorkspacePrefix(text: string): {
|
|
110
|
+
requested: string;
|
|
111
|
+
remainder: string;
|
|
112
|
+
} | null {
|
|
113
|
+
if (!text.toLowerCase().startsWith(PREFIX)) return null;
|
|
114
|
+
const separator = text.indexOf(":");
|
|
115
|
+
if (separator <= PREFIX.length) return null;
|
|
116
|
+
const requested = text.slice(PREFIX.length, separator).trim();
|
|
117
|
+
if (requested.length === 0) return null;
|
|
118
|
+
// A bare address with nothing after it is not a request.
|
|
119
|
+
if (text.slice(separator + 1).trim().length === 0) return null;
|
|
120
|
+
// A label is one line. A colon further down a multi-line message is not a
|
|
121
|
+
// prefix, it is punctuation.
|
|
122
|
+
if (/[\r\n]/u.test(requested)) return null;
|
|
123
|
+
return { requested, remainder: text.slice(separator + 1).replace(/^[ \t]+/u, "") };
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function matchCandidate(
|
|
127
|
+
candidates: readonly SlackRoutableWorkspace[],
|
|
128
|
+
requested: string,
|
|
129
|
+
): SlackRoutableWorkspace | null | "ambiguous" {
|
|
130
|
+
const wanted = requested.toLowerCase();
|
|
131
|
+
const matches = candidates.filter((candidate) => candidate.label.toLowerCase() === wanted);
|
|
132
|
+
// Two workspaces whose labels differ only by case are not a tie to break
|
|
133
|
+
// silently: guessing one is the same failure as ignoring the override.
|
|
134
|
+
if (matches.length > 1) return "ambiguous";
|
|
135
|
+
return matches[0] ?? null;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
export type SlackRouteInputs = {
|
|
139
|
+
/** The installation binding's tenancy. Always the fallback, never a silent one. */
|
|
140
|
+
home: SlackRouteTenancy;
|
|
141
|
+
entry: Pick<
|
|
142
|
+
SlackInteractionInboxEntry,
|
|
143
|
+
"triggerKind" | "slackChannelId" | "slackUserId" | "text"
|
|
144
|
+
>;
|
|
145
|
+
/**
|
|
146
|
+
* The tenancy of an interaction already mapped to this thread, if any. A
|
|
147
|
+
* mapped thread wins unconditionally.
|
|
148
|
+
*/
|
|
149
|
+
threadTenancy: SlackRouteTenancy | null;
|
|
150
|
+
channelRoute: SlackChannelRoute | null;
|
|
151
|
+
dmRoute: SlackUserDmRoute | null;
|
|
152
|
+
/** The subject's own personal workspace in the home organization, if any. */
|
|
153
|
+
personalWorkspaceId: string | null;
|
|
154
|
+
/** Workspaces this subject may actually start work in, ordered stably. */
|
|
155
|
+
candidates: readonly SlackRoutableWorkspace[];
|
|
156
|
+
/** The installation's bot user, so a mention does not hide the prefix. */
|
|
157
|
+
botUserId: string | null;
|
|
158
|
+
/** `OPENGENI_SLACK_WORKSPACE_ROUTING_ENABLED`. */
|
|
159
|
+
routingEnabled: boolean;
|
|
160
|
+
/** Whether the first-use picker exists yet. Until it does, ambiguity keeps home. */
|
|
161
|
+
askEnabled: boolean;
|
|
162
|
+
};
|
|
163
|
+
|
|
164
|
+
function labelFor(
|
|
165
|
+
candidates: readonly SlackRoutableWorkspace[],
|
|
166
|
+
workspaceId: string,
|
|
167
|
+
): string | null {
|
|
168
|
+
return candidates.find((candidate) => candidate.workspaceId === workspaceId)?.label ?? null;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* Decide which workspace this Slack message starts work in.
|
|
173
|
+
*
|
|
174
|
+
* First match wins, and the order is deliberate: continuity beats an explicit
|
|
175
|
+
* override beats configuration beats derivation beats asking.
|
|
176
|
+
*/
|
|
177
|
+
export function resolveSlackWorkspaceRoute(input: SlackRouteInputs): SlackRouteResolution {
|
|
178
|
+
// A personal workspace is only ever a destination for that person's own bot
|
|
179
|
+
// DM. Offering it in a channel would be wrong twice over: routing a shared
|
|
180
|
+
// conversation into one member's private space hides it from everyone else
|
|
181
|
+
// in the channel, and - because managed tenancy provisions a personal
|
|
182
|
+
// workspace for every member - counting it as a candidate means nobody ever
|
|
183
|
+
// has exactly one. That defeats the sole-candidate rule below, so an
|
|
184
|
+
// organization with a single shared workspace would be asked to choose in
|
|
185
|
+
// every channel despite having no choice to make.
|
|
186
|
+
const directMessage = isSlackDirectMessageConversation(input.entry);
|
|
187
|
+
const candidates = directMessage
|
|
188
|
+
? input.candidates
|
|
189
|
+
: input.candidates.filter((candidate) => !candidate.personal);
|
|
190
|
+
|
|
191
|
+
const installation = {
|
|
192
|
+
kind: "resolved" as const,
|
|
193
|
+
accountId: input.home.accountId,
|
|
194
|
+
workspaceId: input.home.workspaceId,
|
|
195
|
+
label: labelFor(candidates, input.home.workspaceId),
|
|
196
|
+
source: "installation" as const,
|
|
197
|
+
};
|
|
198
|
+
|
|
199
|
+
// 0. With routing off no routing read is consulted, so an existing install is
|
|
200
|
+
// byte-identical. A thread that a previous flag-on window already mapped is
|
|
201
|
+
// still honoured, because its interaction, session and events genuinely
|
|
202
|
+
// live there: turning the flag off must stop new routing, not strand a
|
|
203
|
+
// conversation by addressing it in a workspace it is not in.
|
|
204
|
+
if (!input.routingEnabled) {
|
|
205
|
+
return input.threadTenancy
|
|
206
|
+
? {
|
|
207
|
+
kind: "resolved",
|
|
208
|
+
accountId: input.threadTenancy.accountId,
|
|
209
|
+
workspaceId: input.threadTenancy.workspaceId,
|
|
210
|
+
label: null,
|
|
211
|
+
source: "thread",
|
|
212
|
+
}
|
|
213
|
+
: installation;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
// 1. A mapped thread keeps its workspace, even if the channel moved since.
|
|
217
|
+
if (input.threadTenancy) {
|
|
218
|
+
return {
|
|
219
|
+
kind: "resolved",
|
|
220
|
+
accountId: input.threadTenancy.accountId,
|
|
221
|
+
workspaceId: input.threadTenancy.workspaceId,
|
|
222
|
+
label: labelFor(candidates, input.threadTenancy.workspaceId),
|
|
223
|
+
source: "thread",
|
|
224
|
+
};
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
// 2. The strict prefix override. It applies to this message only and never
|
|
228
|
+
// writes a route row: an override is not a decision about the channel.
|
|
229
|
+
//
|
|
230
|
+
// Only text the invoking human actually typed can address anything. A
|
|
231
|
+
// message shortcut and a reaction both carry SOMEONE ELSE'S message, so a
|
|
232
|
+
// prefix found there was never an instruction to OpenGeni.
|
|
233
|
+
const prefix = AUTHORED_BY_INVOKER.has(input.entry.triggerKind)
|
|
234
|
+
? parseSlackWorkspacePrefix(splitSlackLeadingMention(input.entry.text, input.botUserId).rest)
|
|
235
|
+
: null;
|
|
236
|
+
if (prefix) {
|
|
237
|
+
const named = matchCandidate(candidates, prefix.requested);
|
|
238
|
+
if (named === "ambiguous" || !named) {
|
|
239
|
+
return {
|
|
240
|
+
kind: "denied",
|
|
241
|
+
reason: "no_access_to_named",
|
|
242
|
+
requested: prefix.requested,
|
|
243
|
+
candidates: candidates,
|
|
244
|
+
};
|
|
245
|
+
}
|
|
246
|
+
return {
|
|
247
|
+
kind: "resolved",
|
|
248
|
+
accountId: named.accountId,
|
|
249
|
+
workspaceId: named.workspaceId,
|
|
250
|
+
label: named.label,
|
|
251
|
+
source: "prefix",
|
|
252
|
+
};
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
// 3. The channel's remembered answer.
|
|
256
|
+
if (input.channelRoute) {
|
|
257
|
+
return {
|
|
258
|
+
kind: "resolved",
|
|
259
|
+
accountId: input.channelRoute.targetAccountId,
|
|
260
|
+
workspaceId: input.channelRoute.targetWorkspaceId,
|
|
261
|
+
label: labelFor(candidates, input.channelRoute.targetWorkspaceId),
|
|
262
|
+
source: "channel",
|
|
263
|
+
};
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
// 4. A direct message is this human's own conversation with the bot, so it
|
|
267
|
+
// lands in their own workspace unless they chose otherwise. The personal
|
|
268
|
+
// workspace id is DERIVED from an active organization membership pointer;
|
|
269
|
+
// it is never accepted from a Slack payload or a route row.
|
|
270
|
+
if (directMessage) {
|
|
271
|
+
if (input.dmRoute) {
|
|
272
|
+
return {
|
|
273
|
+
kind: "resolved",
|
|
274
|
+
accountId: input.dmRoute.targetAccountId,
|
|
275
|
+
workspaceId: input.dmRoute.targetWorkspaceId,
|
|
276
|
+
label: labelFor(candidates, input.dmRoute.targetWorkspaceId),
|
|
277
|
+
source: "dm_route",
|
|
278
|
+
};
|
|
279
|
+
}
|
|
280
|
+
if (input.personalWorkspaceId) {
|
|
281
|
+
return {
|
|
282
|
+
kind: "resolved",
|
|
283
|
+
accountId: input.home.accountId,
|
|
284
|
+
workspaceId: input.personalWorkspaceId,
|
|
285
|
+
label: labelFor(candidates, input.personalWorkspaceId),
|
|
286
|
+
source: "dm_personal",
|
|
287
|
+
};
|
|
288
|
+
}
|
|
289
|
+
// No workspace of their own is not the same as no workspace at all. Fall
|
|
290
|
+
// through to the ordinary rules so a member of exactly one shared workspace
|
|
291
|
+
// can still work in their bot DM.
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
// 5. One workspace is not a choice. This is what keeps the flag quiet for
|
|
295
|
+
// installs that only ever had one workspace.
|
|
296
|
+
const sole = candidates[0];
|
|
297
|
+
if (candidates.length === 1 && sole) {
|
|
298
|
+
return {
|
|
299
|
+
kind: "resolved",
|
|
300
|
+
accountId: sole.accountId,
|
|
301
|
+
workspaceId: sole.workspaceId,
|
|
302
|
+
label: sole.label,
|
|
303
|
+
source: "sole_candidate",
|
|
304
|
+
};
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
if (candidates.length === 0) {
|
|
308
|
+
return { kind: "denied", reason: "no_candidates", requested: null, candidates: [] };
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
// 6. Genuinely ambiguous. Until the picker exists, keep the installation's
|
|
312
|
+
// workspace rather than inventing an answer.
|
|
313
|
+
return input.askEnabled ? { kind: "ask", candidates: candidates } : installation;
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
/**
|
|
317
|
+
* The message text the model actually sees.
|
|
318
|
+
*
|
|
319
|
+
* A prefix override is addressing information, not part of the request, so it is
|
|
320
|
+
* stripped exactly once and only when it matched.
|
|
321
|
+
*/
|
|
322
|
+
export function slackRoutedRequestText(
|
|
323
|
+
text: string,
|
|
324
|
+
resolution: SlackRouteResolution,
|
|
325
|
+
botUserId: string | null,
|
|
326
|
+
): string {
|
|
327
|
+
if (resolution.kind !== "resolved" || resolution.source !== "prefix") return text;
|
|
328
|
+
const { lead, rest } = splitSlackLeadingMention(text, botUserId);
|
|
329
|
+
// Reached only for a `prefix` resolution, which the authorship rule above
|
|
330
|
+
// already restricted to text the invoking human wrote.
|
|
331
|
+
const parsed = parseSlackWorkspacePrefix(rest);
|
|
332
|
+
return parsed ? `${lead}${parsed.remainder}` : text;
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
export type SlackRouteAuthorization =
|
|
336
|
+
| { kind: "authorized"; grant: AccessGrant; tenancy: SlackRouteTenancy; label: string | null }
|
|
337
|
+
| { kind: "denied"; reason: "no_access_to_route"; tenancy: SlackRouteTenancy };
|