@opengeni/api-router 2.11.3 → 2.12.1-canary.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (64) hide show
  1. package/dist/app.js +1 -1
  2. package/dist/{chunk-5X3XM6CE.js → chunk-Y3EPAIEQ.js} +17643 -14398
  3. package/dist/chunk-Y3EPAIEQ.js.map +1 -0
  4. package/dist/connection-ownership.d.ts +5 -1
  5. package/dist/index.js +3 -1
  6. package/dist/index.js.map +1 -1
  7. package/dist/integrations/atlassian.d.ts +4 -0
  8. package/dist/integrations/connect-authority.d.ts +1 -0
  9. package/dist/integrations/connect-callback-return.d.ts +6 -0
  10. package/dist/integrations/fiken.d.ts +7 -0
  11. package/dist/integrations/github-app-connect.d.ts +47 -0
  12. package/dist/integrations/github-installation-proof.d.ts +3 -0
  13. package/dist/integrations/github-lens-connect.d.ts +153 -0
  14. package/dist/integrations/google-drive.d.ts +4 -0
  15. package/dist/integrations/oauth-client.d.ts +4 -0
  16. package/dist/integrations/oauth-return-path.d.ts +1 -0
  17. package/dist/integrations/personal-github.d.ts +2 -0
  18. package/dist/integrations/provider-oauth.d.ts +11 -1
  19. package/dist/integrations/slack-install.d.ts +20 -0
  20. package/dist/integrations/social-oauth.d.ts +2 -0
  21. package/dist/mcp/scheduled-task-view.d.ts +3 -3
  22. package/dist/routes/api-integrations.d.ts +14 -0
  23. package/dist/routes/connect.d.ts +5 -0
  24. package/dist/routes/external-identity-links.d.ts +6 -0
  25. package/dist/routes/host-mcp-bindings.d.ts +5 -0
  26. package/dist/workspace-tool-gateway.d.ts +1 -0
  27. package/package.json +19 -19
  28. package/src/app.ts +22 -2
  29. package/src/connection-ownership.ts +14 -1
  30. package/src/index.ts +2 -0
  31. package/src/integrations/atlassian.ts +143 -39
  32. package/src/integrations/connect-authority.ts +2 -0
  33. package/src/integrations/connect-callback-return.ts +86 -0
  34. package/src/integrations/fiken.ts +222 -40
  35. package/src/integrations/github-app-connect.ts +389 -0
  36. package/src/integrations/github-installation-proof.ts +60 -0
  37. package/src/integrations/github-lens-connect.ts +97 -0
  38. package/src/integrations/google-drive.ts +152 -47
  39. package/src/integrations/oauth-client.ts +180 -84
  40. package/src/integrations/oauth-return-path.ts +18 -0
  41. package/src/integrations/personal-github.ts +146 -52
  42. package/src/integrations/provider-oauth.ts +132 -21
  43. package/src/integrations/slack-install.ts +83 -0
  44. package/src/integrations/social-oauth.ts +142 -1
  45. package/src/mcp/documents.ts +1 -1
  46. package/src/mcp/server.ts +4 -4
  47. package/src/routes/api-integrations.ts +74 -64
  48. package/src/routes/connect.ts +1869 -0
  49. package/src/routes/connections.ts +263 -221
  50. package/src/routes/documents.ts +2 -2
  51. package/src/routes/external-identity-links.ts +200 -0
  52. package/src/routes/github.ts +39 -64
  53. package/src/routes/host-mcp-bindings.ts +122 -0
  54. package/src/routes/organization-memberships.ts +23 -0
  55. package/src/routes/organization-sessions.ts +2 -2
  56. package/src/routes/personal-github.ts +8 -1
  57. package/src/routes/pr-review-github.ts +30 -3
  58. package/src/routes/scheduled-tasks.ts +29 -4
  59. package/src/routes/sessions.ts +61 -32
  60. package/src/routes/social.ts +5 -1
  61. package/src/routes/supergrok.ts +90 -3
  62. package/src/routes/workspaces.ts +16 -0
  63. package/src/workspace-tool-gateway.ts +136 -24
  64. package/dist/chunk-5X3XM6CE.js.map +0 -1
@@ -1,3 +1,4 @@
1
+ import { ExternalActorContinuation } from "@opengeni/contracts/external-identities";
1
2
  import type { AccessGrant, ScheduledTask } from "@opengeni/contracts";
2
3
  import { AtlassianOAuthStartResponse, type AtlassianDisconnectRequest, type AtlassianLifecycleActionRequest, type AtlassianOAuthStartRequest } from "@opengeni/contracts/atlassian";
3
4
  import { type ApiRouteDeps } from "@opengeni/core";
@@ -9,6 +10,8 @@ export declare function startAtlassianOAuth(deps: ApiRouteDeps, input: {
9
10
  subjectId: string;
10
11
  requestUrl: string;
11
12
  payload: AtlassianOAuthStartRequest;
13
+ connectAttemptId?: string;
14
+ externalContinuation?: ExternalActorContinuation;
12
15
  }): Promise<AtlassianOAuthStartResponse>;
13
16
  export declare function completeAtlassianOAuthCallback(deps: ApiRouteDeps, input: {
14
17
  code?: string | undefined;
@@ -17,6 +20,7 @@ export declare function completeAtlassianOAuthCallback(deps: ApiRouteDeps, input
17
20
  requestUrl: string;
18
21
  }): Promise<{
19
22
  redirectTo: string;
23
+ exactReturn?: boolean;
20
24
  }>;
21
25
  export declare function browseAtlassianSources(deps: ApiRouteDeps, input: {
22
26
  workspaceId: string;
@@ -0,0 +1 @@
1
+ export { requireConnectOwnerAuthority } from "@opengeni/core";
@@ -0,0 +1,6 @@
1
+ import type { Hono } from "hono";
2
+ import type { ApiRouteDeps } from "@opengeni/core";
3
+ /** Navigation-only recovery. Recently expired signed state can identify the host's saved
4
+ * destination, but cannot authorize exchange, persistence, or credential use.
5
+ * Normal callbacks independently enforce current time and live authority. */
6
+ export declare function registerConnectCallbackReturns(app: Hono, deps: ApiRouteDeps): void;
@@ -1,9 +1,13 @@
1
1
  import type { ApiRouteDeps } from "@opengeni/core";
2
2
  import type { Settings } from "@opengeni/config";
3
+ import { ExternalActorContinuation } from "@opengeni/contracts/external-identities";
3
4
  import { FikenOAuthStartResponse, type AccessGrant, type ConnectionMetadata, type FikenCompanySummary, type FikenConnectionMetadata, type FikenOAuthStartRequest } from "@opengeni/contracts";
4
5
  import { type Database } from "@opengeni/db";
5
6
  import { type FetchLike } from "@opengeni/network";
6
7
  import { HTTPException } from "hono/http-exception";
8
+ /** Shared native/embedded token adapter. Provider verification happens before
9
+ * persistence; the caller reauthorizes inside its durable commit transaction. */
10
+ export declare function prepareFikenTokenInstall(deps: ApiRouteDeps, grant: Pick<AccessGrant, "accountId" | "workspaceId" | "subjectId">, raw: unknown, expectedVersion?: number): Promise<(tx: Database) => Promise<ConnectionMetadata>>;
7
11
  export type FikenOperation = "companies.list" | "contacts.list" | "contact.create" | "products.list" | "invoices.list" | "invoice.get" | "invoice_draft.create" | "bank_accounts.list" | "purchases.list" | "sales.list";
8
12
  export declare class FikenProviderError extends Error {
9
13
  readonly code: string;
@@ -226,6 +230,8 @@ export declare function startFikenOAuth(deps: ApiRouteDeps, input: {
226
230
  subjectId: string;
227
231
  requestUrl: string;
228
232
  payload: FikenOAuthStartRequest;
233
+ externalContinuation?: ExternalActorContinuation;
234
+ connectAttemptId?: string;
229
235
  }): Promise<FikenOAuthStartResponse>;
230
236
  export declare function completeFikenOAuthCallback(deps: ApiRouteDeps, input: {
231
237
  code?: string | undefined;
@@ -234,5 +240,6 @@ export declare function completeFikenOAuthCallback(deps: ApiRouteDeps, input: {
234
240
  requestUrl: string;
235
241
  }): Promise<{
236
242
  redirectTo: string;
243
+ exactReturn?: boolean;
237
244
  }>;
238
245
  export {};
@@ -0,0 +1,47 @@
1
+ import { z } from "zod";
2
+ import type { ConnectAdvance, ConnectAttempt } from "@opengeni/contracts/connect";
3
+ import { type ConnectActorScope } from "@opengeni/db";
4
+ import type { ApiRouteDeps, PreparedConnectOperation } from "@opengeni/core";
5
+ declare const stateSchema: z.ZodObject<{
6
+ kind: z.ZodLiteral<"github_app_connect">;
7
+ accountId: z.ZodString;
8
+ workspaceId: z.ZodString;
9
+ subjectId: z.ZodString;
10
+ personalOwnerVerified: z.ZodBoolean;
11
+ connectAttemptId: z.ZodString;
12
+ phase: z.ZodEnum<{
13
+ bind: "bind";
14
+ discover: "discover";
15
+ install: "install";
16
+ }>;
17
+ installationId: z.ZodOptional<z.ZodNumber>;
18
+ providerId: z.ZodDefault<z.ZodEnum<{
19
+ "github-app": "github-app";
20
+ "github-lens": "github-lens";
21
+ }>>;
22
+ nonce: z.ZodString;
23
+ iat: z.ZodNumber;
24
+ }, z.core.$strip>;
25
+ type State = z.infer<typeof stateSchema>;
26
+ export declare function isGitHubAppConnectState(deps: ApiRouteDeps, raw: string | undefined): boolean;
27
+ type GitHubConnectScope = ConnectActorScope & {
28
+ personalOwnerVerified?: boolean;
29
+ providerId?: "github-app" | "github-lens";
30
+ };
31
+ export declare function githubAppConnectNavigation(deps: ApiRouteDeps, scope: GitHubConnectScope, attemptId: string, requestUrl: string, phase?: State["phase"], installationId?: number, providerId?: State["providerId"]): {
32
+ authorizationUrl: string;
33
+ expiresAt: string;
34
+ };
35
+ export declare function prepareGitHubAppConnectAction(deps: ApiRouteDeps, scope: GitHubConnectScope, requestUrl: string, attempt: ConnectAttempt, action: ConnectAdvance): PreparedConnectOperation;
36
+ /** No browser login/cookie required: signed attempt + current stored origin,
37
+ * then fresh GitHub owner proof, are the two separate authority boundaries. */
38
+ export declare function completeGitHubAppConnect(deps: ApiRouteDeps, input: {
39
+ state?: string | undefined;
40
+ code?: string | undefined;
41
+ installationId?: string | undefined;
42
+ setupAction?: string | undefined;
43
+ error?: string | undefined;
44
+ requestUrl: string;
45
+ expectedProvider?: "github-app" | "github-lens";
46
+ }): Promise<Response>;
47
+ export {};
@@ -0,0 +1,3 @@
1
+ import type { GitHubInstallationBindingCandidate, GitHubInstallationBindingProof } from "@opengeni/contracts";
2
+ export declare function isConsistentGitHubBindingCandidates(candidates: GitHubInstallationBindingCandidate[]): boolean;
3
+ export declare function isConsistentGitHubBindingProof(proof: GitHubInstallationBindingProof, installationId: number): boolean;
@@ -0,0 +1,153 @@
1
+ import { type GitHubInstallationBindingProof } from "@opengeni/contracts";
2
+ import { type ApiRouteDeps } from "@opengeni/core";
3
+ import { type Database } from "@opengeni/db";
4
+ export declare function requireGitHubLensConnect(deps: ApiRouteDeps, workspaceId: string): Promise<{
5
+ installation: {
6
+ skillWrites?: {
7
+ operationId: string;
8
+ skillId: string;
9
+ revisionId: string;
10
+ outcome: "applied" | "pending" | "preserved";
11
+ decision?: "rejected" | undefined;
12
+ pendingReason?: "approval" | "source_finalization" | undefined;
13
+ skillReview?: {
14
+ sourceOperationId: string;
15
+ skillId: string;
16
+ revisionId: string;
17
+ expectedRevisionId: string | null;
18
+ expectedScopeVersion: number;
19
+ } | undefined;
20
+ replayed: boolean;
21
+ }[] | undefined;
22
+ skillPublications?: {
23
+ operationId: string;
24
+ skillId: string;
25
+ revisionId: string;
26
+ outcome: "applied" | "pending" | "preserved";
27
+ decision?: "rejected" | undefined;
28
+ pendingReason?: "approval" | "source_finalization" | undefined;
29
+ skillReview?: {
30
+ sourceOperationId: string;
31
+ skillId: string;
32
+ revisionId: string;
33
+ expectedRevisionId: string | null;
34
+ expectedScopeVersion: number;
35
+ } | undefined;
36
+ replayed: boolean;
37
+ sourceOperationId: string;
38
+ activationEventId: string | null;
39
+ }[] | undefined;
40
+ skillReleases?: {
41
+ skillId: string;
42
+ revisionId: string | null;
43
+ disposition: "deactivated" | "inactive" | "preserved";
44
+ eventId: string | null;
45
+ warning: string | null;
46
+ }[] | undefined;
47
+ id: string;
48
+ accountId: string;
49
+ workspaceId: string;
50
+ packId: string;
51
+ status: "active" | "disabled" | "installing" | "needs_attention";
52
+ version: number;
53
+ manifestSnapshot: Record<string, unknown> | null;
54
+ manifestDigest: string | null;
55
+ selectedRigId: string | null;
56
+ installedBySubjectId: string | null;
57
+ metadata: Record<string, unknown>;
58
+ enabledAt: string;
59
+ updatedAt: string;
60
+ };
61
+ template: {
62
+ id: string;
63
+ name: string;
64
+ description: string;
65
+ adapterId: string;
66
+ eventTypes: string[];
67
+ sessionTemplate: {
68
+ bundledSkillIds?: ("builtin:opengeni-documents" | "builtin:opengeni-presentations" | "builtin:opengeni-projects" | "builtin:opengeni-sites" | "builtin:opengeni-skills" | "builtin:opengeni-spreadsheets" | "builtin:opengeni-video-generation")[] | undefined;
69
+ prompt: string;
70
+ instructions: string | null;
71
+ resources: ({
72
+ kind: "repository";
73
+ uri: string;
74
+ ref: string;
75
+ expectedCommitSha?: string | undefined;
76
+ mountPath?: string | undefined;
77
+ subpath?: string | undefined;
78
+ provider?: "azure_devops" | "github" | "gitlab" | undefined;
79
+ connectionType?: "github_personal" | undefined;
80
+ credentialBindingId?: string | undefined;
81
+ access?: "read" | "write" | undefined;
82
+ repositoryId?: string | number | undefined;
83
+ installationId?: string | number | undefined;
84
+ projectId?: string | number | undefined;
85
+ connectionId?: string | undefined;
86
+ githubInstallationId?: number | undefined;
87
+ githubRepositoryId?: number | undefined;
88
+ } | {
89
+ kind: "file";
90
+ fileId: string;
91
+ mountPath?: string | undefined;
92
+ })[];
93
+ skills: {
94
+ name: string;
95
+ description: string;
96
+ files: {
97
+ path: string;
98
+ content: string;
99
+ }[];
100
+ }[];
101
+ tools: {
102
+ kind: "mcp";
103
+ id: string;
104
+ eager?: boolean | undefined;
105
+ optional?: boolean | undefined;
106
+ }[];
107
+ firstPartyMcpTools: ("artifacts_archive" | "artifacts_create" | "artifacts_get_source" | "artifacts_list" | "artifacts_prepare_upload" | "artifacts_publish" | "artifacts_restore" | "artifacts_rollback" | "atlassian_get" | "atlassian_search" | "atlassian_sources_list" | "browser_act" | "browser_auth" | "browser_clipboard" | "browser_debug" | "browser_identity" | "browser_lifecycle" | "browser_observe" | "browser_open" | "browser_publish" | "browser_tabs" | "capability_authorization_request" | "capability_catalog_search" | "command_read" | "command_wait" | "company_profile_confirm" | "company_profile_propose" | "computer_act" | "computer_clipboard" | "computer_lifecycle" | "computer_observe" | "computer_open" | "computer_targets" | "connected_machine_remove" | "editable_artifact_apply" | "editable_artifact_create" | "editable_artifact_export" | "editable_artifact_export_status" | "editable_artifact_get" | "editable_artifact_import" | "editable_artifact_inspect" | "editable_artifact_list" | "environment_list" | "environment_set_variable" | "fiken_bank_accounts_list" | "fiken_companies_list" | "fiken_contact_create" | "fiken_contacts_list" | "fiken_invoice_draft_create" | "fiken_invoice_get" | "fiken_invoices_list" | "fiken_products_list" | "fiken_purchases_list" | "fiken_sales_list" | "github_connect_link" | "github_repositories_list" | "goal_complete" | "goal_pause" | "goal_progress" | "goal_resume" | "goal_set" | "goal_update" | "instruction_policy_propose" | "interaction_discover" | "interaction_request_human" | "knowledge_correct" | "knowledge_propose" | "memory_correct" | "memory_save" | "memory_search" | "preference_propose" | "preference_registry_get" | "preference_registry_summary" | "project_create" | "project_delete" | "project_get" | "project_list" | "project_reorder" | "project_update" | "reddit_accounts_list" | "reddit_mentions_live" | "reddit_post_reply" | "reddit_posts_sync" | "reddit_search_live" | "reddit_thread_fetch" | "remember" | "remember_confirm" | "rig_get" | "rig_list" | "rig_promote" | "rig_propose_change" | "rig_verify" | "run_on" | "sandbox_attach" | "sandbox_file_publish" | "sandbox_provision" | "sandbox_swap" | "sandboxes_list" | "scheduled_task_runs_list" | "scheduled_tasks_create" | "scheduled_tasks_delete" | "scheduled_tasks_get" | "scheduled_tasks_list" | "scheduled_tasks_pause" | "scheduled_tasks_resume" | "scheduled_tasks_trigger" | "scheduled_tasks_update" | "session_create" | "session_events" | "session_get" | "session_human_input_respond" | "session_pause" | "session_resume" | "session_send_message" | "session_set_project" | "session_steer" | "session_wait" | "sessions_list" | "set_other_session_title" | "set_session_title" | "slack_bot_channel_history" | "slack_bot_delete_message" | "slack_bot_file_content" | "slack_bot_file_info" | "slack_bot_list_channels" | "slack_bot_list_files" | "slack_bot_list_users" | "slack_bot_post_message" | "slack_bot_search" | "slack_bot_thread_replies" | "social_connections_list" | "social_daily_analysis_context" | "social_mentions_live" | "social_post_reply" | "social_posts_recent" | "social_posts_sync" | "social_search_live" | "social_thread_fetch" | "task_note_archive" | "task_note_promote_instruction_policy" | "task_note_promote_knowledge" | "task_note_promote_preference" | "task_note_replace" | "task_note_save" | "task_notes_list" | "variable_set_get_variable" | "variable_set_list" | "variable_set_set_variable" | "wait_for_input" | "work_claim_release" | "work_claim_upsert" | "x_accounts_list" | "x_mentions_live" | "x_post_reply" | "x_posts_sync" | "x_search_live" | "x_thread_fetch")[];
108
+ firstPartyMcpPermissions: ("account:admin" | "account:read" | "api_keys:manage" | "artifacts:publish" | "artifacts:read" | "billing:manage" | "billing:read" | "capabilities:manage" | "codemode:call" | "connections:read" | "connections:write" | "documents:manage" | "documents:search" | "enrollments:manage" | "enrollments:read" | "environments:manage" | "environments:use" | "files:read" | "files:upload" | "files:write" | "github:manage" | "github:use" | "goals:manage" | "mcp_servers:attach" | "members:manage" | "rigs:manage" | "rigs:use" | "scheduled_tasks:manage" | "scheduled_tasks:run" | "secrets:list" | "secrets:read" | "secrets:write" | "sessions:control" | "sessions:create" | "sessions:read" | "stream:acknowledge" | "stream:control" | "stream:view" | "terminal:attach" | "variable-sets:attach" | "variable-sets:list" | "variable-sets:manage" | "variable-sets:read" | "variable-sets:use" | "variable-sets:write" | "workspace:admin" | "workspace:create" | "workspace:read")[];
109
+ model: string | null;
110
+ reasoningEffort: "high" | "low" | "max" | "medium" | "minimal" | "none" | "xhigh" | null;
111
+ sandboxBackend: "blaxel" | "cloudflare" | "daytona" | "docker" | "e2b" | "local" | "modal" | "none" | "opensandbox" | "runloop" | "selfhosted" | "vercel" | null;
112
+ policyRole: string | null;
113
+ metadata: Record<string, unknown>;
114
+ };
115
+ configuration: Record<string, unknown>;
116
+ connectionRequirement: string | null;
117
+ };
118
+ }>;
119
+ /** Separate Lens registration/source/automation domain, not a repository-access
120
+ * binding. Invoked inside the Connect receipt's authorized transaction. */
121
+ export declare function commitGitHubLensConnect(deps: ApiRouteDeps, tx: Database, input: {
122
+ accountId: string;
123
+ workspaceId: string;
124
+ subjectId: string;
125
+ installationId: number;
126
+ proof: GitHubInstallationBindingProof;
127
+ checkedAt: Date;
128
+ expiresAt: Date;
129
+ nonce: string;
130
+ }): Promise<{
131
+ id: string;
132
+ sourceId: string;
133
+ accountId: string;
134
+ workspaceId: string;
135
+ name: string;
136
+ provider: "azure_devops" | "github" | "gitlab";
137
+ providerBaseUrl: string;
138
+ appId: string | null;
139
+ installationId: string | null;
140
+ providerAccountLogin: string | null;
141
+ providerAccountType: "Organization" | "User" | null;
142
+ credentialKind: "github_app" | "managed_github_app" | "provider_token";
143
+ hasCredential: boolean;
144
+ accessTokenExpiresAt: string | null;
145
+ webhookAuthKind: "basic" | "hmac_sha256" | "shared_token";
146
+ hasWebhookSecret: boolean;
147
+ webhookUsername: string | null;
148
+ webhookPath: string;
149
+ status: "active" | "disabled";
150
+ createdBySubjectId: string;
151
+ createdAt: string;
152
+ updatedAt: string;
153
+ }>;
@@ -1,3 +1,4 @@
1
+ import { ExternalActorContinuation } from "@opengeni/contracts/external-identities";
1
2
  import type { AccessGrant, ScheduledTask } from "@opengeni/contracts";
2
3
  import { GoogleDriveOAuthStartResponse, type GoogleDriveDisconnectRequest, type GoogleDriveLifecycleActionRequest, type GoogleDriveOAuthStartRequest } from "@opengeni/contracts/google-drive";
3
4
  import { IntegrationFacetMutationResult } from "@opengeni/contracts";
@@ -21,6 +22,8 @@ export declare function startGoogleDriveOAuth(deps: ApiRouteDeps, input: {
21
22
  subjectId: string;
22
23
  requestUrl: string;
23
24
  payload: GoogleDriveOAuthStartRequest;
25
+ connectAttemptId?: string;
26
+ externalContinuation?: ExternalActorContinuation;
24
27
  }): Promise<GoogleDriveOAuthStartResponse>;
25
28
  export declare function completeGoogleDriveOAuthCallback(deps: ApiRouteDeps, input: {
26
29
  code?: string | undefined;
@@ -30,6 +33,7 @@ export declare function completeGoogleDriveOAuthCallback(deps: ApiRouteDeps, inp
30
33
  requestUrl: string;
31
34
  }): Promise<{
32
35
  redirectTo: string;
36
+ exactReturn?: boolean;
33
37
  }>;
34
38
  export declare function transitionGoogleDriveLifecycle(deps: ApiRouteDeps, input: {
35
39
  workspaceId: string;
@@ -1,5 +1,6 @@
1
1
  import { type Settings } from "@opengeni/config";
2
2
  import { OAuthStartResponse, type ConnectionOwnership, type OAuthStartRequest } from "@opengeni/contracts";
3
+ import { ExternalActorContinuation } from "@opengeni/contracts/external-identities";
3
4
  import type { Observability } from "@opengeni/observability";
4
5
  import { type Database } from "@opengeni/db";
5
6
  import { type McpAuthorizationServerMetadata } from "@opengeni/network";
@@ -15,6 +16,8 @@ type OAuthClientDeps = {
15
16
  oauthCallbackDeadlineMs?: number | undefined;
16
17
  };
17
18
  export type OAuthStartContext = {
19
+ connectAttemptId?: string;
20
+ externalContinuation?: ExternalActorContinuation;
18
21
  accountId: string;
19
22
  workspaceId: string;
20
23
  subjectId: string;
@@ -29,6 +32,7 @@ export type OAuthStartContext = {
29
32
  };
30
33
  export type OAuthCallbackResult = {
31
34
  redirectTo: string;
35
+ exactReturn?: boolean;
32
36
  };
33
37
  type AuthorizationServerMetadata = McpAuthorizationServerMetadata;
34
38
  export declare const OAUTH_START_DEADLINE_MS = 15000;
@@ -0,0 +1 @@
1
+ export declare function safeReturnPath(value: string): string;
@@ -24,6 +24,7 @@ export declare function startPersonalGitHubOAuth(deps: ApiRouteDeps, input: {
24
24
  workspaceId: string;
25
25
  connectionId?: string;
26
26
  returnPath?: string;
27
+ connectAttemptId?: string;
27
28
  }): Promise<{
28
29
  authorizationUrl: string;
29
30
  expiresAt: string;
@@ -35,6 +36,7 @@ export declare function completePersonalGitHubOAuthCallback(deps: ApiRouteDeps,
35
36
  requestUrl: string;
36
37
  }): Promise<{
37
38
  redirectTo: string;
39
+ exactReturn?: boolean;
38
40
  }>;
39
41
  export declare function personalGitHubReviewUrl(settings: Settings): string | null;
40
42
  export declare const PERSONAL_GITHUB_CONNECTION_PRINCIPAL_MESSAGE: string;
@@ -1,6 +1,8 @@
1
+ import { type IntegrationDefinition } from "@opengeni/capabilities";
1
2
  import { type Settings } from "@opengeni/config";
2
- import { OAuthStartResponse, type ApiIntegrationOAuthStartRequest } from "@opengeni/contracts";
3
+ import { OAuthStartResponse, type ApiIntegrationOAuthStartRequest, type ConnectionOwnership } from "@opengeni/contracts";
3
4
  import { type ApiRouteDeps } from "@opengeni/core";
5
+ import { ExternalActorContinuation } from "@opengeni/contracts/external-identities";
4
6
  export declare function startApiIntegrationProviderOAuth(deps: ApiRouteDeps, input: {
5
7
  accountId: string;
6
8
  workspaceId: string;
@@ -11,6 +13,8 @@ export declare function startApiIntegrationProviderOAuth(deps: ApiRouteDeps, inp
11
13
  * route from the live authenticated principal, never inferred here.
12
14
  */
13
15
  personalOwnershipAllowed: boolean;
16
+ externalContinuation?: ExternalActorContinuation;
17
+ connectAttemptId?: string;
14
18
  requestUrl: string;
15
19
  payload: ApiIntegrationOAuthStartRequest;
16
20
  }): Promise<OAuthStartResponse>;
@@ -21,5 +25,11 @@ export declare function completeApiIntegrationProviderOAuth(deps: ApiRouteDeps,
21
25
  requestUrl: string;
22
26
  }): Promise<{
23
27
  redirectTo: string;
28
+ exactReturn?: boolean;
24
29
  }>;
30
+ /** Configuration readiness only: consent and installation are still separate. */
31
+ export declare function curatedOAuthReadiness(settings: Settings, definition: IntegrationDefinition): {
32
+ configured: boolean;
33
+ ownership: ConnectionOwnership[];
34
+ };
25
35
  export declare function isApiIntegrationProviderOAuthState(value: string | undefined, settings: Settings): boolean;
@@ -0,0 +1,20 @@
1
+ import { ExternalActorContinuation } from "@opengeni/contracts/external-identities";
2
+ import { type ApiRouteDeps } from "@opengeni/core";
3
+ export declare function requireOpenGeniSlackOAuthSettings(settings: ApiRouteDeps["settings"]): {
4
+ clientId: string;
5
+ clientSecret: string;
6
+ };
7
+ /** Only authenticated route admission may invoke this workspace-bot starter.
8
+ * Hosted personal Slack MCP deliberately uses its own OAuth profile. */
9
+ export declare function startSlackBotInstall(deps: ApiRouteDeps, input: {
10
+ accountId: string;
11
+ workspaceId: string;
12
+ subjectId: string;
13
+ requestUrl: string;
14
+ connectionId?: string;
15
+ connectAttemptId?: string;
16
+ externalContinuation?: ExternalActorContinuation;
17
+ }): Promise<{
18
+ authorizationUrl: string;
19
+ expiresAt: string;
20
+ }>;
@@ -42,6 +42,7 @@ type SocialOAuthDeps = {
42
42
  providerFetch?: SocialProviderFetch | undefined;
43
43
  };
44
44
  export type SocialOAuthStartContext = {
45
+ connectAttemptId?: string;
45
46
  accountId: string;
46
47
  workspaceId: string;
47
48
  subjectId: string;
@@ -66,6 +67,7 @@ export declare function completeSocialOAuthCallback(deps: SocialOAuthDeps, input
66
67
  requestUrl: string;
67
68
  }): Promise<{
68
69
  redirectTo: string;
70
+ exactReturn?: boolean;
69
71
  }>;
70
72
  /**
71
73
  * Resolves a usable access token for a social connection, refreshing (and
@@ -82,8 +82,8 @@ export declare function scheduledTaskMcpSummary(task: ScheduledTask): {
82
82
  runMode: "existing_session" | "new_session_per_run" | "reusable_session";
83
83
  overlapPolicy: "allow_concurrent" | "buffer_one" | "skip";
84
84
  action: {
85
- readonly sourceId?: never;
86
85
  kind: "agent_turn";
86
+ sourceId?: never;
87
87
  sourceGeneration?: never;
88
88
  sourceLifecycleGeneration?: never;
89
89
  destination?: never;
@@ -205,8 +205,8 @@ export declare function boundScheduledTaskDetailMcp(task: ScheduledTask): {
205
205
  runMode: "existing_session" | "new_session_per_run" | "reusable_session";
206
206
  overlapPolicy: "allow_concurrent" | "buffer_one" | "skip";
207
207
  action: {
208
- readonly sourceId?: never;
209
208
  kind: "agent_turn";
209
+ sourceId?: never;
210
210
  sourceGeneration?: never;
211
211
  sourceLifecycleGeneration?: never;
212
212
  destination?: never;
@@ -389,8 +389,8 @@ export declare function boundScheduledTaskMcpPage(input: {
389
389
  runMode: "existing_session" | "new_session_per_run" | "reusable_session";
390
390
  overlapPolicy: "allow_concurrent" | "buffer_one" | "skip";
391
391
  action: {
392
- readonly sourceId?: never;
393
392
  kind: "agent_turn";
393
+ sourceId?: never;
394
394
  sourceGeneration?: never;
395
395
  sourceLifecycleGeneration?: never;
396
396
  destination?: never;
@@ -1,8 +1,22 @@
1
+ import { createPinnedIntegrationTransport } from "@opengeni/capabilities";
2
+ import { InstallApiIntegrationRequest, PreviewApiIntegrationRequest, type AccessGrant } from "@opengeni/contracts";
1
3
  import { type ApiRouteDeps } from "@opengeni/core";
4
+ import { installApiIntegration } from "@opengeni/db";
2
5
  import type { FetchLike } from "@opengeni/network";
3
6
  import type { Hono } from "hono";
7
+ import { resolveApiIntegrationPreview, type ResolvedApiIntegrationPreview } from "../integrations/api-integrations.js";
4
8
  export type ApiIntegrationRouteOverrides = Readonly<{
5
9
  fetchImpl?: FetchLike;
6
10
  }>;
7
11
  export declare function apiIntegrationRequiresConnection(authScheme: Record<string, unknown>): boolean;
12
+ /** Shared native/embedded install validation. The caller must still authorize
13
+ * capabilities:manage and commit under its own live-authority boundary. */
14
+ export declare function validatedIntegrationInstallInput(grant: AccessGrant, workspaceId: string, payload: InstallApiIntegrationRequest, resolved: ResolvedApiIntegrationPreview): Parameters<typeof installApiIntegration>[1];
8
15
  export declare function registerApiIntegrationRoutes(app: Hono, deps: ApiRouteDeps, overrides?: ApiIntegrationRouteOverrides): void;
16
+ export declare function resolveForRoute(input: {
17
+ deps: ApiRouteDeps;
18
+ transport: ReturnType<typeof createPinnedIntegrationTransport>;
19
+ workspaceId: string;
20
+ subjectId: string;
21
+ payload: PreviewApiIntegrationRequest | InstallApiIntegrationRequest;
22
+ }): ReturnType<typeof resolveApiIntegrationPreview>;
@@ -0,0 +1,5 @@
1
+ import type { Hono } from "hono";
2
+ import { type ApiRouteDeps } from "@opengeni/core";
3
+ /** Shared durable setup entry. Provider completion is distinct from
4
+ * subsequent preview/install; never report an OAuth token as a ready integration. */
5
+ export declare function registerConnectRoutes(app: Hono, deps: ApiRouteDeps): void;
@@ -0,0 +1,6 @@
1
+ import type { Hono } from "hono";
2
+ import { type ApiRouteDeps } from "@opengeni/core";
3
+ /** Both products address the link through a workspace they already own. The
4
+ * link itself is organization-local; the host's Personal workspace need not be
5
+ * exposed to the native human, and neither side names the other's subject. */
6
+ export declare function registerExternalIdentityLinkRoutes(app: Hono, deps: ApiRouteDeps): void;
@@ -0,0 +1,5 @@
1
+ import type { Hono } from "hono";
2
+ import { type ApiRouteDeps } from "@opengeni/core";
3
+ /** Registration is not an execution grant. The durable runtime must separately
4
+ * validate the exact binding generation and accepted execution context. */
5
+ export declare function registerHostMcpBindingRoutes(app: Hono, deps: ApiRouteDeps): void;
@@ -10,6 +10,7 @@ import type { Observability } from "@opengeni/observability";
10
10
  export type PreparedWorkspaceToolGateway = Pick<PreparedWorkspaceToolGatewayTools, "toolGateway" | "toolGatewayCatalog" | "close"> & {
11
11
  toolGateway: NonNullable<PreparedWorkspaceToolGatewayTools["toolGateway"]>;
12
12
  toolGatewayCatalog: NonNullable<PreparedWorkspaceToolGatewayTools["toolGatewayCatalog"]>;
13
+ reauthorize?: () => Promise<void>;
13
14
  };
14
15
  type WorkspaceSiteToolContext = {
15
16
  siteArtifactId: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@opengeni/api-router",
3
- "version": "2.11.3",
3
+ "version": "2.12.1-canary.0",
4
4
  "description": "OpenGeni HTTP surface: the Hono adapter/router (createApp), routes, MCP HTTP transport, and HTTP access adapters over @opengeni/core. An engine-distribution surface — its runtime closure includes engine-internal packages.",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
@@ -43,24 +43,24 @@
43
43
  "@hono/zod-validator": "^0.7.6",
44
44
  "@llamaindex/liteparse": "2.14.2",
45
45
  "@modelcontextprotocol/sdk": "^1.29.0",
46
- "@opengeni/agent-proto": "^0.5.1",
47
- "@opengeni/artifact-tool": "^0.3.23",
48
- "@opengeni/capabilities": "^0.3.3",
49
- "@opengeni/codemode": "^0.5.3",
50
- "@opengeni/codex": "^0.2.22",
51
- "@opengeni/config": "^1.0.4",
52
- "@opengeni/contracts": "^2.15.2",
53
- "@opengeni/core": "^2.8.3",
54
- "@opengeni/db": "^4.2.2",
55
- "@opengeni/documents": "^0.8.23",
56
- "@opengeni/events": "^0.4.21",
57
- "@opengeni/github": "^0.7.7",
58
- "@opengeni/network": "^0.3.1",
59
- "@opengeni/observability": "^0.8.23",
60
- "@opengeni/runtime": "^2.4.3",
61
- "@opengeni/storage": "^0.2.124",
62
- "@opengeni/tool-gateway": "^0.1.4",
63
- "@opengeni/xai-subscription": "^0.1.4",
46
+ "@opengeni/agent-proto": "^0.5.1-canary.24",
47
+ "@opengeni/artifact-tool": "^0.3.24-canary.0",
48
+ "@opengeni/capabilities": "^0.3.3-canary.6",
49
+ "@opengeni/codemode": "^0.5.4-canary.0",
50
+ "@opengeni/codex": "^0.2.22-canary.6",
51
+ "@opengeni/config": "^1.1.0-canary.0",
52
+ "@opengeni/contracts": "^3.0.0-canary.0",
53
+ "@opengeni/core": "^2.9.1-canary.0",
54
+ "@opengeni/db": "^4.3.0-canary.0",
55
+ "@opengeni/documents": "^0.8.24-canary.0",
56
+ "@opengeni/events": "^0.4.22-canary.0",
57
+ "@opengeni/github": "^0.7.8-canary.0",
58
+ "@opengeni/network": "^0.3.1-canary.6",
59
+ "@opengeni/observability": "^0.8.24-canary.0",
60
+ "@opengeni/runtime": "^2.5.1-canary.0",
61
+ "@opengeni/storage": "^0.2.125-canary.0",
62
+ "@opengeni/tool-gateway": "^0.1.5-canary.0",
63
+ "@opengeni/xai-subscription": "^0.1.4-canary.6",
64
64
  "@temporalio/client": "^1.17.0",
65
65
  "better-auth": "^1.6.14",
66
66
  "hono": "^4.12.18",
package/src/app.ts CHANGED
@@ -1,3 +1,4 @@
1
+ import { registerConnectCallbackReturns } from "./integrations/connect-callback-return";
1
2
  import { registerFeedbackRoutes } from "./routes/feedback";
2
3
  import { codemodeSessionRequest } from "./codemode";
3
4
  import { SiteSessionPathError } from "@opengeni/contracts";
@@ -40,6 +41,8 @@ import {
40
41
  CodemodePayloadTooLargeError,
41
42
  CodemodeToolApprovalRequiredError,
42
43
  CodemodeToolNotInCatalogError,
44
+ ConnectAttemptConflictError,
45
+ ConnectAttemptNotFoundError,
43
46
  configureChildLifecycleNotices,
44
47
  configureWorkspaceControlRequestLockTimeoutMs,
45
48
  dbSql,
@@ -149,6 +152,9 @@ import { registerCodexRoutes } from "./routes/codex";
149
152
  import { registerOrganizationModelProviderRoutes } from "./routes/organization-model-providers";
150
153
  import { registerSuperGrokRoutes } from "./routes/supergrok";
151
154
  import { registerConnectionRoutes } from "./routes/connections";
155
+ import { registerConnectRoutes } from "./routes/connect";
156
+ import { registerHostMcpBindingRoutes } from "./routes/host-mcp-bindings";
157
+ import { registerExternalIdentityLinkRoutes } from "./routes/external-identity-links";
152
158
  import { registerDocumentRoutes } from "./routes/documents";
153
159
  import { registerEnrollmentRoutes } from "./routes/enrollments";
154
160
  import { registerMachineRoutes } from "./routes/machines";
@@ -1039,9 +1045,14 @@ export function createAppComposition(deps: AppDependencies): {
1039
1045
  // read and write. A missing row resolves to no Memory tools.
1040
1046
  const sessionMemory =
1041
1047
  typeof boundSessionId === "string"
1042
- ? ((await resolveSessionMemoryAgentScope(routeDeps.db, workspaceId, boundSessionId)) ?? {
1048
+ ? ((await resolveSessionMemoryAgentScope(
1049
+ routeDeps.db,
1050
+ workspaceId,
1051
+ boundSessionId,
1052
+ grant.metadata,
1053
+ )) ?? {
1043
1054
  mode: "off" as const,
1044
- endUserSubjectId: null,
1055
+ userSubjectId: null,
1045
1056
  rootSessionId: null,
1046
1057
  })
1047
1058
  : null;
@@ -1224,6 +1235,7 @@ export function createAppComposition(deps: AppDependencies): {
1224
1235
  }
1225
1236
  });
1226
1237
 
1238
+ registerConnectCallbackReturns(app, routeDeps);
1227
1239
  registerFileRoutes(app, routeDeps);
1228
1240
  registerApiKeyRoutes(app, routeDeps);
1229
1241
  registerBillingRoutes(app, routeDeps);
@@ -1249,6 +1261,9 @@ export function createAppComposition(deps: AppDependencies): {
1249
1261
  registerPersonalGitHubRoutes(app, routeDeps);
1250
1262
  registerPersonalGitHubGitBrokerRoutes(app, routeDeps);
1251
1263
  registerConnectionRoutes(app, routeDeps);
1264
+ registerConnectRoutes(app, routeDeps);
1265
+ registerHostMcpBindingRoutes(app, routeDeps);
1266
+ registerExternalIdentityLinkRoutes(app, routeDeps);
1252
1267
  registerCapabilityRoutes(app, routeDeps);
1253
1268
  registerApiIntegrationRoutes(app, routeDeps);
1254
1269
  registerIntegrationFacetRoutes(app, routeDeps);
@@ -1573,6 +1588,8 @@ function codexCompactionV2ProviderLockedError(
1573
1588
  }
1574
1589
 
1575
1590
  export function httpStatusForError(error: unknown): number {
1591
+ if (error instanceof ConnectAttemptConflictError) return 409;
1592
+ if (error instanceof ConnectAttemptNotFoundError) return 404;
1576
1593
  if (codexCompactionV2ProviderLockedError(error)) {
1577
1594
  return 422;
1578
1595
  }
@@ -1605,6 +1622,9 @@ function retryableHttpStatus(status: number): boolean {
1605
1622
  }
1606
1623
 
1607
1624
  function publicErrorMessage(error: unknown, status: number): string {
1625
+ if (error instanceof ConnectAttemptConflictError)
1626
+ return "Connection setup changed or is still in progress. Reload its current status before retrying.";
1627
+ if (error instanceof ConnectAttemptNotFoundError) return "Connection setup not found.";
1608
1628
  if (status === 502 || status === 503 || status === 504) {
1609
1629
  return "OpenGeni is temporarily unavailable — retry.";
1610
1630
  }