@rudderhq/db 0.7.1 → 0.7.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/backup-lib.d.ts.map +1 -1
- package/dist/backup-lib.js +11 -3
- package/dist/backup-lib.js.map +1 -1
- package/dist/client.d.ts +25 -1
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +304 -20
- package/dist/client.js.map +1 -1
- package/dist/client.test.js +217 -29
- package/dist/client.test.js.map +1 -1
- package/dist/embedded-postgres-recovery.d.ts +11 -0
- package/dist/embedded-postgres-recovery.d.ts.map +1 -1
- package/dist/embedded-postgres-recovery.js +50 -11
- package/dist/embedded-postgres-recovery.js.map +1 -1
- package/dist/embedded-postgres-recovery.test.js +50 -2
- package/dist/embedded-postgres-recovery.test.js.map +1 -1
- package/dist/index.d.ts +3 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -2
- package/dist/index.js.map +1 -1
- package/dist/local-postgres-provider.d.ts +3 -0
- package/dist/local-postgres-provider.d.ts.map +1 -1
- package/dist/local-postgres-provider.js +92 -10
- package/dist/local-postgres-provider.js.map +1 -1
- package/dist/local-postgres-provider.test.js +24 -1
- package/dist/local-postgres-provider.test.js.map +1 -1
- package/dist/migration-manifest.d.ts +41 -0
- package/dist/migration-manifest.d.ts.map +1 -0
- package/dist/migration-manifest.js +186 -0
- package/dist/migration-manifest.js.map +1 -0
- package/dist/migration-manifest.test.d.ts +2 -0
- package/dist/migration-manifest.test.d.ts.map +1 -0
- package/dist/migration-manifest.test.js +88 -0
- package/dist/migration-manifest.test.js.map +1 -0
- package/dist/migration-runtime.d.ts.map +1 -1
- package/dist/migration-runtime.js +18 -33
- package/dist/migration-runtime.js.map +1 -1
- package/dist/migrations/0143_goal_contract.sql +97 -0
- package/dist/migrations/0144_eminent_umar.sql +16 -0
- package/dist/migrations/0145_public_nehzno.sql +96 -0
- package/dist/migrations/0146_medical_roulette.sql +1 -0
- package/dist/migrations/0147_github_mcp_provider.sql +4 -0
- package/dist/migrations/0148_chat_message_transcript_entries.sql +20 -0
- package/dist/migrations/chat-transcript-storage.test.ts +211 -0
- package/dist/migrations/github-mcp-provider.test.ts +20 -0
- package/dist/migrations/meta/0143_snapshot.json +25318 -0
- package/dist/migrations/meta/0144_snapshot.json +25336 -0
- package/dist/migrations/meta/0145_snapshot.json +27010 -0
- package/dist/migrations/meta/0146_snapshot.json +27016 -0
- package/dist/migrations/meta/0147_snapshot.json +27016 -0
- package/dist/migrations/meta/_journal.json +42 -0
- package/dist/schema/chat_message_transcript_entries.d.ts +82 -0
- package/dist/schema/chat_message_transcript_entries.d.ts.map +1 -0
- package/dist/schema/chat_message_transcript_entries.js +20 -0
- package/dist/schema/chat_message_transcript_entries.js.map +1 -0
- package/dist/schema/goals.d.ts +2110 -0
- package/dist/schema/goals.d.ts.map +1 -1
- package/dist/schema/goals.js +169 -1
- package/dist/schema/goals.js.map +1 -1
- package/dist/schema/index.d.ts +2 -1
- package/dist/schema/index.d.ts.map +1 -1
- package/dist/schema/index.js +2 -1
- package/dist/schema/index.js.map +1 -1
- package/dist/schema/mcp_connections.js +2 -2
- package/dist/schema/mcp_connections.js.map +1 -1
- package/package.json +2 -2
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
ALTER TABLE "goals" ADD COLUMN IF NOT EXISTS "outcome_statement" text;
|
|
2
|
+
ALTER TABLE "goals" ADD COLUMN IF NOT EXISTS "objective_mode" text DEFAULT 'target' NOT NULL;
|
|
3
|
+
ALTER TABLE "goals" ADD COLUMN IF NOT EXISTS "lifecycle" text DEFAULT 'draft' NOT NULL;
|
|
4
|
+
ALTER TABLE "goals" ADD COLUMN IF NOT EXISTS "contract_revision" integer DEFAULT 1 NOT NULL;
|
|
5
|
+
ALTER TABLE "goals" ADD COLUMN IF NOT EXISTS "criteria" jsonb DEFAULT '[]'::jsonb NOT NULL;
|
|
6
|
+
ALTER TABLE "goals" ADD COLUMN IF NOT EXISTS "autonomy_envelope" jsonb DEFAULT '{}'::jsonb NOT NULL;
|
|
7
|
+
ALTER TABLE "goals" ADD COLUMN IF NOT EXISTS "human_authorities" jsonb DEFAULT '{}'::jsonb NOT NULL;
|
|
8
|
+
ALTER TABLE "goals" ADD COLUMN IF NOT EXISTS "evaluation_policy" jsonb DEFAULT '{}'::jsonb NOT NULL;
|
|
9
|
+
ALTER TABLE "goals" ADD COLUMN IF NOT EXISTS "action_deadline" timestamp with time zone;
|
|
10
|
+
ALTER TABLE "goals" ADD COLUMN IF NOT EXISTS "evaluation_deadline" timestamp with time zone;
|
|
11
|
+
ALTER TABLE "goals" ADD COLUMN IF NOT EXISTS "evaluation_result" jsonb;
|
|
12
|
+
ALTER TABLE "goals" ADD COLUMN IF NOT EXISTS "close_reason" text;
|
|
13
|
+
ALTER TABLE "goals" ADD COLUMN IF NOT EXISTS "result_payload" jsonb;
|
|
14
|
+
ALTER TABLE "goals" ADD COLUMN IF NOT EXISTS "focus" boolean DEFAULT false NOT NULL;
|
|
15
|
+
ALTER TABLE "goals" ADD COLUMN IF NOT EXISTS "plan_revision" integer DEFAULT 0 NOT NULL;
|
|
16
|
+
|
|
17
|
+
UPDATE "goals"
|
|
18
|
+
SET
|
|
19
|
+
"lifecycle" = CASE
|
|
20
|
+
-- Legacy active rows have no canonical Contract/Owner/Plan proof. They
|
|
21
|
+
-- must be explicitly activated after migration instead of bypassing Draft.
|
|
22
|
+
WHEN "status" = 'active' THEN 'draft'
|
|
23
|
+
WHEN "status" IN ('achieved', 'cancelled') THEN 'closed'
|
|
24
|
+
ELSE 'draft'
|
|
25
|
+
END,
|
|
26
|
+
"close_reason" = CASE
|
|
27
|
+
WHEN "status" = 'achieved' THEN 'evaluated'
|
|
28
|
+
WHEN "status" = 'cancelled' THEN 'cancelled'
|
|
29
|
+
ELSE "close_reason"
|
|
30
|
+
END;
|
|
31
|
+
|
|
32
|
+
CREATE UNIQUE INDEX IF NOT EXISTS "goals_org_focus_uq"
|
|
33
|
+
ON "goals" USING btree ("org_id")
|
|
34
|
+
WHERE "focus" = true;
|
|
35
|
+
|
|
36
|
+
CREATE TABLE IF NOT EXISTS "goal_owner_assignments" (
|
|
37
|
+
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
|
|
38
|
+
"org_id" uuid NOT NULL REFERENCES "organizations"("id") ON DELETE cascade,
|
|
39
|
+
"goal_id" uuid NOT NULL REFERENCES "goals"("id") ON DELETE cascade,
|
|
40
|
+
"agent_id" uuid NOT NULL REFERENCES "agents"("id"),
|
|
41
|
+
"assignment_revision" integer DEFAULT 1 NOT NULL,
|
|
42
|
+
"assigned_by_authority_ref" text,
|
|
43
|
+
"starts_at" timestamp with time zone DEFAULT now() NOT NULL,
|
|
44
|
+
"ends_at" timestamp with time zone,
|
|
45
|
+
"created_at" timestamp with time zone DEFAULT now() NOT NULL
|
|
46
|
+
);
|
|
47
|
+
CREATE INDEX IF NOT EXISTS "goal_owner_assignments_goal_idx"
|
|
48
|
+
ON "goal_owner_assignments" USING btree ("goal_id", "starts_at");
|
|
49
|
+
CREATE UNIQUE INDEX IF NOT EXISTS "goal_owner_assignments_current_goal_uq"
|
|
50
|
+
ON "goal_owner_assignments" USING btree ("goal_id")
|
|
51
|
+
WHERE "ends_at" IS NULL;
|
|
52
|
+
|
|
53
|
+
CREATE TABLE IF NOT EXISTS "goal_plans" (
|
|
54
|
+
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
|
|
55
|
+
"org_id" uuid NOT NULL REFERENCES "organizations"("id") ON DELETE cascade,
|
|
56
|
+
"goal_id" uuid NOT NULL REFERENCES "goals"("id") ON DELETE cascade,
|
|
57
|
+
"revision" integer NOT NULL,
|
|
58
|
+
"summary" text NOT NULL,
|
|
59
|
+
"hypotheses" jsonb DEFAULT '[]'::jsonb NOT NULL,
|
|
60
|
+
"selected_paths" jsonb DEFAULT '[]'::jsonb NOT NULL,
|
|
61
|
+
"rejected_paths" jsonb DEFAULT '[]'::jsonb NOT NULL,
|
|
62
|
+
"sequencing" jsonb DEFAULT '[]'::jsonb NOT NULL,
|
|
63
|
+
"budget_allocations" jsonb DEFAULT '{}'::jsonb NOT NULL,
|
|
64
|
+
"invalidation_conditions" jsonb DEFAULT '[]'::jsonb NOT NULL,
|
|
65
|
+
"created_by_agent_id" uuid REFERENCES "agents"("id") ON DELETE set null,
|
|
66
|
+
"created_at" timestamp with time zone DEFAULT now() NOT NULL,
|
|
67
|
+
"updated_at" timestamp with time zone DEFAULT now() NOT NULL
|
|
68
|
+
);
|
|
69
|
+
CREATE UNIQUE INDEX IF NOT EXISTS "goal_plans_goal_revision_uq"
|
|
70
|
+
ON "goal_plans" USING btree ("goal_id", "revision");
|
|
71
|
+
CREATE INDEX IF NOT EXISTS "goal_plans_goal_revision_idx"
|
|
72
|
+
ON "goal_plans" USING btree ("goal_id", "revision");
|
|
73
|
+
|
|
74
|
+
CREATE TABLE IF NOT EXISTS "goal_activities" (
|
|
75
|
+
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
|
|
76
|
+
"org_id" uuid NOT NULL REFERENCES "organizations"("id") ON DELETE cascade,
|
|
77
|
+
"goal_id" uuid NOT NULL REFERENCES "goals"("id") ON DELETE cascade,
|
|
78
|
+
"contract_revision" integer NOT NULL,
|
|
79
|
+
"submitted_by_agent_id" uuid REFERENCES "agents"("id") ON DELETE set null,
|
|
80
|
+
"agent_owner_ref_at_time" uuid REFERENCES "agents"("id") ON DELETE set null,
|
|
81
|
+
"commitment_ref" text,
|
|
82
|
+
"run_ref" uuid,
|
|
83
|
+
"activity_kind" text,
|
|
84
|
+
"summary" text NOT NULL,
|
|
85
|
+
"evidence_refs" jsonb DEFAULT '[]'::jsonb NOT NULL,
|
|
86
|
+
"idempotency_key" text,
|
|
87
|
+
"occurred_at" timestamp with time zone DEFAULT now() NOT NULL,
|
|
88
|
+
"created_at" timestamp with time zone DEFAULT now() NOT NULL
|
|
89
|
+
);
|
|
90
|
+
CREATE INDEX IF NOT EXISTS "goal_activities_goal_occurred_idx"
|
|
91
|
+
ON "goal_activities" USING btree ("goal_id", "occurred_at");
|
|
92
|
+
CREATE UNIQUE INDEX IF NOT EXISTS "goal_activities_closeout_run_uq"
|
|
93
|
+
ON "goal_activities" USING btree ("goal_id", "run_ref", "activity_kind")
|
|
94
|
+
WHERE "activity_kind" = 'closeout' AND "run_ref" IS NOT NULL;
|
|
95
|
+
CREATE UNIQUE INDEX IF NOT EXISTS "goal_activities_idempotency_uq"
|
|
96
|
+
ON "goal_activities" USING btree ("goal_id", "idempotency_key")
|
|
97
|
+
WHERE "idempotency_key" IS NOT NULL;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
ALTER TABLE "goals" ADD COLUMN "continuation_kind" text;--> statement-breakpoint
|
|
2
|
+
ALTER TABLE "goals" ADD COLUMN "continuation_summary" text;--> statement-breakpoint
|
|
3
|
+
ALTER TABLE "goals" ADD COLUMN "wake_condition" text;--> statement-breakpoint
|
|
4
|
+
|
|
5
|
+
UPDATE "goals"
|
|
6
|
+
SET
|
|
7
|
+
"continuation_kind" = COALESCE("continuation_kind", 'verification'),
|
|
8
|
+
"continuation_summary" = COALESCE(
|
|
9
|
+
"continuation_summary",
|
|
10
|
+
'Review the legacy Goal contract and define the next bounded verification'
|
|
11
|
+
)
|
|
12
|
+
WHERE "status" = 'active'
|
|
13
|
+
AND "lifecycle" = 'draft'
|
|
14
|
+
AND "outcome_statement" IS NOT NULL
|
|
15
|
+
AND jsonb_array_length("criteria") > 0
|
|
16
|
+
AND "owner_agent_id" IS NOT NULL;--> statement-breakpoint
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
CREATE TABLE "goal_change_proposals" (
|
|
2
|
+
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
|
|
3
|
+
"org_id" uuid NOT NULL,
|
|
4
|
+
"goal_id" uuid NOT NULL,
|
|
5
|
+
"expected_contract_revision" integer NOT NULL,
|
|
6
|
+
"before_contract" jsonb NOT NULL,
|
|
7
|
+
"after_contract" jsonb NOT NULL,
|
|
8
|
+
"rationale" text NOT NULL,
|
|
9
|
+
"evidence_refs" jsonb DEFAULT '[]'::jsonb NOT NULL,
|
|
10
|
+
"approval_id" uuid NOT NULL,
|
|
11
|
+
"status" text DEFAULT 'pending' NOT NULL,
|
|
12
|
+
"idempotency_key" text NOT NULL,
|
|
13
|
+
"proposed_by_agent_id" uuid NOT NULL,
|
|
14
|
+
"applied_revision" integer,
|
|
15
|
+
"applied_at" timestamp with time zone,
|
|
16
|
+
"created_at" timestamp with time zone DEFAULT now() NOT NULL,
|
|
17
|
+
"updated_at" timestamp with time zone DEFAULT now() NOT NULL
|
|
18
|
+
);
|
|
19
|
+
--> statement-breakpoint
|
|
20
|
+
CREATE TABLE "goal_feedback_entries" (
|
|
21
|
+
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
|
|
22
|
+
"org_id" uuid NOT NULL,
|
|
23
|
+
"goal_id" uuid NOT NULL,
|
|
24
|
+
"actor_type" text NOT NULL,
|
|
25
|
+
"actor_id" text NOT NULL,
|
|
26
|
+
"body" text NOT NULL,
|
|
27
|
+
"attachments" jsonb DEFAULT '[]'::jsonb NOT NULL,
|
|
28
|
+
"content_hash" text NOT NULL,
|
|
29
|
+
"feedback_kind" text DEFAULT 'ordinary' NOT NULL,
|
|
30
|
+
"idempotency_key" text NOT NULL,
|
|
31
|
+
"routed_wakeup_request_id" uuid,
|
|
32
|
+
"created_at" timestamp with time zone DEFAULT now() NOT NULL,
|
|
33
|
+
"updated_at" timestamp with time zone DEFAULT now() NOT NULL
|
|
34
|
+
);
|
|
35
|
+
--> statement-breakpoint
|
|
36
|
+
CREATE TABLE "goal_result_proposals" (
|
|
37
|
+
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
|
|
38
|
+
"org_id" uuid NOT NULL,
|
|
39
|
+
"goal_id" uuid NOT NULL,
|
|
40
|
+
"contract_revision" integer NOT NULL,
|
|
41
|
+
"candidate" jsonb NOT NULL,
|
|
42
|
+
"candidate_hash" text NOT NULL,
|
|
43
|
+
"preflight" jsonb NOT NULL,
|
|
44
|
+
"risk_summary" text NOT NULL,
|
|
45
|
+
"status" text DEFAULT 'ready' NOT NULL,
|
|
46
|
+
"idempotency_key" text NOT NULL,
|
|
47
|
+
"proposed_by_agent_id" uuid NOT NULL,
|
|
48
|
+
"accepted_by_actor_type" text,
|
|
49
|
+
"accepted_by_actor_id" text,
|
|
50
|
+
"accepted_at" timestamp with time zone,
|
|
51
|
+
"rejected_by_actor_type" text,
|
|
52
|
+
"rejected_by_actor_id" text,
|
|
53
|
+
"rejected_at" timestamp with time zone,
|
|
54
|
+
"rejection_feedback" text,
|
|
55
|
+
"consumed_at" timestamp with time zone,
|
|
56
|
+
"created_at" timestamp with time zone DEFAULT now() NOT NULL,
|
|
57
|
+
"updated_at" timestamp with time zone DEFAULT now() NOT NULL
|
|
58
|
+
);
|
|
59
|
+
--> statement-breakpoint
|
|
60
|
+
CREATE TABLE "goal_start_requests" (
|
|
61
|
+
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
|
|
62
|
+
"org_id" uuid NOT NULL,
|
|
63
|
+
"request_key" text NOT NULL,
|
|
64
|
+
"packet_hash" text NOT NULL,
|
|
65
|
+
"packet" jsonb NOT NULL,
|
|
66
|
+
"draft_goal_id" uuid,
|
|
67
|
+
"goal_id" uuid,
|
|
68
|
+
"status" text DEFAULT 'pending' NOT NULL,
|
|
69
|
+
"completed_at" timestamp with time zone,
|
|
70
|
+
"created_at" timestamp with time zone DEFAULT now() NOT NULL,
|
|
71
|
+
"updated_at" timestamp with time zone DEFAULT now() NOT NULL
|
|
72
|
+
);
|
|
73
|
+
--> statement-breakpoint
|
|
74
|
+
ALTER TABLE "goals" ADD COLUMN "alignment_question" text;--> statement-breakpoint
|
|
75
|
+
ALTER TABLE "goal_change_proposals" ADD CONSTRAINT "goal_change_proposals_org_id_organizations_id_fk" FOREIGN KEY ("org_id") REFERENCES "public"."organizations"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
|
76
|
+
ALTER TABLE "goal_change_proposals" ADD CONSTRAINT "goal_change_proposals_goal_id_goals_id_fk" FOREIGN KEY ("goal_id") REFERENCES "public"."goals"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
|
77
|
+
ALTER TABLE "goal_change_proposals" ADD CONSTRAINT "goal_change_proposals_approval_id_approvals_id_fk" FOREIGN KEY ("approval_id") REFERENCES "public"."approvals"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint
|
|
78
|
+
ALTER TABLE "goal_change_proposals" ADD CONSTRAINT "goal_change_proposals_proposed_by_agent_id_agents_id_fk" FOREIGN KEY ("proposed_by_agent_id") REFERENCES "public"."agents"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint
|
|
79
|
+
ALTER TABLE "goal_feedback_entries" ADD CONSTRAINT "goal_feedback_entries_org_id_organizations_id_fk" FOREIGN KEY ("org_id") REFERENCES "public"."organizations"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
|
80
|
+
ALTER TABLE "goal_feedback_entries" ADD CONSTRAINT "goal_feedback_entries_goal_id_goals_id_fk" FOREIGN KEY ("goal_id") REFERENCES "public"."goals"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
|
81
|
+
ALTER TABLE "goal_feedback_entries" ADD CONSTRAINT "goal_feedback_entries_routed_wakeup_request_id_agent_wakeup_requests_id_fk" FOREIGN KEY ("routed_wakeup_request_id") REFERENCES "public"."agent_wakeup_requests"("id") ON DELETE set null ON UPDATE no action;--> statement-breakpoint
|
|
82
|
+
ALTER TABLE "goal_result_proposals" ADD CONSTRAINT "goal_result_proposals_org_id_organizations_id_fk" FOREIGN KEY ("org_id") REFERENCES "public"."organizations"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
|
83
|
+
ALTER TABLE "goal_result_proposals" ADD CONSTRAINT "goal_result_proposals_goal_id_goals_id_fk" FOREIGN KEY ("goal_id") REFERENCES "public"."goals"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
|
84
|
+
ALTER TABLE "goal_result_proposals" ADD CONSTRAINT "goal_result_proposals_proposed_by_agent_id_agents_id_fk" FOREIGN KEY ("proposed_by_agent_id") REFERENCES "public"."agents"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint
|
|
85
|
+
ALTER TABLE "goal_start_requests" ADD CONSTRAINT "goal_start_requests_org_id_organizations_id_fk" FOREIGN KEY ("org_id") REFERENCES "public"."organizations"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
|
86
|
+
ALTER TABLE "goal_start_requests" ADD CONSTRAINT "goal_start_requests_draft_goal_id_goals_id_fk" FOREIGN KEY ("draft_goal_id") REFERENCES "public"."goals"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
|
87
|
+
ALTER TABLE "goal_start_requests" ADD CONSTRAINT "goal_start_requests_goal_id_goals_id_fk" FOREIGN KEY ("goal_id") REFERENCES "public"."goals"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
|
88
|
+
CREATE INDEX "goal_change_proposals_goal_status_created_idx" ON "goal_change_proposals" USING btree ("goal_id","status","created_at");--> statement-breakpoint
|
|
89
|
+
CREATE UNIQUE INDEX "goal_change_proposals_goal_idempotency_uq" ON "goal_change_proposals" USING btree ("goal_id","idempotency_key");--> statement-breakpoint
|
|
90
|
+
CREATE UNIQUE INDEX "goal_change_proposals_approval_uq" ON "goal_change_proposals" USING btree ("approval_id");--> statement-breakpoint
|
|
91
|
+
CREATE INDEX "goal_feedback_entries_goal_created_idx" ON "goal_feedback_entries" USING btree ("goal_id","created_at");--> statement-breakpoint
|
|
92
|
+
CREATE UNIQUE INDEX "goal_feedback_entries_goal_idempotency_uq" ON "goal_feedback_entries" USING btree ("goal_id","idempotency_key");--> statement-breakpoint
|
|
93
|
+
CREATE INDEX "goal_result_proposals_goal_status_created_idx" ON "goal_result_proposals" USING btree ("goal_id","status","created_at");--> statement-breakpoint
|
|
94
|
+
CREATE UNIQUE INDEX "goal_result_proposals_goal_idempotency_uq" ON "goal_result_proposals" USING btree ("goal_id","idempotency_key");--> statement-breakpoint
|
|
95
|
+
CREATE UNIQUE INDEX "goal_start_requests_org_request_key_uq" ON "goal_start_requests" USING btree ("org_id","request_key");--> statement-breakpoint
|
|
96
|
+
CREATE INDEX "goal_start_requests_org_status_updated_idx" ON "goal_start_requests" USING btree ("org_id","status","updated_at");
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
ALTER TABLE "goal_result_proposals" ADD COLUMN "acceptance_idempotency_key" text;
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
DROP INDEX "mcp_connections_org_official_canonical_uq";--> statement-breakpoint
|
|
2
|
+
DROP INDEX "mcp_connections_agent_official_canonical_uq";--> statement-breakpoint
|
|
3
|
+
CREATE UNIQUE INDEX "mcp_connections_org_official_canonical_uq" ON "mcp_connections" USING btree ("org_id","provider") WHERE "mcp_connections"."provider" in ('supabase', 'linear', 'notion', 'github') and "mcp_connections"."canonical_state" = 'canonical' and "mcp_connections"."scope" = 'organization';--> statement-breakpoint
|
|
4
|
+
CREATE UNIQUE INDEX "mcp_connections_agent_official_canonical_uq" ON "mcp_connections" USING btree ("org_id","owner_agent_id","provider") WHERE "mcp_connections"."provider" in ('supabase', 'linear', 'notion', 'github') and "mcp_connections"."canonical_state" = 'canonical' and "mcp_connections"."scope" = 'agent';
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
CREATE TABLE "chat_message_transcript_entries" (
|
|
2
|
+
"org_id" uuid NOT NULL,
|
|
3
|
+
"message_id" uuid NOT NULL,
|
|
4
|
+
"entry_seq" integer NOT NULL,
|
|
5
|
+
"payload" jsonb NOT NULL,
|
|
6
|
+
CONSTRAINT "chat_message_transcript_entries_message_seq_pk" PRIMARY KEY("message_id","entry_seq")
|
|
7
|
+
);
|
|
8
|
+
--> statement-breakpoint
|
|
9
|
+
ALTER TABLE "chat_message_transcript_entries" ADD CONSTRAINT "chat_message_transcript_entries_org_id_organizations_id_fk" FOREIGN KEY ("org_id") REFERENCES "public"."organizations"("id") ON DELETE cascade ON UPDATE no action;
|
|
10
|
+
--> statement-breakpoint
|
|
11
|
+
ALTER TABLE "chat_message_transcript_entries" ADD CONSTRAINT "chat_message_transcript_entries_message_id_chat_messages_id_fk" FOREIGN KEY ("message_id") REFERENCES "public"."chat_messages"("id") ON DELETE cascade ON UPDATE no action;
|
|
12
|
+
--> statement-breakpoint
|
|
13
|
+
CREATE INDEX "chat_message_transcript_entries_org_message_seq_idx" ON "chat_message_transcript_entries" USING btree ("org_id","message_id","entry_seq");
|
|
14
|
+
--> statement-breakpoint
|
|
15
|
+
ALTER TABLE "chat_messages" SET (
|
|
16
|
+
autovacuum_vacuum_scale_factor = 0.05,
|
|
17
|
+
autovacuum_vacuum_threshold = 500,
|
|
18
|
+
autovacuum_analyze_threshold = 500,
|
|
19
|
+
autovacuum_analyze_scale_factor = 0.02
|
|
20
|
+
);
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
import { spawn } from "node:child_process";
|
|
2
|
+
import { randomUUID } from "node:crypto";
|
|
3
|
+
import fs from "node:fs";
|
|
4
|
+
import net from "node:net";
|
|
5
|
+
import os from "node:os";
|
|
6
|
+
import path from "node:path";
|
|
7
|
+
import { fileURLToPath } from "node:url";
|
|
8
|
+
import postgres from "postgres";
|
|
9
|
+
import { describe, expect, it } from "vitest";
|
|
10
|
+
import { applyPendingMigrations, ensurePostgresDatabase } from "../client.js";
|
|
11
|
+
import { createLocalPostgresInstance } from "../local-postgres-provider.js";
|
|
12
|
+
|
|
13
|
+
const repositoryRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../../../..");
|
|
14
|
+
|
|
15
|
+
type BackfillReport = {
|
|
16
|
+
legacyCandidates: number;
|
|
17
|
+
legacyEligible: number;
|
|
18
|
+
legacyMigrated: number;
|
|
19
|
+
legacySkipped: unknown[];
|
|
20
|
+
forksChecked: number;
|
|
21
|
+
forksEligible: number;
|
|
22
|
+
forksRepaired: number;
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
type JsonValue = null | boolean | number | string | JsonValue[] | { [key: string]: JsonValue };
|
|
26
|
+
|
|
27
|
+
async function availablePort(): Promise<number> {
|
|
28
|
+
return await new Promise((resolve, reject) => {
|
|
29
|
+
const server = net.createServer();
|
|
30
|
+
server.unref();
|
|
31
|
+
server.once("error", reject);
|
|
32
|
+
server.listen(0, "127.0.0.1", () => {
|
|
33
|
+
const address = server.address();
|
|
34
|
+
if (!address || typeof address === "string") return reject(new Error("port allocation failed"));
|
|
35
|
+
server.close((error) => error ? reject(error) : resolve(address.port));
|
|
36
|
+
});
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
async function runBackfill(connectionString: string, args: string[]) {
|
|
41
|
+
return await new Promise<{ code: number | null; report: BackfillReport }>((resolve, reject) => {
|
|
42
|
+
const child = spawn(process.execPath, [
|
|
43
|
+
path.join(repositoryRoot, "cli/node_modules/tsx/dist/cli.mjs"),
|
|
44
|
+
"scripts/backfill-chat-transcripts.ts",
|
|
45
|
+
...args,
|
|
46
|
+
], {
|
|
47
|
+
cwd: repositoryRoot,
|
|
48
|
+
env: { ...process.env, DATABASE_URL: connectionString },
|
|
49
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
50
|
+
});
|
|
51
|
+
let stdout = "";
|
|
52
|
+
let stderr = "";
|
|
53
|
+
child.stdout.on("data", (chunk) => { stdout += String(chunk); });
|
|
54
|
+
child.stderr.on("data", (chunk) => { stderr += String(chunk); });
|
|
55
|
+
const timeout = setTimeout(() => child.kill("SIGTERM"), 30_000);
|
|
56
|
+
child.once("error", reject);
|
|
57
|
+
child.once("close", (code) => {
|
|
58
|
+
clearTimeout(timeout);
|
|
59
|
+
if (!stdout.trim()) return reject(new Error(stderr || "backfill produced no report"));
|
|
60
|
+
try {
|
|
61
|
+
resolve({ code, report: (JSON.parse(stdout) as { report: BackfillReport }).report });
|
|
62
|
+
} catch {
|
|
63
|
+
reject(new Error(`${stderr}\n${stdout}`));
|
|
64
|
+
}
|
|
65
|
+
});
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
describe("chat transcript storage migration", () => {
|
|
70
|
+
it("backfills safely, prefers generation events, and is repeatable", async () => {
|
|
71
|
+
const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), "rudder-chat-transcript-storage-"));
|
|
72
|
+
const port = await availablePort();
|
|
73
|
+
const { instance } = await createLocalPostgresInstance({
|
|
74
|
+
databaseDir: path.join(tempRoot, "postgres"),
|
|
75
|
+
user: "rudder",
|
|
76
|
+
password: "rudder",
|
|
77
|
+
port,
|
|
78
|
+
persistent: true,
|
|
79
|
+
initdbFlags: ["--encoding=UTF8", "--locale=C"],
|
|
80
|
+
onLog: () => {},
|
|
81
|
+
onError: () => {},
|
|
82
|
+
});
|
|
83
|
+
let sql: postgres.Sql | null = null;
|
|
84
|
+
try {
|
|
85
|
+
await instance.initialise();
|
|
86
|
+
await instance.start();
|
|
87
|
+
const adminUrl = `postgres://rudder:rudder@127.0.0.1:${port}/postgres`;
|
|
88
|
+
await ensurePostgresDatabase(adminUrl, "rudder");
|
|
89
|
+
const connectionString = `postgres://rudder:rudder@127.0.0.1:${port}/rudder`;
|
|
90
|
+
await applyPendingMigrations(connectionString);
|
|
91
|
+
sql = postgres(connectionString, { max: 1, onnotice: () => {} });
|
|
92
|
+
const db = sql;
|
|
93
|
+
|
|
94
|
+
const orgId = randomUUID();
|
|
95
|
+
const conversationId = randomUUID();
|
|
96
|
+
await db`
|
|
97
|
+
INSERT INTO organizations (id, url_key, name, issue_prefix)
|
|
98
|
+
VALUES (${orgId}, ${`transcript-${orgId}`}, 'Transcript migration', ${`T${orgId.replaceAll("-", "").slice(0, 8)}`})
|
|
99
|
+
`;
|
|
100
|
+
await db`
|
|
101
|
+
INSERT INTO chat_conversations (id, org_id, title)
|
|
102
|
+
VALUES (${conversationId}, ${orgId}, 'Transcript migration')
|
|
103
|
+
`;
|
|
104
|
+
const makeMessage = async (
|
|
105
|
+
targetConversationId: string,
|
|
106
|
+
role: string,
|
|
107
|
+
body: string,
|
|
108
|
+
payload: JsonValue,
|
|
109
|
+
createdAt?: Date,
|
|
110
|
+
) => {
|
|
111
|
+
const id = randomUUID();
|
|
112
|
+
await db`
|
|
113
|
+
INSERT INTO chat_messages (id, org_id, conversation_id, role, kind, status, body, structured_payload)
|
|
114
|
+
VALUES (${id}, ${orgId}, ${targetConversationId}, ${role}, 'message', 'completed', ${body}, ${db.json(payload)})
|
|
115
|
+
`;
|
|
116
|
+
if (createdAt) {
|
|
117
|
+
await db`UPDATE chat_messages SET created_at = ${createdAt}, updated_at = ${createdAt} WHERE id = ${id}`;
|
|
118
|
+
}
|
|
119
|
+
return id;
|
|
120
|
+
};
|
|
121
|
+
await makeMessage(conversationId, "user", "user", { __chatTranscript: [{ kind: "stdout", ts: "2026-08-08T00:00:01.000Z", text: "one" }] });
|
|
122
|
+
await makeMessage(conversationId, "user", "user", { __chatTranscript: [{ kind: "stdout", ts: "2026-08-08T00:00:02.000Z", text: "two" }] });
|
|
123
|
+
await makeMessage(conversationId, "user", "user", { __chatTranscript: "malformed" });
|
|
124
|
+
const assistantMessageId = await makeMessage(conversationId, "assistant", "assistant", {
|
|
125
|
+
__chatTranscript: [{ kind: "thinking", ts: "2026-08-08T00:00:03.000Z", text: "legacy" }],
|
|
126
|
+
});
|
|
127
|
+
const generationId = randomUUID();
|
|
128
|
+
const generationAt = new Date("2026-08-08T00:00:00.000Z");
|
|
129
|
+
await db`
|
|
130
|
+
INSERT INTO chat_generations (id, org_id, conversation_id, status, started_at, created_at, updated_at)
|
|
131
|
+
VALUES (${generationId}, ${orgId}, ${conversationId}, 'completed', ${generationAt}, ${generationAt}, ${generationAt})
|
|
132
|
+
`;
|
|
133
|
+
await db`
|
|
134
|
+
INSERT INTO chat_generation_events (org_id, generation_id, generation_seq, attempt_epoch, event_kind, payload, assistant_message_id)
|
|
135
|
+
VALUES (${orgId}, ${generationId}, 1, 1, 'transcript', ${db.json({ entry: { kind: "thinking", ts: "2026-08-08T00:00:04.000Z", text: "ledger" } })}, ${assistantMessageId})
|
|
136
|
+
`;
|
|
137
|
+
|
|
138
|
+
const forkSourceConversationId = randomUUID();
|
|
139
|
+
const forkTargetConversationId = randomUUID();
|
|
140
|
+
const forkUserCreatedAt = new Date("2026-08-08T00:01:00.000Z");
|
|
141
|
+
const forkAssistantCreatedAt = new Date("2026-08-08T00:01:01.000Z");
|
|
142
|
+
const forkBoundaryCreatedAt = new Date("2026-08-08T00:01:02.000Z");
|
|
143
|
+
await db`
|
|
144
|
+
INSERT INTO chat_conversations (id, org_id, title)
|
|
145
|
+
VALUES (${forkSourceConversationId}, ${orgId}, 'Old fork source'), (${forkTargetConversationId}, ${orgId}, 'Old fork target')
|
|
146
|
+
`;
|
|
147
|
+
await makeMessage(
|
|
148
|
+
forkSourceConversationId,
|
|
149
|
+
"user",
|
|
150
|
+
"fork user",
|
|
151
|
+
{ __chatTranscript: [{ kind: "stdout", ts: "2026-08-08T00:01:00.100Z", text: "fork user transcript" }] },
|
|
152
|
+
forkUserCreatedAt,
|
|
153
|
+
);
|
|
154
|
+
const forkSourceAssistantId = await makeMessage(
|
|
155
|
+
forkSourceConversationId,
|
|
156
|
+
"assistant",
|
|
157
|
+
"fork assistant",
|
|
158
|
+
{ __chatTranscript: [{ kind: "thinking", ts: "2026-08-08T00:01:01.100Z", text: "fork assistant transcript" }] },
|
|
159
|
+
forkAssistantCreatedAt,
|
|
160
|
+
);
|
|
161
|
+
await db`
|
|
162
|
+
UPDATE chat_conversations
|
|
163
|
+
SET forked_from_conversation_id = ${forkSourceConversationId},
|
|
164
|
+
forked_from_message_id = ${forkSourceAssistantId}
|
|
165
|
+
WHERE id = ${forkTargetConversationId}
|
|
166
|
+
`;
|
|
167
|
+
await makeMessage(forkTargetConversationId, "user", "fork user", null, forkUserCreatedAt);
|
|
168
|
+
await makeMessage(forkTargetConversationId, "assistant", "fork assistant", null, forkAssistantCreatedAt);
|
|
169
|
+
await db`
|
|
170
|
+
INSERT INTO chat_messages (id, org_id, conversation_id, role, kind, status, body, structured_payload, created_at, updated_at)
|
|
171
|
+
VALUES (${randomUUID()}, ${orgId}, ${forkTargetConversationId}, 'system', 'system_event', 'completed', 'fork event', ${db.json({ eventType: "chat_fork" })}, ${forkBoundaryCreatedAt}, ${forkBoundaryCreatedAt})
|
|
172
|
+
`;
|
|
173
|
+
|
|
174
|
+
const dryRun = await runBackfill(connectionString, ["--dry-run", "--batch-size", "1"]);
|
|
175
|
+
expect(dryRun.code).toBe(2);
|
|
176
|
+
expect(dryRun.report).toMatchObject({ legacyCandidates: 6, legacyEligible: 5, legacyMigrated: 0, forksChecked: 1, forksEligible: 2 });
|
|
177
|
+
expect(dryRun.report.legacySkipped).toHaveLength(1);
|
|
178
|
+
|
|
179
|
+
const firstRun = await runBackfill(connectionString, ["--batch-size", "1"]);
|
|
180
|
+
expect(firstRun.code).toBe(2);
|
|
181
|
+
expect(firstRun.report).toMatchObject({ legacyCandidates: 6, legacyEligible: 5, legacyMigrated: 5, forksChecked: 1, forksRepaired: 1 });
|
|
182
|
+
|
|
183
|
+
const secondRun = await runBackfill(connectionString, ["--batch-size", "1"]);
|
|
184
|
+
expect(secondRun.code).toBe(2);
|
|
185
|
+
expect(secondRun.report).toMatchObject({ legacyCandidates: 1, legacyMigrated: 0, forksChecked: 1, forksRepaired: 0 });
|
|
186
|
+
|
|
187
|
+
const remaining = await db<{ count: number }[]>`SELECT count(*)::int AS count FROM chat_messages WHERE structured_payload ? '__chatTranscript'`;
|
|
188
|
+
const entries = await db<{ count: number }[]>`SELECT count(*)::int AS count FROM chat_message_transcript_entries`;
|
|
189
|
+
const assistantEntries = await db<{ count: number }[]>`
|
|
190
|
+
SELECT count(*)::int AS count FROM chat_message_transcript_entries WHERE message_id = ${assistantMessageId}
|
|
191
|
+
`;
|
|
192
|
+
expect(Number(remaining[0]?.count)).toBe(1);
|
|
193
|
+
expect(Number(entries[0]?.count)).toBe(6);
|
|
194
|
+
expect(Number(assistantEntries[0]?.count)).toBe(0);
|
|
195
|
+
const forkEntries = await db<{ text: string }[]>`
|
|
196
|
+
SELECT payload->>'text' AS text
|
|
197
|
+
FROM chat_message_transcript_entries
|
|
198
|
+
WHERE message_id = (
|
|
199
|
+
SELECT id FROM chat_messages
|
|
200
|
+
WHERE conversation_id = ${forkTargetConversationId} AND role = 'assistant'
|
|
201
|
+
)
|
|
202
|
+
ORDER BY entry_seq
|
|
203
|
+
`;
|
|
204
|
+
expect(forkEntries).toEqual([{ text: "fork assistant transcript" }]);
|
|
205
|
+
} finally {
|
|
206
|
+
await sql?.end();
|
|
207
|
+
await instance.stop().catch(() => undefined);
|
|
208
|
+
fs.rmSync(tempRoot, { recursive: true, force: true });
|
|
209
|
+
}
|
|
210
|
+
}, 60_000);
|
|
211
|
+
});
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { describe, expect, it } from "vitest";
|
|
4
|
+
|
|
5
|
+
const migrationSql = fs.readFileSync(
|
|
6
|
+
path.join(path.dirname(new URL(import.meta.url).pathname), "0147_github_mcp_provider.sql"),
|
|
7
|
+
"utf8",
|
|
8
|
+
);
|
|
9
|
+
|
|
10
|
+
describe("GitHub MCP provider migration", () => {
|
|
11
|
+
it("adds GitHub to organization and agent canonical uniqueness without widening custom MCP", () => {
|
|
12
|
+
expect(migrationSql).toMatch(
|
|
13
|
+
/create unique index\s+"mcp_connections_org_official_canonical_uq"[\s\S]*"provider"\s+in\s*\('supabase',\s*'linear',\s*'notion',\s*'github'\)[\s\S]*"scope"\s*=\s*'organization'/i,
|
|
14
|
+
);
|
|
15
|
+
expect(migrationSql).toMatch(
|
|
16
|
+
/create unique index\s+"mcp_connections_agent_official_canonical_uq"[\s\S]*"provider"\s+in\s*\('supabase',\s*'linear',\s*'notion',\s*'github'\)[\s\S]*"scope"\s*=\s*'agent'/i,
|
|
17
|
+
);
|
|
18
|
+
expect(migrationSql).not.toMatch(/provider"\s*=\s*'custom'/i);
|
|
19
|
+
});
|
|
20
|
+
});
|