@opengeni/core 1.3.0 → 1.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/domain/connector-tool-permissions.d.ts +1 -1
- package/dist/domain/knowledge-messages.d.ts +2 -2
- package/dist/domain/realtime-voice-billing.d.ts +81 -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-tool-policy.d.ts +4 -4
- package/dist/domain/voice-input-billing.d.ts +76 -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 +790 -142
- package/dist/index.js.map +1 -1
- package/dist/transcription.d.ts +119 -2
- package/package.json +12 -12
- package/src/domain/connector-tool-permissions.ts +24 -8
- package/src/domain/realtime-voice-billing.ts +330 -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-tool-policy.ts +14 -2
- package/src/domain/sessions.ts +15 -2
- package/src/domain/voice-input-billing.ts +310 -0
- package/src/domain/web-search-billing.ts +154 -0
- package/src/index.ts +3 -0
- 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
|
|
@@ -4834,6 +4835,7 @@ import {
|
|
|
4834
4835
|
ORGANIZATION_GATEWAY_MODEL_ID_PREFIX as ORGANIZATION_GATEWAY_MODEL_ID_PREFIX2,
|
|
4835
4836
|
ORGANIZATION_OPENROUTER_MODEL_ID_PREFIX as ORGANIZATION_OPENROUTER_MODEL_ID_PREFIX2,
|
|
4836
4837
|
allowedFirstPartyMcpToolsForSession as allowedFirstPartyMcpToolsForSession3,
|
|
4838
|
+
deploymentUnavailableFirstPartyMcpTools,
|
|
4837
4839
|
resolveFirstPartyMcpToolPolicy,
|
|
4838
4840
|
policyProviderIdForModel,
|
|
4839
4841
|
resolveTurnExecutionPolicyV1,
|
|
@@ -5370,6 +5372,7 @@ import {
|
|
|
5370
5372
|
readActiveSandbox,
|
|
5371
5373
|
retainWorkspaceMutationProcess,
|
|
5372
5374
|
SandboxRetainedProcessPromotionFencedError,
|
|
5375
|
+
SandboxWorkspaceMutationOutputRejectedError,
|
|
5373
5376
|
retainedProcessSettlementIdentity,
|
|
5374
5377
|
settleRetainedProcess,
|
|
5375
5378
|
verifyDirectWorkspaceMutationSettlement,
|
|
@@ -5382,6 +5385,7 @@ import {
|
|
|
5382
5385
|
NatsControlRpc,
|
|
5383
5386
|
NatsOpStreamTransport,
|
|
5384
5387
|
RoutingSandboxSession,
|
|
5388
|
+
RoutingMutationOutputRejectedError,
|
|
5385
5389
|
resolveModalCheckpointProviderBindingForSession
|
|
5386
5390
|
} from "@opengeni/runtime/sandbox";
|
|
5387
5391
|
var retainWorkspaceProviderCommand = createProviderCommandRetainer(
|
|
@@ -5540,22 +5544,35 @@ function wrapChannelABoxWithRouting(services, ids, established) {
|
|
|
5540
5544
|
throw new Error(
|
|
5541
5545
|
"Outcome-unknown command settlement requires its exact retained invocation"
|
|
5542
5546
|
);
|
|
5543
|
-
|
|
5544
|
-
|
|
5545
|
-
|
|
5546
|
-
|
|
5547
|
-
|
|
5548
|
-
|
|
5549
|
-
|
|
5550
|
-
|
|
5551
|
-
|
|
5552
|
-
|
|
5553
|
-
|
|
5554
|
-
|
|
5555
|
-
|
|
5556
|
-
|
|
5557
|
-
|
|
5558
|
-
|
|
5547
|
+
try {
|
|
5548
|
+
await verifyDirectWorkspaceMutationSettlement(db, {
|
|
5549
|
+
accountId: ids.accountId,
|
|
5550
|
+
workspaceId: ids.workspaceId,
|
|
5551
|
+
sessionId: ids.sessionId,
|
|
5552
|
+
requestId: ids.directRequest.requestId,
|
|
5553
|
+
holderId: ids.directRequest.holderId,
|
|
5554
|
+
initiatorSubjectId: ids.resourceSubjectId,
|
|
5555
|
+
sandboxGroupId: homeLease.sandboxGroupId,
|
|
5556
|
+
expectedEpoch: backend.leaseEpoch,
|
|
5557
|
+
expectedInstanceId: backend.providerInstanceId,
|
|
5558
|
+
routeTargetId: exactAdmission.routeTargetId,
|
|
5559
|
+
routeEpoch: exactAdmission.routeEpoch,
|
|
5560
|
+
admission: exactAdmission,
|
|
5561
|
+
operation: op,
|
|
5562
|
+
outcome
|
|
5563
|
+
});
|
|
5564
|
+
} catch (error) {
|
|
5565
|
+
if (error instanceof SandboxWorkspaceMutationOutputRejectedError && error.matchesPhysicalSettlement({
|
|
5566
|
+
accountId: ids.accountId,
|
|
5567
|
+
workspaceId: ids.workspaceId,
|
|
5568
|
+
admission: exactAdmission,
|
|
5569
|
+
operation: op,
|
|
5570
|
+
outcome
|
|
5571
|
+
})) {
|
|
5572
|
+
throw new RoutingMutationOutputRejectedError(op, error.code, { cause: error });
|
|
5573
|
+
}
|
|
5574
|
+
throw error;
|
|
5575
|
+
}
|
|
5559
5576
|
} : void 0;
|
|
5560
5577
|
const beforeProcessMutation = homeLease ? async ({
|
|
5561
5578
|
op,
|
|
@@ -5578,15 +5595,28 @@ function wrapChannelABoxWithRouting(services, ids, established) {
|
|
|
5578
5595
|
if (!admission || typeof admission !== "object" || typeof admission.id !== "string" || typeof admission.workspaceGeneration !== "number") {
|
|
5579
5596
|
throw new Error("API retained-process mutation settlement lacked its exact admission");
|
|
5580
5597
|
}
|
|
5581
|
-
|
|
5582
|
-
|
|
5583
|
-
|
|
5584
|
-
|
|
5585
|
-
|
|
5586
|
-
|
|
5587
|
-
|
|
5588
|
-
|
|
5589
|
-
|
|
5598
|
+
try {
|
|
5599
|
+
await verifyRetainedProcessMutationSettlement(db, {
|
|
5600
|
+
accountId: ids.accountId,
|
|
5601
|
+
workspaceId: ids.workspaceId,
|
|
5602
|
+
sessionId: ids.sessionId,
|
|
5603
|
+
processId: process.id,
|
|
5604
|
+
admission,
|
|
5605
|
+
operation: op,
|
|
5606
|
+
outcome
|
|
5607
|
+
});
|
|
5608
|
+
} catch (error) {
|
|
5609
|
+
if (error instanceof SandboxWorkspaceMutationOutputRejectedError && error.matchesPhysicalSettlement({
|
|
5610
|
+
accountId: ids.accountId,
|
|
5611
|
+
workspaceId: ids.workspaceId,
|
|
5612
|
+
admission,
|
|
5613
|
+
operation: op,
|
|
5614
|
+
outcome
|
|
5615
|
+
})) {
|
|
5616
|
+
throw new RoutingMutationOutputRejectedError(op, error.code, { cause: error });
|
|
5617
|
+
}
|
|
5618
|
+
throw error;
|
|
5619
|
+
}
|
|
5590
5620
|
} : void 0;
|
|
5591
5621
|
const settleProcess = homeLease ? async ({
|
|
5592
5622
|
backend,
|
|
@@ -7802,7 +7832,8 @@ import {
|
|
|
7802
7832
|
allowedFirstPartyMcpToolsForSession as allowedFirstPartyMcpToolsForSession2,
|
|
7803
7833
|
codeSearchDeploymentPolicy,
|
|
7804
7834
|
resolveModelProviderForTurn as resolveModelProviderForTurn2,
|
|
7805
|
-
resolveFirstPartyDelegationSecret as resolveFirstPartyDelegationSecret2
|
|
7835
|
+
resolveFirstPartyDelegationSecret as resolveFirstPartyDelegationSecret2,
|
|
7836
|
+
webSearchToolPlan
|
|
7806
7837
|
} from "@opengeni/config";
|
|
7807
7838
|
import {
|
|
7808
7839
|
AGENT_SKILL_MANAGE_TOOL_NAMES,
|
|
@@ -8076,9 +8107,14 @@ function sessionEffectiveToolProjectionInput(session, toolRefs, context) {
|
|
|
8076
8107
|
const sandboxAvailable = context !== void 0 && sandboxBackend !== "none";
|
|
8077
8108
|
const supportsImages = model?.model.api === "responses" && model.model.capabilities.inputModalities.includes("image");
|
|
8078
8109
|
const hostedToolNames = [];
|
|
8110
|
+
const webSearchPlan = context ? webSearchToolPlan(context.settings, {
|
|
8111
|
+
hostedWebSearch: model?.model.hostedWebSearch ?? context.settings.webSearchEnabled,
|
|
8112
|
+
transportHostedSearch: model?.provider.kind === "xai-subscription"
|
|
8113
|
+
}) : { hostedWebSearch: false, providerTools: [] };
|
|
8079
8114
|
if (model?.provider.kind === "xai-subscription") {
|
|
8080
8115
|
if (context?.settings.webSearchEnabled) hostedToolNames.push("web_search", "x_search");
|
|
8081
|
-
} else if (model?.model.hostedWebSearch
|
|
8116
|
+
} else if (model?.model.hostedWebSearch && webSearchPlan.hostedWebSearch)
|
|
8117
|
+
hostedToolNames.push("web_search");
|
|
8082
8118
|
const mediaAttachment = context?.mediaAttachments?.get(session.id) ?? (context?.objectStorageAvailable === false ? { image: null, video: false } : void 0);
|
|
8083
8119
|
const media = resolveAgentMediaToolSurface(session.agent, mediaAttachment);
|
|
8084
8120
|
hostedToolNames.push(...media.hosted);
|
|
@@ -8087,7 +8123,8 @@ function sessionEffectiveToolProjectionInput(session, toolRefs, context) {
|
|
|
8087
8123
|
"list_models",
|
|
8088
8124
|
"skill_read",
|
|
8089
8125
|
...AGENT_SKILL_MANAGE_TOOL_NAMES,
|
|
8090
|
-
...media.runtime
|
|
8126
|
+
...media.runtime,
|
|
8127
|
+
...webSearchPlan.providerTools
|
|
8091
8128
|
] : [];
|
|
8092
8129
|
const sandboxToolNames = sandboxAvailable ? [
|
|
8093
8130
|
"exec_command",
|
|
@@ -8146,6 +8183,7 @@ function sessionEffectiveToolProjectionInput(session, toolRefs, context) {
|
|
|
8146
8183
|
"skill_read",
|
|
8147
8184
|
"request_human_input",
|
|
8148
8185
|
"list_models",
|
|
8186
|
+
...webSearchPlan.providerTools,
|
|
8149
8187
|
...toolRefs.some((ref) => ref.id === "opengeni" && ref.eager === true) || !progressiveDisclosure ? firstPartyMcpTools.filter((name) => !interactionNames.has(name) || !progressiveDisclosure) : [],
|
|
8150
8188
|
...!progressiveDisclosure ? runtimeToolNames : []
|
|
8151
8189
|
]);
|
|
@@ -8155,7 +8193,7 @@ function sessionEffectiveToolProjectionInput(session, toolRefs, context) {
|
|
|
8155
8193
|
...session.toolPolicy.mode !== "workspace_default" ? session.tools.map((tool) => tool.id) : []
|
|
8156
8194
|
]),
|
|
8157
8195
|
hasSkills: context ? sessionHasSkills(session, context) : false,
|
|
8158
|
-
webSearch: hostedToolNames.includes("web_search"),
|
|
8196
|
+
webSearch: hostedToolNames.includes("web_search") || webSearchPlan.providerTools.length > 0,
|
|
8159
8197
|
humanInput: context?.humanInputEnabled ?? false,
|
|
8160
8198
|
media: media.toolsKnown ? media.hosted.length + media.runtime.length > 0 : void 0,
|
|
8161
8199
|
routerInHistory: context?.routerInHistory === true || context?.routerHistorySessionIds?.has(session.id) === true,
|
|
@@ -10167,6 +10205,12 @@ async function withSessionCreateUsageRecording(input) {
|
|
|
10167
10205
|
}
|
|
10168
10206
|
async function createSessionForRequestInFileScope(unresolvedDeps, grant, workspaceId, rawPayload, authorization, agentChildPresentation, requestOptions = {}) {
|
|
10169
10207
|
const payload = CreateSessionRequest.parse(rawPayload);
|
|
10208
|
+
if (payload.firstPartyMcpTools) {
|
|
10209
|
+
const unavailable = deploymentUnavailableFirstPartyMcpTools(unresolvedDeps.settings);
|
|
10210
|
+
payload.firstPartyMcpTools = payload.firstPartyMcpTools.filter(
|
|
10211
|
+
(tool) => !unavailable.has(tool)
|
|
10212
|
+
);
|
|
10213
|
+
}
|
|
10170
10214
|
const surface = resolveTurnSurface({
|
|
10171
10215
|
grant,
|
|
10172
10216
|
authorization,
|
|
@@ -11464,6 +11508,10 @@ async function acceptSessionUserMessageInFileScope(deps, grant, workspaceId, ses
|
|
|
11464
11508
|
(await requireWorkspace5(db, workspaceId)).settings
|
|
11465
11509
|
)
|
|
11466
11510
|
});
|
|
11511
|
+
const existingEffectiveFirstPartyTools = allowedFirstPartyMcpToolsForSession3(
|
|
11512
|
+
settings,
|
|
11513
|
+
existingSession.firstPartyMcpTools
|
|
11514
|
+
);
|
|
11467
11515
|
const { personalConnectionDelegations, mcpAccountBindings } = await freezeConnectionAccounts({
|
|
11468
11516
|
db,
|
|
11469
11517
|
accountId: grant.accountId,
|
|
@@ -11473,7 +11521,7 @@ async function acceptSessionUserMessageInFileScope(deps, grant, workspaceId, ses
|
|
|
11473
11521
|
resources: [...existingSession.resources, ...requestedResources],
|
|
11474
11522
|
source: connectionDelegationSource,
|
|
11475
11523
|
targetSessionId: sessionId,
|
|
11476
|
-
googleDrivePublicationEnabled:
|
|
11524
|
+
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
11525
|
atlassianEnabled: existingSession.firstPartyMcpTools.some((tool) => tool.startsWith("atlassian_")) && (!existingSession.firstPartyMcpPermissions?.length || existingSession.firstPartyMcpPermissions.includes("connections:read")),
|
|
11478
11526
|
...input.connectionAccounts ? { authoritySelections: input.connectionAccounts } : {}
|
|
11479
11527
|
});
|
|
@@ -12329,6 +12377,10 @@ async function freezeSessionRealtimeConnectionAccounts(input) {
|
|
|
12329
12377
|
(await requireWorkspace6(db, workspaceId)).settings
|
|
12330
12378
|
)
|
|
12331
12379
|
});
|
|
12380
|
+
const effectiveFirstPartyTools = allowedFirstPartyMcpToolsForSession4(
|
|
12381
|
+
settings,
|
|
12382
|
+
session.firstPartyMcpTools
|
|
12383
|
+
);
|
|
12332
12384
|
return await freezeConnectionAccounts({
|
|
12333
12385
|
db,
|
|
12334
12386
|
settings: runtimeSettings,
|
|
@@ -12338,7 +12390,7 @@ async function freezeSessionRealtimeConnectionAccounts(input) {
|
|
|
12338
12390
|
resources: session.resources,
|
|
12339
12391
|
source: personalConnectionDelegationSourceForGrant(grant),
|
|
12340
12392
|
targetSessionId: sessionId,
|
|
12341
|
-
googleDrivePublicationEnabled:
|
|
12393
|
+
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
12394
|
atlassianEnabled: session.firstPartyMcpTools.some((tool) => tool.startsWith("atlassian_")) && (!session.firstPartyMcpPermissions?.length || session.firstPartyMcpPermissions.includes("connections:read"))
|
|
12343
12395
|
});
|
|
12344
12396
|
}
|
|
@@ -12603,6 +12655,652 @@ async function searchKnowledgeEntries(db, context, input, embedder, settings) {
|
|
|
12603
12655
|
);
|
|
12604
12656
|
}
|
|
12605
12657
|
|
|
12658
|
+
// src/domain/voice-input-billing.ts
|
|
12659
|
+
import {
|
|
12660
|
+
calculateVoiceInputCost,
|
|
12661
|
+
configuredStaticUsageLimits as configuredStaticUsageLimits2,
|
|
12662
|
+
voiceInputCreditBillingActive,
|
|
12663
|
+
voiceInputPricingHasTokenRates
|
|
12664
|
+
} from "@opengeni/config";
|
|
12665
|
+
import {
|
|
12666
|
+
applyCreditDebitAfterUse as applyCreditDebitAfterUse2,
|
|
12667
|
+
checkWorkspaceAllowance as checkWorkspaceAllowance3,
|
|
12668
|
+
creditDebitAttributionMetadata,
|
|
12669
|
+
getSpendableCreditBalance as getSpendableCreditBalance3,
|
|
12670
|
+
listUnsettledVoiceTranscriptionCharges,
|
|
12671
|
+
recordUsageEvent as recordUsageEvent3,
|
|
12672
|
+
sumUsageQuantity as sumUsageQuantity3,
|
|
12673
|
+
VOICE_TRANSCRIPTION_DEBIT_TYPE,
|
|
12674
|
+
VOICE_TRANSCRIPTION_SOURCE_TYPE,
|
|
12675
|
+
voiceTranscriptionSettlementKeys
|
|
12676
|
+
} from "@opengeni/db";
|
|
12677
|
+
|
|
12678
|
+
// src/transcription.ts
|
|
12679
|
+
var TRANSCRIPTION_PROVIDER_REQUEST_TIMEOUT_MILLISECONDS = 10 * 60 * 1e3;
|
|
12680
|
+
var TranscriptionBillingRefusedError = class extends Error {
|
|
12681
|
+
status;
|
|
12682
|
+
code;
|
|
12683
|
+
details;
|
|
12684
|
+
constructor(input) {
|
|
12685
|
+
super(input.message);
|
|
12686
|
+
this.name = "TranscriptionBillingRefusedError";
|
|
12687
|
+
this.code = input.code;
|
|
12688
|
+
this.status = input.code === "monthly_model_cost_limit" ? 429 : 402;
|
|
12689
|
+
this.details = input.details ?? {};
|
|
12690
|
+
}
|
|
12691
|
+
};
|
|
12692
|
+
var TranscriptionServiceError = class extends Error {
|
|
12693
|
+
code;
|
|
12694
|
+
status;
|
|
12695
|
+
retryable;
|
|
12696
|
+
/** Explicit rejection before any transcription result; safe to try another provider. */
|
|
12697
|
+
fallbackSafe;
|
|
12698
|
+
constructor(input) {
|
|
12699
|
+
super(input.message);
|
|
12700
|
+
this.name = "TranscriptionServiceError";
|
|
12701
|
+
this.code = input.code;
|
|
12702
|
+
this.status = input.status ?? statusForVoiceInputError(input.code);
|
|
12703
|
+
this.retryable = input.retryable ?? false;
|
|
12704
|
+
this.fallbackSafe = input.fallbackSafe ?? false;
|
|
12705
|
+
}
|
|
12706
|
+
};
|
|
12707
|
+
function statusForVoiceInputError(code) {
|
|
12708
|
+
switch (code) {
|
|
12709
|
+
case "permission_denied":
|
|
12710
|
+
return 403;
|
|
12711
|
+
case "policy_blocked":
|
|
12712
|
+
return 403;
|
|
12713
|
+
case "not_supported":
|
|
12714
|
+
return 415;
|
|
12715
|
+
case "unavailable":
|
|
12716
|
+
return 503;
|
|
12717
|
+
case "too_large":
|
|
12718
|
+
return 413;
|
|
12719
|
+
case "invalid_audio":
|
|
12720
|
+
return 400;
|
|
12721
|
+
case "timeout":
|
|
12722
|
+
return 504;
|
|
12723
|
+
case "cancelled":
|
|
12724
|
+
return 499;
|
|
12725
|
+
case "network":
|
|
12726
|
+
case "provider":
|
|
12727
|
+
return 502;
|
|
12728
|
+
case "unknown":
|
|
12729
|
+
default:
|
|
12730
|
+
return 500;
|
|
12731
|
+
}
|
|
12732
|
+
}
|
|
12733
|
+
function normalizeMimeType(mimeType) {
|
|
12734
|
+
return mimeType.trim().toLowerCase();
|
|
12735
|
+
}
|
|
12736
|
+
function isAcceptedMimeType(mimeType, accepted) {
|
|
12737
|
+
const normalized = normalizeMimeType(mimeType);
|
|
12738
|
+
if (accepted.some((candidate) => normalizeMimeType(candidate) === normalized)) {
|
|
12739
|
+
return true;
|
|
12740
|
+
}
|
|
12741
|
+
const bare = normalized.split(";")[0]?.trim() ?? normalized;
|
|
12742
|
+
return accepted.some((candidate) => {
|
|
12743
|
+
const allowed = normalizeMimeType(candidate);
|
|
12744
|
+
return allowed === bare || allowed.split(";")[0]?.trim() === bare;
|
|
12745
|
+
});
|
|
12746
|
+
}
|
|
12747
|
+
function filenameForMimeType(mimeType) {
|
|
12748
|
+
const bare = normalizeMimeType(mimeType).split(";")[0] ?? "audio/webm";
|
|
12749
|
+
switch (bare) {
|
|
12750
|
+
case "audio/mp4":
|
|
12751
|
+
case "audio/m4a":
|
|
12752
|
+
return "audio.mp4";
|
|
12753
|
+
case "audio/ogg":
|
|
12754
|
+
return "audio.ogg";
|
|
12755
|
+
case "audio/mpeg":
|
|
12756
|
+
case "audio/mp3":
|
|
12757
|
+
return "audio.mp3";
|
|
12758
|
+
case "audio/wav":
|
|
12759
|
+
case "audio/x-wav":
|
|
12760
|
+
return "audio.wav";
|
|
12761
|
+
case "audio/webm":
|
|
12762
|
+
default:
|
|
12763
|
+
return "audio.webm";
|
|
12764
|
+
}
|
|
12765
|
+
}
|
|
12766
|
+
|
|
12767
|
+
// src/domain/voice-input-billing.ts
|
|
12768
|
+
var VOICE_INPUT_DEBIT_TYPE = VOICE_TRANSCRIPTION_DEBIT_TYPE;
|
|
12769
|
+
var VOICE_INPUT_SOURCE_TYPE = VOICE_TRANSCRIPTION_SOURCE_TYPE;
|
|
12770
|
+
function voiceInputBillableUsage(input) {
|
|
12771
|
+
if (input.usage?.kind === "tokens" && voiceInputPricingHasTokenRates(input.pricing)) {
|
|
12772
|
+
return { usage: input.usage, basis: "provider_tokens" };
|
|
12773
|
+
}
|
|
12774
|
+
if (input.usage?.kind === "duration") {
|
|
12775
|
+
return { usage: input.usage, basis: "provider_duration" };
|
|
12776
|
+
}
|
|
12777
|
+
if (input.trustedDurationSeconds !== void 0 && Number.isFinite(input.trustedDurationSeconds) && input.trustedDurationSeconds >= 0) {
|
|
12778
|
+
return {
|
|
12779
|
+
usage: { kind: "duration", seconds: input.trustedDurationSeconds },
|
|
12780
|
+
basis: "server_duration"
|
|
12781
|
+
};
|
|
12782
|
+
}
|
|
12783
|
+
throw new TranscriptionServiceError({
|
|
12784
|
+
code: "provider",
|
|
12785
|
+
message: "Transcription usage was not reported."
|
|
12786
|
+
});
|
|
12787
|
+
}
|
|
12788
|
+
function initiatingHuman(attribution) {
|
|
12789
|
+
return attribution.kind === "human" || attribution.kind === "turn" ? attribution.initiatingHumanSubjectId : null;
|
|
12790
|
+
}
|
|
12791
|
+
function startOfUtcMonth2() {
|
|
12792
|
+
const now = /* @__PURE__ */ new Date();
|
|
12793
|
+
return new Date(Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), 1));
|
|
12794
|
+
}
|
|
12795
|
+
function createVoiceInputBilling(deps) {
|
|
12796
|
+
const reconcileInterval = deps.reconcileIntervalMilliseconds ?? 5 * 6e4;
|
|
12797
|
+
const lastReconciled = /* @__PURE__ */ new Map();
|
|
12798
|
+
const reconcileDue = (key) => {
|
|
12799
|
+
const now = Date.now();
|
|
12800
|
+
const last = lastReconciled.get(key);
|
|
12801
|
+
if (last !== void 0 && now - last < reconcileInterval) return false;
|
|
12802
|
+
if (lastReconciled.size >= 1e4) lastReconciled.clear();
|
|
12803
|
+
lastReconciled.set(key, now);
|
|
12804
|
+
return true;
|
|
12805
|
+
};
|
|
12806
|
+
return {
|
|
12807
|
+
async admit({ accountId, workspaceId, attribution }) {
|
|
12808
|
+
if (!voiceInputCreditBillingActive(deps.settings)) return;
|
|
12809
|
+
if (attribution.kind === "unknown") {
|
|
12810
|
+
throw new TranscriptionServiceError({
|
|
12811
|
+
code: "policy_blocked",
|
|
12812
|
+
status: 403,
|
|
12813
|
+
message: "Voice input payer could not be verified."
|
|
12814
|
+
});
|
|
12815
|
+
}
|
|
12816
|
+
if (reconcileDue(`${accountId}:${workspaceId}`)) {
|
|
12817
|
+
await reconcileUnsettledVoiceInputCharges(deps.db, { accountId, workspaceId }).catch(
|
|
12818
|
+
(error) => {
|
|
12819
|
+
deps.onReconcileError?.(error, { accountId, workspaceId });
|
|
12820
|
+
}
|
|
12821
|
+
);
|
|
12822
|
+
}
|
|
12823
|
+
const balance = await getSpendableCreditBalance3(deps.db, accountId);
|
|
12824
|
+
if (balance.balanceMicros <= 0) {
|
|
12825
|
+
throw new TranscriptionBillingRefusedError({
|
|
12826
|
+
code: "insufficient_credits",
|
|
12827
|
+
message: "Voice input needs Opengeni credits. Add credits to continue."
|
|
12828
|
+
});
|
|
12829
|
+
}
|
|
12830
|
+
const refusal = await checkWorkspaceAllowance3(deps.db, {
|
|
12831
|
+
accountId,
|
|
12832
|
+
workspaceId,
|
|
12833
|
+
subjectId: initiatingHuman(attribution)
|
|
12834
|
+
});
|
|
12835
|
+
if (refusal) {
|
|
12836
|
+
throw new TranscriptionBillingRefusedError({
|
|
12837
|
+
code: refusal.code,
|
|
12838
|
+
message: refusal.message,
|
|
12839
|
+
details: {
|
|
12840
|
+
scope: refusal.scope,
|
|
12841
|
+
resetsAt: refusal.resetsAt,
|
|
12842
|
+
...refusal.subjectId ? { subjectId: refusal.subjectId } : {}
|
|
12843
|
+
}
|
|
12844
|
+
});
|
|
12845
|
+
}
|
|
12846
|
+
if (deps.settings.usageLimitsMode === "static" || deps.settings.usageLimitsMode === "managed") {
|
|
12847
|
+
const cap = configuredStaticUsageLimits2(deps.settings).maxMonthlyCostMicrosPerAccount;
|
|
12848
|
+
if (cap) {
|
|
12849
|
+
const used = await sumUsageQuantity3(deps.db, {
|
|
12850
|
+
accountId,
|
|
12851
|
+
eventType: "model.cost",
|
|
12852
|
+
since: startOfUtcMonth2()
|
|
12853
|
+
});
|
|
12854
|
+
if (used >= cap) {
|
|
12855
|
+
throw new TranscriptionBillingRefusedError({
|
|
12856
|
+
code: "monthly_model_cost_limit",
|
|
12857
|
+
message: "The monthly model cost limit has been reached."
|
|
12858
|
+
});
|
|
12859
|
+
}
|
|
12860
|
+
}
|
|
12861
|
+
}
|
|
12862
|
+
},
|
|
12863
|
+
async settle(input) {
|
|
12864
|
+
if (!voiceInputCreditBillingActive(deps.settings)) return { creditCostMicros: 0 };
|
|
12865
|
+
const { usage, basis } = voiceInputBillableUsage({
|
|
12866
|
+
pricing: input.pricing,
|
|
12867
|
+
usage: input.usage,
|
|
12868
|
+
trustedDurationSeconds: input.billing.trustedDurationSeconds
|
|
12869
|
+
});
|
|
12870
|
+
const cost = calculateVoiceInputCost(input.pricing, usage);
|
|
12871
|
+
return await settleVoiceInputUsage(deps.db, {
|
|
12872
|
+
accountId: input.accountId,
|
|
12873
|
+
workspaceId: input.workspaceId,
|
|
12874
|
+
providerId: input.providerId,
|
|
12875
|
+
model: input.model,
|
|
12876
|
+
billing: input.billing,
|
|
12877
|
+
usage,
|
|
12878
|
+
basis,
|
|
12879
|
+
providerCostMicros: cost.providerCostMicros,
|
|
12880
|
+
creditCostMicros: cost.creditCostMicros
|
|
12881
|
+
});
|
|
12882
|
+
}
|
|
12883
|
+
};
|
|
12884
|
+
}
|
|
12885
|
+
async function settleVoiceInputUsage(db, input) {
|
|
12886
|
+
const attribution = input.billing.attribution;
|
|
12887
|
+
const keys = voiceTranscriptionSettlementKeys({
|
|
12888
|
+
workspaceId: input.workspaceId,
|
|
12889
|
+
sourceId: input.billing.sourceId
|
|
12890
|
+
});
|
|
12891
|
+
const receipt2 = await recordUsageEvent3(db, {
|
|
12892
|
+
accountId: input.accountId,
|
|
12893
|
+
workspaceId: input.workspaceId,
|
|
12894
|
+
eventType: "model.cost",
|
|
12895
|
+
quantity: input.creditCostMicros,
|
|
12896
|
+
unit: "usd_micros",
|
|
12897
|
+
sourceResourceType: VOICE_TRANSCRIPTION_SOURCE_TYPE,
|
|
12898
|
+
sourceResourceId: input.billing.sourceId,
|
|
12899
|
+
idempotencyKey: keys.usageIdempotencyKey,
|
|
12900
|
+
// The API is the writer; the payer is the trusted attribution snapshot.
|
|
12901
|
+
initiator: { kind: "service", subjectId: "api:voice-input" },
|
|
12902
|
+
initiatorContext: { creditDebitAttribution: attribution }
|
|
12903
|
+
});
|
|
12904
|
+
const amount = Number(receipt2.quantity);
|
|
12905
|
+
if (amount > 0) {
|
|
12906
|
+
await applyCreditDebitAfterUse2(db, {
|
|
12907
|
+
accountId: input.accountId,
|
|
12908
|
+
workspaceId: input.workspaceId,
|
|
12909
|
+
type: VOICE_TRANSCRIPTION_DEBIT_TYPE,
|
|
12910
|
+
amountMicros: amount,
|
|
12911
|
+
sourceType: VOICE_TRANSCRIPTION_SOURCE_TYPE,
|
|
12912
|
+
sourceId: input.billing.sourceId,
|
|
12913
|
+
idempotencyKey: keys.debitIdempotencyKey,
|
|
12914
|
+
metadata: {
|
|
12915
|
+
providerId: input.providerId,
|
|
12916
|
+
model: input.model,
|
|
12917
|
+
basis: input.basis,
|
|
12918
|
+
providerCostMicros: input.providerCostMicros,
|
|
12919
|
+
...input.usage.kind === "tokens" ? {
|
|
12920
|
+
inputTokens: input.usage.inputTokens,
|
|
12921
|
+
audioInputTokens: input.usage.audioInputTokens,
|
|
12922
|
+
textInputTokens: input.usage.textInputTokens,
|
|
12923
|
+
outputTokens: input.usage.outputTokens
|
|
12924
|
+
} : { audioMilliseconds: Math.ceil(input.usage.seconds * 1e3) },
|
|
12925
|
+
...attribution.kind === "unknown" ? {} : creditDebitAttributionMetadata(attribution)
|
|
12926
|
+
}
|
|
12927
|
+
});
|
|
12928
|
+
}
|
|
12929
|
+
return { creditCostMicros: amount };
|
|
12930
|
+
}
|
|
12931
|
+
async function reconcileUnsettledVoiceInputCharges(db, input) {
|
|
12932
|
+
const pending = await listUnsettledVoiceTranscriptionCharges(db, {
|
|
12933
|
+
accountId: input.accountId,
|
|
12934
|
+
workspaceId: input.workspaceId,
|
|
12935
|
+
...input.minAgeMilliseconds !== void 0 ? { minAgeMilliseconds: input.minAgeMilliseconds } : {}
|
|
12936
|
+
});
|
|
12937
|
+
for (const charge of pending) {
|
|
12938
|
+
await applyCreditDebitAfterUse2(db, {
|
|
12939
|
+
accountId: input.accountId,
|
|
12940
|
+
workspaceId: input.workspaceId,
|
|
12941
|
+
type: VOICE_TRANSCRIPTION_DEBIT_TYPE,
|
|
12942
|
+
amountMicros: charge.amountMicros,
|
|
12943
|
+
sourceType: VOICE_TRANSCRIPTION_SOURCE_TYPE,
|
|
12944
|
+
sourceId: charge.sourceId,
|
|
12945
|
+
idempotencyKey: voiceTranscriptionSettlementKeys({
|
|
12946
|
+
workspaceId: input.workspaceId,
|
|
12947
|
+
sourceId: charge.sourceId
|
|
12948
|
+
}).debitIdempotencyKey,
|
|
12949
|
+
metadata: {
|
|
12950
|
+
basis: "reconciled_receipt",
|
|
12951
|
+
...charge.attribution.kind === "unknown" ? {} : creditDebitAttributionMetadata(charge.attribution)
|
|
12952
|
+
}
|
|
12953
|
+
});
|
|
12954
|
+
}
|
|
12955
|
+
return { reconciled: pending.length };
|
|
12956
|
+
}
|
|
12957
|
+
|
|
12958
|
+
// src/domain/web-search-billing.ts
|
|
12959
|
+
import {
|
|
12960
|
+
applyCreditDebitAfterUse as applyCreditDebitAfterUse3,
|
|
12961
|
+
checkWorkspaceAllowance as checkWorkspaceAllowance4,
|
|
12962
|
+
creditDebitAttributionForTurn as creditDebitAttributionForTurn2,
|
|
12963
|
+
creditDebitAttributionMetadata as creditDebitAttributionMetadata2,
|
|
12964
|
+
getSpendableCreditBalance as getSpendableCreditBalance4,
|
|
12965
|
+
recordUsageEvent as recordUsageEvent4,
|
|
12966
|
+
withRlsContext
|
|
12967
|
+
} from "@opengeni/db";
|
|
12968
|
+
var WEB_SEARCH_DEBIT_TYPE = "web_search_debit";
|
|
12969
|
+
var WEB_SEARCH_SOURCE_TYPE = "web_search";
|
|
12970
|
+
var WEB_SEARCH_BILLING_INITIATOR = "worker:web-search";
|
|
12971
|
+
function webSearchCreditBillingActive(settings) {
|
|
12972
|
+
return settings.billingMode === "stripe" || settings.usageLimitsMode === "managed";
|
|
12973
|
+
}
|
|
12974
|
+
var WebSearchBillingRefusedError = class extends Error {
|
|
12975
|
+
constructor(code, message) {
|
|
12976
|
+
super(message);
|
|
12977
|
+
this.code = code;
|
|
12978
|
+
this.name = "WebSearchBillingRefusedError";
|
|
12979
|
+
}
|
|
12980
|
+
};
|
|
12981
|
+
function createWebSearchBilling(deps) {
|
|
12982
|
+
const active = webSearchCreditBillingActive(deps.settings);
|
|
12983
|
+
const attributionByTurn = /* @__PURE__ */ new Map();
|
|
12984
|
+
const attributionFor = (scope) => {
|
|
12985
|
+
let attribution = attributionByTurn.get(scope.turnId);
|
|
12986
|
+
if (!attribution) {
|
|
12987
|
+
attribution = creditDebitAttributionForTurn2(deps.db, scope);
|
|
12988
|
+
attributionByTurn.set(scope.turnId, attribution);
|
|
12989
|
+
attribution.catch(() => attributionByTurn.delete(scope.turnId));
|
|
12990
|
+
}
|
|
12991
|
+
return attribution;
|
|
12992
|
+
};
|
|
12993
|
+
return {
|
|
12994
|
+
active,
|
|
12995
|
+
async admit(scope, expectedProviderMicros) {
|
|
12996
|
+
if (!active || expectedProviderMicros <= 0) return;
|
|
12997
|
+
const attribution = await attributionFor(scope);
|
|
12998
|
+
const balance = await getSpendableCreditBalance4(deps.db, scope.accountId);
|
|
12999
|
+
if (balance.balanceMicros <= 0) {
|
|
13000
|
+
throw new WebSearchBillingRefusedError(
|
|
13001
|
+
"insufficient_credits",
|
|
13002
|
+
"Web search needs Opengeni credits, and this account has none left."
|
|
13003
|
+
);
|
|
13004
|
+
}
|
|
13005
|
+
const refusal = await checkWorkspaceAllowance4(deps.db, {
|
|
13006
|
+
accountId: scope.accountId,
|
|
13007
|
+
workspaceId: scope.workspaceId,
|
|
13008
|
+
subjectId: attribution.kind === "turn" ? attribution.initiatingHumanSubjectId : null
|
|
13009
|
+
});
|
|
13010
|
+
if (refusal) throw new WebSearchBillingRefusedError("allowance_exhausted", refusal.message);
|
|
13011
|
+
},
|
|
13012
|
+
async settle(scope, cost) {
|
|
13013
|
+
const shared = {
|
|
13014
|
+
accountId: scope.accountId,
|
|
13015
|
+
workspaceId: scope.workspaceId,
|
|
13016
|
+
sessionId: scope.sessionId,
|
|
13017
|
+
turnId: scope.turnId,
|
|
13018
|
+
turnAttemptId: scope.attemptId,
|
|
13019
|
+
sourceResourceType: WEB_SEARCH_SOURCE_TYPE,
|
|
13020
|
+
sourceResourceId: `${scope.attemptId}:${cost.operationId}`
|
|
13021
|
+
};
|
|
13022
|
+
await recordUsageEvent4(deps.db, {
|
|
13023
|
+
...shared,
|
|
13024
|
+
eventType: `web_search.${cost.operation}_requests`,
|
|
13025
|
+
quantity: 1,
|
|
13026
|
+
unit: "request",
|
|
13027
|
+
idempotencyKey: `usage:web_search.${cost.operation}_requests:${shared.sourceResourceId}`
|
|
13028
|
+
});
|
|
13029
|
+
if (!active || cost.creditMicros <= 0) return;
|
|
13030
|
+
const attribution = await attributionFor(scope);
|
|
13031
|
+
await withRlsContext(
|
|
13032
|
+
deps.db,
|
|
13033
|
+
{ accountId: scope.accountId, workspaceId: scope.workspaceId },
|
|
13034
|
+
async (tx) => {
|
|
13035
|
+
await recordUsageEvent4(tx, {
|
|
13036
|
+
...shared,
|
|
13037
|
+
eventType: "web_search.cost",
|
|
13038
|
+
quantity: cost.creditMicros,
|
|
13039
|
+
unit: "usd_micros",
|
|
13040
|
+
idempotencyKey: `usage:web_search.cost:${shared.sourceResourceId}`,
|
|
13041
|
+
initiator: { kind: "service", subjectId: WEB_SEARCH_BILLING_INITIATOR },
|
|
13042
|
+
initiatorContext: { creditDebitAttribution: attribution }
|
|
13043
|
+
});
|
|
13044
|
+
await applyCreditDebitAfterUse3(tx, {
|
|
13045
|
+
accountId: scope.accountId,
|
|
13046
|
+
workspaceId: scope.workspaceId,
|
|
13047
|
+
type: WEB_SEARCH_DEBIT_TYPE,
|
|
13048
|
+
amountMicros: cost.creditMicros,
|
|
13049
|
+
sourceType: WEB_SEARCH_SOURCE_TYPE,
|
|
13050
|
+
sourceId: shared.sourceResourceId,
|
|
13051
|
+
idempotencyKey: `credit:${WEB_SEARCH_DEBIT_TYPE}:${shared.sourceResourceId}`,
|
|
13052
|
+
metadata: {
|
|
13053
|
+
// turnId lets the allowance trigger resolve the causal human
|
|
13054
|
+
// from the immutable turn receipt.
|
|
13055
|
+
...creditDebitAttributionMetadata2(attribution),
|
|
13056
|
+
provider: cost.provider,
|
|
13057
|
+
operation: cost.operation,
|
|
13058
|
+
providerCostMicros: cost.providerMicros,
|
|
13059
|
+
marginBps: cost.marginBps,
|
|
13060
|
+
basis: cost.basis
|
|
13061
|
+
}
|
|
13062
|
+
});
|
|
13063
|
+
}
|
|
13064
|
+
);
|
|
13065
|
+
}
|
|
13066
|
+
};
|
|
13067
|
+
}
|
|
13068
|
+
|
|
13069
|
+
// src/domain/realtime-voice-billing.ts
|
|
13070
|
+
import {
|
|
13071
|
+
AI_GATEWAY_REALTIME_MODELS,
|
|
13072
|
+
AZURE_LIVE_REALTIME_MODEL_ID,
|
|
13073
|
+
configuredStaticUsageLimits as configuredStaticUsageLimits3,
|
|
13074
|
+
parseRealtimeVoicePricingJson,
|
|
13075
|
+
parseRealtimeVoicePricingTableJson,
|
|
13076
|
+
realtimeVoiceMinuteCreditMicros,
|
|
13077
|
+
realtimeVoiceStartedMinutes,
|
|
13078
|
+
voiceInputCreditBillingActive as voiceInputCreditBillingActive2
|
|
13079
|
+
} from "@opengeni/config";
|
|
13080
|
+
import {
|
|
13081
|
+
applyCreditDebitAfterUse as applyCreditDebitAfterUse4,
|
|
13082
|
+
checkWorkspaceAllowance as checkWorkspaceAllowance5,
|
|
13083
|
+
creditDebitAttributionMetadata as creditDebitAttributionMetadata3,
|
|
13084
|
+
existingUsageEventIdempotencyKeys,
|
|
13085
|
+
getSpendableCreditBalance as getSpendableCreditBalance5,
|
|
13086
|
+
loadSessionRealtimeBillingFacts,
|
|
13087
|
+
recordUsageEvent as recordUsageEvent5,
|
|
13088
|
+
sumUsageQuantity as sumUsageQuantity4,
|
|
13089
|
+
withRlsContext as withRlsContext2
|
|
13090
|
+
} from "@opengeni/db";
|
|
13091
|
+
var REALTIME_VOICE_DEBIT_TYPE = "voice_realtime_debit";
|
|
13092
|
+
var REALTIME_VOICE_SOURCE_TYPE = "voice_realtime";
|
|
13093
|
+
function deploymentRealtimeVoice(settings, model) {
|
|
13094
|
+
const azureConfigured = Boolean(settings.azureLiveEndpoint && settings.azureLiveApiKey?.trim());
|
|
13095
|
+
if (model === AZURE_LIVE_REALTIME_MODEL_ID) {
|
|
13096
|
+
return {
|
|
13097
|
+
provider: "azure-live",
|
|
13098
|
+
configured: azureConfigured,
|
|
13099
|
+
pricing: parseRealtimeVoicePricingJson(settings.azureLivePricingJson)
|
|
13100
|
+
};
|
|
13101
|
+
}
|
|
13102
|
+
for (const gateway of Object.values(AI_GATEWAY_REALTIME_MODELS)) {
|
|
13103
|
+
if (gateway.managedModelId !== model) continue;
|
|
13104
|
+
return {
|
|
13105
|
+
provider: "ai-gateway",
|
|
13106
|
+
configured: Boolean(settings.vercelAiGatewayApiKey),
|
|
13107
|
+
pricing: parseRealtimeVoicePricingTableJson(settings.aiGatewayRealtimePricingJson)[gateway.upstreamModelId] ?? null
|
|
13108
|
+
};
|
|
13109
|
+
}
|
|
13110
|
+
return null;
|
|
13111
|
+
}
|
|
13112
|
+
var RealtimeVoiceUnavailableError = class extends Error {
|
|
13113
|
+
constructor(code, message) {
|
|
13114
|
+
super(message);
|
|
13115
|
+
this.code = code;
|
|
13116
|
+
}
|
|
13117
|
+
name = "RealtimeVoiceUnavailableError";
|
|
13118
|
+
};
|
|
13119
|
+
function realtimeVoiceOfferProblem(settings, voice) {
|
|
13120
|
+
if (!voice.configured) {
|
|
13121
|
+
return new RealtimeVoiceUnavailableError(
|
|
13122
|
+
"not_configured",
|
|
13123
|
+
"Opengeni live voice is not configured on this deployment."
|
|
13124
|
+
);
|
|
13125
|
+
}
|
|
13126
|
+
if (voiceInputCreditBillingActive2(settings) && !voice.pricing) {
|
|
13127
|
+
return new RealtimeVoiceUnavailableError(
|
|
13128
|
+
"pricing_unconfigured",
|
|
13129
|
+
"Opengeni live voice is not available on this deployment yet."
|
|
13130
|
+
);
|
|
13131
|
+
}
|
|
13132
|
+
return null;
|
|
13133
|
+
}
|
|
13134
|
+
var REALTIME_VOICE_INSUFFICIENT_CREDITS_MESSAGE = "Live voice needs Opengeni credits. Add credits to continue.";
|
|
13135
|
+
function startOfUtcMonth3(now) {
|
|
13136
|
+
return new Date(Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), 1));
|
|
13137
|
+
}
|
|
13138
|
+
function initiatingHuman2(attribution) {
|
|
13139
|
+
return attribution.kind === "human" || attribution.kind === "turn" ? attribution.initiatingHumanSubjectId : null;
|
|
13140
|
+
}
|
|
13141
|
+
function createRealtimeVoiceBilling(deps) {
|
|
13142
|
+
const billingActive = () => voiceInputCreditBillingActive2(deps.settings);
|
|
13143
|
+
async function refusal(input) {
|
|
13144
|
+
const balance = await getSpendableCreditBalance5(deps.db, input.accountId);
|
|
13145
|
+
if (balance.balanceMicros <= 0) {
|
|
13146
|
+
return new TranscriptionBillingRefusedError({
|
|
13147
|
+
code: "insufficient_credits",
|
|
13148
|
+
message: REALTIME_VOICE_INSUFFICIENT_CREDITS_MESSAGE
|
|
13149
|
+
});
|
|
13150
|
+
}
|
|
13151
|
+
const allowance = await checkWorkspaceAllowance5(deps.db, {
|
|
13152
|
+
accountId: input.accountId,
|
|
13153
|
+
workspaceId: input.workspaceId,
|
|
13154
|
+
subjectId: initiatingHuman2(input.attribution)
|
|
13155
|
+
});
|
|
13156
|
+
if (allowance) {
|
|
13157
|
+
return new TranscriptionBillingRefusedError({
|
|
13158
|
+
code: allowance.code,
|
|
13159
|
+
message: allowance.message,
|
|
13160
|
+
details: {
|
|
13161
|
+
scope: allowance.scope,
|
|
13162
|
+
resetsAt: allowance.resetsAt,
|
|
13163
|
+
...allowance.subjectId ? { subjectId: allowance.subjectId } : {}
|
|
13164
|
+
}
|
|
13165
|
+
});
|
|
13166
|
+
}
|
|
13167
|
+
if (deps.settings.usageLimitsMode === "static" || deps.settings.usageLimitsMode === "managed") {
|
|
13168
|
+
const cap = configuredStaticUsageLimits3(deps.settings).maxMonthlyCostMicrosPerAccount;
|
|
13169
|
+
if (cap) {
|
|
13170
|
+
const used = await sumUsageQuantity4(deps.db, {
|
|
13171
|
+
accountId: input.accountId,
|
|
13172
|
+
eventType: "model.cost",
|
|
13173
|
+
since: startOfUtcMonth3(input.now)
|
|
13174
|
+
});
|
|
13175
|
+
if (used >= cap) {
|
|
13176
|
+
return new TranscriptionBillingRefusedError({
|
|
13177
|
+
code: "monthly_model_cost_limit",
|
|
13178
|
+
message: "The monthly model cost limit has been reached."
|
|
13179
|
+
});
|
|
13180
|
+
}
|
|
13181
|
+
}
|
|
13182
|
+
}
|
|
13183
|
+
return null;
|
|
13184
|
+
}
|
|
13185
|
+
return {
|
|
13186
|
+
/**
|
|
13187
|
+
* Throws RealtimeVoiceUnavailableError when the deployment cannot offer
|
|
13188
|
+
* the model and TranscriptionBillingRefusedError for a credit refusal.
|
|
13189
|
+
* Workspace-funded models are always admitted here.
|
|
13190
|
+
*/
|
|
13191
|
+
async admit(input) {
|
|
13192
|
+
const voice = deploymentRealtimeVoice(deps.settings, input.model);
|
|
13193
|
+
if (!voice) return;
|
|
13194
|
+
const problem = realtimeVoiceOfferProblem(deps.settings, voice);
|
|
13195
|
+
if (problem) throw problem;
|
|
13196
|
+
if (!billingActive()) return;
|
|
13197
|
+
const refused = await refusal({ ...input, now: input.now ?? /* @__PURE__ */ new Date() });
|
|
13198
|
+
if (refused) throw refused;
|
|
13199
|
+
},
|
|
13200
|
+
/** Spendable credits are positive (always true when credits are not enforced). */
|
|
13201
|
+
async hasSpendableCredits(accountId) {
|
|
13202
|
+
if (!billingActive()) return true;
|
|
13203
|
+
const balance = await getSpendableCreditBalance5(deps.db, accountId);
|
|
13204
|
+
return balance.balanceMicros > 0;
|
|
13205
|
+
},
|
|
13206
|
+
/**
|
|
13207
|
+
* Settle every started minute observed so far for one live-voice mode and
|
|
13208
|
+
* report whether the call must stop. Safe to call repeatedly and
|
|
13209
|
+
* concurrently: receipts and debits are idempotent per (connection, minute).
|
|
13210
|
+
*/
|
|
13211
|
+
async settle(input) {
|
|
13212
|
+
const now = input.now ?? /* @__PURE__ */ new Date();
|
|
13213
|
+
if (!billingActive()) return { debitedMicros: 0, stop: null };
|
|
13214
|
+
const facts = await loadSessionRealtimeBillingFacts(deps.db, { ...input, now });
|
|
13215
|
+
if (!facts) return { debitedMicros: 0, stop: null };
|
|
13216
|
+
const voice = deploymentRealtimeVoice(deps.settings, facts.model);
|
|
13217
|
+
if (!voice) return { debitedMicros: 0, stop: null };
|
|
13218
|
+
if (!voice.pricing) {
|
|
13219
|
+
return {
|
|
13220
|
+
debitedMicros: 0,
|
|
13221
|
+
stop: new RealtimeVoiceUnavailableError(
|
|
13222
|
+
"pricing_unconfigured",
|
|
13223
|
+
"Opengeni live voice is no longer available on this deployment."
|
|
13224
|
+
)
|
|
13225
|
+
};
|
|
13226
|
+
}
|
|
13227
|
+
const attribution = input.callerSubjectId === facts.ownerSubjectId ? input.attribution : { kind: "unknown" };
|
|
13228
|
+
const cost = realtimeVoiceMinuteCreditMicros(voice.pricing);
|
|
13229
|
+
const minutes = facts.connections.flatMap(
|
|
13230
|
+
(connection) => Array.from(
|
|
13231
|
+
{ length: realtimeVoiceStartedMinutes(connection.startedAt, connection.observedUntil) },
|
|
13232
|
+
(_, index) => ({
|
|
13233
|
+
connectionId: connection.id,
|
|
13234
|
+
minute: index + 1,
|
|
13235
|
+
startsAt: new Date(connection.startedAt.getTime() + index * 6e4),
|
|
13236
|
+
key: `voice.realtime_cost:${connection.id}:${index + 1}`
|
|
13237
|
+
})
|
|
13238
|
+
)
|
|
13239
|
+
);
|
|
13240
|
+
const recorded = await existingUsageEventIdempotencyKeys(deps.db, {
|
|
13241
|
+
accountId: facts.accountId,
|
|
13242
|
+
workspaceId: facts.workspaceId,
|
|
13243
|
+
keys: minutes.map((minute) => minute.key)
|
|
13244
|
+
});
|
|
13245
|
+
let debitedMicros = 0;
|
|
13246
|
+
for (const minute of minutes) {
|
|
13247
|
+
if (recorded.has(minute.key)) continue;
|
|
13248
|
+
debitedMicros += await withRlsContext2(
|
|
13249
|
+
deps.db,
|
|
13250
|
+
{ accountId: facts.accountId, workspaceId: facts.workspaceId },
|
|
13251
|
+
async (tx) => {
|
|
13252
|
+
const sourceId = `${minute.connectionId}:${minute.minute}`;
|
|
13253
|
+
const receipt2 = await recordUsageEvent5(tx, {
|
|
13254
|
+
accountId: facts.accountId,
|
|
13255
|
+
workspaceId: facts.workspaceId,
|
|
13256
|
+
sessionId: facts.sessionId,
|
|
13257
|
+
eventType: "model.cost",
|
|
13258
|
+
quantity: cost.creditCostMicros,
|
|
13259
|
+
unit: "usd_micros",
|
|
13260
|
+
sourceResourceType: REALTIME_VOICE_SOURCE_TYPE,
|
|
13261
|
+
sourceResourceId: sourceId,
|
|
13262
|
+
idempotencyKey: minute.key,
|
|
13263
|
+
occurredAt: minute.startsAt,
|
|
13264
|
+
initiator: { kind: "service", subjectId: "api:voice-realtime" },
|
|
13265
|
+
initiatorContext: { creditDebitAttribution: attribution }
|
|
13266
|
+
});
|
|
13267
|
+
const amount = Number(receipt2.quantity);
|
|
13268
|
+
if (amount <= 0) return 0;
|
|
13269
|
+
const debit = await applyCreditDebitAfterUse4(tx, {
|
|
13270
|
+
accountId: facts.accountId,
|
|
13271
|
+
workspaceId: facts.workspaceId,
|
|
13272
|
+
type: REALTIME_VOICE_DEBIT_TYPE,
|
|
13273
|
+
amountMicros: amount,
|
|
13274
|
+
sourceType: REALTIME_VOICE_SOURCE_TYPE,
|
|
13275
|
+
sourceId,
|
|
13276
|
+
idempotencyKey: `credit:${REALTIME_VOICE_DEBIT_TYPE}:${sourceId}`,
|
|
13277
|
+
occurredAt: minute.startsAt,
|
|
13278
|
+
metadata: {
|
|
13279
|
+
provider: voice.provider,
|
|
13280
|
+
model: facts.model,
|
|
13281
|
+
realtimeId: facts.realtimeId,
|
|
13282
|
+
connectionId: minute.connectionId,
|
|
13283
|
+
minute: minute.minute,
|
|
13284
|
+
basis: "server_observed_started_minute",
|
|
13285
|
+
providerCostMicros: cost.providerCostMicros,
|
|
13286
|
+
...attribution.kind === "unknown" ? {} : creditDebitAttributionMetadata3(attribution)
|
|
13287
|
+
}
|
|
13288
|
+
});
|
|
13289
|
+
return debit.debitedMicros;
|
|
13290
|
+
}
|
|
13291
|
+
);
|
|
13292
|
+
}
|
|
13293
|
+
const stop = facts.state === "active" ? await refusal({
|
|
13294
|
+
accountId: facts.accountId,
|
|
13295
|
+
workspaceId: facts.workspaceId,
|
|
13296
|
+
attribution,
|
|
13297
|
+
now
|
|
13298
|
+
}) : null;
|
|
13299
|
+
return { debitedMicros, stop };
|
|
13300
|
+
}
|
|
13301
|
+
};
|
|
13302
|
+
}
|
|
13303
|
+
|
|
12606
13304
|
// src/workflow-wake-contract.ts
|
|
12607
13305
|
var SESSION_WORKFLOW_WAKE_DISPATCHER_SCHEDULE_ID = "opengeni-session-workflow-wake-dispatcher";
|
|
12608
13306
|
var SESSION_WORKFLOW_WAKE_DISPATCHER_WORKFLOW_TYPE = "sessionWorkflowWakeDispatcherWorkflow";
|
|
@@ -12704,83 +13402,6 @@ function createUserPresenceRecorder(options) {
|
|
|
12704
13402
|
};
|
|
12705
13403
|
}
|
|
12706
13404
|
|
|
12707
|
-
// src/transcription.ts
|
|
12708
|
-
var TRANSCRIPTION_PROVIDER_REQUEST_TIMEOUT_MILLISECONDS = 10 * 60 * 1e3;
|
|
12709
|
-
var TranscriptionServiceError = class extends Error {
|
|
12710
|
-
code;
|
|
12711
|
-
status;
|
|
12712
|
-
retryable;
|
|
12713
|
-
/** Explicit rejection before any transcription result; safe to try another provider. */
|
|
12714
|
-
fallbackSafe;
|
|
12715
|
-
constructor(input) {
|
|
12716
|
-
super(input.message);
|
|
12717
|
-
this.name = "TranscriptionServiceError";
|
|
12718
|
-
this.code = input.code;
|
|
12719
|
-
this.status = input.status ?? statusForVoiceInputError(input.code);
|
|
12720
|
-
this.retryable = input.retryable ?? false;
|
|
12721
|
-
this.fallbackSafe = input.fallbackSafe ?? false;
|
|
12722
|
-
}
|
|
12723
|
-
};
|
|
12724
|
-
function statusForVoiceInputError(code) {
|
|
12725
|
-
switch (code) {
|
|
12726
|
-
case "permission_denied":
|
|
12727
|
-
return 403;
|
|
12728
|
-
case "policy_blocked":
|
|
12729
|
-
return 403;
|
|
12730
|
-
case "not_supported":
|
|
12731
|
-
return 415;
|
|
12732
|
-
case "unavailable":
|
|
12733
|
-
return 503;
|
|
12734
|
-
case "too_large":
|
|
12735
|
-
return 413;
|
|
12736
|
-
case "invalid_audio":
|
|
12737
|
-
return 400;
|
|
12738
|
-
case "timeout":
|
|
12739
|
-
return 504;
|
|
12740
|
-
case "cancelled":
|
|
12741
|
-
return 499;
|
|
12742
|
-
case "network":
|
|
12743
|
-
case "provider":
|
|
12744
|
-
return 502;
|
|
12745
|
-
case "unknown":
|
|
12746
|
-
default:
|
|
12747
|
-
return 500;
|
|
12748
|
-
}
|
|
12749
|
-
}
|
|
12750
|
-
function normalizeMimeType(mimeType) {
|
|
12751
|
-
return mimeType.trim().toLowerCase();
|
|
12752
|
-
}
|
|
12753
|
-
function isAcceptedMimeType(mimeType, accepted) {
|
|
12754
|
-
const normalized = normalizeMimeType(mimeType);
|
|
12755
|
-
if (accepted.some((candidate) => normalizeMimeType(candidate) === normalized)) {
|
|
12756
|
-
return true;
|
|
12757
|
-
}
|
|
12758
|
-
const bare = normalized.split(";")[0]?.trim() ?? normalized;
|
|
12759
|
-
return accepted.some((candidate) => {
|
|
12760
|
-
const allowed = normalizeMimeType(candidate);
|
|
12761
|
-
return allowed === bare || allowed.split(";")[0]?.trim() === bare;
|
|
12762
|
-
});
|
|
12763
|
-
}
|
|
12764
|
-
function filenameForMimeType(mimeType) {
|
|
12765
|
-
const bare = normalizeMimeType(mimeType).split(";")[0] ?? "audio/webm";
|
|
12766
|
-
switch (bare) {
|
|
12767
|
-
case "audio/mp4":
|
|
12768
|
-
case "audio/m4a":
|
|
12769
|
-
return "audio.mp4";
|
|
12770
|
-
case "audio/ogg":
|
|
12771
|
-
return "audio.ogg";
|
|
12772
|
-
case "audio/mpeg":
|
|
12773
|
-
case "audio/mp3":
|
|
12774
|
-
return "audio.mp3";
|
|
12775
|
-
case "audio/wav":
|
|
12776
|
-
case "audio/x-wav":
|
|
12777
|
-
return "audio.wav";
|
|
12778
|
-
case "audio/webm":
|
|
12779
|
-
default:
|
|
12780
|
-
return "audio.webm";
|
|
12781
|
-
}
|
|
12782
|
-
}
|
|
12783
|
-
|
|
12784
13405
|
// src/goal-admission.ts
|
|
12785
13406
|
import {
|
|
12786
13407
|
isModelAvailableForNewSelection as isModelAvailableForNewSelection2,
|
|
@@ -12799,13 +13420,13 @@ import { getWorkspaceModelPolicy as getWorkspaceModelPolicy3 } from "@opengeni/d
|
|
|
12799
13420
|
// src/billing/agent-run-admission.ts
|
|
12800
13421
|
import {
|
|
12801
13422
|
canonicalizeConfiguredModelId as canonicalizeConfiguredModelId4,
|
|
12802
|
-
configuredStaticUsageLimits as
|
|
13423
|
+
configuredStaticUsageLimits as configuredStaticUsageLimits4
|
|
12803
13424
|
} from "@opengeni/config";
|
|
12804
13425
|
import {
|
|
12805
|
-
checkWorkspaceAllowance as
|
|
12806
|
-
getSpendableCreditBalance as
|
|
13426
|
+
checkWorkspaceAllowance as checkWorkspaceAllowance6,
|
|
13427
|
+
getSpendableCreditBalance as getSpendableCreditBalance6,
|
|
12807
13428
|
isCodexBilledTurn as isCodexBilledTurn2,
|
|
12808
|
-
sumUsageQuantity as
|
|
13429
|
+
sumUsageQuantity as sumUsageQuantity5
|
|
12809
13430
|
} from "@opengeni/db";
|
|
12810
13431
|
async function agentRunAdmissionDenial(services, input) {
|
|
12811
13432
|
const codexBilled = await isCodexBilledTurn2({
|
|
@@ -12829,7 +13450,7 @@ async function agentRunAdmissionDenial(services, input) {
|
|
|
12829
13450
|
});
|
|
12830
13451
|
if (!decision.allowed) return "insufficient_credits";
|
|
12831
13452
|
} else {
|
|
12832
|
-
const balance = await
|
|
13453
|
+
const balance = await getSpendableCreditBalance6(
|
|
12833
13454
|
services.db,
|
|
12834
13455
|
input.accountId,
|
|
12835
13456
|
canonicalizeConfiguredModelId4(services.settings, input.model)
|
|
@@ -12838,7 +13459,7 @@ async function agentRunAdmissionDenial(services, input) {
|
|
|
12838
13459
|
}
|
|
12839
13460
|
}
|
|
12840
13461
|
if (!externallyBilled) {
|
|
12841
|
-
const refusal = await
|
|
13462
|
+
const refusal = await checkWorkspaceAllowance6(services.db, {
|
|
12842
13463
|
accountId: input.accountId,
|
|
12843
13464
|
workspaceId: input.workspaceId,
|
|
12844
13465
|
subjectId: input.initiatingHumanSubjectId ?? null
|
|
@@ -12848,22 +13469,22 @@ async function agentRunAdmissionDenial(services, input) {
|
|
|
12848
13469
|
if (services.settings.usageLimitsMode !== "static" && services.settings.usageLimitsMode !== "managed") {
|
|
12849
13470
|
return null;
|
|
12850
13471
|
}
|
|
12851
|
-
const limits =
|
|
13472
|
+
const limits = configuredStaticUsageLimits4(services.settings);
|
|
12852
13473
|
if (!externallyBilled && limits.maxMonthlyCostMicrosPerAccount) {
|
|
12853
|
-
const used = await
|
|
13474
|
+
const used = await sumUsageQuantity5(services.db, {
|
|
12854
13475
|
accountId: input.accountId,
|
|
12855
13476
|
eventType: "model.cost",
|
|
12856
|
-
since:
|
|
13477
|
+
since: startOfUtcMonth4()
|
|
12857
13478
|
});
|
|
12858
13479
|
if (used >= limits.maxMonthlyCostMicrosPerAccount) {
|
|
12859
13480
|
return "monthly_model_cost_limit";
|
|
12860
13481
|
}
|
|
12861
13482
|
}
|
|
12862
13483
|
if (limits.maxMonthlyAgentRunsPerWorkspace) {
|
|
12863
|
-
const used = await
|
|
13484
|
+
const used = await sumUsageQuantity5(services.db, {
|
|
12864
13485
|
workspaceId: input.workspaceId,
|
|
12865
13486
|
eventType: "agent_run.created",
|
|
12866
|
-
since:
|
|
13487
|
+
since: startOfUtcMonth4()
|
|
12867
13488
|
});
|
|
12868
13489
|
if (used + input.requestedAgentRuns > limits.maxMonthlyAgentRunsPerWorkspace) {
|
|
12869
13490
|
return "monthly_agent_run_limit";
|
|
@@ -12871,7 +13492,7 @@ async function agentRunAdmissionDenial(services, input) {
|
|
|
12871
13492
|
}
|
|
12872
13493
|
return null;
|
|
12873
13494
|
}
|
|
12874
|
-
function
|
|
13495
|
+
function startOfUtcMonth4() {
|
|
12875
13496
|
const now = /* @__PURE__ */ new Date();
|
|
12876
13497
|
return new Date(Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), 1));
|
|
12877
13498
|
}
|
|
@@ -15581,7 +16202,7 @@ import {
|
|
|
15581
16202
|
SCHEDULED_TASK_OCCURRENCE_PAYLOAD_INGRESS_HEADROOM_BYTES
|
|
15582
16203
|
} from "@opengeni/contracts";
|
|
15583
16204
|
import {
|
|
15584
|
-
allowedFirstPartyMcpToolsForSession as
|
|
16205
|
+
allowedFirstPartyMcpToolsForSession as allowedFirstPartyMcpToolsForSession5,
|
|
15585
16206
|
resolveFirstPartyMcpToolPolicy as resolveFirstPartyMcpToolPolicy2
|
|
15586
16207
|
} from "@opengeni/config";
|
|
15587
16208
|
import {
|
|
@@ -15689,7 +16310,7 @@ async function scheduledConnectionTools(db, workspaceId, settings, target, taskT
|
|
|
15689
16310
|
}).toolRefs;
|
|
15690
16311
|
}
|
|
15691
16312
|
function scheduledConnectionSurfaceEligibility(settings, target) {
|
|
15692
|
-
const tools = target?.firstPartyMcpTools
|
|
16313
|
+
const tools = allowedFirstPartyMcpToolsForSession5(settings, target?.firstPartyMcpTools);
|
|
15693
16314
|
const permissions = target?.firstPartyMcpPermissions ?? DEFAULT_FIRST_PARTY_MCP_PERMISSIONS3;
|
|
15694
16315
|
return {
|
|
15695
16316
|
googleDrivePublicationEnabled: tools.includes("editable_artifact_export") && tools.includes("editable_artifact_export_status") && permissions.includes("artifacts:read") && permissions.includes("artifacts:publish"),
|
|
@@ -16014,7 +16635,7 @@ async function frozenScheduledTaskCreatorPolicy(input) {
|
|
|
16014
16635
|
restricted ||= Boolean(
|
|
16015
16636
|
turnPolicy.kind === "valid" && turnPolicy.policy.credentialRestriction === "developer_setup" || sessionPolicy.kind === "valid" && sessionPolicy.policy.credentialRestriction === "developer_setup"
|
|
16016
16637
|
);
|
|
16017
|
-
const firstPartyMcpTools =
|
|
16638
|
+
const firstPartyMcpTools = allowedFirstPartyMcpToolsForSession5(
|
|
16018
16639
|
input.settings,
|
|
16019
16640
|
session.firstPartyMcpTools
|
|
16020
16641
|
);
|
|
@@ -17315,7 +17936,7 @@ function trimmedScheduledTaskName(name) {
|
|
|
17315
17936
|
|
|
17316
17937
|
// src/domain/scheduled-task-access.ts
|
|
17317
17938
|
import {
|
|
17318
|
-
allowedFirstPartyMcpToolsForSession as
|
|
17939
|
+
allowedFirstPartyMcpToolsForSession as allowedFirstPartyMcpToolsForSession6,
|
|
17319
17940
|
resolveFirstPartyMcpToolPolicy as resolveFirstPartyMcpToolPolicy3
|
|
17320
17941
|
} from "@opengeni/config";
|
|
17321
17942
|
import {
|
|
@@ -17420,7 +18041,7 @@ function sameMembers(left, right) {
|
|
|
17420
18041
|
function planScheduledTaskOpenGeniTools(input) {
|
|
17421
18042
|
const stored = input.creatorPolicy?.firstPartyMcpTools;
|
|
17422
18043
|
if (!stored) return { missing: [], policy: null };
|
|
17423
|
-
const effective = new Set(
|
|
18044
|
+
const effective = new Set(allowedFirstPartyMcpToolsForSession6(input.settings, stored));
|
|
17424
18045
|
const missing = resolveFirstPartyMcpToolPolicy3(input.settings).default.filter(
|
|
17425
18046
|
(tool) => !effective.has(tool) && !input.leaveOut?.has(tool)
|
|
17426
18047
|
);
|
|
@@ -17939,7 +18560,7 @@ function orderScheduledTaskAccessAttention(items) {
|
|
|
17939
18560
|
}
|
|
17940
18561
|
|
|
17941
18562
|
// src/domain/insights.ts
|
|
17942
|
-
import { configuredStaticUsageLimits as
|
|
18563
|
+
import { configuredStaticUsageLimits as configuredStaticUsageLimits5 } from "@opengeni/config";
|
|
17943
18564
|
import {
|
|
17944
18565
|
WorkspaceInsightsSnapshot
|
|
17945
18566
|
} from "@opengeni/contracts";
|
|
@@ -18070,7 +18691,7 @@ function cacheHitPct(cached2, input) {
|
|
|
18070
18691
|
function startOfUtcDay(date) {
|
|
18071
18692
|
return new Date(Date.UTC(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate()));
|
|
18072
18693
|
}
|
|
18073
|
-
function
|
|
18694
|
+
function startOfUtcMonth5(date) {
|
|
18074
18695
|
return new Date(Date.UTC(date.getUTCFullYear(), date.getUTCMonth(), 1));
|
|
18075
18696
|
}
|
|
18076
18697
|
function startOfUtcYear(date) {
|
|
@@ -18099,7 +18720,7 @@ function resolveRangeWindow(range, now) {
|
|
|
18099
18720
|
cacheSeriesLabel = "Cache hit % / UTC day";
|
|
18100
18721
|
break;
|
|
18101
18722
|
case "month":
|
|
18102
|
-
since =
|
|
18723
|
+
since = startOfUtcMonth5(now);
|
|
18103
18724
|
rangeLabel = "This month (UTC)";
|
|
18104
18725
|
priorLabel = "Prior equal window";
|
|
18105
18726
|
seriesLabel = "Credit $ / UTC day";
|
|
@@ -18230,7 +18851,7 @@ async function getWorkspaceInsights(db, settings, input, observePhase) {
|
|
|
18230
18851
|
until: window.until,
|
|
18231
18852
|
priorSince: window.priorSince,
|
|
18232
18853
|
priorUntil: window.priorUntil,
|
|
18233
|
-
monthSince:
|
|
18854
|
+
monthSince: startOfUtcMonth5(now),
|
|
18234
18855
|
granularity: input.range === "today" ? "hour" : "day",
|
|
18235
18856
|
warmGroupLimit: 24
|
|
18236
18857
|
})
|
|
@@ -18473,7 +19094,7 @@ async function getWorkspaceInsights(db, settings, input, observePhase) {
|
|
|
18473
19094
|
billing: fact ? billingPathOf(fact.billingPath) : null
|
|
18474
19095
|
};
|
|
18475
19096
|
});
|
|
18476
|
-
const limits =
|
|
19097
|
+
const limits = configuredStaticUsageLimits5(settings);
|
|
18477
19098
|
const floor = floorRows.filter((row) => {
|
|
18478
19099
|
if (!model) return true;
|
|
18479
19100
|
return row.model === model;
|
|
@@ -20721,7 +21342,7 @@ async function submitComposerDraftForRequest(deps, grant, workspaceId, sessionId
|
|
|
20721
21342
|
import { DraftTimelineAnnotations as DraftTimelineAnnotations3 } from "@opengeni/contracts";
|
|
20722
21343
|
import { HTTPException as HTTPException30 } from "hono/http-exception";
|
|
20723
21344
|
import {
|
|
20724
|
-
checkWorkspaceAllowance as
|
|
21345
|
+
checkWorkspaceAllowance as checkWorkspaceAllowance7,
|
|
20725
21346
|
deleteSessionQueueItemInTransaction,
|
|
20726
21347
|
editQueuedTurnInTransaction,
|
|
20727
21348
|
getComposerDraftInTransaction as getComposerDraftInTransaction2,
|
|
@@ -20819,7 +21440,7 @@ async function assertFreshAgentCommandAllowance(tx, deps, context, targetSession
|
|
|
20819
21440
|
}
|
|
20820
21441
|
}
|
|
20821
21442
|
const frozen = await frozenInitiatorForCommandActor2(tx, context.workspaceId, agentActor(context));
|
|
20822
|
-
const refusal = await
|
|
21443
|
+
const refusal = await checkWorkspaceAllowance7(tx, {
|
|
20823
21444
|
accountId: target.accountId,
|
|
20824
21445
|
workspaceId: context.workspaceId,
|
|
20825
21446
|
subjectId: initiatingHumanForAllowance(frozen)
|
|
@@ -22142,7 +22763,6 @@ import { createPinnedIntegrationTransport as createPinnedIntegrationTransport2 }
|
|
|
22142
22763
|
import {
|
|
22143
22764
|
buildConnectionTokenResolver,
|
|
22144
22765
|
getConnectionMetadata as getConnectionMetadata5,
|
|
22145
|
-
listConnectionsMetadata as listConnectionsMetadata4,
|
|
22146
22766
|
listEnabledMcpCapabilityServers as listEnabledMcpCapabilityServers3,
|
|
22147
22767
|
listConnectorToolPermissionPolicies,
|
|
22148
22768
|
listChangedConnectorToolPermissions,
|
|
@@ -22155,6 +22775,7 @@ import {
|
|
|
22155
22775
|
import { HTTPException as HTTPException32 } from "hono/http-exception";
|
|
22156
22776
|
import {
|
|
22157
22777
|
GMAIL_REST_MCP_TOOLS,
|
|
22778
|
+
gmailToolAvailableOnDeployment,
|
|
22158
22779
|
gmailToolSupportsScopes,
|
|
22159
22780
|
isOfficialGmailMcpConfig,
|
|
22160
22781
|
isOfficialSlackMcpConfig
|
|
@@ -22240,11 +22861,12 @@ async function resolveTarget2(input) {
|
|
|
22240
22861
|
throw new HTTPException32(403, {
|
|
22241
22862
|
message: "Only the authenticated connection owner may manage personal tool permissions"
|
|
22242
22863
|
});
|
|
22243
|
-
const
|
|
22244
|
-
|
|
22245
|
-
input.
|
|
22246
|
-
|
|
22247
|
-
|
|
22864
|
+
const source = personalConnectionDelegationSourceForGrant(input.grant);
|
|
22865
|
+
const visible = ref ? await visibleMcpAccountConnections(input.db, {
|
|
22866
|
+
accountId: input.grant.accountId,
|
|
22867
|
+
workspaceId: input.workspaceId,
|
|
22868
|
+
source: input.personalOwnerVerified && source.kind === "subject" ? source : { kind: "none" }
|
|
22869
|
+
}) : [];
|
|
22248
22870
|
const bindings = mcpAccountBindingsFromVisibleConnections({
|
|
22249
22871
|
accountId: input.grant.accountId,
|
|
22250
22872
|
workspaceId: input.workspaceId,
|
|
@@ -22286,15 +22908,20 @@ async function resolveTarget2(input) {
|
|
|
22286
22908
|
listedTools: null
|
|
22287
22909
|
};
|
|
22288
22910
|
}
|
|
22289
|
-
function reviewedConnectorToolCatalog(server, grantedScopes) {
|
|
22290
|
-
const tools = isOfficialGmailMcpConfig(server.url, server.connectionRef) ? GMAIL_REST_MCP_TOOLS.filter(
|
|
22911
|
+
function reviewedConnectorToolCatalog(server, grantedScopes, deployment) {
|
|
22912
|
+
const tools = isOfficialGmailMcpConfig(server.url, server.connectionRef) ? GMAIL_REST_MCP_TOOLS.filter(
|
|
22913
|
+
(tool) => gmailToolSupportsScopes(tool.name, grantedScopes) && gmailToolAvailableOnDeployment(tool.name, {
|
|
22914
|
+
watchTopicName: deployment.gmailWatchTopicName
|
|
22915
|
+
})
|
|
22916
|
+
) : isOfficialSlackMcpConfig(server.url, server.connectionRef) ? slackRestMcpToolsForScopes(grantedScopes) : null;
|
|
22291
22917
|
return tools?.filter((tool) => !server.allowedTools || server.allowedTools.includes(tool.name)) ?? null;
|
|
22292
22918
|
}
|
|
22293
22919
|
async function listTools(input, target) {
|
|
22294
22920
|
if (target.listedTools !== null) return target.listedTools;
|
|
22295
22921
|
const reviewed = reviewedConnectorToolCatalog(
|
|
22296
22922
|
target.server,
|
|
22297
|
-
target.connection?.grantedScopes ?? []
|
|
22923
|
+
target.connection?.grantedScopes ?? [],
|
|
22924
|
+
input.settings
|
|
22298
22925
|
);
|
|
22299
22926
|
if (reviewed !== null) return reviewed;
|
|
22300
22927
|
let headers = { ...target.server.headers };
|
|
@@ -22303,7 +22930,9 @@ async function listTools(input, target) {
|
|
|
22303
22930
|
input.db,
|
|
22304
22931
|
input.settings
|
|
22305
22932
|
)({
|
|
22306
|
-
|
|
22933
|
+
// The selected account's credentials remain in its origin workspace;
|
|
22934
|
+
// approval preferences remain scoped to the workspace being configured.
|
|
22935
|
+
workspaceId: target.connection?.workspaceId ?? input.workspaceId,
|
|
22307
22936
|
...target.connection?.subjectId ? { subjectId: input.grant.subjectId } : {},
|
|
22308
22937
|
serverId: target.server.id,
|
|
22309
22938
|
toolName: "tools/list",
|
|
@@ -22560,7 +23189,11 @@ export {
|
|
|
22560
23189
|
PR_REVIEW_AUTOMATION_SETUP,
|
|
22561
23190
|
ProductionEditableArtifactSnapshotVerifier,
|
|
22562
23191
|
PublicSkillSearchError,
|
|
23192
|
+
REALTIME_VOICE_DEBIT_TYPE,
|
|
23193
|
+
REALTIME_VOICE_INSUFFICIENT_CREDITS_MESSAGE,
|
|
23194
|
+
REALTIME_VOICE_SOURCE_TYPE,
|
|
22563
23195
|
RIG_DEFAULT_VARIABLE_SET_LOAD_CONCURRENCY,
|
|
23196
|
+
RealtimeVoiceUnavailableError,
|
|
22564
23197
|
SESSION_AUTHORIZATION_DEFAULT_REAUTHORIZE_MS,
|
|
22565
23198
|
SESSION_CREATED_BY_KINDS,
|
|
22566
23199
|
SESSION_WORKFLOW_WAKE_DISPATCHER_PERIOD_MS,
|
|
@@ -22579,14 +23212,20 @@ export {
|
|
|
22579
23212
|
SystemEditableArtifactLiveScheduler,
|
|
22580
23213
|
TRANSCRIPTION_PROVIDER_REQUEST_TIMEOUT_MILLISECONDS,
|
|
22581
23214
|
TURN_ACTIVITY_CANCELLATION_HEARTBEAT_INTERVAL_MS,
|
|
23215
|
+
TranscriptionBillingRefusedError,
|
|
22582
23216
|
TranscriptionServiceError,
|
|
22583
23217
|
USER_PRESENCE_FLUSH_DELAY_MS,
|
|
22584
23218
|
USER_PRESENCE_THROTTLE_MS,
|
|
22585
23219
|
VIDEO_GENERATION_ADAPTER_VERSION,
|
|
22586
23220
|
VIDEO_GENERATION_MODEL_CATALOG,
|
|
23221
|
+
VOICE_INPUT_DEBIT_TYPE,
|
|
23222
|
+
VOICE_INPUT_SOURCE_TYPE,
|
|
23223
|
+
WEB_SEARCH_DEBIT_TYPE,
|
|
23224
|
+
WEB_SEARCH_SOURCE_TYPE,
|
|
22587
23225
|
WORKSPACE_INSIGHTS_MODEL_FILTER_MAX_UTF8_BYTES,
|
|
22588
23226
|
WORKSPACE_INSIGHTS_PROVIDER_FILTER_MAX_UTF8_BYTES,
|
|
22589
23227
|
WebCryptoEditableArtifactLiveTokens,
|
|
23228
|
+
WebSearchBillingRefusedError,
|
|
22590
23229
|
WorkspaceInsightsFilterValidationError,
|
|
22591
23230
|
acceptSessionUserMessage,
|
|
22592
23231
|
acceptSessionUserMessageWithOutcome,
|
|
@@ -22679,12 +23318,15 @@ export {
|
|
|
22679
23318
|
createCompanyProfileDurableLearningAdapter,
|
|
22680
23319
|
createGitHubSkillSourceClient,
|
|
22681
23320
|
createPublicSkillSearchClient,
|
|
23321
|
+
createRealtimeVoiceBilling,
|
|
22682
23322
|
createRigForApi,
|
|
22683
23323
|
createRigVersionForApi,
|
|
22684
23324
|
createSessionForRequest,
|
|
22685
23325
|
createSessionForRequestWithOutcome,
|
|
22686
23326
|
createUserPresenceRecorder,
|
|
22687
23327
|
createValidatedScheduledTask,
|
|
23328
|
+
createVoiceInputBilling,
|
|
23329
|
+
createWebSearchBilling,
|
|
22688
23330
|
creationInitiatorForGrant,
|
|
22689
23331
|
creditsDefaultSessionModel,
|
|
22690
23332
|
decodeEditableArtifactBrokerHint,
|
|
@@ -22697,6 +23339,7 @@ export {
|
|
|
22697
23339
|
deleteHumanQueuePrompt,
|
|
22698
23340
|
deleteRigForApi,
|
|
22699
23341
|
deleteScheduledTaskLifecycle,
|
|
23342
|
+
deploymentRealtimeVoice,
|
|
22700
23343
|
describeConfiguredExportFormats,
|
|
22701
23344
|
describeEditableArtifactExportFormats,
|
|
22702
23345
|
directPersonalConnectionSubjectId,
|
|
@@ -22920,6 +23563,8 @@ export {
|
|
|
22920
23563
|
readSessionAttachmentFiles,
|
|
22921
23564
|
readSessionLineage,
|
|
22922
23565
|
readSkill,
|
|
23566
|
+
realtimeVoiceOfferProblem,
|
|
23567
|
+
reconcileUnsettledVoiceInputCharges,
|
|
22923
23568
|
recordManagedAuthLoggedFailure,
|
|
22924
23569
|
recordRigAuditEvent,
|
|
22925
23570
|
recordSessionCreated,
|
|
@@ -23056,6 +23701,7 @@ export {
|
|
|
23056
23701
|
settingsWithRigProviderImage,
|
|
23057
23702
|
settingsWithSessionMcpServerMetadata,
|
|
23058
23703
|
settingsWithWorkspaceSandboxImage,
|
|
23704
|
+
settleVoiceInputUsage,
|
|
23059
23705
|
signedJsonAutomationAdapter,
|
|
23060
23706
|
skillBundleHash,
|
|
23061
23707
|
stableJson,
|
|
@@ -23113,6 +23759,8 @@ export {
|
|
|
23113
23759
|
videoGenerationProviderIdempotencyKey,
|
|
23114
23760
|
videoGenerationRequestDigest,
|
|
23115
23761
|
visibleMcpAccountConnections,
|
|
23762
|
+
voiceInputBillableUsage,
|
|
23763
|
+
webSearchCreditBillingActive,
|
|
23116
23764
|
withAgentConfigHttpErrors,
|
|
23117
23765
|
withDefaultEnabledCapabilityMcpTools,
|
|
23118
23766
|
withFrozenPersonalConnectionDelegations,
|