@opengeni/core 1.3.0 → 1.4.2
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/application/modal-native-live-declaration-join.d.ts +37 -0
- package/dist/application/modal-native-original-configuration.d.ts +79 -0
- package/dist/domain/capabilities.d.ts +1 -0
- package/dist/domain/connector-tool-permissions.d.ts +1 -1
- package/dist/domain/realtime-voice-billing.d.ts +95 -0
- package/dist/domain/scheduled-task-access.d.ts +2 -2
- package/dist/domain/scheduled-tasks.d.ts +2 -2
- package/dist/domain/session-connection-accounts.d.ts +1 -1
- package/dist/domain/session-start-timing.d.ts +1 -1
- package/dist/domain/session-tool-policy.d.ts +11 -7
- package/dist/domain/voice-input-billing.d.ts +77 -0
- package/dist/domain/web-search-billing.d.ts +45 -0
- package/dist/goal-admission.d.ts +24 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +1065 -365
- package/dist/index.js.map +1 -1
- package/dist/transcription.d.ts +119 -2
- package/package.json +12 -12
- package/src/application/modal-native-live-declaration-join.ts +196 -0
- package/src/application/modal-native-original-configuration.ts +261 -0
- package/src/default-session-model.ts +58 -88
- package/src/domain/capabilities.ts +44 -2
- package/src/domain/connector-tool-permissions.ts +24 -8
- package/src/domain/realtime-voice-billing.ts +400 -0
- package/src/domain/scheduled-task-access.ts +2 -1
- package/src/domain/scheduled-tasks.ts +3 -2
- package/src/domain/session-connection-accounts.ts +7 -3
- package/src/domain/session-start-timing.ts +5 -0
- package/src/domain/session-tool-policy.ts +20 -4
- package/src/domain/sessions.ts +145 -98
- package/src/domain/voice-input-billing.ts +315 -0
- package/src/domain/web-search-billing.ts +154 -0
- package/src/index.ts +3 -0
- package/src/model-catalog.ts +32 -38
- package/src/sandbox/routing.ts +59 -25
- package/src/transcription.ts +122 -3
|
@@ -22,15 +22,11 @@ import {
|
|
|
22
22
|
getWorkspace,
|
|
23
23
|
getWorkspaceConnectionModelRestrictions,
|
|
24
24
|
getWorkspaceModelPolicy,
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
listWorkspaceGatewayCustomModels,
|
|
29
|
-
listWorkspaceOpenRouterCustomModels,
|
|
30
|
-
organizationModelProviderConnectionActiveForWorkspace,
|
|
25
|
+
getOrganizationModelProviderCatalogForWorkspace,
|
|
26
|
+
listConnectionsMetadata,
|
|
27
|
+
listWorkspaceProviderCustomModelsByKind,
|
|
31
28
|
workspaceCodexSubscriptionActive,
|
|
32
|
-
|
|
33
|
-
workspaceVercelAiGatewayConnectionActive,
|
|
29
|
+
workspaceProviderApiKeyConnectionMetadataFromConnections,
|
|
34
30
|
getBillingBalance,
|
|
35
31
|
spendableCreditMicros,
|
|
36
32
|
workspaceXaiSubscriptionActive,
|
|
@@ -41,6 +37,7 @@ import {
|
|
|
41
37
|
workspaceClaudeSubscriptionActiveForAuthority,
|
|
42
38
|
type ConnectionModelRestrictions,
|
|
43
39
|
type Database,
|
|
40
|
+
type WorkspaceCustomModelProviderKind,
|
|
44
41
|
} from "@opengeni/db";
|
|
45
42
|
import {
|
|
46
43
|
isWorkspaceModelAdmissible,
|
|
@@ -422,82 +419,43 @@ export async function loadWorkspaceModelSelectionInput(
|
|
|
422
419
|
options: { observeAvailability?: boolean } = {},
|
|
423
420
|
): Promise<WorkspaceModelSelectionInput> {
|
|
424
421
|
const { accountId, workspaceId } = context;
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
const
|
|
428
|
-
|
|
422
|
+
// The exact provider set the catalog route reads. A disabled Claude
|
|
423
|
+
// subscription contributes no catalog entry, so its rows are not read.
|
|
424
|
+
const providerKinds: WorkspaceCustomModelProviderKind[] = [
|
|
425
|
+
"vercel_gateway",
|
|
426
|
+
"openrouter",
|
|
427
|
+
...CLAUDE_CONNECTION_KINDS.filter(
|
|
428
|
+
(kind) => kind !== "claude_subscription" || settings.claudeSubscriptionEnabled,
|
|
429
|
+
),
|
|
430
|
+
];
|
|
429
431
|
const inputRead = (async () =>
|
|
430
432
|
Promise.all([
|
|
431
|
-
|
|
432
|
-
|
|
433
|
+
connectionRestrictionsAndXaiReadiness(db, settings, context),
|
|
434
|
+
loadWorkspaceClaudeSubscriptionReadiness(db, settings, context),
|
|
433
435
|
getWorkspaceModelPolicy(db, workspaceId),
|
|
434
436
|
workspaceCodexSubscriptionActive(db, settings, workspaceId),
|
|
435
437
|
options.observeAvailability === false
|
|
436
438
|
? Promise.resolve({})
|
|
437
439
|
: loadWorkspaceCodexModelAvailability(db, settings, workspaceId),
|
|
438
|
-
workspaceVercelAiGatewayConnectionActive(db, workspaceId),
|
|
439
|
-
listWorkspaceGatewayCustomModels(db, { accountId, workspaceId }),
|
|
440
|
-
workspaceOpenRouterConnectionActive(db, workspaceId),
|
|
441
|
-
listWorkspaceOpenRouterCustomModels(db, { accountId, workspaceId }),
|
|
442
|
-
organizationModelProviderConnectionActiveForWorkspace(db, {
|
|
443
|
-
accountId,
|
|
444
|
-
workspaceId,
|
|
445
|
-
providerKind: "vercel_gateway",
|
|
446
|
-
}),
|
|
447
|
-
organizationModelProviderConnectionActiveForWorkspace(db, {
|
|
448
|
-
accountId,
|
|
449
|
-
workspaceId,
|
|
450
|
-
providerKind: "openrouter",
|
|
451
|
-
}),
|
|
452
|
-
listOrganizationModelProviderCustomModelsForWorkspace(db, {
|
|
453
|
-
accountId,
|
|
454
|
-
workspaceId,
|
|
455
|
-
providerKind: "vercel_gateway",
|
|
456
|
-
}),
|
|
457
|
-
listOrganizationModelProviderCustomModelsForWorkspace(db, {
|
|
458
|
-
accountId,
|
|
459
|
-
workspaceId,
|
|
460
|
-
providerKind: "openrouter",
|
|
461
|
-
}),
|
|
462
440
|
]))();
|
|
463
|
-
const claudeConnections: ClaudeConnectionCatalog = {};
|
|
464
|
-
const workspaceClaudeConnections: ClaudeConnectionCatalog = {};
|
|
465
441
|
// Transaction handles share one backend and LOCAL scope. Preserve the old
|
|
466
442
|
// batch ordering there rather than introduce concurrent nested savepoints.
|
|
467
443
|
if (typeof (db as Database & { rollback?: unknown }).rollback === "function") await inputRead;
|
|
468
|
-
//
|
|
469
|
-
//
|
|
470
|
-
//
|
|
444
|
+
// One scoped read per catalog family (workspace connection metadata,
|
|
445
|
+
// workspace custom models, organization provider readiness + models) instead
|
|
446
|
+
// of one scoped transaction per provider kind. The batched readers keep each
|
|
447
|
+
// provider's filters, bound and overflow error, exactly as the workspace
|
|
448
|
+
// model catalog route reads them.
|
|
471
449
|
const catalogRead = (async () =>
|
|
472
|
-
Promise.all(
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
accountId,
|
|
480
|
-
workspaceId,
|
|
481
|
-
providerKind: kind,
|
|
482
|
-
}),
|
|
483
|
-
listOrganizationModelProviderCustomModelsForWorkspace(db, {
|
|
484
|
-
accountId,
|
|
485
|
-
workspaceId,
|
|
486
|
-
providerKind: kind,
|
|
487
|
-
}),
|
|
488
|
-
kind === "claude_subscription"
|
|
489
|
-
? Promise.resolve(null)
|
|
490
|
-
: getWorkspaceProviderApiKeyConnectionMetadata(db, workspaceId, kind),
|
|
491
|
-
listWorkspaceProviderCustomModels(db, { accountId, workspaceId, providerKind: kind }),
|
|
492
|
-
]);
|
|
493
|
-
claudeConnections[kind] = { active, models };
|
|
494
|
-
workspaceClaudeConnections[kind] = {
|
|
495
|
-
active:
|
|
496
|
-
kind === "claude_subscription" ? (await claudePoolRead).workspace : metadata !== null,
|
|
497
|
-
models: workspaceModels,
|
|
498
|
-
};
|
|
450
|
+
Promise.all([
|
|
451
|
+
listConnectionsMetadata(db, workspaceId, null),
|
|
452
|
+
listWorkspaceProviderCustomModelsByKind(db, { accountId, workspaceId, providerKinds }),
|
|
453
|
+
getOrganizationModelProviderCatalogForWorkspace(db, {
|
|
454
|
+
accountId,
|
|
455
|
+
workspaceId,
|
|
456
|
+
providerKinds,
|
|
499
457
|
}),
|
|
500
|
-
))();
|
|
458
|
+
]))();
|
|
501
459
|
// Observe both batches before admission resumes or fails. Retain the previous
|
|
502
460
|
// input-batch error precedence even if the independent catalog fails first.
|
|
503
461
|
const [inputResult, catalogResult] = await Promise.allSettled([inputRead, catalogRead]);
|
|
@@ -505,19 +463,31 @@ export async function loadWorkspaceModelSelectionInput(
|
|
|
505
463
|
if (catalogResult.status === "rejected") throw catalogResult.reason;
|
|
506
464
|
const [
|
|
507
465
|
{ restrictions: connectionModelRestrictions, xaiSubscriptionActive },
|
|
508
|
-
|
|
466
|
+
claudePool,
|
|
509
467
|
policy,
|
|
510
468
|
codexSubscriptionActive,
|
|
511
469
|
observations,
|
|
512
|
-
workspaceGatewayConnectionActive,
|
|
513
|
-
workspaceGatewayCustomModels,
|
|
514
|
-
openRouterConnectionActive,
|
|
515
|
-
workspaceOpenRouterCustomModels,
|
|
516
|
-
organizationGatewayConnectionActive,
|
|
517
|
-
organizationOpenRouterConnectionActive,
|
|
518
|
-
organizationGatewayCustomModels,
|
|
519
|
-
organizationOpenRouterCustomModels,
|
|
520
470
|
] = inputResult.value;
|
|
471
|
+
const [workspaceConnections, workspaceCustomModels, organizationProviders] = catalogResult.value;
|
|
472
|
+
const workspaceConnectionActive = (kind: WorkspaceCustomModelProviderKind) =>
|
|
473
|
+
workspaceProviderApiKeyConnectionMetadataFromConnections(workspaceConnections, kind) !== null;
|
|
474
|
+
const claudeConnections: ClaudeConnectionCatalog = {};
|
|
475
|
+
const workspaceClaudeConnections: ClaudeConnectionCatalog = {};
|
|
476
|
+
for (const kind of CLAUDE_CONNECTION_KINDS) {
|
|
477
|
+
if (kind === "claude_subscription" && !settings.claudeSubscriptionEnabled) continue;
|
|
478
|
+
claudeConnections[kind] = {
|
|
479
|
+
active:
|
|
480
|
+
kind === "claude_subscription"
|
|
481
|
+
? claudePool.organization
|
|
482
|
+
: organizationProviders[kind].active,
|
|
483
|
+
models: organizationProviders[kind].models,
|
|
484
|
+
};
|
|
485
|
+
workspaceClaudeConnections[kind] = {
|
|
486
|
+
active:
|
|
487
|
+
kind === "claude_subscription" ? claudePool.workspace : workspaceConnectionActive(kind),
|
|
488
|
+
models: workspaceCustomModels[kind],
|
|
489
|
+
};
|
|
490
|
+
}
|
|
521
491
|
return {
|
|
522
492
|
claudeConnections,
|
|
523
493
|
workspaceClaudeConnections,
|
|
@@ -527,14 +497,14 @@ export async function loadWorkspaceModelSelectionInput(
|
|
|
527
497
|
codexSubscriptionActive,
|
|
528
498
|
observations,
|
|
529
499
|
xaiSubscriptionActive,
|
|
530
|
-
workspaceGatewayConnectionActive,
|
|
531
|
-
workspaceGatewayCustomModels,
|
|
532
|
-
workspaceOpenRouterConnectionActive:
|
|
533
|
-
workspaceOpenRouterCustomModels,
|
|
534
|
-
organizationGatewayConnectionActive,
|
|
535
|
-
organizationOpenRouterConnectionActive,
|
|
536
|
-
organizationGatewayCustomModels,
|
|
537
|
-
organizationOpenRouterCustomModels,
|
|
500
|
+
workspaceGatewayConnectionActive: workspaceConnectionActive("vercel_gateway"),
|
|
501
|
+
workspaceGatewayCustomModels: workspaceCustomModels.vercel_gateway,
|
|
502
|
+
workspaceOpenRouterConnectionActive: workspaceConnectionActive("openrouter"),
|
|
503
|
+
workspaceOpenRouterCustomModels: workspaceCustomModels.openrouter,
|
|
504
|
+
organizationGatewayConnectionActive: organizationProviders.vercel_gateway.active,
|
|
505
|
+
organizationOpenRouterConnectionActive: organizationProviders.openrouter.active,
|
|
506
|
+
organizationGatewayCustomModels: organizationProviders.vercel_gateway.models,
|
|
507
|
+
organizationOpenRouterCustomModels: organizationProviders.openrouter.models,
|
|
538
508
|
};
|
|
539
509
|
}
|
|
540
510
|
|
|
@@ -3,7 +3,12 @@ import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/
|
|
|
3
3
|
import type { Transport } from "@modelcontextprotocol/sdk/shared/transport.js";
|
|
4
4
|
import { isDeepStrictEqual } from "node:util";
|
|
5
5
|
import { withLockedCapabilityInstallation } from "@opengeni/db/capability-reconciliation";
|
|
6
|
-
import {
|
|
6
|
+
import {
|
|
7
|
+
environmentsEncryptionKeyBytes,
|
|
8
|
+
findIntegrationsOauthClient,
|
|
9
|
+
parseIntegrationsOauthClientsJson,
|
|
10
|
+
type Settings,
|
|
11
|
+
} from "@opengeni/config";
|
|
7
12
|
import { pinnedFetch } from "@opengeni/network";
|
|
8
13
|
import {
|
|
9
14
|
CapabilityCatalogItem,
|
|
@@ -102,6 +107,7 @@ export async function buildCapabilityCatalog(input: {
|
|
|
102
107
|
listEnabledMcpCapabilityServers(input.db, input.workspaceId),
|
|
103
108
|
]);
|
|
104
109
|
const runnableCapabilityIds = new Set(runnableMcpServers.map((server) => server.capabilityId));
|
|
110
|
+
const operatorOAuthClientConfigured = operatorOAuthClientResolver(input.settings);
|
|
105
111
|
const catalogInstallations = capabilityInstallations.filter(
|
|
106
112
|
(installation) => installation.kind === "mcp",
|
|
107
113
|
);
|
|
@@ -164,7 +170,9 @@ export async function buildCapabilityCatalog(input: {
|
|
|
164
170
|
},
|
|
165
171
|
}
|
|
166
172
|
: projected;
|
|
167
|
-
return applyCapabilityLifecycle(
|
|
173
|
+
return applyCapabilityLifecycle(
|
|
174
|
+
applyOperatorOAuthClientRequirement(runtimeProjected, operatorOAuthClientConfigured),
|
|
175
|
+
);
|
|
168
176
|
})
|
|
169
177
|
.sort(compareCatalogItems);
|
|
170
178
|
return {
|
|
@@ -173,6 +181,40 @@ export async function buildCapabilityCatalog(input: {
|
|
|
173
181
|
};
|
|
174
182
|
}
|
|
175
183
|
|
|
184
|
+
/**
|
|
185
|
+
* `metadata.oauthClientRequirement` is stamped by the catalog import from
|
|
186
|
+
* `data/catalog/oauth-client-requirements.json` for providers that refuse
|
|
187
|
+
* OAuth self-registration. Whether the row can connect here is a deployment
|
|
188
|
+
* fact, so the catalog projects it rather than every surface guessing. The
|
|
189
|
+
* matcher is the one the OAuth start uses to find an operator client.
|
|
190
|
+
*/
|
|
191
|
+
function operatorOAuthClientResolver(settings: Settings): (issuer: string) => boolean {
|
|
192
|
+
let configured: ReturnType<typeof parseIntegrationsOauthClientsJson> = {};
|
|
193
|
+
try {
|
|
194
|
+
configured = parseIntegrationsOauthClientsJson(settings.integrationsOauthClientsJson);
|
|
195
|
+
} catch {
|
|
196
|
+
// Startup validates this setting; a malformed value can configure nothing.
|
|
197
|
+
}
|
|
198
|
+
return (issuer) => findIntegrationsOauthClient(configured, [issuer]) !== null;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
export function applyOperatorOAuthClientRequirement(
|
|
202
|
+
item: CapabilityCatalogItem,
|
|
203
|
+
configured: (issuer: string) => boolean,
|
|
204
|
+
): CapabilityCatalogItem {
|
|
205
|
+
if (item.kind !== "mcp") return item;
|
|
206
|
+
const requirement = item.metadata.oauthClientRequirement;
|
|
207
|
+
const issuer =
|
|
208
|
+
requirement && typeof requirement === "object" && !Array.isArray(requirement)
|
|
209
|
+
? (requirement as Record<string, unknown>).issuer
|
|
210
|
+
: undefined;
|
|
211
|
+
if (typeof issuer !== "string" || !issuer) return item;
|
|
212
|
+
return {
|
|
213
|
+
...item,
|
|
214
|
+
runtime: { ...item.runtime, operatorOAuthClient: { configured: configured(issuer) } },
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
|
|
176
218
|
export async function createCatalogItem(input: {
|
|
177
219
|
db: Database;
|
|
178
220
|
accountId: string;
|
|
@@ -23,7 +23,6 @@ import type {
|
|
|
23
23
|
import {
|
|
24
24
|
buildConnectionTokenResolver,
|
|
25
25
|
getConnectionMetadata,
|
|
26
|
-
listConnectionsMetadata,
|
|
27
26
|
listEnabledMcpCapabilityServers,
|
|
28
27
|
listConnectorToolPermissionPolicies,
|
|
29
28
|
listChangedConnectorToolPermissions,
|
|
@@ -37,11 +36,16 @@ import {
|
|
|
37
36
|
import { HTTPException } from "hono/http-exception";
|
|
38
37
|
import {
|
|
39
38
|
GMAIL_REST_MCP_TOOLS,
|
|
39
|
+
gmailToolAvailableOnDeployment,
|
|
40
40
|
gmailToolSupportsScopes,
|
|
41
41
|
isOfficialGmailMcpConfig,
|
|
42
42
|
isOfficialSlackMcpConfig,
|
|
43
43
|
} from "@opengeni/runtime";
|
|
44
44
|
import { hasPermission } from "../access";
|
|
45
|
+
import {
|
|
46
|
+
personalConnectionDelegationSourceForGrant,
|
|
47
|
+
visibleMcpAccountConnections,
|
|
48
|
+
} from "./personal-connection-delegations";
|
|
45
49
|
import {
|
|
46
50
|
buildCapabilityCatalog,
|
|
47
51
|
settingsWithMcpCapabilityServers,
|
|
@@ -195,12 +199,14 @@ async function resolveTarget(input: Input) {
|
|
|
195
199
|
throw new HTTPException(403, {
|
|
196
200
|
message: "Only the authenticated connection owner may manage personal tool permissions",
|
|
197
201
|
});
|
|
202
|
+
const source = personalConnectionDelegationSourceForGrant(input.grant);
|
|
198
203
|
const visible = ref
|
|
199
|
-
? await
|
|
200
|
-
input.
|
|
201
|
-
input.workspaceId,
|
|
202
|
-
|
|
203
|
-
|
|
204
|
+
? await visibleMcpAccountConnections(input.db, {
|
|
205
|
+
accountId: input.grant.accountId,
|
|
206
|
+
workspaceId: input.workspaceId,
|
|
207
|
+
source:
|
|
208
|
+
input.personalOwnerVerified && source.kind === "subject" ? source : { kind: "none" },
|
|
209
|
+
})
|
|
204
210
|
: [];
|
|
205
211
|
const bindings = mcpAccountBindingsFromVisibleConnections({
|
|
206
212
|
accountId: input.grant.accountId,
|
|
@@ -256,9 +262,16 @@ async function resolveTarget(input: Input) {
|
|
|
256
262
|
export function reviewedConnectorToolCatalog(
|
|
257
263
|
server: Pick<Settings["mcpServers"][number], "url" | "connectionRef" | "allowedTools">,
|
|
258
264
|
grantedScopes: readonly string[],
|
|
265
|
+
deployment: Pick<Settings, "gmailWatchTopicName">,
|
|
259
266
|
): ListedTool[] | null {
|
|
260
267
|
const tools = isOfficialGmailMcpConfig(server.url, server.connectionRef)
|
|
261
|
-
? GMAIL_REST_MCP_TOOLS.filter(
|
|
268
|
+
? GMAIL_REST_MCP_TOOLS.filter(
|
|
269
|
+
(tool) =>
|
|
270
|
+
gmailToolSupportsScopes(tool.name, grantedScopes) &&
|
|
271
|
+
gmailToolAvailableOnDeployment(tool.name, {
|
|
272
|
+
watchTopicName: deployment.gmailWatchTopicName,
|
|
273
|
+
}),
|
|
274
|
+
)
|
|
262
275
|
: isOfficialSlackMcpConfig(server.url, server.connectionRef)
|
|
263
276
|
? slackRestMcpToolsForScopes(grantedScopes)
|
|
264
277
|
: null;
|
|
@@ -275,6 +288,7 @@ async function listTools(
|
|
|
275
288
|
const reviewed = reviewedConnectorToolCatalog(
|
|
276
289
|
target.server,
|
|
277
290
|
target.connection?.grantedScopes ?? [],
|
|
291
|
+
input.settings,
|
|
278
292
|
);
|
|
279
293
|
if (reviewed !== null) return reviewed;
|
|
280
294
|
let headers = { ...target.server.headers };
|
|
@@ -283,7 +297,9 @@ async function listTools(
|
|
|
283
297
|
input.db,
|
|
284
298
|
input.settings,
|
|
285
299
|
)({
|
|
286
|
-
|
|
300
|
+
// The selected account's credentials remain in its origin workspace;
|
|
301
|
+
// approval preferences remain scoped to the workspace being configured.
|
|
302
|
+
workspaceId: target.connection?.workspaceId ?? input.workspaceId,
|
|
287
303
|
...(target.connection?.subjectId ? { subjectId: input.grant.subjectId } : {}),
|
|
288
304
|
serverId: target.server.id,
|
|
289
305
|
toolName: "tools/list",
|