@opengeni/sdk 1.4.0 → 1.4.4
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 +12 -0
- package/dist/adapters/next.js +2 -2
- package/dist/artifacts.js +5 -5
- package/dist/browser.js +3 -3
- package/dist/chat/index.js +15 -9
- package/dist/chat/index.js.map +1 -1
- package/dist/{chunk-BVIIRSJY.js → chunk-6SWFOZUP.js} +3 -3
- package/dist/{chunk-LEBDXYMA.js → chunk-7A5ZDWQH.js} +2 -2
- package/dist/{chunk-GYM54GKM.js → chunk-DTIMLEPB.js} +1 -1
- package/dist/chunk-DTIMLEPB.js.map +1 -0
- package/dist/{chunk-CA2T7QT4.js → chunk-HORQJBLP.js} +2 -2
- package/dist/{chunk-NC57YMUO.js → chunk-IJWKWFSF.js} +1 -1
- package/dist/chunk-IJWKWFSF.js.map +1 -0
- package/dist/{chunk-ZNC44QEX.js → chunk-JOI7XCG6.js} +45 -14
- package/dist/chunk-JOI7XCG6.js.map +1 -0
- package/dist/{chunk-D534PIXF.js → chunk-MAFL52JD.js} +2 -2
- package/dist/{chunk-FYQAMXDW.js → chunk-OLLQBZAE.js} +111 -5
- package/dist/chunk-OLLQBZAE.js.map +1 -0
- package/dist/{chunk-MXV5O25O.js → chunk-W72BOLRJ.js} +2 -2
- package/dist/codex-realtime-controller.js +1 -1
- package/dist/core.js +4 -4
- package/dist/document-authority.js +4 -4
- package/dist/editable-artifacts.js +1 -1
- package/dist/index.js +9 -9
- package/dist/interaction.d.ts +5 -0
- package/dist/interaction.js +1 -1
- package/dist/realtime.js +1 -1
- package/dist/session-proxy.d.ts +29 -6
- package/dist/session-proxy.js +2 -2
- package/dist/site.js +4 -4
- package/dist/types.d.ts +17 -0
- package/package.json +3 -3
- package/src/chat/opengeni.ts +14 -3
- package/src/codex-realtime-controller.ts +65 -14
- package/src/interaction.ts +5 -0
- package/src/session-proxy.ts +189 -9
- package/src/types.ts +15 -0
- package/dist/chunk-FYQAMXDW.js.map +0 -1
- package/dist/chunk-GYM54GKM.js.map +0 -1
- package/dist/chunk-NC57YMUO.js.map +0 -1
- package/dist/chunk-ZNC44QEX.js.map +0 -1
- /package/dist/{chunk-BVIIRSJY.js.map → chunk-6SWFOZUP.js.map} +0 -0
- /package/dist/{chunk-LEBDXYMA.js.map → chunk-7A5ZDWQH.js.map} +0 -0
- /package/dist/{chunk-CA2T7QT4.js.map → chunk-HORQJBLP.js.map} +0 -0
- /package/dist/{chunk-D534PIXF.js.map → chunk-MAFL52JD.js.map} +0 -0
- /package/dist/{chunk-MXV5O25O.js.map → chunk-W72BOLRJ.js.map} +0 -0
package/src/session-proxy.ts
CHANGED
|
@@ -110,7 +110,12 @@ export type SessionProxyCreateInput = {
|
|
|
110
110
|
export type SessionProxyMessageInput = {
|
|
111
111
|
/** Absent for `create`. */
|
|
112
112
|
sessionId?: string | undefined;
|
|
113
|
-
|
|
113
|
+
/**
|
|
114
|
+
* `realtime` is live voice: once when a call starts (refuse it, or rotate
|
|
115
|
+
* MCP credentials), and before each batch of finalized transcripts and spoken
|
|
116
|
+
* requests is saved (refuse it, or add `modelContext`).
|
|
117
|
+
*/
|
|
118
|
+
delivery: "create" | "send" | "steer" | "submit" | "realtime";
|
|
114
119
|
};
|
|
115
120
|
|
|
116
121
|
/** Server-side additions the host attaches to one forwarded user message or response. */
|
|
@@ -118,7 +123,9 @@ export type SessionProxyMessageExtras = {
|
|
|
118
123
|
/**
|
|
119
124
|
* Model-visible context for this message (current page, time zone, today's
|
|
120
125
|
* date). Placed before any context the browser sent. Not secret. Ignored for
|
|
121
|
-
* approval decisions and human-input responses, which are not new messages
|
|
126
|
+
* approval decisions and human-input responses, which are not new messages,
|
|
127
|
+
* and when a live voice call starts. Keep it stable for the same message:
|
|
128
|
+
* a retried voice entry must carry the same context to be accepted.
|
|
122
129
|
*/
|
|
123
130
|
modelContext?: string | undefined;
|
|
124
131
|
/**
|
|
@@ -193,9 +200,10 @@ export type SessionProxyHandlerOptions = {
|
|
|
193
200
|
| undefined;
|
|
194
201
|
/**
|
|
195
202
|
* Called before every forwarded user message (send, steer, composer submit,
|
|
196
|
-
* and browser-started create), approval decision,
|
|
197
|
-
*
|
|
198
|
-
*
|
|
203
|
+
* and browser-started create), approval decision, human-input response, and
|
|
204
|
+
* live voice start and transcript save (`delivery: "realtime"`).
|
|
205
|
+
* Return server-owned `modelContext` (messages and voice transcripts only)
|
|
206
|
+
* and MCP credential rotations, or a `Response` to reject the action.
|
|
199
207
|
*/
|
|
200
208
|
beforeForwardMessage?:
|
|
201
209
|
| ((
|
|
@@ -237,6 +245,18 @@ export type SessionProxyHandlerOptions = {
|
|
|
237
245
|
* hide the microphone. Defaults to true.
|
|
238
246
|
*/
|
|
239
247
|
voiceInput?: boolean | undefined;
|
|
248
|
+
/**
|
|
249
|
+
* Forward live speech-to-speech voice for existing chats as the resolved
|
|
250
|
+
* user: the voice model catalog and the call lifecycle routes under
|
|
251
|
+
* `.../sessions/{id}/realtime`. Opengeni still requires that user's
|
|
252
|
+
* `sessions:control` permission, binds the call to that user and browser,
|
|
253
|
+
* runs spoken requests as ordinary steers of the chat, and meters
|
|
254
|
+
* deployment-funded voice against the organization's credits. The stock
|
|
255
|
+
* composer shows the voice button only when a voice model is available.
|
|
256
|
+
* `false` reports `realtimeVoice: false` in the client config so stock UIs
|
|
257
|
+
* hide it, and refuses the routes. Defaults to true.
|
|
258
|
+
*/
|
|
259
|
+
realtimeVoice?: boolean | undefined;
|
|
240
260
|
/**
|
|
241
261
|
* Let the browser read a file from the session's sandbox (`POST .../fs/read`)
|
|
242
262
|
* so `sandbox:` links in agent replies can be downloaded. Only `path`,
|
|
@@ -291,10 +311,20 @@ export type SessionProxyHandlerOptions = {
|
|
|
291
311
|
* settings decide which models end users see.
|
|
292
312
|
*/
|
|
293
313
|
modelSelection?: boolean | undefined;
|
|
294
|
-
/**
|
|
314
|
+
/**
|
|
315
|
+
* SSE heartbeat interval. Defaults to 5 seconds, under Bun.serve's default
|
|
316
|
+
* 10-second `idleTimeout`, which otherwise closes a quiet event stream.
|
|
317
|
+
*/
|
|
295
318
|
heartbeatMs?: number | undefined;
|
|
296
319
|
};
|
|
297
320
|
|
|
321
|
+
/**
|
|
322
|
+
* Default SSE heartbeat. Bun.serve closes a connection that sends nothing for
|
|
323
|
+
* 10 seconds by default, so a quiet stream (a long tool call, an idle chat)
|
|
324
|
+
* would drop and reconnect every 10 seconds through a Bun or Hono-on-Bun host.
|
|
325
|
+
*/
|
|
326
|
+
const SESSION_PROXY_DEFAULT_HEARTBEAT_MS = 5_000;
|
|
327
|
+
|
|
298
328
|
const DEFAULT_MAX_BODY_BYTES = 1024 * 1024;
|
|
299
329
|
const SEGMENT = /^[A-Za-z0-9][A-Za-z0-9_-]{0,127}$/;
|
|
300
330
|
const QUEUE_OPERATIONS: ReadonlySet<string> = new Set(["move", "edit", "steer", "delete"]);
|
|
@@ -351,9 +381,12 @@ export function createSessionProxyHandler(
|
|
|
351
381
|
): (request: Request) => Promise<Response> {
|
|
352
382
|
const service = isFacade(target) ? target.client : target;
|
|
353
383
|
const defaultSource = isFacade(target) ? target.source : "default";
|
|
384
|
+
// One rejected-key notice per handler (each handler holds one server key).
|
|
385
|
+
const rejectedKeyNotice = { sent: false };
|
|
354
386
|
const maxBodyBytes = options.maxBodyBytes ?? DEFAULT_MAX_BODY_BYTES;
|
|
355
387
|
const filesEnabled = options.files ?? true;
|
|
356
388
|
const voiceInputEnabled = options.voiceInput ?? true;
|
|
389
|
+
const realtimeVoiceEnabled = options.realtimeVoice ?? true;
|
|
357
390
|
const sandboxFilesEnabled = options.sandboxFiles === true;
|
|
358
391
|
const artifactsEnabled = options.artifacts !== undefined && options.artifacts !== false;
|
|
359
392
|
const editableLiveUrl = artifactsEnabled
|
|
@@ -363,7 +396,7 @@ export function createSessionProxyHandler(
|
|
|
363
396
|
)
|
|
364
397
|
: null;
|
|
365
398
|
const modelSelection = options.modelSelection ?? true;
|
|
366
|
-
const heartbeatMs = options.heartbeatMs ??
|
|
399
|
+
const heartbeatMs = options.heartbeatMs ?? SESSION_PROXY_DEFAULT_HEARTBEAT_MS;
|
|
367
400
|
const sessionList = options.sessionList ?? "mine";
|
|
368
401
|
const archiveEnabled = options.archive ?? true;
|
|
369
402
|
const chats = options.chats ?? "private";
|
|
@@ -520,6 +553,39 @@ export function createSessionProxyHandler(
|
|
|
520
553
|
],
|
|
521
554
|
};
|
|
522
555
|
};
|
|
556
|
+
/**
|
|
557
|
+
* Best-effort standalone rotation before a voice call. Opengeni refuses
|
|
558
|
+
* it while a turn is running; that turn's message already refreshed them.
|
|
559
|
+
*/
|
|
560
|
+
const refreshRealtimeCredentials = async (
|
|
561
|
+
sessionId: string,
|
|
562
|
+
updates: SessionMcpCredentialUpdateInput[],
|
|
563
|
+
): Promise<void> => {
|
|
564
|
+
if (updates.length === 0) return;
|
|
565
|
+
try {
|
|
566
|
+
const servers = (await client.getSession(workspaceId, sessionId, call)).mcpServers ?? [];
|
|
567
|
+
const rotations = updates.flatMap((update) => {
|
|
568
|
+
const server = servers.find((candidate) => candidate.id === update.id);
|
|
569
|
+
return server
|
|
570
|
+
? [
|
|
571
|
+
{
|
|
572
|
+
id: update.id,
|
|
573
|
+
expectedCredentialVersion: server.credentialVersion,
|
|
574
|
+
expectedServerUrl: server.url,
|
|
575
|
+
headers: update.headers,
|
|
576
|
+
},
|
|
577
|
+
]
|
|
578
|
+
: [];
|
|
579
|
+
});
|
|
580
|
+
if (rotations.length === 0) return;
|
|
581
|
+
await client.rotateSessionMcpCredentials(workspaceId, sessionId, {
|
|
582
|
+
operationKey: crypto.randomUUID(),
|
|
583
|
+
updates: rotations,
|
|
584
|
+
});
|
|
585
|
+
} catch (error) {
|
|
586
|
+
if (!(error instanceof OpenGeniApiError)) throw error;
|
|
587
|
+
}
|
|
588
|
+
};
|
|
523
589
|
/** Browser input sanitized, then server-owned extras merged in. */
|
|
524
590
|
const forwardMessage = async (
|
|
525
591
|
value: unknown,
|
|
@@ -617,6 +683,7 @@ export function createSessionProxyHandler(
|
|
|
617
683
|
// Whether the stock "New chat" and "Archive" actions can succeed.
|
|
618
684
|
sessionCreation: options.createSession !== undefined,
|
|
619
685
|
archive: archiveEnabled,
|
|
686
|
+
...(realtimeVoiceEnabled ? {} : { realtimeVoice: false }),
|
|
620
687
|
// Explicit true also tells stock UIs to offer end users the model picker.
|
|
621
688
|
...(modelSelection
|
|
622
689
|
? options.modelSelection === true
|
|
@@ -650,6 +717,10 @@ export function createSessionProxyHandler(
|
|
|
650
717
|
if (area === "model-catalog" && tail.length === 0 && method === "GET") {
|
|
651
718
|
return await read(`${base}/model-catalog`);
|
|
652
719
|
}
|
|
720
|
+
if (area === "realtime-model-catalog" && tail.length === 0 && method === "GET") {
|
|
721
|
+
if (!realtimeVoiceEnabled) return errorJson(404, "route_not_allowed", "Not found.");
|
|
722
|
+
return await read(`${base}/realtime-model-catalog`);
|
|
723
|
+
}
|
|
653
724
|
if (area === "live-events" && tail.length === 1 && tail[0] === "stream" && method === "GET") {
|
|
654
725
|
// Surface authorization failures as HTTP status before streaming.
|
|
655
726
|
await client.requestJson("GET", base, undefined, {}, call);
|
|
@@ -879,6 +950,34 @@ export function createSessionProxyHandler(
|
|
|
879
950
|
? payload
|
|
880
951
|
: json(await client.requestJson("POST", path, payload));
|
|
881
952
|
|
|
953
|
+
if (op[0] === "realtime") {
|
|
954
|
+
if (!realtimeVoiceEnabled) return errorJson(404, "route_not_allowed", "Not found.");
|
|
955
|
+
const realtime = realtimeRoute(method, op.slice(1));
|
|
956
|
+
if (!realtime) return errorJson(404, "route_not_allowed", "Not found.");
|
|
957
|
+
if (!body) reject(400, "invalid_body", "A JSON object body is required.");
|
|
958
|
+
const path = `${session}/${op.join("/")}`;
|
|
959
|
+
if (realtime === "begin") {
|
|
960
|
+
// The host may refuse a call; MCP credentials (including the tool
|
|
961
|
+
// server's per-user token) are refreshed before voice can delegate.
|
|
962
|
+
const extras = await messageExtras({
|
|
963
|
+
sessionId,
|
|
964
|
+
delivery: "realtime",
|
|
965
|
+
});
|
|
966
|
+
if (extras instanceof Response) return extras;
|
|
967
|
+
await refreshRealtimeCredentials(sessionId, extras?.mcpCredentialUpdates ?? []);
|
|
968
|
+
}
|
|
969
|
+
let payload: Record<string, unknown> = body;
|
|
970
|
+
if (realtime === "sync" && options.beforeForwardMessage && hasRealtimeMessages(body)) {
|
|
971
|
+
const extras = await options.beforeForwardMessage(
|
|
972
|
+
{ sessionId, delivery: "realtime" },
|
|
973
|
+
context,
|
|
974
|
+
);
|
|
975
|
+
if (extras instanceof Response) return extras;
|
|
976
|
+
payload = withRealtimeContext(body, extras?.modelContext);
|
|
977
|
+
}
|
|
978
|
+
return json(await client.requestJson(method, path, payload));
|
|
979
|
+
}
|
|
980
|
+
|
|
882
981
|
switch (route) {
|
|
883
982
|
case "GET ":
|
|
884
983
|
return await read(session);
|
|
@@ -1020,7 +1119,7 @@ export function createSessionProxyHandler(
|
|
|
1020
1119
|
}
|
|
1021
1120
|
return errorJson(404, "route_not_allowed", "Not found.");
|
|
1022
1121
|
} catch (error) {
|
|
1023
|
-
return errorResponse(error);
|
|
1122
|
+
return errorResponse(error, rejectedKeyNotice);
|
|
1024
1123
|
}
|
|
1025
1124
|
};
|
|
1026
1125
|
}
|
|
@@ -1033,6 +1132,67 @@ const PROXY_METHODS: readonly string[] = ["GET", "POST", "PUT", "PATCH", "DELETE
|
|
|
1033
1132
|
|
|
1034
1133
|
const UPLOAD_FIELDS = ["scope", "filename", "contentType", "sizeBytes", "sha256"] as const;
|
|
1035
1134
|
|
|
1135
|
+
type RealtimeRoute = "begin" | "connect" | "heartbeat" | "end" | "activate" | "sync";
|
|
1136
|
+
const REALTIME_CONNECT_ROUTES: ReadonlySet<string> = new Set(["webrtc", "gateway", "supergrok"]);
|
|
1137
|
+
/** Ledger entries that become (or join) messages and so accept `modelContext`. */
|
|
1138
|
+
const REALTIME_MESSAGE_KINDS: ReadonlySet<string> = new Set([
|
|
1139
|
+
"delegation_call",
|
|
1140
|
+
"user_transcript",
|
|
1141
|
+
"assistant_transcript",
|
|
1142
|
+
]);
|
|
1143
|
+
|
|
1144
|
+
/**
|
|
1145
|
+
* The live voice routes a browser call uses, after `realtime/`: begin, provider
|
|
1146
|
+
* connect, heartbeat, end, connection activation, and transcript sync. Bodies
|
|
1147
|
+
* pass to Opengeni, which validates them and binds the call to the acting user.
|
|
1148
|
+
*/
|
|
1149
|
+
function realtimeRoute(method: string, rest: string[]): RealtimeRoute | null {
|
|
1150
|
+
if (rest.length === 0) return method === "POST" ? "begin" : null;
|
|
1151
|
+
if (rest.length === 1) {
|
|
1152
|
+
if (REALTIME_CONNECT_ROUTES.has(rest[0]!)) return method === "POST" ? "connect" : null;
|
|
1153
|
+
return method === "DELETE" ? "end" : null;
|
|
1154
|
+
}
|
|
1155
|
+
if (rest.length === 2 && rest[1] === "heartbeat") return method === "PATCH" ? "heartbeat" : null;
|
|
1156
|
+
if (rest.length === 2 && rest[1] === "sync") return method === "POST" ? "sync" : null;
|
|
1157
|
+
if (rest.length === 4 && rest[1] === "connections" && rest[3] === "activate") {
|
|
1158
|
+
return method === "POST" ? "activate" : null;
|
|
1159
|
+
}
|
|
1160
|
+
return null;
|
|
1161
|
+
}
|
|
1162
|
+
|
|
1163
|
+
function hasRealtimeMessages(body: Record<string, unknown>): boolean {
|
|
1164
|
+
return (
|
|
1165
|
+
Array.isArray(body.entries) &&
|
|
1166
|
+
body.entries.some(
|
|
1167
|
+
(entry) =>
|
|
1168
|
+
entry !== null &&
|
|
1169
|
+
typeof entry === "object" &&
|
|
1170
|
+
REALTIME_MESSAGE_KINDS.has((entry as { kind?: unknown }).kind as string),
|
|
1171
|
+
)
|
|
1172
|
+
);
|
|
1173
|
+
}
|
|
1174
|
+
|
|
1175
|
+
/** Server context goes before the browser's on each message-bearing entry. */
|
|
1176
|
+
function withRealtimeContext(
|
|
1177
|
+
body: Record<string, unknown>,
|
|
1178
|
+
serverContext: string | undefined,
|
|
1179
|
+
): Record<string, unknown> {
|
|
1180
|
+
if (!serverContext?.trim() || !Array.isArray(body.entries)) return body;
|
|
1181
|
+
return {
|
|
1182
|
+
...body,
|
|
1183
|
+
entries: body.entries.map((entry: unknown) => {
|
|
1184
|
+
if (entry === null || typeof entry !== "object") return entry;
|
|
1185
|
+
const record = entry as Record<string, unknown>;
|
|
1186
|
+
if (!REALTIME_MESSAGE_KINDS.has(record.kind as string)) return entry;
|
|
1187
|
+
const modelContext = joinContext(
|
|
1188
|
+
serverContext,
|
|
1189
|
+
typeof record.modelContext === "string" ? record.modelContext : undefined,
|
|
1190
|
+
);
|
|
1191
|
+
return modelContext ? { ...record, modelContext } : entry;
|
|
1192
|
+
}),
|
|
1193
|
+
};
|
|
1194
|
+
}
|
|
1195
|
+
|
|
1036
1196
|
type NormalizedToolServer = SessionProxyToolServer & {
|
|
1037
1197
|
url: string;
|
|
1038
1198
|
id: string;
|
|
@@ -1724,8 +1884,24 @@ function workspaceLiveStream(
|
|
|
1724
1884
|
});
|
|
1725
1885
|
}
|
|
1726
1886
|
|
|
1887
|
+
/**
|
|
1888
|
+
* The proxy always calls Opengeni with the server's own key, so an upstream
|
|
1889
|
+
* 401 means that key is wrong, expired or revoked, never that the end user is
|
|
1890
|
+
* signed out. Tell the developer once, in the server log, what to fix.
|
|
1891
|
+
*/
|
|
1892
|
+
function warnRejectedApiKey(error: OpenGeniApiError, notice: { sent: boolean }): void {
|
|
1893
|
+
if (notice.sent) return;
|
|
1894
|
+
notice.sent = true;
|
|
1895
|
+
console.warn(
|
|
1896
|
+
`[@opengeni/sdk] Opengeni rejected the session proxy's API key (401${
|
|
1897
|
+
error.correlationId ? `, reference ${error.correlationId}` : ""
|
|
1898
|
+
}). It may be expired, revoked or mistyped: create a new key in Opengeni under ` +
|
|
1899
|
+
"Organization settings > Developer and update OPENGENI_API_KEY on the server.",
|
|
1900
|
+
);
|
|
1901
|
+
}
|
|
1902
|
+
|
|
1727
1903
|
/** Preserve OpenGeni's error envelope so the browser SDK keeps codes, retryability, and outcome facts. */
|
|
1728
|
-
function errorResponse(error: unknown): Response {
|
|
1904
|
+
function errorResponse(error: unknown, rejectedKeyNotice: { sent: boolean }): Response {
|
|
1729
1905
|
if (error instanceof ProxyRejection) return errorJson(error.status, error.code, error.message);
|
|
1730
1906
|
if (error instanceof OpenGeniSetupError) {
|
|
1731
1907
|
return json(
|
|
@@ -1741,6 +1917,7 @@ function errorResponse(error: unknown): Response {
|
|
|
1741
1917
|
);
|
|
1742
1918
|
}
|
|
1743
1919
|
if (error instanceof OpenGeniApiError) {
|
|
1920
|
+
if (error.status === 401) warnRejectedApiKey(error, rejectedKeyNotice);
|
|
1744
1921
|
const status = error.status >= 400 && error.status <= 599 ? error.status : 502;
|
|
1745
1922
|
// A decoded upstream envelope is forwarded verbatim; the SDK only retains decodable bodies.
|
|
1746
1923
|
if (error.body) {
|
|
@@ -1769,5 +1946,8 @@ function errorResponse(error: unknown): Response {
|
|
|
1769
1946
|
if (error instanceof Error && error.name === "AbortError") {
|
|
1770
1947
|
return errorJson(499, "aborted", "The request was aborted.");
|
|
1771
1948
|
}
|
|
1949
|
+
// The browser gets no details; the host's server log needs the cause
|
|
1950
|
+
// (for example a missing OPENGENI_API_KEY).
|
|
1951
|
+
console.error("[@opengeni/sdk] Session proxy request failed:", error);
|
|
1772
1952
|
return errorJson(500, "proxy_error", "Request could not be completed.");
|
|
1773
1953
|
}
|
package/src/types.ts
CHANGED
|
@@ -4301,6 +4301,13 @@ export type ClientConfig = {
|
|
|
4301
4301
|
modelSelection?: boolean | undefined;
|
|
4302
4302
|
/** Session proxy sandbox-path download opt-in; absent on native deployments. */
|
|
4303
4303
|
sandboxFiles?: boolean | undefined;
|
|
4304
|
+
/**
|
|
4305
|
+
* `false` when a host's session proxy turns live voice off
|
|
4306
|
+
* (`createSessionProxyHandler({ realtimeVoice: false })`), so UIs hide the
|
|
4307
|
+
* voice button. Otherwise absent: availability comes from the workspace's
|
|
4308
|
+
* realtime model catalog.
|
|
4309
|
+
*/
|
|
4310
|
+
realtimeVoice?: boolean | undefined;
|
|
4304
4311
|
/**
|
|
4305
4312
|
* Session proxy only: the workspace the proxy resolved for this user, so a
|
|
4306
4313
|
* browser pointed at the proxy (`<OpenGeniChat baseUrl=... />`) needs no
|
|
@@ -7435,6 +7442,12 @@ export type CapabilityRuntime = {
|
|
|
7435
7442
|
| "missing_verification";
|
|
7436
7443
|
}
|
|
7437
7444
|
| undefined;
|
|
7445
|
+
/**
|
|
7446
|
+
* Present when connecting needs an operator-registered OAuth client because
|
|
7447
|
+
* the provider refuses self-registration; `configured` is whether this
|
|
7448
|
+
* deployment has one. Connector surfaces offer the row only when true.
|
|
7449
|
+
*/
|
|
7450
|
+
operatorOAuthClient?: { configured: boolean } | undefined;
|
|
7438
7451
|
};
|
|
7439
7452
|
|
|
7440
7453
|
export type CapabilityCatalogItem = {
|
|
@@ -8239,6 +8252,8 @@ export type PromotionalCreditScope = {
|
|
|
8239
8252
|
export type PromotionalCreditBalance = PromotionalCreditScope & {
|
|
8240
8253
|
grantId: string;
|
|
8241
8254
|
remainingMicros: number;
|
|
8255
|
+
/** Also pays for dictation and live voice, like general credits (signup credits). */
|
|
8256
|
+
coversVoice?: boolean | undefined;
|
|
8242
8257
|
};
|
|
8243
8258
|
|
|
8244
8259
|
export type BillingBalance = {
|