@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.
- package/dist/canonical-human-identities.js +3 -3
- package/dist/capability-reconciliation.js +2 -2
- package/dist/{chunk-7QWWTVYH.js → chunk-5ROHLEN6.js} +29 -4
- package/dist/chunk-5ROHLEN6.js.map +1 -0
- package/dist/{chunk-AQMH2ZEH.js → chunk-A4J7EECR.js} +2 -2
- package/dist/{chunk-VQX54TK3.js → chunk-B5C5SBSI.js} +3 -3
- package/dist/{chunk-OI6YZXTU.js → chunk-DKFEUGM4.js} +2 -2
- package/dist/{chunk-HYBQABBJ.js → chunk-HUKBIZGH.js} +2 -2
- package/dist/{chunk-LRSIODCQ.js → chunk-IT3EYY2C.js} +6 -4
- package/dist/{chunk-LRSIODCQ.js.map → chunk-IT3EYY2C.js.map} +1 -1
- package/dist/{chunk-DSNEIW5E.js → chunk-JL6TBB76.js} +2 -1
- package/dist/chunk-JL6TBB76.js.map +1 -0
- package/dist/{chunk-OEFNTGR3.js → chunk-JWBEE4CL.js} +4 -4
- package/dist/{chunk-BRVBLQAL.js → chunk-K5MTNKJU.js} +4 -4
- package/dist/{chunk-6UKNJ43T.js → chunk-NZ2SP55O.js} +2 -2
- package/dist/{chunk-3XHC5KC5.js → chunk-OKCBARX6.js} +2 -2
- package/dist/{chunk-PNWWNEDM.js → chunk-OV5AUIBA.js} +2 -2
- package/dist/{chunk-BID3CNN3.js → chunk-TODVDGMO.js} +36 -18
- package/dist/{chunk-BID3CNN3.js.map → chunk-TODVDGMO.js.map} +1 -1
- package/dist/{chunk-6S5T6FSP.js → chunk-W75DRYG3.js} +5 -5
- package/dist/{chunk-7S3O33NM.js → chunk-Y72DP3JF.js} +2 -2
- package/dist/codemode-approvals.d.ts +7 -1
- package/dist/connect-attempts.js +3 -3
- package/dist/connection-metadata.d.ts +416 -0
- package/dist/credit-balances.d.ts +25 -4
- package/dist/database.d.ts +7 -1
- package/dist/editable-artifact-durable-export.js +2 -2
- package/dist/editable-artifacts.js +3 -3
- package/dist/index.d.ts +51 -8
- package/dist/index.js +10826 -10129
- package/dist/index.js.map +1 -1
- package/dist/legacy-tool-action-reviews.d.ts +75 -0
- package/dist/managed-auth-session-sets.js +3 -3
- package/dist/mcp-operations.js +2 -2
- package/dist/modal-native-live-origin.d.ts +75 -0
- package/dist/organization-integration-policy.js +3 -3
- package/dist/organization-slack-bots.d.ts +1 -1
- package/dist/provision-roles.js +1 -1
- package/dist/retained-provider-commands.d.ts +21 -21
- package/dist/retained-provider-commands.js +6 -6
- package/dist/runtime-posture.d.ts +1 -1
- package/dist/sandbox-interaction-idle.d.ts +22 -0
- package/dist/schema.js +1 -1
- package/dist/session-background-commands.js +4 -4
- package/dist/session-command-output.js +5 -5
- package/dist/session-event-slices.js +3 -3
- package/dist/session-mcp-credential-rotation.js +5 -5
- package/dist/session-realtime-billing.d.ts +48 -0
- package/dist/session-realtime.d.ts +8 -2
- package/dist/session-tenancy.js +3 -3
- package/dist/video-generation.js +3 -3
- package/dist/voice-transcription-settlement.d.ts +36 -0
- package/dist/workspace-tool-defaults.js +4 -4
- package/drizzle/0623_voice_transcription_attribution.sql +128 -0
- package/drizzle/0624_azure_live_realtime_model.sql +30 -0
- package/drizzle/0625_transcription_billing_refusal_codes.sql +29 -0
- package/drizzle/0632_modal_native_live_origin.sql +239 -0
- package/package.json +7 -7
- package/src/codemode-approvals.ts +52 -22
- package/src/codemode-operations.ts +198 -181
- package/src/connection-metadata.ts +166 -0
- package/src/credit-balances.ts +58 -17
- package/src/database.ts +9 -0
- package/src/index.ts +540 -412
- package/src/insights-unified.ts +2 -1
- package/src/legacy-tool-action-reviews.ts +242 -0
- package/src/modal-native-live-origin.ts +148 -0
- package/src/organization-slack-bots.ts +1 -1
- package/src/provision-roles.ts +12 -0
- package/src/runtime-posture.ts +32 -3
- package/src/sandbox-interaction-idle.ts +91 -0
- package/src/schema.ts +1 -0
- package/src/session-realtime-billing.ts +135 -0
- package/src/session-realtime-ledger.ts +9 -4
- package/src/session-realtime.ts +20 -2
- package/src/tool-action-reviews.ts +129 -18
- package/src/transcription-recordings.ts +3 -1
- package/src/voice-transcription-settlement.ts +94 -0
- package/dist/chunk-7QWWTVYH.js.map +0 -1
- package/dist/chunk-DSNEIW5E.js.map +0 -1
- /package/dist/{chunk-AQMH2ZEH.js.map → chunk-A4J7EECR.js.map} +0 -0
- /package/dist/{chunk-VQX54TK3.js.map → chunk-B5C5SBSI.js.map} +0 -0
- /package/dist/{chunk-OI6YZXTU.js.map → chunk-DKFEUGM4.js.map} +0 -0
- /package/dist/{chunk-HYBQABBJ.js.map → chunk-HUKBIZGH.js.map} +0 -0
- /package/dist/{chunk-OEFNTGR3.js.map → chunk-JWBEE4CL.js.map} +0 -0
- /package/dist/{chunk-BRVBLQAL.js.map → chunk-K5MTNKJU.js.map} +0 -0
- /package/dist/{chunk-6UKNJ43T.js.map → chunk-NZ2SP55O.js.map} +0 -0
- /package/dist/{chunk-3XHC5KC5.js.map → chunk-OKCBARX6.js.map} +0 -0
- /package/dist/{chunk-PNWWNEDM.js.map → chunk-OV5AUIBA.js.map} +0 -0
- /package/dist/{chunk-6S5T6FSP.js.map → chunk-W75DRYG3.js.map} +0 -0
- /package/dist/{chunk-7S3O33NM.js.map → chunk-Y72DP3JF.js.map} +0 -0
package/src/insights-unified.ts
CHANGED
|
@@ -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'
|
|
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 "./
|
|
3
|
+
import { getConnectionMetadata, listConnectionsMetadata } from "./connection-metadata";
|
|
4
4
|
|
|
5
5
|
export type SlackBotAccess = {
|
|
6
6
|
connectionId: string;
|
package/src/provision-roles.ts
CHANGED
|
@@ -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.
|
package/src/runtime-posture.ts
CHANGED
|
@@ -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',
|