@opengeni/sdk 0.15.0 → 0.23.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +49 -6
- package/dist/index.d.ts +781 -29
- package/dist/index.js +562 -42
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/client.ts +468 -27
- package/src/index.ts +85 -0
- package/src/transcription.ts +496 -0
- package/src/types.ts +701 -23
package/dist/index.js
CHANGED
|
@@ -293,8 +293,11 @@ async function sleep2(delayMs, signal) {
|
|
|
293
293
|
// src/types.ts
|
|
294
294
|
var SESSION_EVENT_TYPES = [
|
|
295
295
|
"session.created",
|
|
296
|
+
// Defensive bounded projection for malformed/legacy oversized envelopes.
|
|
297
|
+
"session.event.envelope_omitted",
|
|
296
298
|
"session.status.changed",
|
|
297
299
|
"session.requiresAction",
|
|
300
|
+
"session.humanInput.requested",
|
|
298
301
|
"session.context.compaction.requested",
|
|
299
302
|
"session.context.compacted",
|
|
300
303
|
"session.context.compaction.skipped",
|
|
@@ -302,6 +305,7 @@ var SESSION_EVENT_TYPES = [
|
|
|
302
305
|
"user.message",
|
|
303
306
|
"user.pause",
|
|
304
307
|
"user.approvalDecision",
|
|
308
|
+
"user.humanInputResponse",
|
|
305
309
|
"turn.queued",
|
|
306
310
|
"turn.started",
|
|
307
311
|
"turn.completed",
|
|
@@ -315,8 +319,10 @@ var SESSION_EVENT_TYPES = [
|
|
|
315
319
|
"agent.reasoning.delta",
|
|
316
320
|
"agent.toolCall.created",
|
|
317
321
|
"agent.toolCall.output",
|
|
322
|
+
"agent.model.request",
|
|
318
323
|
"agent.model.usage",
|
|
319
324
|
"tool.auth_needed",
|
|
325
|
+
"credential.auth_needed",
|
|
320
326
|
"agent.updated",
|
|
321
327
|
"rig.setup.started",
|
|
322
328
|
"rig.setup.completed",
|
|
@@ -365,11 +371,12 @@ var SESSION_EVENT_TYPES = [
|
|
|
365
371
|
"terminal.pty.output.delta",
|
|
366
372
|
"terminal.pty.exited",
|
|
367
373
|
"session.title_set",
|
|
374
|
+
"session.mcp.approval_policy.updated",
|
|
368
375
|
// Multi-account Codex (P1): the session's inference account changed.
|
|
369
376
|
"codex.account.switched",
|
|
370
|
-
//
|
|
377
|
+
// credential allocator metadata-only per-turn credential selection audit.
|
|
371
378
|
"codex.credential.selected",
|
|
372
|
-
//
|
|
379
|
+
// credential allocator durable zero-capacity wait lifecycle. These are system/runtime
|
|
373
380
|
// events, never synthetic user messages.
|
|
374
381
|
"codex.capacity.waiting",
|
|
375
382
|
"codex.capacity.resumed",
|
|
@@ -410,7 +417,7 @@ var KNOWN_PERMISSIONS = [
|
|
|
410
417
|
"sessions:create",
|
|
411
418
|
"sessions:read",
|
|
412
419
|
"sessions:control",
|
|
413
|
-
//
|
|
420
|
+
// sandbox workspace (mirror of @opengeni/contracts Permission). stream:view is
|
|
414
421
|
// strictly broader than sessions:read (un-redacted pixels); stream:control is
|
|
415
422
|
// the never-granted-v1 raw-input plane; stream:acknowledge is the secret-leak
|
|
416
423
|
// consent gate.
|
|
@@ -442,8 +449,10 @@ var KNOWN_PERMISSIONS = [
|
|
|
442
449
|
"rigs:use",
|
|
443
450
|
"rigs:manage"
|
|
444
451
|
];
|
|
445
|
-
var OPENGENI_API_CONTRACT_REVISION = "2026-07-
|
|
452
|
+
var OPENGENI_API_CONTRACT_REVISION = "2026-07-turn-instructions-v1";
|
|
446
453
|
var OPENGENI_API_CONTRACT_HEADER = "x-opengeni-api-contract";
|
|
454
|
+
var RETAINED_OUTPUT_DEFAULT_PAGE_BYTES = 256 * 1024;
|
|
455
|
+
var RETAINED_OUTPUT_MAX_PAGE_BYTES = 1024 * 1024;
|
|
447
456
|
var KNOWN_USAGE_EVENT_TYPES = [
|
|
448
457
|
"agent_run.created",
|
|
449
458
|
"agent_run.completed",
|
|
@@ -490,6 +499,18 @@ var OpenGeniClient = class {
|
|
|
490
499
|
request
|
|
491
500
|
);
|
|
492
501
|
}
|
|
502
|
+
/**
|
|
503
|
+
* Replace one attached MCP server's approval policy. The change is captured
|
|
504
|
+
* by the next claimed attempt; already-claimed work keeps its immutable
|
|
505
|
+
* policy snapshot.
|
|
506
|
+
*/
|
|
507
|
+
async updateSessionMcpApprovalPolicy(workspaceId, sessionId, serverId, request) {
|
|
508
|
+
return await this.requestJson(
|
|
509
|
+
"PATCH",
|
|
510
|
+
`/v1/workspaces/${workspaceId}/sessions/${sessionId}/mcp-servers/${encodeURIComponent(serverId)}/approval-policy`,
|
|
511
|
+
request
|
|
512
|
+
);
|
|
513
|
+
}
|
|
493
514
|
async listSessions(workspaceId, options = {}) {
|
|
494
515
|
return await this.requestJson(
|
|
495
516
|
"GET",
|
|
@@ -559,7 +580,8 @@ var OpenGeniClient = class {
|
|
|
559
580
|
void 0,
|
|
560
581
|
{
|
|
561
582
|
...options.sessionId !== void 0 ? { sessionId: options.sessionId } : {}
|
|
562
|
-
}
|
|
583
|
+
},
|
|
584
|
+
{ signal: options.signal }
|
|
563
585
|
);
|
|
564
586
|
}
|
|
565
587
|
/**
|
|
@@ -659,23 +681,95 @@ var OpenGeniClient = class {
|
|
|
659
681
|
}
|
|
660
682
|
// --- Events: replay, send, stream ----------------------------------------
|
|
661
683
|
/**
|
|
662
|
-
*
|
|
663
|
-
*
|
|
664
|
-
*
|
|
665
|
-
*
|
|
684
|
+
* Return the events from one bounded page. With no cursor, this uses the safe
|
|
685
|
+
* semantic monitoring tail; pass explicit forensic options and a cursor for
|
|
686
|
+
* retained audit replay. Use `listEventPage` when projection, coverage, or
|
|
687
|
+
* resume-cursor facts are required.
|
|
666
688
|
*/
|
|
667
689
|
async listEvents(workspaceId, sessionId, options = {}) {
|
|
668
|
-
return await this.
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
690
|
+
return (await this.listEventPage(workspaceId, sessionId, options)).events;
|
|
691
|
+
}
|
|
692
|
+
async listEventPage(workspaceId, sessionId, options = {}) {
|
|
693
|
+
if (options.latest && ["includeTypes", "excludeTypes", "includeClasses", "excludeClasses"].some(
|
|
694
|
+
(name) => Object.prototype.hasOwnProperty.call(options, name)
|
|
695
|
+
)) {
|
|
696
|
+
throw new TypeError("latest cannot be combined with event filters");
|
|
697
|
+
}
|
|
698
|
+
if (options.resultMode === "compact" && !options.latest) {
|
|
699
|
+
throw new TypeError("resultMode=compact requires latest");
|
|
700
|
+
}
|
|
701
|
+
const listOptions = options.resultMode === "compact" ? null : options;
|
|
702
|
+
const response = await this.fetchImpl(
|
|
703
|
+
this.url(`/v1/workspaces/${workspaceId}/sessions/${sessionId}/events`, {
|
|
704
|
+
...listOptions?.after !== void 0 ? { after: String(listOptions.after) } : {},
|
|
705
|
+
...listOptions?.before !== void 0 ? { before: String(listOptions.before) } : {},
|
|
706
|
+
...listOptions?.limit !== void 0 ? { limit: String(listOptions.limit) } : {},
|
|
707
|
+
...listOptions?.compact ? { compact: "1" } : {},
|
|
708
|
+
...options.mode ? { mode: options.mode } : {},
|
|
709
|
+
...listOptions?.direction ? { direction: listOptions.direction } : {},
|
|
710
|
+
...options.payloadMode ? { payloadMode: options.payloadMode } : {},
|
|
711
|
+
...options.resultMode ? { resultMode: options.resultMode } : {},
|
|
712
|
+
...listOptions?.includeTypes?.length ? { includeTypes: listOptions.includeTypes.join(",") } : {},
|
|
713
|
+
...listOptions?.excludeTypes?.length ? { excludeTypes: listOptions.excludeTypes.join(",") } : {},
|
|
714
|
+
...listOptions?.includeClasses?.length ? { includeClasses: listOptions.includeClasses.join(",") } : {},
|
|
715
|
+
...listOptions?.excludeClasses?.length ? { excludeClasses: listOptions.excludeClasses.join(",") } : {},
|
|
716
|
+
...options.latest ? { latest: options.latest } : {}
|
|
717
|
+
}),
|
|
672
718
|
{
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
...options.limit !== void 0 ? { limit: String(options.limit) } : {},
|
|
676
|
-
...options.compact ? { compact: "1" } : {}
|
|
719
|
+
method: "GET",
|
|
720
|
+
headers: { ...this.headers(), Accept: "application/json" }
|
|
677
721
|
}
|
|
678
722
|
);
|
|
723
|
+
assertApiContractResponse(response);
|
|
724
|
+
if (!response.ok) throw new OpenGeniApiError(response.status, await safeText(response));
|
|
725
|
+
const body = await response.json();
|
|
726
|
+
if (options.resultMode === "compact") {
|
|
727
|
+
return body;
|
|
728
|
+
}
|
|
729
|
+
const events = body;
|
|
730
|
+
const integerHeader = (name) => {
|
|
731
|
+
const raw = response.headers.get(name);
|
|
732
|
+
if (raw === null) return null;
|
|
733
|
+
const value = Number(raw);
|
|
734
|
+
return Number.isSafeInteger(value) && value >= 0 ? value : null;
|
|
735
|
+
};
|
|
736
|
+
const mode = response.headers.get("X-OpenGeni-Event-Mode") === "forensic" ? "forensic" : "monitoring";
|
|
737
|
+
const direction = response.headers.get("X-OpenGeni-Event-Direction") === "after" ? "after" : "before";
|
|
738
|
+
const payloadHeader = response.headers.get("X-OpenGeni-Payload-Mode");
|
|
739
|
+
const payloadMode = payloadHeader === "none" || payloadHeader === "full" ? payloadHeader : "summary";
|
|
740
|
+
const first = integerHeader("X-OpenGeni-Covered-First");
|
|
741
|
+
const last = integerHeader("X-OpenGeni-Covered-Last");
|
|
742
|
+
const bytes = integerHeader("X-OpenGeni-Page-Bytes") ?? new TextEncoder().encode(JSON.stringify(events)).byteLength;
|
|
743
|
+
const maxBytes = integerHeader("X-OpenGeni-Page-Max-Bytes") ?? 1024 * 1024;
|
|
744
|
+
const truncatedByHeader = response.headers.get("X-OpenGeni-Truncated-By");
|
|
745
|
+
const truncatedBy = truncatedByHeader === "count" || truncatedByHeader === "bytes" || truncatedByHeader === "http_bytes" ? truncatedByHeader : null;
|
|
746
|
+
return {
|
|
747
|
+
events,
|
|
748
|
+
mode,
|
|
749
|
+
payloadMode,
|
|
750
|
+
direction,
|
|
751
|
+
bytes,
|
|
752
|
+
maxBytes,
|
|
753
|
+
truncated: response.headers.get("X-OpenGeni-Page-Truncated") === "true",
|
|
754
|
+
hasMore: response.headers.get("X-OpenGeni-Has-More") === "true",
|
|
755
|
+
truncatedBy,
|
|
756
|
+
coveredSequence: first === null || last === null ? null : { first, last },
|
|
757
|
+
nextAfter: integerHeader("X-OpenGeni-Next-After"),
|
|
758
|
+
nextBefore: integerHeader("X-OpenGeni-Next-Before"),
|
|
759
|
+
forensicExact: response.headers.get("X-OpenGeni-Forensic-Exact") === "true"
|
|
760
|
+
};
|
|
761
|
+
}
|
|
762
|
+
/**
|
|
763
|
+
* Fetch the authoritative newest-sequence semantic result directly. This is
|
|
764
|
+
* the callback-loss recovery path: it reads one compact durable result and
|
|
765
|
+
* never creates a model turn. `latest: "receipt"` aliases `tool_receipt`;
|
|
766
|
+
* turn generation remains scoped retry metadata.
|
|
767
|
+
*/
|
|
768
|
+
async getLatestEventResult(workspaceId, sessionId, options = { latest: "terminal" }) {
|
|
769
|
+
return await this.listEventPage(workspaceId, sessionId, {
|
|
770
|
+
...options,
|
|
771
|
+
resultMode: "compact"
|
|
772
|
+
});
|
|
679
773
|
}
|
|
680
774
|
/** POST a user/control event to the session. Returns the accepted event. */
|
|
681
775
|
async sendEvent(workspaceId, sessionId, event) {
|
|
@@ -710,6 +804,28 @@ var OpenGeniClient = class {
|
|
|
710
804
|
payload
|
|
711
805
|
});
|
|
712
806
|
}
|
|
807
|
+
async listHumanInputRequests(workspaceId, sessionId, options = {}) {
|
|
808
|
+
const result = await this.requestJson(
|
|
809
|
+
"GET",
|
|
810
|
+
`/v1/workspaces/${workspaceId}/sessions/${sessionId}/human-input-requests`,
|
|
811
|
+
void 0,
|
|
812
|
+
options.status ? { status: options.status } : void 0
|
|
813
|
+
);
|
|
814
|
+
return result.requests;
|
|
815
|
+
}
|
|
816
|
+
async getHumanInputRequest(workspaceId, sessionId, requestId) {
|
|
817
|
+
return await this.requestJson(
|
|
818
|
+
"GET",
|
|
819
|
+
`/v1/workspaces/${workspaceId}/sessions/${sessionId}/human-input-requests/${requestId}`
|
|
820
|
+
);
|
|
821
|
+
}
|
|
822
|
+
async submitHumanInputResponse(workspaceId, sessionId, requestId, response, options = {}) {
|
|
823
|
+
return await this.sendEvent(workspaceId, sessionId, {
|
|
824
|
+
type: "user.humanInputResponse",
|
|
825
|
+
...options.clientEventId ? { clientEventId: options.clientEventId } : {},
|
|
826
|
+
payload: { requestId, response }
|
|
827
|
+
});
|
|
828
|
+
}
|
|
713
829
|
/**
|
|
714
830
|
* Live-stream a session's events with automatic reconnect, resume from the
|
|
715
831
|
* last seen sequence, gap backfill, and duplicate suppression. See
|
|
@@ -818,15 +934,35 @@ var OpenGeniClient = class {
|
|
|
818
934
|
);
|
|
819
935
|
}
|
|
820
936
|
async listWorkspaceControlEvents(workspaceId, options = {}) {
|
|
821
|
-
return await this.
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
937
|
+
return (await this.listWorkspaceControlEventPage(workspaceId, options)).events;
|
|
938
|
+
}
|
|
939
|
+
/** Count/byte-bounded page plus an explicit continuation cursor. */
|
|
940
|
+
async listWorkspaceControlEventPage(workspaceId, options = {}) {
|
|
941
|
+
const response = await this.fetchImpl(
|
|
942
|
+
this.url(`/v1/workspaces/${workspaceId}/control-events`, {
|
|
826
943
|
...options.after !== void 0 ? { after: String(options.after) } : {},
|
|
827
944
|
...options.limit !== void 0 ? { limit: String(options.limit) } : {}
|
|
945
|
+
}),
|
|
946
|
+
{
|
|
947
|
+
method: "GET",
|
|
948
|
+
headers: { ...this.headers(), Accept: "application/json" }
|
|
828
949
|
}
|
|
829
950
|
);
|
|
951
|
+
assertApiContractResponse(response);
|
|
952
|
+
if (!response.ok) {
|
|
953
|
+
throw new OpenGeniApiError(response.status, await safeText(response));
|
|
954
|
+
}
|
|
955
|
+
const events = await response.json();
|
|
956
|
+
const bytesHeader = response.headers.get("X-OpenGeni-Page-Bytes");
|
|
957
|
+
const nextHeader = response.headers.get("X-OpenGeni-Next-After");
|
|
958
|
+
const parsedBytes = bytesHeader === null ? Number.NaN : Number(bytesHeader);
|
|
959
|
+
const parsedNext = nextHeader === null ? null : Number(nextHeader);
|
|
960
|
+
return {
|
|
961
|
+
events,
|
|
962
|
+
bytes: Number.isSafeInteger(parsedBytes) && parsedBytes >= 0 ? parsedBytes : new TextEncoder().encode(JSON.stringify(events)).byteLength,
|
|
963
|
+
truncated: response.headers.get("X-OpenGeni-Page-Truncated") === "true",
|
|
964
|
+
nextAfter: parsedNext !== null && Number.isSafeInteger(parsedNext) && parsedNext >= 0 ? parsedNext : null
|
|
965
|
+
};
|
|
830
966
|
}
|
|
831
967
|
streamWorkspaceControlEvents(workspaceId, options = {}) {
|
|
832
968
|
return streamWorkspaceControlEvents(this.workspaceControlStreamTransport(workspaceId), options);
|
|
@@ -926,19 +1062,23 @@ var OpenGeniClient = class {
|
|
|
926
1062
|
// synchronous API-direct point query; the fs.changed/git.changed/terminal.pty.*
|
|
927
1063
|
// notifications + the PTY output stream arrive on the existing event SSE.
|
|
928
1064
|
/** FileSystem: list a directory tree (feeds the Pierre file tree). */
|
|
929
|
-
async fsList(workspaceId, sessionId, request = {}) {
|
|
1065
|
+
async fsList(workspaceId, sessionId, request = {}, options = {}) {
|
|
930
1066
|
return await this.requestJson(
|
|
931
1067
|
"POST",
|
|
932
1068
|
`/v1/workspaces/${workspaceId}/sessions/${sessionId}/fs/list`,
|
|
933
|
-
request
|
|
1069
|
+
request,
|
|
1070
|
+
{},
|
|
1071
|
+
options
|
|
934
1072
|
);
|
|
935
1073
|
}
|
|
936
1074
|
/** FileSystem: read a file (text or base64; binary-safe, size-capped). */
|
|
937
|
-
async fsRead(workspaceId, sessionId, request) {
|
|
1075
|
+
async fsRead(workspaceId, sessionId, request, options = {}) {
|
|
938
1076
|
return await this.requestJson(
|
|
939
1077
|
"POST",
|
|
940
1078
|
`/v1/workspaces/${workspaceId}/sessions/${sessionId}/fs/read`,
|
|
941
|
-
request
|
|
1079
|
+
request,
|
|
1080
|
+
{},
|
|
1081
|
+
options
|
|
942
1082
|
);
|
|
943
1083
|
}
|
|
944
1084
|
/** FileSystem: write a file (last-writer-wins; emits fs.changed). */
|
|
@@ -974,19 +1114,23 @@ var OpenGeniClient = class {
|
|
|
974
1114
|
);
|
|
975
1115
|
}
|
|
976
1116
|
/** Git: working-tree/index status (the Pierre file-status feed). */
|
|
977
|
-
async gitStatus(workspaceId, sessionId, request = {}) {
|
|
1117
|
+
async gitStatus(workspaceId, sessionId, request = {}, options = {}) {
|
|
978
1118
|
return await this.requestJson(
|
|
979
1119
|
"POST",
|
|
980
1120
|
`/v1/workspaces/${workspaceId}/sessions/${sessionId}/git/status`,
|
|
981
|
-
request
|
|
1121
|
+
request,
|
|
1122
|
+
{},
|
|
1123
|
+
options
|
|
982
1124
|
);
|
|
983
1125
|
}
|
|
984
1126
|
/** Git: structured diff hunks (the Pierre diff feed). */
|
|
985
|
-
async gitDiff(workspaceId, sessionId, request = {}) {
|
|
1127
|
+
async gitDiff(workspaceId, sessionId, request = {}, options = {}) {
|
|
986
1128
|
return await this.requestJson(
|
|
987
1129
|
"POST",
|
|
988
1130
|
`/v1/workspaces/${workspaceId}/sessions/${sessionId}/git/diff`,
|
|
989
|
-
request
|
|
1131
|
+
request,
|
|
1132
|
+
{},
|
|
1133
|
+
options
|
|
990
1134
|
);
|
|
991
1135
|
}
|
|
992
1136
|
/** Git: commit log. */
|
|
@@ -1009,23 +1153,27 @@ var OpenGeniClient = class {
|
|
|
1009
1153
|
* (tree + per-repo diff + file after-image refs), served from durable storage
|
|
1010
1154
|
* WITHOUT warming a machine — the workbench cold-paint source. Returns
|
|
1011
1155
|
* `{available:false}` when no capture exists yet (fall back to the live path). */
|
|
1012
|
-
async getWorkspaceCapture(workspaceId, sessionId) {
|
|
1156
|
+
async getWorkspaceCapture(workspaceId, sessionId, options = {}) {
|
|
1013
1157
|
return await this.requestJson(
|
|
1014
1158
|
"GET",
|
|
1015
|
-
`/v1/workspaces/${workspaceId}/sessions/${sessionId}/workspace/capture
|
|
1159
|
+
`/v1/workspaces/${workspaceId}/sessions/${sessionId}/workspace/capture`,
|
|
1160
|
+
void 0,
|
|
1161
|
+
{},
|
|
1162
|
+
options
|
|
1016
1163
|
);
|
|
1017
1164
|
}
|
|
1018
1165
|
/** Workspace capture: a single file's after-image from the capture (revision
|
|
1019
1166
|
* pins a specific one; omitted → latest). Content is inline for small files,
|
|
1020
1167
|
* else a short-TTL signed URL; a tooLarge file returns metadata only. */
|
|
1021
|
-
async getWorkspaceCaptureFile(workspaceId, sessionId, path, revision) {
|
|
1168
|
+
async getWorkspaceCaptureFile(workspaceId, sessionId, path, revision, options = {}) {
|
|
1022
1169
|
const query = { path };
|
|
1023
1170
|
if (revision !== void 0) query.revision = String(revision);
|
|
1024
1171
|
return await this.requestJson(
|
|
1025
1172
|
"GET",
|
|
1026
1173
|
`/v1/workspaces/${workspaceId}/sessions/${sessionId}/workspace/capture/file`,
|
|
1027
1174
|
void 0,
|
|
1028
|
-
query
|
|
1175
|
+
query,
|
|
1176
|
+
options
|
|
1029
1177
|
);
|
|
1030
1178
|
}
|
|
1031
1179
|
/** Terminal: run a bounded command, returning buffered stdout/stderr inline. */
|
|
@@ -1080,10 +1228,13 @@ var OpenGeniClient = class {
|
|
|
1080
1228
|
* liveness the client polls on while `cold`/`warming`. The desktop URL/token
|
|
1081
1229
|
* are minted in-process only when the box is warm AND the principal has
|
|
1082
1230
|
* acknowledged the un-redacted plane. */
|
|
1083
|
-
async getStreamCapabilities(workspaceId, sessionId) {
|
|
1231
|
+
async getStreamCapabilities(workspaceId, sessionId, options = {}) {
|
|
1084
1232
|
return await this.requestJson(
|
|
1085
1233
|
"GET",
|
|
1086
|
-
`/v1/workspaces/${workspaceId}/sessions/${sessionId}/stream-capabilities
|
|
1234
|
+
`/v1/workspaces/${workspaceId}/sessions/${sessionId}/stream-capabilities`,
|
|
1235
|
+
void 0,
|
|
1236
|
+
{},
|
|
1237
|
+
options
|
|
1087
1238
|
);
|
|
1088
1239
|
}
|
|
1089
1240
|
/** Record the calling principal's acknowledgment of the un-redacted desktop
|
|
@@ -1146,6 +1297,13 @@ var OpenGeniClient = class {
|
|
|
1146
1297
|
}
|
|
1147
1298
|
return config;
|
|
1148
1299
|
}
|
|
1300
|
+
/** Authenticated model definitions plus workspace-specific selectability. */
|
|
1301
|
+
async getWorkspaceModelCatalog(workspaceId) {
|
|
1302
|
+
return await this.requestJson(
|
|
1303
|
+
"GET",
|
|
1304
|
+
`/v1/workspaces/${workspaceId}/model-catalog`
|
|
1305
|
+
);
|
|
1306
|
+
}
|
|
1149
1307
|
/** The caller's access context: subject, account + workspace grants, defaults. */
|
|
1150
1308
|
async getAccessContext() {
|
|
1151
1309
|
return await this.requestJson("GET", "/v1/access/me");
|
|
@@ -1476,6 +1634,71 @@ var OpenGeniClient = class {
|
|
|
1476
1634
|
`/v1/workspaces/${workspaceId}/files/${fileId}`
|
|
1477
1635
|
);
|
|
1478
1636
|
}
|
|
1637
|
+
/** Read provider-neutral retained evidence metadata; never returns a storage location. */
|
|
1638
|
+
async getRetainedArtifact(workspaceId, artifactId) {
|
|
1639
|
+
return await this.requestJson(
|
|
1640
|
+
"GET",
|
|
1641
|
+
`/v1/workspaces/${workspaceId}/artifacts/${artifactId}`
|
|
1642
|
+
);
|
|
1643
|
+
}
|
|
1644
|
+
/**
|
|
1645
|
+
* Read at most one authenticated retained-evidence range from the API. This
|
|
1646
|
+
* deliberately does not use the ordinary signed file-download URL.
|
|
1647
|
+
*/
|
|
1648
|
+
async getRetainedArtifactContent(workspaceId, artifactId, options = {}) {
|
|
1649
|
+
if (options.range && (options.range.length > 128 || /[^\x20-\x7e]/.test(options.range))) {
|
|
1650
|
+
throw new RangeError("retained artifact range must be at most 128 printable ASCII bytes");
|
|
1651
|
+
}
|
|
1652
|
+
const response = await this.fetchImpl(
|
|
1653
|
+
this.url(`/v1/workspaces/${workspaceId}/artifacts/${artifactId}/content`),
|
|
1654
|
+
{
|
|
1655
|
+
method: "GET",
|
|
1656
|
+
headers: {
|
|
1657
|
+
...this.headers(),
|
|
1658
|
+
Accept: "application/octet-stream",
|
|
1659
|
+
...options.range ? { Range: options.range } : {}
|
|
1660
|
+
},
|
|
1661
|
+
...options.signal ? { signal: options.signal } : {}
|
|
1662
|
+
}
|
|
1663
|
+
);
|
|
1664
|
+
try {
|
|
1665
|
+
assertApiContractResponse(response);
|
|
1666
|
+
} catch (error) {
|
|
1667
|
+
await cancelResponseBody(response, "retained artifact API contract mismatch");
|
|
1668
|
+
throw error;
|
|
1669
|
+
}
|
|
1670
|
+
if (!response.ok) {
|
|
1671
|
+
throw new OpenGeniApiError(response.status, await safeBoundedText(response));
|
|
1672
|
+
}
|
|
1673
|
+
if (response.status !== 200 && response.status !== 206) {
|
|
1674
|
+
await cancelResponseBody(response, "unexpected retained artifact response status");
|
|
1675
|
+
throw new OpenGeniApiError(response.status, "unexpected retained artifact response status");
|
|
1676
|
+
}
|
|
1677
|
+
if (response.headers.get("accept-ranges") !== "bytes") {
|
|
1678
|
+
await cancelResponseBody(response, "retained artifact response omitted byte-range support");
|
|
1679
|
+
throw new OpenGeniApiError(502, "retained artifact response omitted byte-range support");
|
|
1680
|
+
}
|
|
1681
|
+
let declaredLength;
|
|
1682
|
+
try {
|
|
1683
|
+
declaredLength = parseBoundedContentLength(response.headers.get("content-length"));
|
|
1684
|
+
} catch (error) {
|
|
1685
|
+
await cancelResponseBody(response, "invalid retained artifact content-length");
|
|
1686
|
+
throw error;
|
|
1687
|
+
}
|
|
1688
|
+
const bytes = await readBoundedResponseBytes(
|
|
1689
|
+
response,
|
|
1690
|
+
RETAINED_OUTPUT_MAX_PAGE_BYTES,
|
|
1691
|
+
declaredLength
|
|
1692
|
+
);
|
|
1693
|
+
return {
|
|
1694
|
+
bytes,
|
|
1695
|
+
status: response.status,
|
|
1696
|
+
contentType: response.headers.get("content-type") ?? "application/octet-stream",
|
|
1697
|
+
contentLength: bytes.byteLength,
|
|
1698
|
+
contentRange: response.headers.get("content-range"),
|
|
1699
|
+
acceptRanges: "bytes"
|
|
1700
|
+
};
|
|
1701
|
+
}
|
|
1479
1702
|
/** Mint a short-lived signed download URL for a ready file. */
|
|
1480
1703
|
async createFileDownloadUrl(workspaceId, fileId) {
|
|
1481
1704
|
return await this.requestJson(
|
|
@@ -1727,14 +1950,13 @@ var OpenGeniClient = class {
|
|
|
1727
1950
|
return logoAssetPath ? `${this.baseUrl}/v1/${logoAssetPath}` : null;
|
|
1728
1951
|
}
|
|
1729
1952
|
// --- GitHub ----------------------------------------------------------------------------------
|
|
1730
|
-
/** GitHub App configuration status
|
|
1953
|
+
/** GitHub App configuration status; install/link URLs are null while new binding is disabled. */
|
|
1731
1954
|
async getGitHubApp(workspaceId) {
|
|
1732
1955
|
return await this.requestJson("GET", `/v1/workspaces/${workspaceId}/github/app`);
|
|
1733
1956
|
}
|
|
1734
1957
|
/**
|
|
1735
|
-
*
|
|
1736
|
-
*
|
|
1737
|
-
* `getGitHubApp().installUrl` or a github_connect_link tool.
|
|
1958
|
+
* Compatibility URL for previously issued state. New installation binding is
|
|
1959
|
+
* disabled, so the endpoint validates state and terminates with HTTP 410.
|
|
1738
1960
|
*/
|
|
1739
1961
|
githubConnectUrl(workspaceId, state) {
|
|
1740
1962
|
return this.url(`/v1/workspaces/${workspaceId}/github/connect`, { state });
|
|
@@ -1752,6 +1974,13 @@ var OpenGeniClient = class {
|
|
|
1752
1974
|
`/v1/workspaces/${workspaceId}/github/repositories/sync`
|
|
1753
1975
|
);
|
|
1754
1976
|
}
|
|
1977
|
+
/** Remove one workspace binding without uninstalling the GitHub App itself. */
|
|
1978
|
+
async unlinkGitHubInstallation(workspaceId, installationId) {
|
|
1979
|
+
await this.requestVoid(
|
|
1980
|
+
"DELETE",
|
|
1981
|
+
`/v1/workspaces/${workspaceId}/github/installations/${installationId}`
|
|
1982
|
+
);
|
|
1983
|
+
}
|
|
1755
1984
|
/** Build a GitHub App manifest + the GitHub URL to submit it to. */
|
|
1756
1985
|
async createGitHubAppManifest(workspaceId, request = {}) {
|
|
1757
1986
|
return await this.requestJson(
|
|
@@ -1863,6 +2092,13 @@ var OpenGeniClient = class {
|
|
|
1863
2092
|
`/v1/workspaces/${workspaceId}/codex/usage/refresh`
|
|
1864
2093
|
);
|
|
1865
2094
|
}
|
|
2095
|
+
/** Live independently-settled quota + reset-credit overview for every account. */
|
|
2096
|
+
async codexOverview(workspaceId) {
|
|
2097
|
+
return await this.requestJson(
|
|
2098
|
+
"GET",
|
|
2099
|
+
`/v1/workspaces/${workspaceId}/codex/overview`
|
|
2100
|
+
);
|
|
2101
|
+
}
|
|
1866
2102
|
/** Disconnect ALL accounts (legacy workspace-wide). Prefer `disconnectCodexAccount`. */
|
|
1867
2103
|
async codexDisconnect(workspaceId) {
|
|
1868
2104
|
return await this.requestJson(
|
|
@@ -1892,6 +2128,14 @@ var OpenGeniClient = class {
|
|
|
1892
2128
|
patch
|
|
1893
2129
|
);
|
|
1894
2130
|
}
|
|
2131
|
+
/** Toggle only NEW automatic allocations under independent allocator OCC. */
|
|
2132
|
+
async setCodexAccountAllocator(workspaceId, accountId, input) {
|
|
2133
|
+
return await this.requestJson(
|
|
2134
|
+
"PATCH",
|
|
2135
|
+
`/v1/workspaces/${workspaceId}/codex/accounts/${accountId}/allocator`,
|
|
2136
|
+
input
|
|
2137
|
+
);
|
|
2138
|
+
}
|
|
1895
2139
|
/** Disconnect ONE Codex account by id (re-picks active when the removed one was active). */
|
|
1896
2140
|
async disconnectCodexAccount(workspaceId, accountId) {
|
|
1897
2141
|
return await this.requestJson(
|
|
@@ -1915,7 +2159,7 @@ var OpenGeniClient = class {
|
|
|
1915
2159
|
{ target }
|
|
1916
2160
|
);
|
|
1917
2161
|
}
|
|
1918
|
-
async requestJson(method, path, body, query = {}) {
|
|
2162
|
+
async requestJson(method, path, body, query = {}, options = {}) {
|
|
1919
2163
|
const response = await this.fetchImpl(this.url(path, query), {
|
|
1920
2164
|
method,
|
|
1921
2165
|
headers: {
|
|
@@ -1923,7 +2167,8 @@ var OpenGeniClient = class {
|
|
|
1923
2167
|
Accept: "application/json",
|
|
1924
2168
|
...body !== void 0 ? { "Content-Type": "application/json" } : {}
|
|
1925
2169
|
},
|
|
1926
|
-
...body !== void 0 ? { body: JSON.stringify(body) } : {}
|
|
2170
|
+
...body !== void 0 ? { body: JSON.stringify(body) } : {},
|
|
2171
|
+
...options.signal ? { signal: options.signal } : {}
|
|
1927
2172
|
});
|
|
1928
2173
|
assertApiContractResponse(response);
|
|
1929
2174
|
if (!response.ok) {
|
|
@@ -1961,6 +2206,62 @@ async function safeText(response) {
|
|
|
1961
2206
|
return "";
|
|
1962
2207
|
}
|
|
1963
2208
|
}
|
|
2209
|
+
async function safeBoundedText(response) {
|
|
2210
|
+
try {
|
|
2211
|
+
return new TextDecoder().decode(await readBoundedResponseBytes(response, 64 * 1024, null));
|
|
2212
|
+
} catch {
|
|
2213
|
+
return "";
|
|
2214
|
+
}
|
|
2215
|
+
}
|
|
2216
|
+
async function cancelResponseBody(response, reason) {
|
|
2217
|
+
await response.body?.cancel(reason).catch(() => void 0);
|
|
2218
|
+
}
|
|
2219
|
+
function parseBoundedContentLength(value) {
|
|
2220
|
+
if (value === null) return null;
|
|
2221
|
+
if (!/^\d+$/.test(value)) {
|
|
2222
|
+
throw new OpenGeniApiError(502, "invalid retained artifact content-length");
|
|
2223
|
+
}
|
|
2224
|
+
const length = Number(value);
|
|
2225
|
+
if (!Number.isSafeInteger(length) || length > RETAINED_OUTPUT_MAX_PAGE_BYTES) {
|
|
2226
|
+
throw new OpenGeniApiError(502, "retained artifact response exceeds the SDK byte limit");
|
|
2227
|
+
}
|
|
2228
|
+
return length;
|
|
2229
|
+
}
|
|
2230
|
+
async function readBoundedResponseBytes(response, maxBytes, expectedBytes) {
|
|
2231
|
+
if (!response.body) {
|
|
2232
|
+
if (expectedBytes !== null && expectedBytes !== 0) {
|
|
2233
|
+
throw new OpenGeniApiError(502, "retained artifact response length mismatch");
|
|
2234
|
+
}
|
|
2235
|
+
return new Uint8Array();
|
|
2236
|
+
}
|
|
2237
|
+
const reader = response.body.getReader();
|
|
2238
|
+
const chunks = [];
|
|
2239
|
+
let totalBytes = 0;
|
|
2240
|
+
try {
|
|
2241
|
+
while (true) {
|
|
2242
|
+
const { done, value } = await reader.read();
|
|
2243
|
+
if (done) break;
|
|
2244
|
+
totalBytes += value.byteLength;
|
|
2245
|
+
if (totalBytes > maxBytes) {
|
|
2246
|
+
await reader.cancel("retained artifact response exceeded the SDK byte limit").catch(() => void 0);
|
|
2247
|
+
throw new OpenGeniApiError(502, "retained artifact response exceeds the SDK byte limit");
|
|
2248
|
+
}
|
|
2249
|
+
chunks.push(value);
|
|
2250
|
+
}
|
|
2251
|
+
} finally {
|
|
2252
|
+
reader.releaseLock();
|
|
2253
|
+
}
|
|
2254
|
+
if (expectedBytes !== null && totalBytes !== expectedBytes) {
|
|
2255
|
+
throw new OpenGeniApiError(502, "retained artifact response length mismatch");
|
|
2256
|
+
}
|
|
2257
|
+
const bytes = new Uint8Array(totalBytes);
|
|
2258
|
+
let offset = 0;
|
|
2259
|
+
for (const chunk of chunks) {
|
|
2260
|
+
bytes.set(chunk, offset);
|
|
2261
|
+
offset += chunk.byteLength;
|
|
2262
|
+
}
|
|
2263
|
+
return bytes;
|
|
2264
|
+
}
|
|
1964
2265
|
|
|
1965
2266
|
// src/proxy.ts
|
|
1966
2267
|
function formatSseEvent(event) {
|
|
@@ -2144,7 +2445,221 @@ function ttydInputFrame(data) {
|
|
|
2144
2445
|
function ttydResizeFrame(columns, rows) {
|
|
2145
2446
|
return TtydClientCommand.RESIZE + JSON.stringify({ columns, rows });
|
|
2146
2447
|
}
|
|
2448
|
+
|
|
2449
|
+
// src/transcription.ts
|
|
2450
|
+
var DEFAULT_WORKSPACE_TRANSCRIPTION_POLICY = {
|
|
2451
|
+
enabled: false,
|
|
2452
|
+
acceptanceId: null,
|
|
2453
|
+
primary: null,
|
|
2454
|
+
language: null,
|
|
2455
|
+
autoDetectLanguage: false,
|
|
2456
|
+
diarization: { enabled: false, maxSpeakers: null },
|
|
2457
|
+
retention: { mode: "none", maxDays: null },
|
|
2458
|
+
privacy: { allowProviderLogging: false, allowProviderTraining: false },
|
|
2459
|
+
fallback: { mode: "disabled", targets: [] },
|
|
2460
|
+
cost: { currency: "USD", maxPerHour: null, maxPerMonth: null }
|
|
2461
|
+
};
|
|
2462
|
+
function resolveWorkspaceTranscriptionPolicy(settings) {
|
|
2463
|
+
if (!isRecord(settings)) return cloneDefaultPolicy();
|
|
2464
|
+
const candidate = settings.transcription;
|
|
2465
|
+
if (!isWorkspaceTranscriptionPolicy(candidate)) return cloneDefaultPolicy();
|
|
2466
|
+
return {
|
|
2467
|
+
...candidate,
|
|
2468
|
+
primary: candidate.primary ? normalizeTarget(candidate.primary) : null,
|
|
2469
|
+
language: candidate.language?.trim() ?? null,
|
|
2470
|
+
diarization: { ...candidate.diarization },
|
|
2471
|
+
retention: { ...candidate.retention },
|
|
2472
|
+
privacy: { ...candidate.privacy },
|
|
2473
|
+
fallback: {
|
|
2474
|
+
mode: candidate.fallback.mode,
|
|
2475
|
+
targets: candidate.fallback.targets.map(normalizeTarget)
|
|
2476
|
+
},
|
|
2477
|
+
cost: { ...candidate.cost }
|
|
2478
|
+
};
|
|
2479
|
+
}
|
|
2480
|
+
function authorizeTranscriptionAdapter(policy, descriptor, selection = { kind: "primary" }) {
|
|
2481
|
+
if (!isWorkspaceTranscriptionPolicy(policy)) {
|
|
2482
|
+
return { authorized: false, reason: "unaccepted" };
|
|
2483
|
+
}
|
|
2484
|
+
if (!policy.enabled) return { authorized: false, reason: "disabled" };
|
|
2485
|
+
if (!policy.acceptanceId) return { authorized: false, reason: "unaccepted" };
|
|
2486
|
+
let target;
|
|
2487
|
+
if (selection.kind === "primary") {
|
|
2488
|
+
target = policy.primary;
|
|
2489
|
+
} else {
|
|
2490
|
+
if (policy.fallback.mode !== "explicit") {
|
|
2491
|
+
return { authorized: false, reason: "fallback_disabled" };
|
|
2492
|
+
}
|
|
2493
|
+
target = policy.fallback.targets[selection.index];
|
|
2494
|
+
if (!target) return { authorized: false, reason: "fallback_unaccepted" };
|
|
2495
|
+
}
|
|
2496
|
+
if (!target) return { authorized: false, reason: "target_missing" };
|
|
2497
|
+
const acceptedTarget = normalizeTarget(target);
|
|
2498
|
+
if (acceptedTarget.provider !== descriptor.provider) {
|
|
2499
|
+
return { authorized: false, reason: "provider_mismatch" };
|
|
2500
|
+
}
|
|
2501
|
+
if (acceptedTarget.model !== descriptor.model) {
|
|
2502
|
+
return { authorized: false, reason: "model_mismatch" };
|
|
2503
|
+
}
|
|
2504
|
+
if (acceptedTarget.credentialMode !== descriptor.credentialMode) {
|
|
2505
|
+
return { authorized: false, reason: "credential_mode_mismatch" };
|
|
2506
|
+
}
|
|
2507
|
+
if (acceptedTarget.region !== descriptor.region) {
|
|
2508
|
+
return { authorized: false, reason: "region_mismatch" };
|
|
2509
|
+
}
|
|
2510
|
+
return {
|
|
2511
|
+
authorized: true,
|
|
2512
|
+
acceptanceId: policy.acceptanceId,
|
|
2513
|
+
target: acceptedTarget,
|
|
2514
|
+
selection
|
|
2515
|
+
};
|
|
2516
|
+
}
|
|
2517
|
+
function createTranscriptionSessionRequest(input) {
|
|
2518
|
+
const sequenceFloor = input.sequenceFloor ?? 0;
|
|
2519
|
+
if (!Number.isSafeInteger(sequenceFloor) || sequenceFloor < 0) return null;
|
|
2520
|
+
const authorization = authorizeTranscriptionAdapter(
|
|
2521
|
+
input.policy,
|
|
2522
|
+
input.adapter.descriptor,
|
|
2523
|
+
input.selection
|
|
2524
|
+
);
|
|
2525
|
+
if (!authorization.authorized) return null;
|
|
2526
|
+
return {
|
|
2527
|
+
localSessionId: input.localSessionId,
|
|
2528
|
+
policyAcceptanceId: authorization.acceptanceId,
|
|
2529
|
+
selection: authorization.selection,
|
|
2530
|
+
target: { ...authorization.target },
|
|
2531
|
+
language: input.policy.language?.trim() ?? null,
|
|
2532
|
+
autoDetectLanguage: input.policy.autoDetectLanguage,
|
|
2533
|
+
diarization: { ...input.policy.diarization },
|
|
2534
|
+
retention: { ...input.policy.retention },
|
|
2535
|
+
privacy: { ...input.policy.privacy },
|
|
2536
|
+
cost: { ...input.policy.cost },
|
|
2537
|
+
sequenceFloor
|
|
2538
|
+
};
|
|
2539
|
+
}
|
|
2540
|
+
function cloneDefaultPolicy() {
|
|
2541
|
+
return {
|
|
2542
|
+
...DEFAULT_WORKSPACE_TRANSCRIPTION_POLICY,
|
|
2543
|
+
diarization: { ...DEFAULT_WORKSPACE_TRANSCRIPTION_POLICY.diarization },
|
|
2544
|
+
retention: { ...DEFAULT_WORKSPACE_TRANSCRIPTION_POLICY.retention },
|
|
2545
|
+
privacy: { ...DEFAULT_WORKSPACE_TRANSCRIPTION_POLICY.privacy },
|
|
2546
|
+
fallback: { mode: "disabled", targets: [] },
|
|
2547
|
+
cost: { ...DEFAULT_WORKSPACE_TRANSCRIPTION_POLICY.cost }
|
|
2548
|
+
};
|
|
2549
|
+
}
|
|
2550
|
+
function isWorkspaceTranscriptionPolicy(value) {
|
|
2551
|
+
if (!isRecord(value) || typeof value.enabled !== "boolean") return false;
|
|
2552
|
+
if (!hasOnlyKeys(value, [
|
|
2553
|
+
"enabled",
|
|
2554
|
+
"acceptanceId",
|
|
2555
|
+
"primary",
|
|
2556
|
+
"language",
|
|
2557
|
+
"autoDetectLanguage",
|
|
2558
|
+
"diarization",
|
|
2559
|
+
"retention",
|
|
2560
|
+
"privacy",
|
|
2561
|
+
"fallback",
|
|
2562
|
+
"cost"
|
|
2563
|
+
])) {
|
|
2564
|
+
return false;
|
|
2565
|
+
}
|
|
2566
|
+
if (!(value.acceptanceId === null || isUuid(value.acceptanceId))) return false;
|
|
2567
|
+
if (!(value.primary === null || isTarget(value.primary))) return false;
|
|
2568
|
+
if (!(value.language === null || isBoundedString(value.language, 64))) return false;
|
|
2569
|
+
if (typeof value.autoDetectLanguage !== "boolean") return false;
|
|
2570
|
+
if (!isRecord(value.diarization) || !hasOnlyKeys(value.diarization, ["enabled", "maxSpeakers"]) || typeof value.diarization.enabled !== "boolean" || !(value.diarization.maxSpeakers === null || isBoundedInteger(value.diarization.maxSpeakers, 100) && value.diarization.maxSpeakers >= 2)) {
|
|
2571
|
+
return false;
|
|
2572
|
+
}
|
|
2573
|
+
if (!value.diarization.enabled && value.diarization.maxSpeakers !== null) return false;
|
|
2574
|
+
if (!isRecord(value.retention) || !hasOnlyKeys(value.retention, ["mode", "maxDays"])) {
|
|
2575
|
+
return false;
|
|
2576
|
+
}
|
|
2577
|
+
if (value.retention.mode !== "none" && value.retention.mode !== "provider-policy") return false;
|
|
2578
|
+
if (!(value.retention.maxDays === null || isBoundedInteger(value.retention.maxDays, 3650))) {
|
|
2579
|
+
return false;
|
|
2580
|
+
}
|
|
2581
|
+
if (!isRecord(value.privacy) || !hasOnlyKeys(value.privacy, ["allowProviderLogging", "allowProviderTraining"]) || typeof value.privacy.allowProviderLogging !== "boolean" || typeof value.privacy.allowProviderTraining !== "boolean") {
|
|
2582
|
+
return false;
|
|
2583
|
+
}
|
|
2584
|
+
if (!isRecord(value.fallback) || !hasOnlyKeys(value.fallback, ["mode", "targets"])) {
|
|
2585
|
+
return false;
|
|
2586
|
+
}
|
|
2587
|
+
if (value.fallback.mode !== "disabled" && value.fallback.mode !== "explicit") return false;
|
|
2588
|
+
if (!Array.isArray(value.fallback.targets) || value.fallback.targets.length > 8 || !value.fallback.targets.every(isTarget)) {
|
|
2589
|
+
return false;
|
|
2590
|
+
}
|
|
2591
|
+
if (value.fallback.mode === "disabled" && value.fallback.targets.length !== 0) return false;
|
|
2592
|
+
if (value.fallback.mode === "explicit" && value.fallback.targets.length === 0) return false;
|
|
2593
|
+
if (!isRecord(value.cost) || !hasOnlyKeys(value.cost, ["currency", "maxPerHour", "maxPerMonth"]) || value.cost.currency !== "USD") {
|
|
2594
|
+
return false;
|
|
2595
|
+
}
|
|
2596
|
+
if (!isNullableBoundedNumber(value.cost.maxPerHour, 1e4)) return false;
|
|
2597
|
+
if (!isNullableBoundedNumber(value.cost.maxPerMonth, 1e6)) return false;
|
|
2598
|
+
if (value.enabled && (!value.acceptanceId || !value.primary)) return false;
|
|
2599
|
+
if (value.enabled && !value.autoDetectLanguage && value.language === null) return false;
|
|
2600
|
+
if (value.autoDetectLanguage && value.language !== null) return false;
|
|
2601
|
+
const targets = [value.primary, ...value.fallback.targets].filter(
|
|
2602
|
+
(target) => target !== null
|
|
2603
|
+
);
|
|
2604
|
+
if (new Set(targets.map(targetKey)).size !== targets.length) return false;
|
|
2605
|
+
return true;
|
|
2606
|
+
}
|
|
2607
|
+
function targetKey(target) {
|
|
2608
|
+
return [
|
|
2609
|
+
target.provider.trim(),
|
|
2610
|
+
target.model?.trim() ?? "",
|
|
2611
|
+
target.credentialMode,
|
|
2612
|
+
target.credentialConnectionId ?? "",
|
|
2613
|
+
target.region?.trim() ?? ""
|
|
2614
|
+
].join("\0");
|
|
2615
|
+
}
|
|
2616
|
+
function isTarget(value) {
|
|
2617
|
+
if (!isRecord(value)) return false;
|
|
2618
|
+
if (!hasOnlyKeys(value, ["provider", "model", "credentialMode", "credentialConnectionId", "region"])) {
|
|
2619
|
+
return false;
|
|
2620
|
+
}
|
|
2621
|
+
if (!isBoundedString(value.provider, 128)) return false;
|
|
2622
|
+
if (!(value.model === null || isBoundedString(value.model, 256))) return false;
|
|
2623
|
+
if (value.credentialMode !== "managed" && value.credentialMode !== "byok") return false;
|
|
2624
|
+
if (value.provider.trim() === "azure-speech" && value.credentialMode !== "byok") return false;
|
|
2625
|
+
if (!(value.credentialConnectionId === null || isUuid(value.credentialConnectionId))) {
|
|
2626
|
+
return false;
|
|
2627
|
+
}
|
|
2628
|
+
if (!(value.region === null || isBoundedString(value.region, 128))) return false;
|
|
2629
|
+
if (value.credentialMode === "byok" && value.credentialConnectionId === null) return false;
|
|
2630
|
+
if (value.credentialMode === "managed" && value.credentialConnectionId !== null) return false;
|
|
2631
|
+
return true;
|
|
2632
|
+
}
|
|
2633
|
+
function normalizeTarget(target) {
|
|
2634
|
+
return {
|
|
2635
|
+
provider: target.provider.trim(),
|
|
2636
|
+
model: target.model?.trim() ?? null,
|
|
2637
|
+
credentialMode: target.credentialMode,
|
|
2638
|
+
credentialConnectionId: target.credentialConnectionId,
|
|
2639
|
+
region: target.region?.trim() ?? null
|
|
2640
|
+
};
|
|
2641
|
+
}
|
|
2642
|
+
function isRecord(value) {
|
|
2643
|
+
return typeof value === "object" && value !== null;
|
|
2644
|
+
}
|
|
2645
|
+
function hasOnlyKeys(value, keys) {
|
|
2646
|
+
const accepted = new Set(keys);
|
|
2647
|
+
return Object.keys(value).every((key) => accepted.has(key));
|
|
2648
|
+
}
|
|
2649
|
+
function isBoundedString(value, maximum) {
|
|
2650
|
+
return typeof value === "string" && value.trim().length > 0 && value.length <= maximum;
|
|
2651
|
+
}
|
|
2652
|
+
function isUuid(value) {
|
|
2653
|
+
return typeof value === "string" && /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(value);
|
|
2654
|
+
}
|
|
2655
|
+
function isBoundedInteger(value, maximum) {
|
|
2656
|
+
return Number.isInteger(value) && value >= 0 && value <= maximum;
|
|
2657
|
+
}
|
|
2658
|
+
function isNullableBoundedNumber(value, maximum) {
|
|
2659
|
+
return value === null || typeof value === "number" && Number.isFinite(value) && value >= 0 && value <= maximum;
|
|
2660
|
+
}
|
|
2147
2661
|
export {
|
|
2662
|
+
DEFAULT_WORKSPACE_TRANSCRIPTION_POLICY,
|
|
2148
2663
|
KNOWN_PERMISSIONS,
|
|
2149
2664
|
KNOWN_USAGE_EVENT_TYPES,
|
|
2150
2665
|
OPENGENI_API_CONTRACT_HEADER,
|
|
@@ -2153,17 +2668,22 @@ export {
|
|
|
2153
2668
|
OpenGeniApiError,
|
|
2154
2669
|
OpenGeniClient,
|
|
2155
2670
|
OpenGeniStreamError,
|
|
2671
|
+
RETAINED_OUTPUT_DEFAULT_PAGE_BYTES,
|
|
2672
|
+
RETAINED_OUTPUT_MAX_PAGE_BYTES,
|
|
2156
2673
|
SESSION_EVENT_TYPES,
|
|
2157
2674
|
TTYD_SUBPROTOCOL,
|
|
2158
2675
|
TtydClientCommand,
|
|
2159
2676
|
TtydServerCommand,
|
|
2160
2677
|
applyUrlRotation,
|
|
2678
|
+
authorizeTranscriptionAdapter,
|
|
2679
|
+
createTranscriptionSessionRequest,
|
|
2161
2680
|
desktopSocketUrl,
|
|
2162
2681
|
formatSseEvent,
|
|
2163
2682
|
isRetryableStreamError,
|
|
2164
2683
|
nextDesktopState,
|
|
2165
2684
|
parseSseStream,
|
|
2166
2685
|
proxySessionEventStream,
|
|
2686
|
+
resolveWorkspaceTranscriptionPolicy,
|
|
2167
2687
|
resumeSequenceFromRequest,
|
|
2168
2688
|
sessionEventsToSseResponse,
|
|
2169
2689
|
sessionEventsToSseStream,
|