fix(tess): harden durable recovery namespaces
All checks were successful
ci/woodpecker/pr/ci Pipeline was successful

This commit is contained in:
Jarvis
2026-07-12 23:36:14 -05:00
parent 102a7b606b
commit 444988d23b
22 changed files with 449 additions and 391 deletions

View File

@@ -6,6 +6,7 @@ import {
} from './tess-durable-session.js';
const IDENTITY: DurableSessionIdentity = {
agentName: 'Nova',
sessionId: 'tess-session-1',
tenantId: 'tenant-1',
ownerId: 'owner-1',

View File

@@ -1,4 +1,6 @@
export interface DurableSessionIdentity {
/** Provisioned roster identity; isolates durable state between named agents. */
agentName: string;
sessionId: string;
tenantId: string;
ownerId: string;
@@ -220,7 +222,13 @@ export class DurableSessionCoordinator {
}
private assertIdentity(identity: DurableSessionIdentity): void {
this.assertRecord(identity.sessionId, identity.tenantId, identity.ownerId, identity.providerId);
this.assertRecord(
identity.agentName,
identity.sessionId,
identity.tenantId,
identity.ownerId,
identity.providerId,
);
if (identity.runtimeSessionId.trim().length === 0) {
throw new Error('Durable runtime session identity is required');
}
@@ -410,6 +418,7 @@ export class InMemoryDurableSessionStore implements DurableSessionStore {
function identitiesEqual(left: DurableSessionIdentity, right: DurableSessionIdentity): boolean {
return (
left.agentName === right.agentName &&
left.sessionId === right.sessionId &&
left.tenantId === right.tenantId &&
left.ownerId === right.ownerId &&

View File

@@ -0,0 +1,71 @@
CREATE TYPE "public"."interaction_handoff_status" AS ENUM('pending', 'accepted');--> statement-breakpoint
CREATE TYPE "public"."interaction_inbox_status" AS ENUM('pending', 'processing', 'processed');--> statement-breakpoint
CREATE TYPE "public"."interaction_outbox_status" AS ENUM('pending', 'processing', 'delivered');--> statement-breakpoint
CREATE TABLE "interaction_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 "interaction_checkpoints_checkpoint_id_unique" UNIQUE("checkpoint_id")
);
--> statement-breakpoint
CREATE TABLE "interaction_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" "interaction_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 "interaction_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,
"content_digest" text NOT NULL,
"status" "interaction_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 "interaction_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,
"content_digest" text NOT NULL,
"status" "interaction_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 "interaction_sessions" (
"id" text PRIMARY KEY NOT NULL,
"agent_name" text 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 "interaction_checkpoints" ADD CONSTRAINT "interaction_checkpoints_session_id_interaction_sessions_id_fk" FOREIGN KEY ("session_id") REFERENCES "public"."interaction_sessions"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE "interaction_handoffs" ADD CONSTRAINT "interaction_handoffs_session_id_interaction_sessions_id_fk" FOREIGN KEY ("session_id") REFERENCES "public"."interaction_sessions"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE "interaction_inbox" ADD CONSTRAINT "interaction_inbox_session_id_interaction_sessions_id_fk" FOREIGN KEY ("session_id") REFERENCES "public"."interaction_sessions"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE "interaction_outbox" ADD CONSTRAINT "interaction_outbox_session_id_interaction_sessions_id_fk" FOREIGN KEY ("session_id") REFERENCES "public"."interaction_sessions"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE "interaction_sessions" ADD CONSTRAINT "interaction_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 "interaction_handoffs_session_status_idx" ON "interaction_handoffs" USING btree ("session_id","status");--> statement-breakpoint
CREATE UNIQUE INDEX "interaction_inbox_session_idempotency_idx" ON "interaction_inbox" USING btree ("session_id","idempotency_key");--> statement-breakpoint
CREATE INDEX "interaction_inbox_session_status_created_idx" ON "interaction_inbox" USING btree ("session_id","status","created_at");--> statement-breakpoint
CREATE UNIQUE INDEX "interaction_outbox_session_idempotency_idx" ON "interaction_outbox" USING btree ("session_id","idempotency_key");--> statement-breakpoint
CREATE INDEX "interaction_outbox_session_status_created_idx" ON "interaction_outbox" USING btree ("session_id","status","created_at");

View File

@@ -1,68 +0,0 @@
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");

View File

@@ -0,0 +1,5 @@
ALTER TABLE "interaction_checkpoints" DROP CONSTRAINT "interaction_checkpoints_checkpoint_id_unique";--> statement-breakpoint
ALTER TABLE "interaction_checkpoints" DROP CONSTRAINT "interaction_checkpoints_pkey";--> statement-breakpoint
ALTER TABLE "interaction_checkpoints" ADD COLUMN "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL;--> statement-breakpoint
CREATE UNIQUE INDEX "interaction_checkpoints_session_idempotency_idx" ON "interaction_checkpoints" USING btree ("session_id","checkpoint_id");--> statement-breakpoint
CREATE INDEX "interaction_checkpoints_session_epoch_idx" ON "interaction_checkpoints" USING btree ("session_id","compaction_epoch");

View File

@@ -1,5 +0,0 @@
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");

View File

@@ -0,0 +1,5 @@
ALTER TABLE "interaction_outbox" ADD COLUMN "channel_id" text;
--> statement-breakpoint
UPDATE "interaction_outbox" SET "channel_id" = 'legacy:unknown' WHERE "channel_id" IS NULL;
--> statement-breakpoint
ALTER TABLE "interaction_outbox" ALTER COLUMN "channel_id" SET NOT NULL;

View File

@@ -1,5 +0,0 @@
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;

View File

@@ -3354,8 +3354,8 @@
"checkConstraints": {},
"isRLSEnabled": false
},
"public.tess_checkpoints": {
"name": "tess_checkpoints",
"public.interaction_checkpoints": {
"name": "interaction_checkpoints",
"schema": "",
"columns": {
"session_id": {
@@ -3405,10 +3405,10 @@
},
"indexes": {},
"foreignKeys": {
"tess_checkpoints_session_id_tess_sessions_id_fk": {
"name": "tess_checkpoints_session_id_tess_sessions_id_fk",
"tableFrom": "tess_checkpoints",
"tableTo": "tess_sessions",
"interaction_checkpoints_session_id_interaction_sessions_id_fk": {
"name": "interaction_checkpoints_session_id_interaction_sessions_id_fk",
"tableFrom": "interaction_checkpoints",
"tableTo": "interaction_sessions",
"columnsFrom": [
"session_id"
],
@@ -3421,8 +3421,8 @@
},
"compositePrimaryKeys": {},
"uniqueConstraints": {
"tess_checkpoints_checkpoint_id_unique": {
"name": "tess_checkpoints_checkpoint_id_unique",
"interaction_checkpoints_checkpoint_id_unique": {
"name": "interaction_checkpoints_checkpoint_id_unique",
"nullsNotDistinct": false,
"columns": [
"checkpoint_id"
@@ -3433,8 +3433,8 @@
"checkConstraints": {},
"isRLSEnabled": false
},
"public.tess_handoffs": {
"name": "tess_handoffs",
"public.interaction_handoffs": {
"name": "interaction_handoffs",
"schema": "",
"columns": {
"handoff_id": {
@@ -3469,7 +3469,7 @@
},
"status": {
"name": "status",
"type": "tess_handoff_status",
"type": "interaction_handoff_status",
"typeSchema": "public",
"primaryKey": false,
"notNull": true,
@@ -3491,8 +3491,8 @@
}
},
"indexes": {
"tess_handoffs_session_status_idx": {
"name": "tess_handoffs_session_status_idx",
"interaction_handoffs_session_status_idx": {
"name": "interaction_handoffs_session_status_idx",
"columns": [
{
"expression": "session_id",
@@ -3514,10 +3514,10 @@
}
},
"foreignKeys": {
"tess_handoffs_session_id_tess_sessions_id_fk": {
"name": "tess_handoffs_session_id_tess_sessions_id_fk",
"tableFrom": "tess_handoffs",
"tableTo": "tess_sessions",
"interaction_handoffs_session_id_interaction_sessions_id_fk": {
"name": "interaction_handoffs_session_id_interaction_sessions_id_fk",
"tableFrom": "interaction_handoffs",
"tableTo": "interaction_sessions",
"columnsFrom": [
"session_id"
],
@@ -3534,8 +3534,8 @@
"checkConstraints": {},
"isRLSEnabled": false
},
"public.tess_inbox": {
"name": "tess_inbox",
"public.interaction_inbox": {
"name": "interaction_inbox",
"schema": "",
"columns": {
"id": {
@@ -3571,7 +3571,7 @@
},
"status": {
"name": "status",
"type": "tess_inbox_status",
"type": "interaction_inbox_status",
"typeSchema": "public",
"primaryKey": false,
"notNull": true,
@@ -3593,8 +3593,8 @@
}
},
"indexes": {
"tess_inbox_session_idempotency_idx": {
"name": "tess_inbox_session_idempotency_idx",
"interaction_inbox_session_idempotency_idx": {
"name": "interaction_inbox_session_idempotency_idx",
"columns": [
{
"expression": "session_id",
@@ -3614,8 +3614,8 @@
"method": "btree",
"with": {}
},
"tess_inbox_session_status_created_idx": {
"name": "tess_inbox_session_status_created_idx",
"interaction_inbox_session_status_created_idx": {
"name": "interaction_inbox_session_status_created_idx",
"columns": [
{
"expression": "session_id",
@@ -3643,10 +3643,10 @@
}
},
"foreignKeys": {
"tess_inbox_session_id_tess_sessions_id_fk": {
"name": "tess_inbox_session_id_tess_sessions_id_fk",
"tableFrom": "tess_inbox",
"tableTo": "tess_sessions",
"interaction_inbox_session_id_interaction_sessions_id_fk": {
"name": "interaction_inbox_session_id_interaction_sessions_id_fk",
"tableFrom": "interaction_inbox",
"tableTo": "interaction_sessions",
"columnsFrom": [
"session_id"
],
@@ -3663,8 +3663,8 @@
"checkConstraints": {},
"isRLSEnabled": false
},
"public.tess_outbox": {
"name": "tess_outbox",
"public.interaction_outbox": {
"name": "interaction_outbox",
"schema": "",
"columns": {
"id": {
@@ -3706,7 +3706,7 @@
},
"status": {
"name": "status",
"type": "tess_outbox_status",
"type": "interaction_outbox_status",
"typeSchema": "public",
"primaryKey": false,
"notNull": true,
@@ -3728,8 +3728,8 @@
}
},
"indexes": {
"tess_outbox_session_idempotency_idx": {
"name": "tess_outbox_session_idempotency_idx",
"interaction_outbox_session_idempotency_idx": {
"name": "interaction_outbox_session_idempotency_idx",
"columns": [
{
"expression": "session_id",
@@ -3749,8 +3749,8 @@
"method": "btree",
"with": {}
},
"tess_outbox_session_status_created_idx": {
"name": "tess_outbox_session_status_created_idx",
"interaction_outbox_session_status_created_idx": {
"name": "interaction_outbox_session_status_created_idx",
"columns": [
{
"expression": "session_id",
@@ -3778,10 +3778,10 @@
}
},
"foreignKeys": {
"tess_outbox_session_id_tess_sessions_id_fk": {
"name": "tess_outbox_session_id_tess_sessions_id_fk",
"tableFrom": "tess_outbox",
"tableTo": "tess_sessions",
"interaction_outbox_session_id_interaction_sessions_id_fk": {
"name": "interaction_outbox_session_id_interaction_sessions_id_fk",
"tableFrom": "interaction_outbox",
"tableTo": "interaction_sessions",
"columnsFrom": [
"session_id"
],
@@ -3798,8 +3798,8 @@
"checkConstraints": {},
"isRLSEnabled": false
},
"public.tess_sessions": {
"name": "tess_sessions",
"public.interaction_sessions": {
"name": "interaction_sessions",
"schema": "",
"columns": {
"id": {
@@ -3849,9 +3849,9 @@
},
"indexes": {},
"foreignKeys": {
"tess_sessions_owner_id_users_id_fk": {
"name": "tess_sessions_owner_id_users_id_fk",
"tableFrom": "tess_sessions",
"interaction_sessions_owner_id_users_id_fk": {
"name": "interaction_sessions_owner_id_users_id_fk",
"tableFrom": "interaction_sessions",
"tableTo": "users",
"columnsFrom": [
"owner_id"
@@ -4132,16 +4132,16 @@
"revoked"
]
},
"public.tess_handoff_status": {
"name": "tess_handoff_status",
"public.interaction_handoff_status": {
"name": "interaction_handoff_status",
"schema": "public",
"values": [
"pending",
"accepted"
]
},
"public.tess_inbox_status": {
"name": "tess_inbox_status",
"public.interaction_inbox_status": {
"name": "interaction_inbox_status",
"schema": "public",
"values": [
"pending",
@@ -4149,8 +4149,8 @@
"processed"
]
},
"public.tess_outbox_status": {
"name": "tess_outbox_status",
"public.interaction_outbox_status": {
"name": "interaction_outbox_status",
"schema": "public",
"values": [
"pending",

View File

@@ -3354,8 +3354,8 @@
"checkConstraints": {},
"isRLSEnabled": false
},
"public.tess_checkpoints": {
"name": "tess_checkpoints",
"public.interaction_checkpoints": {
"name": "interaction_checkpoints",
"schema": "",
"columns": {
"id": {
@@ -3411,8 +3411,8 @@
}
},
"indexes": {
"tess_checkpoints_session_idempotency_idx": {
"name": "tess_checkpoints_session_idempotency_idx",
"interaction_checkpoints_session_idempotency_idx": {
"name": "interaction_checkpoints_session_idempotency_idx",
"columns": [
{
"expression": "session_id",
@@ -3432,8 +3432,8 @@
"method": "btree",
"with": {}
},
"tess_checkpoints_session_epoch_idx": {
"name": "tess_checkpoints_session_epoch_idx",
"interaction_checkpoints_session_epoch_idx": {
"name": "interaction_checkpoints_session_epoch_idx",
"columns": [
{
"expression": "session_id",
@@ -3455,10 +3455,10 @@
}
},
"foreignKeys": {
"tess_checkpoints_session_id_tess_sessions_id_fk": {
"name": "tess_checkpoints_session_id_tess_sessions_id_fk",
"tableFrom": "tess_checkpoints",
"tableTo": "tess_sessions",
"interaction_checkpoints_session_id_interaction_sessions_id_fk": {
"name": "interaction_checkpoints_session_id_interaction_sessions_id_fk",
"tableFrom": "interaction_checkpoints",
"tableTo": "interaction_sessions",
"columnsFrom": [
"session_id"
],
@@ -3475,8 +3475,8 @@
"checkConstraints": {},
"isRLSEnabled": false
},
"public.tess_handoffs": {
"name": "tess_handoffs",
"public.interaction_handoffs": {
"name": "interaction_handoffs",
"schema": "",
"columns": {
"handoff_id": {
@@ -3511,7 +3511,7 @@
},
"status": {
"name": "status",
"type": "tess_handoff_status",
"type": "interaction_handoff_status",
"typeSchema": "public",
"primaryKey": false,
"notNull": true,
@@ -3533,8 +3533,8 @@
}
},
"indexes": {
"tess_handoffs_session_status_idx": {
"name": "tess_handoffs_session_status_idx",
"interaction_handoffs_session_status_idx": {
"name": "interaction_handoffs_session_status_idx",
"columns": [
{
"expression": "session_id",
@@ -3556,10 +3556,10 @@
}
},
"foreignKeys": {
"tess_handoffs_session_id_tess_sessions_id_fk": {
"name": "tess_handoffs_session_id_tess_sessions_id_fk",
"tableFrom": "tess_handoffs",
"tableTo": "tess_sessions",
"interaction_handoffs_session_id_interaction_sessions_id_fk": {
"name": "interaction_handoffs_session_id_interaction_sessions_id_fk",
"tableFrom": "interaction_handoffs",
"tableTo": "interaction_sessions",
"columnsFrom": [
"session_id"
],
@@ -3576,8 +3576,8 @@
"checkConstraints": {},
"isRLSEnabled": false
},
"public.tess_inbox": {
"name": "tess_inbox",
"public.interaction_inbox": {
"name": "interaction_inbox",
"schema": "",
"columns": {
"id": {
@@ -3613,7 +3613,7 @@
},
"status": {
"name": "status",
"type": "tess_inbox_status",
"type": "interaction_inbox_status",
"typeSchema": "public",
"primaryKey": false,
"notNull": true,
@@ -3635,8 +3635,8 @@
}
},
"indexes": {
"tess_inbox_session_idempotency_idx": {
"name": "tess_inbox_session_idempotency_idx",
"interaction_inbox_session_idempotency_idx": {
"name": "interaction_inbox_session_idempotency_idx",
"columns": [
{
"expression": "session_id",
@@ -3656,8 +3656,8 @@
"method": "btree",
"with": {}
},
"tess_inbox_session_status_created_idx": {
"name": "tess_inbox_session_status_created_idx",
"interaction_inbox_session_status_created_idx": {
"name": "interaction_inbox_session_status_created_idx",
"columns": [
{
"expression": "session_id",
@@ -3685,10 +3685,10 @@
}
},
"foreignKeys": {
"tess_inbox_session_id_tess_sessions_id_fk": {
"name": "tess_inbox_session_id_tess_sessions_id_fk",
"tableFrom": "tess_inbox",
"tableTo": "tess_sessions",
"interaction_inbox_session_id_interaction_sessions_id_fk": {
"name": "interaction_inbox_session_id_interaction_sessions_id_fk",
"tableFrom": "interaction_inbox",
"tableTo": "interaction_sessions",
"columnsFrom": [
"session_id"
],
@@ -3705,8 +3705,8 @@
"checkConstraints": {},
"isRLSEnabled": false
},
"public.tess_outbox": {
"name": "tess_outbox",
"public.interaction_outbox": {
"name": "interaction_outbox",
"schema": "",
"columns": {
"id": {
@@ -3748,7 +3748,7 @@
},
"status": {
"name": "status",
"type": "tess_outbox_status",
"type": "interaction_outbox_status",
"typeSchema": "public",
"primaryKey": false,
"notNull": true,
@@ -3770,8 +3770,8 @@
}
},
"indexes": {
"tess_outbox_session_idempotency_idx": {
"name": "tess_outbox_session_idempotency_idx",
"interaction_outbox_session_idempotency_idx": {
"name": "interaction_outbox_session_idempotency_idx",
"columns": [
{
"expression": "session_id",
@@ -3791,8 +3791,8 @@
"method": "btree",
"with": {}
},
"tess_outbox_session_status_created_idx": {
"name": "tess_outbox_session_status_created_idx",
"interaction_outbox_session_status_created_idx": {
"name": "interaction_outbox_session_status_created_idx",
"columns": [
{
"expression": "session_id",
@@ -3820,10 +3820,10 @@
}
},
"foreignKeys": {
"tess_outbox_session_id_tess_sessions_id_fk": {
"name": "tess_outbox_session_id_tess_sessions_id_fk",
"tableFrom": "tess_outbox",
"tableTo": "tess_sessions",
"interaction_outbox_session_id_interaction_sessions_id_fk": {
"name": "interaction_outbox_session_id_interaction_sessions_id_fk",
"tableFrom": "interaction_outbox",
"tableTo": "interaction_sessions",
"columnsFrom": [
"session_id"
],
@@ -3840,8 +3840,8 @@
"checkConstraints": {},
"isRLSEnabled": false
},
"public.tess_sessions": {
"name": "tess_sessions",
"public.interaction_sessions": {
"name": "interaction_sessions",
"schema": "",
"columns": {
"id": {
@@ -3891,9 +3891,9 @@
},
"indexes": {},
"foreignKeys": {
"tess_sessions_owner_id_users_id_fk": {
"name": "tess_sessions_owner_id_users_id_fk",
"tableFrom": "tess_sessions",
"interaction_sessions_owner_id_users_id_fk": {
"name": "interaction_sessions_owner_id_users_id_fk",
"tableFrom": "interaction_sessions",
"tableTo": "users",
"columnsFrom": [
"owner_id"
@@ -4174,16 +4174,16 @@
"revoked"
]
},
"public.tess_handoff_status": {
"name": "tess_handoff_status",
"public.interaction_handoff_status": {
"name": "interaction_handoff_status",
"schema": "public",
"values": [
"pending",
"accepted"
]
},
"public.tess_inbox_status": {
"name": "tess_inbox_status",
"public.interaction_inbox_status": {
"name": "interaction_inbox_status",
"schema": "public",
"values": [
"pending",
@@ -4191,8 +4191,8 @@
"processed"
]
},
"public.tess_outbox_status": {
"name": "tess_outbox_status",
"public.interaction_outbox_status": {
"name": "interaction_outbox_status",
"schema": "public",
"values": [
"pending",

View File

@@ -3354,8 +3354,8 @@
"checkConstraints": {},
"isRLSEnabled": false
},
"public.tess_checkpoints": {
"name": "tess_checkpoints",
"public.interaction_checkpoints": {
"name": "interaction_checkpoints",
"schema": "",
"columns": {
"id": {
@@ -3411,8 +3411,8 @@
}
},
"indexes": {
"tess_checkpoints_session_idempotency_idx": {
"name": "tess_checkpoints_session_idempotency_idx",
"interaction_checkpoints_session_idempotency_idx": {
"name": "interaction_checkpoints_session_idempotency_idx",
"columns": [
{
"expression": "session_id",
@@ -3432,8 +3432,8 @@
"method": "btree",
"with": {}
},
"tess_checkpoints_session_epoch_idx": {
"name": "tess_checkpoints_session_epoch_idx",
"interaction_checkpoints_session_epoch_idx": {
"name": "interaction_checkpoints_session_epoch_idx",
"columns": [
{
"expression": "session_id",
@@ -3455,10 +3455,10 @@
}
},
"foreignKeys": {
"tess_checkpoints_session_id_tess_sessions_id_fk": {
"name": "tess_checkpoints_session_id_tess_sessions_id_fk",
"tableFrom": "tess_checkpoints",
"tableTo": "tess_sessions",
"interaction_checkpoints_session_id_interaction_sessions_id_fk": {
"name": "interaction_checkpoints_session_id_interaction_sessions_id_fk",
"tableFrom": "interaction_checkpoints",
"tableTo": "interaction_sessions",
"columnsFrom": [
"session_id"
],
@@ -3475,8 +3475,8 @@
"checkConstraints": {},
"isRLSEnabled": false
},
"public.tess_handoffs": {
"name": "tess_handoffs",
"public.interaction_handoffs": {
"name": "interaction_handoffs",
"schema": "",
"columns": {
"handoff_id": {
@@ -3511,7 +3511,7 @@
},
"status": {
"name": "status",
"type": "tess_handoff_status",
"type": "interaction_handoff_status",
"typeSchema": "public",
"primaryKey": false,
"notNull": true,
@@ -3533,8 +3533,8 @@
}
},
"indexes": {
"tess_handoffs_session_status_idx": {
"name": "tess_handoffs_session_status_idx",
"interaction_handoffs_session_status_idx": {
"name": "interaction_handoffs_session_status_idx",
"columns": [
{
"expression": "session_id",
@@ -3556,10 +3556,10 @@
}
},
"foreignKeys": {
"tess_handoffs_session_id_tess_sessions_id_fk": {
"name": "tess_handoffs_session_id_tess_sessions_id_fk",
"tableFrom": "tess_handoffs",
"tableTo": "tess_sessions",
"interaction_handoffs_session_id_interaction_sessions_id_fk": {
"name": "interaction_handoffs_session_id_interaction_sessions_id_fk",
"tableFrom": "interaction_handoffs",
"tableTo": "interaction_sessions",
"columnsFrom": [
"session_id"
],
@@ -3576,8 +3576,8 @@
"checkConstraints": {},
"isRLSEnabled": false
},
"public.tess_inbox": {
"name": "tess_inbox",
"public.interaction_inbox": {
"name": "interaction_inbox",
"schema": "",
"columns": {
"id": {
@@ -3613,7 +3613,7 @@
},
"status": {
"name": "status",
"type": "tess_inbox_status",
"type": "interaction_inbox_status",
"typeSchema": "public",
"primaryKey": false,
"notNull": true,
@@ -3635,8 +3635,8 @@
}
},
"indexes": {
"tess_inbox_session_idempotency_idx": {
"name": "tess_inbox_session_idempotency_idx",
"interaction_inbox_session_idempotency_idx": {
"name": "interaction_inbox_session_idempotency_idx",
"columns": [
{
"expression": "session_id",
@@ -3656,8 +3656,8 @@
"method": "btree",
"with": {}
},
"tess_inbox_session_status_created_idx": {
"name": "tess_inbox_session_status_created_idx",
"interaction_inbox_session_status_created_idx": {
"name": "interaction_inbox_session_status_created_idx",
"columns": [
{
"expression": "session_id",
@@ -3685,10 +3685,10 @@
}
},
"foreignKeys": {
"tess_inbox_session_id_tess_sessions_id_fk": {
"name": "tess_inbox_session_id_tess_sessions_id_fk",
"tableFrom": "tess_inbox",
"tableTo": "tess_sessions",
"interaction_inbox_session_id_interaction_sessions_id_fk": {
"name": "interaction_inbox_session_id_interaction_sessions_id_fk",
"tableFrom": "interaction_inbox",
"tableTo": "interaction_sessions",
"columnsFrom": [
"session_id"
],
@@ -3705,8 +3705,8 @@
"checkConstraints": {},
"isRLSEnabled": false
},
"public.tess_outbox": {
"name": "tess_outbox",
"public.interaction_outbox": {
"name": "interaction_outbox",
"schema": "",
"columns": {
"id": {
@@ -3754,7 +3754,7 @@
},
"status": {
"name": "status",
"type": "tess_outbox_status",
"type": "interaction_outbox_status",
"typeSchema": "public",
"primaryKey": false,
"notNull": true,
@@ -3776,8 +3776,8 @@
}
},
"indexes": {
"tess_outbox_session_idempotency_idx": {
"name": "tess_outbox_session_idempotency_idx",
"interaction_outbox_session_idempotency_idx": {
"name": "interaction_outbox_session_idempotency_idx",
"columns": [
{
"expression": "session_id",
@@ -3797,8 +3797,8 @@
"method": "btree",
"with": {}
},
"tess_outbox_session_status_created_idx": {
"name": "tess_outbox_session_status_created_idx",
"interaction_outbox_session_status_created_idx": {
"name": "interaction_outbox_session_status_created_idx",
"columns": [
{
"expression": "session_id",
@@ -3826,10 +3826,10 @@
}
},
"foreignKeys": {
"tess_outbox_session_id_tess_sessions_id_fk": {
"name": "tess_outbox_session_id_tess_sessions_id_fk",
"tableFrom": "tess_outbox",
"tableTo": "tess_sessions",
"interaction_outbox_session_id_interaction_sessions_id_fk": {
"name": "interaction_outbox_session_id_interaction_sessions_id_fk",
"tableFrom": "interaction_outbox",
"tableTo": "interaction_sessions",
"columnsFrom": [
"session_id"
],
@@ -3846,8 +3846,8 @@
"checkConstraints": {},
"isRLSEnabled": false
},
"public.tess_sessions": {
"name": "tess_sessions",
"public.interaction_sessions": {
"name": "interaction_sessions",
"schema": "",
"columns": {
"id": {
@@ -3897,9 +3897,9 @@
},
"indexes": {},
"foreignKeys": {
"tess_sessions_owner_id_users_id_fk": {
"name": "tess_sessions_owner_id_users_id_fk",
"tableFrom": "tess_sessions",
"interaction_sessions_owner_id_users_id_fk": {
"name": "interaction_sessions_owner_id_users_id_fk",
"tableFrom": "interaction_sessions",
"tableTo": "users",
"columnsFrom": [
"owner_id"
@@ -4180,16 +4180,16 @@
"revoked"
]
},
"public.tess_handoff_status": {
"name": "tess_handoff_status",
"public.interaction_handoff_status": {
"name": "interaction_handoff_status",
"schema": "public",
"values": [
"pending",
"accepted"
]
},
"public.tess_inbox_status": {
"name": "tess_inbox_status",
"public.interaction_inbox_status": {
"name": "interaction_inbox_status",
"schema": "public",
"values": [
"pending",
@@ -4197,8 +4197,8 @@
"processed"
]
},
"public.tess_outbox_status": {
"name": "tess_outbox_status",
"public.interaction_outbox_status": {
"name": "interaction_outbox_status",
"schema": "public",
"values": [
"pending",

View File

@@ -90,21 +90,21 @@
"idx": 12,
"version": "7",
"when": 1783911983447,
"tag": "0012_tess_durable_state",
"tag": "0012_interaction_durable_state",
"breakpoints": true
},
{
"idx": 13,
"version": "7",
"when": 1783913232578,
"tag": "0013_tess_checkpoint_history",
"tag": "0013_interaction_checkpoint_history",
"breakpoints": true
},
{
"idx": 14,
"version": "7",
"when": 1783913398006,
"tag": "0014_tess_outbox_channel_scope",
"tag": "0014_interaction_outbox_channel_scope",
"breakpoints": true
}
]

View File

@@ -491,20 +491,24 @@ export const agentLogs = pgTable(
// 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', [
export const interactionInboxStatusEnum = pgEnum('interaction_inbox_status', [
'pending',
'processing',
'processed',
]);
export const tessOutboxStatusEnum = pgEnum('tess_outbox_status', [
export const interactionOutboxStatusEnum = pgEnum('interaction_outbox_status', [
'pending',
'processing',
'delivered',
]);
export const tessHandoffStatusEnum = pgEnum('tess_handoff_status', ['pending', 'accepted']);
export const interactionHandoffStatusEnum = pgEnum('interaction_handoff_status', [
'pending',
'accepted',
]);
export const tessSessions = pgTable('tess_sessions', {
export const interactionSessions = pgTable('interaction_sessions', {
id: text('id').primaryKey(),
agentName: text('agent_name').notNull(),
tenantId: text('tenant_id').notNull(),
ownerId: text('owner_id')
.notNull()
@@ -515,55 +519,57 @@ export const tessSessions = pgTable('tess_sessions', {
updatedAt: timestamp('updated_at', { withTimezone: true }).notNull().defaultNow(),
});
export const tessInbox = pgTable(
'tess_inbox',
export const interactionInbox = pgTable(
'interaction_inbox',
{
id: uuid('id').primaryKey().defaultRandom(),
sessionId: text('session_id')
.notNull()
.references(() => tessSessions.id, { onDelete: 'cascade' }),
.references(() => interactionSessions.id, { onDelete: 'cascade' }),
idempotencyKey: text('idempotency_key').notNull(),
correlationId: text('correlation_id').notNull(),
content: text('content').notNull(),
status: tessInboxStatusEnum('status').notNull().default('pending'),
contentDigest: text('content_digest').notNull(),
status: interactionInboxStatusEnum('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),
uniqueIndex('interaction_inbox_session_idempotency_idx').on(t.sessionId, t.idempotencyKey),
index('interaction_inbox_session_status_created_idx').on(t.sessionId, t.status, t.createdAt),
],
);
export const tessOutbox = pgTable(
'tess_outbox',
export const interactionOutbox = pgTable(
'interaction_outbox',
{
id: uuid('id').primaryKey().defaultRandom(),
sessionId: text('session_id')
.notNull()
.references(() => tessSessions.id, { onDelete: 'cascade' }),
.references(() => interactionSessions.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'),
contentDigest: text('content_digest').notNull(),
status: interactionOutboxStatusEnum('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),
uniqueIndex('interaction_outbox_session_idempotency_idx').on(t.sessionId, t.idempotencyKey),
index('interaction_outbox_session_status_created_idx').on(t.sessionId, t.status, t.createdAt),
],
);
export const tessCheckpoints = pgTable(
'tess_checkpoints',
export const interactionCheckpoints = pgTable(
'interaction_checkpoints',
{
id: uuid('id').primaryKey().defaultRandom(),
sessionId: text('session_id')
.notNull()
.references(() => tessSessions.id, { onDelete: 'cascade' }),
.references(() => interactionSessions.id, { onDelete: 'cascade' }),
checkpointId: text('checkpoint_id').notNull(),
cursor: text('cursor').notNull(),
summary: text('summary').notNull(),
@@ -572,26 +578,26 @@ export const tessCheckpoints = pgTable(
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),
uniqueIndex('interaction_checkpoints_session_idempotency_idx').on(t.sessionId, t.checkpointId),
index('interaction_checkpoints_session_epoch_idx').on(t.sessionId, t.compactionEpoch),
],
);
export const tessHandoffs = pgTable(
'tess_handoffs',
export const interactionHandoffs = pgTable(
'interaction_handoffs',
{
handoffId: text('handoff_id').primaryKey(),
sessionId: text('session_id')
.notNull()
.references(() => tessSessions.id, { onDelete: 'cascade' }),
.references(() => interactionSessions.id, { onDelete: 'cascade' }),
destination: text('destination').notNull(),
correlationId: text('correlation_id').notNull(),
checkpointId: text('checkpoint_id').notNull(),
status: tessHandoffStatusEnum('status').notNull().default('pending'),
status: interactionHandoffStatusEnum('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)],
(t) => [index('interaction_handoffs_session_status_idx').on(t.sessionId, t.status)],
);
// ─── Skills ─────────────────────────────────────────────────────────────────