feat: add session_messages table for conversation history
Some checks failed
CI / Typecheck & Lint (pull_request) Failing after 0s

Add a session_messages table that stores the full user/assistant
conversation history within a session. Each session request now
persists both the user message and assistant response atomically
alongside the session_request and balance update.

- New schema: session_messages (id, session_id, role, content,
  session_request_id, created_at) with index on session_id
- New migration: 0008_session_messages.sql
- New endpoint: GET /sessions/:id/messages (macaroon-authed)
- Messages inserted transactionally during POST /sessions/:id/request

Fixes #37

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Alexander Whitestone
2026-03-22 21:51:55 -04:00
parent 4c747aa331
commit b4aa672c58
4 changed files with 105 additions and 2 deletions

View File

@@ -0,0 +1,13 @@
-- Migration: Session messages for conversation history
-- Stores user/assistant message pairs produced during session requests.
CREATE TABLE IF NOT EXISTS session_messages (
id SERIAL PRIMARY KEY,
session_id TEXT NOT NULL REFERENCES sessions(id),
role TEXT NOT NULL,
content TEXT NOT NULL,
session_request_id TEXT,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
CREATE INDEX idx_session_messages_session_id ON session_messages(session_id);

View File

@@ -5,6 +5,7 @@ export * from "./messages";
export * from "./bootstrap-jobs";
export * from "./world-events";
export * from "./sessions";
export * from "./session-messages";
export * from "./nostr-identities";
export * from "./timmy-config";
export * from "./free-tier-grants";

View File

@@ -0,0 +1,35 @@
import { pgTable, text, timestamp, serial } from "drizzle-orm/pg-core";
import { createInsertSchema } from "drizzle-zod";
import { z } from "zod/v4";
import { sessions } from "./sessions";
// ── session_messages ─────────────────────────────────────────────────────────
// Stores the full conversation history within a session.
// Each session request produces a "user" message (the request text)
// and an "assistant" message (the AI response), linked to the session
// and optionally to the session_request that produced them.
export const SESSION_MESSAGE_ROLES = ["user", "assistant"] as const;
export type SessionMessageRole = (typeof SESSION_MESSAGE_ROLES)[number];
export const sessionMessages = pgTable("session_messages", {
id: serial("id").primaryKey(),
sessionId: text("session_id")
.notNull()
.references(() => sessions.id),
role: text("role").$type<SessionMessageRole>().notNull(),
content: text("content").notNull(),
// Links back to the session_request that produced this message pair (nullable for flexibility)
sessionRequestId: text("session_request_id"),
createdAt: timestamp("created_at", { withTimezone: true }).defaultNow().notNull(),
});
export const insertSessionMessageSchema = createInsertSchema(sessionMessages).omit({
id: true,
createdAt: true,
});
export type SessionMessage = typeof sessionMessages.$inferSelect;
export type InsertSessionMessage = z.infer<typeof insertSessionMessageSchema>;