lucidAGENTS
Packages

@lucid-agents/analytics

Query and export the six-decimal payment records owned by a Lucid payment tracker.

@lucid-agents/analytics adds read-only summaries, transaction views, and CSV/JSON exports over the PaymentTracker created by @lucid-agents/payments.

It is not a facilitator/chain indexer, finance ledger, refund system, trace backend, or proof that a payment settled externally. Its completeness is exactly the completeness of the configured Lucid payment store.

Install and bind

bun add @lucid-agents/analytics @lucid-agents/payments
import { analytics } from '@lucid-agents/analytics';
import { createAgent } from '@lucid-agents/core';
import { payments } from '@lucid-agents/payments';

const runtime = await createAgent(meta)
  .use(payments({ config }))
  .use(analytics())
  .build();

The extension declares payments as a required capability. Build fails with analytics() requires an enabled payments() runtime with storage when no payment tracker exists.

Runtime API

type AnalyticsRuntime = {
  getSummary(windowMs?: number): Promise<AnalyticsSummary>;
  getTransactions(windowMs?: number): Promise<Transaction[]>;
  getData(windowMs?: number): Promise<AnalyticsData>;
  exportCSV(windowMs?: number): Promise<string>;
  exportJSON(windowMs?: number): Promise<string>;
};
const day = 24 * 60 * 60_000;
const summary = await runtime.analytics.getSummary(day);
const transactions = await runtime.analytics.getTransactions(day);

console.log({
  incomingBaseUnits: summary.incomingTotal.toString(),
  outgoingBaseUnits: summary.outgoingTotal.toString(),
  netBaseUnits: summary.netTotal.toString(),
  transactionCount: summary.incomingCount + summary.outgoingCount,
  newest: transactions[0]?.timestampIso,
});

windowMs uses Date.now() - windowMs and includes records newer than the cutoff. Omit it for all retained records. Results are not a database snapshot across several calls; concurrent payments can make a separately fetched summary and transaction list differ. Use getData() when the one function is the intended API, while recognizing the underlying tracker still performs its own reads.

Data model and units

type PaymentRecord = {
  id?: number;
  groupName: string;
  scope: string;
  direction: 'incoming' | 'outgoing';
  amount: bigint;
  timestamp: number;
};

type Transaction = PaymentRecord & {
  amountUsdc: string;
  timestampIso: string;
};

Amounts are stored as six-decimal base units. 1_500_000n is formatted as "1.5". JSON exports serialize bigint totals as base-10 strings; CSV emits the human-readable amountUsdc plus millisecond/ISO timestamps.

The analytics layer labels the formatted field USDC and should not be used to aggregate arbitrary MPP currencies or assets without an explicit normalized currency/asset model in the application.

getOutgoingSummary(), getIncomingSummary(), and getSummary() standalone helpers currently return the same combined incoming/outgoing summary. Their names do not filter the other direction. Use the individual fields instead of assuming a direction-exclusive result.

Transaction ordering and scope

getTransactions() sorts newest first. groupName and scope are the policy accounting dimensions recorded by the payment tracker; they are not guaranteed to contain a run ID, payer, payee, asset, external transaction, or tenant ID.

For multi-tenant reporting, configure trusted agent/tenant namespaces in the owning payment store and authorize access outside this package. Do not infer tenant ownership from a caller-provided group/scope string.

CSV and JSON export

const csv = await runtime.analytics.exportCSV(day);
const json = await runtime.analytics.exportJSON(day);

await uploadToProtectedReportStore({ csv, json });

CSV fields are quoted/escaped when needed. Values beginning with formula characters (=, +, -, @, tab, or carriage return) receive a leading apostrophe defense. That reduces spreadsheet formula injection; it does not make the file non-sensitive or authorize the recipient.

JSON converts every bigint to a decimal string. Consumers must parse money with integer/decimal-safe code rather than Number.

Standalone helpers

The package exports:

export {
  getOutgoingSummary,
  getIncomingSummary,
  getSummary,
  getAllTransactions,
  getAnalyticsData,
  exportToCSV,
  exportToJSON,
} from '@lucid-agents/analytics';

Each accepts a PaymentTracker first and optional windowMs second. Canonical analytics types are re-exported from the shared types package by this package.

Durability and reconciliation

  • In-memory payment storage makes analytics process-local and ephemeral.
  • SQLite can persist one-host payment records; Postgres is the normal shared option for replicas.
  • A staged settlement that has not committed is counted for policy safety but does not appear as a normal completed payment record in analytics.
  • Provider/chain settlements, refunds, chargebacks, or manual corrections made outside the tracker will not appear automatically.
  • Restoring an old database can omit newer external settlements.

Reconcile analytics against facilitator/provider/chain and the application's fulfillment ledger. Do not use netTotal as an accounting balance or revenue recognition number.

Privacy and access control

Group names, scopes, timestamps, amounts, and IDs can reveal customers and commercial activity. Protect exports with server-side authorization, encryption, retention/deletion policy, audit logs, tenant filtering, and download rate limits. Do not expose runtime.analytics directly as a public entrypoint without an explicit schema and access policy.

Failure and testing

Analytics methods propagate payment-store read errors. Treat an unavailable report as an observability failure, not evidence that totals are zero. Test:

  • exact six-decimal formatting, including values above safe JavaScript integer range;
  • time-window boundary and clock behavior;
  • mixed incoming/outgoing net calculations;
  • CSV quoting, newlines, quotes, and formula prefixes;
  • JSON bigint strings;
  • store disconnect, empty data, cross-tenant access, and backup restore;
  • comparison with one known external settlement set.

For lifecycle tracing beyond completed payment records, use the observability contract and payment recovery model.

On this page