Lattice Grid Buy a licence

api reference

The AI narrative and insights layer

The narrative module: summaries, insights and risk text over what the grid already holds. The grid.ai intent layer is documented on the platform API page.

API reference › The AI narrative and insights layer

All 13 pages Everything on one page → Developer guide →

The AI narrative / insights layer

modules/ai is an opt-in layer that produces a short, plain-language narrative of the grid's computed figures - a per-KPI / per-chart / per-column “Explain”, or an insights panel over the current (filtered) view. It is a separate bundle that adds no weight to a page that does not load it, changes nothing in grid core, and pulls in no dependency. The grid makes no AI call of its own: createAI never imports a provider SDK, never reads a key, and never makes a network request. It calls one async callback you supply, ask() - your model, your key, your privacy decision - exactly the philosophy of the data adapters, where auth and transport are always the caller's.

import { createAI } from '@toclocoinc/lattice-grid/modules/ai';

const ai = createAI(grid, {
  ask: async ({ system, messages, tools, schema, signal }) => {
    const r = await myProvider.chat({ system, messages, tools, signal });
    return { text: r.text, toolCalls: r.toolCalls };   // or a bare string
  },
  maxRows: 50,                 // cap what any tool result carries to ask()
  redact: ['ssn', 'salary'],   // columns whose values never leave the browser
});

ai.insights(document.querySelector('#insights'));                 // the panel
const { text, flagged } = await ai.explain({ kind: 'column', colId: 'amount' });

Grounded, and reconciled. Where your provider offers tool-use, the model is given a curated read-only tool set (getSchema, getProfile, getStatistics, getForecast, runQuery) and our engine computes what it asks for; where it does not, the module builds a facts packet from the grid's computed results (grid.statistics, the profile, forecasts, view counts) and passes it in the prompt. Either way, every figure in the narrative is reconciled against the values the engine produced this render - an ungrounded number is stripped before the user sees it. The prompt is constrained to narrate-only; the layer is read-only and never mutates data. redact and maxRows bound what the module hands ask(), and the module sends nothing itself. An ask() error surfaces a friendly message and the grid stays fully usable - AI is additive, never load-bearing.

createAI is complementary to grid.ai: grid.ai is the intent/plan skill layer (a question becomes a validated filter/sort plan you preview and apply); createAI is the narrative/insights consumer that explains figures. They can share one host ask() - pass none to createAI and it adopts the grid's configured ai.ask, running the facts-packet path over it.

enable gates only the three DOM-mounting convenience methods: 'narrative'/'insights' is what lets insights() mount, 'query'/'ask' is what lets askBar() mount (see §Ask-your-data), and 'actor' is what lets actorBar() mount (see §AI as a governed actor). All three are allowed when enable is omitted. The programmatic API - explain(), query(), propose(), facts(), riskSummary() and the rest of the controller - is never gated by enable and runs regardless of the allowlist: a host that wants no AI surface at all simply never calls these methods.

MemberDescription
createAI(grid, config)Create an AI narrative / insights controller over a live grid (headless or rendered). Config: ask (the host callback; falls back to the grid's ai.ask), enable (gates only insights()/askBar()/actorBar() - see above), maxRows, redact, tools, locale, reconcile ('strip'/'flag'), element, onNarrative, onError.
explain(target?, opts?) / narrate(...)Produce a grounded, reconciled narrative. Always callable - not gated by enable. target is { kind: 'view' }, { kind: 'column', colId }, { kind: 'forecast', colId, options }, { kind: 'kpi'|'chart', facts }, or { kind: 'risk', gantt, board } for a board / Gantt risk summary. Resolves to { text, facts, grounded, flagged, rounds, mode }.
riskSummary(sources?, opts?)A board / Gantt RISK SUMMARY - “3 tasks at risk on the critical path, SPI 0.67, 2 SLA breaches” - grounded on the separate Gantt / Kanban modules' outputs (gantt, board/sla, or their precomputed earnedValue/schedule/breaches). A convenience over explain({ kind: 'risk' }), through the same reconciliation guard. Always callable - not gated by enable.
insights(el?, opts?)Mount (or re-target) the insights panel into an element, its generate control wired to a view narrative. Keeps the grid usable on an ask() error. Mounts only when enable allows 'narrative'/'insights'.
attachExplain(target, opts?)Build an “Explain” button bound to a target (a KPI tile, a chart datum, a column). Clicking it narrates the target.
facts(target?, opts?)Build the facts packet for a target without calling ask() - the exact grounded set a narrative would use, and what would leave the browser.
on(name, fn) / off(name, fn) / destroy()Events: narrative and error. destroy() tears the controller down and leaves the grid untouched.

Grounded, with the reconciliation guard, executed

A mock ask() returns one grounded figure (the row count) and one invented one. The number-reconciliation guard strips the ungrounded figure and keeps the grounded one. Run headless on every build.

const { createHeadlessGrid } = await import('../packages/core/src/index.js');
const { createAI } = await import('../packages/modules/ai/index.js');

const grid = createHeadlessGrid({
  rowKey: 'id',
  columns: [{ field: 'id' }, { field: 'region' }, { field: 'amount', type: 'number' }],
  rows: [
    { id: 1, region: 'EMEA', amount: 100 },
    { id: 2, region: 'AMER', amount: 300 },
    { id: 3, region: 'APAC', amount: 200 },
  ],
});

// Your model, your key. The grid makes no network call - it awaits this.
// This mock returns one grounded figure (3 rows) and one invented one (900%).
const ask = async () => ({ text: 'There are 3 rows in view. Confidence 900%.' });

const ai = createAI(grid, { ask, tools: false });
const result = await ai.explain({ kind: 'view' });

const kept = result.text.includes('3 rows');      // grounded - survives
const stripped = !result.text.includes('900%');   // hallucinated - removed

ai.destroy();
grid.destroy();
return `${result.flagged.length} flagged | ${kept} kept | ${stripped} stripped`;

Board / Gantt risk summary (modules/ai)

A project manager wants one line: “3 tasks at risk on the critical path, SPI 0.67, 2 SLA breaches.” ai.riskSummary(…) (and the { kind: 'risk' } target of explain) produces exactly that, grounded on figures the separate optional modules have already computed: gantt.earnedValue() for SPI/CPI and the schedule/cost variances, gantt.schedule for the critical path and the tasks at risk on it, and board.sla for the SLA breach and warning counts. Every figure runs through the same number-reconciliation guard as the rest of the narrative - an ungrounded figure is stripped before the user sees it.

The AI bundle imports neither the Gantt nor the Kanban module. You pass the module instances (or their already-computed outputs) on the target, and the layer reads them duck-typed - so a page that loads the AI module without those bundles carries none of their weight. buildRiskFacts(target, opts) is exported to build (and preview) the exact grounded facts a risk summary would use, without calling ask(). Redaction: a risk summary carries aggregates only by default - counts and the EVM indices/variances; it withholds task names and card contents. includeTaskNames adds the at-risk task names (bounded by maxTasks) and includeCost adds the money figures (BAC/PV/EV/AC), each the host's explicit opt-in, reported back in meta.exposed.

const { buildRiskFacts } = await import('../packages/modules/ai/index.js');

// The public outputs a host already holds from the SEPARATE gantt / kanban
// modules. The AI bundle imports neither - it reads these duck-typed.
const earnedValue = { ok: true, project: { spi: 0.8, cpi: 0.9, sv: -1000, cv: -500 } };
const schedule = {
  ok: true,
  order: ['a', 'b', 'c'],
  critical: ['a', 'b', 'c'],                    // all three on the critical path
  tasks: new Map([
    ['a', { id: 'a', name: 'Design', percentComplete: 100, totalFloat: 0 }],
    ['b', { id: 'b', name: 'Build', percentComplete: 40, totalFloat: 0 }],
    ['c', { id: 'c', name: 'Ship', percentComplete: 0, totalFloat: -2 }],
  ]),
};
const breaches = [{ key: 'CARD-1' }, { key: 'CARD-2' }];   // from board.sla.breaches()

const { facts } = buildRiskFacts({ kind: 'risk', earnedValue, schedule, breaches });
const by = Object.fromEntries(facts.map((f) => [f.id, f]));

// Two incomplete tasks (Build, Ship) are on the critical path - at risk.
return `${by['risk.atRisk'].display} at risk | SPI ${by['risk.spi'].display} | ${by['risk.sla.breaches'].display} breaches`;

Type reference

Generated from the type declarations, so it always matches the release. Each surface lists its properties, its methods and the events it raises as three tables; an option or value type lists its members once.

The AI module

AIFact

A single computed figure a narrative is grounded on.

PropertyTypeDescription
idstringA stable identifier for the figure, so a caller can find it again in the packet.
labelstringWhat the figure is, in words - the column's resolved title where it has one.
valuenumber | nullThe raw numeric value, or null for a context-only fact.
displaystringThe pre-formatted display string the model is told to use verbatim.
kindstringWhat sort of figure it is, which is what the model is told alongside it. `context` marks a fact with no number: it may be mentioned but grounds nothing.
colIdstringThe column the figure is about, where it is about one. (optional)

AITarget

PropertyTypeDescription
kindAIBriefKind 'view' | 'column' | 'forecast' | 'kpi' | 'chart' | 'risk'What to narrate. Defaults to `view` - the current filtered view - with `column` and `forecast` narrating one column, `kpi` and `chart` narrating figures you pass in `facts`, and `risk` assembling a project summary from a Gantt and a board. (optional)
colIdstringWhich column to narrate, for `column` and `forecast`. A redacted column grounds nothing: the packet comes back empty and flagged as redacted rather than quietly narrating without it. (optional)
optionsobjectForecast options, for `kind: 'forecast'`. (optional)
factsArray<{ id?: string; label: string; value: unknown; display?: string; kind?: string; colId?: string }>Caller-supplied figures for a KPI/chart Explain, grounded like the rest. (optional)
ganttunknownFor `kind: 'risk'`: a Gantt instance (from `createGantt`). Read duck-typed for `earnedValue()` (SPI/CPI/variances) and `schedule` (critical path, float). The AI bundle never imports the Gantt module. (optional)
boardunknownFor `kind: 'risk'`: a Kanban board (from `createKanban`). Read for its `board.sla` monitor (breach / warning counts). The AI bundle never imports the Kanban module. (optional)
slaunknownFor `kind: 'risk'`: an SLA monitor, if not reached through `board`. (optional)
earnedValueobjectFor `kind: 'risk'`: a precomputed `gantt.earnedValue()` result. (optional)
scheduleobjectFor `kind: 'risk'`: a precomputed `gantt.schedule` result. (optional)
breachesobject[]For `kind: 'risk'`: precomputed SLA breach states. (optional)
warningsobject[]For `kind: 'risk'`: precomputed SLA warning states. (optional)
evmOptionsobjectFor `kind: 'risk'`: options passed to `gantt.earnedValue()`. (optional)
includeTaskNamesbooleanFor `kind: 'risk'`: expose the at-risk task NAMES (off by default - a risk summary carries aggregates only unless the host opts in). (optional)
includeCostbooleanFor `kind: 'risk'`: expose the money figures BAC/PV/EV/AC (off by default). (optional)
maxTasksnumberFor `kind: 'risk'`: cap on named at-risk tasks (default 10). (optional)

AIFactsPacket

The facts packet a narrative grounds on.

PropertyTypeDescription
targetAITargetThe target the packet was built for, as given.
factsAIFact[]Every figure the narrative may cite, each with the display string the model is told to use verbatim.
groundedValuesnumber[]The numeric values seeding the reconciliation registry.
meta{ kind: string; filtered: boolean; factCount: number; redacted?: boolean; colId?: string; /** For `kind: 'risk'`: which module sources resolved. */ sources?: { schedule: boolean; earnedValue: boolean; sla: boolean }; /** For `kind: 'risk'`: which opt-in exposures were honoured. */ exposed?: { taskNames: boolean; cost: boolean }; }What the packet is and how it was built: the target kind, whether the view was filtered, how many facts there are, and - where they apply - the redaction flag, the column, which module sources resolved for a risk summary, and which opt-in exposures were honoured.

AIRiskFacts

The risk facts a board / Gantt risk summary grounds on, from {@link buildRiskFacts}: the facts plus which module sources resolved and which opt-in exposures (task names, cost) were honoured.

PropertyTypeDescription
factsAIFact[]The risk figures the summary grounds on - schedule and earned-value indices, float, SLA breaches - and, when task names are allowed, the at-risk tasks as context facts carrying no number.
meta{ kind: 'risk'; sources: { schedule: boolean; earnedValue: boolean; sla: boolean }; exposed: { taskNames: boolean; cost: boolean }; }Which of the three sources actually resolved (schedule, earned value, SLA) and which opt-in exposures were honoured. Nothing resolving warns, because there is then nothing to summarise.

AINarrative

The result of a narrative: reconciled prose plus what grounded and what did not.

PropertyTypeDescription
textstringThe narrative, with every ungrounded figure stripped (or flagged).
factsAIFact[]The figures the narrative was grounded on - the packet's facts, for showing beside the prose.
groundedstring[]The figures that reconciled against a computed value.
flaggedstring[]The figures removed as ungrounded.
packetAIFactsPacketThe whole facts packet the narrative was built from, for a host that wants to show its working.
roundsnumberHow many ask() rounds ran (>1 only on the tool-use path).
modeAINarrativeMode 'tools' | 'packet'Which path ran: `tools` when the model was given read-only tools to call, `packet` when it was handed the facts up front.

AIConfig

AI module configuration.

PropertyTypeDescription
askAIAskThe host's model callback. Falls back to the grid's `ai.ask` when omitted. (optional)
enablestring[]Restricts which of the three DOM-mounting convenience methods are allowed to mount: `'narrative'`/`'insights'` for `insights()`, `'query'`/`'ask'` for `askBar()`, `'actor'` for `actorBar()`. All three are allowed when `enable` is omitted. This does NOT gate the programmatic API - `explain()`, `query()`, `propose()`, `facts()`, `riskSummary()` and the rest of the controller always run regardless of `enable` - because a host that wants no AI surface at all simply never calls these methods. (optional)
autoApplybooleanAsk-your-data: apply a safe (read-only) query result without a confirm step. Off by default - the resolved query is shown and waits for Apply. (optional)
routerunknownA Data Router instance; on applying a query the answer rows are fanned to its attached viewers (grid + chart + KPI together) via `load()`. (optional)
schemaOptionsobjectBudgets passed to the schema builder for ask-your-data. (optional)
contextunknownExtra context passed through to `ask()`. (optional)
boardunknownA Kanban board (from `createKanban`) the governed actor writes moves through: an NL card move applies via the board's own `beforeMove` gate, never a kanban-specific write bypass. (optional)
maxRowsnumberCap on rows any tool result carries to `ask()`. (optional)
redactstring | string[] | ((colId: string) => boolean)Columns whose values must never leave the browser. (optional)
toolsbooleanForce tool-use on or off; auto-detected from how `ask` was supplied otherwise. (optional)
localestringLocale for figure formatting. (optional)
maxColumnsnumberColumn cap for a view summary. (optional)
reconcileAIReconcileMode 'strip' | 'flag'What to do with an ungrounded figure: `'strip'` (default) or `'flag'`. (optional)
elementHTMLElementAn element to mount the insights panel into. (optional)
MethodSignatureParametersReturnsDescription
onQuery(result: AIQueryEvent) => voidresult: AIQueryEvent=> voidCalled with each ask-your-data result, alongside the `query` event. (optional)
onProposal(result: AIProposalEvent) => voidresult: AIProposalEvent=> voidCalled with each governed-actor proposal (Play C), before any approval; alongside the `proposal` event. (optional)
onNarrative(result: AINarrativeEvent) => voidresult: AINarrativeEvent=> voidCalled when a narrative is produced, alongside the `narrative` event. (optional)
onError(error: AIErrorEvent) => voiderror: AIErrorEvent=> voidCalled when a run fails - no `ask()` is configured, or `ask()` threw - alongside the `error` event. The grid stays usable. (optional)

AIApplyReport

The report from applying an ask-your-data query.

PropertyTypeDescription
okbooleanWhether the query was applied. False when the read-only gate refused the plan, and when the grid could not apply it.
appliedstring[]The action types that were applied.
failedArray<{ type: string; reason: string }>Actions that threw while applying.
refusedArray<{ type: string; reason: string }>Actions refused by the read-only gate - a mutation is never applied.
fannedOutnumberHow many answer rows were fanned to a router's viewers.

AIQueryResult

The result of an ask-your-data question: a validated, READ-ONLY query spec - never rows - that the host reviews before applying.

PropertyTypeDescription
okbooleanTrue when the spec is safe to apply: at least one read, nothing unsafe.
questionstringThe user's question.
planRecord<string, unknown>The core plan (from `grid.ai.plan`).
actionsobject[]The read-only actions that will run - the validated query spec.
unsafeArray<{ type: string; reason: string }>Actions refused as not read-only (a mutation the model asked for).
rejectedArray<{ at: string; what: string; reason: string }>Parts the core validator dropped (unknown column, bad operator, …).
explainstringThe model's own one-line summary, if any.
spec{ actions: object[] }The validated query spec as data.
appliedAIApplyReport | nullThe apply report once applied, or null.
MethodSignatureParametersReturnsDescription
describe(): string - stringThe resolved query in one human sentence, from the validated spec.
apply(opts?: { router?: unknown; onResult?: (rows: object[]) => void }): AIApplyReportopts?: { router?: unknown; onResult?: (rows: object[]) => void }AIApplyReportApply the query (re-gated), fanning the answer to a router if configured.

AIDiffEntry

One before/after change in a governed-actor proposal.

PropertyTypeDescription
keystringThe target row key.
rowLabelstringA human label identifying the row (a name-like column, else the key).
colIdstringThe target column id.
colTitlestringThe column's title, for the diff header.
oldValueunknownThe current stored value.
oldDisplaystringThe current value as shown (a lookup id mapped to its label).
newValueunknownThe proposed stored value (a label resolved to its option id).
newDisplaystringThe proposed value as shown.

AIProposal

PropertyTypeDescription
okbooleanTrue when there is at least one applicable change and nothing needs a pick first.
instructionstringThe user's instruction.
scopeAIProposalScope 'view' | 'all'`'view'` (the filtered set, the default) or `'all'` (an opted-in widen).
scopeCountnumberHow many rows the scope covers.
scopeTextstringThe scope in words, always stated in the confirm/diff.
bulkbooleanWhether any proposal was a bulk (`scope:'view'`) edit.
diffAIDiffEntry[]The before/after diff - exactly what would change. Nothing is written yet.
rejectedArray<{ reason: string; [k: string]: unknown }>Proposals refused before apply (unknown column, unknown label, bad type/range, no match).
ambiguousArray<{ reason: string; candidates: Array<{ key: string; label: string }>; [k: string]: unknown }>Matches needing a human pick (>1 row for one phrase), with candidates.
outOfViewArray<{ reason: string; candidates: Array<{ key: string; label: string }>; [k: string]: unknown }>Named targets found only outside the view, offered for an opt-in widen.
noopsArray<{ reason: string; [k: string]: unknown }>Matches whose value already equals the ask (nothing to change).
appliedAIProposalReport | nullThe apply report once applied, or null.
MethodSignatureParametersReturnsDescription
describe(): string - stringThe proposal in one human sentence, always stating the scope.
apply(opts?: { board?: unknown }): Promise<AIProposalReport>opts?: { board?: unknown }Promise<AIProposalReport>Apply the approved diff through the gate (`beforeEdit`, or `beforeMove` for a board).

AIProposalReport

The report from applying a governed-actor proposal.

PropertyTypeDescription
okbooleanTrue when at least one edit landed.
appliednumberHow many edits landed through the gate.
requestednumberHow many edits were attempted.
vetoednumberHow many were stopped by a before-handler veto.
viastringWhich gated path applied them: `'setCells'`, `'board.move'`, or `'none'`.

AINarrativeEvent

`narrative`: a narrative run finished and its result is in hand. The result {@link AI.explain} resolves to, copied with `type` added by the module's emitter - the same figures, already reconciled, so a handler that only wants to paint the text need not await the call itself.

PropertyTypeDescription
typestringWhich event this is: `narrative`.

AIQueryEvent

`query`: an ask-your-data question resolved into a validated, read-only query spec. The result {@link AI.query} resolves to, copied with `type` added. It fires whether or not the spec is safe (`ok`) and whether or not `autoApply` applied it - the apply happens before the event, so `applied` is already filled in when it did.

PropertyTypeDescription
typestringWhich event this is: `query`.

AIProposalEvent

`proposal`: the governed actor produced a reviewable set of edits. The result {@link AI.propose} resolves to, copied with `type` added. Nothing has been written: this is the point at which a host shows the diff and asks a human.

PropertyTypeDescription
typestringWhich event this is: `proposal`.

AIErrorEvent

`error`: a run failed, and the call that started it is rejecting. Raised for a missing `ask()` and for an `ask()` that threw, on all three runs. The grid is untouched either way. Exactly one of `target`, `question` and `instruction` is present - whichever run failed.

PropertyTypeDescription
typestringWhich event this is: `error`.
errorErrorWhat went wrong: the error `ask()` threw, or the one the module raised for a missing `ask()`.
targetAITargetThe narrative target, when {@link AI.explain} or {@link AI.riskSummary} failed. (optional)
questionstringThe question, when {@link AI.query} failed. (optional)
instructionstringThe instruction, when {@link AI.propose} failed. (optional)

AIEventPayloads

What a handler receives, per AI event.

PropertyTypeDescription
narrativeAINarrativeEventThe reconciled narrative.
queryAIQueryEventThe validated query result.
proposalAIProposalEventThe proposed edits, with their before/after diff.
errorAIErrorEventWhat failed, and which run it was.

AI

An AI controller over a live grid. It explains the grid's computed figures (Play A), answers questions with validated read-only query specs (Play B), and PROPOSES governed edits a human approves and the grid's own gate applies (Play C). `grid.ai` (in core) is the complementary intent/plan skill layer this consumes.

Properties
PropertyTypeDescription
elHTMLElement | nullThe mounted insights panel element, or null. (read-only)
readybooleanWhether a usable `ask()` is configured. (read-only)
Methods
MethodSignatureParametersReturnsDescription
explain(target?: AITarget, opts?: object): Promise<AINarrative>target?: AITarget
opts?: object
Promise<AINarrative>Produce a grounded, reconciled narrative for a target.
narrate(target?: AITarget, opts?: object): Promise<AINarrative>target?: AITarget
opts?: object
Promise<AINarrative>An alias for {@link AI.explain}.
riskSummary(sources?: { gantt?: unknown; board?: unknown; sla?: unknown; earnedValue?: object; schedule?: object; breaches?: object[]; warnings?: object[]; includeTaskNames?: boolean; includeCost?: boolean; maxTasks?: number; evmOptions?: object; }, opts?: object): Promise<AINarrative>sources?: { gantt?: unknown; board?: unknown; sla?: unknown; earnedValue?: object; schedule?: object; breaches?: object[]; warnings?: object[]; includeTaskNames?: boolean; includeCost?: boolean; maxTasks?: number; evmOptions?: object; }
opts?: object
Promise<AINarrative>Produce a grounded, reconciled board / Gantt RISK SUMMARY: a plain-language reading like "3 tasks at risk on the critical path, SPI 0.67, 2 SLA breaches". A convenience over `explain({ kind: 'risk', ... })`; the module sources go in `sources` (`gantt`, `board`/`sla`, or precomputed outputs). Every figure runs through the same reconciliation guard as {@link AI.explain}.
insights(el?: HTMLElement, opts?: object): AIel?: HTMLElement
opts?: object
AIMount (or re-target) the insights panel into an element.
attachExplain(target: AITarget, opts?: object): HTMLElement | nulltarget: AITarget
opts?: object
HTMLElement | nullBuild an "Explain" button bound to a target.
facts(target?: AITarget, opts?: object): AIFactsPackettarget?: AITarget
opts?: object
AIFactsPacketBuild the facts packet for a target without calling `ask()`.
query(question: string, opts?: { autoApply?: boolean; router?: unknown; schemaOptions?: object; context?: unknown; tools?: boolean; signal?: AbortSignal; onResult?: (rows: object[]) => void; }): Promise<AIQueryResult>question: string
opts?: { autoApply?: boolean; router?: unknown; schemaOptions?: object; context?: unknown; tools?: boolean; signal?: AbortSignal; onResult?: (rows: object[]) => void; }
Promise<AIQueryResult>Ask-your-data: turn a question into a validated, read-only query spec, run it in the engine, and (on apply) fan the answer to router-attached viewers. Returns a result the host reviews; `autoApply` applies a safe read for you.
applyQuery(result: AIQueryResult, opts?: { router?: unknown; onResult?: (rows: object[]) => void }): AIApplyReportresult: AIQueryResult
opts?: { router?: unknown; onResult?: (rows: object[]) => void }
AIApplyReportApply a reviewed query result (the confirm path); re-gated at the seam.
askBar(el?: HTMLElement, opts?: object): AIel?: HTMLElement
opts?: object
AIMount the ask-your-data bar (input, Ask, auto-apply toggle, preview, Apply/Discard).
propose(instruction: string, opts?: { widen?: boolean; board?: unknown; schemaOptions?: object; maxRows?: number; context?: unknown; redact?: string | string[] | ((colId: string) => boolean); signal?: AbortSignal; }): Promise<AIProposal>instruction: string
opts?: { widen?: boolean; board?: unknown; schemaOptions?: object; maxRows?: number; context?: unknown; redact?: string | string[] | ((colId: string) => boolean); signal?: AbortSignal; }
Promise<AIProposal>Governed actor (Play C): ask the model for structured edit PROPOSALS over the current view, validate and resolve them (label -> stored value, locate a named row, reject unknown columns/labels/out-of-range), and return a reviewable {@link AIProposal} with a before/after diff. NOTHING is written - the model proposes; a human approves.
applyProposal(result: AIProposal, opts?: { board?: unknown }): Promise<AIProposalReport>result: AIProposal
opts?: { board?: unknown }
Promise<AIProposalReport>Apply an approved proposal - the human-approval step. Writes ONLY through the gate: a grid cell edit via `grid.edit.setCells({ origin: 'ai' })` (the `beforeEdit` veto), a kanban move via `board.move({ origin: 'ai' })` (the `beforeMove` veto). A vetoing host handler stops the write.
actorBar(el?: HTMLElement, opts?: object): AIel?: HTMLElement
opts?: object
AIMount the governed-actor bar: an instruction input, Propose, a before/after diff preview stating the scope, and Approve/Discard. Approve applies through the gate.
on(name: AIEventName, fn: (payload: AIEventPayloads[AIEventName]) => void): () => voidname: AIEventName
fn: (payload: AIEventPayloads[AIEventName]) => void
() => voidRegister an event handler; returns a function that removes it. Any other name is warned about once. What each event carries is {@link AIEventPayloads}; the handler is declared with the widest of them, so narrow on the name inside it.
off(name: AIEventName, fn: (payload: AIEventPayloads[AIEventName]) => void): voidname: AIEventName
fn: (payload: AIEventPayloads[AIEventName]) => void
voidRemove a handler registered with `on`.
destroy(): void - voidTear the controller down: empty anything it mounted and remove only the classes it added. The grid is left exactly as it was.
Events
EventWhenPayloadCancellable
narrativeA narrative run finished; the payload is the reconciled result.AINarrativeEventno
queryA question resolved into a validated read-only query spec, already applied when `autoApply` was on.AIQueryEventno
proposalThe governed actor produced a reviewable proposal; nothing has been written.AIProposalEventno
errorA run failed - no `ask()` is configured, or `ask()` threw - and the call is rejecting.AIErrorEventno