diff --git a/.changeset/green-crabs-arrive.md b/.changeset/green-crabs-arrive.md
new file mode 100644
index 000000000..ab7bce0c4
--- /dev/null
+++ b/.changeset/green-crabs-arrive.md
@@ -0,0 +1,5 @@
+---
+"agentweaver": minor
+---
+
+Platform admins can now connect a single platform-wide GitHub Copilot account for GitHub Copilot mode, separate from per-project Copilot connections. Agentweaver now blocks app access behind a setup screen until either BYOK or the platform-default Copilot connection is configured.
diff --git a/apps/Agentweaver.Api.Data/Memory/GitHubConnectionsPersistenceRecords.cs b/apps/Agentweaver.Api.Data/Memory/GitHubConnectionsPersistenceRecords.cs
index ce46ded3d..cacbdc89a 100644
--- a/apps/Agentweaver.Api.Data/Memory/GitHubConnectionsPersistenceRecords.cs
+++ b/apps/Agentweaver.Api.Data/Memory/GitHubConnectionsPersistenceRecords.cs
@@ -1,7 +1,7 @@
namespace Agentweaver.Api.Memory;
public enum GitHubAppKind { Repo, Copilot }
-public enum GitHubAuthorizationPurpose { InteractiveRepository, InteractiveCopilot, UnattendedRepository, UnattendedCopilot }
+public enum GitHubAuthorizationPurpose { InteractiveRepository, InteractiveCopilot, UnattendedRepository, UnattendedCopilot, PlatformDefaultCopilot }
public enum GitHubCapabilityPurpose { InteractiveRepository, InteractiveCopilot, UnattendedRepository, UnattendedCopilot }
public enum GitHubCapabilitySnapshotSourceKind { UserAuthorization, RepositoryGrant, CopilotBinding }
public enum GitHubAuthorizationStatus { Pending, Redeeming, Completed, Failed, Expired }
@@ -141,6 +141,21 @@ public sealed class ProjectCopilotBindingRecord
public DateTimeOffset? DeactivatedAt { get; set; }
}
+public sealed class PlatformDefaultCopilotBindingRecord
+{
+ public const string SingletonId = "platform-default";
+
+ public string Id { get; set; } = SingletonId;
+ public string EntraObjectId { get; set; } = "";
+ public string CredentialReference { get; set; } = "";
+ /// Stable identity of the authorization grant, not an access-token version.
+ public string CredentialVersion { get; set; } = "";
+ public string GrantDigest { get; set; } = "";
+ public GitHubBindingStatus Status { get; set; }
+ public DateTimeOffset BoundAt { get; set; }
+ public DateTimeOffset? DeactivatedAt { get; set; }
+}
+
public sealed class AutomationActivationRecord
{
public string Id { get; set; } = "";
diff --git a/apps/Agentweaver.Api.Data/Memory/MemoryDbContext.cs b/apps/Agentweaver.Api.Data/Memory/MemoryDbContext.cs
index e69019bdc..348d8eb1c 100644
--- a/apps/Agentweaver.Api.Data/Memory/MemoryDbContext.cs
+++ b/apps/Agentweaver.Api.Data/Memory/MemoryDbContext.cs
@@ -32,6 +32,7 @@ public sealed class MemoryDbContext(DbContextOptions options) :
public DbSet GitHubRepositoryGrants => Set();
public DbSet GitHubRepositorySelectionCodes => Set();
public DbSet ProjectCopilotBindings => Set();
+ public DbSet PlatformDefaultCopilotBindings => Set();
public DbSet AutomationActivations => Set();
public DbSet AutomationInvocations => Set();
public DbSet GitHubLifecycleDeliveries => Set();
@@ -630,6 +631,19 @@ private void ConfigureGitHubConnectionsPersistence(ModelBuilder model)
ConfigureProjectForeignKey(e, "FK_project_copilot_bindings_projects_project_id");
});
+ model.Entity(e =>
+ {
+ e.ToTable("platform_default_copilot_bindings").HasKey(x => x.Id);
+ e.Property(x => x.Id).HasColumnName("id");
+ e.Property(x => x.EntraObjectId).HasColumnName("entra_object_id");
+ e.Property(x => x.CredentialReference).HasColumnName("credential_reference");
+ e.Property(x => x.CredentialVersion).HasColumnName("credential_version");
+ e.Property(x => x.GrantDigest).HasColumnName("grant_digest");
+ e.Property(x => x.Status).HasColumnName("status");
+ e.Property(x => x.BoundAt).HasColumnName("bound_at");
+ e.Property(x => x.DeactivatedAt).HasColumnName("deactivated_at");
+ });
+
model.Entity(e =>
{
e.ToTable("automation_activations").HasKey(x => x.Id);
diff --git a/apps/Agentweaver.Api.Migrations.Postgres/Migrations/20260831164755_AddPlatformDefaultCopilotBinding.Designer.cs b/apps/Agentweaver.Api.Migrations.Postgres/Migrations/20260831164755_AddPlatformDefaultCopilotBinding.Designer.cs
new file mode 100644
index 000000000..243d13d7b
--- /dev/null
+++ b/apps/Agentweaver.Api.Migrations.Postgres/Migrations/20260831164755_AddPlatformDefaultCopilotBinding.Designer.cs
@@ -0,0 +1,2899 @@
+//
+using System;
+using Agentweaver.Api.Memory;
+using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.Infrastructure;
+using Microsoft.EntityFrameworkCore.Migrations;
+using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
+using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
+
+#nullable disable
+
+namespace Agentweaver.Api.Migrations.Postgres.Migrations
+{
+ [DbContext(typeof(MemoryDbContext))]
+ [Migration("20260831164755_AddPlatformDefaultCopilotBinding")]
+ partial class AddPlatformDefaultCopilotBinding
+ {
+ ///
+ protected override void BuildTargetModel(ModelBuilder modelBuilder)
+ {
+#pragma warning disable 612, 618
+ modelBuilder
+ .HasAnnotation("ProductVersion", "9.0.7")
+ .HasAnnotation("Relational:MaxIdentifierLength", 63);
+
+ NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
+
+ modelBuilder.Entity("Agentweaver.Api.Auth.OAuth.BrowserEntraSession", b =>
+ {
+ b.Property("Id")
+ .HasColumnType("text");
+
+ b.Property("EntraObjectId")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("ExpiresAt")
+ .HasColumnType("timestamp with time zone");
+
+ b.HasKey("Id");
+
+ b.HasIndex("ExpiresAt");
+
+ b.ToTable("BrowserEntraSessions");
+ });
+
+ modelBuilder.Entity("Agentweaver.Api.Auth.OAuth.EntraOAuthState", b =>
+ {
+ b.Property("State")
+ .HasColumnType("text");
+
+ b.Property("CodeVerifier")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("ExpiresAt")
+ .HasColumnType("timestamp with time zone");
+
+ b.HasKey("State");
+
+ b.HasIndex("ExpiresAt");
+
+ b.ToTable("EntraOAuthStates");
+ });
+
+ modelBuilder.Entity("Agentweaver.Api.Auth.OAuth.WebSessionExchangeCode", b =>
+ {
+ b.Property("Code")
+ .HasColumnType("text");
+
+ b.Property("AccessToken")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("ExpiresAt")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("Login")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.HasKey("Code");
+
+ b.HasIndex("ExpiresAt");
+
+ b.ToTable("WebSessionExchangeCodes");
+ });
+
+ modelBuilder.Entity("Agentweaver.Api.Coordinator.CoordinatorAssemblyReviewRecord", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer");
+
+ NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id"));
+
+ b.Property("AggregateTreeHash")
+ .HasColumnType("text");
+
+ b.Property("CoordinatorFailedAt")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("CoordinatorFailureReason")
+ .HasColumnType("text");
+
+ b.Property("CoordinatorRunId")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("CreatedAt")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("DecisionJson")
+ .HasColumnType("text");
+
+ b.Property("DecisionSubmittedAt")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("IntegrationBranch")
+ .HasColumnType("text");
+
+ b.Property("OwnerUser")
+ .HasColumnType("text");
+
+ b.Property("Reviewer")
+ .HasColumnType("text");
+
+ b.Property("UpdatedAt")
+ .HasColumnType("timestamp with time zone");
+
+ b.HasKey("Id");
+
+ b.HasIndex("CoordinatorRunId")
+ .IsUnique();
+
+ b.ToTable("AssemblyReviews");
+ });
+
+ modelBuilder.Entity("Agentweaver.Api.Diagnostics.HeartbeatStatusRecord", b =>
+ {
+ b.Property("PodName")
+ .HasColumnType("text");
+
+ b.Property("ActedCount")
+ .HasColumnType("integer");
+
+ b.Property("DurationMs")
+ .HasColumnType("bigint");
+
+ b.Property("Enabled")
+ .HasColumnType("boolean");
+
+ b.Property("Error")
+ .HasColumnType("text");
+
+ b.Property("ErrorCount")
+ .HasColumnType("integer");
+
+ b.Property("IntervalSeconds")
+ .HasColumnType("integer");
+
+ b.Property("LastTickUtc")
+ .HasColumnType("timestamp with time zone");
+
+ b.HasKey("PodName");
+
+ b.ToTable("HeartbeatStatuses");
+ });
+
+ modelBuilder.Entity("Agentweaver.Api.Memory.AgentMemory", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer");
+
+ NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id"));
+
+ b.Property("AgentName")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("ApprovedAt")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("ApprovedBy")
+ .HasColumnType("text");
+
+ b.Property("Content")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("CreatedAt")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("Importance")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("ProjectId")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("SessionId")
+ .HasColumnType("text");
+
+ b.Property("SourceIdentity")
+ .HasColumnType("text");
+
+ b.Property("SourceKind")
+ .IsRequired()
+ .ValueGeneratedOnAdd()
+ .HasColumnType("text")
+ .HasDefaultValue("legacy");
+
+ b.Property("SourceRunId")
+ .HasColumnType("text");
+
+ b.Property("Tags")
+ .HasColumnType("text");
+
+ b.Property("TrustState")
+ .IsRequired()
+ .ValueGeneratedOnAdd()
+ .HasColumnType("text")
+ .HasDefaultValue("legacy");
+
+ b.Property("Type")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("UpdatedAt")
+ .HasColumnType("timestamp with time zone");
+
+ b.HasKey("Id");
+
+ b.HasIndex("ProjectId", "AgentName");
+
+ b.HasIndex("ProjectId", "Type");
+
+ b.ToTable("AgentMemory");
+ });
+
+ modelBuilder.Entity("Agentweaver.Api.Memory.AutomationActivationRecord", b =>
+ {
+ b.Property("Id")
+ .HasColumnType("text")
+ .HasColumnName("id");
+
+ b.Property("ActivatedAt")
+ .HasColumnType("timestamp with time zone")
+ .HasColumnName("activated_at");
+
+ b.Property("AutomationKey")
+ .IsRequired()
+ .HasColumnType("text")
+ .HasColumnName("automation_key");
+
+ b.Property("CopilotBindingGrantDigest")
+ .HasColumnType("text")
+ .HasColumnName("copilot_binding_grant_digest");
+
+ b.Property("CopilotBindingId")
+ .HasColumnType("text")
+ .HasColumnName("copilot_binding_id");
+
+ b.Property("InstallationId")
+ .HasColumnType("bigint")
+ .HasColumnName("installation_id");
+
+ b.Property("InvalidatedAt")
+ .HasColumnType("timestamp with time zone")
+ .HasColumnName("invalidated_at");
+
+ b.Property("ProjectId")
+ .IsRequired()
+ .HasColumnType("text")
+ .HasColumnName("project_id");
+
+ b.Property("RepositoryGrantDigest")
+ .HasColumnType("text")
+ .HasColumnName("repository_grant_digest");
+
+ b.Property("RepositoryId")
+ .HasColumnType("bigint")
+ .HasColumnName("repository_id");
+
+ b.Property("Status")
+ .HasColumnType("integer")
+ .HasColumnName("status");
+
+ b.HasKey("Id");
+
+ b.HasIndex("ProjectId")
+ .IsUnique()
+ .HasDatabaseName("UX_automation_activations_active_project")
+ .HasFilter("status = 0");
+
+ b.HasIndex("InstallationId", "RepositoryId");
+
+ b.ToTable("automation_activations", (string)null);
+ });
+
+ modelBuilder.Entity("Agentweaver.Api.Memory.AutomationInvocationRecord", b =>
+ {
+ b.Property("Id")
+ .HasColumnType("text")
+ .HasColumnName("id");
+
+ b.Property("ActivationId")
+ .IsRequired()
+ .HasColumnType("text")
+ .HasColumnName("activation_id");
+
+ b.Property("BacklogTaskId")
+ .HasColumnType("text")
+ .HasColumnName("backlog_task_id");
+
+ b.Property("CompletedAt")
+ .HasColumnType("timestamp with time zone")
+ .HasColumnName("completed_at");
+
+ b.Property("DeliveryId")
+ .HasColumnType("text")
+ .HasColumnName("delivery_id");
+
+ b.Property("EventName")
+ .HasColumnType("text")
+ .HasColumnName("event_name");
+
+ b.Property("InstallationId")
+ .HasColumnType("bigint")
+ .HasColumnName("installation_id");
+
+ b.Property("OccurrenceKey")
+ .IsRequired()
+ .HasColumnType("text")
+ .HasColumnName("occurrence_key");
+
+ b.Property("Outcome")
+ .HasColumnType("integer")
+ .HasColumnName("outcome");
+
+ b.Property("PendingBacklogTaskId")
+ .HasColumnType("text")
+ .HasColumnName("pending_backlog_task_id");
+
+ b.Property("ProjectId")
+ .IsRequired()
+ .HasColumnType("text")
+ .HasColumnName("project_id");
+
+ b.Property("ReceivedAt")
+ .HasColumnType("timestamp with time zone")
+ .HasColumnName("received_at");
+
+ b.Property("RepositoryId")
+ .HasColumnType("bigint")
+ .HasColumnName("repository_id");
+
+ b.HasKey("Id");
+
+ b.HasIndex("BacklogTaskId")
+ .IsUnique()
+ .HasDatabaseName("UX_automation_invocations_backlog_task_id")
+ .HasFilter("backlog_task_id IS NOT NULL");
+
+ b.HasIndex("DeliveryId")
+ .IsUnique()
+ .HasDatabaseName("UX_automation_invocations_delivery_id")
+ .HasFilter("delivery_id IS NOT NULL");
+
+ b.HasIndex("PendingBacklogTaskId")
+ .IsUnique()
+ .HasDatabaseName("UX_automation_invocations_pending_backlog_task_id")
+ .HasFilter("pending_backlog_task_id IS NOT NULL");
+
+ b.HasIndex("ProjectId");
+
+ b.HasIndex("ActivationId", "OccurrenceKey")
+ .IsUnique();
+
+ b.ToTable("automation_invocations", (string)null);
+ });
+
+ modelBuilder.Entity("Agentweaver.Api.Memory.BacklogTaskDependencyRecord", b =>
+ {
+ b.Property("TaskId")
+ .HasColumnType("text")
+ .HasColumnName("task_id");
+
+ b.Property("DependsOnTaskId")
+ .HasColumnType("text")
+ .HasColumnName("depends_on_task_id");
+
+ b.Property("CreatedAt")
+ .HasColumnType("timestamp with time zone")
+ .HasColumnName("created_at");
+
+ b.Property("ProjectId")
+ .IsRequired()
+ .HasColumnType("text")
+ .HasColumnName("project_id");
+
+ b.HasKey("TaskId", "DependsOnTaskId");
+
+ b.HasIndex("DependsOnTaskId")
+ .HasDatabaseName("IX_backlog_task_dependencies_prerequisite");
+
+ b.HasIndex("ProjectId", "TaskId")
+ .HasDatabaseName("IX_backlog_task_dependencies_project_task");
+
+ b.ToTable("backlog_task_dependencies", (string)null);
+ });
+
+ modelBuilder.Entity("Agentweaver.Api.Memory.BacklogTaskRecord", b =>
+ {
+ b.Property("TaskId")
+ .HasColumnType("text")
+ .HasColumnName("task_id");
+
+ b.Property("ArchivedAt")
+ .HasColumnType("timestamp with time zone")
+ .HasColumnName("archived_at");
+
+ b.Property("CapturedBy")
+ .IsRequired()
+ .HasColumnType("text")
+ .HasColumnName("captured_by");
+
+ b.Property("CapturedByUserId")
+ .HasColumnType("text")
+ .HasColumnName("captured_by_user_id");
+
+ b.Property("ClaimedAt")
+ .HasColumnType("timestamp with time zone")
+ .HasColumnName("claimed_at");
+
+ b.Property("CommittedAt")
+ .HasColumnType("timestamp with time zone")
+ .HasColumnName("committed_at");
+
+ b.Property("CreatedAt")
+ .HasColumnType("timestamp with time zone")
+ .HasColumnName("created_at");
+
+ b.Property("Description")
+ .HasColumnType("text")
+ .HasColumnName("description");
+
+ b.Property("IsAutomationInvocationPending")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("boolean")
+ .HasDefaultValue(false)
+ .HasColumnName("automation_invocation_pending");
+
+ b.Property("OrderKey")
+ .IsRequired()
+ .HasColumnType("text")
+ .HasColumnName("order_key");
+
+ b.Property("ParentPrdRunId")
+ .HasColumnType("text")
+ .HasColumnName("parent_prd_run_id");
+
+ b.Property("ProjectId")
+ .IsRequired()
+ .HasColumnType("text")
+ .HasColumnName("project_id");
+
+ b.Property("PromotionKey")
+ .HasColumnType("text")
+ .HasColumnName("promotion_key");
+
+ b.Property("PromotionReason")
+ .HasColumnType("text")
+ .HasColumnName("promotion_reason");
+
+ b.Property("RunId")
+ .HasColumnType("text")
+ .HasColumnName("run_id");
+
+ b.Property("SourceFilePath")
+ .HasColumnType("text")
+ .HasColumnName("source_file_path");
+
+ b.Property("State")
+ .IsRequired()
+ .HasColumnType("text")
+ .HasColumnName("state");
+
+ b.Property("Title")
+ .IsRequired()
+ .HasColumnType("text")
+ .HasColumnName("title");
+
+ b.Property("WorkflowOverrideId")
+ .HasColumnType("text")
+ .HasColumnName("workflow_override_id");
+
+ b.HasKey("TaskId");
+
+ b.HasIndex("RunId")
+ .IsUnique()
+ .HasDatabaseName("IX_backlog_tasks_run")
+ .HasFilter("run_id IS NOT NULL");
+
+ b.HasIndex("ParentPrdRunId", "PromotionKey")
+ .IsUnique()
+ .HasDatabaseName("IX_backlog_tasks_parent_promotion_key")
+ .HasFilter("parent_prd_run_id IS NOT NULL AND promotion_key IS NOT NULL");
+
+ b.HasIndex("ProjectId", "State", "OrderKey")
+ .IsUnique()
+ .HasDatabaseName("IX_backlog_tasks_orderkey_unique")
+ .HasFilter("state IN ('backlog','ready') AND archived_at IS NULL");
+
+ b.ToTable("backlog_tasks", (string)null);
+ });
+
+ modelBuilder.Entity("Agentweaver.Api.Memory.BlueprintPackageAcquisitionRecord", b =>
+ {
+ b.Property("OwnerId")
+ .HasColumnType("text")
+ .HasColumnName("owner_id");
+
+ b.Property("PackageId")
+ .HasColumnType("text")
+ .HasColumnName("package_id");
+
+ b.Property("CanonicalVersionKey")
+ .HasMaxLength(64)
+ .HasColumnType("character varying(64)")
+ .HasColumnName("canonical_version_key");
+
+ b.Property("Ordinal")
+ .HasColumnType("integer")
+ .HasColumnName("ordinal");
+
+ b.Property("AcquiredAt")
+ .HasColumnType("timestamp with time zone")
+ .HasColumnName("acquired_at");
+
+ b.Property("CanonicalVersion")
+ .IsRequired()
+ .HasColumnType("text")
+ .HasColumnName("canonical_version");
+
+ b.Property("Producer")
+ .HasColumnType("text")
+ .HasColumnName("producer");
+
+ b.Property("Repository")
+ .HasColumnType("text")
+ .HasColumnName("repository");
+
+ b.Property("RequestedRef")
+ .HasColumnType("text")
+ .HasColumnName("requested_ref");
+
+ b.Property("Revision")
+ .HasColumnType("text")
+ .HasColumnName("revision");
+
+ b.Property("Source")
+ .IsRequired()
+ .HasColumnType("text")
+ .HasColumnName("source");
+
+ b.HasKey("OwnerId", "PackageId", "CanonicalVersionKey", "Ordinal");
+
+ b.ToTable("blueprint_package_acquisitions", (string)null);
+ });
+
+ modelBuilder.Entity("Agentweaver.Api.Memory.BlueprintPackageLibraryRecord", b =>
+ {
+ b.Property("OwnerId")
+ .HasColumnType("text")
+ .HasColumnName("owner_id");
+
+ b.Property("PackageId")
+ .HasColumnType("text")
+ .HasColumnName("package_id");
+
+ b.Property("CreatedAt")
+ .HasColumnType("timestamp with time zone")
+ .HasColumnName("created_at");
+
+ b.HasKey("OwnerId", "PackageId");
+
+ b.ToTable("blueprint_package_library", (string)null);
+ });
+
+ modelBuilder.Entity("Agentweaver.Api.Memory.BlueprintPackagePayloadRecord", b =>
+ {
+ b.Property("OwnerId")
+ .HasColumnType("text")
+ .HasColumnName("owner_id");
+
+ b.Property("PackageId")
+ .HasColumnType("text")
+ .HasColumnName("package_id");
+
+ b.Property("CanonicalVersionKey")
+ .HasMaxLength(64)
+ .HasColumnType("character varying(64)")
+ .HasColumnName("canonical_version_key");
+
+ b.Property("Path")
+ .HasColumnType("text")
+ .HasColumnName("path");
+
+ b.Property("Bytes")
+ .IsRequired()
+ .HasColumnType("bytea")
+ .HasColumnName("bytes");
+
+ b.Property("CanonicalVersion")
+ .IsRequired()
+ .HasColumnType("text")
+ .HasColumnName("canonical_version");
+
+ b.HasKey("OwnerId", "PackageId", "CanonicalVersionKey", "Path");
+
+ b.ToTable("blueprint_package_payloads", (string)null);
+ });
+
+ modelBuilder.Entity("Agentweaver.Api.Memory.BlueprintPackageVersionRecord", b =>
+ {
+ b.Property("OwnerId")
+ .HasColumnType("text")
+ .HasColumnName("owner_id");
+
+ b.Property("PackageId")
+ .HasColumnType("text")
+ .HasColumnName("package_id");
+
+ b.Property("CanonicalVersionKey")
+ .HasMaxLength(64)
+ .HasColumnType("character varying(64)")
+ .HasColumnName("canonical_version_key");
+
+ b.Property("CanonicalVersion")
+ .IsRequired()
+ .HasColumnType("text")
+ .HasColumnName("canonical_version");
+
+ b.Property("ContainerSha256")
+ .HasColumnType("text")
+ .HasColumnName("container_sha256");
+
+ b.Property("ContentDigest")
+ .IsRequired()
+ .HasColumnType("text")
+ .HasColumnName("content_digest");
+
+ b.Property("CreatedAt")
+ .HasColumnType("timestamp with time zone")
+ .HasColumnName("created_at");
+
+ b.Property("PayloadSetDigest")
+ .IsRequired()
+ .HasColumnType("text")
+ .HasColumnName("payload_set_digest");
+
+ b.Property("RawManifest")
+ .IsRequired()
+ .HasColumnType("bytea")
+ .HasColumnName("raw_manifest");
+
+ b.Property("RawManifestSha256")
+ .IsRequired()
+ .HasColumnType("text")
+ .HasColumnName("raw_manifest_sha256");
+
+ b.HasKey("OwnerId", "PackageId", "CanonicalVersionKey");
+
+ b.ToTable("blueprint_package_versions", (string)null);
+ });
+
+ modelBuilder.Entity("Agentweaver.Api.Memory.CastProposalRecord", b =>
+ {
+ b.Property("Id")
+ .HasColumnType("text")
+ .HasColumnName("id");
+
+ b.Property("CreatedAt")
+ .HasColumnType("timestamp with time zone")
+ .HasColumnName("created_at");
+
+ b.Property("ExpiresAt")
+ .HasColumnType("timestamp with time zone")
+ .HasColumnName("expires_at");
+
+ b.Property("Owner")
+ .IsRequired()
+ .HasColumnType("text")
+ .HasColumnName("owner");
+
+ b.Property("ProjectId")
+ .IsRequired()
+ .HasColumnType("text")
+ .HasColumnName("project_id");
+
+ b.Property("ProposalJson")
+ .IsRequired()
+ .HasColumnType("text")
+ .HasColumnName("proposal_json");
+
+ b.HasKey("Id");
+
+ b.HasIndex("ProjectId")
+ .HasDatabaseName("IX_cast_proposals_project_id");
+
+ b.ToTable("cast_proposals", (string)null);
+ });
+
+ modelBuilder.Entity("Agentweaver.Api.Memory.Decision", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer");
+
+ NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id"));
+
+ b.Property("AgentName")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("ApprovedAt")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("ApprovedBy")
+ .HasColumnType("text");
+
+ b.Property("Content")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("CreatedAt")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("ProjectId")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("Rationale")
+ .HasColumnType("text");
+
+ b.Property("SourceIdentity")
+ .HasColumnType("text");
+
+ b.Property("SourceKind")
+ .IsRequired()
+ .ValueGeneratedOnAdd()
+ .HasColumnType("text")
+ .HasDefaultValue("legacy");
+
+ b.Property("SourceRunId")
+ .HasColumnType("text");
+
+ b.Property("Status")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("SupersededById")
+ .HasColumnType("integer");
+
+ b.Property("Tags")
+ .HasColumnType("text");
+
+ b.Property("Title")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("TrustState")
+ .IsRequired()
+ .ValueGeneratedOnAdd()
+ .HasColumnType("text")
+ .HasDefaultValue("legacy");
+
+ b.Property("Type")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("UpdatedAt")
+ .HasColumnType("timestamp with time zone");
+
+ b.HasKey("Id");
+
+ b.HasIndex("SupersededById");
+
+ b.HasIndex("ProjectId", "AgentName");
+
+ b.HasIndex("ProjectId", "Status");
+
+ b.ToTable("Decisions");
+ });
+
+ modelBuilder.Entity("Agentweaver.Api.Memory.DecisionInboxEntry", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer");
+
+ NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id"));
+
+ b.Property("AgentName")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("Content")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("CreatedAt")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("DecisionId")
+ .HasColumnType("integer");
+
+ b.Property("MergedAt")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("ProjectId")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("Rationale")
+ .HasColumnType("text");
+
+ b.Property("Slug")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("SourceIdentity")
+ .HasColumnType("text");
+
+ b.Property("SourceKind")
+ .IsRequired()
+ .ValueGeneratedOnAdd()
+ .HasColumnType("text")
+ .HasDefaultValue("legacy");
+
+ b.Property("SourceRunId")
+ .HasColumnType("text");
+
+ b.Property("Status")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("Title")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("Type")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("UpdatedAt")
+ .HasColumnType("timestamp with time zone");
+
+ b.HasKey("Id");
+
+ b.HasIndex("DecisionId");
+
+ b.HasIndex("ProjectId", "Slug")
+ .IsUnique();
+
+ b.HasIndex("ProjectId", "Status");
+
+ b.ToTable("DecisionInbox");
+ });
+
+ modelBuilder.Entity("Agentweaver.Api.Memory.DismissedNotification", b =>
+ {
+ b.Property("User")
+ .HasColumnType("text")
+ .HasColumnName("user");
+
+ b.Property("NotificationId")
+ .HasColumnType("text")
+ .HasColumnName("notification_id");
+
+ b.Property("DismissedAt")
+ .HasColumnType("timestamp with time zone")
+ .HasColumnName("dismissed_at");
+
+ b.HasKey("User", "NotificationId");
+
+ b.ToTable("dismissed_notifications", (string)null);
+ });
+
+ modelBuilder.Entity("Agentweaver.Api.Memory.GitHubAppAuthorizationRecord", b =>
+ {
+ b.Property("Id")
+ .HasColumnType("text")
+ .HasColumnName("id");
+
+ b.Property("AppKind")
+ .HasColumnType("integer")
+ .HasColumnName("app_kind");
+
+ b.Property("CreatedAt")
+ .HasColumnType("timestamp with time zone")
+ .HasColumnName("created_at");
+
+ b.Property("CredentialReference")
+ .IsRequired()
+ .HasColumnType("text")
+ .HasColumnName("credential_reference");
+
+ b.Property("CredentialVersion")
+ .IsRequired()
+ .HasColumnType("text")
+ .HasColumnName("credential_version");
+
+ b.Property("EntraObjectId")
+ .IsRequired()
+ .HasColumnType("text")
+ .HasColumnName("entra_object_id");
+
+ b.Property("GrantDigest")
+ .IsRequired()
+ .HasColumnType("text")
+ .HasColumnName("grant_digest");
+
+ b.Property("Purpose")
+ .HasColumnType("integer")
+ .HasColumnName("purpose");
+
+ b.Property("RevokedAt")
+ .HasColumnType("timestamp with time zone")
+ .HasColumnName("revoked_at");
+
+ b.HasKey("Id");
+
+ b.HasIndex("EntraObjectId", "AppKind", "Purpose");
+
+ b.ToTable("github_app_authorizations", (string)null);
+ });
+
+ modelBuilder.Entity("Agentweaver.Api.Memory.GitHubAuditRecord", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("bigint")
+ .HasColumnName("id");
+
+ NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id"));
+
+ b.Property("Action")
+ .HasColumnType("integer")
+ .HasColumnName("action");
+
+ b.Property("ActorKind")
+ .HasColumnType("integer")
+ .HasColumnName("actor_kind");
+
+ b.Property("AppKind")
+ .HasColumnType("integer")
+ .HasColumnName("app_kind");
+
+ b.Property("CapabilityPurpose")
+ .HasColumnType("integer")
+ .HasColumnName("capability_purpose");
+
+ b.Property("CorrelationId")
+ .IsRequired()
+ .HasColumnType("text")
+ .HasColumnName("correlation_id");
+
+ b.Property("EntraObjectId")
+ .HasColumnType("text")
+ .HasColumnName("entra_object_id");
+
+ b.Property("GrantDigest")
+ .HasColumnType("text")
+ .HasColumnName("grant_digest");
+
+ b.Property("OccurredAt")
+ .HasColumnType("timestamp with time zone")
+ .HasColumnName("occurred_at");
+
+ b.Property("Outcome")
+ .HasColumnType("integer")
+ .HasColumnName("outcome");
+
+ b.Property("ReasonCode")
+ .HasColumnType("integer")
+ .HasColumnName("reason_code");
+
+ b.Property("ResourceId")
+ .IsRequired()
+ .HasColumnType("text")
+ .HasColumnName("resource_id");
+
+ b.HasKey("Id");
+
+ b.HasIndex("OccurredAt");
+
+ b.ToTable("github_audit_records", (string)null);
+ });
+
+ modelBuilder.Entity("Agentweaver.Api.Memory.GitHubAuthorizationRecord", b =>
+ {
+ b.Property("State")
+ .HasColumnType("text")
+ .HasColumnName("state");
+
+ b.Property("AppKind")
+ .HasColumnType("integer")
+ .HasColumnName("app_kind");
+
+ b.Property("BrowserSessionId")
+ .HasColumnType("text")
+ .HasColumnName("browser_session_id");
+
+ b.Property("CallbackCookieHash")
+ .IsRequired()
+ .HasColumnType("text")
+ .HasColumnName("callback_cookie_hash");
+
+ b.Property("CompletedAt")
+ .HasColumnType("timestamp with time zone")
+ .HasColumnName("completed_at");
+
+ b.Property("CreatedAt")
+ .HasColumnType("timestamp with time zone")
+ .HasColumnName("created_at");
+
+ b.Property("EntraObjectId")
+ .IsRequired()
+ .HasColumnType("text")
+ .HasColumnName("entra_object_id");
+
+ b.Property("ExpiresAtUnixMilliseconds")
+ .HasColumnType("bigint")
+ .HasColumnName("expires_at_unix_ms");
+
+ b.Property("ExternalTransactionId")
+ .IsRequired()
+ .HasColumnType("text")
+ .HasColumnName("external_transaction_id");
+
+ b.Property("PkceVerifierProtected")
+ .IsRequired()
+ .HasColumnType("text")
+ .HasColumnName("pkce_verifier_protected");
+
+ b.Property("ProjectId")
+ .HasColumnType("text")
+ .HasColumnName("project_id");
+
+ b.Property("Purpose")
+ .HasColumnType("integer")
+ .HasColumnName("purpose");
+
+ b.Property("ReturnRouteKey")
+ .IsRequired()
+ .HasColumnType("text")
+ .HasColumnName("return_route_key");
+
+ b.Property("Status")
+ .HasColumnType("integer")
+ .HasColumnName("status");
+
+ b.HasKey("State");
+
+ b.HasIndex("ExpiresAtUnixMilliseconds");
+
+ b.HasIndex("ExternalTransactionId")
+ .IsUnique();
+
+ b.HasIndex("ProjectId");
+
+ b.HasIndex("EntraObjectId", "State")
+ .IsUnique();
+
+ b.ToTable("github_authorizations", (string)null);
+ });
+
+ modelBuilder.Entity("Agentweaver.Api.Memory.GitHubInstallationRecord", b =>
+ {
+ b.Property("InstallationId")
+ .HasColumnType("bigint")
+ .HasColumnName("installation_id");
+
+ b.Property("AppKind")
+ .HasColumnType("integer")
+ .HasColumnName("app_kind");
+
+ b.Property("CreatedAt")
+ .HasColumnType("timestamp with time zone")
+ .HasColumnName("created_at");
+
+ b.Property("ProjectId")
+ .HasColumnType("text")
+ .HasColumnName("project_id");
+
+ b.Property("RevokedAt")
+ .HasColumnType("timestamp with time zone")
+ .HasColumnName("revoked_at");
+
+ b.HasKey("InstallationId");
+
+ b.HasIndex("ProjectId");
+
+ b.ToTable("github_installations", (string)null);
+ });
+
+ modelBuilder.Entity("Agentweaver.Api.Memory.GitHubLifecycleDeliveryRecord", b =>
+ {
+ b.Property("DeliveryId")
+ .HasColumnType("text")
+ .HasColumnName("delivery_id");
+
+ b.Property("EventName")
+ .IsRequired()
+ .HasColumnType("text")
+ .HasColumnName("event_name");
+
+ b.Property("InstallationId")
+ .HasColumnType("bigint")
+ .HasColumnName("installation_id");
+
+ b.Property("ReceivedAt")
+ .HasColumnType("timestamp with time zone")
+ .HasColumnName("received_at");
+
+ b.Property("RepositoryId")
+ .HasColumnType("bigint")
+ .HasColumnName("repository_id");
+
+ b.HasKey("DeliveryId");
+
+ b.ToTable("github_lifecycle_deliveries", (string)null);
+ });
+
+ modelBuilder.Entity("Agentweaver.Api.Memory.GitHubRepositoryGrantRecord", b =>
+ {
+ b.Property("InstallationId")
+ .HasColumnType("bigint")
+ .HasColumnName("installation_id");
+
+ b.Property("RepositoryId")
+ .HasColumnType("bigint")
+ .HasColumnName("repository_id");
+
+ b.Property("FullNameDisplay")
+ .IsRequired()
+ .HasColumnType("text")
+ .HasColumnName("full_name_display");
+
+ b.Property("GrantedAt")
+ .HasColumnType("timestamp with time zone")
+ .HasColumnName("granted_at");
+
+ b.Property("PermissionDigest")
+ .IsRequired()
+ .HasColumnType("text")
+ .HasColumnName("permission_digest");
+
+ b.Property("ProjectId")
+ .IsRequired()
+ .HasColumnType("text")
+ .HasColumnName("project_id");
+
+ b.Property("RevokedAt")
+ .HasColumnType("timestamp with time zone")
+ .HasColumnName("revoked_at");
+
+ b.HasKey("InstallationId", "RepositoryId");
+
+ b.HasIndex("ProjectId");
+
+ b.HasIndex("InstallationId", "RepositoryId")
+ .IsUnique();
+
+ b.ToTable("github_repository_grants", (string)null);
+ });
+
+ modelBuilder.Entity("Agentweaver.Api.Memory.GitHubRepositorySelectionCodeRecord", b =>
+ {
+ b.Property("CodeHash")
+ .HasColumnType("text")
+ .HasColumnName("code_hash");
+
+ b.Property("ConsumedAtUnixMilliseconds")
+ .HasColumnType("bigint")
+ .HasColumnName("consumed_at_unix_ms");
+
+ b.Property("CreatedAt")
+ .HasColumnType("timestamp with time zone")
+ .HasColumnName("created_at");
+
+ b.Property("EntraObjectId")
+ .IsRequired()
+ .HasColumnType("text")
+ .HasColumnName("entra_object_id");
+
+ b.Property("ExpiresAtUnixMilliseconds")
+ .HasColumnType("bigint")
+ .HasColumnName("expires_at_unix_ms");
+
+ b.Property("RepoAppAuthorizationId")
+ .IsRequired()
+ .HasColumnType("text")
+ .HasColumnName("repo_app_authorization_id");
+
+ b.Property("RepositoryId")
+ .HasColumnType("bigint")
+ .HasColumnName("repository_id");
+
+ b.HasKey("CodeHash");
+
+ b.HasIndex("ExpiresAtUnixMilliseconds");
+
+ b.HasIndex("EntraObjectId", "ExpiresAtUnixMilliseconds");
+
+ b.ToTable("github_repository_selection_codes", (string)null);
+ });
+
+ modelBuilder.Entity("Agentweaver.Api.Memory.IntegrationBuildLockRecord", b =>
+ {
+ b.Property("ProjectId")
+ .HasColumnType("text");
+
+ b.Property("AcquiredAt")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("OwnerPodId")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("OwnerToken")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.HasKey("ProjectId");
+
+ b.ToTable("IntegrationBuildLocks");
+ });
+
+ modelBuilder.Entity("Agentweaver.Api.Memory.MarketplaceCopilotCapabilityRecord", b =>
+ {
+ b.Property("CapabilityRef")
+ .HasColumnType("text")
+ .HasColumnName("capability_ref");
+
+ b.Property("ClaimLeaseExpiresAt")
+ .HasColumnType("timestamp with time zone")
+ .HasColumnName("claim_lease_expires_at");
+
+ b.Property("ConsumedAt")
+ .HasColumnType("timestamp with time zone")
+ .HasColumnName("consumed_at");
+
+ b.Property