Skip to content

feat: add edge-customer-agent-typescript example (DEV-839) - #99

Open
somemothersson wants to merge 3 commits into
team-telnyx:mainfrom
somemothersson:feat/edge-customer-agent-typescript
Open

feat: add edge-customer-agent-typescript example (DEV-839)#99
somemothersson wants to merge 3 commits into
team-telnyx:mainfrom
somemothersson:feat/edge-customer-agent-typescript

Conversation

@somemothersson

Copy link
Copy Markdown
Contributor

What

Durable Entity Agent on Telnyx Edge Compute — one actor per customer phone number, surviving across days, calls, SMS, and actor evictions. The AI Assistant is the reasoning/voice harness inside the agent; the Agent SDK owns the durable entity.

Key insight (Ian Reither, Aug 10): "The Assistant isn't the durable object. Ian is." The actor IS the customer — not a conversation, not a session. A durable entity that lives across channels (voice/SMS/WhatsApp), across days, across interactions. No external state machine. No queue infrastructure. No context reconstruction.

Why

Existing edge-* examples (Python/Node/Go) demonstrate Edge Compute functions. This is the first TypeScript example showing the Agent SDK pattern: a -bound actor that addresses a customer by E.164 number via and persists state across interactions. It unblocks the same pattern for any TS developer building durable customer-facing agents on Telnyx Edge Compute.

Changes

  • edge-customer-agent-typescript/ — new self-contained example (TypeScript, ESM)
    • src/customer-agent.tsCustomerAgent extends Agent<Env, CustomerState>; keyed by E.164, durable across evictions
    • src/index.ts — fetch handler wiring inbound voice + SMS to the actor
    • src/llm.ts, src/messaging.ts, src/voice.ts, src/salesforce.ts, src/state.ts — supporting modules
    • telnyx.toml[[actors]] binding (Agent SDK manifest, not func.toml)
    • package.json (type: module, @telnyx/edge-runtime), tsconfig.json, .env.example, README.md, GUIDE.md, API.md
  • scripts/examples_mapping.yaml — register edge-customer-agent (product: voice, language: typescript, framework: edge-compute)
  • README.md — add row to Voice AI table (148 → 149 examples)

Verification

  • python scripts/verify.py --only edge-customer-agent-typescript --verbosePASS (0 errors)
  • npx tsc --noEmit0 type errors
  • verify.py already supports the telnyx.toml edge marker (added by edge-agri-crop-advisory on main); no verify.py changes needed in this PR.

Linear

Refs DEV-839.

Durable Entity Agent on Telnyx Edge Compute — one actor per customer
phone number, surviving across days, calls, SMS, and evictions.
The AI Assistant is the reasoning harness inside; the Agent SDK
owns the durable entity.

- Register in examples_mapping.yaml (product: voice, lang: typescript)
- Add row to README.md Voice AI table (148 -> 149)
- verify.py already supports telnyx.toml edge marker (no changes needed)
- Passes python scripts/verify.py --only edge-customer-agent-typescript
- Passes tsc --noEmit
…-839)

Three fixes discovered during Edge Compute deploy + smoke test:

1. telnyx.toml: declare each secret with its own [[secrets]] block
   (binding=name=SECRET_NAME). The runtime aggregates them into
   env.SECRETS.get('NAME'). The old single-binding block was rejected
   by 'telnyx-edge ship'.

2. customer-agent.ts: replace process.env['X'] with
   await this.env.SECRETS.get('X') — the Edge runtime exposes secrets
   via the Secrets interface, not process.env. Made getLLM() and
   getSalesforce() async. Added normalizePhone() helper to restore
   the '+' prefix that Dapr's RFC 1123 naming forbids in actor names.

3. index.ts: added actorNameFromPhone() to strip the leading '+'
   from E.164 numbers before idFromName(). Dapr reminder/alarm names
   must be lowercase alphanumeric per RFC 1123 — the '+' caused
   ERR_ACTOR_REMINDER_DELETE on every actor method call.

Smoke test: 8/8 endpoints pass on deployed Edge Compute function.
https://edge-customer-agent-typescript-93b6ca4c-6.telnyxcompute.com
@somemothersson
somemothersson force-pushed the feat/edge-customer-agent-typescript branch from 02eb1ca to 163d7bf Compare August 13, 2026 17:47
Real Telnyx TeXML Application webhooks arrive as application/x-www-form-urlencoded
with PascalCase fields (From, To, CallControlId, CallDuration, Body). The
original handlers called req.json() which would throw on form-encoded bodies,
breaking real inbound calls.

Add parseWebhookBody() helper that normalizes both formats into a common
shape: form-encoded TeXML (PascalCase) and JSON Call Control API
(nested data.payload with snake_case). The voice and call-ended webhook
handlers now use this helper instead of req.json() directly.

Discovered during end-to-end telephony testing: wired +19362204906 to
the deployed edge function via a TeXML Application, placed test calls,
confirmed the voice webhook fires with form-encoded bodies.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

1 participant