How it works

The model proposes.
SQAI disposes.

An agent never writes SQL and never emits code to run. It writes a typed request — and every request travels the same line: validated against a pinned contract, checked against your policy, executed on a deterministic engine, returned with the hashes that replay it.

Every request — ask(), compute(), or an AI SDK tool call — runs the same line. Nothing touches the engine until every check has passed.

01Typed intent

A request the engine can parse. Not a string it must trust.

The model emits a typed object — never SQL, never code. A query intent is a QuerySpec: a metric to aggregate over a named source. A computation intent is a ComputationSpec: a named function from the contract, with arguments and bindings. One discriminated union, two shapes — and nowhere in either for an executable string to hide.

That is the entire authoring surface. What the shape cannot express, the model cannot ask for.

QuerySpecanatomy
{
"kind": "query",the union tag — query or computation
"version": "1",the spec version, pinned
"source_name": "sales",a connected source, by exact name
"metric": "revenue",the column to aggregate
"aggregation": "sum",one of sum · avg · count · min · max
"group_by": "region"optional — split the result by a field
}

also optionalfilter · limit · order

the computation shapemodule · function · args · kwargs · bindings · seed?

02Contract check

One contract, hash-pinned. The unknown is refused with directions.

The spec is validated against the capability contract — one generated, hash-pinned file listing every operation, its exact signature, and its determinism flags. A capability is reachable only if it is read-only and deterministic, or deterministic when seeded. Write-capable and non-deterministic operations are not blocked at runtime; they were never generated into the surface at all.

An unknown function fails as unsupported_operation and answers with nearest_matches from the same index — the model corrects itself instead of looping.

Capability contract

contract_hashsha256:79f1c5a6c7164e7e9e1750e70a5c03292fa87eb52d8148a740c06695924be9a1

4,778
operations listed in the contract
4,574
exposed to the SDKs, read-only
4,564
fully deterministic
10
seed-required simulations
204
excluded from the surface

eligibilityread_only && (deterministic || deterministic_when_seeded)

on an unknown nameunsupported_operation + nearest_matches

03Policy check

Your allow-list decides whether — and how — it runs.

Policy is fixed when createSQAI() constructs the instance and enforced in-process before anything executes — on the source, on every metric, group, filter, and bound column, and on the function name. It can only narrow the contract: naming a capability outside the eligible surface still throws unsupported_operation.

The model's tool input carries no allowed* field. Nothing in the request can widen access — so there is nothing for a prompt injection to widen.

allowedSourcessources the model may name

  • "sales"

allowedFieldscolumns it may read, per source

  • sales.region
  • sales.revenue
  • sales.order_date

allowedFunctionsthe default — every read-only capability, nothing more

  • "all-readonly"

a denial is precise, attributed, final

  • policy_denied_source
  • policy_denied_field
  • policy_denied_function

source: "sqai" · non-retryable

04Deterministic engine

Pinned before it runs: float64, one thread, one runtime.

First, bindings resolve. The model named a source and a field; SQAI pulls the actual values through the engine's row-aligned extractColumns primitive, nulls handled pairwise. SQAI never zips arrays itself — so the input that gets hashed is exactly the input that executed.

Only now does the request touch the engine. The runtime is signed, versioned, and pinned: float64 precision, a single thread. The first compute() ever provisions it once, in about 110 seconds; after that it stays resident — finance.npv measures 0.83–0.93 ms warm.

determinism envelope
runtime_bundle_version0.1.0
runtime_bundle_sha2564d64142e4c1ff63d299cfc8b172fdf97cb59169b545e1e02978e678a632ce6e1
platformdarwin
architecturearm64
precision_modefloat64
thread_count1
seed
input_hash2ea5ede72acd2912fe9e1230cef34afad4c9436bfa8a709bac2da02b478e3212

Recorded with every result — the envelope states the scope of the guarantee instead of overclaiming it.

05Answer + provenance

Two hashes. One before, one after.

invocation_hash is stamped before execution — over the module, function, arguments, resolved bindings, seed, contract, and scope. computation_hash is stamped after: the invocation hash folded with the canonical result. Replay means run it again and compare bytes.

Both SDKs share one canonical serializer, so the same computation yields byte-identical hashes in TypeScript and Python. Query answers carry the same discipline: a plan_hash over the canonical resolved plan, plus the decision path and the exact scope it ran in.

3188.1687606249325finance.npv · bound to a live column · 0.83 ms warm

stamped before execution

invocation_hash

8223a694250fc751fcf8a7777b8e1f524c44ff1f0e7e83451467f554a6123950

module · function · args · resolved bindings · seed · contract · scope

stamped after execution

computation_hash

ff5280ea128113f528dd1e9a28b9bc4a81469075ed7c981c7176fb172d98085d

the invocation hash, folded with the canonical result

Byte-identical in TypeScript and Python — a result computed in one language replays and verifies in the other.

both computed againstcontract_hash sha256:79f1c5a6…

on the query planeplan_hash f87610d8afeb…decision_path "exact_spec"deterministic_scope "local_registered_source"

06The discovery loop

Never guess. Discover, dry-run, then spend the execution.

Field names and function signatures are discovered, not hallucinated. The AI SDK exposes exactly three tools — and only one of them executes.

  1. 01listSources()

    What can I touch?

    Connected sources, with exact field names, types, and the allowed operations per field. Never executes.

  2. 02listSources({ capabilitySearch: "npv" })

    What can I compute?

    The same tool, searching the deterministic catalog — matching modules, exact signatures, and whether a seed is required.

  3. 03explainQuery({ … })

    Will it run?

    A dry run. Returns the resolved plan, confidence, and a preview invocation_hash — a preview, never an executed hash.

  4. 04queryData({ … })

    Run it.

    The one tool that executes. Returns the result plus its full provenance; oversized results stay retrievable by result_id.

The tools never throw — every outcome is typedok · needs_clarification · rejected · error

The mechanics, field by field.

Read the docs