This commit is contained in:
68
packages/db/drizzle/0012_tess_durable_state.sql
Normal file
68
packages/db/drizzle/0012_tess_durable_state.sql
Normal file
@@ -0,0 +1,68 @@
|
||||
CREATE TYPE "public"."tess_handoff_status" AS ENUM('pending', 'accepted');--> statement-breakpoint
|
||||
CREATE TYPE "public"."tess_inbox_status" AS ENUM('pending', 'processing', 'processed');--> statement-breakpoint
|
||||
CREATE TYPE "public"."tess_outbox_status" AS ENUM('pending', 'processing', 'delivered');--> statement-breakpoint
|
||||
CREATE TABLE "tess_checkpoints" (
|
||||
"session_id" text PRIMARY KEY NOT NULL,
|
||||
"checkpoint_id" text NOT NULL,
|
||||
"cursor" text NOT NULL,
|
||||
"summary" text NOT NULL,
|
||||
"compaction_epoch" integer NOT NULL,
|
||||
"created_at" timestamp with time zone DEFAULT now() NOT NULL,
|
||||
"updated_at" timestamp with time zone DEFAULT now() NOT NULL,
|
||||
CONSTRAINT "tess_checkpoints_checkpoint_id_unique" UNIQUE("checkpoint_id")
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "tess_handoffs" (
|
||||
"handoff_id" text PRIMARY KEY NOT NULL,
|
||||
"session_id" text NOT NULL,
|
||||
"destination" text NOT NULL,
|
||||
"correlation_id" text NOT NULL,
|
||||
"checkpoint_id" text NOT NULL,
|
||||
"status" "tess_handoff_status" DEFAULT 'pending' NOT NULL,
|
||||
"created_at" timestamp with time zone DEFAULT now() NOT NULL,
|
||||
"updated_at" timestamp with time zone DEFAULT now() NOT NULL
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "tess_inbox" (
|
||||
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
|
||||
"session_id" text NOT NULL,
|
||||
"idempotency_key" text NOT NULL,
|
||||
"correlation_id" text NOT NULL,
|
||||
"content" text NOT NULL,
|
||||
"status" "tess_inbox_status" DEFAULT 'pending' NOT NULL,
|
||||
"created_at" timestamp with time zone DEFAULT now() NOT NULL,
|
||||
"updated_at" timestamp with time zone DEFAULT now() NOT NULL
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "tess_outbox" (
|
||||
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
|
||||
"session_id" text NOT NULL,
|
||||
"idempotency_key" text NOT NULL,
|
||||
"correlation_id" text NOT NULL,
|
||||
"kind" text NOT NULL,
|
||||
"content" text NOT NULL,
|
||||
"status" "tess_outbox_status" DEFAULT 'pending' NOT NULL,
|
||||
"created_at" timestamp with time zone DEFAULT now() NOT NULL,
|
||||
"updated_at" timestamp with time zone DEFAULT now() NOT NULL
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "tess_sessions" (
|
||||
"id" text PRIMARY KEY NOT NULL,
|
||||
"tenant_id" text NOT NULL,
|
||||
"owner_id" text NOT NULL,
|
||||
"provider_id" text NOT NULL,
|
||||
"runtime_session_id" text NOT NULL,
|
||||
"created_at" timestamp with time zone DEFAULT now() NOT NULL,
|
||||
"updated_at" timestamp with time zone DEFAULT now() NOT NULL
|
||||
);
|
||||
--> statement-breakpoint
|
||||
ALTER TABLE "tess_checkpoints" ADD CONSTRAINT "tess_checkpoints_session_id_tess_sessions_id_fk" FOREIGN KEY ("session_id") REFERENCES "public"."tess_sessions"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "tess_handoffs" ADD CONSTRAINT "tess_handoffs_session_id_tess_sessions_id_fk" FOREIGN KEY ("session_id") REFERENCES "public"."tess_sessions"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "tess_inbox" ADD CONSTRAINT "tess_inbox_session_id_tess_sessions_id_fk" FOREIGN KEY ("session_id") REFERENCES "public"."tess_sessions"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "tess_outbox" ADD CONSTRAINT "tess_outbox_session_id_tess_sessions_id_fk" FOREIGN KEY ("session_id") REFERENCES "public"."tess_sessions"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "tess_sessions" ADD CONSTRAINT "tess_sessions_owner_id_users_id_fk" FOREIGN KEY ("owner_id") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
||||
CREATE INDEX "tess_handoffs_session_status_idx" ON "tess_handoffs" USING btree ("session_id","status");--> statement-breakpoint
|
||||
CREATE UNIQUE INDEX "tess_inbox_session_idempotency_idx" ON "tess_inbox" USING btree ("session_id","idempotency_key");--> statement-breakpoint
|
||||
CREATE INDEX "tess_inbox_session_status_created_idx" ON "tess_inbox" USING btree ("session_id","status","created_at");--> statement-breakpoint
|
||||
CREATE UNIQUE INDEX "tess_outbox_session_idempotency_idx" ON "tess_outbox" USING btree ("session_id","idempotency_key");--> statement-breakpoint
|
||||
CREATE INDEX "tess_outbox_session_status_created_idx" ON "tess_outbox" USING btree ("session_id","status","created_at");
|
||||
5
packages/db/drizzle/0013_tess_checkpoint_history.sql
Normal file
5
packages/db/drizzle/0013_tess_checkpoint_history.sql
Normal file
@@ -0,0 +1,5 @@
|
||||
ALTER TABLE "tess_checkpoints" DROP CONSTRAINT "tess_checkpoints_checkpoint_id_unique";--> statement-breakpoint
|
||||
ALTER TABLE "tess_checkpoints" DROP CONSTRAINT "tess_checkpoints_pkey";--> statement-breakpoint
|
||||
ALTER TABLE "tess_checkpoints" ADD COLUMN "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL;--> statement-breakpoint
|
||||
CREATE UNIQUE INDEX "tess_checkpoints_session_idempotency_idx" ON "tess_checkpoints" USING btree ("session_id","checkpoint_id");--> statement-breakpoint
|
||||
CREATE INDEX "tess_checkpoints_session_epoch_idx" ON "tess_checkpoints" USING btree ("session_id","compaction_epoch");
|
||||
5
packages/db/drizzle/0014_tess_outbox_channel_scope.sql
Normal file
5
packages/db/drizzle/0014_tess_outbox_channel_scope.sql
Normal file
@@ -0,0 +1,5 @@
|
||||
ALTER TABLE "tess_outbox" ADD COLUMN "channel_id" text;
|
||||
--> statement-breakpoint
|
||||
UPDATE "tess_outbox" SET "channel_id" = 'legacy:unknown' WHERE "channel_id" IS NULL;
|
||||
--> statement-breakpoint
|
||||
ALTER TABLE "tess_outbox" ALTER COLUMN "channel_id" SET NOT NULL;
|
||||
4172
packages/db/drizzle/meta/0012_snapshot.json
Normal file
4172
packages/db/drizzle/meta/0012_snapshot.json
Normal file
File diff suppressed because it is too large
Load Diff
4214
packages/db/drizzle/meta/0013_snapshot.json
Normal file
4214
packages/db/drizzle/meta/0013_snapshot.json
Normal file
File diff suppressed because it is too large
Load Diff
4220
packages/db/drizzle/meta/0014_snapshot.json
Normal file
4220
packages/db/drizzle/meta/0014_snapshot.json
Normal file
File diff suppressed because it is too large
Load Diff
@@ -85,6 +85,27 @@
|
||||
"when": 1782310438919,
|
||||
"tag": "0011_bitter_gateway",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 12,
|
||||
"version": "7",
|
||||
"when": 1783911983447,
|
||||
"tag": "0012_tess_durable_state",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 13,
|
||||
"version": "7",
|
||||
"when": 1783913232578,
|
||||
"tag": "0013_tess_checkpoint_history",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 14,
|
||||
"version": "7",
|
||||
"when": 1783913398006,
|
||||
"tag": "0014_tess_outbox_channel_scope",
|
||||
"breakpoints": true
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -487,6 +487,113 @@ export const agentLogs = pgTable(
|
||||
],
|
||||
);
|
||||
|
||||
// ─── Tess durable session state ─────────────────────────────────────────────
|
||||
// PostgreSQL is canonical for restart-safe Tess session recovery. The state
|
||||
// machine lives in @mosaicstack/agent; these records are its durable adapter.
|
||||
|
||||
export const tessInboxStatusEnum = pgEnum('tess_inbox_status', [
|
||||
'pending',
|
||||
'processing',
|
||||
'processed',
|
||||
]);
|
||||
export const tessOutboxStatusEnum = pgEnum('tess_outbox_status', [
|
||||
'pending',
|
||||
'processing',
|
||||
'delivered',
|
||||
]);
|
||||
export const tessHandoffStatusEnum = pgEnum('tess_handoff_status', ['pending', 'accepted']);
|
||||
|
||||
export const tessSessions = pgTable('tess_sessions', {
|
||||
id: text('id').primaryKey(),
|
||||
tenantId: text('tenant_id').notNull(),
|
||||
ownerId: text('owner_id')
|
||||
.notNull()
|
||||
.references(() => users.id, { onDelete: 'cascade' }),
|
||||
providerId: text('provider_id').notNull(),
|
||||
runtimeSessionId: text('runtime_session_id').notNull(),
|
||||
createdAt: timestamp('created_at', { withTimezone: true }).notNull().defaultNow(),
|
||||
updatedAt: timestamp('updated_at', { withTimezone: true }).notNull().defaultNow(),
|
||||
});
|
||||
|
||||
export const tessInbox = pgTable(
|
||||
'tess_inbox',
|
||||
{
|
||||
id: uuid('id').primaryKey().defaultRandom(),
|
||||
sessionId: text('session_id')
|
||||
.notNull()
|
||||
.references(() => tessSessions.id, { onDelete: 'cascade' }),
|
||||
idempotencyKey: text('idempotency_key').notNull(),
|
||||
correlationId: text('correlation_id').notNull(),
|
||||
content: text('content').notNull(),
|
||||
status: tessInboxStatusEnum('status').notNull().default('pending'),
|
||||
createdAt: timestamp('created_at', { withTimezone: true }).notNull().defaultNow(),
|
||||
updatedAt: timestamp('updated_at', { withTimezone: true }).notNull().defaultNow(),
|
||||
},
|
||||
(t) => [
|
||||
uniqueIndex('tess_inbox_session_idempotency_idx').on(t.sessionId, t.idempotencyKey),
|
||||
index('tess_inbox_session_status_created_idx').on(t.sessionId, t.status, t.createdAt),
|
||||
],
|
||||
);
|
||||
|
||||
export const tessOutbox = pgTable(
|
||||
'tess_outbox',
|
||||
{
|
||||
id: uuid('id').primaryKey().defaultRandom(),
|
||||
sessionId: text('session_id')
|
||||
.notNull()
|
||||
.references(() => tessSessions.id, { onDelete: 'cascade' }),
|
||||
idempotencyKey: text('idempotency_key').notNull(),
|
||||
correlationId: text('correlation_id').notNull(),
|
||||
channelId: text('channel_id').notNull(),
|
||||
kind: text('kind').notNull(),
|
||||
content: text('content').notNull(),
|
||||
status: tessOutboxStatusEnum('status').notNull().default('pending'),
|
||||
createdAt: timestamp('created_at', { withTimezone: true }).notNull().defaultNow(),
|
||||
updatedAt: timestamp('updated_at', { withTimezone: true }).notNull().defaultNow(),
|
||||
},
|
||||
(t) => [
|
||||
uniqueIndex('tess_outbox_session_idempotency_idx').on(t.sessionId, t.idempotencyKey),
|
||||
index('tess_outbox_session_status_created_idx').on(t.sessionId, t.status, t.createdAt),
|
||||
],
|
||||
);
|
||||
|
||||
export const tessCheckpoints = pgTable(
|
||||
'tess_checkpoints',
|
||||
{
|
||||
id: uuid('id').primaryKey().defaultRandom(),
|
||||
sessionId: text('session_id')
|
||||
.notNull()
|
||||
.references(() => tessSessions.id, { onDelete: 'cascade' }),
|
||||
checkpointId: text('checkpoint_id').notNull(),
|
||||
cursor: text('cursor').notNull(),
|
||||
summary: text('summary').notNull(),
|
||||
compactionEpoch: integer('compaction_epoch').notNull(),
|
||||
createdAt: timestamp('created_at', { withTimezone: true }).notNull().defaultNow(),
|
||||
updatedAt: timestamp('updated_at', { withTimezone: true }).notNull().defaultNow(),
|
||||
},
|
||||
(t) => [
|
||||
uniqueIndex('tess_checkpoints_session_idempotency_idx').on(t.sessionId, t.checkpointId),
|
||||
index('tess_checkpoints_session_epoch_idx').on(t.sessionId, t.compactionEpoch),
|
||||
],
|
||||
);
|
||||
|
||||
export const tessHandoffs = pgTable(
|
||||
'tess_handoffs',
|
||||
{
|
||||
handoffId: text('handoff_id').primaryKey(),
|
||||
sessionId: text('session_id')
|
||||
.notNull()
|
||||
.references(() => tessSessions.id, { onDelete: 'cascade' }),
|
||||
destination: text('destination').notNull(),
|
||||
correlationId: text('correlation_id').notNull(),
|
||||
checkpointId: text('checkpoint_id').notNull(),
|
||||
status: tessHandoffStatusEnum('status').notNull().default('pending'),
|
||||
createdAt: timestamp('created_at', { withTimezone: true }).notNull().defaultNow(),
|
||||
updatedAt: timestamp('updated_at', { withTimezone: true }).notNull().defaultNow(),
|
||||
},
|
||||
(t) => [index('tess_handoffs_session_status_idx').on(t.sessionId, t.status)],
|
||||
);
|
||||
|
||||
// ─── Skills ─────────────────────────────────────────────────────────────────
|
||||
|
||||
export const skills = pgTable(
|
||||
|
||||
Reference in New Issue
Block a user