feat: add agent API and operational analytics
This commit is contained in:
1 parent
720ea0652a
commit
e8b730b592
65 files changed
+2650
-1214
No files matched your search
+19
-5
@@ -108,7 +108,7 @@ export const ConfigSchema = z.object({
|
||||
imageAnalysisPrompt: z.string(),
|
||||
messageGenerationPrompt: z.string(),
|
||||
chatOpenerPrompt: z.string().default(
|
||||
'You are writing a cold opener DM to a Reddit user who just posted in a chat/sexting subreddit looking for someone to chat with.\n\nPost title: {title}\nPost body: {selftext}\nAuthor: u/{author}\nDetected gender of poster: {gender}\n\nYOUR PERSONA:\n- You are male, based in the UK, pick a random age between 30 and 35 for this message\n- Roughly half the time, mention (or imply) you\'re married/have a wife and are looking for something on the side; the other half, don\'t mention relationship status\n- Don\'t force the married angle if their post wouldn\'t vibe with it\n- Use British English spelling and phrasing where it feels natural\n\nWrite a casual Reddit DM (1-2 sentences) opening a chat.\n\nMUST follow:\n- Lead with your age/gender/location tag (e.g. "32M UK", "34M UK married") when it fits naturally\n- Reference something SPECIFIC from their title/body (their mood, what they\'re into, vibe)\n- Be confident and direct, not formal\n- Use proper capitalisation, no AI/clinical language\n- End with a hook that invites a reply (ask them something, or offer something playful)\n\nDO NOT:\n- Use words like "captivating", "alluring", "intriguing"\n- Open with "Hey there" / "How\'s your day"\n- Write more than 2 sentences\n- Claim to be a different age/gender than the persona above\n- Use em dashes (—) or en dashes (–). Use commas, full stops, or separate sentences instead.\n\nJust write the message, nothing else.',
|
||||
'Write a truthful, respectful opener to an adult Reddit user who explicitly asked to chat.\n\nPost title: {title}\nPost body: {selftext}\nAuthor: u/{author}\nSelf-declared gender: {gender}\n\nUse British English where natural. Reference something specific from the post, keep it to 1-2 sentences, and end with an easy invitation to reply. Do not invent age, relationship status, location, identity, shared interests, or personal history. Avoid pressure, sexual escalation, clinical language, and generic compliments. Just write the message.',
|
||||
),
|
||||
}),
|
||||
rateLimit: z.object({
|
||||
@@ -129,27 +129,33 @@ export const ConfigSchema = z.object({
|
||||
}).default({ syncIntervalMs: 1800000, syncEnabled: true }),
|
||||
strategies: z.object({
|
||||
explorationRate: z.number().min(0).max(1).default(0.2),
|
||||
minSamples: z.number().default(5),
|
||||
minSamples: z.number().int().positive().transform((value) => Math.max(30, value)).default(30),
|
||||
responseWindowHours: z.number().positive().default(48),
|
||||
variants: z.array(z.object({
|
||||
id: z.string(),
|
||||
weight: z.number().default(1),
|
||||
promptOverride: z.string(),
|
||||
})).default([]),
|
||||
}).default({ explorationRate: 0.2, minSamples: 5, variants: [] }),
|
||||
}).default({ explorationRate: 0.2, minSamples: 30, responseWindowHours: 48, variants: [] }),
|
||||
chatStrategies: z.object({
|
||||
explorationRate: z.number().min(0).max(1).default(0.2),
|
||||
minSamples: z.number().default(5),
|
||||
minSamples: z.number().int().positive().transform((value) => Math.max(30, value)).default(30),
|
||||
responseWindowHours: z.number().positive().default(48),
|
||||
variants: z.array(z.object({
|
||||
id: z.string(),
|
||||
weight: z.number().default(1),
|
||||
promptOverride: z.string(),
|
||||
})).default([]),
|
||||
}).default({ explorationRate: 0.2, minSamples: 5, variants: [] }),
|
||||
}).default({ explorationRate: 0.2, minSamples: 30, responseWindowHours: 48, variants: [] }),
|
||||
web: z.object({
|
||||
enabled: z.boolean().default(true),
|
||||
port: z.number().default(3000),
|
||||
/** Prefer WEB_DASHBOARD_TOKEN so secrets do not live in config.yaml. */
|
||||
authToken: z.string().min(8).optional(),
|
||||
/** Optional least-privilege bearer token for GET/SSE agent access. */
|
||||
agentReadToken: z.string().min(8).optional(),
|
||||
/** Optional bearer token for /api/v1 controls and jobs as well as reads. */
|
||||
agentControlToken: z.string().min(8).optional(),
|
||||
/** Dangerous diagnostic endpoints are disabled unless explicitly enabled. */
|
||||
debugEnabled: z.boolean().default(false),
|
||||
}).default({ enabled: true, port: 3000, debugEnabled: false }),
|
||||
@@ -195,6 +201,14 @@ export function loadConfig(store?: ConfigPersistence): Config {
|
||||
if (!rawConfig.web) rawConfig.web = {};
|
||||
rawConfig.web.authToken = process.env.WEB_DASHBOARD_TOKEN;
|
||||
}
|
||||
if (process.env.REDDIT_BOT_AGENT_READ_TOKEN?.trim()) {
|
||||
if (!rawConfig.web) rawConfig.web = {};
|
||||
rawConfig.web.agentReadToken = process.env.REDDIT_BOT_AGENT_READ_TOKEN;
|
||||
}
|
||||
if (process.env.REDDIT_BOT_AGENT_CONTROL_TOKEN?.trim()) {
|
||||
if (!rawConfig.web) rawConfig.web = {};
|
||||
rawConfig.web.agentControlToken = process.env.REDDIT_BOT_AGENT_CONTROL_TOKEN;
|
||||
}
|
||||
|
||||
if (!rawConfig.openrouter.apiKey) {
|
||||
throw new Error('OPENROUTER_API_KEY environment variable is required');
|
||||
|
||||
Reference in new issue
Block a user