@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.
- package/.env.example +41 -0
- package/.node-version +1 -0
- package/basis/BERNAYS.md +233 -0
- package/basis/FOUNDING_TRANSCRIPT.md +218 -0
- package/basis/TECH_SPEC.md +303 -0
- package/basis/VALS.md +255 -0
- package/basis/layers/L1_IDENTITY_AUTH.md +78 -0
- package/basis/layers/L2_CONVERSATION.md +159 -0
- package/basis/layers/L3_RECORDING_STORE.md +104 -0
- package/basis/layers/L4_ANALYSIS_PIPELINE.md +257 -0
- package/basis/layers/L5_MATCHING_ENGINE.md +164 -0
- package/basis/layers/L6_CONSENT_INTRODUCTION.md +143 -0
- package/basis/layers/L7_PORTABLE_IDENTITY.md +139 -0
- package/basis/layers/STACK.md +64 -0
- package/basis/schema.sql +203 -0
- package/dist/agent.d.ts +2 -0
- package/dist/agent.d.ts.map +1 -0
- package/dist/agent.js +114 -0
- package/dist/agent.js.map +1 -0
- package/dist/api/routes/auth.d.ts +2 -0
- package/dist/api/routes/auth.d.ts.map +1 -0
- package/dist/api/routes/auth.js +79 -0
- package/dist/api/routes/auth.js.map +1 -0
- package/dist/api/routes/identity.d.ts +2 -0
- package/dist/api/routes/identity.d.ts.map +1 -0
- package/dist/api/routes/identity.js +92 -0
- package/dist/api/routes/identity.js.map +1 -0
- package/dist/api/routes/text-submission.d.ts +2 -0
- package/dist/api/routes/text-submission.d.ts.map +1 -0
- package/dist/api/routes/text-submission.js +56 -0
- package/dist/api/routes/text-submission.js.map +1 -0
- package/dist/api/webhooks/twilio.d.ts +2 -0
- package/dist/api/webhooks/twilio.d.ts.map +1 -0
- package/dist/api/webhooks/twilio.js +144 -0
- package/dist/api/webhooks/twilio.js.map +1 -0
- package/dist/api/webhooks/vapi.d.ts +2 -0
- package/dist/api/webhooks/vapi.d.ts.map +1 -0
- package/dist/api/webhooks/vapi.js +177 -0
- package/dist/api/webhooks/vapi.js.map +1 -0
- package/dist/bot.d.ts +3 -0
- package/dist/bot.d.ts.map +1 -0
- package/dist/bot.js +39 -0
- package/dist/bot.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +9 -0
- package/dist/index.js.map +1 -0
- package/dist/jobs/compact-identity.d.ts +2 -0
- package/dist/jobs/compact-identity.d.ts.map +1 -0
- package/dist/jobs/compact-identity.js +159 -0
- package/dist/jobs/compact-identity.js.map +1 -0
- package/dist/jobs/consent-call.d.ts +2 -0
- package/dist/jobs/consent-call.d.ts.map +1 -0
- package/dist/jobs/consent-call.js +70 -0
- package/dist/jobs/consent-call.js.map +1 -0
- package/dist/jobs/export-identity.d.ts +2 -0
- package/dist/jobs/export-identity.d.ts.map +1 -0
- package/dist/jobs/export-identity.js +129 -0
- package/dist/jobs/export-identity.js.map +1 -0
- package/dist/jobs/introduction-call.d.ts +2 -0
- package/dist/jobs/introduction-call.d.ts.map +1 -0
- package/dist/jobs/introduction-call.js +86 -0
- package/dist/jobs/introduction-call.js.map +1 -0
- package/dist/jobs/reanalyze-identity.d.ts +2 -0
- package/dist/jobs/reanalyze-identity.d.ts.map +1 -0
- package/dist/jobs/reanalyze-identity.js +56 -0
- package/dist/jobs/reanalyze-identity.js.map +1 -0
- package/dist/jobs/run-matching.d.ts +2 -0
- package/dist/jobs/run-matching.d.ts.map +1 -0
- package/dist/jobs/run-matching.js +200 -0
- package/dist/jobs/run-matching.js.map +1 -0
- package/dist/jobs/scheduled-matching.d.ts +2 -0
- package/dist/jobs/scheduled-matching.d.ts.map +1 -0
- package/dist/jobs/scheduled-matching.js +44 -0
- package/dist/jobs/scheduled-matching.js.map +1 -0
- package/dist/jobs/transcribe-session.d.ts +2 -0
- package/dist/jobs/transcribe-session.d.ts.map +1 -0
- package/dist/jobs/transcribe-session.js +66 -0
- package/dist/jobs/transcribe-session.js.map +1 -0
- package/dist/lib/anthropic.d.ts +4 -0
- package/dist/lib/anthropic.d.ts.map +1 -0
- package/dist/lib/anthropic.js +32 -0
- package/dist/lib/anthropic.js.map +1 -0
- package/dist/lib/config.d.ts +57 -0
- package/dist/lib/config.d.ts.map +1 -0
- package/dist/lib/config.js +73 -0
- package/dist/lib/config.js.map +1 -0
- package/dist/lib/deepgram.d.ts +15 -0
- package/dist/lib/deepgram.d.ts.map +1 -0
- package/dist/lib/deepgram.js +37 -0
- package/dist/lib/deepgram.js.map +1 -0
- package/dist/lib/inngest.d.ts +42 -0
- package/dist/lib/inngest.d.ts.map +1 -0
- package/dist/lib/inngest.js +7 -0
- package/dist/lib/inngest.js.map +1 -0
- package/dist/lib/openai.d.ts +3 -0
- package/dist/lib/openai.d.ts.map +1 -0
- package/dist/lib/openai.js +13 -0
- package/dist/lib/openai.js.map +1 -0
- package/dist/lib/prompts.d.ts +8 -0
- package/dist/lib/prompts.d.ts.map +1 -0
- package/dist/lib/prompts.js +258 -0
- package/dist/lib/prompts.js.map +1 -0
- package/dist/lib/r2.d.ts +7 -0
- package/dist/lib/r2.d.ts.map +1 -0
- package/dist/lib/r2.js +49 -0
- package/dist/lib/r2.js.map +1 -0
- package/dist/lib/session-helpers.d.ts +8 -0
- package/dist/lib/session-helpers.d.ts.map +1 -0
- package/dist/lib/session-helpers.js +31 -0
- package/dist/lib/session-helpers.js.map +1 -0
- package/dist/lib/supabase.d.ts +2 -0
- package/dist/lib/supabase.d.ts.map +1 -0
- package/dist/lib/supabase.js +11 -0
- package/dist/lib/supabase.js.map +1 -0
- package/dist/lib/twilio.d.ts +7 -0
- package/dist/lib/twilio.d.ts.map +1 -0
- package/dist/lib/twilio.js +34 -0
- package/dist/lib/twilio.js.map +1 -0
- package/dist/lib/vapi.d.ts +4 -0
- package/dist/lib/vapi.d.ts.map +1 -0
- package/dist/lib/vapi.js +59 -0
- package/dist/lib/vapi.js.map +1 -0
- package/dist/mcp-server.d.ts +3 -0
- package/dist/mcp-server.d.ts.map +1 -0
- package/dist/mcp-server.js +177 -0
- package/dist/mcp-server.js.map +1 -0
- package/dist/types/index.d.ts +104 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +3 -0
- package/dist/types/index.js.map +1 -0
- package/package.json +28 -0
- package/railway.json +14 -0
- package/src/agent.ts +123 -0
- package/src/api/routes/auth.ts +95 -0
- package/src/api/routes/identity.ts +112 -0
- package/src/api/routes/text-submission.ts +64 -0
- package/src/api/webhooks/twilio.ts +181 -0
- package/src/api/webhooks/vapi.ts +219 -0
- package/src/bot.ts +44 -0
- package/src/index.ts +11 -0
- package/src/jobs/compact-identity.ts +211 -0
- package/src/jobs/consent-call.ts +87 -0
- package/src/jobs/export-identity.ts +166 -0
- package/src/jobs/introduction-call.ts +101 -0
- package/src/jobs/reanalyze-identity.ts +65 -0
- package/src/jobs/run-matching.ts +243 -0
- package/src/jobs/scheduled-matching.ts +59 -0
- package/src/jobs/transcribe-session.ts +77 -0
- package/src/lib/anthropic.ts +37 -0
- package/src/lib/config.ts +81 -0
- package/src/lib/deepgram.ts +57 -0
- package/src/lib/inngest.ts +33 -0
- package/src/lib/openai.ts +14 -0
- package/src/lib/prompts.ts +266 -0
- package/src/lib/r2.ts +79 -0
- package/src/lib/session-helpers.ts +37 -0
- package/src/lib/supabase.ts +15 -0
- package/src/lib/twilio.ts +49 -0
- package/src/lib/vapi.ts +80 -0
- package/src/mcp-server.ts +195 -0
- package/src/types/index.ts +146 -0
- package/supabase/.branches/_current_branch +1 -0
- package/supabase/.temp/cli-latest +1 -0
- package/supabase/.temp/gotrue-version +1 -0
- package/supabase/.temp/pooler-url +1 -0
- package/supabase/.temp/postgres-version +1 -0
- package/supabase/.temp/project-ref +1 -0
- package/supabase/.temp/rest-version +1 -0
- package/supabase/.temp/storage-migration +1 -0
- package/supabase/.temp/storage-version +1 -0
- package/supabase/config.toml +384 -0
- package/supabase/migrations/20260303000000_initial_schema.sql +203 -0
- package/supabase/migrations/20260304000000_brand_consents.sql +13 -0
- package/tsconfig.json +25 -0
package/dist/bot.js
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { Telegraf } from "telegraf";
|
|
2
|
+
import { sendMessage } from "./agent.js";
|
|
3
|
+
const TOKEN = process.env.TELEGRAM_BOT_TOKEN;
|
|
4
|
+
export function createBot() {
|
|
5
|
+
if (!TOKEN)
|
|
6
|
+
throw new Error("TELEGRAM_BOT_TOKEN env var is required");
|
|
7
|
+
const bot = new Telegraf(TOKEN);
|
|
8
|
+
bot.start(async (ctx) => {
|
|
9
|
+
const userId = String(ctx.from.id);
|
|
10
|
+
await ctx.reply("Welcome to Meet The One AI! I'm your personal AI matchmaker.\n\n" +
|
|
11
|
+
"I'll help you find someone truly compatible — not just based on looks, " +
|
|
12
|
+
"but on values, goals, and what matters most to you.\n\n" +
|
|
13
|
+
"Let's start by getting to know you. What's your name?");
|
|
14
|
+
try {
|
|
15
|
+
const response = await sendMessage(userId, "I just joined. Please help me set up my profile.");
|
|
16
|
+
if (response)
|
|
17
|
+
await ctx.reply(response);
|
|
18
|
+
}
|
|
19
|
+
catch (err) {
|
|
20
|
+
console.error(`[bot] /start error for ${userId}:`, err);
|
|
21
|
+
}
|
|
22
|
+
});
|
|
23
|
+
bot.on("text", async (ctx) => {
|
|
24
|
+
const userId = String(ctx.from.id);
|
|
25
|
+
try {
|
|
26
|
+
const response = await sendMessage(userId, ctx.message.text);
|
|
27
|
+
if (response)
|
|
28
|
+
await ctx.reply(response);
|
|
29
|
+
}
|
|
30
|
+
catch (err) {
|
|
31
|
+
console.error(`[bot] message error for ${userId}:`, err);
|
|
32
|
+
await ctx
|
|
33
|
+
.reply("I ran into a technical issue. Please try again in a moment.")
|
|
34
|
+
.catch(() => { });
|
|
35
|
+
}
|
|
36
|
+
});
|
|
37
|
+
return bot;
|
|
38
|
+
}
|
|
39
|
+
//# sourceMappingURL=bot.js.map
|
package/dist/bot.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bot.js","sourceRoot":"","sources":["../src/bot.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAC;AACpC,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAEzC,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC,kBAAkB,CAAC;AAE7C,MAAM,UAAU,SAAS;IACvB,IAAI,CAAC,KAAK;QAAE,MAAM,IAAI,KAAK,CAAC,wCAAwC,CAAC,CAAC;IAEtE,MAAM,GAAG,GAAG,IAAI,QAAQ,CAAC,KAAK,CAAC,CAAC;IAEhC,GAAG,CAAC,KAAK,CAAC,KAAK,EAAE,GAAG,EAAE,EAAE;QACtB,MAAM,MAAM,GAAG,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QAEnC,MAAM,GAAG,CAAC,KAAK,CACb,kEAAkE;YAChE,yEAAyE;YACzE,yDAAyD;YACzD,uDAAuD,CAC1D,CAAC;QAEF,IAAI,CAAC;YACH,MAAM,QAAQ,GAAG,MAAM,WAAW,CAAC,MAAM,EAAE,kDAAkD,CAAC,CAAC;YAC/F,IAAI,QAAQ;gBAAE,MAAM,GAAG,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;QAC1C,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,OAAO,CAAC,KAAK,CAAC,0BAA0B,MAAM,GAAG,EAAE,GAAG,CAAC,CAAC;QAC1D,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,GAAG,CAAC,EAAE,CAAC,MAAM,EAAE,KAAK,EAAE,GAAG,EAAE,EAAE;QAC3B,MAAM,MAAM,GAAG,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QAEnC,IAAI,CAAC;YACH,MAAM,QAAQ,GAAG,MAAM,WAAW,CAAC,MAAM,EAAE,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;YAC7D,IAAI,QAAQ;gBAAE,MAAM,GAAG,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;QAC1C,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,OAAO,CAAC,KAAK,CAAC,2BAA2B,MAAM,GAAG,EAAE,GAAG,CAAC,CAAC;YACzD,MAAM,GAAG;iBACN,KAAK,CAAC,6DAA6D,CAAC;iBACpE,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;QACrB,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,OAAO,GAAG,CAAC;AACb,CAAC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,eAAe,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import "dotenv/config";
|
|
2
|
+
import { createBot } from "./bot.js";
|
|
3
|
+
const bot = createBot();
|
|
4
|
+
bot.launch().then(() => {
|
|
5
|
+
console.log("meet-the-one-ai is running...");
|
|
6
|
+
});
|
|
7
|
+
process.once("SIGTERM", () => bot.stop("SIGTERM"));
|
|
8
|
+
process.once("SIGINT", () => bot.stop("SIGINT"));
|
|
9
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,eAAe,CAAC;AACvB,OAAO,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAErC,MAAM,GAAG,GAAG,SAAS,EAAE,CAAC;AAExB,GAAG,CAAC,MAAM,EAAE,CAAC,IAAI,CAAC,GAAG,EAAE;IACrB,OAAO,CAAC,GAAG,CAAC,+BAA+B,CAAC,CAAC;AAC/C,CAAC,CAAC,CAAC;AAEH,OAAO,CAAC,IAAI,CAAC,SAAS,EAAE,GAAG,EAAE,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC;AACnD,OAAO,CAAC,IAAI,CAAC,QAAQ,EAAE,GAAG,EAAE,CAAC,GAAG,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"compact-identity.d.ts","sourceRoot":"","sources":["../../src/jobs/compact-identity.ts"],"names":[],"mappings":"AAgBA,eAAO,MAAM,gBAAgB,KAoL5B,CAAC"}
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
import { inngest } from "../lib/inngest.js";
|
|
2
|
+
import { supabase } from "../lib/supabase.js";
|
|
3
|
+
import { complete, extract_json } from "../lib/anthropic.js";
|
|
4
|
+
import { embed } from "../lib/openai.js";
|
|
5
|
+
import { download_json, upload_json } from "../lib/r2.js";
|
|
6
|
+
import { SYSTEM_SIGNAL_EXTRACTION, SYSTEM_IDENTITY_MERGE, SYSTEM_MODALITY_RENDER, } from "../lib/prompts.js";
|
|
7
|
+
// Triggered after transcription completes (or after text submission)
|
|
8
|
+
// Pass 1: Extract signals from session
|
|
9
|
+
// Pass 2: Merge signals into compacted identity
|
|
10
|
+
// Pass 3: Render modality-specific portraits → embed per modality
|
|
11
|
+
export const compact_identity = inngest.createFunction({ id: "compact-identity", retries: 2 }, { event: "session/transcribed" }, async ({ event, step }) => {
|
|
12
|
+
const { user_id, session_id, transcript_r2_key } = event.data;
|
|
13
|
+
await step.run("mark-processing", async () => {
|
|
14
|
+
await supabase
|
|
15
|
+
.from("sessions")
|
|
16
|
+
.update({ analysis_status: "processing" })
|
|
17
|
+
.eq("id", session_id);
|
|
18
|
+
});
|
|
19
|
+
// Load transcript or raw text
|
|
20
|
+
const user_transcript = await step.run("load-transcript", async () => {
|
|
21
|
+
if (transcript_r2_key) {
|
|
22
|
+
const data = await download_json(transcript_r2_key);
|
|
23
|
+
return data.text;
|
|
24
|
+
}
|
|
25
|
+
const { data, error } = await supabase
|
|
26
|
+
.from("sessions")
|
|
27
|
+
.select("raw_text")
|
|
28
|
+
.eq("id", session_id)
|
|
29
|
+
.single();
|
|
30
|
+
if (error || !data?.raw_text)
|
|
31
|
+
throw new Error("No transcript or raw_text found");
|
|
32
|
+
return data.raw_text;
|
|
33
|
+
});
|
|
34
|
+
const session_meta = await step.run("load-session-meta", async () => {
|
|
35
|
+
const { data, error } = await supabase
|
|
36
|
+
.from("sessions")
|
|
37
|
+
.select("time_of_day, duration_seconds, source")
|
|
38
|
+
.eq("id", session_id)
|
|
39
|
+
.single();
|
|
40
|
+
if (error || !data)
|
|
41
|
+
throw new Error("Session not found");
|
|
42
|
+
return data;
|
|
43
|
+
});
|
|
44
|
+
const { count: completed_count } = await supabase
|
|
45
|
+
.from("sessions")
|
|
46
|
+
.select("*", { count: "exact", head: true })
|
|
47
|
+
.eq("user_id", user_id)
|
|
48
|
+
.eq("analysis_status", "complete");
|
|
49
|
+
const session_number = (completed_count ?? 0) + 1;
|
|
50
|
+
// ── Pass 1: Signal extraction ─────────────────────────────────────────────
|
|
51
|
+
const session_signals_raw = await step.run("extract-signals", async () => {
|
|
52
|
+
const is_late_night = session_meta.time_of_day === "late_night";
|
|
53
|
+
return complete("analysis", SYSTEM_SIGNAL_EXTRACTION, [
|
|
54
|
+
`Session #${session_number} (source: ${session_meta.source})`,
|
|
55
|
+
`Time: ${session_meta.time_of_day}${is_late_night ? " [HIGH SIGNAL — 4AM PRINCIPLE]" : ""}`,
|
|
56
|
+
`Duration: ${session_meta.duration_seconds ?? "unknown"}s`,
|
|
57
|
+
`\nTranscript:\n${user_transcript}`,
|
|
58
|
+
].join("\n"));
|
|
59
|
+
});
|
|
60
|
+
const existing_identity = await step.run("load-identity", async () => {
|
|
61
|
+
const { data } = await supabase
|
|
62
|
+
.from("identities")
|
|
63
|
+
.select("base_profile, modality_weights, session_count")
|
|
64
|
+
.eq("user_id", user_id)
|
|
65
|
+
.single();
|
|
66
|
+
return data;
|
|
67
|
+
});
|
|
68
|
+
// ── Pass 2: Identity merge ────────────────────────────────────────────────
|
|
69
|
+
const merged_raw = await step.run("merge-identity", async () => {
|
|
70
|
+
const existing_str = existing_identity
|
|
71
|
+
? JSON.stringify(existing_identity.base_profile, null, 2)
|
|
72
|
+
: "null (first session)";
|
|
73
|
+
return complete("analysis", SYSTEM_IDENTITY_MERGE, [
|
|
74
|
+
`Existing identity:\n${existing_str}`,
|
|
75
|
+
`\nNew session signals:\n${session_signals_raw}`,
|
|
76
|
+
`\nSession metadata: ${JSON.stringify(session_meta)}`,
|
|
77
|
+
`\nTotal sessions completed: ${session_number}`,
|
|
78
|
+
].join("\n\n"));
|
|
79
|
+
});
|
|
80
|
+
const { base_profile, modality_weights } = await step.run("parse-output", async () => {
|
|
81
|
+
try {
|
|
82
|
+
return JSON.parse(extract_json(merged_raw));
|
|
83
|
+
}
|
|
84
|
+
catch {
|
|
85
|
+
throw new Error(`LLM returned invalid JSON for identity merge: ${merged_raw.slice(0, 200)}`);
|
|
86
|
+
}
|
|
87
|
+
});
|
|
88
|
+
const completeness = compute_completeness(base_profile);
|
|
89
|
+
const ready = completeness >= 0.5 &&
|
|
90
|
+
base_profile.worldview.world_danger_adventure !== undefined;
|
|
91
|
+
const new_session_count = (existing_identity?.session_count ?? 0) + 1;
|
|
92
|
+
// ── Pass 3: Modality-specific portraits + embeddings ──────────────────────
|
|
93
|
+
const active_modalities = Object.entries(modality_weights)
|
|
94
|
+
.filter(([, w]) => w > 0.1)
|
|
95
|
+
.map(([m]) => m);
|
|
96
|
+
await step.run("upsert-modality-embeddings", async () => {
|
|
97
|
+
for (const modality of active_modalities) {
|
|
98
|
+
// Render a focused portrait through this modality's lens
|
|
99
|
+
const portrait = await complete("analysis", SYSTEM_MODALITY_RENDER, [
|
|
100
|
+
`Target modality: ${modality}`,
|
|
101
|
+
`\nFull identity profile:\n${JSON.stringify(base_profile, null, 2)}`,
|
|
102
|
+
`\nModality weights: ${JSON.stringify(modality_weights)}`,
|
|
103
|
+
].join("\n\n"), 512 // short output — dense paragraph only
|
|
104
|
+
);
|
|
105
|
+
const vec = await embed(portrait);
|
|
106
|
+
await supabase.rpc("upsert_modality_embedding", {
|
|
107
|
+
p_user_id: user_id,
|
|
108
|
+
p_modality: modality,
|
|
109
|
+
p_embedding: vec,
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
});
|
|
113
|
+
// ── Save identity ─────────────────────────────────────────────────────────
|
|
114
|
+
await step.run("save-identity", async () => {
|
|
115
|
+
await supabase.from("identities").upsert({
|
|
116
|
+
user_id,
|
|
117
|
+
base_profile,
|
|
118
|
+
modality_weights,
|
|
119
|
+
signal_completeness_score: completeness,
|
|
120
|
+
session_count: new_session_count,
|
|
121
|
+
ready_for_matching: ready,
|
|
122
|
+
last_updated: new Date().toISOString(),
|
|
123
|
+
}, { onConflict: "user_id" });
|
|
124
|
+
});
|
|
125
|
+
// Audit trail to R2
|
|
126
|
+
await step.run("archive-signals", async () => {
|
|
127
|
+
await upload_json(`signals/${user_id}/${session_id}.json`, {
|
|
128
|
+
session_id,
|
|
129
|
+
signals_raw: session_signals_raw,
|
|
130
|
+
base_profile,
|
|
131
|
+
modality_weights,
|
|
132
|
+
completeness,
|
|
133
|
+
merged_at: new Date().toISOString(),
|
|
134
|
+
});
|
|
135
|
+
});
|
|
136
|
+
await step.run("mark-complete", async () => {
|
|
137
|
+
await supabase
|
|
138
|
+
.from("sessions")
|
|
139
|
+
.update({ analysis_status: "complete" })
|
|
140
|
+
.eq("id", session_id);
|
|
141
|
+
});
|
|
142
|
+
await step.sendEvent("trigger-matching", {
|
|
143
|
+
name: "identity/updated",
|
|
144
|
+
data: { user_id },
|
|
145
|
+
});
|
|
146
|
+
return { user_id, completeness, ready_for_matching: ready, session_count: new_session_count };
|
|
147
|
+
});
|
|
148
|
+
function compute_completeness(profile) {
|
|
149
|
+
const domain_scores = [
|
|
150
|
+
profile.relationships.confidence,
|
|
151
|
+
profile.desire.confidence,
|
|
152
|
+
profile.money.confidence,
|
|
153
|
+
profile.health.confidence,
|
|
154
|
+
];
|
|
155
|
+
const domain_avg = domain_scores.reduce((a, b) => a + b, 0) / domain_scores.length;
|
|
156
|
+
const has_worldview = profile.worldview.world_danger_adventure !== undefined ? 1 : 0;
|
|
157
|
+
return domain_avg * 0.7 + has_worldview * 0.3;
|
|
158
|
+
}
|
|
159
|
+
//# sourceMappingURL=compact-identity.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"compact-identity.js","sourceRoot":"","sources":["../../src/jobs/compact-identity.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"consent-call.d.ts","sourceRoot":"","sources":["../../src/jobs/consent-call.ts"],"names":[],"mappings":"AAQA,eAAO,MAAM,YAAY,KA8ExB,CAAC"}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { inngest } from "../lib/inngest.js";
|
|
2
|
+
import { supabase } from "../lib/supabase.js";
|
|
3
|
+
import { trigger_consent_call } from "../lib/vapi.js";
|
|
4
|
+
import { send_sms } from "../lib/twilio.js";
|
|
5
|
+
// Triggered when a match is confirmed by the matching engine
|
|
6
|
+
// Calls both users via Vapi consent assistant — sequentially with a gap
|
|
7
|
+
// If Vapi call fails, falls back to SMS consent
|
|
8
|
+
export const consent_call = inngest.createFunction({ id: "consent-call", retries: 2 }, { event: "match/confirmed" }, async ({ event, step }) => {
|
|
9
|
+
const { match_id } = event.data;
|
|
10
|
+
// Load match + both users' phones
|
|
11
|
+
const match = await step.run("load-match", async () => {
|
|
12
|
+
const { data, error } = await supabase
|
|
13
|
+
.from("matches")
|
|
14
|
+
.select(`
|
|
15
|
+
id,
|
|
16
|
+
user_a_id,
|
|
17
|
+
user_b_id,
|
|
18
|
+
primary_modality,
|
|
19
|
+
confidence_score,
|
|
20
|
+
consent_call_framing,
|
|
21
|
+
status
|
|
22
|
+
`)
|
|
23
|
+
.eq("id", match_id)
|
|
24
|
+
.single();
|
|
25
|
+
if (error || !data)
|
|
26
|
+
throw new Error(`Match not found: ${match_id}`);
|
|
27
|
+
return data;
|
|
28
|
+
});
|
|
29
|
+
// Only proceed if still pending consent
|
|
30
|
+
if (match.status !== "pending_consent") {
|
|
31
|
+
return { skipped: true, reason: `match status is ${match.status}` };
|
|
32
|
+
}
|
|
33
|
+
const users = await step.run("load-users", async () => {
|
|
34
|
+
const { data, error } = await supabase
|
|
35
|
+
.from("users")
|
|
36
|
+
.select("id, phone, brand")
|
|
37
|
+
.in("id", [match.user_a_id, match.user_b_id]);
|
|
38
|
+
if (error || !data || data.length !== 2) {
|
|
39
|
+
throw new Error("Failed to load both users for consent call");
|
|
40
|
+
}
|
|
41
|
+
return data;
|
|
42
|
+
});
|
|
43
|
+
const user_a = users.find((u) => u.id === match.user_a_id);
|
|
44
|
+
const user_b = users.find((u) => u.id === match.user_b_id);
|
|
45
|
+
const framing = match.consent_call_framing ?? "";
|
|
46
|
+
// ── Call user A ───────────────────────────────────────────────────────────
|
|
47
|
+
await step.run("call-user-a", async () => {
|
|
48
|
+
try {
|
|
49
|
+
await trigger_consent_call(user_a.phone, match_id, framing);
|
|
50
|
+
}
|
|
51
|
+
catch (err) {
|
|
52
|
+
console.error("Vapi consent call failed for user_a, falling back to SMS", err);
|
|
53
|
+
await send_sms(user_a.phone, `We found someone interesting for you. Reply ACCEPT to connect or DECLINE to pass. [match:${match_id}]`, user_a.brand);
|
|
54
|
+
}
|
|
55
|
+
});
|
|
56
|
+
// Small delay between calls so they don't land simultaneously
|
|
57
|
+
await step.sleep("gap-between-calls", "2m");
|
|
58
|
+
// ── Call user B ───────────────────────────────────────────────────────────
|
|
59
|
+
await step.run("call-user-b", async () => {
|
|
60
|
+
try {
|
|
61
|
+
await trigger_consent_call(user_b.phone, match_id, framing);
|
|
62
|
+
}
|
|
63
|
+
catch (err) {
|
|
64
|
+
console.error("Vapi consent call failed for user_b, falling back to SMS", err);
|
|
65
|
+
await send_sms(user_b.phone, `We found someone interesting for you. Reply ACCEPT to connect or DECLINE to pass. [match:${match_id}]`, user_b.brand);
|
|
66
|
+
}
|
|
67
|
+
});
|
|
68
|
+
return { match_id, called: [user_a.id, user_b.id] };
|
|
69
|
+
});
|
|
70
|
+
//# sourceMappingURL=consent-call.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"consent-call.js","sourceRoot":"","sources":["../../src/jobs/consent-call.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"export-identity.d.ts","sourceRoot":"","sources":["../../src/jobs/export-identity.ts"],"names":[],"mappings":"AAYA,eAAO,MAAM,eAAe,KAqJ3B,CAAC"}
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import archiver from "archiver";
|
|
2
|
+
import { PassThrough } from "stream";
|
|
3
|
+
import { inngest } from "../lib/inngest.js";
|
|
4
|
+
import { supabase } from "../lib/supabase.js";
|
|
5
|
+
import { complete } from "../lib/anthropic.js";
|
|
6
|
+
import { upload_buffer, generate_presigned_get_url } from "../lib/r2.js";
|
|
7
|
+
import { send_sms } from "../lib/twilio.js";
|
|
8
|
+
import { SYSTEM_IDENTITY_NARRATIVE } from "../lib/prompts.js";
|
|
9
|
+
// Triggered by identity/export event (SMS "EXPORT" or web portal)
|
|
10
|
+
// Renders 7 markdown files → zips → uploads to R2 → sends SMS download link
|
|
11
|
+
export const export_identity = inngest.createFunction({ id: "export-identity", retries: 1 }, { event: "identity/export" }, async ({ event, step }) => {
|
|
12
|
+
const { user_id } = event.data;
|
|
13
|
+
// Load identity + user
|
|
14
|
+
const { identity, user } = await step.run("load-data", async () => {
|
|
15
|
+
const [id_res, user_res] = await Promise.all([
|
|
16
|
+
supabase
|
|
17
|
+
.from("identities")
|
|
18
|
+
.select("base_profile, modality_weights, signal_completeness_score, session_count")
|
|
19
|
+
.eq("user_id", user_id)
|
|
20
|
+
.single(),
|
|
21
|
+
supabase
|
|
22
|
+
.from("users")
|
|
23
|
+
.select("id, phone, brand")
|
|
24
|
+
.eq("id", user_id)
|
|
25
|
+
.single(),
|
|
26
|
+
]);
|
|
27
|
+
if (id_res.error || !id_res.data)
|
|
28
|
+
throw new Error("Identity not found");
|
|
29
|
+
if (user_res.error || !user_res.data)
|
|
30
|
+
throw new Error("User not found");
|
|
31
|
+
return { identity: id_res.data, user: user_res.data };
|
|
32
|
+
});
|
|
33
|
+
const profile = identity.base_profile;
|
|
34
|
+
const weights = identity.modality_weights;
|
|
35
|
+
// ── Render all narrative files in parallel ────────────────────────────────
|
|
36
|
+
const files = await step.run("render-narratives", async () => {
|
|
37
|
+
const domains = [
|
|
38
|
+
{ name: "relationships", data: profile.relationships },
|
|
39
|
+
{ name: "desire", data: profile.desire },
|
|
40
|
+
{ name: "money", data: profile.money },
|
|
41
|
+
{ name: "health", data: profile.health },
|
|
42
|
+
{ name: "worldview", data: profile.worldview },
|
|
43
|
+
];
|
|
44
|
+
const rendered = await Promise.all(domains.map(async ({ name, data }) => {
|
|
45
|
+
const content = await complete("analysis", SYSTEM_IDENTITY_NARRATIVE, [
|
|
46
|
+
`Domain: ${name}`,
|
|
47
|
+
`\nProfile data:\n${JSON.stringify(data, null, 2)}`,
|
|
48
|
+
`\nFull context (other domains):\n${JSON.stringify(profile, null, 2)}`,
|
|
49
|
+
].join("\n\n"), 1024);
|
|
50
|
+
return { filename: `${name}.md`, content: `# My ${capitalize(name)} Identity\n\n${content}` };
|
|
51
|
+
}));
|
|
52
|
+
// Modality weights file
|
|
53
|
+
const modality_content = await complete("analysis", SYSTEM_IDENTITY_NARRATIVE, [
|
|
54
|
+
`Domain: modality_weights`,
|
|
55
|
+
`\nModality weights:\n${JSON.stringify(weights, null, 2)}`,
|
|
56
|
+
`\nFull profile context:\n${JSON.stringify(profile, null, 2)}`,
|
|
57
|
+
].join("\n\n"), 512);
|
|
58
|
+
rendered.push({
|
|
59
|
+
filename: "modality_weights.md",
|
|
60
|
+
content: `# How I Approach Connection\n\n${modality_content}`,
|
|
61
|
+
});
|
|
62
|
+
// README
|
|
63
|
+
const completeness_pct = Math.round((identity.signal_completeness_score ?? 0) * 100);
|
|
64
|
+
rendered.push({
|
|
65
|
+
filename: "README.md",
|
|
66
|
+
content: [
|
|
67
|
+
`# My Identity — meet-the-one.ai`,
|
|
68
|
+
``,
|
|
69
|
+
`Generated: ${new Date().toISOString().slice(0, 10)}`,
|
|
70
|
+
`Sessions: ${identity.session_count ?? 0}`,
|
|
71
|
+
`Completeness: ${completeness_pct}%`,
|
|
72
|
+
``,
|
|
73
|
+
`## What This Is`,
|
|
74
|
+
``,
|
|
75
|
+
`This is your portable identity — built from your own words across ${identity.session_count ?? 0} conversation sessions.`,
|
|
76
|
+
`It belongs to you. You can read it, keep it, share it, or import it into compatible platforms.`,
|
|
77
|
+
``,
|
|
78
|
+
`## Files`,
|
|
79
|
+
``,
|
|
80
|
+
`- \`relationships.md\` — how you attach, trust, and connect`,
|
|
81
|
+
`- \`desire.md\` — what you want and how you want it`,
|
|
82
|
+
`- \`money.md\` — your relationship with resources and risk`,
|
|
83
|
+
`- \`health.md\` — how you inhabit your body and energy`,
|
|
84
|
+
`- \`worldview.md\` — how you see the world and people in it`,
|
|
85
|
+
`- \`modality_weights.md\` — how you orient toward different kinds of connection`,
|
|
86
|
+
``,
|
|
87
|
+
`## Privacy`,
|
|
88
|
+
``,
|
|
89
|
+
`To delete all your data: text DELETE MY DATA to the number you enrolled with.`,
|
|
90
|
+
``,
|
|
91
|
+
`---`,
|
|
92
|
+
`meet-the-one.ai`,
|
|
93
|
+
].join("\n"),
|
|
94
|
+
});
|
|
95
|
+
return rendered;
|
|
96
|
+
});
|
|
97
|
+
// ── Zip all files + upload to R2 (single step — Buffer can't cross step boundary) ──
|
|
98
|
+
const export_key = `exports/${user_id}/${Date.now()}.zip`;
|
|
99
|
+
await step.run("zip-and-upload", async () => {
|
|
100
|
+
const zip_buf = await new Promise((resolve, reject) => {
|
|
101
|
+
const archive = archiver("zip", { zlib: { level: 6 } });
|
|
102
|
+
const pass = new PassThrough();
|
|
103
|
+
const chunks = [];
|
|
104
|
+
pass.on("data", (chunk) => chunks.push(chunk));
|
|
105
|
+
pass.on("end", () => resolve(Buffer.concat(chunks)));
|
|
106
|
+
pass.on("error", reject);
|
|
107
|
+
archive.on("error", reject);
|
|
108
|
+
archive.pipe(pass);
|
|
109
|
+
for (const { filename, content } of files) {
|
|
110
|
+
archive.append(content, { name: `my-identity/${filename}` });
|
|
111
|
+
}
|
|
112
|
+
archive.finalize();
|
|
113
|
+
});
|
|
114
|
+
await upload_buffer(export_key, zip_buf, "application/zip");
|
|
115
|
+
});
|
|
116
|
+
// ── Generate presigned download URL (24h) ─────────────────────────────────
|
|
117
|
+
const download_url = await step.run("presign-url", async () => {
|
|
118
|
+
return generate_presigned_get_url(export_key, 86400);
|
|
119
|
+
});
|
|
120
|
+
// ── Send SMS with download link ───────────────────────────────────────────
|
|
121
|
+
await step.run("send-sms", async () => {
|
|
122
|
+
await send_sms(user.phone, `Your identity export is ready. Download it here (link expires in 24h):\n${download_url}`, user.brand);
|
|
123
|
+
});
|
|
124
|
+
return { user_id, export_key, files: files.length };
|
|
125
|
+
});
|
|
126
|
+
function capitalize(s) {
|
|
127
|
+
return s.charAt(0).toUpperCase() + s.slice(1);
|
|
128
|
+
}
|
|
129
|
+
//# sourceMappingURL=export-identity.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"export-identity.js","sourceRoot":"","sources":["../../src/jobs/export-identity.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"introduction-call.d.ts","sourceRoot":"","sources":["../../src/jobs/introduction-call.ts"],"names":[],"mappings":"AAQA,eAAO,MAAM,iBAAiB,KA4F7B,CAAC"}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { inngest } from "../lib/inngest.js";
|
|
2
|
+
import { supabase } from "../lib/supabase.js";
|
|
3
|
+
import { twilio } from "../lib/twilio.js";
|
|
4
|
+
import { send_sms } from "../lib/twilio.js";
|
|
5
|
+
import { config } from "../lib/config.js";
|
|
6
|
+
// Triggered when both users have accepted the match
|
|
7
|
+
// Creates a Twilio conference and calls both in
|
|
8
|
+
export const introduction_call = inngest.createFunction({ id: "introduction-call", retries: 2 }, { event: "match/both-accepted" }, async ({ event, step }) => {
|
|
9
|
+
const { match_id } = event.data;
|
|
10
|
+
const match = await step.run("load-match", async () => {
|
|
11
|
+
const { data, error } = await supabase
|
|
12
|
+
.from("matches")
|
|
13
|
+
.select("id, user_a_id, user_b_id, status")
|
|
14
|
+
.eq("id", match_id)
|
|
15
|
+
.single();
|
|
16
|
+
if (error || !data)
|
|
17
|
+
throw new Error(`Match not found: ${match_id}`);
|
|
18
|
+
return data;
|
|
19
|
+
});
|
|
20
|
+
if (match.status !== "both_accepted") {
|
|
21
|
+
return { skipped: true, reason: `match status is ${match.status}` };
|
|
22
|
+
}
|
|
23
|
+
const users = await step.run("load-users", async () => {
|
|
24
|
+
const { data, error } = await supabase
|
|
25
|
+
.from("users")
|
|
26
|
+
.select("id, phone, brand")
|
|
27
|
+
.in("id", [match.user_a_id, match.user_b_id]);
|
|
28
|
+
if (error || !data || data.length !== 2) {
|
|
29
|
+
throw new Error("Failed to load users for introduction call");
|
|
30
|
+
}
|
|
31
|
+
return data;
|
|
32
|
+
});
|
|
33
|
+
const user_a = users.find((u) => u.id === match.user_a_id);
|
|
34
|
+
const user_b = users.find((u) => u.id === match.user_b_id);
|
|
35
|
+
// Conference room name is deterministic from match_id
|
|
36
|
+
const conference_name = `intro-${match_id}`;
|
|
37
|
+
const from_number = config.twilio.numbers["meet-the-one"];
|
|
38
|
+
// TwiML to join a named conference
|
|
39
|
+
const conference_twiml = (participant_name) => `<?xml version="1.0" encoding="UTF-8"?>
|
|
40
|
+
<Response>
|
|
41
|
+
<Say voice="Polly.Joanna">Please hold while we connect you with your match.</Say>
|
|
42
|
+
<Dial>
|
|
43
|
+
<Conference
|
|
44
|
+
startConferenceOnEnter="false"
|
|
45
|
+
endConferenceOnExit="true"
|
|
46
|
+
waitUrl="https://twimlets.com/holdmusic?Bucket=com.twilio.music.soft-rock"
|
|
47
|
+
statusCallback="${config.app.url}/api/webhooks/twilio/conference"
|
|
48
|
+
statusCallbackEvent="end"
|
|
49
|
+
friendlyName="${participant_name}"
|
|
50
|
+
>${conference_name}</Conference>
|
|
51
|
+
</Dial>
|
|
52
|
+
</Response>`;
|
|
53
|
+
// Call both users into the conference
|
|
54
|
+
await step.run("dial-both", async () => {
|
|
55
|
+
await Promise.all([
|
|
56
|
+
twilio.calls.create({
|
|
57
|
+
to: user_a.phone,
|
|
58
|
+
from: from_number,
|
|
59
|
+
twiml: conference_twiml("You"),
|
|
60
|
+
}),
|
|
61
|
+
twilio.calls.create({
|
|
62
|
+
to: user_b.phone,
|
|
63
|
+
from: from_number,
|
|
64
|
+
twiml: conference_twiml("You"),
|
|
65
|
+
}),
|
|
66
|
+
]);
|
|
67
|
+
});
|
|
68
|
+
// Update match status
|
|
69
|
+
await step.run("mark-introduced", async () => {
|
|
70
|
+
await supabase
|
|
71
|
+
.from("matches")
|
|
72
|
+
.update({ status: "introduced", updated_at: new Date().toISOString() })
|
|
73
|
+
.eq("id", match_id);
|
|
74
|
+
});
|
|
75
|
+
// Send follow-up SMS to both after the call
|
|
76
|
+
await step.sleep("post-call-delay", "30m");
|
|
77
|
+
await step.run("send-followup-sms", async () => {
|
|
78
|
+
const msg = "Hope that went well. Let us know how it went — your feedback helps us improve.";
|
|
79
|
+
await Promise.all([
|
|
80
|
+
send_sms(user_a.phone, msg, user_a.brand),
|
|
81
|
+
send_sms(user_b.phone, msg, user_b.brand),
|
|
82
|
+
]);
|
|
83
|
+
});
|
|
84
|
+
return { match_id, introduced: [user_a.id, user_b.id] };
|
|
85
|
+
});
|
|
86
|
+
//# sourceMappingURL=introduction-call.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"introduction-call.js","sourceRoot":"","sources":["../../src/jobs/introduction-call.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reanalyze-identity.d.ts","sourceRoot":"","sources":["../../src/jobs/reanalyze-identity.ts"],"names":[],"mappings":"AAMA,eAAO,MAAM,kBAAkB,KA0D9B,CAAC"}
|