@opengeni/core 2.7.2-canary.0 → 2.7.5-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.
@@ -68,6 +68,16 @@ export declare function accessGrantAuthorizationFromContext(context: AccessConte
68
68
  * access resolver are present in `resolvedAccessGrantAuthorizations`.
69
69
  */
70
70
  export declare function requireAccountAdminAuthorizationStamp(authorization: AccessGrantAuthorization): AccountAdminAuthorizationStamp;
71
+ /**
72
+ * Verify that an access authorization was minted by the canonical request
73
+ * resolver for this exact subject and workspace.
74
+ *
75
+ * This is the protocol-neutral boundary for request-local services that need
76
+ * the authenticated grant rather than a caller-supplied grant-shaped object.
77
+ * Object identity is intentional: matching fields alone are not proof that the
78
+ * request authenticated the named subject.
79
+ */
80
+ export declare function requireResolvedAccessGrantAuthorization(authorization: AccessGrantAuthorization, workspaceId: string): AccessGrant;
71
81
  /**
72
82
  * Resolve the exact built-in single-user local administrator for an account.
73
83
  *
@@ -0,0 +1,20 @@
1
+ import { type IntegrationInvocationAuthority } from "@opengeni/capabilities";
2
+ import type { Settings } from "@opengeni/config";
3
+ import type { ToolAuthNeededPayload } from "@opengeni/contracts";
4
+ import type { ApiIntegrationRuntime, ResolveConnectionCredentialInput, ResolveConnectionCredentialResult } from "@opengeni/db";
5
+ import type { FetchLike } from "@opengeni/network";
6
+ import type { LocalMcpServerRegistration } from "@opengeni/runtime";
7
+ export type BuildApiIntegrationServersInput = {
8
+ settings: Settings;
9
+ integrations: readonly ApiIntegrationRuntime[];
10
+ authority: Omit<IntegrationInvocationAuthority, "connectionRef">;
11
+ resolveCredential: (input: ResolveConnectionCredentialInput) => Promise<ResolveConnectionCredentialResult>;
12
+ onAuthNeeded?: (payload: ToolAuthNeededPayload) => Promise<void> | void;
13
+ fetchImpl?: FetchLike;
14
+ };
15
+ /**
16
+ * Compile active persisted API facets into ordinary in-process MCP providers.
17
+ * The caller supplies its authority resolver, so agent attempts and current
18
+ * humans use the same provider assembly without sharing credentials or policy.
19
+ */
20
+ export declare function buildApiIntegrationMcpServers(input: BuildApiIntegrationServersInput): LocalMcpServerRegistration[];
@@ -80,7 +80,7 @@ export declare const prReviewAutomationAdapter: {
80
80
  eager?: boolean | undefined;
81
81
  optional?: boolean | undefined;
82
82
  }[];
83
- firstPartyMcpTools: ("artifacts_create" | "artifacts_get_source" | "artifacts_list" | "artifacts_publish" | "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" | "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_set" | "goal_update" | "goal_wait" | "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" | "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_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" | "work_claim_release" | "work_claim_upsert" | "x_accounts_list" | "x_mentions_live" | "x_post_reply" | "x_posts_sync" | "x_search_live" | "x_thread_fetch")[];
83
+ firstPartyMcpTools: ("artifacts_archive" | "artifacts_create" | "artifacts_get_source" | "artifacts_list" | "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_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_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" | "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_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")[];
84
84
  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")[];
85
85
  model: string | null;
86
86
  reasoningEffort: "high" | "low" | "max" | "medium" | "minimal" | "none" | "xhigh" | null;
package/dist/index.d.ts CHANGED
@@ -49,5 +49,6 @@ export * from "./application/composer-submit.js";
49
49
  export * from "./application/session-commands.js";
50
50
  export * from "./application/session-tenancy.js";
51
51
  export * from "./application/user-resource-grants.js";
52
+ export * from "./application/api-integration-servers.js";
52
53
  export * from "./editable-artifact-live.js";
53
54
  export * from "./editable-artifacts.js";
package/dist/index.js CHANGED
@@ -731,7 +731,6 @@ import {
731
731
  verifyDirectWorkspaceMutationSettlement,
732
732
  verifyRetainedProcessMutationSettlement
733
733
  } from "@opengeni/db";
734
- import { settleSessionBackgroundCommandForRetainedProcess } from "@opengeni/db/session-background-commands";
735
734
  import { appendAndPublishEvents } from "@opengeni/events";
736
735
  import {
737
736
  isProviderSandboxGoneDuringRoutedOperation,
@@ -965,14 +964,9 @@ function wrapChannelABoxWithRouting(services, ids, established) {
965
964
  reason: proof.reason,
966
965
  idleGraceMs: settings.sandboxIdleGraceMs
967
966
  });
968
- const backgroundSettlement = retainedProcessBackgroundSettlement(settlement.process, proof);
969
- await settleSessionBackgroundCommandForRetainedProcess(db, {
970
- accountId: ids.accountId,
971
- workspaceId: ids.workspaceId,
972
- sessionId: ids.sessionId,
973
- retainedProcessId: process.id,
974
- ...backgroundSettlement
975
- });
967
+ if (settlement.backgroundCommandEvents.length > 0 && bus) {
968
+ await bus.publish(ids.workspaceId, ids.sessionId, settlement.backgroundCommandEvents).catch(() => void 0);
969
+ }
976
970
  } : void 0;
977
971
  const resolver = makeActiveBackendResolver({
978
972
  workspaceId: ids.workspaceId,
@@ -1027,6 +1021,7 @@ function wrapChannelABoxWithRouting(services, ids, established) {
1027
1021
  } : {}
1028
1022
  });
1029
1023
  const proxy = new RoutingSandboxSession({
1024
+ bindActiveRouteOnFirstResolve: true,
1030
1025
  defaultResolved: {
1031
1026
  session: established.session,
1032
1027
  sandboxId: null,
@@ -2086,6 +2081,13 @@ function requireAccountAdminAuthorizationStamp(authorization) {
2086
2081
  permission: "account:admin"
2087
2082
  });
2088
2083
  }
2084
+ function requireResolvedAccessGrantAuthorization(authorization, workspaceId) {
2085
+ const { grant } = authorization;
2086
+ if (!resolvedAccessGrantAuthorizations.has(authorization) || !authorization.contextIntegrity || authorization.authenticatedSubjectId !== grant.subjectId || grant.workspaceId !== workspaceId) {
2087
+ throw new HTTPException(403, { message: "workspace access authorization is invalid" });
2088
+ }
2089
+ return grant;
2090
+ }
2089
2091
  async function requireCanonicalLocalAccountAdministrator(c, deps, accountId) {
2090
2092
  if (c.req.header("authorization")) {
2091
2093
  throw new HTTPException(401, { message: "organization administrator session required" });
@@ -15508,7 +15510,7 @@ async function controlAgentSessionWorkstream(deps, context, input) {
15508
15510
  wakeRevision: result.workflowWake.wakeRevision,
15509
15511
  shouldSignal: true,
15510
15512
  interruptionCount: result.interruptionCount,
15511
- controlRequested: true
15513
+ controlRequested: input.action === "pause"
15512
15514
  });
15513
15515
  }
15514
15516
  }
@@ -15796,7 +15798,7 @@ async function controlHumanSessionWorkstreamWithOutcome(deps, context, input) {
15796
15798
  wakeRevision: result.workflowWake.wakeRevision,
15797
15799
  shouldSignal: true,
15798
15800
  interruptionCount: result.interruptionCount,
15799
- controlRequested: true
15801
+ controlRequested: input.action === "pause"
15800
15802
  });
15801
15803
  }
15802
15804
  }
@@ -16009,6 +16011,143 @@ async function revokeManagedHumanUserResourceGrant(deps, authorization, workspac
16009
16011
  grantId
16010
16012
  });
16011
16013
  }
16014
+
16015
+ // src/application/api-integration-servers.ts
16016
+ import {
16017
+ createGraphqlMcpServer,
16018
+ createOpenApiMcpServer,
16019
+ createPinnedIntegrationTransport,
16020
+ directIntegrationTransport,
16021
+ IntegrationInvocationError
16022
+ } from "@opengeni/capabilities";
16023
+ function buildApiIntegrationMcpServers(input) {
16024
+ const transport = createPinnedIntegrationTransport({
16025
+ network: input.settings,
16026
+ ...input.fetchImpl ? { fetchImpl: input.fetchImpl } : {}
16027
+ });
16028
+ return input.integrations.map((integration) => {
16029
+ const authority = {
16030
+ ...input.authority,
16031
+ ...integration.connectionRef?.connectionId ? { connectionRef: integration.connectionRef.connectionId } : {}
16032
+ };
16033
+ const credentialResolver = integration.connectionRef ? integrationCredentialResolver(input, integration, "execution") : void 0;
16034
+ const buildServer = (serverTransport, serverCredentialResolver = credentialResolver) => integration.revision.protocol === "openapi" ? createOpenApiMcpServer({
16035
+ revision: integration.revision,
16036
+ transport: serverTransport,
16037
+ authority,
16038
+ ...serverCredentialResolver ? { credentialResolver: serverCredentialResolver } : {}
16039
+ }) : createGraphqlMcpServer({
16040
+ revision: integration.revision,
16041
+ endpoint: integration.baseUrl,
16042
+ transport: serverTransport,
16043
+ authority,
16044
+ ...serverCredentialResolver ? { credentialResolver: serverCredentialResolver } : {}
16045
+ });
16046
+ const server = buildServer(transport);
16047
+ const preflightCredentialResolver = integration.connectionRef ? integrationCredentialResolver(input, integration, "preflight") : void 0;
16048
+ const preflightServer = preflightCredentialResolver ? buildServer(providerBlockingPreflightTransport, preflightCredentialResolver) : null;
16049
+ return {
16050
+ id: integration.serverId,
16051
+ server,
16052
+ approvalAuthority: {
16053
+ kind: "api_integration",
16054
+ capabilityId: integration.capabilityId,
16055
+ pluginKey: integration.pluginKey,
16056
+ pluginInstallationId: integration.pluginInstallationId,
16057
+ installationVersion: integration.installationVersion,
16058
+ instanceId: integration.instanceId,
16059
+ instanceKey: integration.instanceKey,
16060
+ instanceVersion: integration.instanceVersion,
16061
+ definitionId: integration.definitionId,
16062
+ definitionProvenance: integration.definitionProvenance,
16063
+ revisionId: integration.revision.id,
16064
+ baseUrl: integration.baseUrl,
16065
+ providerDomain: integration.providerDomain,
16066
+ connectionRef: integration.connectionRef,
16067
+ connectionAuthorityGeneration: integration.connectionAuthorityGeneration
16068
+ },
16069
+ ...integration.connectionRef?.connectionId ? { resolvedConnectionId: integration.connectionRef.connectionId } : {},
16070
+ ...credentialResolver ? {
16071
+ preflightCall: async (toolName, args, options) => await preflightApiIntegrationCall(preflightServer, toolName, args, options)
16072
+ } : {}
16073
+ };
16074
+ });
16075
+ }
16076
+ var providerBlockingPreflightTransport = directIntegrationTransport(async () => {
16077
+ throw new Error("integration provider request blocked by preflight");
16078
+ });
16079
+ async function preflightApiIntegrationCall(server, toolName, args, options) {
16080
+ try {
16081
+ await server.callTool(toolName, args, null, options);
16082
+ } catch (error) {
16083
+ if (error instanceof IntegrationInvocationError && error.code === "request_failed") return;
16084
+ throw error;
16085
+ }
16086
+ throw new Error("integration preflight unexpectedly crossed the provider request boundary");
16087
+ }
16088
+ function integrationCredentialResolver(input, integration, credentialResolutionMode) {
16089
+ const connectionRef = integration.connectionRef;
16090
+ if (!connectionRef) throw new Error("Integration credential resolver requires a connection");
16091
+ const expectedAuthorityGeneration = integration.connectionAuthorityGeneration;
16092
+ if (expectedAuthorityGeneration === null) {
16093
+ throw new Error("Integration credential resolver requires a connection authority generation");
16094
+ }
16095
+ return {
16096
+ resolve: async (request) => {
16097
+ const result = await input.resolveCredential({
16098
+ workspaceId: input.authority.workspaceId,
16099
+ serverId: integration.serverId,
16100
+ toolName: request.operationKey,
16101
+ connectionRef,
16102
+ destinationUrl: request.destinationUrl,
16103
+ credentialTarget: "http_api",
16104
+ forceRefresh: request.forceRefresh === true,
16105
+ credentialResolutionMode,
16106
+ expectedAuthorityGeneration
16107
+ });
16108
+ if (result.status === "auth_needed") {
16109
+ await publishAuthNeeded(
16110
+ input,
16111
+ integration.serverId,
16112
+ request.operationKey,
16113
+ result,
16114
+ connectionRef
16115
+ );
16116
+ return null;
16117
+ }
16118
+ const destination = new URL(request.destinationUrl);
16119
+ return {
16120
+ audience: { origin: destination.origin, pathPrefix: "/" },
16121
+ placements: result.placements ?? Object.entries(result.headers).map(([name, value]) => ({
16122
+ carrier: "header",
16123
+ name,
16124
+ value
16125
+ })),
16126
+ ...result.authorizeProviderRequest ? { authorizeProviderRequest: result.authorizeProviderRequest } : {},
16127
+ ...result.expiresAt ? { expiresAt: result.expiresAt.toISOString() } : {},
16128
+ ...connectionRef.scopes ? { scope: [...connectionRef.scopes] } : {}
16129
+ };
16130
+ }
16131
+ };
16132
+ }
16133
+ async function publishAuthNeeded(input, serverId, toolName, result, connectionRef) {
16134
+ try {
16135
+ await input.onAuthNeeded?.({
16136
+ serverId,
16137
+ toolName,
16138
+ providerDomain: result.providerDomain,
16139
+ ...result.provider ? { provider: result.provider } : {},
16140
+ reason: result.reason,
16141
+ ...result.connectionId ? { connectionId: result.connectionId } : {},
16142
+ ...result.authoritySource === "host" || connectionRef.authoritySource === "host" ? { authoritySource: "host" } : {},
16143
+ ...result.scopes ? { scopes: result.scopes } : {},
16144
+ ...result.resource ? { resource: result.resource } : {},
16145
+ ...result.selectedResources ? { selectedResources: result.selectedResources } : {},
16146
+ ...result.authorizationUrl ? { authorizationUrl: result.authorizationUrl } : {}
16147
+ });
16148
+ } catch {
16149
+ }
16150
+ }
16012
16151
  export {
16013
16152
  CODEX_COMPACTION_V2_PROVIDER_LOCKED,
16014
16153
  CONVERSATION_ATTACHMENT_MAX_COUNT,
@@ -16168,6 +16307,7 @@ export {
16168
16307
  authorizedSocialConnectionsForGrant,
16169
16308
  automationRequestDigest,
16170
16309
  availableToolRefs,
16310
+ buildApiIntegrationMcpServers,
16171
16311
  buildAutomationAcceptedExecution,
16172
16312
  buildCapabilityCatalog,
16173
16313
  buildFleetContextForSession,
@@ -16399,6 +16539,7 @@ export {
16399
16539
  requireOpenGeniSlackBotConnection,
16400
16540
  requirePermission,
16401
16541
  requireQueuedTurnForApi,
16542
+ requireResolvedAccessGrantAuthorization,
16402
16543
  requireRigChangeForApi,
16403
16544
  requireRigForApi,
16404
16545
  requireScheduledTaskForApi,