package main import ( "context" "encoding/json" "errors" "fmt" "git.cin.su/m/Assistant/adapters/postgres" "git.cin.su/m/Assistant/contracts/authority" "git.cin.su/m/Assistant/contracts/capability" "git.cin.su/m/Assistant/contracts/continuity" "git.cin.su/m/Assistant/contracts/epistemics" "git.cin.su/m/Assistant/contracts/identity" "git.cin.su/m/Assistant/contracts/interaction" secretcontracts "git.cin.su/m/Assistant/contracts/secrets" "git.cin.su/m/Assistant/contracts/situation" "git.cin.su/m/Assistant/contracts/work" capabilitydomain "git.cin.su/m/Assistant/modules/capability/domain" identitydomain "git.cin.su/m/Assistant/modules/identity/domain" interactiondomain "git.cin.su/m/Assistant/modules/interaction/domain" situationdomain "git.cin.su/m/Assistant/modules/situation/domain" workdomain "git.cin.su/m/Assistant/modules/work/domain" platformsecrets "git.cin.su/m/Assistant/platform/secrets" "git.cin.su/m/Assistant/runtime/health" "github.com/jackc/pgx/v5" "io" "log" "net/http" "os" "strconv" "strings" "time" ) const maxCreatorCredentialsBytes = 4 << 10 func main() { addr := os.Getenv("LISTEN_ADDR") if addr == "" { addr = ":8080" } bootstrapToken, err := readRequiredSecret("CREATOR_BOOTSTRAP_TOKEN") if err != nil { log.Fatal(err) } sessionKeyValue, err := readRequiredSecret("CREATOR_SESSION_SIGNING_KEY") if err != nil { log.Fatal(err) } creatorPassword, err := readRequiredSecret("CREATOR_PASSWORD") if err != nil { log.Fatal(err) } sessionKey := []byte(sessionKeyValue) creatorUsername := os.Getenv("CREATOR_USERNAME") repository, err := postgres.OpenSituation(context.Background(), postgres.ContinuityConfig{Host: os.Getenv("POSTGRES_HOST"), Port: os.Getenv("POSTGRES_PORT"), Database: os.Getenv("POSTGRES_DATABASE"), User: os.Getenv("POSTGRES_USER"), Password: os.Getenv("POSTGRES_PASSWORD")}) if err != nil { log.Fatal(err) } defer repository.Close() workRepository, err := postgres.OpenWork(context.Background(), postgres.ContinuityConfig{Host: os.Getenv("POSTGRES_HOST"), Port: os.Getenv("POSTGRES_PORT"), Database: os.Getenv("POSTGRES_DATABASE"), User: os.Getenv("WORK_POSTGRES_USER"), Password: os.Getenv("WORK_POSTGRES_PASSWORD")}) if err != nil { log.Fatal(err) } defer workRepository.Close() capabilityRepository, err := postgres.OpenCapability(context.Background(), postgres.ContinuityConfig{Host: os.Getenv("POSTGRES_HOST"), Port: os.Getenv("POSTGRES_PORT"), Database: os.Getenv("POSTGRES_DATABASE"), User: os.Getenv("CAPABILITY_POSTGRES_USER"), Password: os.Getenv("CAPABILITY_POSTGRES_PASSWORD")}) if err != nil { log.Fatal(err) } defer capabilityRepository.Close() epistemicsPassword, err := readCredentialRefSecret("EPISTEMICS_POSTGRES_PASSWORD", "EPISTEMICS_POSTGRES_CREDENTIAL_REF") if err != nil { log.Fatal(err) } epistemicsUser := os.Getenv("EPISTEMICS_POSTGRES_USER") if epistemicsUser == "" { epistemicsUser = "epistemics_role" } epistemicsRepository, err := postgres.OpenEpistemics(context.Background(), postgres.ContinuityConfig{Host: os.Getenv("POSTGRES_HOST"), Port: os.Getenv("POSTGRES_PORT"), Database: os.Getenv("POSTGRES_DATABASE"), User: epistemicsUser, Password: epistemicsPassword}) if err != nil { log.Fatal(err) } defer epistemicsRepository.Close() identityRepository, err := postgres.OpenIdentity(context.Background(), postgres.ContinuityConfig{Host: os.Getenv("POSTGRES_HOST"), Port: os.Getenv("POSTGRES_PORT"), Database: os.Getenv("POSTGRES_DATABASE"), User: os.Getenv("IDENTITY_POSTGRES_USER"), Password: os.Getenv("IDENTITY_POSTGRES_PASSWORD")}) if err != nil { log.Fatal(err) } defer identityRepository.Close() interactionPassword, err := readCredentialRefSecret("INTERACTION_POSTGRES_PASSWORD", "INTERACTION_POSTGRES_CREDENTIAL_REF") if err != nil { log.Fatal(err) } interactionUser := os.Getenv("INTERACTION_POSTGRES_USER") if interactionUser == "" { interactionUser = "interaction_role" } interactionRepository, err := postgres.OpenInteraction(context.Background(), postgres.ContinuityConfig{Host: os.Getenv("POSTGRES_HOST"), Port: os.Getenv("POSTGRES_PORT"), Database: os.Getenv("POSTGRES_DATABASE"), User: interactionUser, Password: interactionPassword}) if err != nil { log.Fatal(err) } defer interactionRepository.Close() reminderClient, err := newContinuityReminderClient(envOr("CONTINUITY_RUNTIME_URL", "http://continuity-runtime:8081"), &http.Client{Timeout: 5 * time.Second}) if err != nil { log.Fatal(err) } responseClient, err := newContinuityResponseClient(envOr("CONTINUITY_RUNTIME_URL", "http://continuity-runtime:8081"), &http.Client{Timeout: 5 * time.Second}) if err != nil { log.Fatal(err) } learning := learningRecorder{epistemics: epistemicsRepository, capability: capabilityRepository} mux := http.NewServeMux() authenticateCreator := func(w http.ResponseWriter, r *http.Request) (identity.CreatorSession, bool) { session, err := identitydomain.ValidateCreatorSession(sessionKey, bearerToken(r), time.Now()) if err != nil { http.Error(w, "creator session denied", http.StatusUnauthorized) return identity.CreatorSession{}, false } return session, true } resolveFocus := func(r *http.Request) (situation.FocusBinding, error) { runtime := situation.RuntimeBinding{SessionID: situation.SessionID(r.URL.Query().Get("session_id")), Workspace: situation.WorkspaceID(r.URL.Query().Get("workspace_id"))} bindings, err := repository.ByRuntime(r.Context(), runtime) if err != nil { return situation.FocusBinding{}, err } return situationdomain.NewResolver(bindings).Resolve(runtime) } mux.Handle("/healthz", health.Handler{Runtime: "core-runtime", SemanticGeneration: 1, ArchitectureGeneration: 1}) mux.HandleFunc("POST /v1/creator/sessions", creatorSessionHandler(bootstrapToken, creatorUsername, creatorPassword, sessionKey, time.Now)) mux.HandleFunc("GET /v1/creator/sessions/current", func(w http.ResponseWriter, r *http.Request) { session, ok := authenticateCreator(w, r) if !ok { return } w.Header().Set("Content-Type", "application/json") _ = json.NewEncoder(w).Encode(session) }) mux.HandleFunc("GET /v1/creator/focus", func(w http.ResponseWriter, r *http.Request) { session, ok := authenticateCreator(w, r) if !ok { return } focus, err := resolveFocus(r) if err != nil { http.Error(w, "creator focus unavailable", http.StatusConflict) return } if focus.ActorID != session.ActorID { http.Error(w, "creator focus denied", http.StatusForbidden) return } w.Header().Set("Content-Type", "application/json") _ = json.NewEncoder(w).Encode(focus) }) mux.HandleFunc("GET /v1/creator/tasks/{id}", func(w http.ResponseWriter, r *http.Request) { session, ok := authenticateCreator(w, r) if !ok { return } focus, err := resolveFocus(r) if err != nil || focus.ActorID != session.ActorID { http.Error(w, "creator focus denied", http.StatusForbidden) return } target, err := workRepository.Load(r.Context(), work.TaskID(r.PathValue("id"))) if err != nil { http.Error(w, "task not found", http.StatusNotFound) return } if err := situationdomain.ValidateTaskTarget(focus, target); err != nil { http.Error(w, "task outside creator focus", http.StatusForbidden) return } w.Header().Set("Content-Type", "application/json") _ = json.NewEncoder(w).Encode(target) }) mux.HandleFunc("GET /v1/creator/capabilities/{id}/health", func(w http.ResponseWriter, r *http.Request) { if _, ok := authenticateCreator(w, r); !ok { return } status, err := capabilityRepository.LoadHealth(r.Context(), capability.CapabilityID(r.PathValue("id"))) if err != nil { http.Error(w, "capability health not found", http.StatusNotFound) return } w.Header().Set("Content-Type", "application/json") _ = json.NewEncoder(w).Encode(status) }) mux.HandleFunc("GET /v1/creator/learning/recent", func(w http.ResponseWriter, r *http.Request) { if _, ok := authenticateCreator(w, r); !ok { return } limit := 10 if raw := r.URL.Query().Get("limit"); raw != "" { parsed, err := strconv.Atoi(raw) if err != nil || parsed < 1 || parsed > 50 { http.Error(w, "invalid learning limit", http.StatusBadRequest) return } limit = parsed } experiences, err := epistemicsRepository.RecentExperienceRecords(r.Context(), limit) if err != nil { http.Error(w, "learning experience unavailable", http.StatusServiceUnavailable) return } reflections, err := epistemicsRepository.RecentReflectionCandidates(r.Context(), limit) if err != nil { http.Error(w, "learning reflection unavailable", http.StatusServiceUnavailable) return } skills, err := capabilityRepository.RecentSkillProfiles(r.Context(), limit) if err != nil { http.Error(w, "learning skills unavailable", http.StatusServiceUnavailable) return } w.Header().Set("Content-Type", "application/json") w.Header().Set("Cache-Control", "no-store") _ = json.NewEncoder(w).Encode(struct { Experiences []epistemics.ExperienceRecord Reflections []epistemics.ReflectionCandidate Skills []capability.SkillProfile }{Experiences: experiences, Reflections: reflections, Skills: skills}) }) mux.HandleFunc("POST /v1/creator/chat/messages", func(w http.ResponseWriter, r *http.Request) { session, ok := authenticateCreator(w, r) if !ok { return } var request interaction.ChatMessageRequest decoder := json.NewDecoder(io.LimitReader(r.Body, interactiondomain.MaxHumanUtteranceBytes+1024)) decoder.DisallowUnknownFields() if err := decoder.Decode(&request); err != nil || decoder.Decode(&struct{}{}) != io.EOF { http.Error(w, "invalid chat message", http.StatusBadRequest) return } now := time.Now() var conversation interaction.Conversation if request.ConversationID == "" { conversation, err = interactiondomain.NewConversation(session.ActorID, session.PrincipalID, now) if err != nil { http.Error(w, "conversation unavailable", http.StatusServiceUnavailable) return } } else { conversation, err = interactionRepository.Conversation(r.Context(), request.ConversationID) if errors.Is(err, pgx.ErrNoRows) { http.Error(w, "conversation not found", http.StatusNotFound) return } if err != nil { http.Error(w, "conversation unavailable", http.StatusServiceUnavailable) return } if conversation.ActorID != session.ActorID || conversation.PrincipalID != session.PrincipalID { http.Error(w, "conversation denied", http.StatusForbidden) return } conversation.UpdatedAtUnixMillis = now.UnixMilli() } utterance, intent, relations, err := interactiondomain.NewHumanUtterance(conversation, request, now) if err != nil { http.Error(w, "invalid chat message", http.StatusBadRequest) return } projects, err := repository.ListProjects(r.Context()) if err != nil { http.Error(w, "project registry unavailable", http.StatusServiceUnavailable) return } var previousFocus *situation.ActiveProjectFocus storedFocus, err := repository.ActiveProjectFocus(r.Context(), session.ActorID, session.PrincipalID) if err == nil { previousFocus = &storedFocus } else if !errors.Is(err, pgx.ErrNoRows) { http.Error(w, "project focus unavailable", http.StatusServiceUnavailable) return } semantic, err := interactiondomain.InterpretUtterance(utterance, projects, previousFocus, now) if err != nil { http.Error(w, "interpret chat message", http.StatusServiceUnavailable) return } if err := interactionRepository.AppendUtterance(r.Context(), conversation, utterance, intent, relations); err != nil { http.Error(w, "save chat message", http.StatusInternalServerError) return } if err := interactionRepository.SaveSemanticResult(r.Context(), semantic.Interpretation, semantic.Projects, semantic.Relations); err != nil { http.Error(w, "save chat semantics", http.StatusInternalServerError) return } responseResult, responseErr := responseClient.Respond(r.Context(), r.Header.Get("Authorization"), continuity.HumanResponseRequest{ SourceUtteranceID: string(utterance.ID), SourceIntentID: string(intent.ID), Message: utterance.Content, RequestedAtUnixMillis: now.UnixMilli(), }) if semantic.Interpretation.SpeechAct == interaction.SpeechActCorrection { learning.recordCorrection(r.Context(), correctionLearningInput{Utterance: utterance, Intent: intent, At: now}) } activeFocusName := "" activeFocusProvenance := situation.ProjectResolutionProvenance("") if semantic.ActiveFocus != nil { if err := repository.SaveActiveProjectFocus(r.Context(), *semantic.ActiveFocus); err != nil { http.Error(w, "save project focus", http.StatusInternalServerError) return } activeFocusName = semantic.ActiveFocus.ProjectDisplayName activeFocusProvenance = semantic.ActiveFocus.Provenance } else if len(semantic.Projects) > 0 && semantic.Projects[0].Provenance == situation.ProjectResolutionInheritedFocus && previousFocus != nil { activeFocusName = previousFocus.ProjectDisplayName activeFocusProvenance = previousFocus.Provenance } var planningReply string workPlanned := false needsCognition := semantic.NeedsCognition projectID := "" if semantic.ActiveFocus != nil { projectID = string(semantic.ActiveFocus.ProjectID) } else if len(semantic.Projects) > 0 && semantic.Projects[0].Provenance == situation.ProjectResolutionInheritedFocus { projectID = string(previousFocus.ProjectID) } if responseErr == nil && responseResult.Handled { planningReply = responseResult.Reply needsCognition = false learning.recordReminderResponse(r.Context(), reminderResponseLearningInput{Utterance: utterance, Intent: intent, Result: responseResult, At: now}) } else if responseErr == nil && looksLikeUnsupportedContinuation(utterance.Content, responseResult) { learning.recordFastPathDecline(r.Context(), fastPathDeclineLearningInput{Utterance: utterance, Intent: intent, Result: responseResult, Reason: declineReasonForResponse(responseResult), At: now}) } planningInput := work.PlanningInput{ ConversationID: work.ConversationRef(conversation.ID), UtteranceID: work.SourceUtteranceRef(utterance.ID), IntentID: work.SourceIntentRef(intent.ID), ActorID: session.ActorID, PrincipalID: session.PrincipalID, ScopeID: authority.ScopeID("domovoy-global-scope"), ProjectID: work.ProjectRef(projectID), ProjectDisplayName: activeFocusName, ProjectProvenance: work.ProjectProvenanceRef(activeFocusProvenance), Message: utterance.Content, SpeechAct: work.SpeechActRef(semantic.Interpretation.SpeechAct), AssertionVerification: epistemics.VerificationState(semantic.Interpretation.AssertionVerification), ReceivedAtUnixMillis: utterance.ReceivedAtUnixMillis, } helpPath := workdomain.DetermineHelpPath(planningInput) if responseErr == nil && responseResult.Handled { helpPath = work.HelpPath{} } if isCapabilityQuestion(utterance.Content) { kinds, err := capabilityRepository.AvailableCapabilityKinds(r.Context(), session.PrincipalID) if err == nil && len(kinds) > 0 { planningReply = "Сейчас у меня подключены реальные capabilities: " + humanCapabilityList(kinds) + ". Для рассуждений я использую отдельный Cognitive Gateway/Router, если локальный CANARY доступен. Возможности вроде отправки почты пока будут фиксироваться как capability gap, а не как выполненное действие." } else { planningReply = "Я умею записывать диалог и намерения, работать с текущим контекстом, строить планы и обращаться к Cognitive Gateway при доступности локального CANARY. Реальные action-capabilities подключаются через registry; если capability нет, я фиксирую gap." } needsCognition = false } if helpPath.Reply != "" { planningReply = helpPath.Reply needsCognition = false } var capabilityStatus capability.CapabilityResolutionStatus var capabilityKind capability.CapabilityKind var capabilityGapID capability.CapabilityGapID var reminder *continuity.ProspectiveItem if helpPath.Kind == work.HelpPathDirectCapability && len(helpPath.SuggestedCapabilityKinds) > 0 { capabilityKind = helpPath.SuggestedCapabilityKinds[0] started := time.Now() resolution, resolveErr := resolveChatCapability(r.Context(), capabilityRepository, capabilityKind, session, intent, utterance, now) capabilityStatus = resolution.Status if resolveErr == nil && len(resolution.Eligible) > 0 { reminderRequest, reminderErr := buildReminderRequest(utterance.Content, session, intent, utterance, now) if reminderErr == nil { result, scheduleErr := reminderClient.Schedule(r.Context(), r.Header.Get("Authorization"), reminderRequest) finished := time.Now() if scheduleErr == nil { reminder = &result.Item planningReply = fmt.Sprintf("Готово. Я создал напоминание: %s.", result.Item.Summary) usageID, _ := observeCapabilityUsage(r.Context(), capabilityRepository, resolution.Eligible[0], resolution.Requirement, capability.UsageSucceeded, string(result.Item.ID), started, finished) learning.recordCapabilityUse(r.Context(), capabilityLearningInput{Utterance: utterance, Intent: intent, ImplementationID: resolution.Eligible[0].Implementation.ID, UsageObservation: usageID, Outcome: capability.UsageSucceeded, OutcomeRef: "continuity:prospective:" + string(result.Item.ID), At: finished}) } else { planningReply = "Scheduling capability сейчас недоступна: напоминание не создано. Я не буду притворяться, что действие выполнено." usageID, _ := observeCapabilityUsage(r.Context(), capabilityRepository, resolution.Eligible[0], resolution.Requirement, capability.UsageFailed, "continuity-reminder-unavailable", started, finished) learning.recordCapabilityUse(r.Context(), capabilityLearningInput{Utterance: utterance, Intent: intent, ImplementationID: resolution.Eligible[0].Implementation.ID, UsageObservation: usageID, Outcome: capability.UsageFailed, OutcomeRef: "continuity-reminder-unavailable", At: finished}) } } else { planningReply = "Мне нужно уточнить время напоминания. Например: «Напомни завтра в 10 позвонить врачу»." capabilityStatus = capability.ResolutionPartiallyAvailable } } else if resolution.Gap != nil { capabilityGapID = resolution.Gap.ID planningReply = "Сейчас scheduling capability недоступна, поэтому напоминание не создано. Я зафиксировал capability gap." learning.recordCapabilityUse(r.Context(), capabilityLearningInput{Utterance: utterance, Intent: intent, GapID: resolution.Gap.ID, Outcome: capability.UsageSkipped, OutcomeRef: "capability:gap:" + string(resolution.Gap.ID), At: now}) } needsCognition = false } if helpPath.Kind == work.HelpPathCapabilityGap && len(helpPath.SuggestedCapabilityKinds) > 0 { capabilityKind = helpPath.SuggestedCapabilityKinds[0] resolution, resolveErr := resolveChatCapability(r.Context(), capabilityRepository, capabilityKind, session, intent, utterance, now) capabilityStatus = resolution.Status if resolveErr == nil { planningReply = "Capability найдена, но в этом шаге она не исполняется без отдельного разрешённого action path." } else if resolution.Gap != nil { capabilityGapID = resolution.Gap.ID planningReply = "Сейчас у меня ещё не подключена возможность отправлять почту. Я зафиксировал capability gap, без ложного выполнения." learning.recordCapabilityUse(r.Context(), capabilityLearningInput{Utterance: utterance, Intent: intent, GapID: resolution.Gap.ID, Outcome: capability.UsageSkipped, OutcomeRef: "capability:gap:" + string(resolution.Gap.ID), At: now}) } needsCognition = false } if helpPath.NeedsDurableWork { planning, err := workdomain.BuildPlanningResult(planningInput, now) if err != nil { http.Error(w, "plan chat work", http.StatusServiceUnavailable) return } if err := workRepository.SavePlanningResult(r.Context(), planning); err != nil { http.Error(w, "save work plan", http.StatusInternalServerError) return } planningReply = planning.Reply workPlanned = true needsCognition = false } response := interaction.ChatMessageResponse{ ConversationID: conversation.ID, UtteranceID: utterance.ID, OriginalIntentID: intent.ID, SpeechAct: semantic.Interpretation.SpeechAct, InterpretationState: utterance.InterpretationState, NeedsCognition: needsCognition, FallbackText: semantic.Reply, ActiveProjectName: activeFocusName, ProjectProvenance: activeFocusProvenance, Clarification: semantic.Interpretation.Clarification, WorkPlanned: workPlanned, PlanningReply: planningReply, CapabilityStatus: capabilityStatus, CapabilityKind: capabilityKind, CapabilityGapID: capabilityGapID, Reminder: reminder, } w.Header().Set("Content-Type", "application/json") w.Header().Set("Cache-Control", "no-store") w.WriteHeader(http.StatusCreated) _ = json.NewEncoder(w).Encode(response) }) mux.HandleFunc("GET /v1/principal-bindings/{actor}/{principal}", func(w http.ResponseWriter, r *http.Request) { binding, err := identityRepository.LoadBinding(r.Context(), identity.ActorID(r.PathValue("actor")), identity.PrincipalID(r.PathValue("principal"))) if err != nil { http.Error(w, "principal binding not found", http.StatusNotFound) return } w.Header().Set("Content-Type", "application/json") _ = json.NewEncoder(w).Encode(binding) }) mux.HandleFunc("GET /v1/tasks/{id}", func(w http.ResponseWriter, r *http.Request) { target, err := workRepository.Load(r.Context(), work.TaskID(r.PathValue("id"))) if err != nil { http.Error(w, "task not found", http.StatusNotFound) return } w.Header().Set("Content-Type", "application/json") _ = json.NewEncoder(w).Encode(target) }) mux.HandleFunc("POST /v1/capabilities/health", func(w http.ResponseWriter, r *http.Request) { var status capability.Health if err := json.NewDecoder(r.Body).Decode(&status); err != nil { http.Error(w, "invalid capability health", http.StatusBadRequest) return } if err := capabilitydomain.ValidateHealth(status); err != nil { http.Error(w, "invalid capability health", http.StatusBadRequest) return } if err := capabilityRepository.SaveHealth(r.Context(), status); err != nil { http.Error(w, "save capability health", http.StatusInternalServerError) return } w.WriteHeader(http.StatusCreated) }) mux.HandleFunc("GET /v1/capabilities/{id}/health", func(w http.ResponseWriter, r *http.Request) { status, err := capabilityRepository.LoadHealth(r.Context(), capability.CapabilityID(r.PathValue("id"))) if err != nil { http.Error(w, "capability health not found", http.StatusNotFound) return } w.Header().Set("Content-Type", "application/json") _ = json.NewEncoder(w).Encode(status) }) mux.HandleFunc("GET /v1/capability-bindings/{id}/revisions/{revision}", func(w http.ResponseWriter, r *http.Request) { revision, err := strconv.ParseInt(r.PathValue("revision"), 10, 64) if err != nil || revision < 1 { http.Error(w, "invalid binding revision", http.StatusBadRequest) return } binding, err := capabilityRepository.LoadBinding(r.Context(), capability.CapabilityBindingID(r.PathValue("id")), revision) if err != nil { http.Error(w, "capability binding not found", http.StatusNotFound) return } if err = capabilitydomain.ValidateBinding(binding); err != nil { http.Error(w, "capability binding invalid", http.StatusConflict) return } w.Header().Set("Content-Type", "application/json") _ = json.NewEncoder(w).Encode(binding) }) mux.HandleFunc("GET /v1/capability-bindings/active", func(w http.ResponseWriter, r *http.Request) { capabilityID := capability.CapabilityID(r.URL.Query().Get("capability_id")) ownerID := identity.PrincipalID(r.URL.Query().Get("owner_principal_id")) if capabilityID == "" || ownerID == "" { http.Error(w, "invalid binding query", http.StatusBadRequest) return } binding, err := capabilityRepository.LoadActiveBinding(r.Context(), capabilityID, ownerID) if err != nil { http.Error(w, "active capability binding not found", http.StatusNotFound) return } if err = capabilitydomain.UsableBinding(binding); err != nil { http.Error(w, "active capability binding unavailable", http.StatusConflict) return } w.Header().Set("Content-Type", "application/json") _ = json.NewEncoder(w).Encode(binding) }) mux.HandleFunc("POST /v1/cognitive-bindings/resolve", func(w http.ResponseWriter, r *http.Request) { var requirement capability.CognitiveRequirement decoder := json.NewDecoder(io.LimitReader(r.Body, 16*1024+1)) decoder.DisallowUnknownFields() if err := decoder.Decode(&requirement); err != nil || requirement.CapabilityID == "" || requirement.OwnerPrincipalID == "" || requirement.InputBytes < 0 || requirement.MaxOutputBytes < 1 { http.Error(w, "invalid cognitive requirement", http.StatusBadRequest) return } candidates, err := capabilityRepository.LoadCognitiveCandidates(r.Context(), requirement.CapabilityID, requirement.OwnerPrincipalID) if err != nil { http.Error(w, "cognitive registry unavailable", http.StatusServiceUnavailable) return } selected, err := capabilitydomain.SelectCognitiveBinding(requirement, candidates) if err != nil { http.Error(w, "no invocable cognitive binding", http.StatusNotFound) return } w.Header().Set("Content-Type", "application/json") _ = json.NewEncoder(w).Encode(selected) }) mux.HandleFunc("POST /v1/cognitive-registry/discoveries", func(w http.ResponseWriter, r *http.Request) { var snapshot capability.CognitiveDiscoverySnapshot decoder := json.NewDecoder(io.LimitReader(r.Body, 1<<20)) decoder.DisallowUnknownFields() if err := decoder.Decode(&snapshot); err != nil { http.Error(w, "invalid cognitive discovery", http.StatusBadRequest) return } registration, err := capabilitydomain.BuildDiscoveryRegistration(snapshot) if err != nil { http.Error(w, "invalid cognitive discovery", http.StatusBadRequest) return } if err := capabilityRepository.SaveCognitiveDiscovery(r.Context(), registration); err != nil { http.Error(w, "save cognitive discovery", http.StatusInternalServerError) return } w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusCreated) _ = json.NewEncoder(w).Encode(registration) }) mux.HandleFunc("POST /v1/cognitive-registry/evaluated-capabilities", func(w http.ResponseWriter, r *http.Request) { var value capability.EvaluatedCapability decoder := json.NewDecoder(io.LimitReader(r.Body, 64<<10)) decoder.DisallowUnknownFields() if err := decoder.Decode(&value); err != nil || value.ID == "" || value.BindingID == "" || value.BindingRevision < 1 || value.CapabilityID == "" || value.BenchmarkVersion == "" || value.ArtifactID == "" || value.ObservedAtUnixMillis < 1 { http.Error(w, "invalid evaluated capability", http.StatusBadRequest) return } if err := capabilityRepository.SaveEvaluatedCapability(r.Context(), value); err != nil { http.Error(w, "save evaluated capability", http.StatusInternalServerError) return } w.WriteHeader(http.StatusCreated) }) mux.HandleFunc("POST /v1/creator/cognitive-canary", func(w http.ResponseWriter, r *http.Request) { session, ok := authenticateCreator(w, r) if !ok { return } var request capability.CognitiveCanaryPromotionRequest decoder := json.NewDecoder(io.LimitReader(r.Body, 16<<10)) decoder.DisallowUnknownFields() if err := decoder.Decode(&request); err != nil { http.Error(w, "invalid canary promotion", http.StatusBadRequest) return } request.RequestedByPrincipalID = session.PrincipalID request.CapabilityID = "creator-advisory" candidates, err := capabilityRepository.LoadCognitiveCandidates(r.Context(), request.CapabilityID, session.PrincipalID) if err != nil { http.Error(w, "cognitive registry unavailable", http.StatusServiceUnavailable) return } var current capability.ResolvedCognitiveBinding for _, candidate := range candidates { if candidate.Binding.ID == request.BindingID && candidate.Binding.Revision == request.ExpectedBindingRevision { current = candidate break } } promotion, err := capabilitydomain.BuildCanaryPromotion(request, current) if err != nil { http.Error(w, "canary promotion precondition failed", http.StatusConflict) return } if err = capabilityRepository.SaveCognitiveCanaryPromotion(r.Context(), promotion); err != nil { http.Error(w, "canary promotion conflict", http.StatusConflict) return } w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusCreated) _ = json.NewEncoder(w).Encode(promotion) }) mux.HandleFunc("POST /v1/cognitive-registry/performance-evidence", func(w http.ResponseWriter, r *http.Request) { var value capability.PerformanceEvidence decoder := json.NewDecoder(io.LimitReader(r.Body, 64<<10)) decoder.DisallowUnknownFields() if err := decoder.Decode(&value); err != nil || value.ID == "" || value.BindingID == "" || value.BindingRevision < 1 || value.ModelDescriptorID == "" || value.ModelRevision < 1 || value.HardwareProfile == "" || value.Runtime == "" || value.BenchmarkVersion == "" || value.ArtifactID == "" || value.ObservedAtUnixMillis < 1 { http.Error(w, "invalid performance evidence", http.StatusBadRequest) return } if err := capabilityRepository.SavePerformanceEvidence(r.Context(), value); err != nil { http.Error(w, "save performance evidence", http.StatusInternalServerError) return } w.WriteHeader(http.StatusCreated) }) mux.HandleFunc("POST /v1/cognitive-usage-observations", func(w http.ResponseWriter, r *http.Request) { var observation capability.CognitiveUsageObservation decoder := json.NewDecoder(io.LimitReader(r.Body, 16*1024+1)) decoder.DisallowUnknownFields() if err := decoder.Decode(&observation); err != nil || observation.ObservationID == "" || observation.RequestID == "" || observation.BindingID == "" || observation.BindingRevision < 1 || observation.ProviderInstanceID == "" || observation.ModelDescriptorID == "" || observation.ObservedAtUnixMillis < 1 || observation.LatencyMillis < 0 || observation.InputUnits < 0 || observation.OutputUnits < 0 || (observation.EstimatedCostMicros != nil && *observation.EstimatedCostMicros < 0) || (observation.ActualCostMicros != nil && *observation.ActualCostMicros < 0) || observation.ResultClass == "" { http.Error(w, "invalid cognitive usage observation", http.StatusBadRequest) return } if err := capabilityRepository.SaveCognitiveUsageObservation(r.Context(), observation); err != nil { http.Error(w, "save cognitive usage observation", http.StatusInternalServerError) return } w.WriteHeader(http.StatusCreated) }) mux.HandleFunc("POST /v1/capabilities/health-projection/rebuild", func(w http.ResponseWriter, r *http.Request) { rebuilt, err := capabilityRepository.RebuildHealthProjection(r.Context()) if err != nil { http.Error(w, "rebuild capability health projection", http.StatusInternalServerError) return } _ = json.NewEncoder(w).Encode(struct{ Rebuilt int64 }{rebuilt}) }) mux.HandleFunc("POST /v1/focus-bindings", func(w http.ResponseWriter, r *http.Request) { var binding situation.FocusBinding if err := json.NewDecoder(r.Body).Decode(&binding); err != nil { http.Error(w, "invalid focus binding", http.StatusBadRequest) return } if err := repository.Save(r.Context(), binding); err != nil { http.Error(w, "save focus binding", http.StatusInternalServerError) return } w.WriteHeader(http.StatusCreated) }) mux.HandleFunc("GET /v1/focus", func(w http.ResponseWriter, r *http.Request) { focus, err := resolveFocus(r) if err != nil { http.Error(w, err.Error(), http.StatusConflict) return } w.Header().Set("Content-Type", "application/json") _ = json.NewEncoder(w).Encode(focus) }) mux.HandleFunc("POST /v1/tasks", func(w http.ResponseWriter, r *http.Request) { var target work.TaskTarget if err := json.NewDecoder(r.Body).Decode(&target); err != nil { http.Error(w, "invalid task", 400) return } if err := workRepository.Create(r.Context(), target); err != nil { http.Error(w, "create task", 500) return } w.WriteHeader(http.StatusCreated) }) mux.HandleFunc("POST /v1/tasks/{id}/commit", func(w http.ResponseWriter, r *http.Request) { var target work.TaskTarget if err := json.NewDecoder(r.Body).Decode(&target); err != nil { http.Error(w, "invalid task commit", 400) return } target.TaskID = work.TaskID(r.PathValue("id")) revision, err := workRepository.Commit(r.Context(), target) if err != nil { http.Error(w, "task revision conflict", http.StatusConflict) return } _ = json.NewEncoder(w).Encode(struct{ Revision work.TaskRevision }{revision}) }) mux.HandleFunc("POST /v1/focus-task-commits", func(w http.ResponseWriter, r *http.Request) { var request struct { SessionID situation.SessionID Workspace situation.WorkspaceID Target work.TaskTarget } if err := json.NewDecoder(r.Body).Decode(&request); err != nil { http.Error(w, "invalid guarded commit", http.StatusBadRequest) return } bindings, err := repository.ByRuntime(r.Context(), situation.RuntimeBinding{SessionID: request.SessionID, Workspace: request.Workspace}) if err != nil { http.Error(w, "load focus", http.StatusInternalServerError) return } focus, err := situationdomain.NewResolver(bindings).Resolve(situation.RuntimeBinding{SessionID: request.SessionID, Workspace: request.Workspace}) if err != nil { http.Error(w, "focus unavailable", http.StatusConflict) return } if err := situationdomain.ValidateTaskTarget(focus, request.Target); err != nil { http.Error(w, "target outside resolved focus", http.StatusConflict) return } revision, err := workRepository.Commit(r.Context(), request.Target) if err != nil { http.Error(w, "task revision conflict", http.StatusConflict) return } _ = json.NewEncoder(w).Encode(struct{ Revision work.TaskRevision }{revision}) }) log.Printf("core-runtime listening on %s", addr) if err := http.ListenAndServe(addr, mux); err != nil { log.Fatal(err) } } func creatorSessionHandler(bootstrapToken, creatorUsername, creatorPassword string, sessionKey []byte, now func() time.Time) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { authenticated := identitydomain.BootstrapMatches(bootstrapToken, bearerToken(r)) if !authenticated { var credentials identity.CreatorCredentials decoder := json.NewDecoder(io.LimitReader(r.Body, maxCreatorCredentialsBytes+1)) decoder.DisallowUnknownFields() if err := decoder.Decode(&credentials); err != nil || decoder.Decode(&struct{}{}) != io.EOF { http.Error(w, "creator authentication denied", http.StatusUnauthorized) return } authenticated = identitydomain.CreatorCredentialsMatch(creatorUsername, creatorPassword, credentials) } if !authenticated { http.Error(w, "creator authentication denied", http.StatusUnauthorized) return } token, session, err := identitydomain.IssueCreatorSession(sessionKey, now(), 8*time.Hour) if err != nil { http.Error(w, "creator session unavailable", http.StatusServiceUnavailable) return } w.Header().Set("Content-Type", "application/json") _ = json.NewEncoder(w).Encode(struct { Token string Session identity.CreatorSession }{token, session}) } } func bearerToken(r *http.Request) string { value := r.Header.Get("Authorization") if !strings.HasPrefix(value, "Bearer ") { return "" } return strings.TrimPrefix(value, "Bearer ") } func readRequiredSecret(name string) (string, error) { direct, directSet := os.LookupEnv(name) fileName, fileSet := os.LookupEnv(name + "_FILE") if directSet && fileSet { return "", fmt.Errorf("%s and %s_FILE cannot both be configured", name, name) } if fileSet { if fileName == "" { return "", fmt.Errorf("%s_FILE is empty", name) } contents, err := os.ReadFile(fileName) if err != nil { return "", fmt.Errorf("read %s_FILE: %w", name, err) } direct = strings.TrimRight(string(contents), "\r\n") if strings.ContainsAny(direct, "\r\n") { return "", fmt.Errorf("%s_FILE must contain one line", name) } } if direct == "" { return "", fmt.Errorf("%s is required", name) } return direct, nil } func readCredentialRefSecret(name, refName string) (string, error) { if _, directSet := os.LookupEnv(name); directSet { return "", fmt.Errorf("%s direct environment secret is forbidden; use %s_FILE", name, name) } fileName := os.Getenv(name + "_FILE") if fileName == "" { return "", fmt.Errorf("%s_FILE is required", name) } ref := os.Getenv(refName) if ref == "" { return "", fmt.Errorf("%s is required", refName) } root := os.Getenv("SECRET_BACKEND_ROOT") if root == "" { root = "/run/secrets" } resolver, err := platformsecrets.NewFileResolver(root) if err != nil { return "", err } return resolver.ResolveFile(secretcontracts.SecretFileRef{ CredentialRef: secretcontracts.CredentialRef(ref), Purpose: secretcontracts.SecretPurposeDatabasePassword, Consumer: secretcontracts.RuntimeConsumerCore, Path: fileName, }) } func envOr(name, fallback string) string { if value := os.Getenv(name); value != "" { return value } return fallback } func isCapabilityQuestion(message string) bool { lower := strings.ToLower(message) return strings.Contains(lower, "что ты умеешь") || strings.Contains(lower, "что умеешь") || strings.Contains(lower, "какие возможности") } func humanCapabilityList(kinds []capability.CapabilityKind) string { labels := make([]string, 0, len(kinds)) for _, kind := range kinds { switch kind { case capability.CapabilityKindScheduleReminder: labels = append(labels, "создавать напоминания") case capability.CapabilityKindCognitiveReasoning: labels = append(labels, "отвечать через когнитивный gateway") default: labels = append(labels, strings.ToLower(strings.ReplaceAll(string(kind), "_", " "))) } } return strings.Join(labels, ", ") }