diff --git a/CHANGELOG.md b/CHANGELOG.md
index e3cab07d1..f4795859e 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,11 @@
# Changelog
+## 0.28.5
+
+### Patch Changes
+
+- 01127ac: Allow OAuth consent submissions to reach only the validated registered client callback while preserving a strict content security policy.
+
## 0.28.4
### Patch Changes
diff --git a/VERSION b/VERSION
index 097bc9362..16b6bcee6 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-0.28.4
+0.28.5
diff --git a/apps/Agentweaver.Api.Data/Auth/OAuth/OAuthServerRecords.cs b/apps/Agentweaver.Api.Data/Auth/OAuth/OAuthServerRecords.cs
index 01d9220f7..80a6ec050 100644
--- a/apps/Agentweaver.Api.Data/Auth/OAuth/OAuthServerRecords.cs
+++ b/apps/Agentweaver.Api.Data/Auth/OAuth/OAuthServerRecords.cs
@@ -22,6 +22,7 @@ public sealed class OAuthAuthorizationTransaction
public string? ClientState { get; set; }
public string? BrowserSessionId { get; set; }
public string? Subject { get; set; }
+ public string? ContinuationDecision { get; set; }
public DateTimeOffset ExpiresAt { get; set; }
public DateTimeOffset? ConsumedAt { get; set; }
}
diff --git a/apps/Agentweaver.Api.Migrations.Postgres/Migrations/20260904224046_AddOAuthConsentContinuationDecisionPostgres.Designer.cs b/apps/Agentweaver.Api.Migrations.Postgres/Migrations/20260904224046_AddOAuthConsentContinuationDecisionPostgres.Designer.cs
new file mode 100644
index 000000000..f7f20948d
--- /dev/null
+++ b/apps/Agentweaver.Api.Migrations.Postgres/Migrations/20260904224046_AddOAuthConsentContinuationDecisionPostgres.Designer.cs
@@ -0,0 +1,3437 @@
+//
+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("20260904224046_AddOAuthConsentContinuationDecisionPostgres")]
+ partial class AddOAuthConsentContinuationDecisionPostgres
+ {
+ ///
+ protected override void BuildTargetModel(ModelBuilder modelBuilder)
+ {
+#pragma warning disable 612, 618
+ modelBuilder
+ .HasAnnotation("ProductVersion", "10.0.10")
+ .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.Property("PlatformRoles")
+ .IsRequired()
+ .HasColumnType("text");
+
+ 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.Property("Nonce")
+ .HasColumnType("text");
+
+ b.Property("ReturnHandle")
+ .HasColumnType("text");
+
+ b.HasKey("State");
+
+ b.HasIndex("ExpiresAt");
+
+ b.ToTable("EntraOAuthStates");
+ });
+
+ modelBuilder.Entity("Agentweaver.Api.Auth.OAuth.OAuthAuthorizationTransaction", b =>
+ {
+ b.Property("HandleHash")
+ .HasColumnType("text");
+
+ b.Property("BrowserSessionId")
+ .HasColumnType("text");
+
+ b.Property("ClientId")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("ClientState")
+ .HasColumnType("text");
+
+ b.Property("CodeChallenge")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("ConsumedAt")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("ContinuationDecision")
+ .HasColumnType("text");
+
+ b.Property("ExpiresAt")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("RedirectUri")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("Scope")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("Subject")
+ .HasColumnType("text");
+
+ b.HasKey("HandleHash");
+
+ b.HasIndex("ExpiresAt");
+
+ b.ToTable("OAuthAuthorizationTransactions");
+ });
+
+ modelBuilder.Entity("Agentweaver.Api.Auth.OAuth.OAuthConsentRecord", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("uuid");
+
+ b.Property("ClientId")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("RevokedAt")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("Scopes")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("Subject")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("UpdatedAt")
+ .HasColumnType("timestamp with time zone");
+
+ b.HasKey("Id");
+
+ b.HasIndex("Subject", "ClientId")
+ .IsUnique();
+
+ b.ToTable("OAuthConsents");
+ });
+
+ modelBuilder.Entity("Agentweaver.Api.Auth.OAuth.OAuthDynamicRegistration", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("uuid");
+
+ b.Property("ClientId")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("DisabledAt")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("RegisteredAt")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("SourceHash")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.HasKey("Id");
+
+ b.HasIndex("ClientId")
+ .IsUnique();
+
+ b.HasIndex("SourceHash", "RegisteredAt");
+
+ b.ToTable("OAuthDynamicRegistrations");
+ });
+
+ modelBuilder.Entity("Agentweaver.Api.Auth.OAuth.OAuthMaintenanceLease", b =>
+ {
+ b.Property("Name")
+ .HasColumnType("text");
+
+ b.Property("LeaseExpiresAt")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("Owner")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.HasKey("Name");
+
+ b.ToTable("OAuthMaintenanceLeases");
+ });
+
+ modelBuilder.Entity("Agentweaver.Api.Auth.OAuth.OAuthRefreshTokenFamily", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("uuid");
+
+ b.Property("AuthorizationId")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("ClientId")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("CreatedAt")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("RevocationReason")
+ .HasColumnType("text");
+
+ b.Property("RevokedAt")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("Subject")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.HasKey("Id");
+
+ b.HasIndex("AuthorizationId")
+ .IsUnique();
+
+ b.ToTable("OAuthRefreshTokenFamilies");
+ });
+
+ 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("ByokProviderId")
+ .HasColumnType("text")
+ .HasColumnName("byok_provider_id");
+
+ 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("ModelProviderSource")
+ .HasColumnType("integer")
+ .HasColumnName("model_provider_source");
+
+ 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", null, t =>
+ {
+ t.HasCheckConstraint("CK_automation_activations_snapshot_tuple", "status <> 0 OR (\n (\n (installation_id IS NULL AND repository_id IS NULL AND repository_grant_digest IS NULL)\n OR\n (installation_id IS NOT NULL AND installation_id > 0 AND\n repository_id IS NOT NULL AND repository_id > 0 AND\n repository_grant_digest IS NOT NULL AND repository_grant_digest <> '')\n ) AND (\n (model_provider_source = 1 AND\n byok_provider_id IS NOT NULL AND byok_provider_id <> '' AND\n (copilot_binding_id IS NULL OR copilot_binding_id = '') AND\n (copilot_binding_grant_digest IS NULL OR copilot_binding_grant_digest = ''))\n OR\n (model_provider_source <> 1 AND\n copilot_binding_id IS NOT NULL AND copilot_binding_id <> '' AND\n copilot_binding_grant_digest IS NOT NULL AND copilot_binding_grant_digest <> '' AND\n (byok_provider_id IS NULL OR byok_provider_id = ''))\n ))");
+ });
+ });
+
+ 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.AutomationProjectGuardRecord", b =>
+ {
+ b.Property("ProjectId")
+ .HasColumnType("text")
+ .HasColumnName("project_id");
+
+ b.Property("RepositoryAttached")
+ .HasColumnType("boolean")
+ .HasColumnName("repository_attached");
+
+ b.HasKey("ProjectId");
+
+ b.ToTable("automation_project_guards", (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