@opengeni/db 0.27.0 → 0.27.7

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.
@@ -1,7 +1,7 @@
1
1
  import type { Database } from "./index";
2
2
  export declare const SESSION_REALTIME_CONTEXT_MAX_BYTES = 65536;
3
3
  export declare const SESSION_REALTIME_TAIL_SOURCE = "transcript_tail_flush";
4
- export declare const SESSION_REALTIME_TAIL_INSTRUCTION = "The user just ended their realtime session. Here is the remaining handoff/transcript tail. You probably do not have to do anything; acknowledge the handoff unless the transcript itself asks for something.";
4
+ export declare const SESSION_REALTIME_TAIL_INSTRUCTION = "The user just ended the realtime voice session but remains reachable by text. Ending voice changes only the communication mode; it does not stop, pause, or complete existing work. Treat the remaining transcript tail as additional context. If work was already underway, continue it from the current state. Change or stop that work only if the user explicitly requested it. If nothing was underway and the transcript contains no unhandled request, acknowledge briefly; otherwise handle any unhandled request.";
5
5
  export type SessionRealtimeContextProjection = {
6
6
  id: string;
7
7
  workspaceId: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@opengeni/db",
3
- "version": "0.27.0",
3
+ "version": "0.27.7",
4
4
  "description": "OpenGeni persistence: Drizzle schema, RLS-scoped query layer, the SQL migration runner, and role provisioning.",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
@@ -51,9 +51,9 @@
51
51
  },
52
52
  "dependencies": {
53
53
  "@opengeni/codex": "^0.2.10",
54
- "@opengeni/config": "^0.10.7",
55
- "@opengeni/contracts": "^0.36.0",
56
- "@opengeni/network": "^0.1.1",
54
+ "@opengeni/config": "^0.10.11",
55
+ "@opengeni/contracts": "^0.38.1",
56
+ "@opengeni/network": "^0.2.0",
57
57
  "drizzle-orm": "^0.45.2",
58
58
  "postgres": "^3.4.7"
59
59
  },
package/src/index.ts CHANGED
@@ -1106,6 +1106,31 @@ export async function withRlsContext<T>(
1106
1106
  }, transactionConfig);
1107
1107
  }
1108
1108
 
1109
+ /**
1110
+ * Run one bounded database operation on a transaction-pinned backend.
1111
+ *
1112
+ * Callers that also have an application deadline should check their abort
1113
+ * signal before returning from `fn`; throwing there rolls the transaction back
1114
+ * even when the application deadline won a surrounding Promise race.
1115
+ */
1116
+ export async function withDatabaseStatementTimeout<T>(
1117
+ db: Database,
1118
+ timeoutMs: number,
1119
+ fn: (db: Database) => Promise<T>,
1120
+ ): Promise<T> {
1121
+ if (!Number.isFinite(timeoutMs) || timeoutMs <= 0) {
1122
+ throw new Error("withDatabaseStatementTimeout requires a positive timeout");
1123
+ }
1124
+ const boundedTimeoutMs = Math.max(1, Math.floor(timeoutMs));
1125
+ return await db.transaction(async (tx) => {
1126
+ const scoped = tx as unknown as Database;
1127
+ await scoped.execute(
1128
+ sql`select set_config('statement_timeout', ${`${boundedTimeoutMs}ms`}, true)`,
1129
+ );
1130
+ return await fn(scoped);
1131
+ });
1132
+ }
1133
+
1109
1134
  export async function rlsContextForWorkspace(
1110
1135
  db: Database,
1111
1136
  workspaceId: string,
@@ -3698,6 +3723,10 @@ export type StoreIntegrationOAuthClientInput = {
3698
3723
 
3699
3724
  export type ReplaceIntegrationOAuthClientInput = StoreIntegrationOAuthClientInput;
3700
3725
 
3726
+ export type ReplaceIntegrationOAuthClientIfCurrentInput = ReplaceIntegrationOAuthClientInput & {
3727
+ expectedClientId: string;
3728
+ };
3729
+
3701
3730
  export type ConsumeOAuthStateNonceInput = {
3702
3731
  accountId: string;
3703
3732
  workspaceId: string;
@@ -7930,6 +7959,30 @@ export async function replaceIntegrationOAuthClient(
7930
7959
  return mapStoredIntegrationOAuthClient(row);
7931
7960
  }
7932
7961
 
7962
+ export async function replaceIntegrationOAuthClientIfCurrent(
7963
+ db: Database,
7964
+ input: ReplaceIntegrationOAuthClientIfCurrentInput,
7965
+ ): Promise<StoredIntegrationOAuthClient | null> {
7966
+ const [row] = await db
7967
+ .update(schema.integrationOauthClients)
7968
+ .set({
7969
+ authorizationServer: input.authorizationServer,
7970
+ clientId: input.clientId,
7971
+ clientSecretEncrypted: input.clientSecretEncrypted ?? null,
7972
+ tokenEndpointAuthMethod: input.tokenEndpointAuthMethod ?? "none",
7973
+ metadata: input.metadata ?? {},
7974
+ updatedAt: new Date(),
7975
+ })
7976
+ .where(
7977
+ and(
7978
+ eq(schema.integrationOauthClients.issuer, input.issuer),
7979
+ eq(schema.integrationOauthClients.clientId, input.expectedClientId),
7980
+ ),
7981
+ )
7982
+ .returning();
7983
+ return row ? mapStoredIntegrationOAuthClient(row) : null;
7984
+ }
7985
+
7933
7986
  function mapStoredIntegrationOAuthClient(
7934
7987
  row: typeof schema.integrationOauthClients.$inferSelect,
7935
7988
  ): StoredIntegrationOAuthClient {
@@ -43994,6 +44047,7 @@ function mapSession(
43994
44047
  // the fence exact even if the column type ever drifts (the lease-epoch lesson).
43995
44048
  activeSandboxId: row.activeSandboxId ?? null,
43996
44049
  activeEpoch: Number(row.activeEpoch),
44050
+ workingDir: row.workingDir ?? null,
43997
44051
  variableSetId: row.variableSetId,
43998
44052
  environmentId: row.variableSetId,
43999
44053
  // The rig + frozen rig version the session rides (M3). Both null for a
@@ -10,7 +10,7 @@ import { submitHumanPromptInTransaction } from "./session-queue-commands";
10
10
  export const SESSION_REALTIME_CONTEXT_MAX_BYTES = 65_536;
11
11
  export const SESSION_REALTIME_TAIL_SOURCE = "transcript_tail_flush";
12
12
  export const SESSION_REALTIME_TAIL_INSTRUCTION =
13
- "The user just ended their realtime session. Here is the remaining handoff/transcript tail. You probably do not have to do anything; acknowledge the handoff unless the transcript itself asks for something.";
13
+ "The user just ended the realtime voice session but remains reachable by text. Ending voice changes only the communication mode; it does not stop, pause, or complete existing work. Treat the remaining transcript tail as additional context. If work was already underway, continue it from the current state. Change or stop that work only if the user explicitly requested it. If nothing was underway and the transcript contains no unhandled request, acknowledge briefly; otherwise handle any unhandled request.";
14
14
 
15
15
  export type SessionRealtimeContextProjection = {
16
16
  id: string;