Demo — Hina cold-open

The instant the server starts, Hina texts first with a fixed opener, then hands off to the live agent.

4 min
On this page
  1. The scripted opener
  2. What happens, start to finish
  3. How to run it
  4. Knobs
  5. Why hardcoded (vs. the model opener)
  6. During the demo
  7. What I changed

The scripted opener

Three bubbles, sent one at a time with the normal human typing choreography (dots → "type" → send → beat → next). Modeled on the north-star anna cold-open.

hina
hey
i heard you want to learn a language
which one?
japanese
hina
へえ 本当??
what made you want to learn japanese?

dashed = hardcoded · everything after is the live model picking up the thread.

The exact bubbles live in one constant, DEMO_BUBBLES in agent/core/demo-opener.mjs. Drop the 'hey' line for exactly the two you asked for. I added it because the north-star opens with a standalone greeting before the question — it reads warmer — but it's a one-line edit either way.

What happens, start to finish

1
You run npm start (+ npm run messages).
The watchdog launches Messages.app with the dylib injected; the helper dials into the server.
2
On first helper connect, the opener fires once.
Guarded by a latch so a watchdog relaunch can't re-fire it. Waits ~1.5s after connect so it doesn't fire the literal instant Messages finishes booting.
3
The demo learner is reset to a clean slate.
Wipes +15109358199 back to fresh onboarding (keeps the chatGuid) so the demo is identical every run — the live agent behaves like a first conversation, not someone already onboarded.
4
The three bubbles send with typing dots.
Same sendBubbles path + sending latch as a real reply, so it looks human and can't collide with an inbound turn or the proactive sweep.
5
The opener is seeded into history as Hina's turn.
So when you reply "japanese", the model continues from the opener — it won't re-greet or re-ask "which one?". It drops Japanese immediately and runs the rest of onboarding live.

How to run it

Two terminals, from agent/. DEMO=1 is already set in .env.

# terminal 1 — launch Messages with the helper injected
npm run messages

# terminal 2 — start the server (fires the cold-open on connect)
npm start

Watch the demo phone: heyi heard you want to learn a languagewhich one? arrive within a couple seconds. Reply japanese and the live agent takes over. To re-run a clean demo, just restart the server — the reset makes it repeatable.

Heads up: DEMO=1 means every npm start now cold-opens and wipes that learner's memory. Set DEMO=0 (or delete the line) to go back to normal operation.

Knobs

Env / locationDefaultWhat it does
DEMO1 (in .env)Master switch. 0/off disarms the cold-open entirely.
DEMO_HANDLEfirst ALLOW_FROMWho to open on. Unset → +15109358199.
DEMO_RESET1Reset the learner to fresh onboarding each run. 0 = demo against existing memory.
DEMO_BUBBLES (code)3 bubblesThe scripted lines, in core/demo-opener.mjs. Edit to change the words.

Why hardcoded (vs. the model opener)

The normal opener

Model-generated, reply-only.

"hey, it's hina" came from the onboarding prompt in agent.mjs — it varies run to run and only fires after the learner texts in. Proactive can re-poke a quiet thread but won't script exact words or cold-open a fresh one.

The demo opener

Fixed, fires first, deterministic.

Same bubbles every run, on a clean slate, the second Messages connects. No model call for the opener — just the scripted lines, then the live agent owns everything after. Exactly what a live demo needs.

During the demo

What I changed

FileChange
agent/core/demo-opener.mjsnewThe cold-open: scripted bubbles, fire-once latch, clean-slate reset, history seed.
agent/core/memory.mjseditAdded resetLearner(handle) — fresh onboarding, keeps chatGuid.
agent/config.mjseditAdded the demo config block (enabled / handle / reset).
agent/server.mjseditWired startDemoOpener(...) into main() after the proactive sweep.
agent/.enveditDEMO=1 to arm it.

Smoke-tested: config parses, modules import, the opener registers its connect handler without sending. The real send needs Messages.app connected — that's the demo itself.