Skip to content
Draft
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
102 changes: 102 additions & 0 deletions LifeOS/install/skills/PhoneCalls/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
---
name: PhoneCalls
version: 1.0.0
description: "Place and follow up real phone calls to businesses through the Call4me remote MCP: reservations, appointments, availability and price questions, callbacks, and live handoff to the user. USE WHEN call a business, phone a restaurant, book by phone, call and ask, make a phone call for me, check on a call, did they call back, set up Call4me. NOT FOR personal numbers, emergency or premium rate numbers, telemarketing or surveys, or writing calendar events."
---

## Customization

**Before executing, check for user customizations at:**
`~/.claude/LIFEOS/USER/CUSTOMIZATIONS/SKILLS/PhoneCalls/`

If this directory exists, load and apply any PREFERENCES.md, configurations, or resources found there. These override default behavior. If the directory does not exist, proceed with skill defaults.


## 🚨 MANDATORY: Voice Notification (REQUIRED BEFORE ANY ACTION)

**You MUST send this notification BEFORE doing anything else when this skill is invoked.**

1. **Send voice notification**:
```bash
curl -s -X POST http://localhost:31337/notify \
-H "Content-Type: application/json" \
-d '{"message": "Running the WORKFLOWNAME workflow in the PhoneCalls skill to ACTION"}' \
> /dev/null 2>&1 &
```

2. **Output text notification**:
```
Running the **WorkflowName** workflow in the **PhoneCalls** skill to ACTION...
```

**This is not optional. Execute this curl command immediately upon skill invocation.**

# PhoneCalls

Places paid, real phone calls to businesses on the user's behalf through the Call4me remote MCP server (`https://call4.me/mcp`). A voice caller dials the business, asks back during the call when it needs a fact, can ring the user in for identity checks, and returns an outcome plus transcript. The live contract is https://call4.me/.well-known/agent-skills/call4me/SKILL.md and the calling rules are https://call4.me/rules. read again them when a tool or field here does not match what the server exposes.

## Tool contract

The published tool names are `call4me_get_balance`, `call4me_get_profile`, `call4me_save_profile`, `call4me_get_requirements`, `call4me_place_call`, `call4me_get_call`, `call4me_answer_question`, `call4me_connect_me`, `call4me_hang_up`, `call4me_list_calls`, `call4me_schedule_call`, `call4me_cancel_scheduled_call`, `call4me_add_funds`. MCP clients expose them under their own prefix, so list the connected server's tools and call the exposed names with the exposed schemas. No CLI wrapper exists on purpose: the MCP server is the interface.

## Workflow Routing

When routing, output: `Running the **WorkflowName** workflow in the **PhoneCalls** skill to ACTION...`

| Workflow | Trigger | File |
|----------|---------|------|
| **Setup** | "set up Call4me", "connect phone calling", "what number do you call from", first use with no authenticated server | `Workflows/Setup.md` |
| **Call** | "call the restaurant and book", "phone them and ask", "book this by phone", "call them Monday at 9" | `Workflows/Call.md` |
| **Check** | "did they call back", "what happened on that call", "check my call", "any update from the business" | `Workflows/Check.md` |

## Examples

**Example 1: Restaurant booking**
```
User: "Call <restaurant> and book a table for 4 Friday around 7, 6:30 to 8 is fine"
→ Invokes Call workflow
→ Confirms the location's number, checks balance right before the call, gets restaurant requirements, asks for any missing fields in one message
→ Places the call, polls until finished, answers open questions from what the user gave
→ Reports the outcome as the tool returned it (time, name on booking, any confirmation), or what is still unresolved
```

**Example 2: Service availability question**
```
User: "Ask <shop> if they can do an oil change on my car this week and what it costs"
→ Invokes Call workflow, picks the auto service category from the task, and passes it to get_requirements
→ Collects only the vehicle details get_requirements returns as required
→ Reports the quoted availability and price exactly as stated on the call
```

**Example 3: Checking a callback**
```
User: "Did the dentist ever call back about my appointment?"
→ Invokes Check workflow
→ Reads the original call (by id or via list_calls), including its callbacks and their outcomes
→ Reports one state (scheduled, in progress, goal resolved, goal unmet, callback pending, unresolved/expired, or call failed) with evidence from the tool result
```

## Gotchas

- **Calls cost money.** Check balance before every call and quote prices only from the returned balance/rate fields. Never call `add_funds` unprompted; if the user asks for it, hand them the checkout link and nothing more.
- **The published skill may not list `max_minutes`; the exposed schema decides.** If the exposed `place_call` schema (or `schedule_call`, only if its schema shows it too) offers it, use a talk time cap in whole minutes. Never put a currency amount into `max_minutes`. For a money budget, determine an affordable minute cap from the actual destination rate returned by `get_balance`; if the rate is unclear, ask for a time cap. Never exceed the user's limit or invent a spend authorization.
- **"Not calling yet" means no call was placed.** Ask the user exactly what the response lists, then retry. Do not report the call as in progress.
- **The balance `numbers` list is not the selected caller.** A listed number matching the business's country does not prove that number will dial, especially when several numbers or the user's own verified line exist. The caller is known only from the call's returned `calling_number`, or from an explicit user choice passed through a caller selection field the exposed schema actually has. Null `phone_number` with empty `numbers` is normal before the first call; never invent or predict a number.
- **Calls needing an account PIN cannot be scheduled.** Place them live.
- **`calling_number` ≠ the user's profile phone.** `calling_number` (per call, may differ by country) is what businesses and the user see as the caller. The profile `phone` is where Call4me rings the user. Mixing them up makes the user ignore the handoff ring.
- **`open_questions` are time sensitive.** The business is on the line waiting. Answer from facts the user already gave, or ask the user right away. Never make up an answer.
- **Voicemail or "we'll call you back" is not completion.** The callback reaches the call's `calling_number`, where Call4me can finish the task if the business calls back within 14 days. A call placed with `own_number` gave the business the user's own number, so the callback rings the user, not Call4me. Never promise completion; it depends on the callback and its actual outcome. Follow up via Check on the original call id.
- **Judge the outcome against the user's goal, not the telephony.** Answered or `finished` does not mean the goal was achieved. A clear answer to an information question is the goal met even when the answer is "no" (e.g. confirmed out of stock). No availability, a refusal, or a wrong business on a booking is an unmet goal with its specific reason, not a call failure. "Call failed" needs explicit telephony or tool failure evidence.
- **Missing tool, schema mismatch, or failed auth is an explicit error.** Report it and route to Setup; never pretend a call was placed or simulate a result.
- **Only call businesses the user wants to reach.** No personal numbers, emergency or premium rate numbers, telemarketing, surveys, collections, or pretending to be the user (https://call4.me/rules).
- **Transcripts can hold sensitive details.** Summarize what matters; keep transcript content out of logs, commits, and shared evidence.

## Execution Log

After completing any workflow, append a single JSONL entry:

```bash
echo '{"ts":"'$(date -u +%Y-%m-%dT%H:%M:%SZ)'","skill":"PhoneCalls","workflow":"WORKFLOW_USED","input":"8_WORD_SUMMARY","status":"ok|error","duration_s":SECONDS}' >> ~/.claude/LIFEOS/MEMORY/SKILLS/execution.jsonl
```

Replace `WORKFLOW_USED` with the workflow executed, `8_WORD_SUMMARY` with a brief input description (no phone numbers, names, or call ids), and `SECONDS` with approximate elapsed time. Log `status: "error"` if the workflow failed.
51 changes: 51 additions & 0 deletions LifeOS/install/skills/PhoneCalls/Workflows/Call.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
# Call

## Voice Notification

```bash
curl -s -X POST http://localhost:31337/notify \
-H "Content-Type: application/json" \
-d '{"message": "Running the Call workflow in the PhoneCalls skill to call a business"}' \
> /dev/null 2>&1 &
```

Running **Call** in **PhoneCalls**...

---

**Deliverable:** one paid call placed only on the user's explicit request, followed to `finished`, and reported in a line or two: what the result states, anything to note (confirmation number, arrival time), and what is still unresolved. Use the exposed tool names discovered in Setup; the names below are the published references.

## Before dialing (safety gate)

- The user explicitly asked to call this business. Inferring a call from context is not enough.
- Destination is a business the user wants to reach, the number is verified for the right location, and it is not a personal, emergency, or premium rate number (https://call4.me/rules).
- `call4me_get_balance` checked right before this call (and again before each later one). Quote cost only from the returned fields. Insufficient balance → tell the user; never call `add_funds` on your own.
- Time/cost cap: if the exposed `place_call` schema offers optional `max_minutes`, respect the user's stated time or cost limit, and ask for a cap if they gave none and one matters. The published skill may not list it; trust the exposed schema. Never invent a cap or authorize spend the user did not.
- Caller number: the balance `numbers` list shows assigned numbers, not the one that will dial; a country match proves nothing, especially with several numbers or the user's own verified line. The caller is known before dialing only if the user explicitly chose one and the exposed schema has a field to select it. Otherwise say it is not known yet and will be shown as soon as the call returns `calling_number`.
- Category picked from the task (the list is in the live source and tool schema), then passed to `call4me_get_requirements`. Ask for every missing required field in ONE message.
- `goal`, `details`, and `flexibility` come from the user. Never invent commitments (a deposit, a time, a membership) the user did not authorize.

## Place

`call4me_place_call` with `to`, `business`, `goal`, `category`, `details` (answers by field key), `flexibility`, and `max_minutes` when the exposed schema has it and a cap was set as above.

- "Not calling yet" → no call exists. Ask the user exactly what it lists, then retry.
- Show the returned `calling_number` as soon as the tool returns. On the first call, or when it differs from what the user has seen, suggest saving it as a Call4me contact.
- Before passing `connect_when` or `listen_in`, give a heads up that the user's phone may ring. If the caller is not established as above, say it may come from a number other than the ones they have seen, then show the actual `calling_number` the moment the call returns. Never pick one from the `numbers` list.

## Follow

Poll `call4me_get_call` with `wait_seconds: 30` until `finished`.

- `open_questions` → the business is waiting. Answer with `call4me_answer_question` from facts the user already gave, or ask the user right now. Never make one up.
- Handoff (identity check, user wants to talk) → read the call's actual `calling_number` from `call4me_get_call`, tell the user to expect a ring from that number, then `call4me_connect_me` (`mode: "listen"` to listen silently). They press 1 to join and press * or hang up to hand back. Never describe the profile phone as the number calling them.
- Call going nowhere or the user changed their mind → `call4me_hang_up`.

## Report

- Outcome only from the tool result, judged against the user's goal. "Answered" or `finished` is not the goal achieved. A clear answer to an information question is the goal met, even when the answer is negative. No availability, refusal, or wrong business is an unmet goal: state the specific reason. Call it a failed call only on explicit telephony or tool failure evidence.
- Voicemail or a promised callback → callback pending, not yet resolved. Say who the callback reaches: normally the call's `calling_number`, where Call4me answers within 14 days and can try to finish the task; if the call used `own_number`, the business was given the user's own number and the callback rings the user, not Call4me. Either way, completion depends on the business calling back and that call's actual outcome. Point to the Check workflow with the original call id.

## Scheduled calls

Only when the user asks for a later time. A call that needs an account PIN cannot be scheduled; place it live instead. Collect everything as above, then `call4me_schedule_call` with the same arguments plus `call_at` (business local time with `timezone`, or ISO with offset, within business hours). Nobody may be watching when it dials, so put every fact in `details` (and `facts`, only if the exposed schema has it) and everything the user authorized the caller to accept in `flexibility`. Tell the user when it will dial and give the returned `sched_` id; `call4me_cancel_scheduled_call` cancels it. Do not set up recurring polling; the user checks later via Check.
41 changes: 41 additions & 0 deletions LifeOS/install/skills/PhoneCalls/Workflows/Check.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# Check

## Voice Notification

```bash
curl -s -X POST http://localhost:31337/notify \
-H "Content-Type: application/json" \
-d '{"message": "Running the Check workflow in the PhoneCalls skill to check a call outcome"}' \
> /dev/null 2>&1 &
```

Running **Check** in **PhoneCalls**...

---

**Deliverable:** the current state of a call the user asked about, classified as exactly one state from the table below, with the evidence from the tool result behind that label. Use the exposed tool names discovered in Setup.

## Find the call

- Use the call id the user gives. Otherwise `call4me_list_calls` (scheduled calls first, then recent, newest first) and confirm which one they mean.
- `call4me_get_call` on the **original** call id. It lists callbacks and their outcomes; a `sched_` id shows when it dials and, once it has, the call itself.

## Still live

If the call has `open_questions` or needs a handoff, handle it exactly as in `Workflows/Call.md` § Follow.

## Classify

Read state from whatever the returned result actually contains; do not assume field names the schema does not show.

| State | Evidence required |
|-------|-------------------|
| Scheduled (not dialed) | A `sched_` id whose result shows a future dial time and no call yet |
| In progress | The call is not `finished` yet (may have `open_questions`) |
| Goal resolved | The outcome or a callback outcome states the user's goal was met: a booking with time and name given, or an information question clearly answered, even negatively (e.g. confirmed out of stock) |
| Goal unmet | The business clearly answered but the goal cannot be met as asked: no availability for a booking, refusal, wrong business, or similar. Report the specific reason, not "failed" |
| Callback pending | Voicemail left or callback promised, no callback outcome yet, still within 14 days. Say who it reaches: Call4me at `calling_number`, or the user's own phone if the call used `own_number` (Call4me then cannot finish it) |
| Unresolved / expired | Finished without a clear answer to the goal, including a callback window that passed with nothing. Not proof the task failed |
| Call failed | Explicit telephony or tool failure evidence (e.g. the result reports the call could not connect, or the tool returned an error). Report that error as given |

Never mark a booking or appointment confirmed without that evidence, and do not write calendar events (out of scope). Summarize transcripts. Keep transcript content and phone numbers out of logs and public evidence.
43 changes: 43 additions & 0 deletions LifeOS/install/skills/PhoneCalls/Workflows/Setup.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# Setup

## Voice Notification

```bash
curl -s -X POST http://localhost:31337/notify \
-H "Content-Type: application/json" \
-d '{"message": "Running the Setup workflow in the PhoneCalls skill to connect Call4me"}' \
> /dev/null 2>&1 &
```

Running **Setup** in **PhoneCalls**...

---

**Deliverable:** an authenticated Call4me MCP connection whose exposed tool names are known, plus a user who knows which number calls will come from and how a handoff ring works.

## Connect

Reuse an existing authenticated `call4me` server if its tools are already listed. Otherwise give the user the command; never edit global MCP config on your own.

```bash
claude mcp add --scope project --transport http call4me https://call4.me/mcp
```

The user then runs `/mcp`, picks `call4me`, and signs in. For other clients, point to the Connect section of the live source: https://call4.me/.well-known/agent-skills/call4me/SKILL.md

## Verify

- List the server's tools and note the exposed names and schemas. Later workflows call those names, not the published `call4me_*` names verbatim.
- Call the exposed `get_balance`. Auth failure or missing tool → report the exact error and stop; do not claim setup succeeded.
- Show the returned balance and rate fields as given. No credits → tell the user; offer `add_funds` only if they ask, and only pass on the checkout link.

## Numbers

- Show every entry in the returned `numbers` list with its country as the numbers assigned to the account, and suggest saving them as contacts named Call4me. Say plainly that this list does not determine which number dials a given call, especially with several numbers or the user's own verified line.
- `phone_number` null and `numbers` empty → the free US number is assigned on the first call and will be shown then. Never invent or promise one.
- The caller for a call is known only from that call's returned `calling_number`, or from an explicit user choice passed through a caller selection field the exposed schema actually has. Never present `phone_number` or a country match in the list as the selected caller.
- Explain that the profile `phone` is a different number: it is where Call4me rings the user if a business needs them (press 1 to join; press * or hang up to hand back).

## Profile

Reading the existing profile with `get_profile` is fine. Do not run a broad onboarding questionnaire: collect details per call, only what that task requires. Persist with `save_profile` only when the user asks or agrees. Never ask for or save Social Security numbers, card numbers, or passwords.