Deskops AI reads incoming customer messages, grounds itself in the shop's real catalog, stock and ledger, and takes action β quoting, invoicing, adjusting inventory and writing the books β while every money decision still stops for the owner's approval.
IDEALIZE 2026 β Open Category Prototype
- The Problem
- What It Does
- π€ The AI Agent β the part that matters
- Guardrails & Responsible AI
- Tech Stack
- System Architecture
- Data Model
- Getting Started
- Project Structure
- Feature Tour
- Roadmap
- Deviations from the Proposal
- Licensing & Third-Party Credits
- Team
A small shop owner's day is not spent running the shop. It's spent on WhatsApp.
"What's the price of this?" Β· "Do you have 20 in stock?" Β· "Can you send me a bill?"
Every one of those messages requires the owner to stop, check a physical shelf or a spreadsheet, do arithmetic in their head, type a reply, and β later, if they remember β write the sale into a book. Multiply by fifty messages a day.
Generic AI chatbots make this worse, not better. A chatbot that confidently quotes a price it invented, or promises stock the shop doesn't have, costs the owner a customer and their reputation.
Deskops AI is built on the opposite premise: the AI is never allowed to know a number. It has to go and look it up.
| π₯ Reads | Ingests WhatsApp messages via WAHA webhook, per-business, signature-verified |
| π Grounds | Retrieves the shop's real catalog, stock levels, customer history and ledger before responding |
| π§ Reasons | Plans a multi-step tool sequence β check stock β look up customer β price the order β draft the invoice |
| β Stops | Anything that becomes money queues in Approvals; the owner approves before it's real |
| π Records | Stock movements, invoices, ledger entries and audit logs all written automatically |
This is not a chatbot, and not a single-prompt wrapper. It is a tool-calling agent that plans across multiple steps, queries live business data, and writes to the database β with a human approval gate on financial actions.
Deskops AI runs a single reasoning loop (the Orchestrator) that is handed different scoped tool groups depending on context. Specialists β Inventory, Sales, Customer, Books β are capability boundaries, not separate agent processes. This was a deliberate choice: it gives one coherent plan per customer message instead of several agents negotiating with each other, and it keeps token cost and latency inside what a small shop can afford.
flowchart TD
A["π± Customer message<br/>(WhatsApp)"] --> B["WAHA webhook<br/>HMAC verified"]
B --> C["Job queue<br/>(idempotent, deduped)"]
C --> D["π§ Orchestrator<br/>runOrchestrator()"]
D --> E["RAG grounding<br/>retrieveContext()"]
E --> D
D --> T{"Tool loop<br/>max 6 steps"}
T --> T1["π¦ Inventory<br/>checkStock"]
T --> T2["π€ Customer<br/>lookupCustomer<br/>getCustomerContext<br/>saveCustomerDetails"]
T --> T3["π° Sales<br/>draftAndQueueInvoice<br/>reviseInvoice Β· cancelInvoice<br/>sendProductImage Β· escalateToOwner"]
T --> T4["π Books<br/>getBooksSnapshot"]
T1 & T2 & T3 & T4 --> DB[("ποΈ Supabase<br/>Postgres + RLS")]
DB --> T
T --> R["βοΈ Reply drafted"]
T --> AP["βΈοΈ Approvals queue<br/>(money actions)"]
AP --> O["π Owner approves"]
R --> W["π€ Sent to customer"]
O --> W
style D fill:#4f46e5,color:#fff
style AP fill:#f59e0b,color:#000
style DB fill:#3FCF8E,color:#000
Every tool is a real, typed, Zod-validated function that hits Postgres. None of them are stubs.
| Tool | Group | What it does |
|---|---|---|
checkStock |
Inventory | Real stock levels and prices from the catalog |
lookupCustomer |
Customer | Find a customer by WhatsApp number + order history |
getCustomerContext |
Customer | Pull conversation and relationship context |
saveCustomerDetails |
Customer | Persist name, address, phone as they're learned |
draftAndQueueInvoice |
Sales | Build an invoice from catalog prices β queue for approval |
reviseInvoice |
Sales | Replace the full item list when the customer changes their order |
cancelInvoice |
Sales | Void an unpaid invoice on cancellation |
sendProductImage |
Sales | Send catalog imagery into the chat |
escalateToOwner |
Sales | Hand off to a human when the agent shouldn't decide |
getBooksSnapshot |
Books | Income/expense totals computed from the real ledger |
Sales tools are only mounted when the run is bound to a real conversation β the agent physically cannot draft an invoice outside a customer chat.
| π₯ INPUT |
|
| βοΈ STEPS |
|
| π€ OUTPUT |
A grounded reply to the customer with true stock and true pricing β plus an invoice sitting in the owner's Approvals queue. On approval: |
| Requirement | How Deskops AI satisfies it |
|---|---|
| Tool use | 10 typed tools with Zod schemas, all hitting live Postgres |
| Multi-step workflow | stopWhen: stepCountIs(6) β the loop chains tool calls and re-reasons on each result |
| Decision-making | Chooses which specialists to engage from the message alone; decides when to escalate vs. act |
| Memory | Conversation history, persisted customer profiles, and RAG retrieval over business documents |
| Planning | Sequences dependent calls β stock must resolve before an invoice can be priced |
| Real consequences | Writes invoices, moves stock, books ledger entries β not a simulated demo path |
Implemented in src/lib/ai/guardrails.ts and enforced at the tool layer:
| Guardrail | Implementation |
|---|---|
| π’ The model never does arithmetic | Every total is computed in TypeScript from catalog prices. verifyAmountAgainstSource() rejects any figure the model echoes that doesn't match the database. |
| β Human-in-the-loop on money | draftAndQueueInvoice writes to approvals with pending status. Nothing financial executes without owner action. |
| π PII redaction | redactPii() strips phone numbers and emails before any text reaches a log line or model trace. |
| π’ Tenant isolation | Row Level Security on every table β enforced by Postgres, not application code. |
| π¦ Abuse protection | Upstash rate limiting on agent endpoints. |
| β Webhook authenticity | HMAC signature verification on every WAHA inbound event. |
| π Cost transparency | Per-business token accounting in model_usage. |
| Layer | Technology | Why |
|---|---|---|
| Framework | Next.js 16.2 (App Router) Β· React 19.2 | Server Components + Route Handlers in one deployable |
| Language | TypeScript 5 | Tool schemas are typed end to end |
| Agent runtime | Vercel AI SDK 7 (ai) |
Provider-agnostic tool calling and streaming |
| Models | Google Gemini (default) Β· OpenAI Β· Anthropic Claude Β· Groq | Swappable per business in Settings β no code change |
| Database | Supabase (Postgres + RLS + Realtime) | Multi-tenant isolation enforced at the database |
| Vector search | pgvector via embeddings / documents |
RAG grounding over business data |
| WAHA | Self-hostable WhatsApp HTTP API | |
| Jobs | Supabase pg_cron β worker route |
Async agent runs + daily insight generation |
| Rate limiting | Upstash Redis | Serverless-native |
| Data fetching | TanStack Query 5 | Cache + realtime invalidation |
| UI | Tailwind v4 Β· shadcn/ui Β· Radix Β· Base UI Β· HugeIcons | Accessible primitives |
| Charts / Motion | Recharts Β· GSAP Β· Lenis | Dashboard analytics and landing page |
| Validation | Zod 4 | Tool input schemas and API boundaries |
| Hosting | Vercel | Native Next.js + cron support |
flowchart LR
subgraph Client["π₯οΈ Dashboard"]
UI["Next.js App Router<br/>React Server Components"]
TQ["TanStack Query<br/>+ Supabase Realtime"]
end
subgraph Edge["β‘ API Layer"]
WH["/api/waha/webhook"]
AG["/api/agent/run"]
CP["/api/copilot"]
JW["/api/jobs/worker"]
REST["/api/products Β· invoices<br/>customers Β· books Β· approvals"]
end
subgraph Brain["π§ Agent Layer"]
OR["Orchestrator"]
TL["Tool Layer"]
RAG["RAG Retrieval"]
GR["Guardrails"]
end
subgraph Data["ποΈ Supabase"]
PG[("Postgres + RLS")]
RT["Realtime"]
CRON["pg_cron"]
end
WA["π± WhatsApp<br/>via WAHA"] <--> WH
UI --> REST --> PG
UI --> CP --> OR
WH --> JW --> OR
AG --> OR
OR <--> TL <--> PG
OR <--> RAG <--> PG
OR --> GR
CRON --> JW
RT --> TQ --> UI
style OR fill:#4f46e5,color:#fff
style PG fill:#3FCF8E,color:#000
Message lifecycle: WhatsApp β WAHA β signature-verified webhook β deduped job β orchestrator β RAG grounding β tool loop β reply + approval queue β owner action β stock/ledger writes β realtime dashboard update.
21 tables, Row Level Security enabled on every one, scoped by business_id.
Expand full table list
| Domain | Tables |
|---|---|
| Tenancy | businesses Β· business_members |
| Messaging | conversations Β· messages Β· webhook_events |
| Catalog | products Β· product_categories Β· suppliers |
| Inventory | stock_movements Β· reorders |
| Sales | invoices Β· invoice_items Β· payments Β· customers |
| Accounting | ledger_entries |
| Agent | approvals Β· jobs Β· model_usage Β· daily_insights |
| RAG | documents Β· embeddings |
| Audit | audit_logs |
Migrations live in supabase/migrations/ and run in order.
- Node.js 20+
- Supabase project (supabase.com) or local CLI
- WAHA instance for WhatsApp (waha.devlike.pro) β optional for dashboard-only development
- At least one AI provider API key (Gemini is the default and has a free tier)
git clone https://github.com/<your-org>/deskops-ai.git
cd deskops-ai
npm installcp .env.example .env.local| Variable | Required | Purpose |
|---|---|---|
NEXT_PUBLIC_SUPABASE_URL |
β | Supabase project URL |
NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY |
β | Client-side anon key |
SUPABASE_SERVICE_ROLE_KEY |
β | Server-side admin operations |
GEMINI_API_KEY |
βͺοΈ | Default provider |
OPENAI_API_KEY |
βͺοΈ | Alternative provider |
ANTHROPIC_API_KEY |
βͺοΈ | Alternative provider |
GROQ_API_KEY |
βͺοΈ | Alternative provider |
AI_PROVIDER |
βͺοΈ | Fallback provider β google | openai | anthropic | groq |
WAHA_BASE_URL |
βͺοΈ | WAHA instance URL |
WAHA_API_KEY |
βͺοΈ | WAHA authentication |
WAHA_WEBHOOK_SECRET |
βͺοΈ | HMAC secret for inbound webhook verification |
CRON_SECRET |
βͺοΈ | Bearer token guarding the job worker route |
UPSTASH_REDIS_REST_URL |
βͺοΈ | Rate limiting |
UPSTASH_REDIS_REST_TOKEN |
βͺοΈ | Rate limiting |
β οΈ At least one provider key is required for the agent to run. API keys stay in the environment β users select a provider in Settings, they never paste keys into the app.
supabase link --project-ref <your-project-ref>
supabase db pushnpm run devSign up, complete onboarding to create your business, then add products under Products. The agent has nothing to ground itself in until the catalog exists.
- Start a WAHA session and note the session name
- Set the session on your business record
- Point the WAHA webhook at
https://<your-domain>/api/waha/webhook - Set
WAHA_WEBHOOK_SECRETon both sides
Without WAHA, you can still exercise the full agent through the in-dashboard Copilot (
/api/copilot).
src/
βββ app/
β βββ (auth)/ login Β· signup
β βββ auth/callback/ OAuth return
β βββ onboarding/ business setup
β βββ dashboard/
β β βββ inbox/ + [conversationId]
β β βββ products/ + [productId] Β· new
β β βββ inventory/ + reorders
β β βββ invoices/ + [invoiceId] Β· new
β β βββ customers/ + [customerId]
β β βββ books/ + reports
β β βββ approvals/ βΈοΈ human-in-the-loop gate
β β βββ settings/ integrations Β· models Β· team
β βββ api/ 26 route handlers
β βββ agent/run agent entry point
β βββ copilot/ in-dashboard agent chat
β βββ waha/webhook inbound WhatsApp (HMAC verified)
β βββ jobs/worker cron-driven async runs
β βββ rag/search vector retrieval
β βββ approvals/ approve / reject queue
β βββ β¦ products Β· invoices Β· customers Β· books Β· inventory Β· reorders Β· insights Β· onboarding Β· business Β· settings
β
βββ lib/
β βββ agents/ orchestrator.ts Β· prompts/orchestrator.ts
β βββ tools/ inventory Β· sales Β· customer Β· books Β· context Β· index
β βββ ai/ provider.ts Β· guardrails.ts Β· embeddings.ts
β βββ rag/ ingest.ts Β· retrieve.ts
β βββ db/ 14 server-side data-access modules
β βββ query/ TanStack Query hooks Β· keys.ts Β· realtime.ts
β βββ jobs/ enqueue.ts Β· worker.ts
β βββ waha/ client.ts Β· verify.ts
β βββ supabase/ admin Β· client Β· server Β· proxy
β βββ invoice/ image.tsx (render) Β· send.ts
β βββ utils/ contact.ts Β· money.ts
β
βββ components/ auth Β· copilot Β· customers Β· dashboard Β· inbox
β inventory Β· invoices Β· landing Β· products Β· ui
βββ hooks/
βββ types/
βββ proxy.ts
supabase/migrations/ schema Β· RLS Β· pg_cron Β· realtime publication
docs/instructions/ engineering reference
| Page | Purpose |
|---|---|
| Home | Today's business summary + anything awaiting the owner |
| Inbox | Every WhatsApp conversation in one place, with agent activity inline |
| Products | Catalog with pricing, stock and imagery |
| Inventory | Live stock levels, low-stock flags, Reorders queue |
| Invoices | Quotes and invoices β create, revise, cancel |
| Customers | Profiles, contact details, full order history |
| Books | Automatic ledger from every sale, with Reports |
| Approvals | βΈοΈ The safety gate β money actions wait here |
| Settings | WhatsApp Integrations, AI Models, Team management |
Next phase
- Payment reconciliation β the
paymentstable is modeled but not yet wired to a payment provider; close the loop from invoice β payment β ledger automatically - Supplier reorder automation β
suppliersandreordersexist; let the agent draft purchase orders when stock crosses threshold, subject to the same approval gate - Proactive agent runs β move beyond reactive replies: daily insights already run on cron, extend to "three customers asked about an out-of-stock item this week"
- Multi-channel β the WAHA adapter is isolated behind an interface; add Instagram DM and Telegram
- Voice notes β transcribe inbound WhatsApp audio, a very common input for this user base
- Sinhala / Tamil support β evaluate model performance on local-language customer messages
Hardening
- Automated test suite over the tool layer and approval state machine
- Agent evaluation harness β scored regression set of customer messages
- Per-business cost caps using
model_usagedata
π To be completed before submission. The guidelines require any pivot in tech stack, features or agent behaviour to be explicitly justified. Fill this in against your original proposal.
| Area | Proposed | Built | Justification |
|---|---|---|---|
| e.g. Agent topology | Multiple autonomous agents | One orchestrator with scoped specialist toolsets | Single coherent plan per message; lower latency and token cost β critical for the target user's budget |
If there were no deviations, state that explicitly β judges are evaluating how closely execution aligns with the original idea.
| Dependency | License | Use |
|---|---|---|
| Vercel AI SDK | Apache-2.0 | Agent tool-calling runtime |
| Supabase JS | MIT | Database client |
| WAHA | Apache-2.0 (core) | WhatsApp HTTP API |
| TanStack Query | MIT | Data fetching |
AI model APIs β Google Gemini, OpenAI, Anthropic and Groq are used via their official commercial APIs under their respective terms of service. No model weights are redistributed. API keys are supplied by the deployer and never bundled.
π Add a
LICENSEfile to the repository root and state the project's own license here.
Team ONYX β IDEALIZE 2026, Open Category
| Name | Institution | Role |
|---|---|---|
| Imesh Madushan | NIBM | Team Leader |
| Thiloko Indhuwari | NIBM | Member |
| Mayantha Udayanga | NIBM | Member |
| Nithija Kalpa | NIBM | Member |