package server import ( "encoding/json" "fmt" "net/http" "strconv" "strings" "agent-system-backend/internal/store" core "agent-system-core" ) func (s *Server) createBackgroundTask(w http.ResponseWriter, r *http.Request) { var body struct { Kind string `json:"kind"` Title string `json:"title"` ConversationID string `json:"conversationId"` } if err := json.NewDecoder(r.Body).Decode(&body); err != nil || body.Kind == "" { http.Error(w, "kind required", http.StatusBadRequest) return } task, err := s.st.CreateBackgroundTask(s.owner(r), body.Kind, body.Title, body.ConversationID) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } writeJSON(w, http.StatusCreated, task) } func (s *Server) listBackgroundTasks(w http.ResponseWriter, r *http.Request) { tasks, err := s.st.ListBackgroundTasks(s.owner(r)) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } writeJSON(w, http.StatusOK, orEmptySlice(tasks)) } func (s *Server) getBackgroundTask(w http.ResponseWriter, r *http.Request) { task, err := s.st.GetBackgroundTask(s.owner(r), r.PathValue("id")) if err != nil { http.Error(w, "not found", http.StatusNotFound) return } writeJSON(w, http.StatusOK, task) } func (s *Server) updateBackgroundTask(w http.ResponseWriter, r *http.Request) { var body struct { Status string `json:"status"` Progress int `json:"progress"` Error string `json:"error"` } if err := json.NewDecoder(r.Body).Decode(&body); err != nil || body.Status == "" { http.Error(w, "status required", http.StatusBadRequest) return } if err := s.st.UpdateBackgroundTask(s.owner(r), r.PathValue("id"), body.Status, body.Progress, body.Error); err != nil { http.Error(w, err.Error(), http.StatusNotFound) return } s.getBackgroundTask(w, r) } func (s *Server) putMemory(w http.ResponseWriter, r *http.Request) { var e core.MemoryEntry if err := json.NewDecoder(r.Body).Decode(&e); err != nil || strings.TrimSpace(e.Content) == "" { http.Error(w, "content required", http.StatusBadRequest) return } if e.Scope == "" { e.Scope = "user" } if e.Type == "" { e.Type = "fact" } if e.Source == "" { e.Source = "manual" } saved, err := s.st.PutMemory(s.owner(r), e) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } writeJSON(w, http.StatusCreated, saved) } func (s *Server) listMemory(w http.ResponseWriter, r *http.Request) { q := r.URL.Query() list, err := s.st.ListMemory(s.owner(r), q.Get("scope"), q.Get("projectId")) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } writeJSON(w, http.StatusOK, orEmptySlice(list)) } func (s *Server) listAudit(w http.ResponseWriter, r *http.Request) { limit, _ := strconv.Atoi(r.URL.Query().Get("limit")) evs, err := s.st.ListAuditEvents(s.owner(r), limit) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } writeJSON(w, http.StatusOK, orEmptySlice(evs)) } func (s *Server) postAudit(w http.ResponseWriter, r *http.Request) { var body struct { Action string `json:"action"` Target string `json:"target"` Evidence string `json:"evidence"` } if err := json.NewDecoder(r.Body).Decode(&body); err != nil || body.Action == "" { http.Error(w, "action required", http.StatusBadRequest) return } if err := s.st.AppendAuditEvent(store.AuditEvent{ Owner: s.owner(r), Actor: s.owner(r), Action: body.Action, Target: body.Target, Evidence: body.Evidence, TraceID: r.Header.Get("X-Trace-Id"), }); err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } w.WriteHeader(http.StatusNoContent) } func (s *Server) setPluginEnabled(w http.ResponseWriter, r *http.Request) { var body struct { Enabled bool `json:"enabled"` } if err := json.NewDecoder(r.Body).Decode(&body); err != nil { http.Error(w, "bad json", http.StatusBadRequest) return } if err := s.st.SetPluginEnabled(s.owner(r), r.PathValue("id"), body.Enabled); err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } w.WriteHeader(http.StatusNoContent) } func (s *Server) llmBudget(w http.ResponseWriter, r *http.Request) { budget := int(s.set.Effective(s.owner(r), "").GetNumber("llm.dailyTokenBudget", 0)) status, err := s.st.AccountTokens(s.owner(r), 0, budget) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } writeJSON(w, http.StatusOK, map[string]any{"total": status.Total, "budget": budget, "over": status.Over}) } func (s *Server) exportConversation(w http.ResponseWriter, r *http.Request) { owner := s.owner(r) id := r.PathValue("id") conv, err := s.st.GetConversation(owner, id) if err != nil { http.Error(w, "not found", http.StatusNotFound) return } msgs, err := s.st.ListMessages(id) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } if r.URL.Query().Get("format") == "md" { w.Header().Set("Content-Type", "text/markdown; charset=utf-8") fmt.Fprintf(w, "# %s\n\n", conv.Title) for _, m := range msgs { fmt.Fprintf(w, "## %s\n\n%s\n\n", m.Role, messageText(m)) } return } writeJSON(w, http.StatusOK, map[string]any{"conversation": conv, "messages": msgs}) } func messageText(m core.Message) string { var b strings.Builder for _, p := range m.Parts { if p.Type == core.PartText { b.WriteString(p.Text) } } return b.String() }