feat(boss): Chief of Staff Boss + spun-crow registry with cap (#42)
This commit is contained in:
1 parent
ccb0e503c5
commit
663c8a19d8
10 files changed
+329
-20
No files matched your search
@@ -0,0 +1,126 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// bossSystem is the Chief of Staff's contract. A spin-up order comes back on a
|
||||
// SPIN line the harness reads and executes; anything else is plain talk.
|
||||
const bossSystem = `You are Boss crow, the Chief of Staff of a flock of AI agents. You run the flock's schedule and keep its books.
|
||||
When the owner asks you to spin up a new crow, your ENTIRE reply must start with this first line:
|
||||
SPIN {"name":"<name>","persona":"<persona>","trust":"stranger|regular"}
|
||||
Pick "persona" from the existing flock roster; a new crow only runs on an existing persona's habits. Starting trust is stranger or regular, never higher.
|
||||
For anything else, reply in plain text. Do not use the SPIN line unless the owner asked for a new crow.`
|
||||
|
||||
// spinOrder is the JSON after a "SPIN " prefix.
|
||||
type spinOrder struct {
|
||||
Name string `json:"name"`
|
||||
Persona string `json:"persona"`
|
||||
Trust string `json:"trust"`
|
||||
}
|
||||
|
||||
// respondBoss answers a request addressed to Boss: one model call through the
|
||||
// flock (CHATROOM_SPEC section 6). A SPIN reply is validated, written to the
|
||||
// registry, and announced; a refusal says why. No reply posts nothing.
|
||||
func (s *Server) respondBoss(nest Nest, req Message) {
|
||||
budget := &Budget{name: "boss-command", limit: s.p.G.TokenBudgetPerItem}
|
||||
system := systemFor("Boss", bossSystem, false)
|
||||
prompt := "FROM: " + req.Author + "\nMESSAGE: " + req.Text
|
||||
answer, err := s.p.Flock.ask(s.ctx, nil, "Boss", system, prompt, budget)
|
||||
if err != nil {
|
||||
return // a flock that cannot answer says and does nothing
|
||||
}
|
||||
order, isSpin, reason := s.parseSpin(answer)
|
||||
switch {
|
||||
case isSpin && reason == "":
|
||||
s.spinCrow(nest, req, order)
|
||||
case isSpin:
|
||||
s.postBossSay(nest, req, order.Name, reason)
|
||||
default:
|
||||
s.postBossSay(nest, req, "", strings.TrimSpace(answer))
|
||||
}
|
||||
}
|
||||
|
||||
// parseSpin reads a SPIN reply. isSpin says the reply was a spin order;
|
||||
// reason, when not "", is why the order was refused.
|
||||
func (s *Server) parseSpin(answer string) (order spinOrder, isSpin bool, reason string) {
|
||||
text := strings.TrimSpace(answer)
|
||||
if !strings.HasPrefix(text, "SPIN ") {
|
||||
return spinOrder{}, false, ""
|
||||
}
|
||||
if err := json.Unmarshal([]byte(strings.TrimSpace(strings.TrimPrefix(text, "SPIN "))), &order); err != nil {
|
||||
return spinOrder{}, true, "that order did not parse; try again."
|
||||
}
|
||||
order.Name = strings.TrimSpace(order.Name)
|
||||
order.Persona = strings.TrimSpace(order.Persona)
|
||||
order.Trust = strings.ToLower(strings.TrimSpace(order.Trust))
|
||||
switch {
|
||||
case order.Name == "":
|
||||
return order, true, "that order had no name in it."
|
||||
case !s.validPersona(order.Persona):
|
||||
return order, true, fmt.Sprintf("%s is not a persona the flock knows.", order.Persona)
|
||||
case s.nameTaken(order.Name):
|
||||
return order, true, fmt.Sprintf("%s is already in the flock; the name is taken.", order.Name)
|
||||
case s.crows.Len() >= s.p.G.MaxSpunCrows:
|
||||
return order, true, fmt.Sprintf("the registry is at its cap of %d spun crows; %s stays on the ground.", s.p.G.MaxSpunCrows, order.Name)
|
||||
}
|
||||
switch order.Trust {
|
||||
case "stranger", "regular":
|
||||
case "trusted":
|
||||
order.Trust = "regular" // earned trust can't be minted: the ceiling is regular
|
||||
default:
|
||||
order.Trust = "stranger" // fail safe: anything unreadable starts at the bottom
|
||||
}
|
||||
return order, true, ""
|
||||
}
|
||||
|
||||
func (s *Server) validPersona(name string) bool {
|
||||
_, ok := personaOf(name)
|
||||
return ok
|
||||
}
|
||||
|
||||
// nameTaken checks the persona roster and the registry alike.
|
||||
func (s *Server) nameTaken(name string) bool {
|
||||
if s.validPersona(name) {
|
||||
return true
|
||||
}
|
||||
_, found := s.crows.Get(name)
|
||||
return found
|
||||
}
|
||||
|
||||
// spinCrow writes the registry, seeds the trust record, and announces it.
|
||||
func (s *Server) spinCrow(nest Nest, req Message, order spinOrder) {
|
||||
level := Stranger
|
||||
if order.Trust == "regular" {
|
||||
level = Regular
|
||||
}
|
||||
if err := s.crows.Add(SpunCrow{Name: order.Name, Persona: order.Persona, Trust: order.Trust}); err != nil {
|
||||
s.postBossSay(nest, req, order.Name, err.Error())
|
||||
return
|
||||
}
|
||||
if err := s.p.Ledger.SetStart(order.Name, "other", level); err != nil {
|
||||
s.postBossSay(nest, req, order.Name, "the books are jammed: "+err.Error())
|
||||
return
|
||||
}
|
||||
s.postBossSay(nest, req, order.Name, fmt.Sprintf("%s joined, running on %s's habits at %s.", order.Name, order.Persona, order.Trust))
|
||||
}
|
||||
|
||||
// postBossSay is Boss's one-line reply: an outcome or a refusal, always a say.
|
||||
func (s *Server) postBossSay(nest Nest, req Message, name, text string) {
|
||||
reply := Message{
|
||||
NestID: nest.ID,
|
||||
Kind: "say",
|
||||
Author: "Boss",
|
||||
Text: text,
|
||||
To: req.Author,
|
||||
RefersTo: req.MsgID(),
|
||||
TaggedBy: "harness",
|
||||
Timestamp: time.Now().UTC().Format(time.RFC3339),
|
||||
}
|
||||
if record, err := s.msgs.Append(reply); err == nil {
|
||||
s.emitNestMessage(record)
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user