@opengeni/db 3.7.4-canary.0 → 3.8.2-canary.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/canonical-human-identities.js +3 -3
- package/dist/{chunk-DXJ2WY6W.js → chunk-53A2PEHT.js} +241 -31
- package/dist/chunk-53A2PEHT.js.map +1 -0
- package/dist/{chunk-JS3AWDKZ.js → chunk-5S4BQAHX.js} +3 -3
- package/dist/{chunk-JP6IYGYI.js → chunk-CITYUGMW.js} +3 -3
- package/dist/{chunk-6FIVOJ3V.js → chunk-F5YOZEYQ.js} +45 -1
- package/dist/chunk-F5YOZEYQ.js.map +1 -0
- package/dist/{chunk-ZTSJPFDH.js → chunk-HZV7O6JR.js} +2 -2
- package/dist/{chunk-VDXHLVDE.js → chunk-RLQVPOVR.js} +2 -2
- package/dist/{chunk-LHKB62GC.js → chunk-UKOGHUGC.js} +2 -2
- package/dist/{chunk-QL4AHBYD.js → chunk-X6TO5HII.js} +2 -2
- package/dist/{chunk-3ZMN4EXC.js → chunk-Y53WEFYB.js} +2 -2
- package/dist/connection-token-resolver.d.ts +4 -0
- package/dist/editable-artifact-durable-export.js +2 -2
- package/dist/editable-artifacts.js +3 -3
- package/dist/index.d.ts +59 -12
- package/dist/index.js +5259 -4374
- package/dist/index.js.map +1 -1
- package/dist/insights.d.ts +9 -0
- package/dist/managed-auth-session-sets.js +3 -3
- package/dist/new-session-drafts.d.ts +12 -0
- package/dist/organization-membership-lifecycle.d.ts +8 -1
- package/dist/organization-model-providers.d.ts +101 -0
- package/dist/pack-components.d.ts +1 -0
- package/dist/provision-roles.js +1 -1
- package/dist/runtime-posture.d.ts +5 -5
- package/dist/schema.d.ts +645 -0
- package/dist/schema.js +7 -1
- package/dist/session-background-commands.js +3 -3
- package/dist/session-tenancy.js +3 -3
- package/dist/video-generation.js +3 -3
- package/drizzle/0390_organization_model_provider_connections.sql +197 -0
- package/drizzle/0391_sandbox_provider_deadline_interaction_followup.sql +295 -0
- package/drizzle/0392_context_compaction_pending_observability.sql +198 -0
- package/drizzle/0393_workspace_memory_and_learning_defaults.sql +102 -0
- package/drizzle/0394_session_selected_skill_activation.sql +20 -0
- package/drizzle/0395_scheduled_task_unclaimed_occurrence_invalidation.sql +154 -0
- package/drizzle/0396_model_call_equivalent_credit_cost.sql +28 -0
- package/drizzle/0397_sandbox_deadline_rotation_preemption.sql +113 -0
- package/drizzle/0398_organization_workspace_management_entry.sql +206 -0
- package/drizzle/0399_additional_managed_organization_creation.sql +691 -0
- package/package.json +6 -6
- package/src/connection-token-resolver.ts +12 -0
- package/src/index.ts +701 -155
- package/src/insights-model-bundle.ts +54 -5
- package/src/insights.ts +32 -0
- package/src/new-session-drafts.ts +66 -21
- package/src/organization-membership-lifecycle.ts +47 -5
- package/src/organization-model-providers.ts +614 -0
- package/src/pack-components.ts +15 -2
- package/src/provision-roles.ts +2 -0
- package/src/runtime-posture.ts +44 -0
- package/src/schema.ts +250 -30
- package/dist/chunk-6FIVOJ3V.js.map +0 -1
- package/dist/chunk-DXJ2WY6W.js.map +0 -1
- /package/dist/{chunk-JS3AWDKZ.js.map → chunk-5S4BQAHX.js.map} +0 -0
- /package/dist/{chunk-JP6IYGYI.js.map → chunk-CITYUGMW.js.map} +0 -0
- /package/dist/{chunk-ZTSJPFDH.js.map → chunk-HZV7O6JR.js.map} +0 -0
- /package/dist/{chunk-VDXHLVDE.js.map → chunk-RLQVPOVR.js.map} +0 -0
- /package/dist/{chunk-LHKB62GC.js.map → chunk-UKOGHUGC.js.map} +0 -0
- /package/dist/{chunk-QL4AHBYD.js.map → chunk-X6TO5HII.js.map} +0 -0
- /package/dist/{chunk-3ZMN4EXC.js.map → chunk-Y53WEFYB.js.map} +0 -0
|
@@ -0,0 +1,614 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { environmentsEncryptionKeyBytes, type Settings } from "@opengeni/config";
|
|
3
|
+
import { and, count, eq, exists, isNull, sql } from "drizzle-orm";
|
|
4
|
+
|
|
5
|
+
import { type Database, setSubjectRlsContext, withRlsContext } from "./database";
|
|
6
|
+
import { decryptEnvironmentValue } from "./environment-crypto";
|
|
7
|
+
import * as schema from "./schema";
|
|
8
|
+
|
|
9
|
+
export type OrganizationModelProviderKind = "vercel_gateway" | "openrouter";
|
|
10
|
+
export type OrganizationModelProviderConnection = {
|
|
11
|
+
providerKind: OrganizationModelProviderKind;
|
|
12
|
+
status: "active" | "revoked";
|
|
13
|
+
version: number;
|
|
14
|
+
createdAt: Date;
|
|
15
|
+
updatedAt: Date;
|
|
16
|
+
};
|
|
17
|
+
export type OrganizationModelProviderCustomModel = {
|
|
18
|
+
id: string;
|
|
19
|
+
accountId: string;
|
|
20
|
+
providerKind: OrganizationModelProviderKind;
|
|
21
|
+
upstreamModelId: string;
|
|
22
|
+
label: string | null;
|
|
23
|
+
version: number;
|
|
24
|
+
retiredAt: Date | null;
|
|
25
|
+
createdAt: Date;
|
|
26
|
+
updatedAt: Date;
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
export class OrganizationModelProviderConflictError extends Error {}
|
|
30
|
+
export class OrganizationModelProviderLimitError extends Error {}
|
|
31
|
+
|
|
32
|
+
function digest(value: unknown): string {
|
|
33
|
+
return createHash("sha256").update(JSON.stringify(value)).digest("hex");
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export function organizationModelProviderCredentialDigest(credential: string): string {
|
|
37
|
+
return createHash("sha256").update(credential).digest("hex");
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
async function withOrganizationProviderAdministrator<T>(
|
|
41
|
+
db: Database,
|
|
42
|
+
input: { organizationId: string; actorSubjectId: string },
|
|
43
|
+
use: (scopedDb: Database) => Promise<T>,
|
|
44
|
+
): Promise<T> {
|
|
45
|
+
return await withRlsContext(
|
|
46
|
+
db,
|
|
47
|
+
{ accountId: input.organizationId, workspaceId: null },
|
|
48
|
+
async (scopedDb) => {
|
|
49
|
+
await setSubjectRlsContext(scopedDb, input.actorSubjectId);
|
|
50
|
+
await scopedDb.execute(sql`
|
|
51
|
+
select get_organization_administration_overview(
|
|
52
|
+
${input.organizationId}::uuid, ${input.actorSubjectId}
|
|
53
|
+
)
|
|
54
|
+
`);
|
|
55
|
+
return await use(scopedDb);
|
|
56
|
+
},
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function mapConnection(
|
|
61
|
+
row: typeof schema.organizationModelProviderConnections.$inferSelect,
|
|
62
|
+
): OrganizationModelProviderConnection {
|
|
63
|
+
return {
|
|
64
|
+
providerKind: row.providerKind,
|
|
65
|
+
status: row.status,
|
|
66
|
+
version: row.version,
|
|
67
|
+
createdAt: row.createdAt,
|
|
68
|
+
updatedAt: row.updatedAt,
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function mapConnectionOperation(
|
|
73
|
+
row: typeof schema.organizationModelProviderConnectionOperations.$inferSelect,
|
|
74
|
+
): OrganizationModelProviderConnection {
|
|
75
|
+
return {
|
|
76
|
+
providerKind: row.providerKind,
|
|
77
|
+
status: row.resultStatus,
|
|
78
|
+
version: row.resultVersion,
|
|
79
|
+
createdAt: row.resultCreatedAt,
|
|
80
|
+
updatedAt: row.resultUpdatedAt,
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
async function recordConnectionOperation(
|
|
85
|
+
db: Database,
|
|
86
|
+
input: {
|
|
87
|
+
accountId: string;
|
|
88
|
+
operationId: string;
|
|
89
|
+
requestHash: string;
|
|
90
|
+
connection: OrganizationModelProviderConnection;
|
|
91
|
+
},
|
|
92
|
+
): Promise<void> {
|
|
93
|
+
await db.insert(schema.organizationModelProviderConnectionOperations).values({
|
|
94
|
+
accountId: input.accountId,
|
|
95
|
+
providerKind: input.connection.providerKind,
|
|
96
|
+
operationId: input.operationId,
|
|
97
|
+
requestHash: input.requestHash,
|
|
98
|
+
resultStatus: input.connection.status,
|
|
99
|
+
resultVersion: input.connection.version,
|
|
100
|
+
resultCreatedAt: input.connection.createdAt,
|
|
101
|
+
resultUpdatedAt: input.connection.updatedAt,
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function mapModel(
|
|
106
|
+
row: typeof schema.organizationModelProviderCustomModels.$inferSelect,
|
|
107
|
+
): OrganizationModelProviderCustomModel {
|
|
108
|
+
return {
|
|
109
|
+
id: row.id,
|
|
110
|
+
accountId: row.accountId,
|
|
111
|
+
providerKind: row.providerKind,
|
|
112
|
+
upstreamModelId: row.upstreamModelId,
|
|
113
|
+
label: row.label,
|
|
114
|
+
version: row.version,
|
|
115
|
+
retiredAt: row.retiredAt,
|
|
116
|
+
createdAt: row.createdAt,
|
|
117
|
+
updatedAt: row.updatedAt,
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
export async function getOrganizationModelProviderConnection(
|
|
122
|
+
db: Database,
|
|
123
|
+
input: {
|
|
124
|
+
organizationId: string;
|
|
125
|
+
actorSubjectId: string;
|
|
126
|
+
providerKind: OrganizationModelProviderKind;
|
|
127
|
+
},
|
|
128
|
+
): Promise<OrganizationModelProviderConnection | null> {
|
|
129
|
+
return await withOrganizationProviderAdministrator(db, input, async (scopedDb) => {
|
|
130
|
+
const [row] = await scopedDb
|
|
131
|
+
.select()
|
|
132
|
+
.from(schema.organizationModelProviderConnections)
|
|
133
|
+
.where(
|
|
134
|
+
and(
|
|
135
|
+
eq(schema.organizationModelProviderConnections.accountId, input.organizationId),
|
|
136
|
+
eq(schema.organizationModelProviderConnections.providerKind, input.providerKind),
|
|
137
|
+
),
|
|
138
|
+
)
|
|
139
|
+
.limit(1);
|
|
140
|
+
return row ? mapConnection(row) : null;
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
export async function upsertOrganizationModelProviderConnection(
|
|
145
|
+
db: Database,
|
|
146
|
+
input: {
|
|
147
|
+
organizationId: string;
|
|
148
|
+
actorSubjectId: string;
|
|
149
|
+
providerKind: OrganizationModelProviderKind;
|
|
150
|
+
credentialEncrypted: string;
|
|
151
|
+
credentialDigest: string;
|
|
152
|
+
operationId: string;
|
|
153
|
+
expectedVersion?: number;
|
|
154
|
+
},
|
|
155
|
+
): Promise<OrganizationModelProviderConnection> {
|
|
156
|
+
return await withOrganizationProviderAdministrator(db, input, async (scopedDb) => {
|
|
157
|
+
await scopedDb.execute(
|
|
158
|
+
sql`select pg_advisory_xact_lock(hashtextextended(${`organization-model-provider:${input.organizationId}:${input.providerKind}`}, 0))`,
|
|
159
|
+
);
|
|
160
|
+
const requestHash = digest({
|
|
161
|
+
operation: "upsert",
|
|
162
|
+
expectedVersion: input.expectedVersion,
|
|
163
|
+
credentialDigest: input.credentialDigest,
|
|
164
|
+
});
|
|
165
|
+
const [replay] = await scopedDb
|
|
166
|
+
.select()
|
|
167
|
+
.from(schema.organizationModelProviderConnectionOperations)
|
|
168
|
+
.where(
|
|
169
|
+
and(
|
|
170
|
+
eq(schema.organizationModelProviderConnectionOperations.accountId, input.organizationId),
|
|
171
|
+
eq(schema.organizationModelProviderConnectionOperations.providerKind, input.providerKind),
|
|
172
|
+
eq(schema.organizationModelProviderConnectionOperations.operationId, input.operationId),
|
|
173
|
+
),
|
|
174
|
+
)
|
|
175
|
+
.limit(1);
|
|
176
|
+
if (replay) {
|
|
177
|
+
if (replay.requestHash !== requestHash) throw new OrganizationModelProviderConflictError();
|
|
178
|
+
return mapConnectionOperation(replay);
|
|
179
|
+
}
|
|
180
|
+
const [existing] = await scopedDb
|
|
181
|
+
.select()
|
|
182
|
+
.from(schema.organizationModelProviderConnections)
|
|
183
|
+
.where(
|
|
184
|
+
and(
|
|
185
|
+
eq(schema.organizationModelProviderConnections.accountId, input.organizationId),
|
|
186
|
+
eq(schema.organizationModelProviderConnections.providerKind, input.providerKind),
|
|
187
|
+
),
|
|
188
|
+
)
|
|
189
|
+
.for("update")
|
|
190
|
+
.limit(1);
|
|
191
|
+
const expected = input.expectedVersion ?? 0;
|
|
192
|
+
if ((existing?.version ?? 0) !== expected) throw new OrganizationModelProviderConflictError();
|
|
193
|
+
const [row] = existing
|
|
194
|
+
? await scopedDb
|
|
195
|
+
.update(schema.organizationModelProviderConnections)
|
|
196
|
+
.set({
|
|
197
|
+
status: "active",
|
|
198
|
+
credentialEncrypted: input.credentialEncrypted,
|
|
199
|
+
version: existing.version + 1,
|
|
200
|
+
operationId: input.operationId,
|
|
201
|
+
requestHash,
|
|
202
|
+
updatedBySubjectId: input.actorSubjectId,
|
|
203
|
+
updatedAt: new Date(),
|
|
204
|
+
})
|
|
205
|
+
.where(eq(schema.organizationModelProviderConnections.id, existing.id))
|
|
206
|
+
.returning()
|
|
207
|
+
: await scopedDb
|
|
208
|
+
.insert(schema.organizationModelProviderConnections)
|
|
209
|
+
.values({
|
|
210
|
+
accountId: input.organizationId,
|
|
211
|
+
providerKind: input.providerKind,
|
|
212
|
+
credentialEncrypted: input.credentialEncrypted,
|
|
213
|
+
operationId: input.operationId,
|
|
214
|
+
requestHash,
|
|
215
|
+
updatedBySubjectId: input.actorSubjectId,
|
|
216
|
+
})
|
|
217
|
+
.returning();
|
|
218
|
+
if (!row) throw new Error("organization provider connection write returned no row");
|
|
219
|
+
const connection = mapConnection(row);
|
|
220
|
+
await recordConnectionOperation(scopedDb, {
|
|
221
|
+
accountId: input.organizationId,
|
|
222
|
+
operationId: input.operationId,
|
|
223
|
+
requestHash,
|
|
224
|
+
connection,
|
|
225
|
+
});
|
|
226
|
+
return connection;
|
|
227
|
+
});
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
export async function revokeOrganizationModelProviderConnection(
|
|
231
|
+
db: Database,
|
|
232
|
+
input: {
|
|
233
|
+
organizationId: string;
|
|
234
|
+
actorSubjectId: string;
|
|
235
|
+
providerKind: OrganizationModelProviderKind;
|
|
236
|
+
operationId: string;
|
|
237
|
+
expectedVersion: number;
|
|
238
|
+
},
|
|
239
|
+
): Promise<OrganizationModelProviderConnection> {
|
|
240
|
+
return await withOrganizationProviderAdministrator(db, input, async (scopedDb) => {
|
|
241
|
+
await scopedDb.execute(
|
|
242
|
+
sql`select pg_advisory_xact_lock(hashtextextended(${`organization-model-provider:${input.organizationId}:${input.providerKind}`}, 0))`,
|
|
243
|
+
);
|
|
244
|
+
await scopedDb.execute(
|
|
245
|
+
sql`select pg_advisory_xact_lock(hashtextextended(${`organization-model-provider-models:${input.organizationId}:${input.providerKind}`}, 0))`,
|
|
246
|
+
);
|
|
247
|
+
const requestHash = digest({ operation: "revoke", expectedVersion: input.expectedVersion });
|
|
248
|
+
const [replay] = await scopedDb
|
|
249
|
+
.select()
|
|
250
|
+
.from(schema.organizationModelProviderConnectionOperations)
|
|
251
|
+
.where(
|
|
252
|
+
and(
|
|
253
|
+
eq(schema.organizationModelProviderConnectionOperations.accountId, input.organizationId),
|
|
254
|
+
eq(schema.organizationModelProviderConnectionOperations.providerKind, input.providerKind),
|
|
255
|
+
eq(schema.organizationModelProviderConnectionOperations.operationId, input.operationId),
|
|
256
|
+
),
|
|
257
|
+
)
|
|
258
|
+
.limit(1);
|
|
259
|
+
if (replay) {
|
|
260
|
+
if (replay.requestHash !== requestHash) throw new OrganizationModelProviderConflictError();
|
|
261
|
+
return mapConnectionOperation(replay);
|
|
262
|
+
}
|
|
263
|
+
const [existing] = await scopedDb
|
|
264
|
+
.select()
|
|
265
|
+
.from(schema.organizationModelProviderConnections)
|
|
266
|
+
.where(
|
|
267
|
+
and(
|
|
268
|
+
eq(schema.organizationModelProviderConnections.accountId, input.organizationId),
|
|
269
|
+
eq(schema.organizationModelProviderConnections.providerKind, input.providerKind),
|
|
270
|
+
),
|
|
271
|
+
)
|
|
272
|
+
.for("update")
|
|
273
|
+
.limit(1);
|
|
274
|
+
if (!existing) throw new OrganizationModelProviderConflictError();
|
|
275
|
+
if (existing.version !== input.expectedVersion)
|
|
276
|
+
throw new OrganizationModelProviderConflictError();
|
|
277
|
+
const [row] = await scopedDb
|
|
278
|
+
.update(schema.organizationModelProviderConnections)
|
|
279
|
+
.set({
|
|
280
|
+
status: "revoked",
|
|
281
|
+
credentialEncrypted: "",
|
|
282
|
+
version: existing.version + 1,
|
|
283
|
+
operationId: input.operationId,
|
|
284
|
+
requestHash,
|
|
285
|
+
updatedBySubjectId: input.actorSubjectId,
|
|
286
|
+
updatedAt: new Date(),
|
|
287
|
+
})
|
|
288
|
+
.where(eq(schema.organizationModelProviderConnections.id, existing.id))
|
|
289
|
+
.returning();
|
|
290
|
+
if (!row) throw new Error("organization provider revoke returned no row");
|
|
291
|
+
const connection = mapConnection(row);
|
|
292
|
+
await recordConnectionOperation(scopedDb, {
|
|
293
|
+
accountId: input.organizationId,
|
|
294
|
+
operationId: input.operationId,
|
|
295
|
+
requestHash,
|
|
296
|
+
connection,
|
|
297
|
+
});
|
|
298
|
+
return connection;
|
|
299
|
+
});
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
export async function organizationModelProviderConnectionActiveForWorkspace(
|
|
303
|
+
db: Database,
|
|
304
|
+
input: { accountId: string; workspaceId: string; providerKind: OrganizationModelProviderKind },
|
|
305
|
+
): Promise<boolean> {
|
|
306
|
+
return await withRlsContext(db, input, async (scopedDb) => {
|
|
307
|
+
const [row] = await scopedDb
|
|
308
|
+
.select({ id: schema.organizationModelProviderConnections.id })
|
|
309
|
+
.from(schema.organizationModelProviderConnections)
|
|
310
|
+
.where(
|
|
311
|
+
and(
|
|
312
|
+
eq(schema.organizationModelProviderConnections.accountId, input.accountId),
|
|
313
|
+
eq(schema.organizationModelProviderConnections.providerKind, input.providerKind),
|
|
314
|
+
eq(schema.organizationModelProviderConnections.status, "active"),
|
|
315
|
+
),
|
|
316
|
+
)
|
|
317
|
+
.limit(1);
|
|
318
|
+
return Boolean(row);
|
|
319
|
+
});
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
export async function loadOrganizationModelProviderApiKey(
|
|
323
|
+
db: Database,
|
|
324
|
+
settings: Settings,
|
|
325
|
+
input: { accountId: string; workspaceId: string; providerKind: OrganizationModelProviderKind },
|
|
326
|
+
): Promise<string | null> {
|
|
327
|
+
const key = environmentsEncryptionKeyBytes(settings);
|
|
328
|
+
if (!key) return null;
|
|
329
|
+
return await withRlsContext(db, input, async (scopedDb) => {
|
|
330
|
+
const [row] = await scopedDb
|
|
331
|
+
.select({
|
|
332
|
+
credentialEncrypted: schema.organizationModelProviderConnections.credentialEncrypted,
|
|
333
|
+
})
|
|
334
|
+
.from(schema.organizationModelProviderConnections)
|
|
335
|
+
.where(
|
|
336
|
+
and(
|
|
337
|
+
eq(schema.organizationModelProviderConnections.accountId, input.accountId),
|
|
338
|
+
eq(schema.organizationModelProviderConnections.providerKind, input.providerKind),
|
|
339
|
+
eq(schema.organizationModelProviderConnections.status, "active"),
|
|
340
|
+
),
|
|
341
|
+
)
|
|
342
|
+
.limit(1);
|
|
343
|
+
return row ? decryptEnvironmentValue(key, row.credentialEncrypted) : null;
|
|
344
|
+
});
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
export async function listOrganizationModelProviderCustomModelsForWorkspace(
|
|
348
|
+
db: Database,
|
|
349
|
+
input: {
|
|
350
|
+
accountId: string;
|
|
351
|
+
workspaceId: string;
|
|
352
|
+
providerKind: OrganizationModelProviderKind;
|
|
353
|
+
includeRetired?: boolean;
|
|
354
|
+
},
|
|
355
|
+
): Promise<OrganizationModelProviderCustomModel[]> {
|
|
356
|
+
return await withRlsContext(db, input, async (scopedDb) => {
|
|
357
|
+
const rows = await scopedDb
|
|
358
|
+
.select()
|
|
359
|
+
.from(schema.organizationModelProviderCustomModels)
|
|
360
|
+
.where(
|
|
361
|
+
and(
|
|
362
|
+
eq(schema.organizationModelProviderCustomModels.accountId, input.accountId),
|
|
363
|
+
eq(schema.organizationModelProviderCustomModels.providerKind, input.providerKind),
|
|
364
|
+
...(input.includeRetired
|
|
365
|
+
? []
|
|
366
|
+
: [isNull(schema.organizationModelProviderCustomModels.retiredAt)]),
|
|
367
|
+
),
|
|
368
|
+
);
|
|
369
|
+
return rows.map(mapModel);
|
|
370
|
+
});
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
export async function getOrganizationModelProviderCustomModelForExecution(
|
|
374
|
+
db: Database,
|
|
375
|
+
input: {
|
|
376
|
+
accountId: string;
|
|
377
|
+
workspaceId: string;
|
|
378
|
+
providerKind: OrganizationModelProviderKind;
|
|
379
|
+
upstreamModelId: string;
|
|
380
|
+
},
|
|
381
|
+
): Promise<OrganizationModelProviderCustomModel | null> {
|
|
382
|
+
return await withRlsContext(db, input, async (scopedDb) => {
|
|
383
|
+
const [row] = await scopedDb
|
|
384
|
+
.select()
|
|
385
|
+
.from(schema.organizationModelProviderCustomModels)
|
|
386
|
+
.where(
|
|
387
|
+
and(
|
|
388
|
+
eq(schema.organizationModelProviderCustomModels.accountId, input.accountId),
|
|
389
|
+
eq(schema.organizationModelProviderCustomModels.providerKind, input.providerKind),
|
|
390
|
+
eq(schema.organizationModelProviderCustomModels.upstreamModelId, input.upstreamModelId),
|
|
391
|
+
),
|
|
392
|
+
)
|
|
393
|
+
.orderBy(
|
|
394
|
+
sql`(${schema.organizationModelProviderCustomModels.retiredAt} is null) desc`,
|
|
395
|
+
sql`${schema.organizationModelProviderCustomModels.updatedAt} desc`,
|
|
396
|
+
)
|
|
397
|
+
.limit(1);
|
|
398
|
+
return row ? mapModel(row) : null;
|
|
399
|
+
});
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
export async function withOrganizationModelProviderCustomModelReadLock<T>(
|
|
403
|
+
db: Database,
|
|
404
|
+
input: { accountId: string; workspaceId: string; providerKind: OrganizationModelProviderKind },
|
|
405
|
+
use: (scopedDb: Database) => Promise<T>,
|
|
406
|
+
): Promise<T> {
|
|
407
|
+
return await withRlsContext(db, input, async (scopedDb) => {
|
|
408
|
+
await scopedDb.execute(
|
|
409
|
+
sql`select pg_advisory_xact_lock_shared(hashtextextended(${`organization-model-provider-models:${input.accountId}:${input.providerKind}`}, 0))`,
|
|
410
|
+
);
|
|
411
|
+
return await use(scopedDb);
|
|
412
|
+
});
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
export async function lockActiveOrganizationModelProviderCustomModelForAdmission(
|
|
416
|
+
db: Database,
|
|
417
|
+
input: {
|
|
418
|
+
accountId: string;
|
|
419
|
+
workspaceId: string;
|
|
420
|
+
providerKind: OrganizationModelProviderKind;
|
|
421
|
+
upstreamModelId: string;
|
|
422
|
+
},
|
|
423
|
+
): Promise<OrganizationModelProviderCustomModel | null> {
|
|
424
|
+
return await withOrganizationModelProviderCustomModelReadLock(db, input, async (scopedDb) => {
|
|
425
|
+
const [row] = await scopedDb
|
|
426
|
+
.select()
|
|
427
|
+
.from(schema.organizationModelProviderCustomModels)
|
|
428
|
+
.where(
|
|
429
|
+
and(
|
|
430
|
+
eq(schema.organizationModelProviderCustomModels.accountId, input.accountId),
|
|
431
|
+
eq(schema.organizationModelProviderCustomModels.providerKind, input.providerKind),
|
|
432
|
+
eq(schema.organizationModelProviderCustomModels.upstreamModelId, input.upstreamModelId),
|
|
433
|
+
isNull(schema.organizationModelProviderCustomModels.retiredAt),
|
|
434
|
+
exists(
|
|
435
|
+
scopedDb
|
|
436
|
+
.select({ id: schema.organizationModelProviderConnections.id })
|
|
437
|
+
.from(schema.organizationModelProviderConnections)
|
|
438
|
+
.where(
|
|
439
|
+
and(
|
|
440
|
+
eq(schema.organizationModelProviderConnections.accountId, input.accountId),
|
|
441
|
+
eq(schema.organizationModelProviderConnections.providerKind, input.providerKind),
|
|
442
|
+
eq(schema.organizationModelProviderConnections.status, "active"),
|
|
443
|
+
),
|
|
444
|
+
),
|
|
445
|
+
),
|
|
446
|
+
),
|
|
447
|
+
)
|
|
448
|
+
.limit(1);
|
|
449
|
+
return row ? mapModel(row) : null;
|
|
450
|
+
});
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
export async function listOrganizationModelProviderCustomModels(
|
|
454
|
+
db: Database,
|
|
455
|
+
input: {
|
|
456
|
+
organizationId: string;
|
|
457
|
+
actorSubjectId: string;
|
|
458
|
+
providerKind: OrganizationModelProviderKind;
|
|
459
|
+
},
|
|
460
|
+
): Promise<OrganizationModelProviderCustomModel[]> {
|
|
461
|
+
return await withOrganizationProviderAdministrator(db, input, async (scopedDb) => {
|
|
462
|
+
const rows = await scopedDb
|
|
463
|
+
.select()
|
|
464
|
+
.from(schema.organizationModelProviderCustomModels)
|
|
465
|
+
.where(
|
|
466
|
+
and(
|
|
467
|
+
eq(schema.organizationModelProviderCustomModels.accountId, input.organizationId),
|
|
468
|
+
eq(schema.organizationModelProviderCustomModels.providerKind, input.providerKind),
|
|
469
|
+
isNull(schema.organizationModelProviderCustomModels.retiredAt),
|
|
470
|
+
),
|
|
471
|
+
);
|
|
472
|
+
return rows.map(mapModel);
|
|
473
|
+
});
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
export async function createOrganizationModelProviderCustomModel(
|
|
477
|
+
db: Database,
|
|
478
|
+
input: {
|
|
479
|
+
organizationId: string;
|
|
480
|
+
actorSubjectId: string;
|
|
481
|
+
providerKind: OrganizationModelProviderKind;
|
|
482
|
+
upstreamModelId: string;
|
|
483
|
+
label?: string;
|
|
484
|
+
operationId: string;
|
|
485
|
+
},
|
|
486
|
+
): Promise<OrganizationModelProviderCustomModel> {
|
|
487
|
+
return await withOrganizationProviderAdministrator(db, input, async (scopedDb) => {
|
|
488
|
+
await scopedDb.execute(
|
|
489
|
+
sql`select pg_advisory_xact_lock(hashtextextended(${`organization-model-provider-models:${input.organizationId}:${input.providerKind}`}, 0))`,
|
|
490
|
+
);
|
|
491
|
+
const requestHash = digest({
|
|
492
|
+
upstreamModelId: input.upstreamModelId,
|
|
493
|
+
label: input.label ?? null,
|
|
494
|
+
});
|
|
495
|
+
const [replay] = await scopedDb
|
|
496
|
+
.select()
|
|
497
|
+
.from(schema.organizationModelProviderCustomModels)
|
|
498
|
+
.where(
|
|
499
|
+
and(
|
|
500
|
+
eq(schema.organizationModelProviderCustomModels.accountId, input.organizationId),
|
|
501
|
+
eq(schema.organizationModelProviderCustomModels.providerKind, input.providerKind),
|
|
502
|
+
eq(schema.organizationModelProviderCustomModels.createOperationId, input.operationId),
|
|
503
|
+
),
|
|
504
|
+
)
|
|
505
|
+
.limit(1);
|
|
506
|
+
if (replay) {
|
|
507
|
+
if (replay.createRequestHash !== requestHash)
|
|
508
|
+
throw new OrganizationModelProviderConflictError();
|
|
509
|
+
return mapModel(replay);
|
|
510
|
+
}
|
|
511
|
+
const [{ active = 0, total = 0 } = {}] = await scopedDb
|
|
512
|
+
.select({
|
|
513
|
+
active: count(
|
|
514
|
+
sql`case when ${schema.organizationModelProviderCustomModels.retiredAt} is null then 1 end`,
|
|
515
|
+
),
|
|
516
|
+
total: count(),
|
|
517
|
+
})
|
|
518
|
+
.from(schema.organizationModelProviderCustomModels)
|
|
519
|
+
.where(
|
|
520
|
+
and(
|
|
521
|
+
eq(schema.organizationModelProviderCustomModels.accountId, input.organizationId),
|
|
522
|
+
eq(schema.organizationModelProviderCustomModels.providerKind, input.providerKind),
|
|
523
|
+
),
|
|
524
|
+
);
|
|
525
|
+
if (Number(active) >= 100 || Number(total) >= 1000)
|
|
526
|
+
throw new OrganizationModelProviderLimitError();
|
|
527
|
+
const [duplicate] = await scopedDb
|
|
528
|
+
.select({ id: schema.organizationModelProviderCustomModels.id })
|
|
529
|
+
.from(schema.organizationModelProviderCustomModels)
|
|
530
|
+
.where(
|
|
531
|
+
and(
|
|
532
|
+
eq(schema.organizationModelProviderCustomModels.accountId, input.organizationId),
|
|
533
|
+
eq(schema.organizationModelProviderCustomModels.providerKind, input.providerKind),
|
|
534
|
+
eq(schema.organizationModelProviderCustomModels.upstreamModelId, input.upstreamModelId),
|
|
535
|
+
isNull(schema.organizationModelProviderCustomModels.retiredAt),
|
|
536
|
+
),
|
|
537
|
+
)
|
|
538
|
+
.limit(1);
|
|
539
|
+
if (duplicate) throw new OrganizationModelProviderConflictError();
|
|
540
|
+
const [row] = await scopedDb
|
|
541
|
+
.insert(schema.organizationModelProviderCustomModels)
|
|
542
|
+
.values({
|
|
543
|
+
accountId: input.organizationId,
|
|
544
|
+
providerKind: input.providerKind,
|
|
545
|
+
upstreamModelId: input.upstreamModelId,
|
|
546
|
+
label: input.label ?? null,
|
|
547
|
+
createOperationId: input.operationId,
|
|
548
|
+
createRequestHash: requestHash,
|
|
549
|
+
createdBySubjectId: input.actorSubjectId,
|
|
550
|
+
})
|
|
551
|
+
.returning();
|
|
552
|
+
if (!row) throw new Error("organization custom model create returned no row");
|
|
553
|
+
return mapModel(row);
|
|
554
|
+
});
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
export async function retireOrganizationModelProviderCustomModel(
|
|
558
|
+
db: Database,
|
|
559
|
+
input: {
|
|
560
|
+
organizationId: string;
|
|
561
|
+
actorSubjectId: string;
|
|
562
|
+
providerKind: OrganizationModelProviderKind;
|
|
563
|
+
customModelId: string;
|
|
564
|
+
expectedVersion: number;
|
|
565
|
+
operationId: string;
|
|
566
|
+
},
|
|
567
|
+
): Promise<OrganizationModelProviderCustomModel> {
|
|
568
|
+
return await withOrganizationProviderAdministrator(db, input, async (scopedDb) => {
|
|
569
|
+
await scopedDb.execute(
|
|
570
|
+
sql`select pg_advisory_xact_lock(hashtextextended(${`organization-model-provider-models:${input.organizationId}:${input.providerKind}`}, 0))`,
|
|
571
|
+
);
|
|
572
|
+
const requestHash = digest({
|
|
573
|
+
customModelId: input.customModelId,
|
|
574
|
+
expectedVersion: input.expectedVersion,
|
|
575
|
+
});
|
|
576
|
+
const [replay] = await scopedDb
|
|
577
|
+
.select()
|
|
578
|
+
.from(schema.organizationModelProviderCustomModels)
|
|
579
|
+
.where(
|
|
580
|
+
and(
|
|
581
|
+
eq(schema.organizationModelProviderCustomModels.accountId, input.organizationId),
|
|
582
|
+
eq(schema.organizationModelProviderCustomModels.providerKind, input.providerKind),
|
|
583
|
+
eq(schema.organizationModelProviderCustomModels.deleteOperationId, input.operationId),
|
|
584
|
+
),
|
|
585
|
+
)
|
|
586
|
+
.limit(1);
|
|
587
|
+
if (replay) {
|
|
588
|
+
if (replay.deleteRequestHash !== requestHash)
|
|
589
|
+
throw new OrganizationModelProviderConflictError();
|
|
590
|
+
return mapModel(replay);
|
|
591
|
+
}
|
|
592
|
+
const [row] = await scopedDb
|
|
593
|
+
.update(schema.organizationModelProviderCustomModels)
|
|
594
|
+
.set({
|
|
595
|
+
retiredAt: new Date(),
|
|
596
|
+
deleteOperationId: input.operationId,
|
|
597
|
+
deleteRequestHash: requestHash,
|
|
598
|
+
version: input.expectedVersion + 1,
|
|
599
|
+
updatedAt: new Date(),
|
|
600
|
+
})
|
|
601
|
+
.where(
|
|
602
|
+
and(
|
|
603
|
+
eq(schema.organizationModelProviderCustomModels.accountId, input.organizationId),
|
|
604
|
+
eq(schema.organizationModelProviderCustomModels.providerKind, input.providerKind),
|
|
605
|
+
eq(schema.organizationModelProviderCustomModels.id, input.customModelId),
|
|
606
|
+
eq(schema.organizationModelProviderCustomModels.version, input.expectedVersion),
|
|
607
|
+
isNull(schema.organizationModelProviderCustomModels.retiredAt),
|
|
608
|
+
),
|
|
609
|
+
)
|
|
610
|
+
.returning();
|
|
611
|
+
if (!row) throw new OrganizationModelProviderConflictError();
|
|
612
|
+
return mapModel(row);
|
|
613
|
+
});
|
|
614
|
+
}
|
package/src/pack-components.ts
CHANGED
|
@@ -29,6 +29,7 @@ export type PackInlineSkillRequirement = {
|
|
|
29
29
|
key: string;
|
|
30
30
|
capabilityId: string;
|
|
31
31
|
name: string;
|
|
32
|
+
activationMode: "workspace_managed" | "session_selected";
|
|
32
33
|
contentSha256: string;
|
|
33
34
|
};
|
|
34
35
|
|
|
@@ -97,9 +98,14 @@ export async function resolvePackInlineSkillReferences(
|
|
|
97
98
|
.select({
|
|
98
99
|
facetInstallationId: schema.capabilityFacetInstallations.id,
|
|
99
100
|
name: schema.capabilitySkillFacets.name,
|
|
101
|
+
activationMode: schema.capabilityFacets.activationMode,
|
|
100
102
|
contentSha256: schema.capabilitySkillFacets.contentSha256,
|
|
101
103
|
})
|
|
102
104
|
.from(schema.capabilitySkillFacets)
|
|
105
|
+
.innerJoin(
|
|
106
|
+
schema.capabilityFacets,
|
|
107
|
+
eq(schema.capabilityFacets.id, schema.capabilitySkillFacets.facetId),
|
|
108
|
+
)
|
|
103
109
|
.innerJoin(
|
|
104
110
|
schema.capabilityFacetInstallations,
|
|
105
111
|
eq(schema.capabilityFacetInstallations.facetId, schema.capabilitySkillFacets.facetId),
|
|
@@ -141,9 +147,16 @@ export async function resolvePackInlineSkillReferences(
|
|
|
141
147
|
(row) => row.name.toLowerCase() === requirement.name.toLowerCase(),
|
|
142
148
|
);
|
|
143
149
|
const exact = candidates.find(
|
|
144
|
-
(candidate) =>
|
|
150
|
+
(candidate) =>
|
|
151
|
+
candidate.contentSha256 === requirement.contentSha256 &&
|
|
152
|
+
candidate.activationMode === requirement.activationMode,
|
|
145
153
|
);
|
|
146
|
-
const mismatch =
|
|
154
|
+
const mismatch =
|
|
155
|
+
candidates.find(
|
|
156
|
+
(candidate) =>
|
|
157
|
+
candidate.contentSha256 !== requirement.contentSha256 ||
|
|
158
|
+
candidate.activationMode !== requirement.activationMode,
|
|
159
|
+
) ?? null;
|
|
147
160
|
return {
|
|
148
161
|
key: requirement.key,
|
|
149
162
|
kind: "inline_skill" as const,
|
package/src/provision-roles.ts
CHANGED
|
@@ -536,9 +536,11 @@ async function grantAppRoleIfSchemaExists(
|
|
|
536
536
|
"resolve_workspace_codex_subscription_source(uuid,uuid)",
|
|
537
537
|
"list_organization_workspace_ids(uuid)",
|
|
538
538
|
"organization_workspace_command(jsonb)",
|
|
539
|
+
"authorize_organization_shared_workspace_administration(uuid,uuid,text)",
|
|
539
540
|
"resolve_organization_workspace_removal_subject(uuid,text,uuid)",
|
|
540
541
|
"prepare_organization_workspace_member_removal(jsonb)",
|
|
541
542
|
"record_organization_workspace_member_removal(jsonb,uuid,uuid)",
|
|
543
|
+
"create_additional_managed_organization(text,text,text,text,uuid)",
|
|
542
544
|
"create_managed_organization(text,text,text,uuid)",
|
|
543
545
|
"assert_organization_shared_workspace_administrator(uuid,uuid,text)",
|
|
544
546
|
"open_organization_shared_workspace_administration_capability(uuid,uuid,text)",
|