Skip to content

The agent loop

The agent loop in Arivie is the Mastra Agent primitive — not a custom orchestrator. Arivie attaches analytics and workspace tools directly to this single agent; there is no supervisor or sub-agent layer.

One agent handles the full pipeline: SQL execution, workspace file operations, skill loading, and report generation. Rows stay in one scratchpad from SQL through to file write, eliminating the prose-handoff boundary that lets weak models fabricate numbers. See The single agent for the rationale.

The agent gets some combination of these tools depending on configuration:

ToolWhen activePurpose
execute_<source>Always (one per source)Runs validated read-only SQL against a source. Tool name is derived from the source key (e.g., execute_postgres, execute_mixpanel).
compile_metriccompileMetric: trueLets the agent invoke canonical metric definitions instead of ad-hoc SQL.
mastra_workspace_*When workspace is configuredRead/write/grep/edit/list/mkdir/delete/stat files in the sandbox.
workspace_bashworkspace.bash: trueOpt-in shell access for jq, awk, python3, etc. inside the sandbox.
finalize_reportWhen workspace + sandboxed FSSignals report completion with SQL, CSV results, and narrative.
search_skills / load_skill / skill_readWhen skillsMode: "on-demand"Lazy-load SOP skills from the skills directory.

execute_<source> rejects any SQL that does not begin with SELECT or WITH (case-insensitive after trim). This is defence-in-depth on top of the read-only database role. The guard also rejects multi-statement queries, CTE-DML hybrids, and DML/DDL operations.

The agent must self-correct when results are suspicious: zero-row results, all-null columns, single-group GROUP BY, and implausible numeric results. Instead of returning questionable data, the agent re-investigates.

These rules are embedded in the agent system prompt configured by @arivie/agent.

When the user’s question is ambiguous, the agent picks a sensible default, runs, and states the assumption in the final answer. Assumptions are structured so UIs can render them as a list.

Inside one instance, memory uses @mastra/pg with two scopes: instance-global (resource: "__instance__") and personal (per-user resource). See Conversation continuity for the full memory model.

Lifecycle hooks onMemorySave and onMemoryDelete make memory auditable. You can also pass a Mastra observability instance through ArivieConfig.observability to forward OTEL traces to your own collector.