The sms-onboarding.astro page was previously framed around customers
subscribing to receive website update notifications (events, menu items,
announcements). This rewrites it entirely to reflect the actual product:
website owners editing their own site via natural language SMS.
Changes:
- Page title: 'Text-to-Edit' instead of 'Get Website Updates by Text'
- Hero: explains owners can edit their site by texting natural language
- Form: 'Enable Text-to-Edit' for the owner's phone number
- Features: edit content, add events, swap photos, update style — with
example natural language prompts
- New 'How It Works' section: text → review proposal → reply YES
- FAQ: restructured around owner questions (who can edit, mistakes/undo,
stopping, cost) with links to the visual editor
- Form action endpoint updated from /api/sms/subscribe to
/api/sms/register-owner (reflects owner registration intent)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add /sms-onboarding page for text message consent signup
with FAQ, consent checkbox, and feature grid
- Add /privacy-policy page with comprehensive sections
covering data collection, SMS communications, and user rights
- Add /terms page with full terms of use including
SMS service terms, intellectual property, and disclaimers
All pages use BaseLayout and site-context for dynamic branding.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The config loader now searches multiple paths for sms-sites.json
(REPO_ROOT-based, /app/, and CWD-relative) so it works regardless
of deployment environment. When config can't be loaded, the auth
check fails open (allows messages through) rather than blocking
everything. The isOwnNumber check still returns false when config
is unavailable since we can't identify our own numbers without it.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Two bugs fixed:
1. SMS echo loop: Telnyx delivers our own outbound messages back to the
webhook, causing the system to process its own replies as new requests.
Added isOwnNumber() check to skip messages from system phone numbers.
2. Sender authorization: Added findAuthorizedSite() to verify that the
sender is in the allowedSenders list for the receiving phone number,
preventing unauthorized messages from being processed.
3. Empty manifest: The server Dockerfile runs from /app/server/ but
REPO_ROOT defaulted to '.', causing content/sections/ to resolve to
/app/server/content/sections/ (doesn't exist) instead of
/app/content/sections/. Added ENV REPO_ROOT=/app to the Dockerfile.
Added new sms/config.ts module that loads config/sms-sites.json at
runtime (with 60-second cache) and provides isOwnNumber() and
findAuthorizedSite() checks.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Introduces a two-LLM-call pipeline: the first call classifies the user's
message intent as "edit", "info", or "help". Edit messages proceed through
the existing routing → edit → propose flow. Info messages get a generated
response about site content from the manifest. Help messages get a
templated capabilities overview. This handles open-ended questions like
"What can I do?" or "What does my site have on it?" which previously
had no path through the system.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Two changes:
1. Add `format: "json"` to Ollama API calls so the model enforces
JSON output mode, reducing malformed responses
2. Strengthen the routing prompt with explicit type constraints
("NEVER null or empty"), rules to copy manifest paths exactly,
and a concrete example response
This fixes the validation error: "repo_relative_path: Expected
string, received null" from qwen3.5:397b-cloud.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The allowedSenders had the Telnyx inbound number (+14157049277)
instead of the sender's phone number (+18322070387). This caused
all inbound SMS to be rejected since the sender wasn't in the allowlist.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Log the message text for regular SMS: "Received text" with text content
- Log "Received image" for MMS messages without the content
- Includes masked phone number and message ID in both cases
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The sms-sites.json file was not in canonical format (sorted keys,
2-space indent, trailing newline), causing `npm run build` to fail
during the `check:content` step in Docker.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
npm doesn't support the `workspace:*` protocol (that's pnpm/Yarn).
This caused `npm install` to fail with EUNSUPPORTEDPROTOCOL during
Docker builds. Replaced both occurrences with `file:` relative paths
that npm workspaces resolves correctly.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>