fix(commands): normalize parsing and remove routing metadata literals
This commit is contained in:
parent
a0db5ea03e
commit
aa56870875
4 changed files with 78 additions and 15 deletions
|
|
@ -58,7 +58,17 @@ type processOptions struct {
|
||||||
NoHistory bool // If true, don't load session history (for heartbeat)
|
NoHistory bool // If true, don't load session history (for heartbeat)
|
||||||
}
|
}
|
||||||
|
|
||||||
const defaultResponse = "I've completed processing but have no response to give. Increase `max_tool_iterations` in config.json."
|
const (
|
||||||
|
defaultResponse = "I've completed processing but have no response to give. Increase `max_tool_iterations` in config.json."
|
||||||
|
sessionKeyAgentPrefix = "agent:"
|
||||||
|
switchCommandToken = "/switch"
|
||||||
|
commandMentionSeparator = "@"
|
||||||
|
metadataKeyAccountID = "account_id"
|
||||||
|
metadataKeyGuildID = "guild_id"
|
||||||
|
metadataKeyTeamID = "team_id"
|
||||||
|
metadataKeyParentPeerKind = "parent_peer_kind"
|
||||||
|
metadataKeyParentPeerID = "parent_peer_id"
|
||||||
|
)
|
||||||
|
|
||||||
func NewAgentLoop(
|
func NewAgentLoop(
|
||||||
cfg *config.Config,
|
cfg *config.Config,
|
||||||
|
|
@ -498,11 +508,11 @@ func (al *AgentLoop) processMessage(ctx context.Context, msg bus.InboundMessage)
|
||||||
func (al *AgentLoop) resolveMessageRoute(msg bus.InboundMessage) (routing.ResolvedRoute, *AgentInstance, error) {
|
func (al *AgentLoop) resolveMessageRoute(msg bus.InboundMessage) (routing.ResolvedRoute, *AgentInstance, error) {
|
||||||
route := al.registry.ResolveRoute(routing.RouteInput{
|
route := al.registry.ResolveRoute(routing.RouteInput{
|
||||||
Channel: msg.Channel,
|
Channel: msg.Channel,
|
||||||
AccountID: msg.Metadata["account_id"],
|
AccountID: inboundMetadata(msg, metadataKeyAccountID),
|
||||||
Peer: extractPeer(msg),
|
Peer: extractPeer(msg),
|
||||||
ParentPeer: extractParentPeer(msg),
|
ParentPeer: extractParentPeer(msg),
|
||||||
GuildID: msg.Metadata["guild_id"],
|
GuildID: inboundMetadata(msg, metadataKeyGuildID),
|
||||||
TeamID: msg.Metadata["team_id"],
|
TeamID: inboundMetadata(msg, metadataKeyTeamID),
|
||||||
})
|
})
|
||||||
|
|
||||||
agent, ok := al.registry.GetAgent(route.AgentID)
|
agent, ok := al.registry.GetAgent(route.AgentID)
|
||||||
|
|
@ -517,7 +527,7 @@ func (al *AgentLoop) resolveMessageRoute(msg bus.InboundMessage) (routing.Resolv
|
||||||
}
|
}
|
||||||
|
|
||||||
func resolveScopeKey(route routing.ResolvedRoute, msgSessionKey string) string {
|
func resolveScopeKey(route routing.ResolvedRoute, msgSessionKey string) string {
|
||||||
if msgSessionKey != "" && strings.HasPrefix(msgSessionKey, "agent:") {
|
if msgSessionKey != "" && strings.HasPrefix(msgSessionKey, sessionKeyAgentPrefix) {
|
||||||
return msgSessionKey
|
return msgSessionKey
|
||||||
}
|
}
|
||||||
return route.SessionKey
|
return route.SessionKey
|
||||||
|
|
@ -1430,10 +1440,10 @@ func (al *AgentLoop) handleCommand(
|
||||||
return "", false
|
return "", false
|
||||||
}
|
}
|
||||||
cmd := parts[0]
|
cmd := parts[0]
|
||||||
if at := strings.Index(cmd, "@"); at > 0 {
|
if at := strings.Index(cmd, commandMentionSeparator); at > 0 {
|
||||||
cmd = cmd[:at]
|
cmd = cmd[:at]
|
||||||
}
|
}
|
||||||
if cmd != "/switch" {
|
if cmd != switchCommandToken {
|
||||||
return "", false
|
return "", false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1529,10 +1539,17 @@ func extractPeer(msg bus.InboundMessage) *routing.RoutePeer {
|
||||||
return &routing.RoutePeer{Kind: msg.Peer.Kind, ID: peerID}
|
return &routing.RoutePeer{Kind: msg.Peer.Kind, ID: peerID}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func inboundMetadata(msg bus.InboundMessage, key string) string {
|
||||||
|
if msg.Metadata == nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return msg.Metadata[key]
|
||||||
|
}
|
||||||
|
|
||||||
// extractParentPeer extracts the parent peer (reply-to) from inbound message metadata.
|
// extractParentPeer extracts the parent peer (reply-to) from inbound message metadata.
|
||||||
func extractParentPeer(msg bus.InboundMessage) *routing.RoutePeer {
|
func extractParentPeer(msg bus.InboundMessage) *routing.RoutePeer {
|
||||||
parentKind := msg.Metadata["parent_peer_kind"]
|
parentKind := inboundMetadata(msg, metadataKeyParentPeerKind)
|
||||||
parentID := msg.Metadata["parent_peer_id"]
|
parentID := inboundMetadata(msg, metadataKeyParentPeerID)
|
||||||
if parentKind == "" || parentID == "" {
|
if parentKind == "" || parentID == "" {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,8 @@ type Request struct {
|
||||||
Reply func(text string) error
|
Reply func(text string) error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var commandPrefixes = []string{"/", "!"}
|
||||||
|
|
||||||
func firstToken(input string) string {
|
func firstToken(input string) string {
|
||||||
parts := strings.Fields(strings.TrimSpace(input))
|
parts := strings.Fields(strings.TrimSpace(input))
|
||||||
if len(parts) == 0 {
|
if len(parts) == 0 {
|
||||||
|
|
@ -24,24 +26,41 @@ func firstToken(input string) string {
|
||||||
return parts[0]
|
return parts[0]
|
||||||
}
|
}
|
||||||
|
|
||||||
// parseCommandName accepts both "/name" and "/name@bot", then normalizes to "name".
|
// parseCommandName accepts "/name", "!name", and Telegram's "/name@bot", then
|
||||||
|
// normalizes to lowercase command names.
|
||||||
func parseCommandName(input string) (string, bool) {
|
func parseCommandName(input string) (string, bool) {
|
||||||
token := firstToken(input)
|
token := firstToken(input)
|
||||||
if token == "" || !strings.HasPrefix(token, "/") {
|
if token == "" {
|
||||||
return "", false
|
return "", false
|
||||||
}
|
}
|
||||||
|
|
||||||
name := strings.TrimPrefix(token, "/")
|
name, ok := trimCommandPrefix(token)
|
||||||
|
if !ok {
|
||||||
|
return "", false
|
||||||
|
}
|
||||||
if i := strings.Index(name, "@"); i >= 0 {
|
if i := strings.Index(name, "@"); i >= 0 {
|
||||||
name = name[:i]
|
name = name[:i]
|
||||||
}
|
}
|
||||||
name = strings.TrimSpace(name)
|
name = normalizeCommandName(name)
|
||||||
if name == "" {
|
if name == "" {
|
||||||
return "", false
|
return "", false
|
||||||
}
|
}
|
||||||
return name, true
|
return name, true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func trimCommandPrefix(token string) (string, bool) {
|
||||||
|
for _, prefix := range commandPrefixes {
|
||||||
|
if strings.HasPrefix(token, prefix) {
|
||||||
|
return strings.TrimPrefix(token, prefix), true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "", false
|
||||||
|
}
|
||||||
|
|
||||||
|
func normalizeCommandName(name string) string {
|
||||||
|
return strings.ToLower(strings.TrimSpace(name))
|
||||||
|
}
|
||||||
|
|
||||||
func contains(items []string, target string) bool {
|
func contains(items []string, target string) bool {
|
||||||
for _, item := range items {
|
for _, item := range items {
|
||||||
if item == target {
|
if item == target {
|
||||||
|
|
|
||||||
|
|
@ -64,8 +64,13 @@ func (e *Executor) Execute(ctx context.Context, req Request) ExecuteResult {
|
||||||
}
|
}
|
||||||
|
|
||||||
func matchesCommand(def Definition, cmdName string) bool {
|
func matchesCommand(def Definition, cmdName string) bool {
|
||||||
if def.Name == cmdName {
|
if normalizeCommandName(def.Name) == cmdName {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
return contains(def.Aliases, cmdName)
|
for _, alias := range def.Aliases {
|
||||||
|
if normalizeCommandName(alias) == cmdName {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -153,3 +153,25 @@ func TestExecutor_HandlerErrorIsPropagated(t *testing.T) {
|
||||||
t.Fatalf("err=%v, want=%v", res.Err, wantErr)
|
t.Fatalf("err=%v, want=%v", res.Err, wantErr)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestExecutor_SupportsBangPrefixAndCaseInsensitiveCommand(t *testing.T) {
|
||||||
|
called := false
|
||||||
|
defs := []Definition{
|
||||||
|
{
|
||||||
|
Name: "help",
|
||||||
|
Handler: func(context.Context, Request) error {
|
||||||
|
called = true
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
ex := NewExecutor(NewRegistry(defs))
|
||||||
|
|
||||||
|
res := ex.Execute(context.Background(), Request{Channel: "telegram", Text: "!HELP"})
|
||||||
|
if res.Outcome != OutcomeHandled {
|
||||||
|
t.Fatalf("outcome=%v, want=%v", res.Outcome, OutcomeHandled)
|
||||||
|
}
|
||||||
|
if !called {
|
||||||
|
t.Fatalf("expected handler to be called")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue