summaryrefslogtreecommitdiff
path: root/makima/frontend/src/lib
diff options
context:
space:
mode:
Diffstat (limited to 'makima/frontend/src/lib')
-rw-r--r--makima/frontend/src/lib/api.ts125
1 files changed, 125 insertions, 0 deletions
diff --git a/makima/frontend/src/lib/api.ts b/makima/frontend/src/lib/api.ts
index d7ac8b6..2ea1128 100644
--- a/makima/frontend/src/lib/api.ts
+++ b/makima/frontend/src/lib/api.ts
@@ -590,6 +590,9 @@ export interface Task {
version: number;
createdAt: string;
updatedAt: string;
+
+ // Supervisor flag
+ isSupervisor: boolean;
}
export interface TaskWithSubtasks extends Task {
@@ -892,6 +895,75 @@ export async function checkTargetExists(
return res.json();
}
+// =============================================================================
+// Task Recovery (Daemon Failover)
+// =============================================================================
+
+/** Request to reassign a task to a new daemon */
+export interface ReassignTaskRequest {
+ targetDaemonId?: string;
+ includeContext?: boolean;
+}
+
+/** Response from reassigning a task */
+export interface ReassignTaskResponse {
+ task: Task;
+ daemonId: string;
+ oldTaskId: string;
+ contextIncluded: boolean;
+ contextEntries: number;
+}
+
+/**
+ * Reassign a task to a new daemon after daemon disconnect.
+ * Creates a new task with conversation context, deletes the old one.
+ */
+export async function reassignTask(
+ taskId: string,
+ options?: ReassignTaskRequest
+): Promise<ReassignTaskResponse> {
+ const res = await authFetch(`${API_BASE}/api/v1/mesh/tasks/${taskId}/reassign`, {
+ method: "POST",
+ body: JSON.stringify(options || {}),
+ });
+ if (!res.ok) {
+ const errorText = await res.text();
+ throw new Error(`Failed to reassign task: ${errorText || res.statusText}`);
+ }
+ return res.json();
+}
+
+/** Request to continue a task */
+export interface ContinueTaskRequest {
+ targetDaemonId?: string;
+}
+
+/** Response from continuing a task */
+export interface ContinueTaskResponse {
+ task: Task;
+ daemonId: string;
+ contextEntries: number;
+}
+
+/**
+ * Continue a task after daemon disconnect by restarting it with conversation context.
+ * Unlike reassign, this keeps the same task ID.
+ */
+export async function continueTask(
+ taskId: string,
+ options?: ContinueTaskRequest
+): Promise<ContinueTaskResponse> {
+ const res = await authFetch(`${API_BASE}/api/v1/mesh/tasks/${taskId}/continue`, {
+ method: "POST",
+ body: JSON.stringify(options || {}),
+ });
+ if (!res.ok) {
+ const errorText = await res.text();
+ throw new Error(`Failed to continue task: ${errorText || res.statusText}`);
+ }
+ return res.json();
+}
+
export async function listSubtasks(taskId: string): Promise<TaskListResponse> {
const res = await authFetch(`${API_BASE}/api/v1/mesh/tasks/${taskId}/subtasks`);
if (!res.ok) {
@@ -1848,3 +1920,56 @@ export async function getTemplate(id: string): Promise<FileTemplate> {
}
return res.json();
}
+
+// =============================================================================
+// Supervisor Question Types and Functions
+// =============================================================================
+
+export interface PendingQuestion {
+ questionId: string;
+ taskId: string;
+ contractId: string;
+ question: string;
+ choices: string[];
+ context: string | null;
+ createdAt: string;
+}
+
+export interface AnswerQuestionRequest {
+ response: string;
+}
+
+export interface AnswerQuestionResponse {
+ success: boolean;
+}
+
+/**
+ * Get all pending supervisor questions for the current user.
+ */
+export async function listPendingQuestions(): Promise<PendingQuestion[]> {
+ const res = await authFetch(`${API_BASE}/api/v1/mesh/questions`);
+ if (!res.ok) {
+ throw new Error(`Failed to list questions: ${res.statusText}`);
+ }
+ return res.json();
+}
+
+/**
+ * Answer a pending supervisor question.
+ */
+export async function answerQuestion(
+ questionId: string,
+ response: string
+): Promise<AnswerQuestionResponse> {
+ const res = await authFetch(
+ `${API_BASE}/api/v1/mesh/questions/${questionId}/answer`,
+ {
+ method: "POST",
+ body: JSON.stringify({ response }),
+ }
+ );
+ if (!res.ok) {
+ throw new Error(`Failed to answer question: ${res.statusText}`);
+ }
+ return res.json();
+}