CSV Parser
Normalizes uploaded bank CSVs (or pasted rows) into the shared Transaction shape before any model call.
A three-agent system that turns a raw bank CSV into a categorized ledger, a 30/60/90-day cash forecast, and a short list of proposed actions — with two mandatory human approval gates standing between every AI suggestion and anything that touches money or a customer inbox. This page is a working build of that design, run entirely in your browser against sample data.
A hand-rolled TypeScript state machine — not a black box. Every transition out of a gate is triggered by a human action, never a timer or a model.
type RunState = {
status: "idle" | "categorizing"
| "awaiting_categorization_approval"
| "forecasting" | "awaiting_action_approval"
| "executing" | "completed";
rawTransactions: Transaction[];
categorizedTransactions: CategorizedTransaction[];
forecast: CashForecast | null;
proposedActions: ActionProposal[];
auditLog: AuditEntry[];
};Gate 1 is low-stakes — recategorizing a transaction has no external effect, so a simple approve/edit table is enough. Gate 2 guards anything that sends an email or moves money, so it's a stronger per-action gate: approve, edit the draft, or reject — individually, not as a batch. Nothing in EXECUTE_ACTIONS runs while any action is still pending.
Sample data: a Stripe-connected bank feed for Juniper & Co. Consulting. Nothing here calls a real model or sends a real email — the state machine and UI are real.
Load a sample bank feed to kick off the IngestCategorizeAgent. Two approval gates stand between here and anything customer-facing.
Nothing yet — load sample data to start a run.
Structured-JSON output, mandatory confidence scoring, current-date awareness, and a conservative-tone constraint on anything financial — the same requirements a production build enforces.
You are the Ingest & Categorize agent inside Cashflow Copilot, a
tool for small-business owners. Today's date is {{current_date}}.
ROLE
Given a list of raw bank transactions, assign each one a spending/revenue
category and flag anything a careful bookkeeper would double-check.
CONSTRAINTS
- Output ONLY valid JSON matching the CategorizedTransaction[] schema below.
No prose, no markdown fences.
- Never invent a transaction that wasn't in the input. Never drop one.
- Assign a confidence score in [0, 1] for every category you assign. Scores
below 0.8 MUST be reviewed by a human before anything downstream trusts
them — say so is implicit in the score, don't add a disclaimer field.
- Flag duplicates (same amount + same/adjacent date), anomalies (>20% off
the category's trailing average when history is available), and likely
missing invoices (unmatched client names with no corresponding payment).
- This is categorization support, not accounting or tax advice. If asked to
editorialize, decline and stick to the schema.
OUTPUT SCHEMA
{
"id": string,
"category": string,
"confidence": number,
"flag"?: "anomaly" | "duplicate" | "missing_invoice",
"flagNote"?: string
}[]
FEW-SHOT
Input: { "id": "tx-11", "date": "2026-08-09", "description": "Adobe CC — annual", "amount": -599 }
Output: { "id": "tx-11", "category": "Software & Subscriptions", "confidence": 0.93 }Arithmetic, CSV parsing, and email sending are deterministic code — the model proposes, structured tools execute, and only after a human says go.
Normalizes uploaded bank CSVs (or pasted rows) into the shared Transaction shape before any model call.
Deterministic TS functions for running balance, % of revenue, and burn rate — kept out of the model to avoid arithmetic hallucination.
Projects 30/60/90-day balance from categorized inflow/outflow patterns and surfaces the first day balance goes negative.
Flags same-day/same-amount duplicates, missing invoice matches, and category outliers for human review.
Generates a customer-facing reminder draft; sending is a separate, explicitly-approved tool call — never automatic.
Renders the approved run state into a Markdown (and optionally PDF) Financial Health Brief after Gate 2 clears.