Claude 20a900c505
feat(memory): learn the user profile from conversations; add built-in semantic memory
Why: the user profile only knew what onboarding wrote, so people had to repeat themselves,
and semantic recall needed a hosted or separately run memory service. The profile now grows
from everyday conversations, and recall can run on the user's own server with a local
embeddings endpoint (for example llama.cpp). Both stay optional and are tested offline.

What:
- adapter-kit: EmbeddingProvider contract (describe, embed) and the profile.consolidate job
  (payload schema and a replace-keyed job factory, one pending job per user).
- adapters: OpenAI-compatible embeddings (POST {baseUrl}/embeddings through the guarded
  OpenAI-compatible fetch, batching, bounded responses, clear errors), a deterministic
  emulator, an in-process /v1/embeddings server emulator, and a shared conformance suite.
- adapters: "builtin" semantic memory stored in Postgres (semantic_memory_items with Float[]
  embeddings, cosine ranking in process, bot/shared/history scopes matching the existing
  providers), registered in the memory provider factory and configured through
  memory.connectProvider with an embeddings URL, model and optional API key. Private
  endpoints require the deployment owner, like the other local memory providers.
- adapters: profile consolidation reads the user's settled app conversations since
  UserProfile.consolidatedAt (never messaging channels), asks the main bot's model for strict
  JSON facts and an updated summary, drops restatements (embedding cosine when built-in
  memory is configured, normalized text otherwise), stores sensitive categories as pending,
  and advances the cursor with a compare-and-set. A reconciler hook enqueues it at most daily
  per active user; it skips under the scripted runtime and without a usable model.
- db: additive migration 20261002210400_ws4_semantic_memory.
- web: a "Built-in" option in Memory settings; AI data sharing names the embeddings server.

Tests: new embedding conformance, OpenAI-compatible wire, built-in provider and consolidation
unit tests; new Postgres suites for built-in memory (including a real loopback embeddings
server through the memory provider resolver) and for consolidation and its scheduling query,
both added to the integration harness. The settings E2E spec now opens the new form.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MRtGG9ARfqqhma3t85Ro2u
2026-10-02 21:45:54 +00:00

65 lines
2.3 KiB
TypeScript

import type { SpaceMemoryConfig } from "@rakazo/contracts";
import type { ComponentType } from "react";
import { BuiltinSettingsForm } from "./BuiltinSettingsForm";
import { SerenitySettingsForm } from "./SerenitySettingsForm";
import { SupermemorySettingsForm } from "./SupermemorySettingsForm";
export interface MemoryProviderConnectionDraft {
settings: Record<string, string>;
credentials: Record<string, string>;
}
export interface MemoryProviderSettingsFormProps {
busy: boolean;
onConnect: (draft: MemoryProviderConnectionDraft) => Promise<boolean>;
}
export interface MemoryProviderSettingsRegistration {
id: string;
name: string;
description: string;
SettingsForm: ComponentType<MemoryProviderSettingsFormProps>;
connectedLabel: (config: SpaceMemoryConfig) => string;
}
export const MEMORY_PROVIDER_SETTINGS: readonly MemoryProviderSettingsRegistration[] = [
{
id: "supermemory",
name: "Supermemory",
description: "Add semantic recall alongside native MEMORY.md memory.",
SettingsForm: SupermemorySettingsForm,
connectedLabel: (config) =>
config.settings.mode === "cloud" ? "Supermemory Cloud" : `Local · ${config.settings.baseUrl}`,
},
{
id: "serenity",
name: "Serenity",
description: "Self-hosted Serenity brain for durable memory.",
SettingsForm: SerenitySettingsForm,
connectedLabel: (config) => {
const label = config.settings.brainLabel?.trim();
const endpoint = config.settings.endpoint ?? "Serenity";
const mode = config.settings.allowWrites === "true" ? "read/write" : "recall only";
return label ? `Serenity · ${label} · ${mode}` : `Serenity · ${endpoint} · ${mode}`;
},
},
{
id: "builtin",
name: "Built-in",
description: "Semantic recall stored on this server.",
SettingsForm: BuiltinSettingsForm,
connectedLabel: (config) =>
`Built-in · ${config.settings.model ?? ""} · ${config.settings.baseUrl ?? ""}`,
},
];
export function memoryProviderSettings(provider: string) {
return MEMORY_PROVIDER_SETTINGS.find((entry) => entry.id === provider) ?? null;
}
export function defaultMemoryProviderSettings(): MemoryProviderSettingsRegistration {
const registration = MEMORY_PROVIDER_SETTINGS[0];
if (!registration) throw new Error("No memory provider settings are registered.");
return registration;
}