Skip to main content
A sales agent that builds shopping carts during WhatsApp conversations, persists them in the KV Store (so they survive sessions, restarts and days of silence), and runs an hourly recovery job that finds abandoned carts and follows up — resuming the conversation with full Memory context. This is the canonical KV + Memory split: the cart is business state (KV), the conversation is context (Memory). Neither could do the other’s job.

Project Structure

How It Works

Step 1: Cart Tools Backed by the KV Store

The cart lives in the carts namespace, keyed by the customer’s phone. Tools are built with a factory so the key never depends on the LLM getting a phone number right:
tools/cart-tools.ts

Step 2: The Agent

agent.ts

Step 3: The Entry Point

main.ts

Step 4: The Recovery Sweep

A CRON trigger (hourly) scans the carts namespace. listEntries() returns updatedAt for every entry, so finding idle carts requires no extra bookkeeping — the KV Store already tracks the last write:
recovery.ts
Don’t call identify() inside the loop — it sets a global singleton. Pass entityType/entityValue directly in agent.process(), exactly like the SDR follow-up example.

Key Concepts

Why the cart lives in KV, not Memory. Memory is conversation context: it gets trimmed by maxTurns and compacted by summarization, and it’s scoped to the dialogue. A cart parked inside message history could be summarized away, and reading it back would mean parsing prose. In the KV Store the cart is structured data with its own lifecycle — readable by the recovery job, the checkout tool, or a dashboard, independent of any conversation. Why the follow-up uses Memory. The nudge is only natural because the agent resumes with full conversation history — the customer’s name, the sizes they asked about, the objection they raised. State says what is in the cart; Memory says how the conversation got there. TTL as the cleanup policy. Carts refresh their 24h TTL on every write and silently expire after a day of inactivity. The reminder markers expire on the same window. Nobody writes a cleanup job; expiration is the data model. Idempotent reminders. The cart-reminders namespace is a dedup ledger: has() before sending, set() after. If the CRON fires twice or overlaps, customers still get at most one nudge.

Next Steps

KV Store

TTL, namespaces and pattern search reference

Memory

How memory and identify() work

Schedule

CRON triggers and scheduled callbacks

SDR Follow-up

The same pattern applied to lead qualification