AI for everyone: where the edge, hosted and channel paths fall short, and what to build¶
Date: 2026-10-14. Status: gap analysis with the first three items shipped (see "Shipped with this document"). Everything below is read from the code in this repository on that date; file paths are relative to the repo root. Nothing here was measured on real users.
The claim being tested¶
"AI reachable to everyone" means someone with no laptop, no GPU, no English, no data plan, no card and no account can still use an agent. The platform already has three paths that each cover part of that, and no other open-source agent platform combines them:
| Path | What it is today | Who it reaches |
|---|---|---|
Edge (web/app/lib/edge, ~35k lines) |
WebGPU models in the browser (WebLLM), Chrome's built-in model, on-device vision, English speech in and out, local tools, a guarded bridge to server tools, WebMCP | A signed-in person on a recent laptop or Android with WebGPU |
Hosted free pool (core/providers platform_hosted_llm_provider, core/agents/FreeTierChatClientService) |
The operator's shared model, by default the --local-ai Ollama container, for every FREE tenant |
Anyone with an account on an instance whose operator set a pool up |
Channels (core/omnichannel) |
WhatsApp, Telegram, Slack, Discord, Teams, Messenger, Instagram, X, email, phone calls with DTMF menus, and now SMS | Anyone who already uses one of those apps, or any phone |
Plus: a console in 30 locales including all 22 scheduled Indian languages, Sarvam for Indic speech and translation, 77 agent templates aimed at non-technical people (homework helper, farmer advisory, citizen scheme guide, pharmacy counter), public share links and an embeddable widget.
Gaps found¶
Ordered by how many people each one locks out. "Pointer" is where the gap is in the code.
A. Reach: who cannot get a message to an agent at all¶
| # | Gap | Pointer | Status |
|---|---|---|---|
| A1 | Plain SMS could not hold a conversation. An inbound SMS could only fire a workflow; without one it was acknowledged and dropped. Feature phones (no data) had no way in. | TwilioSmsWebhookController (old javadoc: "no chat/pairing/session model for plain SMS"); ChannelType had no SMS |
Shipped (see below) |
| A2 | No USSD, no "give a missed call" entry, no RCS/Signal/Viber/LINE/Zalo/KakaoTalk/WeChat | no adapters; "missed call" appears only in marketing copy web/app/help/workflow-recipes/recipes-automotive-telecom.tsx:33 |
open |
| A3 | Phone calls are English-only: no language/voice on the TwiML, hard-coded "Hi, connecting you now." |
TwilioVoiceWebhookController.java:96,172-176 |
open, small |
| A4 | No shared-device or kiosk mode (library, school, village centre, clinic): no anonymous multi-user session on one device, no wipe between users, no "continue on your phone" | no code; "kiosk" only in recipes-transit-temple.tsx:177 |
open |
| A5 | Share links have no QR code in the agent Share panel (only in the studio demo) | ShareAgentPanel.tsx |
Shipped |
| A6 | Unknown senders are held for admin pairing on every channel by default; a public number needs autoApproveNewSenders and nothing in the UI explains the trade-off |
ChannelDefaultAgent.java:39-49 |
open, docs/UX |
B. Cost: who is told to pay for something that is free¶
| # | Gap | Pointer | Status |
|---|---|---|---|
| B1 | A local model was billed like a paid API. Ollama tags are not in the pricing table, so every call cost the $0.50/$2.00 fallback, debited the $0.15/day per-user budget, counted against the 400k-token monthly quota ("upgrade to Premium" on your own laptop) and sorted last in cheapest-first failover. | LlmPricingCatalog.java:534-538, FreeTierChatClientService.java:616-624 (the code admitted it), PerUserBudgetService, FreeTierUsageLimiterService:129 |
Shipped |
| B2 | A fresh install has no free path unless the operator passes --local-ai or adds a pool row; app.local-llm.bootstrap.enabled is off by default and the no-provider banner tells a new user to add a key |
LocalLlmPoolBootstrap.java:50, providerGate.ts:39-49 |
open; a deliberate default, revisit for the OSS site mode |
| B3 | Defaults disagreed: Java and Compose said llama3.2, the CLI pulls qwen3.5:4b; neither tag is in the model catalog seed |
LocalLlmPoolBootstrap.java:82, lite.yml:215, ProviderModelCatalogService:226-232 |
Shipped (defaults aligned; catalog seed still open) |
| B4 | No way for one tenant to sponsor another's usage (a school paying for a village, an NGO for its field workers). The only grant is the Academy scholarship, service-to-service | CompGrantService.java; wallets in core/payments/wallet are for agents spending, not for AI usage |
open |
| B5 | app.llm.default-source, app.free-tier.*, per-user budget keys existed only as @Value defaults, in no YAML and no doc |
TenantLimitsServiceImpl:83-94, FreeTierUsageLimiterService:129-131 |
partly shipped (documented the two that matter) |
C. Language: who cannot be understood¶
| # | Gap | Pointer | Status |
|---|---|---|---|
| C1 | No local text-to-speech anywhere on the server. Indic voice needs a paid Sarvam key; Sarvam TTS is fixed to en-IN and covers 11 of 22 languages |
services/sidecar/app/tts.py:56, TtsModelProvider.java:43-52 |
open |
| C2 | On-device speech is English-only (whisper-tiny.en, Kokoro English voices); a multilingual swap is described as one line |
whisperWorker.ts:45-50, kokoroWorker.ts:41 |
open, small |
| C3 | No language-aware model selection; the edge catalog has no language metadata; guardrail regexes and the NER redactor are English; local_wikipedia is en. only |
tierRouter.ts, edgeGuardrails.ts, localToolsWeb.ts |
open |
| C4 | OCR is English only (bare tesseract-ocr, no language packs, no lang passed) |
services/sidecar/Dockerfile:31, extraction.py |
open, small |
| C5 | RTL is marked but not applied: 522 physical margin classes vs 7 logical, no rtl: variants, no RTL tests |
web/app |
open |
| C6 | Edge UI strings outside the edge namespace are untranslated (branding, setup nudge, chip, admin page) |
branding.ts:9-12, OnDeviceSetupNudge.tsx |
open |
D. Device: who has no GPU, no stable network, no storage¶
| # | Gap | Pointer | Status |
|---|---|---|---|
| D1 | The CPU fallback (tier 2) is unreachable: no UI ever installs it, and it was never loaded in a real browser | tier2.ts:82-94, EdgeConsole.tsx:300 filters it out |
open |
| D2 | No offline console: PWA manifest exists, but no app service worker, no install prompt; only already-cached weights work offline, the page itself does not load | web/public/manifest.webmanifest, layout.tsx:292, no beforeinstallprompt |
open |
| D3 | /edge needs sign-in; the docs call it "no server at all". Signed-out on-device use exists only on /code with pre-cached weights |
publicRoutes.ts:75-111, docs/local-models.md:66 (corrected in this change) |
open (doc fixed) |
| D4 | Weights come from public CDNs with no hash check for any tier-1, speech, vision or face model; the instance cannot mirror them; low-bandwidth users re-download per device | tier1.ts:977,1233, downloadManager.ts:17-21 (all sha256: null) |
open |
| D5 | Downloads are not in a worker and have no Background Fetch; Save-Data and 2G only warn | downloadManager.ts:29-32, preflight.ts:103-160 |
open |
| D6 | No mobile client source in the repo (Android is a beta APK built elsewhere; iOS never compiled) | docs/plugins-and-apps.md, surfaces.ts |
open, out of scope here |
E. Hosted-pool robustness (affects everyone on the free path)¶
| # | Gap | Pointer |
|---|---|---|
| E1 | No capability registry per pool row or model (tools, streaming, context); small local models' tool quality is a doc warning only | AgentChatClientFactory.java:13-32, docs/local-models.md:77-79 |
| E2 | No fallback from a tenant's own key to the pool; FREE tenants cannot set a backup provider | AgentService.java:1752-1757 |
| E3 | Pool health and latency are per replica, in memory, empty after a restart | HostedProviderHealth |
| E4 | providerGate.ts:41 treats isFreePlan as "on the pool" even when no pool exists, so a stored-FREE tenant with free mode off can see no banner and then a 400 |
providerGate.ts:41 |
Shipped with this document¶
- SMS conversations (A1, A5 via the QR):
ChannelType.SMS,TwilioSmsChannelAdapter(channel.sms.send, sentence-boundary cut atapp.omnichannel.sms.max-reply-chars),TwilioSmsWebhookControllerrewritten on the shared session/pairing/approval machinery with the workflow trigger kept,SmsChannelController(PUT /api/channels/sms/default-agent), the SMS card and health probe,channel.sms.message.received, the inbox filter and the Channels routing card. Docs:docs/sms-channel.md. Unit tests only; not run against a Twilio number. - Zero-cost pool rows (B1, B3):
zero_costcolumn (V484, with a backfill for Ollama rows),LlmPricingCatalog.zeroCost()view used byHostedLlmChatClientFactory, the per-user budget advisor releasing instead of charging, the free-tier chain skipping both reservations when every row is free (app.free-tier.meter-zero-cost-rowsrestores the quota), zero-cost rows first in failover order, the bootstrap row created as free, the Platform Admin toggle and badge, and one default model (qwen3.5:4b) across Java, Compose and the CLI. Unit tests only; not run on a stack. - QR code on the Share panel (A5).
- Edge relay (next-steps item 1, shipped the same day):
core/omnichannel/relay(Valkey registry, per-channel queues, lease and reply list, hosted fallback on the inbound thread),EdgeRelayControllerunder/api/channels/edge-relay, hooks in the Twilio SMS, Twilio WhatsApp and Meta WhatsApp webhooks,web/app/lib/edge/relay.tsand the /edge card. Docs:docs/edge-relay.md. Unit tests on both sides; not run against a real number.
What to build next, in order¶
Each item is scoped so one person can finish it; the first four are the ones no comparable platform offers out of the box.
- Edge relay: your laptop is the model behind your number. Shipped (see above). Open follow-ups: Telegram and the other channels (a three-line hook each), media turns, a "relay answered" marker in the conversation inbox, and a measured run against a real number.
- Kiosk mode (A4): a share-link variant that starts a fresh anonymous session per visitor,
wipes on idle, hides history, large-type UI, optional language picker on the first screen, and a
"continue on your phone" QR that hands the session to WhatsApp or SMS. Mostly
web/app/a/plus a session TTL on the share controller. - Offline-first console (D1, D2): one app service worker that caches the shell of
/edge,/codeand the agent playground, an install prompt, and an install card for the CPU tier so a phone without WebGPU gets the 0.6B model instead of nothing. Verify tier 2 in a real browser first; it has never been loaded. - Pay it forward (B4): a tenant-level sponsorship that moves a monthly token or dollar
allowance from one tenant's cap to another (or to "any new tenant on this instance"), built on
the existing
CompGrantServiceshape with a ledger row per grant and a Platform Admin view. A school, NGO or company can then fund usage for the people it serves. - Speak every language (C1, C2, C3): add a local TTS engine to the sidecar (Piper or
Kokoro-multilingual) as a
TtsModelProvider, swap the on-device Whisper to the multilingual tiny model, pass the detected language to the Prompt API session and to<Say language>on Twilio, and addlanguagesto the edge model manifest so the router can prefer a model that speaks the user's language. - Mirror the weights (D4): let an instance host its own copy of the edge manifest's assets with sha256, so a classroom downloads once from the LAN and the integrity check runs. The download manager already verifies hashes when they are present.
- Phone-first entry points (A2, A3): missed-call callback (Twilio status callback → outbound
call or SMS), the caller's language on the TwiML, and a USSD gateway adapter behind the same
ChannelTypemachinery. - Hosted-pool hygiene (E1 to E4): a per-row capability JSON, own-key-to-pool fallback for
FREE tenants, pool health in Valkey, and the
providerGatefix.
How this was verified¶
Three code sweeps (edge stack, hosted pool, channels and reach), each with file:line evidence, cross-checked against the docs and the changelog; the shipped items compile across the Maven reactor and have unit tests. No stack was started and no device, phone number or real model was exercised for this document.