diff options
Diffstat (limited to 'makima/frontend/src/lib')
| -rw-r--r-- | makima/frontend/src/lib/api.ts | 758 |
1 files changed, 758 insertions, 0 deletions
diff --git a/makima/frontend/src/lib/api.ts b/makima/frontend/src/lib/api.ts index 2f4ee62..80a43eb 100644 --- a/makima/frontend/src/lib/api.ts +++ b/makima/frontend/src/lib/api.ts @@ -3527,3 +3527,761 @@ export async function setChainRepositoryPrimary( } return res.json(); } + +// ============================================================================= +// Directive Types and API +// ============================================================================= + +/** Directive status */ +export type DirectiveStatus = + | "draft" + | "planning" + | "active" + | "paused" + | "completed" + | "archived" + | "failed"; + +/** Autonomy level */ +export type AutonomyLevel = "full_auto" | "guardrails" | "manual"; + +/** Confidence level (traffic light) */ +export type ConfidenceLevel = "green" | "yellow" | "red"; + +/** Step status */ +export type StepStatus = + | "pending" + | "ready" + | "running" + | "evaluating" + | "passed" + | "failed" + | "rework" + | "skipped" + | "blocked"; + +/** Evaluation type */ +export type EvaluationType = "programmatic" | "llm" | "composite" | "manual"; + +/** Directive summary for list view */ +export interface DirectiveSummary { + id: string; + title: string; + goal: string; + status: DirectiveStatus; + autonomyLevel: AutonomyLevel; + repositoryUrl: string | null; + currentChainId: string | null; + currentChainGeneration: number | null; + totalSteps: number; + completedSteps: number; + failedSteps: number; + currentConfidence: number | null; + totalCostUsd: number; + createdAt: string; + updatedAt: string; +} + +/** Full directive */ +export interface Directive { + id: string; + ownerId: string; + title: string; + goal: string; + status: DirectiveStatus; + autonomyLevel: AutonomyLevel; + repositoryUrl: string | null; + localPath: string | null; + requirements: unknown | null; + acceptanceCriteria: unknown | null; + constraints: unknown | null; + confidenceThresholdGreen: number; + confidenceThresholdYellow: number; + maxReworkCycles: number; + maxTotalCostUsd: number | null; + maxWallTimeMinutes: number | null; + maxChainRegenerations: number; + totalCostUsd: number; + currentChainId: string | null; + version: number; + createdAt: string; + updatedAt: string; + startedAt: string | null; + completedAt: string | null; +} + +/** Directive chain */ +export interface DirectiveChain { + id: string; + directiveId: string; + generation: number; + name: string; + description: string | null; + rationale: string | null; + planningModel: string | null; + status: string; + totalSteps: number; + completedSteps: number; + failedSteps: number; + currentConfidence: number | null; + startedAt: string | null; + completedAt: string | null; + version: number; + createdAt: string; + updatedAt: string; +} + +/** Chain step */ +export interface ChainStep { + id: string; + chainId: string; + name: string; + description: string | null; + stepType: string; + contractType: string; + initialPhase: string | null; + taskPlan: string | null; + phases: string[]; + dependsOn: string[]; + parallelGroup: string | null; + requirementIds: string[]; + acceptanceCriteriaIds: string[]; + verifierConfig: unknown; + status: StepStatus; + contractId: string | null; + supervisorTaskId: string | null; + confidenceScore: number | null; + confidenceLevel: ConfidenceLevel | null; + evaluationCount: number; + reworkCount: number; + lastEvaluationId: string | null; + editorX: number | null; + editorY: number | null; + startedAt: string | null; + completedAt: string | null; + createdAt: string; + updatedAt: string; +} + +/** Directive with progress info */ +export interface DirectiveWithProgress extends Directive { + chain: DirectiveChain | null; + steps: ChainStep[]; + recentEvents: DirectiveEvent[]; + pendingApprovals: DirectiveApproval[]; +} + +/** Directive evaluation */ +export interface DirectiveEvaluation { + id: string; + directiveId: string; + chainId: string | null; + stepId: string | null; + evaluationType: EvaluationType; + passed: boolean; + overallScore: number; + confidenceLevel: ConfidenceLevel; + programmaticResults: unknown | null; + llmResults: unknown | null; + compositeBreakdown: unknown | null; + feedback: string | null; + reworkInstructions: string | null; + verifierIds: string[]; + evaluatedBy: string | null; + createdAt: string; +} + +/** Directive event */ +export interface DirectiveEvent { + id: string; + directiveId: string; + chainId: string | null; + stepId: string | null; + eventType: string; + severity: string; + eventData: unknown | null; + actorType: string; + actorId: string | null; + createdAt: string; +} + +/** Directive approval */ +export interface DirectiveApproval { + id: string; + directiveId: string; + chainId: string | null; + stepId: string | null; + approvalType: string; + description: string; + context: unknown | null; + urgency: string; + status: string; + requestedAt: string; + resolvedAt: string | null; + resolvedBy: string | null; + response: string | null; +} + +/** Directive verifier */ +export interface DirectiveVerifier { + id: string; + directiveId: string; + name: string; + verifierType: string; + command: string | null; + workingDirectory: string | null; + timeoutSeconds: number; + environment: unknown; + autoDetect: boolean; + detectFiles: string[]; + weight: number; + required: boolean; + enabled: boolean; + lastRunAt: string | null; + lastResult: unknown | null; + createdAt: string; + updatedAt: string; +} + +/** Directive graph node */ +export interface DirectiveGraphNode { + id: string; + name: string; + stepType: string; + status: StepStatus; + confidenceScore: number | null; + confidenceLevel: ConfidenceLevel | null; + contractId: string | null; + editorX: number | null; + editorY: number | null; +} + +/** Directive graph edge */ +export interface DirectiveGraphEdge { + source: string; + target: string; +} + +/** Directive graph response */ +export interface DirectiveGraphResponse { + chainId: string; + directiveId: string; + nodes: DirectiveGraphNode[]; + edges: DirectiveGraphEdge[]; +} + +/** Create directive request */ +export interface CreateDirectiveRequest { + goal: string; + repositoryUrl?: string; + localPath?: string; + autonomyLevel?: AutonomyLevel; + confidenceThresholdGreen?: number; + confidenceThresholdYellow?: number; + maxReworkCycles?: number; + maxTotalCostUsd?: number; + maxWallTimeMinutes?: number; +} + +/** Update directive request */ +export interface UpdateDirectiveRequest { + title?: string; + goal?: string; + requirements?: unknown; + acceptanceCriteria?: unknown; + constraints?: unknown; + autonomyLevel?: AutonomyLevel; + confidenceThresholdGreen?: number; + confidenceThresholdYellow?: number; + maxReworkCycles?: number; + maxTotalCostUsd?: number; + maxWallTimeMinutes?: number; + version?: number; +} + +/** Add step request */ +export interface AddStepRequest { + name: string; + description?: string; + stepType?: string; + contractType?: string; + initialPhase?: string; + taskPlan?: string; + phases?: string[]; + dependsOn?: string[]; + parallelGroup?: string; + requirementIds?: string[]; + acceptanceCriteriaIds?: string[]; + verifierConfig?: unknown; + editorX?: number; + editorY?: number; +} + +/** Update step request */ +export interface UpdateStepRequest { + name?: string; + description?: string; + initialPhase?: string; + taskPlan?: string; + phases?: string[]; + dependsOn?: string[]; + parallelGroup?: string; + requirementIds?: string[]; + acceptanceCriteriaIds?: string[]; + verifierConfig?: unknown; + editorX?: number; + editorY?: number; +} + +/** Create verifier request */ +export interface CreateVerifierRequest { + name: string; + verifierType: string; + command?: string; + workingDirectory?: string; + timeoutSeconds?: number; + weight?: number; + required?: boolean; + enabled?: boolean; +} + +/** Update verifier request */ +export interface UpdateVerifierRequest { + command?: string; + weight?: number; + required?: boolean; + enabled?: boolean; +} + +/** Approval action request */ +export interface ApprovalActionRequest { + response?: string; +} + +/** Start directive response */ +export interface StartDirectiveResponse { + directiveId: string; + chainId: string; + chainGeneration: number; + steps: ChainStep[]; + status: string; +} + +/** Directive list response */ +export interface DirectiveListResponse { + directives: DirectiveSummary[]; + total: number; +} + +// ============================================================================= +// Directive API Functions +// ============================================================================= + +/** List directives */ +export async function listDirectives( + status?: DirectiveStatus, + limit = 50, + offset = 0 +): Promise<DirectiveListResponse> { + const params = new URLSearchParams(); + if (status) params.set("status", status); + params.set("limit", String(limit)); + params.set("offset", String(offset)); + + const res = await authFetch(`${API_BASE}/api/v1/directives?${params}`); + if (!res.ok) { + throw new Error(`Failed to list directives: ${res.statusText}`); + } + return res.json(); +} + +/** Get directive by ID */ +export async function getDirective(directiveId: string): Promise<DirectiveWithProgress> { + const res = await authFetch(`${API_BASE}/api/v1/directives/${directiveId}`); + if (!res.ok) { + throw new Error(`Failed to get directive: ${res.statusText}`); + } + return res.json(); +} + +/** Create a new directive */ +export async function createDirective(req: CreateDirectiveRequest): Promise<Directive> { + const res = await authFetch(`${API_BASE}/api/v1/directives`, { + method: "POST", + body: JSON.stringify(req), + }); + if (!res.ok) { + const errorText = await res.text(); + throw new Error(`Failed to create directive: ${errorText || res.statusText}`); + } + return res.json(); +} + +/** Update directive */ +export async function updateDirective( + directiveId: string, + req: UpdateDirectiveRequest +): Promise<Directive> { + const res = await authFetch(`${API_BASE}/api/v1/directives/${directiveId}`, { + method: "PUT", + body: JSON.stringify(req), + }); + if (!res.ok) { + throw new Error(`Failed to update directive: ${res.statusText}`); + } + return res.json(); +} + +/** Archive directive */ +export async function archiveDirective(directiveId: string): Promise<{ archived: boolean }> { + const res = await authFetch(`${API_BASE}/api/v1/directives/${directiveId}`, { + method: "DELETE", + }); + if (!res.ok) { + throw new Error(`Failed to archive directive: ${res.statusText}`); + } + return res.json(); +} + +/** Start directive */ +export async function startDirective(directiveId: string): Promise<StartDirectiveResponse> { + const res = await authFetch(`${API_BASE}/api/v1/directives/${directiveId}/start`, { + method: "POST", + }); + if (!res.ok) { + throw new Error(`Failed to start directive: ${res.statusText}`); + } + return res.json(); +} + +/** Pause directive */ +export async function pauseDirective(directiveId: string): Promise<Directive> { + const res = await authFetch(`${API_BASE}/api/v1/directives/${directiveId}/pause`, { + method: "POST", + }); + if (!res.ok) { + throw new Error(`Failed to pause directive: ${res.statusText}`); + } + return res.json(); +} + +/** Resume directive */ +export async function resumeDirective(directiveId: string): Promise<Directive> { + const res = await authFetch(`${API_BASE}/api/v1/directives/${directiveId}/resume`, { + method: "POST", + }); + if (!res.ok) { + throw new Error(`Failed to resume directive: ${res.statusText}`); + } + return res.json(); +} + +/** Stop directive */ +export async function stopDirective(directiveId: string): Promise<Directive> { + const res = await authFetch(`${API_BASE}/api/v1/directives/${directiveId}/stop`, { + method: "POST", + }); + if (!res.ok) { + throw new Error(`Failed to stop directive: ${res.statusText}`); + } + return res.json(); +} + +/** Get directive chain */ +export async function getDirectiveChain( + directiveId: string +): Promise<{ chain: DirectiveChain | null; steps: ChainStep[] }> { + const res = await authFetch(`${API_BASE}/api/v1/directives/${directiveId}/chain`); + if (!res.ok) { + throw new Error(`Failed to get directive chain: ${res.statusText}`); + } + return res.json(); +} + +/** Get directive chain graph */ +export async function getDirectiveGraph(directiveId: string): Promise<DirectiveGraphResponse> { + const res = await authFetch(`${API_BASE}/api/v1/directives/${directiveId}/chain/graph`); + if (!res.ok) { + throw new Error(`Failed to get directive graph: ${res.statusText}`); + } + return res.json(); +} + +/** Replan directive chain */ +export async function replanDirectiveChain(directiveId: string): Promise<DirectiveChain> { + const res = await authFetch(`${API_BASE}/api/v1/directives/${directiveId}/chain/replan`, { + method: "POST", + }); + if (!res.ok) { + throw new Error(`Failed to replan directive chain: ${res.statusText}`); + } + return res.json(); +} + +/** Add step to directive chain */ +export async function addDirectiveStep( + directiveId: string, + req: AddStepRequest +): Promise<ChainStep> { + const res = await authFetch(`${API_BASE}/api/v1/directives/${directiveId}/chain/steps`, { + method: "POST", + body: JSON.stringify(req), + }); + if (!res.ok) { + throw new Error(`Failed to add step: ${res.statusText}`); + } + return res.json(); +} + +/** Get step details */ +export async function getDirectiveStep( + directiveId: string, + stepId: string +): Promise<ChainStep> { + const res = await authFetch(`${API_BASE}/api/v1/directives/${directiveId}/steps/${stepId}`); + if (!res.ok) { + throw new Error(`Failed to get step: ${res.statusText}`); + } + return res.json(); +} + +/** Update step */ +export async function updateDirectiveStep( + directiveId: string, + stepId: string, + req: UpdateStepRequest +): Promise<ChainStep> { + const res = await authFetch( + `${API_BASE}/api/v1/directives/${directiveId}/chain/steps/${stepId}`, + { + method: "PUT", + body: JSON.stringify(req), + } + ); + if (!res.ok) { + throw new Error(`Failed to update step: ${res.statusText}`); + } + return res.json(); +} + +/** Delete step */ +export async function deleteDirectiveStep( + directiveId: string, + stepId: string +): Promise<{ deleted: boolean }> { + const res = await authFetch( + `${API_BASE}/api/v1/directives/${directiveId}/chain/steps/${stepId}`, + { + method: "DELETE", + } + ); + if (!res.ok) { + throw new Error(`Failed to delete step: ${res.statusText}`); + } + return res.json(); +} + +/** Skip step */ +export async function skipDirectiveStep( + directiveId: string, + stepId: string +): Promise<ChainStep> { + const res = await authFetch( + `${API_BASE}/api/v1/directives/${directiveId}/steps/${stepId}/skip`, + { + method: "POST", + } + ); + if (!res.ok) { + throw new Error(`Failed to skip step: ${res.statusText}`); + } + return res.json(); +} + +/** List directive evaluations */ +export async function listDirectiveEvaluations( + directiveId: string, + limit = 50 +): Promise<DirectiveEvaluation[]> { + const res = await authFetch( + `${API_BASE}/api/v1/directives/${directiveId}/evaluations?limit=${limit}` + ); + if (!res.ok) { + throw new Error(`Failed to list evaluations: ${res.statusText}`); + } + return res.json(); +} + +/** List directive events */ +export async function listDirectiveEvents( + directiveId: string, + limit = 50 +): Promise<DirectiveEvent[]> { + const res = await authFetch( + `${API_BASE}/api/v1/directives/${directiveId}/events?limit=${limit}` + ); + if (!res.ok) { + throw new Error(`Failed to list events: ${res.statusText}`); + } + return res.json(); +} + +/** List directive verifiers */ +export async function listDirectiveVerifiers( + directiveId: string +): Promise<DirectiveVerifier[]> { + const res = await authFetch(`${API_BASE}/api/v1/directives/${directiveId}/verifiers`); + if (!res.ok) { + throw new Error(`Failed to list verifiers: ${res.statusText}`); + } + return res.json(); +} + +/** Add verifier */ +export async function addDirectiveVerifier( + directiveId: string, + req: CreateVerifierRequest +): Promise<DirectiveVerifier> { + const res = await authFetch(`${API_BASE}/api/v1/directives/${directiveId}/verifiers`, { + method: "POST", + body: JSON.stringify(req), + }); + if (!res.ok) { + throw new Error(`Failed to add verifier: ${res.statusText}`); + } + return res.json(); +} + +/** Update verifier */ +export async function updateDirectiveVerifier( + directiveId: string, + verifierId: string, + req: UpdateVerifierRequest +): Promise<DirectiveVerifier> { + const res = await authFetch( + `${API_BASE}/api/v1/directives/${directiveId}/verifiers/${verifierId}`, + { + method: "PUT", + body: JSON.stringify(req), + } + ); + if (!res.ok) { + throw new Error(`Failed to update verifier: ${res.statusText}`); + } + return res.json(); +} + +/** Auto-detect verifiers */ +export async function autoDetectVerifiers( + directiveId: string +): Promise<DirectiveVerifier[]> { + const res = await authFetch( + `${API_BASE}/api/v1/directives/${directiveId}/verifiers/auto-detect`, + { + method: "POST", + } + ); + if (!res.ok) { + throw new Error(`Failed to auto-detect verifiers: ${res.statusText}`); + } + return res.json(); +} + +/** List pending approvals */ +export async function listDirectiveApprovals( + directiveId: string +): Promise<DirectiveApproval[]> { + const res = await authFetch(`${API_BASE}/api/v1/directives/${directiveId}/approvals`); + if (!res.ok) { + throw new Error(`Failed to list approvals: ${res.statusText}`); + } + return res.json(); +} + +/** Approve request */ +export async function approveDirectiveRequest( + directiveId: string, + approvalId: string, + req?: ApprovalActionRequest +): Promise<DirectiveApproval> { + const res = await authFetch( + `${API_BASE}/api/v1/directives/${directiveId}/approvals/${approvalId}/approve`, + { + method: "POST", + body: JSON.stringify(req || {}), + } + ); + if (!res.ok) { + throw new Error(`Failed to approve request: ${res.statusText}`); + } + return res.json(); +} + +/** Deny request */ +export async function denyDirectiveRequest( + directiveId: string, + approvalId: string, + req?: ApprovalActionRequest +): Promise<DirectiveApproval> { + const res = await authFetch( + `${API_BASE}/api/v1/directives/${directiveId}/approvals/${approvalId}/deny`, + { + method: "POST", + body: JSON.stringify(req || {}), + } + ); + if (!res.ok) { + throw new Error(`Failed to deny request: ${res.statusText}`); + } + return res.json(); +} + +/** Subscribe to directive events via SSE */ +export async function subscribeToDirectiveEvents( + directiveId: string, + onEvent: (event: DirectiveEvent) => void, + onError?: (error: Error) => void +): Promise<() => void> { + // Get auth token for the request + let authToken: string | null = null; + if (supabase) { + const { data: { session } } = await supabase.auth.getSession(); + if (session?.access_token) { + authToken = session.access_token; + } + } + + // Build URL with auth token as query param (since EventSource doesn't support headers) + const url = new URL(`${API_BASE}/api/v1/directives/${directiveId}/events/stream`); + if (authToken) { + url.searchParams.set("token", authToken); + } else { + const apiKey = getStoredApiKey(); + if (apiKey) { + url.searchParams.set("api_key", apiKey); + } + } + + // Create EventSource connection + const eventSource = new EventSource(url.toString()); + + eventSource.onmessage = (e) => { + try { + const event = JSON.parse(e.data) as DirectiveEvent; + onEvent(event); + } catch (err) { + console.error("Failed to parse SSE event:", err); + } + }; + + eventSource.onerror = (_e) => { + if (onError) { + onError(new Error("SSE connection error")); + } + }; + + // Return cleanup function + return () => { + eventSource.close(); + }; +} |
