// The tables of the knowledge module. Five. // // `knowledge_collection` is one named collection of documents, project-owned; the collection name // is the slug procedures take (a procedure's input carries slugs, never ids). `knowledge_document` // is one file's indexing state in one collection — the row the UI's progress states read, and the // idempotency key of the index job (a file is indexed into a collection once, and re-indexing the // same pair resumes the one row). `knowledge_config` is the organization's enablement state: the // durable answer to "is document AI on" and which backend serves it. `knowledge_auto_index` is a // project's opt-in to indexing its uploaded PDFs, and `knowledge_passage_set` what one such PDF sent // to the service, so removing it can tombstone each passage. No secret ever lands here. import { defineEntity, idSchema, instantSchema } from "@conusai-kit/data" import { z } from "zod" import { DOCUMENT_STATUSES, KNOWLEDGE_BACKENDS } from "./statuses.js" export const collections = defineEntity({ name: "collection", table: "knowledge_collection", tenancy: "project", fields: { name: z.string().min(1).max(100), createdBy: z.string().min(1).max(128), }, unique: [["name"]], }) export const documents = defineEntity({ name: "document", table: "knowledge_document", tenancy: "project", fields: { collectionName: z.string().min(1).max(100), /** The row of the files module this document indexes: an opaque id, never a foreign key. */ fileId: idSchema, filename: z.string().min(1).max(255), status: z.enum(DOCUMENT_STATUSES), /** A stable failure code when the indexing failed; null otherwise. */ error: z.string().max(200).nullable(), indexedAt: z.string().nullable(), requestedBy: z.string().min(1).max(128), }, // One row per (collection, file): a re-index resumes the same row instead of duplicating it. unique: [["collectionName", "fileId"]], indexes: [["collectionName"]], }) export const config = defineEntity({ name: "config", table: "knowledge_config", tenancy: "org", fields: { enabled: z.boolean(), /** Which search backend serves this organization. */ backend: z.enum(KNOWLEDGE_BACKENDS), // The three below held the app's own vector store, which is gone (ADR-061); the migration that made // them has run, so they stay as "managed, nothing to link" and nothing reads them. source: z.enum(["managed"]), serviceId: z.string().max(128).nullable(), url: z.string().max(500).nullable(), updatedBy: z.string().min(1).max(128), /** When the configuration last changed; the managed updated_at is the row's own bookkeeping. */ configuredAt: instantSchema, }, }) /** * A project's opt-in to indexing its uploaded PDFs automatically (core-11): no row means off. It names * the collection the files go into and the administrator who turned it on, whose permission every * indexing job checks again, since the job runs for nobody else. */ export const autoIndex = defineEntity({ name: "autoindex", table: "knowledge_auto_index", tenancy: "project", fields: { collectionName: z.string().min(1).max(100), enabledBy: z.string().min(1).max(128), enabledAt: instantSchema, }, }) /** * The passages an automatic PDF index sent for one document (core-11). The service indexed one inline * item per passage under the file's reference plus `#p{page}.{index}`, and removes a reference only * exactly, so the row keeps the file's reference (which outlives the file) and the count of passages * on each page, comma-separated from page 1 ("2,0,1": two on page 1, none on page 2, one on page 3). */ export const passageSets = defineEntity({ name: "passageset", table: "knowledge_passage_set", tenancy: "project", fields: { collectionName: z.string().min(1).max(100), fileId: idSchema, documentRef: z.string().min(1).max(1024), pages: z.string().max(65_536), }, unique: [["collectionName", "fileId"]], }) /** The tables of the first migration, as first shipped. */ export const KNOWLEDGE_ENTITIES_V1 = Object.freeze([collections, documents, config]) /** Every table this module owns, in the order its migrations create them. */ export const KNOWLEDGE_ENTITIES = Object.freeze([...KNOWLEDGE_ENTITIES_V1, autoIndex, passageSets])