summaryrefslogtreecommitdiff
path: root/makima/frontend/src/lib/api.ts
diff options
context:
space:
mode:
Diffstat (limited to 'makima/frontend/src/lib/api.ts')
-rw-r--r--makima/frontend/src/lib/api.ts399
1 files changed, 399 insertions, 0 deletions
diff --git a/makima/frontend/src/lib/api.ts b/makima/frontend/src/lib/api.ts
index 1e62732..9c56f6b 100644
--- a/makima/frontend/src/lib/api.ts
+++ b/makima/frontend/src/lib/api.ts
@@ -2093,3 +2093,402 @@ export async function createAdhocTask(
}
return res.json();
}
+
+// =============================================================================
+// History Types
+// =============================================================================
+
+/** History event from the timeline */
+export interface HistoryEvent {
+ id: string;
+ ownerId: string;
+ contractId: string | null;
+ taskId: string | null;
+ eventType: string;
+ eventSubtype: string | null;
+ phase: string | null;
+ eventData: Record<string, unknown>;
+ createdAt: string;
+}
+
+/** Response for contract history endpoint */
+export interface ContractHistoryResponse {
+ contractId: string;
+ entries: HistoryEvent[];
+ totalCount: number;
+ cursor: string | null;
+}
+
+/** Tool call info in conversation messages */
+export interface ToolCallInfo {
+ id: string;
+ name: string;
+ input: Record<string, unknown>;
+}
+
+/** Conversation message with optional tool calls */
+export interface ConversationMessage {
+ id: string;
+ role: string;
+ content: string;
+ timestamp: string;
+ toolCalls?: ToolCallInfo[];
+ toolName?: string;
+ toolInput?: Record<string, unknown>;
+ toolResult?: string;
+ isError?: boolean;
+ tokenCount?: number;
+ costUsd?: number;
+}
+
+/** Reference to a spawned task in supervisor conversation */
+export interface TaskReference {
+ taskId: string;
+ taskName: string;
+ status: string;
+ createdAt: string;
+ completedAt: string | null;
+}
+
+/** Response for supervisor conversation endpoint */
+export interface SupervisorConversationResponse {
+ contractId: string;
+ supervisorTaskId: string;
+ phase: string;
+ lastActivity: string;
+ pendingTaskIds: string[];
+ messages: ConversationMessage[];
+ spawnedTasks: TaskReference[];
+}
+
+/** Response for task conversation endpoint */
+export interface TaskConversationResponse {
+ taskId: string;
+ taskName: string;
+ status: string;
+ messages: ConversationMessage[];
+ totalTokens: number | null;
+ totalCost: number | null;
+}
+
+/** Query filters for timeline endpoint */
+export interface TimelineQueryFilters {
+ contractId?: string;
+ taskId?: string;
+ includeSubtasks?: boolean;
+ from?: string;
+ to?: string;
+ limit?: number;
+}
+
+/** Query filters for contract history endpoint */
+export interface HistoryQueryFilters {
+ phase?: string;
+ eventTypes?: string;
+ from?: string;
+ to?: string;
+ limit?: number;
+}
+
+/** Task checkpoint */
+export interface TaskCheckpoint {
+ id: string;
+ taskId: string;
+ checkpointNumber: number;
+ commitSha: string;
+ branchName: string;
+ message: string;
+ filesChanged: Array<{ path: string; action: string }>;
+ linesAdded: number;
+ linesRemoved: number;
+ createdAt: string;
+}
+
+// =============================================================================
+// History API Functions
+// =============================================================================
+
+/**
+ * Get contract history timeline.
+ */
+export async function getContractHistory(
+ contractId: string,
+ filters?: HistoryQueryFilters
+): Promise<ContractHistoryResponse> {
+ const params = new URLSearchParams();
+ if (filters?.phase) params.append("phase", filters.phase);
+ if (filters?.eventTypes) params.append("event_types", filters.eventTypes);
+ if (filters?.from) params.append("from", filters.from);
+ if (filters?.to) params.append("to", filters.to);
+ if (filters?.limit) params.append("limit", filters.limit.toString());
+
+ const query = params.toString();
+ const url = `${API_BASE}/api/v1/contracts/${contractId}/history${query ? `?${query}` : ""}`;
+
+ const res = await authFetch(url);
+ if (!res.ok) {
+ throw new Error(`Failed to get contract history: ${res.statusText}`);
+ }
+ return res.json();
+}
+
+/**
+ * Get supervisor conversation history.
+ */
+export async function getSupervisorConversation(
+ contractId: string
+): Promise<SupervisorConversationResponse> {
+ const res = await authFetch(
+ `${API_BASE}/api/v1/contracts/${contractId}/supervisor/conversation`
+ );
+ if (!res.ok) {
+ throw new Error(`Failed to get supervisor conversation: ${res.statusText}`);
+ }
+ return res.json();
+}
+
+/**
+ * Get task conversation history.
+ */
+export async function getTaskConversation(
+ taskId: string,
+ options?: { includeToolCalls?: boolean; includeToolResults?: boolean; limit?: number }
+): Promise<TaskConversationResponse> {
+ const params = new URLSearchParams();
+ if (options?.includeToolCalls !== undefined)
+ params.append("include_tool_calls", options.includeToolCalls.toString());
+ if (options?.includeToolResults !== undefined)
+ params.append("include_tool_results", options.includeToolResults.toString());
+ if (options?.limit) params.append("limit", options.limit.toString());
+
+ const query = params.toString();
+ const url = `${API_BASE}/api/v1/mesh/tasks/${taskId}/conversation${query ? `?${query}` : ""}`;
+
+ const res = await authFetch(url);
+ if (!res.ok) {
+ throw new Error(`Failed to get task conversation: ${res.statusText}`);
+ }
+ return res.json();
+}
+
+/**
+ * Get unified timeline for current user.
+ */
+export async function getTimeline(
+ filters?: TimelineQueryFilters
+): Promise<ContractHistoryResponse> {
+ const params = new URLSearchParams();
+ if (filters?.contractId) params.append("contract_id", filters.contractId);
+ if (filters?.taskId) params.append("task_id", filters.taskId);
+ if (filters?.includeSubtasks !== undefined)
+ params.append("include_subtasks", filters.includeSubtasks.toString());
+ if (filters?.from) params.append("from", filters.from);
+ if (filters?.to) params.append("to", filters.to);
+ if (filters?.limit) params.append("limit", filters.limit.toString());
+
+ const query = params.toString();
+ const url = `${API_BASE}/api/v1/timeline${query ? `?${query}` : ""}`;
+
+ const res = await authFetch(url);
+ if (!res.ok) {
+ throw new Error(`Failed to get timeline: ${res.statusText}`);
+ }
+ return res.json();
+}
+
+/**
+ * Get task checkpoints.
+ */
+export async function getTaskCheckpoints(taskId: string): Promise<TaskCheckpoint[]> {
+ const res = await authFetch(`${API_BASE}/api/v1/mesh/tasks/${taskId}/checkpoints`);
+ if (!res.ok) {
+ throw new Error(`Failed to get task checkpoints: ${res.statusText}`);
+ }
+ return res.json();
+}
+
+// =============================================================================
+// Resume/Rewind/Fork Types
+// =============================================================================
+
+/** Request to rewind a task to a checkpoint */
+export interface RewindTaskRequest {
+ checkpointId?: string;
+ checkpointSha?: string;
+ preserveMode: "discard" | "create_branch" | "stash";
+ branchName?: string;
+}
+
+/** Response from task rewind */
+export interface RewindTaskResponse {
+ taskId: string;
+ rewindedTo: {
+ checkpointNumber: number;
+ sha: string;
+ message: string;
+ };
+ preservedAs?: {
+ stateType: string;
+ reference: string;
+ };
+}
+
+/** Request to fork a task from a checkpoint */
+export interface ForkTaskRequest {
+ forkFromType: "checkpoint" | "timestamp" | "message_id";
+ forkFromValue: string;
+ newTaskName: string;
+ newTaskPlan: string;
+ includeConversation?: boolean;
+ createBranch?: boolean;
+ branchName?: string;
+}
+
+/** Response from task fork */
+export interface ForkTaskResponse {
+ newTaskId: string;
+ sourceTaskId: string;
+ forkPoint: {
+ forkType: string;
+ checkpoint?: TaskCheckpoint;
+ timestamp: string;
+ };
+ branchName?: string;
+ conversationIncluded: boolean;
+ messageCount?: number;
+}
+
+/** Request to resume supervisor */
+export interface ResumeSupervisorRequest {
+ resumeMode: "continue" | "restart_phase" | "from_checkpoint";
+ checkpointId?: string;
+ additionalContext?: string;
+}
+
+/** Response from supervisor resume */
+export interface ResumeSupervisorResponse {
+ supervisorTaskId: string;
+ daemonId: string | null;
+ contractId: string;
+ phase: string;
+ status: string;
+ conversationMessageCount: number;
+}
+
+/** Request to rewind supervisor conversation */
+export interface RewindConversationRequest {
+ toMessageId?: string;
+ byMessageCount?: number;
+ rewindCode?: boolean;
+}
+
+/** Response from conversation rewind */
+export interface RewindConversationResponse {
+ contractId: string;
+ messagesRemoved: number;
+ newMessageCount: number;
+}
+
+// =============================================================================
+// Resume/Rewind/Fork API Functions
+// =============================================================================
+
+/**
+ * Rewind a task to a checkpoint.
+ */
+export async function rewindTask(
+ taskId: string,
+ request: RewindTaskRequest
+): Promise<RewindTaskResponse> {
+ const res = await authFetch(`${API_BASE}/api/v1/mesh/tasks/${taskId}/rewind`, {
+ method: "POST",
+ body: JSON.stringify(request),
+ });
+ if (!res.ok) {
+ const errorText = await res.text();
+ throw new Error(`Failed to rewind task: ${errorText || res.statusText}`);
+ }
+ return res.json();
+}
+
+/**
+ * Fork a task from a checkpoint.
+ */
+export async function forkTask(
+ taskId: string,
+ request: ForkTaskRequest
+): Promise<ForkTaskResponse> {
+ const res = await authFetch(`${API_BASE}/api/v1/mesh/tasks/${taskId}/fork`, {
+ method: "POST",
+ body: JSON.stringify(request),
+ });
+ if (!res.ok) {
+ const errorText = await res.text();
+ throw new Error(`Failed to fork task: ${errorText || res.statusText}`);
+ }
+ return res.json();
+}
+
+/**
+ * Resume a supervisor.
+ */
+export async function resumeSupervisor(
+ contractId: string,
+ request: ResumeSupervisorRequest
+): Promise<ResumeSupervisorResponse> {
+ const res = await authFetch(
+ `${API_BASE}/api/v1/contracts/${contractId}/supervisor/resume`,
+ {
+ method: "POST",
+ body: JSON.stringify(request),
+ }
+ );
+ if (!res.ok) {
+ const errorText = await res.text();
+ throw new Error(`Failed to resume supervisor: ${errorText || res.statusText}`);
+ }
+ return res.json();
+}
+
+/**
+ * Rewind supervisor conversation.
+ */
+export async function rewindSupervisorConversation(
+ contractId: string,
+ request: RewindConversationRequest
+): Promise<RewindConversationResponse> {
+ const res = await authFetch(
+ `${API_BASE}/api/v1/contracts/${contractId}/supervisor/conversation/rewind`,
+ {
+ method: "POST",
+ body: JSON.stringify(request),
+ }
+ );
+ if (!res.ok) {
+ const errorText = await res.text();
+ throw new Error(`Failed to rewind conversation: ${errorText || res.statusText}`);
+ }
+ return res.json();
+}
+
+/**
+ * Resume task from a checkpoint.
+ */
+export async function resumeFromCheckpoint(
+ taskId: string,
+ checkpointId: string,
+ request: { taskName?: string; plan: string; includeConversation?: boolean }
+): Promise<{ taskId: string }> {
+ const res = await authFetch(
+ `${API_BASE}/api/v1/mesh/tasks/${taskId}/checkpoints/${checkpointId}/resume`,
+ {
+ method: "POST",
+ body: JSON.stringify(request),
+ }
+ );
+ if (!res.ok) {
+ const errorText = await res.text();
+ throw new Error(`Failed to resume from checkpoint: ${errorText || res.statusText}`);
+ }
+ return res.json();
+}