Signed-off-by: OpenClaw Bot <ai-agent@topdoglabs.com>
This commit is contained in:
parent
59f85de572
commit
62b730dae1
92
app/api/voxyz/cap-gates/route.ts
Normal file
92
app/api/voxyz/cap-gates/route.ts
Normal file
@ -0,0 +1,92 @@
|
||||
/**
|
||||
* Voxyz API Routes - Cap Gates
|
||||
* Handles cap gate management and capacity checks
|
||||
*/
|
||||
|
||||
import { NextRequest, NextResponse } from 'next/server';
|
||||
import {
|
||||
getActiveCapGates,
|
||||
getCapacityStatus,
|
||||
canAddProposal,
|
||||
initializeCapGates,
|
||||
checkCapGates
|
||||
} from '@/lib/services/cap-gates.service';
|
||||
|
||||
// GET /api/voxyz/cap-gates - Get cap gates status
|
||||
export async function GET(request: NextRequest) {
|
||||
try {
|
||||
const { searchParams } = new URL(request.url);
|
||||
const status = searchParams.get('status');
|
||||
const check = searchParams.get('check');
|
||||
|
||||
if (status === 'capacity') {
|
||||
const capacity = await getCapacityStatus();
|
||||
return NextResponse.json({ capacity });
|
||||
}
|
||||
|
||||
if (check === 'proposal') {
|
||||
const estimatedMinutes = parseInt(searchParams.get('minutes') || '120');
|
||||
const canAdd = await canAddProposal(estimatedMinutes);
|
||||
return NextResponse.json(canAdd);
|
||||
}
|
||||
|
||||
const gates = await getActiveCapGates();
|
||||
return NextResponse.json({ gates });
|
||||
|
||||
} catch (error) {
|
||||
console.error('Error fetching cap gates:', error);
|
||||
return NextResponse.json(
|
||||
{ error: 'Failed to fetch cap gates' },
|
||||
{ status: 500 }
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// POST /api/voxyz/cap-gates - Initialize cap gates
|
||||
export async function POST(request: NextRequest) {
|
||||
try {
|
||||
const success = await initializeCapGates();
|
||||
|
||||
if (!success) {
|
||||
return NextResponse.json(
|
||||
{ error: 'Failed to initialize cap gates' },
|
||||
{ status: 500 }
|
||||
);
|
||||
}
|
||||
|
||||
return NextResponse.json({ success: true }, { status: 201 });
|
||||
|
||||
} catch (error) {
|
||||
console.error('Error initializing cap gates:', error);
|
||||
return NextResponse.json(
|
||||
{ error: 'Failed to initialize cap gates' },
|
||||
{ status: 500 }
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// POST /api/voxyz/cap-gates/check - Check a proposal against cap gates
|
||||
export async function PATCH(request: NextRequest) {
|
||||
try {
|
||||
const body = await request.json();
|
||||
const { estimatedMinutes, existingProposals } = body;
|
||||
|
||||
if (!estimatedMinutes) {
|
||||
return NextResponse.json(
|
||||
{ error: 'Missing required field: estimatedMinutes' },
|
||||
{ status: 400 }
|
||||
);
|
||||
}
|
||||
|
||||
const result = await checkCapGates(estimatedMinutes, existingProposals || []);
|
||||
|
||||
return NextResponse.json(result);
|
||||
|
||||
} catch (error) {
|
||||
console.error('Error checking cap gates:', error);
|
||||
return NextResponse.json(
|
||||
{ error: 'Failed to check cap gates' },
|
||||
{ status: 500 }
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -13,7 +13,7 @@ import {
|
||||
runDailyMissionGeneration
|
||||
} from '@/lib/services/daily-mission-agent';
|
||||
import { getProposalsForDate, approveProposal, rejectProposal } from '@/lib/services/proposal.service';
|
||||
import { createClient } from '@/lib/supabase/server';
|
||||
import { getServiceSupabase } from '@/lib/supabase/client';
|
||||
|
||||
// GET /api/voxyz/daily-missions - Get today's daily mission
|
||||
export async function GET(request: NextRequest) {
|
||||
@ -38,7 +38,7 @@ export async function GET(request: NextRequest) {
|
||||
}
|
||||
|
||||
// Fetch full proposal details
|
||||
const supabase = await createClient();
|
||||
const supabase = getServiceSupabase();
|
||||
const proposalIds = [
|
||||
dailyMission.primary_mission_id,
|
||||
dailyMission.secondary_mission_id,
|
||||
|
||||
@ -12,7 +12,7 @@ import {
|
||||
getTopProposalsForToday,
|
||||
expireOldProposals
|
||||
} from '@/lib/services/proposal.service';
|
||||
import { createClient } from '@/lib/supabase/server';
|
||||
import { getServiceSupabase } from '@/lib/supabase/client';
|
||||
|
||||
// GET /api/voxyz/proposals - Get proposals
|
||||
export async function GET(request: NextRequest) {
|
||||
@ -33,7 +33,7 @@ export async function GET(request: NextRequest) {
|
||||
}
|
||||
|
||||
// Get all proposals with optional status filter
|
||||
const supabase = await createClient();
|
||||
const supabase = getServiceSupabase();
|
||||
let query = supabase.from('ops_mission_proposals').select('*');
|
||||
|
||||
if (status) {
|
||||
@ -72,7 +72,7 @@ export async function POST(request: NextRequest) {
|
||||
);
|
||||
}
|
||||
|
||||
const supabase = await createClient();
|
||||
const supabase = getServiceSupabase();
|
||||
|
||||
const proposal = {
|
||||
proposal_date: new Date().toISOString().split('T')[0],
|
||||
|
||||
150
app/api/voxyz/trigger-rules/route.ts
Normal file
150
app/api/voxyz/trigger-rules/route.ts
Normal file
@ -0,0 +1,150 @@
|
||||
/**
|
||||
* Voxyz API Routes - Trigger Rules
|
||||
* Handles trigger rule execution and management
|
||||
*/
|
||||
|
||||
import { NextRequest, NextResponse } from 'next/server';
|
||||
import {
|
||||
getActiveTriggerRules,
|
||||
evaluateAllTriggers,
|
||||
triggerManualEvaluation,
|
||||
runTriggerMonitor,
|
||||
getRecentTriggerLogs
|
||||
} from '@/lib/services/trigger-rules.service';
|
||||
import { fetchAllTasks } from '@/lib/data/tasks';
|
||||
import { getServiceSupabase } from '@/lib/supabase/client';
|
||||
|
||||
// GET /api/voxyz/trigger-rules - Get trigger rules and logs
|
||||
export async function GET(request: NextRequest) {
|
||||
try {
|
||||
const { searchParams } = new URL(request.url);
|
||||
const logs = searchParams.get('logs');
|
||||
const limit = parseInt(searchParams.get('limit') || '50');
|
||||
|
||||
if (logs) {
|
||||
const triggerLogs = await getRecentTriggerLogs(limit);
|
||||
return NextResponse.json({ logs: triggerLogs });
|
||||
}
|
||||
|
||||
const rules = await getActiveTriggerRules();
|
||||
return NextResponse.json({ rules });
|
||||
|
||||
} catch (error) {
|
||||
console.error('Error fetching trigger rules:', error);
|
||||
return NextResponse.json(
|
||||
{ error: 'Failed to fetch trigger rules' },
|
||||
{ status: 500 }
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// POST /api/voxyz/trigger-rules - Run trigger evaluation or manual trigger
|
||||
export async function POST(request: NextRequest) {
|
||||
try {
|
||||
const body = await request.json();
|
||||
const { action, triggerType, taskIds } = body;
|
||||
|
||||
if (action === 'run-monitor') {
|
||||
// Run the trigger monitor agent
|
||||
await runTriggerMonitor();
|
||||
return NextResponse.json({
|
||||
success: true,
|
||||
message: 'Trigger monitor executed'
|
||||
});
|
||||
}
|
||||
|
||||
if (action === 'manual-trigger') {
|
||||
if (!triggerType) {
|
||||
return NextResponse.json(
|
||||
{ error: 'Missing required field: triggerType' },
|
||||
{ status: 400 }
|
||||
);
|
||||
}
|
||||
|
||||
const results = await triggerManualEvaluation(triggerType, taskIds);
|
||||
|
||||
return NextResponse.json({
|
||||
success: true,
|
||||
triggered: results.length,
|
||||
results,
|
||||
});
|
||||
}
|
||||
|
||||
if (action === 'evaluate-all') {
|
||||
// Evaluate all triggers against current context
|
||||
const tasks = await fetchAllTasks();
|
||||
const context = {
|
||||
currentTime: new Date(),
|
||||
activeTasks: tasks.filter(t => t.status !== 'done' && t.status !== 'canceled'),
|
||||
recentProposals: [],
|
||||
todayMissions: [],
|
||||
userAvailability: [],
|
||||
};
|
||||
|
||||
const results = await evaluateAllTriggers(context);
|
||||
|
||||
return NextResponse.json({
|
||||
success: true,
|
||||
evaluated: results.length,
|
||||
triggered: results.filter(r => r.triggered).length,
|
||||
results,
|
||||
});
|
||||
}
|
||||
|
||||
return NextResponse.json(
|
||||
{ error: 'Unknown action' },
|
||||
{ status: 400 }
|
||||
);
|
||||
|
||||
} catch (error) {
|
||||
console.error('Error running trigger evaluation:', error);
|
||||
return NextResponse.json(
|
||||
{ error: 'Failed to run trigger evaluation' },
|
||||
{ status: 500 }
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// PATCH /api/voxyz/trigger-rules - Update a trigger rule
|
||||
export async function PATCH(request: NextRequest) {
|
||||
try {
|
||||
const body = await request.json();
|
||||
const { id, isActive, priority } = body;
|
||||
|
||||
if (!id) {
|
||||
return NextResponse.json(
|
||||
{ error: 'Missing required field: id' },
|
||||
{ status: 400 }
|
||||
);
|
||||
}
|
||||
|
||||
const supabase = getServiceSupabase();
|
||||
|
||||
const updates: Record<string, unknown> = {
|
||||
updated_at: new Date().toISOString(),
|
||||
};
|
||||
|
||||
if (isActive !== undefined) updates.is_active = isActive;
|
||||
if (priority !== undefined) updates.priority = priority;
|
||||
|
||||
const { data, error } = await supabase
|
||||
.from('ops_trigger_rules')
|
||||
.update(updates)
|
||||
.eq('id', id)
|
||||
.select()
|
||||
.single();
|
||||
|
||||
if (error) {
|
||||
throw error;
|
||||
}
|
||||
|
||||
return NextResponse.json({ rule: data });
|
||||
|
||||
} catch (error) {
|
||||
console.error('Error updating trigger rule:', error);
|
||||
return NextResponse.json(
|
||||
{ error: 'Failed to update trigger rule' },
|
||||
{ status: 500 }
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -13,7 +13,7 @@ import {
|
||||
MissionProposal,
|
||||
DailyMission
|
||||
} from '@/lib/supabase/voxyz.types';
|
||||
import { createClient } from '@/lib/supabase/client';
|
||||
import { getServiceSupabase } from '@/lib/supabase/client';
|
||||
|
||||
const DEFAULT_CONFIG: CapGateConfig = {
|
||||
dailyMissionLimit: 3,
|
||||
@ -28,7 +28,7 @@ const DEFAULT_CONFIG: CapGateConfig = {
|
||||
* Get all active cap gates from the database
|
||||
*/
|
||||
export async function getActiveCapGates(): Promise<CapGate[]> {
|
||||
const supabase = createClient();
|
||||
const supabase = getServiceSupabase();
|
||||
|
||||
const { data, error } = await supabase
|
||||
.from('ops_cap_gates')
|
||||
@ -48,7 +48,7 @@ export async function getActiveCapGates(): Promise<CapGate[]> {
|
||||
* Get a specific cap gate by type
|
||||
*/
|
||||
export async function getCapGateByType(gateType: CapGate['gate_type']): Promise<CapGate | null> {
|
||||
const supabase = createClient();
|
||||
const supabase = getServiceSupabase();
|
||||
|
||||
const { data, error } = await supabase
|
||||
.from('ops_cap_gates')
|
||||
@ -72,7 +72,7 @@ export async function updateCapGateValue(
|
||||
gateId: string,
|
||||
currentValue: number
|
||||
): Promise<boolean> {
|
||||
const supabase = createClient();
|
||||
const supabase = getServiceSupabase();
|
||||
|
||||
const { error } = await supabase
|
||||
.from('ops_cap_gates')
|
||||
@ -387,7 +387,7 @@ export async function getCapacityStatus(config: CapGateConfig = DEFAULT_CONFIG):
|
||||
focusTimeAllocatedMinutes: number;
|
||||
reactionTimeReservedMinutes: number;
|
||||
}> {
|
||||
const supabase = createClient();
|
||||
const supabase = getServiceSupabase();
|
||||
const today = new Date().toISOString().split('T')[0];
|
||||
|
||||
// Get today's proposals
|
||||
@ -446,7 +446,7 @@ export async function canAddProposal(
|
||||
* Create default cap gates in database
|
||||
*/
|
||||
export async function initializeCapGates(config: CapGateConfig = DEFAULT_CONFIG): Promise<boolean> {
|
||||
const supabase = createClient();
|
||||
const supabase = getServiceSupabase();
|
||||
|
||||
// Check if cap gates already exist
|
||||
const { count } = await supabase
|
||||
@ -557,7 +557,7 @@ export async function initializeCapGates(config: CapGateConfig = DEFAULT_CONFIG)
|
||||
* Reset cap gate values for a new day
|
||||
*/
|
||||
export async function resetCapGatesForNewDay(): Promise<boolean> {
|
||||
const supabase = createClient();
|
||||
const supabase = getServiceSupabase();
|
||||
|
||||
const { error } = await supabase
|
||||
.from('ops_cap_gates')
|
||||
|
||||
@ -28,7 +28,7 @@ import {
|
||||
initializeCapGates,
|
||||
resetCapGatesForNewDay
|
||||
} from '@/lib/services/cap-gates.service';
|
||||
import { createClient } from '@/lib/supabase/client';
|
||||
import { getServiceSupabase } from '@/lib/supabase/client';
|
||||
|
||||
const GENERATION_HOUR = 7;
|
||||
const GENERATION_MINUTE = 0;
|
||||
@ -279,7 +279,7 @@ async function canAddAnotherProposal(
|
||||
async function createCalendarBlockForProposal(
|
||||
proposal: MissionProposal
|
||||
): Promise<CalendarBlock | null> {
|
||||
const supabase = createClient();
|
||||
const supabase = getServiceSupabase();
|
||||
|
||||
const startTime = proposal.suggested_start_time
|
||||
? new Date(proposal.suggested_start_time)
|
||||
@ -325,7 +325,7 @@ async function createDailyMissionRecord(
|
||||
proposals: MissionProposal[],
|
||||
calendarBlocks: CalendarBlock[]
|
||||
): Promise<DailyMission> {
|
||||
const supabase = createClient();
|
||||
const supabase = getServiceSupabase();
|
||||
|
||||
const totalMinutes = proposals.reduce((sum, p) =>
|
||||
sum + (p.estimated_duration_minutes || 120), 0
|
||||
@ -396,11 +396,11 @@ async function createDailyMissionRecord(
|
||||
/**
|
||||
* Start execution log entry
|
||||
*/
|
||||
async function startExecutionLog(
|
||||
export async function startExecutionLog(
|
||||
agentName: 'proposal_generator' | 'cap_gate_validator' | 'calendar_scheduler' | 'trigger_monitor' | 'mission_optimizer' | 'stale_task_detector',
|
||||
executionType: 'scheduled' | 'triggered' | 'manual' | 'reaction'
|
||||
): Promise<string> {
|
||||
const supabase = createClient();
|
||||
const supabase = getServiceSupabase();
|
||||
|
||||
const { data, error } = await supabase
|
||||
.from('ops_agent_execution_log')
|
||||
@ -429,7 +429,7 @@ async function startExecutionLog(
|
||||
/**
|
||||
* Complete execution log entry
|
||||
*/
|
||||
async function completeExecutionLog(
|
||||
export async function completeExecutionLog(
|
||||
logId: string,
|
||||
result: {
|
||||
status: 'success' | 'partial' | 'failed';
|
||||
@ -441,7 +441,7 @@ async function completeExecutionLog(
|
||||
errorMessage?: string;
|
||||
}
|
||||
): Promise<void> {
|
||||
const supabase = createClient();
|
||||
const supabase = getServiceSupabase();
|
||||
|
||||
await supabase
|
||||
.from('ops_agent_execution_log')
|
||||
@ -470,7 +470,7 @@ function isNewDay(): boolean {
|
||||
* Get today's daily mission
|
||||
*/
|
||||
export async function getTodaysDailyMission(): Promise<DailyMission | null> {
|
||||
const supabase = createClient();
|
||||
const supabase = getServiceSupabase();
|
||||
const today = new Date().toISOString().split('T')[0];
|
||||
|
||||
const { data, error } = await supabase
|
||||
@ -537,7 +537,7 @@ export async function runDailyMissionGeneration(): Promise<DailyMissionGeneratio
|
||||
export async function approveDailyMission(
|
||||
missionId: string
|
||||
): Promise<boolean> {
|
||||
const supabase = createClient();
|
||||
const supabase = getServiceSupabase();
|
||||
|
||||
const { error } = await supabase
|
||||
.from('ops_daily_missions')
|
||||
@ -560,7 +560,7 @@ export async function approveDailyMission(
|
||||
export async function scheduleDailyMission(
|
||||
missionId: string
|
||||
): Promise<boolean> {
|
||||
const supabase = createClient();
|
||||
const supabase = getServiceSupabase();
|
||||
|
||||
// Get mission details
|
||||
const { data: mission } = await supabase
|
||||
@ -621,7 +621,7 @@ export async function completeDailyMission(
|
||||
rating?: number,
|
||||
notes?: string
|
||||
): Promise<boolean> {
|
||||
const supabase = createClient();
|
||||
const supabase = getServiceSupabase();
|
||||
|
||||
const { error } = await supabase
|
||||
.from('ops_daily_missions')
|
||||
|
||||
@ -13,7 +13,7 @@ import {
|
||||
ProposalStatus,
|
||||
ProposalServiceConfig
|
||||
} from '@/lib/supabase/voxyz.types';
|
||||
import { createClient } from '@/lib/supabase/client';
|
||||
import { getServiceSupabase } from '@/lib/supabase/client';
|
||||
import { Task } from '@/lib/data/tasks';
|
||||
import { Project } from '@/lib/data/projects';
|
||||
|
||||
@ -136,9 +136,10 @@ function calculateProjectImportanceScore(project: Project | undefined): number {
|
||||
function calculateBlockedScore(task: Task, context: ProposalContext): number {
|
||||
if (task.status === 'blocked') {
|
||||
// Check how long it's been blocked
|
||||
const blockedHours = task.comments?.reduce((hours, comment) => {
|
||||
if (comment.text?.toLowerCase().includes('blocked')) {
|
||||
const commentTime = new Date(comment.createdAt);
|
||||
const blockedHours = task.comments?.reduce<number>((hours, comment) => {
|
||||
const item = comment as { text?: unknown; createdAt?: unknown };
|
||||
if (typeof item.text === 'string' && item.text.toLowerCase().includes('blocked') && typeof item.createdAt === 'string') {
|
||||
const commentTime = new Date(item.createdAt);
|
||||
const blockedHours = (context.currentTime.getTime() - commentTime.getTime()) / (1000 * 60 * 60);
|
||||
return Math.max(hours, blockedHours);
|
||||
}
|
||||
@ -386,12 +387,20 @@ export function createProposalFromTask(
|
||||
source_task_ids: [task.id],
|
||||
source_project_ids: project ? [project.id] : [],
|
||||
title: task.title,
|
||||
description: task.description,
|
||||
description: task.description ?? null,
|
||||
rationale,
|
||||
ai_analysis: aiAnalysis,
|
||||
suggested_start_time: suggestedStartTime?.toISOString() || null,
|
||||
suggested_end_time: suggestedEndTime?.toISOString() || null,
|
||||
estimated_duration_minutes: estimatedDuration,
|
||||
approved_at: null,
|
||||
approved_by: null,
|
||||
rejected_at: null,
|
||||
rejection_reason: null,
|
||||
calendar_event_id: null,
|
||||
calendar_synced_at: null,
|
||||
executed_at: null,
|
||||
execution_notes: null,
|
||||
expires_at: new Date(context.currentTime.getTime() + config.defaultProposalExpiryHours * 60 * 60 * 1000).toISOString(),
|
||||
};
|
||||
}
|
||||
@ -575,7 +584,7 @@ export async function saveProposal(
|
||||
proposal: MissionProposalInsert,
|
||||
capGateResult: CapGateCheckResult
|
||||
): Promise<MissionProposal | null> {
|
||||
const supabase = createClient();
|
||||
const supabase = getServiceSupabase();
|
||||
|
||||
const proposalWithCapGates: MissionProposalInsert = {
|
||||
...proposal,
|
||||
@ -601,7 +610,7 @@ export async function saveProposal(
|
||||
* Get proposals for a specific date
|
||||
*/
|
||||
export async function getProposalsForDate(date: Date): Promise<MissionProposal[]> {
|
||||
const supabase = createClient();
|
||||
const supabase = getServiceSupabase();
|
||||
const dateString = date.toISOString().split('T')[0];
|
||||
|
||||
const { data, error } = await supabase
|
||||
@ -625,7 +634,7 @@ export async function approveProposal(
|
||||
proposalId: string,
|
||||
userId: string
|
||||
): Promise<boolean> {
|
||||
const supabase = createClient();
|
||||
const supabase = getServiceSupabase();
|
||||
|
||||
const { error } = await supabase
|
||||
.from('ops_mission_proposals')
|
||||
@ -651,7 +660,7 @@ export async function rejectProposal(
|
||||
proposalId: string,
|
||||
reason: string
|
||||
): Promise<boolean> {
|
||||
const supabase = createClient();
|
||||
const supabase = getServiceSupabase();
|
||||
|
||||
const { error } = await supabase
|
||||
.from('ops_mission_proposals')
|
||||
@ -677,7 +686,7 @@ export async function completeProposal(
|
||||
proposalId: string,
|
||||
notes?: string
|
||||
): Promise<boolean> {
|
||||
const supabase = createClient();
|
||||
const supabase = getServiceSupabase();
|
||||
|
||||
const { error } = await supabase
|
||||
.from('ops_mission_proposals')
|
||||
@ -718,7 +727,7 @@ export async function getTopProposalsForToday(limit: number = 3): Promise<Missio
|
||||
* Expire old proposals
|
||||
*/
|
||||
export async function expireOldProposals(): Promise<number> {
|
||||
const supabase = createClient();
|
||||
const supabase = getServiceSupabase();
|
||||
const now = new Date().toISOString();
|
||||
|
||||
const { data, error } = await supabase
|
||||
|
||||
@ -13,14 +13,14 @@ import {
|
||||
TriggerLog
|
||||
} from '@/lib/supabase/voxyz.types';
|
||||
import { Task } from '@/lib/data/tasks';
|
||||
import { createClient } from '@/lib/supabase/client';
|
||||
import { getServiceSupabase } from '@/lib/supabase/client';
|
||||
import { startExecutionLog, completeExecutionLog } from '@/lib/services/daily-mission-agent';
|
||||
|
||||
/**
|
||||
* Get all active trigger rules
|
||||
*/
|
||||
export async function getActiveTriggerRules(): Promise<TriggerRule[]> {
|
||||
const supabase = createClient();
|
||||
const supabase = getServiceSupabase();
|
||||
|
||||
const { data, error } = await supabase
|
||||
.from('ops_trigger_rules')
|
||||
@ -224,7 +224,7 @@ function evaluateHighPriorityTrigger(
|
||||
const triggeredTasks = context.activeTasks.filter(task => {
|
||||
if (task.priority !== priorityFilter) return false;
|
||||
|
||||
const createdAt = new Date(task.created_at || task.updatedAt);
|
||||
const createdAt = new Date(task.createdAt || task.updatedAt);
|
||||
return createdAt > oneHourAgo;
|
||||
});
|
||||
|
||||
@ -419,7 +419,7 @@ async function isOnCooldown(rule: TriggerRule): Promise<boolean> {
|
||||
* Check if trigger has reached daily limit
|
||||
*/
|
||||
async function hasReachedDailyLimit(rule: TriggerRule): Promise<boolean> {
|
||||
const supabase = createClient();
|
||||
const supabase = getServiceSupabase();
|
||||
const today = new Date().toISOString().split('T')[0];
|
||||
|
||||
const { count, error } = await supabase
|
||||
@ -440,7 +440,7 @@ async function hasReachedDailyLimit(rule: TriggerRule): Promise<boolean> {
|
||||
* Update trigger timestamp
|
||||
*/
|
||||
async function updateTriggerTimestamp(ruleId: string): Promise<void> {
|
||||
const supabase = createClient();
|
||||
const supabase = getServiceSupabase();
|
||||
|
||||
await supabase
|
||||
.from('ops_trigger_rules')
|
||||
@ -459,7 +459,7 @@ async function logTrigger(
|
||||
result: TriggerEvaluationResult,
|
||||
success: boolean
|
||||
): Promise<void> {
|
||||
const supabase = createClient();
|
||||
const supabase = getServiceSupabase();
|
||||
|
||||
await supabase
|
||||
.from('ops_trigger_log')
|
||||
@ -527,7 +527,7 @@ export async function triggerManualEvaluation(
|
||||
taskIds?: string[]
|
||||
): Promise<TriggerEvaluationResult[]> {
|
||||
// Fetch active trigger rules of this type
|
||||
const supabase = createClient();
|
||||
const supabase = getServiceSupabase();
|
||||
|
||||
const { data: rules } = await supabase
|
||||
.from('ops_trigger_rules')
|
||||
@ -570,7 +570,7 @@ export async function triggerManualEvaluation(
|
||||
* Get recent trigger logs
|
||||
*/
|
||||
export async function getRecentTriggerLogs(limit: number = 50): Promise<TriggerLog[]> {
|
||||
const supabase = createClient();
|
||||
const supabase = getServiceSupabase();
|
||||
|
||||
const { data, error } = await supabase
|
||||
.from('ops_trigger_log')
|
||||
|
||||
13
lib/services/voxyz/index.ts
Normal file
13
lib/services/voxyz/index.ts
Normal file
@ -0,0 +1,13 @@
|
||||
/**
|
||||
* Voxyz Autonomous Architecture - Services Index
|
||||
* Phase 10: Mission Control Autonomous System
|
||||
*/
|
||||
|
||||
// Database types
|
||||
export * from '@/lib/supabase/voxyz.types';
|
||||
|
||||
// Services
|
||||
export * from '../proposal.service';
|
||||
export * from '../cap-gates.service';
|
||||
export * from '../daily-mission-agent';
|
||||
export * from '../trigger-rules.service';
|
||||
@ -151,6 +151,9 @@ export interface Database {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
type Json = any;
|
||||
|
||||
// Voxyz Phase 10: Import Voxyz database extensions
|
||||
export type { VoxyzDatabase } from './voxyz.types';
|
||||
|
||||
// Extended types for activity feed
|
||||
export interface TaskComment {
|
||||
id: string;
|
||||
|
||||
@ -4,6 +4,7 @@
|
||||
*/
|
||||
|
||||
import { Database as BaseDatabase } from './database.types';
|
||||
import type { Task } from '@/lib/data/tasks';
|
||||
|
||||
// ============================================
|
||||
// ENUM TYPES
|
||||
@ -184,7 +185,7 @@ export interface DailyMission {
|
||||
focus_time_start: string | null;
|
||||
focus_time_end: string | null;
|
||||
focus_time_minutes: number | null;
|
||||
calendar_blocks: CalendarBlock[];
|
||||
calendar_blocks: DailyMissionCalendarBlock[];
|
||||
planned_reactions: number;
|
||||
actual_reactions: number;
|
||||
reaction_ratio: number;
|
||||
@ -194,6 +195,13 @@ export interface DailyMission {
|
||||
user_feedback: Record<string, unknown>;
|
||||
}
|
||||
|
||||
export interface DailyMissionCalendarBlock {
|
||||
id: string;
|
||||
start_time: string;
|
||||
end_time: string;
|
||||
block_type: BlockType;
|
||||
}
|
||||
|
||||
export interface CalendarBlock {
|
||||
id: string;
|
||||
created_at: string;
|
||||
@ -253,7 +261,7 @@ export interface AgentExecutionLog {
|
||||
|
||||
export interface CapGateViolation {
|
||||
gate_name: string;
|
||||
gate_type: GateType;
|
||||
gate_type: GateType | 'prioritization';
|
||||
current_value: number;
|
||||
max_value: number;
|
||||
violation_message: string;
|
||||
@ -342,7 +350,7 @@ export interface MissionProposalInput {
|
||||
|
||||
export interface TriggerEvaluationContext {
|
||||
currentTime: Date;
|
||||
activeTasks: BaseDatabase['public']['Tables']['tasks']['Row'][];
|
||||
activeTasks: Task[];
|
||||
recentProposals: MissionProposal[];
|
||||
todayMissions: DailyMission[];
|
||||
userAvailability: CalendarBlock[];
|
||||
|
||||
76
scripts/voxyz-daily-cron.sh
Executable file
76
scripts/voxyz-daily-cron.sh
Executable file
@ -0,0 +1,76 @@
|
||||
#!/bin/bash
|
||||
# Voxyz Daily Mission Generation Cron Job
|
||||
# Runs at 7:00 AM daily to generate mission proposals
|
||||
# Phase 10: Autonomous Architecture
|
||||
|
||||
set -e
|
||||
|
||||
# Configuration
|
||||
API_BASE_URL="${VOXYZ_API_URL:-http://localhost:3001/api/voxyz}"
|
||||
API_KEY="${VOXYZ_API_KEY:-}"
|
||||
LOG_FILE="${VOXYZ_LOG_FILE:-/tmp/voxyz-daily-missions.log}"
|
||||
|
||||
# Timestamp
|
||||
TIMESTAMP=$(date '+%Y-%m-%d %H:%M:%S')
|
||||
|
||||
echo "[$TIMESTAMP] Starting Voxyz daily mission generation..." >> "$LOG_FILE"
|
||||
|
||||
# Function to make API calls
|
||||
make_api_call() {
|
||||
local endpoint=$1
|
||||
local method=$2
|
||||
local data=$3
|
||||
|
||||
if [ -n "$API_KEY" ]; then
|
||||
curl -s -X "$method" \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "Authorization: Bearer $API_KEY" \
|
||||
-d "$data" \
|
||||
"${API_BASE_URL}${endpoint}"
|
||||
else
|
||||
curl -s -X "$method" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d "$data" \
|
||||
"${API_BASE_URL}${endpoint}"
|
||||
fi
|
||||
}
|
||||
|
||||
# Step 1: Check if missions already generated for today
|
||||
echo "[$TIMESTAMP] Checking for existing daily missions..." >> "$LOG_FILE"
|
||||
EXISTING=$(make_api_call "/daily-missions" "GET" "")
|
||||
|
||||
if echo "$EXISTING" | grep -q '"dailyMission":null'; then
|
||||
echo "[$TIMESTAMP] No existing daily mission found. Generating..." >> "$LOG_FILE"
|
||||
|
||||
# Step 2: Generate daily missions
|
||||
RESULT=$(make_api_call "/daily-missions" "POST" '{"targetMissionCount": 3, "includeReactions": true}')
|
||||
|
||||
if echo "$RESULT" | grep -q '"dailyMission"'; then
|
||||
echo "[$TIMESTAMP] Daily missions generated successfully" >> "$LOG_FILE"
|
||||
|
||||
# Extract proposal count
|
||||
PROPOSAL_COUNT=$(echo "$RESULT" | grep -o '"proposals":\[[^]]*\]' | tr ',' '\n' | wc -l)
|
||||
echo "[$TIMESTAMP] Generated $PROPOSAL_COUNT proposals" >> "$LOG_FILE"
|
||||
|
||||
# Step 3: Run trigger monitor to check for urgent items
|
||||
echo "[$TIMESTAMP] Running trigger monitor..." >> "$LOG_FILE"
|
||||
TRIGGER_RESULT=$(make_api_call "/trigger-rules" "POST" '{"action": "run-monitor"}')
|
||||
echo "[$TIMESTAMP] Trigger monitor completed" >> "$LOG_FILE"
|
||||
|
||||
# Step 4: Clean up expired proposals
|
||||
echo "[$TIMESTAMP] Cleaning up expired proposals..." >> "$LOG_FILE"
|
||||
EXPIRED_RESULT=$(make_api_call "/proposals" "DELETE" "")
|
||||
EXPIRED_COUNT=$(echo "$EXPIRED_RESULT" | grep -o '"expiredCount":[0-9]*' | cut -d: -f2)
|
||||
echo "[$TIMESTAMP] Expired $EXPIRED_COUNT old proposals" >> "$LOG_FILE"
|
||||
|
||||
echo "[$TIMESTAMP] Daily mission generation completed successfully" >> "$LOG_FILE"
|
||||
exit 0
|
||||
else
|
||||
echo "[$TIMESTAMP] ERROR: Failed to generate daily missions" >> "$LOG_FILE"
|
||||
echo "[$TIMESTAMP] Response: $RESULT" >> "$LOG_FILE"
|
||||
exit 1
|
||||
fi
|
||||
else
|
||||
echo "[$TIMESTAMP] Daily missions already exist for today. Skipping generation." >> "$LOG_FILE"
|
||||
exit 0
|
||||
fi
|
||||
Loading…
Reference in New Issue
Block a user