nc://phone-call

v0.1.0Pinned
ThomSharktools/nodecode-phone-call: An approved outbound phone call with an AI disclosure first, a recorded answer, and its transcript. - a rewrite of winchxyz/hermes-phone (MIT), credited to winchxyz58f448e· 10 Nov1 commit
test1 filebrief.lisp4.5 KBcell.lisp13.5 KBsafety.lisp16.5 KBstore.lisp7.7 KBtools.lisp14.1 KBwire.lisp8.9 KB

nodecode-phone-call

Phone Call: an approved outbound phone call with an AI disclosure, and what came of it. A Nodecode rewrite of winchxyz/hermes-phone by winchxyz.

The agent writes a brief, you approve it, and only then is anything dialed. The callee hears an AI disclosure first and then the message you approved. You get the brief back, the state of the call, the thing it said, and — when the call was recorded — a link to what the other side answered.

Get it

Tell the agent "add the phone-call cell", or run nodecode add nc://phone-call.

Set it up

A real call needs a Twilio account and a number of your own. In /settings, or in the phone-call section of config.jsonc:

"phone-call": {
  "account_sid": "AC...",              // console.twilio.com
  "auth_token": "...",                  // the account's auth token
  "from_number": "+14155550100",       // a number on that account
  "allowed_numbers": ["+14155550123"], // what may be dialed; empty dials nothing
  "user_name": "Ada",                  // who the call is on behalf of
  "daily_call_limit": 10,
  "max_duration_s": 300
}

/phone-call check says which settings are set (names only, never a value), how the allowlist stands, and whether calls are real or dry runs. With no Twilio account every call is a dry run: it is prepared, approved and reported, nothing is dialed, and the cell says so.

The allowlist is the safety valve and it fails closed: allowed_numbers empty means no number may be dialed at all, and allow_all: true is the developer's explicit way past it. NODECODE_PHONE_CALL_ALLOWLIST, a comma-separated list in the environment, is read too.

Quiet hours are off by default, as in the original. Turn them on with enforce_quiet_hours: true and callee_utc_offset (hours from UTC): calls outside 08:00 to 21:00 callee time are then refused unless the brief says override_quiet_hours.

Use it

"Call Bella Napoli on +14155550123 and book a table for four on Friday at eight; my name is on the booking."

The agent prepares the brief with phone_prepare and shows you the card: who, which number and where it came from, the goal, the exact opening line, what the call will and will not share, the limits, and the one command that dials.

Command What it does
/phone-call approve <id> approve this call and place it: one use, fifteen minutes
/phone-call cancel <id> cancel an unapproved card
/phone-call status [<id>] where the call stands, or the recent log
/phone-call transcript <id> what the call said, and the recording link
/phone-call consent <id> <field> allow one sensitive field on one call
/phone-call log [<n>] the recent calls
/phone-call check which settings are set, and how calls go out
Tool Use
phone_prepare write the brief and the approval card; never dials
phone_call dial a call you approved, by its id
phone_status one call's state and result, or the log
phone_transcript what was said, and the recording of the answer

(help :phone-call) is the manual the model reads before it reaches for any of them.

What the original taught, kept here

Nothing is dialed without the operator's approval for that exact call, valid fifteen minutes and usable once. Emergency numbers (911, 112, 999, 000, 110, 119, 100 to 103 and the rest of the original's list), premium-rate numbers and short codes are refused before and again at dial time. One call is live at a time, the daily limit and the sixty-second gap to one number hold, and an unanswered or failed call is never redialed on its own. A fact under a sensitive name — card, address, ID number, bank, email, phone — is withheld from the brief unless the operator grants that field for that call, and a cvv, a password or a one-time code is never shared, consent or not. The AI disclosure is the first sentence of every call, because Twilio speaks it first.

Not ported

The original's FastAPI bridge: Twilio Media Streams carrying a realtime voice session, the live transcript of both legs, and the mid-call question card that put the callee on hold and asked the operator in chat. A cell has no public URL to give Twilio, so the call speaks a TwiML document — the disclosure, the approved message, an optional recording of the answer, goodbye — and the other side's words are heard through Twilio's recording rather than transcribed. The seven settings, the safety rules and the four tools are the original's; the SQLite file shared by two processes is a table in this one.

Test

From the cells repository root:

.github/test-cell.sh tools/nodecode-phone-call

Every request is a stubbed nlk:http and the clock is a constant, so nothing reaches Twilio, needs an account or touches the network.

MIT licensed, as the original is.

Add nc://phone-call

  1. 1

    Install Nodecode

    Once per machine.

    $ curl -fsSL https://nodecode.ai/install.sh | sh
  2. 2

    Add the cell

    Pinned at 58f448e.

    $ nodecode add nc://phone-call

    Or tell the agent add the phone call cell.