diff options
| author | soryu <soryu@soryu.co> | 2026-02-13 14:08:26 +0000 |
|---|---|---|
| committer | soryu <soryu@soryu.co> | 2026-02-13 14:08:26 +0000 |
| commit | 13ef4416e5c35c7f03296765943c298d10d3aaf9 (patch) | |
| tree | c8d01936f0749253db78f6fc2facfc18e619370d | |
| parent | 0fdcbea27b3b736dd21a460339b39b975ca91b99 (diff) | |
| download | soryu-makima/makima-jp--add-question-system-for-directive-orche-47f3c023.tar.gz soryu-makima/makima-jp--add-question-system-for-directive-orche-47f3c023.zip | |
WIP: heartbeat checkpointmakima/makima-jp--add-question-system-for-directive-orche-47f3c023
| -rw-r--r-- | makima/frontend/src/components/SupervisorQuestionNotification.tsx | 93 | ||||
| -rw-r--r-- | makima/frontend/src/components/directives/DirectiveDetail.tsx | 23 | ||||
| -rw-r--r-- | makima/frontend/src/hooks/useDirectives.ts | 9 | ||||
| -rw-r--r-- | makima/frontend/src/lib/api.ts | 27 | ||||
| -rw-r--r-- | makima/frontend/src/routes/directives.tsx | 3 |
5 files changed, 120 insertions, 35 deletions
diff --git a/makima/frontend/src/components/SupervisorQuestionNotification.tsx b/makima/frontend/src/components/SupervisorQuestionNotification.tsx index b1cbacc..2bded08 100644 --- a/makima/frontend/src/components/SupervisorQuestionNotification.tsx +++ b/makima/frontend/src/components/SupervisorQuestionNotification.tsx @@ -14,47 +14,74 @@ export function SupervisorQuestionNotification() { return null; } - const handleGoToTask = (questionId: string, taskId: string) => { + const handleGoToTask = (questionId: string, taskId: string, directiveId?: string | null) => { dismissNotification(questionId); - navigate(`/mesh/${taskId}`); + if (directiveId) { + navigate(`/directives/${directiveId}`); + } else { + navigate(`/mesh/${taskId}`); + } }; return ( <div className="fixed bottom-4 right-4 z-50 max-w-md space-y-2"> - {filteredQuestions.map((question) => ( - <div - key={question.questionId} - className="bg-[#0d1b2d] border border-amber-500/50 rounded-lg shadow-lg overflow-hidden" - > - {/* Header */} - <div className="flex items-center justify-between px-4 py-3 bg-amber-900/30"> - <div className="flex items-center gap-2"> - <span className="text-amber-400 text-lg">?</span> - <span className="font-mono text-sm text-amber-300 uppercase"> - Task needs input - </span> - </div> - <button - onClick={() => handleGoToTask(question.questionId, question.taskId)} - className="px-3 py-1 font-mono text-xs text-amber-400 border border-amber-500/30 hover:border-amber-400/50 hover:bg-amber-900/20 transition-colors uppercase" + {filteredQuestions.map((question) => { + const isDirective = !!question.directiveId; + return ( + <div + key={question.questionId} + className={`border rounded-lg shadow-lg overflow-hidden ${ + isDirective + ? "bg-[#0d1b2d] border-violet-500/50" + : "bg-[#0d1b2d] border-amber-500/50" + }`} + > + {/* Header */} + <div + className={`flex items-center justify-between px-4 py-3 ${ + isDirective ? "bg-violet-900/30" : "bg-amber-900/30" + }`} > - View Task - </button> - </div> - - {/* Question preview */} - <div className="px-4 py-3"> - {question.context && ( - <div className="text-xs text-[#8b949e] font-mono mb-1 uppercase"> - {question.context} + <div className="flex items-center gap-2"> + <span className={isDirective ? "text-violet-400 text-lg" : "text-amber-400 text-lg"}> + {isDirective ? "⚡" : "?"} + </span> + <span + className={`font-mono text-sm uppercase ${ + isDirective ? "text-violet-300" : "text-amber-300" + }`} + > + {isDirective ? "Directive needs input" : "Task needs input"} + </span> </div> - )} - <p className="text-sm text-[#dbe7ff] font-mono line-clamp-2"> - {question.question} - </p> + <button + onClick={() => + handleGoToTask(question.questionId, question.taskId, question.directiveId) + } + className={`px-3 py-1 font-mono text-xs border transition-colors uppercase ${ + isDirective + ? "text-violet-400 border-violet-500/30 hover:border-violet-400/50 hover:bg-violet-900/20" + : "text-amber-400 border-amber-500/30 hover:border-amber-400/50 hover:bg-amber-900/20" + }`} + > + {isDirective ? "View Directive" : "View Task"} + </button> + </div> + + {/* Question preview */} + <div className="px-4 py-3"> + {question.context && ( + <div className="text-xs text-[#8b949e] font-mono mb-1 uppercase"> + {question.context} + </div> + )} + <p className="text-sm text-[#dbe7ff] font-mono line-clamp-2"> + {question.question} + </p> + </div> </div> - </div> - ))} + ); + })} </div> ); } diff --git a/makima/frontend/src/components/directives/DirectiveDetail.tsx b/makima/frontend/src/components/directives/DirectiveDetail.tsx index 369cdaa..c8d026f 100644 --- a/makima/frontend/src/components/directives/DirectiveDetail.tsx +++ b/makima/frontend/src/components/directives/DirectiveDetail.tsx @@ -37,6 +37,7 @@ interface DirectiveDetailProps { onDelete: () => void; onRefresh: () => void; onCleanupTasks: () => void; + onToggleReconcileMode: (enabled: boolean) => void; } export function DirectiveDetail({ @@ -51,6 +52,7 @@ export function DirectiveDetail({ onDelete, onRefresh, onCleanupTasks, + onToggleReconcileMode, }: DirectiveDetailProps) { const [editingGoal, setEditingGoal] = useState(false); const [goalText, setGoalText] = useState(directive.goal); @@ -296,6 +298,27 @@ export function DirectiveDetail({ Delete </button> </div> + + {/* Settings row */} + <div className="flex items-center gap-4 mt-2 pt-2 border-t border-[rgba(117,170,252,0.05)]"> + <label className="flex items-center gap-1.5 cursor-pointer group"> + <input + type="checkbox" + checked={directive.reconcileMode} + onChange={(e) => onToggleReconcileMode(e.target.checked)} + className="w-3 h-3 rounded border-[#2a3a5a] bg-transparent accent-violet-500" + /> + <span className="text-[10px] font-mono text-[#556677] group-hover:text-[#9bc3ff] transition-colors"> + Reconcile mode + </span> + <span + className="text-[9px] font-mono text-[#3a4a60] cursor-help" + title="When enabled, directive tasks pause indefinitely when asking questions and wait for your response. When disabled (default), questions timeout after 30 seconds and the directive continues anyway." + > + [?] + </span> + </label> + </div> </div> {/* Goal */} diff --git a/makima/frontend/src/hooks/useDirectives.ts b/makima/frontend/src/hooks/useDirectives.ts index e67733c..0a40338 100644 --- a/makima/frontend/src/hooks/useDirectives.ts +++ b/makima/frontend/src/hooks/useDirectives.ts @@ -20,6 +20,7 @@ import { skipDirectiveStep, updateDirectiveGoal, cleanupDirectiveTasks, + setDirectiveReconcileMode, } from "../lib/api"; export function useDirectives() { @@ -160,11 +161,17 @@ export function useDirective(id: string | undefined) { await refresh(); }, [id, refresh]); + const toggleReconcileMode = useCallback(async (enabled: boolean) => { + if (!id) return; + await setDirectiveReconcileMode(id, enabled); + await refresh(); + }, [id, refresh]); + return { directive, loading, error, refresh, update, addStep, removeStep, start, pause, advance, completeStep, failStep, skipStep, - updateGoal, cleanupTasks, + updateGoal, cleanupTasks, toggleReconcileMode, }; } diff --git a/makima/frontend/src/lib/api.ts b/makima/frontend/src/lib/api.ts index 9d9cb1c..52bbf73 100644 --- a/makima/frontend/src/lib/api.ts +++ b/makima/frontend/src/lib/api.ts @@ -2241,6 +2241,8 @@ export interface PendingQuestion { questionId: string; taskId: string; contractId: string; + /** Directive ID if this question is from a directive task */ + directiveId?: string | null; question: string; choices: string[]; context: string | null; @@ -3025,6 +3027,8 @@ export interface Directive { completionTaskId: string | null; /** Whether the memory system is enabled for this directive */ memoryEnabled: boolean; + /** Whether reconcile mode is enabled (directive tasks pause indefinitely when asking questions) */ + reconcileMode: boolean; goalUpdatedAt: string; startedAt: string | null; version: number; @@ -3064,6 +3068,8 @@ export interface DirectiveSummary { completionTaskId: string | null; /** Whether the memory system is enabled for this directive */ memoryEnabled: boolean; + /** Whether reconcile mode is enabled */ + reconcileMode: boolean; version: number; createdAt: string; updatedAt: string; @@ -3246,6 +3252,27 @@ export async function cleanupDirectiveTasks(id: string): Promise<{ deleted: numb return res.json(); } +/** + * Set reconcile mode for a directive. + * When enabled, directive tasks that ask questions pause indefinitely until answered. + * When disabled, questions have a 30s timeout and the directive continues regardless. + */ +export async function setDirectiveReconcileMode( + id: string, + reconcileMode: boolean +): Promise<Directive> { + const res = await authFetch( + `${API_BASE}/api/v1/directives/${id}/reconcile-mode`, + { + method: "PUT", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ reconcile_mode: reconcileMode }), + } + ); + if (!res.ok) throw new Error(`Failed to set reconcile mode: ${res.statusText}`); + return res.json(); +} + // ============================================================================= // Directive Memory Types & API // ============================================================================= diff --git a/makima/frontend/src/routes/directives.tsx b/makima/frontend/src/routes/directives.tsx index ca4437c..5f9b3a8 100644 --- a/makima/frontend/src/routes/directives.tsx +++ b/makima/frontend/src/routes/directives.tsx @@ -12,7 +12,7 @@ export default function DirectivesPage() { const navigate = useNavigate(); const { id: selectedId } = useParams<{ id: string }>(); const { directives, loading: listLoading, create, remove } = useDirectives(); - const { directive, refresh: refreshDetail, start, pause, advance, completeStep, failStep, skipStep, updateGoal, cleanupTasks } = useDirective(selectedId); + const { directive, refresh: refreshDetail, start, pause, advance, completeStep, failStep, skipStep, updateGoal, cleanupTasks, toggleReconcileMode } = useDirective(selectedId); const [showCreate, setShowCreate] = useState(false); const [newTitle, setNewTitle] = useState(""); @@ -210,6 +210,7 @@ export default function DirectivesPage() { onDelete={handleDelete} onRefresh={refreshDetail} onCleanupTasks={cleanupTasks} + onToggleReconcileMode={toggleReconcileMode} /> ) : ( <div className="flex-1 flex items-center justify-center h-full"> |
