@opengeni/db 1.3.0 → 1.4.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (91) hide show
  1. package/dist/canonical-human-identities.js +3 -3
  2. package/dist/capability-reconciliation.js +2 -2
  3. package/dist/{chunk-7QWWTVYH.js → chunk-5ROHLEN6.js} +29 -4
  4. package/dist/chunk-5ROHLEN6.js.map +1 -0
  5. package/dist/{chunk-AQMH2ZEH.js → chunk-A4J7EECR.js} +2 -2
  6. package/dist/{chunk-VQX54TK3.js → chunk-B5C5SBSI.js} +3 -3
  7. package/dist/{chunk-OI6YZXTU.js → chunk-DKFEUGM4.js} +2 -2
  8. package/dist/{chunk-HYBQABBJ.js → chunk-HUKBIZGH.js} +2 -2
  9. package/dist/{chunk-LRSIODCQ.js → chunk-IT3EYY2C.js} +6 -4
  10. package/dist/{chunk-LRSIODCQ.js.map → chunk-IT3EYY2C.js.map} +1 -1
  11. package/dist/{chunk-DSNEIW5E.js → chunk-JL6TBB76.js} +2 -1
  12. package/dist/chunk-JL6TBB76.js.map +1 -0
  13. package/dist/{chunk-OEFNTGR3.js → chunk-JWBEE4CL.js} +4 -4
  14. package/dist/{chunk-BRVBLQAL.js → chunk-K5MTNKJU.js} +4 -4
  15. package/dist/{chunk-6UKNJ43T.js → chunk-NZ2SP55O.js} +2 -2
  16. package/dist/{chunk-3XHC5KC5.js → chunk-OKCBARX6.js} +2 -2
  17. package/dist/{chunk-PNWWNEDM.js → chunk-OV5AUIBA.js} +2 -2
  18. package/dist/{chunk-BID3CNN3.js → chunk-TODVDGMO.js} +36 -18
  19. package/dist/{chunk-BID3CNN3.js.map → chunk-TODVDGMO.js.map} +1 -1
  20. package/dist/{chunk-6S5T6FSP.js → chunk-W75DRYG3.js} +5 -5
  21. package/dist/{chunk-7S3O33NM.js → chunk-Y72DP3JF.js} +2 -2
  22. package/dist/codemode-approvals.d.ts +7 -1
  23. package/dist/connect-attempts.js +3 -3
  24. package/dist/connection-metadata.d.ts +416 -0
  25. package/dist/credit-balances.d.ts +25 -4
  26. package/dist/database.d.ts +7 -1
  27. package/dist/editable-artifact-durable-export.js +2 -2
  28. package/dist/editable-artifacts.js +3 -3
  29. package/dist/index.d.ts +51 -8
  30. package/dist/index.js +10826 -10129
  31. package/dist/index.js.map +1 -1
  32. package/dist/legacy-tool-action-reviews.d.ts +75 -0
  33. package/dist/managed-auth-session-sets.js +3 -3
  34. package/dist/mcp-operations.js +2 -2
  35. package/dist/modal-native-live-origin.d.ts +75 -0
  36. package/dist/organization-integration-policy.js +3 -3
  37. package/dist/organization-slack-bots.d.ts +1 -1
  38. package/dist/provision-roles.js +1 -1
  39. package/dist/retained-provider-commands.d.ts +21 -21
  40. package/dist/retained-provider-commands.js +6 -6
  41. package/dist/runtime-posture.d.ts +1 -1
  42. package/dist/sandbox-interaction-idle.d.ts +22 -0
  43. package/dist/schema.js +1 -1
  44. package/dist/session-background-commands.js +4 -4
  45. package/dist/session-command-output.js +5 -5
  46. package/dist/session-event-slices.js +3 -3
  47. package/dist/session-mcp-credential-rotation.js +5 -5
  48. package/dist/session-realtime-billing.d.ts +48 -0
  49. package/dist/session-realtime.d.ts +8 -2
  50. package/dist/session-tenancy.js +3 -3
  51. package/dist/video-generation.js +3 -3
  52. package/dist/voice-transcription-settlement.d.ts +36 -0
  53. package/dist/workspace-tool-defaults.js +4 -4
  54. package/drizzle/0623_voice_transcription_attribution.sql +128 -0
  55. package/drizzle/0624_azure_live_realtime_model.sql +30 -0
  56. package/drizzle/0625_transcription_billing_refusal_codes.sql +29 -0
  57. package/drizzle/0632_modal_native_live_origin.sql +239 -0
  58. package/package.json +7 -7
  59. package/src/codemode-approvals.ts +52 -22
  60. package/src/codemode-operations.ts +198 -181
  61. package/src/connection-metadata.ts +166 -0
  62. package/src/credit-balances.ts +58 -17
  63. package/src/database.ts +9 -0
  64. package/src/index.ts +540 -412
  65. package/src/insights-unified.ts +2 -1
  66. package/src/legacy-tool-action-reviews.ts +242 -0
  67. package/src/modal-native-live-origin.ts +148 -0
  68. package/src/organization-slack-bots.ts +1 -1
  69. package/src/provision-roles.ts +12 -0
  70. package/src/runtime-posture.ts +32 -3
  71. package/src/sandbox-interaction-idle.ts +91 -0
  72. package/src/schema.ts +1 -0
  73. package/src/session-realtime-billing.ts +135 -0
  74. package/src/session-realtime-ledger.ts +9 -4
  75. package/src/session-realtime.ts +20 -2
  76. package/src/tool-action-reviews.ts +129 -18
  77. package/src/transcription-recordings.ts +3 -1
  78. package/src/voice-transcription-settlement.ts +94 -0
  79. package/dist/chunk-7QWWTVYH.js.map +0 -1
  80. package/dist/chunk-DSNEIW5E.js.map +0 -1
  81. /package/dist/{chunk-AQMH2ZEH.js.map → chunk-A4J7EECR.js.map} +0 -0
  82. /package/dist/{chunk-VQX54TK3.js.map → chunk-B5C5SBSI.js.map} +0 -0
  83. /package/dist/{chunk-OI6YZXTU.js.map → chunk-DKFEUGM4.js.map} +0 -0
  84. /package/dist/{chunk-HYBQABBJ.js.map → chunk-HUKBIZGH.js.map} +0 -0
  85. /package/dist/{chunk-OEFNTGR3.js.map → chunk-JWBEE4CL.js.map} +0 -0
  86. /package/dist/{chunk-BRVBLQAL.js.map → chunk-K5MTNKJU.js.map} +0 -0
  87. /package/dist/{chunk-6UKNJ43T.js.map → chunk-NZ2SP55O.js.map} +0 -0
  88. /package/dist/{chunk-3XHC5KC5.js.map → chunk-OKCBARX6.js.map} +0 -0
  89. /package/dist/{chunk-PNWWNEDM.js.map → chunk-OV5AUIBA.js.map} +0 -0
  90. /package/dist/{chunk-6S5T6FSP.js.map → chunk-W75DRYG3.js.map} +0 -0
  91. /package/dist/{chunk-7S3O33NM.js.map → chunk-Y72DP3JF.js.map} +0 -0
@@ -331,7 +331,8 @@ export async function readInsightsUsage(
331
331
  ||jsonb_strip_nulls(jsonb_build_object(
332
332
  'provider',case when ${query.groupBy} in ('model','provider') then provider end,
333
333
  'model',case when ${query.groupBy}='model' then model end,
334
- 'workspaceId',case when ${query.groupBy}='workspace' then workspace_id end,
334
+ 'workspaceId',case when ${query.groupBy}='workspace'
335
+ or (${query.groupBy}='rootSession' and kind='item') then workspace_id end,
335
336
  'you',case when ${query.groupBy}='person' or kind='personal' then you end))
336
337
  order by (measures->>'chargedMicros')::bigint+(measures->>'listMicros')::bigint desc,key) from selected_groups),'[]'::jsonb),
337
338
  'series',coalesce((select jsonb_agg(jsonb_build_object('start',points.start,'measures',coalesce(buckets.measures,zero.measures))
@@ -0,0 +1,242 @@
1
+ import { createHash } from "node:crypto";
2
+ import { and, desc, eq, gt, isNull, sql } from "drizzle-orm";
3
+ import { parseVerifiedAttemptToolCatalog } from "@opengeni/codemode";
4
+ import {
5
+ McpConnectionAccountBindings,
6
+ ToolActionReview,
7
+ decodeReviewArguments,
8
+ ToolDisplayMetadata,
9
+ stableJson,
10
+ toolReviewAction,
11
+ toolReviewContextFromSchema,
12
+ toolReviewFields,
13
+ type ToolReviewContext,
14
+ } from "@opengeni/contracts";
15
+ import { type Database, withRlsContext } from "./database";
16
+ import { fromPostgresLosslessJson } from "./lossless-json";
17
+ import * as schema from "./schema";
18
+
19
+ const object = (value: unknown): Record<string, unknown> | null =>
20
+ value !== null && typeof value === "object" && !Array.isArray(value)
21
+ ? (value as Record<string, unknown>)
22
+ : null;
23
+
24
+ function approval(value: unknown) {
25
+ const item = object(value);
26
+ const raw = object(item?.rawItem);
27
+ if (!item) return null;
28
+ return {
29
+ id: raw?.callId ?? raw?.id ?? item.id ?? item.callId,
30
+ name: item.name ?? item.toolName ?? raw?.name,
31
+ args: decodeReviewArguments(item.arguments ?? raw?.arguments),
32
+ };
33
+ }
34
+
35
+ /** Read-only presentation for SDK approvals created before durable connector
36
+ * reviews. Neither current catalogs nor display labels establish a provider.
37
+ * This never creates a request, changes a policy, or resumes an operation. */
38
+ export async function legacyToolActionReview(
39
+ db: Database,
40
+ input: { accountId: string; workspaceId: string; sessionId: string; approvalId: string },
41
+ ) {
42
+ return await withRlsContext(db, input, async (tx) => {
43
+ const [snapshot] = await tx
44
+ .select({
45
+ state: {
46
+ id: schema.agentRunStates.id,
47
+ pending: schema.agentRunStates.pendingApprovals,
48
+ codec: schema.agentRunStates.pendingApprovalsCodecVersion,
49
+ },
50
+ turn: schema.sessionTurns,
51
+ session: { status: schema.sessions.status, activeTurnId: schema.sessions.activeTurnId },
52
+ })
53
+ .from(schema.agentRunStates)
54
+ .innerJoin(schema.sessionTurns, eq(schema.sessionTurns.id, schema.agentRunStates.turnId))
55
+ .innerJoin(schema.sessions, eq(schema.sessions.id, schema.agentRunStates.sessionId))
56
+ .where(
57
+ and(
58
+ eq(schema.agentRunStates.accountId, input.accountId),
59
+ eq(schema.agentRunStates.workspaceId, input.workspaceId),
60
+ eq(schema.agentRunStates.sessionId, input.sessionId),
61
+ ),
62
+ )
63
+ .orderBy(desc(schema.agentRunStates.stateVersion))
64
+ .limit(1);
65
+ if (!snapshot) return null;
66
+ const pending = fromPostgresLosslessJson(snapshot.state.pending, snapshot.state.codec);
67
+ const matching = Array.isArray(pending)
68
+ ? pending.map(approval).filter((item) => item?.id === input.approvalId)
69
+ : [];
70
+ if (matching.length !== 1) return null;
71
+ const saved = matching[0]!;
72
+ if (typeof saved.name !== "string" || saved.args === null) return null;
73
+ const args = saved.args;
74
+ const { turn, session } = snapshot;
75
+ const scope = and(
76
+ eq(schema.sessionEvents.accountId, input.accountId),
77
+ eq(schema.sessionEvents.workspaceId, input.workspaceId),
78
+ eq(schema.sessionEvents.sessionId, input.sessionId),
79
+ eq(schema.sessionEvents.turnId, turn.id),
80
+ eq(schema.sessionEvents.turnAssociation, "current"),
81
+ isNull(schema.sessionEvents.duplicateOfEventId),
82
+ );
83
+ const [waiting] = await tx
84
+ .select()
85
+ .from(schema.sessionEvents)
86
+ .where(and(scope, eq(schema.sessionEvents.type, "session.requiresAction")))
87
+ .orderBy(desc(schema.sessionEvents.sequence))
88
+ .limit(1);
89
+ if (!waiting?.turnAttemptId || waiting.turnGeneration === null) return null;
90
+ const waitPayload = object(
91
+ fromPostgresLosslessJson(waiting.payload, waiting.payloadCodecVersion),
92
+ );
93
+ const waitApprovals = Array.isArray(waitPayload?.approvals)
94
+ ? waitPayload.approvals.map(approval).filter((item) => item?.id === input.approvalId)
95
+ : [];
96
+ if (
97
+ waitApprovals.length !== 1 ||
98
+ waitApprovals[0]!.name !== saved.name ||
99
+ stableJson(waitApprovals[0]!.args) !== stableJson(args)
100
+ )
101
+ return null;
102
+
103
+ let toolName = saved.name;
104
+ let context: ToolReviewContext = { kind: "generic" };
105
+ // Only the immutable attempt catalog proves which fields its schema protects.
106
+ // Without that proof no argument value is shown and approval is unavailable.
107
+ let proven = false;
108
+ const calls = await tx
109
+ .select()
110
+ .from(schema.sessionEvents)
111
+ .where(
112
+ and(
113
+ scope,
114
+ eq(schema.sessionEvents.turnAttemptId, waiting.turnAttemptId),
115
+ eq(schema.sessionEvents.turnGeneration, waiting.turnGeneration),
116
+ eq(schema.sessionEvents.type, "agent.toolCall.created"),
117
+ sql`${schema.sessionEvents.payload}->>'id' = ${input.approvalId}`,
118
+ ),
119
+ )
120
+ .limit(2);
121
+ if (calls.length === 1 && calls[0]!.sequence < waiting.sequence) {
122
+ const call = object(
123
+ fromPostgresLosslessJson(calls[0]!.payload, calls[0]!.payloadCodecVersion),
124
+ );
125
+ if (
126
+ call?.name === saved.name &&
127
+ stableJson(decodeReviewArguments(call.arguments)) === stableJson(args)
128
+ ) {
129
+ const displayResult = ToolDisplayMetadata.safeParse(call.display);
130
+ const display = displayResult.success ? displayResult.data : undefined;
131
+ if (display)
132
+ context = {
133
+ kind: "generic",
134
+ title: (display.title ?? display.toolName.replaceAll("_", " ")).slice(0, 256),
135
+ ...(display.accountLabel ? { accountLabel: display.accountLabel.slice(0, 256) } : {}),
136
+ };
137
+ const [stored] = await tx
138
+ .select()
139
+ .from(schema.sessionAttemptToolCatalogs)
140
+ .where(
141
+ and(
142
+ eq(schema.sessionAttemptToolCatalogs.accountId, input.accountId),
143
+ eq(schema.sessionAttemptToolCatalogs.workspaceId, input.workspaceId),
144
+ eq(schema.sessionAttemptToolCatalogs.sessionId, input.sessionId),
145
+ eq(schema.sessionAttemptToolCatalogs.turnId, turn.id),
146
+ eq(schema.sessionAttemptToolCatalogs.attemptId, waiting.turnAttemptId),
147
+ eq(schema.sessionAttemptToolCatalogs.executionGeneration, waiting.turnGeneration),
148
+ ),
149
+ )
150
+ .limit(1);
151
+ // The immutable model-name map supplies schema privacy and exact tool
152
+ // identity; the accepted account binding independently supplies provider.
153
+ if (stored) {
154
+ const catalog = parseVerifiedAttemptToolCatalog(stored.catalog);
155
+ const entry = catalog.entries.find((item) => item.modelName === saved.name);
156
+ if (entry) {
157
+ const bindings = McpConnectionAccountBindings.safeParse(turn.mcpAccountBindings);
158
+ const binding = bindings.success
159
+ ? bindings.data.find((item) => item.serverId === entry.identity.serverId)
160
+ : undefined;
161
+ proven = true;
162
+ toolName = entry.identity.toolName;
163
+ context = toolReviewContextFromSchema(entry.inputSchema, {
164
+ kind:
165
+ entry.source === "mcp" && binding?.providerDomain === "gmailmcp.googleapis.com"
166
+ ? "gmail"
167
+ : "generic",
168
+ ...(entry.title ? { title: entry.title } : {}),
169
+ ...(binding?.accountLabel
170
+ ? { accountLabel: binding.accountLabel }
171
+ : context.accountLabel
172
+ ? { accountLabel: context.accountLabel }
173
+ : {}),
174
+ });
175
+ }
176
+ }
177
+ }
178
+ }
179
+ const [decision] = await tx
180
+ .select({ payload: schema.sessionEvents.payload })
181
+ .from(schema.sessionEvents)
182
+ .where(
183
+ and(
184
+ scope,
185
+ eq(schema.sessionEvents.type, "user.approvalDecision"),
186
+ gt(schema.sessionEvents.sequence, waiting.sequence),
187
+ sql`${schema.sessionEvents.payload}->>'approvalId' = ${input.approvalId}`,
188
+ ),
189
+ )
190
+ .orderBy(desc(schema.sessionEvents.sequence))
191
+ .limit(1);
192
+ const active =
193
+ !decision &&
194
+ turn.status === "requires_action" &&
195
+ session.status === "requires_action" &&
196
+ session.activeTurnId === turn.id;
197
+ const status = decision
198
+ ? object(decision.payload)?.decision === "reject"
199
+ ? "rejected"
200
+ : "approved"
201
+ : active
202
+ ? "pending"
203
+ : "cancelled";
204
+ const actionDigest = createHash("sha256")
205
+ .update(
206
+ stableJson({
207
+ accountId: input.accountId,
208
+ workspaceId: input.workspaceId,
209
+ sessionId: input.sessionId,
210
+ turnId: turn.id,
211
+ approvalId: input.approvalId,
212
+ name: saved.name,
213
+ args,
214
+ }),
215
+ )
216
+ .digest("hex");
217
+ return {
218
+ args,
219
+ context,
220
+ proven,
221
+ review: ToolActionReview.parse({
222
+ version: 1,
223
+ id: input.approvalId,
224
+ actionDigest,
225
+ revision: `${snapshot.state.id}:${turn.updatedAt.toISOString()}:${status}`,
226
+ status,
227
+ ...(proven
228
+ ? {
229
+ ...toolReviewAction(toolName, args, context),
230
+ ...toolReviewFields(args, context),
231
+ }
232
+ : { ...toolReviewAction(toolName, null, context), fields: [], moreFields: 0 }),
233
+ ...(context.accountLabel ? { accountLabel: context.accountLabel } : {}),
234
+ reason: "This action asks first.",
235
+ createdAt: waiting.createdAt.toISOString(),
236
+ updatedAt: turn.updatedAt.toISOString(),
237
+ availableActions: active ? (proven ? ["approve", "reject"] : ["reject"]) : [],
238
+ detailsAvailable: proven,
239
+ }),
240
+ };
241
+ });
242
+ }
@@ -0,0 +1,148 @@
1
+ import { sql } from "drizzle-orm";
2
+ import { rawRows, type Database } from "./database";
3
+
4
+ /** Structural V2 correlation scope. This is deliberately NOT an actor/grant. */
5
+ export type ModalNativeLiveOriginScope = Readonly<{
6
+ version: 2;
7
+ declarationId: string;
8
+ planId: string;
9
+ creatorId: string;
10
+ accountId: string;
11
+ workspaceId: string;
12
+ sessionId: string;
13
+ turnId: string;
14
+ attemptId: string;
15
+ executionGeneration: number;
16
+ triggerEventId: string;
17
+ sandboxGroupId: string;
18
+ routeKind: "home";
19
+ routeTargetId: null;
20
+ routeEpoch: number;
21
+ }>;
22
+
23
+ export type ModalNativeLiveOriginProjection = Readonly<{
24
+ version: 1;
25
+ scope: ModalNativeLiveOriginScope;
26
+ initiator: {
27
+ kind: "subject" | "service";
28
+ subjectId: string;
29
+ initiatingHumanSubjectId: string;
30
+ };
31
+ membership: {
32
+ id: string;
33
+ authorizationRevision: string;
34
+ basis: "workspace_membership" | "personal_workspace";
35
+ };
36
+ acceptedAuthority: {
37
+ epoch: number;
38
+ visibility: "user_private" | "workspace_shared";
39
+ ownerOrganizationMembershipId: string | null;
40
+ };
41
+ currentAuthority: {
42
+ epoch: number;
43
+ executionEpoch: number;
44
+ visibility: "user_private" | "workspace_shared";
45
+ ownerSubjectId: string | null;
46
+ ownerOrganizationMembershipId: string | null;
47
+ };
48
+ control: { workspaceRevision: string; sessionVersion: string };
49
+ execution: { workflowId: string; workflowRunId: string; activityId: string };
50
+ providerRecoveryCount: 0 | 1 | 2 | 3 | 4 | 5;
51
+ checkedAt: string;
52
+ }>;
53
+
54
+ export type ModalNativeLiveOriginResult =
55
+ | { kind: "live"; projection: ModalNativeLiveOriginProjection }
56
+ | { kind: "fenced" }
57
+ | {
58
+ kind: "unsupported";
59
+ reason: "invalid_scope" | "human_unavailable" | "external_authority" | "not_premodel";
60
+ };
61
+
62
+ const keys = [
63
+ "version",
64
+ "declarationId",
65
+ "planId",
66
+ "creatorId",
67
+ "accountId",
68
+ "workspaceId",
69
+ "sessionId",
70
+ "turnId",
71
+ "attemptId",
72
+ "executionGeneration",
73
+ "triggerEventId",
74
+ "sandboxGroupId",
75
+ "routeKind",
76
+ "routeTargetId",
77
+ "routeEpoch",
78
+ ] as const;
79
+ const ids = [
80
+ "declarationId",
81
+ "planId",
82
+ "creatorId",
83
+ "accountId",
84
+ "workspaceId",
85
+ "sessionId",
86
+ "turnId",
87
+ "attemptId",
88
+ "triggerEventId",
89
+ "sandboxGroupId",
90
+ ] as const;
91
+ const uuid = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/;
92
+
93
+ function scopeSnapshot(value: ModalNativeLiveOriginScope): ModalNativeLiveOriginScope | null {
94
+ if (!value || typeof value !== "object") return null;
95
+ const descriptors = Object.getOwnPropertyDescriptors(value);
96
+ if (Reflect.ownKeys(value).length !== keys.length) return null;
97
+ const copy: Record<string, unknown> = {};
98
+ for (const key of keys) {
99
+ const descriptor = descriptors[key];
100
+ if (!descriptor || !("value" in descriptor)) return null;
101
+ copy[key] = descriptor.value;
102
+ }
103
+ if (ids.some((key) => typeof copy[key] !== "string" || !uuid.test(copy[key] as string)))
104
+ return null;
105
+ if (
106
+ copy.version !== 2 ||
107
+ copy.routeKind !== "home" ||
108
+ copy.routeTargetId !== null ||
109
+ copy.creatorId === copy.attemptId ||
110
+ copy.creatorId === copy.planId ||
111
+ !Number.isSafeInteger(copy.executionGeneration) ||
112
+ (copy.executionGeneration as number) < 1 ||
113
+ (copy.executionGeneration as number) > 2_147_483_647 ||
114
+ !Number.isSafeInteger(copy.routeEpoch) ||
115
+ (copy.routeEpoch as number) < 0 ||
116
+ (copy.routeEpoch as number) > 2_147_483_647
117
+ )
118
+ return null;
119
+ return Object.freeze(copy) as ModalNativeLiveOriginScope;
120
+ }
121
+
122
+ /**
123
+ * Private-server integration primitive, NOT host authentication or a grant.
124
+ * The caller must authenticate the exact host request separately and consume
125
+ * this projection in this SAME transaction. No native/provider awaits here.
126
+ * Enter before taking any tenancy/control/session/lease lock: SQL acquires the
127
+ * organization-membership -> tenancy -> canonical control/row prefix itself.
128
+ * Preserves all subject and tenant GUCs. SQL uncertainty propagates (including
129
+ * 55P03); no failed/aborted transaction is reported as successful admission.
130
+ */
131
+ export async function lockLiveNativeOriginalOriginTx(
132
+ scopedTx: Database,
133
+ scope: ModalNativeLiveOriginScope,
134
+ ): Promise<ModalNativeLiveOriginResult> {
135
+ if (typeof (scopedTx as Database & { rollback?: unknown }).rollback !== "function") {
136
+ throw new Error("LIVE native origin projection requires an existing transaction");
137
+ }
138
+ const snapshot = scopeSnapshot(scope);
139
+ if (!snapshot) return { kind: "unsupported", reason: "invalid_scope" };
140
+ const [row] = await rawRows<{ result: ModalNativeLiveOriginResult }>(
141
+ scopedTx,
142
+ sql`select lock_live_native_original_origin_v2(${JSON.stringify(snapshot)}::jsonb) as result`,
143
+ );
144
+ if (!row || !["live", "fenced", "unsupported"].includes(row.result?.kind)) {
145
+ throw new Error("Invalid LIVE native origin database projection");
146
+ }
147
+ return row.result;
148
+ }
@@ -1,6 +1,6 @@
1
1
  import { sql } from "drizzle-orm";
2
2
  import { rawRows, setSubjectRlsContext, withRlsContext, type Database } from "./database";
3
- import { getConnectionMetadata, listConnectionsMetadata } from "./index";
3
+ import { getConnectionMetadata, listConnectionsMetadata } from "./connection-metadata";
4
4
 
5
5
  export type SlackBotAccess = {
6
6
  connectionId: string;
@@ -578,6 +578,8 @@ async function grantAppRoleIfSchemaExists(
578
578
  "prepare_organization_membership_protocol_settlements(jsonb)",
579
579
  "assert_active_managed_human_organization_membership(uuid,text)",
580
580
  "resolve_workspace_writer_grant_identity(uuid,text)",
581
+ "lock_live_native_original_origin_v2(jsonb)",
582
+ "modal_native_origin_member_read_active(uuid,text)",
581
583
  "prepare_workspace_membership_removal_settlements(jsonb)",
582
584
  "workspace_membership_removal_command(jsonb)",
583
585
  "get_organization_retention_policy(uuid,text)",
@@ -2105,6 +2107,16 @@ BEGIN
2105
2107
  REVOKE ALL ON FUNCTION opengeni_private.list_pending_modal_provider_creates() FROM PUBLIC;
2106
2108
  END IF;
2107
2109
  END IF;
2110
+ IF to_regclass('opengeni_private.modal_native_origin_read_capabilities') IS NOT NULL THEN
2111
+ EXECUTE format('REVOKE ALL ON TABLE opengeni_private.modal_native_origin_read_capabilities FROM %I', ${literal(role)});
2112
+ EXECUTE format('REVOKE ALL (%s) ON TABLE opengeni_private.modal_native_origin_read_capabilities FROM %I',
2113
+ (SELECT string_agg(quote_ident(attname), ',') FROM pg_attribute
2114
+ WHERE attrelid='opengeni_private.modal_native_origin_read_capabilities'::regclass AND attnum>0 AND NOT attisdropped), ${literal(role)});
2115
+ REVOKE ALL ON TABLE opengeni_private.modal_native_origin_read_capabilities FROM PUBLIC;
2116
+ EXECUTE format('REVOKE ALL (%s) ON TABLE opengeni_private.modal_native_origin_read_capabilities FROM PUBLIC',
2117
+ (SELECT string_agg(quote_ident(attname), ',') FROM pg_attribute
2118
+ WHERE attrelid='opengeni_private.modal_native_origin_read_capabilities'::regclass AND attnum>0 AND NOT attisdropped));
2119
+ END IF;
2108
2120
  IF to_regclass('opengeni_private.sandbox_recovery_rollout') IS NOT NULL THEN
2109
2121
  -- Migration may precede this role's creation. Converge only read access;
2110
2122
  -- runtime identities and PUBLIC never receive recovery activation writes.
@@ -609,6 +609,8 @@ const PRIVATE_SESSION_CREATE_CAPABILITY_ROUTINES = [
609
609
  "close_private_session_create_capability(uuid)",
610
610
  ] as const;
611
611
  const SESSION_AUTHORITY_ROUTINES = new Set<string>([
612
+ "lock_live_native_original_origin_v2(jsonb)",
613
+ "modal_native_origin_member_read_active(uuid, text)",
612
614
  LEGACY_FORK_SESSION_CONTENT_ROUTINE,
613
615
  FORK_SESSION_CONTENT_ROUTINE,
614
616
  MESSAGE_FORK_SESSION_CONTENT_ROUTINE,
@@ -712,6 +714,8 @@ const UNIFIED_KNOWLEDGE_AUTHORITY_TABLES = [
712
714
  "documents",
713
715
  ] as const;
714
716
  export const RUNTIME_TARGET_SCHEMA_CAPABILITY_ROUTINES = [
717
+ "lock_live_native_original_origin_v2(jsonb)",
718
+ "modal_native_origin_member_read_active(uuid, text)",
715
719
  ...CLAUDE_SUBSCRIPTION_CAPABILITY_ROUTINES,
716
720
  "maintain_usage_allowances(integer, integer)",
717
721
  "usage_allowance_command(jsonb)",
@@ -2135,15 +2139,15 @@ export async function inspectRuntimeDatabasePosture(
2135
2139
  -- Column-only grants on capability tables are also unsafe; a pin
2136
2140
  -- table must remain EXECUTE-only even after column ACL drift.
2137
2141
  (has_table_privilege(current_user, c.oid, 'SELECT') or
2138
- ((c.relname in ('modal_inventory_read_capabilities','usage_allowance_capabilities','session_import_batches', ${ARTIFACT_PINS_TABLE}) or
2142
+ ((c.relname in ('modal_native_origin_read_capabilities','modal_inventory_read_capabilities','usage_allowance_capabilities','session_import_batches', ${ARTIFACT_PINS_TABLE}) or
2139
2143
  c.relname in (select jsonb_array_elements_text(${JSON.stringify(RUNTIME_ALLOWANCE_PRIVATE_TABLES)}::jsonb))) and
2140
2144
  has_any_column_privilege(current_user, c.oid, 'SELECT'))) as can_select,
2141
2145
  (has_table_privilege(current_user, c.oid, 'INSERT') or
2142
- ((c.relname in ('modal_inventory_read_capabilities','usage_allowance_capabilities','session_import_batches', ${ARTIFACT_PINS_TABLE}) or
2146
+ ((c.relname in ('modal_native_origin_read_capabilities','modal_inventory_read_capabilities','usage_allowance_capabilities','session_import_batches', ${ARTIFACT_PINS_TABLE}) or
2143
2147
  c.relname in (select jsonb_array_elements_text(${JSON.stringify(RUNTIME_ALLOWANCE_PRIVATE_TABLES)}::jsonb))) and
2144
2148
  has_any_column_privilege(current_user, c.oid, 'INSERT'))) as can_insert,
2145
2149
  (has_table_privilege(current_user, c.oid, 'UPDATE') or
2146
- ((c.relname in ('modal_inventory_read_capabilities','usage_allowance_capabilities','session_import_batches', ${ARTIFACT_PINS_TABLE}) or
2150
+ ((c.relname in ('modal_native_origin_read_capabilities','modal_inventory_read_capabilities','usage_allowance_capabilities','session_import_batches', ${ARTIFACT_PINS_TABLE}) or
2147
2151
  c.relname in (select jsonb_array_elements_text(${JSON.stringify(RUNTIME_ALLOWANCE_PRIVATE_TABLES)}::jsonb))) and
2148
2152
  has_any_column_privilege(current_user, c.oid, 'UPDATE'))) as can_update,
2149
2153
  has_table_privilege(current_user, c.oid, 'DELETE') as can_delete,
@@ -2190,6 +2194,7 @@ export async function inspectRuntimeDatabasePosture(
2190
2194
  'session_file_read_capabilities',
2191
2195
  'session_import_batches',
2192
2196
  'modal_inventory_read_capabilities',
2197
+ 'modal_native_origin_read_capabilities',
2193
2198
  ${AUTOMATIC_SESSION_TITLE_FANOUT_OUTBOX_TABLE},
2194
2199
  ${VERIFIED_SIGNUP_TRIAL_SWITCH_TABLE},
2195
2200
  'credit_promotion_policy_revisions',
@@ -4235,6 +4240,30 @@ export function evaluateRuntimeDatabasePosture(
4235
4240
  ) {
4236
4241
  violations.push("usage allowance attribution receipts lack same-owner FORCE-RLS isolation");
4237
4242
  }
4243
+ const nativeOriginCapability = posture.privateTables.find(
4244
+ (table) => table.name === "modal_native_origin_read_capabilities",
4245
+ );
4246
+ const nativeOriginRoutineInstalled = posture.targetRoutines.some(
4247
+ (routine) => routine.name === "lock_live_native_original_origin_v2(jsonb)",
4248
+ );
4249
+ if (
4250
+ (nativeOriginRoutineInstalled || nativeOriginCapability) &&
4251
+ (!nativeOriginCapability ||
4252
+ nativeOriginCapability.owner === expectedRole ||
4253
+ nativeOriginCapability.owner !== tableByName.get("sessions")?.owner ||
4254
+ nativeOriginCapability.owner !== tableByName.get("organization_memberships")?.owner ||
4255
+ !nativeOriginCapability.rlsEnabled ||
4256
+ !nativeOriginCapability.rlsForced ||
4257
+ !nativeOriginCapability.rlsActive ||
4258
+ nativeOriginCapability.select ||
4259
+ nativeOriginCapability.insert ||
4260
+ nativeOriginCapability.update ||
4261
+ nativeOriginCapability.delete ||
4262
+ nativeOriginCapability.truncate ||
4263
+ nativeOriginCapability.references ||
4264
+ nativeOriginCapability.trigger)
4265
+ )
4266
+ violations.push("Native LIVE-origin read capability has unsafe owner, RLS or privileges");
4238
4267
  const modalInventoryCapability = posture.privateTables.find(
4239
4268
  (table) => table.name === "modal_inventory_read_capabilities",
4240
4269
  );
@@ -0,0 +1,91 @@
1
+ import { sql } from "drizzle-orm";
2
+ import { rawRows, withRlsContext, type Database } from "./database";
3
+
4
+ export const INTERACTION_ONLY_IDLE_BUCKETS = ["lt_30m", "30m_2h", "2h_6h", "gte_6h"] as const;
5
+ export type InteractionOnlyIdleBucket = (typeof INTERACTION_ONLY_IDLE_BUCKETS)[number];
6
+
7
+ export type InteractionOnlyLeaseCandidate = {
8
+ accountId: string;
9
+ workspaceId: string;
10
+ sandboxGroupId: string;
11
+ };
12
+
13
+ export function interactionOnlyIdleBucket(idleMs: number): InteractionOnlyIdleBucket {
14
+ if (idleMs < 30 * 60_000) return "lt_30m";
15
+ if (idleMs < 2 * 60 * 60_000) return "30m_2h";
16
+ if (idleMs < 6 * 60 * 60_000) return "2h_6h";
17
+ return "gte_6h";
18
+ }
19
+
20
+ /**
21
+ * Read-only inventory of warm leases whose ONLY holders are Browser/Computer
22
+ * interaction holders, bucketed by time since the newest interaction activity
23
+ * (holder heartbeat, controller heartbeat, or last use). Interaction holders
24
+ * deliberately never expire by timestamp, so such a box stays warm until its
25
+ * session ends or the provider deadline; this makes that visible. Candidates
26
+ * come from the sanctioned cross-workspace warm-lease list; each workspace is
27
+ * then read under its own RLS scope. No lock beyond the snapshot is taken.
28
+ */
29
+ export async function countInteractionOnlyWarmLeasesByIdle(
30
+ db: Database,
31
+ candidates: InteractionOnlyLeaseCandidate[],
32
+ options: { now?: Date; maxWorkspaces?: number } = {},
33
+ ): Promise<Record<InteractionOnlyIdleBucket, number>> {
34
+ const nowMs = (options.now ?? new Date()).getTime();
35
+ const counts = Object.fromEntries(INTERACTION_ONLY_IDLE_BUCKETS.map((b) => [b, 0])) as Record<
36
+ InteractionOnlyIdleBucket,
37
+ number
38
+ >;
39
+ const byWorkspace = new Map<string, InteractionOnlyLeaseCandidate[]>();
40
+ for (const candidate of candidates) {
41
+ const key = `${candidate.accountId}:${candidate.workspaceId}`;
42
+ byWorkspace.set(key, [...(byWorkspace.get(key) ?? []), candidate]);
43
+ }
44
+ const workspaces = [...byWorkspace.values()].slice(0, options.maxWorkspaces ?? 500);
45
+ for (const group of workspaces) {
46
+ const { accountId, workspaceId } = group[0]!;
47
+ const groupIds = group.map((candidate) => candidate.sandboxGroupId);
48
+ const rows = await withRlsContext(
49
+ db,
50
+ { accountId, workspaceId },
51
+ async (scoped) =>
52
+ await rawRows<{ last_activity_at: Date | string | null }>(
53
+ scoped,
54
+ sql`
55
+ select max(greatest(
56
+ holder.last_heartbeat_at,
57
+ browser.controller_heartbeat_at, browser.last_used_at,
58
+ computer.controller_heartbeat_at, computer.last_used_at
59
+ )) as last_activity_at
60
+ from sandbox_leases lease
61
+ join sandbox_lease_holders holder
62
+ on holder.lease_id = lease.id and holder.workspace_id = lease.workspace_id
63
+ left join browser_sessions browser
64
+ on holder.holder_id = 'browser-session:' || browser.id::text
65
+ and browser.workspace_id = lease.workspace_id
66
+ left join computer_sessions computer
67
+ on holder.holder_id = 'computer-session:' || computer.id::text
68
+ and computer.workspace_id = lease.workspace_id
69
+ where lease.account_id = ${accountId}
70
+ and lease.workspace_id = ${workspaceId}
71
+ and lease.sandbox_group_id in ${sql`(${sql.join(
72
+ groupIds.map((id) => sql`${id}`),
73
+ sql`, `,
74
+ )})`}
75
+ and lease.liveness = 'warm'
76
+ group by lease.id
77
+ having bool_and(holder.kind = 'interaction')
78
+ `,
79
+ ),
80
+ undefined,
81
+ "none",
82
+ );
83
+ for (const row of rows) {
84
+ const last = row.last_activity_at ? new Date(row.last_activity_at).getTime() : Number.NaN;
85
+ counts[
86
+ interactionOnlyIdleBucket(Number.isFinite(last) ? Math.max(0, nowMs - last) : Infinity)
87
+ ] += 1;
88
+ }
89
+ }
90
+ return counts;
91
+ }
package/src/schema.ts CHANGED
@@ -5293,6 +5293,7 @@ export const sessionRealtimeModes = pgTable(
5293
5293
  modelValid: check(
5294
5294
  "session_realtime_modes_model_check",
5295
5295
  sql`${table.model} in (
5296
+ 'opengeni-azure/gpt-live-1',
5296
5297
  'gpt-live-1-boulder-alpha',
5297
5298
  'supergrok/grok-voice-think-fast-2.0',
5298
5299
  'opengeni-gateway/openai/gpt-realtime-2.1',