@opengeni/api-router 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/access-grant-rls.d.ts +7 -0
- package/dist/app.d.ts +2 -2
- package/dist/app.js +1 -1
- package/dist/azure-live.d.ts +28 -0
- package/dist/{chunk-IZEGLLKZ.js → chunk-F3LQ6VWR.js} +1334 -486
- package/dist/{chunk-IZEGLLKZ.js.map → chunk-F3LQ6VWR.js.map} +1 -1
- package/dist/codemode.d.ts +3 -3
- package/dist/http/api-error.d.ts +14 -0
- package/dist/index.js +1 -1
- package/dist/integrations/oauth-profiles.d.ts +3 -1
- package/dist/routes/sessions.d.ts +2 -0
- package/dist/transcription/billing-refusal.d.ts +8 -0
- package/dist/transcription/normalize.d.ts +23 -0
- package/dist/transcription/providers/azure-mai.d.ts +12 -0
- package/dist/transcription/providers/azure-openai.d.ts +4 -0
- package/dist/transcription/providers/openai.d.ts +16 -0
- package/dist/transcription/service.d.ts +10 -1
- package/dist/transcription/wav-duration.d.ts +8 -0
- package/package.json +20 -20
- package/src/access-grant-rls.ts +18 -10
- package/src/app.ts +62 -8
- package/src/azure-live.ts +103 -0
- package/src/codemode.ts +3 -5
- package/src/gateway-realtime.ts +11 -7
- package/src/http/api-error.ts +36 -1
- package/src/integrations/oauth-client.ts +9 -17
- package/src/integrations/oauth-profiles.ts +17 -6
- package/src/routes/editable-artifacts.ts +8 -0
- package/src/routes/session-artifact-associations.ts +80 -6
- package/src/routes/sessions.ts +263 -66
- package/src/routes/transcription-recordings.ts +71 -4
- package/src/routes/transcriptions.ts +26 -2
- package/src/routes/workspaces.ts +49 -7
- package/src/sandbox/channel-a.ts +22 -0
- package/src/transcription/billing-refusal.ts +14 -0
- package/src/transcription/normalize.ts +77 -0
- package/src/transcription/providers/azure-mai.ts +98 -0
- package/src/transcription/providers/azure-openai.ts +16 -7
- package/src/transcription/providers/openai.ts +79 -5
- package/src/transcription/segmenter.ts +7 -1
- package/src/transcription/service.ts +215 -10
- package/src/transcription/wav-duration.ts +39 -0
|
@@ -6517,8 +6517,8 @@ import {
|
|
|
6517
6517
|
canonicalPublicOrigin as canonicalPublicOrigin3,
|
|
6518
6518
|
configuredAllowedReasoningEfforts,
|
|
6519
6519
|
resolveFirstPartyMcpToolPolicy as resolveFirstPartyMcpToolPolicy2,
|
|
6520
|
-
|
|
6521
|
-
|
|
6520
|
+
UnsupportedLatencyModeError as UnsupportedLatencyModeError2,
|
|
6521
|
+
voiceInputPricingIssues
|
|
6522
6522
|
} from "@opengeni/config";
|
|
6523
6523
|
import {
|
|
6524
6524
|
AGENT_CAPABILITY_IDS,
|
|
@@ -6532,6 +6532,7 @@ import {
|
|
|
6532
6532
|
resolveWorkspaceMemoryEnabled,
|
|
6533
6533
|
resolveWorkspaceMemoryPromptMode,
|
|
6534
6534
|
VOICE_INPUT_ACCEPTED_MIME_TYPES as VOICE_INPUT_ACCEPTED_MIME_TYPES2,
|
|
6535
|
+
WorkspaceVoiceInputSettings as WorkspaceVoiceInputSettings3,
|
|
6535
6536
|
TRANSCRIPTION_RECORDING_PROVIDER_SEGMENT_SECONDS as TRANSCRIPTION_RECORDING_PROVIDER_SEGMENT_SECONDS2,
|
|
6536
6537
|
ToolGatewayApprovalRequest as ToolGatewayApprovalRequest2,
|
|
6537
6538
|
ToolGatewayCallRequest as ToolGatewayCallRequest2
|
|
@@ -6611,7 +6612,11 @@ import {
|
|
|
6611
6612
|
ModelUnavailableError,
|
|
6612
6613
|
ScheduledTaskTargetAccessChange
|
|
6613
6614
|
} from "@opengeni/contracts";
|
|
6614
|
-
import {
|
|
6615
|
+
import {
|
|
6616
|
+
isDatabaseConnectionLoss,
|
|
6617
|
+
nestedPostgresSqlState as nestedPostgresSqlState4,
|
|
6618
|
+
WorkspaceControlBusyError
|
|
6619
|
+
} from "@opengeni/db";
|
|
6615
6620
|
import { HTTPException as HTTPException20 } from "hono/http-exception";
|
|
6616
6621
|
var ApiHttpError = class extends HTTPException20 {
|
|
6617
6622
|
code;
|
|
@@ -6678,6 +6683,21 @@ function databaseUnavailableHttpError(error, method) {
|
|
|
6678
6683
|
details: { code: DATABASE_UNAVAILABLE_ERROR_DETAIL_CODE }
|
|
6679
6684
|
});
|
|
6680
6685
|
}
|
|
6686
|
+
var DATABASE_CONTENTION_ERROR_DETAIL_CODE = "DATABASE_CONTENTION";
|
|
6687
|
+
function databaseContentionHttpError(error, method, options = {}) {
|
|
6688
|
+
if (error instanceof ApiHttpError) return null;
|
|
6689
|
+
if (error instanceof HTTPException20 && error.status < 500) return null;
|
|
6690
|
+
const sqlState = nestedPostgresSqlState4(error);
|
|
6691
|
+
if (sqlState !== "40P01" && sqlState !== "40001") return null;
|
|
6692
|
+
const safeMethod = method === "GET" || method === "HEAD" || method === "OPTIONS";
|
|
6693
|
+
return new ApiHttpError(503, {
|
|
6694
|
+
code: "upstream_unavailable",
|
|
6695
|
+
message: "Opengeni hit transient database contention. Retry shortly.",
|
|
6696
|
+
retryable: true,
|
|
6697
|
+
outcomeUnknown: options.outcomeUnknown ?? !safeMethod,
|
|
6698
|
+
details: { code: DATABASE_CONTENTION_ERROR_DETAIL_CODE, sqlState }
|
|
6699
|
+
});
|
|
6700
|
+
}
|
|
6681
6701
|
function agentConfigHttpError(error) {
|
|
6682
6702
|
const cause = error instanceof AgentConfigError ? error : error instanceof HTTPException20 && error.cause instanceof AgentConfigError ? error.cause : null;
|
|
6683
6703
|
if (!cause) return null;
|
|
@@ -7065,8 +7085,8 @@ import {
|
|
|
7065
7085
|
CodexCompactionV2ProviderLockedError,
|
|
7066
7086
|
ManagedAuthActorLeaseOutcomeUnknownError,
|
|
7067
7087
|
hasPermission as hasPermission34,
|
|
7068
|
-
requireAccessGrant as
|
|
7069
|
-
requireAccessGrantAuthorization as
|
|
7088
|
+
requireAccessGrant as requireAccessGrant53,
|
|
7089
|
+
requireAccessGrantAuthorization as requireAccessGrantAuthorization41,
|
|
7070
7090
|
accountScopedApiKeyWorkspaceAuthority as accountScopedApiKeyWorkspaceAuthority10,
|
|
7071
7091
|
requireAccessContext as requireAccessContext15,
|
|
7072
7092
|
requirePermission as requirePermission13,
|
|
@@ -8515,7 +8535,7 @@ function startManagedSignInNotificationDelivery(db, transport) {
|
|
|
8515
8535
|
if (active) return;
|
|
8516
8536
|
active = true;
|
|
8517
8537
|
try {
|
|
8518
|
-
for (let
|
|
8538
|
+
for (let count2 = 0; count2 < 10; count2++)
|
|
8519
8539
|
if (await deliverManagedSignInNotification(db, transport) === "outcome_unknown") break;
|
|
8520
8540
|
} finally {
|
|
8521
8541
|
active = false;
|
|
@@ -10277,9 +10297,9 @@ function dispositionFilenameParameters(filename) {
|
|
|
10277
10297
|
const cleaned = Array.from(
|
|
10278
10298
|
filename.replace(/[\u0000-\u001f\u007f/\\]+/g, "_").replace(/[\ud800-\udbff](?![\udc00-\udfff])|(?<![\ud800-\udbff])[\udc00-\udfff]/g, "_").trim()
|
|
10279
10299
|
).slice(0, DISPOSITION_FILENAME_MAX_CHARS).join("");
|
|
10280
|
-
const
|
|
10281
|
-
if (!cleaned ||
|
|
10282
|
-
return `filename="${
|
|
10300
|
+
const ascii3 = cleaned.replace(/[^A-Za-z0-9._ -]+/g, "_").replace(/\s+/g, " ").trim().slice(0, DISPOSITION_FILENAME_MAX_CHARS) || "download";
|
|
10301
|
+
if (!cleaned || ascii3 === cleaned) return `filename="${ascii3}"`;
|
|
10302
|
+
return `filename="${ascii3}"; filename*=UTF-8''${encodeExtendedParameterValue(cleaned)}`;
|
|
10283
10303
|
}
|
|
10284
10304
|
function userContentDispositionHeaders(contentType, filename) {
|
|
10285
10305
|
if (!isActiveUserContentType(contentType)) return {};
|
|
@@ -10817,13 +10837,18 @@ import {
|
|
|
10817
10837
|
import {
|
|
10818
10838
|
freezeAgentLearningPolicy,
|
|
10819
10839
|
withSessionRlsActorContext,
|
|
10820
|
-
withCreditDebitAttribution
|
|
10840
|
+
withCreditDebitAttribution,
|
|
10841
|
+
currentCreditDebitAttribution
|
|
10821
10842
|
} from "@opengeni/db";
|
|
10822
10843
|
import { HTTPException as HTTPException21 } from "hono/http-exception";
|
|
10844
|
+
function creditDebitAttributionForGrant(grant) {
|
|
10845
|
+
if (grant.principalKind === "agent_attempt") return currentCreditDebitAttribution();
|
|
10846
|
+
return grant.principalKind === "human_session" ? { kind: "human", initiatingHumanSubjectId: grant.subjectId } : ["service", "api_key", "configured_key", "mcp_gateway"].includes(grant.principalKind ?? "") ? { kind: "service" } : { kind: "unknown" };
|
|
10847
|
+
}
|
|
10823
10848
|
async function withAccessGrantSessionRlsContext(deps, grant, fn) {
|
|
10824
10849
|
if (grant.principalKind !== "agent_attempt") {
|
|
10825
10850
|
return await withCreditDebitAttribution(
|
|
10826
|
-
grant
|
|
10851
|
+
creditDebitAttributionForGrant(grant),
|
|
10827
10852
|
() => withSessionRlsActorContext({ subjectId: grant.subjectId }, fn)
|
|
10828
10853
|
);
|
|
10829
10854
|
}
|
|
@@ -11270,6 +11295,13 @@ function registerEditableArtifactRoutes(app, deps) {
|
|
|
11270
11295
|
const body3 = await parseBoundedMaterializationRequest(context);
|
|
11271
11296
|
const scope = editableArtifactScope({ accountId: grant.accountId, workspaceId });
|
|
11272
11297
|
const actor = editableArtifactActorForGrant(grant, body3.replicaId);
|
|
11298
|
+
if (!deps.settings.artifactMaterializerDeployed) {
|
|
11299
|
+
throw new ApiHttpError(503, {
|
|
11300
|
+
code: "upstream_unavailable",
|
|
11301
|
+
message: "File export is not available on this deployment.",
|
|
11302
|
+
retryable: false
|
|
11303
|
+
});
|
|
11304
|
+
}
|
|
11273
11305
|
try {
|
|
11274
11306
|
const result = await requireEditableArtifactExports(deps).enqueueMaterialization({
|
|
11275
11307
|
scope,
|
|
@@ -11815,7 +11847,7 @@ import {
|
|
|
11815
11847
|
confirmLegacyInstruction,
|
|
11816
11848
|
getKnowledgeEntry,
|
|
11817
11849
|
listKnowledgeEntries,
|
|
11818
|
-
nestedPostgresSqlState as
|
|
11850
|
+
nestedPostgresSqlState as nestedPostgresSqlState5,
|
|
11819
11851
|
saveKnowledgeEntry,
|
|
11820
11852
|
saveAgentInstruction,
|
|
11821
11853
|
getAgentInstruction,
|
|
@@ -11861,7 +11893,7 @@ function registerKnowledgeEntryTools(server, deps, grant, sessionId) {
|
|
|
11861
11893
|
]
|
|
11862
11894
|
};
|
|
11863
11895
|
}
|
|
11864
|
-
const state =
|
|
11896
|
+
const state = nestedPostgresSqlState5(error);
|
|
11865
11897
|
const message = surface === "instruction" ? state === "40001" ? "This instruction changed. Read its current content and baseline, then retry the same intended edit against that exact state." : state === "23505" ? "This instruction operation ID was already used with different input. Reuse it only for an exact retry." : state === "42501" ? "The instruction change is unavailable in this task's scope or Agent learning policy. Off disables authoring, not the task. Do not ask for approval to continue." : state === "22023" || state === "23514" ? `Invalid instruction change. Read the current instruction, use append for a new rule, or edit with one localized exact oldText match to update or remove text. Agents cannot replace the complete instruction. Each supplied text and the resulting instruction must stay within ${WORKSPACE_INSTRUCTION_POLICY_CONTENT_MAX_CHARS} characters, the same limit as the human editor.` : "Workspace instructions are temporarily unavailable. Retry the same operation ID only if the prior change may have succeeded." : state === "40001" ? "This entry changed. Read its current revision and retry your correction with the current version." : state === "23505" ? "This operation ID was already used with different input. Reuse it only for an exact retry." : state === "42501" ? "The entry, reference, or write is unavailable in this task's scope and Agent learning policy. Off disables saving, not your task. Do not ask for an approval to continue." : state === "22023" || state === "23514" ? "Invalid Knowledge entry or relationship. Check the referenced IDs and revision before retrying." : "Knowledge is temporarily unavailable. Your task can continue; retry the same operation ID if saving may have succeeded.";
|
|
11866
11898
|
return {
|
|
11867
11899
|
isError: true,
|
|
@@ -12406,7 +12438,7 @@ import { HTTPException as HTTPException22 } from "hono/http-exception";
|
|
|
12406
12438
|
import {
|
|
12407
12439
|
getScheduledScopedRigVersionMetadata,
|
|
12408
12440
|
loadWorkspaceEnvironmentForRun,
|
|
12409
|
-
nestedPostgresSqlState as
|
|
12441
|
+
nestedPostgresSqlState as nestedPostgresSqlState6
|
|
12410
12442
|
} from "@opengeni/db";
|
|
12411
12443
|
async function loadSessionAttachVariableSetValues(db, settings, input) {
|
|
12412
12444
|
const { accountId, workspaceId, session, subjectId } = input;
|
|
@@ -12456,7 +12488,7 @@ async function loadSessionAttachVariableSetValues(db, settings, input) {
|
|
|
12456
12488
|
variableSetId,
|
|
12457
12489
|
source
|
|
12458
12490
|
});
|
|
12459
|
-
if (
|
|
12491
|
+
if (nestedPostgresSqlState6(error) === "42501") {
|
|
12460
12492
|
throw new ApiHttpError(403, {
|
|
12461
12493
|
code: "forbidden",
|
|
12462
12494
|
message: source === "sandbox_environment_default" ? "A default Variable Set of this session's Sandbox Environment is not available to this session." : "A Variable Set selected for this session is not available to this session.",
|
|
@@ -12470,7 +12502,7 @@ async function loadSessionAttachVariableSetValues(db, settings, input) {
|
|
|
12470
12502
|
return values;
|
|
12471
12503
|
}
|
|
12472
12504
|
function logAttachFailure(message, error, context) {
|
|
12473
|
-
const sqlState =
|
|
12505
|
+
const sqlState = nestedPostgresSqlState6(error);
|
|
12474
12506
|
console.warn(`[session-attach] ${message}`, {
|
|
12475
12507
|
...context,
|
|
12476
12508
|
...sqlState ? { sqlState } : {},
|
|
@@ -12498,6 +12530,7 @@ import {
|
|
|
12498
12530
|
SandboxResumeIdentityMismatchError,
|
|
12499
12531
|
SandboxResumeIdentityUnavailableError,
|
|
12500
12532
|
RoutingActiveRouteChangedError,
|
|
12533
|
+
RoutingMutationOutputRejectedError,
|
|
12501
12534
|
RoutingWorkspaceRootChangedError,
|
|
12502
12535
|
SelfhostedWorkspaceRootChangedError,
|
|
12503
12536
|
ChannelAConflictError,
|
|
@@ -13594,6 +13627,20 @@ function mapChannelAError(error, waitSignal) {
|
|
|
13594
13627
|
});
|
|
13595
13628
|
if (error instanceof SandboxResumeIdentityMismatchError || error instanceof SandboxResumeIdentityUnavailableError)
|
|
13596
13629
|
return new HTTPException22(409, { message: error.message });
|
|
13630
|
+
if (error instanceof RoutingMutationOutputRejectedError) {
|
|
13631
|
+
const revoked = error.reasonCode === "authority_revoked";
|
|
13632
|
+
return new ApiHttpError(revoked ? 403 : 409, {
|
|
13633
|
+
code: revoked ? "forbidden" : "conflict",
|
|
13634
|
+
message: error.message,
|
|
13635
|
+
retryable: false,
|
|
13636
|
+
outcomeUnknown: false,
|
|
13637
|
+
details: {
|
|
13638
|
+
code: error.code,
|
|
13639
|
+
reasonCode: error.reasonCode,
|
|
13640
|
+
physicalOutcome: "resolved"
|
|
13641
|
+
}
|
|
13642
|
+
});
|
|
13643
|
+
}
|
|
13597
13644
|
if (error instanceof RoutingActiveRouteChangedError || error instanceof RoutingWorkspaceRootChangedError || error instanceof SelfhostedWorkspaceRootChangedError || error instanceof ChannelAFileSystemRouteChangedError)
|
|
13598
13645
|
return new ApiHttpError(409, {
|
|
13599
13646
|
code: "conflict",
|
|
@@ -13660,6 +13707,13 @@ function channelAOperationFailureDiagnostic(error, waitSignal) {
|
|
|
13660
13707
|
errorCode: "sandbox_channel_a_cancelled"
|
|
13661
13708
|
};
|
|
13662
13709
|
}
|
|
13710
|
+
if (error instanceof RoutingMutationOutputRejectedError) {
|
|
13711
|
+
return {
|
|
13712
|
+
reason: "request_rejected",
|
|
13713
|
+
status: error.reasonCode === "authority_revoked" ? 403 : 409,
|
|
13714
|
+
errorCode: "sandbox_channel_a_operation_failed"
|
|
13715
|
+
};
|
|
13716
|
+
}
|
|
13663
13717
|
if (error instanceof SandboxProviderReadLockUnavailableError) {
|
|
13664
13718
|
return {
|
|
13665
13719
|
reason: "provider_read_busy",
|
|
@@ -15507,8 +15561,8 @@ function tokenHash(value) {
|
|
|
15507
15561
|
function mcpOAuthRegistrationClientKey(c, trust) {
|
|
15508
15562
|
return `mcp-oauth-registration:${trustedRequestSourceRateLimitKey(c, trust)}`;
|
|
15509
15563
|
}
|
|
15510
|
-
function expiresIn(
|
|
15511
|
-
return new Date(Date.now() +
|
|
15564
|
+
function expiresIn(seconds2) {
|
|
15565
|
+
return new Date(Date.now() + seconds2 * 1e3);
|
|
15512
15566
|
}
|
|
15513
15567
|
function authorizationRedirect(redirectUri, params) {
|
|
15514
15568
|
const url = new URL(redirectUri);
|
|
@@ -16872,7 +16926,10 @@ import {
|
|
|
16872
16926
|
} from "@opengeni/db/organization-integration-policy";
|
|
16873
16927
|
import { Client as Client2 } from "@modelcontextprotocol/sdk/client/index.js";
|
|
16874
16928
|
import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
|
|
16875
|
-
import {
|
|
16929
|
+
import {
|
|
16930
|
+
findIntegrationsOauthClient,
|
|
16931
|
+
parseIntegrationsOauthClientsJson
|
|
16932
|
+
} from "@opengeni/config";
|
|
16876
16933
|
import {
|
|
16877
16934
|
OAuthStartResponse,
|
|
16878
16935
|
selectCanonicalPersonalSlackConnection
|
|
@@ -17002,7 +17059,11 @@ import {
|
|
|
17002
17059
|
import { OPENGENI_PERSONAL_SLACK_MCP_URL } from "@opengeni/contracts";
|
|
17003
17060
|
import { HTTPException as HTTPException31 } from "hono/http-exception";
|
|
17004
17061
|
import { z as z25 } from "zod";
|
|
17005
|
-
import {
|
|
17062
|
+
import {
|
|
17063
|
+
GMAIL_REST_MCP_TOOLS,
|
|
17064
|
+
gmailToolAvailableOnDeployment,
|
|
17065
|
+
gmailToolSupportsScopes
|
|
17066
|
+
} from "@opengeni/runtime/gmail-rest-mcp";
|
|
17006
17067
|
|
|
17007
17068
|
// src/integrations/provider-domain.ts
|
|
17008
17069
|
import { HTTPException as HTTPException30 } from "hono/http-exception";
|
|
@@ -17137,10 +17198,12 @@ var OFFICIAL_GMAIL_PROFILE = {
|
|
|
17137
17198
|
}
|
|
17138
17199
|
return { gmailEmail: payload.emailAddress };
|
|
17139
17200
|
},
|
|
17140
|
-
toolsForScopes: (scopes) => {
|
|
17141
|
-
return GMAIL_REST_MCP_TOOLS.filter(
|
|
17142
|
-
(
|
|
17143
|
-
|
|
17201
|
+
toolsForScopes: (scopes, deployment) => {
|
|
17202
|
+
return GMAIL_REST_MCP_TOOLS.filter(
|
|
17203
|
+
(tool) => gmailToolSupportsScopes(tool.name, scopes) && gmailToolAvailableOnDeployment(tool.name, {
|
|
17204
|
+
watchTopicName: deployment.gmailWatchTopicName
|
|
17205
|
+
})
|
|
17206
|
+
).map(({ name, description }) => ({ name, ...description ? { description } : {} }));
|
|
17144
17207
|
}
|
|
17145
17208
|
}
|
|
17146
17209
|
};
|
|
@@ -18472,7 +18535,6 @@ function operatorClientForAs(settings, as) {
|
|
|
18472
18535
|
};
|
|
18473
18536
|
}
|
|
18474
18537
|
function operatorClientEntryFor(settings, candidates) {
|
|
18475
|
-
const normalizedCandidates = new Set(candidates.map(normalizedIssuerKey));
|
|
18476
18538
|
const candidateOrigins = candidates.flatMap((candidate) => {
|
|
18477
18539
|
try {
|
|
18478
18540
|
return [new URL(candidate).origin];
|
|
@@ -18489,22 +18551,10 @@ function operatorClientEntryFor(settings, candidates) {
|
|
|
18489
18551
|
return resolved;
|
|
18490
18552
|
}
|
|
18491
18553
|
}
|
|
18492
|
-
|
|
18493
|
-
|
|
18494
|
-
candidates
|
|
18554
|
+
return findIntegrationsOauthClient(
|
|
18555
|
+
parseIntegrationsOauthClientsJson(settings.integrationsOauthClientsJson),
|
|
18556
|
+
candidates
|
|
18495
18557
|
);
|
|
18496
|
-
for (const key of exactKeys) {
|
|
18497
|
-
const entry2 = configured2[key];
|
|
18498
|
-
if (entry2) {
|
|
18499
|
-
return entry2;
|
|
18500
|
-
}
|
|
18501
|
-
}
|
|
18502
|
-
for (const [key, entry2] of Object.entries(configured2)) {
|
|
18503
|
-
if (normalizedCandidates.has(normalizedIssuerKey(key))) {
|
|
18504
|
-
return entry2;
|
|
18505
|
-
}
|
|
18506
|
-
}
|
|
18507
|
-
return null;
|
|
18508
18558
|
}
|
|
18509
18559
|
function gmailOAuthClientConfigured(settings) {
|
|
18510
18560
|
const entry2 = operatorClientEntryFor(settings, ["https://accounts.google.com"]);
|
|
@@ -19122,7 +19172,8 @@ async function verifyMcpToolsListNonFatal(observability, settings, state, token,
|
|
|
19122
19172
|
grantRejected = local.isRejectedGrant?.(payload) ?? false;
|
|
19123
19173
|
providerIdentity = local.validateIdentity(payload);
|
|
19124
19174
|
const verifiedTools = local.toolsForScopes(
|
|
19125
|
-
grantedScopes(token.scopeText, state.authorizeScopes, profile)
|
|
19175
|
+
grantedScopes(token.scopeText, state.authorizeScopes, profile),
|
|
19176
|
+
settings
|
|
19126
19177
|
);
|
|
19127
19178
|
if (local.required && verifiedTools.length === 0)
|
|
19128
19179
|
throw new Error("Connector verification did not report an authorized tool scope");
|
|
@@ -20502,15 +20553,15 @@ import { COMMAND_OUTPUT_MAX_BYTES } from "@opengeni/db/session-background-comman
|
|
|
20502
20553
|
var COMMAND_READ_MAX_WAIT_SECONDS = 50;
|
|
20503
20554
|
var COMMAND_WAIT_DEFAULT_SECONDS = 45;
|
|
20504
20555
|
async function readCommandWithWait(input) {
|
|
20505
|
-
const
|
|
20506
|
-
if (!Number.isInteger(
|
|
20556
|
+
const seconds2 = input.waitSeconds ?? 0;
|
|
20557
|
+
if (!Number.isInteger(seconds2) || seconds2 < 0 || seconds2 > COMMAND_READ_MAX_WAIT_SECONDS) {
|
|
20507
20558
|
throw new Error("waitSeconds must be between 0 and 50");
|
|
20508
20559
|
}
|
|
20509
20560
|
if (input.maxOutputBytes !== void 0 && (!Number.isInteger(input.maxOutputBytes) || input.maxOutputBytes < 4 || input.maxOutputBytes > COMMAND_OUTPUT_MAX_BYTES)) {
|
|
20510
20561
|
throw new Error("maxOutputBytes must be between 4 and 65536");
|
|
20511
20562
|
}
|
|
20512
20563
|
const started2 = Date.now();
|
|
20513
|
-
const deadline = started2 +
|
|
20564
|
+
const deadline = started2 + seconds2 * 1e3;
|
|
20514
20565
|
let unsubscribe;
|
|
20515
20566
|
let wake;
|
|
20516
20567
|
let changed2 = false;
|
|
@@ -20521,7 +20572,7 @@ async function readCommandWithWait(input) {
|
|
|
20521
20572
|
};
|
|
20522
20573
|
input.signal?.addEventListener("abort", notify);
|
|
20523
20574
|
try {
|
|
20524
|
-
if (
|
|
20575
|
+
if (seconds2 > 0) {
|
|
20525
20576
|
try {
|
|
20526
20577
|
unsubscribe = await input.subscribe((events) => {
|
|
20527
20578
|
if (events.some(
|
|
@@ -20541,7 +20592,7 @@ async function readCommandWithWait(input) {
|
|
|
20541
20592
|
return {
|
|
20542
20593
|
...result,
|
|
20543
20594
|
waitedMs: Date.now() - started2,
|
|
20544
|
-
timedOut:
|
|
20595
|
+
timedOut: seconds2 > 0 && expired && !result.terminal && result.chunks.length === 0 && !result.hasMore,
|
|
20545
20596
|
aborted: input.signal?.aborted ?? false,
|
|
20546
20597
|
liveFanout
|
|
20547
20598
|
};
|
|
@@ -22021,7 +22072,7 @@ function buildScheduledTaskDetailMcp(task, budget, reducedForBytes) {
|
|
|
22021
22072
|
deliveredCount: resources.length,
|
|
22022
22073
|
originalBytes: jsonBytes(task.agentConfig.resources),
|
|
22023
22074
|
truncatedIdentityFieldCount: resourceProjections.reduce(
|
|
22024
|
-
(
|
|
22075
|
+
(count2, projection) => count2 + projection.truncatedFieldCount,
|
|
22025
22076
|
0
|
|
22026
22077
|
)
|
|
22027
22078
|
},
|
|
@@ -22030,7 +22081,7 @@ function buildScheduledTaskDetailMcp(task, budget, reducedForBytes) {
|
|
|
22030
22081
|
deliveredCount: tools.length,
|
|
22031
22082
|
originalBytes: jsonBytes(task.agentConfig.tools),
|
|
22032
22083
|
truncatedIdentityFieldCount: toolProjections.reduce(
|
|
22033
|
-
(
|
|
22084
|
+
(count2, projection) => count2 + projection.truncatedFieldCount,
|
|
22034
22085
|
0
|
|
22035
22086
|
)
|
|
22036
22087
|
},
|
|
@@ -25033,9 +25084,9 @@ function slackRetryAfterMs(response) {
|
|
|
25033
25084
|
if (response.status !== 429) return null;
|
|
25034
25085
|
const raw = response.headers.get("retry-after");
|
|
25035
25086
|
if (!raw || !/^\d+$/.test(raw)) return null;
|
|
25036
|
-
const
|
|
25037
|
-
if (!Number.isSafeInteger(
|
|
25038
|
-
return Math.min(
|
|
25087
|
+
const seconds2 = Number(raw);
|
|
25088
|
+
if (!Number.isSafeInteger(seconds2) || seconds2 < 1) return null;
|
|
25089
|
+
return Math.min(seconds2, 3600) * 1e3;
|
|
25039
25090
|
}
|
|
25040
25091
|
function assertOpenGeniSlackBotScopes(grantedScopes2) {
|
|
25041
25092
|
const policy = evaluateOpenGeniSlackBotScopes(grantedScopes2);
|
|
@@ -25613,11 +25664,11 @@ function nextSlackFilesListPage(payload, requested, returnedFileCount) {
|
|
|
25613
25664
|
if (responseMetadata && Object.hasOwn(responseMetadata, "next_cursor")) {
|
|
25614
25665
|
throw new SlackBotProviderError("invalid_files_paging");
|
|
25615
25666
|
}
|
|
25616
|
-
const
|
|
25667
|
+
const count2 = paging?.count;
|
|
25617
25668
|
const total = paging?.total;
|
|
25618
25669
|
const page2 = paging?.page;
|
|
25619
25670
|
const pages = paging?.pages;
|
|
25620
|
-
if (!positiveSafeInt(
|
|
25671
|
+
if (!positiveSafeInt(count2) || !nonNegativeSafeInt(total) || !positiveSafeInt(page2) || !nonNegativeSafeInt(pages) || count2 !== requested.count || page2 !== requested.page || returnedFileCount > count2 || pages !== (total === 0 ? 0 : Math.ceil(total / count2)) || page2 > Math.max(1, pages)) {
|
|
25621
25672
|
throw new SlackBotProviderError("invalid_files_paging");
|
|
25622
25673
|
}
|
|
25623
25674
|
if (page2 >= pages) return null;
|
|
@@ -74235,7 +74286,7 @@ import {
|
|
|
74235
74286
|
getOrganizationIntegrationPolicy,
|
|
74236
74287
|
updateOrganizationIntegrationPolicy
|
|
74237
74288
|
} from "@opengeni/db/organization-integration-policy";
|
|
74238
|
-
import { nestedPostgresSqlState as
|
|
74289
|
+
import { nestedPostgresSqlState as nestedPostgresSqlState7 } from "@opengeni/db";
|
|
74239
74290
|
import { HTTPException as HTTPException46 } from "hono/http-exception";
|
|
74240
74291
|
import { z as z32 } from "zod";
|
|
74241
74292
|
async function authorizeOrganizationIntegrationAdministration(context, deps, accountId, mutation) {
|
|
@@ -74264,7 +74315,7 @@ async function policyResponse(readOrWrite) {
|
|
|
74264
74315
|
return await readOrWrite();
|
|
74265
74316
|
} catch (error) {
|
|
74266
74317
|
if (error instanceof HTTPException46) throw error;
|
|
74267
|
-
const state =
|
|
74318
|
+
const state = nestedPostgresSqlState7(error);
|
|
74268
74319
|
if (state === "42501") {
|
|
74269
74320
|
throw new HTTPException46(403, { message: "Organization administration required" });
|
|
74270
74321
|
}
|
|
@@ -81187,7 +81238,7 @@ import {
|
|
|
81187
81238
|
listAgentLearningOverrides,
|
|
81188
81239
|
listKnowledgeEntryHistory,
|
|
81189
81240
|
listKnowledgeReviewBatches,
|
|
81190
|
-
nestedPostgresSqlState as
|
|
81241
|
+
nestedPostgresSqlState as nestedPostgresSqlState8,
|
|
81191
81242
|
restoreKnowledgeEntry,
|
|
81192
81243
|
reviewKnowledgeEntry,
|
|
81193
81244
|
saveAgentLearningSettings,
|
|
@@ -81241,7 +81292,7 @@ function knowledgeHttpError(error) {
|
|
|
81241
81292
|
throw new HTTPException57(422, {
|
|
81242
81293
|
message: error.issues[0]?.message ?? "Invalid Knowledge request"
|
|
81243
81294
|
});
|
|
81244
|
-
const state =
|
|
81295
|
+
const state = nestedPostgresSqlState8(error);
|
|
81245
81296
|
if (state === "40001" || state === "23505")
|
|
81246
81297
|
throw new HTTPException57(409, { message: "This item changed. Refresh it and try again." });
|
|
81247
81298
|
if (state === "42501")
|
|
@@ -83461,12 +83512,12 @@ function creditScopeMetadata(scope) {
|
|
|
83461
83512
|
}
|
|
83462
83513
|
function creditScopeFromMetadata(metadata) {
|
|
83463
83514
|
if (!metadata || !Object.keys(metadata).some((key) => key.startsWith(prefix))) return void 0;
|
|
83464
|
-
const
|
|
83465
|
-
if (!Number.isInteger(
|
|
83515
|
+
const count2 = Number(metadata[countKey]);
|
|
83516
|
+
if (!Number.isInteger(count2) || count2 < 1 || count2 > 24) {
|
|
83466
83517
|
throw new Error("Invalid promotional credit snapshot");
|
|
83467
83518
|
}
|
|
83468
83519
|
let encoded = "";
|
|
83469
|
-
for (let index = 0; index <
|
|
83520
|
+
for (let index = 0; index < count2; index++) {
|
|
83470
83521
|
const chunk = metadata[`${prefix}${index}`];
|
|
83471
83522
|
if (!chunk) throw new Error("Incomplete promotional credit snapshot");
|
|
83472
83523
|
encoded += chunk;
|
|
@@ -95517,7 +95568,7 @@ import {
|
|
|
95517
95568
|
getPrReviewAppRegistrationSecret,
|
|
95518
95569
|
listPrReviewAppRegistrations as listPrReviewAppRegistrations3,
|
|
95519
95570
|
listPrReviewRepositoryBindings as listPrReviewRepositoryBindings2,
|
|
95520
|
-
nestedPostgresSqlState as
|
|
95571
|
+
nestedPostgresSqlState as nestedPostgresSqlState10,
|
|
95521
95572
|
recordAuditEvent as recordAuditEvent12,
|
|
95522
95573
|
updatePrReviewAppRegistration,
|
|
95523
95574
|
updatePrReviewRepositoryBinding
|
|
@@ -95733,7 +95784,7 @@ import {
|
|
|
95733
95784
|
getAutomationSourceSecret as getAutomationSourceSecret2,
|
|
95734
95785
|
listPrReviewAppRegistrations as listPrReviewAppRegistrations2,
|
|
95735
95786
|
listPrReviewRepositoryBindings,
|
|
95736
|
-
nestedPostgresSqlState as
|
|
95787
|
+
nestedPostgresSqlState as nestedPostgresSqlState9,
|
|
95737
95788
|
PrReviewDispatchAuthorityError,
|
|
95738
95789
|
recordAuditEvent as recordAuditEvent11,
|
|
95739
95790
|
resolveManagedGitHubPrReviewRoute,
|
|
@@ -96774,7 +96825,7 @@ function registerPrReviewGitHubRoutes(app, deps) {
|
|
|
96774
96825
|
if (error instanceof PrReviewDispatchAuthorityError) {
|
|
96775
96826
|
throw new HTTPException76(409, { message: error.message });
|
|
96776
96827
|
}
|
|
96777
|
-
if (
|
|
96828
|
+
if (nestedPostgresSqlState9(error) === "23505") {
|
|
96778
96829
|
throw new HTTPException76(409, {
|
|
96779
96830
|
message: "One of these repositories is already connected to Opengeni Lens in another workspace"
|
|
96780
96831
|
});
|
|
@@ -97519,7 +97570,7 @@ async function mapPrReviewUniqueConflict(operation, message) {
|
|
|
97519
97570
|
try {
|
|
97520
97571
|
return await operation;
|
|
97521
97572
|
} catch (error) {
|
|
97522
|
-
if (
|
|
97573
|
+
if (nestedPostgresSqlState10(error) === "23505") {
|
|
97523
97574
|
throw new HTTPException77(409, { message });
|
|
97524
97575
|
}
|
|
97525
97576
|
throw error;
|
|
@@ -100182,219 +100233,10 @@ function registerScheduledTaskRoutes(app, deps) {
|
|
|
100182
100233
|
});
|
|
100183
100234
|
}
|
|
100184
100235
|
|
|
100185
|
-
// src/
|
|
100186
|
-
import { getRetainedProviderCommand } from "@opengeni/db/retained-provider-commands";
|
|
100187
|
-
import { assertGoalResumeAllowed as assertGoalResumeAllowed2, GoalResumeBlockedError } from "@opengeni/core";
|
|
100188
|
-
import { UnsupportedLatencyModeError } from "@opengeni/config";
|
|
100189
|
-
import { searchSessionMessagesForSubject, SessionMessageSearchCursorError } from "@opengeni/db";
|
|
100190
|
-
import { listSessionEventSlices } from "@opengeni/db/session-event-slices";
|
|
100191
|
-
import * as sessionPreviewSchema from "@opengeni/db/schema";
|
|
100192
|
-
import { SessionMessageSearchRequest } from "@opengeni/contracts";
|
|
100193
|
-
import { scheduledSessionIds } from "@opengeni/db";
|
|
100194
|
-
import { isVerifiedDelegatedHumanAuthorization as isVerifiedDelegatedHumanAuthorization4, withSiteSessionOrigin as withSiteSessionOrigin3 } from "@opengeni/core";
|
|
100195
|
-
import { freezeSessionRealtimeConnectionAccounts } from "@opengeni/core";
|
|
100196
|
-
import { SandboxRecoveryRequest } from "@opengeni/contracts";
|
|
100197
|
-
import { getManagedHumanSandboxRecovery, consentManagedHumanSandboxRecovery } from "@opengeni/core";
|
|
100198
|
-
import { SandboxRecoveryConflictError } from "@opengeni/db";
|
|
100199
|
-
import { getSessionCodexAccounts } from "@opengeni/db";
|
|
100200
|
-
import { getToolActionReview, getToolReviewDetailsPage } from "@opengeni/db";
|
|
100201
|
-
import {
|
|
100202
|
-
AcknowledgeStreamRequest,
|
|
100203
|
-
ApplySessionGoalRevisionRequest,
|
|
100204
|
-
ListSessionGoalRevisionsQuery,
|
|
100205
|
-
RejectSessionGoalRevisionRequest,
|
|
100206
|
-
RollbackSessionGoalRevisionRequest,
|
|
100207
|
-
ActivateCodexRealtimeConnectionRequest,
|
|
100208
|
-
AttachViewerRequest,
|
|
100209
|
-
BeginSessionRealtimeRequest,
|
|
100210
|
-
ClearSessionContextRequest,
|
|
100211
|
-
CodexRealtimeWebrtcRequest,
|
|
100212
|
-
GatewayRealtimeConnectRequest,
|
|
100213
|
-
ClientSessionEvent,
|
|
100214
|
-
CompactSessionContextRequest,
|
|
100215
|
-
CreateSessionRequest as CreateSessionRequest2,
|
|
100216
|
-
DeleteSessionQueueItemRequest,
|
|
100217
|
-
EditSessionQueueItemRequest,
|
|
100218
|
-
EndSessionRealtimeRequest,
|
|
100219
|
-
FsDeleteRequest,
|
|
100220
|
-
FsListBatchRequest,
|
|
100221
|
-
FsListRequest,
|
|
100222
|
-
FsMkdirRequest,
|
|
100223
|
-
FsMoveRequest,
|
|
100224
|
-
FsReadRequest,
|
|
100225
|
-
FsWriteRequest,
|
|
100226
|
-
ForkSessionRequest,
|
|
100227
|
-
HumanInputRequestStatus,
|
|
100228
|
-
GitDiffRequest,
|
|
100229
|
-
GitReadBatchRequest,
|
|
100230
|
-
GitLogRequest,
|
|
100231
|
-
GitShowRequest,
|
|
100232
|
-
GitStatusRequest,
|
|
100233
|
-
MoveSessionQueueItemRequest,
|
|
100234
|
-
PtyCloseRequest,
|
|
100235
|
-
PtyOpenRequest,
|
|
100236
|
-
PtyResizeRequest,
|
|
100237
|
-
PtyWriteRequest,
|
|
100238
|
-
PublishSandboxFileArtifactRequest,
|
|
100239
|
-
RenewSessionRealtimeRequest,
|
|
100240
|
-
SyncSessionRealtimeLedgerRequest,
|
|
100241
|
-
SessionControlRequest,
|
|
100242
|
-
SessionRetryRequest,
|
|
100243
|
-
SESSION_EVENT_RAW_DELTA_TYPES as SESSION_EVENT_RAW_DELTA_TYPES2,
|
|
100244
|
-
SessionEventPayloadMode as SessionEventPayloadMode2,
|
|
100245
|
-
SessionEventReadDirection as SessionEventReadDirection2,
|
|
100246
|
-
SessionEventReadMode as SessionEventReadMode2,
|
|
100247
|
-
SessionEventLatestClass as SessionEventLatestClass2,
|
|
100248
|
-
SessionEventResultMode as SessionEventResultMode2,
|
|
100249
|
-
SessionScopeSubjectId,
|
|
100250
|
-
SessionEventSemanticClass as SessionEventSemanticClass2,
|
|
100251
|
-
SessionEventType as SessionEventType2,
|
|
100252
|
-
SessionMcpServerId,
|
|
100253
|
-
SaveNewSessionDraftRequest,
|
|
100254
|
-
compactSessionEventResult as compactSessionEventResult3,
|
|
100255
|
-
sessionEventLatestClassToSemanticClass as sessionEventLatestClassToSemanticClass2,
|
|
100256
|
-
SaveComposerDraftRequest,
|
|
100257
|
-
SubmitComposerDraftRequest,
|
|
100258
|
-
SteerSessionQueueItemRequest,
|
|
100259
|
-
SteerSessionMessageRequest,
|
|
100260
|
-
TerminalExecRequest,
|
|
100261
|
-
UpdateSessionChannelRequest,
|
|
100262
|
-
UpdateSessionAttentionRequest,
|
|
100263
|
-
UpdateSessionArchiveRequest,
|
|
100264
|
-
UpdateSessionPinRequest,
|
|
100265
|
-
UpdateSessionGoalRequest,
|
|
100266
|
-
UpdateSessionMcpApprovalPolicyRequest,
|
|
100267
|
-
UpdateSessionRequest,
|
|
100268
|
-
UpdateSessionVariableSetsRequest,
|
|
100269
|
-
UpdateSessionVisibilityRequest,
|
|
100270
|
-
UpdateSessionToolPolicyRequest,
|
|
100271
|
-
UpdateSessionAgentRequest,
|
|
100272
|
-
AgentConfigError as AgentConfigError2,
|
|
100273
|
-
ViewerHeartbeatRequest,
|
|
100274
|
-
WORKSPACE_CONTROL_ACTOR_MAX_BYTES,
|
|
100275
|
-
WORK_CLAIM_CANONICAL_KEY_MAX_BYTES as WORK_CLAIM_CANONICAL_KEY_MAX_BYTES2,
|
|
100276
|
-
WORK_CLAIM_DISCOVERY_LIMIT as WORK_CLAIM_DISCOVERY_LIMIT2,
|
|
100277
|
-
WORK_CLAIM_NAMESPACE_MAX_BYTES as WORK_CLAIM_NAMESPACE_MAX_BYTES2,
|
|
100278
|
-
WORK_DISCOVERY_QUERY_MAX_CHARS as WORK_DISCOVERY_QUERY_MAX_CHARS2,
|
|
100279
|
-
WORK_DISCOVERY_RECENT_HOURS_MAX as WORK_DISCOVERY_RECENT_HOURS_MAX2,
|
|
100280
|
-
WorkClaimSubjectFilter as WorkClaimSubjectFilterSchema,
|
|
100281
|
-
WorkClaimSubjectType as WorkClaimSubjectType2,
|
|
100282
|
-
normalizeWorkClaimCanonicalKey,
|
|
100283
|
-
normalizeWorkClaimNamespace,
|
|
100284
|
-
workspaceControlUtf8Bytes
|
|
100285
|
-
} from "@opengeni/contracts";
|
|
100286
|
-
import { streamTokenDegraded } from "@opengeni/config";
|
|
100287
|
-
import {
|
|
100288
|
-
recordAuditEvent as recordAuditEvent13,
|
|
100289
|
-
acceptSessionApprovalDecision as acceptSessionApprovalDecision2,
|
|
100290
|
-
acceptSessionHumanInputResponse as acceptSessionHumanInputResponse2,
|
|
100291
|
-
clearSessionGoal,
|
|
100292
|
-
clearSessionContext,
|
|
100293
|
-
getOpenPtySession,
|
|
100294
|
-
getEnrollment as getEnrollment2,
|
|
100295
|
-
getLiveEnrollmentConnection as getLiveEnrollmentConnection7,
|
|
100296
|
-
getRetainedProcess,
|
|
100297
|
-
getSandbox as getSandbox4,
|
|
100298
|
-
getSession as getSession9,
|
|
100299
|
-
readActiveSandbox as readActiveSandbox3,
|
|
100300
|
-
deleteSessionTreeIfQuiescent,
|
|
100301
|
-
getSessionEvent,
|
|
100302
|
-
getSessionForSubject as getSessionForSubject2,
|
|
100303
|
-
getSessionGoal as getSessionGoal2,
|
|
100304
|
-
getLatestSessionModelContext,
|
|
100305
|
-
getSessionHumanInputRequest,
|
|
100306
|
-
getSessionGoalWithContinuation,
|
|
100307
|
-
getSessionGoalRevision,
|
|
100308
|
-
getSessionQueueSnapshot as getSessionQueueSnapshot2,
|
|
100309
|
-
getStreamAcknowledgment,
|
|
100310
|
-
insertPtySession,
|
|
100311
|
-
listSessionEventPage as listSessionEventPage3,
|
|
100312
|
-
listSessionHumanInputRequests,
|
|
100313
|
-
listSessionGoalRevisionPage,
|
|
100314
|
-
listSessionGoalRevisions,
|
|
100315
|
-
rejectSessionGoalRevisionWithEvent,
|
|
100316
|
-
listSessionIdsInGroup,
|
|
100317
|
-
listSessionDiscoverySummaries as listSessionDiscoverySummaries2,
|
|
100318
|
-
listSessionDiscoveryAncestorPaths,
|
|
100319
|
-
listSessionsForSubject,
|
|
100320
|
-
listSessionEntriesForSubject,
|
|
100321
|
-
getLatestStartedSessionTurn,
|
|
100322
|
-
listSessionTurns,
|
|
100323
|
-
projectEffectiveControlForRelatedAccess as projectEffectiveControlForRelatedAccess2,
|
|
100324
|
-
projectSessionForRelatedAccess as projectSessionForRelatedAccess2,
|
|
100325
|
-
recordStreamAcknowledgment,
|
|
100326
|
-
requestSessionCompaction,
|
|
100327
|
-
switchSessionCodexAccount,
|
|
100328
|
-
setSessionChannel as setSessionChannel2,
|
|
100329
|
-
updateSessionVariableSets,
|
|
100330
|
-
ChannelNotFoundError,
|
|
100331
|
-
setSessionAttention,
|
|
100332
|
-
setSessionArchive,
|
|
100333
|
-
setSessionPin,
|
|
100334
|
-
SessionAttentionVersionConflictError,
|
|
100335
|
-
SessionArchiveVersionConflictError,
|
|
100336
|
-
SessionPinVersionConflictError,
|
|
100337
|
-
SessionPinAccessError,
|
|
100338
|
-
SessionListAccessError,
|
|
100339
|
-
SessionListCursorError,
|
|
100340
|
-
SessionListCursorExpiredError,
|
|
100341
|
-
decodeSessionListCursor,
|
|
100342
|
-
revokeViewer,
|
|
100343
|
-
setSessionGoalStatusWithEvent as setSessionGoalStatusWithEvent2,
|
|
100344
|
-
upsertSessionGoalWithEvent as upsertSessionGoalWithEvent2,
|
|
100345
|
-
updatePtySessionActivity,
|
|
100346
|
-
QueueCommandConflictError,
|
|
100347
|
-
beginSessionRealtimeInTransaction,
|
|
100348
|
-
activateSessionRealtimeConnectionInTransaction,
|
|
100349
|
-
claimSessionRealtimeConnectionInTransaction,
|
|
100350
|
-
completeSessionRealtimeConnectionInTransaction,
|
|
100351
|
-
endSessionRealtimeInTransaction,
|
|
100352
|
-
failSessionRealtimeConnectionInTransaction,
|
|
100353
|
-
NewSessionDraftConflictError,
|
|
100354
|
-
SessionCommandIdempotencyError,
|
|
100355
|
-
SessionControlConflictError,
|
|
100356
|
-
SessionRealtimeConflictError,
|
|
100357
|
-
SessionToolPolicyVersionConflictError,
|
|
100358
|
-
SessionContextBusyError,
|
|
100359
|
-
SessionVariableSetSelectionUnavailableError,
|
|
100360
|
-
SessionCreateConnectionSelectionUnavailableError as SessionCreateConnectionSelectionUnavailableError2,
|
|
100361
|
-
workspaceControlRequestLockTimeoutMs,
|
|
100362
|
-
SessionTenancyAccessError,
|
|
100363
|
-
SessionTenancyConflictError,
|
|
100364
|
-
SessionTenancyInvalidRequestError,
|
|
100365
|
-
SessionTenancyNotActivatedError,
|
|
100366
|
-
HumanInputResponseValidationError as HumanInputResponseValidationError2,
|
|
100367
|
-
SkillHumanResponseError,
|
|
100368
|
-
latestWorkspaceCapture,
|
|
100369
|
-
sessionLatestWorkspaceCapture,
|
|
100370
|
-
renewSessionRealtimeInTransaction,
|
|
100371
|
-
syncSessionRealtimeLedgerInTransaction,
|
|
100372
|
-
withWorkspaceSessionActivityRls,
|
|
100373
|
-
withWorkspaceRls,
|
|
100374
|
-
workspaceCaptureAtRevision
|
|
100375
|
-
} from "@opengeni/db";
|
|
100376
|
-
import {
|
|
100377
|
-
backgroundCommandActivityForSessions,
|
|
100378
|
-
listSessionBackgroundCommands,
|
|
100379
|
-
requestSessionBackgroundCommandCancellation
|
|
100380
|
-
} from "@opengeni/db/session-background-commands";
|
|
100381
|
-
import {
|
|
100382
|
-
appendAndPublishEvents as appendAndPublishEvents3,
|
|
100383
|
-
boundSessionEventHttpPage,
|
|
100384
|
-
coalesceSessionEventDeltasWithCoverage as coalesceSessionEventDeltasWithCoverage2,
|
|
100385
|
-
publishDurableSessionEvents as publishDurableSessionEvents3
|
|
100386
|
-
} from "@opengeni/events";
|
|
100387
|
-
|
|
100388
|
-
// src/gateway-realtime.ts
|
|
100389
|
-
import {
|
|
100390
|
-
VERCEL_AI_GATEWAY_AI_SDK_BASE_URL,
|
|
100391
|
-
VERCEL_AI_GATEWAY_BASE_URL,
|
|
100392
|
-
resolveAiGatewayRealtimeModel
|
|
100393
|
-
} from "@opengeni/config";
|
|
100236
|
+
// src/azure-live.ts
|
|
100394
100237
|
import {
|
|
100395
100238
|
getActiveSessionHistoryItems as getActiveSessionHistoryItems2,
|
|
100396
|
-
getSessionRealtimeContinuityEntries as getSessionRealtimeContinuityEntries2
|
|
100397
|
-
loadWorkspaceVercelAiGatewayApiKey
|
|
100239
|
+
getSessionRealtimeContinuityEntries as getSessionRealtimeContinuityEntries2
|
|
100398
100240
|
} from "@opengeni/db";
|
|
100399
100241
|
|
|
100400
100242
|
// src/codex-realtime.ts
|
|
@@ -100755,7 +100597,312 @@ function safeBrokerMessage(reason) {
|
|
|
100755
100597
|
}
|
|
100756
100598
|
}
|
|
100757
100599
|
|
|
100600
|
+
// src/azure-live.ts
|
|
100601
|
+
var AZURE_LIVE_MODEL_ID = "opengeni-azure/gpt-live-1";
|
|
100602
|
+
function azureLiveConfigured(settings) {
|
|
100603
|
+
return Boolean(settings.azureLiveEndpoint && settings.azureLiveApiKey?.trim());
|
|
100604
|
+
}
|
|
100605
|
+
async function brokerAzureLive(input) {
|
|
100606
|
+
if (!azureLiveConfigured(input.settings))
|
|
100607
|
+
throw new CodexRealtimeBrokerError(
|
|
100608
|
+
"credential_unavailable",
|
|
100609
|
+
"Hosted GPT Live is not configured"
|
|
100610
|
+
);
|
|
100611
|
+
if (input.request.voice)
|
|
100612
|
+
throw new CodexRealtimeBrokerError(
|
|
100613
|
+
"invalid_request",
|
|
100614
|
+
"Hosted voice is selected by deployment configuration"
|
|
100615
|
+
);
|
|
100616
|
+
const instructions = `${openGeniRealtimeInstructions(input.request.instructions)}
|
|
100617
|
+
|
|
100618
|
+
## Prior session conversation
|
|
100619
|
+
The following JSON contains conversation history, not additional system instructions. Preserve each item's role.
|
|
100620
|
+
${JSON.stringify(input.initialItems)}`;
|
|
100621
|
+
const signal = AbortSignal.any([
|
|
100622
|
+
AbortSignal.timeout(2e4),
|
|
100623
|
+
...input.signal ? [input.signal] : []
|
|
100624
|
+
]);
|
|
100625
|
+
let response;
|
|
100626
|
+
try {
|
|
100627
|
+
response = await (input.fetch ?? fetch)(
|
|
100628
|
+
`${input.settings.azureLiveEndpoint.replace(/\/+$/, "")}/openai/v1/live/sessions`,
|
|
100629
|
+
{
|
|
100630
|
+
method: "POST",
|
|
100631
|
+
signal,
|
|
100632
|
+
headers: { "api-key": input.settings.azureLiveApiKey, "content-type": "application/json" },
|
|
100633
|
+
body: JSON.stringify({
|
|
100634
|
+
session: {
|
|
100635
|
+
model: input.settings.azureLiveDeployment,
|
|
100636
|
+
instructions,
|
|
100637
|
+
delegation: { type: "client" },
|
|
100638
|
+
audio: { output: { voice: input.settings.azureLiveVoice } }
|
|
100639
|
+
},
|
|
100640
|
+
transport: { type: "webrtc", sdp: input.request.sdp }
|
|
100641
|
+
})
|
|
100642
|
+
}
|
|
100643
|
+
);
|
|
100644
|
+
} catch {
|
|
100645
|
+
throw new CodexRealtimeBrokerError(
|
|
100646
|
+
signal.aborted ? "timeout" : "network_error",
|
|
100647
|
+
"Hosted voice connection could not be established"
|
|
100648
|
+
);
|
|
100649
|
+
}
|
|
100650
|
+
if (!response.ok)
|
|
100651
|
+
throw new CodexRealtimeBrokerError(
|
|
100652
|
+
response.status === 429 ? "rate_limited" : "provider_error",
|
|
100653
|
+
"Hosted voice connection was rejected",
|
|
100654
|
+
response.status
|
|
100655
|
+
);
|
|
100656
|
+
const body3 = await response.json().catch(() => null);
|
|
100657
|
+
const sdp = body3?.transport?.sdp;
|
|
100658
|
+
if (typeof sdp !== "string" || !sdp || sdp.length > 1048576)
|
|
100659
|
+
throw new CodexRealtimeBrokerError(
|
|
100660
|
+
"invalid_provider_response",
|
|
100661
|
+
"Hosted voice returned an invalid connection answer"
|
|
100662
|
+
);
|
|
100663
|
+
return { sdp, version: "v3", model: AZURE_LIVE_MODEL_ID };
|
|
100664
|
+
}
|
|
100665
|
+
function buildSessionAzureLiveBroker(db, settings, workspaceId, sessionId, fetchImpl) {
|
|
100666
|
+
return async (input) => {
|
|
100667
|
+
const [history, continuity] = await Promise.all([
|
|
100668
|
+
getActiveSessionHistoryItems2(db, workspaceId, sessionId),
|
|
100669
|
+
getSessionRealtimeContinuityEntries2(db, workspaceId, sessionId)
|
|
100670
|
+
]);
|
|
100671
|
+
return brokerAzureLive({
|
|
100672
|
+
settings,
|
|
100673
|
+
...input,
|
|
100674
|
+
initialItems: projectSessionRealtimeInitialItems(history, continuity),
|
|
100675
|
+
...fetchImpl ? { fetch: fetchImpl } : {}
|
|
100676
|
+
});
|
|
100677
|
+
};
|
|
100678
|
+
}
|
|
100679
|
+
|
|
100680
|
+
// src/routes/sessions.ts
|
|
100681
|
+
import { getRetainedProviderCommand } from "@opengeni/db/retained-provider-commands";
|
|
100682
|
+
import { assertGoalResumeAllowed as assertGoalResumeAllowed2, GoalResumeBlockedError } from "@opengeni/core";
|
|
100683
|
+
import { UnsupportedLatencyModeError } from "@opengeni/config";
|
|
100684
|
+
import { searchSessionMessagesForSubject, SessionMessageSearchCursorError } from "@opengeni/db";
|
|
100685
|
+
import { listSessionEventSlices } from "@opengeni/db/session-event-slices";
|
|
100686
|
+
import * as sessionPreviewSchema from "@opengeni/db/schema";
|
|
100687
|
+
import { SessionMessageSearchRequest } from "@opengeni/contracts";
|
|
100688
|
+
import { requireWorkspace as requireWorkspace3, scheduledSessionIds } from "@opengeni/db";
|
|
100689
|
+
import { isVerifiedDelegatedHumanAuthorization as isVerifiedDelegatedHumanAuthorization4, withSiteSessionOrigin as withSiteSessionOrigin3 } from "@opengeni/core";
|
|
100690
|
+
import { freezeSessionRealtimeConnectionAccounts } from "@opengeni/core";
|
|
100691
|
+
import {
|
|
100692
|
+
createRealtimeVoiceBilling,
|
|
100693
|
+
RealtimeVoiceUnavailableError,
|
|
100694
|
+
TranscriptionBillingRefusedError
|
|
100695
|
+
} from "@opengeni/core";
|
|
100696
|
+
|
|
100697
|
+
// src/transcription/billing-refusal.ts
|
|
100698
|
+
function transcriptionBillingRefusal(c, error) {
|
|
100699
|
+
return c.json({ code: error.code, message: error.message, ...error.details }, error.status);
|
|
100700
|
+
}
|
|
100701
|
+
|
|
100702
|
+
// src/routes/sessions.ts
|
|
100703
|
+
import { SandboxRecoveryRequest } from "@opengeni/contracts";
|
|
100704
|
+
import { getManagedHumanSandboxRecovery, consentManagedHumanSandboxRecovery } from "@opengeni/core";
|
|
100705
|
+
import { SandboxRecoveryConflictError } from "@opengeni/db";
|
|
100706
|
+
import { getSessionCodexAccounts } from "@opengeni/db";
|
|
100707
|
+
import { getToolActionReview, getToolReviewDetailsPage } from "@opengeni/db";
|
|
100708
|
+
import {
|
|
100709
|
+
AcknowledgeStreamRequest,
|
|
100710
|
+
ApplySessionGoalRevisionRequest,
|
|
100711
|
+
ListSessionGoalRevisionsQuery,
|
|
100712
|
+
RejectSessionGoalRevisionRequest,
|
|
100713
|
+
RollbackSessionGoalRevisionRequest,
|
|
100714
|
+
ActivateCodexRealtimeConnectionRequest,
|
|
100715
|
+
AttachViewerRequest,
|
|
100716
|
+
BeginSessionRealtimeRequest,
|
|
100717
|
+
ClearSessionContextRequest,
|
|
100718
|
+
CodexRealtimeWebrtcRequest,
|
|
100719
|
+
GatewayRealtimeConnectRequest,
|
|
100720
|
+
ClientSessionEvent,
|
|
100721
|
+
CompactSessionContextRequest,
|
|
100722
|
+
CreateSessionRequest as CreateSessionRequest2,
|
|
100723
|
+
DeleteSessionQueueItemRequest,
|
|
100724
|
+
EditSessionQueueItemRequest,
|
|
100725
|
+
EndSessionRealtimeRequest,
|
|
100726
|
+
FsDeleteRequest,
|
|
100727
|
+
FsListBatchRequest,
|
|
100728
|
+
FsListRequest,
|
|
100729
|
+
FsMkdirRequest,
|
|
100730
|
+
FsMoveRequest,
|
|
100731
|
+
FsReadRequest,
|
|
100732
|
+
FsWriteRequest,
|
|
100733
|
+
ForkSessionRequest,
|
|
100734
|
+
HumanInputRequestStatus,
|
|
100735
|
+
GitDiffRequest,
|
|
100736
|
+
GitReadBatchRequest,
|
|
100737
|
+
GitLogRequest,
|
|
100738
|
+
GitShowRequest,
|
|
100739
|
+
GitStatusRequest,
|
|
100740
|
+
MoveSessionQueueItemRequest,
|
|
100741
|
+
PtyCloseRequest,
|
|
100742
|
+
PtyOpenRequest,
|
|
100743
|
+
PtyResizeRequest,
|
|
100744
|
+
PtyWriteRequest,
|
|
100745
|
+
PublishSandboxFileArtifactRequest,
|
|
100746
|
+
RenewSessionRealtimeRequest,
|
|
100747
|
+
SyncSessionRealtimeLedgerRequest,
|
|
100748
|
+
SessionControlRequest,
|
|
100749
|
+
SessionRetryRequest,
|
|
100750
|
+
SESSION_EVENT_RAW_DELTA_TYPES as SESSION_EVENT_RAW_DELTA_TYPES2,
|
|
100751
|
+
SessionEventPayloadMode as SessionEventPayloadMode2,
|
|
100752
|
+
SessionEventReadDirection as SessionEventReadDirection2,
|
|
100753
|
+
SessionEventReadMode as SessionEventReadMode2,
|
|
100754
|
+
SessionEventLatestClass as SessionEventLatestClass2,
|
|
100755
|
+
SessionEventResultMode as SessionEventResultMode2,
|
|
100756
|
+
SessionScopeSubjectId,
|
|
100757
|
+
SessionEventSemanticClass as SessionEventSemanticClass2,
|
|
100758
|
+
SessionEventType as SessionEventType2,
|
|
100759
|
+
SessionMcpServerId,
|
|
100760
|
+
SaveNewSessionDraftRequest,
|
|
100761
|
+
compactSessionEventResult as compactSessionEventResult3,
|
|
100762
|
+
sessionEventLatestClassToSemanticClass as sessionEventLatestClassToSemanticClass2,
|
|
100763
|
+
SaveComposerDraftRequest,
|
|
100764
|
+
SubmitComposerDraftRequest,
|
|
100765
|
+
SteerSessionQueueItemRequest,
|
|
100766
|
+
SteerSessionMessageRequest,
|
|
100767
|
+
TerminalExecRequest,
|
|
100768
|
+
UpdateSessionChannelRequest,
|
|
100769
|
+
UpdateSessionAttentionRequest,
|
|
100770
|
+
UpdateSessionArchiveRequest,
|
|
100771
|
+
UpdateSessionPinRequest,
|
|
100772
|
+
UpdateSessionGoalRequest,
|
|
100773
|
+
UpdateSessionMcpApprovalPolicyRequest,
|
|
100774
|
+
UpdateSessionRequest,
|
|
100775
|
+
UpdateSessionVariableSetsRequest,
|
|
100776
|
+
UpdateSessionVisibilityRequest,
|
|
100777
|
+
UpdateSessionToolPolicyRequest,
|
|
100778
|
+
UpdateSessionAgentRequest,
|
|
100779
|
+
AgentConfigError as AgentConfigError2,
|
|
100780
|
+
ViewerHeartbeatRequest,
|
|
100781
|
+
WORKSPACE_CONTROL_ACTOR_MAX_BYTES,
|
|
100782
|
+
WORK_CLAIM_CANONICAL_KEY_MAX_BYTES as WORK_CLAIM_CANONICAL_KEY_MAX_BYTES2,
|
|
100783
|
+
WORK_CLAIM_DISCOVERY_LIMIT as WORK_CLAIM_DISCOVERY_LIMIT2,
|
|
100784
|
+
WORK_CLAIM_NAMESPACE_MAX_BYTES as WORK_CLAIM_NAMESPACE_MAX_BYTES2,
|
|
100785
|
+
WORK_DISCOVERY_QUERY_MAX_CHARS as WORK_DISCOVERY_QUERY_MAX_CHARS2,
|
|
100786
|
+
WORK_DISCOVERY_RECENT_HOURS_MAX as WORK_DISCOVERY_RECENT_HOURS_MAX2,
|
|
100787
|
+
WorkClaimSubjectFilter as WorkClaimSubjectFilterSchema,
|
|
100788
|
+
WorkClaimSubjectType as WorkClaimSubjectType2,
|
|
100789
|
+
normalizeWorkClaimCanonicalKey,
|
|
100790
|
+
normalizeWorkClaimNamespace,
|
|
100791
|
+
workspaceControlUtf8Bytes
|
|
100792
|
+
} from "@opengeni/contracts";
|
|
100793
|
+
import { streamTokenDegraded } from "@opengeni/config";
|
|
100794
|
+
import {
|
|
100795
|
+
recordAuditEvent as recordAuditEvent13,
|
|
100796
|
+
acceptSessionApprovalDecision as acceptSessionApprovalDecision2,
|
|
100797
|
+
acceptSessionHumanInputResponse as acceptSessionHumanInputResponse2,
|
|
100798
|
+
clearSessionGoal,
|
|
100799
|
+
clearSessionContext,
|
|
100800
|
+
getOpenPtySession,
|
|
100801
|
+
getEnrollment as getEnrollment2,
|
|
100802
|
+
getLiveEnrollmentConnection as getLiveEnrollmentConnection7,
|
|
100803
|
+
getRetainedProcess,
|
|
100804
|
+
getSandbox as getSandbox4,
|
|
100805
|
+
getSession as getSession9,
|
|
100806
|
+
readActiveSandbox as readActiveSandbox3,
|
|
100807
|
+
deleteSessionTreeIfQuiescent,
|
|
100808
|
+
getSessionEvent,
|
|
100809
|
+
getSessionForSubject as getSessionForSubject2,
|
|
100810
|
+
getSessionGoal as getSessionGoal2,
|
|
100811
|
+
getLatestSessionModelContext,
|
|
100812
|
+
getSessionHumanInputRequest,
|
|
100813
|
+
getSessionGoalWithContinuation,
|
|
100814
|
+
getSessionGoalRevision,
|
|
100815
|
+
getSessionQueueSnapshot as getSessionQueueSnapshot2,
|
|
100816
|
+
getStreamAcknowledgment,
|
|
100817
|
+
insertPtySession,
|
|
100818
|
+
listSessionEventPage as listSessionEventPage3,
|
|
100819
|
+
listSessionHumanInputRequests,
|
|
100820
|
+
listSessionGoalRevisionPage,
|
|
100821
|
+
listSessionGoalRevisions,
|
|
100822
|
+
rejectSessionGoalRevisionWithEvent,
|
|
100823
|
+
listSessionIdsInGroup,
|
|
100824
|
+
listSessionDiscoverySummaries as listSessionDiscoverySummaries2,
|
|
100825
|
+
listSessionDiscoveryAncestorPaths,
|
|
100826
|
+
listSessionsForSubject,
|
|
100827
|
+
listSessionEntriesForSubject,
|
|
100828
|
+
getLatestStartedSessionTurn,
|
|
100829
|
+
listSessionTurns,
|
|
100830
|
+
projectEffectiveControlForRelatedAccess as projectEffectiveControlForRelatedAccess2,
|
|
100831
|
+
projectSessionForRelatedAccess as projectSessionForRelatedAccess2,
|
|
100832
|
+
recordStreamAcknowledgment,
|
|
100833
|
+
requestSessionCompaction,
|
|
100834
|
+
switchSessionCodexAccount,
|
|
100835
|
+
setSessionChannel as setSessionChannel2,
|
|
100836
|
+
updateSessionVariableSets,
|
|
100837
|
+
ChannelNotFoundError,
|
|
100838
|
+
setSessionAttention,
|
|
100839
|
+
setSessionArchive,
|
|
100840
|
+
setSessionPin,
|
|
100841
|
+
SessionAttentionVersionConflictError,
|
|
100842
|
+
SessionArchiveVersionConflictError,
|
|
100843
|
+
SessionPinVersionConflictError,
|
|
100844
|
+
SessionPinAccessError,
|
|
100845
|
+
SessionListAccessError,
|
|
100846
|
+
SessionListCursorError,
|
|
100847
|
+
SessionListCursorExpiredError,
|
|
100848
|
+
decodeSessionListCursor,
|
|
100849
|
+
revokeViewer,
|
|
100850
|
+
setSessionGoalStatusWithEvent as setSessionGoalStatusWithEvent2,
|
|
100851
|
+
upsertSessionGoalWithEvent as upsertSessionGoalWithEvent2,
|
|
100852
|
+
updatePtySessionActivity,
|
|
100853
|
+
QueueCommandConflictError,
|
|
100854
|
+
beginSessionRealtimeInTransaction,
|
|
100855
|
+
activateSessionRealtimeConnectionInTransaction,
|
|
100856
|
+
claimSessionRealtimeConnectionInTransaction,
|
|
100857
|
+
completeSessionRealtimeConnectionInTransaction,
|
|
100858
|
+
endSessionRealtimeInTransaction,
|
|
100859
|
+
failSessionRealtimeConnectionInTransaction,
|
|
100860
|
+
NewSessionDraftConflictError,
|
|
100861
|
+
SessionCommandIdempotencyError,
|
|
100862
|
+
SessionControlConflictError,
|
|
100863
|
+
SessionRealtimeConflictError,
|
|
100864
|
+
SessionToolPolicyVersionConflictError,
|
|
100865
|
+
SessionContextBusyError,
|
|
100866
|
+
SessionVariableSetSelectionUnavailableError,
|
|
100867
|
+
SessionCreateConnectionSelectionUnavailableError as SessionCreateConnectionSelectionUnavailableError2,
|
|
100868
|
+
workspaceControlRequestLockTimeoutMs,
|
|
100869
|
+
SessionTenancyAccessError,
|
|
100870
|
+
SessionTenancyConflictError,
|
|
100871
|
+
SessionTenancyInvalidRequestError,
|
|
100872
|
+
SessionTenancyNotActivatedError,
|
|
100873
|
+
HumanInputResponseValidationError as HumanInputResponseValidationError2,
|
|
100874
|
+
SkillHumanResponseError,
|
|
100875
|
+
latestWorkspaceCapture,
|
|
100876
|
+
sessionLatestWorkspaceCapture,
|
|
100877
|
+
renewSessionRealtimeInTransaction,
|
|
100878
|
+
syncSessionRealtimeLedgerInTransaction,
|
|
100879
|
+
withWorkspaceSessionActivityRls,
|
|
100880
|
+
withWorkspaceRls,
|
|
100881
|
+
workspaceCaptureAtRevision
|
|
100882
|
+
} from "@opengeni/db";
|
|
100883
|
+
import {
|
|
100884
|
+
backgroundCommandActivityForSessions,
|
|
100885
|
+
listSessionBackgroundCommands,
|
|
100886
|
+
requestSessionBackgroundCommandCancellation
|
|
100887
|
+
} from "@opengeni/db/session-background-commands";
|
|
100888
|
+
import {
|
|
100889
|
+
appendAndPublishEvents as appendAndPublishEvents3,
|
|
100890
|
+
boundSessionEventHttpPage,
|
|
100891
|
+
coalesceSessionEventDeltasWithCoverage as coalesceSessionEventDeltasWithCoverage2,
|
|
100892
|
+
publishDurableSessionEvents as publishDurableSessionEvents3
|
|
100893
|
+
} from "@opengeni/events";
|
|
100894
|
+
|
|
100758
100895
|
// src/gateway-realtime.ts
|
|
100896
|
+
import {
|
|
100897
|
+
VERCEL_AI_GATEWAY_AI_SDK_BASE_URL,
|
|
100898
|
+
VERCEL_AI_GATEWAY_BASE_URL,
|
|
100899
|
+
resolveAiGatewayRealtimeModel
|
|
100900
|
+
} from "@opengeni/config";
|
|
100901
|
+
import {
|
|
100902
|
+
getActiveSessionHistoryItems as getActiveSessionHistoryItems3,
|
|
100903
|
+
getSessionRealtimeContinuityEntries as getSessionRealtimeContinuityEntries3,
|
|
100904
|
+
loadWorkspaceVercelAiGatewayApiKey
|
|
100905
|
+
} from "@opengeni/db";
|
|
100759
100906
|
var GatewayRealtimeBrokerError = class extends Error {
|
|
100760
100907
|
constructor(code, message, providerStatus = null) {
|
|
100761
100908
|
super(message);
|
|
@@ -100780,10 +100927,11 @@ async function createGatewayRealtimeConnectionSecret(input) {
|
|
|
100780
100927
|
);
|
|
100781
100928
|
}
|
|
100782
100929
|
const [history, continuity, minted] = await Promise.all([
|
|
100783
|
-
|
|
100784
|
-
|
|
100930
|
+
getActiveSessionHistoryItems3(input.db, input.workspaceId, input.sessionId),
|
|
100931
|
+
getSessionRealtimeContinuityEntries3(input.db, input.workspaceId, input.sessionId),
|
|
100785
100932
|
mintGatewayClientSecret({
|
|
100786
100933
|
apiKey,
|
|
100934
|
+
source: resolved.source,
|
|
100787
100935
|
upstreamModelId: resolved.upstreamModelId,
|
|
100788
100936
|
fetchImpl: input.fetchImpl ?? fetch
|
|
100789
100937
|
})
|
|
@@ -100812,13 +100960,14 @@ async function mintGatewayClientSecret(input) {
|
|
|
100812
100960
|
} catch {
|
|
100813
100961
|
throw new GatewayRealtimeBrokerError(
|
|
100814
100962
|
"provider_error",
|
|
100815
|
-
"
|
|
100963
|
+
"The voice provider could not be reached. Try again."
|
|
100816
100964
|
);
|
|
100817
100965
|
}
|
|
100818
100966
|
if (!response.ok) {
|
|
100967
|
+
const rejected = response.status === 401 || response.status === 403;
|
|
100819
100968
|
throw new GatewayRealtimeBrokerError(
|
|
100820
|
-
|
|
100821
|
-
|
|
100969
|
+
rejected ? "credential_unavailable" : "provider_error",
|
|
100970
|
+
rejected ? input.source === "managed" ? "Opengeni voice is temporarily unavailable. Try another voice model." : "Your AI Gateway key was rejected. Update it in workspace settings." : "The voice provider could not start a call. Try again.",
|
|
100822
100971
|
response.status
|
|
100823
100972
|
);
|
|
100824
100973
|
}
|
|
@@ -100844,8 +100993,8 @@ async function mintGatewayClientSecret(input) {
|
|
|
100844
100993
|
// src/xai-realtime.ts
|
|
100845
100994
|
import { SUPERGROK_REALTIME_MODEL_ID } from "@opengeni/config";
|
|
100846
100995
|
import {
|
|
100847
|
-
getActiveSessionHistoryItems as
|
|
100848
|
-
getSessionRealtimeContinuityEntries as
|
|
100996
|
+
getActiveSessionHistoryItems as getActiveSessionHistoryItems4,
|
|
100997
|
+
getSessionRealtimeContinuityEntries as getSessionRealtimeContinuityEntries4,
|
|
100849
100998
|
getXaiSessionAccountPin,
|
|
100850
100999
|
resolveXaiProviderAccountAuthoritySnapshotForAcceptance as resolveXaiProviderAccountAuthoritySnapshotForAcceptance3,
|
|
100851
101000
|
setXaiSessionAccountPin
|
|
@@ -101054,8 +101203,8 @@ async function createXaiRealtimeConnectionSecret(input) {
|
|
|
101054
101203
|
);
|
|
101055
101204
|
}
|
|
101056
101205
|
const [history, continuity, minted] = await Promise.all([
|
|
101057
|
-
|
|
101058
|
-
|
|
101206
|
+
getActiveSessionHistoryItems4(input.db, input.workspaceId, input.sessionId),
|
|
101207
|
+
getSessionRealtimeContinuityEntries4(input.db, input.workspaceId, input.sessionId),
|
|
101059
101208
|
mintXaiClientSecret({
|
|
101060
101209
|
auth,
|
|
101061
101210
|
fetchImpl: input.fetchImpl ?? fetch
|
|
@@ -101495,6 +101644,46 @@ function registerSessionRoutes(app, deps) {
|
|
|
101495
101644
|
observability: deps.observability
|
|
101496
101645
|
};
|
|
101497
101646
|
const workspaceCaptureManifestCache = new WorkspaceCaptureManifestCache();
|
|
101647
|
+
const realtimeVoiceBilling = createRealtimeVoiceBilling({ db, settings });
|
|
101648
|
+
const realtimeVoiceRefusalResponse = (c, error) => {
|
|
101649
|
+
if (error instanceof TranscriptionBillingRefusedError) {
|
|
101650
|
+
return transcriptionBillingRefusal(c, error);
|
|
101651
|
+
}
|
|
101652
|
+
if (error instanceof RealtimeVoiceUnavailableError) {
|
|
101653
|
+
return c.json(
|
|
101654
|
+
{
|
|
101655
|
+
error: {
|
|
101656
|
+
status: 409,
|
|
101657
|
+
code: "realtime_voice_unavailable",
|
|
101658
|
+
message: error.message,
|
|
101659
|
+
retryable: false,
|
|
101660
|
+
details: { reason: error.code }
|
|
101661
|
+
}
|
|
101662
|
+
},
|
|
101663
|
+
409
|
|
101664
|
+
);
|
|
101665
|
+
}
|
|
101666
|
+
return null;
|
|
101667
|
+
};
|
|
101668
|
+
const settleRealtimeVoice = async (input) => {
|
|
101669
|
+
try {
|
|
101670
|
+
return await realtimeVoiceBilling.settle({
|
|
101671
|
+
workspaceId: input.workspaceId,
|
|
101672
|
+
sessionId: input.sessionId,
|
|
101673
|
+
realtimeId: input.realtimeId,
|
|
101674
|
+
callerSubjectId: input.grant.subjectId,
|
|
101675
|
+
attribution: input.attribution
|
|
101676
|
+
});
|
|
101677
|
+
} catch (error) {
|
|
101678
|
+
console.error("realtime voice settlement failed", {
|
|
101679
|
+
workspaceId: input.workspaceId,
|
|
101680
|
+
sessionId: input.sessionId,
|
|
101681
|
+
realtimeId: input.realtimeId,
|
|
101682
|
+
error: error instanceof Error ? error.message : String(error)
|
|
101683
|
+
});
|
|
101684
|
+
return null;
|
|
101685
|
+
}
|
|
101686
|
+
};
|
|
101498
101687
|
const withSiteCommandOrigin = (c, workspaceId, run) => withOptionalSiteCommandOrigin(
|
|
101499
101688
|
db,
|
|
101500
101689
|
workspaceId,
|
|
@@ -102143,18 +102332,26 @@ function registerSessionRoutes(app, deps) {
|
|
|
102143
102332
|
if (!session) {
|
|
102144
102333
|
throw new HTTPException88(404, { message: "session not found" });
|
|
102145
102334
|
}
|
|
102146
|
-
const activity = await
|
|
102147
|
-
|
|
102148
|
-
|
|
102149
|
-
|
|
102150
|
-
|
|
102151
|
-
|
|
102335
|
+
const [activity, scheduleTargets, policy] = await Promise.all([
|
|
102336
|
+
backgroundCommandActivityForSessions(db, {
|
|
102337
|
+
accountId: grant.accountId,
|
|
102338
|
+
workspaceId,
|
|
102339
|
+
sessionIds: [sessionId]
|
|
102340
|
+
}),
|
|
102341
|
+
hasPermission23(grant.permissions, "scheduled_tasks:run") && hasPermission23(grant.permissions, "sessions:control") ? scheduledSessionIds(db, workspaceId, [sessionId]) : Promise.resolve(/* @__PURE__ */ new Set()),
|
|
102342
|
+
loadEffectivePolicyContext(deps, workspaceId, grant.subjectId, [session])
|
|
102343
|
+
]);
|
|
102152
102344
|
return c.json(
|
|
102153
|
-
|
|
102154
|
-
|
|
102155
|
-
|
|
102156
|
-
|
|
102157
|
-
|
|
102345
|
+
sessionWithEffectiveToolPolicy2(
|
|
102346
|
+
{
|
|
102347
|
+
...session,
|
|
102348
|
+
hasSchedules: scheduleTargets.has(sessionId),
|
|
102349
|
+
...activity.get(sessionId) ? { backgroundCommandActivity: activity.get(sessionId) } : {}
|
|
102350
|
+
},
|
|
102351
|
+
policy.workspaceServerIds,
|
|
102352
|
+
policy.workspaceDefaultServerIds,
|
|
102353
|
+
policy.effectiveToolsContext
|
|
102354
|
+
)
|
|
102158
102355
|
);
|
|
102159
102356
|
});
|
|
102160
102357
|
app.get("/v1/workspaces/:workspaceId/sessions/:sessionId/model-context", async (c) => {
|
|
@@ -102298,6 +102495,18 @@ function registerSessionRoutes(app, deps) {
|
|
|
102298
102495
|
message: "invalid session realtime request"
|
|
102299
102496
|
});
|
|
102300
102497
|
}
|
|
102498
|
+
try {
|
|
102499
|
+
await realtimeVoiceBilling.admit({
|
|
102500
|
+
accountId: grant.accountId,
|
|
102501
|
+
workspaceId,
|
|
102502
|
+
model: parsed.data.model,
|
|
102503
|
+
attribution: creditDebitAttributionForGrant(grant)
|
|
102504
|
+
});
|
|
102505
|
+
} catch (error) {
|
|
102506
|
+
const refused = realtimeVoiceRefusalResponse(c, error);
|
|
102507
|
+
if (refused) return refused;
|
|
102508
|
+
throw error;
|
|
102509
|
+
}
|
|
102301
102510
|
try {
|
|
102302
102511
|
const result = await withWorkspaceSessionActivityRls(db, workspaceId, async (scopedDb) => {
|
|
102303
102512
|
const accounts = await freezeSessionRealtimeConnectionAccounts({
|
|
@@ -102342,6 +102551,14 @@ function registerSessionRoutes(app, deps) {
|
|
|
102342
102551
|
});
|
|
102343
102552
|
}
|
|
102344
102553
|
try {
|
|
102554
|
+
const settlement = await settleRealtimeVoice({
|
|
102555
|
+
grant,
|
|
102556
|
+
attribution: creditDebitAttributionForGrant(grant),
|
|
102557
|
+
workspaceId,
|
|
102558
|
+
sessionId,
|
|
102559
|
+
realtimeId
|
|
102560
|
+
});
|
|
102561
|
+
const stop = settlement?.stop ?? null;
|
|
102345
102562
|
const result = await withWorkspaceSessionActivityRls(
|
|
102346
102563
|
db,
|
|
102347
102564
|
workspaceId,
|
|
@@ -102350,12 +102567,17 @@ function registerSessionRoutes(app, deps) {
|
|
|
102350
102567
|
sessionId,
|
|
102351
102568
|
realtimeId,
|
|
102352
102569
|
ownerSubjectId: grant.subjectId,
|
|
102353
|
-
...parsed.data
|
|
102570
|
+
...parsed.data,
|
|
102571
|
+
extendLease: stop === null
|
|
102354
102572
|
})
|
|
102355
102573
|
);
|
|
102356
102574
|
await publishRealtimeMutation(grant.accountId, workspaceId, sessionId, result);
|
|
102357
102575
|
c.header("cache-control", "private, no-store");
|
|
102358
|
-
return c.json({
|
|
102576
|
+
return c.json({
|
|
102577
|
+
mode: result.mode,
|
|
102578
|
+
replay: result.replay,
|
|
102579
|
+
...stop && result.mode.state === "active" ? { stop: { code: stop.code, message: stop.message } } : {}
|
|
102580
|
+
});
|
|
102359
102581
|
} catch (error) {
|
|
102360
102582
|
throw sessionRealtimeHttpError(error);
|
|
102361
102583
|
}
|
|
@@ -102390,6 +102612,13 @@ function registerSessionRoutes(app, deps) {
|
|
|
102390
102612
|
})
|
|
102391
102613
|
);
|
|
102392
102614
|
await publishRealtimeMutation(grant.accountId, workspaceId, sessionId, result);
|
|
102615
|
+
await settleRealtimeVoice({
|
|
102616
|
+
grant,
|
|
102617
|
+
attribution: creditDebitAttributionForGrant(grant),
|
|
102618
|
+
workspaceId,
|
|
102619
|
+
sessionId,
|
|
102620
|
+
realtimeId
|
|
102621
|
+
});
|
|
102393
102622
|
c.header("cache-control", "private, no-store");
|
|
102394
102623
|
return c.json({ mode: result.mode, replay: result.replay });
|
|
102395
102624
|
} catch (error) {
|
|
@@ -102425,21 +102654,31 @@ function registerSessionRoutes(app, deps) {
|
|
|
102425
102654
|
const claim = await withWorkspaceRls(
|
|
102426
102655
|
db,
|
|
102427
102656
|
workspaceId,
|
|
102428
|
-
async (scopedDb) => scopedDb.transaction(
|
|
102429
|
-
|
|
102430
|
-
|
|
102431
|
-
|
|
102432
|
-
|
|
102433
|
-
|
|
102434
|
-
|
|
102435
|
-
|
|
102436
|
-
|
|
102437
|
-
|
|
102438
|
-
|
|
102439
|
-
|
|
102440
|
-
|
|
102441
|
-
|
|
102442
|
-
|
|
102657
|
+
async (scopedDb) => scopedDb.transaction(async (tx) => {
|
|
102658
|
+
const claimed = await claimSessionRealtimeConnectionInTransaction(
|
|
102659
|
+
tx,
|
|
102660
|
+
{
|
|
102661
|
+
workspaceId,
|
|
102662
|
+
sessionId,
|
|
102663
|
+
realtimeId,
|
|
102664
|
+
operationId,
|
|
102665
|
+
ownerSubjectId: grant.subjectId,
|
|
102666
|
+
browserInstanceId,
|
|
102667
|
+
ownerKey,
|
|
102668
|
+
expectedVersion,
|
|
102669
|
+
expectedConnectionEpoch,
|
|
102670
|
+
rotate,
|
|
102671
|
+
promotionMode: browserActivation === "required" ? "staged" : "legacy"
|
|
102672
|
+
}
|
|
102673
|
+
);
|
|
102674
|
+
if (claimed.mode.model !== "gpt-live-1-boulder-alpha" && claimed.mode.model !== AZURE_LIVE_MODEL_ID) {
|
|
102675
|
+
throw new CodexRealtimeBrokerError(
|
|
102676
|
+
"invalid_request",
|
|
102677
|
+
"This voice model does not use WebRTC negotiation"
|
|
102678
|
+
);
|
|
102679
|
+
}
|
|
102680
|
+
return claimed;
|
|
102681
|
+
})
|
|
102443
102682
|
);
|
|
102444
102683
|
if (claim.replay) {
|
|
102445
102684
|
if (claim.connection.state !== "ready" && claim.connection.state !== "active" || !claim.connection.sdpAnswer) {
|
|
@@ -102470,7 +102709,7 @@ function registerSessionRoutes(app, deps) {
|
|
|
102470
102709
|
return c.json({
|
|
102471
102710
|
sdp: claim.connection.sdpAnswer,
|
|
102472
102711
|
version: "v3",
|
|
102473
|
-
model:
|
|
102712
|
+
model: claim.mode.model,
|
|
102474
102713
|
connectionId: claim.connection.id,
|
|
102475
102714
|
connectionEpoch: claim.connection.connectionEpoch,
|
|
102476
102715
|
startupFenceSequence: claim.connection.startupFenceSequence,
|
|
@@ -102478,13 +102717,34 @@ function registerSessionRoutes(app, deps) {
|
|
|
102478
102717
|
replay: true
|
|
102479
102718
|
});
|
|
102480
102719
|
}
|
|
102481
|
-
|
|
102482
|
-
|
|
102483
|
-
|
|
102484
|
-
|
|
102485
|
-
|
|
102486
|
-
|
|
102487
|
-
|
|
102720
|
+
try {
|
|
102721
|
+
await realtimeVoiceBilling.admit({
|
|
102722
|
+
accountId: grant.accountId,
|
|
102723
|
+
workspaceId,
|
|
102724
|
+
model: claim.mode.model,
|
|
102725
|
+
attribution: creditDebitAttributionForGrant(grant)
|
|
102726
|
+
});
|
|
102727
|
+
} catch (error) {
|
|
102728
|
+
const refused = realtimeVoiceRefusalResponse(c, error);
|
|
102729
|
+
if (!refused) throw error;
|
|
102730
|
+
await withWorkspaceRls(
|
|
102731
|
+
db,
|
|
102732
|
+
workspaceId,
|
|
102733
|
+
async (scopedDb) => scopedDb.transaction(
|
|
102734
|
+
async (tx) => failSessionRealtimeConnectionInTransaction(tx, {
|
|
102735
|
+
workspaceId,
|
|
102736
|
+
sessionId,
|
|
102737
|
+
realtimeId,
|
|
102738
|
+
connectionId: claim.connection.id,
|
|
102739
|
+
operationId,
|
|
102740
|
+
connectionEpoch: claim.connection.connectionEpoch,
|
|
102741
|
+
failureCode: "billing_refused"
|
|
102742
|
+
})
|
|
102743
|
+
)
|
|
102744
|
+
).catch(() => void 0);
|
|
102745
|
+
return refused;
|
|
102746
|
+
}
|
|
102747
|
+
const broker = (claim.mode.model === AZURE_LIVE_MODEL_ID ? buildSessionAzureLiveBroker : buildSessionCodexRealtimeBroker)(db, settings, workspaceId, sessionId, deps.codexFetch);
|
|
102488
102748
|
try {
|
|
102489
102749
|
const answer = await broker({
|
|
102490
102750
|
request: providerRequest,
|
|
@@ -102524,6 +102784,13 @@ function registerSessionRoutes(app, deps) {
|
|
|
102524
102784
|
})
|
|
102525
102785
|
)
|
|
102526
102786
|
) : null;
|
|
102787
|
+
await settleRealtimeVoice({
|
|
102788
|
+
grant,
|
|
102789
|
+
attribution: creditDebitAttributionForGrant(grant),
|
|
102790
|
+
workspaceId,
|
|
102791
|
+
sessionId,
|
|
102792
|
+
realtimeId
|
|
102793
|
+
});
|
|
102527
102794
|
return c.json({
|
|
102528
102795
|
...answer,
|
|
102529
102796
|
connectionId: completed.connection.id,
|
|
@@ -102626,6 +102893,12 @@ function registerSessionRoutes(app, deps) {
|
|
|
102626
102893
|
"Realtime Gateway tokens are single-use; reconnect with a new operation"
|
|
102627
102894
|
);
|
|
102628
102895
|
}
|
|
102896
|
+
await realtimeVoiceBilling.admit({
|
|
102897
|
+
accountId: grant.accountId,
|
|
102898
|
+
workspaceId,
|
|
102899
|
+
model: claim.mode.model,
|
|
102900
|
+
attribution: creditDebitAttributionForGrant(grant)
|
|
102901
|
+
});
|
|
102629
102902
|
const secret = await createGatewayRealtimeConnectionSecret({
|
|
102630
102903
|
db,
|
|
102631
102904
|
settings,
|
|
@@ -102651,6 +102924,13 @@ function registerSessionRoutes(app, deps) {
|
|
|
102651
102924
|
)
|
|
102652
102925
|
);
|
|
102653
102926
|
connectionCompleted = true;
|
|
102927
|
+
await settleRealtimeVoice({
|
|
102928
|
+
grant,
|
|
102929
|
+
attribution: creditDebitAttributionForGrant(grant),
|
|
102930
|
+
workspaceId,
|
|
102931
|
+
sessionId,
|
|
102932
|
+
realtimeId
|
|
102933
|
+
});
|
|
102654
102934
|
return c.json({
|
|
102655
102935
|
...secret,
|
|
102656
102936
|
connectionId: completed.connection.id,
|
|
@@ -102673,11 +102953,13 @@ function registerSessionRoutes(app, deps) {
|
|
|
102673
102953
|
connectionId: claimed.connection.id,
|
|
102674
102954
|
operationId,
|
|
102675
102955
|
connectionEpoch: claimed.connection.connectionEpoch,
|
|
102676
|
-
failureCode: error instanceof GatewayRealtimeBrokerError ? error.code : "gateway_error"
|
|
102956
|
+
failureCode: error instanceof GatewayRealtimeBrokerError ? error.code : error instanceof TranscriptionBillingRefusedError || error instanceof RealtimeVoiceUnavailableError ? "billing_refused" : "gateway_error"
|
|
102677
102957
|
})
|
|
102678
102958
|
)
|
|
102679
102959
|
).catch(() => void 0);
|
|
102680
102960
|
}
|
|
102961
|
+
const refused = realtimeVoiceRefusalResponse(c, error);
|
|
102962
|
+
if (refused) return refused;
|
|
102681
102963
|
if (error instanceof SessionRealtimeConflictError) throw sessionRealtimeHttpError(error);
|
|
102682
102964
|
if (!(error instanceof GatewayRealtimeBrokerError)) throw error;
|
|
102683
102965
|
const status = error.code === "credential_unavailable" ? 409 : 502;
|
|
@@ -102883,23 +103165,31 @@ function registerSessionRoutes(app, deps) {
|
|
|
102883
103165
|
});
|
|
102884
103166
|
}
|
|
102885
103167
|
try {
|
|
102886
|
-
const result = await withWorkspaceSessionActivityRls(
|
|
102887
|
-
|
|
102888
|
-
|
|
102889
|
-
|
|
102890
|
-
|
|
102891
|
-
|
|
102892
|
-
|
|
102893
|
-
|
|
102894
|
-
|
|
102895
|
-
|
|
102896
|
-
|
|
102897
|
-
|
|
102898
|
-
|
|
102899
|
-
|
|
102900
|
-
|
|
102901
|
-
|
|
102902
|
-
|
|
103168
|
+
const result = await withWorkspaceSessionActivityRls(
|
|
103169
|
+
db,
|
|
103170
|
+
workspaceId,
|
|
103171
|
+
async (scopedDb) => {
|
|
103172
|
+
await beforeCommit?.(scopedDb);
|
|
103173
|
+
return syncSessionRealtimeLedgerInTransaction(
|
|
103174
|
+
scopedDb,
|
|
103175
|
+
{
|
|
103176
|
+
workspaceId,
|
|
103177
|
+
sessionId,
|
|
103178
|
+
realtimeId,
|
|
103179
|
+
ownerSubjectId: grant.subjectId,
|
|
103180
|
+
...parsed.data,
|
|
103181
|
+
controlLockTimeoutMs: workspaceControlRequestLockTimeoutMs()
|
|
103182
|
+
},
|
|
103183
|
+
captureLinked ? {
|
|
103184
|
+
afterDelegationAdmission: async ({ turnId }) => captureLinked(scopedDb, sessionId, turnId)
|
|
103185
|
+
} : {}
|
|
103186
|
+
);
|
|
103187
|
+
},
|
|
103188
|
+
void 0,
|
|
103189
|
+
// External reauthorization takes the organization-membership lock,
|
|
103190
|
+
// which precedes the tenancy fence and the canonical session prefix.
|
|
103191
|
+
Boolean(beforeCommit)
|
|
103192
|
+
);
|
|
102903
103193
|
await publishRealtimeMutation(grant.accountId, workspaceId, sessionId, result);
|
|
102904
103194
|
c.header("cache-control", "private, no-store");
|
|
102905
103195
|
return c.json({ accepted: result.accepted, outbound: result.outbound });
|
|
@@ -105543,11 +105833,16 @@ function codexRealtimeHttpFailure(error) {
|
|
|
105543
105833
|
return { status: 409, code: "conflict", retryable: false };
|
|
105544
105834
|
}
|
|
105545
105835
|
}
|
|
105836
|
+
var SESSION_REALTIME_USER_MESSAGES = {
|
|
105837
|
+
REALTIME_ACTIVE: "Voice is already on for this session in another tab or window. End it there, or try again in a minute.",
|
|
105838
|
+
CONTROL_NOT_ACTIVE: "Resume this session to start voice.",
|
|
105839
|
+
SESSION_CANCELLED: "This session was cancelled, so voice can't start."
|
|
105840
|
+
};
|
|
105546
105841
|
function sessionRealtimeHttpError(error) {
|
|
105547
105842
|
if (error instanceof HTTPException88) return error;
|
|
105548
105843
|
if (error instanceof SessionRealtimeConflictError) {
|
|
105549
105844
|
return new HTTPException88(error.code === "REALTIME_NOT_FOUND" ? 404 : 409, {
|
|
105550
|
-
message: error.message,
|
|
105845
|
+
message: SESSION_REALTIME_USER_MESSAGES[error.code] ?? error.message,
|
|
105551
105846
|
cause: error
|
|
105552
105847
|
});
|
|
105553
105848
|
}
|
|
@@ -106325,9 +106620,17 @@ function commandConflictResponse(c, error) {
|
|
|
106325
106620
|
throw error;
|
|
106326
106621
|
}
|
|
106327
106622
|
async function loadEffectivePolicyContext(deps, workspaceId, subjectId, sessions) {
|
|
106623
|
+
const workspaceRead = requireWorkspace3(deps.db, workspaceId);
|
|
106624
|
+
void workspaceRead.catch(() => void 0);
|
|
106328
106625
|
const [policy, effectiveToolsContext] = await Promise.all([
|
|
106329
|
-
workspaceSessionToolPolicyContext(
|
|
106330
|
-
|
|
106626
|
+
workspaceSessionToolPolicyContext(
|
|
106627
|
+
deps.db,
|
|
106628
|
+
workspaceId,
|
|
106629
|
+
deps.settings,
|
|
106630
|
+
subjectId,
|
|
106631
|
+
workspaceRead
|
|
106632
|
+
),
|
|
106633
|
+
workspaceSessionEffectiveToolsContext2(deps, workspaceId, subjectId, sessions, workspaceRead)
|
|
106331
106634
|
]);
|
|
106332
106635
|
return { ...policy, effectiveToolsContext };
|
|
106333
106636
|
}
|
|
@@ -106798,6 +107101,11 @@ function socialHttpException(error) {
|
|
|
106798
107101
|
}
|
|
106799
107102
|
|
|
106800
107103
|
// src/routes/workspaces.ts
|
|
107104
|
+
import {
|
|
107105
|
+
createRealtimeVoiceBilling as createRealtimeVoiceBilling2,
|
|
107106
|
+
deploymentRealtimeVoice,
|
|
107107
|
+
realtimeVoiceOfferProblem
|
|
107108
|
+
} from "@opengeni/core";
|
|
106801
107109
|
import { withDirectModelProviders } from "@opengeni/config";
|
|
106802
107110
|
import { listConnectionsMetadata as listConnectionsMetadata6 } from "@opengeni/db";
|
|
106803
107111
|
import {
|
|
@@ -106866,9 +107174,9 @@ import {
|
|
|
106866
107174
|
replayWorkspaceGatewayCustomModelCreate,
|
|
106867
107175
|
replayWorkspaceOpenRouterCustomModelCreate,
|
|
106868
107176
|
listWorkspacesForSubject as listWorkspacesForSubject2,
|
|
106869
|
-
nestedPostgresSqlState as
|
|
107177
|
+
nestedPostgresSqlState as nestedPostgresSqlState12,
|
|
106870
107178
|
removeWorkspaceMember,
|
|
106871
|
-
requireWorkspace as
|
|
107179
|
+
requireWorkspace as requireWorkspace4,
|
|
106872
107180
|
updateWorkspaceSettings,
|
|
106873
107181
|
getRig,
|
|
106874
107182
|
setWorkspaceDefaultRig,
|
|
@@ -106917,7 +107225,7 @@ import {
|
|
|
106917
107225
|
} from "@opengeni/core";
|
|
106918
107226
|
|
|
106919
107227
|
// src/workspace-deletion.ts
|
|
106920
|
-
import { deleteWorkspaceIfQuiescent, nestedPostgresSqlState as
|
|
107228
|
+
import { deleteWorkspaceIfQuiescent, nestedPostgresSqlState as nestedPostgresSqlState11 } from "@opengeni/db";
|
|
106921
107229
|
import { HTTPException as HTTPException91 } from "hono/http-exception";
|
|
106922
107230
|
|
|
106923
107231
|
// src/workspace-delete-observability.ts
|
|
@@ -106959,13 +107267,13 @@ function recordWorkspaceDeleteObservation(observability, identity2, observation)
|
|
|
106959
107267
|
labels: { outcome: observation.outcome }
|
|
106960
107268
|
});
|
|
106961
107269
|
}
|
|
106962
|
-
for (const [kind,
|
|
107270
|
+
for (const [kind, count2] of Object.entries(observation.inventory ?? {})) {
|
|
106963
107271
|
observability.observeHistogram({
|
|
106964
107272
|
name: "opengeni_workspace_delete_inventory_rows",
|
|
106965
107273
|
help: "Rows or live owners observed by workspace deletion preflight inventory class.",
|
|
106966
107274
|
labels: { kind },
|
|
106967
107275
|
buckets: INVENTORY_BUCKETS,
|
|
106968
|
-
value: Math.max(0, Math.floor(
|
|
107276
|
+
value: Math.max(0, Math.floor(count2 ?? 0))
|
|
106969
107277
|
});
|
|
106970
107278
|
}
|
|
106971
107279
|
observability.info("Workspace deletion phase", {
|
|
@@ -107007,7 +107315,7 @@ async function deleteWorkspaceForRequest(deps, input) {
|
|
|
107007
107315
|
...observer ? { observer } : {}
|
|
107008
107316
|
});
|
|
107009
107317
|
} catch (error) {
|
|
107010
|
-
const state =
|
|
107318
|
+
const state = nestedPostgresSqlState11(error);
|
|
107011
107319
|
if (state === "42501") {
|
|
107012
107320
|
throw new HTTPException91(403, { message: "organization administration is not authorized" });
|
|
107013
107321
|
}
|
|
@@ -107161,7 +107469,7 @@ function registerWorkspaceRoutes(app, deps) {
|
|
|
107161
107469
|
];
|
|
107162
107470
|
if (readableWorkspaceIds.length > 0) {
|
|
107163
107471
|
const workspaces = await Promise.all(
|
|
107164
|
-
readableWorkspaceIds.map((workspaceId) =>
|
|
107472
|
+
readableWorkspaceIds.map((workspaceId) => requireWorkspace4(deps.db, workspaceId))
|
|
107165
107473
|
);
|
|
107166
107474
|
return c.json(workspaces.map((workspace) => Workspace.parse(workspace)));
|
|
107167
107475
|
}
|
|
@@ -107278,7 +107586,7 @@ function registerWorkspaceRoutes(app, deps) {
|
|
|
107278
107586
|
app.get("/v1/workspaces/:workspaceId", async (c) => {
|
|
107279
107587
|
const workspaceId = c.req.param("workspaceId");
|
|
107280
107588
|
await requireAccessGrant39(c, deps, workspaceId, "workspace:read");
|
|
107281
|
-
return c.json(Workspace.parse(await
|
|
107589
|
+
return c.json(Workspace.parse(await requireWorkspace4(deps.db, workspaceId)));
|
|
107282
107590
|
});
|
|
107283
107591
|
app.patch("/v1/workspaces/:workspaceId", async (c) => {
|
|
107284
107592
|
const workspaceId = c.req.param("workspaceId");
|
|
@@ -107340,14 +107648,14 @@ function registerWorkspaceRoutes(app, deps) {
|
|
|
107340
107648
|
deps.db,
|
|
107341
107649
|
workspaceId,
|
|
107342
107650
|
parsed.data,
|
|
107343
|
-
{ requireWorkspace:
|
|
107651
|
+
{ requireWorkspace: requireWorkspace4, updateWorkspaceSettings },
|
|
107344
107652
|
{
|
|
107345
107653
|
controlLockTimeoutMs: workspaceControlRequestLockTimeoutMs2()
|
|
107346
107654
|
}
|
|
107347
107655
|
);
|
|
107348
107656
|
return c.json(Workspace.parse(workspace));
|
|
107349
107657
|
} catch (error) {
|
|
107350
|
-
if (
|
|
107658
|
+
if (nestedPostgresSqlState12(error) === "0A000") {
|
|
107351
107659
|
throw new HTTPException92(409, {
|
|
107352
107660
|
message: "Memory settings moved to Settings > Agent learning. Refresh the app to change them."
|
|
107353
107661
|
});
|
|
@@ -107543,7 +107851,7 @@ function registerWorkspaceRoutes(app, deps) {
|
|
|
107543
107851
|
if (error instanceof WorkspaceGatewayCustomModelLimitError || error instanceof WorkspaceGatewayCustomModelHistoryLimitError) {
|
|
107544
107852
|
throw new HTTPException92(422, { message: error.message });
|
|
107545
107853
|
}
|
|
107546
|
-
if (
|
|
107854
|
+
if (nestedPostgresSqlState12(error) === "23505") {
|
|
107547
107855
|
throw new HTTPException92(422, {
|
|
107548
107856
|
message: "Gateway custom model already exists"
|
|
107549
107857
|
});
|
|
@@ -107672,7 +107980,7 @@ function registerWorkspaceRoutes(app, deps) {
|
|
|
107672
107980
|
if (error instanceof WorkspaceOpenRouterCustomModelLimitError || error instanceof WorkspaceOpenRouterCustomModelHistoryLimitError) {
|
|
107673
107981
|
throw new HTTPException92(422, { message: error.message });
|
|
107674
107982
|
}
|
|
107675
|
-
if (
|
|
107983
|
+
if (nestedPostgresSqlState12(error) === "23505") {
|
|
107676
107984
|
throw new HTTPException92(422, {
|
|
107677
107985
|
message: "OpenRouter custom model already exists"
|
|
107678
107986
|
});
|
|
@@ -107735,16 +108043,37 @@ function registerWorkspaceRoutes(app, deps) {
|
|
|
107735
108043
|
return credentialReady ? { available: true, unavailableReason: null } : { available: false, unavailableReason: credentialReason };
|
|
107736
108044
|
};
|
|
107737
108045
|
const gatewayModels = Object.values(AI_GATEWAY_REALTIME_MODELS);
|
|
107738
|
-
const
|
|
107739
|
-
|
|
108046
|
+
const managedCandidates = [
|
|
108047
|
+
{
|
|
108048
|
+
id: AZURE_LIVE_MODEL_ID,
|
|
108049
|
+
label: "GPT Live 1",
|
|
108050
|
+
description: "Realtime voice with session delegation"
|
|
108051
|
+
},
|
|
108052
|
+
...gatewayModels.map((model) => ({
|
|
107740
108053
|
id: model.managedModelId,
|
|
107741
108054
|
label: model.label,
|
|
108055
|
+
description: model.description
|
|
108056
|
+
}))
|
|
108057
|
+
].filter((model) => {
|
|
108058
|
+
const voice = deploymentRealtimeVoice(deps.settings, model.id);
|
|
108059
|
+
return voice !== null && realtimeVoiceOfferProblem(deps.settings, voice) === null;
|
|
108060
|
+
}).filter(
|
|
108061
|
+
(model, _index, offered) => offered.some((candidate) => candidate.id === AZURE_LIVE_MODEL_ID) ? model.id === AZURE_LIVE_MODEL_ID : true
|
|
108062
|
+
);
|
|
108063
|
+
const creditStanding = managedCandidates.length > 0 ? await createRealtimeVoiceBilling2({
|
|
108064
|
+
db: deps.db,
|
|
108065
|
+
settings: deps.settings
|
|
108066
|
+
}).creditStanding(grant.accountId) : "none";
|
|
108067
|
+
const hasCredits = creditStanding === "spendable";
|
|
108068
|
+
const models = [
|
|
108069
|
+
...managedCandidates.map((model, index) => ({
|
|
108070
|
+
...model,
|
|
107742
108071
|
provider: "OpenGeni",
|
|
107743
|
-
|
|
107744
|
-
|
|
107745
|
-
|
|
107746
|
-
|
|
107747
|
-
|
|
108072
|
+
...hasCredits ? { available: true, unavailableReason: null, unavailableCode: null } : {
|
|
108073
|
+
available: false,
|
|
108074
|
+
unavailableReason: creditStanding === "promotional_only" ? "Promotional credits don't cover live voice. Add credits to use it." : "Add Opengeni credits to use live voice",
|
|
108075
|
+
unavailableCode: "insufficient_credits"
|
|
108076
|
+
},
|
|
107748
108077
|
recommended: index === 0
|
|
107749
108078
|
})),
|
|
107750
108079
|
{
|
|
@@ -108090,7 +108419,7 @@ function rethrowWorkspaceMemberCandidateError(error) {
|
|
|
108090
108419
|
message: "the workspace must keep at least one administrator"
|
|
108091
108420
|
});
|
|
108092
108421
|
}
|
|
108093
|
-
const sqlState =
|
|
108422
|
+
const sqlState = nestedPostgresSqlState12(error);
|
|
108094
108423
|
if (sqlState === "P0002") {
|
|
108095
108424
|
throw new HTTPException92(404, { message: "workspace not found" });
|
|
108096
108425
|
}
|
|
@@ -108155,7 +108484,7 @@ import {
|
|
|
108155
108484
|
getWorkspace as getWorkspace6,
|
|
108156
108485
|
getWorkspaceUsage,
|
|
108157
108486
|
grantWorkspaceCredits,
|
|
108158
|
-
nestedPostgresSqlState as
|
|
108487
|
+
nestedPostgresSqlState as nestedPostgresSqlState13,
|
|
108159
108488
|
setMemberAllowance,
|
|
108160
108489
|
setWorkspaceAllowance,
|
|
108161
108490
|
withAccountRls as withAccountRls3,
|
|
@@ -108290,7 +108619,7 @@ async function lifecycle2(operation) {
|
|
|
108290
108619
|
} catch (error) {
|
|
108291
108620
|
if (error instanceof UsageAllowanceVersionConflictError)
|
|
108292
108621
|
throw new HTTPException93(409, { message: error.message });
|
|
108293
|
-
const state =
|
|
108622
|
+
const state = nestedPostgresSqlState13(error);
|
|
108294
108623
|
if (state === "40001" || state === "23505")
|
|
108295
108624
|
throw new HTTPException93(409, { message: "Allowance version or operation conflicts" });
|
|
108296
108625
|
if (state === "42501")
|
|
@@ -110041,7 +110370,7 @@ import {
|
|
|
110041
110370
|
} from "@opengeni/core";
|
|
110042
110371
|
import {
|
|
110043
110372
|
availableSlackBotConnectionMetadata as availableSlackBotConnectionMetadata2,
|
|
110044
|
-
nestedPostgresSqlState as
|
|
110373
|
+
nestedPostgresSqlState as nestedPostgresSqlState14,
|
|
110045
110374
|
readOrganizationSlackBotAccess,
|
|
110046
110375
|
setOrganizationSlackBotAccess,
|
|
110047
110376
|
sharedOrganizationSlackBots as sharedOrganizationSlackBots2
|
|
@@ -110071,7 +110400,7 @@ function registerSlackBotAccessRoutes(app, deps) {
|
|
|
110071
110400
|
)
|
|
110072
110401
|
);
|
|
110073
110402
|
} catch (error) {
|
|
110074
|
-
if (
|
|
110403
|
+
if (nestedPostgresSqlState14(error) === "42501")
|
|
110075
110404
|
throw new HTTPException99(404, { message: "Local bot installation unavailable" });
|
|
110076
110405
|
throw error;
|
|
110077
110406
|
}
|
|
@@ -110125,7 +110454,7 @@ function registerSlackBotAccessRoutes(app, deps) {
|
|
|
110125
110454
|
return context.json(OpenGeniSlackBotOrganizationAccess.parse(result));
|
|
110126
110455
|
} catch (error) {
|
|
110127
110456
|
if (error instanceof HTTPException99) throw error;
|
|
110128
|
-
if (
|
|
110457
|
+
if (nestedPostgresSqlState14(error) === "42501")
|
|
110129
110458
|
throw new HTTPException99(403, {
|
|
110130
110459
|
message: "Organization administrator and a verified local bot installation required"
|
|
110131
110460
|
});
|
|
@@ -110322,7 +110651,7 @@ import {
|
|
|
110322
110651
|
getWorkspaceArtifact as getWorkspaceArtifact3,
|
|
110323
110652
|
getWorkspaceArtifactContentRef as getWorkspaceArtifactContentRef3,
|
|
110324
110653
|
listWorkspaceArtifacts as listWorkspaceArtifacts2,
|
|
110325
|
-
nestedPostgresSqlState as
|
|
110654
|
+
nestedPostgresSqlState as nestedPostgresSqlState15,
|
|
110326
110655
|
publishWorkspaceArtifactVersion as publishWorkspaceArtifactVersion2,
|
|
110327
110656
|
rollbackWorkspaceArtifact as rollbackWorkspaceArtifact2,
|
|
110328
110657
|
setWorkspaceArtifactStatus as setWorkspaceArtifactStatus2,
|
|
@@ -110369,7 +110698,7 @@ function workspaceArtifactErrorResponse(context, error) {
|
|
|
110369
110698
|
422
|
|
110370
110699
|
);
|
|
110371
110700
|
}
|
|
110372
|
-
if (
|
|
110701
|
+
if (nestedPostgresSqlState15(error) === "23505") {
|
|
110373
110702
|
return context.json(
|
|
110374
110703
|
{
|
|
110375
110704
|
code: "WORKSPACE_ARTIFACT_CONFLICT",
|
|
@@ -110753,7 +111082,7 @@ import {
|
|
|
110753
111082
|
getGeneratedImageArtifact as getGeneratedImageArtifact2,
|
|
110754
111083
|
getSessionAuthorityProjection as getSessionAuthorityProjection3,
|
|
110755
111084
|
listArtifactCatalogCandidates,
|
|
110756
|
-
nestedPostgresSqlState as
|
|
111085
|
+
nestedPostgresSqlState as nestedPostgresSqlState16,
|
|
110757
111086
|
updateArtifactPin,
|
|
110758
111087
|
withSessionRlsActorContext as withSessionRlsActorContext9
|
|
110759
111088
|
} from "@opengeni/db";
|
|
@@ -110888,7 +111217,7 @@ function registerArtifactCatalogRoutes(app, deps) {
|
|
|
110888
111217
|
}
|
|
110889
111218
|
);
|
|
110890
111219
|
} catch (error) {
|
|
110891
|
-
if (
|
|
111220
|
+
if (nestedPostgresSqlState16(error) === "42501")
|
|
110892
111221
|
throw new HTTPException103(404, { message: "Artifact not found" });
|
|
110893
111222
|
throw error;
|
|
110894
111223
|
}
|
|
@@ -111549,7 +111878,11 @@ import {
|
|
|
111549
111878
|
resolveWorkspaceVoiceInputEnabled as resolveWorkspaceVoiceInputEnabled2,
|
|
111550
111879
|
WorkspaceVoiceInputSettings as WorkspaceVoiceInputSettings2
|
|
111551
111880
|
} from "@opengeni/contracts";
|
|
111552
|
-
import {
|
|
111881
|
+
import {
|
|
111882
|
+
requireAccessGrant as requireAccessGrant50,
|
|
111883
|
+
TranscriptionBillingRefusedError as TranscriptionBillingRefusedError3,
|
|
111884
|
+
TranscriptionServiceError as TranscriptionServiceError2
|
|
111885
|
+
} from "@opengeni/core";
|
|
111553
111886
|
import { getWorkspace as getWorkspace10 } from "@opengeni/db";
|
|
111554
111887
|
|
|
111555
111888
|
// src/routes/transcription-recordings.ts
|
|
@@ -111567,6 +111900,7 @@ import {
|
|
|
111567
111900
|
normalizeMimeType,
|
|
111568
111901
|
requireAccessGrant as requireAccessGrant49,
|
|
111569
111902
|
TRANSCRIPTION_PROVIDER_REQUEST_TIMEOUT_MILLISECONDS,
|
|
111903
|
+
TranscriptionBillingRefusedError as TranscriptionBillingRefusedError2,
|
|
111570
111904
|
TranscriptionServiceError
|
|
111571
111905
|
} from "@opengeni/core";
|
|
111572
111906
|
import { retryWhileMissing as retryWhileMissing7 } from "@opengeni/storage";
|
|
@@ -111598,6 +111932,38 @@ import {
|
|
|
111598
111932
|
} from "@opengeni/db";
|
|
111599
111933
|
import { getWorkspace as getWorkspace9 } from "@opengeni/db";
|
|
111600
111934
|
|
|
111935
|
+
// src/transcription/wav-duration.ts
|
|
111936
|
+
function wavDurationSeconds(bytes) {
|
|
111937
|
+
if (bytes.byteLength < 12) return null;
|
|
111938
|
+
const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
|
|
111939
|
+
if (ascii2(bytes, 0) !== "RIFF" || ascii2(bytes, 8) !== "WAVE") return null;
|
|
111940
|
+
let offset = 12;
|
|
111941
|
+
let byteRate = null;
|
|
111942
|
+
while (offset + 8 <= bytes.byteLength) {
|
|
111943
|
+
const id = ascii2(bytes, offset);
|
|
111944
|
+
const size = view.getUint32(offset + 4, true);
|
|
111945
|
+
const body3 = offset + 8;
|
|
111946
|
+
if (id === "fmt " && body3 + 12 <= bytes.byteLength) {
|
|
111947
|
+
byteRate = view.getUint32(body3 + 8, true);
|
|
111948
|
+
} else if (id === "data") {
|
|
111949
|
+
if (!byteRate) return null;
|
|
111950
|
+
const available = bytes.byteLength - body3;
|
|
111951
|
+
const dataBytes = size === 0 || size === 4294967295 ? available : Math.min(size, available);
|
|
111952
|
+
return dataBytes / byteRate;
|
|
111953
|
+
}
|
|
111954
|
+
offset = body3 + size + size % 2;
|
|
111955
|
+
}
|
|
111956
|
+
return null;
|
|
111957
|
+
}
|
|
111958
|
+
function ascii2(bytes, offset) {
|
|
111959
|
+
return String.fromCharCode(
|
|
111960
|
+
bytes[offset] ?? 0,
|
|
111961
|
+
bytes[offset + 1] ?? 0,
|
|
111962
|
+
bytes[offset + 2] ?? 0,
|
|
111963
|
+
bytes[offset + 3] ?? 0
|
|
111964
|
+
);
|
|
111965
|
+
}
|
|
111966
|
+
|
|
111601
111967
|
// src/transcription/segmenter.ts
|
|
111602
111968
|
import { spawn } from "child_process";
|
|
111603
111969
|
import { createWriteStream } from "fs";
|
|
@@ -111629,7 +111995,7 @@ function createFfmpegTranscriptionSegmenter(input) {
|
|
|
111629
111995
|
true
|
|
111630
111996
|
);
|
|
111631
111997
|
}
|
|
111632
|
-
if (!Number.isSafeInteger(request.providerSegmentSeconds) || request.providerSegmentSeconds <= 0 || !Number.isSafeInteger(request.totalDurationMilliseconds) || request.totalDurationMilliseconds <= 0) {
|
|
111998
|
+
if (!Number.isSafeInteger(request.providerSegmentSeconds) || request.providerSegmentSeconds <= 0 || !Number.isSafeInteger(request.totalDurationMilliseconds) || request.totalDurationMilliseconds <= 0 || request.maxDecodeSeconds !== void 0 && (!Number.isSafeInteger(request.maxDecodeSeconds) || request.maxDecodeSeconds <= 0)) {
|
|
111633
111999
|
throw new TranscriptionSegmenterError(
|
|
111634
112000
|
"Audio segmentation bounds are invalid",
|
|
111635
112001
|
"invalid_audio",
|
|
@@ -111661,6 +112027,8 @@ function createFfmpegTranscriptionSegmenter(input) {
|
|
|
111661
112027
|
"-y",
|
|
111662
112028
|
"-i",
|
|
111663
112029
|
inputPath,
|
|
112030
|
+
// Output-side ceiling: audio past it is never decoded or written.
|
|
112031
|
+
...request.maxDecodeSeconds !== void 0 ? ["-t", String(request.maxDecodeSeconds)] : [],
|
|
111664
112032
|
"-map",
|
|
111665
112033
|
"0:a:0",
|
|
111666
112034
|
"-vn",
|
|
@@ -112123,7 +112491,9 @@ function registerResumableTranscriptionRoutes(app, deps) {
|
|
|
112123
112491
|
const service = deps.transcription;
|
|
112124
112492
|
if (!deps.objectStorage || !service || !await service.available({
|
|
112125
112493
|
workspaceId: authority.workspaceId,
|
|
112126
|
-
subjectId: authority.subjectId
|
|
112494
|
+
subjectId: authority.subjectId,
|
|
112495
|
+
preferredProvider: authority.voiceInput?.preferredProvider,
|
|
112496
|
+
fallbackEnabled: authority.voiceInput?.fallbackEnabled
|
|
112127
112497
|
})) {
|
|
112128
112498
|
return c.json({ code: "unavailable" }, 503);
|
|
112129
112499
|
}
|
|
@@ -112166,6 +112536,31 @@ function registerResumableTranscriptionRoutes(app, deps) {
|
|
|
112166
112536
|
false
|
|
112167
112537
|
);
|
|
112168
112538
|
}
|
|
112539
|
+
const segmentProviderId = claim.segment.providerId && claim.segment.providerId !== "host" ? claim.segment.providerId : null;
|
|
112540
|
+
if (segmentProviderId && service.admit) {
|
|
112541
|
+
try {
|
|
112542
|
+
await service.admit({
|
|
112543
|
+
providerId: segmentProviderId,
|
|
112544
|
+
accountId: authority.accountId,
|
|
112545
|
+
workspaceId: authority.workspaceId,
|
|
112546
|
+
attribution: authority.attribution
|
|
112547
|
+
});
|
|
112548
|
+
} catch (error) {
|
|
112549
|
+
if (!(error instanceof TranscriptionBillingRefusedError2)) throw error;
|
|
112550
|
+
await failTranscriptionRecordingSegment(deps.db, {
|
|
112551
|
+
workspaceId: authority.workspaceId,
|
|
112552
|
+
subjectId: authority.subjectId,
|
|
112553
|
+
recordingId: uuidParam3(c, "recordingId"),
|
|
112554
|
+
segmentNumber,
|
|
112555
|
+
attemptId,
|
|
112556
|
+
fallbackProviderId: null,
|
|
112557
|
+
// The real refusal, so a reloaded recording still explains it.
|
|
112558
|
+
errorCode: error.code,
|
|
112559
|
+
retryable: true
|
|
112560
|
+
}).catch(() => null);
|
|
112561
|
+
return transcriptionBillingRefusal(c, error);
|
|
112562
|
+
}
|
|
112563
|
+
}
|
|
112169
112564
|
const providerStartedAt = /* @__PURE__ */ new Date();
|
|
112170
112565
|
const providerDeadlineAt = new Date(
|
|
112171
112566
|
providerStartedAt.getTime() + TRANSCRIPTION_PROVIDER_REQUEST_TIMEOUT_MILLISECONDS
|
|
@@ -112188,7 +112583,17 @@ function registerResumableTranscriptionRoutes(app, deps) {
|
|
|
112188
112583
|
durationSeconds: claim.segment.durationMilliseconds / 1e3,
|
|
112189
112584
|
requestId: attemptId,
|
|
112190
112585
|
providerDeadlineAt,
|
|
112191
|
-
|
|
112586
|
+
deferBillingSettlement: true,
|
|
112587
|
+
...segmentProviderId ? { providerId: segmentProviderId } : {},
|
|
112588
|
+
billing: {
|
|
112589
|
+
// One settlement per prepared segment of this exact recording,
|
|
112590
|
+
// however many provider attempts it takes. The recording id is
|
|
112591
|
+
// client-chosen, so the unit id also binds its server creation
|
|
112592
|
+
// time and the server-produced segment bytes.
|
|
112593
|
+
sourceId: `${claim.recording.recording.id}:${Date.parse(claim.recording.recording.createdAt)}:${segmentNumber}:${claim.segment.sha256}`,
|
|
112594
|
+
attribution: authority.attribution,
|
|
112595
|
+
trustedDurationSeconds: wavDurationSeconds(stored.bytes) ?? void 0
|
|
112596
|
+
}
|
|
112192
112597
|
});
|
|
112193
112598
|
const completed = await completeTranscriptionRecordingSegment(deps.db, {
|
|
112194
112599
|
workspaceId: authority.workspaceId,
|
|
@@ -112200,6 +112605,7 @@ function registerResumableTranscriptionRoutes(app, deps) {
|
|
|
112200
112605
|
languages: result.languages,
|
|
112201
112606
|
providerId: claim.segment.providerId ?? result.providerId
|
|
112202
112607
|
});
|
|
112608
|
+
await result.settleBilling?.().catch(() => void 0);
|
|
112203
112609
|
return c.json(await cleanupTerminalObjects(deps, authority, completed));
|
|
112204
112610
|
} catch (error) {
|
|
112205
112611
|
if (authority && attemptId && segmentNumber !== null) {
|
|
@@ -112223,6 +112629,9 @@ function registerResumableTranscriptionRoutes(app, deps) {
|
|
|
112223
112629
|
errorCode: failure2.code,
|
|
112224
112630
|
retryable: failure2.retryable
|
|
112225
112631
|
}).catch(() => null);
|
|
112632
|
+
if (error instanceof TranscriptionBillingRefusedError2) {
|
|
112633
|
+
return transcriptionBillingRefusal(c, error);
|
|
112634
|
+
}
|
|
112226
112635
|
if (persisted) return c.json(persisted);
|
|
112227
112636
|
}
|
|
112228
112637
|
return routeError2(c, error);
|
|
@@ -112269,7 +112678,8 @@ async function requireRecordingAuthority(c, deps, requirePolicy) {
|
|
|
112269
112678
|
voiceInput: WorkspaceVoiceInputSettings.safeParse(workspace?.settings.voiceInput).data,
|
|
112270
112679
|
accountId: grant.accountId,
|
|
112271
112680
|
workspaceId,
|
|
112272
|
-
subjectId: grant.subjectId
|
|
112681
|
+
subjectId: grant.subjectId,
|
|
112682
|
+
attribution: creditDebitAttributionForGrant(grant)
|
|
112273
112683
|
};
|
|
112274
112684
|
}
|
|
112275
112685
|
async function resumableAvailable(deps, workspaceId) {
|
|
@@ -112340,6 +112750,9 @@ function processingFailure(error) {
|
|
|
112340
112750
|
if (error instanceof TranscriptionSegmenterError) {
|
|
112341
112751
|
return { code: error.code, retryable: error.retryable };
|
|
112342
112752
|
}
|
|
112753
|
+
if (error instanceof TranscriptionBillingRefusedError2) {
|
|
112754
|
+
return { code: error.code, retryable: true };
|
|
112755
|
+
}
|
|
112343
112756
|
if (error instanceof TranscriptionServiceError) {
|
|
112344
112757
|
return {
|
|
112345
112758
|
code: error.code,
|
|
@@ -112355,6 +112768,9 @@ function routeError2(c, error) {
|
|
|
112355
112768
|
if (error instanceof TranscriptionRecordingNotFoundError) {
|
|
112356
112769
|
return c.json({ code: "not_found" }, 404);
|
|
112357
112770
|
}
|
|
112771
|
+
if (error instanceof TranscriptionBillingRefusedError2) {
|
|
112772
|
+
return transcriptionBillingRefusal(c, error);
|
|
112773
|
+
}
|
|
112358
112774
|
if (error instanceof TranscriptionRecordingConflictError) {
|
|
112359
112775
|
return c.json({ code: "conflict" }, 409);
|
|
112360
112776
|
}
|
|
@@ -112478,12 +112894,21 @@ function registerTranscriptionRoutes(app, deps) {
|
|
|
112478
112894
|
}
|
|
112479
112895
|
const preferences = WorkspaceVoiceInputSettings2.safeParse(workspace.settings.voiceInput).data;
|
|
112480
112896
|
const service = deps.transcription;
|
|
112481
|
-
if (!service || !await service.available({
|
|
112897
|
+
if (!service || !await service.available({
|
|
112898
|
+
workspaceId,
|
|
112899
|
+
subjectId: grant.subjectId,
|
|
112900
|
+
preferredProvider: preferences?.preferredProvider,
|
|
112901
|
+
fallbackEnabled: preferences?.fallbackEnabled
|
|
112902
|
+
})) {
|
|
112482
112903
|
return c.json({ code: "unavailable" }, 503);
|
|
112483
112904
|
}
|
|
112484
112905
|
try {
|
|
112485
112906
|
const body3 = await audioRequest(c.req.raw, service.limits().maxSizeBytes);
|
|
112486
112907
|
const result = await service.transcribe({
|
|
112908
|
+
billing: {
|
|
112909
|
+
sourceId: crypto.randomUUID(),
|
|
112910
|
+
attribution: creditDebitAttributionForGrant(grant)
|
|
112911
|
+
},
|
|
112487
112912
|
preferredProvider: preferences?.preferredProvider,
|
|
112488
112913
|
fallbackEnabled: preferences?.fallbackEnabled,
|
|
112489
112914
|
workspaceId,
|
|
@@ -112504,6 +112929,9 @@ function registerTranscriptionRoutes(app, deps) {
|
|
|
112504
112929
|
if (error instanceof TranscriptionServiceError2) {
|
|
112505
112930
|
return c.json({ code: error.code }, error.status);
|
|
112506
112931
|
}
|
|
112932
|
+
if (error instanceof TranscriptionBillingRefusedError3) {
|
|
112933
|
+
return transcriptionBillingRefusal(c, error);
|
|
112934
|
+
}
|
|
112507
112935
|
if (isAbort3(error)) return c.json({ code: "cancelled" }, 499);
|
|
112508
112936
|
return c.json({ code: "unknown" }, 500);
|
|
112509
112937
|
}
|
|
@@ -112609,17 +113037,23 @@ function isAbort3(error) {
|
|
|
112609
113037
|
|
|
112610
113038
|
// src/routes/session-artifact-associations.ts
|
|
112611
113039
|
import {
|
|
112612
|
-
|
|
113040
|
+
fileOwnerContextForAccess as fileOwnerContextForAccess6,
|
|
113041
|
+
requireAccessGrantAuthorization as requireAccessGrantAuthorization37,
|
|
112613
113042
|
requirePermission as requirePermission12,
|
|
112614
113043
|
requireSessionAuthorization as requireSessionAuthorization15,
|
|
112615
113044
|
SessionAuthorizationDeniedError as SessionAuthorizationDeniedError15,
|
|
112616
113045
|
SessionAuthorizationUnavailableError as SessionAuthorizationUnavailableError13
|
|
112617
113046
|
} from "@opengeni/core";
|
|
112618
113047
|
import {
|
|
113048
|
+
getGeneratedImageArtifact as getGeneratedImageArtifact3,
|
|
113049
|
+
getGeneratedVideoArtifact as getGeneratedVideoArtifact2,
|
|
113050
|
+
getRetainedFileArtifact as getRetainedFileArtifact2,
|
|
112619
113051
|
hasEditableArtifactSessionLink as hasEditableArtifactSessionLink2,
|
|
112620
113052
|
hasWorkspaceArtifactSessionLink,
|
|
113053
|
+
listArtifactCatalogCandidates as listArtifactCatalogCandidates2,
|
|
112621
113054
|
transactionallyAuthorizeEditableArtifactActor,
|
|
112622
|
-
withRlsContext as withRlsContext3
|
|
113055
|
+
withRlsContext as withRlsContext3,
|
|
113056
|
+
withSessionRlsActorContext as withSessionRlsActorContext10
|
|
112623
113057
|
} from "@opengeni/db";
|
|
112624
113058
|
import { HTTPException as HTTPException107 } from "hono/http-exception";
|
|
112625
113059
|
import { z as z55 } from "zod";
|
|
@@ -112630,12 +113064,18 @@ function registerSessionArtifactAssociationRoutes(app, deps) {
|
|
|
112630
113064
|
"/v1/workspaces/:workspaceId/sessions/:sessionId/artifact-associations/:artifactId",
|
|
112631
113065
|
async (context) => {
|
|
112632
113066
|
const workspaceId = context.req.param("workspaceId");
|
|
112633
|
-
const grant = await requireAccessGrant51(context, deps, workspaceId, "artifacts:read");
|
|
112634
|
-
requirePermission12(grant, "sessions:read");
|
|
112635
113067
|
const sessionId = context.req.param("sessionId");
|
|
112636
113068
|
const artifactId2 = context.req.param("artifactId");
|
|
112637
|
-
const kind = EditableArtifactId.safeParse(artifactId2).success ? "editable" : SessionId2.safeParse(artifactId2).success ? "site" : null;
|
|
112638
113069
|
const requestedKind = context.req.query("kind");
|
|
113070
|
+
const kind = EditableArtifactId.safeParse(artifactId2).success ? "editable" : SessionId2.safeParse(artifactId2).success ? requestedKind === "retained" ? "retained" : "site" : null;
|
|
113071
|
+
const authorization = await requireAccessGrantAuthorization37(
|
|
113072
|
+
context,
|
|
113073
|
+
deps,
|
|
113074
|
+
workspaceId,
|
|
113075
|
+
kind === "retained" ? "files:read" : "artifacts:read"
|
|
113076
|
+
);
|
|
113077
|
+
const grant = authorization.grant;
|
|
113078
|
+
requirePermission12(grant, "sessions:read");
|
|
112639
113079
|
if (!SessionId2.safeParse(sessionId).success || !kind || requestedKind !== void 0 && requestedKind !== kind) {
|
|
112640
113080
|
throw new HTTPException107(404, { message: "Artifact association not found" });
|
|
112641
113081
|
}
|
|
@@ -112647,7 +113087,18 @@ function registerSessionArtifactAssociationRoutes(app, deps) {
|
|
|
112647
113087
|
surface: "http"
|
|
112648
113088
|
});
|
|
112649
113089
|
const scope = { accountId: grant.accountId, workspaceId };
|
|
112650
|
-
const linked = kind === "editable" ? await hasEditableArtifactSessionLink2(deps.db, scope, sessionId, artifactId2) :
|
|
113090
|
+
const linked = kind === "editable" ? await hasEditableArtifactSessionLink2(deps.db, scope, sessionId, artifactId2) : kind === "retained" ? await hasRetainedArtifactSessionLink(
|
|
113091
|
+
deps,
|
|
113092
|
+
authorization,
|
|
113093
|
+
workspaceId,
|
|
113094
|
+
sessionId,
|
|
113095
|
+
artifactId2.toLowerCase()
|
|
113096
|
+
) : await hasWorkspaceArtifactSessionLink(
|
|
113097
|
+
deps.db,
|
|
113098
|
+
workspaceId,
|
|
113099
|
+
sessionId,
|
|
113100
|
+
artifactId2
|
|
113101
|
+
);
|
|
112651
113102
|
if (!linked) throw new HTTPException107(404, { message: "Artifact association not found" });
|
|
112652
113103
|
if (kind === "editable") {
|
|
112653
113104
|
const decision = await withRlsContext3(
|
|
@@ -112679,6 +113130,38 @@ function registerSessionArtifactAssociationRoutes(app, deps) {
|
|
|
112679
113130
|
}
|
|
112680
113131
|
);
|
|
112681
113132
|
}
|
|
113133
|
+
async function hasRetainedArtifactSessionLink(deps, authorization, workspaceId, sessionId, artifactId2) {
|
|
113134
|
+
const image = await getGeneratedImageArtifact3(deps.db, workspaceId, artifactId2);
|
|
113135
|
+
if (image) return image.sessionId === sessionId;
|
|
113136
|
+
const video = await getGeneratedVideoArtifact2(deps.db, workspaceId, artifactId2);
|
|
113137
|
+
if (video) return !video.artifact.deletedAt && video.artifact.sessionId === sessionId;
|
|
113138
|
+
const file = await getRetainedFileArtifact2(deps.db, workspaceId, artifactId2);
|
|
113139
|
+
if (!file) return false;
|
|
113140
|
+
const grant = authorization.grant;
|
|
113141
|
+
const actor = await fileOwnerContextForAccess6(deps, authorization, "files:read");
|
|
113142
|
+
const candidates = await withSessionRlsActorContext10(
|
|
113143
|
+
actor,
|
|
113144
|
+
() => listArtifactCatalogCandidates2(
|
|
113145
|
+
deps.db,
|
|
113146
|
+
{ accountId: grant.accountId, workspaceId },
|
|
113147
|
+
{
|
|
113148
|
+
query: {
|
|
113149
|
+
sourceSessionId: sessionId,
|
|
113150
|
+
q: file.file.filename.slice(0, 200),
|
|
113151
|
+
sort: "newest",
|
|
113152
|
+
status: "active",
|
|
113153
|
+
limit: 100
|
|
113154
|
+
},
|
|
113155
|
+
kinds: ["file", "image"],
|
|
113156
|
+
snapshotAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
113157
|
+
limit: 201
|
|
113158
|
+
}
|
|
113159
|
+
)
|
|
113160
|
+
);
|
|
113161
|
+
return candidates.some(
|
|
113162
|
+
(candidate) => candidate.origin === "sandbox_file" && candidate.id === artifactId2
|
|
113163
|
+
);
|
|
113164
|
+
}
|
|
112682
113165
|
|
|
112683
113166
|
// src/routes/video-generation.ts
|
|
112684
113167
|
import {
|
|
@@ -112689,7 +113172,7 @@ import {
|
|
|
112689
113172
|
} from "@opengeni/contracts";
|
|
112690
113173
|
import {
|
|
112691
113174
|
getVideoGenerationOperationSummary,
|
|
112692
|
-
withSessionRlsActorContext as
|
|
113175
|
+
withSessionRlsActorContext as withSessionRlsActorContext11,
|
|
112693
113176
|
getWorkspaceVercelAiGatewayConnectionMetadata,
|
|
112694
113177
|
getWorkspaceVideoGenerationPolicy,
|
|
112695
113178
|
updateWorkspaceVideoGenerationPolicy,
|
|
@@ -112697,9 +113180,9 @@ import {
|
|
|
112697
113180
|
workspaceXaiSubscriptionActive as workspaceXaiSubscriptionActive2
|
|
112698
113181
|
} from "@opengeni/db";
|
|
112699
113182
|
import {
|
|
112700
|
-
requireAccessGrant as
|
|
112701
|
-
requireAccessGrantAuthorization as
|
|
112702
|
-
fileOwnerContextForAccess as
|
|
113183
|
+
requireAccessGrant as requireAccessGrant51,
|
|
113184
|
+
requireAccessGrantAuthorization as requireAccessGrantAuthorization38,
|
|
113185
|
+
fileOwnerContextForAccess as fileOwnerContextForAccess7,
|
|
112703
113186
|
requireWorkspaceSettingsGrant as requireWorkspaceSettingsGrant6,
|
|
112704
113187
|
VIDEO_GENERATION_MODEL_CATALOG,
|
|
112705
113188
|
videoGenerationModelSupportsFundingSource,
|
|
@@ -112709,7 +113192,7 @@ import { HTTPException as HTTPException108 } from "hono/http-exception";
|
|
|
112709
113192
|
function registerVideoGenerationRoutes(app, deps) {
|
|
112710
113193
|
app.get("/v1/workspaces/:workspaceId/video-generation", async (c) => {
|
|
112711
113194
|
const workspaceId = c.req.param("workspaceId");
|
|
112712
|
-
const grant = await
|
|
113195
|
+
const grant = await requireAccessGrant51(c, deps, workspaceId, "workspace:read");
|
|
112713
113196
|
const [policy, connection, supergrokConfigured] = await Promise.all([
|
|
112714
113197
|
getWorkspaceVideoGenerationPolicy(deps.db, workspaceId),
|
|
112715
113198
|
getWorkspaceVercelAiGatewayConnectionMetadata(deps.db, workspaceId),
|
|
@@ -112783,9 +113266,9 @@ function registerVideoGenerationRoutes(app, deps) {
|
|
|
112783
113266
|
});
|
|
112784
113267
|
app.get("/v1/workspaces/:workspaceId/video-generation/operations/:operationId", async (c) => {
|
|
112785
113268
|
const workspaceId = c.req.param("workspaceId");
|
|
112786
|
-
const access = await
|
|
112787
|
-
const actor = await
|
|
112788
|
-
const summary = await
|
|
113269
|
+
const access = await requireAccessGrantAuthorization38(c, deps, workspaceId, "workspace:read");
|
|
113270
|
+
const actor = await fileOwnerContextForAccess7(deps, access, "workspace:read");
|
|
113271
|
+
const summary = await withSessionRlsActorContext11(
|
|
112789
113272
|
actor,
|
|
112790
113273
|
() => getVideoGenerationOperationSummary(deps.db, workspaceId, c.req.param("operationId"))
|
|
112791
113274
|
);
|
|
@@ -113983,7 +114466,7 @@ function registerManagedSignInMethodRoutes(app, deps) {
|
|
|
113983
114466
|
select provider_id as provider, (provider_id <> 'credential' or length(password)>0) as usable from auth_identities where user_id=${actor.authUserId}`
|
|
113984
114467
|
);
|
|
113985
114468
|
const connected = (provider) => accounts.some((a) => a.provider === provider && a.usable) && projection.loginBindings.some((b) => b.providerId === provider && b.status === "active");
|
|
113986
|
-
const
|
|
114469
|
+
const count2 = ["credential", "google", "github"].filter(
|
|
113987
114470
|
(p) => connected(p) && configured(deps, p)
|
|
113988
114471
|
).length;
|
|
113989
114472
|
return context.json(
|
|
@@ -113996,7 +114479,7 @@ function registerManagedSignInMethodRoutes(app, deps) {
|
|
|
113996
114479
|
provider,
|
|
113997
114480
|
connected: connected(provider),
|
|
113998
114481
|
available: configured(deps, provider),
|
|
113999
|
-
canDisconnect: provider !== "credential" && connected(provider) &&
|
|
114482
|
+
canDisconnect: provider !== "credential" && connected(provider) && count2 - (configured(deps, provider) ? 1 : 0) > 0,
|
|
114000
114483
|
implicitRelinkingSuppressed: !connected(provider) && projection.loginBindings.some(
|
|
114001
114484
|
(b) => b.providerId === provider && b.status !== "active"
|
|
114002
114485
|
)
|
|
@@ -114454,11 +114937,11 @@ import {
|
|
|
114454
114937
|
getOrganizationPrivateSessionSettings,
|
|
114455
114938
|
getSelfOrganizationInvitation,
|
|
114456
114939
|
getOrganizationRetentionPolicy,
|
|
114457
|
-
requireWorkspace as
|
|
114940
|
+
requireWorkspace as requireWorkspace5,
|
|
114458
114941
|
listOrganizationAdministrationMembers,
|
|
114459
114942
|
listOrganizationInvitations,
|
|
114460
114943
|
listSelfOrganizationInvitations,
|
|
114461
|
-
nestedPostgresSqlState as
|
|
114944
|
+
nestedPostgresSqlState as nestedPostgresSqlState17,
|
|
114462
114945
|
prepareOrganizationUserSetupDelivery,
|
|
114463
114946
|
revokeOrganizationInvitation,
|
|
114464
114947
|
settleOrganizationUserSetupDelivery,
|
|
@@ -114507,7 +114990,7 @@ async function requireOrganizationKeyWorkspaceDeletion(context, deps, organizati
|
|
|
114507
114990
|
message: "deleting an organization workspace requires an organization owner session or an organization API key with workspace:admin"
|
|
114508
114991
|
});
|
|
114509
114992
|
}
|
|
114510
|
-
const workspace = await
|
|
114993
|
+
const workspace = await requireWorkspace5(deps.db, workspaceId).catch(() => null);
|
|
114511
114994
|
if (!workspace || workspace.accountId !== organizationId2) {
|
|
114512
114995
|
throw new HTTPException112(404, { message: "workspace not found" });
|
|
114513
114996
|
}
|
|
@@ -114575,7 +115058,7 @@ function parseId(schema, value, label) {
|
|
|
114575
115058
|
return parsed.data;
|
|
114576
115059
|
}
|
|
114577
115060
|
function rethrowMembershipError(error, resourceLimitMessage) {
|
|
114578
|
-
const sqlState =
|
|
115061
|
+
const sqlState = nestedPostgresSqlState17(error);
|
|
114579
115062
|
if (sqlState === "54000" && resourceLimitMessage) {
|
|
114580
115063
|
throw new HTTPException112(409, {
|
|
114581
115064
|
message: resourceLimitMessage
|
|
@@ -114681,7 +115164,7 @@ function registerOrganizationMembershipRoutes(app, deps) {
|
|
|
114681
115164
|
})
|
|
114682
115165
|
);
|
|
114683
115166
|
} catch (error) {
|
|
114684
|
-
if (
|
|
115167
|
+
if (nestedPostgresSqlState17(error) === "42501") {
|
|
114685
115168
|
throw new HTTPException112(403, {
|
|
114686
115169
|
message: "organization membership is not active"
|
|
114687
115170
|
});
|
|
@@ -115321,7 +115804,7 @@ import {
|
|
|
115321
115804
|
organizationWorkspaceInScope as organizationWorkspaceInScope4,
|
|
115322
115805
|
hasPermission as hasPermission31,
|
|
115323
115806
|
requireAccessContext as requireAccessContext13,
|
|
115324
|
-
requireAccessGrantAuthorization as
|
|
115807
|
+
requireAccessGrantAuthorization as requireAccessGrantAuthorization39,
|
|
115325
115808
|
requireSessionAuthorizationListScope as requireSessionAuthorizationListScope3,
|
|
115326
115809
|
SessionAuthorizationDeniedError as SessionAuthorizationDeniedError16,
|
|
115327
115810
|
SessionAuthorizationUnavailableError as SessionAuthorizationUnavailableError14,
|
|
@@ -115474,7 +115957,7 @@ function registerOrganizationSessionRoutes(app, deps) {
|
|
|
115474
115957
|
async function listWorkspaceSessionPage(c, deps, input) {
|
|
115475
115958
|
let authorization;
|
|
115476
115959
|
try {
|
|
115477
|
-
authorization = await
|
|
115960
|
+
authorization = await requireAccessGrantAuthorization39(
|
|
115478
115961
|
c,
|
|
115479
115962
|
deps,
|
|
115480
115963
|
input.workspace.id,
|
|
@@ -115824,7 +116307,7 @@ import {
|
|
|
115824
116307
|
completeSelfServiceOrganizationSetup,
|
|
115825
116308
|
completeOrganizationUserSetup,
|
|
115826
116309
|
getSelfServiceOrganizationOnboardingState,
|
|
115827
|
-
nestedPostgresSqlState as
|
|
116310
|
+
nestedPostgresSqlState as nestedPostgresSqlState18,
|
|
115828
116311
|
preflightOrganizationUserSetup,
|
|
115829
116312
|
previewOrganizationUserSetup,
|
|
115830
116313
|
recordOrganizationSignupUseCase
|
|
@@ -115882,14 +116365,14 @@ function registerManagedOnboardingRoutes(app, deps, options = {}) {
|
|
|
115882
116365
|
useCase: parsed.data.useCase
|
|
115883
116366
|
}).catch((error) => {
|
|
115884
116367
|
console.warn("[api] could not record the signup use case", {
|
|
115885
|
-
sqlState:
|
|
116368
|
+
sqlState: nestedPostgresSqlState18(error) ?? null
|
|
115886
116369
|
});
|
|
115887
116370
|
});
|
|
115888
116371
|
}
|
|
115889
116372
|
return context.json(completed);
|
|
115890
116373
|
} catch (error) {
|
|
115891
116374
|
recordOrganizationSetupOutcome(deps.observability, "failed");
|
|
115892
|
-
const sqlState =
|
|
116375
|
+
const sqlState = nestedPostgresSqlState18(error);
|
|
115893
116376
|
if (sqlState === "22023") {
|
|
115894
116377
|
throw new HTTPException115(422, {
|
|
115895
116378
|
message: "invalid organization setup request"
|
|
@@ -115969,7 +116452,7 @@ function registerManagedOnboardingRoutes(app, deps, options = {}) {
|
|
|
115969
116452
|
)
|
|
115970
116453
|
);
|
|
115971
116454
|
} catch (error) {
|
|
115972
|
-
const sqlState =
|
|
116455
|
+
const sqlState = nestedPostgresSqlState18(error);
|
|
115973
116456
|
if (sqlState === "22023") {
|
|
115974
116457
|
throw new HTTPException115(422, {
|
|
115975
116458
|
message: "invalid account setup request"
|
|
@@ -116084,13 +116567,13 @@ import {
|
|
|
116084
116567
|
import {
|
|
116085
116568
|
issueManagedHumanUserResourceGrant,
|
|
116086
116569
|
listManagedHumanUserResourceAuthorities,
|
|
116087
|
-
requireAccessGrantAuthorization as
|
|
116570
|
+
requireAccessGrantAuthorization as requireAccessGrantAuthorization40,
|
|
116088
116571
|
revokeManagedHumanUserResourceGrant,
|
|
116089
116572
|
SessionAuthorizationDeniedError as SessionAuthorizationDeniedError17,
|
|
116090
116573
|
SessionAuthorizationUnavailableError as SessionAuthorizationUnavailableError15,
|
|
116091
116574
|
SessionTenancyManagedHumanRequiredError as SessionTenancyManagedHumanRequiredError3
|
|
116092
116575
|
} from "@opengeni/core";
|
|
116093
|
-
import { nestedPostgresSqlState as
|
|
116576
|
+
import { nestedPostgresSqlState as nestedPostgresSqlState19, SessionTenancyNotActivatedError as SessionTenancyNotActivatedError3 } from "@opengeni/db";
|
|
116094
116577
|
import { HTTPException as HTTPException116 } from "hono/http-exception";
|
|
116095
116578
|
import { z as z62 } from "zod";
|
|
116096
116579
|
async function body2(context, schema) {
|
|
@@ -116105,10 +116588,10 @@ function lifecycleError(error) {
|
|
|
116105
116588
|
if (error instanceof SessionAuthorizationUnavailableError15) {
|
|
116106
116589
|
throw new HTTPException116(503, { message: "session authorization unavailable" });
|
|
116107
116590
|
}
|
|
116108
|
-
if (
|
|
116591
|
+
if (nestedPostgresSqlState19(error) === "42501") {
|
|
116109
116592
|
throw new HTTPException116(403, { message: "user-resource authority denied" });
|
|
116110
116593
|
}
|
|
116111
|
-
if (
|
|
116594
|
+
if (nestedPostgresSqlState19(error) === "22023") {
|
|
116112
116595
|
throw new HTTPException116(422, { message: "invalid user-resource request" });
|
|
116113
116596
|
}
|
|
116114
116597
|
throw error;
|
|
@@ -116124,7 +116607,7 @@ function registerUserResourceAuthorityRoutes(app, deps) {
|
|
|
116124
116607
|
});
|
|
116125
116608
|
if (!query.success) throw new HTTPException116(422, { message: "explicit scope=user required" });
|
|
116126
116609
|
const workspaceId = context.req.param("workspaceId");
|
|
116127
|
-
const access = await
|
|
116610
|
+
const access = await requireAccessGrantAuthorization40(
|
|
116128
116611
|
context,
|
|
116129
116612
|
deps,
|
|
116130
116613
|
workspaceId,
|
|
@@ -116152,7 +116635,7 @@ function registerUserResourceAuthorityRoutes(app, deps) {
|
|
|
116152
116635
|
const authorityId = z62.string().uuid().safeParse(context.req.param("authorityId"));
|
|
116153
116636
|
if (!authorityId.success) throw new HTTPException116(422, { message: "invalid authority id" });
|
|
116154
116637
|
const request = await body2(context, IssueUserResourceGrantRequest);
|
|
116155
|
-
const access = await
|
|
116638
|
+
const access = await requireAccessGrantAuthorization40(
|
|
116156
116639
|
context,
|
|
116157
116640
|
deps,
|
|
116158
116641
|
workspaceId,
|
|
@@ -116182,7 +116665,7 @@ function registerUserResourceAuthorityRoutes(app, deps) {
|
|
|
116182
116665
|
const grantId = z62.string().uuid().safeParse(context.req.param("grantId"));
|
|
116183
116666
|
if (!grantId.success) throw new HTTPException116(422, { message: "invalid grant id" });
|
|
116184
116667
|
const workspaceId = context.req.param("workspaceId");
|
|
116185
|
-
const access = await
|
|
116668
|
+
const access = await requireAccessGrantAuthorization40(
|
|
116186
116669
|
context,
|
|
116187
116670
|
deps,
|
|
116188
116671
|
workspaceId,
|
|
@@ -116202,28 +116685,94 @@ function registerUserResourceAuthorityRoutes(app, deps) {
|
|
|
116202
116685
|
}
|
|
116203
116686
|
|
|
116204
116687
|
// src/transcription/service.ts
|
|
116205
|
-
import {
|
|
116688
|
+
import {
|
|
116689
|
+
resolveVoiceInputProviderRegistry,
|
|
116690
|
+
voiceInputCreditBillingActive
|
|
116691
|
+
} from "@opengeni/config";
|
|
116206
116692
|
import { VOICE_INPUT_ACCEPTED_MIME_TYPES } from "@opengeni/contracts";
|
|
116693
|
+
import { voiceTranscriptionSettlementKeys } from "@opengeni/db";
|
|
116207
116694
|
import {
|
|
116695
|
+
createVoiceInputBilling,
|
|
116208
116696
|
filenameForMimeType,
|
|
116209
116697
|
isAcceptedMimeType as isAcceptedMimeType2,
|
|
116210
116698
|
normalizeMimeType as normalizeMimeType2,
|
|
116211
116699
|
TRANSCRIPTION_PROVIDER_REQUEST_TIMEOUT_MILLISECONDS as TRANSCRIPTION_PROVIDER_REQUEST_TIMEOUT_MILLISECONDS2,
|
|
116212
|
-
TranscriptionServiceError as
|
|
116700
|
+
TranscriptionServiceError as TranscriptionServiceError7
|
|
116213
116701
|
} from "@opengeni/core";
|
|
116214
116702
|
|
|
116215
|
-
// src/transcription/
|
|
116703
|
+
// src/transcription/normalize.ts
|
|
116216
116704
|
import { TranscriptionServiceError as TranscriptionServiceError3 } from "@opengeni/core";
|
|
116705
|
+
function createFfmpegAudioNormalizer(input) {
|
|
116706
|
+
const segmenter = createFfmpegTranscriptionSegmenter({ ffmpegPath: input.ffmpegPath });
|
|
116707
|
+
return async ({ audio, mimeType, maxDurationSeconds, signal }) => {
|
|
116708
|
+
if (!Number.isFinite(maxDurationSeconds) || maxDurationSeconds <= 0) {
|
|
116709
|
+
throw new TranscriptionServiceError3({
|
|
116710
|
+
code: "unavailable",
|
|
116711
|
+
message: "Voice input duration limit is not configured."
|
|
116712
|
+
});
|
|
116713
|
+
}
|
|
116714
|
+
const decodeSeconds = Math.ceil(maxDurationSeconds) + 1;
|
|
116715
|
+
const segmentSeconds = decodeSeconds + 1;
|
|
116716
|
+
let normalized;
|
|
116717
|
+
try {
|
|
116718
|
+
for await (const segment of segmenter.segment({
|
|
116719
|
+
sourceMimeType: mimeType,
|
|
116720
|
+
chunks: async function* () {
|
|
116721
|
+
yield audio;
|
|
116722
|
+
}(),
|
|
116723
|
+
providerSegmentSeconds: segmentSeconds,
|
|
116724
|
+
totalDurationMilliseconds: segmentSeconds * 1e3,
|
|
116725
|
+
maxDecodeSeconds: decodeSeconds,
|
|
116726
|
+
...signal ? { signal } : {}
|
|
116727
|
+
})) {
|
|
116728
|
+
if (normalized) {
|
|
116729
|
+
throw new TranscriptionServiceError3({ code: "too_large", message: "Audio is too long." });
|
|
116730
|
+
}
|
|
116731
|
+
normalized = segment.bytes;
|
|
116732
|
+
}
|
|
116733
|
+
} catch (error) {
|
|
116734
|
+
if (error instanceof TranscriptionSegmenterError) {
|
|
116735
|
+
throw new TranscriptionServiceError3({
|
|
116736
|
+
code: error.code === "unknown" ? "invalid_audio" : error.code,
|
|
116737
|
+
message: "Audio could not be prepared for transcription.",
|
|
116738
|
+
retryable: error.retryable
|
|
116739
|
+
});
|
|
116740
|
+
}
|
|
116741
|
+
throw error;
|
|
116742
|
+
}
|
|
116743
|
+
const durationSeconds = normalized ? wavDurationSeconds(normalized) : null;
|
|
116744
|
+
if (!normalized || durationSeconds === null || !(durationSeconds > 0)) {
|
|
116745
|
+
throw new TranscriptionServiceError3({
|
|
116746
|
+
code: "invalid_audio",
|
|
116747
|
+
message: "Audio could not be decoded."
|
|
116748
|
+
});
|
|
116749
|
+
}
|
|
116750
|
+
if (durationSeconds > decodeSeconds - 0.02) {
|
|
116751
|
+
throw new TranscriptionServiceError3({ code: "too_large", message: "Audio is too long." });
|
|
116752
|
+
}
|
|
116753
|
+
return { bytes: normalized, durationSeconds };
|
|
116754
|
+
};
|
|
116755
|
+
}
|
|
116756
|
+
|
|
116757
|
+
// src/transcription/providers/openai.ts
|
|
116758
|
+
import {
|
|
116759
|
+
voiceInputModelReportsDurationOnly
|
|
116760
|
+
} from "@opengeni/config";
|
|
116761
|
+
import { TranscriptionServiceError as TranscriptionServiceError4 } from "@opengeni/core";
|
|
116217
116762
|
function createOpenAiTranscriptionProvider(input) {
|
|
116218
116763
|
const fetchImpl = input.fetch ?? fetch;
|
|
116219
116764
|
return {
|
|
116220
116765
|
id: "openai",
|
|
116221
116766
|
supportsServerDeadline: true,
|
|
116767
|
+
deploymentFunded: { model: input.model, pricing: input.pricing ?? null },
|
|
116222
116768
|
available: () => true,
|
|
116223
116769
|
async transcribe({ audio, mimeType, filename, requestId, signal }) {
|
|
116224
116770
|
const form = new FormData();
|
|
116225
116771
|
form.append("file", audioBlob(audio, mimeType), filename);
|
|
116226
116772
|
form.append("model", input.model);
|
|
116773
|
+
if (voiceInputModelReportsDurationOnly(input.model)) {
|
|
116774
|
+
form.append("response_format", "verbose_json");
|
|
116775
|
+
}
|
|
116227
116776
|
let response;
|
|
116228
116777
|
try {
|
|
116229
116778
|
response = await fetchImpl(`${input.baseUrl}/audio/transcriptions`, {
|
|
@@ -116241,50 +116790,89 @@ function createOpenAiTranscriptionProvider(input) {
|
|
|
116241
116790
|
}
|
|
116242
116791
|
if (!response.ok) throw responseError(response.status);
|
|
116243
116792
|
const body3 = await response.json().catch(() => null);
|
|
116244
|
-
|
|
116245
|
-
|
|
116793
|
+
const parsed = parseTranscriptionResponseBody(body3);
|
|
116794
|
+
if (!parsed) {
|
|
116795
|
+
throw new TranscriptionServiceError4({
|
|
116246
116796
|
code: "provider",
|
|
116247
116797
|
message: "Invalid transcription response."
|
|
116248
116798
|
});
|
|
116249
116799
|
}
|
|
116250
|
-
return
|
|
116251
|
-
text: body3.text,
|
|
116252
|
-
languages: typeof body3.language === "string" && body3.language ? [body3.language] : []
|
|
116253
|
-
};
|
|
116800
|
+
return parsed;
|
|
116254
116801
|
}
|
|
116255
116802
|
};
|
|
116256
116803
|
}
|
|
116804
|
+
function parseTranscriptionResponseBody(body3) {
|
|
116805
|
+
if (!body3 || typeof body3 !== "object") return null;
|
|
116806
|
+
const record6 = body3;
|
|
116807
|
+
if (typeof record6.text !== "string") return null;
|
|
116808
|
+
const languages = typeof record6.language === "string" && record6.language ? [record6.language] : Array.isArray(record6.languages) ? record6.languages.flatMap((language) => {
|
|
116809
|
+
const code = typeof language === "string" ? language : language?.code;
|
|
116810
|
+
return typeof code === "string" && code ? [code] : [];
|
|
116811
|
+
}) : [];
|
|
116812
|
+
return { text: record6.text, languages, usage: parseTranscriptionUsage(record6) };
|
|
116813
|
+
}
|
|
116814
|
+
function parseTranscriptionUsage(body3) {
|
|
116815
|
+
const usage = body3.usage;
|
|
116816
|
+
if (usage && typeof usage === "object") {
|
|
116817
|
+
const record6 = usage;
|
|
116818
|
+
if (record6.type === "tokens") {
|
|
116819
|
+
const inputTokens = count(record6.input_tokens);
|
|
116820
|
+
const outputTokens = count(record6.output_tokens);
|
|
116821
|
+
if (inputTokens !== null && outputTokens !== null) {
|
|
116822
|
+
const details = record6.input_token_details && typeof record6.input_token_details === "object" ? record6.input_token_details : {};
|
|
116823
|
+
return {
|
|
116824
|
+
kind: "tokens",
|
|
116825
|
+
inputTokens,
|
|
116826
|
+
audioInputTokens: count(details.audio_tokens) ?? 0,
|
|
116827
|
+
textInputTokens: count(details.text_tokens) ?? 0,
|
|
116828
|
+
outputTokens
|
|
116829
|
+
};
|
|
116830
|
+
}
|
|
116831
|
+
}
|
|
116832
|
+
if (record6.type === "duration" && seconds(record6.seconds) !== null) {
|
|
116833
|
+
return { kind: "duration", seconds: seconds(record6.seconds) };
|
|
116834
|
+
}
|
|
116835
|
+
}
|
|
116836
|
+
const duration = seconds(body3.duration);
|
|
116837
|
+
return duration === null ? null : { kind: "duration", seconds: duration };
|
|
116838
|
+
}
|
|
116839
|
+
function count(value) {
|
|
116840
|
+
return typeof value === "number" && Number.isSafeInteger(value) && value >= 0 ? value : null;
|
|
116841
|
+
}
|
|
116842
|
+
function seconds(value) {
|
|
116843
|
+
return typeof value === "number" && Number.isFinite(value) && value >= 0 && value < 86400 ? value : null;
|
|
116844
|
+
}
|
|
116257
116845
|
function audioBlob(audio, mimeType) {
|
|
116258
116846
|
return new Blob([Uint8Array.from(audio).buffer], { type: mimeType });
|
|
116259
116847
|
}
|
|
116260
116848
|
function responseError(status) {
|
|
116261
116849
|
if (status === 401 || status === 403) {
|
|
116262
|
-
return new
|
|
116850
|
+
return new TranscriptionServiceError4({
|
|
116263
116851
|
fallbackSafe: true,
|
|
116264
116852
|
code: "unavailable",
|
|
116265
116853
|
message: "Transcription is unavailable."
|
|
116266
116854
|
});
|
|
116267
116855
|
}
|
|
116268
116856
|
if (status === 413) {
|
|
116269
|
-
return new
|
|
116857
|
+
return new TranscriptionServiceError4({
|
|
116270
116858
|
code: "too_large",
|
|
116271
116859
|
message: "Audio is too large."
|
|
116272
116860
|
});
|
|
116273
116861
|
}
|
|
116274
116862
|
if (status === 400 || status === 422) {
|
|
116275
|
-
return new
|
|
116863
|
+
return new TranscriptionServiceError4({
|
|
116276
116864
|
code: "invalid_audio",
|
|
116277
116865
|
message: "Audio could not be transcribed."
|
|
116278
116866
|
});
|
|
116279
116867
|
}
|
|
116280
116868
|
if (status === 408 || status === 504) {
|
|
116281
|
-
return new
|
|
116869
|
+
return new TranscriptionServiceError4({
|
|
116282
116870
|
code: "timeout",
|
|
116283
116871
|
message: "Transcription timed out.",
|
|
116284
116872
|
retryable: true
|
|
116285
116873
|
});
|
|
116286
116874
|
}
|
|
116287
|
-
return new
|
|
116875
|
+
return new TranscriptionServiceError4({
|
|
116288
116876
|
code: status >= 500 ? "unavailable" : "provider",
|
|
116289
116877
|
message: "Transcription provider failed.",
|
|
116290
116878
|
retryable: status >= 500
|
|
@@ -116292,29 +116880,105 @@ function responseError(status) {
|
|
|
116292
116880
|
}
|
|
116293
116881
|
function fetchError(error) {
|
|
116294
116882
|
if (error instanceof DOMException && error.name === "AbortError") {
|
|
116295
|
-
return new
|
|
116883
|
+
return new TranscriptionServiceError4({
|
|
116296
116884
|
code: "cancelled",
|
|
116297
116885
|
message: "Transcription was cancelled."
|
|
116298
116886
|
});
|
|
116299
116887
|
}
|
|
116300
|
-
return new
|
|
116888
|
+
return new TranscriptionServiceError4({
|
|
116301
116889
|
code: "network",
|
|
116302
116890
|
message: "Transcription provider is unreachable.",
|
|
116303
116891
|
retryable: true
|
|
116304
116892
|
});
|
|
116305
116893
|
}
|
|
116306
116894
|
|
|
116895
|
+
// src/transcription/providers/azure-mai.ts
|
|
116896
|
+
function createMaiTranscriptionProvider(input) {
|
|
116897
|
+
const fetchImpl = input.fetch ?? fetch;
|
|
116898
|
+
const normalize = createFfmpegAudioNormalizer({ ffmpegPath: input.ffmpegPath });
|
|
116899
|
+
return {
|
|
116900
|
+
id: "azure-mai",
|
|
116901
|
+
supportsServerDeadline: true,
|
|
116902
|
+
deploymentFunded: { model: input.model, pricing: input.pricing ?? null },
|
|
116903
|
+
available: () => Boolean(input.apiKey),
|
|
116904
|
+
async transcribe({ audio, mimeType, filename, requestId, signal }) {
|
|
116905
|
+
let bytes = audio;
|
|
116906
|
+
let contentType = mimeType;
|
|
116907
|
+
let name = filename;
|
|
116908
|
+
if (!["audio/wav", "audio/x-wav", "audio/mpeg", "audio/flac"].includes(mimeType)) {
|
|
116909
|
+
const normalized = await normalize({
|
|
116910
|
+
audio,
|
|
116911
|
+
mimeType,
|
|
116912
|
+
maxDurationSeconds: 600,
|
|
116913
|
+
...signal ? { signal } : {}
|
|
116914
|
+
});
|
|
116915
|
+
bytes = normalized.bytes;
|
|
116916
|
+
contentType = "audio/wav";
|
|
116917
|
+
name = "audio.wav";
|
|
116918
|
+
}
|
|
116919
|
+
const form = new FormData();
|
|
116920
|
+
form.append("audio", new Blob([Uint8Array.from(bytes).buffer], { type: contentType }), name);
|
|
116921
|
+
form.append(
|
|
116922
|
+
"definition",
|
|
116923
|
+
JSON.stringify({ enhancedMode: { enabled: true, model: input.model } })
|
|
116924
|
+
);
|
|
116925
|
+
let response;
|
|
116926
|
+
try {
|
|
116927
|
+
response = await fetchImpl(
|
|
116928
|
+
`${input.endpoint.replace(/\/+$/, "")}/speechtotext/transcriptions:transcribe?api-version=${encodeURIComponent(input.apiVersion)}`,
|
|
116929
|
+
{
|
|
116930
|
+
method: "POST",
|
|
116931
|
+
headers: {
|
|
116932
|
+
"Ocp-Apim-Subscription-Key": input.apiKey,
|
|
116933
|
+
"x-opengeni-request-id": requestId
|
|
116934
|
+
},
|
|
116935
|
+
body: form,
|
|
116936
|
+
...signal ? { signal } : {}
|
|
116937
|
+
}
|
|
116938
|
+
);
|
|
116939
|
+
} catch (error) {
|
|
116940
|
+
throw fetchError(error);
|
|
116941
|
+
}
|
|
116942
|
+
if (!response.ok) throw responseError(response.status);
|
|
116943
|
+
const body3 = await response.json().catch(() => null);
|
|
116944
|
+
if (!body3 || !Array.isArray(body3.combinedPhrases) || !body3.combinedPhrases.every(
|
|
116945
|
+
(phrase) => typeof phrase === "object" && phrase !== null && "text" in phrase && typeof phrase.text === "string"
|
|
116946
|
+
)) {
|
|
116947
|
+
throw responseError(502);
|
|
116948
|
+
}
|
|
116949
|
+
return {
|
|
116950
|
+
usage: typeof body3.durationMilliseconds === "number" && Number.isFinite(body3.durationMilliseconds) && body3.durationMilliseconds >= 0 ? { kind: "duration", seconds: body3.durationMilliseconds / 1e3 } : null,
|
|
116951
|
+
text: body3.combinedPhrases.map((phrase) => phrase.text).join("\n"),
|
|
116952
|
+
languages: [
|
|
116953
|
+
...new Set(
|
|
116954
|
+
(Array.isArray(body3.phrases) ? body3.phrases : []).flatMap(
|
|
116955
|
+
(phrase) => typeof phrase?.locale === "string" && phrase.locale ? [phrase.locale] : []
|
|
116956
|
+
)
|
|
116957
|
+
)
|
|
116958
|
+
]
|
|
116959
|
+
};
|
|
116960
|
+
}
|
|
116961
|
+
};
|
|
116962
|
+
}
|
|
116963
|
+
|
|
116307
116964
|
// src/transcription/providers/azure-openai.ts
|
|
116965
|
+
import { voiceInputModelReportsDurationOnly as voiceInputModelReportsDurationOnly2 } from "@opengeni/config";
|
|
116308
116966
|
function createAzureOpenAiTranscriptionProvider(input) {
|
|
116309
116967
|
const fetchImpl = input.fetch ?? fetch;
|
|
116310
|
-
const
|
|
116968
|
+
const model = input.model ?? input.deployment;
|
|
116969
|
+
const endpoint = input.endpoint.replace(/\/+$/, "");
|
|
116970
|
+
const url = `${endpoint}/openai/deployments/${encodeURIComponent(input.deployment)}/audio/transcriptions?api-version=${encodeURIComponent(input.apiVersion)}`;
|
|
116311
116971
|
return {
|
|
116312
116972
|
id: "azure-openai",
|
|
116313
116973
|
supportsServerDeadline: true,
|
|
116974
|
+
deploymentFunded: { model, pricing: input.pricing ?? null },
|
|
116314
116975
|
available: () => Boolean(input.apiKey || input.adToken),
|
|
116315
116976
|
async transcribe({ audio, mimeType, filename, requestId, signal }) {
|
|
116316
116977
|
const form = new FormData();
|
|
116317
116978
|
form.append("file", new Blob([Uint8Array.from(audio).buffer], { type: mimeType }), filename);
|
|
116979
|
+
if (voiceInputModelReportsDurationOnly2(model)) {
|
|
116980
|
+
form.append("response_format", "verbose_json");
|
|
116981
|
+
}
|
|
116318
116982
|
const headers = input.apiKey ? { "api-key": input.apiKey, "x-opengeni-request-id": requestId } : { Authorization: `Bearer ${input.adToken}`, "x-opengeni-request-id": requestId };
|
|
116319
116983
|
let response;
|
|
116320
116984
|
try {
|
|
@@ -116329,13 +116993,11 @@ function createAzureOpenAiTranscriptionProvider(input) {
|
|
|
116329
116993
|
}
|
|
116330
116994
|
if (!response.ok) throw responseError(response.status);
|
|
116331
116995
|
const body3 = await response.json().catch(() => null);
|
|
116332
|
-
|
|
116996
|
+
const parsed = parseTranscriptionResponseBody(body3);
|
|
116997
|
+
if (!parsed) {
|
|
116333
116998
|
throw responseError(502);
|
|
116334
116999
|
}
|
|
116335
|
-
return
|
|
116336
|
-
text: body3.text,
|
|
116337
|
-
languages: typeof body3.language === "string" && body3.language ? [body3.language] : []
|
|
116338
|
-
};
|
|
117000
|
+
return parsed;
|
|
116339
117001
|
}
|
|
116340
117002
|
};
|
|
116341
117003
|
}
|
|
@@ -116343,7 +117005,7 @@ function createAzureOpenAiTranscriptionProvider(input) {
|
|
|
116343
117005
|
// src/transcription/providers/codex-subscription.ts
|
|
116344
117006
|
import { CODEX_CLIENT_VERSION as CODEX_CLIENT_VERSION4, CODEX_ORIGINATOR } from "@opengeni/codex/constants";
|
|
116345
117007
|
import {
|
|
116346
|
-
TranscriptionServiceError as
|
|
117008
|
+
TranscriptionServiceError as TranscriptionServiceError5
|
|
116347
117009
|
} from "@opengeni/core";
|
|
116348
117010
|
import { buildCodexTokenResolver as buildCodexTokenResolver4, listCodexAccountStatuses as listCodexAccountStatuses3 } from "@opengeni/db";
|
|
116349
117011
|
var TRANSCRIBE_URL = "https://chatgpt.com/backend-api/transcribe";
|
|
@@ -116369,7 +117031,7 @@ function createCodexSubscriptionTranscriptionProvider(input) {
|
|
|
116369
117031
|
(candidate) => candidate.isActive && candidate.status === "active"
|
|
116370
117032
|
);
|
|
116371
117033
|
if (!account) {
|
|
116372
|
-
throw new
|
|
117034
|
+
throw new TranscriptionServiceError5({
|
|
116373
117035
|
fallbackSafe: true,
|
|
116374
117036
|
code: "unavailable",
|
|
116375
117037
|
message: "Transcription is unavailable."
|
|
@@ -116380,7 +117042,7 @@ function createCodexSubscriptionTranscriptionProvider(input) {
|
|
|
116380
117042
|
try {
|
|
116381
117043
|
token = await resolver.getToken();
|
|
116382
117044
|
} catch {
|
|
116383
|
-
throw new
|
|
117045
|
+
throw new TranscriptionServiceError5({
|
|
116384
117046
|
fallbackSafe: true,
|
|
116385
117047
|
code: "unavailable",
|
|
116386
117048
|
message: "Transcription is unavailable."
|
|
@@ -116431,7 +117093,7 @@ function createCodexSubscriptionTranscriptionProvider(input) {
|
|
|
116431
117093
|
|
|
116432
117094
|
// src/transcription/providers/xai-subscription.ts
|
|
116433
117095
|
import {
|
|
116434
|
-
TranscriptionServiceError as
|
|
117096
|
+
TranscriptionServiceError as TranscriptionServiceError6
|
|
116435
117097
|
} from "@opengeni/core";
|
|
116436
117098
|
import { workspaceXaiSubscriptionActive as workspaceXaiSubscriptionActive3 } from "@opengeni/db";
|
|
116437
117099
|
import {
|
|
@@ -116480,7 +117142,7 @@ function createXaiSubscriptionTranscriptionProvider(input) {
|
|
|
116480
117142
|
...input.fetch ? { fetch: input.fetch } : {}
|
|
116481
117143
|
});
|
|
116482
117144
|
} catch {
|
|
116483
|
-
throw new
|
|
117145
|
+
throw new TranscriptionServiceError6({
|
|
116484
117146
|
code: "unavailable",
|
|
116485
117147
|
fallbackSafe: true,
|
|
116486
117148
|
message: "Transcription is unavailable."
|
|
@@ -116511,7 +117173,7 @@ function createXaiSubscriptionTranscriptionProvider(input) {
|
|
|
116511
117173
|
try {
|
|
116512
117174
|
return await (refresh ? auth.context.refresh() : auth.context.getToken());
|
|
116513
117175
|
} catch {
|
|
116514
|
-
throw new
|
|
117176
|
+
throw new TranscriptionServiceError6({
|
|
116515
117177
|
code: "unavailable",
|
|
116516
117178
|
message: "Reconnect the SuperGrok account to use transcription.",
|
|
116517
117179
|
fallbackSafe: true
|
|
@@ -116528,7 +117190,7 @@ function createXaiSubscriptionTranscriptionProvider(input) {
|
|
|
116528
117190
|
response = await request(token.accessToken);
|
|
116529
117191
|
}
|
|
116530
117192
|
} catch (error) {
|
|
116531
|
-
if (error instanceof
|
|
117193
|
+
if (error instanceof TranscriptionServiceError6) throw error;
|
|
116532
117194
|
throw fetchError(error);
|
|
116533
117195
|
}
|
|
116534
117196
|
if (!response.ok) throw responseError(response.status);
|
|
@@ -116581,6 +117243,12 @@ function createTranscriptionService(input) {
|
|
|
116581
117243
|
...config,
|
|
116582
117244
|
...input.fetch ? { fetch: input.fetch } : {}
|
|
116583
117245
|
});
|
|
117246
|
+
case "azure-mai":
|
|
117247
|
+
return createMaiTranscriptionProvider({
|
|
117248
|
+
...config,
|
|
117249
|
+
ffmpegPath: input.settings.voiceInputFfmpegPath,
|
|
117250
|
+
...input.fetch ? { fetch: input.fetch } : {}
|
|
117251
|
+
});
|
|
116584
117252
|
case "azure-openai":
|
|
116585
117253
|
return createAzureOpenAiTranscriptionProvider({
|
|
116586
117254
|
...config,
|
|
@@ -116611,32 +117279,113 @@ function createTranscriptionService(input) {
|
|
|
116611
117279
|
};
|
|
116612
117280
|
const providerRequestTimeoutMilliseconds = input.providerRequestTimeoutMilliseconds ?? TRANSCRIPTION_PROVIDER_REQUEST_TIMEOUT_MILLISECONDS2;
|
|
116613
117281
|
const now = input.now ?? (() => /* @__PURE__ */ new Date());
|
|
117282
|
+
const log = input.log ?? ((message, attributes) => {
|
|
117283
|
+
console.error(message, attributes);
|
|
117284
|
+
});
|
|
117285
|
+
const billing = input.billing ?? createVoiceInputBilling({
|
|
117286
|
+
db: input.db,
|
|
117287
|
+
settings: input.settings,
|
|
117288
|
+
onReconcileError: (error, scope) => {
|
|
117289
|
+
log("Voice input charge reconciliation failed", {
|
|
117290
|
+
...scope,
|
|
117291
|
+
errorClass: error instanceof Error ? error.name : "UnknownError",
|
|
117292
|
+
error: boundedErrorMessage(error)
|
|
117293
|
+
});
|
|
117294
|
+
}
|
|
117295
|
+
});
|
|
117296
|
+
let normalizer = input.normalizeAudio;
|
|
117297
|
+
const normalizeAudio = (request) => {
|
|
117298
|
+
normalizer ??= createFfmpegAudioNormalizer({ ffmpegPath: input.settings.voiceInputFfmpegPath });
|
|
117299
|
+
return normalizer(request);
|
|
117300
|
+
};
|
|
117301
|
+
const retryDelays = input.settlementRetryDelaysMilliseconds ?? [2e3, 15e3, 6e4];
|
|
117302
|
+
const settleWithoutFailing = async (settlement) => {
|
|
117303
|
+
try {
|
|
117304
|
+
return (await billing.settle(settlement)).creditCostMicros;
|
|
117305
|
+
} catch (error) {
|
|
117306
|
+
reportUnsettled(settlement, error, 0, retryDelays.length > 0 && !deterministic(error));
|
|
117307
|
+
if (!deterministic(error)) scheduleSettlementRetry(settlement, 0);
|
|
117308
|
+
return 0;
|
|
117309
|
+
}
|
|
117310
|
+
};
|
|
117311
|
+
const deterministic = (error) => error instanceof TranscriptionServiceError7;
|
|
117312
|
+
const scheduleSettlementRetry = (settlement, attempt) => {
|
|
117313
|
+
const delay = retryDelays[attempt];
|
|
117314
|
+
if (delay === void 0) return;
|
|
117315
|
+
const timer = setTimeout(() => {
|
|
117316
|
+
void billing.settle(settlement).then(
|
|
117317
|
+
() => {
|
|
117318
|
+
log("Voice input charge settled on retry", {
|
|
117319
|
+
...settlementAttributes(settlement),
|
|
117320
|
+
attempt: attempt + 1
|
|
117321
|
+
});
|
|
117322
|
+
},
|
|
117323
|
+
(error) => {
|
|
117324
|
+
const more = attempt + 1 < retryDelays.length;
|
|
117325
|
+
reportUnsettled(settlement, error, attempt + 1, more);
|
|
117326
|
+
if (more) scheduleSettlementRetry(settlement, attempt + 1);
|
|
117327
|
+
}
|
|
117328
|
+
);
|
|
117329
|
+
}, delay);
|
|
117330
|
+
timer.unref?.();
|
|
117331
|
+
};
|
|
117332
|
+
const reportUnsettled = (settlement, error, attempt, willRetry) => {
|
|
117333
|
+
log(
|
|
117334
|
+
willRetry ? "Voice input charge is unsettled after provider success; retrying" : "Voice input charge is UNSETTLED after provider success; manual reconciliation needed unless its usage receipt committed",
|
|
117335
|
+
{
|
|
117336
|
+
...settlementAttributes(settlement),
|
|
117337
|
+
attempt,
|
|
117338
|
+
willRetry,
|
|
117339
|
+
errorClass: error instanceof Error ? error.name : "UnknownError",
|
|
117340
|
+
error: boundedErrorMessage(error)
|
|
117341
|
+
}
|
|
117342
|
+
);
|
|
117343
|
+
};
|
|
117344
|
+
const creditBillingActive = voiceInputCreditBillingActive(input.settings);
|
|
117345
|
+
const chargeable = (provider) => creditBillingActive && provider.deploymentFunded !== void 0;
|
|
116614
117346
|
return {
|
|
116615
117347
|
limits: () => limits,
|
|
116616
117348
|
async available(context) {
|
|
116617
|
-
return (
|
|
116618
|
-
|
|
117349
|
+
return Boolean(
|
|
117350
|
+
await firstAvailable(orderedProviders(providers, context ?? {}), context ?? {})
|
|
116619
117351
|
);
|
|
116620
117352
|
},
|
|
117353
|
+
async availableProviderIds(context) {
|
|
117354
|
+
const available = await Promise.all(
|
|
117355
|
+
providers.map(async (provider) => {
|
|
117356
|
+
try {
|
|
117357
|
+
return await provider.available(context) ? provider.id : null;
|
|
117358
|
+
} catch {
|
|
117359
|
+
return null;
|
|
117360
|
+
}
|
|
117361
|
+
})
|
|
117362
|
+
);
|
|
117363
|
+
return available.filter((id) => id !== null);
|
|
117364
|
+
},
|
|
116621
117365
|
async selectProvider(context) {
|
|
116622
117366
|
return (await firstAvailable(orderedProviders(providers, context), context))?.id ?? null;
|
|
116623
117367
|
},
|
|
117368
|
+
async admit({ providerId, accountId, workspaceId, attribution }) {
|
|
117369
|
+
const provider = providers.find((candidate) => candidate.id === providerId);
|
|
117370
|
+
if (!provider || !chargeable(provider)) return;
|
|
117371
|
+
await billing.admit({ accountId, workspaceId, attribution });
|
|
117372
|
+
},
|
|
116624
117373
|
async transcribe(request) {
|
|
116625
117374
|
const mimeType = normalizeMimeType2(request.mimeType);
|
|
116626
117375
|
if (!isAcceptedMimeType2(mimeType, limits.acceptedMimeTypes)) {
|
|
116627
|
-
throw new
|
|
117376
|
+
throw new TranscriptionServiceError7({
|
|
116628
117377
|
code: "not_supported",
|
|
116629
117378
|
message: "Unsupported audio format."
|
|
116630
117379
|
});
|
|
116631
117380
|
}
|
|
116632
117381
|
if (request.audio.byteLength > limits.maxSizeBytes) {
|
|
116633
|
-
throw new
|
|
117382
|
+
throw new TranscriptionServiceError7({
|
|
116634
117383
|
code: "too_large",
|
|
116635
117384
|
message: "Audio is too large."
|
|
116636
117385
|
});
|
|
116637
117386
|
}
|
|
116638
117387
|
if (request.durationSeconds !== void 0 && (!Number.isFinite(request.durationSeconds) || request.durationSeconds < 0 || request.durationSeconds > limits.maxDurationSeconds)) {
|
|
116639
|
-
throw new
|
|
117388
|
+
throw new TranscriptionServiceError7({
|
|
116640
117389
|
code: "invalid_audio",
|
|
116641
117390
|
message: "Invalid audio duration."
|
|
116642
117391
|
});
|
|
@@ -116646,22 +117395,51 @@ function createTranscriptionService(input) {
|
|
|
116646
117395
|
subjectId: request.subjectId
|
|
116647
117396
|
}) : await firstAvailable(orderedProviders(providers, request), request);
|
|
116648
117397
|
if (!provider) {
|
|
116649
|
-
throw new
|
|
117398
|
+
throw new TranscriptionServiceError7({
|
|
116650
117399
|
fallbackSafe: true,
|
|
116651
117400
|
code: "unavailable",
|
|
116652
117401
|
message: "Transcription is unavailable."
|
|
116653
117402
|
});
|
|
116654
117403
|
}
|
|
116655
117404
|
if (provider.supportsServerDeadline !== true) {
|
|
116656
|
-
throw new
|
|
117405
|
+
throw new TranscriptionServiceError7({
|
|
116657
117406
|
code: "unavailable",
|
|
116658
117407
|
message: "Transcription provider does not support bounded requests."
|
|
116659
117408
|
});
|
|
116660
117409
|
}
|
|
117410
|
+
if (chargeable(provider)) {
|
|
117411
|
+
if (!provider.deploymentFunded?.pricing || !request.billing) {
|
|
117412
|
+
throw new TranscriptionServiceError7({
|
|
117413
|
+
code: "unavailable",
|
|
117414
|
+
message: "Transcription billing is not configured."
|
|
117415
|
+
});
|
|
117416
|
+
}
|
|
117417
|
+
await billing.admit({
|
|
117418
|
+
accountId: request.accountId,
|
|
117419
|
+
workspaceId: request.workspaceId,
|
|
117420
|
+
attribution: request.billing.attribution
|
|
117421
|
+
});
|
|
117422
|
+
}
|
|
117423
|
+
let audio = request.audio;
|
|
117424
|
+
let providerMimeType = mimeType;
|
|
117425
|
+
let billingContext = request.billing;
|
|
117426
|
+
let audioSeconds = request.durationSeconds ?? 0;
|
|
117427
|
+
if (chargeable(provider) && billingContext?.trustedDurationSeconds === void 0) {
|
|
117428
|
+
const normalized = await normalizeAudio({
|
|
117429
|
+
audio,
|
|
117430
|
+
mimeType,
|
|
117431
|
+
maxDurationSeconds: limits.maxDurationSeconds,
|
|
117432
|
+
...request.signal ? { signal: request.signal } : {}
|
|
117433
|
+
});
|
|
117434
|
+
audio = normalized.bytes;
|
|
117435
|
+
providerMimeType = "audio/wav";
|
|
117436
|
+
audioSeconds = normalized.durationSeconds;
|
|
117437
|
+
billingContext = { ...billingContext, trustedDurationSeconds: normalized.durationSeconds };
|
|
117438
|
+
}
|
|
116661
117439
|
const startedAt = performance.now();
|
|
116662
117440
|
const remainingMilliseconds = request.providerDeadlineAt ? remainingTranscriptionProviderRequestMilliseconds(request.providerDeadlineAt, now()) : providerRequestTimeoutMilliseconds;
|
|
116663
117441
|
if (request.providerDeadlineAt && (!Number.isFinite(remainingMilliseconds) || remainingMilliseconds <= 0)) {
|
|
116664
|
-
throw new
|
|
117442
|
+
throw new TranscriptionServiceError7({
|
|
116665
117443
|
code: "timeout",
|
|
116666
117444
|
message: "Transcription provider deadline expired.",
|
|
116667
117445
|
retryable: true
|
|
@@ -116671,9 +117449,9 @@ function createTranscriptionService(input) {
|
|
|
116671
117449
|
let result;
|
|
116672
117450
|
try {
|
|
116673
117451
|
result = await provider.transcribe({
|
|
116674
|
-
audio
|
|
116675
|
-
mimeType,
|
|
116676
|
-
filename: filenameForMimeType(
|
|
117452
|
+
audio,
|
|
117453
|
+
mimeType: providerMimeType,
|
|
117454
|
+
filename: filenameForMimeType(providerMimeType),
|
|
116677
117455
|
workspaceId: request.workspaceId,
|
|
116678
117456
|
accountId: request.accountId,
|
|
116679
117457
|
subjectId: request.subjectId,
|
|
@@ -116681,7 +117459,7 @@ function createTranscriptionService(input) {
|
|
|
116681
117459
|
signal: deadline.signal
|
|
116682
117460
|
});
|
|
116683
117461
|
if (deadline.timedOut && !request.signal?.aborted) {
|
|
116684
|
-
throw new
|
|
117462
|
+
throw new TranscriptionServiceError7({
|
|
116685
117463
|
code: "timeout",
|
|
116686
117464
|
message: "Transcription provider timed out.",
|
|
116687
117465
|
retryable: true
|
|
@@ -116689,13 +117467,13 @@ function createTranscriptionService(input) {
|
|
|
116689
117467
|
}
|
|
116690
117468
|
} catch (error) {
|
|
116691
117469
|
if (deadline.timedOut && !request.signal?.aborted) {
|
|
116692
|
-
throw new
|
|
117470
|
+
throw new TranscriptionServiceError7({
|
|
116693
117471
|
code: "timeout",
|
|
116694
117472
|
message: "Transcription provider timed out.",
|
|
116695
117473
|
retryable: true
|
|
116696
117474
|
});
|
|
116697
117475
|
}
|
|
116698
|
-
if (error instanceof
|
|
117476
|
+
if (error instanceof TranscriptionServiceError7 && error.fallbackSafe && !request.providerId && request.fallbackEnabled !== false && !request.signal?.aborted) {
|
|
116699
117477
|
const excludedProviders = [...request.excludedProviders ?? [], provider.id];
|
|
116700
117478
|
if (await firstAvailable(
|
|
116701
117479
|
orderedProviders(providers, { ...request, excludedProviders }),
|
|
@@ -116708,11 +117486,30 @@ function createTranscriptionService(input) {
|
|
|
116708
117486
|
} finally {
|
|
116709
117487
|
deadline.dispose();
|
|
116710
117488
|
}
|
|
117489
|
+
const latencyMs = Math.round(performance.now() - startedAt);
|
|
117490
|
+
let creditCostMicros = 0;
|
|
117491
|
+
const settle = async () => {
|
|
117492
|
+
if (chargeable(provider) && provider.deploymentFunded?.pricing && billingContext) {
|
|
117493
|
+
creditCostMicros = await settleWithoutFailing({
|
|
117494
|
+
accountId: request.accountId,
|
|
117495
|
+
workspaceId: request.workspaceId,
|
|
117496
|
+
providerId: provider.id,
|
|
117497
|
+
model: provider.deploymentFunded.model,
|
|
117498
|
+
pricing: provider.deploymentFunded.pricing,
|
|
117499
|
+
usage: result.usage ?? null,
|
|
117500
|
+
billing: billingContext
|
|
117501
|
+
});
|
|
117502
|
+
}
|
|
117503
|
+
};
|
|
117504
|
+
if (!request.deferBillingSettlement) await settle();
|
|
116711
117505
|
return {
|
|
116712
|
-
|
|
117506
|
+
text: result.text,
|
|
117507
|
+
languages: result.languages,
|
|
116713
117508
|
providerId: provider.id,
|
|
116714
|
-
audioSeconds
|
|
116715
|
-
latencyMs
|
|
117509
|
+
audioSeconds,
|
|
117510
|
+
latencyMs,
|
|
117511
|
+
creditCostMicros,
|
|
117512
|
+
...request.deferBillingSettlement ? { settleBilling: settle } : {}
|
|
116716
117513
|
};
|
|
116717
117514
|
}
|
|
116718
117515
|
};
|
|
@@ -116767,6 +117564,23 @@ async function exactAvailable(providers, providerId, context) {
|
|
|
116767
117564
|
const provider = providers.find((candidate) => candidate.id === providerId);
|
|
116768
117565
|
return provider && await provider.available(context) ? provider : null;
|
|
116769
117566
|
}
|
|
117567
|
+
function settlementAttributes(settlement) {
|
|
117568
|
+
const keys2 = voiceTranscriptionSettlementKeys({
|
|
117569
|
+
workspaceId: settlement.workspaceId,
|
|
117570
|
+
sourceId: settlement.billing.sourceId
|
|
117571
|
+
});
|
|
117572
|
+
return {
|
|
117573
|
+
accountId: settlement.accountId,
|
|
117574
|
+
workspaceId: settlement.workspaceId,
|
|
117575
|
+
providerId: settlement.providerId,
|
|
117576
|
+
sourceId: settlement.billing.sourceId,
|
|
117577
|
+
usageIdempotencyKey: keys2.usageIdempotencyKey,
|
|
117578
|
+
debitIdempotencyKey: keys2.debitIdempotencyKey
|
|
117579
|
+
};
|
|
117580
|
+
}
|
|
117581
|
+
function boundedErrorMessage(error) {
|
|
117582
|
+
return (error instanceof Error ? error.message : String(error)).slice(0, 500);
|
|
117583
|
+
}
|
|
116770
117584
|
|
|
116771
117585
|
// src/integrations/slack-tool-review.ts
|
|
116772
117586
|
function slackToolReviewCanDecide(review) {
|
|
@@ -116936,7 +117750,7 @@ import {
|
|
|
116936
117750
|
getActorNewSessionModelChoice,
|
|
116937
117751
|
hasPermission as hasPermission33,
|
|
116938
117752
|
requireAccessContext as requireAccessContext14,
|
|
116939
|
-
requireAccessGrant as
|
|
117753
|
+
requireAccessGrant as requireAccessGrant52,
|
|
116940
117754
|
requireSessionAuthorizationListScope as requireSessionAuthorizationListScope4,
|
|
116941
117755
|
resolveWorkspaceCatalogSettings as resolveWorkspaceCatalogSettings9
|
|
116942
117756
|
} from "@opengeni/core";
|
|
@@ -118103,7 +118917,7 @@ function registerSlackInteractionRoutes(app, deps) {
|
|
|
118103
118917
|
);
|
|
118104
118918
|
app.get("/v1/workspaces/:workspaceId/members/access-requests/slack", async (c) => {
|
|
118105
118919
|
const workspaceId = c.req.param("workspaceId");
|
|
118106
|
-
await
|
|
118920
|
+
await requireAccessGrant52(c, deps, workspaceId, "members:manage");
|
|
118107
118921
|
const workspace = await getWorkspace11(deps.db, workspaceId);
|
|
118108
118922
|
const requests = await listPendingSlackUserLinkAccessRequests(deps.db, workspaceId);
|
|
118109
118923
|
return c.json(
|
|
@@ -118119,7 +118933,7 @@ function registerSlackInteractionRoutes(app, deps) {
|
|
|
118119
118933
|
"/v1/workspaces/:workspaceId/members/access-requests/slack/:requestId/approve",
|
|
118120
118934
|
async (c) => {
|
|
118121
118935
|
const workspaceId = c.req.param("workspaceId");
|
|
118122
|
-
const grant = await
|
|
118936
|
+
const grant = await requireAccessGrant52(c, deps, workspaceId, "members:manage");
|
|
118123
118937
|
const payload = await parseRequestJson(c, ApproveSlackUserLinkAccessRequest);
|
|
118124
118938
|
const requestId = c.req.param("requestId");
|
|
118125
118939
|
const requestTeam = await slackUserLinkAccessRequestTeam(deps.db, {
|
|
@@ -118159,7 +118973,7 @@ function registerSlackInteractionRoutes(app, deps) {
|
|
|
118159
118973
|
"/v1/workspaces/:workspaceId/members/access-requests/slack/:requestId/deny",
|
|
118160
118974
|
async (c) => {
|
|
118161
118975
|
const workspaceId = c.req.param("workspaceId");
|
|
118162
|
-
const grant = await
|
|
118976
|
+
const grant = await requireAccessGrant52(c, deps, workspaceId, "members:manage");
|
|
118163
118977
|
const payload = await parseRequestJson(c, SlackUserLinkAccessMutationRequest);
|
|
118164
118978
|
try {
|
|
118165
118979
|
const request = await denySlackUserLinkAccessRequest(deps.db, {
|
|
@@ -118182,7 +118996,7 @@ function registerSlackInteractionRoutes(app, deps) {
|
|
|
118182
118996
|
);
|
|
118183
118997
|
app.post("/v1/workspaces/:workspaceId/integrations/slack/user-links", async (c) => {
|
|
118184
118998
|
const workspaceId = c.req.param("workspaceId");
|
|
118185
|
-
const grant = await
|
|
118999
|
+
const grant = await requireAccessGrant52(c, deps, workspaceId, "sessions:create");
|
|
118186
119000
|
const body3 = record5(await c.req.json().catch(() => null));
|
|
118187
119001
|
const linkToken = boundedString(body3?.linkToken, 2048);
|
|
118188
119002
|
const signingSecret = deps.settings.slackSigningSecret;
|
|
@@ -118238,7 +119052,7 @@ function registerSlackInteractionRoutes(app, deps) {
|
|
|
118238
119052
|
"/v1/workspaces/:workspaceId/integrations/slack/user-links/:slackUserId",
|
|
118239
119053
|
async (c) => {
|
|
118240
119054
|
const workspaceId = c.req.param("workspaceId");
|
|
118241
|
-
await
|
|
119055
|
+
await requireAccessGrant52(c, deps, workspaceId, "connections:write");
|
|
118242
119056
|
const connectionId = boundedString(c.req.query("connectionId"), 64);
|
|
118243
119057
|
if (!connectionId) throw new HTTPException117(400, { message: "connectionId is required" });
|
|
118244
119058
|
return c.json({
|
|
@@ -118253,7 +119067,7 @@ function registerSlackInteractionRoutes(app, deps) {
|
|
|
118253
119067
|
);
|
|
118254
119068
|
app.get("/v1/workspaces/:workspaceId/integrations/slack/channel-routes", async (c) => {
|
|
118255
119069
|
const workspaceId = c.req.param("workspaceId");
|
|
118256
|
-
const grant = await
|
|
119070
|
+
const grant = await requireAccessGrant52(c, deps, workspaceId, "workspace:admin");
|
|
118257
119071
|
const connectionId = boundedString(c.req.query("connectionId"), 64);
|
|
118258
119072
|
if (!connectionId) throw new HTTPException117(400, { message: "connectionId is required" });
|
|
118259
119073
|
const routes = await listSlackChannelRoutes(
|
|
@@ -118284,7 +119098,7 @@ function registerSlackInteractionRoutes(app, deps) {
|
|
|
118284
119098
|
});
|
|
118285
119099
|
app.put("/v1/workspaces/:workspaceId/integrations/slack/channel-routes", async (c) => {
|
|
118286
119100
|
const workspaceId = c.req.param("workspaceId");
|
|
118287
|
-
const grant = await
|
|
119101
|
+
const grant = await requireAccessGrant52(c, deps, workspaceId, "workspace:admin");
|
|
118288
119102
|
const payload = await parseRequestJson(c, UpdateSlackChannelRoutesRequest);
|
|
118289
119103
|
const home = { accountId: grant.accountId, workspaceId };
|
|
118290
119104
|
const installation = await listSlackInstallationBindings2(deps.db, home);
|
|
@@ -118339,7 +119153,7 @@ function registerSlackInteractionRoutes(app, deps) {
|
|
|
118339
119153
|
});
|
|
118340
119154
|
app.get("/v1/workspaces/:workspaceId/integrations/slack/reaction-channels", async (c) => {
|
|
118341
119155
|
const workspaceId = c.req.param("workspaceId");
|
|
118342
|
-
const grant = await
|
|
119156
|
+
const grant = await requireAccessGrant52(c, deps, workspaceId, "workspace:admin");
|
|
118343
119157
|
const connectionId = boundedString(c.req.query("connectionId"), 64);
|
|
118344
119158
|
if (!connectionId) throw new HTTPException117(400, { message: "connectionId is required" });
|
|
118345
119159
|
const cursor = boundedString(c.req.query("cursor"), 1024);
|
|
@@ -122244,10 +123058,23 @@ function createAppComposition(deps) {
|
|
|
122244
123058
|
}, MANAGED_AUTH_REAPER_INTERVAL_MS);
|
|
122245
123059
|
timer.unref?.();
|
|
122246
123060
|
}
|
|
123061
|
+
if (deps.transcription === void 0) {
|
|
123062
|
+
for (const issue of voiceInputPricingIssues(deps.settings)) {
|
|
123063
|
+
observability.error(
|
|
123064
|
+
`Voice input provider ${issue.providerId} is unavailable: ${issue.message}`,
|
|
123065
|
+
{
|
|
123066
|
+
providerId: issue.providerId,
|
|
123067
|
+
env: issue.env,
|
|
123068
|
+
reason: issue.reason
|
|
123069
|
+
}
|
|
123070
|
+
);
|
|
123071
|
+
}
|
|
123072
|
+
}
|
|
122247
123073
|
const transcription = deps.transcription === void 0 ? createTranscriptionService({
|
|
122248
123074
|
settings: deps.settings,
|
|
122249
123075
|
db: deps.db,
|
|
122250
|
-
...deps.codexFetch ? { codexFetch: deps.codexFetch } : {}
|
|
123076
|
+
...deps.codexFetch ? { codexFetch: deps.codexFetch } : {},
|
|
123077
|
+
log: (message, attributes) => observability.error(message, attributes)
|
|
122251
123078
|
}) : deps.transcription;
|
|
122252
123079
|
const transcriptionSegmenter = deps.transcriptionSegmenter === void 0 ? createFfmpegTranscriptionSegmenter({
|
|
122253
123080
|
ffmpegPath: deps.settings.voiceInputFfmpegPath
|
|
@@ -122355,8 +123182,14 @@ function createAppComposition(deps) {
|
|
|
122355
123182
|
"X-OpenGeni-Access-Key",
|
|
122356
123183
|
"X-OpenGeni-Api-Contract",
|
|
122357
123184
|
"X-OpenGeni-Actor-Epoch",
|
|
123185
|
+
// Resumable voice-input chunk integrity/timing (cross-origin embeds).
|
|
123186
|
+
"X-OpenGeni-Chunk-Duration-Milliseconds",
|
|
123187
|
+
"X-OpenGeni-Chunk-Sha256",
|
|
123188
|
+
"X-OpenGeni-Chunk-Start-Milliseconds",
|
|
122358
123189
|
"X-OpenGeni-Correlation-Id",
|
|
122359
123190
|
"X-OpenGeni-Session-Csrf",
|
|
123191
|
+
// Session scope for a session proxy; the API itself ignores it.
|
|
123192
|
+
"X-OpenGeni-Session-Id",
|
|
122360
123193
|
"X-OpenGeni-Site-Id",
|
|
122361
123194
|
"X-OpenGeni-Site-Version",
|
|
122362
123195
|
"X-OpenGeni-Subject",
|
|
@@ -122803,6 +123636,8 @@ function createAppComposition(deps) {
|
|
|
122803
123636
|
creditsAvailable: false
|
|
122804
123637
|
});
|
|
122805
123638
|
let modelSelectionForbidden = false;
|
|
123639
|
+
let voiceInputAvailable = false;
|
|
123640
|
+
let voiceInputProviders = [];
|
|
122806
123641
|
const requestedWorkspaceId = c.req.query("workspaceId");
|
|
122807
123642
|
if (requestedWorkspaceId !== void 0 && requestedWorkspaceId.trim() === "") {
|
|
122808
123643
|
throw new HTTPException118(422, { message: "workspaceId must not be empty" });
|
|
@@ -122821,7 +123656,7 @@ function createAppComposition(deps) {
|
|
|
122821
123656
|
}
|
|
122822
123657
|
const workspaceId = requestedWorkspaceId ?? context?.defaultWorkspaceId;
|
|
122823
123658
|
if (context && workspaceId) {
|
|
122824
|
-
const grant = await
|
|
123659
|
+
const grant = await requireAccessGrant53(c, routeDeps, workspaceId);
|
|
122825
123660
|
await withAccessGrantSessionRlsContext(routeDeps, grant, async () => {
|
|
122826
123661
|
catalogSettings = (await resolveWorkspaceCatalogSettings10(deps.db, deps.settings, {
|
|
122827
123662
|
accountId: grant.accountId,
|
|
@@ -122838,6 +123673,19 @@ function createAppComposition(deps) {
|
|
|
122838
123673
|
selections = [];
|
|
122839
123674
|
}
|
|
122840
123675
|
const workspace = await getWorkspace12(deps.db, workspaceId);
|
|
123676
|
+
const voicePreferences = WorkspaceVoiceInputSettings3.safeParse(
|
|
123677
|
+
workspace?.settings.voiceInput
|
|
123678
|
+
).data;
|
|
123679
|
+
const voiceContext = {
|
|
123680
|
+
workspaceId,
|
|
123681
|
+
subjectId: grant.subjectId,
|
|
123682
|
+
preferredProvider: voicePreferences?.preferredProvider,
|
|
123683
|
+
fallbackEnabled: voicePreferences?.fallbackEnabled
|
|
123684
|
+
};
|
|
123685
|
+
if (hasPermission34(grant.permissions, "sessions:create") && transcription) {
|
|
123686
|
+
voiceInputProviders = await transcription.availableProviderIds?.(voiceContext) ?? [];
|
|
123687
|
+
voiceInputAvailable = voiceInputProviders.length > 0 && await Promise.resolve(transcription.available(voiceContext)).catch(() => false);
|
|
123688
|
+
}
|
|
122841
123689
|
defaultSelection = await resolveDefaultSessionModelForSelections2(deps.db, {
|
|
122842
123690
|
settings: catalogSettings,
|
|
122843
123691
|
accountId: grant.accountId,
|
|
@@ -122893,14 +123741,12 @@ function createAppComposition(deps) {
|
|
|
122893
123741
|
maxSizeBytes: objectStorage?.maxSinglePutSizeBytes ?? 5e9
|
|
122894
123742
|
},
|
|
122895
123743
|
voiceInput: {
|
|
122896
|
-
providers:
|
|
122897
|
-
|
|
122898
|
-
),
|
|
122899
|
-
available: await transcription?.available() ?? false,
|
|
123744
|
+
providers: voiceInputProviders,
|
|
123745
|
+
available: voiceInputAvailable,
|
|
122900
123746
|
maxDurationSeconds: deps.settings.voiceInputMaxDurationSeconds,
|
|
122901
123747
|
maxSizeBytes: deps.settings.voiceInputMaxSizeBytes,
|
|
122902
123748
|
acceptedMimeTypes: [...VOICE_INPUT_ACCEPTED_MIME_TYPES2],
|
|
122903
|
-
...deps.settings.voiceInputResumableEnabled && objectStorage && transcription && transcriptionSegmenter &&
|
|
123749
|
+
...deps.settings.voiceInputResumableEnabled && objectStorage && transcription && transcriptionSegmenter && voiceInputAvailable && await transcriptionSegmenter.available() ? {
|
|
122904
123750
|
resumable: {
|
|
122905
123751
|
maxDurationSeconds: deps.settings.voiceInputResumableMaxDurationSeconds,
|
|
122906
123752
|
maxSizeBytes: deps.settings.voiceInputResumableMaxSizeBytes,
|
|
@@ -123009,7 +123855,7 @@ function createAppComposition(deps) {
|
|
|
123009
123855
|
app.use("/v1/workspaces/:workspaceId/*", async (c, next) => {
|
|
123010
123856
|
const workspaceId = c.req.param("workspaceId");
|
|
123011
123857
|
if (workspaceRequestRequiresCodexAccountPrevalidation(c.req.raw)) {
|
|
123012
|
-
const grant2 = await
|
|
123858
|
+
const grant2 = await requireAccessGrant53(c, routeDeps, workspaceId);
|
|
123013
123859
|
await validateCodexAccountTarget(c.req.raw);
|
|
123014
123860
|
await withAccessGrantSessionRlsContext(routeDeps, grant2, next);
|
|
123015
123861
|
return;
|
|
@@ -123019,7 +123865,7 @@ function createAppComposition(deps) {
|
|
|
123019
123865
|
return;
|
|
123020
123866
|
}
|
|
123021
123867
|
const permission = c.req.method === "PUT" && /^\/v1\/workspaces\/[^/]+\/artifact-catalog\/[^/]+\/[^/]+\/pin$/.test(c.req.path) ? "artifacts:publish" : void 0;
|
|
123022
|
-
const grant = await
|
|
123868
|
+
const grant = await requireAccessGrant53(c, routeDeps, workspaceId, permission);
|
|
123023
123869
|
await withAccessGrantSessionRlsContext(routeDeps, grant, next);
|
|
123024
123870
|
});
|
|
123025
123871
|
app.all("/v1/workspaces/:workspaceId/mcp", async (c) => {
|
|
@@ -123174,7 +124020,7 @@ function createAppComposition(deps) {
|
|
|
123174
124020
|
});
|
|
123175
124021
|
app.get("/v1/workspaces/:workspaceId/tools/catalog", async (c) => {
|
|
123176
124022
|
const workspaceId = c.req.param("workspaceId");
|
|
123177
|
-
const authorization = await
|
|
124023
|
+
const authorization = await requireAccessGrantAuthorization41(
|
|
123178
124024
|
c,
|
|
123179
124025
|
routeDeps,
|
|
123180
124026
|
workspaceId,
|
|
@@ -123190,7 +124036,7 @@ function createAppComposition(deps) {
|
|
|
123190
124036
|
});
|
|
123191
124037
|
app.post("/v1/workspaces/:workspaceId/tools/calls", async (c) => {
|
|
123192
124038
|
const workspaceId = c.req.param("workspaceId");
|
|
123193
|
-
const authorization = await
|
|
124039
|
+
const authorization = await requireAccessGrantAuthorization41(
|
|
123194
124040
|
c,
|
|
123195
124041
|
routeDeps,
|
|
123196
124042
|
workspaceId,
|
|
@@ -123219,7 +124065,7 @@ function createAppComposition(deps) {
|
|
|
123219
124065
|
});
|
|
123220
124066
|
app.post("/v1/workspaces/:workspaceId/tools/approvals", async (c) => {
|
|
123221
124067
|
const workspaceId = c.req.param("workspaceId");
|
|
123222
|
-
const authorization = await
|
|
124068
|
+
const authorization = await requireAccessGrantAuthorization41(
|
|
123223
124069
|
c,
|
|
123224
124070
|
routeDeps,
|
|
123225
124071
|
workspaceId,
|
|
@@ -123248,7 +124094,7 @@ function createAppComposition(deps) {
|
|
|
123248
124094
|
});
|
|
123249
124095
|
app.get("/v1/workspaces/:workspaceId/tools/declarations", async (c) => {
|
|
123250
124096
|
const workspaceId = c.req.param("workspaceId");
|
|
123251
|
-
const authorization = await
|
|
124097
|
+
const authorization = await requireAccessGrantAuthorization41(
|
|
123252
124098
|
c,
|
|
123253
124099
|
routeDeps,
|
|
123254
124100
|
workspaceId,
|
|
@@ -123264,7 +124110,7 @@ function createAppComposition(deps) {
|
|
|
123264
124110
|
});
|
|
123265
124111
|
app.all("/v1/workspaces/:workspaceId/codemode/sdk/*", async (c) => {
|
|
123266
124112
|
const workspaceId = c.req.param("workspaceId");
|
|
123267
|
-
const grant = await
|
|
124113
|
+
const grant = await requireAccessGrant53(c, routeDeps, workspaceId);
|
|
123268
124114
|
const url = new URL(c.req.url);
|
|
123269
124115
|
const prefix2 = `/v1/workspaces/${workspaceId}/codemode/sdk`;
|
|
123270
124116
|
let forwarded;
|
|
@@ -123282,7 +124128,7 @@ function createAppComposition(deps) {
|
|
|
123282
124128
|
});
|
|
123283
124129
|
app.get("/v1/workspaces/:workspaceId/codemode/catalog", async (c) => {
|
|
123284
124130
|
const workspaceId = c.req.param("workspaceId");
|
|
123285
|
-
const grant = await
|
|
124131
|
+
const grant = await requireAccessGrant53(c, routeDeps, workspaceId);
|
|
123286
124132
|
if (!isCodemodeGrant(grant)) {
|
|
123287
124133
|
throw new HTTPException118(403, { message: "Codemode access denied" });
|
|
123288
124134
|
}
|
|
@@ -123294,7 +124140,7 @@ function createAppComposition(deps) {
|
|
|
123294
124140
|
});
|
|
123295
124141
|
app.post("/v1/workspaces/:workspaceId/codemode/calls", async (c) => {
|
|
123296
124142
|
const workspaceId = c.req.param("workspaceId");
|
|
123297
|
-
const grant = await
|
|
124143
|
+
const grant = await requireAccessGrant53(c, routeDeps, workspaceId);
|
|
123298
124144
|
if (!isCodemodeGrant(grant)) {
|
|
123299
124145
|
throw new HTTPException118(403, { message: "Codemode access denied" });
|
|
123300
124146
|
}
|
|
@@ -123314,7 +124160,7 @@ function createAppComposition(deps) {
|
|
|
123314
124160
|
});
|
|
123315
124161
|
app.get("/v1/workspaces/:workspaceId/codemode/calls/:operationId", async (c) => {
|
|
123316
124162
|
const workspaceId = c.req.param("workspaceId");
|
|
123317
|
-
const grant = await
|
|
124163
|
+
const grant = await requireAccessGrant53(c, routeDeps, workspaceId);
|
|
123318
124164
|
if (!isCodemodeGrant(grant)) {
|
|
123319
124165
|
throw new HTTPException118(403, { message: "Codemode access denied" });
|
|
123320
124166
|
}
|
|
@@ -123439,13 +124285,13 @@ function createAppComposition(deps) {
|
|
|
123439
124285
|
latencyMode: rawError.latencyMode,
|
|
123440
124286
|
allowedLatencyModes: [...rawError.allowedLatencyModes]
|
|
123441
124287
|
}
|
|
123442
|
-
}) : null) ?? requestBodyValidationHttpError(rawError) ?? invalidPathIdentifierHttpError(rawError, new URL(c.req.url).pathname) ?? databaseUnavailableHttpError(rawError, c.req.method) ?? rawError;
|
|
124288
|
+
}) : null) ?? requestBodyValidationHttpError(rawError) ?? invalidPathIdentifierHttpError(rawError, new URL(c.req.url).pathname) ?? databaseUnavailableHttpError(rawError, c.req.method) ?? databaseContentionHttpError(rawError, c.req.method) ?? rawError;
|
|
123443
124289
|
const compactionLock = codexCompactionV2ProviderLockedError(error);
|
|
123444
124290
|
const apiError = error instanceof ApiHttpError ? error : null;
|
|
123445
124291
|
const status = compactionLock ? 422 : httpStatusForError(error);
|
|
123446
124292
|
const code = compactionLock ? compactionLock.code : apiError?.code ?? errorCodeForStatus(status);
|
|
123447
124293
|
if (status >= 500) logHttpFailure(c, status, code, rawError);
|
|
123448
|
-
if (apiError?.details?.code === DATABASE_UNAVAILABLE_ERROR_DETAIL_CODE) {
|
|
124294
|
+
if (apiError?.details?.code === DATABASE_UNAVAILABLE_ERROR_DETAIL_CODE || apiError?.details?.code === DATABASE_CONTENTION_ERROR_DETAIL_CODE) {
|
|
123449
124295
|
c.header("retry-after", "1");
|
|
123450
124296
|
}
|
|
123451
124297
|
const envelope = ErrorEnvelope.parse({
|
|
@@ -123511,7 +124357,7 @@ async function validateCodexAccountTarget(request) {
|
|
|
123511
124357
|
}
|
|
123512
124358
|
}
|
|
123513
124359
|
async function requireMcpAccessGrantAuthorization(c, deps, workspaceId) {
|
|
123514
|
-
const authorization = await
|
|
124360
|
+
const authorization = await requireAccessGrantAuthorization41(c, deps, workspaceId);
|
|
123515
124361
|
const grant = authorization.grant;
|
|
123516
124362
|
if (hasPermission34(grant.permissions, "workspace:read")) {
|
|
123517
124363
|
return authorization;
|
|
@@ -123666,6 +124512,8 @@ function assertConfiguredCodemodeSessionProxyPath(session, path, method) {
|
|
|
123666
124512
|
}
|
|
123667
124513
|
}
|
|
123668
124514
|
function codemodeHttpError(error) {
|
|
124515
|
+
const contention = databaseContentionHttpError(error, "POST", { outcomeUnknown: false });
|
|
124516
|
+
if (contention) return contention;
|
|
123669
124517
|
if (error instanceof SiteSessionPathError) {
|
|
123670
124518
|
return new HTTPException118(404, { message: error.message, cause: error });
|
|
123671
124519
|
}
|
|
@@ -124905,4 +125753,4 @@ export {
|
|
|
124905
125753
|
withDefaultEnabledCapabilityMcpTools,
|
|
124906
125754
|
workflowIdForSession4 as workflowIdForSession
|
|
124907
125755
|
};
|
|
124908
|
-
//# sourceMappingURL=chunk-
|
|
125756
|
+
//# sourceMappingURL=chunk-F3LQ6VWR.js.map
|