feat(boss): Boss creates nests and assigns registry crows (#43)
This commit is contained in:
1 parent
3e4b591762
commit
dab245cb73
5 files changed
+106
-11
No files matched your search
@@ -8,12 +8,16 @@ import (
|
||||
)
|
||||
|
||||
// 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.
|
||||
// SPIN line and a nest order on a NEST line, both read and executed by the
|
||||
// harness; 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.`
|
||||
When the owner asks you to create a nest, your ENTIRE reply must start with this first line:
|
||||
NEST {"name":"<nest name>","crows":["<crow>","<crow>"]}
|
||||
The crows are the working flock members the owner named; the Sentry, Elder, Quill and Boss serve every nest and are never members.
|
||||
For anything else, reply in plain text. Do not use the SPIN or NEST line unless the owner asked for a new crow or a new nest.`
|
||||
|
||||
// spinOrder is the JSON after a "SPIN " prefix.
|
||||
type spinOrder struct {
|
||||
@@ -22,9 +26,16 @@ type spinOrder struct {
|
||||
Trust string `json:"trust"`
|
||||
}
|
||||
|
||||
// nestOrder is the JSON after a "NEST " prefix.
|
||||
type nestOrder struct {
|
||||
Name string `json:"name"`
|
||||
Crows []string `json:"crows"`
|
||||
}
|
||||
|
||||
// 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.
|
||||
// registry, and announced; a NEST reply is validated and built through the
|
||||
// nest store; 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)
|
||||
@@ -34,11 +45,16 @@ func (s *Server) respondBoss(nest Nest, req Message) {
|
||||
return // a flock that cannot answer says and does nothing
|
||||
}
|
||||
order, isSpin, reason := s.parseSpin(answer)
|
||||
nOrder, isNest, nestReason := s.parseNest(answer)
|
||||
switch {
|
||||
case isSpin && reason == "":
|
||||
s.spinCrow(nest, req, order)
|
||||
case isSpin:
|
||||
s.postBossSay(nest, req, order.Name, reason)
|
||||
case isNest && nestReason == "":
|
||||
s.createNest(nest, req, nOrder)
|
||||
case isNest:
|
||||
s.postBossSay(nest, req, nOrder.Name, nestReason)
|
||||
default:
|
||||
s.postBossSay(nest, req, "", strings.TrimSpace(answer))
|
||||
}
|
||||
@@ -82,6 +98,31 @@ func (s *Server) validPersona(name string) bool {
|
||||
return ok
|
||||
}
|
||||
|
||||
// parseNest reads a NEST reply. isNest says the reply was a nest order;
|
||||
// reason, when not "", is why the order never reaches the nest store. Member
|
||||
// names are resolved by the store itself: persona crows and registry crows
|
||||
// pass, anything else (service roles included) is refused there.
|
||||
func (s *Server) parseNest(answer string) (order nestOrder, isNest bool, reason string) {
|
||||
text := strings.TrimSpace(answer)
|
||||
if !strings.HasPrefix(text, "NEST ") {
|
||||
return nestOrder{}, false, ""
|
||||
}
|
||||
if err := json.Unmarshal([]byte(strings.TrimSpace(strings.TrimPrefix(text, "NEST "))), &order); err != nil {
|
||||
return nestOrder{}, true, "that order did not parse; try again."
|
||||
}
|
||||
order.Name = strings.TrimSpace(order.Name)
|
||||
for i, c := range order.Crows {
|
||||
order.Crows[i] = strings.TrimSpace(c)
|
||||
}
|
||||
switch {
|
||||
case order.Name == "":
|
||||
return order, true, "that order had no nest name in it."
|
||||
case len(order.Crows) == 0:
|
||||
return order, true, "that order had no crows in it."
|
||||
}
|
||||
return order, true, ""
|
||||
}
|
||||
|
||||
// nameTaken checks the persona roster and the registry alike.
|
||||
func (s *Server) nameTaken(name string) bool {
|
||||
if s.validPersona(name) {
|
||||
@@ -108,6 +149,34 @@ func (s *Server) spinCrow(nest Nest, req Message, order spinOrder) {
|
||||
s.postBossSay(nest, req, order.Name, fmt.Sprintf("%s joined, running on %s's habits at %s.", order.Name, order.Persona, order.Trust))
|
||||
}
|
||||
|
||||
// createNest builds the nest Boss was ordered into. The store has the last
|
||||
// word on guardrails — name, members (persona roster or registry), duplicates
|
||||
// and the nest cap are all checked there before anything mutates, so a bad
|
||||
// order never leaves a partial nest. Success posts a Boss say announcing
|
||||
// membership in the new nest, plus a confirming say in the requesting nest; a
|
||||
// refusal says why.
|
||||
func (s *Server) createNest(reqNest Nest, req Message, order nestOrder) {
|
||||
created, err := s.nests.Create(order.Name, order.Crows)
|
||||
if err != nil {
|
||||
s.postBossSay(reqNest, req, order.Name, err.Error())
|
||||
return
|
||||
}
|
||||
members := strings.Join(created.Crows, ", ")
|
||||
open := Message{
|
||||
NestID: created.ID,
|
||||
Kind: "say",
|
||||
Author: "Boss",
|
||||
Text: fmt.Sprintf("This nest is open: %s. Its crows: %s.", created.Name, members),
|
||||
To: req.Author,
|
||||
TaggedBy: "harness",
|
||||
Timestamp: time.Now().UTC().Format(time.RFC3339),
|
||||
}
|
||||
if record, err := s.msgs.Append(open); err == nil {
|
||||
s.emitNestMessage(record)
|
||||
}
|
||||
s.postBossSay(reqNest, req, created.Name, fmt.Sprintf("%s opened with %s.", created.Name, members))
|
||||
}
|
||||
|
||||
// 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{
|
||||
|
||||
Reference in new issue
Block a user