An act alias resolves the verb but not the target (found in V-633) #185
@@ -4,7 +4,6 @@ import (
|
||||
"context"
|
||||
"errors"
|
||||
"log"
|
||||
"strings"
|
||||
|
||||
"github.com/kami/maven/internal/mcp"
|
||||
"github.com/kami/maven/internal/phraser"
|
||||
@@ -66,8 +65,12 @@ func (h *reactiveHandler) actionAct(ctx context.Context, dec router.Decision) st
|
||||
// nothing ran. Saying which word she could not place is the whole
|
||||
// answer: he either renames it or gives the row an alias that
|
||||
// carries the target, and both are one turn away (V-634).
|
||||
return phraser.A(phraser.ActUnknownTarget,
|
||||
map[string]string{"name": unknownTargetOf(err, dec.Slots.Args)})
|
||||
word := ""
|
||||
var unknown *tool.UnknownTargetError
|
||||
if errors.As(err, &unknown) {
|
||||
word = unknown.Target
|
||||
}
|
||||
return phraser.A(phraser.ActUnknownTarget, map[string]string{"name": word})
|
||||
case errors.Is(err, tool.ErrNeedsAuthedSurface):
|
||||
// Irreversible (internal/tool/risk.go). A confirm turn would not
|
||||
// help: everything that proposed this act — the STT, the router,
|
||||
@@ -102,17 +105,3 @@ func (h *reactiveHandler) actionAct(ctx context.Context, dec router.Decision) st
|
||||
return phraser.A(phraser.ActDone, nil)
|
||||
}
|
||||
|
||||
// unknownTargetOf pulls the word the executor could not place out of its error,
|
||||
// falling back to the first arg. The word is what makes the reply usable, and a
|
||||
// reply naming no word would send him to the log.
|
||||
func unknownTargetOf(err error, args []string) string {
|
||||
if _, rest, ok := strings.Cut(err.Error(), `"`); ok {
|
||||
if word, _, ok := strings.Cut(rest, `"`); ok && word != "" {
|
||||
return word
|
||||
}
|
||||
}
|
||||
if len(args) > 0 {
|
||||
return args[0]
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
+16
-1
@@ -84,6 +84,17 @@ var (
|
||||
ErrUnknownTarget = errors.New("the act names a target the system cannot have")
|
||||
)
|
||||
|
||||
// UnknownTargetError carries the word the executor could not place, because the
|
||||
// reply names it: "«роутер» — не знаю такой цели" is actionable and "не
|
||||
// получилось" sends him to the log. errors.Is(err, ErrUnknownTarget) holds.
|
||||
type UnknownTargetError struct{ Target string }
|
||||
|
||||
func (e *UnknownTargetError) Error() string {
|
||||
return fmt.Sprintf("%s: %q", ErrUnknownTarget, e.Target)
|
||||
}
|
||||
|
||||
func (e *UnknownTargetError) Unwrap() error { return ErrUnknownTarget }
|
||||
|
||||
// MCPCaller is the seam for an act that is an MCP tool call rather than a
|
||||
// process (Vikunja #251). internal/mcp.Manager satisfies it via CallPositional.
|
||||
// nil ⇒ MCP is not configured, and an MCP row refuses to run rather than
|
||||
@@ -160,7 +171,7 @@ func (e *Executor) Exec(ctx context.Context, name string, args []string, confirm
|
||||
// since a task title is not a target, and a house row drops the spoken args.
|
||||
if !isMCPRow(t.Cmd) && !isHouseRow(t.Cmd) {
|
||||
if bad, ok := firstUnknownTarget(args); !ok {
|
||||
return "", fmt.Errorf("%w: %q", ErrUnknownTarget, bad)
|
||||
return "", &UnknownTargetError{Target: bad}
|
||||
}
|
||||
}
|
||||
// The tier decides, not the column (Vikunja #449). RiskOf reads the row and
|
||||
@@ -290,6 +301,10 @@ func (m *Matcher) Match(utterance string) (string, []string, bool) {
|
||||
// sentence. "перезагрузи роутер" is the case: restart is a real tool and
|
||||
// "роутер" is a real word, and `systemctl restart роутер` is neither.
|
||||
//
|
||||
// Every process row this box enables takes a system identifier (systemctl,
|
||||
// docker, journalctl, df). A process row that legitimately wanted Russian text
|
||||
// would want a different dispatch, not a hole in this check.
|
||||
//
|
||||
// It deliberately does not try to guess the right target. Identity is Nexus's
|
||||
// (CLAUDE.md § "The ecosystem"), and a target Nexus resolves reaches Hexis
|
||||
// through handleHexisAct before this executor is asked.
|
||||
|
||||
Reference in New Issue
Block a user