{"id":50613726,"url":"https://github.com/stepandel/ycagentphone","last_synced_at":"2026-06-06T06:30:43.999Z","repository":{"id":358586251,"uuid":"1241922644","full_name":"stepandel/ycagentphone","owner":"stepandel","description":"Your virtual host of the restaurant","archived":false,"fork":false,"pushed_at":"2026-05-18T03:17:22.000Z","size":258,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"master","last_synced_at":"2026-05-18T05:38:54.945Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/stepandel.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-05-18T01:07:54.000Z","updated_at":"2026-05-18T03:17:26.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/stepandel/ycagentphone","commit_stats":null,"previous_names":["stepandel/ycagentphone"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/stepandel/ycagentphone","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/stepandel%2Fycagentphone","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/stepandel%2Fycagentphone/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/stepandel%2Fycagentphone/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/stepandel%2Fycagentphone/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/stepandel","download_url":"https://codeload.github.com/stepandel/ycagentphone/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/stepandel%2Fycagentphone/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33972397,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-06T02:00:07.033Z","response_time":107,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-06-06T06:30:40.863Z","updated_at":"2026-06-06T06:30:43.994Z","avatar_url":"https://github.com/stepandel.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# CallHost\n\nOne of the winners of AgentPhone YC Hackathon.\n\n[Video demo](https://youtu.be/3pKax01f33U)\n\nCallHost is a restaurant phone host for AgentPhone webhooks, backed by a Moss knowledgebase.\n\n## Architecture\n\n```text\nCaller\n  -\u003e AgentPhone phone number\n  -\u003e AgentPhone webhook\n  -\u003e Express service\n  -\u003e Call skill matching\n  -\u003e Moss search\n  -\u003e OpenAI Responses API\n  -\u003e AgentPhone speaks the answer\n```\n\nCodex is useful as the builder and maintenance harness. The live call path is this service.\n\n## Setup\n\n```bash\ncd ~/Development/ycagentphone\nbun install\ncp .env.example .env\n```\n\nEdit `.env` and set:\n\n```bash\nOPENAI_API_KEY=...\nLANGFUSE_PUBLIC_KEY=...\nLANGFUSE_SECRET_KEY=...\nMOSS_PROJECT_ID=...\nMOSS_PROJECT_KEY=...\nMOSS_INDEX_NAME=ycagentphone-restaurant-kb\nAGENTPHONE_API_KEY=...\nAGENTPHONE_AGENT_ID=...\nAGENTPHONE_BASE_URL=https://api.agentphone.ai\nAGENTPHONE_WEBHOOK_BASE_URL=...\nSTRIPE_SECRET_KEY=sk_test_...\nSTRIPE_STANDARD_RESERVATION_DEPOSIT_AMOUNT_CENTS=2000\nSTRIPE_LARGE_PARTY_RESERVATION_DEPOSIT_AMOUNT_CENTS=10000\nSTRIPE_RESERVATION_DEPOSIT_CURRENCY=usd\nSTRIPE_STANDARD_RESERVATION_PAYMENT_LINK_URL=...\nSTRIPE_LARGE_PARTY_RESERVATION_PAYMENT_LINK_URL=...\nCOMPANY_NAME=...\nRESTAURANT_GREETING=\"Good evening, and thank you for calling. This is the restaurant's virtual host. How may I help you today?\"\nRESTAURANT_PROCESSING_MESSAGE=\"Of course. Let me check that for you.\"\nPUBLIC_CONTACT_EMAIL=...\n```\n\nThen seed the Moss knowledgebase:\n\n```bash\nbun run ingest:kb\n```\n\nThe default Moss index is `ycagentphone-restaurant-kb`. Override `MOSS_INDEX_NAME` in `.env` if you want to use a different knowledge space. Retrieval uses hybrid search by default with `MOSS_SEARCH_ALPHA=0.8`, where `1.0` is semantic-only and `0.0` is keyword-only.\n\nLangfuse tracing is enabled automatically when `LANGFUSE_PUBLIC_KEY`, `LANGFUSE_SECRET_KEY`, and `LANGFUSE_BASE_URL` are set. Each answered turn is traced as `agentphone.answer`, with Moss retrieval and the OpenAI Responses API call captured underneath it.\n\n## Run Locally\n\n```bash\nbun run dev\n```\n\nHealth check:\n\n```bash\ncurl http://localhost:3000/health\n```\n\nLocal turn test through HTTP:\n\n```bash\ncurl -X POST http://localhost:3000/test/turn \\\n  -H 'content-type: application/json' \\\n  -d '{\"transcript\":\"Do you have a private dining menu for 14 people?\"}'\n```\n\nLocal turn test through the script:\n\n```bash\nbun run test:turn -- \"Do you have gluten-free pasta?\"\n```\n\nSigned local webhook test:\n\n```bash\nbun run test:webhook -- \"Can I book a birthday dinner for 12 people?\"\n```\n\n## AgentPhone Webhook\n\nUse a stable public URL for AgentPhone. With ngrok, reserve a static domain in the ngrok dashboard, then run:\n\n```bash\nngrok http --url=YOUR-STATIC-DOMAIN.ngrok-free.app 127.0.0.1:3000\n```\n\nSet the stable base URL in `.env`:\n\n```bash\nAGENTPHONE_WEBHOOK_BASE_URL=https://YOUR-STATIC-DOMAIN.ngrok-free.app\n```\n\nConfigure AgentPhone from that constant:\n\n```bash\nbun run configure:webhook\n```\n\nThat script configures AgentPhone to call:\n\n```text\n${AGENTPHONE_WEBHOOK_BASE_URL}/webhooks/agentphone\n```\n\nIt also updates `AGENTPHONE_WEBHOOK_SECRET` in `.env` with the returned AgentPhone signing secret.\nRestart the webhook server after running this command so signature verification uses the new secret.\n\nConfigure a Stripe test-mode reservation deposit link:\n\n```bash\nbun run configure:stripe\n```\n\nThe script requires `STRIPE_SECRET_KEY=sk_test_...`, creates a test-mode reservation deposit Payment Link using `STRIPE_RESERVATION_DEPOSIT_AMOUNT_CENTS` or the `$20` standard deposit by default, and writes `STRIPE_RESERVATION_PAYMENT_LINK_URL` into `.env`. For production-like behavior, configure separate `STRIPE_STANDARD_RESERVATION_PAYMENT_LINK_URL` and `STRIPE_LARGE_PARTY_RESERVATION_PAYMENT_LINK_URL` links for the `$20` and `$100` deposits.\n\nConfigure Stripe to send `checkout.session.completed` events to:\n\n```text\n${AGENTPHONE_WEBHOOK_BASE_URL}/webhooks/stripe\n```\n\nSet the endpoint signing secret as `STRIPE_WEBHOOK_SECRET=whsec_...`. Reservation SMS links include a Stripe Payment Link `client_reference_id`, which the Stripe webhook uses to mark the matching reservation deposit paid in the admin UI and send the guest a payment confirmation text.\n\nThe webhook parser accepts several common payload shapes, including:\n\n```json\n{\n  \"callId\": \"call_123\",\n  \"from\": \"+15551234567\",\n  \"transcript\": \"Can I book a birthday dinner for 12 people?\"\n}\n```\n\nand:\n\n```json\n{\n  \"call\": { \"id\": \"call_123\", \"from\": \"+15551234567\" },\n  \"messages\": [\n    { \"role\": \"caller\", \"content\": \"Do you accommodate nut allergies?\" }\n  ]\n}\n```\n\nThe JSON response includes `response`, `text`, and `message` fields with the same answer so it is easy to adapt to AgentPhone's exact expected response key. When the agent should end the call after speaking, the response also includes `hangup: true` and `action: \"hangup\"`.\n\nInbound SMS/text webhooks can use the same endpoint. Send `channel: \"sms\"` or an AgentPhone message-style payload such as:\n\n```json\n{\n  \"event\": \"message.received\",\n  \"data\": {\n    \"channel\": \"sms\",\n    \"messageId\": \"msg_123\",\n    \"fromNumber\": \"+15551234567\",\n    \"body\": \"Can we move the reservation to 7:30 and add a high chair?\"\n  }\n}\n```\n\nText turns are answered with normal JSON instead of voice streaming. The agent receives the caller's latest reservation log context, can answer restaurant or reservation questions from the knowledgebase and log, and appends text follow-ups to the reservation history. Common change requests like time, date, party-size, and special-note updates are stored as reservation amendments so later text replies see the updated current reservation.\n\nFor NDJSON-style streaming, call:\n\n```text\n/webhooks/agentphone?stream=1\n```\n\nPost-call webhooks use AgentPhone's `agent.call_ended` event. The service extracts the full transcript from `data.transcript`, summarizes reservation context with OpenAI, writes a Markdown reservation log entry, then sends a brief follow-up message to the caller with party size, day/time, special notes, and the correct Stripe deposit link when configured. Parties of 10 or fewer use the `$20` standard reservation deposit; parties over 10 use the `$100` large-party deposit. Outbound messaging requires `AGENTPHONE_API_KEY` and `AGENTPHONE_AGENT_ID`; `AGENTPHONE_BASE_URL` defaults to `https://api.agentphone.ai`.\n\nReservation log entries default to `data/reservation-log.md` and can be moved with `RESERVATION_LOG_PATH`. The file is readable Markdown with embedded metadata so later SMS/text webhooks from the same caller can pull the latest reservation context and append follow-up changes to the same reservation history.\n\n## Knowledgebase\n\nPut approved caller-facing information in `kb/`.\n\nRecommended files:\n\n- `kb/restaurant-overview.md`\n- `kb/menu.md`\n- `kb/food-modifications.md`\n- `kb/allergy-practices.md`\n- `kb/large-party-policy.md`\n- `kb/prix-fixe-large-parties.md`\n- `kb/chef-and-owners.md`\n- `kb/reservation-inquiry-large-party.md`\n- `kb/faq.md`\n- `kb/handoff-rules.md`\n- `kb/competitors/*.md`\n\nAfter editing the knowledgebase, run:\n\n```bash\nbun run ingest:kb\n```\n\n## Notes\n\n- The system prompt lives in `src/prompt.ts`.\n- Call skills live in `src/skills/`. The reservation-taking flow is `src/skills/reservation-taking.ts`.\n- The webhook adapter lives in `src/agentphone.ts`.\n- The OpenAI call lives in `src/agent.ts`.\n- Moss retrieval lives in `src/memory.ts`.\n- If AgentPhone provides a signing secret, set `AGENTPHONE_WEBHOOK_SECRET` and confirm the exact signature header/hash format in `src/agentphone.ts`.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fstepandel%2Fycagentphone","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fstepandel%2Fycagentphone","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fstepandel%2Fycagentphone/lists"}