Operations
The AIMO agent runs inside your environment and holds credentials to your databases. AIMO delivers work as jobs.
Strict job types, not arbitrary code
The agent is not a general runtime for scripts or user programs. It accepts only a fixed, enumerated set of job types, a closed allowlist. Each type maps to known behavior in the agent (connection tests, schema analysis, profiling, monitor calculation, validation).
- Transparency. You know in advance what categories of work can be requested; there is no hidden "run anything" channel.
- No unconstrained code on the agent. AIMO does not execute arbitrary smuggled logic in your perimeter. Monitor SQL is generated from typed definitions and validated; see Monitors.
Payloads that do not deserialize as a supported job model are rejected. Monitor generation, the detector and the episode check run on AIMO workers, not as mystery payloads on the agent.
The sections below are the agent job types, JobType in code. Orchestration that runs only in AIMO's cloud is listed at the end.
Examples use illustrative table and column names. SQL is compiled for your engine through SQLAlchemy, and every client-database connection runs in autocommit with the engine's read-only session mode where it has one. The supported engines are PostgreSQL, MySQL, Microsoft SQL Server, Snowflake, BigQuery, Databricks, Amazon Redshift, DuckDB and SQLite.
ANALYZE_TABLE: analyze table
Purpose. Inspection of one table: structure, statistics and a bounded sample, packaged for downstream use, including monitor generation on AIMO.
Behavior. Connects with stored credentials, reflects the table, gathers profile and sample data, returns an analysis payload AIMO uses to derive the time binding and author monitors.
When. When a table starts monitoring. The scheduled run dispatches the same job with the sample switched off, reflection only, to compare the schema with the previous block.
Example SQL
Schema and constraints. Implemented with the SQLAlchemy inspector: dialect-specific metadata queries (for example against
information_schemaor catalog tables) to load columns, indexes, primary key, unique constraints and foreign keys. There is no single fixedSELECTfor this step.Profiling sample (when enabled): one bounded read of row data, the same on every engine:
sql
SELECT *
FROM orders
LIMIT 10000;The limit comes from the job's sample_limit (10,000 by default). All columns are selected. Profiles are computed in the agent from the resulting dataframe; the rows are not sent to AIMO.
PROFILE_COLUMNS: profile columns on a recent window
Purpose. Profile a table's columns from a recent slice of data, so the monitors AIMO authors are tuned to the window the detector will later read, not to the table's all-time distribution.
Behavior. Connects with stored credentials and pulls a sample of a recent window, newest time blocks first. AIMO resolves the window from the table's time block and the detector's lookback: it runs from window_start through the block in progress. Runs the same per-column summary as ANALYZE_TABLE over that sample, and the resulting profile replaces the earlier sample_statistics on the cached analysis. When there is no time binding (whole-table mode), it falls back to the time-blind sample path.
When. During the workflow that starts a table, after the time binding is chosen and before monitor definitions are written.
Example SQL
Bounded reads over the window's time blocks, from the newest block back: the newest block first, then the two before it, then the four before those, until the sample is full or the window is read (the exact filter depends on the engine and the time binding):
sql
SELECT *
FROM orders
WHERE created_at >= :block_start AND created_at < :block_end
LIMIT 10000;The limit is the same sample_limit as ANALYZE_TABLE, less the rows already read. The WHERE predicate is the sargable time-block filter production monitoring uses, over the blocks of each read, so the sample holds exactly the rows the monitors are computed over; a busy table gives its sample from its newest block alone. Profiles are computed in the agent from the sampled dataframe; raw rows are not sent to AIMO.
TEST_CONNECTION: test database connection
Purpose. Verify credentials and reachability.
Behavior. Short-lived connection; success or failure with a useful error message.
When. After saving or rotating connections, or from product health checks.
Example SQL
sql
SELECT 1;CALCULATE_MONITORING: calculate monitors
Purpose. Production monitoring: compute monitor values for time blocks, using the table's time binding, optional dimensions and the active monitor definitions.
Behavior. Builds the grouped aggregate query implied by your monitors (the same shape validation uses, without the validation-only row limit on the inner source). Returns one row of values per block for AIMO to store, price and raise episodes on.
Why it matters. This is where strict monitor types become bounded SQL on your database.
Example SQL: outer shape
All active monitors for a table are combined into one statement. The inner source is always a subquery over the monitored table (production: no LIMIT on the inner SELECT).
Inner source:
sql
FROM (
SELECT *
FROM schema.orders
) AS anon_1Optional grouping. The same query builder can group by dimension columns, exposing each as dim_<column_name> (the example below shows that shape); production monitoring currently dispatches without dimensions.
Production time blocks. When the table has a time binding, the agent runs the grouped query once per requested block. The block's boundaries are bound values on the row's time column (a sargable filter, no per-row expression), and the block's start is projected as a constant. For a timestamp column:
sql
WHERE created_at >= :tb_lo AND created_at < :tb_hiA binding on date parts, a Unix epoch column or a string-encoded date renders its own equivalent predicate on the raw columns. On PostgreSQL, SQLite and DuckDB the values travel as driver bind parameters; on the other engines the compiler inlines them as literals when it renders the statement.
No time block and no dimensions. No GROUP BY; one row of aggregates over the whole inner source, for example:
sql
SELECT count(*) AS row_count_3
FROM (SELECT orders.id FROM orders) AS anon_1;With a time block and one dimension (example; the bound values change per scheduled block):
sql
SELECT '2024-01-15 00:00:00+00:00' AS time_block_start,
anon_1.status AS dim_status,
count(CASE WHEN (anon_1.id IS NULL) THEN 1 END) AS count_where_7,
count(*) AS row_count_3
FROM (SELECT orders.id, orders.created_at, orders.status FROM orders) AS anon_1
WHERE anon_1.created_at >= :tb_lo AND anon_1.created_at < :tb_hi
GROUP BY anon_1.status;Monitor column aliases are {family}_{monitor_id} normalized to lowercase with non-alphanumeric characters replaced by _.
Example SQL: monitor aggregate fragments (by family)
Each monitor adds one aggregate column. Possible shapes:
family | Aggregate expression (conceptual; c = inner column, cond = the compiled typed condition) |
|---|---|
row_count | count(*) |
count_where | count(CASE WHEN (cond) THEN 1 END) |
ratio | CAST(count(CASE WHEN (cond) THEN 1 END) AS FLOAT) / nullif(count(*), 0) |
share | CAST(sum(CASE WHEN (cond) THEN expr ELSE 0 END) AS FLOAT) / nullif(sum(expr), 0) |
agg | avg(expr), sum(expr), stddev_samp(expr), percentile_cont(q) WITHIN GROUP (ORDER BY expr), count(expr), … |
spread | max(expr) - min(expr) |
distinct_count | count(DISTINCT expr) (composite keys concatenate cast parts with a separator) |
duplicates | count(key) - count(DISTINCT key) (inverted: count(DISTINCT key)) |
custom | ( <the approved single aggregate SQL> ) |
Count families return 0 on an empty block (never NULL), so "no rows" is always distinguishable from "not computed". Regular-expression conditions compile to the engine's own function: POSIX ~ and !~ on PostgreSQL, DuckDB and Redshift, REGEXP_CONTAINS on BigQuery, REGEXP_LIKE on Databricks and MySQL, REGEXP_INSTR on Snowflake; SQLite and Microsoft SQL Server have no regex, so generation does not author such conditions for them. Every free-form fragment (custom aggregates, raw conditions, expression fields) is structurally validated to a single read-only expression (no subqueries, CTEs, window functions, set operations, DML/DDL, or denial-of-service, file, network or OS-exec functions); the authoritative protection remains the read-only database role the agent connects with.
ANALYZE_CONNECTION_SCHEMA: analyze database schema
Purpose. Schema-level inventory: tables, columns and related metadata for browsing and setup in the UI.
Behavior. Dialect-appropriate discovery; results feed connection and table navigation.
When. New connection, or after large migrations.
Example SQL
Table list. Via the SQLAlchemy inspector (dialect-specific queries to enumerate schemas and tables).
Per table. The same inspector-based metadata as in
ANALYZE_TABLE(columns, indexes, constraints). Profiling is off for this job: there is noSELECT * … LIMIT nover table data, only metadata and empty profile shells.
VALIDATE_TABLE_CHARACTERISTICS: validate table characteristics
Purpose. Safety gate for the proposed time binding and dimension columns before monitor SQL is validated: prove the binding is usable over the whole table and record its range.
Behavior. Reflects the table, checks that the dimension columns exist, fails on an empty table, and runs full-table aggregates over the raw time column the binding names: its MIN and MAX, which the scheduler uses as the table's first and last block. It also checks that the column can be filtered efficiently on your engine: on the row stores a string-encoded time column needs an index, and on the warehouses the column must be in the partition or clustering key. For a binding on a string-encoded date it confirms that the declared format actually parses the sampled values.
When. During the workflow that starts a table, before monitor definitions are written and validated.
Example SQL
Over the full table, on the raw column, no sampling:
sql
SELECT min(orders.created_at) AS min_1,
max(orders.created_at) AS max_1
FROM orders;Dimension columns are validated to exist on the table; this statement does not group by dimensions.
VALIDATE_MONITOR_ARTIFACTS: validate monitor artifacts
Purpose. Safety gate for AIMO-generated monitor configuration: prove the time binding, the dimensions and the compiled SQL against your real table before production use.
Behavior. Builds the same grouped monitor query context as production (time binding and dimensions with GROUP BY). Runs the validation queries over a sample of up to 1,000 rows of the inner source. Free-form fragments (custom aggregates, raw conditions) run in this real context; each leaf of a combined condition is also run on its own, so a short-circuited AND cannot hide a broken half; and a monitor whose result is not a number is rejected. Failures return per-monitor errors. Empty tables block validation where a row is required.
Relationship to AIMO. After monitors are generated in AIMO, this job proves them on your database: assign, constrain, validate.
Example SQL
- Non-empty table check (when execution is required):
sql
SELECT 1
FROM orders
LIMIT 1;- Time-block and dimension shape (no monitor columns), proving the
WHEREandGROUP BYcompile and execute. The inner source is capped at 1,000 rows and the block is a fixed example instant,2020-01-02 12:00:00+00:00UTC, projected as a constant and filtered on the raw column exactly as production does:
sql
SELECT '2020-01-02 12:00:00+00:00' AS time_block_start,
anon_1.status AS dim_status
FROM (
SELECT orders.id, orders.created_at, orders.status
FROM orders
LIMIT 1000
) AS anon_1
WHERE anon_1.created_at >= :tb_lo AND anon_1.created_at < :tb_hi
GROUP BY anon_1.status;- Per-monitor validation. The same grouped shell as
CALCULATE_MONITORING, the inner source(SELECT … FROM table LIMIT 1000) AS anon_1, and one monitor's aggregate column appended. No time filter is applied here on purpose: the example block above would match no real rows and hide data-dependent failures, so each aggregate runs over the sampled rows. The aggregate fragments match the family table in theCALCULATE_MONITORINGsection above.
What runs in the cloud
A separate set of worker jobs runs only on AIMO's infrastructure and never touches your database directly. They orchestrate a table's life and operate on the artifacts the agent already produced (analysis payloads, monitor values per block, schema snapshots):
- Starting a table: the time binding, the column profile request, monitor generation, the first monitor values, the detector's pass over the history, the episode check and the table's health snapshot.
- The scheduled run: the schema compared with the previous block (a changed column is an episode), the new blocks' monitor values, the detector, the episode check, the health snapshot, a roll-up of monitors that keep failing, the briefing, and the alerts sent to your destinations. AIMO decides what the briefing states (which tables were read, the findings that hold, their order and the status); an AI model writes its prose over those facts.
- A re-roll, on request: the detector's pass repeated over a table's whole history.
When one of these needs fresh numbers from your data, it dispatches one of the agent jobs above; the database-facing work always happens on the agent, under your control.
Relation to monitors
- Strict monitor families. The only flexible text path is the
customfamily, still a single aggregate in a fixed outer query. VALIDATE_TABLE_CHARACTERISTICSconfirms the time binding and the dimensions are valid over the full table, and records the time column's range, before monitor SQL is validated.CALCULATE_MONITORINGturns definitions into repeatable, bounded SQL.VALIDATE_MONITOR_ARTIFACTSconfirms monitor definitions execute in the same grouped shape as production before you rely on them.
See Monitors for the monitor catalog and how AIMO assigns and validates definitions.