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
2 changes: 1 addition & 1 deletion .cursor-plugin/marketplace.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
},
"metadata": {
"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"
"version": "0.4.2"
},
"plugins": [
{
Expand Down
12 changes: 6 additions & 6 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -97,26 +97,26 @@ Use that `organization_id`. (Developer curl `GET /v2/whoami` is the same value.
2. **`PHONEZERO_TEXML_APP_ID`** — `invoke_api_endpoint` with `endpoint_name` `list_texml_applications` and args:

```json
{ "filter": { "friendly_name": "PhoneZero" }, "jq_filter": ".data[] | {id, friendly_name}" }
{ "filter": { "friendly_name": "PhoneZero" }, "jq_filter": ".data[] | {id, friendly_name, outbound}" }
```

If a row named `PhoneZero` exists, use its `id`. If not, create it in **7**.

3. **From** — `get_call_config`. If `from_wired` is false, Telnyx `list_phone_numbers` for the DID on the PhoneZero TeXML app.

4. **Destinations** — read Telnyx, do not invent a PhoneZero field. `invoke_api_endpoint` `list_outbound_voice_profiles` args:
4. **Destinations** — the outbound voice profile **attached to the PhoneZero TeXML app**, whatever it is named. Do **not** invent a PhoneZero field. Do **not** filter by `PhoneZero US-only`. Read `outbound.outbound_voice_profile_id` from the TeXML app (step 2). Then `invoke_api_endpoint` `list_outbound_voice_profiles` args:

```json
{ "filter": { "name": { "contains": "PhoneZero" } }, "jq_filter": ".data[] | select(.name==\"PhoneZero US-only\") | {id, name, whitelisted_destinations}" }
{ "jq_filter": ".data[] | {id, name, whitelisted_destinations}" }
```

Show the codes in chat: “Telnyx outbound voice profile **PhoneZero US-only** currently allows: … . Change this in Telnyx Mission Control → Voice → Outbound voice profiles (or ask me to PATCH). It is not a PhoneZero plugin setting.” Only PATCH if they ask to add/remove countries. Do not give legal advice.
Pick the row whose `id` matches that profile id. Show the codes in chat: “Telnyx outbound voice profile **{name}** currently allows: … . Change this in Telnyx Mission Control → Voice → Outbound voice profiles (or ask me to PATCH). It is not a PhoneZero plugin setting.” If the TeXML app has no profile yet: same list, no name filter — one profile → use it; several → ask which; none → create in **7**. Only PATCH if they ask to add/remove countries. Do not give legal advice.

### 7. Provision only if missing

If profile **PhoneZero US-only**, TeXML app **PhoneZero**, and the DID is already attached: skip create. Do **not** overwrite an existing `whitelisted_destinations` unless they asked to change countries. Approve each credentialed write. Never echo keys.
If a TeXML app **PhoneZero** already has an outbound voice profile attached and the DID is on that app: skip create. Use that profile’s name and whitelist as-is. Do **not** overwrite an existing `whitelisted_destinations` unless they asked to change countries. Approve each credentialed write. Never echo keys.

If something is missing, skill **Setup** via MCP names (not REST path names): `list_outbound_voice_profiles` / `create_outbound_voice_profiles` (name `PhoneZero US-only`, `traffic_type=conversational`, `service_plan=global`, `usage_payment_method=rate-deck`, `whitelisted_destinations` default `["US"]` on create, `daily_spend_limit="5.00"`, `daily_spend_limit_enabled=true`); `create_texml_applications` / `update_texml_applications` (`voice_url` = `https://raw.githubusercontent.com/function1st/PhoneZero/main/texml/inbound.xml`, verify HTTP 200, `voice_method=get`); `update_phone_numbers` `connection_id` = TeXML app id.
If something is missing, skill **Setup** via MCP names (not REST path names): `list_outbound_voice_profiles` (no name filter) / `create_outbound_voice_profiles` only when they have **no** profile (create-default name `PhoneZero` unless they pick another, `traffic_type=conversational`, `service_plan=global`, `usage_payment_method=rate-deck`, `whitelisted_destinations` default `["US"]` on create, `daily_spend_limit="5.00"`, `daily_spend_limit_enabled=true`); `create_texml_applications` / `update_texml_applications` (`voice_url` = `https://raw.githubusercontent.com/function1st/PhoneZero/main/texml/inbound.xml`, verify HTTP 200, `voice_method=get`, attach the chosen profile); `update_phone_numbers` `connection_id` = TeXML app id.

xAI: `list_phone_numbers` → `register_byo_number` if the DID is not `byo_trunk` → `attach_agent` onto **your** DID. Skip Builder if they said already set up.

Expand Down
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,13 +76,13 @@ Fallback only if stdio will not start: Bearer on `https://api.x.ai/v1` and `/v2`
Keep in session. Do not look these up after yes.

1. `TELNYX_ACCOUNT_SID` — `invoke_api_endpoint` `list_billing_groups` args `{ "jq_filter": "[.data[].organization_id] | unique" }`
2. `PHONEZERO_TEXML_APP_ID` — `invoke_api_endpoint` `list_texml_applications` args `{ "filter": { "friendly_name": "PhoneZero" }, "jq_filter": ".data[] | {id, friendly_name}" }`
2. `PHONEZERO_TEXML_APP_ID` — `invoke_api_endpoint` `list_texml_applications` args `{ "filter": { "friendly_name": "PhoneZero" }, "jq_filter": ".data[] | {id, friendly_name, outbound}" }`
3. From — `get_call_config` (last-4 in chat)
4. Destinations — `invoke_api_endpoint` `list_outbound_voice_profiles` (name `PhoneZero US-only` → `whitelisted_destinations`). Show the list. This is **Telnyx** (Mission Control → Voice → Outbound voice profiles), not a PhoneZero field. PATCH only if they ask to add/remove countries.
4. Destinations — the profile **attached to the PhoneZero TeXML app** (any name). `list_texml_applications` → `outbound.outbound_voice_profile_id`, then `invoke_api_endpoint` `list_outbound_voice_profiles` `{ "jq_filter": ".data[] | {id, name, whitelisted_destinations}" }` and pick that `id`. **No name filter.** Show the actual name + codes. This is **Telnyx** (Mission Control → Voice → Outbound voice profiles), not a PhoneZero field. PATCH only if they ask to add/remove countries.

### 7. Provision only if missing

If profile **PhoneZero US-only**, app **PhoneZero**, and the DID is attached: skip create. Do not overwrite an existing whitelist unless they asked. Else skill Setup via MCP names: `list_outbound_voice_profiles` / `create_outbound_voice_profiles` (create default `["US"]`), `create_texml_applications` (`voice_url` = `https://raw.githubusercontent.com/function1st/PhoneZero/main/texml/inbound.xml`, verify 200), `update_phone_numbers` `connection_id`. xAI: `register_byo_number` + `attach_agent` on **your** DID if needed.
If app **PhoneZero** already has an outbound voice profile attached and the DID is on that app: skip create. Use that profile’s name and whitelist as-is. Do not overwrite an existing whitelist unless they asked. Else skill Setup via MCP names: `list_outbound_voice_profiles` (no name filter) / `create_outbound_voice_profiles` only if they have none (create-default name `PhoneZero` unless they pick another; whitelist default `["US"]` on create), `create_texml_applications` (`voice_url` = `https://raw.githubusercontent.com/function1st/PhoneZero/main/texml/inbound.xml`, verify 200, attach the chosen profile), `update_phone_numbers` `connection_id`. xAI: `register_byo_number` + `attach_agent` on **your** DID if needed.

### 8. First call

Expand Down Expand Up @@ -146,7 +146,7 @@ Keys live in Configure / MCP headers only — not the agent shell.

This sample ships with:

- **Destinations:** Telnyx outbound voice profile **PhoneZero US-only**, `whitelisted_destinations` default `US` on create (Mission Control → Voice → Outbound voice profiles)
- **Destinations:** whichever Telnyx outbound voice profile is attached to the PhoneZero TeXML app (`whitelisted_destinations`; default `US` only when PhoneZero **creates** a new profile). Name and country list are the user’s. Mission Control → Voice → Outbound voice profiles.
- **AI disclosure:** default **on** in each task (`disclose_ai`) and in the one-time Builder paste
- **Spoken name:** `PhoneZero`
- **Recording:** dual-channel Telnyx recording on every call; the opener says the call is on a recorded line
Expand Down
9 changes: 5 additions & 4 deletions docs/SETUP.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ The Telnyx MCP cannot run until `TELNYX_API_KEY` is saved as a plugin variable.

The Configure card must match [`plugins/phonezero/.cursor-plugin/plugin.json`](../plugins/phonezero/.cursor-plugin/plugin.json). Cursor injects those values into the plugin MCP processes only — **not** the agent shell. After save, start a **new** conversation and check Telnyx MCP plus the PhoneZero xAI MCP (`get_call_config`). Never `source ~/.phonezero/env` in chat. Never ask the agent to `curl` with `$XAI_API_KEY`.

**Defaults.** Destinations are the Telnyx profile **PhoneZero US-only** (`whitelisted_destinations`, default `US` on create) — Mission Control → Voice → Outbound voice profiles. PhoneZero has no plugin field for this. AI disclosure defaults on. Calls are recorded. You are solely responsible for lawful use; see [DISCLAIMER.md](../DISCLAIMER.md).
**Defaults.** Destinations are the Telnyx outbound voice profile **attached to the PhoneZero TeXML app** (`whitelisted_destinations`; default `US` only when a new profile is created). Name and country list are yours — Mission Control → Voice → Outbound voice profiles. PhoneZero has no plugin field for this. AI disclosure defaults on. Calls are recorded. You are solely responsible for lawful use; see [DISCLAIMER.md](../DISCLAIMER.md).

To add a country (e.g. Japan): change that Telnyx whitelist in Mission Control, or ask the Bot to PATCH `whitelisted_destinations`. Telnyx rejects calls outside that list.

Expand All @@ -57,7 +57,7 @@ To add a country (e.g. Japan): change that Telnyx whitelist in Mission Control,
| `PHONEZERO_FROM_NUMBER` | plugin variable | A | Telnyx US DID (E.164): outbound caller ID and SIP bridge target (`sip:{PHONEZERO_FROM_NUMBER}@sip.voice.x.ai;transport=tls`). |
| `XAI_API_KEY` | plugin variable (secret) | A | From a team with **ZDR off**. Injected into the PhoneZero xAI MCP only (Files, collections, STT, phone-numbers). Not in the agent shell. |
| Spoken name / disclose | per-task (`phonezero-task.json`) | chat | `spoken_name` and `disclose_ai`. Default PhoneZero / true. Not on the Configure card. `{disclosure_clause}` is pasted once into the Builder prompt. |
| Destinations | Telnyx voice profile **PhoneZero US-only** | Telnyx | `whitelisted_destinations`. Mission Control → Voice → Outbound voice profiles. Not a PhoneZero plugin variable. |
| Destinations | Telnyx voice profile on the PhoneZero TeXML app | Telnyx | `whitelisted_destinations` of whatever profile is attached (`outbound.outbound_voice_profile_id`). Any name, any country list. Mission Control → Voice → Outbound voice profiles. Not a PhoneZero plugin variable. |
| `TELNYX_ACCOUNT_SID` | resolved in session | B | TeXML REST account SID. MCP: `list_billing_groups` → `data[].organization_id` (no `whoami` tool). Developer curl: `GET /v2/whoami` → `data.organization_id`. Not on the Configure card. |
| `PHONEZERO_TEXML_APP_ID` | resolved in session | B | TeXML application SID. Not on the Configure card. |
| `PHONEZERO_XAI_COLLECTION_ID` | resolved in session | B | Collection for `phonezero-task.json` and optional `phonezero-template-*.json`. Find-or-create name `PhoneZero bookings`. |
Expand All @@ -68,7 +68,7 @@ Account, KYC, and the DID stay manual. Everything after that is API-automatable.

12. **End-user path.** Run `/setup-phone-calling` (or ask *"Set up phone calling."*). The first message is a vendor checklist — stop if anything is missing (including ZDR off). Then the Bot uses the Telnyx hosted MCP (`list_api_endpoints` → `get_api_endpoint_schema` → `invoke_api_endpoint`) and `XAI_API_KEY` for xAI Files + phone-numbers. Approve each credentialed step. It will:
- Resolve `TELNYX_ACCOUNT_SID` from Telnyx MCP `list_billing_groups` → `data[].organization_id` (developer curl: `GET /v2/whoami`).
- Find-or-create outbound voice profile **PhoneZero US-only**: `traffic_type=conversational`, `service_plan=global`, `usage_payment_method=rate-deck` (the only accepted combo today; error 10015 otherwise), `whitelisted_destinations` default `["US"]` on create, `daily_spend_limit="5.00"`, `daily_spend_limit_enabled=true`. If the profile exists, keep its current whitelist. PATCH only if the user asks to add or remove countries.
- Resolve the outbound voice profile **already attached** to the PhoneZero TeXML app (any name). If none: use the account’s only profile, or ask which of several to attach, or create one (create-default name `PhoneZero` unless they pick another; `traffic_type=conversational`, `service_plan=global`, `usage_payment_method=rate-deck` the only accepted combo today; error 10015 otherwise; `whitelisted_destinations` default `["US"]` on create; `daily_spend_limit="5.00"`, `daily_spend_limit_enabled=true`). Keep an existing whitelist and name. PATCH only if the user asks to add or remove countries.
- Find-or-create TeXML application **PhoneZero** with `voice_url` = [`texml/inbound.xml`](../texml/inbound.xml) (default raw URL: `https://raw.githubusercontent.com/function1st/PhoneZero/main/texml/inbound.xml`) and `voice_method=get`. **Verify that URL returns HTTP 200 before writing it.** That URL is fetched **only for inbound** calls to the DID. Outbound calls carry inline `Texml` (template [`plugins/phonezero/texml/bridge.xml`](../plugins/phonezero/texml/bridge.xml)).
- Attach the DID: `PATCH /v2/phone_numbers/{phone_number_id}` `{"connection_id":"<texml_app_id>"}`.
- Register the DID with xAI if it is not already registered: `POST https://api.x.ai/v2/phone-numbers` `{"name":"PhoneZero","phoneNumber":"+1…","origin":"byo_trunk"}`.
Expand All @@ -79,6 +79,7 @@ Account, KYC, and the DID stay manual. Everything after that is API-automatable.
- `XAI_API_KEY` — register the DID with xAI. The SIP bridge target is `PHONEZERO_FROM_NUMBER`.
- `PHONEZERO_XAI_AGENT_ID` — attach a Builder agent (step 15) via the fieldMask PATCH.
- `PHONEZERO_ALLOWED_COUNTRIES` — developer-only env. If set, writes that ISO list onto the Telnyx profile `whitelisted_destinations`. If unset, create uses `["US"]` and an existing profile is left as-is. Not a plugin variable.
- `PHONEZERO_PROFILE_NAME` — developer-only env. Pin a Telnyx outbound voice profile by name (find or create). If unset: use the profile already attached to the PhoneZero TeXML app, or the only profile on the account. Not a plugin variable.

14. **Create the Voice Agent (once, in Builder).** There is no public create API (`/v1/agents` is not enabled). Preferred: the Bot opens [https://console.x.ai](https://console.x.ai) with your approved session. Fallback: you do this and give the Bot the `agentId`. On the **same ZDR-off team** as the API key:

Expand Down Expand Up @@ -107,7 +108,7 @@ Account, KYC, and the DID stay manual. Everything after that is API-automatable.
- Telnyx auth works (a real `tools/call` succeeds);
- `get_call_config` returns From last-4 (or Telnyx `list_phone_numbers` shows the DID);
- a TeXML application named PhoneZero exists and the DID's `connection_id` matches it;
- Telnyx outbound voice profile **PhoneZero US-only** `whitelisted_destinations` is listed (change countries there, not on the Configure card);
- the outbound voice profile **attached to the PhoneZero TeXML app** is listed (`name` + `whitelisted_destinations`; change countries there, not on the Configure card);
- xAI MCP `ensure_collection` finds **PhoneZero bookings** (403 + "Zero Data Retention" → wrong team);
- xAI MCP `list_phone_numbers` shows the DID as `origin=byo_trunk` with an `agentId` (missing agent is a warning — finish step 14–15).
20. **Optional developer path.** [`scripts/setup-check.sh`](../scripts/setup-check.sh) is developer-only. Run it on a **personal machine that is allowed to hold keys**, never on the Bot computer. It verifies Telnyx auth, that the number is on the account, that the TeXML application exists, and that the DID is attached to that app. If `XAI_API_KEY` is set, it also checks xAI BYO registration. It does **not** verify that the agent answers.
Expand Down
2 changes: 1 addition & 1 deletion plugins/phonezero-grok/.grok-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "phonezero-grok",
"displayName": "PhoneZero (Grok)",
"version": "0.4.1",
"version": "0.4.2",
"description": "Grok Bot: phone skills (restaurant first). Telnyx hosted HTTP + PhoneZero xAI stdio. Edit Values for keys. Do not click Authenticate on Telnyx.",
"author": {
"name": "Function1st"
Expand Down
2 changes: 1 addition & 1 deletion plugins/phonezero-grok/commands/setup-phone-calling.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ After the vendor gate, **ask in chat and wait** before wiring. Do not silently k
- Spoken name (default PhoneZero) — what the callee hears
- AI disclaimer ON or OFF — they may turn it OFF

**Do not ask for destination countries.** Read them from Telnyx after the HTTP MCP is proven: `list_outbound_voice_profiles` → profile **PhoneZero US-only** → `whitelisted_destinations`. Show the list. That setting is Telnyx Mission Control → Voice → Outbound voice profiles. PATCH only if they ask to add or remove countries. Do not give legal advice.
**Do not ask for destination countries.** Read them from Telnyx after the HTTP MCP is proven: the outbound voice profile **attached to the PhoneZero TeXML app** (any name) → `whitelisted_destinations`. `list_outbound_voice_profiles` with **no name filter**; pick the id on that TeXML app. Show the actual name + codes. That setting is Telnyx Mission Control → Voice → Outbound voice profiles. PATCH only if they ask to add or remove countries. Do not give legal advice.

Do not search Telnyx MCP for `whoami`. After Telnyx HTTP + xAI stdio are proven, resolve session ids immediately:

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ Computer-use. Drive the UI. Do these steps **in order**. Same playbook: repo-roo
5. If `put_task` / `put_booking` is missing: add stdio `xai` from [`plugins/phonezero/mcp.json`](../../../phonezero/mcp.json) with **real** env (not `${…}`). Then `get_call_config` + `ensure_collection`. If they said xAI is already set up, do not open the Builder. Ignore the wizard number. If they turned disclaimer OFF and Builder already exists, or the agent still searches `phonezero-booking.json`, re-paste `prompts/voice-agent.md` and `prompts/end_call.md` once.
6. Session ids **now**, before any plan:
- `TELNYX_ACCOUNT_SID` = `invoke_api_endpoint` `list_billing_groups` args `{ "jq_filter": "[.data[].organization_id] | unique" }`
- `PHONEZERO_TEXML_APP_ID` = `invoke_api_endpoint` `list_texml_applications` args `{ "filter": { "friendly_name": "PhoneZero" }, "jq_filter": ".data[] | {id, friendly_name}" }`
- `PHONEZERO_TEXML_APP_ID` = `invoke_api_endpoint` `list_texml_applications` args `{ "filter": { "friendly_name": "PhoneZero" }, "jq_filter": ".data[] | {id, friendly_name, outbound}" }`
- From = `get_call_config`
- Destinations = `list_outbound_voice_profiles` → **PhoneZero US-only** → `whitelisted_destinations`. Show them. This is Telnyx Mission Control → Voice → Outbound voice profiles, not a PhoneZero field. PATCH only if they ask to change countries.
- Destinations = TeXML app `outbound.outbound_voice_profile_id` → `list_outbound_voice_profiles` (no name filter) → that row’s `name` + `whitelisted_destinations`. Show them. This is Telnyx Mission Control → Voice → Outbound voice profiles, not a PhoneZero field. PATCH only if they ask to change countries.
7. Provision only if profile / TeXML app / DID attach is missing. Do not overwrite an existing whitelist unless they asked.
8. On yes: `put_task` (or `put_booking` alias) → `calls_accounts_texml_calls` (session ids, do not look up SID again) → poll → recordings → `transcribe` → classify → `delete_booking` (live brief only).
2 changes: 1 addition & 1 deletion plugins/phonezero/.cursor-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "phonezero",
"displayName": "PhoneZero",
"version": "0.4.1",
"version": "0.4.2",
"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.",
"author": {
"name": "Function1st"
Expand Down
Loading
Loading