@opengeni/db 0.21.0 → 0.22.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/{chunk-OTJHD33E.js → chunk-BNGEN5QZ.js} +5 -1
- package/dist/chunk-BNGEN5QZ.js.map +1 -0
- package/dist/{chunk-NK2A36KP.js → chunk-CYGFLLMN.js} +150 -1
- package/dist/chunk-CYGFLLMN.js.map +1 -0
- package/dist/index.d.ts +87 -0
- package/dist/index.js +599 -6
- package/dist/index.js.map +1 -1
- package/dist/provision-roles.js +1 -1
- package/dist/runtime-posture.d.ts +2 -2
- package/dist/schema.d.ts +820 -0
- package/dist/schema.js +5 -1
- package/dist/session-control.d.ts +1 -0
- package/drizzle/0155_connector_action_policies.sql +218 -0
- package/package.json +3 -3
- package/src/index.ts +926 -1
- package/src/runtime-posture.ts +4 -0
- package/src/schema.ts +181 -0
- package/src/session-control.ts +3 -0
- package/dist/chunk-NK2A36KP.js.map +0 -1
- package/dist/chunk-OTJHD33E.js.map +0 -1
package/dist/index.js
CHANGED
|
@@ -12,6 +12,8 @@ import {
|
|
|
12
12
|
codexSubscriptionCredentials,
|
|
13
13
|
composerDrafts,
|
|
14
14
|
connections,
|
|
15
|
+
connectorActionPolicies,
|
|
16
|
+
connectorActionRequests,
|
|
15
17
|
creditLedgerEntries,
|
|
16
18
|
deviceEnrollmentRequests,
|
|
17
19
|
enrollments,
|
|
@@ -105,7 +107,7 @@ import {
|
|
|
105
107
|
workspaceVariableSetVariables,
|
|
106
108
|
workspaceVariableSets,
|
|
107
109
|
workspaces
|
|
108
|
-
} from "./chunk-
|
|
110
|
+
} from "./chunk-CYGFLLMN.js";
|
|
109
111
|
import {
|
|
110
112
|
migrate,
|
|
111
113
|
runMigrations
|
|
@@ -125,7 +127,7 @@ import {
|
|
|
125
127
|
inspectRuntimeDatabasePosture,
|
|
126
128
|
provisionRoles,
|
|
127
129
|
runtimeDatabaseReadyCheck
|
|
128
|
-
} from "./chunk-
|
|
130
|
+
} from "./chunk-BNGEN5QZ.js";
|
|
129
131
|
import "./chunk-PZ5AY32C.js";
|
|
130
132
|
|
|
131
133
|
// src/index.ts
|
|
@@ -172,7 +174,11 @@ import {
|
|
|
172
174
|
SubmitHumanInputResponseRequest,
|
|
173
175
|
TurnExecutionPolicyV1
|
|
174
176
|
} from "@opengeni/contracts";
|
|
175
|
-
import {
|
|
177
|
+
import {
|
|
178
|
+
environmentsEncryptionKeyBytes as environmentsEncryptionKeyBytes3,
|
|
179
|
+
VERCEL_AI_GATEWAY_CONNECTION_DOMAIN,
|
|
180
|
+
VERCEL_AI_GATEWAY_CONNECTION_ROLE
|
|
181
|
+
} from "@opengeni/config";
|
|
176
182
|
import { boundModelToolOutputItem as boundModelToolOutputItem2, isCodexBilledModel } from "@opengeni/codex";
|
|
177
183
|
import { isCodexBilledModel as isCodexBilledModel2 } from "@opengeni/codex";
|
|
178
184
|
import {
|
|
@@ -1397,7 +1403,7 @@ async function registerSessionTurnAttemptClaim(db, input) {
|
|
|
1397
1403
|
eq4(sessionTurnAttempts.id, input.id)
|
|
1398
1404
|
)
|
|
1399
1405
|
).for("update").limit(1);
|
|
1400
|
-
if (!existing || existing.accountId !== input.accountId || existing.sessionId !== input.sessionId || existing.turnId !== input.turnId || existing.executionGeneration !== input.executionGeneration || existing.temporalWorkflowId !== input.temporalWorkflowId || existing.temporalWorkflowRunId !== input.temporalWorkflowRunId || existing.temporalActivityId !== input.temporalActivityId || existing.state === "closed") {
|
|
1406
|
+
if (!existing || existing.accountId !== input.accountId || existing.sessionId !== input.sessionId || existing.turnId !== input.turnId || existing.executionGeneration !== input.executionGeneration || existing.temporalWorkflowId !== input.temporalWorkflowId || existing.temporalWorkflowRunId !== input.temporalWorkflowRunId || existing.temporalActivityId !== input.temporalActivityId || JSON.stringify(existing.connectorActionPolicies) !== JSON.stringify(input.connectorActionPolicies) || existing.state === "closed") {
|
|
1401
1407
|
throw new SessionControlInvariantError(
|
|
1402
1408
|
`Attempt ${input.id} conflicts with a different or closed ownership chain`
|
|
1403
1409
|
);
|
|
@@ -14014,6 +14020,32 @@ async function loadConnectionCredentialForBroker(db, settings, input) {
|
|
|
14014
14020
|
}
|
|
14015
14021
|
);
|
|
14016
14022
|
}
|
|
14023
|
+
async function workspaceVercelAiGatewayConnectionActive(db, workspaceId) {
|
|
14024
|
+
const connections2 = await listConnectionsMetadata(db, workspaceId, null);
|
|
14025
|
+
return connections2.some(
|
|
14026
|
+
(connection) => connection.subjectId === null && connection.providerDomain === VERCEL_AI_GATEWAY_CONNECTION_DOMAIN && connection.kind === "api_key" && connection.status === "active" && connection.metadata.credentialRole === VERCEL_AI_GATEWAY_CONNECTION_ROLE
|
|
14027
|
+
);
|
|
14028
|
+
}
|
|
14029
|
+
async function loadWorkspaceVercelAiGatewayApiKey(db, settings, workspaceId) {
|
|
14030
|
+
const metadata = (await listConnectionsMetadata(db, workspaceId, null)).find(
|
|
14031
|
+
(connection2) => connection2.subjectId === null && connection2.providerDomain === VERCEL_AI_GATEWAY_CONNECTION_DOMAIN && connection2.kind === "api_key" && connection2.status === "active" && connection2.metadata.credentialRole === VERCEL_AI_GATEWAY_CONNECTION_ROLE
|
|
14032
|
+
);
|
|
14033
|
+
if (!metadata) {
|
|
14034
|
+
return null;
|
|
14035
|
+
}
|
|
14036
|
+
const connection = await loadConnectionCredentialForBroker(db, settings, {
|
|
14037
|
+
workspaceId,
|
|
14038
|
+
connectionId: metadata.id,
|
|
14039
|
+
providerDomain: VERCEL_AI_GATEWAY_CONNECTION_DOMAIN,
|
|
14040
|
+
kind: "api_key",
|
|
14041
|
+
allowSubjectOwned: false
|
|
14042
|
+
});
|
|
14043
|
+
if (!connection || connection.status !== "active" || connection.metadata.credentialRole !== VERCEL_AI_GATEWAY_CONNECTION_ROLE) {
|
|
14044
|
+
return null;
|
|
14045
|
+
}
|
|
14046
|
+
const apiKey = connection.credential.apiKey;
|
|
14047
|
+
return typeof apiKey === "string" && apiKey.trim().length > 0 ? apiKey : null;
|
|
14048
|
+
}
|
|
14017
14049
|
async function recordConnectionTokenRefresh(db, input) {
|
|
14018
14050
|
return await withConnectionSubjectRls(
|
|
14019
14051
|
db,
|
|
@@ -19117,6 +19149,510 @@ async function listSessionMcpServersForRun(db, workspaceId, sessionId, attemptId
|
|
|
19117
19149
|
});
|
|
19118
19150
|
});
|
|
19119
19151
|
}
|
|
19152
|
+
var CONNECTOR_ACTION_POLICY_SNAPSHOT_MAX = 2048;
|
|
19153
|
+
var CONNECTOR_ACTION_APPROVAL_ID_MAX = 1024;
|
|
19154
|
+
var CONNECTOR_ACTION_CONNECTION_ID_MAX = 512;
|
|
19155
|
+
var CONNECTOR_ACTION_SERVER_ID_MAX = 256;
|
|
19156
|
+
var CONNECTOR_ACTION_NAME_MAX = 512;
|
|
19157
|
+
function boundedConnectorActionText(value, label, max) {
|
|
19158
|
+
const trimmed = value.trim();
|
|
19159
|
+
if (trimmed.length === 0 || Buffer.byteLength(trimmed, "utf8") > max) {
|
|
19160
|
+
throw new Error(`${label} must be between 1 and ${max} UTF-8 bytes`);
|
|
19161
|
+
}
|
|
19162
|
+
return trimmed;
|
|
19163
|
+
}
|
|
19164
|
+
function connectorActionPolicySelector(toolName, args) {
|
|
19165
|
+
if (args && typeof args === "object" && !Array.isArray(args)) {
|
|
19166
|
+
const action = args.action;
|
|
19167
|
+
if (typeof action === "string" && action.trim().length > 0) {
|
|
19168
|
+
return boundedConnectorActionText(action, "connector action name", CONNECTOR_ACTION_NAME_MAX);
|
|
19169
|
+
}
|
|
19170
|
+
}
|
|
19171
|
+
return toolName;
|
|
19172
|
+
}
|
|
19173
|
+
function connectorActionEvidenceName(resolved) {
|
|
19174
|
+
return resolved.entry?.actionName ?? "*";
|
|
19175
|
+
}
|
|
19176
|
+
function connectorActionFingerprint(input) {
|
|
19177
|
+
return createHash6("sha256").update(
|
|
19178
|
+
stableJson3({
|
|
19179
|
+
workspaceId: input.workspaceId,
|
|
19180
|
+
connectionId: input.connectionId,
|
|
19181
|
+
serverId: input.serverId,
|
|
19182
|
+
toolName: input.toolName,
|
|
19183
|
+
actionName: input.actionName,
|
|
19184
|
+
arguments: input.arguments ?? null
|
|
19185
|
+
}),
|
|
19186
|
+
"utf8"
|
|
19187
|
+
).digest("hex");
|
|
19188
|
+
}
|
|
19189
|
+
function resolveConnectorActionPolicy(snapshot, input) {
|
|
19190
|
+
const candidates = snapshot.filter(
|
|
19191
|
+
(entry) => entry.connectionId === input.connectionId && (entry.serverId === input.serverId || entry.serverId === "*") && (entry.toolName === input.toolName || entry.toolName === "*") && (entry.actionName === input.actionName || entry.actionName === "*")
|
|
19192
|
+
).map((entry) => ({
|
|
19193
|
+
entry,
|
|
19194
|
+
specificity: Number(entry.serverId !== "*") + Number(entry.toolName !== "*") + Number(entry.actionName !== "*")
|
|
19195
|
+
})).sort(
|
|
19196
|
+
(left, right) => right.specificity - left.specificity || left.entry.id.localeCompare(right.entry.id)
|
|
19197
|
+
);
|
|
19198
|
+
const selected = candidates[0];
|
|
19199
|
+
if (!selected) return { managed: false };
|
|
19200
|
+
if (candidates[1]?.specificity === selected.specificity) {
|
|
19201
|
+
return { managed: true, source: "ambiguous", entry: null, decision: "block" };
|
|
19202
|
+
}
|
|
19203
|
+
return { managed: true, source: "explicit", entry: selected.entry };
|
|
19204
|
+
}
|
|
19205
|
+
function connectorActionAuditMetadata(row, extra = {}) {
|
|
19206
|
+
return {
|
|
19207
|
+
requestId: row.id,
|
|
19208
|
+
sessionId: row.sessionId,
|
|
19209
|
+
turnId: row.turnId,
|
|
19210
|
+
attemptId: row.creationAttemptId,
|
|
19211
|
+
creationExecutionGeneration: row.creationExecutionGeneration,
|
|
19212
|
+
executionAttemptId: row.executionAttemptId,
|
|
19213
|
+
executionAttemptGeneration: row.executionAttemptGeneration,
|
|
19214
|
+
approvalId: row.approvalId,
|
|
19215
|
+
initiatorKind: row.initiatorKind,
|
|
19216
|
+
initiatorSubjectId: row.initiatorSubjectId,
|
|
19217
|
+
connectionId: row.connectionId,
|
|
19218
|
+
connectionVersion: row.connectionVersion,
|
|
19219
|
+
serverId: row.serverId,
|
|
19220
|
+
toolName: row.toolName,
|
|
19221
|
+
actionName: row.actionName,
|
|
19222
|
+
policyId: row.policyId,
|
|
19223
|
+
policyVersion: row.policyVersion,
|
|
19224
|
+
policySource: row.policySource,
|
|
19225
|
+
policyDecision: row.policyDecision,
|
|
19226
|
+
actionFingerprint: row.actionFingerprint,
|
|
19227
|
+
...extra
|
|
19228
|
+
};
|
|
19229
|
+
}
|
|
19230
|
+
async function insertConnectorActionAudit(db, input) {
|
|
19231
|
+
await db.insert(auditEvents).values({
|
|
19232
|
+
accountId: input.row.accountId,
|
|
19233
|
+
workspaceId: input.row.workspaceId,
|
|
19234
|
+
subjectId: input.subjectId,
|
|
19235
|
+
action: input.action,
|
|
19236
|
+
targetType: "connector_action_request",
|
|
19237
|
+
targetId: input.row.id,
|
|
19238
|
+
metadata: connectorActionAuditMetadata(input.row, input.extra)
|
|
19239
|
+
});
|
|
19240
|
+
}
|
|
19241
|
+
function normalizedConnectorActionInvocation(identity, invocation) {
|
|
19242
|
+
const approvalId = boundedConnectorActionText(
|
|
19243
|
+
invocation.approvalId,
|
|
19244
|
+
"connector approval id",
|
|
19245
|
+
CONNECTOR_ACTION_APPROVAL_ID_MAX
|
|
19246
|
+
);
|
|
19247
|
+
const serverId = boundedConnectorActionText(
|
|
19248
|
+
invocation.serverId,
|
|
19249
|
+
"connector server id",
|
|
19250
|
+
CONNECTOR_ACTION_SERVER_ID_MAX
|
|
19251
|
+
);
|
|
19252
|
+
const toolName = boundedConnectorActionText(
|
|
19253
|
+
invocation.toolName,
|
|
19254
|
+
"connector tool name",
|
|
19255
|
+
CONNECTOR_ACTION_NAME_MAX
|
|
19256
|
+
);
|
|
19257
|
+
const policyActionSelector = connectorActionPolicySelector(toolName, invocation.arguments);
|
|
19258
|
+
const connectionId = invocation.connectionId?.trim() ? boundedConnectorActionText(
|
|
19259
|
+
invocation.connectionId,
|
|
19260
|
+
"connector connection id",
|
|
19261
|
+
CONNECTOR_ACTION_CONNECTION_ID_MAX
|
|
19262
|
+
) : null;
|
|
19263
|
+
return {
|
|
19264
|
+
approvalId,
|
|
19265
|
+
connectionId,
|
|
19266
|
+
serverId,
|
|
19267
|
+
toolName,
|
|
19268
|
+
policyActionSelector,
|
|
19269
|
+
arguments: invocation.arguments
|
|
19270
|
+
};
|
|
19271
|
+
}
|
|
19272
|
+
function durableConnectorActionInvocation(identity, invocation, resolved) {
|
|
19273
|
+
const actionName = connectorActionEvidenceName(resolved);
|
|
19274
|
+
return {
|
|
19275
|
+
approvalId: invocation.approvalId,
|
|
19276
|
+
connectionId: invocation.connectionId,
|
|
19277
|
+
serverId: invocation.serverId,
|
|
19278
|
+
toolName: invocation.toolName,
|
|
19279
|
+
actionName,
|
|
19280
|
+
actionFingerprint: connectorActionFingerprint({
|
|
19281
|
+
workspaceId: identity.workspaceId,
|
|
19282
|
+
connectionId: invocation.connectionId,
|
|
19283
|
+
serverId: invocation.serverId,
|
|
19284
|
+
toolName: invocation.toolName,
|
|
19285
|
+
actionName,
|
|
19286
|
+
arguments: invocation.arguments
|
|
19287
|
+
})
|
|
19288
|
+
};
|
|
19289
|
+
}
|
|
19290
|
+
async function connectorActionAttemptSnapshot(db, identity) {
|
|
19291
|
+
const [attempt] = await db.select({
|
|
19292
|
+
accountId: sessionTurnAttempts.accountId,
|
|
19293
|
+
sessionId: sessionTurnAttempts.sessionId,
|
|
19294
|
+
turnId: sessionTurnAttempts.turnId,
|
|
19295
|
+
executionGeneration: sessionTurnAttempts.executionGeneration,
|
|
19296
|
+
state: sessionTurnAttempts.state,
|
|
19297
|
+
connectorActionPolicies: sessionTurnAttempts.connectorActionPolicies
|
|
19298
|
+
}).from(sessionTurnAttempts).where(
|
|
19299
|
+
and11(
|
|
19300
|
+
eq11(sessionTurnAttempts.workspaceId, identity.workspaceId),
|
|
19301
|
+
eq11(sessionTurnAttempts.id, identity.attemptId)
|
|
19302
|
+
)
|
|
19303
|
+
).limit(1);
|
|
19304
|
+
if (!attempt || attempt.accountId !== identity.accountId || attempt.sessionId !== identity.sessionId || attempt.turnId !== identity.turnId || attempt.executionGeneration !== identity.executionGeneration || !["claimed", "running"].includes(attempt.state)) {
|
|
19305
|
+
throw new Error(`connector action attempt ownership is unavailable: ${identity.attemptId}`);
|
|
19306
|
+
}
|
|
19307
|
+
if (attempt.connectorActionPolicies.length > CONNECTOR_ACTION_POLICY_SNAPSHOT_MAX) {
|
|
19308
|
+
throw new Error("connector action policy snapshot exceeds the runtime bound");
|
|
19309
|
+
}
|
|
19310
|
+
return attempt.connectorActionPolicies;
|
|
19311
|
+
}
|
|
19312
|
+
function connectorActionRequestMatches(row, input) {
|
|
19313
|
+
const entry = input.resolved.entry;
|
|
19314
|
+
return row.accountId === input.identity.accountId && row.workspaceId === input.identity.workspaceId && row.sessionId === input.identity.sessionId && row.turnId === input.identity.turnId && row.creationAttemptId === input.identity.attemptId && row.creationExecutionGeneration === input.identity.executionGeneration && row.approvalId === input.invocation.approvalId && row.initiatorKind === input.identity.initiator.kind && row.initiatorSubjectId === input.identity.initiator.subjectId && row.connectionId === input.invocation.connectionId && row.serverId === input.invocation.serverId && row.toolName === input.invocation.toolName && row.actionName === input.invocation.actionName && row.policyId === (entry?.id ?? null) && row.policyVersion === (entry?.version ?? null) && row.policySource === input.resolved.source && row.policyDecision === (entry?.policy ?? "block") && row.actionFingerprint === input.invocation.actionFingerprint;
|
|
19315
|
+
}
|
|
19316
|
+
function connectorActionRequestMatchesLogicalCall(row, identity, invocation) {
|
|
19317
|
+
if (!invocation.connectionId) return false;
|
|
19318
|
+
return row.accountId === identity.accountId && row.workspaceId === identity.workspaceId && row.sessionId === identity.sessionId && row.turnId === identity.turnId && row.approvalId === invocation.approvalId && row.initiatorKind === identity.initiator.kind && row.initiatorSubjectId === identity.initiator.subjectId && row.connectionId === invocation.connectionId && row.serverId === invocation.serverId && row.toolName === invocation.toolName && row.actionFingerprint === connectorActionFingerprint({
|
|
19319
|
+
workspaceId: identity.workspaceId,
|
|
19320
|
+
connectionId: invocation.connectionId,
|
|
19321
|
+
serverId: invocation.serverId,
|
|
19322
|
+
toolName: invocation.toolName,
|
|
19323
|
+
actionName: row.actionName,
|
|
19324
|
+
arguments: invocation.arguments
|
|
19325
|
+
});
|
|
19326
|
+
}
|
|
19327
|
+
async function insertConnectorActionRequest(db, input) {
|
|
19328
|
+
const entry = input.resolved.entry;
|
|
19329
|
+
const [inserted] = await db.insert(connectorActionRequests).values({
|
|
19330
|
+
accountId: input.identity.accountId,
|
|
19331
|
+
workspaceId: input.identity.workspaceId,
|
|
19332
|
+
sessionId: input.identity.sessionId,
|
|
19333
|
+
turnId: input.identity.turnId,
|
|
19334
|
+
creationAttemptId: input.identity.attemptId,
|
|
19335
|
+
creationExecutionGeneration: input.identity.executionGeneration,
|
|
19336
|
+
approvalId: input.invocation.approvalId,
|
|
19337
|
+
initiatorKind: input.identity.initiator.kind,
|
|
19338
|
+
initiatorSubjectId: input.identity.initiator.subjectId,
|
|
19339
|
+
connectionId: input.invocation.connectionId,
|
|
19340
|
+
serverId: input.invocation.serverId,
|
|
19341
|
+
toolName: input.invocation.toolName,
|
|
19342
|
+
actionName: input.invocation.actionName,
|
|
19343
|
+
policyId: entry?.id ?? null,
|
|
19344
|
+
policyVersion: entry?.version ?? null,
|
|
19345
|
+
policySource: input.resolved.source,
|
|
19346
|
+
policyDecision: entry?.policy ?? "block",
|
|
19347
|
+
actionFingerprint: input.invocation.actionFingerprint,
|
|
19348
|
+
status: input.status,
|
|
19349
|
+
...input.status === "executing" ? {
|
|
19350
|
+
executionAttemptId: input.identity.attemptId,
|
|
19351
|
+
executionAttemptGeneration: input.identity.executionGeneration,
|
|
19352
|
+
executionStartedAt: /* @__PURE__ */ new Date()
|
|
19353
|
+
} : {}
|
|
19354
|
+
}).onConflictDoNothing({
|
|
19355
|
+
target: [
|
|
19356
|
+
connectorActionRequests.workspaceId,
|
|
19357
|
+
connectorActionRequests.sessionId,
|
|
19358
|
+
connectorActionRequests.turnId,
|
|
19359
|
+
connectorActionRequests.approvalId
|
|
19360
|
+
]
|
|
19361
|
+
}).returning();
|
|
19362
|
+
if (inserted) return { row: inserted, inserted: true };
|
|
19363
|
+
const [existing] = await db.select().from(connectorActionRequests).where(
|
|
19364
|
+
and11(
|
|
19365
|
+
eq11(connectorActionRequests.workspaceId, input.identity.workspaceId),
|
|
19366
|
+
eq11(connectorActionRequests.sessionId, input.identity.sessionId),
|
|
19367
|
+
eq11(connectorActionRequests.turnId, input.identity.turnId),
|
|
19368
|
+
eq11(connectorActionRequests.approvalId, input.invocation.approvalId)
|
|
19369
|
+
)
|
|
19370
|
+
).for("update").limit(1);
|
|
19371
|
+
if (!existing || !connectorActionRequestMatches(existing, input)) {
|
|
19372
|
+
throw new Error("connector action approval id conflicts with different immutable inputs");
|
|
19373
|
+
}
|
|
19374
|
+
return { row: existing, inserted: false };
|
|
19375
|
+
}
|
|
19376
|
+
async function upsertConnectorActionPolicy(db, input) {
|
|
19377
|
+
const scope = {
|
|
19378
|
+
connectionId: boundedConnectorActionText(
|
|
19379
|
+
input.connectionId,
|
|
19380
|
+
"connector connection id",
|
|
19381
|
+
CONNECTOR_ACTION_CONNECTION_ID_MAX
|
|
19382
|
+
),
|
|
19383
|
+
serverId: boundedConnectorActionText(
|
|
19384
|
+
input.serverId,
|
|
19385
|
+
"connector server id",
|
|
19386
|
+
CONNECTOR_ACTION_SERVER_ID_MAX
|
|
19387
|
+
),
|
|
19388
|
+
toolName: boundedConnectorActionText(
|
|
19389
|
+
input.toolName,
|
|
19390
|
+
"connector tool name",
|
|
19391
|
+
CONNECTOR_ACTION_NAME_MAX
|
|
19392
|
+
),
|
|
19393
|
+
actionName: boundedConnectorActionText(
|
|
19394
|
+
input.actionName,
|
|
19395
|
+
"connector action name",
|
|
19396
|
+
CONNECTOR_ACTION_NAME_MAX
|
|
19397
|
+
)
|
|
19398
|
+
};
|
|
19399
|
+
const subjectId = boundedConnectorActionText(input.subjectId, "policy actor", 1024);
|
|
19400
|
+
return await withRlsContext(
|
|
19401
|
+
db,
|
|
19402
|
+
{ accountId: input.accountId, workspaceId: input.workspaceId },
|
|
19403
|
+
async (scopedDb) => await scopedDb.transaction(async (tx) => {
|
|
19404
|
+
await assertWorkspaceAccountPairInScope(tx, input.accountId, input.workspaceId);
|
|
19405
|
+
const [existing] = await tx.select().from(connectorActionPolicies).where(
|
|
19406
|
+
and11(
|
|
19407
|
+
eq11(connectorActionPolicies.workspaceId, input.workspaceId),
|
|
19408
|
+
eq11(connectorActionPolicies.connectionId, scope.connectionId),
|
|
19409
|
+
eq11(connectorActionPolicies.serverId, scope.serverId),
|
|
19410
|
+
eq11(connectorActionPolicies.toolName, scope.toolName),
|
|
19411
|
+
eq11(connectorActionPolicies.actionName, scope.actionName)
|
|
19412
|
+
)
|
|
19413
|
+
).for("update").limit(1);
|
|
19414
|
+
if (existing?.policy === input.policy) return { policy: existing, changed: false };
|
|
19415
|
+
const now = /* @__PURE__ */ new Date();
|
|
19416
|
+
const [row] = existing ? await tx.update(connectorActionPolicies).set({
|
|
19417
|
+
policy: input.policy,
|
|
19418
|
+
version: existing.version + 1,
|
|
19419
|
+
updatedBySubjectId: subjectId,
|
|
19420
|
+
updatedAt: now
|
|
19421
|
+
}).where(eq11(connectorActionPolicies.id, existing.id)).returning() : await tx.insert(connectorActionPolicies).values({
|
|
19422
|
+
accountId: input.accountId,
|
|
19423
|
+
workspaceId: input.workspaceId,
|
|
19424
|
+
...scope,
|
|
19425
|
+
policy: input.policy,
|
|
19426
|
+
createdBySubjectId: subjectId,
|
|
19427
|
+
updatedBySubjectId: subjectId
|
|
19428
|
+
}).returning();
|
|
19429
|
+
if (!row) throw new Error("Failed to persist connector action policy");
|
|
19430
|
+
await tx.insert(auditEvents).values({
|
|
19431
|
+
accountId: input.accountId,
|
|
19432
|
+
workspaceId: input.workspaceId,
|
|
19433
|
+
subjectId,
|
|
19434
|
+
action: "connector.action.policy_changed",
|
|
19435
|
+
targetType: "connector_action_policy",
|
|
19436
|
+
targetId: row.id,
|
|
19437
|
+
metadata: {
|
|
19438
|
+
connectionId: row.connectionId,
|
|
19439
|
+
serverId: row.serverId,
|
|
19440
|
+
toolName: row.toolName,
|
|
19441
|
+
actionName: row.actionName,
|
|
19442
|
+
policy: row.policy,
|
|
19443
|
+
version: row.version,
|
|
19444
|
+
previousPolicy: existing?.policy ?? null,
|
|
19445
|
+
previousVersion: existing?.version ?? null
|
|
19446
|
+
}
|
|
19447
|
+
});
|
|
19448
|
+
return { policy: row, changed: true };
|
|
19449
|
+
})
|
|
19450
|
+
);
|
|
19451
|
+
}
|
|
19452
|
+
async function prepareConnectorActionApproval(db, identity, invocation) {
|
|
19453
|
+
const normalized = normalizedConnectorActionInvocation(identity, invocation);
|
|
19454
|
+
if (!normalized.connectionId) {
|
|
19455
|
+
return { managed: false, decision: "unmanaged" };
|
|
19456
|
+
}
|
|
19457
|
+
return await withRlsContext(
|
|
19458
|
+
db,
|
|
19459
|
+
{ accountId: identity.accountId, workspaceId: identity.workspaceId },
|
|
19460
|
+
async (scopedDb) => await scopedDb.transaction(async (tx) => {
|
|
19461
|
+
const snapshot = await connectorActionAttemptSnapshot(tx, identity);
|
|
19462
|
+
const resolved = resolveConnectorActionPolicy(snapshot, {
|
|
19463
|
+
connectionId: normalized.connectionId,
|
|
19464
|
+
serverId: normalized.serverId,
|
|
19465
|
+
toolName: normalized.toolName,
|
|
19466
|
+
actionName: normalized.policyActionSelector
|
|
19467
|
+
});
|
|
19468
|
+
if (!resolved.managed) return { managed: false, decision: "unmanaged" };
|
|
19469
|
+
const durable = durableConnectorActionInvocation(
|
|
19470
|
+
identity,
|
|
19471
|
+
{ ...normalized, connectionId: normalized.connectionId },
|
|
19472
|
+
resolved
|
|
19473
|
+
);
|
|
19474
|
+
const decision = resolved.entry?.policy ?? "block";
|
|
19475
|
+
if (decision === "allow") {
|
|
19476
|
+
return {
|
|
19477
|
+
managed: true,
|
|
19478
|
+
decision,
|
|
19479
|
+
actionFingerprint: durable.actionFingerprint
|
|
19480
|
+
};
|
|
19481
|
+
}
|
|
19482
|
+
const { row, inserted } = await insertConnectorActionRequest(tx, {
|
|
19483
|
+
identity,
|
|
19484
|
+
invocation: durable,
|
|
19485
|
+
resolved,
|
|
19486
|
+
status: decision === "ask" ? "pending" : "blocked"
|
|
19487
|
+
});
|
|
19488
|
+
if (inserted) {
|
|
19489
|
+
await insertConnectorActionAudit(tx, {
|
|
19490
|
+
row,
|
|
19491
|
+
action: decision === "ask" ? "connector.action.approval_requested" : "connector.action.blocked",
|
|
19492
|
+
subjectId: identity.initiator.subjectId,
|
|
19493
|
+
extra: { outcome: decision }
|
|
19494
|
+
});
|
|
19495
|
+
}
|
|
19496
|
+
return {
|
|
19497
|
+
managed: true,
|
|
19498
|
+
decision,
|
|
19499
|
+
requestId: row.id,
|
|
19500
|
+
actionFingerprint: row.actionFingerprint
|
|
19501
|
+
};
|
|
19502
|
+
})
|
|
19503
|
+
);
|
|
19504
|
+
}
|
|
19505
|
+
async function beginConnectorActionExecution(db, identity, invocation) {
|
|
19506
|
+
const normalized = normalizedConnectorActionInvocation(identity, invocation);
|
|
19507
|
+
if (!normalized.connectionId) {
|
|
19508
|
+
return { allowed: true, managed: false };
|
|
19509
|
+
}
|
|
19510
|
+
return await withRlsContext(
|
|
19511
|
+
db,
|
|
19512
|
+
{ accountId: identity.accountId, workspaceId: identity.workspaceId },
|
|
19513
|
+
async (scopedDb) => await scopedDb.transaction(async (tx) => {
|
|
19514
|
+
const snapshot = await connectorActionAttemptSnapshot(tx, identity);
|
|
19515
|
+
const [existing] = await tx.select().from(connectorActionRequests).where(
|
|
19516
|
+
and11(
|
|
19517
|
+
eq11(connectorActionRequests.workspaceId, identity.workspaceId),
|
|
19518
|
+
eq11(connectorActionRequests.sessionId, identity.sessionId),
|
|
19519
|
+
eq11(connectorActionRequests.turnId, identity.turnId),
|
|
19520
|
+
eq11(connectorActionRequests.approvalId, normalized.approvalId)
|
|
19521
|
+
)
|
|
19522
|
+
).for("update").limit(1);
|
|
19523
|
+
if (existing && !connectorActionRequestMatchesLogicalCall(existing, identity, normalized)) {
|
|
19524
|
+
throw new Error("connector action approval id conflicts with different immutable inputs");
|
|
19525
|
+
}
|
|
19526
|
+
let row = existing;
|
|
19527
|
+
let inserted = false;
|
|
19528
|
+
if (!row) {
|
|
19529
|
+
const resolved = resolveConnectorActionPolicy(snapshot, {
|
|
19530
|
+
connectionId: normalized.connectionId,
|
|
19531
|
+
serverId: normalized.serverId,
|
|
19532
|
+
toolName: normalized.toolName,
|
|
19533
|
+
actionName: normalized.policyActionSelector
|
|
19534
|
+
});
|
|
19535
|
+
if (!resolved.managed) return { allowed: true, managed: false };
|
|
19536
|
+
const durable = durableConnectorActionInvocation(
|
|
19537
|
+
identity,
|
|
19538
|
+
{ ...normalized, connectionId: normalized.connectionId },
|
|
19539
|
+
resolved
|
|
19540
|
+
);
|
|
19541
|
+
const decision = resolved.entry?.policy ?? "block";
|
|
19542
|
+
const created = await insertConnectorActionRequest(tx, {
|
|
19543
|
+
identity,
|
|
19544
|
+
invocation: durable,
|
|
19545
|
+
resolved,
|
|
19546
|
+
status: decision === "ask" ? "pending" : decision === "block" ? "blocked" : "executing"
|
|
19547
|
+
});
|
|
19548
|
+
row = created.row;
|
|
19549
|
+
inserted = created.inserted;
|
|
19550
|
+
if (inserted) {
|
|
19551
|
+
await insertConnectorActionAudit(tx, {
|
|
19552
|
+
row,
|
|
19553
|
+
action: decision === "ask" ? "connector.action.approval_requested" : decision === "block" ? "connector.action.blocked" : "connector.action.execution_started",
|
|
19554
|
+
subjectId: identity.initiator.subjectId,
|
|
19555
|
+
extra: { outcome: decision === "allow" ? "started" : decision }
|
|
19556
|
+
});
|
|
19557
|
+
}
|
|
19558
|
+
}
|
|
19559
|
+
if (row.status === "approved") {
|
|
19560
|
+
const [executing] = await tx.update(connectorActionRequests).set({
|
|
19561
|
+
status: "executing",
|
|
19562
|
+
executionAttemptId: identity.attemptId,
|
|
19563
|
+
executionAttemptGeneration: identity.executionGeneration,
|
|
19564
|
+
executionStartedAt: /* @__PURE__ */ new Date(),
|
|
19565
|
+
updatedAt: /* @__PURE__ */ new Date()
|
|
19566
|
+
}).where(eq11(connectorActionRequests.id, row.id)).returning();
|
|
19567
|
+
if (!executing) throw new Error("Approved connector action request disappeared");
|
|
19568
|
+
row = executing;
|
|
19569
|
+
await insertConnectorActionAudit(tx, {
|
|
19570
|
+
row,
|
|
19571
|
+
action: "connector.action.execution_started",
|
|
19572
|
+
subjectId: identity.initiator.subjectId,
|
|
19573
|
+
extra: { outcome: "started" }
|
|
19574
|
+
});
|
|
19575
|
+
return {
|
|
19576
|
+
allowed: true,
|
|
19577
|
+
managed: true,
|
|
19578
|
+
requestId: row.id,
|
|
19579
|
+
actionFingerprint: row.actionFingerprint
|
|
19580
|
+
};
|
|
19581
|
+
}
|
|
19582
|
+
if (row.status === "executing") {
|
|
19583
|
+
if (inserted) {
|
|
19584
|
+
return {
|
|
19585
|
+
allowed: true,
|
|
19586
|
+
managed: true,
|
|
19587
|
+
requestId: row.id,
|
|
19588
|
+
actionFingerprint: row.actionFingerprint
|
|
19589
|
+
};
|
|
19590
|
+
}
|
|
19591
|
+
const [uncertain] = await tx.update(connectorActionRequests).set({
|
|
19592
|
+
status: "uncertain",
|
|
19593
|
+
outcome: "retry_after_execution_started",
|
|
19594
|
+
executionFinishedAt: /* @__PURE__ */ new Date(),
|
|
19595
|
+
updatedAt: /* @__PURE__ */ new Date()
|
|
19596
|
+
}).where(eq11(connectorActionRequests.id, row.id)).returning();
|
|
19597
|
+
if (!uncertain) throw new Error("Executing connector action request disappeared");
|
|
19598
|
+
await insertConnectorActionAudit(tx, {
|
|
19599
|
+
row: uncertain,
|
|
19600
|
+
action: "connector.action.execution_uncertain",
|
|
19601
|
+
subjectId: identity.initiator.subjectId,
|
|
19602
|
+
extra: { outcome: "retry_denied" }
|
|
19603
|
+
});
|
|
19604
|
+
return {
|
|
19605
|
+
allowed: false,
|
|
19606
|
+
managed: true,
|
|
19607
|
+
reason: "uncertain_retry",
|
|
19608
|
+
requestId: uncertain.id,
|
|
19609
|
+
actionFingerprint: uncertain.actionFingerprint
|
|
19610
|
+
};
|
|
19611
|
+
}
|
|
19612
|
+
const reason = row.status === "pending" ? "approval_required" : row.status === "rejected" ? "rejected" : row.status === "blocked" ? "blocked" : "already_executed";
|
|
19613
|
+
return {
|
|
19614
|
+
allowed: false,
|
|
19615
|
+
managed: true,
|
|
19616
|
+
reason,
|
|
19617
|
+
requestId: row.id,
|
|
19618
|
+
actionFingerprint: row.actionFingerprint
|
|
19619
|
+
};
|
|
19620
|
+
})
|
|
19621
|
+
);
|
|
19622
|
+
}
|
|
19623
|
+
async function completeConnectorActionExecution(db, input) {
|
|
19624
|
+
await withRlsContext(
|
|
19625
|
+
db,
|
|
19626
|
+
{ accountId: input.accountId, workspaceId: input.workspaceId },
|
|
19627
|
+
async (scopedDb) => await scopedDb.transaction(async (tx) => {
|
|
19628
|
+
const [existing] = await tx.select().from(connectorActionRequests).where(
|
|
19629
|
+
and11(
|
|
19630
|
+
eq11(connectorActionRequests.workspaceId, input.workspaceId),
|
|
19631
|
+
eq11(connectorActionRequests.id, input.requestId),
|
|
19632
|
+
eq11(connectorActionRequests.executionAttemptId, input.attemptId)
|
|
19633
|
+
)
|
|
19634
|
+
).for("update").limit(1);
|
|
19635
|
+
if (!existing) throw new Error("Connector action request not found for completion");
|
|
19636
|
+
if (existing.status === input.outcome) return;
|
|
19637
|
+
if (existing.status !== "executing") {
|
|
19638
|
+
throw new Error(`Connector action request cannot complete from ${existing.status}`);
|
|
19639
|
+
}
|
|
19640
|
+
const [row] = await tx.update(connectorActionRequests).set({
|
|
19641
|
+
status: input.outcome,
|
|
19642
|
+
outcome: input.outcome,
|
|
19643
|
+
executionFinishedAt: /* @__PURE__ */ new Date(),
|
|
19644
|
+
updatedAt: /* @__PURE__ */ new Date()
|
|
19645
|
+
}).where(eq11(connectorActionRequests.id, existing.id)).returning();
|
|
19646
|
+
if (!row) throw new Error("Connector action request disappeared during completion");
|
|
19647
|
+
await insertConnectorActionAudit(tx, {
|
|
19648
|
+
row,
|
|
19649
|
+
action: input.outcome === "completed" ? "connector.action.execution_completed" : "connector.action.execution_uncertain",
|
|
19650
|
+
subjectId: row.initiatorSubjectId,
|
|
19651
|
+
extra: { outcome: input.outcome }
|
|
19652
|
+
});
|
|
19653
|
+
})
|
|
19654
|
+
);
|
|
19655
|
+
}
|
|
19120
19656
|
async function getNestedAgentDepthDeploymentPolicy(db) {
|
|
19121
19657
|
const [row] = await db.select({
|
|
19122
19658
|
maxNestedAgentDepth: nestedAgentDepthConfiguration.maxNestedAgentDepth,
|
|
@@ -31178,6 +31714,24 @@ async function claimSessionWorkForAttempt(db, workspaceId, input) {
|
|
|
31178
31714
|
const mcpApprovalPolicies = Object.fromEntries(
|
|
31179
31715
|
policyRows.map((row2) => [row2.serverId, row2.requireApproval ?? false])
|
|
31180
31716
|
);
|
|
31717
|
+
const connectorPolicyRows = await tx.select({
|
|
31718
|
+
id: connectorActionPolicies.id,
|
|
31719
|
+
connectionId: connectorActionPolicies.connectionId,
|
|
31720
|
+
serverId: connectorActionPolicies.serverId,
|
|
31721
|
+
toolName: connectorActionPolicies.toolName,
|
|
31722
|
+
actionName: connectorActionPolicies.actionName,
|
|
31723
|
+
policy: connectorActionPolicies.policy,
|
|
31724
|
+
version: connectorActionPolicies.version
|
|
31725
|
+
}).from(connectorActionPolicies).where(eq11(connectorActionPolicies.workspaceId, workspaceId)).orderBy(
|
|
31726
|
+
asc5(connectorActionPolicies.connectionId),
|
|
31727
|
+
asc5(connectorActionPolicies.serverId),
|
|
31728
|
+
asc5(connectorActionPolicies.toolName),
|
|
31729
|
+
asc5(connectorActionPolicies.actionName),
|
|
31730
|
+
asc5(connectorActionPolicies.id)
|
|
31731
|
+
).limit(2049);
|
|
31732
|
+
if (connectorPolicyRows.length > 2048) {
|
|
31733
|
+
throw new Error("Connector action policy snapshot exceeds the 2048-row bound");
|
|
31734
|
+
}
|
|
31181
31735
|
return await registerSessionTurnAttemptClaim(tx, {
|
|
31182
31736
|
id: input.attemptId,
|
|
31183
31737
|
accountId: session.accountId,
|
|
@@ -31189,7 +31743,8 @@ async function claimSessionWorkForAttempt(db, workspaceId, input) {
|
|
|
31189
31743
|
temporalWorkflowRunId: input.workflowRunId,
|
|
31190
31744
|
temporalActivityId: input.dispatchId,
|
|
31191
31745
|
verifiedControlRevision: Number(workspaceControl.revision),
|
|
31192
|
-
mcpApprovalPolicies
|
|
31746
|
+
mcpApprovalPolicies,
|
|
31747
|
+
connectorActionPolicies: connectorPolicyRows
|
|
31193
31748
|
});
|
|
31194
31749
|
};
|
|
31195
31750
|
if (session.activeTurnId !== null) {
|
|
@@ -34918,6 +35473,37 @@ async function acceptSessionApprovalDecision(db, input) {
|
|
|
34918
35473
|
clientEventId: input.clientEventId ?? null
|
|
34919
35474
|
}).returning();
|
|
34920
35475
|
if (!event) throw new Error("Failed to append approval decision");
|
|
35476
|
+
const approvalDecision = input.payload.decision;
|
|
35477
|
+
if (approvalDecision === "approve" || approvalDecision === "reject") {
|
|
35478
|
+
const [connectorRequest] = await tx.update(connectorActionRequests).set({
|
|
35479
|
+
status: approvalDecision === "approve" ? "approved" : "rejected",
|
|
35480
|
+
decision: approvalDecision,
|
|
35481
|
+
decisionBySubjectId: boundedConnectorActionText(
|
|
35482
|
+
input.subjectId,
|
|
35483
|
+
"approval actor",
|
|
35484
|
+
1024
|
|
35485
|
+
),
|
|
35486
|
+
decisionEventId: event.id,
|
|
35487
|
+
decidedAt: event.occurredAt,
|
|
35488
|
+
updatedAt: /* @__PURE__ */ new Date()
|
|
35489
|
+
}).where(
|
|
35490
|
+
and11(
|
|
35491
|
+
eq11(connectorActionRequests.workspaceId, input.workspaceId),
|
|
35492
|
+
eq11(connectorActionRequests.sessionId, input.sessionId),
|
|
35493
|
+
eq11(connectorActionRequests.turnId, turn.id),
|
|
35494
|
+
eq11(connectorActionRequests.approvalId, approvalId),
|
|
35495
|
+
eq11(connectorActionRequests.status, "pending")
|
|
35496
|
+
)
|
|
35497
|
+
).returning();
|
|
35498
|
+
if (connectorRequest) {
|
|
35499
|
+
await insertConnectorActionAudit(tx, {
|
|
35500
|
+
row: connectorRequest,
|
|
35501
|
+
action: "connector.action.approval_decided",
|
|
35502
|
+
subjectId: input.subjectId,
|
|
35503
|
+
extra: { decision: approvalDecision }
|
|
35504
|
+
});
|
|
35505
|
+
}
|
|
35506
|
+
}
|
|
34921
35507
|
await tx.update(sessions).set({
|
|
34922
35508
|
lastSequence: session.lastSequence + 1,
|
|
34923
35509
|
updatedAt: /* @__PURE__ */ new Date()
|
|
@@ -36214,6 +36800,7 @@ export {
|
|
|
36214
36800
|
attachKnowledgeEntityAlias,
|
|
36215
36801
|
autoResumeSessionBranchInTransaction,
|
|
36216
36802
|
backfillModelCallFactsFromSessionEvents,
|
|
36803
|
+
beginConnectorActionExecution,
|
|
36217
36804
|
beginKnowledgeSyncRun,
|
|
36218
36805
|
beginRigChangeVerificationAttempt,
|
|
36219
36806
|
beginSandboxRematerialization,
|
|
@@ -36257,6 +36844,7 @@ export {
|
|
|
36257
36844
|
codexCapacityRefreshBackoffMs,
|
|
36258
36845
|
commitWarmingToWarm,
|
|
36259
36846
|
completeCodexResetRedemption,
|
|
36847
|
+
completeConnectorActionExecution,
|
|
36260
36848
|
completeExpiredFileUploadCleanup,
|
|
36261
36849
|
completeFileUpload,
|
|
36262
36850
|
completeFileUploadCleanup,
|
|
@@ -36586,6 +37174,7 @@ export {
|
|
|
36586
37174
|
loadSocialConnectionCredential,
|
|
36587
37175
|
loadVariableSetForRun,
|
|
36588
37176
|
loadWorkspaceEnvironmentForRun,
|
|
37177
|
+
loadWorkspaceVercelAiGatewayApiKey,
|
|
36589
37178
|
lockSessionEventWriteRows,
|
|
36590
37179
|
lockWorkspaceInferenceControl,
|
|
36591
37180
|
markFileUploadFailed,
|
|
@@ -36627,6 +37216,7 @@ export {
|
|
|
36627
37216
|
persistDrainSnapshot,
|
|
36628
37217
|
persistWarmSnapshot,
|
|
36629
37218
|
planWorkspaceCaptureGc,
|
|
37219
|
+
prepareConnectorActionApproval,
|
|
36630
37220
|
previewColdLostLeaseInstanceBlockers,
|
|
36631
37221
|
projectEffectiveControlForRelatedAccess,
|
|
36632
37222
|
projectSessionForRelatedAccess,
|
|
@@ -36708,6 +37298,7 @@ export {
|
|
|
36708
37298
|
requireWorkspace,
|
|
36709
37299
|
reserveSessionCommandReceipt,
|
|
36710
37300
|
reserveToolspaceCallForAttempt,
|
|
37301
|
+
resolveConnectorActionPolicy,
|
|
36711
37302
|
resolveSlackInstallationRoute,
|
|
36712
37303
|
resolveWorkspaceMemoryBlock,
|
|
36713
37304
|
restoreKnowledgeSourceObject,
|
|
@@ -36823,6 +37414,7 @@ export {
|
|
|
36823
37414
|
upsertBillingCustomer,
|
|
36824
37415
|
upsertCapabilityCatalogItem,
|
|
36825
37416
|
upsertCodexSubscriptionCredential,
|
|
37417
|
+
upsertConnectorActionPolicy,
|
|
36826
37418
|
upsertGitHubInstallation,
|
|
36827
37419
|
upsertKnowledgeEntity,
|
|
36828
37420
|
upsertKnowledgeFact,
|
|
@@ -36849,6 +37441,7 @@ export {
|
|
|
36849
37441
|
withWorkspaceSubjectRls,
|
|
36850
37442
|
withWorkspaceUsageLock,
|
|
36851
37443
|
workspaceCaptureAtRevision,
|
|
36852
|
-
workspaceCodexSubscriptionActive
|
|
37444
|
+
workspaceCodexSubscriptionActive,
|
|
37445
|
+
workspaceVercelAiGatewayConnectionActive
|
|
36853
37446
|
};
|
|
36854
37447
|
//# sourceMappingURL=index.js.map
|