@opengeni/db 0.13.1 → 0.13.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-N4WTE6SB.js → chunk-IQOXTFKA.js} +2 -1
- package/dist/chunk-IQOXTFKA.js.map +1 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.js +396 -172
- package/dist/index.js.map +1 -1
- package/dist/provision-roles.d.ts +43 -6
- package/dist/{schema-D-hyrsFt.d.ts → schema-SbaSoRdr.d.ts} +19 -0
- package/dist/schema.d.ts +1 -1
- package/dist/schema.js +1 -1
- package/drizzle/0132_connection_subject_isolation.sql +100 -0
- package/drizzle/0133_session_skills.sql +3 -0
- package/package.json +3 -3
- package/src/connection-token-resolver.ts +17 -7
- package/src/index.ts +552 -201
- package/src/schema.ts +1 -0
- package/dist/chunk-N4WTE6SB.js.map +0 -1
package/dist/index.js
CHANGED
|
@@ -76,7 +76,7 @@ import {
|
|
|
76
76
|
workspaceVariableSetVariables,
|
|
77
77
|
workspaceVariableSets,
|
|
78
78
|
workspaces
|
|
79
|
-
} from "./chunk-
|
|
79
|
+
} from "./chunk-IQOXTFKA.js";
|
|
80
80
|
import {
|
|
81
81
|
migrate,
|
|
82
82
|
runMigrations
|
|
@@ -5194,11 +5194,15 @@ var REFRESH_WINDOW_MS = 6e4;
|
|
|
5194
5194
|
var CONNECTION_REFRESH_TIMEOUT_MS = 1e4;
|
|
5195
5195
|
function buildConnectionTokenResolver(db, settings, deps = defaultDeps2) {
|
|
5196
5196
|
const load = async (input) => {
|
|
5197
|
+
const subjectOwned = input.connectionRef.subjectScope === "subject";
|
|
5198
|
+
if (subjectOwned && !input.subjectId) {
|
|
5199
|
+
return null;
|
|
5200
|
+
}
|
|
5197
5201
|
const request = {
|
|
5198
5202
|
workspaceId: input.workspaceId,
|
|
5199
5203
|
providerDomain: input.connectionRef.providerDomain,
|
|
5200
|
-
|
|
5201
|
-
|
|
5204
|
+
allowSubjectOwned: subjectOwned,
|
|
5205
|
+
...subjectOwned ? { subjectId: input.subjectId } : {}
|
|
5202
5206
|
};
|
|
5203
5207
|
if (input.connectionRef.connectionId !== void 0) {
|
|
5204
5208
|
request.connectionId = input.connectionRef.connectionId;
|
|
@@ -5206,10 +5210,12 @@ function buildConnectionTokenResolver(db, settings, deps = defaultDeps2) {
|
|
|
5206
5210
|
if (input.connectionRef.kind !== void 0) {
|
|
5207
5211
|
request.kind = input.connectionRef.kind;
|
|
5208
5212
|
}
|
|
5209
|
-
|
|
5210
|
-
|
|
5213
|
+
const credential = await deps.loadCredential(db, settings, request);
|
|
5214
|
+
if (!credential) return null;
|
|
5215
|
+
if (subjectOwned) {
|
|
5216
|
+
return credential.subjectId === input.subjectId ? credential : null;
|
|
5211
5217
|
}
|
|
5212
|
-
return
|
|
5218
|
+
return credential.subjectId === null ? credential : null;
|
|
5213
5219
|
};
|
|
5214
5220
|
const snapshot = async (cred, ref, destinationUrl) => {
|
|
5215
5221
|
if (cred.status !== "active") {
|
|
@@ -5244,7 +5250,7 @@ function buildConnectionTokenResolver(db, settings, deps = defaultDeps2) {
|
|
|
5244
5250
|
...ref.selectedResources ? { selectedResources: copySelectedResources(ref.selectedResources) } : {}
|
|
5245
5251
|
};
|
|
5246
5252
|
}
|
|
5247
|
-
await deps.recordUsed(db, cred.workspaceId, cred.id);
|
|
5253
|
+
await deps.recordUsed(db, cred.workspaceId, cred.id, cred.subjectId);
|
|
5248
5254
|
return {
|
|
5249
5255
|
status: "ok",
|
|
5250
5256
|
headers,
|
|
@@ -5264,7 +5270,8 @@ function buildConnectionTokenResolver(db, settings, deps = defaultDeps2) {
|
|
|
5264
5270
|
workspaceId: cred.workspaceId,
|
|
5265
5271
|
credentialEncrypted: deps.encrypt(key, JSON.stringify(refreshed.credential)),
|
|
5266
5272
|
expiresAt: refreshed.expiresAt,
|
|
5267
|
-
lastRefreshAt: deps.now()
|
|
5273
|
+
lastRefreshAt: deps.now(),
|
|
5274
|
+
subjectId: cred.subjectId
|
|
5268
5275
|
};
|
|
5269
5276
|
if (refreshed.grantedScopes !== void 0) {
|
|
5270
5277
|
refreshRecord.grantedScopes = refreshed.grantedScopes;
|
|
@@ -5273,7 +5280,8 @@ function buildConnectionTokenResolver(db, settings, deps = defaultDeps2) {
|
|
|
5273
5280
|
if (persisted) {
|
|
5274
5281
|
const current = await load({
|
|
5275
5282
|
workspaceId: cred.workspaceId,
|
|
5276
|
-
connectionRef: { ...ref, connectionId: cred.id }
|
|
5283
|
+
connectionRef: { ...ref, connectionId: cred.id },
|
|
5284
|
+
...cred.subjectId ? { subjectId: cred.subjectId } : {}
|
|
5277
5285
|
});
|
|
5278
5286
|
if (current) {
|
|
5279
5287
|
return current;
|
|
@@ -5281,7 +5289,8 @@ function buildConnectionTokenResolver(db, settings, deps = defaultDeps2) {
|
|
|
5281
5289
|
}
|
|
5282
5290
|
const winner = await load({
|
|
5283
5291
|
workspaceId: cred.workspaceId,
|
|
5284
|
-
connectionRef: { ...ref, connectionId: cred.id }
|
|
5292
|
+
connectionRef: { ...ref, connectionId: cred.id },
|
|
5293
|
+
...cred.subjectId ? { subjectId: cred.subjectId } : {}
|
|
5285
5294
|
});
|
|
5286
5295
|
if (winner?.status === "active") {
|
|
5287
5296
|
return winner;
|
|
@@ -5289,7 +5298,7 @@ function buildConnectionTokenResolver(db, settings, deps = defaultDeps2) {
|
|
|
5289
5298
|
throw new Error("connection credential changed during token refresh");
|
|
5290
5299
|
};
|
|
5291
5300
|
const refreshSingleFlight = (cred, ref) => {
|
|
5292
|
-
const key = `${cred.id}:${cred.version}`;
|
|
5301
|
+
const key = `${cred.subjectId ?? "workspace"}:${cred.id}:${cred.version}`;
|
|
5293
5302
|
const existing = inflight2.get(key);
|
|
5294
5303
|
if (existing) {
|
|
5295
5304
|
return existing;
|
|
@@ -5334,7 +5343,8 @@ function buildConnectionTokenResolver(db, settings, deps = defaultDeps2) {
|
|
|
5334
5343
|
error instanceof Error ? error.message : String(error),
|
|
5335
5344
|
{
|
|
5336
5345
|
id: cred.id,
|
|
5337
|
-
version: cred.version
|
|
5346
|
+
version: cred.version,
|
|
5347
|
+
subjectId: cred.subjectId
|
|
5338
5348
|
}
|
|
5339
5349
|
).catch(() => void 0);
|
|
5340
5350
|
}
|
|
@@ -6151,6 +6161,17 @@ async function bootstrapWorkspace(db, input) {
|
|
|
6151
6161
|
updatedAt: /* @__PURE__ */ new Date()
|
|
6152
6162
|
}).where(eq7(workspaceMemberships.id, membership.id));
|
|
6153
6163
|
}
|
|
6164
|
+
await setRlsContext(tx, {
|
|
6165
|
+
accountId: workspace.accountId,
|
|
6166
|
+
workspaceId: null
|
|
6167
|
+
});
|
|
6168
|
+
const memberships = await tx.select({
|
|
6169
|
+
membership: workspaceMemberships,
|
|
6170
|
+
workspace: workspaces
|
|
6171
|
+
}).from(workspaceMemberships).innerJoin(
|
|
6172
|
+
workspaces,
|
|
6173
|
+
eq7(workspaceMemberships.workspaceId, workspaces.id)
|
|
6174
|
+
).where(eq7(workspaceMemberships.subjectId, input.subjectId)).orderBy(desc2(workspaces.createdAt));
|
|
6154
6175
|
return {
|
|
6155
6176
|
mode: input.accountExternalSource === "opengeni:local" ? "local" : "configured",
|
|
6156
6177
|
subjectId: input.subjectId,
|
|
@@ -6164,15 +6185,13 @@ async function bootstrapWorkspace(db, input) {
|
|
|
6164
6185
|
permissions: input.accountPermissions ?? allAccountPermissions
|
|
6165
6186
|
}
|
|
6166
6187
|
],
|
|
6167
|
-
workspaceGrants:
|
|
6168
|
-
|
|
6169
|
-
|
|
6170
|
-
|
|
6171
|
-
|
|
6172
|
-
|
|
6173
|
-
|
|
6174
|
-
}
|
|
6175
|
-
],
|
|
6188
|
+
workspaceGrants: memberships.map((row) => ({
|
|
6189
|
+
workspaceId: row.workspace.id,
|
|
6190
|
+
accountId: row.workspace.accountId,
|
|
6191
|
+
subjectId: input.subjectId,
|
|
6192
|
+
...input.subjectLabel ? { subjectLabel: input.subjectLabel } : {},
|
|
6193
|
+
permissions: row.membership.permissions
|
|
6194
|
+
})),
|
|
6176
6195
|
defaultAccountId: account.id,
|
|
6177
6196
|
defaultWorkspaceId: workspace.id
|
|
6178
6197
|
};
|
|
@@ -8032,36 +8051,48 @@ var connectionMetadataColumns = {
|
|
|
8032
8051
|
function connectionSubjectVisibility(subjectId) {
|
|
8033
8052
|
return subjectId ? or(isNull2(connections.subjectId), eq7(connections.subjectId, subjectId)) : isNull2(connections.subjectId);
|
|
8034
8053
|
}
|
|
8054
|
+
function connectionExactSubject(subjectId) {
|
|
8055
|
+
return subjectId ? eq7(connections.subjectId, subjectId) : isNull2(connections.subjectId);
|
|
8056
|
+
}
|
|
8057
|
+
async function withConnectionSubjectRls(db, workspaceId, subjectId, fn) {
|
|
8058
|
+
return subjectId ? await withWorkspaceSubjectRls(db, workspaceId, subjectId, fn) : await withWorkspaceRls(db, workspaceId, fn);
|
|
8059
|
+
}
|
|
8060
|
+
async function createConnectionInScope(db, input) {
|
|
8061
|
+
const [row] = await db.insert(connections).values({
|
|
8062
|
+
accountId: input.accountId,
|
|
8063
|
+
workspaceId: input.workspaceId,
|
|
8064
|
+
subjectId: input.subjectId ?? null,
|
|
8065
|
+
providerDomain: input.providerDomain,
|
|
8066
|
+
kind: input.kind,
|
|
8067
|
+
status: input.status ?? "active",
|
|
8068
|
+
credentialEncrypted: input.credentialEncrypted,
|
|
8069
|
+
grantedScopes: input.grantedScopes ?? [],
|
|
8070
|
+
expiresAt: input.expiresAt ?? null,
|
|
8071
|
+
verifiedInstallAt: input.verifiedInstallAt ?? null,
|
|
8072
|
+
verifiedInstallVersion: input.verifiedInstallVersion ?? null,
|
|
8073
|
+
metadata: input.metadata ?? {},
|
|
8074
|
+
createdBySubjectId: input.createdBySubjectId ?? null,
|
|
8075
|
+
updatedBySubjectId: input.updatedBySubjectId ?? input.createdBySubjectId ?? null
|
|
8076
|
+
}).returning(connectionMetadataColumns);
|
|
8077
|
+
if (!row) {
|
|
8078
|
+
throw new Error("Failed to create connection");
|
|
8079
|
+
}
|
|
8080
|
+
return mapConnectionMetadata(row);
|
|
8081
|
+
}
|
|
8035
8082
|
async function createConnection(db, input) {
|
|
8036
8083
|
return await withRlsContext(
|
|
8037
8084
|
db,
|
|
8038
8085
|
{ accountId: input.accountId, workspaceId: input.workspaceId },
|
|
8039
8086
|
async (scopedDb) => {
|
|
8040
|
-
|
|
8041
|
-
|
|
8042
|
-
workspaceId: input.workspaceId,
|
|
8043
|
-
subjectId: input.subjectId ?? null,
|
|
8044
|
-
providerDomain: input.providerDomain,
|
|
8045
|
-
kind: input.kind,
|
|
8046
|
-
status: input.status ?? "active",
|
|
8047
|
-
credentialEncrypted: input.credentialEncrypted,
|
|
8048
|
-
grantedScopes: input.grantedScopes ?? [],
|
|
8049
|
-
expiresAt: input.expiresAt ?? null,
|
|
8050
|
-
verifiedInstallAt: input.verifiedInstallAt ?? null,
|
|
8051
|
-
verifiedInstallVersion: input.verifiedInstallVersion ?? null,
|
|
8052
|
-
metadata: input.metadata ?? {},
|
|
8053
|
-
createdBySubjectId: input.createdBySubjectId ?? null,
|
|
8054
|
-
updatedBySubjectId: input.updatedBySubjectId ?? input.createdBySubjectId ?? null
|
|
8055
|
-
}).returning(connectionMetadataColumns);
|
|
8056
|
-
if (!row) {
|
|
8057
|
-
throw new Error("Failed to create connection");
|
|
8087
|
+
if (input.subjectId) {
|
|
8088
|
+
await setSubjectRlsContext(scopedDb, input.subjectId);
|
|
8058
8089
|
}
|
|
8059
|
-
return
|
|
8090
|
+
return await createConnectionInScope(scopedDb, input);
|
|
8060
8091
|
}
|
|
8061
8092
|
);
|
|
8062
8093
|
}
|
|
8063
8094
|
async function listConnectionsMetadata(db, workspaceId, subjectId) {
|
|
8064
|
-
return await
|
|
8095
|
+
return await withConnectionSubjectRls(db, workspaceId, subjectId, async (scopedDb) => {
|
|
8065
8096
|
const rows = await scopedDb.select(connectionMetadataColumns).from(connections).where(
|
|
8066
8097
|
and7(
|
|
8067
8098
|
eq7(connections.workspaceId, workspaceId),
|
|
@@ -8072,7 +8103,7 @@ async function listConnectionsMetadata(db, workspaceId, subjectId) {
|
|
|
8072
8103
|
});
|
|
8073
8104
|
}
|
|
8074
8105
|
async function getConnectionMetadata(db, workspaceId, connectionId, subjectId) {
|
|
8075
|
-
return await
|
|
8106
|
+
return await withConnectionSubjectRls(db, workspaceId, subjectId, async (scopedDb) => {
|
|
8076
8107
|
const [row] = await scopedDb.select(connectionMetadataColumns).from(connections).where(
|
|
8077
8108
|
and7(
|
|
8078
8109
|
eq7(connections.workspaceId, workspaceId),
|
|
@@ -8083,66 +8114,217 @@ async function getConnectionMetadata(db, workspaceId, connectionId, subjectId) {
|
|
|
8083
8114
|
return row ? mapConnectionMetadata(row) : null;
|
|
8084
8115
|
});
|
|
8085
8116
|
}
|
|
8117
|
+
async function updateConnectionInScope(db, input) {
|
|
8118
|
+
const set = {
|
|
8119
|
+
updatedAt: /* @__PURE__ */ new Date(),
|
|
8120
|
+
...input.providerDomain !== void 0 ? { providerDomain: input.providerDomain } : {},
|
|
8121
|
+
...input.subjectId !== void 0 ? { subjectId: input.subjectId } : {},
|
|
8122
|
+
...input.kind !== void 0 ? { kind: input.kind } : {},
|
|
8123
|
+
...input.status !== void 0 ? { status: input.status } : {},
|
|
8124
|
+
...input.credentialEncrypted !== void 0 ? {
|
|
8125
|
+
credentialEncrypted: input.credentialEncrypted,
|
|
8126
|
+
version: sql4`${connections.version} + 1`,
|
|
8127
|
+
lastError: null
|
|
8128
|
+
} : {},
|
|
8129
|
+
...input.grantedScopes !== void 0 ? { grantedScopes: input.grantedScopes } : {},
|
|
8130
|
+
...input.expiresAt !== void 0 ? { expiresAt: input.expiresAt } : {},
|
|
8131
|
+
...input.verifiedInstallAt !== void 0 ? { verifiedInstallAt: input.verifiedInstallAt } : {},
|
|
8132
|
+
...input.verifiedInstallVersion !== void 0 ? { verifiedInstallVersion: input.verifiedInstallVersion } : {},
|
|
8133
|
+
...input.metadata !== void 0 ? { metadata: input.metadata } : {},
|
|
8134
|
+
...input.updatedBySubjectId !== void 0 ? { updatedBySubjectId: input.updatedBySubjectId } : {}
|
|
8135
|
+
};
|
|
8136
|
+
const [row] = await db.update(connections).set(set).where(
|
|
8137
|
+
and7(
|
|
8138
|
+
eq7(connections.workspaceId, input.workspaceId),
|
|
8139
|
+
eq7(connections.id, input.connectionId),
|
|
8140
|
+
connectionSubjectVisibility(input.visibleToSubjectId),
|
|
8141
|
+
...input.expectedVersion !== void 0 ? [eq7(connections.version, input.expectedVersion)] : []
|
|
8142
|
+
)
|
|
8143
|
+
).returning(connectionMetadataColumns);
|
|
8144
|
+
return row ? mapConnectionMetadata(row) : null;
|
|
8145
|
+
}
|
|
8086
8146
|
async function updateConnection(db, input) {
|
|
8087
|
-
return await
|
|
8088
|
-
|
|
8089
|
-
|
|
8090
|
-
|
|
8091
|
-
|
|
8092
|
-
|
|
8093
|
-
|
|
8094
|
-
|
|
8095
|
-
|
|
8096
|
-
|
|
8097
|
-
|
|
8098
|
-
|
|
8099
|
-
|
|
8100
|
-
|
|
8101
|
-
|
|
8102
|
-
|
|
8103
|
-
|
|
8104
|
-
|
|
8105
|
-
|
|
8106
|
-
|
|
8107
|
-
|
|
8108
|
-
|
|
8109
|
-
|
|
8110
|
-
|
|
8111
|
-
|
|
8112
|
-
)
|
|
8113
|
-
|
|
8114
|
-
|
|
8115
|
-
|
|
8147
|
+
return await withConnectionSubjectRls(
|
|
8148
|
+
db,
|
|
8149
|
+
input.workspaceId,
|
|
8150
|
+
input.visibleToSubjectId,
|
|
8151
|
+
async (scopedDb) => await updateConnectionInScope(scopedDb, input)
|
|
8152
|
+
);
|
|
8153
|
+
}
|
|
8154
|
+
async function revokeConnectionInScope(db, workspaceId, connectionId, updatedBySubjectId, expectedVersion) {
|
|
8155
|
+
const [row] = await db.update(connections).set({
|
|
8156
|
+
status: "revoked",
|
|
8157
|
+
// The version bump invalidates any in-flight refresh's (id, version) CAS,
|
|
8158
|
+
// so a racing refresh cannot commit and flip the row back to active.
|
|
8159
|
+
version: sql4`${connections.version} + 1`,
|
|
8160
|
+
// Status-only revocation does not replace the verified credential or bot
|
|
8161
|
+
// identity. Carry the marker to the same new CAS version so the dedicated
|
|
8162
|
+
// reinstall path can still recognize (but not use) the inactive row.
|
|
8163
|
+
verifiedInstallVersion: sql4`case
|
|
8164
|
+
when ${connections.verifiedInstallAt} is null then null
|
|
8165
|
+
else ${connections.version} + 1
|
|
8166
|
+
end`,
|
|
8167
|
+
updatedBySubjectId: updatedBySubjectId ?? null,
|
|
8168
|
+
updatedAt: /* @__PURE__ */ new Date()
|
|
8169
|
+
}).where(
|
|
8170
|
+
and7(
|
|
8171
|
+
eq7(connections.workspaceId, workspaceId),
|
|
8172
|
+
eq7(connections.id, connectionId),
|
|
8173
|
+
// Same visibility rule as get/update: shared rows plus the caller's own
|
|
8174
|
+
// subject rows. Cross-subject revocation (admin janitorial) arrives with
|
|
8175
|
+
// the subject-connections UX in I5, deliberately not before.
|
|
8176
|
+
connectionSubjectVisibility(updatedBySubjectId),
|
|
8177
|
+
...expectedVersion !== void 0 ? [eq7(connections.version, expectedVersion)] : []
|
|
8178
|
+
)
|
|
8179
|
+
).returning(connectionMetadataColumns);
|
|
8180
|
+
return row ? mapConnectionMetadata(row) : null;
|
|
8116
8181
|
}
|
|
8117
8182
|
async function revokeConnection(db, workspaceId, connectionId, updatedBySubjectId) {
|
|
8118
|
-
return await
|
|
8119
|
-
|
|
8120
|
-
|
|
8121
|
-
|
|
8122
|
-
|
|
8123
|
-
|
|
8124
|
-
|
|
8125
|
-
|
|
8126
|
-
|
|
8127
|
-
|
|
8128
|
-
|
|
8129
|
-
|
|
8130
|
-
|
|
8131
|
-
|
|
8132
|
-
|
|
8133
|
-
|
|
8134
|
-
|
|
8135
|
-
|
|
8136
|
-
|
|
8137
|
-
|
|
8138
|
-
|
|
8139
|
-
|
|
8140
|
-
|
|
8141
|
-
|
|
8142
|
-
|
|
8143
|
-
|
|
8183
|
+
return await withConnectionSubjectRls(
|
|
8184
|
+
db,
|
|
8185
|
+
workspaceId,
|
|
8186
|
+
updatedBySubjectId,
|
|
8187
|
+
async (scopedDb) => await revokeConnectionInScope(scopedDb, workspaceId, connectionId, updatedBySubjectId)
|
|
8188
|
+
);
|
|
8189
|
+
}
|
|
8190
|
+
var SlackBotLifecycleSuccessAuditError = class extends Error {
|
|
8191
|
+
constructor() {
|
|
8192
|
+
super("OpenGeni Slack bot lifecycle success audit failed");
|
|
8193
|
+
this.name = "SlackBotLifecycleSuccessAuditError";
|
|
8194
|
+
}
|
|
8195
|
+
};
|
|
8196
|
+
async function insertSlackBotLifecycleSuccessAuditInScope(db, input) {
|
|
8197
|
+
try {
|
|
8198
|
+
await db.insert(auditEvents).values({
|
|
8199
|
+
accountId: input.accountId,
|
|
8200
|
+
workspaceId: input.workspaceId,
|
|
8201
|
+
subjectId: input.subjectId,
|
|
8202
|
+
action: input.action,
|
|
8203
|
+
targetType: "connection",
|
|
8204
|
+
targetId: input.connectionId,
|
|
8205
|
+
metadata: {
|
|
8206
|
+
credentialRole: input.credentialRole,
|
|
8207
|
+
credentialLabel: input.credentialLabel,
|
|
8208
|
+
connectionId: input.connectionId,
|
|
8209
|
+
slackTeamId: input.slackTeamId,
|
|
8210
|
+
outcome: "succeeded"
|
|
8211
|
+
}
|
|
8212
|
+
});
|
|
8213
|
+
} catch {
|
|
8214
|
+
throw new SlackBotLifecycleSuccessAuditError();
|
|
8215
|
+
}
|
|
8216
|
+
}
|
|
8217
|
+
async function assertWorkspaceAccountPairInScope(db, accountId, workspaceId) {
|
|
8218
|
+
const [workspace] = await db.select({ id: workspaces.id }).from(workspaces).where(and7(eq7(workspaces.id, workspaceId), eq7(workspaces.accountId, accountId))).limit(1);
|
|
8219
|
+
if (!workspace) {
|
|
8220
|
+
throw new Error("Workspace does not belong to the expected account");
|
|
8221
|
+
}
|
|
8222
|
+
}
|
|
8223
|
+
async function withSlackBotLifecycleRls(db, input, fn) {
|
|
8224
|
+
return await withRlsContext(
|
|
8225
|
+
db,
|
|
8226
|
+
{ accountId: input.accountId, workspaceId: input.workspaceId },
|
|
8227
|
+
async (scopedDb) => {
|
|
8228
|
+
await setSubjectRlsContext(scopedDb, input.subjectId);
|
|
8229
|
+
await assertWorkspaceAccountPairInScope(scopedDb, input.accountId, input.workspaceId);
|
|
8230
|
+
return await fn(scopedDb);
|
|
8231
|
+
}
|
|
8232
|
+
);
|
|
8233
|
+
}
|
|
8234
|
+
async function createConnectionWithSlackBotSuccessAudit(db, input) {
|
|
8235
|
+
if (input.connection.accountId !== input.accountId || input.connection.workspaceId !== input.workspaceId) {
|
|
8236
|
+
throw new Error("Slack bot connection and lifecycle audit tenant must match");
|
|
8237
|
+
}
|
|
8238
|
+
return await withSlackBotLifecycleRls(db, input, async (scopedDb) => {
|
|
8239
|
+
const connection = await createConnectionInScope(scopedDb, input.connection);
|
|
8240
|
+
await insertSlackBotLifecycleSuccessAuditInScope(scopedDb, {
|
|
8241
|
+
...input,
|
|
8242
|
+
action: "slack_bot.connected",
|
|
8243
|
+
connectionId: connection.id
|
|
8244
|
+
});
|
|
8245
|
+
return connection;
|
|
8144
8246
|
});
|
|
8145
8247
|
}
|
|
8248
|
+
async function updateConnectionWithSlackBotSuccessAudit(db, input) {
|
|
8249
|
+
if (input.connection.workspaceId !== input.workspaceId) {
|
|
8250
|
+
throw new Error("Slack bot connection and lifecycle audit workspace must match");
|
|
8251
|
+
}
|
|
8252
|
+
return await withSlackBotLifecycleRls(db, input, async (scopedDb) => {
|
|
8253
|
+
const connection = await updateConnectionInScope(scopedDb, input.connection);
|
|
8254
|
+
if (!connection) return null;
|
|
8255
|
+
if (connection.accountId !== input.accountId) {
|
|
8256
|
+
throw new Error("Slack bot connection and lifecycle audit account must match");
|
|
8257
|
+
}
|
|
8258
|
+
await insertSlackBotLifecycleSuccessAuditInScope(scopedDb, {
|
|
8259
|
+
...input,
|
|
8260
|
+
action: "slack_bot.reinstalled",
|
|
8261
|
+
connectionId: connection.id
|
|
8262
|
+
});
|
|
8263
|
+
return connection;
|
|
8264
|
+
});
|
|
8265
|
+
}
|
|
8266
|
+
async function revokeConnectionWithSlackBotSuccessAudit(db, input) {
|
|
8267
|
+
return await withSlackBotLifecycleRls(db, input, async (scopedDb) => {
|
|
8268
|
+
const connection = await revokeConnectionInScope(
|
|
8269
|
+
scopedDb,
|
|
8270
|
+
input.workspaceId,
|
|
8271
|
+
input.connectionId,
|
|
8272
|
+
input.subjectId,
|
|
8273
|
+
input.expectedVersion
|
|
8274
|
+
);
|
|
8275
|
+
if (!connection) return null;
|
|
8276
|
+
if (connection.accountId !== input.accountId) {
|
|
8277
|
+
throw new Error("Slack bot connection and lifecycle audit account must match");
|
|
8278
|
+
}
|
|
8279
|
+
await insertSlackBotLifecycleSuccessAuditInScope(scopedDb, {
|
|
8280
|
+
...input,
|
|
8281
|
+
action: "slack_bot.disconnected",
|
|
8282
|
+
connectionId: connection.id
|
|
8283
|
+
});
|
|
8284
|
+
return connection;
|
|
8285
|
+
});
|
|
8286
|
+
}
|
|
8287
|
+
async function recordSlackBotInstallCallbackFailure(db, input) {
|
|
8288
|
+
if (!/^[a-f0-9]{64}$/.test(input.callbackDigest)) {
|
|
8289
|
+
throw new Error("Slack callback digest must be a lowercase SHA-256 value");
|
|
8290
|
+
}
|
|
8291
|
+
return await withRlsContext(
|
|
8292
|
+
db,
|
|
8293
|
+
{ accountId: input.accountId, workspaceId: input.workspaceId },
|
|
8294
|
+
async (scopedDb) => {
|
|
8295
|
+
await setSubjectRlsContext(scopedDb, input.subjectId);
|
|
8296
|
+
await assertWorkspaceAccountPairInScope(scopedDb, input.accountId, input.workspaceId);
|
|
8297
|
+
await scopedDb.execute(
|
|
8298
|
+
sql4`select pg_advisory_xact_lock(hashtextextended(${`slack-callback-failure:${input.workspaceId}:${input.callbackDigest}`}, 0))`
|
|
8299
|
+
);
|
|
8300
|
+
const [existing] = await scopedDb.select({ id: auditEvents.id }).from(auditEvents).where(
|
|
8301
|
+
and7(
|
|
8302
|
+
eq7(auditEvents.accountId, input.accountId),
|
|
8303
|
+
eq7(auditEvents.workspaceId, input.workspaceId),
|
|
8304
|
+
eq7(auditEvents.action, "slack_bot.install.callback.failed"),
|
|
8305
|
+
eq7(auditEvents.targetType, "slack_oauth_callback"),
|
|
8306
|
+
eq7(auditEvents.targetId, input.callbackDigest)
|
|
8307
|
+
)
|
|
8308
|
+
).limit(1);
|
|
8309
|
+
if (existing) return false;
|
|
8310
|
+
await scopedDb.insert(auditEvents).values({
|
|
8311
|
+
accountId: input.accountId,
|
|
8312
|
+
workspaceId: input.workspaceId,
|
|
8313
|
+
subjectId: input.subjectId,
|
|
8314
|
+
action: "slack_bot.install.callback.failed",
|
|
8315
|
+
targetType: "slack_oauth_callback",
|
|
8316
|
+
targetId: input.callbackDigest,
|
|
8317
|
+
metadata: {
|
|
8318
|
+
outcome: "failed",
|
|
8319
|
+
installMode: input.installMode,
|
|
8320
|
+
stage: input.stage,
|
|
8321
|
+
reason: input.reason
|
|
8322
|
+
}
|
|
8323
|
+
});
|
|
8324
|
+
return true;
|
|
8325
|
+
}
|
|
8326
|
+
);
|
|
8327
|
+
}
|
|
8146
8328
|
async function claimSlackBotPostOperation(db, input) {
|
|
8147
8329
|
const claimLeaseMs = Math.max(1, Math.min(Math.trunc(input.claimLeaseMs), 12e4));
|
|
8148
8330
|
return await withRlsContext(
|
|
@@ -8302,13 +8484,16 @@ async function getSlackBotPostOperation(db, workspaceId, connectionId, operation
|
|
|
8302
8484
|
});
|
|
8303
8485
|
}
|
|
8304
8486
|
async function loadConnectionCredentialForBroker(db, settings, input) {
|
|
8487
|
+
if (input.allowSubjectOwned && !input.subjectId) {
|
|
8488
|
+
return null;
|
|
8489
|
+
}
|
|
8305
8490
|
const key = environmentsEncryptionKeyBytes3(settings);
|
|
8306
8491
|
if (!key) {
|
|
8307
8492
|
throw new Error(
|
|
8308
8493
|
"connection credential present but OPENGENI_ENVIRONMENTS_ENCRYPTION_KEY is not configured"
|
|
8309
8494
|
);
|
|
8310
8495
|
}
|
|
8311
|
-
const subjectPredicate = input.allowSubjectOwned ?
|
|
8496
|
+
const subjectPredicate = input.allowSubjectOwned ? connectionExactSubject(input.subjectId) : isNull2(connections.subjectId);
|
|
8312
8497
|
const conditions = [
|
|
8313
8498
|
eq7(connections.workspaceId, input.workspaceId),
|
|
8314
8499
|
subjectPredicate
|
|
@@ -8321,70 +8506,81 @@ async function loadConnectionCredentialForBroker(db, settings, input) {
|
|
|
8321
8506
|
conditions.push(eq7(connections.kind, input.kind));
|
|
8322
8507
|
}
|
|
8323
8508
|
}
|
|
8324
|
-
return await
|
|
8325
|
-
|
|
8326
|
-
|
|
8327
|
-
|
|
8328
|
-
|
|
8329
|
-
|
|
8330
|
-
|
|
8331
|
-
|
|
8332
|
-
|
|
8333
|
-
|
|
8334
|
-
|
|
8335
|
-
|
|
8336
|
-
|
|
8337
|
-
|
|
8338
|
-
|
|
8339
|
-
)
|
|
8340
|
-
|
|
8341
|
-
|
|
8342
|
-
|
|
8509
|
+
return await withConnectionSubjectRls(
|
|
8510
|
+
db,
|
|
8511
|
+
input.workspaceId,
|
|
8512
|
+
input.allowSubjectOwned ? input.subjectId : null,
|
|
8513
|
+
async (scopedDb) => {
|
|
8514
|
+
const [row] = await scopedDb.select().from(connections).where(and7(...conditions)).orderBy(
|
|
8515
|
+
desc2(sql4`(${connections.status} = 'active')`),
|
|
8516
|
+
desc2(connections.updatedAt)
|
|
8517
|
+
).limit(1);
|
|
8518
|
+
if (!row) {
|
|
8519
|
+
return null;
|
|
8520
|
+
}
|
|
8521
|
+
let credential;
|
|
8522
|
+
try {
|
|
8523
|
+
credential = JSON.parse(decryptEnvironmentValue(key, row.credentialEncrypted));
|
|
8524
|
+
} catch (error) {
|
|
8525
|
+
throw new Error(
|
|
8526
|
+
`connection credential could not be decrypted for ${row.id}: ${error instanceof Error ? error.message : String(error)}`,
|
|
8527
|
+
{ cause: error }
|
|
8528
|
+
);
|
|
8529
|
+
}
|
|
8530
|
+
if (!credential || typeof credential !== "object" || Array.isArray(credential)) {
|
|
8531
|
+
throw new Error(`connection credential bundle for ${row.id} is not a JSON object`);
|
|
8532
|
+
}
|
|
8533
|
+
return {
|
|
8534
|
+
id: row.id,
|
|
8535
|
+
accountId: row.accountId,
|
|
8536
|
+
workspaceId: row.workspaceId,
|
|
8537
|
+
subjectId: row.subjectId,
|
|
8538
|
+
providerDomain: row.providerDomain,
|
|
8539
|
+
kind: row.kind,
|
|
8540
|
+
status: row.status,
|
|
8541
|
+
credential,
|
|
8542
|
+
grantedScopes: row.grantedScopes,
|
|
8543
|
+
expiresAt: row.expiresAt,
|
|
8544
|
+
lastRefreshAt: row.lastRefreshAt,
|
|
8545
|
+
version: row.version,
|
|
8546
|
+
metadata: row.metadata
|
|
8547
|
+
};
|
|
8343
8548
|
}
|
|
8344
|
-
|
|
8345
|
-
id: row.id,
|
|
8346
|
-
accountId: row.accountId,
|
|
8347
|
-
workspaceId: row.workspaceId,
|
|
8348
|
-
subjectId: row.subjectId,
|
|
8349
|
-
providerDomain: row.providerDomain,
|
|
8350
|
-
kind: row.kind,
|
|
8351
|
-
status: row.status,
|
|
8352
|
-
credential,
|
|
8353
|
-
grantedScopes: row.grantedScopes,
|
|
8354
|
-
expiresAt: row.expiresAt,
|
|
8355
|
-
lastRefreshAt: row.lastRefreshAt,
|
|
8356
|
-
version: row.version,
|
|
8357
|
-
metadata: row.metadata
|
|
8358
|
-
};
|
|
8359
|
-
});
|
|
8549
|
+
);
|
|
8360
8550
|
}
|
|
8361
8551
|
async function recordConnectionTokenRefresh(db, input) {
|
|
8362
|
-
return await
|
|
8363
|
-
|
|
8364
|
-
|
|
8365
|
-
|
|
8366
|
-
|
|
8367
|
-
|
|
8368
|
-
|
|
8369
|
-
|
|
8370
|
-
|
|
8371
|
-
|
|
8372
|
-
|
|
8373
|
-
|
|
8374
|
-
|
|
8375
|
-
|
|
8376
|
-
|
|
8377
|
-
|
|
8378
|
-
|
|
8379
|
-
|
|
8380
|
-
|
|
8381
|
-
|
|
8382
|
-
|
|
8383
|
-
|
|
8384
|
-
|
|
8552
|
+
return await withConnectionSubjectRls(
|
|
8553
|
+
db,
|
|
8554
|
+
input.workspaceId,
|
|
8555
|
+
input.subjectId,
|
|
8556
|
+
async (scopedDb) => {
|
|
8557
|
+
const set = {
|
|
8558
|
+
credentialEncrypted: input.credentialEncrypted,
|
|
8559
|
+
expiresAt: input.expiresAt,
|
|
8560
|
+
lastRefreshAt: input.lastRefreshAt,
|
|
8561
|
+
status: "active",
|
|
8562
|
+
lastError: null,
|
|
8563
|
+
version: sql4`${connections.version} + 1`,
|
|
8564
|
+
updatedAt: /* @__PURE__ */ new Date(),
|
|
8565
|
+
...input.grantedScopes !== void 0 ? { grantedScopes: input.grantedScopes } : {}
|
|
8566
|
+
};
|
|
8567
|
+
const updated = await scopedDb.update(connections).set(set).where(
|
|
8568
|
+
and7(
|
|
8569
|
+
eq7(connections.id, input.id),
|
|
8570
|
+
eq7(connections.workspaceId, input.workspaceId),
|
|
8571
|
+
eq7(connections.version, input.version),
|
|
8572
|
+
connectionExactSubject(input.subjectId),
|
|
8573
|
+
// A refresh may only ever renew a live credential; revoked/errored rows
|
|
8574
|
+
// stay dead even if a status change somewhere forgot to bump version.
|
|
8575
|
+
eq7(connections.status, "active")
|
|
8576
|
+
)
|
|
8577
|
+
).returning({ id: connections.id });
|
|
8578
|
+
return updated.length > 0;
|
|
8579
|
+
}
|
|
8580
|
+
);
|
|
8385
8581
|
}
|
|
8386
8582
|
async function setConnectionStatus(db, workspaceId, status, lastError, guard) {
|
|
8387
|
-
return await
|
|
8583
|
+
return await withConnectionSubjectRls(db, workspaceId, guard.subjectId, async (scopedDb) => {
|
|
8388
8584
|
const updated = await scopedDb.update(connections).set({
|
|
8389
8585
|
status,
|
|
8390
8586
|
lastError,
|
|
@@ -8398,21 +8594,23 @@ async function setConnectionStatus(db, workspaceId, status, lastError, guard) {
|
|
|
8398
8594
|
and7(
|
|
8399
8595
|
eq7(connections.workspaceId, workspaceId),
|
|
8400
8596
|
eq7(connections.id, guard.id),
|
|
8401
|
-
eq7(connections.version, guard.version)
|
|
8597
|
+
eq7(connections.version, guard.version),
|
|
8598
|
+
connectionExactSubject(guard.subjectId)
|
|
8402
8599
|
)
|
|
8403
8600
|
).returning({ id: connections.id });
|
|
8404
8601
|
return updated.length > 0;
|
|
8405
8602
|
});
|
|
8406
8603
|
}
|
|
8407
|
-
async function recordConnectionUsed(db, workspaceId, connectionId) {
|
|
8408
|
-
await
|
|
8604
|
+
async function recordConnectionUsed(db, workspaceId, connectionId, subjectId) {
|
|
8605
|
+
await withConnectionSubjectRls(db, workspaceId, subjectId, async (scopedDb) => {
|
|
8409
8606
|
await scopedDb.update(connections).set({
|
|
8410
8607
|
lastUsedAt: /* @__PURE__ */ new Date(),
|
|
8411
8608
|
updatedAt: /* @__PURE__ */ new Date()
|
|
8412
8609
|
}).where(
|
|
8413
8610
|
and7(
|
|
8414
8611
|
eq7(connections.workspaceId, workspaceId),
|
|
8415
|
-
eq7(connections.id, connectionId)
|
|
8612
|
+
eq7(connections.id, connectionId),
|
|
8613
|
+
connectionExactSubject(subjectId)
|
|
8416
8614
|
)
|
|
8417
8615
|
);
|
|
8418
8616
|
});
|
|
@@ -13536,6 +13734,7 @@ async function createSessionInTransaction(tx, input, id) {
|
|
|
13536
13734
|
initialMessage: input.initialMessage,
|
|
13537
13735
|
initialTurnInstructions: input.initialTurnInstructions ?? null,
|
|
13538
13736
|
resources: input.resources,
|
|
13737
|
+
skills: input.skills ?? [],
|
|
13539
13738
|
tools: input.tools ?? [],
|
|
13540
13739
|
toolPolicy: input.toolPolicy ?? null,
|
|
13541
13740
|
metadata: input.metadata,
|
|
@@ -14852,25 +15051,34 @@ async function getSessionLineage(db, workspaceId, sessionId) {
|
|
|
14852
15051
|
if (rootRows.length === 0) {
|
|
14853
15052
|
return null;
|
|
14854
15053
|
}
|
|
14855
|
-
const
|
|
14856
|
-
with recursive ancestors(id, parent_session_id, depth, path) as (
|
|
14857
|
-
select ${sessions.id}, ${sessions.parentSessionId}, 0, array[${sessions.id}]
|
|
15054
|
+
const ancestorLineageRows = await scopedDb.execute(sql4`
|
|
15055
|
+
with recursive ancestors(id, parent_session_id, depth, path, cycle) as (
|
|
15056
|
+
select ${sessions.id}, ${sessions.parentSessionId}, 0, array[${sessions.id}], false
|
|
14858
15057
|
from ${sessions}
|
|
14859
15058
|
where ${sessions.workspaceId} = ${workspaceId}
|
|
14860
15059
|
and ${sessions.id} = ${sessionId}
|
|
14861
15060
|
union all
|
|
14862
|
-
select
|
|
15061
|
+
select
|
|
15062
|
+
parent.id,
|
|
15063
|
+
parent.parent_session_id,
|
|
15064
|
+
ancestors.depth + 1,
|
|
15065
|
+
ancestors.path || parent.id,
|
|
15066
|
+
parent.id = any(ancestors.path)
|
|
14863
15067
|
from ${sessions} parent
|
|
14864
15068
|
join ancestors on ancestors.parent_session_id = parent.id
|
|
14865
15069
|
where parent.workspace_id = ${workspaceId}
|
|
14866
|
-
and ancestors.
|
|
14867
|
-
and
|
|
15070
|
+
and not ancestors.cycle
|
|
15071
|
+
and ancestors.depth < 64
|
|
14868
15072
|
)
|
|
14869
|
-
select id, parent_session_id as "parentSessionId", depth, path
|
|
15073
|
+
select id, parent_session_id as "parentSessionId", depth, path, cycle
|
|
14870
15074
|
from ancestors
|
|
14871
|
-
where depth > 0
|
|
14872
15075
|
order by depth desc
|
|
14873
15076
|
`);
|
|
15077
|
+
const frontier = ancestorLineageRows[0];
|
|
15078
|
+
if (!frontier || frontier.cycle || frontier.parentSessionId !== null || Number(frontier.depth) >= 64) {
|
|
15079
|
+
throw new Error(`session lineage for ${sessionId} has no valid workspace root`);
|
|
15080
|
+
}
|
|
15081
|
+
const ancestorRows = ancestorLineageRows.filter((row) => row.depth > 0);
|
|
14874
15082
|
const childRows = await scopedDb.execute(sql4`
|
|
14875
15083
|
with recursive descendants(id, parent_session_id, depth, path) as (
|
|
14876
15084
|
select child.id, child.parent_session_id, 1, array[${sessionId}, child.id]
|
|
@@ -14885,7 +15093,7 @@ async function getSessionLineage(db, workspaceId, sessionId) {
|
|
|
14885
15093
|
and descendants.depth < 5
|
|
14886
15094
|
and not child.id = any(descendants.path)
|
|
14887
15095
|
)
|
|
14888
|
-
select id, parent_session_id as "parentSessionId", depth, path
|
|
15096
|
+
select id, parent_session_id as "parentSessionId", depth, path, false as cycle
|
|
14889
15097
|
from descendants
|
|
14890
15098
|
order by path
|
|
14891
15099
|
limit ${descendantLimit + 1}
|
|
@@ -22694,6 +22902,12 @@ async function claimSessionWorkForAttempt(db, workspaceId, input) {
|
|
|
22694
22902
|
and7(
|
|
22695
22903
|
eq7(sessionAttemptInterruptions.workspaceId, workspaceId),
|
|
22696
22904
|
eq7(sessionAttemptInterruptions.sessionId, sessionId),
|
|
22905
|
+
inArray3(sessionAttemptInterruptions.state, [
|
|
22906
|
+
"pending",
|
|
22907
|
+
"delivered",
|
|
22908
|
+
"acknowledged",
|
|
22909
|
+
"settled"
|
|
22910
|
+
]),
|
|
22697
22911
|
isNull2(sessionTurnAttempts.quiescedAt)
|
|
22698
22912
|
)
|
|
22699
22913
|
).orderBy(desc2(sessionAttemptInterruptions.requestedAt)).limit(1);
|
|
@@ -22905,11 +23119,15 @@ async function claimSessionWorkForAttempt(db, workspaceId, input) {
|
|
|
22905
23119
|
desc2(sessionSystemUpdates.createdAt),
|
|
22906
23120
|
desc2(sessionSystemUpdates.id)
|
|
22907
23121
|
).limit(1).for("update");
|
|
22908
|
-
const rows =
|
|
23122
|
+
const rows = await rawRows(
|
|
22909
23123
|
tx,
|
|
22910
23124
|
sql4`select id, trigger_event_id, metadata from session_turns
|
|
22911
23125
|
where workspace_id = ${workspaceId} and session_id = ${sessionId}
|
|
22912
23126
|
and status = 'queued' and source in ('user', 'api')
|
|
23127
|
+
and (
|
|
23128
|
+
${Boolean(pendingAgentSteer)} = false
|
|
23129
|
+
or metadata->>'delivery' = 'steer'
|
|
23130
|
+
)
|
|
22913
23131
|
order by position asc, created_at asc, id asc
|
|
22914
23132
|
limit 1`
|
|
22915
23133
|
);
|
|
@@ -25415,7 +25633,7 @@ async function getSessionQueueSnapshot(db, workspaceId, sessionId) {
|
|
|
25415
25633
|
return {
|
|
25416
25634
|
version: session.queueVersion,
|
|
25417
25635
|
effectiveControl: serializeEffectiveSessionControl(effectiveControl),
|
|
25418
|
-
stoppingPreviousAttempt: latestInterruption !== null && latestInterruption.quiescedAt === null,
|
|
25636
|
+
stoppingPreviousAttempt: latestInterruption !== null && latestInterruption.interruptionState !== "rejected_stale" && latestInterruption.quiescedAt === null,
|
|
25419
25637
|
items: rows.map(mapSessionTurn)
|
|
25420
25638
|
};
|
|
25421
25639
|
});
|
|
@@ -26571,6 +26789,7 @@ function mapSession(row, effectiveControl, mcpServers = [], pin = mapSessionPin(
|
|
|
26571
26789
|
titleSource: row.titleSource ?? null,
|
|
26572
26790
|
instructions: row.instructions ?? null,
|
|
26573
26791
|
resources: row.resources,
|
|
26792
|
+
skills: row.skills ?? [],
|
|
26574
26793
|
tools: row.tools,
|
|
26575
26794
|
toolPolicy: row.toolPolicy ?? {
|
|
26576
26795
|
mode: "legacy",
|
|
@@ -27314,6 +27533,7 @@ export {
|
|
|
27314
27533
|
SessionPinVersionConflictError,
|
|
27315
27534
|
SessionSpawnDeniedDbError,
|
|
27316
27535
|
SessionToolPolicyVersionConflictError,
|
|
27536
|
+
SlackBotLifecycleSuccessAuditError,
|
|
27317
27537
|
WORKSPACE_MEMORY_BLOCK_EMPTY,
|
|
27318
27538
|
WORKSPACE_MEMORY_BLOCK_HEADER_POPULATED,
|
|
27319
27539
|
WORKSPACE_MEMORY_BLOCK_TOKEN_BUDGET,
|
|
@@ -27417,6 +27637,7 @@ export {
|
|
|
27417
27637
|
countWorkspacesForAccount,
|
|
27418
27638
|
createApiKey,
|
|
27419
27639
|
createConnection,
|
|
27640
|
+
createConnectionWithSlackBotSuccessAudit,
|
|
27420
27641
|
createDb,
|
|
27421
27642
|
createDeviceEnrollmentRequest,
|
|
27422
27643
|
createEnrollment,
|
|
@@ -27727,6 +27948,7 @@ export {
|
|
|
27727
27948
|
recordRetainedProcessReconciliationProof,
|
|
27728
27949
|
recordSessionActiveCodexCredential,
|
|
27729
27950
|
recordSkippedContextCompaction,
|
|
27951
|
+
recordSlackBotInstallCallbackFailure,
|
|
27730
27952
|
recordStreamAcknowledgment,
|
|
27731
27953
|
recordStripeWebhookEvent,
|
|
27732
27954
|
recordUsageEvent,
|
|
@@ -27765,6 +27987,7 @@ export {
|
|
|
27765
27987
|
retireHostExportConsumer,
|
|
27766
27988
|
revokeApiKey,
|
|
27767
27989
|
revokeConnection,
|
|
27990
|
+
revokeConnectionWithSlackBotSuccessAudit,
|
|
27768
27991
|
revokeEnrollment,
|
|
27769
27992
|
revokeViewer,
|
|
27770
27993
|
rewindHostExportConsumer,
|
|
@@ -27833,6 +28056,7 @@ export {
|
|
|
27833
28056
|
updateCodexAllocatorEligibility,
|
|
27834
28057
|
updateCodexRotationSettings,
|
|
27835
28058
|
updateConnection,
|
|
28059
|
+
updateConnectionWithSlackBotSuccessAudit,
|
|
27836
28060
|
updateImportBatchCounts,
|
|
27837
28061
|
updateKnowledgeMemory,
|
|
27838
28062
|
updatePackInstallationStatus,
|