import type { BackendContext } from '@jaspers-ai/sdk' import { docName, isReadable, READ_TYPES, type Doc } from './docs.ts' import { research, type StoredDoc } from './research.ts' // Documents: what the user uploads, and the pages agents read. Each lives in the research store, the // account's own, cut by its pages or headings; nothing is kept on this machine. The list is the live // value `docs`, published again after every change. function asDoc(stored: StoredDoc): Doc { return { name: stored.title, id: stored.id, size: stored.chars, modified: Date.parse(stored.added_at) || 0 } } export async function publishDocs(ctx: BackendContext): Promise { const docs = (await research(ctx, 'documents', {})) .map(asDoc) .sort((a, b) => a.name.localeCompare(b.name)) ctx.live.set('docs', docs) return docs } /** * Puts a document's text in the store under its name; the same name again replaces it. A PDF's text * is read in the view, so only the text comes here. */ export async function uploadDoc( ctx: BackendContext, upload: { name: string; text: string }, ): Promise<{ id: string; title: string; said: string }> { const title = docName(upload.name) if (!isReadable(title)) { throw new Error(`${upload.name} is not read yet: the files read are ${READ_TYPES.join(', ')}.`) } if (!upload.text.trim()) throw new Error(`${upload.name} has no text.`) const put = await research<{ id: string; chunks: number }>(ctx, 'index_document', { origin: 'upload', title, text: upload.text, }) await publishDocs(ctx) return { id: put.id, title, said: `Indexed ${title}: ${put.chunks} ${put.chunks === 1 ? 'chunk' : 'chunks'}.` } } /** A public page an agent asked for, read by the app and put in the store as a web page under its address. */ export async function savePage( ctx: BackendContext, url: string, signal: AbortSignal, ): Promise<{ url: string; doc: string }> { const page = await ctx.web.fetch(url, { signal }) if (!page.text.trim()) throw new Error(`${page.url} has no text: a page drawn by script reads empty here.`) const put = await research<{ id: string }>( ctx, 'index_document', { origin: 'web', title: page.url, text: page.text, url: page.url }, signal, ) await publishDocs(ctx) return { url: page.url, doc: put.id } } export async function deleteDoc(ctx: BackendContext, id: string): Promise { const { deleted } = await research<{ deleted: number }>(ctx, 'delete', { documents: [id] }) await publishDocs(ctx) return deleted ? 'Deleted.' : 'Already gone.' }