@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
package/dist/index.js
CHANGED
|
@@ -837,6 +837,7 @@ function expandApiIntegrationAccountRoutes(input) {
|
|
|
837
837
|
}
|
|
838
838
|
|
|
839
839
|
// src/domain/session-connection-accounts.ts
|
|
840
|
+
import { allowedFirstPartyMcpToolsForSession as allowedFirstPartyMcpToolsForSession4 } from "@opengeni/config";
|
|
840
841
|
import { requireSession as requireSession3, requireWorkspace as requireWorkspace6 } from "@opengeni/db";
|
|
841
842
|
|
|
842
843
|
// src/domain/capabilities.ts
|
|
@@ -844,7 +845,11 @@ import { Client } from "@modelcontextprotocol/sdk/client/index.js";
|
|
|
844
845
|
import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
|
|
845
846
|
import { isDeepStrictEqual } from "util";
|
|
846
847
|
import { withLockedCapabilityInstallation } from "@opengeni/db/capability-reconciliation";
|
|
847
|
-
import {
|
|
848
|
+
import {
|
|
849
|
+
environmentsEncryptionKeyBytes,
|
|
850
|
+
findIntegrationsOauthClient,
|
|
851
|
+
parseIntegrationsOauthClientsJson
|
|
852
|
+
} from "@opengeni/config";
|
|
848
853
|
import { pinnedFetch } from "@opengeni/network";
|
|
849
854
|
import {
|
|
850
855
|
CapabilityCatalogItem,
|
|
@@ -2026,6 +2031,7 @@ async function buildCapabilityCatalog(input) {
|
|
|
2026
2031
|
listEnabledMcpCapabilityServers(input.db, input.workspaceId)
|
|
2027
2032
|
]);
|
|
2028
2033
|
const runnableCapabilityIds = new Set(runnableMcpServers.map((server) => server.capabilityId));
|
|
2034
|
+
const operatorOAuthClientConfigured = operatorOAuthClientResolver(input.settings);
|
|
2029
2035
|
const catalogInstallations = capabilityInstallations.filter(
|
|
2030
2036
|
(installation) => installation.kind === "mcp"
|
|
2031
2037
|
);
|
|
@@ -2063,13 +2069,33 @@ async function buildCapabilityCatalog(input) {
|
|
|
2063
2069
|
notes: "This installed connection is unavailable. Review its connection settings."
|
|
2064
2070
|
}
|
|
2065
2071
|
} : projected;
|
|
2066
|
-
return applyCapabilityLifecycle(
|
|
2072
|
+
return applyCapabilityLifecycle(
|
|
2073
|
+
applyOperatorOAuthClientRequirement(runtimeProjected, operatorOAuthClientConfigured)
|
|
2074
|
+
);
|
|
2067
2075
|
}).sort(compareCatalogItems);
|
|
2068
2076
|
return {
|
|
2069
2077
|
items,
|
|
2070
2078
|
installations: catalogInstallations
|
|
2071
2079
|
};
|
|
2072
2080
|
}
|
|
2081
|
+
function operatorOAuthClientResolver(settings) {
|
|
2082
|
+
let configured = {};
|
|
2083
|
+
try {
|
|
2084
|
+
configured = parseIntegrationsOauthClientsJson(settings.integrationsOauthClientsJson);
|
|
2085
|
+
} catch {
|
|
2086
|
+
}
|
|
2087
|
+
return (issuer) => findIntegrationsOauthClient(configured, [issuer]) !== null;
|
|
2088
|
+
}
|
|
2089
|
+
function applyOperatorOAuthClientRequirement(item, configured) {
|
|
2090
|
+
if (item.kind !== "mcp") return item;
|
|
2091
|
+
const requirement = item.metadata.oauthClientRequirement;
|
|
2092
|
+
const issuer = requirement && typeof requirement === "object" && !Array.isArray(requirement) ? requirement.issuer : void 0;
|
|
2093
|
+
if (typeof issuer !== "string" || !issuer) return item;
|
|
2094
|
+
return {
|
|
2095
|
+
...item,
|
|
2096
|
+
runtime: { ...item.runtime, operatorOAuthClient: { configured: configured(issuer) } }
|
|
2097
|
+
};
|
|
2098
|
+
}
|
|
2073
2099
|
async function createCatalogItem(input) {
|
|
2074
2100
|
const id = input.payload.id?.trim() || generatedCapabilityId(input.payload);
|
|
2075
2101
|
if (id.startsWith("skill:")) {
|
|
@@ -4834,6 +4860,7 @@ import {
|
|
|
4834
4860
|
ORGANIZATION_GATEWAY_MODEL_ID_PREFIX as ORGANIZATION_GATEWAY_MODEL_ID_PREFIX2,
|
|
4835
4861
|
ORGANIZATION_OPENROUTER_MODEL_ID_PREFIX as ORGANIZATION_OPENROUTER_MODEL_ID_PREFIX2,
|
|
4836
4862
|
allowedFirstPartyMcpToolsForSession as allowedFirstPartyMcpToolsForSession3,
|
|
4863
|
+
deploymentUnavailableFirstPartyMcpTools,
|
|
4837
4864
|
resolveFirstPartyMcpToolPolicy,
|
|
4838
4865
|
policyProviderIdForModel,
|
|
4839
4866
|
resolveTurnExecutionPolicyV1,
|
|
@@ -5370,6 +5397,7 @@ import {
|
|
|
5370
5397
|
readActiveSandbox,
|
|
5371
5398
|
retainWorkspaceMutationProcess,
|
|
5372
5399
|
SandboxRetainedProcessPromotionFencedError,
|
|
5400
|
+
SandboxWorkspaceMutationOutputRejectedError,
|
|
5373
5401
|
retainedProcessSettlementIdentity,
|
|
5374
5402
|
settleRetainedProcess,
|
|
5375
5403
|
verifyDirectWorkspaceMutationSettlement,
|
|
@@ -5382,6 +5410,7 @@ import {
|
|
|
5382
5410
|
NatsControlRpc,
|
|
5383
5411
|
NatsOpStreamTransport,
|
|
5384
5412
|
RoutingSandboxSession,
|
|
5413
|
+
RoutingMutationOutputRejectedError,
|
|
5385
5414
|
resolveModalCheckpointProviderBindingForSession
|
|
5386
5415
|
} from "@opengeni/runtime/sandbox";
|
|
5387
5416
|
var retainWorkspaceProviderCommand = createProviderCommandRetainer(
|
|
@@ -5540,22 +5569,35 @@ function wrapChannelABoxWithRouting(services, ids, established) {
|
|
|
5540
5569
|
throw new Error(
|
|
5541
5570
|
"Outcome-unknown command settlement requires its exact retained invocation"
|
|
5542
5571
|
);
|
|
5543
|
-
|
|
5544
|
-
|
|
5545
|
-
|
|
5546
|
-
|
|
5547
|
-
|
|
5548
|
-
|
|
5549
|
-
|
|
5550
|
-
|
|
5551
|
-
|
|
5552
|
-
|
|
5553
|
-
|
|
5554
|
-
|
|
5555
|
-
|
|
5556
|
-
|
|
5557
|
-
|
|
5558
|
-
|
|
5572
|
+
try {
|
|
5573
|
+
await verifyDirectWorkspaceMutationSettlement(db, {
|
|
5574
|
+
accountId: ids.accountId,
|
|
5575
|
+
workspaceId: ids.workspaceId,
|
|
5576
|
+
sessionId: ids.sessionId,
|
|
5577
|
+
requestId: ids.directRequest.requestId,
|
|
5578
|
+
holderId: ids.directRequest.holderId,
|
|
5579
|
+
initiatorSubjectId: ids.resourceSubjectId,
|
|
5580
|
+
sandboxGroupId: homeLease.sandboxGroupId,
|
|
5581
|
+
expectedEpoch: backend.leaseEpoch,
|
|
5582
|
+
expectedInstanceId: backend.providerInstanceId,
|
|
5583
|
+
routeTargetId: exactAdmission.routeTargetId,
|
|
5584
|
+
routeEpoch: exactAdmission.routeEpoch,
|
|
5585
|
+
admission: exactAdmission,
|
|
5586
|
+
operation: op,
|
|
5587
|
+
outcome
|
|
5588
|
+
});
|
|
5589
|
+
} catch (error) {
|
|
5590
|
+
if (error instanceof SandboxWorkspaceMutationOutputRejectedError && error.matchesPhysicalSettlement({
|
|
5591
|
+
accountId: ids.accountId,
|
|
5592
|
+
workspaceId: ids.workspaceId,
|
|
5593
|
+
admission: exactAdmission,
|
|
5594
|
+
operation: op,
|
|
5595
|
+
outcome
|
|
5596
|
+
})) {
|
|
5597
|
+
throw new RoutingMutationOutputRejectedError(op, error.code, { cause: error });
|
|
5598
|
+
}
|
|
5599
|
+
throw error;
|
|
5600
|
+
}
|
|
5559
5601
|
} : void 0;
|
|
5560
5602
|
const beforeProcessMutation = homeLease ? async ({
|
|
5561
5603
|
op,
|
|
@@ -5578,15 +5620,28 @@ function wrapChannelABoxWithRouting(services, ids, established) {
|
|
|
5578
5620
|
if (!admission || typeof admission !== "object" || typeof admission.id !== "string" || typeof admission.workspaceGeneration !== "number") {
|
|
5579
5621
|
throw new Error("API retained-process mutation settlement lacked its exact admission");
|
|
5580
5622
|
}
|
|
5581
|
-
|
|
5582
|
-
|
|
5583
|
-
|
|
5584
|
-
|
|
5585
|
-
|
|
5586
|
-
|
|
5587
|
-
|
|
5588
|
-
|
|
5589
|
-
|
|
5623
|
+
try {
|
|
5624
|
+
await verifyRetainedProcessMutationSettlement(db, {
|
|
5625
|
+
accountId: ids.accountId,
|
|
5626
|
+
workspaceId: ids.workspaceId,
|
|
5627
|
+
sessionId: ids.sessionId,
|
|
5628
|
+
processId: process.id,
|
|
5629
|
+
admission,
|
|
5630
|
+
operation: op,
|
|
5631
|
+
outcome
|
|
5632
|
+
});
|
|
5633
|
+
} catch (error) {
|
|
5634
|
+
if (error instanceof SandboxWorkspaceMutationOutputRejectedError && error.matchesPhysicalSettlement({
|
|
5635
|
+
accountId: ids.accountId,
|
|
5636
|
+
workspaceId: ids.workspaceId,
|
|
5637
|
+
admission,
|
|
5638
|
+
operation: op,
|
|
5639
|
+
outcome
|
|
5640
|
+
})) {
|
|
5641
|
+
throw new RoutingMutationOutputRejectedError(op, error.code, { cause: error });
|
|
5642
|
+
}
|
|
5643
|
+
throw error;
|
|
5644
|
+
}
|
|
5590
5645
|
} : void 0;
|
|
5591
5646
|
const settleProcess = homeLease ? async ({
|
|
5592
5647
|
backend,
|
|
@@ -6677,15 +6732,13 @@ import {
|
|
|
6677
6732
|
import {
|
|
6678
6733
|
listConnectionsMetadata as listConnectionsMetadata3,
|
|
6679
6734
|
getDeploymentModelCatalog,
|
|
6680
|
-
|
|
6735
|
+
listWorkspaceProviderCustomModelsByKind,
|
|
6681
6736
|
getWorkspaceProviderCustomModelForExecution,
|
|
6682
6737
|
lockActiveWorkspaceProviderCustomModelForAdmission,
|
|
6683
6738
|
getWorkspaceGatewayCustomModelForExecution,
|
|
6684
6739
|
getWorkspaceOpenRouterCustomModelForExecution,
|
|
6685
6740
|
getOrganizationModelProviderCustomModelForExecution,
|
|
6686
|
-
|
|
6687
|
-
listWorkspaceOpenRouterCustomModels,
|
|
6688
|
-
listOrganizationModelProviderCustomModelsForWorkspace,
|
|
6741
|
+
getOrganizationModelProviderCatalogForWorkspace,
|
|
6689
6742
|
lockActiveOrganizationModelProviderCustomModelForAdmission
|
|
6690
6743
|
} from "@opengeni/db";
|
|
6691
6744
|
function isWorkspaceGatewayCustomModelId(settings, modelId) {
|
|
@@ -6800,26 +6853,28 @@ async function resolveWorkspaceCatalogSettings(db, envSettings, input) {
|
|
|
6800
6853
|
const retainedOrganizationOpenRouterUpstreamModelIds = retainedProductModelIds.flatMap(
|
|
6801
6854
|
(productModelId) => productModelId?.startsWith(ORGANIZATION_OPENROUTER_MODEL_ID_PREFIX) ? [productModelId.slice(ORGANIZATION_OPENROUTER_MODEL_ID_PREFIX.length)] : []
|
|
6802
6855
|
);
|
|
6856
|
+
const workspaceCustomModelKinds = [
|
|
6857
|
+
"vercel_gateway",
|
|
6858
|
+
"openrouter",
|
|
6859
|
+
...supportsOrganizationProviderReads ? CLAUDE_CONNECTION_KINDS.filter(
|
|
6860
|
+
(kind) => kind !== "claude_subscription" || envSettings.claudeSubscriptionEnabled
|
|
6861
|
+
) : []
|
|
6862
|
+
];
|
|
6803
6863
|
const [
|
|
6804
6864
|
resolved,
|
|
6805
|
-
|
|
6806
|
-
activeOpenRouterCustomModels,
|
|
6865
|
+
workspaceCustomModels,
|
|
6807
6866
|
retainedGatewayCustomModels,
|
|
6808
6867
|
retainedOpenRouterCustomModels,
|
|
6809
|
-
|
|
6810
|
-
organizationOpenRouterCustomModels,
|
|
6868
|
+
organizationProviders,
|
|
6811
6869
|
retainedOrganizationGatewayCustomModels,
|
|
6812
6870
|
retainedOrganizationOpenRouterCustomModels,
|
|
6813
6871
|
directConnections
|
|
6814
6872
|
] = await Promise.all([
|
|
6815
6873
|
resolveCatalogSettings(db, envSettings),
|
|
6816
|
-
|
|
6817
|
-
accountId: input.accountId,
|
|
6818
|
-
workspaceId: input.workspaceId
|
|
6819
|
-
}),
|
|
6820
|
-
listWorkspaceOpenRouterCustomModels(db, {
|
|
6874
|
+
listWorkspaceProviderCustomModelsByKind(db, {
|
|
6821
6875
|
accountId: input.accountId,
|
|
6822
|
-
workspaceId: input.workspaceId
|
|
6876
|
+
workspaceId: input.workspaceId,
|
|
6877
|
+
providerKinds: workspaceCustomModelKinds
|
|
6823
6878
|
}),
|
|
6824
6879
|
Promise.all(
|
|
6825
6880
|
[...new Set(retainedGatewayUpstreamModelIds)].map(
|
|
@@ -6839,16 +6894,11 @@ async function resolveWorkspaceCatalogSettings(db, envSettings, input) {
|
|
|
6839
6894
|
})
|
|
6840
6895
|
)
|
|
6841
6896
|
),
|
|
6842
|
-
supportsOrganizationProviderReads ?
|
|
6843
|
-
accountId: input.accountId,
|
|
6844
|
-
workspaceId: input.workspaceId,
|
|
6845
|
-
providerKind: "vercel_gateway"
|
|
6846
|
-
}) : Promise.resolve([]),
|
|
6847
|
-
supportsOrganizationProviderReads ? listOrganizationModelProviderCustomModelsForWorkspace(db, {
|
|
6897
|
+
supportsOrganizationProviderReads ? getOrganizationModelProviderCatalogForWorkspace(db, {
|
|
6848
6898
|
accountId: input.accountId,
|
|
6849
6899
|
workspaceId: input.workspaceId,
|
|
6850
|
-
|
|
6851
|
-
}) :
|
|
6900
|
+
providerKinds: ["vercel_gateway", "openrouter", ...CLAUDE_CONNECTION_KINDS]
|
|
6901
|
+
}) : null,
|
|
6852
6902
|
supportsOrganizationProviderReads ? Promise.all(
|
|
6853
6903
|
[...new Set(retainedOrganizationGatewayUpstreamModelIds)].map(
|
|
6854
6904
|
async (upstreamModelId) => await getOrganizationModelProviderCustomModelForExecution(db, {
|
|
@@ -6883,29 +6933,25 @@ async function resolveWorkspaceCatalogSettings(db, envSettings, input) {
|
|
|
6883
6933
|
return customModels;
|
|
6884
6934
|
};
|
|
6885
6935
|
const gatewayCustomModels = includeRetainedModels(
|
|
6886
|
-
|
|
6936
|
+
workspaceCustomModels.vercel_gateway,
|
|
6887
6937
|
retainedGatewayCustomModels
|
|
6888
6938
|
);
|
|
6889
6939
|
const openRouterCustomModels = includeRetainedModels(
|
|
6890
|
-
|
|
6940
|
+
workspaceCustomModels.openrouter,
|
|
6891
6941
|
retainedOpenRouterCustomModels
|
|
6892
6942
|
);
|
|
6893
6943
|
const organizationGatewayModels = includeRetainedModels(
|
|
6894
|
-
|
|
6944
|
+
organizationProviders?.vercel_gateway.models ?? [],
|
|
6895
6945
|
retainedOrganizationGatewayCustomModels
|
|
6896
6946
|
);
|
|
6897
6947
|
const organizationOpenRouterModels = includeRetainedModels(
|
|
6898
|
-
|
|
6948
|
+
organizationProviders?.openrouter.models ?? [],
|
|
6899
6949
|
retainedOrganizationOpenRouterCustomModels
|
|
6900
6950
|
);
|
|
6901
6951
|
const claudeConnections = {};
|
|
6902
|
-
if (
|
|
6952
|
+
if (organizationProviders)
|
|
6903
6953
|
for (const kind of CLAUDE_CONNECTION_KINDS) {
|
|
6904
|
-
const models =
|
|
6905
|
-
accountId: input.accountId,
|
|
6906
|
-
workspaceId: input.workspaceId,
|
|
6907
|
-
providerKind: kind
|
|
6908
|
-
});
|
|
6954
|
+
const models = [...organizationProviders[kind].models];
|
|
6909
6955
|
const prefix = claudeProviderId(kind) + "/";
|
|
6910
6956
|
for (const productId of retainedProductModelIds)
|
|
6911
6957
|
if (productId?.startsWith(prefix)) {
|
|
@@ -6923,11 +6969,7 @@ async function resolveWorkspaceCatalogSettings(db, envSettings, input) {
|
|
|
6923
6969
|
if (supportsOrganizationProviderReads)
|
|
6924
6970
|
for (const kind of CLAUDE_CONNECTION_KINDS) {
|
|
6925
6971
|
if (kind === "claude_subscription" && !resolved.settings.claudeSubscriptionEnabled) continue;
|
|
6926
|
-
const models =
|
|
6927
|
-
accountId: input.accountId,
|
|
6928
|
-
workspaceId: input.workspaceId,
|
|
6929
|
-
providerKind: kind
|
|
6930
|
-
});
|
|
6972
|
+
const models = [...workspaceCustomModels[kind]];
|
|
6931
6973
|
const prefix = claudeProviderId(kind, "workspace") + "/";
|
|
6932
6974
|
for (const productId of retainedProductModelIds) {
|
|
6933
6975
|
if (!productId?.startsWith(prefix)) continue;
|
|
@@ -7237,15 +7279,11 @@ import {
|
|
|
7237
7279
|
getWorkspace as getWorkspace2,
|
|
7238
7280
|
getWorkspaceConnectionModelRestrictions,
|
|
7239
7281
|
getWorkspaceModelPolicy,
|
|
7240
|
-
|
|
7241
|
-
|
|
7242
|
-
|
|
7243
|
-
listWorkspaceGatewayCustomModels as listWorkspaceGatewayCustomModels2,
|
|
7244
|
-
listWorkspaceOpenRouterCustomModels as listWorkspaceOpenRouterCustomModels2,
|
|
7245
|
-
organizationModelProviderConnectionActiveForWorkspace,
|
|
7282
|
+
getOrganizationModelProviderCatalogForWorkspace as getOrganizationModelProviderCatalogForWorkspace2,
|
|
7283
|
+
listConnectionsMetadata as listConnectionsMetadata4,
|
|
7284
|
+
listWorkspaceProviderCustomModelsByKind as listWorkspaceProviderCustomModelsByKind2,
|
|
7246
7285
|
workspaceCodexSubscriptionActive,
|
|
7247
|
-
|
|
7248
|
-
workspaceVercelAiGatewayConnectionActive,
|
|
7286
|
+
workspaceProviderApiKeyConnectionMetadataFromConnections,
|
|
7249
7287
|
getBillingBalance,
|
|
7250
7288
|
spendableCreditMicros,
|
|
7251
7289
|
workspaceXaiSubscriptionActive,
|
|
@@ -7562,84 +7600,55 @@ async function loadWorkspaceClaudeSubscriptionReadiness(db, settings, context) {
|
|
|
7562
7600
|
}
|
|
7563
7601
|
async function loadWorkspaceModelSelectionInput(db, settings, context, options = {}) {
|
|
7564
7602
|
const { accountId, workspaceId } = context;
|
|
7565
|
-
const
|
|
7566
|
-
|
|
7603
|
+
const providerKinds = [
|
|
7604
|
+
"vercel_gateway",
|
|
7605
|
+
"openrouter",
|
|
7606
|
+
...CLAUDE_CONNECTION_KINDS2.filter(
|
|
7607
|
+
(kind) => kind !== "claude_subscription" || settings.claudeSubscriptionEnabled
|
|
7608
|
+
)
|
|
7609
|
+
];
|
|
7567
7610
|
const inputRead = (async () => Promise.all([
|
|
7568
|
-
|
|
7569
|
-
|
|
7611
|
+
connectionRestrictionsAndXaiReadiness(db, settings, context),
|
|
7612
|
+
loadWorkspaceClaudeSubscriptionReadiness(db, settings, context),
|
|
7570
7613
|
getWorkspaceModelPolicy(db, workspaceId),
|
|
7571
7614
|
workspaceCodexSubscriptionActive(db, settings, workspaceId),
|
|
7572
|
-
options.observeAvailability === false ? Promise.resolve({}) : loadWorkspaceCodexModelAvailability(db, settings, workspaceId)
|
|
7573
|
-
|
|
7574
|
-
|
|
7575
|
-
|
|
7576
|
-
|
|
7577
|
-
|
|
7578
|
-
|
|
7579
|
-
workspaceId,
|
|
7580
|
-
providerKind: "vercel_gateway"
|
|
7581
|
-
}),
|
|
7582
|
-
organizationModelProviderConnectionActiveForWorkspace(db, {
|
|
7583
|
-
accountId,
|
|
7584
|
-
workspaceId,
|
|
7585
|
-
providerKind: "openrouter"
|
|
7586
|
-
}),
|
|
7587
|
-
listOrganizationModelProviderCustomModelsForWorkspace2(db, {
|
|
7588
|
-
accountId,
|
|
7589
|
-
workspaceId,
|
|
7590
|
-
providerKind: "vercel_gateway"
|
|
7591
|
-
}),
|
|
7592
|
-
listOrganizationModelProviderCustomModelsForWorkspace2(db, {
|
|
7615
|
+
options.observeAvailability === false ? Promise.resolve({}) : loadWorkspaceCodexModelAvailability(db, settings, workspaceId)
|
|
7616
|
+
]))();
|
|
7617
|
+
if (typeof db.rollback === "function") await inputRead;
|
|
7618
|
+
const catalogRead = (async () => Promise.all([
|
|
7619
|
+
listConnectionsMetadata4(db, workspaceId, null),
|
|
7620
|
+
listWorkspaceProviderCustomModelsByKind2(db, { accountId, workspaceId, providerKinds }),
|
|
7621
|
+
getOrganizationModelProviderCatalogForWorkspace2(db, {
|
|
7593
7622
|
accountId,
|
|
7594
7623
|
workspaceId,
|
|
7595
|
-
|
|
7624
|
+
providerKinds
|
|
7596
7625
|
})
|
|
7597
7626
|
]))();
|
|
7598
|
-
const claudeConnections = {};
|
|
7599
|
-
const workspaceClaudeConnections = {};
|
|
7600
|
-
if (typeof db.rollback === "function") await inputRead;
|
|
7601
|
-
const catalogRead = (async () => Promise.all(
|
|
7602
|
-
CLAUDE_CONNECTION_KINDS2.map(async (kind) => {
|
|
7603
|
-
if (kind === "claude_subscription" && !settings.claudeSubscriptionEnabled) return;
|
|
7604
|
-
const [active, models, metadata, workspaceModels] = await Promise.all([
|
|
7605
|
-
kind === "claude_subscription" ? claudePoolRead.then((pool) => pool.organization) : organizationModelProviderConnectionActiveForWorkspace(db, {
|
|
7606
|
-
accountId,
|
|
7607
|
-
workspaceId,
|
|
7608
|
-
providerKind: kind
|
|
7609
|
-
}),
|
|
7610
|
-
listOrganizationModelProviderCustomModelsForWorkspace2(db, {
|
|
7611
|
-
accountId,
|
|
7612
|
-
workspaceId,
|
|
7613
|
-
providerKind: kind
|
|
7614
|
-
}),
|
|
7615
|
-
kind === "claude_subscription" ? Promise.resolve(null) : getWorkspaceProviderApiKeyConnectionMetadata(db, workspaceId, kind),
|
|
7616
|
-
listWorkspaceProviderCustomModels2(db, { accountId, workspaceId, providerKind: kind })
|
|
7617
|
-
]);
|
|
7618
|
-
claudeConnections[kind] = { active, models };
|
|
7619
|
-
workspaceClaudeConnections[kind] = {
|
|
7620
|
-
active: kind === "claude_subscription" ? (await claudePoolRead).workspace : metadata !== null,
|
|
7621
|
-
models: workspaceModels
|
|
7622
|
-
};
|
|
7623
|
-
})
|
|
7624
|
-
))();
|
|
7625
7627
|
const [inputResult, catalogResult] = await Promise.allSettled([inputRead, catalogRead]);
|
|
7626
7628
|
if (inputResult.status === "rejected") throw inputResult.reason;
|
|
7627
7629
|
if (catalogResult.status === "rejected") throw catalogResult.reason;
|
|
7628
7630
|
const [
|
|
7629
7631
|
{ restrictions: connectionModelRestrictions, xaiSubscriptionActive },
|
|
7630
|
-
|
|
7632
|
+
claudePool,
|
|
7631
7633
|
policy,
|
|
7632
7634
|
codexSubscriptionActive,
|
|
7633
|
-
observations
|
|
7634
|
-
workspaceGatewayConnectionActive,
|
|
7635
|
-
workspaceGatewayCustomModels,
|
|
7636
|
-
openRouterConnectionActive,
|
|
7637
|
-
workspaceOpenRouterCustomModels,
|
|
7638
|
-
organizationGatewayConnectionActive,
|
|
7639
|
-
organizationOpenRouterConnectionActive,
|
|
7640
|
-
organizationGatewayCustomModels,
|
|
7641
|
-
organizationOpenRouterCustomModels
|
|
7635
|
+
observations
|
|
7642
7636
|
] = inputResult.value;
|
|
7637
|
+
const [workspaceConnections, workspaceCustomModels, organizationProviders] = catalogResult.value;
|
|
7638
|
+
const workspaceConnectionActive = (kind) => workspaceProviderApiKeyConnectionMetadataFromConnections(workspaceConnections, kind) !== null;
|
|
7639
|
+
const claudeConnections = {};
|
|
7640
|
+
const workspaceClaudeConnections = {};
|
|
7641
|
+
for (const kind of CLAUDE_CONNECTION_KINDS2) {
|
|
7642
|
+
if (kind === "claude_subscription" && !settings.claudeSubscriptionEnabled) continue;
|
|
7643
|
+
claudeConnections[kind] = {
|
|
7644
|
+
active: kind === "claude_subscription" ? claudePool.organization : organizationProviders[kind].active,
|
|
7645
|
+
models: organizationProviders[kind].models
|
|
7646
|
+
};
|
|
7647
|
+
workspaceClaudeConnections[kind] = {
|
|
7648
|
+
active: kind === "claude_subscription" ? claudePool.workspace : workspaceConnectionActive(kind),
|
|
7649
|
+
models: workspaceCustomModels[kind]
|
|
7650
|
+
};
|
|
7651
|
+
}
|
|
7643
7652
|
return {
|
|
7644
7653
|
claudeConnections,
|
|
7645
7654
|
workspaceClaudeConnections,
|
|
@@ -7649,14 +7658,14 @@ async function loadWorkspaceModelSelectionInput(db, settings, context, options =
|
|
|
7649
7658
|
codexSubscriptionActive,
|
|
7650
7659
|
observations,
|
|
7651
7660
|
xaiSubscriptionActive,
|
|
7652
|
-
workspaceGatewayConnectionActive,
|
|
7653
|
-
workspaceGatewayCustomModels,
|
|
7654
|
-
workspaceOpenRouterConnectionActive:
|
|
7655
|
-
workspaceOpenRouterCustomModels,
|
|
7656
|
-
organizationGatewayConnectionActive,
|
|
7657
|
-
organizationOpenRouterConnectionActive,
|
|
7658
|
-
organizationGatewayCustomModels,
|
|
7659
|
-
organizationOpenRouterCustomModels
|
|
7661
|
+
workspaceGatewayConnectionActive: workspaceConnectionActive("vercel_gateway"),
|
|
7662
|
+
workspaceGatewayCustomModels: workspaceCustomModels.vercel_gateway,
|
|
7663
|
+
workspaceOpenRouterConnectionActive: workspaceConnectionActive("openrouter"),
|
|
7664
|
+
workspaceOpenRouterCustomModels: workspaceCustomModels.openrouter,
|
|
7665
|
+
organizationGatewayConnectionActive: organizationProviders.vercel_gateway.active,
|
|
7666
|
+
organizationOpenRouterConnectionActive: organizationProviders.openrouter.active,
|
|
7667
|
+
organizationGatewayCustomModels: organizationProviders.vercel_gateway.models,
|
|
7668
|
+
organizationOpenRouterCustomModels: organizationProviders.openrouter.models
|
|
7660
7669
|
};
|
|
7661
7670
|
}
|
|
7662
7671
|
async function resolveCallerWorkspaceModelSelections(db, settings, context, options = {}) {
|
|
@@ -7802,7 +7811,8 @@ import {
|
|
|
7802
7811
|
allowedFirstPartyMcpToolsForSession as allowedFirstPartyMcpToolsForSession2,
|
|
7803
7812
|
codeSearchDeploymentPolicy,
|
|
7804
7813
|
resolveModelProviderForTurn as resolveModelProviderForTurn2,
|
|
7805
|
-
resolveFirstPartyDelegationSecret as resolveFirstPartyDelegationSecret2
|
|
7814
|
+
resolveFirstPartyDelegationSecret as resolveFirstPartyDelegationSecret2,
|
|
7815
|
+
webSearchToolPlan
|
|
7806
7816
|
} from "@opengeni/config";
|
|
7807
7817
|
import {
|
|
7808
7818
|
AGENT_SKILL_MANAGE_TOOL_NAMES,
|
|
@@ -7938,12 +7948,12 @@ function resolveTurnToolPolicy(input) {
|
|
|
7938
7948
|
defaultMcpServerIds: scheduledIds ?? input.defaultMcpServerIds ?? []
|
|
7939
7949
|
});
|
|
7940
7950
|
}
|
|
7941
|
-
async function workspaceSessionToolPolicyContext(db, workspaceId, settings, subjectId) {
|
|
7951
|
+
async function workspaceSessionToolPolicyContext(db, workspaceId, settings, subjectId, workspaceRead) {
|
|
7942
7952
|
const [runtimeSettings, workspace] = await Promise.all([
|
|
7943
7953
|
settingsWithEnabledCapabilityMcpServers(db, workspaceId, settings, {
|
|
7944
7954
|
...subjectId ? { subjectId } : {}
|
|
7945
7955
|
}),
|
|
7946
|
-
requireWorkspace3(db, workspaceId)
|
|
7956
|
+
workspaceRead ?? requireWorkspace3(db, workspaceId)
|
|
7947
7957
|
]);
|
|
7948
7958
|
return {
|
|
7949
7959
|
workspaceServerIds: sortedIds(runtimeSettings.mcpServers.map((server) => server.id)),
|
|
@@ -7979,7 +7989,7 @@ function workspaceSessionToolPolicyDefaultServerIdsFor(runtimeMcpServers, worksp
|
|
|
7979
7989
|
...configured.inheritConnectedMcpServers ? availableDefaults.filter((id) => !["opengeni", "files", "docs"].includes(id)) : []
|
|
7980
7990
|
]);
|
|
7981
7991
|
}
|
|
7982
|
-
async function workspaceSessionEffectiveToolsContext(deps, workspaceId, subjectId, sessions) {
|
|
7992
|
+
async function workspaceSessionEffectiveToolsContext(deps, workspaceId, subjectId, sessions, workspaceRead) {
|
|
7983
7993
|
const configured = sessions.filter((session) => session.agent != null);
|
|
7984
7994
|
const baseline = {
|
|
7985
7995
|
settings: deps.settings,
|
|
@@ -7989,7 +7999,7 @@ async function workspaceSessionEffectiveToolsContext(deps, workspaceId, subjectI
|
|
|
7989
7999
|
};
|
|
7990
8000
|
if (configured.length === 0) return baseline;
|
|
7991
8001
|
const [workspace, catalog, descriptors, sandboxes, routerHistory] = await Promise.all([
|
|
7992
|
-
requireWorkspace3(deps.db, workspaceId),
|
|
8002
|
+
workspaceRead ?? requireWorkspace3(deps.db, workspaceId),
|
|
7993
8003
|
resolveWorkspaceCatalogSettings(deps.db, deps.settings, {
|
|
7994
8004
|
accountId: configured[0].accountId,
|
|
7995
8005
|
workspaceId,
|
|
@@ -8076,9 +8086,14 @@ function sessionEffectiveToolProjectionInput(session, toolRefs, context) {
|
|
|
8076
8086
|
const sandboxAvailable = context !== void 0 && sandboxBackend !== "none";
|
|
8077
8087
|
const supportsImages = model?.model.api === "responses" && model.model.capabilities.inputModalities.includes("image");
|
|
8078
8088
|
const hostedToolNames = [];
|
|
8089
|
+
const webSearchPlan = context ? webSearchToolPlan(context.settings, {
|
|
8090
|
+
hostedWebSearch: model?.model.hostedWebSearch ?? context.settings.webSearchEnabled,
|
|
8091
|
+
transportHostedSearch: model?.provider.kind === "xai-subscription"
|
|
8092
|
+
}) : { hostedWebSearch: false, providerTools: [] };
|
|
8079
8093
|
if (model?.provider.kind === "xai-subscription") {
|
|
8080
8094
|
if (context?.settings.webSearchEnabled) hostedToolNames.push("web_search", "x_search");
|
|
8081
|
-
} else if (model?.model.hostedWebSearch
|
|
8095
|
+
} else if (model?.model.hostedWebSearch && webSearchPlan.hostedWebSearch)
|
|
8096
|
+
hostedToolNames.push("web_search");
|
|
8082
8097
|
const mediaAttachment = context?.mediaAttachments?.get(session.id) ?? (context?.objectStorageAvailable === false ? { image: null, video: false } : void 0);
|
|
8083
8098
|
const media = resolveAgentMediaToolSurface(session.agent, mediaAttachment);
|
|
8084
8099
|
hostedToolNames.push(...media.hosted);
|
|
@@ -8087,7 +8102,8 @@ function sessionEffectiveToolProjectionInput(session, toolRefs, context) {
|
|
|
8087
8102
|
"list_models",
|
|
8088
8103
|
"skill_read",
|
|
8089
8104
|
...AGENT_SKILL_MANAGE_TOOL_NAMES,
|
|
8090
|
-
...media.runtime
|
|
8105
|
+
...media.runtime,
|
|
8106
|
+
...webSearchPlan.providerTools
|
|
8091
8107
|
] : [];
|
|
8092
8108
|
const sandboxToolNames = sandboxAvailable ? [
|
|
8093
8109
|
"exec_command",
|
|
@@ -8146,6 +8162,7 @@ function sessionEffectiveToolProjectionInput(session, toolRefs, context) {
|
|
|
8146
8162
|
"skill_read",
|
|
8147
8163
|
"request_human_input",
|
|
8148
8164
|
"list_models",
|
|
8165
|
+
...webSearchPlan.providerTools,
|
|
8149
8166
|
...toolRefs.some((ref) => ref.id === "opengeni" && ref.eager === true) || !progressiveDisclosure ? firstPartyMcpTools.filter((name) => !interactionNames.has(name) || !progressiveDisclosure) : [],
|
|
8150
8167
|
...!progressiveDisclosure ? runtimeToolNames : []
|
|
8151
8168
|
]);
|
|
@@ -8155,7 +8172,7 @@ function sessionEffectiveToolProjectionInput(session, toolRefs, context) {
|
|
|
8155
8172
|
...session.toolPolicy.mode !== "workspace_default" ? session.tools.map((tool) => tool.id) : []
|
|
8156
8173
|
]),
|
|
8157
8174
|
hasSkills: context ? sessionHasSkills(session, context) : false,
|
|
8158
|
-
webSearch: hostedToolNames.includes("web_search"),
|
|
8175
|
+
webSearch: hostedToolNames.includes("web_search") || webSearchPlan.providerTools.length > 0,
|
|
8159
8176
|
humanInput: context?.humanInputEnabled ?? false,
|
|
8160
8177
|
media: media.toolsKnown ? media.hosted.length + media.runtime.length > 0 : void 0,
|
|
8161
8178
|
routerInHistory: context?.routerInHistory === true || context?.routerHistorySessionIds?.has(session.id) === true,
|
|
@@ -9914,7 +9931,12 @@ async function postUserMessageTurn(input) {
|
|
|
9914
9931
|
}
|
|
9915
9932
|
} : {},
|
|
9916
9933
|
controlLockTimeoutMs: workspaceControlRequestLockTimeoutMs()
|
|
9917
|
-
})
|
|
9934
|
+
}),
|
|
9935
|
+
void 0,
|
|
9936
|
+
"shared",
|
|
9937
|
+
// Linked-actor capture reauthorizes under the organization-membership
|
|
9938
|
+
// lock; take it before the tenancy fence and the canonical prefix.
|
|
9939
|
+
Boolean(input.captureTurnAuthority)
|
|
9918
9940
|
)
|
|
9919
9941
|
);
|
|
9920
9942
|
} catch (error) {
|
|
@@ -10167,6 +10189,12 @@ async function withSessionCreateUsageRecording(input) {
|
|
|
10167
10189
|
}
|
|
10168
10190
|
async function createSessionForRequestInFileScope(unresolvedDeps, grant, workspaceId, rawPayload, authorization, agentChildPresentation, requestOptions = {}) {
|
|
10169
10191
|
const payload = CreateSessionRequest.parse(rawPayload);
|
|
10192
|
+
if (payload.firstPartyMcpTools) {
|
|
10193
|
+
const unavailable = deploymentUnavailableFirstPartyMcpTools(unresolvedDeps.settings);
|
|
10194
|
+
payload.firstPartyMcpTools = payload.firstPartyMcpTools.filter(
|
|
10195
|
+
(tool) => !unavailable.has(tool)
|
|
10196
|
+
);
|
|
10197
|
+
}
|
|
10170
10198
|
const surface = resolveTurnSurface({
|
|
10171
10199
|
grant,
|
|
10172
10200
|
authorization,
|
|
@@ -10298,24 +10326,29 @@ async function createSessionForRequestInFileScope(unresolvedDeps, grant, workspa
|
|
|
10298
10326
|
let retainedKeyedShellModel = null;
|
|
10299
10327
|
let retainedKeyedShellReasoningEffort = null;
|
|
10300
10328
|
if (payload.idempotencyKey && (effectiveVisibility !== "user_private" || replayManagedHumanSubjectId !== null)) {
|
|
10329
|
+
const createIdempotencyKey = payload.idempotencyKey;
|
|
10301
10330
|
try {
|
|
10302
|
-
const initializedReplay = await
|
|
10303
|
-
|
|
10304
|
-
|
|
10305
|
-
|
|
10306
|
-
|
|
10307
|
-
|
|
10308
|
-
|
|
10309
|
-
|
|
10310
|
-
|
|
10311
|
-
|
|
10312
|
-
|
|
10313
|
-
|
|
10314
|
-
|
|
10315
|
-
|
|
10316
|
-
|
|
10317
|
-
|
|
10318
|
-
|
|
10331
|
+
const initializedReplay = await measureSessionStartPhase(
|
|
10332
|
+
unresolvedDeps.observability,
|
|
10333
|
+
"idempotent_replay",
|
|
10334
|
+
() => getInitializedSessionCreateReplay(db, {
|
|
10335
|
+
bundledSkillIds,
|
|
10336
|
+
accountId: grant.accountId,
|
|
10337
|
+
workspaceId,
|
|
10338
|
+
subjectId: replayManagedHumanSubjectId ?? grant.subjectId,
|
|
10339
|
+
...replayManagedHumanSubjectId ? { activeManagedHumanSubjectId: replayManagedHumanSubjectId } : {},
|
|
10340
|
+
createIdempotencyKey,
|
|
10341
|
+
selectedInstalledSkillIds: payload.installedSkillIds ?? [],
|
|
10342
|
+
initialAgentLearning: payload.agentLearning,
|
|
10343
|
+
...sessionScope,
|
|
10344
|
+
...payload.requestedSessionId ? { requestedSessionId: payload.requestedSessionId } : {},
|
|
10345
|
+
visibility: effectiveVisibility,
|
|
10346
|
+
variableSetIds: payload.variableSetIds ?? [],
|
|
10347
|
+
initialPersonalResourceAttachmentIntent: payload.personalResourceAttachment ?? null,
|
|
10348
|
+
deferInitialTurn: payload.startMode === "realtime",
|
|
10349
|
+
agentConfig
|
|
10350
|
+
})
|
|
10351
|
+
);
|
|
10319
10352
|
if (initializedReplay) {
|
|
10320
10353
|
if (initializedReplay.outcome === "denied") {
|
|
10321
10354
|
throw new SessionSpawnDeniedError(SessionSpawnDenial.parse(initializedReplay.denial));
|
|
@@ -10624,40 +10657,50 @@ async function createSessionForRequestInFileScope(unresolvedDeps, grant, workspa
|
|
|
10624
10657
|
}
|
|
10625
10658
|
const tools = withFirstPartyTools(selectedTools, runtimeSettings);
|
|
10626
10659
|
const captureLinkedAuthority = prepareExternalLinkTurnAdmission(authorization);
|
|
10627
|
-
|
|
10628
|
-
|
|
10629
|
-
|
|
10630
|
-
|
|
10631
|
-
|
|
10632
|
-
|
|
10633
|
-
|
|
10634
|
-
|
|
10635
|
-
await validateFileResources(
|
|
10636
|
-
db,
|
|
10637
|
-
grant.accountId,
|
|
10638
|
-
workspaceId,
|
|
10639
|
-
personalResourceSubjectId ?? grant.subjectId,
|
|
10640
|
-
resources,
|
|
10641
|
-
attachmentOwnerContext
|
|
10642
|
-
);
|
|
10643
|
-
const variableSets = [];
|
|
10644
|
-
for (const variableSetId of payload.variableSetIds ?? []) {
|
|
10645
|
-
variableSets.push(
|
|
10646
|
-
await validateVariableSetAttachment({ settings, db }, grant, workspaceId, variableSetId)
|
|
10647
|
-
);
|
|
10648
|
-
}
|
|
10649
|
-
const requestedRigId = payload.rigId === void 0 ? await getWorkspaceDefaultRigId(db, workspaceId) : payload.rigId;
|
|
10650
|
-
let frozenRigId = null;
|
|
10651
|
-
let frozenRigVersionId = null;
|
|
10652
|
-
if (requestedRigId) {
|
|
10653
|
-
const rig = await getRig2(db, grant, requestedRigId);
|
|
10654
|
-
if (!rig || !rig.activeVersion) {
|
|
10655
|
-
if (payload.rigId) {
|
|
10656
|
-
throw new HTTPException17(422, {
|
|
10657
|
-
message: rig ? `sandbox environment ${payload.rigId} has no active version to bind` : `unknown rigId: ${payload.rigId}`
|
|
10660
|
+
const { attachmentOwnerContext, variableSets } = await measureSessionStartPhase(
|
|
10661
|
+
unresolvedDeps.observability,
|
|
10662
|
+
"resource_validation",
|
|
10663
|
+
async () => {
|
|
10664
|
+
await validateGitHubRepositorySelection(db, workspaceId, resources);
|
|
10665
|
+
if (resources.some((resource) => resource.kind === "file") && !objectStorage) {
|
|
10666
|
+
throw new HTTPException17(503, {
|
|
10667
|
+
message: "object storage is not configured"
|
|
10658
10668
|
});
|
|
10659
10669
|
}
|
|
10660
|
-
|
|
10670
|
+
const ownerContext = authorization ? await fileOwnerContextForAccess({ db }, authorization, "sessions:create") : grant.principalKind === "agent_attempt" ? await fileOwnerContextForAgent({ db }, grant, "sessions:create") : void 0;
|
|
10671
|
+
await validateFileResources(
|
|
10672
|
+
db,
|
|
10673
|
+
grant.accountId,
|
|
10674
|
+
workspaceId,
|
|
10675
|
+
personalResourceSubjectId ?? grant.subjectId,
|
|
10676
|
+
resources,
|
|
10677
|
+
ownerContext
|
|
10678
|
+
);
|
|
10679
|
+
const validatedVariableSets = [];
|
|
10680
|
+
for (const variableSetId of payload.variableSetIds ?? []) {
|
|
10681
|
+
validatedVariableSets.push(
|
|
10682
|
+
await validateVariableSetAttachment({ settings, db }, grant, workspaceId, variableSetId)
|
|
10683
|
+
);
|
|
10684
|
+
}
|
|
10685
|
+
return { attachmentOwnerContext: ownerContext, variableSets: validatedVariableSets };
|
|
10686
|
+
}
|
|
10687
|
+
);
|
|
10688
|
+
const attachmentOwner = attachmentOwnerContext?.privateFileOwnerSubjectId === grant.subjectId ? grant.subjectId : null;
|
|
10689
|
+
const { frozenRigId, frozenRigVersionId } = await measureSessionStartPhase(
|
|
10690
|
+
unresolvedDeps.observability,
|
|
10691
|
+
"rig_binding",
|
|
10692
|
+
async () => {
|
|
10693
|
+
const requestedRigId = payload.rigId === void 0 ? await getWorkspaceDefaultRigId(db, workspaceId) : payload.rigId;
|
|
10694
|
+
if (!requestedRigId) return { frozenRigId: null, frozenRigVersionId: null };
|
|
10695
|
+
const rig = await getRig2(db, grant, requestedRigId);
|
|
10696
|
+
if (!rig || !rig.activeVersion) {
|
|
10697
|
+
if (payload.rigId) {
|
|
10698
|
+
throw new HTTPException17(422, {
|
|
10699
|
+
message: rig ? `sandbox environment ${payload.rigId} has no active version to bind` : `unknown rigId: ${payload.rigId}`
|
|
10700
|
+
});
|
|
10701
|
+
}
|
|
10702
|
+
return { frozenRigId: null, frozenRigVersionId: null };
|
|
10703
|
+
}
|
|
10661
10704
|
for (const defaultVariableSetId of new Set(rig.activeVersion.defaultVariableSetIds)) {
|
|
10662
10705
|
await validateVariableSetAttachment(
|
|
10663
10706
|
{ settings, db },
|
|
@@ -10666,10 +10709,12 @@ async function createSessionForRequestInFileScope(unresolvedDeps, grant, workspa
|
|
|
10666
10709
|
defaultVariableSetId
|
|
10667
10710
|
);
|
|
10668
10711
|
}
|
|
10669
|
-
|
|
10670
|
-
|
|
10712
|
+
return {
|
|
10713
|
+
frozenRigId: rig.id,
|
|
10714
|
+
frozenRigVersionId: rig.activeVersion.id
|
|
10715
|
+
};
|
|
10671
10716
|
}
|
|
10672
|
-
|
|
10717
|
+
);
|
|
10673
10718
|
let channelId = null;
|
|
10674
10719
|
if (payload.channelId) {
|
|
10675
10720
|
const channel = await getChannel(db, workspaceId, payload.channelId);
|
|
@@ -10684,19 +10729,21 @@ async function createSessionForRequestInFileScope(unresolvedDeps, grant, workspa
|
|
|
10684
10729
|
if (model === null || model === void 0) {
|
|
10685
10730
|
throw new Error("effective session model unexpectedly resolved to null");
|
|
10686
10731
|
}
|
|
10687
|
-
await
|
|
10688
|
-
|
|
10689
|
-
|
|
10690
|
-
|
|
10691
|
-
|
|
10692
|
-
|
|
10693
|
-
|
|
10694
|
-
|
|
10695
|
-
|
|
10696
|
-
|
|
10697
|
-
|
|
10732
|
+
await measureSessionStartPhase(unresolvedDeps.observability, "model_admission", async () => {
|
|
10733
|
+
await assertWorkspaceModelPolicyAllows(db, settings, workspaceId, model);
|
|
10734
|
+
if (retainedKeyedShellModel === null && !parentSession) {
|
|
10735
|
+
const selections = await resolveCallerWorkspaceModelSelections(db, settings, {
|
|
10736
|
+
accountId: grant.accountId,
|
|
10737
|
+
workspaceId,
|
|
10738
|
+
subjectId: personalResourceSubjectId ?? grant.subjectId,
|
|
10739
|
+
...xaiProviderAccountAuthoritySnapshot ? { xaiAuthoritySnapshot: xaiProviderAccountAuthoritySnapshot } : {},
|
|
10740
|
+
...claudeProviderAccountAuthoritySnapshot ? { claudeAuthoritySnapshot: claudeProviderAccountAuthoritySnapshot } : {}
|
|
10741
|
+
});
|
|
10742
|
+
if (!admissibleWorkspaceModel(selections, model)) {
|
|
10743
|
+
throw new HTTPException17(422, { message: `model is not selectable: ${model}` });
|
|
10744
|
+
}
|
|
10698
10745
|
}
|
|
10699
|
-
}
|
|
10746
|
+
});
|
|
10700
10747
|
const inheritedReasoningEffort = parentCallingTurn?.reasoningEffort ?? parentSession?.reasoningEffort ?? resolvedDefault?.reasoningEffort ?? settings.openaiReasoningEffort;
|
|
10701
10748
|
const reasoningEffort = payload.reasoningEffort ?? inheritedReasoningEffort;
|
|
10702
10749
|
const latencyMode = payload.latencyMode ?? "standard";
|
|
@@ -10815,18 +10862,22 @@ async function createSessionForRequestInFileScope(unresolvedDeps, grant, workspa
|
|
|
10815
10862
|
workspace.settings
|
|
10816
10863
|
)
|
|
10817
10864
|
});
|
|
10818
|
-
const { personalConnectionDelegations, mcpAccountBindings } = await
|
|
10819
|
-
|
|
10820
|
-
|
|
10821
|
-
|
|
10822
|
-
|
|
10823
|
-
|
|
10824
|
-
|
|
10825
|
-
|
|
10826
|
-
|
|
10827
|
-
|
|
10828
|
-
|
|
10829
|
-
|
|
10865
|
+
const { personalConnectionDelegations, mcpAccountBindings } = await measureSessionStartPhase(
|
|
10866
|
+
unresolvedDeps.observability,
|
|
10867
|
+
"connection_freeze",
|
|
10868
|
+
() => freezeConnectionAccounts({
|
|
10869
|
+
db,
|
|
10870
|
+
accountId: grant.accountId,
|
|
10871
|
+
workspaceId,
|
|
10872
|
+
settings: runtimeSettings,
|
|
10873
|
+
tools: connectionAccountTools,
|
|
10874
|
+
resources,
|
|
10875
|
+
source: connectionDelegationSource,
|
|
10876
|
+
authoritySelections: payload.connectionAccounts,
|
|
10877
|
+
googleDrivePublicationEnabled,
|
|
10878
|
+
atlassianEnabled
|
|
10879
|
+
})
|
|
10880
|
+
);
|
|
10830
10881
|
if (effectiveGoal) {
|
|
10831
10882
|
const missingGoalTools = ["goal_update", "goal_progress", "goal_complete", "goal_pause"].filter(
|
|
10832
10883
|
(name) => !firstPartyMcpTools.includes(name)
|
|
@@ -11464,6 +11515,10 @@ async function acceptSessionUserMessageInFileScope(deps, grant, workspaceId, ses
|
|
|
11464
11515
|
(await requireWorkspace5(db, workspaceId)).settings
|
|
11465
11516
|
)
|
|
11466
11517
|
});
|
|
11518
|
+
const existingEffectiveFirstPartyTools = allowedFirstPartyMcpToolsForSession3(
|
|
11519
|
+
settings,
|
|
11520
|
+
existingSession.firstPartyMcpTools
|
|
11521
|
+
);
|
|
11467
11522
|
const { personalConnectionDelegations, mcpAccountBindings } = await freezeConnectionAccounts({
|
|
11468
11523
|
db,
|
|
11469
11524
|
accountId: grant.accountId,
|
|
@@ -11473,7 +11528,7 @@ async function acceptSessionUserMessageInFileScope(deps, grant, workspaceId, ses
|
|
|
11473
11528
|
resources: [...existingSession.resources, ...requestedResources],
|
|
11474
11529
|
source: connectionDelegationSource,
|
|
11475
11530
|
targetSessionId: sessionId,
|
|
11476
|
-
googleDrivePublicationEnabled:
|
|
11531
|
+
googleDrivePublicationEnabled: existingEffectiveFirstPartyTools.includes("editable_artifact_export") && existingEffectiveFirstPartyTools.includes("editable_artifact_export_status") && (!existingSession.firstPartyMcpPermissions?.length || existingSession.firstPartyMcpPermissions.includes("artifacts:read") && existingSession.firstPartyMcpPermissions.includes("artifacts:publish")),
|
|
11477
11532
|
atlassianEnabled: existingSession.firstPartyMcpTools.some((tool) => tool.startsWith("atlassian_")) && (!existingSession.firstPartyMcpPermissions?.length || existingSession.firstPartyMcpPermissions.includes("connections:read")),
|
|
11478
11533
|
...input.connectionAccounts ? { authoritySelections: input.connectionAccounts } : {}
|
|
11479
11534
|
});
|
|
@@ -12329,6 +12384,10 @@ async function freezeSessionRealtimeConnectionAccounts(input) {
|
|
|
12329
12384
|
(await requireWorkspace6(db, workspaceId)).settings
|
|
12330
12385
|
)
|
|
12331
12386
|
});
|
|
12387
|
+
const effectiveFirstPartyTools = allowedFirstPartyMcpToolsForSession4(
|
|
12388
|
+
settings,
|
|
12389
|
+
session.firstPartyMcpTools
|
|
12390
|
+
);
|
|
12332
12391
|
return await freezeConnectionAccounts({
|
|
12333
12392
|
db,
|
|
12334
12393
|
settings: runtimeSettings,
|
|
@@ -12338,7 +12397,7 @@ async function freezeSessionRealtimeConnectionAccounts(input) {
|
|
|
12338
12397
|
resources: session.resources,
|
|
12339
12398
|
source: personalConnectionDelegationSourceForGrant(grant),
|
|
12340
12399
|
targetSessionId: sessionId,
|
|
12341
|
-
googleDrivePublicationEnabled:
|
|
12400
|
+
googleDrivePublicationEnabled: effectiveFirstPartyTools.includes("editable_artifact_export") && effectiveFirstPartyTools.includes("editable_artifact_export_status") && (!session.firstPartyMcpPermissions?.length || session.firstPartyMcpPermissions.includes("artifacts:read") && session.firstPartyMcpPermissions.includes("artifacts:publish")),
|
|
12342
12401
|
atlassianEnabled: session.firstPartyMcpTools.some((tool) => tool.startsWith("atlassian_")) && (!session.firstPartyMcpPermissions?.length || session.firstPartyMcpPermissions.includes("connections:read"))
|
|
12343
12402
|
});
|
|
12344
12403
|
}
|
|
@@ -12603,109 +12662,41 @@ async function searchKnowledgeEntries(db, context, input, embedder, settings) {
|
|
|
12603
12662
|
);
|
|
12604
12663
|
}
|
|
12605
12664
|
|
|
12606
|
-
// src/
|
|
12607
|
-
|
|
12608
|
-
|
|
12609
|
-
|
|
12610
|
-
|
|
12665
|
+
// src/domain/voice-input-billing.ts
|
|
12666
|
+
import {
|
|
12667
|
+
calculateVoiceInputCost,
|
|
12668
|
+
configuredStaticUsageLimits as configuredStaticUsageLimits3,
|
|
12669
|
+
voiceInputCreditBillingActive as voiceInputCreditBillingActive2,
|
|
12670
|
+
voiceInputPricingHasTokenRates
|
|
12671
|
+
} from "@opengeni/config";
|
|
12672
|
+
import {
|
|
12673
|
+
applyCreditDebitAfterUse as applyCreditDebitAfterUse3,
|
|
12674
|
+
checkWorkspaceAllowance as checkWorkspaceAllowance4,
|
|
12675
|
+
creditDebitAttributionMetadata as creditDebitAttributionMetadata2,
|
|
12676
|
+
getSpendableCreditBalance as getSpendableCreditBalance4,
|
|
12677
|
+
listUnsettledVoiceTranscriptionCharges,
|
|
12678
|
+
recordUsageEvent as recordUsageEvent4,
|
|
12679
|
+
sumUsageQuantity as sumUsageQuantity4,
|
|
12680
|
+
VOICE_TRANSCRIPTION_DEBIT_TYPE,
|
|
12681
|
+
VOICE_TRANSCRIPTION_SOURCE_TYPE,
|
|
12682
|
+
voiceTranscriptionSettlementKeys,
|
|
12683
|
+
VOICE_CREDIT_USAGE as VOICE_CREDIT_USAGE2
|
|
12684
|
+
} from "@opengeni/db";
|
|
12611
12685
|
|
|
12612
|
-
// src/
|
|
12613
|
-
|
|
12614
|
-
var
|
|
12615
|
-
|
|
12616
|
-
|
|
12617
|
-
|
|
12618
|
-
|
|
12619
|
-
|
|
12620
|
-
|
|
12686
|
+
// src/transcription.ts
|
|
12687
|
+
var TRANSCRIPTION_PROVIDER_REQUEST_TIMEOUT_MILLISECONDS = 10 * 60 * 1e3;
|
|
12688
|
+
var TranscriptionBillingRefusedError = class extends Error {
|
|
12689
|
+
status;
|
|
12690
|
+
code;
|
|
12691
|
+
details;
|
|
12692
|
+
constructor(input) {
|
|
12693
|
+
super(input.message);
|
|
12694
|
+
this.name = "TranscriptionBillingRefusedError";
|
|
12695
|
+
this.code = input.code;
|
|
12696
|
+
this.status = input.code === "monthly_model_cost_limit" ? 429 : 402;
|
|
12697
|
+
this.details = input.details ?? {};
|
|
12698
|
+
}
|
|
12621
12699
|
};
|
|
12622
|
-
function createUserPresenceRecorder(options) {
|
|
12623
|
-
const throttleMs = options.throttleMs ?? USER_PRESENCE_THROTTLE_MS;
|
|
12624
|
-
const flushDelayMs = options.flushDelayMs ?? USER_PRESENCE_FLUSH_DELAY_MS;
|
|
12625
|
-
const maxTracked = options.maxTracked ?? USER_PRESENCE_MAX_TRACKED;
|
|
12626
|
-
const now = options.now ?? Date.now;
|
|
12627
|
-
const record3 = options.record ?? recordUserActivityPresence;
|
|
12628
|
-
const lastQueuedAt = /* @__PURE__ */ new Map();
|
|
12629
|
-
const pending = /* @__PURE__ */ new Set();
|
|
12630
|
-
let timer = null;
|
|
12631
|
-
let closed = false;
|
|
12632
|
-
let lastWarningAt = 0;
|
|
12633
|
-
let tail = Promise.resolve();
|
|
12634
|
-
const count = (outcome) => {
|
|
12635
|
-
try {
|
|
12636
|
-
options.observability?.incrementCounter({ ...PRESENCE_FLUSH_METRIC, labels: { outcome } });
|
|
12637
|
-
} catch {
|
|
12638
|
-
}
|
|
12639
|
-
};
|
|
12640
|
-
const prune = (at) => {
|
|
12641
|
-
for (const [subject, queuedAt] of lastQueuedAt) {
|
|
12642
|
-
if (at - queuedAt >= throttleMs && !pending.has(subject)) lastQueuedAt.delete(subject);
|
|
12643
|
-
}
|
|
12644
|
-
if (lastQueuedAt.size > maxTracked) {
|
|
12645
|
-
const excess = lastQueuedAt.size - maxTracked;
|
|
12646
|
-
let removed = 0;
|
|
12647
|
-
for (const subject of lastQueuedAt.keys()) {
|
|
12648
|
-
if (removed >= excess) break;
|
|
12649
|
-
if (pending.has(subject)) continue;
|
|
12650
|
-
lastQueuedAt.delete(subject);
|
|
12651
|
-
removed += 1;
|
|
12652
|
-
}
|
|
12653
|
-
}
|
|
12654
|
-
};
|
|
12655
|
-
const writeBatch = async () => {
|
|
12656
|
-
if (pending.size === 0) return;
|
|
12657
|
-
const batch = [...pending];
|
|
12658
|
-
pending.clear();
|
|
12659
|
-
try {
|
|
12660
|
-
await record3(options.db, batch);
|
|
12661
|
-
count("ok");
|
|
12662
|
-
} catch (error) {
|
|
12663
|
-
for (const subject of batch) lastQueuedAt.delete(subject);
|
|
12664
|
-
count("failed");
|
|
12665
|
-
const at = now();
|
|
12666
|
-
if (at - lastWarningAt >= throttleMs) {
|
|
12667
|
-
lastWarningAt = at;
|
|
12668
|
-
options.observability?.warn("user presence write failed", {
|
|
12669
|
-
errorClass: error instanceof Error ? error.name : "UnknownError"
|
|
12670
|
-
});
|
|
12671
|
-
}
|
|
12672
|
-
}
|
|
12673
|
-
};
|
|
12674
|
-
const flush = () => {
|
|
12675
|
-
if (timer) {
|
|
12676
|
-
clearTimeout(timer);
|
|
12677
|
-
timer = null;
|
|
12678
|
-
}
|
|
12679
|
-
tail = tail.then(writeBatch, writeBatch);
|
|
12680
|
-
return tail;
|
|
12681
|
-
};
|
|
12682
|
-
return {
|
|
12683
|
-
touch(subjectId) {
|
|
12684
|
-
if (closed || !USER_PRESENCE_SUBJECT.test(subjectId)) return;
|
|
12685
|
-
const at = now();
|
|
12686
|
-
const previous = lastQueuedAt.get(subjectId);
|
|
12687
|
-
if (previous !== void 0 && at - previous < throttleMs) return;
|
|
12688
|
-
lastQueuedAt.set(subjectId, at);
|
|
12689
|
-
pending.add(subjectId);
|
|
12690
|
-
if (lastQueuedAt.size > maxTracked) prune(at);
|
|
12691
|
-
if (!timer) {
|
|
12692
|
-
timer = setTimeout(() => {
|
|
12693
|
-
timer = null;
|
|
12694
|
-
void flush();
|
|
12695
|
-
}, flushDelayMs);
|
|
12696
|
-
timer.unref?.();
|
|
12697
|
-
}
|
|
12698
|
-
},
|
|
12699
|
-
flush,
|
|
12700
|
-
async close() {
|
|
12701
|
-
closed = true;
|
|
12702
|
-
await flush();
|
|
12703
|
-
}
|
|
12704
|
-
};
|
|
12705
|
-
}
|
|
12706
|
-
|
|
12707
|
-
// src/transcription.ts
|
|
12708
|
-
var TRANSCRIPTION_PROVIDER_REQUEST_TIMEOUT_MILLISECONDS = 10 * 60 * 1e3;
|
|
12709
12700
|
var TranscriptionServiceError = class extends Error {
|
|
12710
12701
|
code;
|
|
12711
12702
|
status;
|
|
@@ -12781,6 +12772,685 @@ function filenameForMimeType(mimeType) {
|
|
|
12781
12772
|
}
|
|
12782
12773
|
}
|
|
12783
12774
|
|
|
12775
|
+
// src/domain/realtime-voice-billing.ts
|
|
12776
|
+
import {
|
|
12777
|
+
AI_GATEWAY_REALTIME_MODELS,
|
|
12778
|
+
AZURE_LIVE_REALTIME_MODEL_ID,
|
|
12779
|
+
configuredStaticUsageLimits as configuredStaticUsageLimits2,
|
|
12780
|
+
parseRealtimeVoicePricingJson,
|
|
12781
|
+
parseRealtimeVoicePricingTableJson,
|
|
12782
|
+
realtimeVoiceMinuteCreditMicros,
|
|
12783
|
+
realtimeVoiceStartedMinutes,
|
|
12784
|
+
voiceInputCreditBillingActive
|
|
12785
|
+
} from "@opengeni/config";
|
|
12786
|
+
import {
|
|
12787
|
+
applyCreditDebitAfterUse as applyCreditDebitAfterUse2,
|
|
12788
|
+
checkWorkspaceAllowance as checkWorkspaceAllowance3,
|
|
12789
|
+
creditDebitAttributionMetadata,
|
|
12790
|
+
existingUsageEventIdempotencyKeys,
|
|
12791
|
+
getSpendableCreditBalance as getSpendableCreditBalance3,
|
|
12792
|
+
loadSessionRealtimeBillingFacts,
|
|
12793
|
+
recordUsageEvent as recordUsageEvent3,
|
|
12794
|
+
sumUsageQuantity as sumUsageQuantity3,
|
|
12795
|
+
VOICE_CREDIT_USAGE,
|
|
12796
|
+
withRlsContext
|
|
12797
|
+
} from "@opengeni/db";
|
|
12798
|
+
var REALTIME_VOICE_DEBIT_TYPE = "voice_realtime_debit";
|
|
12799
|
+
var REALTIME_VOICE_SOURCE_TYPE = "voice_realtime";
|
|
12800
|
+
var reportedPricingErrors = /* @__PURE__ */ new Set();
|
|
12801
|
+
function tolerantPricing(parse, fallback) {
|
|
12802
|
+
try {
|
|
12803
|
+
return parse();
|
|
12804
|
+
} catch (error) {
|
|
12805
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
12806
|
+
if (!reportedPricingErrors.has(message)) {
|
|
12807
|
+
reportedPricingErrors.add(message);
|
|
12808
|
+
console.error(`Live voice pricing ignored: ${message}`);
|
|
12809
|
+
}
|
|
12810
|
+
return fallback;
|
|
12811
|
+
}
|
|
12812
|
+
}
|
|
12813
|
+
function deploymentRealtimeVoice(settings, model) {
|
|
12814
|
+
const azureConfigured = Boolean(settings.azureLiveEndpoint && settings.azureLiveApiKey?.trim());
|
|
12815
|
+
if (model === AZURE_LIVE_REALTIME_MODEL_ID) {
|
|
12816
|
+
return {
|
|
12817
|
+
provider: "azure-live",
|
|
12818
|
+
configured: azureConfigured,
|
|
12819
|
+
pricing: tolerantPricing(
|
|
12820
|
+
() => parseRealtimeVoicePricingJson(
|
|
12821
|
+
settings.azureLivePricingJson,
|
|
12822
|
+
"OPENGENI_AZURE_LIVE_PRICING_JSON"
|
|
12823
|
+
),
|
|
12824
|
+
null
|
|
12825
|
+
)
|
|
12826
|
+
};
|
|
12827
|
+
}
|
|
12828
|
+
for (const gateway of Object.values(AI_GATEWAY_REALTIME_MODELS)) {
|
|
12829
|
+
if (gateway.managedModelId !== model) continue;
|
|
12830
|
+
return {
|
|
12831
|
+
provider: "ai-gateway",
|
|
12832
|
+
configured: Boolean(settings.vercelAiGatewayApiKey),
|
|
12833
|
+
pricing: tolerantPricing(
|
|
12834
|
+
() => parseRealtimeVoicePricingTableJson(
|
|
12835
|
+
settings.aiGatewayRealtimePricingJson,
|
|
12836
|
+
"OPENGENI_AI_GATEWAY_REALTIME_PRICING_JSON"
|
|
12837
|
+
),
|
|
12838
|
+
{}
|
|
12839
|
+
)[gateway.upstreamModelId] ?? null
|
|
12840
|
+
};
|
|
12841
|
+
}
|
|
12842
|
+
return null;
|
|
12843
|
+
}
|
|
12844
|
+
var RealtimeVoiceUnavailableError = class extends Error {
|
|
12845
|
+
constructor(code, message) {
|
|
12846
|
+
super(message);
|
|
12847
|
+
this.code = code;
|
|
12848
|
+
}
|
|
12849
|
+
name = "RealtimeVoiceUnavailableError";
|
|
12850
|
+
};
|
|
12851
|
+
function realtimeVoiceOfferProblem(settings, voice) {
|
|
12852
|
+
if (!voice.configured) {
|
|
12853
|
+
return new RealtimeVoiceUnavailableError(
|
|
12854
|
+
"not_configured",
|
|
12855
|
+
"Opengeni live voice is not configured on this deployment."
|
|
12856
|
+
);
|
|
12857
|
+
}
|
|
12858
|
+
if (voiceInputCreditBillingActive(settings) && !voice.pricing) {
|
|
12859
|
+
return new RealtimeVoiceUnavailableError(
|
|
12860
|
+
"pricing_unconfigured",
|
|
12861
|
+
"Opengeni live voice is not available on this deployment yet."
|
|
12862
|
+
);
|
|
12863
|
+
}
|
|
12864
|
+
return null;
|
|
12865
|
+
}
|
|
12866
|
+
var REALTIME_VOICE_INSUFFICIENT_CREDITS_MESSAGE = "Live voice needs Opengeni credits. Add credits to continue.";
|
|
12867
|
+
function voiceCreditStanding(balance) {
|
|
12868
|
+
if (balance.balanceMicros > 0) return "spendable";
|
|
12869
|
+
return (balance.promotionalCredits ?? []).some((grant) => grant.remainingMicros > 0) ? "promotional_only" : "none";
|
|
12870
|
+
}
|
|
12871
|
+
function voiceInsufficientCreditsMessage(feature, standing) {
|
|
12872
|
+
return standing === "promotional_only" ? `Promotional credits don't cover ${feature.toLowerCase()}. Add credits to use it.` : `${feature} needs Opengeni credits. Add credits to continue.`;
|
|
12873
|
+
}
|
|
12874
|
+
function startOfUtcMonth2(now) {
|
|
12875
|
+
return new Date(Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), 1));
|
|
12876
|
+
}
|
|
12877
|
+
function initiatingHuman(attribution) {
|
|
12878
|
+
return attribution.kind === "human" || attribution.kind === "turn" ? attribution.initiatingHumanSubjectId : null;
|
|
12879
|
+
}
|
|
12880
|
+
function createRealtimeVoiceBilling(deps) {
|
|
12881
|
+
const billingActive = () => voiceInputCreditBillingActive(deps.settings);
|
|
12882
|
+
async function refusal(input) {
|
|
12883
|
+
const balance = await getSpendableCreditBalance3(deps.db, input.accountId, VOICE_CREDIT_USAGE);
|
|
12884
|
+
if (balance.balanceMicros <= 0) {
|
|
12885
|
+
return new TranscriptionBillingRefusedError({
|
|
12886
|
+
code: "insufficient_credits",
|
|
12887
|
+
message: voiceInsufficientCreditsMessage("Live voice", voiceCreditStanding(balance))
|
|
12888
|
+
});
|
|
12889
|
+
}
|
|
12890
|
+
const allowance = await checkWorkspaceAllowance3(deps.db, {
|
|
12891
|
+
accountId: input.accountId,
|
|
12892
|
+
workspaceId: input.workspaceId,
|
|
12893
|
+
subjectId: initiatingHuman(input.attribution)
|
|
12894
|
+
});
|
|
12895
|
+
if (allowance) {
|
|
12896
|
+
return new TranscriptionBillingRefusedError({
|
|
12897
|
+
code: allowance.code,
|
|
12898
|
+
message: allowance.message,
|
|
12899
|
+
details: {
|
|
12900
|
+
scope: allowance.scope,
|
|
12901
|
+
resetsAt: allowance.resetsAt,
|
|
12902
|
+
...allowance.subjectId ? { subjectId: allowance.subjectId } : {}
|
|
12903
|
+
}
|
|
12904
|
+
});
|
|
12905
|
+
}
|
|
12906
|
+
if (deps.settings.usageLimitsMode === "static" || deps.settings.usageLimitsMode === "managed") {
|
|
12907
|
+
const cap = configuredStaticUsageLimits2(deps.settings).maxMonthlyCostMicrosPerAccount;
|
|
12908
|
+
if (cap) {
|
|
12909
|
+
const used = await sumUsageQuantity3(deps.db, {
|
|
12910
|
+
accountId: input.accountId,
|
|
12911
|
+
eventType: "model.cost",
|
|
12912
|
+
since: startOfUtcMonth2(input.now)
|
|
12913
|
+
});
|
|
12914
|
+
if (used >= cap) {
|
|
12915
|
+
return new TranscriptionBillingRefusedError({
|
|
12916
|
+
code: "monthly_model_cost_limit",
|
|
12917
|
+
message: "The monthly model cost limit has been reached."
|
|
12918
|
+
});
|
|
12919
|
+
}
|
|
12920
|
+
}
|
|
12921
|
+
}
|
|
12922
|
+
return null;
|
|
12923
|
+
}
|
|
12924
|
+
return {
|
|
12925
|
+
/**
|
|
12926
|
+
* Throws RealtimeVoiceUnavailableError when the deployment cannot offer
|
|
12927
|
+
* the model and TranscriptionBillingRefusedError for a credit refusal.
|
|
12928
|
+
* Workspace-funded models are always admitted here.
|
|
12929
|
+
*/
|
|
12930
|
+
async admit(input) {
|
|
12931
|
+
const voice = deploymentRealtimeVoice(deps.settings, input.model);
|
|
12932
|
+
if (!voice) return;
|
|
12933
|
+
const problem = realtimeVoiceOfferProblem(deps.settings, voice);
|
|
12934
|
+
if (problem) throw problem;
|
|
12935
|
+
if (!billingActive()) return;
|
|
12936
|
+
const refused = await refusal({ ...input, now: input.now ?? /* @__PURE__ */ new Date() });
|
|
12937
|
+
if (refused) throw refused;
|
|
12938
|
+
},
|
|
12939
|
+
/** Spendable credits are positive (always true when credits are not enforced). */
|
|
12940
|
+
async hasSpendableCredits(accountId) {
|
|
12941
|
+
return await this.creditStanding(accountId) === "spendable";
|
|
12942
|
+
},
|
|
12943
|
+
/** Credit standing for live voice ("spendable" when credits are not enforced). */
|
|
12944
|
+
async creditStanding(accountId) {
|
|
12945
|
+
if (!billingActive()) return "spendable";
|
|
12946
|
+
return voiceCreditStanding(
|
|
12947
|
+
await getSpendableCreditBalance3(deps.db, accountId, VOICE_CREDIT_USAGE)
|
|
12948
|
+
);
|
|
12949
|
+
},
|
|
12950
|
+
/**
|
|
12951
|
+
* Settle every started minute observed so far for one live-voice mode and
|
|
12952
|
+
* report whether the call must stop. Safe to call repeatedly and
|
|
12953
|
+
* concurrently: receipts and debits are idempotent per (connection, minute).
|
|
12954
|
+
*/
|
|
12955
|
+
async settle(input) {
|
|
12956
|
+
const now = input.now ?? /* @__PURE__ */ new Date();
|
|
12957
|
+
if (!billingActive()) return { debitedMicros: 0, stop: null };
|
|
12958
|
+
const facts = await loadSessionRealtimeBillingFacts(deps.db, { ...input, now });
|
|
12959
|
+
if (!facts) return { debitedMicros: 0, stop: null };
|
|
12960
|
+
const voice = deploymentRealtimeVoice(deps.settings, facts.model);
|
|
12961
|
+
if (!voice) return { debitedMicros: 0, stop: null };
|
|
12962
|
+
if (!voice.pricing) {
|
|
12963
|
+
return {
|
|
12964
|
+
debitedMicros: 0,
|
|
12965
|
+
stop: new RealtimeVoiceUnavailableError(
|
|
12966
|
+
"pricing_unconfigured",
|
|
12967
|
+
"Opengeni live voice is no longer available on this deployment."
|
|
12968
|
+
)
|
|
12969
|
+
};
|
|
12970
|
+
}
|
|
12971
|
+
const attribution = input.callerSubjectId === facts.ownerSubjectId ? input.attribution : { kind: "unknown" };
|
|
12972
|
+
const cost = realtimeVoiceMinuteCreditMicros(voice.pricing);
|
|
12973
|
+
const minutes = facts.connections.flatMap(
|
|
12974
|
+
(connection) => Array.from(
|
|
12975
|
+
{ length: realtimeVoiceStartedMinutes(connection.startedAt, connection.observedUntil) },
|
|
12976
|
+
(_, index) => ({
|
|
12977
|
+
connectionId: connection.id,
|
|
12978
|
+
minute: index + 1,
|
|
12979
|
+
startsAt: new Date(connection.startedAt.getTime() + index * 6e4),
|
|
12980
|
+
key: `voice.realtime_cost:${connection.id}:${index + 1}`
|
|
12981
|
+
})
|
|
12982
|
+
)
|
|
12983
|
+
);
|
|
12984
|
+
const recorded = await existingUsageEventIdempotencyKeys(deps.db, {
|
|
12985
|
+
accountId: facts.accountId,
|
|
12986
|
+
workspaceId: facts.workspaceId,
|
|
12987
|
+
keys: minutes.map((minute) => minute.key)
|
|
12988
|
+
});
|
|
12989
|
+
let debitedMicros = 0;
|
|
12990
|
+
for (const minute of minutes) {
|
|
12991
|
+
if (recorded.has(minute.key)) continue;
|
|
12992
|
+
debitedMicros += await withRlsContext(
|
|
12993
|
+
deps.db,
|
|
12994
|
+
{ accountId: facts.accountId, workspaceId: facts.workspaceId },
|
|
12995
|
+
async (tx) => {
|
|
12996
|
+
const sourceId = `${minute.connectionId}:${minute.minute}`;
|
|
12997
|
+
const receipt2 = await recordUsageEvent3(tx, {
|
|
12998
|
+
accountId: facts.accountId,
|
|
12999
|
+
workspaceId: facts.workspaceId,
|
|
13000
|
+
sessionId: facts.sessionId,
|
|
13001
|
+
eventType: "model.cost",
|
|
13002
|
+
quantity: cost.creditCostMicros,
|
|
13003
|
+
unit: "usd_micros",
|
|
13004
|
+
sourceResourceType: REALTIME_VOICE_SOURCE_TYPE,
|
|
13005
|
+
sourceResourceId: sourceId,
|
|
13006
|
+
idempotencyKey: minute.key,
|
|
13007
|
+
occurredAt: minute.startsAt,
|
|
13008
|
+
initiator: { kind: "service", subjectId: "api:voice-realtime" },
|
|
13009
|
+
initiatorContext: { creditDebitAttribution: attribution }
|
|
13010
|
+
});
|
|
13011
|
+
const amount = Number(receipt2.quantity);
|
|
13012
|
+
if (amount <= 0) return 0;
|
|
13013
|
+
const debit = await applyCreditDebitAfterUse2(tx, {
|
|
13014
|
+
accountId: facts.accountId,
|
|
13015
|
+
workspaceId: facts.workspaceId,
|
|
13016
|
+
type: REALTIME_VOICE_DEBIT_TYPE,
|
|
13017
|
+
amountMicros: amount,
|
|
13018
|
+
sourceType: REALTIME_VOICE_SOURCE_TYPE,
|
|
13019
|
+
sourceId,
|
|
13020
|
+
idempotencyKey: `credit:${REALTIME_VOICE_DEBIT_TYPE}:${sourceId}`,
|
|
13021
|
+
occurredAt: minute.startsAt,
|
|
13022
|
+
usage: VOICE_CREDIT_USAGE,
|
|
13023
|
+
metadata: {
|
|
13024
|
+
provider: voice.provider,
|
|
13025
|
+
model: facts.model,
|
|
13026
|
+
realtimeId: facts.realtimeId,
|
|
13027
|
+
connectionId: minute.connectionId,
|
|
13028
|
+
minute: minute.minute,
|
|
13029
|
+
basis: "server_observed_started_minute",
|
|
13030
|
+
providerCostMicros: cost.providerCostMicros,
|
|
13031
|
+
...attribution.kind === "unknown" ? {} : creditDebitAttributionMetadata(attribution)
|
|
13032
|
+
}
|
|
13033
|
+
});
|
|
13034
|
+
return debit.debitedMicros;
|
|
13035
|
+
}
|
|
13036
|
+
);
|
|
13037
|
+
}
|
|
13038
|
+
const stop = facts.state === "active" ? await refusal({
|
|
13039
|
+
accountId: facts.accountId,
|
|
13040
|
+
workspaceId: facts.workspaceId,
|
|
13041
|
+
attribution,
|
|
13042
|
+
now
|
|
13043
|
+
}) : null;
|
|
13044
|
+
return { debitedMicros, stop };
|
|
13045
|
+
}
|
|
13046
|
+
};
|
|
13047
|
+
}
|
|
13048
|
+
|
|
13049
|
+
// src/domain/voice-input-billing.ts
|
|
13050
|
+
var VOICE_INPUT_DEBIT_TYPE = VOICE_TRANSCRIPTION_DEBIT_TYPE;
|
|
13051
|
+
var VOICE_INPUT_SOURCE_TYPE = VOICE_TRANSCRIPTION_SOURCE_TYPE;
|
|
13052
|
+
function voiceInputBillableUsage(input) {
|
|
13053
|
+
if (input.usage?.kind === "tokens" && voiceInputPricingHasTokenRates(input.pricing)) {
|
|
13054
|
+
return { usage: input.usage, basis: "provider_tokens" };
|
|
13055
|
+
}
|
|
13056
|
+
if (input.usage?.kind === "duration") {
|
|
13057
|
+
return { usage: input.usage, basis: "provider_duration" };
|
|
13058
|
+
}
|
|
13059
|
+
if (input.trustedDurationSeconds !== void 0 && Number.isFinite(input.trustedDurationSeconds) && input.trustedDurationSeconds >= 0) {
|
|
13060
|
+
return {
|
|
13061
|
+
usage: { kind: "duration", seconds: input.trustedDurationSeconds },
|
|
13062
|
+
basis: "server_duration"
|
|
13063
|
+
};
|
|
13064
|
+
}
|
|
13065
|
+
throw new TranscriptionServiceError({
|
|
13066
|
+
code: "provider",
|
|
13067
|
+
message: "Transcription usage was not reported."
|
|
13068
|
+
});
|
|
13069
|
+
}
|
|
13070
|
+
function initiatingHuman2(attribution) {
|
|
13071
|
+
return attribution.kind === "human" || attribution.kind === "turn" ? attribution.initiatingHumanSubjectId : null;
|
|
13072
|
+
}
|
|
13073
|
+
function startOfUtcMonth3() {
|
|
13074
|
+
const now = /* @__PURE__ */ new Date();
|
|
13075
|
+
return new Date(Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), 1));
|
|
13076
|
+
}
|
|
13077
|
+
function createVoiceInputBilling(deps) {
|
|
13078
|
+
const reconcileInterval = deps.reconcileIntervalMilliseconds ?? 5 * 6e4;
|
|
13079
|
+
const lastReconciled = /* @__PURE__ */ new Map();
|
|
13080
|
+
const reconcileDue = (key) => {
|
|
13081
|
+
const now = Date.now();
|
|
13082
|
+
const last = lastReconciled.get(key);
|
|
13083
|
+
if (last !== void 0 && now - last < reconcileInterval) return false;
|
|
13084
|
+
if (lastReconciled.size >= 1e4) lastReconciled.clear();
|
|
13085
|
+
lastReconciled.set(key, now);
|
|
13086
|
+
return true;
|
|
13087
|
+
};
|
|
13088
|
+
return {
|
|
13089
|
+
async admit({ accountId, workspaceId, attribution }) {
|
|
13090
|
+
if (!voiceInputCreditBillingActive2(deps.settings)) return;
|
|
13091
|
+
if (attribution.kind === "unknown") {
|
|
13092
|
+
throw new TranscriptionServiceError({
|
|
13093
|
+
code: "policy_blocked",
|
|
13094
|
+
status: 403,
|
|
13095
|
+
message: "Voice input payer could not be verified."
|
|
13096
|
+
});
|
|
13097
|
+
}
|
|
13098
|
+
if (reconcileDue(`${accountId}:${workspaceId}`)) {
|
|
13099
|
+
await reconcileUnsettledVoiceInputCharges(deps.db, { accountId, workspaceId }).catch(
|
|
13100
|
+
(error) => {
|
|
13101
|
+
deps.onReconcileError?.(error, { accountId, workspaceId });
|
|
13102
|
+
}
|
|
13103
|
+
);
|
|
13104
|
+
}
|
|
13105
|
+
const balance = await getSpendableCreditBalance4(deps.db, accountId, VOICE_CREDIT_USAGE2);
|
|
13106
|
+
if (balance.balanceMicros <= 0) {
|
|
13107
|
+
throw new TranscriptionBillingRefusedError({
|
|
13108
|
+
code: "insufficient_credits",
|
|
13109
|
+
message: voiceInsufficientCreditsMessage("Voice input", voiceCreditStanding(balance))
|
|
13110
|
+
});
|
|
13111
|
+
}
|
|
13112
|
+
const refusal = await checkWorkspaceAllowance4(deps.db, {
|
|
13113
|
+
accountId,
|
|
13114
|
+
workspaceId,
|
|
13115
|
+
subjectId: initiatingHuman2(attribution)
|
|
13116
|
+
});
|
|
13117
|
+
if (refusal) {
|
|
13118
|
+
throw new TranscriptionBillingRefusedError({
|
|
13119
|
+
code: refusal.code,
|
|
13120
|
+
message: refusal.message,
|
|
13121
|
+
details: {
|
|
13122
|
+
scope: refusal.scope,
|
|
13123
|
+
resetsAt: refusal.resetsAt,
|
|
13124
|
+
...refusal.subjectId ? { subjectId: refusal.subjectId } : {}
|
|
13125
|
+
}
|
|
13126
|
+
});
|
|
13127
|
+
}
|
|
13128
|
+
if (deps.settings.usageLimitsMode === "static" || deps.settings.usageLimitsMode === "managed") {
|
|
13129
|
+
const cap = configuredStaticUsageLimits3(deps.settings).maxMonthlyCostMicrosPerAccount;
|
|
13130
|
+
if (cap) {
|
|
13131
|
+
const used = await sumUsageQuantity4(deps.db, {
|
|
13132
|
+
accountId,
|
|
13133
|
+
eventType: "model.cost",
|
|
13134
|
+
since: startOfUtcMonth3()
|
|
13135
|
+
});
|
|
13136
|
+
if (used >= cap) {
|
|
13137
|
+
throw new TranscriptionBillingRefusedError({
|
|
13138
|
+
code: "monthly_model_cost_limit",
|
|
13139
|
+
message: "The monthly model cost limit has been reached."
|
|
13140
|
+
});
|
|
13141
|
+
}
|
|
13142
|
+
}
|
|
13143
|
+
}
|
|
13144
|
+
},
|
|
13145
|
+
async settle(input) {
|
|
13146
|
+
if (!voiceInputCreditBillingActive2(deps.settings)) return { creditCostMicros: 0 };
|
|
13147
|
+
const { usage, basis } = voiceInputBillableUsage({
|
|
13148
|
+
pricing: input.pricing,
|
|
13149
|
+
usage: input.usage,
|
|
13150
|
+
trustedDurationSeconds: input.billing.trustedDurationSeconds
|
|
13151
|
+
});
|
|
13152
|
+
const cost = calculateVoiceInputCost(input.pricing, usage);
|
|
13153
|
+
return await settleVoiceInputUsage(deps.db, {
|
|
13154
|
+
accountId: input.accountId,
|
|
13155
|
+
workspaceId: input.workspaceId,
|
|
13156
|
+
providerId: input.providerId,
|
|
13157
|
+
model: input.model,
|
|
13158
|
+
billing: input.billing,
|
|
13159
|
+
usage,
|
|
13160
|
+
basis,
|
|
13161
|
+
providerCostMicros: cost.providerCostMicros,
|
|
13162
|
+
creditCostMicros: cost.creditCostMicros
|
|
13163
|
+
});
|
|
13164
|
+
}
|
|
13165
|
+
};
|
|
13166
|
+
}
|
|
13167
|
+
async function settleVoiceInputUsage(db, input) {
|
|
13168
|
+
const attribution = input.billing.attribution;
|
|
13169
|
+
const keys = voiceTranscriptionSettlementKeys({
|
|
13170
|
+
workspaceId: input.workspaceId,
|
|
13171
|
+
sourceId: input.billing.sourceId
|
|
13172
|
+
});
|
|
13173
|
+
const receipt2 = await recordUsageEvent4(db, {
|
|
13174
|
+
accountId: input.accountId,
|
|
13175
|
+
workspaceId: input.workspaceId,
|
|
13176
|
+
eventType: "model.cost",
|
|
13177
|
+
quantity: input.creditCostMicros,
|
|
13178
|
+
unit: "usd_micros",
|
|
13179
|
+
sourceResourceType: VOICE_TRANSCRIPTION_SOURCE_TYPE,
|
|
13180
|
+
sourceResourceId: input.billing.sourceId,
|
|
13181
|
+
idempotencyKey: keys.usageIdempotencyKey,
|
|
13182
|
+
// The API is the writer; the payer is the trusted attribution snapshot.
|
|
13183
|
+
initiator: { kind: "service", subjectId: "api:voice-input" },
|
|
13184
|
+
initiatorContext: { creditDebitAttribution: attribution }
|
|
13185
|
+
});
|
|
13186
|
+
const amount = Number(receipt2.quantity);
|
|
13187
|
+
if (amount > 0) {
|
|
13188
|
+
await applyCreditDebitAfterUse3(db, {
|
|
13189
|
+
accountId: input.accountId,
|
|
13190
|
+
workspaceId: input.workspaceId,
|
|
13191
|
+
type: VOICE_TRANSCRIPTION_DEBIT_TYPE,
|
|
13192
|
+
amountMicros: amount,
|
|
13193
|
+
sourceType: VOICE_TRANSCRIPTION_SOURCE_TYPE,
|
|
13194
|
+
sourceId: input.billing.sourceId,
|
|
13195
|
+
idempotencyKey: keys.debitIdempotencyKey,
|
|
13196
|
+
usage: VOICE_CREDIT_USAGE2,
|
|
13197
|
+
metadata: {
|
|
13198
|
+
providerId: input.providerId,
|
|
13199
|
+
model: input.model,
|
|
13200
|
+
basis: input.basis,
|
|
13201
|
+
providerCostMicros: input.providerCostMicros,
|
|
13202
|
+
...input.usage.kind === "tokens" ? {
|
|
13203
|
+
inputTokens: input.usage.inputTokens,
|
|
13204
|
+
audioInputTokens: input.usage.audioInputTokens,
|
|
13205
|
+
textInputTokens: input.usage.textInputTokens,
|
|
13206
|
+
outputTokens: input.usage.outputTokens
|
|
13207
|
+
} : { audioMilliseconds: Math.ceil(input.usage.seconds * 1e3) },
|
|
13208
|
+
...attribution.kind === "unknown" ? {} : creditDebitAttributionMetadata2(attribution)
|
|
13209
|
+
}
|
|
13210
|
+
});
|
|
13211
|
+
}
|
|
13212
|
+
return { creditCostMicros: amount };
|
|
13213
|
+
}
|
|
13214
|
+
async function reconcileUnsettledVoiceInputCharges(db, input) {
|
|
13215
|
+
const pending = await listUnsettledVoiceTranscriptionCharges(db, {
|
|
13216
|
+
accountId: input.accountId,
|
|
13217
|
+
workspaceId: input.workspaceId,
|
|
13218
|
+
...input.minAgeMilliseconds !== void 0 ? { minAgeMilliseconds: input.minAgeMilliseconds } : {}
|
|
13219
|
+
});
|
|
13220
|
+
for (const charge of pending) {
|
|
13221
|
+
await applyCreditDebitAfterUse3(db, {
|
|
13222
|
+
accountId: input.accountId,
|
|
13223
|
+
workspaceId: input.workspaceId,
|
|
13224
|
+
type: VOICE_TRANSCRIPTION_DEBIT_TYPE,
|
|
13225
|
+
amountMicros: charge.amountMicros,
|
|
13226
|
+
sourceType: VOICE_TRANSCRIPTION_SOURCE_TYPE,
|
|
13227
|
+
sourceId: charge.sourceId,
|
|
13228
|
+
idempotencyKey: voiceTranscriptionSettlementKeys({
|
|
13229
|
+
workspaceId: input.workspaceId,
|
|
13230
|
+
sourceId: charge.sourceId
|
|
13231
|
+
}).debitIdempotencyKey,
|
|
13232
|
+
usage: VOICE_CREDIT_USAGE2,
|
|
13233
|
+
metadata: {
|
|
13234
|
+
basis: "reconciled_receipt",
|
|
13235
|
+
...charge.attribution.kind === "unknown" ? {} : creditDebitAttributionMetadata2(charge.attribution)
|
|
13236
|
+
}
|
|
13237
|
+
});
|
|
13238
|
+
}
|
|
13239
|
+
return { reconciled: pending.length };
|
|
13240
|
+
}
|
|
13241
|
+
|
|
13242
|
+
// src/domain/web-search-billing.ts
|
|
13243
|
+
import {
|
|
13244
|
+
applyCreditDebitAfterUse as applyCreditDebitAfterUse4,
|
|
13245
|
+
checkWorkspaceAllowance as checkWorkspaceAllowance5,
|
|
13246
|
+
creditDebitAttributionForTurn as creditDebitAttributionForTurn2,
|
|
13247
|
+
creditDebitAttributionMetadata as creditDebitAttributionMetadata3,
|
|
13248
|
+
getSpendableCreditBalance as getSpendableCreditBalance5,
|
|
13249
|
+
recordUsageEvent as recordUsageEvent5,
|
|
13250
|
+
withRlsContext as withRlsContext2
|
|
13251
|
+
} from "@opengeni/db";
|
|
13252
|
+
var WEB_SEARCH_DEBIT_TYPE = "web_search_debit";
|
|
13253
|
+
var WEB_SEARCH_SOURCE_TYPE = "web_search";
|
|
13254
|
+
var WEB_SEARCH_BILLING_INITIATOR = "worker:web-search";
|
|
13255
|
+
function webSearchCreditBillingActive(settings) {
|
|
13256
|
+
return settings.billingMode === "stripe" || settings.usageLimitsMode === "managed";
|
|
13257
|
+
}
|
|
13258
|
+
var WebSearchBillingRefusedError = class extends Error {
|
|
13259
|
+
constructor(code, message) {
|
|
13260
|
+
super(message);
|
|
13261
|
+
this.code = code;
|
|
13262
|
+
this.name = "WebSearchBillingRefusedError";
|
|
13263
|
+
}
|
|
13264
|
+
};
|
|
13265
|
+
function createWebSearchBilling(deps) {
|
|
13266
|
+
const active = webSearchCreditBillingActive(deps.settings);
|
|
13267
|
+
const attributionByTurn = /* @__PURE__ */ new Map();
|
|
13268
|
+
const attributionFor = (scope) => {
|
|
13269
|
+
let attribution = attributionByTurn.get(scope.turnId);
|
|
13270
|
+
if (!attribution) {
|
|
13271
|
+
attribution = creditDebitAttributionForTurn2(deps.db, scope);
|
|
13272
|
+
attributionByTurn.set(scope.turnId, attribution);
|
|
13273
|
+
attribution.catch(() => attributionByTurn.delete(scope.turnId));
|
|
13274
|
+
}
|
|
13275
|
+
return attribution;
|
|
13276
|
+
};
|
|
13277
|
+
return {
|
|
13278
|
+
active,
|
|
13279
|
+
async admit(scope, expectedProviderMicros) {
|
|
13280
|
+
if (!active || expectedProviderMicros <= 0) return;
|
|
13281
|
+
const attribution = await attributionFor(scope);
|
|
13282
|
+
const balance = await getSpendableCreditBalance5(deps.db, scope.accountId);
|
|
13283
|
+
if (balance.balanceMicros <= 0) {
|
|
13284
|
+
throw new WebSearchBillingRefusedError(
|
|
13285
|
+
"insufficient_credits",
|
|
13286
|
+
"Web search needs Opengeni credits, and this account has none left."
|
|
13287
|
+
);
|
|
13288
|
+
}
|
|
13289
|
+
const refusal = await checkWorkspaceAllowance5(deps.db, {
|
|
13290
|
+
accountId: scope.accountId,
|
|
13291
|
+
workspaceId: scope.workspaceId,
|
|
13292
|
+
subjectId: attribution.kind === "turn" ? attribution.initiatingHumanSubjectId : null
|
|
13293
|
+
});
|
|
13294
|
+
if (refusal) throw new WebSearchBillingRefusedError("allowance_exhausted", refusal.message);
|
|
13295
|
+
},
|
|
13296
|
+
async settle(scope, cost) {
|
|
13297
|
+
const shared = {
|
|
13298
|
+
accountId: scope.accountId,
|
|
13299
|
+
workspaceId: scope.workspaceId,
|
|
13300
|
+
sessionId: scope.sessionId,
|
|
13301
|
+
turnId: scope.turnId,
|
|
13302
|
+
turnAttemptId: scope.attemptId,
|
|
13303
|
+
sourceResourceType: WEB_SEARCH_SOURCE_TYPE,
|
|
13304
|
+
sourceResourceId: `${scope.attemptId}:${cost.operationId}`
|
|
13305
|
+
};
|
|
13306
|
+
await recordUsageEvent5(deps.db, {
|
|
13307
|
+
...shared,
|
|
13308
|
+
eventType: `web_search.${cost.operation}_requests`,
|
|
13309
|
+
quantity: 1,
|
|
13310
|
+
unit: "request",
|
|
13311
|
+
idempotencyKey: `usage:web_search.${cost.operation}_requests:${shared.sourceResourceId}`
|
|
13312
|
+
});
|
|
13313
|
+
if (!active || cost.creditMicros <= 0) return;
|
|
13314
|
+
const attribution = await attributionFor(scope);
|
|
13315
|
+
await withRlsContext2(
|
|
13316
|
+
deps.db,
|
|
13317
|
+
{ accountId: scope.accountId, workspaceId: scope.workspaceId },
|
|
13318
|
+
async (tx) => {
|
|
13319
|
+
await recordUsageEvent5(tx, {
|
|
13320
|
+
...shared,
|
|
13321
|
+
eventType: "web_search.cost",
|
|
13322
|
+
quantity: cost.creditMicros,
|
|
13323
|
+
unit: "usd_micros",
|
|
13324
|
+
idempotencyKey: `usage:web_search.cost:${shared.sourceResourceId}`,
|
|
13325
|
+
initiator: { kind: "service", subjectId: WEB_SEARCH_BILLING_INITIATOR },
|
|
13326
|
+
initiatorContext: { creditDebitAttribution: attribution }
|
|
13327
|
+
});
|
|
13328
|
+
await applyCreditDebitAfterUse4(tx, {
|
|
13329
|
+
accountId: scope.accountId,
|
|
13330
|
+
workspaceId: scope.workspaceId,
|
|
13331
|
+
type: WEB_SEARCH_DEBIT_TYPE,
|
|
13332
|
+
amountMicros: cost.creditMicros,
|
|
13333
|
+
sourceType: WEB_SEARCH_SOURCE_TYPE,
|
|
13334
|
+
sourceId: shared.sourceResourceId,
|
|
13335
|
+
idempotencyKey: `credit:${WEB_SEARCH_DEBIT_TYPE}:${shared.sourceResourceId}`,
|
|
13336
|
+
metadata: {
|
|
13337
|
+
// turnId lets the allowance trigger resolve the causal human
|
|
13338
|
+
// from the immutable turn receipt.
|
|
13339
|
+
...creditDebitAttributionMetadata3(attribution),
|
|
13340
|
+
provider: cost.provider,
|
|
13341
|
+
operation: cost.operation,
|
|
13342
|
+
providerCostMicros: cost.providerMicros,
|
|
13343
|
+
marginBps: cost.marginBps,
|
|
13344
|
+
basis: cost.basis
|
|
13345
|
+
}
|
|
13346
|
+
});
|
|
13347
|
+
}
|
|
13348
|
+
);
|
|
13349
|
+
}
|
|
13350
|
+
};
|
|
13351
|
+
}
|
|
13352
|
+
|
|
13353
|
+
// src/workflow-wake-contract.ts
|
|
13354
|
+
var SESSION_WORKFLOW_WAKE_DISPATCHER_SCHEDULE_ID = "opengeni-session-workflow-wake-dispatcher";
|
|
13355
|
+
var SESSION_WORKFLOW_WAKE_DISPATCHER_WORKFLOW_TYPE = "sessionWorkflowWakeDispatcherWorkflow";
|
|
13356
|
+
var SESSION_WORKFLOW_WAKE_DISPATCHER_PERIOD_MS = 1e4;
|
|
13357
|
+
var TURN_ACTIVITY_CANCELLATION_HEARTBEAT_INTERVAL_MS = 500;
|
|
13358
|
+
|
|
13359
|
+
// src/user-presence.ts
|
|
13360
|
+
import { recordUserActivityPresence } from "@opengeni/db";
|
|
13361
|
+
var USER_PRESENCE_THROTTLE_MS = 6e4;
|
|
13362
|
+
var USER_PRESENCE_FLUSH_DELAY_MS = 5e3;
|
|
13363
|
+
var USER_PRESENCE_MAX_TRACKED = 5e4;
|
|
13364
|
+
var USER_PRESENCE_SUBJECT = /^user:[A-Za-z0-9_-]{8,128}$/;
|
|
13365
|
+
var PRESENCE_FLUSH_METRIC = {
|
|
13366
|
+
name: "opengeni_user_presence_flushes_total",
|
|
13367
|
+
help: "Batched user presence writes from this API process by outcome."
|
|
13368
|
+
};
|
|
13369
|
+
function createUserPresenceRecorder(options) {
|
|
13370
|
+
const throttleMs = options.throttleMs ?? USER_PRESENCE_THROTTLE_MS;
|
|
13371
|
+
const flushDelayMs = options.flushDelayMs ?? USER_PRESENCE_FLUSH_DELAY_MS;
|
|
13372
|
+
const maxTracked = options.maxTracked ?? USER_PRESENCE_MAX_TRACKED;
|
|
13373
|
+
const now = options.now ?? Date.now;
|
|
13374
|
+
const record3 = options.record ?? recordUserActivityPresence;
|
|
13375
|
+
const lastQueuedAt = /* @__PURE__ */ new Map();
|
|
13376
|
+
const pending = /* @__PURE__ */ new Set();
|
|
13377
|
+
let timer = null;
|
|
13378
|
+
let closed = false;
|
|
13379
|
+
let lastWarningAt = 0;
|
|
13380
|
+
let tail = Promise.resolve();
|
|
13381
|
+
const count = (outcome) => {
|
|
13382
|
+
try {
|
|
13383
|
+
options.observability?.incrementCounter({ ...PRESENCE_FLUSH_METRIC, labels: { outcome } });
|
|
13384
|
+
} catch {
|
|
13385
|
+
}
|
|
13386
|
+
};
|
|
13387
|
+
const prune = (at) => {
|
|
13388
|
+
for (const [subject, queuedAt] of lastQueuedAt) {
|
|
13389
|
+
if (at - queuedAt >= throttleMs && !pending.has(subject)) lastQueuedAt.delete(subject);
|
|
13390
|
+
}
|
|
13391
|
+
if (lastQueuedAt.size > maxTracked) {
|
|
13392
|
+
const excess = lastQueuedAt.size - maxTracked;
|
|
13393
|
+
let removed = 0;
|
|
13394
|
+
for (const subject of lastQueuedAt.keys()) {
|
|
13395
|
+
if (removed >= excess) break;
|
|
13396
|
+
if (pending.has(subject)) continue;
|
|
13397
|
+
lastQueuedAt.delete(subject);
|
|
13398
|
+
removed += 1;
|
|
13399
|
+
}
|
|
13400
|
+
}
|
|
13401
|
+
};
|
|
13402
|
+
const writeBatch = async () => {
|
|
13403
|
+
if (pending.size === 0) return;
|
|
13404
|
+
const batch = [...pending];
|
|
13405
|
+
pending.clear();
|
|
13406
|
+
try {
|
|
13407
|
+
await record3(options.db, batch);
|
|
13408
|
+
count("ok");
|
|
13409
|
+
} catch (error) {
|
|
13410
|
+
for (const subject of batch) lastQueuedAt.delete(subject);
|
|
13411
|
+
count("failed");
|
|
13412
|
+
const at = now();
|
|
13413
|
+
if (at - lastWarningAt >= throttleMs) {
|
|
13414
|
+
lastWarningAt = at;
|
|
13415
|
+
options.observability?.warn("user presence write failed", {
|
|
13416
|
+
errorClass: error instanceof Error ? error.name : "UnknownError"
|
|
13417
|
+
});
|
|
13418
|
+
}
|
|
13419
|
+
}
|
|
13420
|
+
};
|
|
13421
|
+
const flush = () => {
|
|
13422
|
+
if (timer) {
|
|
13423
|
+
clearTimeout(timer);
|
|
13424
|
+
timer = null;
|
|
13425
|
+
}
|
|
13426
|
+
tail = tail.then(writeBatch, writeBatch);
|
|
13427
|
+
return tail;
|
|
13428
|
+
};
|
|
13429
|
+
return {
|
|
13430
|
+
touch(subjectId) {
|
|
13431
|
+
if (closed || !USER_PRESENCE_SUBJECT.test(subjectId)) return;
|
|
13432
|
+
const at = now();
|
|
13433
|
+
const previous = lastQueuedAt.get(subjectId);
|
|
13434
|
+
if (previous !== void 0 && at - previous < throttleMs) return;
|
|
13435
|
+
lastQueuedAt.set(subjectId, at);
|
|
13436
|
+
pending.add(subjectId);
|
|
13437
|
+
if (lastQueuedAt.size > maxTracked) prune(at);
|
|
13438
|
+
if (!timer) {
|
|
13439
|
+
timer = setTimeout(() => {
|
|
13440
|
+
timer = null;
|
|
13441
|
+
void flush();
|
|
13442
|
+
}, flushDelayMs);
|
|
13443
|
+
timer.unref?.();
|
|
13444
|
+
}
|
|
13445
|
+
},
|
|
13446
|
+
flush,
|
|
13447
|
+
async close() {
|
|
13448
|
+
closed = true;
|
|
13449
|
+
await flush();
|
|
13450
|
+
}
|
|
13451
|
+
};
|
|
13452
|
+
}
|
|
13453
|
+
|
|
12784
13454
|
// src/goal-admission.ts
|
|
12785
13455
|
import {
|
|
12786
13456
|
isModelAvailableForNewSelection as isModelAvailableForNewSelection2,
|
|
@@ -12799,13 +13469,13 @@ import { getWorkspaceModelPolicy as getWorkspaceModelPolicy3 } from "@opengeni/d
|
|
|
12799
13469
|
// src/billing/agent-run-admission.ts
|
|
12800
13470
|
import {
|
|
12801
13471
|
canonicalizeConfiguredModelId as canonicalizeConfiguredModelId4,
|
|
12802
|
-
configuredStaticUsageLimits as
|
|
13472
|
+
configuredStaticUsageLimits as configuredStaticUsageLimits4
|
|
12803
13473
|
} from "@opengeni/config";
|
|
12804
13474
|
import {
|
|
12805
|
-
checkWorkspaceAllowance as
|
|
12806
|
-
getSpendableCreditBalance as
|
|
13475
|
+
checkWorkspaceAllowance as checkWorkspaceAllowance6,
|
|
13476
|
+
getSpendableCreditBalance as getSpendableCreditBalance6,
|
|
12807
13477
|
isCodexBilledTurn as isCodexBilledTurn2,
|
|
12808
|
-
sumUsageQuantity as
|
|
13478
|
+
sumUsageQuantity as sumUsageQuantity5
|
|
12809
13479
|
} from "@opengeni/db";
|
|
12810
13480
|
async function agentRunAdmissionDenial(services, input) {
|
|
12811
13481
|
const codexBilled = await isCodexBilledTurn2({
|
|
@@ -12829,7 +13499,7 @@ async function agentRunAdmissionDenial(services, input) {
|
|
|
12829
13499
|
});
|
|
12830
13500
|
if (!decision.allowed) return "insufficient_credits";
|
|
12831
13501
|
} else {
|
|
12832
|
-
const balance = await
|
|
13502
|
+
const balance = await getSpendableCreditBalance6(
|
|
12833
13503
|
services.db,
|
|
12834
13504
|
input.accountId,
|
|
12835
13505
|
canonicalizeConfiguredModelId4(services.settings, input.model)
|
|
@@ -12838,7 +13508,7 @@ async function agentRunAdmissionDenial(services, input) {
|
|
|
12838
13508
|
}
|
|
12839
13509
|
}
|
|
12840
13510
|
if (!externallyBilled) {
|
|
12841
|
-
const refusal = await
|
|
13511
|
+
const refusal = await checkWorkspaceAllowance6(services.db, {
|
|
12842
13512
|
accountId: input.accountId,
|
|
12843
13513
|
workspaceId: input.workspaceId,
|
|
12844
13514
|
subjectId: input.initiatingHumanSubjectId ?? null
|
|
@@ -12848,22 +13518,22 @@ async function agentRunAdmissionDenial(services, input) {
|
|
|
12848
13518
|
if (services.settings.usageLimitsMode !== "static" && services.settings.usageLimitsMode !== "managed") {
|
|
12849
13519
|
return null;
|
|
12850
13520
|
}
|
|
12851
|
-
const limits =
|
|
13521
|
+
const limits = configuredStaticUsageLimits4(services.settings);
|
|
12852
13522
|
if (!externallyBilled && limits.maxMonthlyCostMicrosPerAccount) {
|
|
12853
|
-
const used = await
|
|
13523
|
+
const used = await sumUsageQuantity5(services.db, {
|
|
12854
13524
|
accountId: input.accountId,
|
|
12855
13525
|
eventType: "model.cost",
|
|
12856
|
-
since:
|
|
13526
|
+
since: startOfUtcMonth4()
|
|
12857
13527
|
});
|
|
12858
13528
|
if (used >= limits.maxMonthlyCostMicrosPerAccount) {
|
|
12859
13529
|
return "monthly_model_cost_limit";
|
|
12860
13530
|
}
|
|
12861
13531
|
}
|
|
12862
13532
|
if (limits.maxMonthlyAgentRunsPerWorkspace) {
|
|
12863
|
-
const used = await
|
|
13533
|
+
const used = await sumUsageQuantity5(services.db, {
|
|
12864
13534
|
workspaceId: input.workspaceId,
|
|
12865
13535
|
eventType: "agent_run.created",
|
|
12866
|
-
since:
|
|
13536
|
+
since: startOfUtcMonth4()
|
|
12867
13537
|
});
|
|
12868
13538
|
if (used + input.requestedAgentRuns > limits.maxMonthlyAgentRunsPerWorkspace) {
|
|
12869
13539
|
return "monthly_agent_run_limit";
|
|
@@ -12871,7 +13541,7 @@ async function agentRunAdmissionDenial(services, input) {
|
|
|
12871
13541
|
}
|
|
12872
13542
|
return null;
|
|
12873
13543
|
}
|
|
12874
|
-
function
|
|
13544
|
+
function startOfUtcMonth4() {
|
|
12875
13545
|
const now = /* @__PURE__ */ new Date();
|
|
12876
13546
|
return new Date(Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), 1));
|
|
12877
13547
|
}
|
|
@@ -15581,7 +16251,7 @@ import {
|
|
|
15581
16251
|
SCHEDULED_TASK_OCCURRENCE_PAYLOAD_INGRESS_HEADROOM_BYTES
|
|
15582
16252
|
} from "@opengeni/contracts";
|
|
15583
16253
|
import {
|
|
15584
|
-
allowedFirstPartyMcpToolsForSession as
|
|
16254
|
+
allowedFirstPartyMcpToolsForSession as allowedFirstPartyMcpToolsForSession5,
|
|
15585
16255
|
resolveFirstPartyMcpToolPolicy as resolveFirstPartyMcpToolPolicy2
|
|
15586
16256
|
} from "@opengeni/config";
|
|
15587
16257
|
import {
|
|
@@ -15689,7 +16359,7 @@ async function scheduledConnectionTools(db, workspaceId, settings, target, taskT
|
|
|
15689
16359
|
}).toolRefs;
|
|
15690
16360
|
}
|
|
15691
16361
|
function scheduledConnectionSurfaceEligibility(settings, target) {
|
|
15692
|
-
const tools = target?.firstPartyMcpTools
|
|
16362
|
+
const tools = allowedFirstPartyMcpToolsForSession5(settings, target?.firstPartyMcpTools);
|
|
15693
16363
|
const permissions = target?.firstPartyMcpPermissions ?? DEFAULT_FIRST_PARTY_MCP_PERMISSIONS3;
|
|
15694
16364
|
return {
|
|
15695
16365
|
googleDrivePublicationEnabled: tools.includes("editable_artifact_export") && tools.includes("editable_artifact_export_status") && permissions.includes("artifacts:read") && permissions.includes("artifacts:publish"),
|
|
@@ -16014,7 +16684,7 @@ async function frozenScheduledTaskCreatorPolicy(input) {
|
|
|
16014
16684
|
restricted ||= Boolean(
|
|
16015
16685
|
turnPolicy.kind === "valid" && turnPolicy.policy.credentialRestriction === "developer_setup" || sessionPolicy.kind === "valid" && sessionPolicy.policy.credentialRestriction === "developer_setup"
|
|
16016
16686
|
);
|
|
16017
|
-
const firstPartyMcpTools =
|
|
16687
|
+
const firstPartyMcpTools = allowedFirstPartyMcpToolsForSession5(
|
|
16018
16688
|
input.settings,
|
|
16019
16689
|
session.firstPartyMcpTools
|
|
16020
16690
|
);
|
|
@@ -17315,7 +17985,7 @@ function trimmedScheduledTaskName(name) {
|
|
|
17315
17985
|
|
|
17316
17986
|
// src/domain/scheduled-task-access.ts
|
|
17317
17987
|
import {
|
|
17318
|
-
allowedFirstPartyMcpToolsForSession as
|
|
17988
|
+
allowedFirstPartyMcpToolsForSession as allowedFirstPartyMcpToolsForSession6,
|
|
17319
17989
|
resolveFirstPartyMcpToolPolicy as resolveFirstPartyMcpToolPolicy3
|
|
17320
17990
|
} from "@opengeni/config";
|
|
17321
17991
|
import {
|
|
@@ -17420,7 +18090,7 @@ function sameMembers(left, right) {
|
|
|
17420
18090
|
function planScheduledTaskOpenGeniTools(input) {
|
|
17421
18091
|
const stored = input.creatorPolicy?.firstPartyMcpTools;
|
|
17422
18092
|
if (!stored) return { missing: [], policy: null };
|
|
17423
|
-
const effective = new Set(
|
|
18093
|
+
const effective = new Set(allowedFirstPartyMcpToolsForSession6(input.settings, stored));
|
|
17424
18094
|
const missing = resolveFirstPartyMcpToolPolicy3(input.settings).default.filter(
|
|
17425
18095
|
(tool) => !effective.has(tool) && !input.leaveOut?.has(tool)
|
|
17426
18096
|
);
|
|
@@ -17939,7 +18609,7 @@ function orderScheduledTaskAccessAttention(items) {
|
|
|
17939
18609
|
}
|
|
17940
18610
|
|
|
17941
18611
|
// src/domain/insights.ts
|
|
17942
|
-
import { configuredStaticUsageLimits as
|
|
18612
|
+
import { configuredStaticUsageLimits as configuredStaticUsageLimits5 } from "@opengeni/config";
|
|
17943
18613
|
import {
|
|
17944
18614
|
WorkspaceInsightsSnapshot
|
|
17945
18615
|
} from "@opengeni/contracts";
|
|
@@ -18070,7 +18740,7 @@ function cacheHitPct(cached2, input) {
|
|
|
18070
18740
|
function startOfUtcDay(date) {
|
|
18071
18741
|
return new Date(Date.UTC(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate()));
|
|
18072
18742
|
}
|
|
18073
|
-
function
|
|
18743
|
+
function startOfUtcMonth5(date) {
|
|
18074
18744
|
return new Date(Date.UTC(date.getUTCFullYear(), date.getUTCMonth(), 1));
|
|
18075
18745
|
}
|
|
18076
18746
|
function startOfUtcYear(date) {
|
|
@@ -18099,7 +18769,7 @@ function resolveRangeWindow(range, now) {
|
|
|
18099
18769
|
cacheSeriesLabel = "Cache hit % / UTC day";
|
|
18100
18770
|
break;
|
|
18101
18771
|
case "month":
|
|
18102
|
-
since =
|
|
18772
|
+
since = startOfUtcMonth5(now);
|
|
18103
18773
|
rangeLabel = "This month (UTC)";
|
|
18104
18774
|
priorLabel = "Prior equal window";
|
|
18105
18775
|
seriesLabel = "Credit $ / UTC day";
|
|
@@ -18230,7 +18900,7 @@ async function getWorkspaceInsights(db, settings, input, observePhase) {
|
|
|
18230
18900
|
until: window.until,
|
|
18231
18901
|
priorSince: window.priorSince,
|
|
18232
18902
|
priorUntil: window.priorUntil,
|
|
18233
|
-
monthSince:
|
|
18903
|
+
monthSince: startOfUtcMonth5(now),
|
|
18234
18904
|
granularity: input.range === "today" ? "hour" : "day",
|
|
18235
18905
|
warmGroupLimit: 24
|
|
18236
18906
|
})
|
|
@@ -18473,7 +19143,7 @@ async function getWorkspaceInsights(db, settings, input, observePhase) {
|
|
|
18473
19143
|
billing: fact ? billingPathOf(fact.billingPath) : null
|
|
18474
19144
|
};
|
|
18475
19145
|
});
|
|
18476
|
-
const limits =
|
|
19146
|
+
const limits = configuredStaticUsageLimits5(settings);
|
|
18477
19147
|
const floor = floorRows.filter((row) => {
|
|
18478
19148
|
if (!model) return true;
|
|
18479
19149
|
return row.model === model;
|
|
@@ -20721,7 +21391,7 @@ async function submitComposerDraftForRequest(deps, grant, workspaceId, sessionId
|
|
|
20721
21391
|
import { DraftTimelineAnnotations as DraftTimelineAnnotations3 } from "@opengeni/contracts";
|
|
20722
21392
|
import { HTTPException as HTTPException30 } from "hono/http-exception";
|
|
20723
21393
|
import {
|
|
20724
|
-
checkWorkspaceAllowance as
|
|
21394
|
+
checkWorkspaceAllowance as checkWorkspaceAllowance7,
|
|
20725
21395
|
deleteSessionQueueItemInTransaction,
|
|
20726
21396
|
editQueuedTurnInTransaction,
|
|
20727
21397
|
getComposerDraftInTransaction as getComposerDraftInTransaction2,
|
|
@@ -20819,7 +21489,7 @@ async function assertFreshAgentCommandAllowance(tx, deps, context, targetSession
|
|
|
20819
21489
|
}
|
|
20820
21490
|
}
|
|
20821
21491
|
const frozen = await frozenInitiatorForCommandActor2(tx, context.workspaceId, agentActor(context));
|
|
20822
|
-
const refusal = await
|
|
21492
|
+
const refusal = await checkWorkspaceAllowance7(tx, {
|
|
20823
21493
|
accountId: target.accountId,
|
|
20824
21494
|
workspaceId: context.workspaceId,
|
|
20825
21495
|
subjectId: initiatingHumanForAllowance(frozen)
|
|
@@ -22142,7 +22812,6 @@ import { createPinnedIntegrationTransport as createPinnedIntegrationTransport2 }
|
|
|
22142
22812
|
import {
|
|
22143
22813
|
buildConnectionTokenResolver,
|
|
22144
22814
|
getConnectionMetadata as getConnectionMetadata5,
|
|
22145
|
-
listConnectionsMetadata as listConnectionsMetadata4,
|
|
22146
22815
|
listEnabledMcpCapabilityServers as listEnabledMcpCapabilityServers3,
|
|
22147
22816
|
listConnectorToolPermissionPolicies,
|
|
22148
22817
|
listChangedConnectorToolPermissions,
|
|
@@ -22155,6 +22824,7 @@ import {
|
|
|
22155
22824
|
import { HTTPException as HTTPException32 } from "hono/http-exception";
|
|
22156
22825
|
import {
|
|
22157
22826
|
GMAIL_REST_MCP_TOOLS,
|
|
22827
|
+
gmailToolAvailableOnDeployment,
|
|
22158
22828
|
gmailToolSupportsScopes,
|
|
22159
22829
|
isOfficialGmailMcpConfig,
|
|
22160
22830
|
isOfficialSlackMcpConfig
|
|
@@ -22240,11 +22910,12 @@ async function resolveTarget2(input) {
|
|
|
22240
22910
|
throw new HTTPException32(403, {
|
|
22241
22911
|
message: "Only the authenticated connection owner may manage personal tool permissions"
|
|
22242
22912
|
});
|
|
22243
|
-
const
|
|
22244
|
-
|
|
22245
|
-
input.
|
|
22246
|
-
|
|
22247
|
-
|
|
22913
|
+
const source = personalConnectionDelegationSourceForGrant(input.grant);
|
|
22914
|
+
const visible = ref ? await visibleMcpAccountConnections(input.db, {
|
|
22915
|
+
accountId: input.grant.accountId,
|
|
22916
|
+
workspaceId: input.workspaceId,
|
|
22917
|
+
source: input.personalOwnerVerified && source.kind === "subject" ? source : { kind: "none" }
|
|
22918
|
+
}) : [];
|
|
22248
22919
|
const bindings = mcpAccountBindingsFromVisibleConnections({
|
|
22249
22920
|
accountId: input.grant.accountId,
|
|
22250
22921
|
workspaceId: input.workspaceId,
|
|
@@ -22286,15 +22957,20 @@ async function resolveTarget2(input) {
|
|
|
22286
22957
|
listedTools: null
|
|
22287
22958
|
};
|
|
22288
22959
|
}
|
|
22289
|
-
function reviewedConnectorToolCatalog(server, grantedScopes) {
|
|
22290
|
-
const tools = isOfficialGmailMcpConfig(server.url, server.connectionRef) ? GMAIL_REST_MCP_TOOLS.filter(
|
|
22960
|
+
function reviewedConnectorToolCatalog(server, grantedScopes, deployment) {
|
|
22961
|
+
const tools = isOfficialGmailMcpConfig(server.url, server.connectionRef) ? GMAIL_REST_MCP_TOOLS.filter(
|
|
22962
|
+
(tool) => gmailToolSupportsScopes(tool.name, grantedScopes) && gmailToolAvailableOnDeployment(tool.name, {
|
|
22963
|
+
watchTopicName: deployment.gmailWatchTopicName
|
|
22964
|
+
})
|
|
22965
|
+
) : isOfficialSlackMcpConfig(server.url, server.connectionRef) ? slackRestMcpToolsForScopes(grantedScopes) : null;
|
|
22291
22966
|
return tools?.filter((tool) => !server.allowedTools || server.allowedTools.includes(tool.name)) ?? null;
|
|
22292
22967
|
}
|
|
22293
22968
|
async function listTools(input, target) {
|
|
22294
22969
|
if (target.listedTools !== null) return target.listedTools;
|
|
22295
22970
|
const reviewed = reviewedConnectorToolCatalog(
|
|
22296
22971
|
target.server,
|
|
22297
|
-
target.connection?.grantedScopes ?? []
|
|
22972
|
+
target.connection?.grantedScopes ?? [],
|
|
22973
|
+
input.settings
|
|
22298
22974
|
);
|
|
22299
22975
|
if (reviewed !== null) return reviewed;
|
|
22300
22976
|
let headers = { ...target.server.headers };
|
|
@@ -22303,7 +22979,9 @@ async function listTools(input, target) {
|
|
|
22303
22979
|
input.db,
|
|
22304
22980
|
input.settings
|
|
22305
22981
|
)({
|
|
22306
|
-
|
|
22982
|
+
// The selected account's credentials remain in its origin workspace;
|
|
22983
|
+
// approval preferences remain scoped to the workspace being configured.
|
|
22984
|
+
workspaceId: target.connection?.workspaceId ?? input.workspaceId,
|
|
22307
22985
|
...target.connection?.subjectId ? { subjectId: input.grant.subjectId } : {},
|
|
22308
22986
|
serverId: target.server.id,
|
|
22309
22987
|
toolName: "tools/list",
|
|
@@ -22560,7 +23238,11 @@ export {
|
|
|
22560
23238
|
PR_REVIEW_AUTOMATION_SETUP,
|
|
22561
23239
|
ProductionEditableArtifactSnapshotVerifier,
|
|
22562
23240
|
PublicSkillSearchError,
|
|
23241
|
+
REALTIME_VOICE_DEBIT_TYPE,
|
|
23242
|
+
REALTIME_VOICE_INSUFFICIENT_CREDITS_MESSAGE,
|
|
23243
|
+
REALTIME_VOICE_SOURCE_TYPE,
|
|
22563
23244
|
RIG_DEFAULT_VARIABLE_SET_LOAD_CONCURRENCY,
|
|
23245
|
+
RealtimeVoiceUnavailableError,
|
|
22564
23246
|
SESSION_AUTHORIZATION_DEFAULT_REAUTHORIZE_MS,
|
|
22565
23247
|
SESSION_CREATED_BY_KINDS,
|
|
22566
23248
|
SESSION_WORKFLOW_WAKE_DISPATCHER_PERIOD_MS,
|
|
@@ -22579,14 +23261,20 @@ export {
|
|
|
22579
23261
|
SystemEditableArtifactLiveScheduler,
|
|
22580
23262
|
TRANSCRIPTION_PROVIDER_REQUEST_TIMEOUT_MILLISECONDS,
|
|
22581
23263
|
TURN_ACTIVITY_CANCELLATION_HEARTBEAT_INTERVAL_MS,
|
|
23264
|
+
TranscriptionBillingRefusedError,
|
|
22582
23265
|
TranscriptionServiceError,
|
|
22583
23266
|
USER_PRESENCE_FLUSH_DELAY_MS,
|
|
22584
23267
|
USER_PRESENCE_THROTTLE_MS,
|
|
22585
23268
|
VIDEO_GENERATION_ADAPTER_VERSION,
|
|
22586
23269
|
VIDEO_GENERATION_MODEL_CATALOG,
|
|
23270
|
+
VOICE_INPUT_DEBIT_TYPE,
|
|
23271
|
+
VOICE_INPUT_SOURCE_TYPE,
|
|
23272
|
+
WEB_SEARCH_DEBIT_TYPE,
|
|
23273
|
+
WEB_SEARCH_SOURCE_TYPE,
|
|
22587
23274
|
WORKSPACE_INSIGHTS_MODEL_FILTER_MAX_UTF8_BYTES,
|
|
22588
23275
|
WORKSPACE_INSIGHTS_PROVIDER_FILTER_MAX_UTF8_BYTES,
|
|
22589
23276
|
WebCryptoEditableArtifactLiveTokens,
|
|
23277
|
+
WebSearchBillingRefusedError,
|
|
22590
23278
|
WorkspaceInsightsFilterValidationError,
|
|
22591
23279
|
acceptSessionUserMessage,
|
|
22592
23280
|
acceptSessionUserMessageWithOutcome,
|
|
@@ -22604,6 +23292,7 @@ export {
|
|
|
22604
23292
|
appendRigSetupCommand,
|
|
22605
23293
|
applyCapabilityEnablement,
|
|
22606
23294
|
applyGitHubRepositoryBindings,
|
|
23295
|
+
applyOperatorOAuthClientRequirement,
|
|
22607
23296
|
applySessionAgentConfigWriteThrough,
|
|
22608
23297
|
approveSkill,
|
|
22609
23298
|
assertAllowedEnvironmentVariableName,
|
|
@@ -22679,12 +23368,15 @@ export {
|
|
|
22679
23368
|
createCompanyProfileDurableLearningAdapter,
|
|
22680
23369
|
createGitHubSkillSourceClient,
|
|
22681
23370
|
createPublicSkillSearchClient,
|
|
23371
|
+
createRealtimeVoiceBilling,
|
|
22682
23372
|
createRigForApi,
|
|
22683
23373
|
createRigVersionForApi,
|
|
22684
23374
|
createSessionForRequest,
|
|
22685
23375
|
createSessionForRequestWithOutcome,
|
|
22686
23376
|
createUserPresenceRecorder,
|
|
22687
23377
|
createValidatedScheduledTask,
|
|
23378
|
+
createVoiceInputBilling,
|
|
23379
|
+
createWebSearchBilling,
|
|
22688
23380
|
creationInitiatorForGrant,
|
|
22689
23381
|
creditsDefaultSessionModel,
|
|
22690
23382
|
decodeEditableArtifactBrokerHint,
|
|
@@ -22697,6 +23389,7 @@ export {
|
|
|
22697
23389
|
deleteHumanQueuePrompt,
|
|
22698
23390
|
deleteRigForApi,
|
|
22699
23391
|
deleteScheduledTaskLifecycle,
|
|
23392
|
+
deploymentRealtimeVoice,
|
|
22700
23393
|
describeConfiguredExportFormats,
|
|
22701
23394
|
describeEditableArtifactExportFormats,
|
|
22702
23395
|
directPersonalConnectionSubjectId,
|
|
@@ -22920,6 +23613,8 @@ export {
|
|
|
22920
23613
|
readSessionAttachmentFiles,
|
|
22921
23614
|
readSessionLineage,
|
|
22922
23615
|
readSkill,
|
|
23616
|
+
realtimeVoiceOfferProblem,
|
|
23617
|
+
reconcileUnsettledVoiceInputCharges,
|
|
22923
23618
|
recordManagedAuthLoggedFailure,
|
|
22924
23619
|
recordRigAuditEvent,
|
|
22925
23620
|
recordSessionCreated,
|
|
@@ -23056,6 +23751,7 @@ export {
|
|
|
23056
23751
|
settingsWithRigProviderImage,
|
|
23057
23752
|
settingsWithSessionMcpServerMetadata,
|
|
23058
23753
|
settingsWithWorkspaceSandboxImage,
|
|
23754
|
+
settleVoiceInputUsage,
|
|
23059
23755
|
signedJsonAutomationAdapter,
|
|
23060
23756
|
skillBundleHash,
|
|
23061
23757
|
stableJson,
|
|
@@ -23113,6 +23809,10 @@ export {
|
|
|
23113
23809
|
videoGenerationProviderIdempotencyKey,
|
|
23114
23810
|
videoGenerationRequestDigest,
|
|
23115
23811
|
visibleMcpAccountConnections,
|
|
23812
|
+
voiceCreditStanding,
|
|
23813
|
+
voiceInputBillableUsage,
|
|
23814
|
+
voiceInsufficientCreditsMessage,
|
|
23815
|
+
webSearchCreditBillingActive,
|
|
23116
23816
|
withAgentConfigHttpErrors,
|
|
23117
23817
|
withDefaultEnabledCapabilityMcpTools,
|
|
23118
23818
|
withFrozenPersonalConnectionDelegations,
|