Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
aa01e8e99b | ||
|
|
1113ced623 | ||
|
|
f3ff430812 | ||
|
|
2ec53e40cf |
@@ -9,6 +9,7 @@ import (
|
||||
|
||||
"git.cin.su/m/Assistant/contracts/capability"
|
||||
"git.cin.su/m/Assistant/contracts/cognitive"
|
||||
"git.cin.su/m/Assistant/contracts/continuity"
|
||||
"git.cin.su/m/Assistant/contracts/identity"
|
||||
"git.cin.su/m/Assistant/contracts/interaction"
|
||||
)
|
||||
@@ -127,3 +128,44 @@ func TestChatMessageReturnsHumanCapabilityMetadata(t *testing.T) {
|
||||
t.Fatalf("unexpected browser response: %#v", response)
|
||||
}
|
||||
}
|
||||
|
||||
func TestChatAttentionQuestionUsesContinuityToday(t *testing.T) {
|
||||
core := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
switch {
|
||||
case r.Method == http.MethodGet && r.URL.Path == "/v1/creator/sessions/current":
|
||||
_ = json.NewEncoder(w).Encode(identity.CreatorSession{ActorID: "actor-1", PrincipalID: "principal-1"})
|
||||
case r.Method == http.MethodPost && r.URL.Path == "/v1/creator/chat/messages":
|
||||
w.WriteHeader(http.StatusCreated)
|
||||
_ = json.NewEncoder(w).Encode(interaction.ChatMessageResponse{ConversationID: "conversation-1", UtteranceID: "utterance-1", SpeechAct: interaction.SpeechActQuestion, FallbackText: "Сейчас в фокусе: УПО."})
|
||||
default:
|
||||
t.Fatalf("unexpected core route %s %s", r.Method, r.URL.Path)
|
||||
}
|
||||
}))
|
||||
defer core.Close()
|
||||
continuityRuntime := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != http.MethodGet || r.URL.Path != "/v1/creator/today" {
|
||||
t.Fatalf("unexpected continuity route %s %s", r.Method, r.URL.Path)
|
||||
}
|
||||
_ = json.NewEncoder(w).Encode(continuity.TodayResult{Items: []continuity.TodayItem{{Title: "проверить уведомление", HumanState: "доставлено", DeliveryState: "уведомление отправлено"}}})
|
||||
}))
|
||||
defer continuityRuntime.Close()
|
||||
handler, err := newHandler(core.URL, continuityRuntime.URL, "http://commit", core.Client(), "http://cognitive")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
request := httptest.NewRequest(http.MethodPost, "/api/chat/messages", strings.NewReader(`{"message":"Что сейчас требует моего внимания?"}`))
|
||||
request.Header.Set("Authorization", "Bearer session-token")
|
||||
request.Header.Set("Content-Type", "application/json")
|
||||
recorder := httptest.NewRecorder()
|
||||
handler.ServeHTTP(recorder, request)
|
||||
if recorder.Code != http.StatusOK {
|
||||
t.Fatalf("status=%d body=%s", recorder.Code, recorder.Body.String())
|
||||
}
|
||||
var response browserChatResponse
|
||||
if err := json.NewDecoder(recorder.Body).Decode(&response); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !strings.Contains(response.ReplyText, "проверить уведомление") || strings.Contains(response.ReplyText, "УПО") {
|
||||
t.Fatalf("attention reply did not use canonical today: %#v", response)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ import (
|
||||
|
||||
"git.cin.su/m/Assistant/contracts/capability"
|
||||
"git.cin.su/m/Assistant/contracts/cognitive"
|
||||
"git.cin.su/m/Assistant/contracts/continuity"
|
||||
"git.cin.su/m/Assistant/contracts/identity"
|
||||
"git.cin.su/m/Assistant/contracts/interaction"
|
||||
"git.cin.su/m/Assistant/contracts/situation"
|
||||
@@ -323,6 +324,15 @@ func (g *gateway) handleChatMessage(w http.ResponseWriter, r *http.Request) {
|
||||
if saved.PlanningReply != "" {
|
||||
reply = saved.PlanningReply
|
||||
}
|
||||
if asksAttention(browserRequest.Message) {
|
||||
var today continuity.TodayResult
|
||||
status, err = g.getTrustedJSON(r, g.continuity, "/v1/creator/today", "", authorization, &today)
|
||||
if err == nil && status == http.StatusOK {
|
||||
reply = renderTodayReply(today)
|
||||
cognition = "not_requested"
|
||||
saved.NeedsCognition = false
|
||||
}
|
||||
}
|
||||
if saved.NeedsCognition && !saved.WorkPlanned && saved.Clarification != interaction.ClarificationRequired {
|
||||
cognition = "unavailable"
|
||||
requestID, idErr := ids.New()
|
||||
@@ -346,6 +356,45 @@ func (g *gateway) handleChatMessage(w http.ResponseWriter, r *http.Request) {
|
||||
_ = json.NewEncoder(w).Encode(browserChatResponse{ConversationID: string(saved.ConversationID), UtteranceID: string(saved.UtteranceID), SpeechAct: string(saved.SpeechAct), ReplyText: reply, Cognition: cognition, ActiveFocus: saved.ActiveProjectName, ProjectProvenance: string(saved.ProjectProvenance), Clarification: string(saved.Clarification), WorkPlanned: saved.WorkPlanned, CapabilityStatus: string(saved.CapabilityStatus), CapabilityKind: string(saved.CapabilityKind), ReminderScheduled: saved.Reminder != nil})
|
||||
}
|
||||
|
||||
func asksAttention(message string) bool {
|
||||
lower := strings.ToLower(message)
|
||||
return strings.Contains(lower, "требует моего внимания") || strings.Contains(lower, "требует внимания") || strings.Contains(lower, "что сейчас важно")
|
||||
}
|
||||
|
||||
func renderTodayReply(today continuity.TodayResult) string {
|
||||
if len(today.Items) == 0 {
|
||||
return "Сейчас ничего срочного не требует внимания."
|
||||
}
|
||||
limit := len(today.Items)
|
||||
if limit > 5 {
|
||||
limit = 5
|
||||
}
|
||||
var builder strings.Builder
|
||||
builder.WriteString("Сейчас требует внимания:\n")
|
||||
for index := 0; index < limit; index++ {
|
||||
item := today.Items[index]
|
||||
title := strings.TrimSpace(item.Title)
|
||||
if title == "" {
|
||||
title = "событие Домового"
|
||||
}
|
||||
state := strings.TrimSpace(item.HumanState)
|
||||
if state == "" {
|
||||
state = "ожидает внимания"
|
||||
}
|
||||
builder.WriteString("- ")
|
||||
builder.WriteString(title)
|
||||
builder.WriteString(" — ")
|
||||
builder.WriteString(state)
|
||||
if item.DeliveryState != "" {
|
||||
builder.WriteString(" (")
|
||||
builder.WriteString(item.DeliveryState)
|
||||
builder.WriteString(")")
|
||||
}
|
||||
builder.WriteString("\n")
|
||||
}
|
||||
return strings.TrimSpace(builder.String())
|
||||
}
|
||||
|
||||
func validAdvisoryResponse(response cognitive.GatewayAdvisoryResponse, request cognitive.GatewayAdvisoryRequest) bool {
|
||||
if response.Version != cognitive.CreatorAdvisoryV1 || response.RequestID != request.RequestID || len(response.ReferencedExcerptIDs) != 0 {
|
||||
return false
|
||||
|
||||
@@ -4,19 +4,22 @@ Audit date: 2026-08-17. Active implementation: DEV-000065 revision 1.
|
||||
|
||||
## DEV-000065 checkpoint
|
||||
|
||||
- `DEV_000065_STATUS = IN_PROGRESS`
|
||||
- `DEV_000065_STATUS = COMPLETED`
|
||||
- `LAST_COMPLETED = ADR-0014 accepted; Continuity-owned AttentionItem contract and persistence; due reminder scanner; Commit internal prospective notification endpoint; human-facing Chat / Today / History surface; relative reminder parsing for "через N минут"; deterministic due-delivery regression tests; notification binding origin corrected to assistant.cin.su revision 2`
|
||||
- `ADR_STATUS = ADR-0014 accepted`
|
||||
- `OWNERSHIP_STATUS = no new owner; AttentionItem belongs to Continuity; notification delivery remains Commit/Capability/receiver path`
|
||||
- `REMINDER_LIFECYCLE_STATUS = ARMED -> TRIGGERED -> REVALIDATING -> DELIVERED implemented for due scanner`
|
||||
- `REMINDER_DELIVERY_STATUS = implemented through Commit deliver-prospective-notification-v1 path; production live verification pending`
|
||||
- `REMINDER_DELIVERY_STATUS = PASS: production live delayed reminder delivered through Commit deliver-prospective-notification-v1 and immutable receiver receipt`
|
||||
- `ATTENTION_STATUS = typed AttentionItem PENDING -> DELIVERING -> DELIVERED foundation implemented`
|
||||
- `HUMAN_SURFACE_STATUS = /chat remains primary; Today/Attention and History added; Operator Console preserved at /`
|
||||
- `AUTHORITY_STATUS = external notification still requires Commit authorization, current Creator authority context, and active Capability notification binding`
|
||||
- `LLM_FREE_STATUS = deterministic reminder firing does not require cognitive model; LLM-free env policy regression PASS`
|
||||
- `TESTS_PASS_SO_FAR = go test ./...; go vet ./...; go run ./cmd/devctl preflight -active dev/ACTIVE_TASK.json; go run ./cmd/devctl check-architecture; go run ./cmd/devctl check-diff -active dev/ACTIVE_TASK.json; tests/cognitive_advisory/cognitive_worker_env_policy_test.ps1`
|
||||
- `PRODUCTION_STATUS = pending deploy/live acceptance`
|
||||
- `NEXT_STEP = apply migration, deploy affected services, run owner ACL and live delayed reminder acceptance, then promote if PASS`
|
||||
- `TESTS_PASS = go test ./...; go vet ./...; go run ./cmd/devctl preflight -active dev/ACTIVE_TASK.json; go run ./cmd/devctl check-architecture; go run ./cmd/devctl check-diff -active dev/ACTIVE_TASK.json; tests/cognitive_advisory/cognitive_worker_env_policy_test.ps1; tests/owner_acl/verify.ps1 -DockerContext docker-prod -PostgresContainer assistant-postgres-1; production live delayed reminder acceptance; production /chat attention and capability-gap acceptance`
|
||||
- `LIVE_ACCEPTANCE = PASS: "Напомни мне через 3 минуты проверить уведомление." persisted ARMED reminder, transitioned to DELIVERED, generated AttentionItem, delivered notification through Commit, persisted SUCCEEDED attempt and receiver receipt, duplicate counts action_attempts=1 attention_items=1; "Отправь Ивану письмо." returned readable capability gap; "Что сейчас требует моего внимания?" answered from canonical Today with cognition=not_requested`
|
||||
- `PRODUCTION_STATUS = healthy`
|
||||
- `PRODUCTION_REVISION = origin/main 1113ced623896f1498d606653d71f36c361e9bb0; creator-web deployed at 1113ced623896f1498d606653d71f36c361e9bb0; continuity-runtime deployed at 2ec53e40cf19c609193c1aa74955573cac7abfe1; core-runtime/commit-runtime deployed at a0dce634f3c6efec0481f730c9dcc12797749ec4`
|
||||
- `KNOWN_LIMITATION = one early DEV-000065 live probe occurred before notification binding origin revision 2 and produced UNKNOWN_OUTCOME; later regression prevents UNKNOWN_OUTCOME from being marked as delivered`
|
||||
- `NEXT_STEP = Human response operations for attention/reminders: Done/Snooze/Cancel semantic commands, if separately approved`
|
||||
|
||||
## DEV-000064 checkpoint
|
||||
|
||||
|
||||
Reference in New Issue
Block a user