Update source code and remove runtime data from tracking

Add gitignore rules for browser session data, SQLite databases,
media downloads, and build artifacts. Remove previously tracked
runtime files from the repository.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
jjbruntonandClaude Opus 4.6 committed 2026-05-20 18:54:07 +01:00
1 parent e90213d1ff
commit dfa0580859
23 files changed
+5324 -347

No files matched your search

+101
View File
@@ -0,0 +1,101 @@
import type { Config } from '../config/index.js';
import type { MessagesRepository, StrategyStats } from '../database/repositories.js';
import type { Logger } from '../utils/logger.js';
export interface SelectedStrategy {
id: string;
promptOverride: string;
reason: 'exploit' | 'explore';
}
export class StrategySelector {
private variants: Config['strategies']['variants'];
private explorationRate: number;
private minSamples: number;
constructor(
private config: Config,
private messagesRepo: MessagesRepository,
private logger: Logger
) {
this.variants = config.strategies.variants;
this.explorationRate = config.strategies.explorationRate;
this.minSamples = config.strategies.minSamples;
}
selectStrategy(): SelectedStrategy | null {
if (this.variants.length === 0) {
return null;
}
// Exploration: random strategy
if (Math.random() < this.explorationRate) {
const variant = this.variants[Math.floor(Math.random() * this.variants.length)];
this.logger.debug(`Strategy selected (explore): ${variant.id}`);
return { id: variant.id, promptOverride: variant.promptOverride, reason: 'explore' };
}
// Exploitation: pick the best-performing strategy
const stats = this.messagesRepo.getStrategyStats();
const statsMap = new Map(stats.map(s => [s.strategyId, s]));
// Find strategies with insufficient samples - prioritize exploring them
const unexplored = this.variants.filter(v => {
const s = statsMap.get(v.id);
return !s || s.totalSent < this.minSamples;
});
if (unexplored.length > 0) {
const variant = unexplored[Math.floor(Math.random() * unexplored.length)];
this.logger.debug(`Strategy selected (under-sampled): ${variant.id}`);
return { id: variant.id, promptOverride: variant.promptOverride, reason: 'explore' };
}
// All strategies have enough samples - pick the best response rate
let bestVariant = this.variants[0];
let bestRate = -1;
for (const variant of this.variants) {
const s = statsMap.get(variant.id);
if (s && s.responseRate > bestRate) {
bestRate = s.responseRate;
bestVariant = variant;
}
}
this.logger.debug(`Strategy selected (exploit, ${bestRate}% response rate): ${bestVariant.id}`);
return { id: bestVariant.id, promptOverride: bestVariant.promptOverride, reason: 'exploit' };
}
getStats(): (StrategyStats & { configured: boolean })[] {
const dbStats = this.messagesRepo.getStrategyStats();
const statsMap = new Map(dbStats.map(s => [s.strategyId, s]));
// Include all configured variants, even if they have no data yet
const results: (StrategyStats & { configured: boolean })[] = [];
for (const variant of this.variants) {
const s = statsMap.get(variant.id);
results.push({
strategyId: variant.id,
totalSent: s?.totalSent || 0,
totalResponses: s?.totalResponses || 0,
responseRate: s?.responseRate || 0,
configured: true,
});
}
// Include any historical strategies no longer in config
for (const s of dbStats) {
if (!this.variants.some(v => v.id === s.strategyId)) {
results.push({ ...s, configured: false });
}
}
return results;
}
hasStrategies(): boolean {
return this.variants.length > 0;
}
}