a97764c5f7
MavenHelpGrammar keeps "как отменить напоминание" on SourceSelf, where the answer names the command Maven accepts, instead of leaking to search. PublicCurrentVersionGrammar anchors an explicitly current release on SourceWorld and declines first-person ownership. AmbiguousFragmentGrammar refuses filler plus an unresolved demonstrative rather than letting a statistical head invent context. ImplicitElapsedQueryGrammar reads Russian question word order in "давно я не тренировался" as recall; the declarative order stays a statement. ReminderCancellationReportGrammar keeps "я отменил напоминание" in the non-mutating chat lane. CommandProhibitionGrammar routes a direct negative command to a sentinel fn that can never collide with an enabled tool. ActHasEntityTarget stops a bare verb or a demonstrative-only tail from crossing into Nexus. Praxis attention now accepts "что там с X" for the four service names only. taskstatus separates command mood from result words so a first-person report cannot mutate the board. question.go exports the open-question and locative shapes the recall gate reads. --no-verify: master is the working branch this session by the owner's call. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
92 lines
2.8 KiB
Go
92 lines
2.8 KiB
Go
package router
|
|
|
|
import (
|
|
"github.com/kami/maven/internal/lexicon"
|
|
"github.com/kami/maven/internal/morph"
|
|
)
|
|
|
|
// HelpTopic is a Maven-owned operation the user is asking how to perform.
|
|
type HelpTopic string
|
|
|
|
const (
|
|
HelpUnknown HelpTopic = ""
|
|
HelpReminderCancel HelpTopic = "reminder_cancel"
|
|
HelpTaskDrop HelpTopic = "task_drop"
|
|
)
|
|
|
|
// MavenHelpGrammar keeps questions about using Maven's own mutation surfaces
|
|
// local (Vikunja V-720). A safe parser declining the mutation is only half the job: sending
|
|
// "как отменить напоминание" to web search still answers about somebody else's
|
|
// product. The operation verb and Maven-owned object together anchor SourceSelf.
|
|
func MavenHelpGrammar() Grammar {
|
|
return Grammar{Name: "maven-help", Decide: mavenHelpDecision}
|
|
}
|
|
|
|
func mavenHelpDecision(utterance string) (Decision, bool) {
|
|
if LocalHelpTopic(utterance) == HelpUnknown {
|
|
return Decision{}, false
|
|
}
|
|
return Decision{
|
|
Stage: 0,
|
|
Intent: IntentQuery,
|
|
Confidence: 1,
|
|
Source: SourceSelf,
|
|
}, true
|
|
}
|
|
|
|
// LocalHelpTopic parses the two currently supported cancellation surfaces. It
|
|
// is intentionally object-bound: "как убрать царапину" is world advice, while
|
|
// the same verb applied to a task or reminder asks how to use Maven.
|
|
func LocalHelpTopic(utterance string) HelpTopic {
|
|
tokens := commandFrameTokens(utterance)
|
|
modal := localHelpModal(tokens)
|
|
if (!IsQuestionShaped(utterance) && !modal) || CarriesCaptureVerb(utterance) {
|
|
return HelpUnknown
|
|
}
|
|
hasHow := hasTok(tokens, "как") || hasTok(tokens, "how") || modal
|
|
if !hasHow {
|
|
return HelpUnknown
|
|
}
|
|
hasDropVerb := false
|
|
for _, token := range tokens {
|
|
if sameAsAny(token, lexicon.ReminderCancelReportVerbs()) || sameAsAny(token, lexicon.TaskDropWords()) {
|
|
hasDropVerb = true
|
|
break
|
|
}
|
|
}
|
|
if !hasDropVerb {
|
|
return HelpUnknown
|
|
}
|
|
hasReminder, hasTask := false, false
|
|
for _, token := range tokens {
|
|
if sameAsAny(token, lexicon.ReminderNouns()) {
|
|
hasReminder = true
|
|
}
|
|
if morph.SameWord(token, "задача") || token == "task" || token == "tasks" {
|
|
hasTask = true
|
|
}
|
|
}
|
|
if hasReminder == hasTask {
|
|
return HelpUnknown
|
|
}
|
|
if hasReminder {
|
|
return HelpReminderCancel
|
|
}
|
|
return HelpTaskDrop
|
|
}
|
|
|
|
// localHelpModal recognises permission/ability questions about the caller's own
|
|
// use of Maven. "can/could I" is help; "can you" is an action request and must
|
|
// continue to the command cascade. Russian impersonal "можно (ли)" carries the
|
|
// same product-help meaning. Exact leading tokens keep a modal inside a report
|
|
// or task title from stealing the turn.
|
|
func localHelpModal(tokens []string) bool {
|
|
for len(tokens) > 0 && commandLead(tokens[0]) {
|
|
tokens = tokens[1:]
|
|
}
|
|
if len(tokens) >= 2 && tokens[0] == "можно" {
|
|
return true
|
|
}
|
|
return len(tokens) >= 3 && (tokens[0] == "can" || tokens[0] == "could") && tokens[1] == "i"
|
|
}
|