Switch to prefixed IDs and tell agents which Mac a message came from

This commit is contained in:
Ian Gulliver
2026-10-20 23:15:19 -07:00
parent f1296ba13d
commit 59bf17189d
42 changed files with 770 additions and 145 deletions
+25 -29
View File
@@ -2,7 +2,6 @@ package agents
import (
"context"
"crypto/rand"
"encoding/json"
"errors"
"fmt"
@@ -18,6 +17,7 @@ import (
"g.fc.run/ai/byre/internal/claude"
"g.fc.run/ai/byre/internal/clients"
"g.fc.run/ai/byre/internal/git"
"g.fc.run/ai/byre/internal/ids"
"g.fc.run/ai/byre/internal/mcp"
"g.fc.run/ai/byre/internal/store"
"g.fc.run/ai/byre/internal/terminal"
@@ -186,7 +186,7 @@ func (m *Manager) taskFor(ctx context.Context, sess api.AgentSession) (api.Task,
return m.store.GetTask(ctx, sess.TaskId)
}
func (m *Manager) Create(ctx context.Context, task api.Task, creator api.User, prompt, model, sendID string, images ...string) (api.AgentSession, error) {
func (m *Manager) Create(ctx context.Context, task api.Task, creator api.User, prompt, model, sendID, client string, images ...string) (api.AgentSession, error) {
unlock := m.lockDelivery("create:" + task.Id)
defer unlock()
@@ -201,12 +201,12 @@ func (m *Manager) Create(ctx context.Context, task api.Task, creator api.User, p
}
if found {
slog.Info("first message to an unused session", "session", unused, "task", task.Id)
return m.Send(ctx, unused, creator.Login, prompt, model, sendID, images...)
return m.Send(ctx, unused, creator.Login, prompt, model, sendID, client, images...)
}
return m.create(ctx, task, creator, "", prompt, model, creator.Login, sendID, images)
return m.create(ctx, task, creator, "", prompt, model, creator.Login, sendID, client, images)
}
func (m *Manager) create(ctx context.Context, task api.Task, creator api.User, member, prompt, model, actor, sendID string, images []string) (api.AgentSession, error) {
func (m *Manager) create(ctx context.Context, task api.Task, creator api.User, member, prompt, model, actor, sendID, client string, images []string) (api.AgentSession, error) {
if creator.GitName == "" || creator.GitEmail == "" {
return api.AgentSession{}, ErrNoGitIdentity
}
@@ -219,7 +219,7 @@ func (m *Manager) create(ctx context.Context, task api.Task, creator api.User, m
if err != nil {
return api.AgentSession{}, err
}
return m.turn(ctx, sess, task, actor, prompt, sendID, images)
return m.turn(ctx, sess, task, actor, prompt, sendID, client, images)
}
func (m *Manager) newSession(ctx context.Context, task api.Task, creator api.User, member, model string) (api.AgentSession, error) {
@@ -233,14 +233,14 @@ func (m *Manager) newSession(ctx context.Context, task api.Task, creator api.Use
}
}
id := newUUID()
id := ids.New(ids.Session)
vendor := vendorOf(model)
sess := api.AgentSession{
Id: id,
TaskId: task.Id,
Vendor: vendor,
Model: model,
VendorSessionId: initialVendorSessionID(vendor, id),
VendorSessionId: initialVendorSessionID(vendor),
State: api.AgentSessionStateIdle,
Role: api.Implementer,
Member: member,
@@ -252,7 +252,7 @@ func (m *Manager) newSession(ctx context.Context, task api.Task, creator api.Use
return sess, err
}
func (m *Manager) Send(ctx context.Context, sessionID, actor, text, model, sendID string, images ...string) (api.AgentSession, error) {
func (m *Manager) Send(ctx context.Context, sessionID, actor, text, model, sendID, client string, images ...string) (api.AgentSession, error) {
sess, err := m.store.GetSession(ctx, sessionID)
if err != nil {
return api.AgentSession{}, err
@@ -272,7 +272,7 @@ func (m *Manager) Send(ctx context.Context, sessionID, actor, text, model, sendI
return api.AgentSession{}, err
}
return m.turn(ctx, sess, task, actor, text, sendID, images)
return m.turn(ctx, sess, task, actor, text, sendID, client, images)
}
func (m *Manager) Interrupt(ctx context.Context, sessionID string) (api.AgentSession, error) {
@@ -575,12 +575,12 @@ func (m *Manager) Usage(ctx context.Context) (Usage, error) {
return u, nil
}
func (m *Manager) turn(ctx context.Context, sess api.AgentSession, task api.Task, actor, text, sendID string, images []string) (api.AgentSession, error) {
func (m *Manager) turn(ctx context.Context, sess api.AgentSession, task api.Task, actor, text, sendID, client string, images []string) (api.AgentSession, error) {
err := m.startRunning(ctx, sess, task.Id, actor)
if err != nil {
return api.AgentSession{}, err
}
return m.launch(ctx, sess, task, actor, text, sendID, images)
return m.launch(ctx, sess, task, actor, text, sendID, client, images)
}
func (m *Manager) startRunning(ctx context.Context, sess api.AgentSession, taskID, actor string) error {
@@ -602,20 +602,20 @@ func (m *Manager) startRunning(ctx context.Context, sess api.AgentSession, taskI
return nil
}
func (m *Manager) launch(ctx context.Context, sess api.AgentSession, task api.Task, actor, text, sendID string, images []string) (api.AgentSession, error) {
imgs, compacting, err := m.recordMessage(ctx, sess, task, actor, text, sendID, images)
func (m *Manager) launch(ctx context.Context, sess api.AgentSession, task api.Task, actor, text, sendID, client string, images []string) (api.AgentSession, error) {
imgs, compacting, err := m.recordMessage(ctx, sess, task, actor, text, sendID, client, images)
if err != nil {
m.failStart(ctx, sess.Id, actor, err)
return api.AgentSession{}, err
}
err = m.deliver(ctx, sess, task, actor, text, imgs, compacting)
err = m.deliver(ctx, sess, task, actor, client, text, imgs, compacting)
if err != nil {
return api.AgentSession{}, err
}
return m.store.GetSession(ctx, sess.Id)
}
func (m *Manager) deliver(ctx context.Context, sess api.AgentSession, task api.Task, actor, text string, imgs []image, compacting bool) error {
func (m *Manager) deliver(ctx context.Context, sess api.AgentSession, task api.Task, actor, client, text string, imgs []image, compacting bool) error {
l, err := m.ensureLive(ctx, sess, task)
if err == nil {
err = m.syncModel(ctx, l, sess.Model)
@@ -626,7 +626,7 @@ func (m *Manager) deliver(ctx context.Context, sess api.AgentSession, task api.T
}
m.beginTurn(l, compacting)
err = m.send(ctx, l, agentText(actor, text), imgs)
err = m.send(ctx, l, m.agentText(actor, client, text), imgs)
if err != nil {
m.endMergeTurn(sess.Id, false)
return err
@@ -640,6 +640,7 @@ func (m *Manager) deliverRecorded(ctx context.Context, sess api.AgentSession, ta
return false, err
}
text, _ := ev.Payload["text"].(string)
client, _ := ev.Payload["client"].(string)
ids := []string{}
raw, _ := ev.Payload["images"].([]any)
for _, id := range raw {
@@ -651,7 +652,7 @@ func (m *Manager) deliverRecorded(ctx context.Context, sess api.AgentSession, ta
m.failStart(ctx, sess.Id, ev.Actor, err)
return true, err
}
return true, m.deliver(ctx, sess, task, ev.Actor, text, imgs, isHandoffPrompt(ev.Actor, text))
return true, m.deliver(ctx, sess, task, ev.Actor, client, text, imgs, isHandoffPrompt(ev.Actor, text))
}
func (m *Manager) readImages(ctx context.Context, ids []string) ([]image, error) {
@@ -702,12 +703,12 @@ func transition(ctx context.Context, st *store.Store, id string, from, to api.Ag
}
func (m *Manager) message(ctx context.Context, sess api.AgentSession, task api.Task, l *live, actor, text string, images []string) error {
imgs, compacting, err := m.recordMessage(ctx, sess, task, actor, text, "", images)
imgs, compacting, err := m.recordMessage(ctx, sess, task, actor, text, "", "", images)
if err != nil {
return err
}
m.beginTurn(l, compacting)
return m.send(ctx, l, agentText(actor, text), imgs)
return m.send(ctx, l, m.agentText(actor, "", text), imgs)
}
func (m *Manager) send(ctx context.Context, l *live, text string, imgs []image) error {
@@ -718,7 +719,7 @@ func (m *Manager) send(ctx context.Context, l *live, text string, imgs []image)
return m.store.MarkSessionDelivered(ctx, l.id)
}
func (m *Manager) recordMessage(ctx context.Context, sess api.AgentSession, task api.Task, actor, text, sendID string, images []string) ([]image, bool, error) {
func (m *Manager) recordMessage(ctx context.Context, sess api.AgentSession, task api.Task, actor, text, sendID, client string, images []string) ([]image, bool, error) {
implementer := sess.Role == api.Implementer
if implementer && isMergeInPrompt(actor, text) {
m.ownMerge(sess.Id, task.Id, task.WorktreePath)
@@ -752,6 +753,9 @@ func (m *Manager) recordMessage(ctx context.Context, sess api.AgentSession, task
if sendID != "" {
payload["send_id"] = sendID
}
if client != "" {
payload["client"] = client
}
err = m.store.RecordTurnMessage(ctx, sess.Id, newEvent(sess.Id, sess.Owner, task.ProjectId, task.Id, actor, "user.message", payload))
if err != nil {
return nil, false, err
@@ -1473,11 +1477,3 @@ func decode(raw json.RawMessage) (any, error) {
err := json.Unmarshal(raw, &v)
return v, err
}
func newUUID() string {
b := make([]byte, 16)
rand.Read(b)
b[6] = b[6]&0x0f | 0x40
b[8] = b[8]&0x3f | 0x80
return fmt.Sprintf("%x-%x-%x-%x-%x", b[0:4], b[4:6], b[6:8], b[8:10], b[10:])
}