|
|
|
@@ -29,6 +29,14 @@ import (
|
|
|
|
|
shellmem "github.com/enmanuel/agents/shell/memory"
|
|
|
|
|
"github.com/enmanuel/agents/shell/ssh"
|
|
|
|
|
"github.com/enmanuel/agents/tools"
|
|
|
|
|
toolclock "github.com/enmanuel/agents/tools/clock"
|
|
|
|
|
toolfile "github.com/enmanuel/agents/tools/file"
|
|
|
|
|
toolhttp "github.com/enmanuel/agents/tools/http"
|
|
|
|
|
toolknowledge "github.com/enmanuel/agents/tools/knowledgetools"
|
|
|
|
|
toolmatrix "github.com/enmanuel/agents/tools/matrix"
|
|
|
|
|
toolmemory "github.com/enmanuel/agents/tools/memorytools"
|
|
|
|
|
toolssh "github.com/enmanuel/agents/tools/ssh"
|
|
|
|
|
toolweather "github.com/enmanuel/agents/tools/weather"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
const (
|
|
|
|
@@ -63,7 +71,10 @@ type Agent struct {
|
|
|
|
|
windowsMu sync.RWMutex
|
|
|
|
|
memStore memory.Store // nil when memory is disabled
|
|
|
|
|
windowSize int
|
|
|
|
|
roomCtx *tools.RoomContext
|
|
|
|
|
roomCtx *toolmemory.RoomContext
|
|
|
|
|
|
|
|
|
|
// Prompt-commands — loaded from prompts/*.md at startup
|
|
|
|
|
promptCmds map[string]string // name → prompt content
|
|
|
|
|
|
|
|
|
|
// Knowledge store — non-nil when knowledge is enabled
|
|
|
|
|
knowledgeStore *shellknowledge.FileStore
|
|
|
|
@@ -73,7 +84,7 @@ type Agent struct {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ClearWindow resets the conversation window for a room and deletes persisted
|
|
|
|
|
// messages from SQLite so the agent starts fresh. Implements tools.WindowClearer.
|
|
|
|
|
// messages from SQLite so the agent starts fresh. Implements toolmemory.WindowClearer.
|
|
|
|
|
func (a *Agent) ClearWindow(roomID string) {
|
|
|
|
|
a.windowsMu.Lock()
|
|
|
|
|
a.windows[roomID] = memory.NewWindow(a.windowSize)
|
|
|
|
@@ -159,7 +170,7 @@ func New(cfg *config.AgentConfig, rules []decision.Rule, logger *slog.Logger) (*
|
|
|
|
|
// Memory subsystem
|
|
|
|
|
var memStore memory.Store
|
|
|
|
|
windowSize := defaultWindowSize
|
|
|
|
|
roomCtx := &tools.RoomContext{}
|
|
|
|
|
roomCtx := &toolmemory.RoomContext{}
|
|
|
|
|
|
|
|
|
|
if cfg.Memory.Enabled {
|
|
|
|
|
windowSize = cfg.Memory.WindowSize
|
|
|
|
@@ -223,9 +234,12 @@ func New(cfg *config.AgentConfig, rules []decision.Rule, logger *slog.Logger) (*
|
|
|
|
|
// Register built-in command handlers
|
|
|
|
|
a.registerBuiltinCommands()
|
|
|
|
|
|
|
|
|
|
// Load prompt-commands from prompts/ directory
|
|
|
|
|
a.loadPromptCommands()
|
|
|
|
|
|
|
|
|
|
// Register memory_clear_context with self as WindowClearer (after a is created)
|
|
|
|
|
if cfg.Tools.Memory.Enabled && memStore != nil {
|
|
|
|
|
toolReg.Register(tools.NewMemoryClearContext(a, roomCtx))
|
|
|
|
|
toolReg.Register(toolmemory.NewMemoryClearContext(a, roomCtx))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Matrix event listener
|
|
|
|
@@ -247,6 +261,26 @@ func (a *Agent) RegisterCommand(spec command.Spec, handler CommandHandler) {
|
|
|
|
|
a.logger.Info("command_registered", "command", spec.Name, "aliases", spec.Aliases)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// loadPromptCommands scans the project-root prompts/ directory and loads all .md files.
|
|
|
|
|
func (a *Agent) loadPromptCommands() {
|
|
|
|
|
prompts, err := command.LoadPromptCommands("prompts")
|
|
|
|
|
if err != nil {
|
|
|
|
|
a.logger.Warn("failed to load prompt-commands", "err", err)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
a.promptCmds = make(map[string]string, len(prompts))
|
|
|
|
|
for _, p := range prompts {
|
|
|
|
|
a.promptCmds[p.Name] = p.Content
|
|
|
|
|
}
|
|
|
|
|
if len(a.promptCmds) > 0 {
|
|
|
|
|
names := make([]string, 0, len(a.promptCmds))
|
|
|
|
|
for n := range a.promptCmds {
|
|
|
|
|
names = append(names, n)
|
|
|
|
|
}
|
|
|
|
|
a.logger.Info("prompt-commands loaded", "count", len(a.promptCmds), "names", names)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// SetBus attaches the agent to the inter-agent bus for orchestration.
|
|
|
|
|
// Must be called before Run().
|
|
|
|
|
func (a *Agent) SetBus(b *bus.Bus) {
|
|
|
|
@@ -469,11 +503,20 @@ func (a *Agent) handleEvent(ctx context.Context, msgCtx decision.MessageContext,
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Unknown command — never falls through to rules or LLM
|
|
|
|
|
a.logger.Info("command_unknown", "command", msgCtx.Command)
|
|
|
|
|
_ = a.matrix.SendReplyMarkdown(ctx, roomID, msgCtx.EventID,
|
|
|
|
|
fmt.Sprintf("Comando desconocido: `!%s`. Usa `!help` para ver comandos disponibles.", msgCtx.Command))
|
|
|
|
|
return
|
|
|
|
|
// Prompt-command: expand .md content and pass to LLM
|
|
|
|
|
if content, ok := a.promptCmds[cmdName]; ok {
|
|
|
|
|
a.logger.Info("prompt_command_expanded", "command", cmdName)
|
|
|
|
|
msgCtx.Content = command.ExpandPrompt(content, msgCtx.Args)
|
|
|
|
|
msgCtx.Command = ""
|
|
|
|
|
msgCtx.Args = nil
|
|
|
|
|
// Fall through to rules/LLM flow below
|
|
|
|
|
} else {
|
|
|
|
|
// Unknown command — never falls through to rules or LLM
|
|
|
|
|
a.logger.Info("command_unknown", "command", msgCtx.Command)
|
|
|
|
|
_ = a.matrix.SendReplyMarkdown(ctx, roomID, msgCtx.EventID,
|
|
|
|
|
fmt.Sprintf("Comando desconocido: `!%s`. Usa `!help` para ver comandos disponibles.", msgCtx.Command))
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ── Non-command flow ─────────────────────────────────────────────
|
|
|
|
@@ -729,54 +772,54 @@ func buildToolRegistry(
|
|
|
|
|
matrixClient *matrix.Client,
|
|
|
|
|
memStore memory.Store,
|
|
|
|
|
kStore *shellknowledge.FileStore,
|
|
|
|
|
roomCtx *tools.RoomContext,
|
|
|
|
|
roomCtx *toolmemory.RoomContext,
|
|
|
|
|
logger *slog.Logger,
|
|
|
|
|
) *tools.Registry {
|
|
|
|
|
reg := tools.NewRegistry(logger)
|
|
|
|
|
|
|
|
|
|
if cfg.Tools.HTTP.Enabled {
|
|
|
|
|
reg.Register(tools.NewHTTPGet(cfg.Tools.HTTP))
|
|
|
|
|
reg.Register(tools.NewHTTPPost(cfg.Tools.HTTP))
|
|
|
|
|
reg.Register(toolhttp.NewHTTPGet(cfg.Tools.HTTP))
|
|
|
|
|
reg.Register(toolhttp.NewHTTPPost(cfg.Tools.HTTP))
|
|
|
|
|
logger.Debug("registered http tools")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if cfg.Tools.SSH.Enabled {
|
|
|
|
|
reg.Register(tools.NewSSHCommand(cfg.Tools.SSH, sshExec))
|
|
|
|
|
reg.Register(toolssh.NewSSHCommand(cfg.Tools.SSH, sshExec))
|
|
|
|
|
logger.Debug("registered ssh tool")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if cfg.Tools.FileOps.Enabled {
|
|
|
|
|
reg.Register(tools.NewReadFile(cfg.Tools.FileOps))
|
|
|
|
|
reg.Register(toolfile.NewReadFile(cfg.Tools.FileOps))
|
|
|
|
|
logger.Debug("registered file tool")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// current_time is always available
|
|
|
|
|
reg.Register(tools.NewCurrentTime())
|
|
|
|
|
reg.Register(toolclock.NewCurrentTime())
|
|
|
|
|
logger.Debug("registered current_time tool")
|
|
|
|
|
|
|
|
|
|
// weather tool is always available
|
|
|
|
|
reg.Register(tools.NewWeather())
|
|
|
|
|
reg.Register(toolweather.NewWeather())
|
|
|
|
|
logger.Debug("registered weather tool")
|
|
|
|
|
|
|
|
|
|
// matrix_send is always available
|
|
|
|
|
reg.Register(tools.NewMatrixSend(matrixClient))
|
|
|
|
|
reg.Register(toolmatrix.NewMatrixSend(matrixClient))
|
|
|
|
|
logger.Debug("registered matrix tool")
|
|
|
|
|
|
|
|
|
|
// Memory tools (memory_clear_context registered later since it needs the Agent)
|
|
|
|
|
if cfg.Tools.Memory.Enabled && memStore != nil {
|
|
|
|
|
reg.Register(tools.NewMemorySave(cfg.Agent.ID, memStore))
|
|
|
|
|
reg.Register(tools.NewMemoryRecall(cfg.Agent.ID, memStore))
|
|
|
|
|
reg.Register(tools.NewMemoryForget(cfg.Agent.ID, memStore))
|
|
|
|
|
reg.Register(tools.NewMemorySummary(cfg.Agent.ID, memStore))
|
|
|
|
|
reg.Register(toolmemory.NewMemorySave(cfg.Agent.ID, memStore))
|
|
|
|
|
reg.Register(toolmemory.NewMemoryRecall(cfg.Agent.ID, memStore))
|
|
|
|
|
reg.Register(toolmemory.NewMemoryForget(cfg.Agent.ID, memStore))
|
|
|
|
|
reg.Register(toolmemory.NewMemorySummary(cfg.Agent.ID, memStore))
|
|
|
|
|
logger.Debug("registered memory tools")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Knowledge tools
|
|
|
|
|
if cfg.Tools.Knowledge.Enabled && kStore != nil {
|
|
|
|
|
reg.Register(tools.NewKnowledgeSearch(kStore))
|
|
|
|
|
reg.Register(tools.NewKnowledgeRead(kStore))
|
|
|
|
|
reg.Register(tools.NewKnowledgeWrite(kStore))
|
|
|
|
|
reg.Register(tools.NewKnowledgeList(kStore))
|
|
|
|
|
reg.Register(toolknowledge.NewKnowledgeSearch(kStore))
|
|
|
|
|
reg.Register(toolknowledge.NewKnowledgeRead(kStore))
|
|
|
|
|
reg.Register(toolknowledge.NewKnowledgeWrite(kStore))
|
|
|
|
|
reg.Register(toolknowledge.NewKnowledgeList(kStore))
|
|
|
|
|
logger.Debug("registered knowledge tools")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|