feat(tess): persist durable session state (#729)
This commit was merged in pull request #729.
This commit is contained in:
@@ -487,6 +487,120 @@ 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 interactionInboxStatusEnum = pgEnum('interaction_inbox_status', [
|
||||
'pending',
|
||||
'processing',
|
||||
'processed',
|
||||
]);
|
||||
export const interactionOutboxStatusEnum = pgEnum('interaction_outbox_status', [
|
||||
'pending',
|
||||
'processing',
|
||||
'delivered',
|
||||
]);
|
||||
export const interactionHandoffStatusEnum = pgEnum('interaction_handoff_status', [
|
||||
'pending',
|
||||
'accepted',
|
||||
]);
|
||||
|
||||
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()
|
||||
.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 interactionInbox = pgTable(
|
||||
'interaction_inbox',
|
||||
{
|
||||
id: uuid('id').primaryKey().defaultRandom(),
|
||||
sessionId: text('session_id')
|
||||
.notNull()
|
||||
.references(() => interactionSessions.id, { onDelete: 'cascade' }),
|
||||
idempotencyKey: text('idempotency_key').notNull(),
|
||||
correlationId: text('correlation_id').notNull(),
|
||||
content: text('content').notNull(),
|
||||
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('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 interactionOutbox = pgTable(
|
||||
'interaction_outbox',
|
||||
{
|
||||
id: uuid('id').primaryKey().defaultRandom(),
|
||||
sessionId: text('session_id')
|
||||
.notNull()
|
||||
.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(),
|
||||
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('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 interactionCheckpoints = pgTable(
|
||||
'interaction_checkpoints',
|
||||
{
|
||||
id: uuid('id').primaryKey().defaultRandom(),
|
||||
sessionId: text('session_id')
|
||||
.notNull()
|
||||
.references(() => interactionSessions.id, { onDelete: 'cascade' }),
|
||||
checkpointId: text('checkpoint_id').notNull(),
|
||||
contentDigest: text('content_digest').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('interaction_checkpoints_session_idempotency_idx').on(t.sessionId, t.checkpointId),
|
||||
index('interaction_checkpoints_session_epoch_idx').on(t.sessionId, t.compactionEpoch),
|
||||
],
|
||||
);
|
||||
|
||||
export const interactionHandoffs = pgTable(
|
||||
'interaction_handoffs',
|
||||
{
|
||||
handoffId: text('handoff_id').primaryKey(),
|
||||
sessionId: text('session_id')
|
||||
.notNull()
|
||||
.references(() => interactionSessions.id, { onDelete: 'cascade' }),
|
||||
destination: text('destination').notNull(),
|
||||
correlationId: text('correlation_id').notNull(),
|
||||
checkpointId: text('checkpoint_id').notNull(),
|
||||
status: interactionHandoffStatusEnum('status').notNull().default('pending'),
|
||||
createdAt: timestamp('created_at', { withTimezone: true }).notNull().defaultNow(),
|
||||
updatedAt: timestamp('updated_at', { withTimezone: true }).notNull().defaultNow(),
|
||||
},
|
||||
(t) => [index('interaction_handoffs_session_status_idx').on(t.sessionId, t.status)],
|
||||
);
|
||||
|
||||
// ─── Skills ─────────────────────────────────────────────────────────────────
|
||||
|
||||
export const skills = pgTable(
|
||||
|
||||
Reference in New Issue
Block a user