import { type RecommendOptions } from './recommend.ts';
import { type FindOptions } from './search.ts';
import { type ImageOptions } from './images.ts';
import { Telemetry, type TelemetryOptions } from './telemetry.ts';
/**
 * Teach the runtime the words an app's own vocabulary is spoken with.
 *
 * Declaring a value *replaces* its forms rather than adding to them. Merging
 * looked harmless and was not: editing this list during development left the
 * process holding the union of every version it had seen, which shifts every
 * pointer position in the window — reading broke and the cause was invisible.
 * The app states the full list for a value; what it states is what is used.
 */
export declare function addSurfaceForms(forms: Record<string, string[]>): void;
/**
 * A text as the model's encoder reads it: the mean of its token states, unit
 * length. Two texts about the same thing land close together — the basis of
 * `recommend`. Texts longer than the window are cut.
 */
export declare function embed(text: string): Promise<Float32Array>;
export declare const normalize: (s: unknown) => string;
export type Rule = {
    /** `'insert'`: required when a row is created, optional in an edit */
    required?: boolean | 'insert';
    min?: number;
    max?: number;
    email?: boolean;
    pattern?: RegExp;
    /** trim the value before checking and writing it */
    trim?: boolean;
    /** no other row may hold the same value; a string is that failure's own message */
    unique?: boolean | string;
    /** with `unique`: the value only has to be unique together with these logical columns (one grant per subscriber and article) */
    uniqueWith?: string[];
    /** shown to the caller when the value breaks the rule */
    message?: string;
};
export type Table = {
    /** the word the model sees; may differ from the physical table */
    as: string;
    table: string;
    /** optional read-side view, when a useful row needs a join */
    view?: string;
    columns: Record<string, string>;
    enums?: Record<string, string[]>;
    /**
     * Boolean columns, as the two words people use for them: `visible:
     * ['masquee', 'visible']`. They are a two-value domain to the model and 0 or 1
     * to the database.
     */
    flags?: Record<string, [string, string]>;
    /**
     * Rows this table points at, read along with it: `author: { table: 'users',
     * on: 'author_id', columns: { id: 'id', name: 'name' } }` gives each row an
     * `author` object. Reads only; writes still touch this table alone.
     */
    relations?: Record<string, {
        table: string;
        on: string;
        key?: string;
        columns: Record<string, string>;
    }>;
    /** logical columns a route's free-text search looks in */
    searchable?: string[];
    /** physical columns that hold JSON, parsed on the way out */
    json?: string[];
    /**
     * How physical columns read back, when the driver hands out something else:
     * `{ is_published: 'boolean', event_date: 'date', meta: 'json', price: 'number' }`.
     * A date is read from ISO or SQL text, or from seconds or milliseconds.
     */
    types?: Record<string, 'boolean' | 'date' | 'json' | 'number'>;
    /**
     * Per physical column, how a driver value reads back and how a value is
     * written — an ORM's own column conversions (see `fromDrizzle`). They win
     * over `types`.
     */
    read?: Record<string, (v: unknown) => unknown>;
    write?: Record<string, (v: unknown) => unknown>;
    /**
     * Slug columns and the logical column each is made from: `{ slug: 'title' }`,
     * or `{ slug: { from: 'title', keep: true } }` for a slug set once, at creation.
     */
    slugs?: Record<string, string | {
        from: string;
        keep?: boolean;
    }>;
    /** values the schema computes when a write leaves the column out, by physical column */
    defaults?: {
        insert?: Record<string, () => unknown>;
        update?: Record<string, () => unknown>;
    };
    /**
     * A column the model fills by meaning when a write leaves it empty: the
     * value whose description the row's text is closest to (a category from the
     * title and content). `{ category: { from: ['title', 'content'], labels: {
     * sport: 'football, match, stade', culture: 'concert, festival, expo' } } }`,
     * by physical column. Needs the embedder; skipped without it.
     */
    infer?: Record<string, {
        from: string[];
        labels: Record<string, string>;
        min?: number;
    }>;
    /** physical columns whose written value is replaced by its hash (a password), after the rules */
    hash?: Record<string, (v: string) => string | Promise<string>>;
    /**
     * Physical columns that hold an uploaded file's path. A multipart request's
     * file of the same field name is checked, optionally transformed (resized),
     * stored under `dir` with a random name, and the column set to `url` + name;
     * the file it replaces, or that belonged to a deleted row, is removed.
     */
    files?: Record<string, {
        dir: string;
        url?: string;
        accept?: RegExp;
        maxBytes?: number;
        ext?: string;
        message?: string;
        /** a row must be created with a file: this is the message when none is sent */
        required?: string;
        /** an image is processed before it is stored: size, format, quality, metadata stripped */
        image?: ImageOptions;
        /** more sizes of the same image, each stored in its own column: `{ thumb_path: { width: 300 } }` */
        variants?: Record<string, ImageOptions>;
        /** columns that receive the stored file's facts: `{ width: 'width', height: 'height', size: 'size', type: 'mime_type', name: 'original_name' }` */
        record?: Partial<Record<'width' | 'height' | 'size' | 'type' | 'name', string>>;
        transform?: (data: Buffer, file: {
            filename?: string;
            type?: string;
        }) => Buffer | Promise<Buffer>;
    }>;
    /** fields added to each row read, from the row: `{ gravatar: r => hash(r.email) }` */
    computed?: Record<string, (row: Record<string, any>) => unknown>;
    /** physical columns never returned (a password hash); dotted for a relation's (`author.email`) */
    hidden?: string[];
    /**
     * What a written value must look like, by logical column. Checked on every
     * INSERT and UPDATE, whoever wrote the value — the model or a form. A failed
     * check is a 400 listing every message, as a form wants them.
     */
    rules?: Record<string, Rule>;
    /**
     * Declared functions to call after a write on this table succeeds, with the
     * written values and the new id: `{ insert: ['notifyModerators'] }`.
     */
    after?: {
        insert?: string[];
        update?: string[];
        delete?: string[];
    };
    /**
     * Rows elsewhere that point at this table, for a delete: `cascade` removes
     * them first, `restrict` refuses the delete while any exist (with `message`).
     * For schemas that do not declare it as a foreign key.
     */
    children?: {
        table: string;
        on: string;
        cascade?: boolean;
        message?: string;
    }[];
    /**
     * Rows elsewhere read along with each row, as a list: `attachments: { table:
     * 'article_attachments', on: 'article_id', columns: {...}, orderBy: 'position' }`.
     */
    lists?: Record<string, {
        table: string;
        on: string;
        columns: Record<string, string>;
        orderBy?: string;
        limit?: number;
        /** conditions on the listed rows, by physical column (`{ status: 'published' }`, comparisons as in `where`) */
        where?: Record<string, unknown>;
        /** rows each listed row points at, nested as objects (a purchase's article) */
        relations?: Record<string, {
            table: string;
            on: string;
            key?: string;
            columns: Record<string, string>;
        }>;
    }>;
    /**
     * Many-to-many through a join table, read as a list and written from a list
     * of ids in the body (`field`, default `<name>Ids`): `categories: { through:
     * 'article_categories', from: 'article_id', to: 'category_id', table:
     * 'categories', columns: { id: 'id', name: 'name' } }`.
     */
    many?: Record<string, {
        through: string;
        from: string;
        to: string;
        table: string;
        key?: string;
        columns: Record<string, string>;
        field?: string;
    }>;
    /**
     * A count of rows elsewhere, per row: `views: { table: 'article_views', on:
     * 'article_id' }`. Returned with each row, and usable in `orderBy` and `where`.
     */
    tallies?: Record<string, {
        table: string;
        on: string;
        where?: Record<string, unknown>;
    }>;
    /** rows belong to one caller; the runtime pins that, never the model */
    ownerColumn?: string;
    ownerFrom?: 'userId' | 'sid';
    /** columns a caller may write; anything else in a plan is ignored */
    writable?: string[];
    /**
     * How people ask for this table, besides its name: "abonnés" for `members`,
     * "commandes" for `orders`. Matched literally against the request, which is
     * what lets a schema use a word the weights know while readers use their own.
     */
    says?: string[];
    /**
     * How people name a column, besides its own name: `section: ['rubrique']`.
     *
     * These are structural words, not values. Asked for "les articles de la
     * rubrique sport" the model filtered the title on `rubrique`; declaring it
     * here is what lets the runtime see that the caller was naming a column and
     * not searching for the word.
     */
    columnSays?: Record<string, string[]>;
    /**
     * Who may read and who may write this table.
     *
     * Authorisation is NOT the model's job. Across six training runs its refusal of
     * out-of-scope requests reached 1.000 while its denial of unauthorised ones
     * stalled at 0.607 — and a permission check that is right two times in three is
     * not a permission check. The model says what was asked; this says who may.
     */
    roles?: {
        read?: string[];
        write?: string[];
        /** who may INSERT and who may DELETE, when that differs from write */
        create?: string[];
        remove?: string[];
        /**
         * Who may write a particular column. A journalist files a draft; only an
         * editor moves it to published — which is one column, not one table, so
         * table-level write cannot say it.
         */
        columns?: Record<string, string[]>;
    };
};
export type Fn = {
    name: string;
    /** the argument names the prose may use, in the order a sentence tends to give them */
    params: string[];
    /** those that must be present; the call is refused rather than run without them */
    required?: string[];
    /** a parameter the runtime fills from the session, never from the sentence */
    fromContext?: Record<string, keyof Ctx | string>;
    /**
     * How people ask for this, besides its name: "rebuild the search index",
     * "envoie la newsletter". Matched literally against the request, so an app can
     * name its actions freely and still be reached by ordinary sentences.
     */
    says?: string[];
    roles?: string[];
};
export type Schema = {
    tables: Table[];
    functions?: Fn[];
};
type Maybe<T> = T | Promise<T>;
export type Store = {
    all(sql: string, ...p: unknown[]): Maybe<Record<string, any>[]>;
    run(sql: string, ...p: unknown[]): Maybe<{
        changes: number | bigint;
        lastInsertRowid: number | bigint;
    }>;
    /** free text -> row id, for an item a sentence names */
    lookup(table: string, text: string): Maybe<number | null>;
    /** how identifiers are quoted and rows inserted; SQLite when absent */
    dialect?: 'sqlite' | 'mysql';
};
/**
 * Warn about schema words the model has never seen.
 *
 * An out-of-vocabulary word in the input window is not ignored by a pointer
 * decoder — it is a position, so it becomes something to point at, and it
 * competes with the right answer. Naming a column `headline` instead of `title`
 * was enough to turn every filter on this newspaper into
 * `SELECT articles FILTER headline VAL <the first noun of the sentence>`.
 *
 * Values are a different matter: a row's title or an invented section is
 * *meant* to be unknown, and the pointer mechanism exists precisely so those
 * work. It is the schema's own words — tables, columns, enum values — that
 * should be words the weights know.
 */
export declare function checkVocabulary(schema: Schema, warn?: (...data: any[]) => void): string[];
/**
 * What the model is told the caller is.
 *
 * An app may name its roles anything — reader, journalist, editor — but those
 * words are not in the model's vocabulary, and an unknown word in the context
 * does not merely get ignored: it becomes a pointer target. With `role = editor`
 * in the window, "montre moi tous les articles" came back as
 * `SELECT articles FILTER title VAL editor`. Every skill collapsed, not just the
 * ones about permission.
 *
 * So the model is told one of the three words it was trained on, derived from
 * what the schema actually lets this role do. It costs nothing: the model does
 * not decide permission, `authorise` does, and that keeps the real role.
 */
export declare function modelRole(schema: Schema, role: string | undefined, signedIn: boolean): string;
/**
 * The permission gate: the only thing that decides access, and the reason no app
 * in this repo has an auth.ts. Pure and exported so it can be tested against a
 * plan directly — testing it through a sentence would test the model instead,
 * and the model is not what enforces this.
 *
 * Returns the refusal message, or null when the role may proceed.
 */
export declare function authorise(plan: Plan, schema: Schema, role: string): string | null;
export type Ctx = Record<string, any> & {
    userId?: number;
    role?: string;
};
/**
 * How a route wants its rows back. Presentation, not intent: the order a list
 * is shown in and how much of it fits a page are the route's to say, never the
 * model's to guess. Only a SELECT reads them.
 */
export type Shape = {
    /** a column, or several; `-date` is descending */
    orderBy?: string | string[];
    desc?: boolean;
    limit?: number;
    offset?: number;
    /** 1-based page of `limit` rows, instead of an offset */
    page?: number;
    /** also count every matching row, for a pager */
    total?: boolean;
    /** one row expected: returned alone, and a 404 when there is none */
    one?: boolean;
    /** free text matched against the table's `searchable` columns */
    search?: string;
    /** a DELETE also returns the rows it removed; an INSERT returns the row it made (`result.row`) */
    returning?: boolean;
    /**
     * Rank the rows the plan chose for one reader — what they read, what readers
     * like them read, popularity, recency — and keep the best `limit` (see
     * recommend.ts). Columns are physical.
     */
    recommend?: RecommendOptions;
    /**
     * Order the rows the plan chose by how close their text is to `to` (a text,
     * or a row's text when `to` is `{ id }`): "à lire aussi", near-duplicates.
     */
    similar?: {
        to: string | {
            id: unknown;
        };
        text: string[];
        limit?: number;
        min?: number;
    };
    /** full-text search over the rows the plan chose, by weighted fields, prefixes, typos and meaning (see search.ts) */
    find?: FindOptions;
    /** which of the table's `lists` a read loads (all by default; `false` for none) */
    lists?: string[] | false;
    /** a route's own image processing for a file column, over the table's (`{ filename: { width: 400, height: 400, fit: 'cover' } }`) */
    images?: Record<string, ImageOptions>;
    /** a multipart request's files, for the table's `files` columns */
    uploads?: {
        name?: string;
        filename?: string;
        type?: string;
        data: Buffer;
    }[];
    /**
     * More counts for the same read, each with its own conditions (same syntax as
     * `where`, applied instead of the request's): `{ pending: { status: 'pending' } }`
     * for a badge next to a filtered list.
     */
    counts?: Record<string, Record<string, unknown>>;
    /** sums over a column, each with its own conditions: `{ revenue: { column: 'amount', where: {...} } }` */
    sums?: Record<string, {
        column: string;
        where?: Record<string, unknown>;
    }>;
    /**
     * An INSERT of several rows at once, by column name, each written or — with
     * `upsert` — replacing the row with the same unique key: key/value settings.
     */
    rows?: Record<string, unknown>[];
    /** insert or update in place on a unique column (`'email'`; `true` means the key); the result says `action: 'inserted' | 'updated'` */
    upsert?: boolean | string;
    /** bulk rows: a row colliding with a unique key is skipped and counted, not an error */
    ignore?: boolean;
    /** a DELETE that removes duplicates: all but the first row (lowest id) of each value of `by` */
    dedupe?: {
        by: string;
    };
    /**
     * Runs after the rules pass and before the write, and may add columns: the
     * place to store an upload, so an invalid form leaves no orphan file.
     */
    before?: (row: Record<string, unknown>) => Record<string, unknown> | void | Promise<Record<string, unknown> | void>;
    /**
     * The operations this route can mean. A PUT route is an update whatever the
     * sentence sounds like: "modifie la categorie 2 : nom Sport, masquee" came
     * back as a SELECT, and a read answering a write with 200 tells the caller
     * the change was made. Refusal stays possible.
     */
    ops?: string[];
    /**
     * The row, when the URL already names it: `/api/categories/1`. It replaces any
     * condition the model wrote on those columns — "change la categorie 1 ... et
     * l ordre 2" once updated category 2.
     */
    where?: Record<string, unknown>;
    /**
     * Values the request already holds by name, as a form body does. Written to
     * the columns they name (logical or physical, camelCase or not) if the plan
     * writes that table and the column is writable; they win over whatever the
     * model copied out of the sentence.
     */
    values?: Record<string, unknown>;
    /**
     * Physical columns the route itself writes on an INSERT or UPDATE — who
     * moderated and when. Facts about the request, not things the caller says.
     */
    stamp?: Record<string, unknown>;
};
export type Actions = Record<string, (a: {
    args: Record<string, string>;
    ctx: Ctx;
    plan: Plan;
}) => unknown | Promise<unknown>>;
/**
 * The exact token window the model reads for a request — for diagnosis, e.g.
 * comparing a real app's window with the ones training generates.
 */
export declare function windowFor(schema: Schema, prompt: string, ctx: Ctx, input: string): string[];
export type Clause = {
    column: string;
    value: string[];
    /** true when the value was pointed at inside the caller's own sentence */
    said?: boolean;
};
export type Plan = {
    op: string;
    target: string | null;
    filters: Clause[];
    sets: Clause[];
    tokens: string[];
    /** clauses the runtime moved to the column that declares their value */
    repaired?: string[];
    /** the model call that made this plan — what telemetry and feedback refer to */
    callId?: string;
};
/**
 * The plan as it will actually run, which is not always what the model wrote:
 * a clause the runtime moved to the column that declares its value shows here in
 * its repaired form. `plan.tokens` keeps the raw generation, and `plan.repaired`
 * says what moved, so both are visible rather than one quietly standing in for
 * the other.
 */
export declare const planText: (plan: Plan) => string;
/** `slugify('Économie locale')` → `economie-locale` */
export declare const slugify: (s: string) => string;
export declare function defineApp(opts: {
    schema: Schema;
    prompt: string;
    /** Your database. Left out, Talos keeps its own (see local.ts): a SQLite file made from the schema. */
    store?: Store;
    /** Rows for Talos's own database, put in tables that are still empty (logical or physical names). */
    seed?: Record<string, Record<string, unknown>[]>;
    actions?: Actions;
    /** Set false to stop the vocabulary warning, once you have read it. */
    checkVocab?: boolean;
    /** What this instance reports (see telemetry.ts): on by default, `false` for nothing, or part by part. */
    telemetry?: TelemetryOptions | false;
}): {
    run: (input: string, ctx?: Ctx, shape?: Shape) => Promise<{
        callId: string;
        plan: Plan;
        ms: number;
        status: any;
        result: {
            error: any;
            errors: any;
            data: any;
        };
    } | {
        callId: string;
        plan: Plan;
        ms: number;
        status: number;
        result: any;
    }>;
    decide: (input: string, ctx: Ctx, ops?: string[], template?: string) => Promise<{
        plan: Plan;
        ms: number;
        refuseP: number;
        callId: `${string}-${string}-${string}-${string}-${string}`;
    }>;
    execute: (plan: Plan, ms: number, input: string, ctx: Ctx, shape: Shape, cached?: boolean) => Promise<{
        callId: string;
        plan: Plan;
        ms: number;
        status: any;
        result: {
            error: any;
            errors: any;
            data: any;
        };
    } | {
        callId: string;
        plan: Plan;
        ms: number;
        status: number;
        result: any;
    }>;
    route: (template: string, base?: Shape) => (p: Record<string, unknown>, ctx?: Ctx, shape?: Shape) => Promise<{
        callId: string;
        plan: Plan;
        ms: number;
        status: any;
        result: {
            error: any;
            errors: any;
            data: any;
        };
        sentence: string;
        cached: boolean;
    } | {
        callId: string;
        plan: Plan;
        ms: number;
        status: number;
        result: any;
        sentence: string;
        cached: boolean;
    }>;
    prompt: string;
    schema: Schema;
    /** The database Talos reads and writes: yours, or its own. */
    store: Store;
    /** Resolves once Talos's own database is open, with its file; at once with your store. */
    ready: Promise<{
        file: string | undefined;
    }>;
    /** This instance's id: what someone quotes to be helped with it. */
    instanceId: string;
    /** Feedback on one call (its `callId`): a text, a rating 1–5, or both. */
    feedback: (callId: string, fb: {
        text?: string;
        rating?: number;
    }) => Promise<void>;
    telemetry: Telemetry;
};
export declare const modelInfo: () => {
    version: string;
    talos: string;
    params: number;
    dim: number;
    maxSrc: number;
    maxTgt: number;
    vocab: number;
    ops: string[];
};
export {};
