forked from mosaicstack/stack
feat(tess): persist durable session state
This commit is contained in:
@@ -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