Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions .cursor-plugin/marketplace.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,14 @@
"name": "Function1st"
},
"metadata": {
"description": "Book a restaurant by phone. Need: Telnyx KYC + one US number, xAI team with ZDR off, API keys, Voice Agent Builder.",
"version": "0.3.20"
"description": "Phone skills for voice tasks (restaurant booking first). Need: Telnyx KYC + one US number, xAI team with ZDR off, API keys, Voice Agent Builder.",
"version": "0.4.1"
},
"plugins": [
{
"name": "phonezero",
"source": "./plugins/phonezero",
"description": "Book a restaurant by phone. Need: Telnyx KYC + one US number, xAI team with ZDR off, API keys, Voice Agent Builder. Then /setup-phone-calling.",
"description": "Phone skills for voice tasks (restaurant booking first). Need: Telnyx KYC + one US number, xAI team with ZDR off, API keys, Voice Agent Builder. Then /setup-phone-calling.",
"logo": "assets/logo.png"
}
]
Expand Down
15 changes: 15 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,20 @@ jobs:
node scripts/xai-mcp.mjs --self-test
node plugins/phonezero/scripts/launch-xai-mcp.mjs --resolve-only

- name: Prompt paste files
run: |
set -euo pipefail
python3 - <<'PY'
from pathlib import Path
desc = Path("plugins/phonezero/prompts/end_call.md").read_text(encoding="utf-8").strip()
voice = Path("plugins/phonezero/prompts/voice-agent.md").read_text(encoding="utf-8")
if not desc:
raise SystemExit("end_call.md is empty")
if desc not in voice:
raise SystemExit("end_call.md must appear verbatim in voice-agent.md")
print("end_call description in sync")
PY

- name: TeXML well-formed
run: |
set -euo pipefail
Expand All @@ -80,5 +94,6 @@ jobs:
- name: Privacy guards
run: |
set -euo pipefail
git fetch --no-tags origin "${GITHUB_BASE_REF:-main}:refs/remotes/origin/${GITHUB_BASE_REF:-main}" || true
python3 scripts/privacy-check.py --self-test
python3 scripts/privacy-check.py
30 changes: 15 additions & 15 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# PhoneZero — agent bootstrap

PhoneZero books a restaurant table by phone (Telnyx hosted MCP + xAI Voice Agent) with zero user infrastructure.
PhoneZero is a phone-skills platform (Telnyx hosted MCP + xAI Voice Agent) with zero user infrastructure. Restaurant booking is the first skill.

If the user pointed you at `https://github.com/function1st/PhoneZero` or said **set this up** / **set up phone calling** / `/setup-phone-calling`: identify the host, then run **only** that host’s section. Do not mix them.

Expand All @@ -15,7 +15,7 @@ If the user pointed you at `https://github.com/function1st/PhoneZero` or said **

You are a **computer-use** agent. Drive the UI yourself. Do these steps **in order**. Do not treat missing package names as missing APIs. Do not hunt the Telnyx catalog for `whoami` or `/v2/whoami` — they are not MCP tools.

[README.md](README.md) has the same playbook at the top. Booking rules after the ids are in session: [`plugins/phonezero/skills/phonezero/SKILL.md`](plugins/phonezero/skills/phonezero/SKILL.md) §§2–12.
[README.md](README.md) has the same playbook at the top. After the ids are in session: [`plugins/phonezero/skills/phonezero-runtime/SKILL.md`](plugins/phonezero/skills/phonezero-runtime/SKILL.md) (dial + Grok ad-hoc interview) and the matching phone skill ([`book-restaurant`](plugins/phonezero/skills/book-restaurant/SKILL.md) or [`confirm-business-hours`](plugins/phonezero/skills/confirm-business-hours/SKILL.md)). How-to: [`docs/SKILLS.md`](docs/SKILLS.md).

**Do not** Customize → **+ Add** this repo as a Cursor plugin. Cursor **PhoneZero** (`plugins/phonezero`) is stdio Telnyx → on Grok the key never arrives → Telnyx **10009**.

Expand All @@ -34,16 +34,16 @@ Do **not** silently keep defaults. “Set this up” is not consent to these. Sh
```
Call settings (defaults — change any now)

1. Spoken name the restaurant hears: PhoneZero
“Hello, this is {name}…” — keep PhoneZero, or set your name / an alias.
1. Spoken name the callee hears: PhoneZero
“Hello, this is {name}…” — keep PhoneZero, or set your name / an alias.

2. AI disclaimer in the opener: ON
ON → “…{name}, an automated assistant, calling on a recorded line…”
OFF → omit “, an automated assistant,”
You may turn this OFF.
```

Keep their answers in session. Write them into Edit Values / xAI env: `PHONEZERO_AGENT_NAME`, `PHONEZERO_DISCLOSE_AI` (or `PHONEZERO_CFG_*`). If they turn disclaimer OFF and the Builder agent already exists, re-paste `prompts/voice-agent.md` once with `{disclosure_clause}` empty — toggling the variable does not change a baked prompt.
Keep their answers in session and put them on each `phonezero-task` (`spoken_name`, `disclose_ai`). Do **not** put name or disclose on the Configure / Edit Values card. If they turn disclaimer OFF and the Builder agent already exists, re-paste `prompts/voice-agent.md` once with `{disclosure_clause}` empty — that paste is the only way to change a baked prompt. Per-call they may still pick a different spoken name.

### 2. Uninstall the wrong plugin

Expand All @@ -61,7 +61,7 @@ Customize → **MCPs** → add **HTTP** (not stdio, not `npx`):
| URL | `https://api.telnyx.com/v2/mcp` |
| Header | `Authorization` = `Bearer ` + key **in that form** |

If Grok-native **phonezero-grok** is available (not Cursor + Add): install it, **Edit Values** for `TELNYX_API_KEY`, From, `XAI_API_KEY`, plus name / disclose from **1**. If a secret field needs the human, open that field and let them type.
If Grok-native **phonezero-grok** is available (not Cursor + Add): install it, **Edit Values** for `TELNYX_API_KEY`, From, `XAI_API_KEY` only. If a secret field needs the human, open that field and let them type.

[`.grok/config.toml`](.grok/config.toml) is the same HTTP Telnyx for hosts that load project Grok MCP.

Expand All @@ -73,8 +73,8 @@ Call `list_api_endpoints`. Real endpoint list → **5**. `401` / **10009** / con

Prefer the PhoneZero xAI MCP (8 tools). “No Grok xAI *package*” does **not** mean you cannot upload. Hosts are always `api.x.ai`.

- If `put_booking` is already listed → skip adding a connector.
- Else Customize → MCPs → **stdio** (not HTTP) name `xai`. Copy the `xai` block from [`plugins/phonezero/mcp.json`](plugins/phonezero/mcp.json) (same launcher is in [`plugins/phonezero-grok/.mcp.json`](plugins/phonezero-grok/.mcp.json)). Bind **real** values from secure fields: `XAI_API_KEY` or `PHONEZERO_CFG_XAI_API_KEY`, `PHONEZERO_FROM_NUMBER` or `PHONEZERO_CFG_FROM_NUMBER`, plus name / disclose from **1**. Do **not** leave literal `${…}` in env.
- If `put_task` or `put_booking` is already listed → skip adding a connector.
- Else Customize → MCPs → **stdio** (not HTTP) name `xai`. Copy the `xai` block from [`plugins/phonezero/mcp.json`](plugins/phonezero/mcp.json) (same launcher is in [`plugins/phonezero-grok/.mcp.json`](plugins/phonezero-grok/.mcp.json)). Bind **real** values from secure fields: `XAI_API_KEY` or `PHONEZERO_CFG_XAI_API_KEY`, `PHONEZERO_FROM_NUMBER` or `PHONEZERO_CFG_FROM_NUMBER`. Do **not** leave literal `${…}` in env.

Prove: `get_call_config` (`xai_key_wired`, `from_wired`, From last-4) then `ensure_collection` (name `PhoneZero bookings`).

Expand Down Expand Up @@ -122,24 +122,24 @@ xAI: `list_phone_numbers` → `register_byo_number` if the DID is not `byo_trunk

### 8. Calls

You already have SID, TeXML id, From, spoken name, disclose, and the Telnyx destination list. Read skill §§2–12 (plan-first, two attempts, `booked` only with a host confirmation in the transcript). Owner setup-test to **their own confirmed number** may skip the hours guard — restaurants may not. The call plan must show Spoken as and only dial countries on that Telnyx whitelist. Per-call they may still override the spoken name.
You already have SID, TeXML id, From, spoken name, disclose, and the Telnyx destination list. Read `phonezero-runtime` (plan-first, two attempts, `succeeded` / `booked` only with a live-person confirmation in the transcript). Match a shipped skill, or **interview into a `phonezero-task`** — do not ask them to paste a `SKILL.md` or write `~/.cursor/skills`. Owner setup-test to **their own confirmed number** may skip the hours guard — restaurants may not. The call plan must show Spoken as and only dial countries on that Telnyx whitelist. Per-call they may still override the spoken name. If they ask to save the shape as a template, pick memory or `put_template` and say where it went.

On explicit yes, in this order — do not resolve SID again:

1. `put_booking` — wait until processed
2. `invoke_api_endpoint` `calls_accounts_texml_calls` (skill Dial JSON; `account_sid` + `ApplicationSid` from session)
1. `put_task` (or `put_booking` alias) — wait until processed
2. `invoke_api_endpoint` `calls_accounts_texml_calls` (runtime Dial JSON; `account_sid` + `ApplicationSid` from session)
3. Poll `retrieve_calls_accounts_texml_calls`
4. `retrieve_recordings_json_calls_accounts_texml_recordings_json` (not the write-named twin)
5. `transcribe` — do not paste the audio URL
6. Classify, then `delete_booking`. Keep the Telnyx recording.
6. Classify, then `delete_booking` (live brief only). Keep the Telnyx recording.

---

## Cursor IDE — set this up

1. Read [`plugins/phonezero/skills/phonezero/SKILL.md`](plugins/phonezero/skills/phonezero/SKILL.md) in full. Installed plugin: `skills/phonezero/SKILL.md`. Commands: `/setup-phone-calling`, `/book-table`.
1. Read [`plugins/phonezero/skills/phonezero-runtime/SKILL.md`](plugins/phonezero/skills/phonezero-runtime/SKILL.md) and [`plugins/phonezero/skills/book-restaurant/SKILL.md`](plugins/phonezero/skills/book-restaurant/SKILL.md) in full. Commands: `/setup-phone-calling`, `/book-table`, `/book-restaurant`, `/confirm-business-hours`. Local skills: [`docs/SKILLS.md`](docs/SKILLS.md).
2. Install the **Cursor** package only: [`.cursor-plugin/marketplace.json`](.cursor-plugin/marketplace.json) → [`plugins/phonezero/`](plugins/phonezero/). Channels: Customize → **+ Add** this repo root; Cursor Marketplace; copy `plugins/phonezero/` to `~/.cursor/plugins/local/phonezero`; or Team Marketplace import. Telnyx is stdio `npx @telnyx/mcp` + Configure `TELNYX_API_KEY`. Do **not** add hosted-HTTP Telnyx in this package (SSE GET 404 tombstone).
3. Configure card: `TELNYX_API_KEY`, `PHONEZERO_FROM_NUMBER`, `XAI_API_KEY` (ZDR **off**). Name / disclose have defaults. Destinations are the Telnyx voice-profile whitelist, not this card. Do not put account SID, TeXML id, or collection id on the card. New conversation after install. Verify with `list_api_endpoints` and `get_call_config`.
3. Configure card: `TELNYX_API_KEY`, `PHONEZERO_FROM_NUMBER`, `XAI_API_KEY` (ZDR **off**) only. Spoken name and disclose are per-task (chat), not this card. Destinations are the Telnyx voice-profile whitelist. Do not put account SID, TeXML id, or collection id on the card. New conversation after install. Verify with `list_api_endpoints` and `get_call_config`.
4. Then run the skill Setup (Telnyx MCP + xAI MCP + Builder once). Session SID is MCP `list_billing_groups` → `organization_id` (not a `whoami` tool).

Human walkthrough: [`docs/SETUP.md`](docs/SETUP.md). `scripts/provision.sh` is developer-only on a personal machine — never here.
Expand All @@ -149,5 +149,5 @@ Human walkthrough: [`docs/SETUP.md`](docs/SETUP.md). `scripts/provision.sh` is d
## Both hosts

- Never commit or echo keys. Configure / Edit Values / MCP headers only — not the agent shell.
- Builder prompt is static. Brief each call with `phonezero-booking.json` in the xAI collection — no TeXML `<Say>`, no per-call Builder edit.
- Builder prompt is static. Brief each call with `phonezero-task.json` in the xAI collection — no TeXML `<Say>`, no per-call Builder edit. Re-paste `prompts/voice-agent.md` and `prompts/end_call.md` if the agent still searches `phonezero-booking.json`.
- An old chat missing new MCP tools is not a failure — new conversation after install.
6 changes: 3 additions & 3 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,19 @@
## Dev setup

1. Fork and clone. Do not put real keys or numbers in the tree.
2. Read [docs/SETUP.md](docs/SETUP.md). The skill, voice prompt, and inline TeXML template are the product; this repo is a Cursor plugin marketplace ([`.cursor-plugin/marketplace.json`](.cursor-plugin/marketplace.json) at the repo root, plugin at [`plugins/phonezero/`](plugins/phonezero/)).
2. Read [docs/SETUP.md](docs/SETUP.md) and [docs/SKILLS.md](docs/SKILLS.md). The runtime skill, first-party phone skills, voice prompt, and inline TeXML template are the product; this repo is a Cursor plugin marketplace ([`.cursor-plugin/marketplace.json`](.cursor-plugin/marketplace.json) at the repo root, plugin at [`plugins/phonezero/`](plugins/phonezero/)). Contributing a skill is **optional** — Cursor users can keep a folder in `~/.phonezero/skills` or `~/.cursor/skills`. Grok users interview into a `phonezero-task` and are not asked to PR.
3. Load the plugin locally per [Test plugins locally](https://cursor.com/docs/plugins#test-plugins-locally): copy `plugins/phonezero/` to `~/.cursor/plugins/local/phonezero` (`rsync -a plugins/phonezero/ ~/.cursor/plugins/local/phonezero/`), then Reload Window. Fill the Configure card (`TELNYX_API_KEY`, `PHONEZERO_FROM_NUMBER`, `XAI_API_KEY`) on a dedicated Telnyx account. Start a **new** conversation after install or a plugin update. Runtime: `POST https://api.x.ai/v1/stt` and Files; setup: `GET`/`POST`/`PATCH https://api.x.ai/v2/phone-numbers`. Never `source ~/.phonezero/env`. Use fixture numbers in docs and fixtures only (`+15555550100`-style). Add any new example number to [`scripts/privacy-phone-allowlist.txt`](scripts/privacy-phone-allowlist.txt) first.
4. Set a non-personal Git identity before you commit (`Function1st` + a `users.noreply.github.com` address). CI rejects personal mailbox authors.
```
git config user.name Function1st
git config user.email function1st@users.noreply.github.com
```
5. Run `scripts/setup-check.sh` after any TeXML, SIP, or Builder change.
6. CI must stay green: secret scan, `shellcheck` on `scripts/*.sh`, `node plugins/phonezero/scripts/xai-mcp.mjs --self-test`, `node plugins/phonezero/scripts/launch-xai-mcp.mjs --resolve-only`, `xmllint --noout` on `texml/*.xml` and `plugins/phonezero/texml/*.xml`, and `python3 scripts/privacy-check.py` (E.164 / US numbers, file-content emails, and commit-author emails).
6. CI must stay green: secret scan, `shellcheck` on `scripts/*.sh`, `node plugins/phonezero/scripts/xai-mcp.mjs --self-test`, `node plugins/phonezero/scripts/launch-xai-mcp.mjs --resolve-only`, `end_call.md` verbatim in `voice-agent.md`, `xmllint --noout` on `texml/*.xml` and `plugins/phonezero/texml/*.xml`, and `python3 scripts/privacy-check.py` (E.164 / US numbers, file-content emails, and commit-author emails).

## Regression suite

The persona checklist is the regression suite. After any change to `plugins/phonezero/prompts/voice-agent.md` or call-flow rules in `plugins/phonezero/skills/phonezero/SKILL.md`, re-run **all scenarios** in [docs/PERSONAS.md](docs/PERSONAS.md). Assert the spoken recap is correct each time. Prompt/TeXML lint and `setup-check.sh` are the cheap gates; personas are the quality gate.
The persona checklist is the regression suite. After any change to `plugins/phonezero/prompts/voice-agent.md` or call-flow rules in `plugins/phonezero/skills/phonezero-runtime/SKILL.md` / a first-party skill, re-run **all scenarios** in [docs/PERSONAS.md](docs/PERSONAS.md) (restaurant) and the hours scripts. The voice agent must not speak a recap; classify from the transcript. Prompt/TeXML lint and `setup-check.sh` are the cheap gates; personas are the quality gate. A first-party skill PR needs privacy-check + personas. Grok users are not asked to contribute.

## Releases

Expand Down
Loading