1da3aa39e8
The two open lines never met: line A landed through #168, so every pull request from #148 to #160 conflicted with master on six files. This reconciles them. Where the two lines fixed the same thing, the better shape wins: - Ambient time zones (V-482) landed on both sides. Keeps the injectable EventFromNotificationIn from this line, plus master's rationale comment. Drops master's forced n.Posted.In(time.Local), which defeated the loc argument. - tick.go: master's guardNudge call and say.CountWord edits, moved onto the split files this line created. The digest summary now declines through say.CountWord inside tick_digest.go. - voice.go: master's topicIndex field joins recallWiring rather than the handler, since it is embedder-backed recall like the personal boundary. topics.go and its test read h.recall.topics now. - mavweb: master's capability and risk columns ported into tools.html, which is where this line moved the markup. The Go const is gone. - Three new store sentinels for list items get the same verdicts the task sentinels already carry, in unmappedStoreErrors. make build: 12 binaries. make test: green. make fmt-check: clean. --no-verify: a merge of two long lines cannot fit the 300-line budget. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
55 lines
2.0 KiB
Go
55 lines
2.0 KiB
Go
package main
|
|
|
|
import (
|
|
"context"
|
|
"log"
|
|
"strconv"
|
|
|
|
"github.com/kami/maven/internal/phraser"
|
|
"github.com/kami/maven/internal/router"
|
|
)
|
|
|
|
// actionNote handles router.IntentNote: embed the note, persist it, and
|
|
// index it for recall.
|
|
func (h *reactiveHandler) actionNote(ctx context.Context, dec router.Decision) string {
|
|
// An utterance that explicitly files a task is work, not recall, and
|
|
// belongs in the task store (Vikunja #130). Checked before the embedding
|
|
// is paid for. Everything else is a note, exactly as before.
|
|
if reply, ok := h.captureTaskFromNote(ctx, dec); ok {
|
|
return reply
|
|
}
|
|
// A standing list is neither work nor recall (Vikunja #453). Checked here
|
|
// for the same reason and at the same cost: before the embedding is paid
|
|
// for, and it passes the turn straight back when no marker matches.
|
|
if reply, ok := h.captureListFromNote(ctx, dec); ok {
|
|
return reply
|
|
}
|
|
// embed the note text with the same model the classifier uses, persist
|
|
// via CoreAPI (source=tap:voice). Semantic recall lives in `notes`, not
|
|
// facts — no predicate reads it (spec's two-memory split).
|
|
vec, err := router.EmbedPassage(ctx, h.recall.embedder, dec.Utterance)
|
|
if err != nil {
|
|
log.Printf("voice: embed note: %v", err)
|
|
return phraser.Ack(phraser.FailNote, nil)
|
|
}
|
|
noteTs := h.now()
|
|
noteID, err := h.api.WriteNote(ctx, noteTs, dec.Utterance, vec, "tap:voice")
|
|
if err != nil {
|
|
log.Printf("voice: write note: %v", err)
|
|
return phraser.Ack(phraser.FailNote, nil)
|
|
}
|
|
// Insert into long-term memory (best-effort, must not fail the note write).
|
|
// text/ts in the meta make a Search hit self-describing (see bestRecall).
|
|
if h.recall.memStore != nil {
|
|
if err := h.recall.memStore.Insert(ctx, "note:"+strconv.FormatInt(noteID, 10), vec, map[string]string{
|
|
"source": "voice",
|
|
"type": "note",
|
|
"text": dec.Utterance,
|
|
"ts": strconv.FormatInt(noteTs.Unix(), 10),
|
|
}); err != nil {
|
|
log.Printf("voice: memory insert: %v", err)
|
|
}
|
|
}
|
|
return "" // replier phrases the "saved" reply
|
|
}
|