99 lines
2.4 KiB
Go
99 lines
2.4 KiB
Go
// commands.go — Slash command interception.
|
|
//
|
|
// Intercepts /commands in user prompts before they reach the LLM. Each
|
|
// slash command maps to a ctl write: /compact → "compact", /clear → "clear".
|
|
// Returns true if the input was consumed as a command.
|
|
|
|
package agent
|
|
|
|
import (
|
|
"context"
|
|
"io"
|
|
"os"
|
|
"strings"
|
|
|
|
"9fans.net/go/plan9"
|
|
p9client "9fans.net/go/plan9/client"
|
|
)
|
|
|
|
// HandleCommand intercepts /commands before they reach the LLM.
|
|
// Each slash command is a rdwr to the agent's ctl file: /compact → write "compact" to ctl.
|
|
// Returns true if the input was consumed as a command.
|
|
func (ag *Agent) HandleCommand(_ context.Context, input string) bool {
|
|
if !strings.HasPrefix(input, "/") {
|
|
return false
|
|
}
|
|
rest := strings.TrimSpace(input[1:])
|
|
if rest == "" {
|
|
return false
|
|
}
|
|
if strings.HasPrefix(rest, "compactionmodel ") {
|
|
value := strings.TrimSpace(strings.TrimPrefix(rest, "compactionmodel"))
|
|
rest = "compactionmodel=" + value
|
|
}
|
|
ns := os.Getenv("NAMESPACE")
|
|
if ns == "" {
|
|
ns = p9client.Namespace()
|
|
}
|
|
user := os.Getenv("USER")
|
|
if user == "" {
|
|
user = "none"
|
|
}
|
|
aname := os.Getenv("OLLIE_UNAME")
|
|
if aname == "" {
|
|
aname = user
|
|
}
|
|
conn, err := p9client.Dial("unix", ns+"/ollie")
|
|
if err != nil {
|
|
ag.emit(infoEvent("error: " + err.Error()))
|
|
return true
|
|
}
|
|
fsys, err := conn.Attach(nil, user, aname)
|
|
if err != nil {
|
|
conn.Close()
|
|
ag.emit(infoEvent("error: " + err.Error()))
|
|
return true
|
|
}
|
|
defer fsys.Close()
|
|
ctlPath := "session/" + ag.sessionID + "/agent/" + ag.id + "/ctl"
|
|
fid, err := fsys.Open(ctlPath, plan9.ORDWR)
|
|
if err == nil {
|
|
_, err = fid.Write([]byte(rest))
|
|
}
|
|
var out []byte
|
|
if err == nil {
|
|
_, err = fid.Seek(0, 0)
|
|
}
|
|
if err == nil {
|
|
out, err = io.ReadAll(fid)
|
|
}
|
|
if fid != nil {
|
|
fid.Close()
|
|
}
|
|
if err != nil {
|
|
ag.emit(infoEvent("error: " + err.Error()))
|
|
return true
|
|
}
|
|
if len(out) > 0 {
|
|
ag.emit(infoEvent(strings.TrimRight(string(out), "\n")))
|
|
}
|
|
return true
|
|
}
|
|
|
|
// Inject sets a pending inject prompt. If one is already pending, queues it.
|
|
func (ag *Agent) Inject(prompt string) {
|
|
if !ag.pendingInject.CompareAndSwap(nil, &prompt) {
|
|
ag.fifo.Push(prompt)
|
|
return
|
|
}
|
|
ag.emit(Event{Role: "info", Content: "\n"})
|
|
ag.emit(Event{Role: "user", Content: prompt})
|
|
}
|
|
|
|
// InjectRewrite replaces any pending inject with the given prompt.
|
|
func (ag *Agent) InjectRewrite(prompt string) {
|
|
ag.pendingInject.Store(&prompt)
|
|
ag.emit(Event{Role: "info", Content: "\n"})
|
|
ag.emit(Event{Role: "user", Content: prompt})
|
|
}
|