@opengeni/db 0.21.0 → 0.22.1

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/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-NK2A36KP.js";
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-OTJHD33E.js";
130
+ } from "./chunk-BNGEN5QZ.js";
129
131
  import "./chunk-PZ5AY32C.js";
130
132
 
131
133
  // src/index.ts
@@ -1397,7 +1399,7 @@ async function registerSessionTurnAttemptClaim(db, input) {
1397
1399
  eq4(sessionTurnAttempts.id, input.id)
1398
1400
  )
1399
1401
  ).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") {
1402
+ 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
1403
  throw new SessionControlInvariantError(
1402
1404
  `Attempt ${input.id} conflicts with a different or closed ownership chain`
1403
1405
  );
@@ -19117,6 +19119,510 @@ async function listSessionMcpServersForRun(db, workspaceId, sessionId, attemptId
19117
19119
  });
19118
19120
  });
19119
19121
  }
19122
+ var CONNECTOR_ACTION_POLICY_SNAPSHOT_MAX = 2048;
19123
+ var CONNECTOR_ACTION_APPROVAL_ID_MAX = 1024;
19124
+ var CONNECTOR_ACTION_CONNECTION_ID_MAX = 512;
19125
+ var CONNECTOR_ACTION_SERVER_ID_MAX = 256;
19126
+ var CONNECTOR_ACTION_NAME_MAX = 512;
19127
+ function boundedConnectorActionText(value, label, max) {
19128
+ const trimmed = value.trim();
19129
+ if (trimmed.length === 0 || Buffer.byteLength(trimmed, "utf8") > max) {
19130
+ throw new Error(`${label} must be between 1 and ${max} UTF-8 bytes`);
19131
+ }
19132
+ return trimmed;
19133
+ }
19134
+ function connectorActionPolicySelector(toolName, args) {
19135
+ if (args && typeof args === "object" && !Array.isArray(args)) {
19136
+ const action = args.action;
19137
+ if (typeof action === "string" && action.trim().length > 0) {
19138
+ return boundedConnectorActionText(action, "connector action name", CONNECTOR_ACTION_NAME_MAX);
19139
+ }
19140
+ }
19141
+ return toolName;
19142
+ }
19143
+ function connectorActionEvidenceName(resolved) {
19144
+ return resolved.entry?.actionName ?? "*";
19145
+ }
19146
+ function connectorActionFingerprint(input) {
19147
+ return createHash6("sha256").update(
19148
+ stableJson3({
19149
+ workspaceId: input.workspaceId,
19150
+ connectionId: input.connectionId,
19151
+ serverId: input.serverId,
19152
+ toolName: input.toolName,
19153
+ actionName: input.actionName,
19154
+ arguments: input.arguments ?? null
19155
+ }),
19156
+ "utf8"
19157
+ ).digest("hex");
19158
+ }
19159
+ function resolveConnectorActionPolicy(snapshot, input) {
19160
+ const candidates = snapshot.filter(
19161
+ (entry) => entry.connectionId === input.connectionId && (entry.serverId === input.serverId || entry.serverId === "*") && (entry.toolName === input.toolName || entry.toolName === "*") && (entry.actionName === input.actionName || entry.actionName === "*")
19162
+ ).map((entry) => ({
19163
+ entry,
19164
+ specificity: Number(entry.serverId !== "*") + Number(entry.toolName !== "*") + Number(entry.actionName !== "*")
19165
+ })).sort(
19166
+ (left, right) => right.specificity - left.specificity || left.entry.id.localeCompare(right.entry.id)
19167
+ );
19168
+ const selected = candidates[0];
19169
+ if (!selected) return { managed: false };
19170
+ if (candidates[1]?.specificity === selected.specificity) {
19171
+ return { managed: true, source: "ambiguous", entry: null, decision: "block" };
19172
+ }
19173
+ return { managed: true, source: "explicit", entry: selected.entry };
19174
+ }
19175
+ function connectorActionAuditMetadata(row, extra = {}) {
19176
+ return {
19177
+ requestId: row.id,
19178
+ sessionId: row.sessionId,
19179
+ turnId: row.turnId,
19180
+ attemptId: row.creationAttemptId,
19181
+ creationExecutionGeneration: row.creationExecutionGeneration,
19182
+ executionAttemptId: row.executionAttemptId,
19183
+ executionAttemptGeneration: row.executionAttemptGeneration,
19184
+ approvalId: row.approvalId,
19185
+ initiatorKind: row.initiatorKind,
19186
+ initiatorSubjectId: row.initiatorSubjectId,
19187
+ connectionId: row.connectionId,
19188
+ connectionVersion: row.connectionVersion,
19189
+ serverId: row.serverId,
19190
+ toolName: row.toolName,
19191
+ actionName: row.actionName,
19192
+ policyId: row.policyId,
19193
+ policyVersion: row.policyVersion,
19194
+ policySource: row.policySource,
19195
+ policyDecision: row.policyDecision,
19196
+ actionFingerprint: row.actionFingerprint,
19197
+ ...extra
19198
+ };
19199
+ }
19200
+ async function insertConnectorActionAudit(db, input) {
19201
+ await db.insert(auditEvents).values({
19202
+ accountId: input.row.accountId,
19203
+ workspaceId: input.row.workspaceId,
19204
+ subjectId: input.subjectId,
19205
+ action: input.action,
19206
+ targetType: "connector_action_request",
19207
+ targetId: input.row.id,
19208
+ metadata: connectorActionAuditMetadata(input.row, input.extra)
19209
+ });
19210
+ }
19211
+ function normalizedConnectorActionInvocation(identity, invocation) {
19212
+ const approvalId = boundedConnectorActionText(
19213
+ invocation.approvalId,
19214
+ "connector approval id",
19215
+ CONNECTOR_ACTION_APPROVAL_ID_MAX
19216
+ );
19217
+ const serverId = boundedConnectorActionText(
19218
+ invocation.serverId,
19219
+ "connector server id",
19220
+ CONNECTOR_ACTION_SERVER_ID_MAX
19221
+ );
19222
+ const toolName = boundedConnectorActionText(
19223
+ invocation.toolName,
19224
+ "connector tool name",
19225
+ CONNECTOR_ACTION_NAME_MAX
19226
+ );
19227
+ const policyActionSelector = connectorActionPolicySelector(toolName, invocation.arguments);
19228
+ const connectionId = invocation.connectionId?.trim() ? boundedConnectorActionText(
19229
+ invocation.connectionId,
19230
+ "connector connection id",
19231
+ CONNECTOR_ACTION_CONNECTION_ID_MAX
19232
+ ) : null;
19233
+ return {
19234
+ approvalId,
19235
+ connectionId,
19236
+ serverId,
19237
+ toolName,
19238
+ policyActionSelector,
19239
+ arguments: invocation.arguments
19240
+ };
19241
+ }
19242
+ function durableConnectorActionInvocation(identity, invocation, resolved) {
19243
+ const actionName = connectorActionEvidenceName(resolved);
19244
+ return {
19245
+ approvalId: invocation.approvalId,
19246
+ connectionId: invocation.connectionId,
19247
+ serverId: invocation.serverId,
19248
+ toolName: invocation.toolName,
19249
+ actionName,
19250
+ actionFingerprint: connectorActionFingerprint({
19251
+ workspaceId: identity.workspaceId,
19252
+ connectionId: invocation.connectionId,
19253
+ serverId: invocation.serverId,
19254
+ toolName: invocation.toolName,
19255
+ actionName,
19256
+ arguments: invocation.arguments
19257
+ })
19258
+ };
19259
+ }
19260
+ async function connectorActionAttemptSnapshot(db, identity) {
19261
+ const [attempt] = await db.select({
19262
+ accountId: sessionTurnAttempts.accountId,
19263
+ sessionId: sessionTurnAttempts.sessionId,
19264
+ turnId: sessionTurnAttempts.turnId,
19265
+ executionGeneration: sessionTurnAttempts.executionGeneration,
19266
+ state: sessionTurnAttempts.state,
19267
+ connectorActionPolicies: sessionTurnAttempts.connectorActionPolicies
19268
+ }).from(sessionTurnAttempts).where(
19269
+ and11(
19270
+ eq11(sessionTurnAttempts.workspaceId, identity.workspaceId),
19271
+ eq11(sessionTurnAttempts.id, identity.attemptId)
19272
+ )
19273
+ ).limit(1);
19274
+ if (!attempt || attempt.accountId !== identity.accountId || attempt.sessionId !== identity.sessionId || attempt.turnId !== identity.turnId || attempt.executionGeneration !== identity.executionGeneration || !["claimed", "running"].includes(attempt.state)) {
19275
+ throw new Error(`connector action attempt ownership is unavailable: ${identity.attemptId}`);
19276
+ }
19277
+ if (attempt.connectorActionPolicies.length > CONNECTOR_ACTION_POLICY_SNAPSHOT_MAX) {
19278
+ throw new Error("connector action policy snapshot exceeds the runtime bound");
19279
+ }
19280
+ return attempt.connectorActionPolicies;
19281
+ }
19282
+ function connectorActionRequestMatches(row, input) {
19283
+ const entry = input.resolved.entry;
19284
+ 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;
19285
+ }
19286
+ function connectorActionRequestMatchesLogicalCall(row, identity, invocation) {
19287
+ if (!invocation.connectionId) return false;
19288
+ 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({
19289
+ workspaceId: identity.workspaceId,
19290
+ connectionId: invocation.connectionId,
19291
+ serverId: invocation.serverId,
19292
+ toolName: invocation.toolName,
19293
+ actionName: row.actionName,
19294
+ arguments: invocation.arguments
19295
+ });
19296
+ }
19297
+ async function insertConnectorActionRequest(db, input) {
19298
+ const entry = input.resolved.entry;
19299
+ const [inserted] = await db.insert(connectorActionRequests).values({
19300
+ accountId: input.identity.accountId,
19301
+ workspaceId: input.identity.workspaceId,
19302
+ sessionId: input.identity.sessionId,
19303
+ turnId: input.identity.turnId,
19304
+ creationAttemptId: input.identity.attemptId,
19305
+ creationExecutionGeneration: input.identity.executionGeneration,
19306
+ approvalId: input.invocation.approvalId,
19307
+ initiatorKind: input.identity.initiator.kind,
19308
+ initiatorSubjectId: input.identity.initiator.subjectId,
19309
+ connectionId: input.invocation.connectionId,
19310
+ serverId: input.invocation.serverId,
19311
+ toolName: input.invocation.toolName,
19312
+ actionName: input.invocation.actionName,
19313
+ policyId: entry?.id ?? null,
19314
+ policyVersion: entry?.version ?? null,
19315
+ policySource: input.resolved.source,
19316
+ policyDecision: entry?.policy ?? "block",
19317
+ actionFingerprint: input.invocation.actionFingerprint,
19318
+ status: input.status,
19319
+ ...input.status === "executing" ? {
19320
+ executionAttemptId: input.identity.attemptId,
19321
+ executionAttemptGeneration: input.identity.executionGeneration,
19322
+ executionStartedAt: /* @__PURE__ */ new Date()
19323
+ } : {}
19324
+ }).onConflictDoNothing({
19325
+ target: [
19326
+ connectorActionRequests.workspaceId,
19327
+ connectorActionRequests.sessionId,
19328
+ connectorActionRequests.turnId,
19329
+ connectorActionRequests.approvalId
19330
+ ]
19331
+ }).returning();
19332
+ if (inserted) return { row: inserted, inserted: true };
19333
+ const [existing] = await db.select().from(connectorActionRequests).where(
19334
+ and11(
19335
+ eq11(connectorActionRequests.workspaceId, input.identity.workspaceId),
19336
+ eq11(connectorActionRequests.sessionId, input.identity.sessionId),
19337
+ eq11(connectorActionRequests.turnId, input.identity.turnId),
19338
+ eq11(connectorActionRequests.approvalId, input.invocation.approvalId)
19339
+ )
19340
+ ).for("update").limit(1);
19341
+ if (!existing || !connectorActionRequestMatches(existing, input)) {
19342
+ throw new Error("connector action approval id conflicts with different immutable inputs");
19343
+ }
19344
+ return { row: existing, inserted: false };
19345
+ }
19346
+ async function upsertConnectorActionPolicy(db, input) {
19347
+ const scope = {
19348
+ connectionId: boundedConnectorActionText(
19349
+ input.connectionId,
19350
+ "connector connection id",
19351
+ CONNECTOR_ACTION_CONNECTION_ID_MAX
19352
+ ),
19353
+ serverId: boundedConnectorActionText(
19354
+ input.serverId,
19355
+ "connector server id",
19356
+ CONNECTOR_ACTION_SERVER_ID_MAX
19357
+ ),
19358
+ toolName: boundedConnectorActionText(
19359
+ input.toolName,
19360
+ "connector tool name",
19361
+ CONNECTOR_ACTION_NAME_MAX
19362
+ ),
19363
+ actionName: boundedConnectorActionText(
19364
+ input.actionName,
19365
+ "connector action name",
19366
+ CONNECTOR_ACTION_NAME_MAX
19367
+ )
19368
+ };
19369
+ const subjectId = boundedConnectorActionText(input.subjectId, "policy actor", 1024);
19370
+ return await withRlsContext(
19371
+ db,
19372
+ { accountId: input.accountId, workspaceId: input.workspaceId },
19373
+ async (scopedDb) => await scopedDb.transaction(async (tx) => {
19374
+ await assertWorkspaceAccountPairInScope(tx, input.accountId, input.workspaceId);
19375
+ const [existing] = await tx.select().from(connectorActionPolicies).where(
19376
+ and11(
19377
+ eq11(connectorActionPolicies.workspaceId, input.workspaceId),
19378
+ eq11(connectorActionPolicies.connectionId, scope.connectionId),
19379
+ eq11(connectorActionPolicies.serverId, scope.serverId),
19380
+ eq11(connectorActionPolicies.toolName, scope.toolName),
19381
+ eq11(connectorActionPolicies.actionName, scope.actionName)
19382
+ )
19383
+ ).for("update").limit(1);
19384
+ if (existing?.policy === input.policy) return { policy: existing, changed: false };
19385
+ const now = /* @__PURE__ */ new Date();
19386
+ const [row] = existing ? await tx.update(connectorActionPolicies).set({
19387
+ policy: input.policy,
19388
+ version: existing.version + 1,
19389
+ updatedBySubjectId: subjectId,
19390
+ updatedAt: now
19391
+ }).where(eq11(connectorActionPolicies.id, existing.id)).returning() : await tx.insert(connectorActionPolicies).values({
19392
+ accountId: input.accountId,
19393
+ workspaceId: input.workspaceId,
19394
+ ...scope,
19395
+ policy: input.policy,
19396
+ createdBySubjectId: subjectId,
19397
+ updatedBySubjectId: subjectId
19398
+ }).returning();
19399
+ if (!row) throw new Error("Failed to persist connector action policy");
19400
+ await tx.insert(auditEvents).values({
19401
+ accountId: input.accountId,
19402
+ workspaceId: input.workspaceId,
19403
+ subjectId,
19404
+ action: "connector.action.policy_changed",
19405
+ targetType: "connector_action_policy",
19406
+ targetId: row.id,
19407
+ metadata: {
19408
+ connectionId: row.connectionId,
19409
+ serverId: row.serverId,
19410
+ toolName: row.toolName,
19411
+ actionName: row.actionName,
19412
+ policy: row.policy,
19413
+ version: row.version,
19414
+ previousPolicy: existing?.policy ?? null,
19415
+ previousVersion: existing?.version ?? null
19416
+ }
19417
+ });
19418
+ return { policy: row, changed: true };
19419
+ })
19420
+ );
19421
+ }
19422
+ async function prepareConnectorActionApproval(db, identity, invocation) {
19423
+ const normalized = normalizedConnectorActionInvocation(identity, invocation);
19424
+ if (!normalized.connectionId) {
19425
+ return { managed: false, decision: "unmanaged" };
19426
+ }
19427
+ return await withRlsContext(
19428
+ db,
19429
+ { accountId: identity.accountId, workspaceId: identity.workspaceId },
19430
+ async (scopedDb) => await scopedDb.transaction(async (tx) => {
19431
+ const snapshot = await connectorActionAttemptSnapshot(tx, identity);
19432
+ const resolved = resolveConnectorActionPolicy(snapshot, {
19433
+ connectionId: normalized.connectionId,
19434
+ serverId: normalized.serverId,
19435
+ toolName: normalized.toolName,
19436
+ actionName: normalized.policyActionSelector
19437
+ });
19438
+ if (!resolved.managed) return { managed: false, decision: "unmanaged" };
19439
+ const durable = durableConnectorActionInvocation(
19440
+ identity,
19441
+ { ...normalized, connectionId: normalized.connectionId },
19442
+ resolved
19443
+ );
19444
+ const decision = resolved.entry?.policy ?? "block";
19445
+ if (decision === "allow") {
19446
+ return {
19447
+ managed: true,
19448
+ decision,
19449
+ actionFingerprint: durable.actionFingerprint
19450
+ };
19451
+ }
19452
+ const { row, inserted } = await insertConnectorActionRequest(tx, {
19453
+ identity,
19454
+ invocation: durable,
19455
+ resolved,
19456
+ status: decision === "ask" ? "pending" : "blocked"
19457
+ });
19458
+ if (inserted) {
19459
+ await insertConnectorActionAudit(tx, {
19460
+ row,
19461
+ action: decision === "ask" ? "connector.action.approval_requested" : "connector.action.blocked",
19462
+ subjectId: identity.initiator.subjectId,
19463
+ extra: { outcome: decision }
19464
+ });
19465
+ }
19466
+ return {
19467
+ managed: true,
19468
+ decision,
19469
+ requestId: row.id,
19470
+ actionFingerprint: row.actionFingerprint
19471
+ };
19472
+ })
19473
+ );
19474
+ }
19475
+ async function beginConnectorActionExecution(db, identity, invocation) {
19476
+ const normalized = normalizedConnectorActionInvocation(identity, invocation);
19477
+ if (!normalized.connectionId) {
19478
+ return { allowed: true, managed: false };
19479
+ }
19480
+ return await withRlsContext(
19481
+ db,
19482
+ { accountId: identity.accountId, workspaceId: identity.workspaceId },
19483
+ async (scopedDb) => await scopedDb.transaction(async (tx) => {
19484
+ const snapshot = await connectorActionAttemptSnapshot(tx, identity);
19485
+ const [existing] = await tx.select().from(connectorActionRequests).where(
19486
+ and11(
19487
+ eq11(connectorActionRequests.workspaceId, identity.workspaceId),
19488
+ eq11(connectorActionRequests.sessionId, identity.sessionId),
19489
+ eq11(connectorActionRequests.turnId, identity.turnId),
19490
+ eq11(connectorActionRequests.approvalId, normalized.approvalId)
19491
+ )
19492
+ ).for("update").limit(1);
19493
+ if (existing && !connectorActionRequestMatchesLogicalCall(existing, identity, normalized)) {
19494
+ throw new Error("connector action approval id conflicts with different immutable inputs");
19495
+ }
19496
+ let row = existing;
19497
+ let inserted = false;
19498
+ if (!row) {
19499
+ const resolved = resolveConnectorActionPolicy(snapshot, {
19500
+ connectionId: normalized.connectionId,
19501
+ serverId: normalized.serverId,
19502
+ toolName: normalized.toolName,
19503
+ actionName: normalized.policyActionSelector
19504
+ });
19505
+ if (!resolved.managed) return { allowed: true, managed: false };
19506
+ const durable = durableConnectorActionInvocation(
19507
+ identity,
19508
+ { ...normalized, connectionId: normalized.connectionId },
19509
+ resolved
19510
+ );
19511
+ const decision = resolved.entry?.policy ?? "block";
19512
+ const created = await insertConnectorActionRequest(tx, {
19513
+ identity,
19514
+ invocation: durable,
19515
+ resolved,
19516
+ status: decision === "ask" ? "pending" : decision === "block" ? "blocked" : "executing"
19517
+ });
19518
+ row = created.row;
19519
+ inserted = created.inserted;
19520
+ if (inserted) {
19521
+ await insertConnectorActionAudit(tx, {
19522
+ row,
19523
+ action: decision === "ask" ? "connector.action.approval_requested" : decision === "block" ? "connector.action.blocked" : "connector.action.execution_started",
19524
+ subjectId: identity.initiator.subjectId,
19525
+ extra: { outcome: decision === "allow" ? "started" : decision }
19526
+ });
19527
+ }
19528
+ }
19529
+ if (row.status === "approved") {
19530
+ const [executing] = await tx.update(connectorActionRequests).set({
19531
+ status: "executing",
19532
+ executionAttemptId: identity.attemptId,
19533
+ executionAttemptGeneration: identity.executionGeneration,
19534
+ executionStartedAt: /* @__PURE__ */ new Date(),
19535
+ updatedAt: /* @__PURE__ */ new Date()
19536
+ }).where(eq11(connectorActionRequests.id, row.id)).returning();
19537
+ if (!executing) throw new Error("Approved connector action request disappeared");
19538
+ row = executing;
19539
+ await insertConnectorActionAudit(tx, {
19540
+ row,
19541
+ action: "connector.action.execution_started",
19542
+ subjectId: identity.initiator.subjectId,
19543
+ extra: { outcome: "started" }
19544
+ });
19545
+ return {
19546
+ allowed: true,
19547
+ managed: true,
19548
+ requestId: row.id,
19549
+ actionFingerprint: row.actionFingerprint
19550
+ };
19551
+ }
19552
+ if (row.status === "executing") {
19553
+ if (inserted) {
19554
+ return {
19555
+ allowed: true,
19556
+ managed: true,
19557
+ requestId: row.id,
19558
+ actionFingerprint: row.actionFingerprint
19559
+ };
19560
+ }
19561
+ const [uncertain] = await tx.update(connectorActionRequests).set({
19562
+ status: "uncertain",
19563
+ outcome: "retry_after_execution_started",
19564
+ executionFinishedAt: /* @__PURE__ */ new Date(),
19565
+ updatedAt: /* @__PURE__ */ new Date()
19566
+ }).where(eq11(connectorActionRequests.id, row.id)).returning();
19567
+ if (!uncertain) throw new Error("Executing connector action request disappeared");
19568
+ await insertConnectorActionAudit(tx, {
19569
+ row: uncertain,
19570
+ action: "connector.action.execution_uncertain",
19571
+ subjectId: identity.initiator.subjectId,
19572
+ extra: { outcome: "retry_denied" }
19573
+ });
19574
+ return {
19575
+ allowed: false,
19576
+ managed: true,
19577
+ reason: "uncertain_retry",
19578
+ requestId: uncertain.id,
19579
+ actionFingerprint: uncertain.actionFingerprint
19580
+ };
19581
+ }
19582
+ const reason = row.status === "pending" ? "approval_required" : row.status === "rejected" ? "rejected" : row.status === "blocked" ? "blocked" : "already_executed";
19583
+ return {
19584
+ allowed: false,
19585
+ managed: true,
19586
+ reason,
19587
+ requestId: row.id,
19588
+ actionFingerprint: row.actionFingerprint
19589
+ };
19590
+ })
19591
+ );
19592
+ }
19593
+ async function completeConnectorActionExecution(db, input) {
19594
+ await withRlsContext(
19595
+ db,
19596
+ { accountId: input.accountId, workspaceId: input.workspaceId },
19597
+ async (scopedDb) => await scopedDb.transaction(async (tx) => {
19598
+ const [existing] = await tx.select().from(connectorActionRequests).where(
19599
+ and11(
19600
+ eq11(connectorActionRequests.workspaceId, input.workspaceId),
19601
+ eq11(connectorActionRequests.id, input.requestId),
19602
+ eq11(connectorActionRequests.executionAttemptId, input.attemptId)
19603
+ )
19604
+ ).for("update").limit(1);
19605
+ if (!existing) throw new Error("Connector action request not found for completion");
19606
+ if (existing.status === input.outcome) return;
19607
+ if (existing.status !== "executing") {
19608
+ throw new Error(`Connector action request cannot complete from ${existing.status}`);
19609
+ }
19610
+ const [row] = await tx.update(connectorActionRequests).set({
19611
+ status: input.outcome,
19612
+ outcome: input.outcome,
19613
+ executionFinishedAt: /* @__PURE__ */ new Date(),
19614
+ updatedAt: /* @__PURE__ */ new Date()
19615
+ }).where(eq11(connectorActionRequests.id, existing.id)).returning();
19616
+ if (!row) throw new Error("Connector action request disappeared during completion");
19617
+ await insertConnectorActionAudit(tx, {
19618
+ row,
19619
+ action: input.outcome === "completed" ? "connector.action.execution_completed" : "connector.action.execution_uncertain",
19620
+ subjectId: row.initiatorSubjectId,
19621
+ extra: { outcome: input.outcome }
19622
+ });
19623
+ })
19624
+ );
19625
+ }
19120
19626
  async function getNestedAgentDepthDeploymentPolicy(db) {
19121
19627
  const [row] = await db.select({
19122
19628
  maxNestedAgentDepth: nestedAgentDepthConfiguration.maxNestedAgentDepth,
@@ -31178,6 +31684,24 @@ async function claimSessionWorkForAttempt(db, workspaceId, input) {
31178
31684
  const mcpApprovalPolicies = Object.fromEntries(
31179
31685
  policyRows.map((row2) => [row2.serverId, row2.requireApproval ?? false])
31180
31686
  );
31687
+ const connectorPolicyRows = await tx.select({
31688
+ id: connectorActionPolicies.id,
31689
+ connectionId: connectorActionPolicies.connectionId,
31690
+ serverId: connectorActionPolicies.serverId,
31691
+ toolName: connectorActionPolicies.toolName,
31692
+ actionName: connectorActionPolicies.actionName,
31693
+ policy: connectorActionPolicies.policy,
31694
+ version: connectorActionPolicies.version
31695
+ }).from(connectorActionPolicies).where(eq11(connectorActionPolicies.workspaceId, workspaceId)).orderBy(
31696
+ asc5(connectorActionPolicies.connectionId),
31697
+ asc5(connectorActionPolicies.serverId),
31698
+ asc5(connectorActionPolicies.toolName),
31699
+ asc5(connectorActionPolicies.actionName),
31700
+ asc5(connectorActionPolicies.id)
31701
+ ).limit(2049);
31702
+ if (connectorPolicyRows.length > 2048) {
31703
+ throw new Error("Connector action policy snapshot exceeds the 2048-row bound");
31704
+ }
31181
31705
  return await registerSessionTurnAttemptClaim(tx, {
31182
31706
  id: input.attemptId,
31183
31707
  accountId: session.accountId,
@@ -31189,7 +31713,8 @@ async function claimSessionWorkForAttempt(db, workspaceId, input) {
31189
31713
  temporalWorkflowRunId: input.workflowRunId,
31190
31714
  temporalActivityId: input.dispatchId,
31191
31715
  verifiedControlRevision: Number(workspaceControl.revision),
31192
- mcpApprovalPolicies
31716
+ mcpApprovalPolicies,
31717
+ connectorActionPolicies: connectorPolicyRows
31193
31718
  });
31194
31719
  };
31195
31720
  if (session.activeTurnId !== null) {
@@ -34918,6 +35443,37 @@ async function acceptSessionApprovalDecision(db, input) {
34918
35443
  clientEventId: input.clientEventId ?? null
34919
35444
  }).returning();
34920
35445
  if (!event) throw new Error("Failed to append approval decision");
35446
+ const approvalDecision = input.payload.decision;
35447
+ if (approvalDecision === "approve" || approvalDecision === "reject") {
35448
+ const [connectorRequest] = await tx.update(connectorActionRequests).set({
35449
+ status: approvalDecision === "approve" ? "approved" : "rejected",
35450
+ decision: approvalDecision,
35451
+ decisionBySubjectId: boundedConnectorActionText(
35452
+ input.subjectId,
35453
+ "approval actor",
35454
+ 1024
35455
+ ),
35456
+ decisionEventId: event.id,
35457
+ decidedAt: event.occurredAt,
35458
+ updatedAt: /* @__PURE__ */ new Date()
35459
+ }).where(
35460
+ and11(
35461
+ eq11(connectorActionRequests.workspaceId, input.workspaceId),
35462
+ eq11(connectorActionRequests.sessionId, input.sessionId),
35463
+ eq11(connectorActionRequests.turnId, turn.id),
35464
+ eq11(connectorActionRequests.approvalId, approvalId),
35465
+ eq11(connectorActionRequests.status, "pending")
35466
+ )
35467
+ ).returning();
35468
+ if (connectorRequest) {
35469
+ await insertConnectorActionAudit(tx, {
35470
+ row: connectorRequest,
35471
+ action: "connector.action.approval_decided",
35472
+ subjectId: input.subjectId,
35473
+ extra: { decision: approvalDecision }
35474
+ });
35475
+ }
35476
+ }
34921
35477
  await tx.update(sessions).set({
34922
35478
  lastSequence: session.lastSequence + 1,
34923
35479
  updatedAt: /* @__PURE__ */ new Date()
@@ -36214,6 +36770,7 @@ export {
36214
36770
  attachKnowledgeEntityAlias,
36215
36771
  autoResumeSessionBranchInTransaction,
36216
36772
  backfillModelCallFactsFromSessionEvents,
36773
+ beginConnectorActionExecution,
36217
36774
  beginKnowledgeSyncRun,
36218
36775
  beginRigChangeVerificationAttempt,
36219
36776
  beginSandboxRematerialization,
@@ -36257,6 +36814,7 @@ export {
36257
36814
  codexCapacityRefreshBackoffMs,
36258
36815
  commitWarmingToWarm,
36259
36816
  completeCodexResetRedemption,
36817
+ completeConnectorActionExecution,
36260
36818
  completeExpiredFileUploadCleanup,
36261
36819
  completeFileUpload,
36262
36820
  completeFileUploadCleanup,
@@ -36627,6 +37185,7 @@ export {
36627
37185
  persistDrainSnapshot,
36628
37186
  persistWarmSnapshot,
36629
37187
  planWorkspaceCaptureGc,
37188
+ prepareConnectorActionApproval,
36630
37189
  previewColdLostLeaseInstanceBlockers,
36631
37190
  projectEffectiveControlForRelatedAccess,
36632
37191
  projectSessionForRelatedAccess,
@@ -36708,6 +37267,7 @@ export {
36708
37267
  requireWorkspace,
36709
37268
  reserveSessionCommandReceipt,
36710
37269
  reserveToolspaceCallForAttempt,
37270
+ resolveConnectorActionPolicy,
36711
37271
  resolveSlackInstallationRoute,
36712
37272
  resolveWorkspaceMemoryBlock,
36713
37273
  restoreKnowledgeSourceObject,
@@ -36823,6 +37383,7 @@ export {
36823
37383
  upsertBillingCustomer,
36824
37384
  upsertCapabilityCatalogItem,
36825
37385
  upsertCodexSubscriptionCredential,
37386
+ upsertConnectorActionPolicy,
36826
37387
  upsertGitHubInstallation,
36827
37388
  upsertKnowledgeEntity,
36828
37389
  upsertKnowledgeFact,