import type { AdapterContext, MemoryStore } from "@rakazo/adapter-kit"; import { MEMORY_DOCUMENT_MAX_BYTES, MemoryDocumentTooLargeError } from "@rakazo/adapter-kit"; const DEFAULT_MEMORY_PATH = "MEMORY.md"; /** One dated Markdown bullet; later lines are indented so the note stays a single list item. */ export function datedMemoryNote(content: string, date: string): string { const [first = "", ...rest] = content.trim().split(/\r?\n/); return [`- ${date}: ${first}`, ...rest.map((line) => (line.trim() ? ` ${line}` : ""))].join( "\n", ); } /** * The builtin `remember` tool: appends a dated note to the bot's memory document, or rewrites the * whole document with `mode: "replace"`. Retries of the same call are replayed from its effect * record by the executor, so a note is appended once. */ export async function rememberFromTool( memory: Pick, context: AdapterContext, input: { botId: string; runId: string; threadId: string; /** The user's local date, YYYY-MM-DD. */ date: string; args: Record; }, ): Promise<{ ok: true } | { error: string }> { const replace = input.args.mode === "replace"; const content = String(input.args.content ?? ""); const path = String(input.args.path ?? "").trim() || DEFAULT_MEMORY_PATH; if (!replace && !content.trim()) return { error: "content is empty" }; try { await memory.commit( { scope: "bot", botId: input.botId, path, content: replace ? content : datedMemoryNote(content, input.date), mode: replace ? "replace" : "append", sourceRunId: input.runId, sourceThreadId: input.threadId, }, context, ); } catch (error) { if (!(error instanceof MemoryDocumentTooLargeError)) throw error; return { error: `${path} is full (${MEMORY_DOCUMENT_MAX_BYTES / 1024} KB). Condense it with mode "replace", then save this note again.`, }; } return { ok: true }; }