diff options
| author | soryu <soryu@soryu.co> | 2026-01-20 23:20:32 +0000 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2026-01-20 23:20:32 +0000 |
| commit | 7155e6cd7ddf25a5a4d4f6d6abecd49f0cf519dc (patch) | |
| tree | 2475a951c4bcba685b010909bf4abd5351cb3623 /makima/frontend/src/routes | |
| parent | 055e2c4a72e3b2331a18fdc9f8132ef990af7e38 (diff) | |
| download | soryu-7155e6cd7ddf25a5a4d4f6d6abecd49f0cf519dc.tar.gz soryu-7155e6cd7ddf25a5a4d4f6d6abecd49f0cf519dc.zip | |
Add non-blocking persistent contract completion questions (#14)
* [WIP] Heartbeat checkpoint - 2026-01-20 22:40:37 UTC
* Task completion checkpoint
Diffstat (limited to 'makima/frontend/src/routes')
| -rw-r--r-- | makima/frontend/src/routes/mesh.tsx | 63 |
1 files changed, 43 insertions, 20 deletions
diff --git a/makima/frontend/src/routes/mesh.tsx b/makima/frontend/src/routes/mesh.tsx index a8d3574..142cc54 100644 --- a/makima/frontend/src/routes/mesh.tsx +++ b/makima/frontend/src/routes/mesh.tsx @@ -5,6 +5,7 @@ import { TaskList } from "../components/mesh/TaskList"; import { TaskDetail } from "../components/mesh/TaskDetail"; import { TaskOutput } from "../components/mesh/TaskOutput"; import { UnifiedMeshChatInput } from "../components/mesh/UnifiedMeshChatInput"; +import { ContractCompleteQuestion } from "../components/mesh/ContractCompleteQuestion"; import { useTasks } from "../hooks/useTasks"; import { useTaskSubscription, type TaskUpdateEvent, type TaskOutputEvent } from "../hooks/useTaskSubscription"; import type { TaskWithSubtasks, MeshChatContext, ContractSummary, ContractWithRelations, DaemonDirectory, TaskSummary } from "../lib/api"; @@ -89,6 +90,14 @@ export default function MeshPage() { [pendingQuestions] ); + // Filter contract_complete questions for the current task + const contractCompleteQuestionsForTask = useMemo( + () => pendingQuestions.filter( + (q) => q.questionType === "contract_complete" && q.taskId === id + ), + [pendingQuestions, id] + ); + // Handler for answering supervisor questions const handleAnswerQuestion = useCallback(async (questionId: string, response: string) => { await submitAnswer(questionId, response); @@ -751,27 +760,41 @@ export default function MeshPage() { {/* Output panel */} {(viewMode === "split" || viewMode === "output") && ( <div - className="panel min-h-0 overflow-hidden flex-1" + className="panel min-h-0 overflow-hidden flex-1 flex flex-col" > - <TaskOutput - entries={taskOutputEntries} - isStreaming={isStreaming || taskDetail.status === "running"} - viewingSubtaskName={viewingSubtaskName} - onClearSubtaskView={viewingSubtaskId ? () => { - setViewingSubtaskId(null); - setViewingSubtaskName(null); - } : undefined} - onClear={() => { - setTaskOutputEntries([]); - if (activeOutputTaskId) { - clearPersistedOutput(activeOutputTaskId); - } - }} - taskId={activeOutputTaskId} - onUserInput={handleUserInput} - pendingQuestionIds={pendingQuestionIds} - onAnswerQuestion={handleAnswerQuestion} - /> + {/* Contract complete questions - shown prominently at top */} + {contractCompleteQuestionsForTask.length > 0 && ( + <div className="shrink-0 px-3 pt-3"> + {contractCompleteQuestionsForTask.map((question) => ( + <ContractCompleteQuestion + key={question.questionId} + question={question} + onAnswer={handleAnswerQuestion} + /> + ))} + </div> + )} + <div className="flex-1 min-h-0 overflow-hidden"> + <TaskOutput + entries={taskOutputEntries} + isStreaming={isStreaming || taskDetail.status === "running"} + viewingSubtaskName={viewingSubtaskName} + onClearSubtaskView={viewingSubtaskId ? () => { + setViewingSubtaskId(null); + setViewingSubtaskName(null); + } : undefined} + onClear={() => { + setTaskOutputEntries([]); + if (activeOutputTaskId) { + clearPersistedOutput(activeOutputTaskId); + } + }} + taskId={activeOutputTaskId} + onUserInput={handleUserInput} + pendingQuestionIds={pendingQuestionIds} + onAnswerQuestion={handleAnswerQuestion} + /> + </div> </div> )} </div> |
