@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.
@@ -25,6 +25,8 @@ export const FORCE_RLS_TABLES = [
25
25
  "codex_subscription_credentials",
26
26
  "composer_drafts",
27
27
  "connections",
28
+ "connector_action_policies",
29
+ "connector_action_requests",
28
30
  "credit_ledger_entries",
29
31
  "device_enrollment_requests",
30
32
  "document_bases",
@@ -170,6 +172,8 @@ export const RUNTIME_FULL_DML_TABLES = [
170
172
  "codex_subscription_credentials",
171
173
  "composer_drafts",
172
174
  "connections",
175
+ "connector_action_policies",
176
+ "connector_action_requests",
173
177
  "credit_ledger_entries",
174
178
  "device_enrollment_requests",
175
179
  "document_bases",
package/src/schema.ts CHANGED
@@ -33,6 +33,18 @@ const vector = customType<{ data: number[]; driverData: string }>({
33
33
  },
34
34
  });
35
35
 
36
+ export type ConnectorActionPolicyDecision = "allow" | "ask" | "block";
37
+
38
+ export type ConnectorActionPolicySnapshotEntry = {
39
+ id: string;
40
+ connectionId: string;
41
+ serverId: string;
42
+ toolName: string;
43
+ actionName: string;
44
+ policy: ConnectorActionPolicyDecision;
45
+ version: number;
46
+ };
47
+
36
48
  export const managedAccounts = pgTable(
37
49
  "managed_accounts",
38
50
  {
@@ -714,6 +726,53 @@ export const connections = pgTable(
714
726
  }),
715
727
  );
716
728
 
729
+ export const connectorActionPolicies = pgTable(
730
+ "connector_action_policies",
731
+ {
732
+ id: uuid("id").primaryKey().defaultRandom(),
733
+ accountId: uuid("account_id")
734
+ .notNull()
735
+ .references(() => managedAccounts.id, { onDelete: "cascade" }),
736
+ workspaceId: uuid("workspace_id")
737
+ .notNull()
738
+ .references(() => workspaces.id, { onDelete: "cascade" }),
739
+ connectionId: text("connection_id").notNull(),
740
+ serverId: text("server_id").notNull(),
741
+ toolName: text("tool_name").notNull(),
742
+ actionName: text("action_name").notNull(),
743
+ policy: text("policy").$type<ConnectorActionPolicyDecision>().notNull(),
744
+ version: integer("version").notNull().default(1),
745
+ createdBySubjectId: text("created_by_subject_id").notNull(),
746
+ updatedBySubjectId: text("updated_by_subject_id").notNull(),
747
+ createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
748
+ updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow(),
749
+ },
750
+ (table) => ({
751
+ workspaceAccount: foreignKey({
752
+ name: "connector_action_policies_workspace_account_fk",
753
+ columns: [table.workspaceId, table.accountId],
754
+ foreignColumns: [workspaces.id, workspaces.accountId],
755
+ }).onDelete("cascade"),
756
+ scope: uniqueIndex("connector_action_policies_scope_uq").on(
757
+ table.workspaceId,
758
+ table.connectionId,
759
+ table.serverId,
760
+ table.toolName,
761
+ table.actionName,
762
+ ),
763
+ workspaceConnection: index("connector_action_policies_workspace_connection_idx").on(
764
+ table.workspaceId,
765
+ table.connectionId,
766
+ table.serverId,
767
+ ),
768
+ policyValid: check(
769
+ "connector_action_policies_policy_chk",
770
+ sql`${table.policy} in ('allow', 'ask', 'block')`,
771
+ ),
772
+ versionValid: check("connector_action_policies_version_chk", sql`${table.version} > 0`),
773
+ }),
774
+ );
775
+
717
776
  export const slackBotUserLinks = pgTable(
718
777
  "slack_bot_user_links",
719
778
  {
@@ -2089,6 +2148,10 @@ export const sessionTurnAttempts = pgTable(
2089
2148
  mcpApprovalPolicies: jsonb("mcp_approval_policies")
2090
2149
  .$type<Record<string, SessionMcpApprovalPolicy>>()
2091
2150
  .notNull(),
2151
+ connectorActionPolicies: jsonb("connector_action_policies")
2152
+ .$type<ConnectorActionPolicySnapshotEntry[]>()
2153
+ .notNull()
2154
+ .default([]),
2092
2155
  startedAt: timestamp("started_at", { withTimezone: true }).notNull().defaultNow(),
2093
2156
  updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow(),
2094
2157
  closedAt: timestamp("closed_at", { withTimezone: true }),
@@ -2166,6 +2229,124 @@ export const sessionTurnAttempts = pgTable(
2166
2229
  }),
2167
2230
  );
2168
2231
 
2232
+ export const connectorActionRequests = pgTable(
2233
+ "connector_action_requests",
2234
+ {
2235
+ id: uuid("id").primaryKey().defaultRandom(),
2236
+ accountId: uuid("account_id")
2237
+ .notNull()
2238
+ .references(() => managedAccounts.id, { onDelete: "cascade" }),
2239
+ workspaceId: uuid("workspace_id")
2240
+ .notNull()
2241
+ .references(() => workspaces.id, { onDelete: "cascade" }),
2242
+ sessionId: uuid("session_id").notNull(),
2243
+ turnId: uuid("turn_id").notNull(),
2244
+ creationAttemptId: uuid("creation_attempt_id").notNull(),
2245
+ creationExecutionGeneration: integer("creation_execution_generation").notNull(),
2246
+ executionAttemptId: uuid("execution_attempt_id"),
2247
+ executionAttemptGeneration: integer("execution_attempt_generation"),
2248
+ approvalId: text("approval_id").notNull(),
2249
+ initiatorKind: text("initiator_kind").$type<"subject" | "service">().notNull(),
2250
+ initiatorSubjectId: text("initiator_subject_id").notNull(),
2251
+ connectionId: text("connection_id").notNull(),
2252
+ connectionVersion: integer("connection_version"),
2253
+ serverId: text("server_id").notNull(),
2254
+ toolName: text("tool_name").notNull(),
2255
+ actionName: text("action_name").notNull(),
2256
+ // Attempt-frozen provenance intentionally survives policy deletion.
2257
+ policyId: uuid("policy_id"),
2258
+ policyVersion: integer("policy_version"),
2259
+ policySource: text("policy_source").$type<"explicit" | "ambiguous">().notNull(),
2260
+ policyDecision: text("policy_decision").$type<ConnectorActionPolicyDecision>().notNull(),
2261
+ actionFingerprint: text("action_fingerprint").notNull(),
2262
+ status: text("status")
2263
+ .$type<
2264
+ | "pending"
2265
+ | "approved"
2266
+ | "rejected"
2267
+ | "blocked"
2268
+ | "executing"
2269
+ | "completed"
2270
+ | "failed"
2271
+ | "uncertain"
2272
+ >()
2273
+ .notNull(),
2274
+ decision: text("decision").$type<"approve" | "reject">(),
2275
+ decisionBySubjectId: text("decision_by_subject_id"),
2276
+ decisionEventId: uuid("decision_event_id"),
2277
+ decidedAt: timestamp("decided_at", { withTimezone: true }),
2278
+ executionStartedAt: timestamp("execution_started_at", { withTimezone: true }),
2279
+ executionFinishedAt: timestamp("execution_finished_at", { withTimezone: true }),
2280
+ outcome: text("outcome"),
2281
+ createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
2282
+ updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow(),
2283
+ },
2284
+ (table) => ({
2285
+ creationAttempt: foreignKey({
2286
+ name: "connector_action_requests_creation_attempt_fk",
2287
+ columns: [
2288
+ table.accountId,
2289
+ table.workspaceId,
2290
+ table.sessionId,
2291
+ table.turnId,
2292
+ table.creationAttemptId,
2293
+ ],
2294
+ foreignColumns: [
2295
+ sessionTurnAttempts.accountId,
2296
+ sessionTurnAttempts.workspaceId,
2297
+ sessionTurnAttempts.sessionId,
2298
+ sessionTurnAttempts.turnId,
2299
+ sessionTurnAttempts.id,
2300
+ ],
2301
+ }).onDelete("cascade"),
2302
+ executionAttempt: foreignKey({
2303
+ name: "connector_action_requests_execution_attempt_fk",
2304
+ columns: [
2305
+ table.accountId,
2306
+ table.workspaceId,
2307
+ table.sessionId,
2308
+ table.turnId,
2309
+ table.executionAttemptId,
2310
+ ],
2311
+ foreignColumns: [
2312
+ sessionTurnAttempts.accountId,
2313
+ sessionTurnAttempts.workspaceId,
2314
+ sessionTurnAttempts.sessionId,
2315
+ sessionTurnAttempts.turnId,
2316
+ sessionTurnAttempts.id,
2317
+ ],
2318
+ }).onDelete("cascade"),
2319
+ identity: uniqueIndex("connector_action_requests_identity_uq").on(
2320
+ table.workspaceId,
2321
+ table.sessionId,
2322
+ table.turnId,
2323
+ table.approvalId,
2324
+ ),
2325
+ attemptStatus: index("connector_action_requests_attempt_status_idx").on(
2326
+ table.workspaceId,
2327
+ table.creationAttemptId,
2328
+ table.status,
2329
+ ),
2330
+ sessionCreated: index("connector_action_requests_session_created_idx").on(
2331
+ table.workspaceId,
2332
+ table.sessionId,
2333
+ table.createdAt.desc(),
2334
+ table.id.desc(),
2335
+ ),
2336
+ policyDecisionValid: check(
2337
+ "connector_action_requests_policy_decision_chk",
2338
+ sql`${table.policyDecision} in ('allow', 'ask', 'block')`,
2339
+ ),
2340
+ statusValid: check(
2341
+ "connector_action_requests_status_chk",
2342
+ sql`${table.status} in (
2343
+ 'pending', 'approved', 'rejected', 'blocked', 'executing',
2344
+ 'completed', 'failed', 'uncertain'
2345
+ )`,
2346
+ ),
2347
+ }),
2348
+ );
2349
+
2169
2350
  // One durable idempotency/operation record for every queue, control,
2170
2351
  // foreground Send/Steer, and Agent MCP mutation. The database migration owns
2171
2352
  // the NULLS NOT DISTINCT uniqueness form because Drizzle does not model it.
@@ -519,6 +519,7 @@ export async function registerSessionTurnAttemptClaim(
519
519
  temporalActivityId: string;
520
520
  verifiedControlRevision: number;
521
521
  mcpApprovalPolicies: Record<string, SessionMcpApprovalPolicy>;
522
+ connectorActionPolicies: schema.ConnectorActionPolicySnapshotEntry[];
522
523
  },
523
524
  ): Promise<typeof schema.sessionTurnAttempts.$inferSelect> {
524
525
  const [inserted] = await db
@@ -554,6 +555,8 @@ export async function registerSessionTurnAttemptClaim(
554
555
  existing.temporalWorkflowId !== input.temporalWorkflowId ||
555
556
  existing.temporalWorkflowRunId !== input.temporalWorkflowRunId ||
556
557
  existing.temporalActivityId !== input.temporalActivityId ||
558
+ JSON.stringify(existing.connectorActionPolicies) !==
559
+ JSON.stringify(input.connectorActionPolicies) ||
557
560
  existing.state === "closed"
558
561
  ) {
559
562
  throw new SessionControlInvariantError(