server: reconcile ai_sessions persistence to Chunk 9 AISession shape
Reshape the ai_sessions table (migration 003) and mapping/routes to the full AISession contract: title, kind, notes, created_at, last_message_at; drop legacy role/model/settings. toAISession now maps the new columns and routes create/patch/messages handlers use the new shape.
This commit is contained in:
@@ -200,11 +200,14 @@ export function createServerClient(opts: ServerClientOptions): StorageAdapter {
|
||||
async saveAISession(s: AISession): Promise<void> {
|
||||
await req("POST", `/api/projects/${s.projectId}/ai/sessions`, {
|
||||
id: s.id,
|
||||
role: s.role,
|
||||
model: s.model,
|
||||
settings: s.settings,
|
||||
title: s.title,
|
||||
kind: s.kind,
|
||||
messages: s.messages,
|
||||
suggestions: s.suggestions,
|
||||
notes: s.notes,
|
||||
createdAt: s.createdAt,
|
||||
updatedAt: s.updatedAt,
|
||||
lastMessageAt: s.lastMessageAt,
|
||||
});
|
||||
},
|
||||
|
||||
|
||||
@@ -96,7 +96,8 @@ function mockServerFetch(): typeof fetch {
|
||||
}
|
||||
m = u.pathname.match(/^\/api\/projects\/([^/]+)\/ai\/sessions$/);
|
||||
if (method === "POST" && m) {
|
||||
const s = { id: body.id ?? id(), projectId: m[1]!, role: body.role, model: body.model, settings: body.settings ?? {}, messages: body.messages ?? [], suggestions: body.suggestions ?? [] };
|
||||
const now = new Date().toISOString();
|
||||
const s = { id: body.id ?? id(), projectId: m[1]!, title: body.title ?? "", kind: body.kind ?? "session", messages: body.messages ?? [], suggestions: body.suggestions ?? [], notes: body.notes ?? [], createdAt: body.createdAt ?? now, updatedAt: body.updatedAt ?? now, lastMessageAt: body.lastMessageAt ?? null };
|
||||
sessions.set(s.id, s);
|
||||
return json({ session: s });
|
||||
}
|
||||
@@ -131,7 +132,8 @@ async function runScenario(adapter: StorageAdapter) {
|
||||
edges: [],
|
||||
});
|
||||
await adapter.saveAISession({
|
||||
id: "sess-1", projectId: project.id, role: "editor", model: "m", settings: {}, messages: [], suggestions: [],
|
||||
id: "sess-1", projectId: project.id, title: "Session", kind: "session",
|
||||
messages: [], suggestions: [], notes: [], createdAt: new Date().toISOString(), updatedAt: new Date().toISOString(), lastMessageAt: null,
|
||||
});
|
||||
|
||||
const unsub = adapter.onEvent(() => {});
|
||||
|
||||
@@ -211,13 +211,23 @@ export interface Note {
|
||||
linkedTo?: string;
|
||||
}
|
||||
|
||||
export type SuggestionKind =
|
||||
/** Editor inline-review suggestion kinds. */
|
||||
export type EditorSuggestionKind =
|
||||
| "replace"
|
||||
| "insert"
|
||||
| "delete"
|
||||
| "entity-update"
|
||||
| "draft";
|
||||
|
||||
/** AI suggestion kinds produced by the AI provider layer. */
|
||||
export type SuggestionKind =
|
||||
| "brainstorm"
|
||||
| "outline"
|
||||
| "research"
|
||||
| "review"
|
||||
| "expand"
|
||||
| "summarize";
|
||||
|
||||
export type SuggestionStatus = "pending" | "accepted" | "rejected" | "pinned";
|
||||
|
||||
export interface SuggestionTarget {
|
||||
@@ -234,20 +244,81 @@ export interface Suggestion {
|
||||
before: string;
|
||||
after: string;
|
||||
rationale: string;
|
||||
kind: SuggestionKind;
|
||||
kind: EditorSuggestionKind;
|
||||
status: SuggestionStatus;
|
||||
}
|
||||
|
||||
export type AISessionRole = "editor" | "reader" | "publisher" | "co-author";
|
||||
/** Kinds of AI-backed sessions. */
|
||||
export type SessionKind = "session" | "agent";
|
||||
|
||||
/** Role of a message within an AI session. */
|
||||
export type AIMessageRole = "system" | "user" | "assistant";
|
||||
|
||||
/** A single message in an AI session. */
|
||||
export interface AIMessage {
|
||||
id: string;
|
||||
createdAt: string;
|
||||
role: AIMessageRole;
|
||||
content: string;
|
||||
}
|
||||
|
||||
/** Kind of an AI-generated note. */
|
||||
export type NoteKind = "character" | "world" | "plot" | "theme" | "general";
|
||||
|
||||
/** Lifecycle status of an AI-generated note. */
|
||||
export type NoteStatus = "draft" | "published";
|
||||
|
||||
/** An AI-generated note attached to a session. */
|
||||
export interface AINote {
|
||||
id: string;
|
||||
sessionId: string;
|
||||
kind: NoteKind;
|
||||
title?: string;
|
||||
body: string;
|
||||
tags?: string[];
|
||||
status: NoteStatus;
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
}
|
||||
|
||||
/** Lifecycle status of an AI-generated suggestion. */
|
||||
export type AISuggestionStatus =
|
||||
| "pending"
|
||||
| "applied"
|
||||
| "accepted"
|
||||
| "rejected";
|
||||
|
||||
/** An AI-generated suggestion attached to a session. */
|
||||
export interface AISuggestion {
|
||||
id: string;
|
||||
sessionId: string;
|
||||
createdAt: string;
|
||||
kind: SuggestionKind;
|
||||
title: string;
|
||||
text: string;
|
||||
summary: string;
|
||||
originalText?: string;
|
||||
newText?: string;
|
||||
anchorIndex?: number;
|
||||
spanLength?: number;
|
||||
status: AISuggestionStatus;
|
||||
appliedAt?: string;
|
||||
resolvedAt?: string;
|
||||
resolution?: "accepted" | "rejected";
|
||||
}
|
||||
|
||||
/** An AI-backed session with its messages, suggestions, and notes. */
|
||||
export interface AISession {
|
||||
id: string;
|
||||
projectId: string;
|
||||
role: AISessionRole;
|
||||
model: string;
|
||||
settings: Record<string, unknown>;
|
||||
messages: Array<{ role: "system" | "user" | "assistant"; content: string }>;
|
||||
suggestions: Suggestion[];
|
||||
title: string;
|
||||
kind: SessionKind;
|
||||
messages: AIMessage[];
|
||||
suggestions: AISuggestion[];
|
||||
notes: AINote[];
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
lastMessageAt: string | null;
|
||||
}
|
||||
|
||||
/** A reference to a document that can be saved through the adapter. */
|
||||
|
||||
Reference in New Issue
Block a user