/** * The visual language every interactive card in this channel speaks. * * A bot that interrupts someone's chat to ask for a decision should look like * it belongs to the product, so the cards here are built from a small fixed * vocabulary rather than composed ad hoc: one ink colour per semantic state, * one type role per text purpose, a 20px content grid, and copy that names the * action rather than the gesture. Callers pick a state and pass content; every * other visual decision is made once, here. * * Three rules are load-bearing rather than cosmetic: * * - **Model-authored text never renders as markup.** A command's arguments, * the model's justification, the question it wrote and the options it offers * are all untrusted; each rides a `plain_text` element so it renders * literally and cannot forge the card's own words. * - **This module's own copy is bilingual.** Every string authored here ships * as a {@link Copy}, and the platform renders the reader's own language from * the element's `i18n` map — which only `plain_text` carries, so nothing * here uses markdown, bold included. One card serves a mixed room without * anyone detecting a locale. Model-authored text stays in whatever language * the model wrote: translating a command would be a lie about what runs. * - **No images.** These cards are built at runtime and every image would need * an uploaded `img_key`, so headers are text and status is carried by ink. * @module dsh-lark-channel/cards */ /** * One string this module authored, in the languages a card can carry. * * `zh` is also the fallback every other locale gets, matching a channel whose * default deployment is Feishu. */ export interface Copy { readonly zh: string; readonly en: string; } /** Semantic state of a card, which picks its title ink. */ export type CardState = 'info' | 'success' | 'warning' | 'danger' | 'neutral'; /** Names the form and the select inside it, so a submission can be read back. */ export declare const QUESTION_FORM = "dsh_question_form"; export declare const QUESTION_SELECT = "dsh_question_options"; /** * The card that asks a human to approve one escalated tool call. * @param input - what is being asked, and the payloads its buttons carry. * @returns a schema 2.0 card object. */ export declare function approvalCard(input: { readonly toolName: string; readonly reason?: string | undefined; readonly command?: string | undefined; /** The sandbox mode this call asked to be raised to, when it asked. */ readonly escalateTo?: string | undefined; readonly allow: object; readonly reject: object; /** Payload for the button that switches the session, when one is offered. */ readonly always?: object | undefined; }): object; /** * The card an approval is replaced with once decided — no live buttons, and * the decision legible from the ink alone. * @param input - the tool, the outcome, and who decided when someone did. * @returns a schema 2.0 card object. */ export declare function settledApprovalCard(input: { readonly toolName: string; readonly outcome: string; readonly decidedBy?: string | undefined; }): object; /** * The card that asks a group to authorize one outbound file. * * The file, the workspace and the size are the whole card: an approver who * cannot see what is leaving cannot judge whether it may leave, which is the * same reason the tool approval prints its command verbatim. * * The file is named by its place INSIDE the workspace, and the workspace by its * own name. An absolute path would publish the host's directory layout — the * operator's login name included — to everyone in the room, none of which is * what an approver is judging. What makes that safe to shorten is where the * relative form comes from: `resolveOutboundFile` derives it from the canonical * path it just cleared, so the room still reads the real object and a symlink * cannot present itself as a file the room believes it is approving. A * prettified path — `~`, an ellipsis, a bare basename — would give that away, * and is exactly what this is not. * @param input - the file being offered, and the payloads its buttons carry. * @returns a schema 2.0 card object. */ export declare function fileApprovalCard(input: { /** Where the file sits inside the workspace, derived from its canonical path. */ readonly path: string; /** The workspace's own name. */ readonly workspace: string; readonly bytes: number; readonly allow: object; readonly reject: object; }): object; /** * The card a file approval is replaced with once decided — no live buttons, and * the decision legible from the ink alone. * * It names the same two things the live card named, and for the same reason it * named them that way: this card REPLACES the one the room read, so a record * that dropped the workspace would leave the room unable to tell later which * directory the file came out of — and one that spelled the absolute path would * put the host's layout back in the room the moment a decision was made. * @param input - the file, its workspace, the outcome, and who decided when someone did. * @returns a schema 2.0 card object. */ export declare function settledFileApprovalCard(input: { /** Where the file sits inside the workspace, derived from its canonical path. */ readonly path: string; /** The workspace's own name. */ readonly workspace: string; readonly outcome: string; readonly decidedBy?: string | undefined; }): object; /** * The card that carries one model question into the chat. * @param input - the question, its options, and each option's click payload. * @returns a schema 2.0 card object. */ export declare function questionCard(input: { readonly question: string; readonly header?: string | undefined; readonly options: readonly { readonly label: string; readonly description?: string | undefined; }[]; readonly valueFor: (index: number) => object; /** Several answers may be chosen; the card then submits a set, not a press. */ readonly multiSelect?: boolean | undefined; /** The callback payload the submit button carries, for a multiple choice. */ readonly submit?: object | undefined; }): object; /** * The card a question is replaced with once answered. * @param input - the question asked, and how it ended. * @returns a schema 2.0 card object. */ export declare function settledQuestionCard(input: { readonly question: string; readonly header?: string | undefined; readonly answer?: string | undefined; readonly cancelled?: boolean | undefined; }): object; /** * The model picker: what this conversation runs on, and what else it could. * * The catalog is advertised, not exhaustive — the host's registry says so * itself — which is why the card never presents itself as the whole set of * choices and always names the typed form that can reach an unlisted route. * @param input - the current route and the routes worth offering. * @returns a schema 2.0 card object. */ export declare function modelCard(input: { readonly current: string; readonly isDefault: boolean; readonly entries: readonly { readonly label: string; readonly detail?: string | undefined; readonly current: boolean; readonly value: object; }[]; readonly hidden: number; readonly reset?: object | undefined; }): object; /** * The status readout: everything that decides what the next message does. * @param input - the resolved facts, and the payload its refresh carries. * @returns a schema 2.0 card object. */ export declare function statusCard(input: { readonly context?: { readonly used: number; readonly window?: number | undefined; } | undefined; readonly usage?: { readonly input: number; readonly output: number; readonly cacheRead: number; readonly cacheWrite: number; } | undefined; readonly workspace: string; readonly workspaceIsDefault: boolean; readonly route: string; readonly routeIsDefault: boolean; readonly sessionId: string; readonly activity: 'running' | 'idle' | 'unbound' | 'switched'; readonly pendingApprovals: number; readonly version: string; /** The permission preset in force, as the deployment defines it. */ readonly preset?: PresetRow | undefined; readonly refresh: object; }): object; /** One row of the session picker, as the bridge resolves it. */ export interface SessionCardRow { readonly id: string; readonly title?: string | undefined; /** The last thing a person said in it. */ readonly lastSaid?: string | undefined; /** Turns taken. */ readonly turns?: number | undefined; /** When it last moved, in unix milliseconds. */ readonly when?: number | undefined; readonly live: boolean; readonly own: boolean; readonly current: boolean; } /** * The session picker: what this conversation may continue, one press each. * * Ids are carried by the buttons and never printed. A session id is a machine * identifier — asking someone to read one off a card and type it back is how * this ends up unusable on the phone it is mostly used from, and it is also * what would force a second authorization path for typed ids. * @param input - the rows, the workspace they belong to, and each row's payload. * @returns a schema 2.0 card object. */ export declare function sessionsCard(input: { readonly rows: readonly SessionCardRow[]; readonly workspace: string; readonly hidden?: number | undefined; /** The keyword the list was narrowed by, so an empty one says which emptiness. */ readonly keyword?: string | undefined; /** The clock the relative labels are read against; injectable for tests. */ readonly now?: number | undefined; /** False when the deployment composes no query service at all. */ readonly canList?: boolean | undefined; readonly valueFor: (sessionId: string) => object; }): object; /** Every toast this channel raises, in the languages it raises them. */ export declare const TOAST: { readonly allowed: { readonly zh: "已允许执行一次"; readonly en: "Allowed once"; }; readonly rejected: { readonly zh: "已拒绝"; readonly en: "Rejected"; }; readonly approvalGone: { readonly zh: "该审批已失效"; readonly en: "This request is no longer open"; }; readonly notApprover: { readonly zh: "你无权批准此操作"; readonly en: "You are not allowed to approve this"; }; readonly answered: { readonly zh: "已作答"; readonly en: "Answered"; }; readonly questionGone: { readonly zh: "该提问已结束"; readonly en: "This question is closed"; }; readonly modelSwitched: { readonly zh: "已切换模型,下一条消息起生效"; readonly en: "Model switched — effective from your next message"; }; readonly modelUnchanged: { readonly zh: "本会话已经在用这个模型"; readonly en: "This conversation already uses that model"; }; readonly modelReset: { readonly zh: "已回到默认模型"; readonly en: "Back to the default model"; }; readonly modelUnreadable: { readonly zh: "这个路由无法解析"; readonly en: "That route could not be read"; }; readonly refreshed: { readonly zh: "已刷新"; readonly en: "Refreshed"; }; readonly notYours: { readonly zh: "你无权修改本会话"; readonly en: "You are not allowed to change this conversation"; }; readonly presetSwitching: { readonly zh: "正在切换…"; readonly en: "Switching…"; }; readonly sessionSwitched: { readonly zh: "已切过去,下一条消息接续"; readonly en: "Switched — your next message continues it"; }; readonly sessionOwn: { readonly zh: "已回到这个聊天自己的会话"; readonly en: "Back on this chat's own session"; }; readonly sessionGone: { readonly zh: "这个会话已经不在可接续列表里了"; readonly en: "That session is no longer on offer here"; }; readonly presetFailed: { readonly zh: "切换失败,看操作台日志"; readonly en: "The switch failed; see the operator console"; }; readonly presetQueued: { readonly zh: "已记下,当前这轮任务结束后生效"; readonly en: "Noted — it applies once the running turn finishes"; }; }; /** * The toast one click raises, in the reader's own language. * @param type - which of the platform's four toast styles to use. * @param copy - what it says. * @returns the `toast` field of a card-action response. */ export declare function toast(type: 'success' | 'info' | 'error' | 'warning', copy: Copy): object; /** * The permission picker: which preset is in force, and what the others mean. * * The unconfined row is a danger row on purpose. Its name reads like "allow * everything", while what it actually does is remove the sandbox AND stop the * asking — so the row says both, in the place where someone is about to press * it rather than in documentation they have not opened. * @param input - the presets, which one is current, and each row's payload. * @returns a schema 2.0 card object. */ export declare function permissionCard(input: { readonly current?: string | undefined; readonly presets: readonly PresetRow[]; readonly valueFor: (preset: string) => object; }): object; /** * One preset as a card row: what the host called it, and what the host says it * does. Both optional, because a deployment may publish neither. */ export interface PresetRow { readonly value: string; readonly name?: string | undefined; readonly description?: string | undefined; /** What the preset does, when the deployment's table could be read. */ readonly sandbox?: string | undefined; readonly approval?: string | undefined; } /** * The card a picker is replaced with once a preset was chosen. * * A picker that stays live after the choice invites pressing what is already * true, so it settles the way an approval does: the decision stated, nothing * clickable, and the way back said out loud rather than assumed. * @param input - the preset chosen, and whether it is in force yet. * @returns a schema 2.0 card object. */ export declare function settledPermissionCard(input: { /** The preset as this deployment defines it, not merely as it is named. */ readonly preset: PresetRow; /** Where the switch is: asked for, waiting on a turn, or in force. */ readonly stage?: 'switching' | 'held' | 'done' | undefined; }): object; //# sourceMappingURL=cards.d.ts.map