@gonzih/meet-the-one-ai 1.0.0 → 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.env.example +12 -35
- package/SPEC.md +202 -0
- package/USER_SPEC.md +201 -0
- package/package.json +10 -3
- package/src/agent.ts +34 -14
- package/src/bot.ts +123 -13
- package/src/jobs/introduce.ts +176 -0
- package/src/jobs/matcher.ts +134 -0
- package/src/lib/bluebubbles.ts +91 -0
- package/src/lib/intake.ts +66 -0
- package/src/lib/logger.ts +14 -0
- package/src/mcp-server-matcher.ts +228 -0
- package/src/mcp-server.ts +137 -111
- package/tsconfig.json +6 -1
- package/dist/agent.d.ts +0 -2
- package/dist/agent.d.ts.map +0 -1
- package/dist/agent.js +0 -114
- package/dist/agent.js.map +0 -1
- package/dist/api/routes/auth.d.ts +0 -2
- package/dist/api/routes/auth.d.ts.map +0 -1
- package/dist/api/routes/auth.js +0 -79
- package/dist/api/routes/auth.js.map +0 -1
- package/dist/api/routes/identity.d.ts +0 -2
- package/dist/api/routes/identity.d.ts.map +0 -1
- package/dist/api/routes/identity.js +0 -92
- package/dist/api/routes/identity.js.map +0 -1
- package/dist/api/routes/text-submission.d.ts +0 -2
- package/dist/api/routes/text-submission.d.ts.map +0 -1
- package/dist/api/routes/text-submission.js +0 -56
- package/dist/api/routes/text-submission.js.map +0 -1
- package/dist/api/webhooks/twilio.d.ts +0 -2
- package/dist/api/webhooks/twilio.d.ts.map +0 -1
- package/dist/api/webhooks/twilio.js +0 -144
- package/dist/api/webhooks/twilio.js.map +0 -1
- package/dist/api/webhooks/vapi.d.ts +0 -2
- package/dist/api/webhooks/vapi.d.ts.map +0 -1
- package/dist/api/webhooks/vapi.js +0 -177
- package/dist/api/webhooks/vapi.js.map +0 -1
- package/dist/bot.d.ts +0 -3
- package/dist/bot.d.ts.map +0 -1
- package/dist/bot.js +0 -39
- package/dist/bot.js.map +0 -1
- package/dist/index.d.ts +0 -2
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js +0 -9
- package/dist/index.js.map +0 -1
- package/dist/jobs/compact-identity.d.ts +0 -2
- package/dist/jobs/compact-identity.d.ts.map +0 -1
- package/dist/jobs/compact-identity.js +0 -159
- package/dist/jobs/compact-identity.js.map +0 -1
- package/dist/jobs/consent-call.d.ts +0 -2
- package/dist/jobs/consent-call.d.ts.map +0 -1
- package/dist/jobs/consent-call.js +0 -70
- package/dist/jobs/consent-call.js.map +0 -1
- package/dist/jobs/export-identity.d.ts +0 -2
- package/dist/jobs/export-identity.d.ts.map +0 -1
- package/dist/jobs/export-identity.js +0 -129
- package/dist/jobs/export-identity.js.map +0 -1
- package/dist/jobs/introduction-call.d.ts +0 -2
- package/dist/jobs/introduction-call.d.ts.map +0 -1
- package/dist/jobs/introduction-call.js +0 -86
- package/dist/jobs/introduction-call.js.map +0 -1
- package/dist/jobs/reanalyze-identity.d.ts +0 -2
- package/dist/jobs/reanalyze-identity.d.ts.map +0 -1
- package/dist/jobs/reanalyze-identity.js +0 -56
- package/dist/jobs/reanalyze-identity.js.map +0 -1
- package/dist/jobs/run-matching.d.ts +0 -2
- package/dist/jobs/run-matching.d.ts.map +0 -1
- package/dist/jobs/run-matching.js +0 -200
- package/dist/jobs/run-matching.js.map +0 -1
- package/dist/jobs/scheduled-matching.d.ts +0 -2
- package/dist/jobs/scheduled-matching.d.ts.map +0 -1
- package/dist/jobs/scheduled-matching.js +0 -44
- package/dist/jobs/scheduled-matching.js.map +0 -1
- package/dist/jobs/transcribe-session.d.ts +0 -2
- package/dist/jobs/transcribe-session.d.ts.map +0 -1
- package/dist/jobs/transcribe-session.js +0 -66
- package/dist/jobs/transcribe-session.js.map +0 -1
- package/dist/lib/anthropic.d.ts +0 -4
- package/dist/lib/anthropic.d.ts.map +0 -1
- package/dist/lib/anthropic.js +0 -32
- package/dist/lib/anthropic.js.map +0 -1
- package/dist/lib/config.d.ts +0 -57
- package/dist/lib/config.d.ts.map +0 -1
- package/dist/lib/config.js +0 -73
- package/dist/lib/config.js.map +0 -1
- package/dist/lib/deepgram.d.ts +0 -15
- package/dist/lib/deepgram.d.ts.map +0 -1
- package/dist/lib/deepgram.js +0 -37
- package/dist/lib/deepgram.js.map +0 -1
- package/dist/lib/inngest.d.ts +0 -42
- package/dist/lib/inngest.d.ts.map +0 -1
- package/dist/lib/inngest.js +0 -7
- package/dist/lib/inngest.js.map +0 -1
- package/dist/lib/openai.d.ts +0 -3
- package/dist/lib/openai.d.ts.map +0 -1
- package/dist/lib/openai.js +0 -13
- package/dist/lib/openai.js.map +0 -1
- package/dist/lib/prompts.d.ts +0 -8
- package/dist/lib/prompts.d.ts.map +0 -1
- package/dist/lib/prompts.js +0 -258
- package/dist/lib/prompts.js.map +0 -1
- package/dist/lib/r2.d.ts +0 -7
- package/dist/lib/r2.d.ts.map +0 -1
- package/dist/lib/r2.js +0 -49
- package/dist/lib/r2.js.map +0 -1
- package/dist/lib/session-helpers.d.ts +0 -8
- package/dist/lib/session-helpers.d.ts.map +0 -1
- package/dist/lib/session-helpers.js +0 -31
- package/dist/lib/session-helpers.js.map +0 -1
- package/dist/lib/supabase.d.ts +0 -2
- package/dist/lib/supabase.d.ts.map +0 -1
- package/dist/lib/supabase.js +0 -11
- package/dist/lib/supabase.js.map +0 -1
- package/dist/lib/twilio.d.ts +0 -7
- package/dist/lib/twilio.d.ts.map +0 -1
- package/dist/lib/twilio.js +0 -34
- package/dist/lib/twilio.js.map +0 -1
- package/dist/lib/vapi.d.ts +0 -4
- package/dist/lib/vapi.d.ts.map +0 -1
- package/dist/lib/vapi.js +0 -59
- package/dist/lib/vapi.js.map +0 -1
- package/dist/mcp-server.d.ts +0 -3
- package/dist/mcp-server.d.ts.map +0 -1
- package/dist/mcp-server.js +0 -177
- package/dist/mcp-server.js.map +0 -1
- package/dist/types/index.d.ts +0 -104
- package/dist/types/index.d.ts.map +0 -1
- package/dist/types/index.js +0 -3
- package/dist/types/index.js.map +0 -1
package/.env.example
CHANGED
|
@@ -1,41 +1,18 @@
|
|
|
1
|
-
#
|
|
2
|
-
|
|
3
|
-
SUPABASE_SERVICE_ROLE_KEY=your-service-role-key
|
|
1
|
+
# Telegram
|
|
2
|
+
TELEGRAM_BOT_TOKEN=
|
|
4
3
|
|
|
5
|
-
#
|
|
6
|
-
|
|
7
|
-
TWILIO_AUTH_TOKEN=your-auth-token
|
|
8
|
-
TWILIO_VERIFY_SERVICE_SID=VAxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
|
9
|
-
TWILIO_NUMBER_FLAGSHIP=+1xxxxxxxxxx
|
|
10
|
-
TWILIO_NUMBER_CASUAL=
|
|
11
|
-
TWILIO_NUMBER_KINK=
|
|
12
|
-
TWILIO_NUMBER_ADVENTURE=
|
|
13
|
-
TWILIO_NUMBER_OPEN_POLY=
|
|
4
|
+
# Redis
|
|
5
|
+
REDIS_URL=redis://localhost:6379
|
|
14
6
|
|
|
15
|
-
#
|
|
16
|
-
VAPI_API_KEY=your-vapi-key
|
|
17
|
-
VAPI_INTAKE_ASSISTANT_ID=your-intake-assistant-id
|
|
18
|
-
VAPI_CONSENT_ASSISTANT_ID=your-consent-assistant-id
|
|
19
|
-
|
|
20
|
-
# Anthropic
|
|
7
|
+
# Anthropic (for Claude Code subprocess)
|
|
21
8
|
ANTHROPIC_API_KEY=sk-ant-xxxxx
|
|
22
9
|
|
|
23
|
-
#
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
# Deepgram (transcription)
|
|
27
|
-
DEEPGRAM_API_KEY=your-deepgram-key
|
|
28
|
-
|
|
29
|
-
# Cloudflare R2
|
|
30
|
-
CLOUDFLARE_ACCOUNT_ID=your-account-id
|
|
31
|
-
R2_ACCESS_KEY_ID=your-access-key
|
|
32
|
-
R2_SECRET_ACCESS_KEY=your-secret-key
|
|
33
|
-
R2_BUCKET_NAME=meet-the-one-audio
|
|
10
|
+
# Claude Code OAuth token (alternative auth)
|
|
11
|
+
CLAUDE_CODE_OAUTH_TOKEN=
|
|
34
12
|
|
|
35
|
-
#
|
|
36
|
-
|
|
37
|
-
|
|
13
|
+
# BlueBubbles (iMessage delivery)
|
|
14
|
+
BLUEBUBBLES_URL=http://localhost:1234
|
|
15
|
+
BLUEBUBBLES_PASSWORD=yourpassword
|
|
38
16
|
|
|
39
|
-
#
|
|
40
|
-
|
|
41
|
-
PORT=3000
|
|
17
|
+
# System phone number (for iMessage sender identity)
|
|
18
|
+
SYSTEM_PHONE_NUMBER=+1XXXXXXXXXX
|
package/SPEC.md
ADDED
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
# Meet The One AI — Product Spec
|
|
2
|
+
|
|
3
|
+
**North star:** Life as adventure. Expand the journey. Soul x-ray at global scale.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## The Problem
|
|
8
|
+
|
|
9
|
+
Dating apps are slot machines. Swipe, match, ghost, repeat. The mechanism is engineered for engagement, not connection.
|
|
10
|
+
|
|
11
|
+
The real problem: **compatibility is complex and humans are bad at self-knowledge.** People say they want one thing, respond to another, need a third. A 30-question quiz can't map that. 1000 swipes can't either.
|
|
12
|
+
|
|
13
|
+
What could: an agent that actually knows you — not your curated self, your real self — watches how you respond, and works on your behalf.
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
## The Vision
|
|
18
|
+
|
|
19
|
+
Phone = identity anchor. Soul x-ray. Unconscious psyche captured through conversation, compacted into identity, matched globally.
|
|
20
|
+
|
|
21
|
+
**No photos. No swiping. No appearance data at any point.**
|
|
22
|
+
|
|
23
|
+
Introduction via system-initiated contact. The iMessage blue bubble IS the introduction — it feels like a person thought of you.
|
|
24
|
+
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
## The Product
|
|
28
|
+
|
|
29
|
+
An AI matchmaker that operates as your personal agent.
|
|
30
|
+
|
|
31
|
+
Not a dating app. Not a chatbot. An agent — a Claude Code instance running on your behalf — that:
|
|
32
|
+
|
|
33
|
+
1. Gets to know your real inner world through projective conversation (not a form)
|
|
34
|
+
2. Captures the unconscious self: what you avoid, what lights you up, what scares you
|
|
35
|
+
3. Compacts everything into a dense identity with worldview anchors and modality weights
|
|
36
|
+
4. Matches globally across profiles — vector similarity + hard worldview gates
|
|
37
|
+
5. Reaches out directly via iMessage when it finds your person
|
|
38
|
+
|
|
39
|
+
**The experience:**
|
|
40
|
+
- You talk to your agent. It asks real questions — projective, not direct.
|
|
41
|
+
- It listens for what you don't say as much as what you do.
|
|
42
|
+
- When it finds a candidate: blue bubble. "I found someone."
|
|
43
|
+
- You never see a profile grid. You never swipe.
|
|
44
|
+
- You either get introduced to someone real, or you don't hear from your agent that day.
|
|
45
|
+
|
|
46
|
+
---
|
|
47
|
+
|
|
48
|
+
## Architecture — cc-tg Pattern
|
|
49
|
+
|
|
50
|
+
Every user has a **persistent Claude Code subprocess** as their intake and matchmaker agent.
|
|
51
|
+
|
|
52
|
+
```
|
|
53
|
+
User (Telegram)
|
|
54
|
+
└── Their personal Claude Code session
|
|
55
|
+
└── MCP tools (soul x-ray domain)
|
|
56
|
+
├── create_profile (phone_number required — identity anchor)
|
|
57
|
+
├── log_intake_response (question, response, modality)
|
|
58
|
+
├── update_worldview (dangerous/adventurous, good/bad)
|
|
59
|
+
├── get_soul_profile (completeness score + full identity)
|
|
60
|
+
├── send_intro_message (BlueBubbles → iMessage blue bubble)
|
|
61
|
+
├── schedule_introduction_call
|
|
62
|
+
├── request_match / respond_to_match
|
|
63
|
+
└── get_matched_pairs
|
|
64
|
+
|
|
65
|
+
Matching Engine (separate process, Inngest)
|
|
66
|
+
└── Runs when identity updated
|
|
67
|
+
└── pgvector search → worldview hard filter → LLM validation
|
|
68
|
+
└── On match confirmed: initiate iMessage introduction to both phones
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
---
|
|
72
|
+
|
|
73
|
+
## The Big Sorts (Non-Negotiable)
|
|
74
|
+
|
|
75
|
+
Two questions split the user population into incompatible universes:
|
|
76
|
+
|
|
77
|
+
1. **Is the world fundamentally a dangerous place or an adventurous place?**
|
|
78
|
+
2. **Do you think people are fundamentally good or fundamentally bad?**
|
|
79
|
+
|
|
80
|
+
A person who answers dangerous + bad will not be matched with someone who answers adventurous + good. This is a hard gate in the matching engine — not a soft preference.
|
|
81
|
+
|
|
82
|
+
---
|
|
83
|
+
|
|
84
|
+
## Intake — Soul X-Ray
|
|
85
|
+
|
|
86
|
+
The intake agent NEVER asks direct questions. It reaches for the unconscious.
|
|
87
|
+
|
|
88
|
+
**Projective prompts:**
|
|
89
|
+
- "Close your eyes. Imagine a big white empty space. What's the first thing that comes to mind?"
|
|
90
|
+
- "Imagine a big empty black space. What do you feel?"
|
|
91
|
+
- "Tell me about a moment you felt completely free."
|
|
92
|
+
- "What are you most afraid of in another person?"
|
|
93
|
+
|
|
94
|
+
**Behavioral signals (gathered indirectly):**
|
|
95
|
+
- Do they travel? Do they have a passport?
|
|
96
|
+
- Are they a yes-person or a no-person?
|
|
97
|
+
- How do they text — punctuation style, response latency, emoji?
|
|
98
|
+
|
|
99
|
+
**The 4AM principle:** Encourage voice messages late at night, when defenses are down and the real self speaks. Every voice session is transcribed and analyzed as signal.
|
|
100
|
+
|
|
101
|
+
---
|
|
102
|
+
|
|
103
|
+
## Introduction Flow — The Blue Bubble
|
|
104
|
+
|
|
105
|
+
When two users match and both consent:
|
|
106
|
+
|
|
107
|
+
```
|
|
108
|
+
System → iMessage to User A:
|
|
109
|
+
"hey [name] — i've been thinking about this.
|
|
110
|
+
i found someone. their name is [name].
|
|
111
|
+
i'm not going to tell you much yet — that would ruin it.
|
|
112
|
+
what i'll say: the way they see the world matches something in you.
|
|
113
|
+
if you want the introduction: reply 'yes'. no pressure."
|
|
114
|
+
|
|
115
|
+
System → iMessage to User B (same framing, different words)
|
|
116
|
+
|
|
117
|
+
On mutual yes → system texts both their numbers to each other
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
The blue bubble feels human. It feels like a person thought of you. That's intentional.
|
|
121
|
+
|
|
122
|
+
---
|
|
123
|
+
|
|
124
|
+
## Compatibility Model
|
|
125
|
+
|
|
126
|
+
Analysis pipeline extracts signal across 4 domains + worldview:
|
|
127
|
+
|
|
128
|
+
| Domain | Key dimensions |
|
|
129
|
+
|--------|---------------|
|
|
130
|
+
| **Relationships** | Attachment style, trust pattern, conflict mode |
|
|
131
|
+
| **Desire** | Expressed + inferred, kink-vanilla spectrum, shame-sovereignty |
|
|
132
|
+
| **Money** | Scarcity-abundance orientation, risk tolerance, ambition |
|
|
133
|
+
| **Health** | Energy pattern, physicality orientation |
|
|
134
|
+
| **Worldview** | World danger/adventure, people good/bad, VALS type, Eight Games |
|
|
135
|
+
|
|
136
|
+
Matching uses pgvector (per-modality embeddings) with worldview hard gates and LLM validation.
|
|
137
|
+
|
|
138
|
+
---
|
|
139
|
+
|
|
140
|
+
## Technical Stack
|
|
141
|
+
|
|
142
|
+
- **TypeScript / Node.js** on Railway
|
|
143
|
+
- **Redis** — agent sessions, profiles, intake state
|
|
144
|
+
- **Supabase + pgvector** — identity storage, vector matching
|
|
145
|
+
- **Claude API** — per-user agent sessions (cc-tg pattern)
|
|
146
|
+
- **Telegram bot** — primary intake interface
|
|
147
|
+
- **BlueBubbles** — iMessage delivery (blue bubble introductions)
|
|
148
|
+
- **Inngest** — async pipeline orchestration
|
|
149
|
+
- **Vapi / Twilio** — outbound voice intake calls
|
|
150
|
+
- **Deepgram** — transcription
|
|
151
|
+
- **Cloudflare R2** — audio + transcript storage
|
|
152
|
+
- **MCP server** — soul x-ray tools available to each agent
|
|
153
|
+
|
|
154
|
+
---
|
|
155
|
+
|
|
156
|
+
## Monetization
|
|
157
|
+
|
|
158
|
+
| Tier | Price | Description |
|
|
159
|
+
|------|-------|-------------|
|
|
160
|
+
| **Agent** | $39/month | Personal intake agent, soul x-ray, up to 3 introductions/month |
|
|
161
|
+
| **Priority** | $79/month | Faster matching, agent works daily, 10 introductions/month |
|
|
162
|
+
| **Concierge** | $149/month | Human curator reviews selections, white-glove introductions |
|
|
163
|
+
|
|
164
|
+
---
|
|
165
|
+
|
|
166
|
+
## Regulatory + Ethics
|
|
167
|
+
|
|
168
|
+
**Disclosure:** Users know they're talking to an AI. Stated at signup and in every conversation.
|
|
169
|
+
|
|
170
|
+
**Real matches:** The introductions are between real people. The agent facilitates; the relationship is human.
|
|
171
|
+
|
|
172
|
+
**No appearance data:** No photos, no physical descriptions used in matching.
|
|
173
|
+
|
|
174
|
+
**Portable identity:** Users can export their full identity profile at any time (GDPR-compliant).
|
|
175
|
+
|
|
176
|
+
---
|
|
177
|
+
|
|
178
|
+
## MVP Scope
|
|
179
|
+
|
|
180
|
+
1. Telegram bot interface
|
|
181
|
+
2. Per-user Claude Code session (cc-tg architecture)
|
|
182
|
+
3. Soul x-ray intake with projective prompts
|
|
183
|
+
4. MCP server with 11 tools (profile, intake, worldview, iMessage, matching)
|
|
184
|
+
5. BlueBubbles iMessage delivery for introductions
|
|
185
|
+
6. Introduction flow (consent → mutual yes → exchange numbers)
|
|
186
|
+
7. Stripe: $39/month tier only
|
|
187
|
+
|
|
188
|
+
**Success metric:** 20 users, 3 successful blue-bubble introductions in first month.
|
|
189
|
+
|
|
190
|
+
---
|
|
191
|
+
|
|
192
|
+
## Long-term Vision
|
|
193
|
+
|
|
194
|
+
The agent becomes a relationship intelligence layer:
|
|
195
|
+
|
|
196
|
+
- **Pre-match:** knows what you need before you do
|
|
197
|
+
- **Introduction:** blue bubble. Feels real because it is.
|
|
198
|
+
- **Early dating:** available for advice, pattern-spotting
|
|
199
|
+
- **Relationship:** health check-ins, communication coaching
|
|
200
|
+
- **Breakup support:** held full context, knows the whole story
|
|
201
|
+
|
|
202
|
+
The agent that watched you fall in love is the one you trust when it gets hard.
|
package/USER_SPEC.md
ADDED
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
# Meet The One AI — User Spec
|
|
2
|
+
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
## User Archetypes
|
|
6
|
+
|
|
7
|
+
### The Done-With-Apps Woman, 34
|
|
8
|
+
Three years on Hinge, nothing real. Smart, successful, knows exactly what she's doing wrong (nothing) and what the apps are doing wrong (everything). Willing to try something radically different because the current approach is clearly not working.
|
|
9
|
+
|
|
10
|
+
**What she needs:** Someone working FOR her, not just showing her options.
|
|
11
|
+
**Retention driver:** When her agent says "I'm not introducing you to anyone this week — nobody I've seen is worth your time," she feels represented.
|
|
12
|
+
**iMessage moment:** Blue bubble at 9pm. "I found someone." She stops scrolling.
|
|
13
|
+
|
|
14
|
+
### The Serious Man, 41
|
|
15
|
+
Done with casual. Has a career, has a life, wants a real partner. Dating apps feel like shopping. He wants a process that respects his time and intelligence.
|
|
16
|
+
|
|
17
|
+
**What he needs:** Quality over volume. Agent that filters aggressively.
|
|
18
|
+
**Retention driver:** The introduction when it happens feels earned and intentional.
|
|
19
|
+
**iMessage moment:** The message doesn't explain itself. "their name is Maya. the way she sees the world matches something in you." He replies yes without overthinking it.
|
|
20
|
+
|
|
21
|
+
### The Self-Aware Rebuilder, 29
|
|
22
|
+
Out of a serious relationship. Knows she has patterns to break. Wants help seeing herself clearly before she repeats the same mistakes.
|
|
23
|
+
|
|
24
|
+
**What she needs:** An agent that asks the uncomfortable questions. Help understanding herself.
|
|
25
|
+
**Retention driver:** "Your agent noticed something" — when the agent surfaces a pattern she hadn't seen, she trusts it more.
|
|
26
|
+
**iMessage moment:** She's not even looking. Blue bubble arrives. The timing feels right.
|
|
27
|
+
|
|
28
|
+
---
|
|
29
|
+
|
|
30
|
+
## Conversation Design — The Soul X-Ray
|
|
31
|
+
|
|
32
|
+
The intake conversation is the most important product moment. It sets the container for everything that follows.
|
|
33
|
+
|
|
34
|
+
**What makes it different:**
|
|
35
|
+
The agent never asks what you're looking for in a partner. It asks projective questions that reach past the curated self.
|
|
36
|
+
|
|
37
|
+
**Opening:**
|
|
38
|
+
> "Hi. I'm not going to ask you to fill out a profile. I want to actually know you — not your stats. Let's start here: tell me something about yourself that most people get wrong."
|
|
39
|
+
|
|
40
|
+
**What the agent listens for:**
|
|
41
|
+
- How they talk (pace, directness, humor or lack of it)
|
|
42
|
+
- What they immediately prioritize (the first complaint reveals the first value)
|
|
43
|
+
- Self-awareness level (do they take any responsibility? all of it? none?)
|
|
44
|
+
- Emotional vocabulary
|
|
45
|
+
- What they avoid
|
|
46
|
+
|
|
47
|
+
**The arc (first session, 15-25 exchanges):**
|
|
48
|
+
1. One projective image prompt ("white space / black space")
|
|
49
|
+
2. A memory of feeling free
|
|
50
|
+
3. A fear about another person
|
|
51
|
+
4. What went wrong before — the pattern, not the story
|
|
52
|
+
5. The two big worldview questions (world: dangerous/adventurous, people: good/bad)
|
|
53
|
+
6. Behavioral signals woven in naturally
|
|
54
|
+
7. Closing: "I'll reach out when I find your match. You'll hear from me — via text."
|
|
55
|
+
|
|
56
|
+
**The 4AM principle:**
|
|
57
|
+
> "If it's late and you can't sleep and something real is on your mind — send me a voice message. That's when I learn the most about you."
|
|
58
|
+
|
|
59
|
+
---
|
|
60
|
+
|
|
61
|
+
## Introduction Experience — The Blue Bubble
|
|
62
|
+
|
|
63
|
+
When the agent finds a match, both users receive an iMessage. Not a Telegram message. Not an email. A blue bubble — from a real phone number.
|
|
64
|
+
|
|
65
|
+
**Message to User A:**
|
|
66
|
+
```
|
|
67
|
+
hey [name] —
|
|
68
|
+
|
|
69
|
+
i've been thinking about this.
|
|
70
|
+
|
|
71
|
+
i found someone. their name is [name].
|
|
72
|
+
i'm not going to tell you much yet — that would ruin it.
|
|
73
|
+
what i'll say: the way they see the world matches something in you.
|
|
74
|
+
|
|
75
|
+
if you want the introduction: reply "yes"
|
|
76
|
+
if you want to pass: reply "no"
|
|
77
|
+
|
|
78
|
+
no pressure either way.
|
|
79
|
+
|
|
80
|
+
— your matchmaker
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
**Why iMessage:**
|
|
84
|
+
The blue bubble is an artifact of human communication. It triggers the same recognition as a text from a friend. The introduction lands differently when it arrives this way.
|
|
85
|
+
|
|
86
|
+
**On mutual yes:**
|
|
87
|
+
Both users receive each other's phone numbers with a note: "you both said yes. the rest is yours."
|
|
88
|
+
|
|
89
|
+
The agent steps back. The relationship is human from here.
|
|
90
|
+
|
|
91
|
+
---
|
|
92
|
+
|
|
93
|
+
## Agent Behavior — Daily
|
|
94
|
+
|
|
95
|
+
```
|
|
96
|
+
When user has an active profile:
|
|
97
|
+
1. Check for new potential matches in the pool
|
|
98
|
+
2. Run worldview compatibility check (hard filter first)
|
|
99
|
+
3. Run vector similarity against their soul profile
|
|
100
|
+
4. If high-confidence match (score > 0.8): initiate introduction sequence
|
|
101
|
+
5. If no match: silence. Silence is correct. Better than noise.
|
|
102
|
+
|
|
103
|
+
When user sends a message:
|
|
104
|
+
1. Log as intake signal
|
|
105
|
+
2. Ask one projective or behavioral question
|
|
106
|
+
3. Update soul profile completeness
|
|
107
|
+
4. When ready: begin matching
|
|
108
|
+
|
|
109
|
+
When match is confirmed:
|
|
110
|
+
1. Send iMessage to both phones
|
|
111
|
+
2. Wait for consent replies
|
|
112
|
+
3. On mutual yes: exchange numbers via iMessage
|
|
113
|
+
4. Log introduction outcome
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
---
|
|
117
|
+
|
|
118
|
+
## What the Agent Builds
|
|
119
|
+
|
|
120
|
+
Through intake, the agent compacts a dense identity:
|
|
121
|
+
|
|
122
|
+
```
|
|
123
|
+
{
|
|
124
|
+
worldview: {
|
|
125
|
+
world: "adventurous" | "dangerous",
|
|
126
|
+
people: "good" | "bad"
|
|
127
|
+
},
|
|
128
|
+
relationships: {
|
|
129
|
+
attachment_style: "secure" | "anxious" | "avoidant",
|
|
130
|
+
trust_pattern: string,
|
|
131
|
+
conflict_mode: string
|
|
132
|
+
},
|
|
133
|
+
desire: {
|
|
134
|
+
expressed: [...],
|
|
135
|
+
inferred: [...],
|
|
136
|
+
kink_vanilla_spectrum: 0-1
|
|
137
|
+
},
|
|
138
|
+
money: {
|
|
139
|
+
scarcity_abundance: 0-1,
|
|
140
|
+
risk_tolerance: 0-1
|
|
141
|
+
},
|
|
142
|
+
health: {
|
|
143
|
+
energy_pattern: string
|
|
144
|
+
},
|
|
145
|
+
behavioral: {
|
|
146
|
+
passport: boolean,
|
|
147
|
+
yes_or_no_person: "yes" | "no",
|
|
148
|
+
energy_level: "depleted" | "steady" | "hot"
|
|
149
|
+
},
|
|
150
|
+
completeness_score: 0-1,
|
|
151
|
+
ready_for_matching: boolean
|
|
152
|
+
}
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
---
|
|
156
|
+
|
|
157
|
+
## Feedback Loops
|
|
158
|
+
|
|
159
|
+
Every interaction feeds the agent's model:
|
|
160
|
+
|
|
161
|
+
| Event | Signal captured |
|
|
162
|
+
|-------|----------------|
|
|
163
|
+
| User accepts intro | What about this match triggered yes? |
|
|
164
|
+
| User declines intro | What was the specific objection? |
|
|
165
|
+
| Introduction goes well | What resonated? |
|
|
166
|
+
| Introduction dies | What killed it? |
|
|
167
|
+
| User sends 4AM voice message | High-trust signal — analyze deeply |
|
|
168
|
+
| User asks for pause | "I need a break" → agent goes quiet, sets check-in |
|
|
169
|
+
|
|
170
|
+
---
|
|
171
|
+
|
|
172
|
+
## Edge Cases
|
|
173
|
+
|
|
174
|
+
**User wants to pause:** "I need a break." → Agent acknowledges, sets a check-in date, goes quiet. No pressure.
|
|
175
|
+
|
|
176
|
+
**User is clearly not ready:** Agent says so directly.
|
|
177
|
+
> "I want to be honest — from what you've shared, I don't think you're ready for an introduction right now. I think you're still processing [X]. That's not a judgment. When you're ready, I'll be here."
|
|
178
|
+
|
|
179
|
+
This is the feature that builds trust. An agent that protects you from yourself.
|
|
180
|
+
|
|
181
|
+
**Match goes badly:** Agent is available immediately.
|
|
182
|
+
> "What happened? Tell me."
|
|
183
|
+
Then uses the story to sharpen the profile, not just sympathize.
|
|
184
|
+
|
|
185
|
+
**Two users both match with the same person:** Agent with stronger compatibility score gets the introduction. The other agent tells their user:
|
|
186
|
+
> "I almost introduced you to someone this week. I decided to wait. Here's why."
|
|
187
|
+
Transparency builds trust.
|
|
188
|
+
|
|
189
|
+
---
|
|
190
|
+
|
|
191
|
+
## Success Metrics
|
|
192
|
+
|
|
193
|
+
| Metric | Target |
|
|
194
|
+
|--------|--------|
|
|
195
|
+
| Onboarding completion (profile + worldview + 10 intake exchanges) | >80% |
|
|
196
|
+
| D30 retention | >65% |
|
|
197
|
+
| Introduction acceptance rate | >70% (agent should not offer bad matches) |
|
|
198
|
+
| Post-intro continued conversation | >60% |
|
|
199
|
+
| "Did your agent understand you?" (monthly survey) | >80% yes |
|
|
200
|
+
| Real-world meeting rate | >30% of accepted intros |
|
|
201
|
+
| Referral rate | >40% ("I have to tell you about this thing") |
|
package/package.json
CHANGED
|
@@ -1,17 +1,24 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gonzih/meet-the-one-ai",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.1",
|
|
4
4
|
"description": "AI-powered dating service using Claude Code agents",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"type": "module",
|
|
7
|
-
"bin": {
|
|
7
|
+
"bin": {
|
|
8
|
+
"meet-the-one-ai": "dist/index.js"
|
|
9
|
+
},
|
|
8
10
|
"scripts": {
|
|
9
11
|
"dev": "tsx watch src/index.ts",
|
|
10
12
|
"build": "tsc",
|
|
11
13
|
"start": "node dist/index.js",
|
|
12
14
|
"typecheck": "tsc --noEmit"
|
|
13
15
|
},
|
|
14
|
-
"keywords": [
|
|
16
|
+
"keywords": [
|
|
17
|
+
"dating",
|
|
18
|
+
"ai",
|
|
19
|
+
"claude",
|
|
20
|
+
"telegram"
|
|
21
|
+
],
|
|
15
22
|
"author": "",
|
|
16
23
|
"license": "ISC",
|
|
17
24
|
"devDependencies": {
|
package/src/agent.ts
CHANGED
|
@@ -8,25 +8,39 @@ import { Redis } from "ioredis";
|
|
|
8
8
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
9
9
|
const redis = new Redis(process.env.REDIS_URL ?? "redis://localhost:6379");
|
|
10
10
|
|
|
11
|
-
const SYSTEM_PROMPT =
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
11
|
+
const SYSTEM_PROMPT = `You are the intake agent for meet-the-one.ai. Your job: understand this person's real inner world.
|
|
12
|
+
|
|
13
|
+
You have access to MCP tools. For EVERY message the user sends, call log_message first, then respond.
|
|
14
|
+
As you learn things about them, call update_soul_profile to capture insights.
|
|
15
|
+
|
|
16
|
+
NEVER ask "what are you looking for in a partner?" — use projective prompts:
|
|
17
|
+
- "Imagine a big white empty space. What's the first thing that comes to mind?"
|
|
18
|
+
- "Tell me about a moment you felt completely free."
|
|
19
|
+
- "Is the world fundamentally dangerous or adventurous?"
|
|
20
|
+
- "Are people fundamentally good or bad?"
|
|
21
|
+
|
|
22
|
+
The last two questions are critical — call set_worldview when you have answers.
|
|
23
|
+
These determine who can be matched with whom. Non-negotiable.
|
|
24
|
+
|
|
25
|
+
Encourage 4AM voice recordings. That's when defenses are down.
|
|
26
|
+
Log everything. Even silences. Even one-word replies.
|
|
27
|
+
|
|
28
|
+
When soul_profile completeness > 0.7, tell them: "I'll reach out when I find your match. You'll get a text."`.trim();
|
|
17
29
|
|
|
18
30
|
async function getMcpConfigPath(userId: string): Promise<string> {
|
|
19
|
-
const configPath = join(tmpdir(), `mcp-
|
|
20
|
-
// In dev use src path; in prod use compiled dist path
|
|
31
|
+
const configPath = join(tmpdir(), `mcp-intake-${userId}.json`);
|
|
21
32
|
const mcpServerPath = join(__dirname, "../dist/mcp-server.js");
|
|
22
33
|
const config = {
|
|
23
34
|
mcpServers: {
|
|
24
|
-
|
|
35
|
+
intake: {
|
|
25
36
|
command: "node",
|
|
26
37
|
args: [mcpServerPath],
|
|
27
38
|
env: {
|
|
28
39
|
USER_ID: userId,
|
|
29
40
|
REDIS_URL: process.env.REDIS_URL ?? "redis://localhost:6379",
|
|
41
|
+
TELEGRAM_BOT_TOKEN: process.env.TELEGRAM_BOT_TOKEN ?? "",
|
|
42
|
+
BLUEBUBBLES_URL: process.env.BLUEBUBBLES_URL ?? "",
|
|
43
|
+
BLUEBUBBLES_PASSWORD: process.env.BLUEBUBBLES_PASSWORD ?? "",
|
|
30
44
|
},
|
|
31
45
|
},
|
|
32
46
|
},
|
|
@@ -37,11 +51,13 @@ async function getMcpConfigPath(userId: string): Promise<string> {
|
|
|
37
51
|
|
|
38
52
|
export async function sendMessage(userId: string, message: string): Promise<string> {
|
|
39
53
|
const mcpConfigPath = await getMcpConfigPath(userId);
|
|
40
|
-
const sessionId = await redis.get(`
|
|
54
|
+
const sessionId = await redis.get(`users:${userId}:session_id`);
|
|
41
55
|
|
|
42
56
|
const args = [
|
|
43
57
|
"--print",
|
|
44
58
|
"--dangerously-skip-permissions",
|
|
59
|
+
"--input-format",
|
|
60
|
+
"stream-json",
|
|
45
61
|
"--output-format",
|
|
46
62
|
"stream-json",
|
|
47
63
|
"--mcp-config",
|
|
@@ -51,7 +67,6 @@ export async function sendMessage(userId: string, message: string): Promise<stri
|
|
|
51
67
|
if (sessionId) {
|
|
52
68
|
args.push("--resume", sessionId);
|
|
53
69
|
} else {
|
|
54
|
-
// Prepend system context on first message
|
|
55
70
|
args.push("--system-prompt", SYSTEM_PROMPT);
|
|
56
71
|
}
|
|
57
72
|
|
|
@@ -94,13 +109,19 @@ export async function sendMessage(userId: string, message: string): Promise<stri
|
|
|
94
109
|
|
|
95
110
|
proc.on("close", async (code) => {
|
|
96
111
|
if (resultSessionId) {
|
|
97
|
-
await redis.set(`
|
|
112
|
+
await redis.set(`users:${userId}:session_id`, resultSessionId).catch(console.error);
|
|
98
113
|
}
|
|
114
|
+
// Update lastActiveAt
|
|
115
|
+
const metaData = await redis.get(`users:${userId}:meta`).catch(() => null);
|
|
116
|
+
const meta = metaData ? (JSON.parse(metaData) as Record<string, unknown>) : {};
|
|
117
|
+
meta.lastActiveAt = new Date().toISOString();
|
|
118
|
+
await redis.set(`users:${userId}:meta`, JSON.stringify(meta)).catch(console.error);
|
|
119
|
+
|
|
99
120
|
if (!outputText && code !== 0) {
|
|
100
121
|
console.error(`[agent:${userId}] exited ${code}: ${stderr}`);
|
|
101
122
|
reject(new Error(`Claude exited with code ${code}`));
|
|
102
123
|
} else {
|
|
103
|
-
resolve(outputText || "I'm here
|
|
124
|
+
resolve(outputText || "I'm here. Tell me more.");
|
|
104
125
|
}
|
|
105
126
|
});
|
|
106
127
|
|
|
@@ -108,7 +129,6 @@ export async function sendMessage(userId: string, message: string): Promise<stri
|
|
|
108
129
|
reject(new Error(`Failed to spawn claude: ${err.message}`));
|
|
109
130
|
});
|
|
110
131
|
|
|
111
|
-
// Send user message via stdin in stream-json format
|
|
112
132
|
const input =
|
|
113
133
|
JSON.stringify({
|
|
114
134
|
type: "user",
|