summaryrefslogtreecommitdiff
path: root/internal/api/server.go
diff options
context:
space:
mode:
Diffstat (limited to 'internal/api/server.go')
-rw-r--r--internal/api/server.go36
1 files changed, 33 insertions, 3 deletions
diff --git a/internal/api/server.go b/internal/api/server.go
index c545253..df35536 100644
--- a/internal/api/server.go
+++ b/internal/api/server.go
@@ -24,6 +24,7 @@ type questionStore interface {
GetLatestExecution(taskID string) (*storage.Execution, error)
UpdateTaskQuestion(taskID, questionJSON string) error
UpdateTaskState(id string, newState task.State) error
+ AppendTaskInteraction(taskID string, interaction task.Interaction) error
}
// Server provides the REST API and WebSocket endpoint for Claudomator.
@@ -250,6 +251,25 @@ func (s *Server) handleAnswerQuestion(w http.ResponseWriter, r *http.Request) {
return
}
+ // Record the Q&A interaction before clearing the question.
+ if tk.QuestionJSON != "" {
+ var qData struct {
+ Text string `json:"text"`
+ Options []string `json:"options"`
+ }
+ if jsonErr := json.Unmarshal([]byte(tk.QuestionJSON), &qData); jsonErr == nil {
+ interaction := task.Interaction{
+ QuestionText: qData.Text,
+ Options: qData.Options,
+ Answer: input.Answer,
+ AskedAt: tk.UpdatedAt,
+ }
+ if appendErr := s.questionStore.AppendTaskInteraction(taskID, interaction); appendErr != nil {
+ s.logger.Error("failed to append interaction", "taskID", taskID, "error", appendErr)
+ }
+ }
+ }
+
// Clear the question and transition to QUEUED.
if err := s.questionStore.UpdateTaskQuestion(taskID, ""); err != nil {
writeJSON(w, http.StatusInternalServerError, map[string]string{"error": "failed to clear question"})
@@ -277,6 +297,14 @@ func (s *Server) handleAnswerQuestion(w http.ResponseWriter, r *http.Request) {
writeJSON(w, http.StatusOK, map[string]string{"message": "task queued for resume", "task_id": taskID})
}
+// resumableStates are the task states from which a session-based resume is valid.
+var resumableStates = map[task.State]string{
+ task.StateTimedOut: "Your previous execution timed out. Please continue where you left off and complete the task.",
+ task.StateCancelled: "Your previous execution was cancelled. Please continue where you left off and complete the task.",
+ task.StateFailed: "Your previous execution failed. Please review what happened and continue from where you left off.",
+ task.StateBudgetExceeded: "Your previous execution exceeded its budget. Please continue where you left off and complete the task.",
+}
+
func (s *Server) handleResumeTimedOutTask(w http.ResponseWriter, r *http.Request) {
taskID := r.PathValue("id")
@@ -285,8 +313,10 @@ func (s *Server) handleResumeTimedOutTask(w http.ResponseWriter, r *http.Request
writeJSON(w, http.StatusNotFound, map[string]string{"error": "task not found"})
return
}
- if tk.State != task.StateTimedOut {
- writeJSON(w, http.StatusConflict, map[string]string{"error": "task is not timed out"})
+
+ resumeMsg, resumable := resumableStates[tk.State]
+ if !resumable {
+ writeJSON(w, http.StatusConflict, map[string]string{"error": "task is not in a resumable state"})
return
}
@@ -302,7 +332,7 @@ func (s *Server) handleResumeTimedOutTask(w http.ResponseWriter, r *http.Request
ID: uuid.New().String(),
TaskID: taskID,
ResumeSessionID: latest.SessionID,
- ResumeAnswer: "Your previous execution timed out. Please continue where you left off and complete the task.",
+ ResumeAnswer: resumeMsg,
}
if err := s.pool.SubmitResume(context.Background(), tk, resumeExec); err != nil {
writeJSON(w, http.StatusServiceUnavailable, map[string]string{"error": err.Error()})