@opengeni/sdk 0.15.0 → 0.20.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/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",
@@ -317,6 +321,7 @@ var SESSION_EVENT_TYPES = [
317
321
  "agent.toolCall.output",
318
322
  "agent.model.usage",
319
323
  "tool.auth_needed",
324
+ "credential.auth_needed",
320
325
  "agent.updated",
321
326
  "rig.setup.started",
322
327
  "rig.setup.completed",
@@ -367,9 +372,9 @@ var SESSION_EVENT_TYPES = [
367
372
  "session.title_set",
368
373
  // Multi-account Codex (P1): the session's inference account changed.
369
374
  "codex.account.switched",
370
- // OPE-21 metadata-only per-turn credential selection audit.
375
+ // credential allocator metadata-only per-turn credential selection audit.
371
376
  "codex.credential.selected",
372
- // OPE-21 durable zero-capacity wait lifecycle. These are system/runtime
377
+ // credential allocator durable zero-capacity wait lifecycle. These are system/runtime
373
378
  // events, never synthetic user messages.
374
379
  "codex.capacity.waiting",
375
380
  "codex.capacity.resumed",
@@ -410,7 +415,7 @@ var KNOWN_PERMISSIONS = [
410
415
  "sessions:create",
411
416
  "sessions:read",
412
417
  "sessions:control",
413
- // Sandbox-surfacing (mirror of @opengeni/contracts Permission). stream:view is
418
+ // sandbox workspace (mirror of @opengeni/contracts Permission). stream:view is
414
419
  // strictly broader than sessions:read (un-redacted pixels); stream:control is
415
420
  // the never-granted-v1 raw-input plane; stream:acknowledge is the secret-leak
416
421
  // consent gate.
@@ -442,7 +447,7 @@ var KNOWN_PERMISSIONS = [
442
447
  "rigs:use",
443
448
  "rigs:manage"
444
449
  ];
445
- var OPENGENI_API_CONTRACT_REVISION = "2026-07-session-control-v1";
450
+ var OPENGENI_API_CONTRACT_REVISION = "2026-07-turn-instructions-v1";
446
451
  var OPENGENI_API_CONTRACT_HEADER = "x-opengeni-api-contract";
447
452
  var KNOWN_USAGE_EVENT_TYPES = [
448
453
  "agent_run.created",
@@ -559,7 +564,8 @@ var OpenGeniClient = class {
559
564
  void 0,
560
565
  {
561
566
  ...options.sessionId !== void 0 ? { sessionId: options.sessionId } : {}
562
- }
567
+ },
568
+ { signal: options.signal }
563
569
  );
564
570
  }
565
571
  /**
@@ -659,23 +665,75 @@ var OpenGeniClient = class {
659
665
  }
660
666
  // --- Events: replay, send, stream ----------------------------------------
661
667
  /**
662
- * Replay durable events by sequence, ascending. `before` is exclusive and
663
- * returns the newest matching window. With `compact`, consecutive delta runs
664
- * may be coalesced; `payload.coalescedUntil` carries the run's last sequence
665
- * for resume cursors.
668
+ * Return the events from one bounded page. With no cursor, this uses the safe
669
+ * semantic monitoring tail; pass explicit forensic options and a cursor for
670
+ * retained audit replay. Use `listEventPage` when projection, coverage, or
671
+ * resume-cursor facts are required.
666
672
  */
667
673
  async listEvents(workspaceId, sessionId, options = {}) {
668
- return await this.requestJson(
669
- "GET",
670
- `/v1/workspaces/${workspaceId}/sessions/${sessionId}/events`,
671
- void 0,
672
- {
674
+ return (await this.listEventPage(workspaceId, sessionId, options)).events;
675
+ }
676
+ /** Bounded durable/monitoring page plus exact projection and cursor facts. */
677
+ async listEventPage(workspaceId, sessionId, options = {}) {
678
+ if (options.latest && ["includeTypes", "excludeTypes", "includeClasses", "excludeClasses"].some(
679
+ (name) => Object.prototype.hasOwnProperty.call(options, name)
680
+ )) {
681
+ throw new TypeError("latest cannot be combined with event filters");
682
+ }
683
+ const response = await this.fetchImpl(
684
+ this.url(`/v1/workspaces/${workspaceId}/sessions/${sessionId}/events`, {
673
685
  ...options.after !== void 0 ? { after: String(options.after) } : {},
674
686
  ...options.before !== void 0 ? { before: String(options.before) } : {},
675
687
  ...options.limit !== void 0 ? { limit: String(options.limit) } : {},
676
- ...options.compact ? { compact: "1" } : {}
688
+ ...options.compact ? { compact: "1" } : {},
689
+ ...options.mode ? { mode: options.mode } : {},
690
+ ...options.direction ? { direction: options.direction } : {},
691
+ ...options.payloadMode ? { payloadMode: options.payloadMode } : {},
692
+ ...options.includeTypes?.length ? { includeTypes: options.includeTypes.join(",") } : {},
693
+ ...options.excludeTypes?.length ? { excludeTypes: options.excludeTypes.join(",") } : {},
694
+ ...options.includeClasses?.length ? { includeClasses: options.includeClasses.join(",") } : {},
695
+ ...options.excludeClasses?.length ? { excludeClasses: options.excludeClasses.join(",") } : {},
696
+ ...options.latest ? { latest: options.latest } : {}
697
+ }),
698
+ {
699
+ method: "GET",
700
+ headers: { ...this.headers(), Accept: "application/json" }
677
701
  }
678
702
  );
703
+ assertApiContractResponse(response);
704
+ if (!response.ok) throw new OpenGeniApiError(response.status, await safeText(response));
705
+ const events = await response.json();
706
+ const integerHeader = (name) => {
707
+ const raw = response.headers.get(name);
708
+ if (raw === null) return null;
709
+ const value = Number(raw);
710
+ return Number.isSafeInteger(value) && value >= 0 ? value : null;
711
+ };
712
+ const mode = response.headers.get("X-OpenGeni-Event-Mode") === "forensic" ? "forensic" : "monitoring";
713
+ const direction = response.headers.get("X-OpenGeni-Event-Direction") === "after" ? "after" : "before";
714
+ const payloadHeader = response.headers.get("X-OpenGeni-Payload-Mode");
715
+ const payloadMode = payloadHeader === "none" || payloadHeader === "full" ? payloadHeader : "summary";
716
+ const first = integerHeader("X-OpenGeni-Covered-First");
717
+ const last = integerHeader("X-OpenGeni-Covered-Last");
718
+ const bytes = integerHeader("X-OpenGeni-Page-Bytes") ?? new TextEncoder().encode(JSON.stringify(events)).byteLength;
719
+ const maxBytes = integerHeader("X-OpenGeni-Page-Max-Bytes") ?? 1024 * 1024;
720
+ const truncatedByHeader = response.headers.get("X-OpenGeni-Truncated-By");
721
+ const truncatedBy = truncatedByHeader === "count" || truncatedByHeader === "bytes" || truncatedByHeader === "http_bytes" ? truncatedByHeader : null;
722
+ return {
723
+ events,
724
+ mode,
725
+ payloadMode,
726
+ direction,
727
+ bytes,
728
+ maxBytes,
729
+ truncated: response.headers.get("X-OpenGeni-Page-Truncated") === "true",
730
+ hasMore: response.headers.get("X-OpenGeni-Has-More") === "true",
731
+ truncatedBy,
732
+ coveredSequence: first === null || last === null ? null : { first, last },
733
+ nextAfter: integerHeader("X-OpenGeni-Next-After"),
734
+ nextBefore: integerHeader("X-OpenGeni-Next-Before"),
735
+ forensicExact: response.headers.get("X-OpenGeni-Forensic-Exact") === "true"
736
+ };
679
737
  }
680
738
  /** POST a user/control event to the session. Returns the accepted event. */
681
739
  async sendEvent(workspaceId, sessionId, event) {
@@ -710,6 +768,28 @@ var OpenGeniClient = class {
710
768
  payload
711
769
  });
712
770
  }
771
+ async listHumanInputRequests(workspaceId, sessionId, options = {}) {
772
+ const result = await this.requestJson(
773
+ "GET",
774
+ `/v1/workspaces/${workspaceId}/sessions/${sessionId}/human-input-requests`,
775
+ void 0,
776
+ options.status ? { status: options.status } : void 0
777
+ );
778
+ return result.requests;
779
+ }
780
+ async getHumanInputRequest(workspaceId, sessionId, requestId) {
781
+ return await this.requestJson(
782
+ "GET",
783
+ `/v1/workspaces/${workspaceId}/sessions/${sessionId}/human-input-requests/${requestId}`
784
+ );
785
+ }
786
+ async submitHumanInputResponse(workspaceId, sessionId, requestId, response, options = {}) {
787
+ return await this.sendEvent(workspaceId, sessionId, {
788
+ type: "user.humanInputResponse",
789
+ ...options.clientEventId ? { clientEventId: options.clientEventId } : {},
790
+ payload: { requestId, response }
791
+ });
792
+ }
713
793
  /**
714
794
  * Live-stream a session's events with automatic reconnect, resume from the
715
795
  * last seen sequence, gap backfill, and duplicate suppression. See
@@ -818,15 +898,35 @@ var OpenGeniClient = class {
818
898
  );
819
899
  }
820
900
  async listWorkspaceControlEvents(workspaceId, options = {}) {
821
- return await this.requestJson(
822
- "GET",
823
- `/v1/workspaces/${workspaceId}/control-events`,
824
- void 0,
825
- {
901
+ return (await this.listWorkspaceControlEventPage(workspaceId, options)).events;
902
+ }
903
+ /** Count/byte-bounded page plus an explicit continuation cursor. */
904
+ async listWorkspaceControlEventPage(workspaceId, options = {}) {
905
+ const response = await this.fetchImpl(
906
+ this.url(`/v1/workspaces/${workspaceId}/control-events`, {
826
907
  ...options.after !== void 0 ? { after: String(options.after) } : {},
827
908
  ...options.limit !== void 0 ? { limit: String(options.limit) } : {}
909
+ }),
910
+ {
911
+ method: "GET",
912
+ headers: { ...this.headers(), Accept: "application/json" }
828
913
  }
829
914
  );
915
+ assertApiContractResponse(response);
916
+ if (!response.ok) {
917
+ throw new OpenGeniApiError(response.status, await safeText(response));
918
+ }
919
+ const events = await response.json();
920
+ const bytesHeader = response.headers.get("X-OpenGeni-Page-Bytes");
921
+ const nextHeader = response.headers.get("X-OpenGeni-Next-After");
922
+ const parsedBytes = bytesHeader === null ? Number.NaN : Number(bytesHeader);
923
+ const parsedNext = nextHeader === null ? null : Number(nextHeader);
924
+ return {
925
+ events,
926
+ bytes: Number.isSafeInteger(parsedBytes) && parsedBytes >= 0 ? parsedBytes : new TextEncoder().encode(JSON.stringify(events)).byteLength,
927
+ truncated: response.headers.get("X-OpenGeni-Page-Truncated") === "true",
928
+ nextAfter: parsedNext !== null && Number.isSafeInteger(parsedNext) && parsedNext >= 0 ? parsedNext : null
929
+ };
830
930
  }
831
931
  streamWorkspaceControlEvents(workspaceId, options = {}) {
832
932
  return streamWorkspaceControlEvents(this.workspaceControlStreamTransport(workspaceId), options);
@@ -926,19 +1026,23 @@ var OpenGeniClient = class {
926
1026
  // synchronous API-direct point query; the fs.changed/git.changed/terminal.pty.*
927
1027
  // notifications + the PTY output stream arrive on the existing event SSE.
928
1028
  /** FileSystem: list a directory tree (feeds the Pierre file tree). */
929
- async fsList(workspaceId, sessionId, request = {}) {
1029
+ async fsList(workspaceId, sessionId, request = {}, options = {}) {
930
1030
  return await this.requestJson(
931
1031
  "POST",
932
1032
  `/v1/workspaces/${workspaceId}/sessions/${sessionId}/fs/list`,
933
- request
1033
+ request,
1034
+ {},
1035
+ options
934
1036
  );
935
1037
  }
936
1038
  /** FileSystem: read a file (text or base64; binary-safe, size-capped). */
937
- async fsRead(workspaceId, sessionId, request) {
1039
+ async fsRead(workspaceId, sessionId, request, options = {}) {
938
1040
  return await this.requestJson(
939
1041
  "POST",
940
1042
  `/v1/workspaces/${workspaceId}/sessions/${sessionId}/fs/read`,
941
- request
1043
+ request,
1044
+ {},
1045
+ options
942
1046
  );
943
1047
  }
944
1048
  /** FileSystem: write a file (last-writer-wins; emits fs.changed). */
@@ -974,19 +1078,23 @@ var OpenGeniClient = class {
974
1078
  );
975
1079
  }
976
1080
  /** Git: working-tree/index status (the Pierre file-status feed). */
977
- async gitStatus(workspaceId, sessionId, request = {}) {
1081
+ async gitStatus(workspaceId, sessionId, request = {}, options = {}) {
978
1082
  return await this.requestJson(
979
1083
  "POST",
980
1084
  `/v1/workspaces/${workspaceId}/sessions/${sessionId}/git/status`,
981
- request
1085
+ request,
1086
+ {},
1087
+ options
982
1088
  );
983
1089
  }
984
1090
  /** Git: structured diff hunks (the Pierre diff feed). */
985
- async gitDiff(workspaceId, sessionId, request = {}) {
1091
+ async gitDiff(workspaceId, sessionId, request = {}, options = {}) {
986
1092
  return await this.requestJson(
987
1093
  "POST",
988
1094
  `/v1/workspaces/${workspaceId}/sessions/${sessionId}/git/diff`,
989
- request
1095
+ request,
1096
+ {},
1097
+ options
990
1098
  );
991
1099
  }
992
1100
  /** Git: commit log. */
@@ -1009,23 +1117,27 @@ var OpenGeniClient = class {
1009
1117
  * (tree + per-repo diff + file after-image refs), served from durable storage
1010
1118
  * WITHOUT warming a machine — the workbench cold-paint source. Returns
1011
1119
  * `{available:false}` when no capture exists yet (fall back to the live path). */
1012
- async getWorkspaceCapture(workspaceId, sessionId) {
1120
+ async getWorkspaceCapture(workspaceId, sessionId, options = {}) {
1013
1121
  return await this.requestJson(
1014
1122
  "GET",
1015
- `/v1/workspaces/${workspaceId}/sessions/${sessionId}/workspace/capture`
1123
+ `/v1/workspaces/${workspaceId}/sessions/${sessionId}/workspace/capture`,
1124
+ void 0,
1125
+ {},
1126
+ options
1016
1127
  );
1017
1128
  }
1018
1129
  /** Workspace capture: a single file's after-image from the capture (revision
1019
1130
  * pins a specific one; omitted → latest). Content is inline for small files,
1020
1131
  * else a short-TTL signed URL; a tooLarge file returns metadata only. */
1021
- async getWorkspaceCaptureFile(workspaceId, sessionId, path, revision) {
1132
+ async getWorkspaceCaptureFile(workspaceId, sessionId, path, revision, options = {}) {
1022
1133
  const query = { path };
1023
1134
  if (revision !== void 0) query.revision = String(revision);
1024
1135
  return await this.requestJson(
1025
1136
  "GET",
1026
1137
  `/v1/workspaces/${workspaceId}/sessions/${sessionId}/workspace/capture/file`,
1027
1138
  void 0,
1028
- query
1139
+ query,
1140
+ options
1029
1141
  );
1030
1142
  }
1031
1143
  /** Terminal: run a bounded command, returning buffered stdout/stderr inline. */
@@ -1080,10 +1192,13 @@ var OpenGeniClient = class {
1080
1192
  * liveness the client polls on while `cold`/`warming`. The desktop URL/token
1081
1193
  * are minted in-process only when the box is warm AND the principal has
1082
1194
  * acknowledged the un-redacted plane. */
1083
- async getStreamCapabilities(workspaceId, sessionId) {
1195
+ async getStreamCapabilities(workspaceId, sessionId, options = {}) {
1084
1196
  return await this.requestJson(
1085
1197
  "GET",
1086
- `/v1/workspaces/${workspaceId}/sessions/${sessionId}/stream-capabilities`
1198
+ `/v1/workspaces/${workspaceId}/sessions/${sessionId}/stream-capabilities`,
1199
+ void 0,
1200
+ {},
1201
+ options
1087
1202
  );
1088
1203
  }
1089
1204
  /** Record the calling principal's acknowledgment of the un-redacted desktop
@@ -1727,14 +1842,13 @@ var OpenGeniClient = class {
1727
1842
  return logoAssetPath ? `${this.baseUrl}/v1/${logoAssetPath}` : null;
1728
1843
  }
1729
1844
  // --- GitHub ----------------------------------------------------------------------------------
1730
- /** GitHub App configuration status + a signed install URL when configured. */
1845
+ /** GitHub App configuration status; install/link URLs are null while new binding is disabled. */
1731
1846
  async getGitHubApp(workspaceId) {
1732
1847
  return await this.requestJson("GET", `/v1/workspaces/${workspaceId}/github/app`);
1733
1848
  }
1734
1849
  /**
1735
- * Browser entry point that plants the CSRF cookie and forwards to GitHub's
1736
- * install page. Open this in a browser (it redirects); `state` comes from
1737
- * `getGitHubApp().installUrl` or a github_connect_link tool.
1850
+ * Compatibility URL for previously issued state. New installation binding is
1851
+ * disabled, so the endpoint validates state and terminates with HTTP 410.
1738
1852
  */
1739
1853
  githubConnectUrl(workspaceId, state) {
1740
1854
  return this.url(`/v1/workspaces/${workspaceId}/github/connect`, { state });
@@ -1752,6 +1866,13 @@ var OpenGeniClient = class {
1752
1866
  `/v1/workspaces/${workspaceId}/github/repositories/sync`
1753
1867
  );
1754
1868
  }
1869
+ /** Remove one workspace binding without uninstalling the GitHub App itself. */
1870
+ async unlinkGitHubInstallation(workspaceId, installationId) {
1871
+ await this.requestVoid(
1872
+ "DELETE",
1873
+ `/v1/workspaces/${workspaceId}/github/installations/${installationId}`
1874
+ );
1875
+ }
1755
1876
  /** Build a GitHub App manifest + the GitHub URL to submit it to. */
1756
1877
  async createGitHubAppManifest(workspaceId, request = {}) {
1757
1878
  return await this.requestJson(
@@ -1915,7 +2036,7 @@ var OpenGeniClient = class {
1915
2036
  { target }
1916
2037
  );
1917
2038
  }
1918
- async requestJson(method, path, body, query = {}) {
2039
+ async requestJson(method, path, body, query = {}, options = {}) {
1919
2040
  const response = await this.fetchImpl(this.url(path, query), {
1920
2041
  method,
1921
2042
  headers: {
@@ -1923,7 +2044,8 @@ var OpenGeniClient = class {
1923
2044
  Accept: "application/json",
1924
2045
  ...body !== void 0 ? { "Content-Type": "application/json" } : {}
1925
2046
  },
1926
- ...body !== void 0 ? { body: JSON.stringify(body) } : {}
2047
+ ...body !== void 0 ? { body: JSON.stringify(body) } : {},
2048
+ ...options.signal ? { signal: options.signal } : {}
1927
2049
  });
1928
2050
  assertApiContractResponse(response);
1929
2051
  if (!response.ok) {
@@ -2144,7 +2266,221 @@ function ttydInputFrame(data) {
2144
2266
  function ttydResizeFrame(columns, rows) {
2145
2267
  return TtydClientCommand.RESIZE + JSON.stringify({ columns, rows });
2146
2268
  }
2269
+
2270
+ // src/transcription.ts
2271
+ var DEFAULT_WORKSPACE_TRANSCRIPTION_POLICY = {
2272
+ enabled: false,
2273
+ acceptanceId: null,
2274
+ primary: null,
2275
+ language: null,
2276
+ autoDetectLanguage: false,
2277
+ diarization: { enabled: false, maxSpeakers: null },
2278
+ retention: { mode: "none", maxDays: null },
2279
+ privacy: { allowProviderLogging: false, allowProviderTraining: false },
2280
+ fallback: { mode: "disabled", targets: [] },
2281
+ cost: { currency: "USD", maxPerHour: null, maxPerMonth: null }
2282
+ };
2283
+ function resolveWorkspaceTranscriptionPolicy(settings) {
2284
+ if (!isRecord(settings)) return cloneDefaultPolicy();
2285
+ const candidate = settings.transcription;
2286
+ if (!isWorkspaceTranscriptionPolicy(candidate)) return cloneDefaultPolicy();
2287
+ return {
2288
+ ...candidate,
2289
+ primary: candidate.primary ? normalizeTarget(candidate.primary) : null,
2290
+ language: candidate.language?.trim() ?? null,
2291
+ diarization: { ...candidate.diarization },
2292
+ retention: { ...candidate.retention },
2293
+ privacy: { ...candidate.privacy },
2294
+ fallback: {
2295
+ mode: candidate.fallback.mode,
2296
+ targets: candidate.fallback.targets.map(normalizeTarget)
2297
+ },
2298
+ cost: { ...candidate.cost }
2299
+ };
2300
+ }
2301
+ function authorizeTranscriptionAdapter(policy, descriptor, selection = { kind: "primary" }) {
2302
+ if (!isWorkspaceTranscriptionPolicy(policy)) {
2303
+ return { authorized: false, reason: "unaccepted" };
2304
+ }
2305
+ if (!policy.enabled) return { authorized: false, reason: "disabled" };
2306
+ if (!policy.acceptanceId) return { authorized: false, reason: "unaccepted" };
2307
+ let target;
2308
+ if (selection.kind === "primary") {
2309
+ target = policy.primary;
2310
+ } else {
2311
+ if (policy.fallback.mode !== "explicit") {
2312
+ return { authorized: false, reason: "fallback_disabled" };
2313
+ }
2314
+ target = policy.fallback.targets[selection.index];
2315
+ if (!target) return { authorized: false, reason: "fallback_unaccepted" };
2316
+ }
2317
+ if (!target) return { authorized: false, reason: "target_missing" };
2318
+ const acceptedTarget = normalizeTarget(target);
2319
+ if (acceptedTarget.provider !== descriptor.provider) {
2320
+ return { authorized: false, reason: "provider_mismatch" };
2321
+ }
2322
+ if (acceptedTarget.model !== descriptor.model) {
2323
+ return { authorized: false, reason: "model_mismatch" };
2324
+ }
2325
+ if (acceptedTarget.credentialMode !== descriptor.credentialMode) {
2326
+ return { authorized: false, reason: "credential_mode_mismatch" };
2327
+ }
2328
+ if (acceptedTarget.region !== descriptor.region) {
2329
+ return { authorized: false, reason: "region_mismatch" };
2330
+ }
2331
+ return {
2332
+ authorized: true,
2333
+ acceptanceId: policy.acceptanceId,
2334
+ target: acceptedTarget,
2335
+ selection
2336
+ };
2337
+ }
2338
+ function createTranscriptionSessionRequest(input) {
2339
+ const sequenceFloor = input.sequenceFloor ?? 0;
2340
+ if (!Number.isSafeInteger(sequenceFloor) || sequenceFloor < 0) return null;
2341
+ const authorization = authorizeTranscriptionAdapter(
2342
+ input.policy,
2343
+ input.adapter.descriptor,
2344
+ input.selection
2345
+ );
2346
+ if (!authorization.authorized) return null;
2347
+ return {
2348
+ localSessionId: input.localSessionId,
2349
+ policyAcceptanceId: authorization.acceptanceId,
2350
+ selection: authorization.selection,
2351
+ target: { ...authorization.target },
2352
+ language: input.policy.language?.trim() ?? null,
2353
+ autoDetectLanguage: input.policy.autoDetectLanguage,
2354
+ diarization: { ...input.policy.diarization },
2355
+ retention: { ...input.policy.retention },
2356
+ privacy: { ...input.policy.privacy },
2357
+ cost: { ...input.policy.cost },
2358
+ sequenceFloor
2359
+ };
2360
+ }
2361
+ function cloneDefaultPolicy() {
2362
+ return {
2363
+ ...DEFAULT_WORKSPACE_TRANSCRIPTION_POLICY,
2364
+ diarization: { ...DEFAULT_WORKSPACE_TRANSCRIPTION_POLICY.diarization },
2365
+ retention: { ...DEFAULT_WORKSPACE_TRANSCRIPTION_POLICY.retention },
2366
+ privacy: { ...DEFAULT_WORKSPACE_TRANSCRIPTION_POLICY.privacy },
2367
+ fallback: { mode: "disabled", targets: [] },
2368
+ cost: { ...DEFAULT_WORKSPACE_TRANSCRIPTION_POLICY.cost }
2369
+ };
2370
+ }
2371
+ function isWorkspaceTranscriptionPolicy(value) {
2372
+ if (!isRecord(value) || typeof value.enabled !== "boolean") return false;
2373
+ if (!hasOnlyKeys(value, [
2374
+ "enabled",
2375
+ "acceptanceId",
2376
+ "primary",
2377
+ "language",
2378
+ "autoDetectLanguage",
2379
+ "diarization",
2380
+ "retention",
2381
+ "privacy",
2382
+ "fallback",
2383
+ "cost"
2384
+ ])) {
2385
+ return false;
2386
+ }
2387
+ if (!(value.acceptanceId === null || isUuid(value.acceptanceId))) return false;
2388
+ if (!(value.primary === null || isTarget(value.primary))) return false;
2389
+ if (!(value.language === null || isBoundedString(value.language, 64))) return false;
2390
+ if (typeof value.autoDetectLanguage !== "boolean") return false;
2391
+ 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)) {
2392
+ return false;
2393
+ }
2394
+ if (!value.diarization.enabled && value.diarization.maxSpeakers !== null) return false;
2395
+ if (!isRecord(value.retention) || !hasOnlyKeys(value.retention, ["mode", "maxDays"])) {
2396
+ return false;
2397
+ }
2398
+ if (value.retention.mode !== "none" && value.retention.mode !== "provider-policy") return false;
2399
+ if (!(value.retention.maxDays === null || isBoundedInteger(value.retention.maxDays, 3650))) {
2400
+ return false;
2401
+ }
2402
+ if (!isRecord(value.privacy) || !hasOnlyKeys(value.privacy, ["allowProviderLogging", "allowProviderTraining"]) || typeof value.privacy.allowProviderLogging !== "boolean" || typeof value.privacy.allowProviderTraining !== "boolean") {
2403
+ return false;
2404
+ }
2405
+ if (!isRecord(value.fallback) || !hasOnlyKeys(value.fallback, ["mode", "targets"])) {
2406
+ return false;
2407
+ }
2408
+ if (value.fallback.mode !== "disabled" && value.fallback.mode !== "explicit") return false;
2409
+ if (!Array.isArray(value.fallback.targets) || value.fallback.targets.length > 8 || !value.fallback.targets.every(isTarget)) {
2410
+ return false;
2411
+ }
2412
+ if (value.fallback.mode === "disabled" && value.fallback.targets.length !== 0) return false;
2413
+ if (value.fallback.mode === "explicit" && value.fallback.targets.length === 0) return false;
2414
+ if (!isRecord(value.cost) || !hasOnlyKeys(value.cost, ["currency", "maxPerHour", "maxPerMonth"]) || value.cost.currency !== "USD") {
2415
+ return false;
2416
+ }
2417
+ if (!isNullableBoundedNumber(value.cost.maxPerHour, 1e4)) return false;
2418
+ if (!isNullableBoundedNumber(value.cost.maxPerMonth, 1e6)) return false;
2419
+ if (value.enabled && (!value.acceptanceId || !value.primary)) return false;
2420
+ if (value.enabled && !value.autoDetectLanguage && value.language === null) return false;
2421
+ if (value.autoDetectLanguage && value.language !== null) return false;
2422
+ const targets = [value.primary, ...value.fallback.targets].filter(
2423
+ (target) => target !== null
2424
+ );
2425
+ if (new Set(targets.map(targetKey)).size !== targets.length) return false;
2426
+ return true;
2427
+ }
2428
+ function targetKey(target) {
2429
+ return [
2430
+ target.provider.trim(),
2431
+ target.model?.trim() ?? "",
2432
+ target.credentialMode,
2433
+ target.credentialConnectionId ?? "",
2434
+ target.region?.trim() ?? ""
2435
+ ].join("\0");
2436
+ }
2437
+ function isTarget(value) {
2438
+ if (!isRecord(value)) return false;
2439
+ if (!hasOnlyKeys(value, ["provider", "model", "credentialMode", "credentialConnectionId", "region"])) {
2440
+ return false;
2441
+ }
2442
+ if (!isBoundedString(value.provider, 128)) return false;
2443
+ if (!(value.model === null || isBoundedString(value.model, 256))) return false;
2444
+ if (value.credentialMode !== "managed" && value.credentialMode !== "byok") return false;
2445
+ if (value.provider.trim() === "azure-speech" && value.credentialMode !== "byok") return false;
2446
+ if (!(value.credentialConnectionId === null || isUuid(value.credentialConnectionId))) {
2447
+ return false;
2448
+ }
2449
+ if (!(value.region === null || isBoundedString(value.region, 128))) return false;
2450
+ if (value.credentialMode === "byok" && value.credentialConnectionId === null) return false;
2451
+ if (value.credentialMode === "managed" && value.credentialConnectionId !== null) return false;
2452
+ return true;
2453
+ }
2454
+ function normalizeTarget(target) {
2455
+ return {
2456
+ provider: target.provider.trim(),
2457
+ model: target.model?.trim() ?? null,
2458
+ credentialMode: target.credentialMode,
2459
+ credentialConnectionId: target.credentialConnectionId,
2460
+ region: target.region?.trim() ?? null
2461
+ };
2462
+ }
2463
+ function isRecord(value) {
2464
+ return typeof value === "object" && value !== null;
2465
+ }
2466
+ function hasOnlyKeys(value, keys) {
2467
+ const accepted = new Set(keys);
2468
+ return Object.keys(value).every((key) => accepted.has(key));
2469
+ }
2470
+ function isBoundedString(value, maximum) {
2471
+ return typeof value === "string" && value.trim().length > 0 && value.length <= maximum;
2472
+ }
2473
+ function isUuid(value) {
2474
+ 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);
2475
+ }
2476
+ function isBoundedInteger(value, maximum) {
2477
+ return Number.isInteger(value) && value >= 0 && value <= maximum;
2478
+ }
2479
+ function isNullableBoundedNumber(value, maximum) {
2480
+ return value === null || typeof value === "number" && Number.isFinite(value) && value >= 0 && value <= maximum;
2481
+ }
2147
2482
  export {
2483
+ DEFAULT_WORKSPACE_TRANSCRIPTION_POLICY,
2148
2484
  KNOWN_PERMISSIONS,
2149
2485
  KNOWN_USAGE_EVENT_TYPES,
2150
2486
  OPENGENI_API_CONTRACT_HEADER,
@@ -2158,12 +2494,15 @@ export {
2158
2494
  TtydClientCommand,
2159
2495
  TtydServerCommand,
2160
2496
  applyUrlRotation,
2497
+ authorizeTranscriptionAdapter,
2498
+ createTranscriptionSessionRequest,
2161
2499
  desktopSocketUrl,
2162
2500
  formatSseEvent,
2163
2501
  isRetryableStreamError,
2164
2502
  nextDesktopState,
2165
2503
  parseSseStream,
2166
2504
  proxySessionEventStream,
2505
+ resolveWorkspaceTranscriptionPolicy,
2167
2506
  resumeSequenceFromRequest,
2168
2507
  sessionEventsToSseResponse,
2169
2508
  sessionEventsToSseStream,