@opengeni/db 3.2.0-canary.0 → 3.4.0-canary.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (81) hide show
  1. package/dist/canonical-human-identities.js +3 -3
  2. package/dist/{chunk-SIBYBA2D.js → chunk-6DPJA36C.js} +30 -11
  3. package/dist/chunk-6DPJA36C.js.map +1 -0
  4. package/dist/{chunk-KRLPEMLH.js → chunk-C2AFP7SB.js} +184 -6
  5. package/dist/chunk-C2AFP7SB.js.map +1 -0
  6. package/dist/{chunk-AZHW7R5V.js → chunk-H7MXYG7R.js} +12 -8
  7. package/dist/chunk-H7MXYG7R.js.map +1 -0
  8. package/dist/{chunk-2PRS4J5Q.js → chunk-HY3F5Q4E.js} +3 -3
  9. package/dist/{chunk-NAB6FKFN.js → chunk-O6FTELKH.js} +3 -3
  10. package/dist/{chunk-UZNXG44K.js → chunk-PYMY3AJW.js} +324 -8
  11. package/dist/chunk-PYMY3AJW.js.map +1 -0
  12. package/dist/{chunk-6HMM2R5T.js → chunk-TRBOWUG3.js} +98 -4
  13. package/dist/chunk-TRBOWUG3.js.map +1 -0
  14. package/dist/{chunk-E6CLMKEE.js → chunk-V3TRUXFJ.js} +2 -2
  15. package/dist/{chunk-2EH4WRHL.js → chunk-ZWRIPCTR.js} +2 -2
  16. package/dist/editable-artifact-durable-export.js +2 -2
  17. package/dist/editable-artifacts.js +3 -3
  18. package/dist/governed-learning-activation.d.ts +16 -0
  19. package/dist/index.d.ts +108 -7
  20. package/dist/index.js +3605 -1417
  21. package/dist/index.js.map +1 -1
  22. package/dist/insights-model-bundle.d.ts +30 -0
  23. package/dist/insights-usage-bundle.d.ts +32 -0
  24. package/dist/insights.d.ts +10 -2
  25. package/dist/lossless-json.d.ts +1 -1
  26. package/dist/managed-user-setup.d.ts +82 -0
  27. package/dist/memory-domain.d.ts +2 -2
  28. package/dist/migrate.d.ts +3 -1
  29. package/dist/migrate.js +1 -1
  30. package/dist/new-session-drafts.d.ts +4 -4
  31. package/dist/organization-membership-lifecycle.d.ts +60 -1
  32. package/dist/pr-review.d.ts +44 -3
  33. package/dist/provision-roles.js +1 -1
  34. package/dist/runtime-posture.d.ts +12 -7
  35. package/dist/schema.d.ts +839 -101
  36. package/dist/schema.js +9 -1
  37. package/dist/session-background-commands.js +3 -3
  38. package/dist/session-tenancy.d.ts +12 -1
  39. package/dist/session-tenancy.js +3 -3
  40. package/dist/video-generation.js +3 -3
  41. package/drizzle/0348_named_signup_and_user_setup.sql +838 -0
  42. package/drizzle/0349_greenfield_session_tenancy_activation.sql +792 -0
  43. package/drizzle/0350_organization_shared_workspace_administration.sql +889 -0
  44. package/drizzle/0351_organization_user_setup_delivery.sql +978 -0
  45. package/drizzle/0352_session_variable_set_attachments.sql +1661 -0
  46. package/drizzle/0353_automatic_session_title_policy_fence.sql +476 -0
  47. package/drizzle/0354_automatic_session_title_quarantine_index.sql +9 -0
  48. package/drizzle/0355_automatic_session_title_quarantine.sql +92 -0
  49. package/drizzle/0356_set_based_insights_session_visibility.sql +237 -0
  50. package/drizzle/0357_rig_platform_base_only.sql +44 -0
  51. package/drizzle/0358_pr_review_managed_github_app.sql +108 -0
  52. package/drizzle/0359_insights_force_rls_read_capability.sql +936 -0
  53. package/drizzle/0360_organization_identity_confirmation_prompt.sql +103 -0
  54. package/drizzle/0361_remember_knowledge_memory_materialization.sql +249 -0
  55. package/package.json +6 -6
  56. package/src/database.ts +12 -8
  57. package/src/governed-learning-activation.ts +74 -2
  58. package/src/index.ts +879 -92
  59. package/src/insights-model-bundle.ts +681 -0
  60. package/src/insights-usage-bundle.ts +240 -0
  61. package/src/insights.ts +265 -152
  62. package/src/lossless-json.ts +2 -1
  63. package/src/managed-user-setup.ts +316 -0
  64. package/src/memory-domain.ts +3 -3
  65. package/src/migrate.ts +38 -14
  66. package/src/new-session-drafts.ts +22 -15
  67. package/src/organization-membership-lifecycle.ts +253 -1
  68. package/src/pr-review.ts +449 -6
  69. package/src/provision-roles.ts +92 -0
  70. package/src/runtime-posture.ts +247 -4
  71. package/src/schema.ts +210 -4
  72. package/src/session-tenancy.ts +120 -2
  73. package/dist/chunk-6HMM2R5T.js.map +0 -1
  74. package/dist/chunk-AZHW7R5V.js.map +0 -1
  75. package/dist/chunk-KRLPEMLH.js.map +0 -1
  76. package/dist/chunk-SIBYBA2D.js.map +0 -1
  77. package/dist/chunk-UZNXG44K.js.map +0 -1
  78. /package/dist/{chunk-2PRS4J5Q.js.map → chunk-HY3F5Q4E.js.map} +0 -0
  79. /package/dist/{chunk-NAB6FKFN.js.map → chunk-O6FTELKH.js.map} +0 -0
  80. /package/dist/{chunk-E6CLMKEE.js.map → chunk-V3TRUXFJ.js.map} +0 -0
  81. /package/dist/{chunk-2EH4WRHL.js.map → chunk-ZWRIPCTR.js.map} +0 -0
package/src/pr-review.ts CHANGED
@@ -3,6 +3,7 @@ import {
3
3
  OPENGENI_PR_REVIEW_PACK_ID,
4
4
  OPENGENI_PR_REVIEW_SESSION_ROLE,
5
5
  type PrReviewAppRegistration,
6
+ type GitHubRepository,
6
7
  type PrReviewProvider,
7
8
  type PrReviewRepositoryBinding,
8
9
  } from "@opengeni/contracts";
@@ -32,8 +33,11 @@ export async function createPrReviewAppRegistration(
32
33
  provider: PrReviewProvider;
33
34
  providerBaseUrl: string;
34
35
  appId: string | null;
35
- credentialKind: "github_app" | "provider_token";
36
+ credentialKind: "github_app" | "managed_github_app" | "provider_token";
36
37
  credentialEncrypted: string | null;
38
+ installationId?: string | null;
39
+ providerAccountLogin?: string | null;
40
+ providerAccountType?: "User" | "Organization" | null;
37
41
  accessTokenExpiresAt: Date | null;
38
42
  webhookAuthKind: "hmac_sha256" | "shared_token" | "basic";
39
43
  webhookSecretEncrypted: string;
@@ -83,6 +87,9 @@ export async function createPrReviewAppRegistration(
83
87
  provider: input.provider,
84
88
  providerBaseUrl: input.providerBaseUrl,
85
89
  appId: input.appId,
90
+ installationId: input.installationId ?? null,
91
+ providerAccountLogin: input.providerAccountLogin ?? null,
92
+ providerAccountType: input.providerAccountType ?? null,
86
93
  credentialKind: input.credentialKind,
87
94
  credentialEncrypted: input.credentialEncrypted,
88
95
  accessTokenExpiresAt: input.accessTokenExpiresAt,
@@ -246,6 +253,11 @@ export async function updatePrReviewAppRegistration(
246
253
  ),
247
254
  )
248
255
  .returning();
256
+ if (input.status === "disabled") {
257
+ await tx
258
+ .delete(schema.prReviewManagedGithubRoutes)
259
+ .where(eq(schema.prReviewManagedGithubRoutes.registrationId, input.registrationId));
260
+ }
249
261
  return row ? mapRegistration(row, source.endpointId) : null;
250
262
  });
251
263
  });
@@ -268,6 +280,9 @@ export async function deletePrReviewAppRegistration(
268
280
  )
269
281
  .returning({ sourceId: schema.prReviewAppRegistrations.sourceId });
270
282
  if (!registration) return false;
283
+ await tx
284
+ .delete(schema.prReviewManagedGithubRoutes)
285
+ .where(eq(schema.prReviewManagedGithubRoutes.registrationId, input.registrationId));
271
286
  await tx
272
287
  .update(schema.automationSources)
273
288
  .set({
@@ -383,6 +398,425 @@ export async function createPrReviewRepositoryBinding(
383
398
  });
384
399
  }
385
400
 
401
+ /** Atomically converge one owner-authorized installation of the deployment
402
+ * review App onto the Pack's ordinary source/trigger/binding model. Re-running
403
+ * the browser flow repairs disabled rows, follows GitHub repository selection,
404
+ * and removes shared-webhook routes for repositories no longer selected. */
405
+ export async function syncManagedGitHubPrReviewInstallation(
406
+ db: Database,
407
+ input: {
408
+ accountId: string;
409
+ workspaceId: string;
410
+ installationId: number;
411
+ providerAccountLogin: string | null;
412
+ providerAccountType: "User" | "Organization";
413
+ githubActorId: number;
414
+ authorityKind: "personal_owner" | "organization_owner";
415
+ authorityCheckedAt: Date;
416
+ authorityExpiresAt: Date;
417
+ authorityNonce: string;
418
+ appId: string;
419
+ webhookSecretEncrypted: string;
420
+ repositories: GitHubRepository[];
421
+ createdBySubjectId: string;
422
+ packInstallationId: string;
423
+ packConnectorId: string;
424
+ packTemplateId: string;
425
+ adapterId: string;
426
+ eventTypes: string[];
427
+ configuration: Record<string, unknown>;
428
+ sessionTemplate: AutomationSessionTemplate;
429
+ },
430
+ ): Promise<{
431
+ registration: PrReviewAppRegistration;
432
+ repositories: PrReviewRepositoryBinding[];
433
+ }> {
434
+ return await withRlsContext(
435
+ db,
436
+ input,
437
+ async (scopedDb) =>
438
+ await scopedDb.transaction(async (tx) => {
439
+ const [packInstallation] = await tx
440
+ .select({ id: schema.packInstallations.id })
441
+ .from(schema.packInstallations)
442
+ .where(
443
+ and(
444
+ eq(schema.packInstallations.workspaceId, input.workspaceId),
445
+ eq(schema.packInstallations.id, input.packInstallationId),
446
+ eq(schema.packInstallations.packId, OPENGENI_PR_REVIEW_PACK_ID),
447
+ eq(schema.packInstallations.status, "active"),
448
+ ),
449
+ )
450
+ .limit(1)
451
+ .for("update");
452
+ if (!packInstallation) {
453
+ throw new PrReviewDispatchAuthorityError("OpenGeni Review Bot Pack is not active");
454
+ }
455
+ const installationId = String(input.installationId);
456
+ const [nonceReceipt] = await tx
457
+ .insert(schema.prReviewManagedGithubAuthorityNonces)
458
+ .values({
459
+ authorityNonce: input.authorityNonce,
460
+ accountId: input.accountId,
461
+ workspaceId: input.workspaceId,
462
+ installationId,
463
+ authorityExpiresAt: input.authorityExpiresAt,
464
+ })
465
+ .onConflictDoNothing()
466
+ .returning({
467
+ authorityNonce: schema.prReviewManagedGithubAuthorityNonces.authorityNonce,
468
+ });
469
+ if (!nonceReceipt) {
470
+ throw new PrReviewDispatchAuthorityError("OpenGeni Lens authorization was already used");
471
+ }
472
+ const [existingRegistration] = await tx
473
+ .select()
474
+ .from(schema.prReviewAppRegistrations)
475
+ .where(
476
+ and(
477
+ eq(schema.prReviewAppRegistrations.workspaceId, input.workspaceId),
478
+ eq(schema.prReviewAppRegistrations.credentialKind, "managed_github_app"),
479
+ eq(schema.prReviewAppRegistrations.installationId, installationId),
480
+ ),
481
+ )
482
+ .limit(1)
483
+ .for("update");
484
+
485
+ const registrationId = existingRegistration?.id ?? randomUUID();
486
+ const registrationName = `OpenGeni Lens · ${
487
+ input.providerAccountLogin ?? `installation ${installationId}`
488
+ } (${installationId})`;
489
+ let source: typeof schema.automationSources.$inferSelect;
490
+ let registration: typeof schema.prReviewAppRegistrations.$inferSelect;
491
+
492
+ if (existingRegistration) {
493
+ const nextSourceConfiguration = {
494
+ provider: "github",
495
+ providerBaseUrl: "https://github.com",
496
+ registrationId,
497
+ webhookUsername: null,
498
+ };
499
+ const [updatedSource] = await tx
500
+ .update(schema.automationSources)
501
+ .set({
502
+ name: `${registrationName} webhooks`,
503
+ adapterId: input.adapterId,
504
+ configuration: nextSourceConfiguration,
505
+ webhookSecretEncrypted: input.webhookSecretEncrypted,
506
+ status: "active",
507
+ packInstallationId: input.packInstallationId,
508
+ packConnectorId: input.packConnectorId,
509
+ version: sql`${schema.automationSources.version} + 1`,
510
+ updatedAt: new Date(),
511
+ })
512
+ .where(
513
+ and(
514
+ eq(schema.automationSources.workspaceId, input.workspaceId),
515
+ eq(schema.automationSources.id, existingRegistration.sourceId),
516
+ ),
517
+ )
518
+ .returning();
519
+ if (!updatedSource) {
520
+ throw new PrReviewDispatchAuthorityError(
521
+ "OpenGeni Lens automation source is unavailable",
522
+ );
523
+ }
524
+ source = updatedSource;
525
+ const [updatedRegistration] = await tx
526
+ .update(schema.prReviewAppRegistrations)
527
+ .set({
528
+ name: registrationName,
529
+ appId: input.appId,
530
+ providerAccountLogin: input.providerAccountLogin,
531
+ providerAccountType: input.providerAccountType,
532
+ githubActorId: String(input.githubActorId),
533
+ authorityKind: input.authorityKind,
534
+ authorityCheckedAt: input.authorityCheckedAt,
535
+ authorityExpiresAt: input.authorityExpiresAt,
536
+ authorityNonce: input.authorityNonce,
537
+ status: "active",
538
+ updatedAt: new Date(),
539
+ })
540
+ .where(eq(schema.prReviewAppRegistrations.id, existingRegistration.id))
541
+ .returning();
542
+ if (!updatedRegistration) throw new Error("Failed to update OpenGeni Lens registration");
543
+ registration = updatedRegistration;
544
+ } else {
545
+ const [createdSource] = await tx
546
+ .insert(schema.automationSources)
547
+ .values({
548
+ accountId: input.accountId,
549
+ workspaceId: input.workspaceId,
550
+ name: `${registrationName} webhooks`,
551
+ adapterId: input.adapterId,
552
+ configuration: {
553
+ provider: "github",
554
+ providerBaseUrl: "https://github.com",
555
+ registrationId,
556
+ webhookUsername: null,
557
+ },
558
+ webhookSecretEncrypted: input.webhookSecretEncrypted,
559
+ packInstallationId: input.packInstallationId,
560
+ packConnectorId: input.packConnectorId,
561
+ createdBySubjectId: input.createdBySubjectId,
562
+ })
563
+ .returning();
564
+ if (!createdSource) throw new Error("Failed to create OpenGeni Lens automation source");
565
+ source = createdSource;
566
+ const [createdRegistration] = await tx
567
+ .insert(schema.prReviewAppRegistrations)
568
+ .values({
569
+ id: registrationId,
570
+ sourceId: source.id,
571
+ accountId: input.accountId,
572
+ workspaceId: input.workspaceId,
573
+ name: registrationName,
574
+ provider: "github",
575
+ providerBaseUrl: "https://github.com",
576
+ appId: input.appId,
577
+ installationId,
578
+ providerAccountLogin: input.providerAccountLogin,
579
+ providerAccountType: input.providerAccountType,
580
+ githubActorId: String(input.githubActorId),
581
+ authorityKind: input.authorityKind,
582
+ authorityCheckedAt: input.authorityCheckedAt,
583
+ authorityExpiresAt: input.authorityExpiresAt,
584
+ authorityNonce: input.authorityNonce,
585
+ credentialKind: "managed_github_app",
586
+ credentialEncrypted: null,
587
+ accessTokenExpiresAt: null,
588
+ webhookAuthKind: "hmac_sha256",
589
+ webhookUsername: null,
590
+ createdBySubjectId: input.createdBySubjectId,
591
+ })
592
+ .returning();
593
+ if (!createdRegistration) throw new Error("Failed to create OpenGeni Lens registration");
594
+ registration = createdRegistration;
595
+ }
596
+
597
+ const existingBindings = await tx
598
+ .select({
599
+ binding: schema.prReviewRepositoryBindings,
600
+ trigger: schema.automationTriggers,
601
+ revision: schema.automationTriggerRevisions,
602
+ })
603
+ .from(schema.prReviewRepositoryBindings)
604
+ .innerJoin(
605
+ schema.automationTriggers,
606
+ and(
607
+ eq(
608
+ schema.automationTriggers.workspaceId,
609
+ schema.prReviewRepositoryBindings.workspaceId,
610
+ ),
611
+ eq(schema.automationTriggers.id, schema.prReviewRepositoryBindings.triggerId),
612
+ ),
613
+ )
614
+ .innerJoin(
615
+ schema.automationTriggerRevisions,
616
+ and(
617
+ eq(schema.automationTriggerRevisions.triggerId, schema.automationTriggers.id),
618
+ eq(
619
+ schema.automationTriggerRevisions.revision,
620
+ schema.automationTriggers.currentRevision,
621
+ ),
622
+ ),
623
+ )
624
+ .where(
625
+ and(
626
+ eq(schema.prReviewRepositoryBindings.workspaceId, input.workspaceId),
627
+ eq(schema.prReviewRepositoryBindings.registrationId, registrationId),
628
+ ),
629
+ )
630
+ .for("update", { of: schema.automationTriggers });
631
+ const existingByRepositoryId = new Map(
632
+ existingBindings.map((row) => [row.binding.providerRepositoryId, row] as const),
633
+ );
634
+ const selectedIds = new Set(input.repositories.map((repository) => String(repository.id)));
635
+ const synchronized: Array<typeof schema.prReviewRepositoryBindings.$inferSelect> = [];
636
+
637
+ for (const repository of input.repositories) {
638
+ if (repository.installationId !== input.installationId) {
639
+ throw new PrReviewDispatchAuthorityError(
640
+ "OpenGeni Lens repository does not belong to the authorized installation",
641
+ );
642
+ }
643
+ const providerRepositoryId = String(repository.id);
644
+ const current = existingByRepositoryId.get(providerRepositoryId);
645
+ if (!current) {
646
+ const bindingId = randomUUID();
647
+ const [trigger] = await tx
648
+ .insert(schema.automationTriggers)
649
+ .values({
650
+ accountId: input.accountId,
651
+ workspaceId: input.workspaceId,
652
+ sourceId: source.id,
653
+ name: `Review ${repository.fullName}`,
654
+ status: "active",
655
+ packInstallationId: input.packInstallationId,
656
+ packTemplateId: input.packTemplateId,
657
+ createdBySubjectId: input.createdBySubjectId,
658
+ })
659
+ .returning();
660
+ if (!trigger) throw new Error("Failed to create OpenGeni Lens trigger");
661
+ const bindingShape = {
662
+ registrationId,
663
+ provider: "github" as const,
664
+ repositoryUri: repository.cloneUrl,
665
+ repositoryFullName: repository.fullName,
666
+ providerRepositoryId,
667
+ installationId,
668
+ projectId: null,
669
+ model: null,
670
+ additionalInstructions: null,
671
+ };
672
+ await tx.insert(schema.automationTriggerRevisions).values({
673
+ triggerId: trigger.id,
674
+ revision: 1,
675
+ accountId: input.accountId,
676
+ workspaceId: input.workspaceId,
677
+ adapterId: input.adapterId,
678
+ eventTypes: input.eventTypes,
679
+ configuration: input.configuration,
680
+ parameters: prReviewTriggerParameters(bindingShape, bindingId),
681
+ sessionTemplate: input.sessionTemplate,
682
+ createdBySubjectId: input.createdBySubjectId,
683
+ });
684
+ const [binding] = await tx
685
+ .insert(schema.prReviewRepositoryBindings)
686
+ .values({
687
+ id: bindingId,
688
+ triggerId: trigger.id,
689
+ accountId: input.accountId,
690
+ workspaceId: input.workspaceId,
691
+ ...bindingShape,
692
+ status: "active",
693
+ createdBySubjectId: input.createdBySubjectId,
694
+ })
695
+ .returning();
696
+ if (!binding) throw new Error("Failed to create OpenGeni Lens repository binding");
697
+ await tx.insert(schema.prReviewManagedGithubRoutes).values({
698
+ bindingId,
699
+ accountId: input.accountId,
700
+ workspaceId: input.workspaceId,
701
+ registrationId,
702
+ sourceId: source.id,
703
+ installationId,
704
+ providerRepositoryId,
705
+ });
706
+ synchronized.push(binding);
707
+ continue;
708
+ }
709
+
710
+ const nextBinding = {
711
+ ...current.binding,
712
+ repositoryUri: repository.cloneUrl,
713
+ repositoryFullName: repository.fullName,
714
+ installationId,
715
+ status: "active" as const,
716
+ };
717
+ const nextRevision = current.trigger.currentRevision + 1;
718
+ await tx.insert(schema.automationTriggerRevisions).values({
719
+ triggerId: current.trigger.id,
720
+ revision: nextRevision,
721
+ accountId: input.accountId,
722
+ workspaceId: input.workspaceId,
723
+ adapterId: input.adapterId,
724
+ eventTypes: input.eventTypes,
725
+ configuration: input.configuration,
726
+ parameters: prReviewTriggerParameters(nextBinding, current.binding.id),
727
+ sessionTemplate: input.sessionTemplate,
728
+ createdBySubjectId: input.createdBySubjectId,
729
+ });
730
+ await tx
731
+ .update(schema.automationTriggers)
732
+ .set({
733
+ name: `Review ${repository.fullName}`,
734
+ status: "active",
735
+ currentRevision: nextRevision,
736
+ updatedAt: new Date(),
737
+ })
738
+ .where(eq(schema.automationTriggers.id, current.trigger.id));
739
+ const [binding] = await tx
740
+ .update(schema.prReviewRepositoryBindings)
741
+ .set({
742
+ repositoryUri: repository.cloneUrl,
743
+ repositoryFullName: repository.fullName,
744
+ installationId,
745
+ status: "active",
746
+ updatedAt: new Date(),
747
+ })
748
+ .where(eq(schema.prReviewRepositoryBindings.id, current.binding.id))
749
+ .returning();
750
+ if (!binding) throw new Error("Failed to update OpenGeni Lens repository binding");
751
+ await tx
752
+ .insert(schema.prReviewManagedGithubRoutes)
753
+ .values({
754
+ bindingId: binding.id,
755
+ accountId: input.accountId,
756
+ workspaceId: input.workspaceId,
757
+ registrationId,
758
+ sourceId: source.id,
759
+ installationId,
760
+ providerRepositoryId,
761
+ })
762
+ .onConflictDoUpdate({
763
+ target: schema.prReviewManagedGithubRoutes.bindingId,
764
+ set: {
765
+ accountId: input.accountId,
766
+ workspaceId: input.workspaceId,
767
+ registrationId,
768
+ sourceId: source.id,
769
+ installationId,
770
+ providerRepositoryId,
771
+ },
772
+ });
773
+ synchronized.push(binding);
774
+ }
775
+
776
+ for (const current of existingBindings) {
777
+ if (selectedIds.has(current.binding.providerRepositoryId)) continue;
778
+ await tx
779
+ .update(schema.prReviewRepositoryBindings)
780
+ .set({ status: "disabled", updatedAt: new Date() })
781
+ .where(eq(schema.prReviewRepositoryBindings.id, current.binding.id));
782
+ await tx
783
+ .update(schema.automationTriggers)
784
+ .set({ status: "disabled", updatedAt: new Date() })
785
+ .where(eq(schema.automationTriggers.id, current.trigger.id));
786
+ await tx
787
+ .delete(schema.prReviewManagedGithubRoutes)
788
+ .where(eq(schema.prReviewManagedGithubRoutes.bindingId, current.binding.id));
789
+ }
790
+
791
+ return {
792
+ registration: mapRegistration(registration, source.endpointId),
793
+ repositories: synchronized.map(mapRepositoryBinding),
794
+ };
795
+ }),
796
+ );
797
+ }
798
+
799
+ export async function resolveManagedGitHubPrReviewRoute(
800
+ db: Database,
801
+ input: { installationId: string; providerRepositoryId: string },
802
+ ): Promise<{ accountId: string; workspaceId: string; sourceId: string } | null> {
803
+ const [row] = await db
804
+ .select({
805
+ accountId: schema.prReviewManagedGithubRoutes.accountId,
806
+ workspaceId: schema.prReviewManagedGithubRoutes.workspaceId,
807
+ sourceId: schema.prReviewManagedGithubRoutes.sourceId,
808
+ })
809
+ .from(schema.prReviewManagedGithubRoutes)
810
+ .where(
811
+ and(
812
+ eq(schema.prReviewManagedGithubRoutes.installationId, input.installationId),
813
+ eq(schema.prReviewManagedGithubRoutes.providerRepositoryId, input.providerRepositoryId),
814
+ ),
815
+ )
816
+ .limit(1);
817
+ return row ?? null;
818
+ }
819
+
386
820
  export async function listPrReviewRepositoryBindings(
387
821
  db: Database,
388
822
  accountId: string,
@@ -529,7 +963,7 @@ export async function resolvePrReviewGitCredential(
529
963
  }>;
530
964
  },
531
965
  ): Promise<{
532
- credentialKind: "github_app" | "provider_token";
966
+ credentialKind: "github_app" | "managed_github_app" | "provider_token";
533
967
  appId: string | null;
534
968
  credentialEncrypted: string | null;
535
969
  expiresAt: string | null;
@@ -754,7 +1188,10 @@ export async function resolvePrReviewGitCredential(
754
1188
  if (pack?.status !== "active")
755
1189
  throw new PrReviewDispatchAuthorityError("OpenGeni Review Bot Pack is not active");
756
1190
  return {
757
- credentialKind: registration.credentialKind as "github_app" | "provider_token",
1191
+ credentialKind: registration.credentialKind as
1192
+ | "github_app"
1193
+ | "managed_github_app"
1194
+ | "provider_token",
758
1195
  appId: registration.appId,
759
1196
  credentialEncrypted: registration.credentialEncrypted,
760
1197
  expiresAt: registration.accessTokenExpiresAt?.toISOString() ?? null,
@@ -851,13 +1288,19 @@ function mapRegistration(
851
1288
  provider: row.provider as PrReviewProvider,
852
1289
  providerBaseUrl: row.providerBaseUrl,
853
1290
  appId: row.appId,
854
- credentialKind: row.credentialKind as "github_app" | "provider_token",
855
- hasCredential: row.credentialEncrypted !== null,
1291
+ credentialKind: row.credentialKind as "github_app" | "managed_github_app" | "provider_token",
1292
+ installationId: row.installationId,
1293
+ providerAccountLogin: row.providerAccountLogin,
1294
+ providerAccountType: row.providerAccountType as "User" | "Organization" | null,
1295
+ hasCredential: row.credentialKind === "managed_github_app" || row.credentialEncrypted !== null,
856
1296
  accessTokenExpiresAt: row.accessTokenExpiresAt?.toISOString() ?? null,
857
1297
  webhookAuthKind: row.webhookAuthKind as "hmac_sha256" | "shared_token" | "basic",
858
1298
  hasWebhookSecret: true,
859
1299
  webhookUsername: row.webhookUsername,
860
- webhookPath: `/v1/webhooks/automations/${endpointId}`,
1300
+ webhookPath:
1301
+ row.credentialKind === "managed_github_app"
1302
+ ? "/v1/webhooks/pr-review/github"
1303
+ : `/v1/webhooks/automations/${endpointId}`,
861
1304
  status: row.status as "active" | "disabled",
862
1305
  createdBySubjectId: row.createdBySubjectId,
863
1306
  createdAt: row.createdAt.toISOString(),
@@ -488,8 +488,14 @@ async function grantAppRoleIfSchemaExists(
488
488
  "list_self_organization_invitations(text,uuid,integer)",
489
489
  "get_self_organization_invitation(text,uuid)",
490
490
  "list_organization_members(uuid,text)",
491
+ "list_organization_administration_members(uuid,text)",
491
492
  "list_organization_invitations(uuid,text,uuid,integer)",
492
493
  "get_organization_administration_overview(uuid,text)",
494
+ "get_workspace_kind(uuid,uuid)",
495
+ "organization_workspace_command(jsonb)",
496
+ "resolve_organization_workspace_removal_subject(uuid,text,uuid)",
497
+ "prepare_organization_workspace_member_removal(jsonb)",
498
+ "record_organization_workspace_member_removal(jsonb,uuid,uuid)",
493
499
  "create_managed_organization(text,text,text,uuid)",
494
500
  "assert_organization_shared_workspace_administrator(uuid,uuid,text)",
495
501
  "open_organization_shared_workspace_administration_capability(uuid,uuid,text)",
@@ -501,6 +507,15 @@ async function grantAppRoleIfSchemaExists(
501
507
  "bind_pending_organization_invitations_for_verified_email(text,text)",
502
508
  "has_pending_organization_invitation_for_subject(text)",
503
509
  "accept_organization_invitation_v2(jsonb)",
510
+ "complete_self_service_organization_setup(jsonb)",
511
+ "ensure_organization_user_setup_intent(jsonb)",
512
+ "claim_organization_user_setup_delivery(jsonb)",
513
+ "prepare_organization_user_setup_delivery(jsonb)",
514
+ "settle_organization_user_setup_delivery(jsonb)",
515
+ "preview_organization_user_setup(text)",
516
+ "get_organization_invitation_for_administration(uuid,text,uuid)",
517
+ "preflight_organization_user_setup(text)",
518
+ "complete_organization_user_setup(jsonb)",
504
519
  "organization_membership_command(jsonb)",
505
520
  "prepare_organization_membership_protocol_settlements(jsonb)",
506
521
  "assert_active_managed_human_organization_membership(uuid,text)",
@@ -884,6 +899,19 @@ BEGIN
884
899
  ${literal(role)}
885
900
  );
886
901
  END IF;
902
+ IF to_regprocedure(
903
+ format('%I.materialize_remember_knowledge_memory(uuid,uuid,uuid)', ${literal(schema)})
904
+ ) IS NOT NULL THEN
905
+ EXECUTE format(
906
+ 'REVOKE ALL ON FUNCTION %I.materialize_remember_knowledge_memory(uuid, uuid, uuid) FROM PUBLIC',
907
+ ${literal(schema)}
908
+ );
909
+ EXECUTE format(
910
+ 'GRANT EXECUTE ON FUNCTION %I.materialize_remember_knowledge_memory(uuid, uuid, uuid) TO %I',
911
+ ${literal(schema)},
912
+ ${literal(role)}
913
+ );
914
+ END IF;
887
915
  IF to_regprocedure(
888
916
  format('%I.undo_governed_learning_activation(uuid,uuid,uuid,uuid)', ${literal(schema)})
889
917
  ) IS NOT NULL THEN
@@ -1298,6 +1326,38 @@ BEGIN
1298
1326
  ${literal(role)}
1299
1327
  );
1300
1328
  END IF;
1329
+ IF to_regprocedure(
1330
+ format(
1331
+ '%I.fork_session_content_with_runtime(uuid,uuid,uuid,text,uuid,text,boolean,text,text,integer,jsonb,uuid,uuid,text)',
1332
+ ${literal(schema)}
1333
+ )
1334
+ ) IS NOT NULL THEN
1335
+ EXECUTE format(
1336
+ 'REVOKE ALL ON FUNCTION %I.fork_session_content_with_runtime(uuid, uuid, uuid, text, uuid, text, boolean, text, text, integer, jsonb, uuid, uuid, text) FROM PUBLIC',
1337
+ ${literal(schema)}
1338
+ );
1339
+ EXECUTE format(
1340
+ 'GRANT EXECUTE ON FUNCTION %I.fork_session_content_with_runtime(uuid, uuid, uuid, text, uuid, text, boolean, text, text, integer, jsonb, uuid, uuid, text) TO %I',
1341
+ ${literal(schema)},
1342
+ ${literal(role)}
1343
+ );
1344
+ END IF;
1345
+ IF to_regprocedure(
1346
+ format(
1347
+ '%I.replay_applied_session_fork_with_runtime(uuid,uuid,uuid,text,uuid,text,boolean,text,text,integer,text)',
1348
+ ${literal(schema)}
1349
+ )
1350
+ ) IS NOT NULL THEN
1351
+ EXECUTE format(
1352
+ 'REVOKE ALL ON FUNCTION %I.replay_applied_session_fork_with_runtime(uuid, uuid, uuid, text, uuid, text, boolean, text, text, integer, text) FROM PUBLIC',
1353
+ ${literal(schema)}
1354
+ );
1355
+ EXECUTE format(
1356
+ 'GRANT EXECUTE ON FUNCTION %I.replay_applied_session_fork_with_runtime(uuid, uuid, uuid, text, uuid, text, boolean, text, text, integer, text) TO %I',
1357
+ ${literal(schema)},
1358
+ ${literal(role)}
1359
+ );
1360
+ END IF;
1301
1361
  IF to_regprocedure(
1302
1362
  format('%I.session_tenancy_product_activated(uuid,integer)', ${literal(schema)})
1303
1363
  ) IS NOT NULL THEN
@@ -1921,6 +1981,15 @@ BEGIN
1921
1981
  ${literal(role)}
1922
1982
  );
1923
1983
  END IF;
1984
+ IF to_regprocedure('opengeni_private.session_variable_set_attachments_protocol_v1_active()') IS NOT NULL THEN
1985
+ REVOKE ALL ON FUNCTION
1986
+ opengeni_private.session_variable_set_attachments_protocol_v1_active()
1987
+ FROM PUBLIC;
1988
+ EXECUTE format(
1989
+ 'GRANT EXECUTE ON FUNCTION opengeni_private.session_variable_set_attachments_protocol_v1_active() TO %I',
1990
+ ${literal(role)}
1991
+ );
1992
+ END IF;
1924
1993
  IF to_regprocedure('opengeni_private.scoped_compute_capability_active(text)') IS NOT NULL THEN
1925
1994
  REVOKE ALL ON FUNCTION opengeni_private.scoped_compute_capability_active(text) FROM PUBLIC;
1926
1995
  EXECUTE format(
@@ -1932,6 +2001,29 @@ BEGIN
1932
2001
  ${literal(role)}
1933
2002
  );
1934
2003
  END IF;
2004
+ -- The automatic-title migration helper and policy trigger deliberately
2005
+ -- retain role-scoped EXECUTE for pre-policy startup/readiness
2006
+ -- compatibility. Both are SECURITY INVOKER and PUBLIC remains revoked; RLS
2007
+ -- and the private-table ACL deny app-role enqueue writes, while PostgreSQL
2008
+ -- refuses direct trigger calls, so neither grant conveys data authority.
2009
+ IF to_regclass('opengeni_private.automatic_session_title_fanout_outbox_v1') IS NOT NULL THEN
2010
+ EXECUTE format(
2011
+ 'REVOKE ALL PRIVILEGES ON TABLE opengeni_private.automatic_session_title_fanout_outbox_v1 FROM %I',
2012
+ ${literal(role)}
2013
+ );
2014
+ END IF;
2015
+ IF to_regprocedure('opengeni_private.enqueue_automatic_session_title_fanout_v1(uuid,uuid,uuid,uuid)') IS NOT NULL THEN
2016
+ EXECUTE format(
2017
+ 'GRANT EXECUTE ON FUNCTION opengeni_private.enqueue_automatic_session_title_fanout_v1(uuid, uuid, uuid, uuid) TO %I',
2018
+ ${literal(role)}
2019
+ );
2020
+ END IF;
2021
+ IF to_regprocedure('opengeni_private.enforce_automatic_session_title_policy_v1()') IS NOT NULL THEN
2022
+ EXECUTE format(
2023
+ 'GRANT EXECUTE ON FUNCTION opengeni_private.enforce_automatic_session_title_policy_v1() TO %I',
2024
+ ${literal(role)}
2025
+ );
2026
+ END IF;
1935
2027
  -- Global cross-workspace artifact workers are separate capabilities. Never
1936
2028
  -- let the generic tenant-scoped app role inherit them from the historical
1937
2029
  -- blanket grant of already-installed helpers.