@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
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
// apps/api/src/sandbox/metrics-ingestion.ts — the M10 metrics INGESTION consumer
|
|
2
2
|
// + the connect-Hello DISPLAY-REFRESH consumer. The
|
|
3
3
|
// enrolled agent piggybacks a `MetricsSample` on its ~5s heartbeat (an
|
|
4
|
-
// `AgentEvent` published one-way on
|
|
5
|
-
// `Hello` (its live self-description) on
|
|
4
|
+
// `AgentEvent` published one-way on the exact process events subject) and publishes a
|
|
5
|
+
// `Hello` (its live self-description) on the exact process hello subject on every connect
|
|
6
6
|
// /reconnect. This module owns the two agent→control-plane inbound consumers:
|
|
7
7
|
//
|
|
8
|
-
// `agent.*.*.events` (heartbeat) →
|
|
8
|
+
// `agent.*.*.connection.*.events` (heartbeat) →
|
|
9
9
|
// 1. touchEnrollmentLastSeen — the liveness cursor (online/reconnecting/offline
|
|
10
10
|
// derivation + the M3 probe disambiguation).
|
|
11
11
|
// 2. ingestMachineMetricsSample — UPSERT machine_metrics_latest (the "now" row)
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
// A GOING-OFFLINE event is not a metrics point — liveness flips via the lease/
|
|
14
14
|
// probe path; we skip it here (no-op).
|
|
15
15
|
//
|
|
16
|
-
// `agent.*.*.hello` (connect) →
|
|
16
|
+
// `agent.*.*.connection.*.hello` (connect) →
|
|
17
17
|
// refreshEnrollmentDisplay — reconcile `enrollments.has_display` to the LIVE
|
|
18
18
|
// capability the Hello reports. `has_display` was previously FROZEN at the
|
|
19
19
|
// enroll-time offer snapshot; a machine that GAINS a display later (a Mac that
|
|
@@ -22,25 +22,32 @@
|
|
|
22
22
|
// `has_display` track reality (both directions), which the desktop-capability
|
|
23
23
|
// gate (packages/runtime capabilities.ts) keys off.
|
|
24
24
|
// refreshEnrollmentOpStream — reconcile `enrollments.op_stream` to the LIVE
|
|
25
|
-
// runner capability the Hello reports
|
|
26
|
-
//
|
|
25
|
+
// runner capability the Hello reports. Exec is unavailable unless the runner
|
|
26
|
+
// advertises the streaming engine.
|
|
27
27
|
//
|
|
28
28
|
// Both consumers are BEST-EFFORT and fail-soft: a decode/DB error for one message
|
|
29
29
|
// is logged + swallowed (the bus subscription already swallows handler throws) so
|
|
30
30
|
// a metrics blip / a display-refresh write failure never tears down the consumer,
|
|
31
|
-
// back-pressures the agent, or breaks its connect.
|
|
31
|
+
// back-pressures the agent, or breaks its connect. Event ingestion drains the NATS
|
|
32
|
+
// subscription immediately into exact-subject queues: different runner connections
|
|
33
|
+
// progress concurrently, one runner's ordering is preserved, and consecutive queued
|
|
34
|
+
// heartbeats collapse latest-wins. A delayed heartbeat backlog therefore cannot keep
|
|
35
|
+
// renewing a dead runner's short connection lease one stale message at a time.
|
|
32
36
|
|
|
33
37
|
import {
|
|
34
38
|
clearEnrollmentWentOffline,
|
|
35
39
|
disconnectAttachedBrowserDevices,
|
|
40
|
+
advanceEnrollmentAgentUpdate,
|
|
36
41
|
getEnrollment,
|
|
42
|
+
getLiveEnrollmentConnection,
|
|
37
43
|
ingestMachineMetricsSample,
|
|
38
44
|
reconcileAttachedBrowserInventory,
|
|
45
|
+
releaseEnrollmentConnection,
|
|
46
|
+
renewEnrollmentConnection,
|
|
39
47
|
sessionsWithActiveOpOnEnrollment,
|
|
40
48
|
setEnrollmentDisplayState,
|
|
49
|
+
setEnrollmentAgentRuntime,
|
|
41
50
|
setEnrollmentOpStreamState,
|
|
42
|
-
setEnrollmentWentOffline,
|
|
43
|
-
touchEnrollmentLastSeen,
|
|
44
51
|
type AppendEventInput,
|
|
45
52
|
type Database,
|
|
46
53
|
type MachineMetricsSample,
|
|
@@ -51,8 +58,10 @@ import {
|
|
|
51
58
|
} from "@opengeni/contracts";
|
|
52
59
|
import { appendAndPublishEvents, type EventBus } from "@opengeni/events";
|
|
53
60
|
import type { Observability } from "@opengeni/observability";
|
|
61
|
+
import { normalizeConnectedMachineWorkspaceRoot } from "@opengeni/runtime/sandbox";
|
|
54
62
|
import {
|
|
55
63
|
AgentEvent,
|
|
64
|
+
AgentUpdateStage,
|
|
56
65
|
Arch,
|
|
57
66
|
GoingOfflineReason,
|
|
58
67
|
Hello,
|
|
@@ -61,40 +70,186 @@ import {
|
|
|
61
70
|
type AttachedBrowserInventorySnapshot as WireAttachedBrowserInventorySnapshot,
|
|
62
71
|
type MetricsSample,
|
|
63
72
|
} from "@opengeni/agent-proto";
|
|
73
|
+
import { AGENT_CONNECTION_LEASE_MS } from "./connection-authority";
|
|
64
74
|
|
|
65
75
|
/** The wildcard subject the agent event plane publishes heartbeats on. */
|
|
66
|
-
export const AGENT_EVENTS_SUBJECT = "agent.*.*.events";
|
|
76
|
+
export const AGENT_EVENTS_SUBJECT = "agent.*.*.connection.*.events";
|
|
67
77
|
|
|
68
78
|
/** The wildcard subject the agent publishes its connect Hello on. */
|
|
69
|
-
export const AGENT_HELLO_SUBJECT = "agent.*.*.hello";
|
|
79
|
+
export const AGENT_HELLO_SUBJECT = "agent.*.*.connection.*.hello";
|
|
80
|
+
|
|
81
|
+
type QueuedAgentEvent = {
|
|
82
|
+
payload: Uint8Array;
|
|
83
|
+
subject: string;
|
|
84
|
+
heartbeat: boolean;
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
type AgentEventQueueState = {
|
|
88
|
+
scheduled: boolean;
|
|
89
|
+
pending: QueuedAgentEvent[];
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
type ScheduledAgentEventQueue = {
|
|
93
|
+
subject: string;
|
|
94
|
+
state: AgentEventQueueState;
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
export type AgentEventIngestionScheduler = {
|
|
98
|
+
enqueue: (payload: Uint8Array, subject: string) => void;
|
|
99
|
+
whenIdle: () => Promise<void>;
|
|
100
|
+
close: () => void;
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
function payloadIsHeartbeat(payload: Uint8Array): boolean {
|
|
104
|
+
try {
|
|
105
|
+
return AgentEvent.decode(payload).event?.$case === "heartbeat";
|
|
106
|
+
} catch {
|
|
107
|
+
// Preserve the normal decoder/logging path for malformed events. They are
|
|
108
|
+
// barriers rather than heartbeat-coalescing candidates.
|
|
109
|
+
return false;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
70
112
|
|
|
71
113
|
/**
|
|
72
|
-
*
|
|
114
|
+
* Drain agent events without making every Connected Machine wait behind one
|
|
115
|
+
* deployment-wide serial DB queue. Each exact process subject retains FIFO order;
|
|
116
|
+
* separate subjects drain concurrently. Consecutive pending heartbeats for one
|
|
117
|
+
* subject are latest-wins because they are point-in-time liveness/metrics samples,
|
|
118
|
+
* while GoingOffline and update-progress events remain ordered barriers.
|
|
119
|
+
*/
|
|
120
|
+
export function createAgentEventIngestionScheduler(
|
|
121
|
+
handler: (payload: Uint8Array, subject: string) => void | Promise<void>,
|
|
122
|
+
options: { maxConcurrentSubjects?: number; onHeartbeatCoalesced?: () => void } = {},
|
|
123
|
+
): AgentEventIngestionScheduler {
|
|
124
|
+
const maxConcurrentSubjects = options.maxConcurrentSubjects ?? 32;
|
|
125
|
+
if (!Number.isSafeInteger(maxConcurrentSubjects) || maxConcurrentSubjects <= 0) {
|
|
126
|
+
throw new RangeError("maxConcurrentSubjects must be a positive safe integer");
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
const states = new Map<string, AgentEventQueueState>();
|
|
130
|
+
const readySubjects: ScheduledAgentEventQueue[] = [];
|
|
131
|
+
const idleWaiters = new Set<() => void>();
|
|
132
|
+
let accepting = true;
|
|
133
|
+
let activeSubjects = 0;
|
|
134
|
+
|
|
135
|
+
const resolveIdleWaiters = (): void => {
|
|
136
|
+
if (states.size !== 0) return;
|
|
137
|
+
for (const resolve of idleWaiters) resolve();
|
|
138
|
+
idleWaiters.clear();
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
async function drain(subject: string, state: AgentEventQueueState): Promise<void> {
|
|
142
|
+
while (true) {
|
|
143
|
+
const event = state.pending.shift();
|
|
144
|
+
if (!event) break;
|
|
145
|
+
try {
|
|
146
|
+
await handler(event.payload, event.subject);
|
|
147
|
+
} catch {
|
|
148
|
+
// Agent-event ingestion is best-effort. One bad event must not strand the
|
|
149
|
+
// exact-subject queue or prevent later heartbeats from restoring liveness.
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
activeSubjects -= 1;
|
|
154
|
+
state.scheduled = false;
|
|
155
|
+
if (state.pending.length > 0) {
|
|
156
|
+
schedule(subject, state);
|
|
157
|
+
} else {
|
|
158
|
+
if (states.get(subject) === state) states.delete(subject);
|
|
159
|
+
resolveIdleWaiters();
|
|
160
|
+
}
|
|
161
|
+
pump();
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
function pump(): void {
|
|
165
|
+
while (activeSubjects < maxConcurrentSubjects) {
|
|
166
|
+
const next = readySubjects.shift();
|
|
167
|
+
if (!next) return;
|
|
168
|
+
if (states.get(next.subject) !== next.state || !next.state.scheduled) continue;
|
|
169
|
+
activeSubjects += 1;
|
|
170
|
+
void drain(next.subject, next.state);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
function schedule(subject: string, state: AgentEventQueueState): void {
|
|
175
|
+
if (state.scheduled) return;
|
|
176
|
+
state.scheduled = true;
|
|
177
|
+
readySubjects.push({ subject, state });
|
|
178
|
+
pump();
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
return {
|
|
182
|
+
enqueue(payload, subject) {
|
|
183
|
+
if (!accepting) return;
|
|
184
|
+
const heartbeat = payloadIsHeartbeat(payload);
|
|
185
|
+
let state = states.get(subject);
|
|
186
|
+
if (!state) {
|
|
187
|
+
state = { scheduled: false, pending: [] };
|
|
188
|
+
states.set(subject, state);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
const queued = { payload, subject, heartbeat };
|
|
192
|
+
const lastIndex = state.pending.length - 1;
|
|
193
|
+
if (heartbeat && lastIndex >= 0 && state.pending[lastIndex]!.heartbeat) {
|
|
194
|
+
state.pending[lastIndex] = queued;
|
|
195
|
+
options.onHeartbeatCoalesced?.();
|
|
196
|
+
} else {
|
|
197
|
+
state.pending.push(queued);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
schedule(subject, state);
|
|
201
|
+
},
|
|
202
|
+
async whenIdle() {
|
|
203
|
+
if (states.size === 0) return;
|
|
204
|
+
await new Promise<void>((resolve) => idleWaiters.add(resolve));
|
|
205
|
+
},
|
|
206
|
+
close() {
|
|
207
|
+
accepting = false;
|
|
208
|
+
for (const state of states.values()) {
|
|
209
|
+
state.pending.length = 0;
|
|
210
|
+
}
|
|
211
|
+
resolveIdleWaiters();
|
|
212
|
+
},
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
/**
|
|
217
|
+
* Parse `agent.<ws>.<id>.connection.<instance>.<tail>` into its exact authority,
|
|
73
218
|
* expected tail token. Returns null for a subject that does not match the shape
|
|
74
219
|
* (defensive — the subscription pattern already constrains it).
|
|
75
220
|
*/
|
|
76
221
|
function parseAgentSubject(
|
|
77
222
|
subject: string,
|
|
78
223
|
tail: "events" | "hello",
|
|
79
|
-
): { workspaceId: string; agentId: string } | null {
|
|
224
|
+
): { workspaceId: string; agentId: string; connectionInstanceId: string } | null {
|
|
80
225
|
const parts = subject.split(".");
|
|
81
|
-
if (
|
|
226
|
+
if (
|
|
227
|
+
parts.length !== 6 ||
|
|
228
|
+
parts[0] !== "agent" ||
|
|
229
|
+
parts[3] !== "connection" ||
|
|
230
|
+
!parts[4] ||
|
|
231
|
+
parts[5] !== tail
|
|
232
|
+
) {
|
|
82
233
|
return null;
|
|
83
234
|
}
|
|
84
|
-
return {
|
|
235
|
+
return {
|
|
236
|
+
workspaceId: parts[1]!,
|
|
237
|
+
agentId: parts[2]!,
|
|
238
|
+
connectionInstanceId: parts[4]!,
|
|
239
|
+
};
|
|
85
240
|
}
|
|
86
241
|
|
|
87
|
-
/** Parse
|
|
242
|
+
/** Parse the exact process events subject (heartbeat plane). */
|
|
88
243
|
export function parseAgentEventSubject(
|
|
89
244
|
subject: string,
|
|
90
|
-
): { workspaceId: string; agentId: string } | null {
|
|
245
|
+
): { workspaceId: string; agentId: string; connectionInstanceId: string } | null {
|
|
91
246
|
return parseAgentSubject(subject, "events");
|
|
92
247
|
}
|
|
93
248
|
|
|
94
|
-
/** Parse
|
|
249
|
+
/** Parse the exact process hello subject (connect plane). */
|
|
95
250
|
export function parseAgentHelloSubject(
|
|
96
251
|
subject: string,
|
|
97
|
-
): { workspaceId: string; agentId: string } | null {
|
|
252
|
+
): { workspaceId: string; agentId: string; connectionInstanceId: string } | null {
|
|
98
253
|
return parseAgentSubject(subject, "hello");
|
|
99
254
|
}
|
|
100
255
|
|
|
@@ -218,30 +373,23 @@ async function ingestAttachedBrowserInventory(
|
|
|
218
373
|
}
|
|
219
374
|
|
|
220
375
|
/**
|
|
221
|
-
* Ingest
|
|
222
|
-
*
|
|
223
|
-
*
|
|
224
|
-
*
|
|
376
|
+
* Ingest the OPTIONAL metrics sample carried by an already-authorized heartbeat.
|
|
377
|
+
* Connection renewal is deliberately owned by the outer event handler and runs
|
|
378
|
+
* before optional telemetry/inventory work, so telemetry absence or failure can
|
|
379
|
+
* never make a healthy runner lose authority.
|
|
225
380
|
*/
|
|
226
|
-
|
|
381
|
+
async function ingestHeartbeatMetrics(
|
|
227
382
|
db: Database,
|
|
228
|
-
input: {
|
|
383
|
+
input: {
|
|
384
|
+
accountId: string;
|
|
385
|
+
workspaceId: string;
|
|
386
|
+
agentId: string;
|
|
387
|
+
sample: MetricsSample;
|
|
388
|
+
},
|
|
229
389
|
): Promise<{ ingested: boolean; seriesAppended: boolean }> {
|
|
230
|
-
// The enrollment row is the source of the accountId (the RLS principal) and the
|
|
231
|
-
// existence check. A revoked machine still reports its accountId, so we ingest
|
|
232
|
-
// (the dashboard shows its last sample); a truly unknown id is a no-op.
|
|
233
|
-
const enrollment = await getEnrollment(db, input.workspaceId, input.agentId);
|
|
234
|
-
if (!enrollment) {
|
|
235
|
-
return { ingested: false, seriesAppended: false };
|
|
236
|
-
}
|
|
237
390
|
const sample = wireSampleToDbSample(input.sample);
|
|
238
|
-
await touchEnrollmentLastSeen(db, {
|
|
239
|
-
accountId: enrollment.accountId,
|
|
240
|
-
workspaceId: input.workspaceId,
|
|
241
|
-
enrollmentId: input.agentId,
|
|
242
|
-
});
|
|
243
391
|
const result = await ingestMachineMetricsSample(db, {
|
|
244
|
-
accountId:
|
|
392
|
+
accountId: input.accountId,
|
|
245
393
|
workspaceId: input.workspaceId,
|
|
246
394
|
enrollmentId: input.agentId,
|
|
247
395
|
sample,
|
|
@@ -340,12 +488,16 @@ export async function handleAgentEventPayload(
|
|
|
340
488
|
try {
|
|
341
489
|
const enrollment = await getEnrollment(db, ids.workspaceId, ids.agentId);
|
|
342
490
|
if (enrollment) {
|
|
343
|
-
await
|
|
491
|
+
const released = await releaseEnrollmentConnection(db, {
|
|
344
492
|
accountId: enrollment.accountId,
|
|
345
493
|
workspaceId: ids.workspaceId,
|
|
346
494
|
enrollmentId: ids.agentId,
|
|
495
|
+
connectionInstanceId: ids.connectionInstanceId,
|
|
347
496
|
reason,
|
|
348
497
|
});
|
|
498
|
+
// A late goodbye from a superseded process is not the current machine
|
|
499
|
+
// going offline. It must not disconnect browsers or fan out link loss.
|
|
500
|
+
if (!released.released) return;
|
|
349
501
|
try {
|
|
350
502
|
await disconnectAttachedBrowserDevices(db, {
|
|
351
503
|
accountId: enrollment.accountId,
|
|
@@ -396,10 +548,77 @@ export async function handleAgentEventPayload(
|
|
|
396
548
|
}
|
|
397
549
|
return;
|
|
398
550
|
}
|
|
551
|
+
if (event.event?.$case === "agentUpdateProgress") {
|
|
552
|
+
const enrollment = await getEnrollment(db, ids.workspaceId, ids.agentId).catch(() => null);
|
|
553
|
+
if (!enrollment || enrollment.connectionInstanceId !== ids.connectionInstanceId) return;
|
|
554
|
+
const progress = event.event.agentUpdateProgress;
|
|
555
|
+
const status = (() => {
|
|
556
|
+
switch (progress.stage) {
|
|
557
|
+
case AgentUpdateStage.AGENT_UPDATE_STAGE_ACCEPTED:
|
|
558
|
+
return "accepted" as const;
|
|
559
|
+
case AgentUpdateStage.AGENT_UPDATE_STAGE_WAITING_FOR_IDLE:
|
|
560
|
+
return "waiting_for_idle" as const;
|
|
561
|
+
case AgentUpdateStage.AGENT_UPDATE_STAGE_DOWNLOADING:
|
|
562
|
+
return "downloading" as const;
|
|
563
|
+
case AgentUpdateStage.AGENT_UPDATE_STAGE_VERIFYING:
|
|
564
|
+
return "verifying" as const;
|
|
565
|
+
case AgentUpdateStage.AGENT_UPDATE_STAGE_APPLYING:
|
|
566
|
+
return "applying" as const;
|
|
567
|
+
case AgentUpdateStage.AGENT_UPDATE_STAGE_RESTARTING:
|
|
568
|
+
return "restarting" as const;
|
|
569
|
+
case AgentUpdateStage.AGENT_UPDATE_STAGE_FAILED:
|
|
570
|
+
return "failed" as const;
|
|
571
|
+
default:
|
|
572
|
+
return null;
|
|
573
|
+
}
|
|
574
|
+
})();
|
|
575
|
+
if (!status) return;
|
|
576
|
+
try {
|
|
577
|
+
await advanceEnrollmentAgentUpdate(db, {
|
|
578
|
+
accountId: enrollment.accountId,
|
|
579
|
+
workspaceId: ids.workspaceId,
|
|
580
|
+
enrollmentId: ids.agentId,
|
|
581
|
+
connectionInstanceId: ids.connectionInstanceId,
|
|
582
|
+
connectionGeneration: enrollment.connectionGeneration,
|
|
583
|
+
operationId: progress.operationId,
|
|
584
|
+
status,
|
|
585
|
+
expectedBinarySha256: /^[0-9a-f]{64}$/.test(progress.expectedBinarySha256)
|
|
586
|
+
? progress.expectedBinarySha256
|
|
587
|
+
: null,
|
|
588
|
+
errorCode: progress.errorCode || null,
|
|
589
|
+
retryable: progress.retryable,
|
|
590
|
+
rolledBack: progress.rolledBack,
|
|
591
|
+
});
|
|
592
|
+
} catch (error) {
|
|
593
|
+
observability?.warn?.("Failed to ingest a machine self-update progress event", {
|
|
594
|
+
subject,
|
|
595
|
+
error: error instanceof Error ? error.message : String(error),
|
|
596
|
+
});
|
|
597
|
+
}
|
|
598
|
+
return;
|
|
599
|
+
}
|
|
399
600
|
if (event.event?.$case !== "heartbeat") {
|
|
400
601
|
return; // an unknown event kind → not a metrics point.
|
|
401
602
|
}
|
|
402
603
|
const heartbeat = event.event.heartbeat;
|
|
604
|
+
const enrollment = await getEnrollment(db, ids.workspaceId, ids.agentId).catch(() => null);
|
|
605
|
+
if (!enrollment || enrollment.connectionInstanceId !== ids.connectionInstanceId) return;
|
|
606
|
+
try {
|
|
607
|
+
const renewed = await renewEnrollmentConnection(db, {
|
|
608
|
+
accountId: enrollment.accountId,
|
|
609
|
+
workspaceId: ids.workspaceId,
|
|
610
|
+
enrollmentId: ids.agentId,
|
|
611
|
+
connectionInstanceId: ids.connectionInstanceId,
|
|
612
|
+
leaseMs: AGENT_CONNECTION_LEASE_MS,
|
|
613
|
+
});
|
|
614
|
+
if (!renewed.renewed) return;
|
|
615
|
+
} catch (error) {
|
|
616
|
+
observability?.warn?.("Failed to renew a machine connection heartbeat", {
|
|
617
|
+
subject,
|
|
618
|
+
error: error instanceof Error ? error.message : String(error),
|
|
619
|
+
});
|
|
620
|
+
return;
|
|
621
|
+
}
|
|
403
622
|
if (heartbeat.attachedBrowserInventory) {
|
|
404
623
|
try {
|
|
405
624
|
await ingestAttachedBrowserInventory(db, {
|
|
@@ -414,10 +633,39 @@ export async function handleAgentEventPayload(
|
|
|
414
633
|
});
|
|
415
634
|
}
|
|
416
635
|
}
|
|
636
|
+
if (heartbeat.desktopStatus) {
|
|
637
|
+
try {
|
|
638
|
+
const status = heartbeat.desktopStatus;
|
|
639
|
+
const available = status.available && !status.unavailableReason;
|
|
640
|
+
const reason = status.unavailableReason || null;
|
|
641
|
+
// The heartbeat already read the exact enrollment. Steady state adds no
|
|
642
|
+
// extra query; the change path re-reads and fences its eventual write.
|
|
643
|
+
if (
|
|
644
|
+
enrollment.hasDisplay !== available ||
|
|
645
|
+
(enrollment.desktopUnavailableReason ?? null) !== reason ||
|
|
646
|
+
enrollment.agentCapabilities?.desktop !== available
|
|
647
|
+
) {
|
|
648
|
+
await refreshEnrollmentDisplay(db, {
|
|
649
|
+
workspaceId: ids.workspaceId,
|
|
650
|
+
agentId: ids.agentId,
|
|
651
|
+
hasDisplay: available,
|
|
652
|
+
desktopUnavailableReason: reason,
|
|
653
|
+
runtimeDesktop: available,
|
|
654
|
+
connectionInstanceId: ids.connectionInstanceId,
|
|
655
|
+
});
|
|
656
|
+
}
|
|
657
|
+
} catch (error) {
|
|
658
|
+
observability?.warn?.("Failed to refresh a machine desktop heartbeat", {
|
|
659
|
+
subject,
|
|
660
|
+
error: error instanceof Error ? error.message : String(error),
|
|
661
|
+
});
|
|
662
|
+
}
|
|
663
|
+
}
|
|
417
664
|
const metrics = heartbeat.metrics;
|
|
418
665
|
if (!metrics) return;
|
|
419
666
|
try {
|
|
420
|
-
await
|
|
667
|
+
await ingestHeartbeatMetrics(db, {
|
|
668
|
+
accountId: enrollment.accountId,
|
|
421
669
|
workspaceId: ids.workspaceId,
|
|
422
670
|
agentId: ids.agentId,
|
|
423
671
|
sample: metrics,
|
|
@@ -431,7 +679,7 @@ export async function handleAgentEventPayload(
|
|
|
431
679
|
}
|
|
432
680
|
|
|
433
681
|
/**
|
|
434
|
-
* Start the metrics-ingestion consumer: subscribe
|
|
682
|
+
* Start the metrics-ingestion consumer: subscribe exact process events and ingest
|
|
435
683
|
* every heartbeat. Gated by sandboxSelfhostedEnabled (the caller checks the flag;
|
|
436
684
|
* a disabled deployment never starts the consumer). Returns the unsubscribe fn.
|
|
437
685
|
*/
|
|
@@ -440,9 +688,24 @@ export function startMetricsIngestion(deps: {
|
|
|
440
688
|
bus: EventBus;
|
|
441
689
|
observability?: Observability;
|
|
442
690
|
}): () => void {
|
|
443
|
-
|
|
444
|
-
|
|
691
|
+
const scheduler = createAgentEventIngestionScheduler(
|
|
692
|
+
(payload, subject) =>
|
|
693
|
+
handleAgentEventPayload(deps.db, deps.observability, payload, subject, deps.bus),
|
|
694
|
+
{
|
|
695
|
+
onHeartbeatCoalesced: () =>
|
|
696
|
+
deps.observability?.incrementCounter({
|
|
697
|
+
name: "opengeni_machine_heartbeat_coalesced_total",
|
|
698
|
+
help: "Connected Machine heartbeats collapsed while an exact runner event queue was busy.",
|
|
699
|
+
}),
|
|
700
|
+
},
|
|
445
701
|
);
|
|
702
|
+
const unsubscribe = deps.bus.subscribeAgentEvents(AGENT_EVENTS_SUBJECT, (payload, subject) => {
|
|
703
|
+
scheduler.enqueue(payload, subject);
|
|
704
|
+
});
|
|
705
|
+
return () => {
|
|
706
|
+
unsubscribe();
|
|
707
|
+
scheduler.close();
|
|
708
|
+
};
|
|
446
709
|
}
|
|
447
710
|
|
|
448
711
|
// ── Connect-Hello display refresh ─────────────────────────────────────────────
|
|
@@ -490,6 +753,47 @@ export function helloReportsOpStream(hello: Hello): boolean {
|
|
|
490
753
|
return hello.capabilities?.opStream === true;
|
|
491
754
|
}
|
|
492
755
|
|
|
756
|
+
export function helloRuntimeCapabilities(hello: Hello): Record<string, boolean> {
|
|
757
|
+
const caps = hello.capabilities;
|
|
758
|
+
// Absence is an older-agent/unknown signal, not a fabricated set of false claims.
|
|
759
|
+
// Keep the durable cursor empty so reconnects from legacy agents remain a
|
|
760
|
+
// no-op and future capability additions do not get silently fabricated.
|
|
761
|
+
if (!caps) return {};
|
|
762
|
+
return {
|
|
763
|
+
exec: caps.exec === true,
|
|
764
|
+
filesystem: caps.filesystem === true,
|
|
765
|
+
git: caps.git === true,
|
|
766
|
+
pty: caps.pty === true,
|
|
767
|
+
desktop: caps.desktop === true,
|
|
768
|
+
opStream: caps.opStream === true,
|
|
769
|
+
browserBridge: caps.browserBridge === true,
|
|
770
|
+
operationResourcePolicy: caps.operationResourcePolicy === true,
|
|
771
|
+
operationCpuQuota: caps.operationCpuQuota === true,
|
|
772
|
+
transactionalFsWrite: caps.transactionalFsWrite === true,
|
|
773
|
+
};
|
|
774
|
+
}
|
|
775
|
+
|
|
776
|
+
function helloCompletedUpdate(hello: Hello): {
|
|
777
|
+
operationId: string;
|
|
778
|
+
targetVersion: string;
|
|
779
|
+
binarySha256: string;
|
|
780
|
+
} | null {
|
|
781
|
+
if (
|
|
782
|
+
!/^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(
|
|
783
|
+
hello.completedUpdateOperationId,
|
|
784
|
+
) ||
|
|
785
|
+
!/^(?:v)?\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?$/.test(hello.completedUpdateTargetVersion) ||
|
|
786
|
+
!/^[0-9a-f]{64}$/.test(hello.completedUpdateBinarySha256)
|
|
787
|
+
) {
|
|
788
|
+
return null;
|
|
789
|
+
}
|
|
790
|
+
return {
|
|
791
|
+
operationId: hello.completedUpdateOperationId,
|
|
792
|
+
targetVersion: hello.completedUpdateTargetVersion,
|
|
793
|
+
binarySha256: hello.completedUpdateBinarySha256,
|
|
794
|
+
};
|
|
795
|
+
}
|
|
796
|
+
|
|
493
797
|
/**
|
|
494
798
|
* Reconcile `enrollments.has_display` (+ the capture-blocked reason) to what a Hello
|
|
495
799
|
* reports. Resolves the enrollment (the accountId is the RLS principal + the
|
|
@@ -505,6 +809,8 @@ export async function refreshEnrollmentDisplay(
|
|
|
505
809
|
agentId: string;
|
|
506
810
|
hasDisplay: boolean;
|
|
507
811
|
desktopUnavailableReason?: string | null;
|
|
812
|
+
runtimeDesktop?: boolean;
|
|
813
|
+
connectionInstanceId?: string;
|
|
508
814
|
},
|
|
509
815
|
): Promise<{ updated: boolean }> {
|
|
510
816
|
const desktopUnavailableReason = input.desktopUnavailableReason ?? null;
|
|
@@ -514,7 +820,9 @@ export async function refreshEnrollmentDisplay(
|
|
|
514
820
|
}
|
|
515
821
|
if (
|
|
516
822
|
enrollment.hasDisplay === input.hasDisplay &&
|
|
517
|
-
(enrollment.desktopUnavailableReason ?? null) === desktopUnavailableReason
|
|
823
|
+
(enrollment.desktopUnavailableReason ?? null) === desktopUnavailableReason &&
|
|
824
|
+
(input.runtimeDesktop === undefined ||
|
|
825
|
+
enrollment.agentCapabilities?.desktop === input.runtimeDesktop)
|
|
518
826
|
) {
|
|
519
827
|
// Both fields unchanged — do not even issue the UPDATE (no churn on a
|
|
520
828
|
// steady-state Hello).
|
|
@@ -526,6 +834,8 @@ export async function refreshEnrollmentDisplay(
|
|
|
526
834
|
enrollmentId: input.agentId,
|
|
527
835
|
hasDisplay: input.hasDisplay,
|
|
528
836
|
desktopUnavailableReason,
|
|
837
|
+
...(input.runtimeDesktop !== undefined ? { runtimeDesktop: input.runtimeDesktop } : {}),
|
|
838
|
+
...(input.connectionInstanceId ? { connectionInstanceId: input.connectionInstanceId } : {}),
|
|
529
839
|
});
|
|
530
840
|
}
|
|
531
841
|
|
|
@@ -541,6 +851,7 @@ export async function refreshEnrollmentOpStream(
|
|
|
541
851
|
workspaceId: string;
|
|
542
852
|
agentId: string;
|
|
543
853
|
opStream: boolean;
|
|
854
|
+
connectionInstanceId?: string;
|
|
544
855
|
},
|
|
545
856
|
): Promise<{ updated: boolean }> {
|
|
546
857
|
const enrollment = await getEnrollment(db, input.workspaceId, input.agentId);
|
|
@@ -557,6 +868,7 @@ export async function refreshEnrollmentOpStream(
|
|
|
557
868
|
workspaceId: input.workspaceId,
|
|
558
869
|
enrollmentId: input.agentId,
|
|
559
870
|
opStream: input.opStream,
|
|
871
|
+
...(input.connectionInstanceId ? { connectionInstanceId: input.connectionInstanceId } : {}),
|
|
560
872
|
});
|
|
561
873
|
}
|
|
562
874
|
|
|
@@ -589,17 +901,56 @@ export async function handleHelloPayload(
|
|
|
589
901
|
});
|
|
590
902
|
return;
|
|
591
903
|
}
|
|
904
|
+
const authority = await getLiveEnrollmentConnection(db, ids.workspaceId, ids.agentId).catch(
|
|
905
|
+
() => null,
|
|
906
|
+
);
|
|
907
|
+
if (!authority || authority.connectionInstanceId !== ids.connectionInstanceId) {
|
|
908
|
+
observability?.warn?.("Ignored a Hello from a superseded runner instance", {
|
|
909
|
+
workspaceId: ids.workspaceId,
|
|
910
|
+
agentId: ids.agentId,
|
|
911
|
+
});
|
|
912
|
+
return;
|
|
913
|
+
}
|
|
914
|
+
let workspaceRoot: string | undefined;
|
|
915
|
+
if (hello.workspaceRoot.length > 0) {
|
|
916
|
+
try {
|
|
917
|
+
workspaceRoot = normalizeConnectedMachineWorkspaceRoot(hello.workspaceRoot, authority.os);
|
|
918
|
+
} catch (error) {
|
|
919
|
+
observability?.warn?.("Ignored an invalid Connected Machine workspace root", {
|
|
920
|
+
workspaceId: ids.workspaceId,
|
|
921
|
+
agentId: ids.agentId,
|
|
922
|
+
error: error instanceof Error ? error.message : String(error),
|
|
923
|
+
});
|
|
924
|
+
}
|
|
925
|
+
}
|
|
592
926
|
try {
|
|
593
927
|
await refreshEnrollmentDisplay(db, {
|
|
594
928
|
workspaceId: ids.workspaceId,
|
|
595
929
|
agentId: ids.agentId,
|
|
596
930
|
hasDisplay: helloReportsDisplay(hello),
|
|
597
931
|
desktopUnavailableReason: helloDesktopUnavailableReason(hello),
|
|
932
|
+
connectionInstanceId: ids.connectionInstanceId,
|
|
598
933
|
});
|
|
599
934
|
await refreshEnrollmentOpStream(db, {
|
|
600
935
|
workspaceId: ids.workspaceId,
|
|
601
936
|
agentId: ids.agentId,
|
|
602
937
|
opStream: helloReportsOpStream(hello),
|
|
938
|
+
connectionInstanceId: ids.connectionInstanceId,
|
|
939
|
+
});
|
|
940
|
+
await setEnrollmentAgentRuntime(db, {
|
|
941
|
+
accountId: authority.accountId,
|
|
942
|
+
workspaceId: ids.workspaceId,
|
|
943
|
+
enrollmentId: ids.agentId,
|
|
944
|
+
connectionInstanceId: ids.connectionInstanceId,
|
|
945
|
+
agentVersion: hello.agentVersion.trim() || null,
|
|
946
|
+
binarySha256: /^[0-9a-f]{64}$/.test(hello.binarySha256) ? hello.binarySha256 : null,
|
|
947
|
+
updateChannel:
|
|
948
|
+
hello.updateChannel === "stable" || hello.updateChannel === "beta"
|
|
949
|
+
? hello.updateChannel
|
|
950
|
+
: null,
|
|
951
|
+
capabilities: helloRuntimeCapabilities(hello),
|
|
952
|
+
...(workspaceRoot !== undefined ? { workspaceRoot } : {}),
|
|
953
|
+
completedUpdate: helloCompletedUpdate(hello),
|
|
603
954
|
});
|
|
604
955
|
} catch (error) {
|
|
605
956
|
observability?.warn?.("Failed to refresh an enrollment's capabilities from a Hello", {
|
|
@@ -616,23 +967,21 @@ export async function handleHelloPayload(
|
|
|
616
967
|
// sessions with an active op on it — a restored only ever pairs a prior lost, so
|
|
617
968
|
// a routine connect Hello (no marker) emits nothing.
|
|
618
969
|
try {
|
|
619
|
-
const
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
);
|
|
635
|
-
}
|
|
970
|
+
const { cleared } = await clearEnrollmentWentOffline(db, {
|
|
971
|
+
accountId: authority.accountId,
|
|
972
|
+
workspaceId: ids.workspaceId,
|
|
973
|
+
enrollmentId: ids.agentId,
|
|
974
|
+
connectionInstanceId: ids.connectionInstanceId,
|
|
975
|
+
});
|
|
976
|
+
if (cleared && bus) {
|
|
977
|
+
await fanOutMachineLinkEvents(
|
|
978
|
+
db,
|
|
979
|
+
bus,
|
|
980
|
+
observability,
|
|
981
|
+
ids.workspaceId,
|
|
982
|
+
ids.agentId,
|
|
983
|
+
(activeTurnId) => [{ type: "machine.link.restored", turnId: activeTurnId, payload: {} }],
|
|
984
|
+
);
|
|
636
985
|
}
|
|
637
986
|
} catch (error) {
|
|
638
987
|
observability?.warn?.("Failed to clear a machine going-offline marker on a Hello", {
|
|
@@ -643,7 +992,7 @@ export async function handleHelloPayload(
|
|
|
643
992
|
}
|
|
644
993
|
|
|
645
994
|
/**
|
|
646
|
-
* Start the Hello display-refresh consumer: subscribe
|
|
995
|
+
* Start the Hello display-refresh consumer: subscribe exact process hellos and
|
|
647
996
|
* reconcile `has_display` to the live capability the agent reports on every
|
|
648
997
|
* connect. Gated by sandboxSelfhostedEnabled (the caller checks the flag). Returns
|
|
649
998
|
* the unsubscribe fn.
|