Skip to content

For agents

Use execution records you already have to get an evidence-backed cost answer.

nemulai explain runs the same accounting as nemulai.com/explain on files on disk and prints structured JSON with evidence ids. Local, offline, no account. Built so a coding agent with authorized access to a team's records can answer four questions without a browser.

When it is useful

Not useful for: finding causes (it does not infer them), deciding whether an agent is stuck (a task with many retries is reported as a task with many retries), or arbitrary trace formats (see inputs).

Supported inputs

Two CSV files, or a workspace file exported from the results page. Column names that differ are handled with a mapping profile; fields that are not in the export cannot be reconstructed.

runs.csv

  • run_id *
  • started_at *
  • customer_id
  • task_id
  • parent_run_id
  • relationship
  • workflow
  • workflow_version
  • outcome

calls.csv

  • run_id *
  • model
  • input_tokens
  • output_tokens
  • cached_input_tokens
  • cost_usd
  • call_id
  • occurred_at
  • provider
  • status

plus either cost_usd, or model + input_tokens + output_tokens

Column reference, descriptions and templates: nemulai.com/explain#format. Without relationship attempts per task cannot be measured; without outcome nothing is a success; without cost_usd money is estimated from tokens at dated list prices and labelled as such.

Install

The published npm package nemulai (0.3.2) predates this command. Until 0.3.4 or later is released, run the CLI from a checkout of the repository:

git clone <repository> nemulai && cd nemulai
npm ci && node packages/cli/build.mjs
node packages/cli/dist/nemulai.js explain      # prints the options

Requires Node 20+. No other prerequisite. When the release is published, npm i -g nemulai replaces the clone; check npm view nemulai version.

Complete example

# from a checkout of the repository (npm release pending; see "Install")
node packages/cli/dist/nemulai.js explain \
  --runs exports/runs.csv --calls exports/calls.csv \
  --profile exports/nemulai-profile.json \
  --customer acct-northgate --period 2026-08 --prior 2026-07 \
  --json --out /tmp/explain.json

Structured response (abridged; numbers from the synthetic Ledgerline fixture in tests/fixtures/explain):

{
  "format": "nemulai-explain-result",
  "version": 1,
  "scope": { "customer_id": "acct-northgate", "period": "2026-08", "prior": "2026-07" },
  "current": { "tasks": 690, "attempts": 805, "calls": 1978, "unpricedCalls": 368,
               "totalMicros": 49688466, "total_usd": 49.688466,
               "measuredMicros": 11559405, "estimatedMicros": 38129061,
               "outcomes": { "success": 511, "failure": 94, "abandoned": 74, "unknown": 11 },
               "costPerSuccessMicros": 97238, "successRateKnownBp": 7526 },
  "change": { "comparable": true, "totalDeltaMicros": 15634148, "total_delta_usd": 15.634148,
              "volume": { "label": "Volume: more or fewer tasks", "micros": 11133100, "supported": true },
              "unit":   { "label": "Cost per task", "micros": 4501048, "supported": true },
              "unitSplit": [
                { "label": "More or fewer attempts per task (retries)", "micros": 5113000, "supported": true },
                { "label": "More or fewer calls per attempt", "micros": 10165900, "supported": true },
                { "label": "Cost per call (tokens per call and price per token)", "micros": -10777800, "supported": true } ] },
  "contributors": {
    "by_version": [ { "key": "v4", "tasks": 368, "costMicros": 28351000 }, { "key": "v3", "tasks": 322, "costMicros": 21337400 } ],
    "most_expensive_tasks": [ { "task_key": "root:run-01591", "root_run_id": "run-01591", "run_ids": ["run-01591", "run-01592"],
                                "attempts": 2, "retries": 1, "outcome": "success", "total_usd": 0.1406, "unpriced_calls": 1 } ] },
  "investigate": { "most_retried_tasks": [ "…" ], "note": "Lists, not verdicts. Every entry carries its task key and run ids so the underlying records can be inspected." },
  "missing_evidence": [ "368 of 1978 calls in 2026-08 are unpriced (368 × no list price for o4-policy-preview); the total is a lower bound." ],
  "limitations": [ "Descriptive accounting of supplied records. It does not identify causes; a task with many retries is a task with many retries." ]
}

Agent usage guide

  1. Run with --json --out. If exit code is 1, print the message: it names the missing column, the detected header, or the unknown period/customer.
  2. Read missing_evidence first. Quote it; do not fill the gap.
  3. Answer "what changed" from change; each effect has supported and a note.
  4. Answer "where to look" from contributors.most_expensive_tasks and investigate.most_retried_tasks; cite task_key and run_ids, which exist in the runs file.
  5. Never turn a cost pattern into a cause. The result is descriptive; say so.
  6. Save the mapping with --save-profile so the next export with the same header needs no flags. The profile is checked against each new header before use.

Full contract: docs/wiki/explain-cli.md. Claude Code walkthrough: docs/wiki/agent-integration-claude-code.md. To test a workflow change on comparable tasks, see nemulai eval-compare (docs/wiki/eval-compare.md).

Schemas

Versions are literal fields in every file; a future incompatible change bumps the number and the CLI refuses versions it does not know.

Data handling

The CLI reads the files you name and writes to stdout and the files you name. The analysis code has no network calls; it is the same code the browser page runs client-side. Record cells are data, never executed or interpreted as instructions. The browser page at /explain keeps records in the tab unless you tick "remember in this browser". There is no hosted analysis API and no MCP endpoint yet; if one ships it will be opt-in and documented here.

Getting a small sample from your own system

Export one recent full calendar month and the month before, for one customer or workflow: a runs file with an id, a start timestamp and whatever you have of customer id, parent run id, retry/subagent relationship, workflow, version and outcome label; a calls file with the run id and either recorded cost or model plus token counts. Ids and labels only; no prompts, documents or personal names. A few thousand rows is plenty. Run nemulai explain; if it fails on column names it prints the detected header, and a profile fixes it.

Publishing this page does not make agents find it. A developer installs the CLI and mentions it to their agent; the walkthrough above shows how.