@gonzih/meet-the-one-ai 1.0.0

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.
Files changed (175) hide show
  1. package/.env.example +41 -0
  2. package/.node-version +1 -0
  3. package/basis/BERNAYS.md +233 -0
  4. package/basis/FOUNDING_TRANSCRIPT.md +218 -0
  5. package/basis/TECH_SPEC.md +303 -0
  6. package/basis/VALS.md +255 -0
  7. package/basis/layers/L1_IDENTITY_AUTH.md +78 -0
  8. package/basis/layers/L2_CONVERSATION.md +159 -0
  9. package/basis/layers/L3_RECORDING_STORE.md +104 -0
  10. package/basis/layers/L4_ANALYSIS_PIPELINE.md +257 -0
  11. package/basis/layers/L5_MATCHING_ENGINE.md +164 -0
  12. package/basis/layers/L6_CONSENT_INTRODUCTION.md +143 -0
  13. package/basis/layers/L7_PORTABLE_IDENTITY.md +139 -0
  14. package/basis/layers/STACK.md +64 -0
  15. package/basis/schema.sql +203 -0
  16. package/dist/agent.d.ts +2 -0
  17. package/dist/agent.d.ts.map +1 -0
  18. package/dist/agent.js +114 -0
  19. package/dist/agent.js.map +1 -0
  20. package/dist/api/routes/auth.d.ts +2 -0
  21. package/dist/api/routes/auth.d.ts.map +1 -0
  22. package/dist/api/routes/auth.js +79 -0
  23. package/dist/api/routes/auth.js.map +1 -0
  24. package/dist/api/routes/identity.d.ts +2 -0
  25. package/dist/api/routes/identity.d.ts.map +1 -0
  26. package/dist/api/routes/identity.js +92 -0
  27. package/dist/api/routes/identity.js.map +1 -0
  28. package/dist/api/routes/text-submission.d.ts +2 -0
  29. package/dist/api/routes/text-submission.d.ts.map +1 -0
  30. package/dist/api/routes/text-submission.js +56 -0
  31. package/dist/api/routes/text-submission.js.map +1 -0
  32. package/dist/api/webhooks/twilio.d.ts +2 -0
  33. package/dist/api/webhooks/twilio.d.ts.map +1 -0
  34. package/dist/api/webhooks/twilio.js +144 -0
  35. package/dist/api/webhooks/twilio.js.map +1 -0
  36. package/dist/api/webhooks/vapi.d.ts +2 -0
  37. package/dist/api/webhooks/vapi.d.ts.map +1 -0
  38. package/dist/api/webhooks/vapi.js +177 -0
  39. package/dist/api/webhooks/vapi.js.map +1 -0
  40. package/dist/bot.d.ts +3 -0
  41. package/dist/bot.d.ts.map +1 -0
  42. package/dist/bot.js +39 -0
  43. package/dist/bot.js.map +1 -0
  44. package/dist/index.d.ts +2 -0
  45. package/dist/index.d.ts.map +1 -0
  46. package/dist/index.js +9 -0
  47. package/dist/index.js.map +1 -0
  48. package/dist/jobs/compact-identity.d.ts +2 -0
  49. package/dist/jobs/compact-identity.d.ts.map +1 -0
  50. package/dist/jobs/compact-identity.js +159 -0
  51. package/dist/jobs/compact-identity.js.map +1 -0
  52. package/dist/jobs/consent-call.d.ts +2 -0
  53. package/dist/jobs/consent-call.d.ts.map +1 -0
  54. package/dist/jobs/consent-call.js +70 -0
  55. package/dist/jobs/consent-call.js.map +1 -0
  56. package/dist/jobs/export-identity.d.ts +2 -0
  57. package/dist/jobs/export-identity.d.ts.map +1 -0
  58. package/dist/jobs/export-identity.js +129 -0
  59. package/dist/jobs/export-identity.js.map +1 -0
  60. package/dist/jobs/introduction-call.d.ts +2 -0
  61. package/dist/jobs/introduction-call.d.ts.map +1 -0
  62. package/dist/jobs/introduction-call.js +86 -0
  63. package/dist/jobs/introduction-call.js.map +1 -0
  64. package/dist/jobs/reanalyze-identity.d.ts +2 -0
  65. package/dist/jobs/reanalyze-identity.d.ts.map +1 -0
  66. package/dist/jobs/reanalyze-identity.js +56 -0
  67. package/dist/jobs/reanalyze-identity.js.map +1 -0
  68. package/dist/jobs/run-matching.d.ts +2 -0
  69. package/dist/jobs/run-matching.d.ts.map +1 -0
  70. package/dist/jobs/run-matching.js +200 -0
  71. package/dist/jobs/run-matching.js.map +1 -0
  72. package/dist/jobs/scheduled-matching.d.ts +2 -0
  73. package/dist/jobs/scheduled-matching.d.ts.map +1 -0
  74. package/dist/jobs/scheduled-matching.js +44 -0
  75. package/dist/jobs/scheduled-matching.js.map +1 -0
  76. package/dist/jobs/transcribe-session.d.ts +2 -0
  77. package/dist/jobs/transcribe-session.d.ts.map +1 -0
  78. package/dist/jobs/transcribe-session.js +66 -0
  79. package/dist/jobs/transcribe-session.js.map +1 -0
  80. package/dist/lib/anthropic.d.ts +4 -0
  81. package/dist/lib/anthropic.d.ts.map +1 -0
  82. package/dist/lib/anthropic.js +32 -0
  83. package/dist/lib/anthropic.js.map +1 -0
  84. package/dist/lib/config.d.ts +57 -0
  85. package/dist/lib/config.d.ts.map +1 -0
  86. package/dist/lib/config.js +73 -0
  87. package/dist/lib/config.js.map +1 -0
  88. package/dist/lib/deepgram.d.ts +15 -0
  89. package/dist/lib/deepgram.d.ts.map +1 -0
  90. package/dist/lib/deepgram.js +37 -0
  91. package/dist/lib/deepgram.js.map +1 -0
  92. package/dist/lib/inngest.d.ts +42 -0
  93. package/dist/lib/inngest.d.ts.map +1 -0
  94. package/dist/lib/inngest.js +7 -0
  95. package/dist/lib/inngest.js.map +1 -0
  96. package/dist/lib/openai.d.ts +3 -0
  97. package/dist/lib/openai.d.ts.map +1 -0
  98. package/dist/lib/openai.js +13 -0
  99. package/dist/lib/openai.js.map +1 -0
  100. package/dist/lib/prompts.d.ts +8 -0
  101. package/dist/lib/prompts.d.ts.map +1 -0
  102. package/dist/lib/prompts.js +258 -0
  103. package/dist/lib/prompts.js.map +1 -0
  104. package/dist/lib/r2.d.ts +7 -0
  105. package/dist/lib/r2.d.ts.map +1 -0
  106. package/dist/lib/r2.js +49 -0
  107. package/dist/lib/r2.js.map +1 -0
  108. package/dist/lib/session-helpers.d.ts +8 -0
  109. package/dist/lib/session-helpers.d.ts.map +1 -0
  110. package/dist/lib/session-helpers.js +31 -0
  111. package/dist/lib/session-helpers.js.map +1 -0
  112. package/dist/lib/supabase.d.ts +2 -0
  113. package/dist/lib/supabase.d.ts.map +1 -0
  114. package/dist/lib/supabase.js +11 -0
  115. package/dist/lib/supabase.js.map +1 -0
  116. package/dist/lib/twilio.d.ts +7 -0
  117. package/dist/lib/twilio.d.ts.map +1 -0
  118. package/dist/lib/twilio.js +34 -0
  119. package/dist/lib/twilio.js.map +1 -0
  120. package/dist/lib/vapi.d.ts +4 -0
  121. package/dist/lib/vapi.d.ts.map +1 -0
  122. package/dist/lib/vapi.js +59 -0
  123. package/dist/lib/vapi.js.map +1 -0
  124. package/dist/mcp-server.d.ts +3 -0
  125. package/dist/mcp-server.d.ts.map +1 -0
  126. package/dist/mcp-server.js +177 -0
  127. package/dist/mcp-server.js.map +1 -0
  128. package/dist/types/index.d.ts +104 -0
  129. package/dist/types/index.d.ts.map +1 -0
  130. package/dist/types/index.js +3 -0
  131. package/dist/types/index.js.map +1 -0
  132. package/package.json +28 -0
  133. package/railway.json +14 -0
  134. package/src/agent.ts +123 -0
  135. package/src/api/routes/auth.ts +95 -0
  136. package/src/api/routes/identity.ts +112 -0
  137. package/src/api/routes/text-submission.ts +64 -0
  138. package/src/api/webhooks/twilio.ts +181 -0
  139. package/src/api/webhooks/vapi.ts +219 -0
  140. package/src/bot.ts +44 -0
  141. package/src/index.ts +11 -0
  142. package/src/jobs/compact-identity.ts +211 -0
  143. package/src/jobs/consent-call.ts +87 -0
  144. package/src/jobs/export-identity.ts +166 -0
  145. package/src/jobs/introduction-call.ts +101 -0
  146. package/src/jobs/reanalyze-identity.ts +65 -0
  147. package/src/jobs/run-matching.ts +243 -0
  148. package/src/jobs/scheduled-matching.ts +59 -0
  149. package/src/jobs/transcribe-session.ts +77 -0
  150. package/src/lib/anthropic.ts +37 -0
  151. package/src/lib/config.ts +81 -0
  152. package/src/lib/deepgram.ts +57 -0
  153. package/src/lib/inngest.ts +33 -0
  154. package/src/lib/openai.ts +14 -0
  155. package/src/lib/prompts.ts +266 -0
  156. package/src/lib/r2.ts +79 -0
  157. package/src/lib/session-helpers.ts +37 -0
  158. package/src/lib/supabase.ts +15 -0
  159. package/src/lib/twilio.ts +49 -0
  160. package/src/lib/vapi.ts +80 -0
  161. package/src/mcp-server.ts +195 -0
  162. package/src/types/index.ts +146 -0
  163. package/supabase/.branches/_current_branch +1 -0
  164. package/supabase/.temp/cli-latest +1 -0
  165. package/supabase/.temp/gotrue-version +1 -0
  166. package/supabase/.temp/pooler-url +1 -0
  167. package/supabase/.temp/postgres-version +1 -0
  168. package/supabase/.temp/project-ref +1 -0
  169. package/supabase/.temp/rest-version +1 -0
  170. package/supabase/.temp/storage-migration +1 -0
  171. package/supabase/.temp/storage-version +1 -0
  172. package/supabase/config.toml +384 -0
  173. package/supabase/migrations/20260303000000_initial_schema.sql +203 -0
  174. package/supabase/migrations/20260304000000_brand_consents.sql +13 -0
  175. package/tsconfig.json +25 -0
@@ -0,0 +1,59 @@
1
+ import { config } from "./config.js";
2
+ const VAPI_BASE = "https://api.vapi.ai";
3
+ // Trigger an outbound intake call via Vapi
4
+ export async function trigger_intake_call(to_phone, user_id) {
5
+ const res = await fetch(`${VAPI_BASE}/call/phone`, {
6
+ method: "POST",
7
+ headers: {
8
+ Authorization: `Bearer ${config.vapi.api_key}`,
9
+ "Content-Type": "application/json",
10
+ },
11
+ body: JSON.stringify({
12
+ assistantId: config.vapi.intake_assistant_id,
13
+ customer: { number: to_phone },
14
+ assistantOverrides: {
15
+ variableValues: { user_id },
16
+ },
17
+ }),
18
+ });
19
+ if (!res.ok) {
20
+ const body = await res.text();
21
+ throw new Error(`Vapi trigger_intake_call failed: ${res.status} ${body}`);
22
+ }
23
+ const data = (await res.json());
24
+ return data.id;
25
+ }
26
+ // Trigger an outbound consent call via Vapi
27
+ export async function trigger_consent_call(to_phone, match_id, framing) {
28
+ const res = await fetch(`${VAPI_BASE}/call/phone`, {
29
+ method: "POST",
30
+ headers: {
31
+ Authorization: `Bearer ${config.vapi.api_key}`,
32
+ "Content-Type": "application/json",
33
+ },
34
+ body: JSON.stringify({
35
+ assistantId: config.vapi.consent_assistant_id,
36
+ customer: { number: to_phone },
37
+ assistantOverrides: {
38
+ variableValues: { match_id, framing },
39
+ },
40
+ }),
41
+ });
42
+ if (!res.ok) {
43
+ const body = await res.text();
44
+ throw new Error(`Vapi trigger_consent_call failed: ${res.status} ${body}`);
45
+ }
46
+ const data = (await res.json());
47
+ return data.id;
48
+ }
49
+ // Get call status
50
+ export async function get_call_status(call_id) {
51
+ const res = await fetch(`${VAPI_BASE}/call/${call_id}`, {
52
+ headers: { Authorization: `Bearer ${config.vapi.api_key}` },
53
+ });
54
+ if (!res.ok)
55
+ throw new Error(`Vapi get_call_status failed: ${res.status}`);
56
+ const data = (await res.json());
57
+ return data.status;
58
+ }
59
+ //# sourceMappingURL=vapi.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"vapi.js","sourceRoot":"","sources":["../../src/lib/vapi.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAErC,MAAM,SAAS,GAAG,qBAAqB,CAAC;AASxC,2CAA2C;AAC3C,MAAM,CAAC,KAAK,UAAU,mBAAmB,CACvC,QAAgB,EAChB,OAAe;IAEf,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,GAAG,SAAS,aAAa,EAAE;QACjD,MAAM,EAAE,MAAM;QACd,OAAO,EAAE;YACP,aAAa,EAAE,UAAU,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE;YAC9C,cAAc,EAAE,kBAAkB;SACnC;QACD,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC;YACnB,WAAW,EAAE,MAAM,CAAC,IAAI,CAAC,mBAAmB;YAC5C,QAAQ,EAAE,EAAE,MAAM,EAAE,QAAQ,EAAE;YAC9B,kBAAkB,EAAE;gBAClB,cAAc,EAAE,EAAE,OAAO,EAAE;aAC5B;SACF,CAAC;KACH,CAAC,CAAC;IAEH,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;QACZ,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC;QAC9B,MAAM,IAAI,KAAK,CAAC,oCAAoC,GAAG,CAAC,MAAM,IAAI,IAAI,EAAE,CAAC,CAAC;IAC5E,CAAC;IAED,MAAM,IAAI,GAAG,CAAC,MAAM,GAAG,CAAC,IAAI,EAAE,CAAqB,CAAC;IACpD,OAAO,IAAI,CAAC,EAAE,CAAC;AACjB,CAAC;AAED,4CAA4C;AAC5C,MAAM,CAAC,KAAK,UAAU,oBAAoB,CACxC,QAAgB,EAChB,QAAgB,EAChB,OAAe;IAEf,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,GAAG,SAAS,aAAa,EAAE;QACjD,MAAM,EAAE,MAAM;QACd,OAAO,EAAE;YACP,aAAa,EAAE,UAAU,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE;YAC9C,cAAc,EAAE,kBAAkB;SACnC;QACD,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC;YACnB,WAAW,EAAE,MAAM,CAAC,IAAI,CAAC,oBAAoB;YAC7C,QAAQ,EAAE,EAAE,MAAM,EAAE,QAAQ,EAAE;YAC9B,kBAAkB,EAAE;gBAClB,cAAc,EAAE,EAAE,QAAQ,EAAE,OAAO,EAAE;aACtC;SACF,CAAC;KACH,CAAC,CAAC;IAEH,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;QACZ,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC;QAC9B,MAAM,IAAI,KAAK,CAAC,qCAAqC,GAAG,CAAC,MAAM,IAAI,IAAI,EAAE,CAAC,CAAC;IAC7E,CAAC;IAED,MAAM,IAAI,GAAG,CAAC,MAAM,GAAG,CAAC,IAAI,EAAE,CAAqB,CAAC;IACpD,OAAO,IAAI,CAAC,EAAE,CAAC;AACjB,CAAC;AAED,kBAAkB;AAClB,MAAM,CAAC,KAAK,UAAU,eAAe,CAAC,OAAe;IACnD,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,GAAG,SAAS,SAAS,OAAO,EAAE,EAAE;QACtD,OAAO,EAAE,EAAE,aAAa,EAAE,UAAU,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,EAAE;KAC5D,CAAC,CAAC;IAEH,IAAI,CAAC,GAAG,CAAC,EAAE;QAAE,MAAM,IAAI,KAAK,CAAC,gCAAgC,GAAG,CAAC,MAAM,EAAE,CAAC,CAAC;IAC3E,MAAM,IAAI,GAAG,CAAC,MAAM,GAAG,CAAC,IAAI,EAAE,CAAqB,CAAC;IACpD,OAAO,IAAI,CAAC,MAAM,CAAC;AACrB,CAAC"}
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=mcp-server.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mcp-server.d.ts","sourceRoot":"","sources":["../src/mcp-server.ts"],"names":[],"mappings":""}
@@ -0,0 +1,177 @@
1
+ #!/usr/bin/env node
2
+ import { Server } from "@modelcontextprotocol/sdk/server/index.js";
3
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
4
+ import { CallToolRequestSchema, ListToolsRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
5
+ import { Redis } from "ioredis";
6
+ import { randomUUID } from "crypto";
7
+ const USER_ID = process.env.USER_ID ?? "";
8
+ const redis = new Redis(process.env.REDIS_URL ?? "redis://localhost:6379");
9
+ const server = new Server({ name: "dating-tools", version: "1.0.0" }, { capabilities: { tools: {} } });
10
+ server.setRequestHandler(ListToolsRequestSchema, async () => ({
11
+ tools: [
12
+ {
13
+ name: "create_profile",
14
+ description: "Create or update a dating profile for the current user",
15
+ inputSchema: {
16
+ type: "object",
17
+ properties: {
18
+ name: { type: "string", description: "User's display name" },
19
+ age: { type: "number", description: "User's age" },
20
+ bio: { type: "string", description: "Short bio" },
21
+ preferences: { type: "string", description: "What user is looking for in a partner" },
22
+ dealbreakers: { type: "string", description: "User's dealbreakers" },
23
+ },
24
+ required: ["name", "age", "bio", "preferences", "dealbreakers"],
25
+ },
26
+ },
27
+ {
28
+ name: "get_my_profile",
29
+ description: "Get the current user's dating profile",
30
+ inputSchema: { type: "object", properties: {} },
31
+ },
32
+ {
33
+ name: "list_profiles",
34
+ description: "List all profiles in the system (for matchmaking evaluation)",
35
+ inputSchema: { type: "object", properties: {} },
36
+ },
37
+ {
38
+ name: "request_match",
39
+ description: "Send a match request with an intro message to another user",
40
+ inputSchema: {
41
+ type: "object",
42
+ properties: {
43
+ target_profile_id: { type: "string", description: "The user ID to request a match with" },
44
+ intro_message: { type: "string", description: "Personalized introduction message" },
45
+ },
46
+ required: ["target_profile_id", "intro_message"],
47
+ },
48
+ },
49
+ {
50
+ name: "get_match_requests",
51
+ description: "Get incoming match requests for the current user",
52
+ inputSchema: { type: "object", properties: {} },
53
+ },
54
+ {
55
+ name: "respond_to_match",
56
+ description: "Accept or decline a match request",
57
+ inputSchema: {
58
+ type: "object",
59
+ properties: {
60
+ request_id: { type: "string", description: "The match request ID" },
61
+ accepted: { type: "boolean", description: "Whether to accept the match" },
62
+ message: { type: "string", description: "Optional response message" },
63
+ },
64
+ required: ["request_id", "accepted"],
65
+ },
66
+ },
67
+ {
68
+ name: "get_matched_pairs",
69
+ description: "Get all accepted matches in the system",
70
+ inputSchema: { type: "object", properties: {} },
71
+ },
72
+ ],
73
+ }));
74
+ server.setRequestHandler(CallToolRequestSchema, async (request) => {
75
+ const { name, arguments: args } = request.params;
76
+ const a = (args ?? {});
77
+ try {
78
+ switch (name) {
79
+ case "create_profile": {
80
+ const profile = {
81
+ id: USER_ID,
82
+ name: a.name,
83
+ age: a.age,
84
+ bio: a.bio,
85
+ preferences: a.preferences,
86
+ dealbreakers: a.dealbreakers,
87
+ createdAt: new Date().toISOString(),
88
+ };
89
+ await redis.set(`dating:profile:${USER_ID}`, JSON.stringify(profile));
90
+ return { content: [{ type: "text", text: `Profile created! Your ID: ${USER_ID}` }] };
91
+ }
92
+ case "get_my_profile": {
93
+ const data = await redis.get(`dating:profile:${USER_ID}`);
94
+ if (!data) {
95
+ return { content: [{ type: "text", text: "No profile yet — let's create one!" }] };
96
+ }
97
+ return { content: [{ type: "text", text: data }] };
98
+ }
99
+ case "list_profiles": {
100
+ const keys = await redis.keys("dating:profile:*");
101
+ if (keys.length === 0) {
102
+ return { content: [{ type: "text", text: "[]" }] };
103
+ }
104
+ const profiles = await Promise.all(keys.map((k) => redis.get(k)));
105
+ const result = profiles.filter(Boolean).map((p) => JSON.parse(p));
106
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
107
+ }
108
+ case "request_match": {
109
+ const id = randomUUID();
110
+ const req = {
111
+ id,
112
+ fromUserId: USER_ID,
113
+ toUserId: a.target_profile_id,
114
+ introMessage: a.intro_message,
115
+ status: "pending",
116
+ createdAt: new Date().toISOString(),
117
+ };
118
+ await redis.set(`dating:match_request:${id}`, JSON.stringify(req));
119
+ await redis.sadd(`dating:requests_to:${a.target_profile_id}`, id);
120
+ return { content: [{ type: "text", text: `Match request sent! Request ID: ${id}` }] };
121
+ }
122
+ case "get_match_requests": {
123
+ const requestIds = await redis.smembers(`dating:requests_to:${USER_ID}`);
124
+ if (requestIds.length === 0) {
125
+ return { content: [{ type: "text", text: "No pending match requests." }] };
126
+ }
127
+ const requests = await Promise.all(requestIds.map((id) => redis.get(`dating:match_request:${id}`)));
128
+ const result = requests.filter(Boolean).map((r) => JSON.parse(r));
129
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
130
+ }
131
+ case "respond_to_match": {
132
+ const data = await redis.get(`dating:match_request:${a.request_id}`);
133
+ if (!data) {
134
+ return { content: [{ type: "text", text: "Request not found." }] };
135
+ }
136
+ const req = JSON.parse(data);
137
+ req.status = a.accepted ? "accepted" : "declined";
138
+ if (a.message)
139
+ req.responseMessage = a.message;
140
+ req.respondedAt = new Date().toISOString();
141
+ await redis.set(`dating:match_request:${a.request_id}`, JSON.stringify(req));
142
+ if (a.accepted) {
143
+ const pairId = randomUUID();
144
+ const pair = {
145
+ id: pairId,
146
+ user1: req.fromUserId,
147
+ user2: req.toUserId,
148
+ matchedAt: new Date().toISOString(),
149
+ };
150
+ await redis.set(`dating:match:${pairId}`, JSON.stringify(pair));
151
+ await redis.sadd("dating:matches", pairId);
152
+ }
153
+ return {
154
+ content: [{ type: "text", text: `Request ${a.accepted ? "accepted! You have a new match 🎉" : "declined."}` }],
155
+ };
156
+ }
157
+ case "get_matched_pairs": {
158
+ const pairIds = await redis.smembers("dating:matches");
159
+ if (pairIds.length === 0) {
160
+ return { content: [{ type: "text", text: "No matches yet." }] };
161
+ }
162
+ const pairs = await Promise.all(pairIds.map((id) => redis.get(`dating:match:${id}`)));
163
+ const result = pairs.filter(Boolean).map((p) => JSON.parse(p));
164
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
165
+ }
166
+ default:
167
+ return { content: [{ type: "text", text: `Unknown tool: ${name}` }], isError: true };
168
+ }
169
+ }
170
+ catch (error) {
171
+ const msg = error instanceof Error ? error.message : String(error);
172
+ return { content: [{ type: "text", text: `Error: ${msg}` }], isError: true };
173
+ }
174
+ });
175
+ const transport = new StdioServerTransport();
176
+ await server.connect(transport);
177
+ //# sourceMappingURL=mcp-server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mcp-server.js","sourceRoot":"","sources":["../src/mcp-server.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,104 @@
1
+ export type Brand = "meet-the-one" | "casual" | "kink" | "adventure" | "open-poly";
2
+ export interface User {
3
+ id: string;
4
+ phone_e164: string;
5
+ email?: string;
6
+ brand: Brand;
7
+ created_at: string;
8
+ verified_at?: string;
9
+ }
10
+ export type SessionSource = "vapi" | "twilio_direct" | "text";
11
+ export type TimeOfDayBucket = "morning" | "afternoon" | "evening" | "late_night";
12
+ export type AnalysisStatus = "pending" | "processing" | "complete" | "failed" | "stale";
13
+ export interface Session {
14
+ session_id: string;
15
+ user_id: string;
16
+ started_at: string;
17
+ ended_at?: string;
18
+ duration_seconds?: number;
19
+ source: SessionSource;
20
+ time_of_day_bucket: TimeOfDayBucket;
21
+ session_number: number;
22
+ late_night_flag: boolean;
23
+ audio_r2_key?: string;
24
+ transcript_r2_key?: string;
25
+ transcription_status: "pending" | "processing" | "complete" | "failed";
26
+ analysis_status: AnalysisStatus;
27
+ }
28
+ export type Modality = "long-term" | "casual" | "kink" | "open-relationship" | "polyamory" | "swinging" | "friends";
29
+ export type ModalityWeights = Record<Modality, number>;
30
+ export interface DomainProfile {
31
+ confidence: number;
32
+ evidence: string[];
33
+ [key: string]: unknown;
34
+ }
35
+ export interface BaseIdentityProfile {
36
+ updated_at: string;
37
+ session_count: number;
38
+ total_minutes: number;
39
+ relationships: DomainProfile & {
40
+ attachment_style: string;
41
+ trust_pattern: string;
42
+ conflict_mode: string;
43
+ depth_vs_breadth: number;
44
+ };
45
+ desire: DomainProfile & {
46
+ expressed_desires: string[];
47
+ inferred_desires: string[];
48
+ kink_vanilla_spectrum: number;
49
+ shame_sovereignty_score: number;
50
+ };
51
+ money: DomainProfile & {
52
+ scarcity_abundance_orientation: number;
53
+ risk_tolerance: number;
54
+ ambition_contentment: number;
55
+ financial_mythology_active: string[];
56
+ };
57
+ health: DomainProfile & {
58
+ energy_pattern: string;
59
+ physicality_orientation: number;
60
+ self_care_mode: string;
61
+ };
62
+ worldview: {
63
+ world_danger_adventure: number;
64
+ people_good_bad: number;
65
+ vals_type: string;
66
+ vals_confidence: number;
67
+ games_active: string[];
68
+ symbolic_responses: Array<{
69
+ prompt: string;
70
+ response: string;
71
+ valence: string;
72
+ }>;
73
+ };
74
+ }
75
+ export interface Identity {
76
+ user_id: string;
77
+ updated_at: string;
78
+ version: number;
79
+ base_profile: BaseIdentityProfile;
80
+ modality_weights: ModalityWeights;
81
+ signal_completeness_score: number;
82
+ ready_for_matching: boolean;
83
+ embedding_id?: string;
84
+ }
85
+ export type MatchStatus = "pending_consent" | "a_accepted" | "b_accepted" | "both_accepted" | "declined" | "introduced" | "stale";
86
+ export interface Match {
87
+ match_id: string;
88
+ user_a_id: string;
89
+ user_b_id: string;
90
+ primary_modality: Modality;
91
+ cross_modality: boolean;
92
+ cross_modality_bridge?: string;
93
+ confidence_score: number;
94
+ resonances: string[];
95
+ tensions: string[];
96
+ tension_fatal: boolean;
97
+ consent_call_framing: string;
98
+ status: MatchStatus;
99
+ created_at: string;
100
+ a_consented_at?: string;
101
+ b_consented_at?: string;
102
+ outcome?: "positive" | "negative";
103
+ }
104
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/types/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ // ─── User & Identity ──────────────────────────────────────────────────────────
2
+ export {};
3
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/types/index.ts"],"names":[],"mappings":"AAAA,iFAAiF"}
package/package.json ADDED
@@ -0,0 +1,28 @@
1
+ {
2
+ "name": "@gonzih/meet-the-one-ai",
3
+ "version": "1.0.0",
4
+ "description": "AI-powered dating service using Claude Code agents",
5
+ "main": "dist/index.js",
6
+ "type": "module",
7
+ "bin": { "meet-the-one-ai": "dist/index.js" },
8
+ "scripts": {
9
+ "dev": "tsx watch src/index.ts",
10
+ "build": "tsc",
11
+ "start": "node dist/index.js",
12
+ "typecheck": "tsc --noEmit"
13
+ },
14
+ "keywords": ["dating", "ai", "claude", "telegram"],
15
+ "author": "",
16
+ "license": "ISC",
17
+ "devDependencies": {
18
+ "@types/node": "22.15.3",
19
+ "tsx": "4.19.4",
20
+ "typescript": "5.8.3"
21
+ },
22
+ "dependencies": {
23
+ "@modelcontextprotocol/sdk": "1.29.0",
24
+ "dotenv": "16.5.0",
25
+ "ioredis": "5.6.1",
26
+ "telegraf": "4.16.3"
27
+ }
28
+ }
package/railway.json ADDED
@@ -0,0 +1,14 @@
1
+ {
2
+ "$schema": "https://railway.com/railway.schema.json",
3
+ "build": {
4
+ "builder": "NIXPACKS",
5
+ "buildCommand": "npm run build"
6
+ },
7
+ "deploy": {
8
+ "startCommand": "npm run migrate && npm start",
9
+ "healthcheckPath": "/",
10
+ "healthcheckTimeout": 30,
11
+ "restartPolicyType": "ON_FAILURE",
12
+ "restartPolicyMaxRetries": 3
13
+ }
14
+ }
package/src/agent.ts ADDED
@@ -0,0 +1,123 @@
1
+ import { spawn } from "child_process";
2
+ import { writeFile } from "fs/promises";
3
+ import { tmpdir } from "os";
4
+ import { join, dirname } from "path";
5
+ import { fileURLToPath } from "url";
6
+ import { Redis } from "ioredis";
7
+
8
+ const __dirname = dirname(fileURLToPath(import.meta.url));
9
+ const redis = new Redis(process.env.REDIS_URL ?? "redis://localhost:6379");
10
+
11
+ const SYSTEM_PROMPT =
12
+ "You are a compassionate AI matchmaker helping people find meaningful connections. " +
13
+ "Use the dating tools to manage profiles and matches. " +
14
+ "Be warm, curious, and insightful. Ask about values, life goals, and what matters to them — not just surface traits. " +
15
+ "When a user first arrives, help them create their profile by asking thoughtful questions. " +
16
+ "Proactively look for compatible matches and suggest them with care.";
17
+
18
+ async function getMcpConfigPath(userId: string): Promise<string> {
19
+ const configPath = join(tmpdir(), `mcp-dating-${userId}.json`);
20
+ // In dev use src path; in prod use compiled dist path
21
+ const mcpServerPath = join(__dirname, "../dist/mcp-server.js");
22
+ const config = {
23
+ mcpServers: {
24
+ dating: {
25
+ command: "node",
26
+ args: [mcpServerPath],
27
+ env: {
28
+ USER_ID: userId,
29
+ REDIS_URL: process.env.REDIS_URL ?? "redis://localhost:6379",
30
+ },
31
+ },
32
+ },
33
+ };
34
+ await writeFile(configPath, JSON.stringify(config, null, 2));
35
+ return configPath;
36
+ }
37
+
38
+ export async function sendMessage(userId: string, message: string): Promise<string> {
39
+ const mcpConfigPath = await getMcpConfigPath(userId);
40
+ const sessionId = await redis.get(`dating:session:${userId}`);
41
+
42
+ const args = [
43
+ "--print",
44
+ "--dangerously-skip-permissions",
45
+ "--output-format",
46
+ "stream-json",
47
+ "--mcp-config",
48
+ mcpConfigPath,
49
+ ];
50
+
51
+ if (sessionId) {
52
+ args.push("--resume", sessionId);
53
+ } else {
54
+ // Prepend system context on first message
55
+ args.push("--system-prompt", SYSTEM_PROMPT);
56
+ }
57
+
58
+ return new Promise((resolve, reject) => {
59
+ const proc = spawn("claude", args, {
60
+ stdio: ["pipe", "pipe", "pipe"],
61
+ env: { ...process.env },
62
+ });
63
+
64
+ let outputText = "";
65
+ let resultSessionId = "";
66
+ let stderr = "";
67
+
68
+ proc.stdout.on("data", (chunk: Buffer) => {
69
+ for (const line of chunk.toString().split("\n")) {
70
+ if (!line.trim()) continue;
71
+ try {
72
+ const event = JSON.parse(line) as Record<string, unknown>;
73
+ if (event.type === "assistant") {
74
+ const msg = event.message as { content?: Array<{ type: string; text?: string }> } | undefined;
75
+ if (msg?.content) {
76
+ for (const block of msg.content) {
77
+ if (block.type === "text" && block.text) {
78
+ outputText += block.text;
79
+ }
80
+ }
81
+ }
82
+ } else if (event.type === "result") {
83
+ resultSessionId = (event.session_id as string | undefined) ?? "";
84
+ }
85
+ } catch {
86
+ // non-JSON line — ignore
87
+ }
88
+ }
89
+ });
90
+
91
+ proc.stderr.on("data", (chunk: Buffer) => {
92
+ stderr += chunk.toString();
93
+ });
94
+
95
+ proc.on("close", async (code) => {
96
+ if (resultSessionId) {
97
+ await redis.set(`dating:session:${userId}`, resultSessionId).catch(console.error);
98
+ }
99
+ if (!outputText && code !== 0) {
100
+ console.error(`[agent:${userId}] exited ${code}: ${stderr}`);
101
+ reject(new Error(`Claude exited with code ${code}`));
102
+ } else {
103
+ resolve(outputText || "I'm here to help you find love! What would you like to explore?");
104
+ }
105
+ });
106
+
107
+ proc.on("error", (err) => {
108
+ reject(new Error(`Failed to spawn claude: ${err.message}`));
109
+ });
110
+
111
+ // Send user message via stdin in stream-json format
112
+ const input =
113
+ JSON.stringify({
114
+ type: "user",
115
+ message: { role: "user", content: message },
116
+ }) + "\n";
117
+
118
+ proc.stdin.write(input, (err) => {
119
+ if (err) console.error(`[agent:${userId}] stdin write error:`, err);
120
+ });
121
+ proc.stdin.end();
122
+ });
123
+ }
@@ -0,0 +1,95 @@
1
+ import { Router } from "express";
2
+ import { z } from "zod";
3
+ import { send_otp, verify_otp, normalize_phone } from "../../lib/twilio.js";
4
+ import { supabase } from "../../lib/supabase.js";
5
+ import { trigger_intake_call } from "../../lib/vapi.js";
6
+
7
+ export const auth_router = Router();
8
+
9
+ const RequestOtpBody = z.object({
10
+ phone: z.string().min(7),
11
+ brand: z.string().optional().default("flagship"),
12
+ });
13
+
14
+ const VerifyOtpBody = z.object({
15
+ phone: z.string().min(7),
16
+ code: z.string().length(6),
17
+ brand: z.string().optional().default("flagship"),
18
+ });
19
+
20
+ // POST /api/auth/request-otp
21
+ auth_router.post("/request-otp", async (req, res) => {
22
+ const parsed = RequestOtpBody.safeParse(req.body);
23
+ if (!parsed.success) {
24
+ res.status(400).json({ error: parsed.error.flatten() });
25
+ return;
26
+ }
27
+
28
+ let phone: string;
29
+ try {
30
+ phone = normalize_phone(parsed.data.phone);
31
+ } catch {
32
+ res.status(400).json({ error: "Invalid phone number" });
33
+ return;
34
+ }
35
+
36
+ try {
37
+ await send_otp(phone);
38
+ res.json({ ok: true });
39
+ } catch (err) {
40
+ console.error("send_otp error", err);
41
+ res.status(500).json({ error: "Failed to send OTP" });
42
+ }
43
+ });
44
+
45
+ // POST /api/auth/verify-otp
46
+ auth_router.post("/verify-otp", async (req, res) => {
47
+ const parsed = VerifyOtpBody.safeParse(req.body);
48
+ if (!parsed.success) {
49
+ res.status(400).json({ error: parsed.error.flatten() });
50
+ return;
51
+ }
52
+
53
+ let phone: string;
54
+ try {
55
+ phone = normalize_phone(parsed.data.phone);
56
+ } catch {
57
+ res.status(400).json({ error: "Invalid phone number" });
58
+ return;
59
+ }
60
+
61
+ const ok = await verify_otp(phone, parsed.data.code);
62
+ if (!ok) {
63
+ res.status(401).json({ error: "Invalid or expired code" });
64
+ return;
65
+ }
66
+
67
+ // Upsert user
68
+ const { data: user, error } = await supabase
69
+ .from("users")
70
+ .upsert(
71
+ { phone, brand: parsed.data.brand, last_active: new Date().toISOString() },
72
+ { onConflict: "phone" }
73
+ )
74
+ .select("id, phone, brand, created_at")
75
+ .single();
76
+
77
+ if (error || !user) {
78
+ console.error("upsert user error", error);
79
+ res.status(500).json({ error: "Failed to create user" });
80
+ return;
81
+ }
82
+
83
+ // New user (created_at === last_active within a few ms) → trigger intake call
84
+ const is_new_user =
85
+ new Date(user.created_at).getTime() > Date.now() - 5000;
86
+
87
+ if (is_new_user) {
88
+ // Fire-and-forget — don't block the auth response
89
+ trigger_intake_call(phone, user.id).catch((err) =>
90
+ console.error("trigger_intake_call error", err)
91
+ );
92
+ }
93
+
94
+ res.json({ ok: true, user, is_new_user });
95
+ });