#Turn unfamiliar CSV files into contact previews
Your product expects name, email, and organization. Customers bring their
own column headings. This example recognizes a known export with code, asks an
Agent to interpret unfamiliar headings, and uses code to check and convert rows.
The importer calls the same mapping slot in all three configurations.
#Try it
With Jig installed on a supported host, use the contact import source and run from that directory. The default known-format preview uses code and needs no Agent:
The CSV parser is an ordinary declared dependency. The resolution flag permits
fresh dependency resolution during this review; inspect and approve the result.
The reviewed entrypoint supplies the fixture, read attachment and one-minute deadline.
Because this Flow writes deliverables, Jig chooses a fresh packet directory in
operating-system temporary storage and reports it on stderr. Repeating jig run
leaves earlier packets intact. The operating system may clear temporary storage;
use jig run --out ./preview-result to choose a retained location. That directory
must not already exist; Jig never overwrites an earlier result.
Open files/preview.json inside the reported destination. Ada and Mei have accepted contact
records. Grace's invalid email appears in rejected, with CSV record number 3.
result.json in that destination contains the execution result. These are synthetic
contacts, and no database records are created.
To use the Agent or mixed variants, configure a supported native client through
Choose an Agent. Their shared grants/agent.json
is selected by bindings/model.ts. Interactive review can offer a confirmed
client correction when the selected executable is missing; manual grant edits
also require review. Unavailable Agent routes do not block the code preview.
Mixed still requires its Agent route even when a particular input takes its code branch.
Now use different headings and a different column order:
Code returns needs_mapping. The Agent is asked to interpret Organisation,
Courriel, and Nom complet; the intended mapping is
{"name":2,"email":1,"organization":0}. Its answer may differ or it may abstain.
Mixed recognizes the known format in code and invokes the Agent only for
unfamiliar headings. Try binding:mixed with fixtures/known to take
the direct code path. Each invocation receives its own temporary packet unless
you choose a new destination with --out DIR.
#Follow the work
Mapping shows the headings and proposed column indices. Contacts shows accepted
preview records; Rejected rows explains exclusions. Lists show up to twelve records
with explicit totals and marked text excerpts; the complete returned output and
preview.json retain every record. Select the delivered preview reference to read its
content. The mapper and converter also offer domain views when invoked independently.
needs_mapping and rejected rows remain distinct from a completed import: no database
is changed.
Add --display tui for a terminal workspace or --display web for the same
views in a browser. Open the private link printed after execution starts. The completed inspector
stays open until you explicitly close it; leaving a live display does not cancel work.
Blocking causes remain reachable from every view. Plain output and --json remain
available.
#Follow the composition
Compare bindings/code.ts, agent.ts, and mixed.ts. They select different
implementations of mapper while using the same importer and converter. The mixed
implementation combines code and an Agent Flow inside one leaf. Each method is self-contained;
none imports another Flow's implementation.
Review changes to code or Bindings before running them.
The mapping is a proposal of three zero-based column indices. It cannot contain
scripts or expressions. The converter requires distinct existing columns for
all three fields. Its small email rule checks basic shape, not deliverability.
CSV parsing uses csv-parse, including quoted commas and multiline fields.
#Watch an unsuitable proposal stop
This is deliberate fault injection into the converter, not a captured model answer:
The supplied email column is 7, but the file has only three columns. The result
is needs_mapping, with no accepted contacts. fixtures/ambiguous contains two
email columns: the Agent is instructed to abstain, but prompting cannot guarantee
that it will. A structurally valid wrong mapping can still pass the code checks.
Inspect the mapping and preview before using them.
fixtures/malformed has duplicate headings and fails before calling a mapper:
#Know what the result means
donemeans a preview or mapping decision completed.readycan contain some or even all rejected rows.needs_mappingasks for clarification; it is not successful data import.blockedandlimitremain visible without automatic fallback or retries. Execution errors and cancellation propagate. Ctrl-C cancels owned work.- Input is one UTF-8 CSV named
contacts.csv: at most 64 KiB, 100 data records, 16 columns, 80 characters per heading, and 2048 per cell. Empty or duplicate headings are rejected. Record numbers include the header; quoted newlines do not create new record numbers. Blank lines are skipped. - Only headings reach the configured Agent provider; headings themselves may contain sensitive text. The source stays unchanged. Only complete previews are delivered; the example does not save checkpoints.
#Make it your own
Change the destination fields, mapping instructions, and conversion rules for one import task. Keep mapping and row validation separate. Database writes, duplicate handling, and user confirmation belong to a consuming application; this example stops at its reviewable preview.
Run bun test examples/contact-import/test from the repository root after
workspace setup. Tests use deterministic Agent substitutes and include a
structurally valid semantic mistake to make the limit of validation explicit.
The release gate also tests the application against the packed SDK.