Match pending sends by a send id the server echoes
This commit is contained in:
@@ -1749,6 +1749,9 @@ components:
|
|||||||
type: array
|
type: array
|
||||||
items: { type: string }
|
items: { type: string }
|
||||||
description: Blob ids from uploadBlob, sent to the agent as images alongside the prompt.
|
description: Blob ids from uploadBlob, sent to the agent as images alongside the prompt.
|
||||||
|
send_id:
|
||||||
|
type: string
|
||||||
|
description: The caller's id for this send, carried on the user.message event the prompt records.
|
||||||
|
|
||||||
MessageCreate:
|
MessageCreate:
|
||||||
type: object
|
type: object
|
||||||
@@ -1762,6 +1765,9 @@ components:
|
|||||||
model:
|
model:
|
||||||
type: string
|
type: string
|
||||||
description: Model for this turn and later ones. When it differs from the session's model, the session switches before the message is sent.
|
description: Model for this turn and later ones. When it differs from the session's model, the session switches before the message is sent.
|
||||||
|
send_id:
|
||||||
|
type: string
|
||||||
|
description: The caller's id for this send, carried on the user.queued and user.message events the message records.
|
||||||
|
|
||||||
BoardPostCreate:
|
BoardPostCreate:
|
||||||
type: object
|
type: object
|
||||||
|
|||||||
+2
-2
File diff suppressed because one or more lines are too long
@@ -44,7 +44,7 @@ func (m *Manager) Compact(ctx context.Context, sessionID, model string) (api.Age
|
|||||||
m.mu.Lock()
|
m.mu.Lock()
|
||||||
m.handoffModel[sessionID] = model
|
m.handoffModel[sessionID] = model
|
||||||
m.mu.Unlock()
|
m.mu.Unlock()
|
||||||
return m.launch(ctx, sess, task, "byre", prompt, nil)
|
return m.launch(ctx, sess, task, "byre", prompt, "", nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Manager) continuationModel(sess api.AgentSession) string {
|
func (m *Manager) continuationModel(sess api.AgentSession) string {
|
||||||
@@ -90,7 +90,7 @@ func (m *Manager) handoff(ctx context.Context, sessionID, handoff string) error
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
_, err = m.createLead(ctx, creator, leadHandoffPreamble+handoff, model, "byre", nil)
|
_, err = m.createLead(ctx, creator, leadHandoffPreamble+handoff, model, "byre", "", nil)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -112,7 +112,7 @@ func (m *Manager) handoff(ctx context.Context, sessionID, handoff string) error
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
_, err = m.create(ctx, task, creator, sess.Member, handoffPreamble+handoff, model, "byre", nil)
|
_, err = m.create(ctx, task, creator, sess.Member, handoffPreamble+handoff, model, "byre", "", nil)
|
||||||
if errors.Is(err, ErrConflict) {
|
if errors.Is(err, ErrConflict) {
|
||||||
m.startDiscard(task.Id)
|
m.startDiscard(task.Id)
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
@@ -73,7 +73,7 @@ func (m *Manager) forkTool(l *live) mcp.Tool {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
_, _, err = m.Message(ctx, sessionID, agentActor, args.Prompt, "")
|
_, _, err = m.Message(ctx, sessionID, agentActor, args.Prompt, "", "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", fmt.Errorf("task %s forked, but its agent did not start: %w", t.Id, err)
|
return "", fmt.Errorf("task %s forked, but its agent did not start: %w", t.Id, err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -373,7 +373,7 @@ func (m *Manager) Deliver(ctx context.Context, sessionID string) error {
|
|||||||
for _, p := range pending {
|
for _, p := range pending {
|
||||||
images = append(images, p.Response.Images...)
|
images = append(images, p.Response.Images...)
|
||||||
}
|
}
|
||||||
after, err := m.turn(ctx, sess, task, pending[0].Response.User, deliveryMessage(pending), images)
|
after, err := m.turn(ctx, sess, task, pending[0].Response.User, deliveryMessage(pending), "", images)
|
||||||
if errors.Is(err, ErrConflict) {
|
if errors.Is(err, ErrConflict) {
|
||||||
slog.Info("deliver held: session changed state", "session", sessionID, "err", err, "responses", ids)
|
slog.Info("deliver held: session changed state", "session", sessionID, "err", err, "responses", ids)
|
||||||
return nil
|
return nil
|
||||||
@@ -412,7 +412,7 @@ func (m *Manager) deliverMidTurn(ctx context.Context, l *live) midTurn {
|
|||||||
}
|
}
|
||||||
msgs := []queued{}
|
msgs := []queued{}
|
||||||
for _, in := range inputs {
|
for _, in := range inputs {
|
||||||
msgs = append(msgs, queued{actor: in.Actor, text: in.Text})
|
msgs = append(msgs, queued{actor: in.Actor, text: in.Text, sendID: in.SendID})
|
||||||
}
|
}
|
||||||
return midTurn{text: m.recordQueued(ctx, l, msgs), leadInputs: inputs}
|
return midTurn{text: m.recordQueued(ctx, l, msgs), leadInputs: inputs}
|
||||||
}
|
}
|
||||||
@@ -435,7 +435,7 @@ func (m *Manager) deliverMidTurn(ctx context.Context, l *live) midTurn {
|
|||||||
if len(mt.taskInputs) > 0 {
|
if len(mt.taskInputs) > 0 {
|
||||||
msgs := []queued{}
|
msgs := []queued{}
|
||||||
for _, in := range mt.taskInputs {
|
for _, in := range mt.taskInputs {
|
||||||
msgs = append(msgs, queued{actor: in.Actor, text: in.Text})
|
msgs = append(msgs, queued{actor: in.Actor, text: in.Text, sendID: in.SendID})
|
||||||
}
|
}
|
||||||
parts = append(parts, m.recordQueued(ctx, l, msgs))
|
parts = append(parts, m.recordQueued(ctx, l, msgs))
|
||||||
}
|
}
|
||||||
|
|||||||
+11
-7
@@ -88,7 +88,7 @@ func (m *Manager) activeLead(ctx context.Context, owner string) (api.AgentSessio
|
|||||||
return api.AgentSession{}, false, nil
|
return api.AgentSession{}, false, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Manager) createLead(ctx context.Context, owner api.User, prompt, model, actor string, images []string) (api.AgentSession, error) {
|
func (m *Manager) createLead(ctx context.Context, owner api.User, prompt, model, actor, sendID string, images []string) (api.AgentSession, error) {
|
||||||
_, found, err := m.activeLead(ctx, owner.Login)
|
_, found, err := m.activeLead(ctx, owner.Login)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return api.AgentSession{}, err
|
return api.AgentSession{}, err
|
||||||
@@ -114,10 +114,10 @@ func (m *Manager) createLead(ctx context.Context, owner api.User, prompt, model,
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return api.AgentSession{}, err
|
return api.AgentSession{}, err
|
||||||
}
|
}
|
||||||
return m.turn(ctx, sess, api.Task{}, actor, prompt, images)
|
return m.turn(ctx, sess, api.Task{}, actor, prompt, sendID, images)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Manager) MessageLead(ctx context.Context, user api.User, text, model string, images []string) (api.AgentSession, error) {
|
func (m *Manager) MessageLead(ctx context.Context, user api.User, text, model, sendID string, images []string) (api.AgentSession, error) {
|
||||||
m.setStartedTodo(user.Login, false)
|
m.setStartedTodo(user.Login, false)
|
||||||
|
|
||||||
m.leadMu.Lock()
|
m.leadMu.Lock()
|
||||||
@@ -128,11 +128,11 @@ func (m *Manager) MessageLead(ctx context.Context, user api.User, text, model st
|
|||||||
return api.AgentSession{}, err
|
return api.AgentSession{}, err
|
||||||
}
|
}
|
||||||
if !found {
|
if !found {
|
||||||
return m.createLead(ctx, user, text, model, user.Login, images)
|
return m.createLead(ctx, user, text, model, user.Login, sendID, images)
|
||||||
}
|
}
|
||||||
|
|
||||||
if sess.State == api.AgentSessionStateIdle {
|
if sess.State == api.AgentSessionStateIdle {
|
||||||
s, err := m.Send(ctx, sess.Id, user.Login, text, model, images...)
|
s, err := m.Send(ctx, sess.Id, user.Login, text, model, sendID, images...)
|
||||||
if !errors.Is(err, ErrConflict) {
|
if !errors.Is(err, ErrConflict) {
|
||||||
return s, err
|
return s, err
|
||||||
}
|
}
|
||||||
@@ -149,7 +149,7 @@ func (m *Manager) MessageLead(ctx context.Context, user api.User, text, model st
|
|||||||
}
|
}
|
||||||
sess.Model = model
|
sess.Model = model
|
||||||
}
|
}
|
||||||
err = m.store.AddLeadInput(ctx, store.LeadInput{Owner: user.Login, Kind: store.LeadMessage, Actor: user.Login, Text: text, Images: images})
|
err = m.store.AddLeadInput(ctx, store.LeadInput{Owner: user.Login, Kind: store.LeadMessage, Actor: user.Login, Text: text, Images: images, SendID: sendID})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return api.AgentSession{}, err
|
return api.AgentSession{}, err
|
||||||
}
|
}
|
||||||
@@ -171,6 +171,10 @@ func (m *Manager) DeliverLead(ctx context.Context, owner string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
batch, actor, text, images := nextLeadTurn(inputs)
|
batch, actor, text, images := nextLeadTurn(inputs)
|
||||||
|
sendID := ""
|
||||||
|
if batch[0].Kind == store.LeadMessage {
|
||||||
|
sendID = batch[0].SendID
|
||||||
|
}
|
||||||
if batch[0].Kind == store.LeadSignal && m.interrupted(sess.Id) {
|
if batch[0].Kind == store.LeadSignal && m.interrupted(sess.Id) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -182,7 +186,7 @@ func (m *Manager) DeliverLead(ctx context.Context, owner string) error {
|
|||||||
}
|
}
|
||||||
text += "\n\n" + state
|
text += "\n\n" + state
|
||||||
}
|
}
|
||||||
_, err = m.Send(ctx, sess.Id, actor, text, "", images...)
|
_, err = m.Send(ctx, sess.Id, actor, text, "", sendID, images...)
|
||||||
if errors.Is(err, ErrConflict) {
|
if errors.Is(err, ErrConflict) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -196,13 +196,13 @@ func (m *Manager) leadTools(l *live) []mcp.Tool {
|
|||||||
return fmt.Sprintf("Posted to %s's board; every agent of its team gets it.", task.Id), nil
|
return fmt.Sprintf("Posted to %s's board; every agent of its team gets it.", task.Id), nil
|
||||||
}
|
}
|
||||||
if !found {
|
if !found {
|
||||||
_, err = m.create(ctx, task, user, "", args.Text, args.Model, leadActor(user.Login), nil)
|
_, err = m.create(ctx, task, user, "", args.Text, args.Model, leadActor(user.Login), "", nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
return fmt.Sprintf("Started an agent on %s with your message.", task.Id), nil
|
return fmt.Sprintf("Started an agent on %s with your message.", task.Id), nil
|
||||||
}
|
}
|
||||||
_, queued, err := m.Message(ctx, sess.Id, leadActor(user.Login), args.Text, args.Model)
|
_, queued, err := m.Message(ctx, sess.Id, leadActor(user.Login), args.Text, args.Model, "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
@@ -301,7 +301,7 @@ func (m *Manager) leadTools(l *live) []mcp.Tool {
|
|||||||
}
|
}
|
||||||
return fmt.Sprintf("Task %s %q created from %s and its team of %d agents started.", task.Id, task.Title, task.BaseRef, len(args.Agents)), nil
|
return fmt.Sprintf("Task %s %q created from %s and its team of %d agents started.", task.Id, task.Title, task.BaseRef, len(args.Agents)), nil
|
||||||
}
|
}
|
||||||
_, err = m.create(ctx, task, user, "", args.Prompt, m.modelOr(args.Model, l), leadActor(user.Login), nil)
|
_, err = m.create(ctx, task, user, "", args.Prompt, m.modelOr(args.Model, l), leadActor(user.Login), "", nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", fmt.Errorf("task %s created, but its agent did not start: %w", task.Id, err)
|
return "", fmt.Errorf("task %s created, but its agent did not start: %w", task.Id, err)
|
||||||
}
|
}
|
||||||
|
|||||||
+15
-12
@@ -185,11 +185,11 @@ func (m *Manager) taskFor(ctx context.Context, sess api.AgentSession) (api.Task,
|
|||||||
return m.store.GetTask(ctx, sess.TaskId)
|
return m.store.GetTask(ctx, sess.TaskId)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Manager) Create(ctx context.Context, task api.Task, creator api.User, prompt, model string, images ...string) (api.AgentSession, error) {
|
func (m *Manager) Create(ctx context.Context, task api.Task, creator api.User, prompt, model, sendID string, images ...string) (api.AgentSession, error) {
|
||||||
return m.create(ctx, task, creator, "", prompt, model, creator.Login, images)
|
return m.create(ctx, task, creator, "", prompt, model, creator.Login, sendID, images)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Manager) create(ctx context.Context, task api.Task, creator api.User, member, prompt, model, actor string, images []string) (api.AgentSession, error) {
|
func (m *Manager) create(ctx context.Context, task api.Task, creator api.User, member, prompt, model, actor, sendID string, images []string) (api.AgentSession, error) {
|
||||||
if creator.GitName == "" || creator.GitEmail == "" {
|
if creator.GitName == "" || creator.GitEmail == "" {
|
||||||
return api.AgentSession{}, ErrNoGitIdentity
|
return api.AgentSession{}, ErrNoGitIdentity
|
||||||
}
|
}
|
||||||
@@ -202,7 +202,7 @@ func (m *Manager) create(ctx context.Context, task api.Task, creator api.User, m
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return api.AgentSession{}, err
|
return api.AgentSession{}, err
|
||||||
}
|
}
|
||||||
return m.turn(ctx, sess, task, actor, prompt, images)
|
return m.turn(ctx, sess, task, actor, prompt, sendID, images)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Manager) newSession(ctx context.Context, task api.Task, creator api.User, member, model string) (api.AgentSession, error) {
|
func (m *Manager) newSession(ctx context.Context, task api.Task, creator api.User, member, model string) (api.AgentSession, error) {
|
||||||
@@ -235,7 +235,7 @@ func (m *Manager) newSession(ctx context.Context, task api.Task, creator api.Use
|
|||||||
return sess, err
|
return sess, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Manager) Send(ctx context.Context, sessionID, actor, text, model string, images ...string) (api.AgentSession, error) {
|
func (m *Manager) Send(ctx context.Context, sessionID, actor, text, model, sendID string, images ...string) (api.AgentSession, error) {
|
||||||
sess, err := m.store.GetSession(ctx, sessionID)
|
sess, err := m.store.GetSession(ctx, sessionID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return api.AgentSession{}, err
|
return api.AgentSession{}, err
|
||||||
@@ -262,7 +262,7 @@ func (m *Manager) Send(ctx context.Context, sessionID, actor, text, model string
|
|||||||
return api.AgentSession{}, err
|
return api.AgentSession{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return m.turn(ctx, sess, task, actor, text, images)
|
return m.turn(ctx, sess, task, actor, text, sendID, images)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Manager) Interrupt(ctx context.Context, sessionID string) (api.AgentSession, error) {
|
func (m *Manager) Interrupt(ctx context.Context, sessionID string) (api.AgentSession, error) {
|
||||||
@@ -564,12 +564,12 @@ func (m *Manager) Usage(ctx context.Context) (Usage, error) {
|
|||||||
return u, nil
|
return u, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Manager) turn(ctx context.Context, sess api.AgentSession, task api.Task, actor, text string, images []string) (api.AgentSession, error) {
|
func (m *Manager) turn(ctx context.Context, sess api.AgentSession, task api.Task, actor, text, sendID string, images []string) (api.AgentSession, error) {
|
||||||
err := m.startRunning(ctx, sess, task.Id, actor)
|
err := m.startRunning(ctx, sess, task.Id, actor)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return api.AgentSession{}, err
|
return api.AgentSession{}, err
|
||||||
}
|
}
|
||||||
return m.launch(ctx, sess, task, actor, text, images)
|
return m.launch(ctx, sess, task, actor, text, sendID, images)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Manager) startRunning(ctx context.Context, sess api.AgentSession, taskID, actor string) error {
|
func (m *Manager) startRunning(ctx context.Context, sess api.AgentSession, taskID, actor string) error {
|
||||||
@@ -591,8 +591,8 @@ func (m *Manager) startRunning(ctx context.Context, sess api.AgentSession, taskI
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Manager) launch(ctx context.Context, sess api.AgentSession, task api.Task, actor, text string, images []string) (api.AgentSession, error) {
|
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, images)
|
imgs, compacting, err := m.recordMessage(ctx, sess, task, actor, text, sendID, images)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
m.failStart(ctx, sess.Id, actor, err)
|
m.failStart(ctx, sess.Id, actor, err)
|
||||||
return api.AgentSession{}, err
|
return api.AgentSession{}, err
|
||||||
@@ -691,7 +691,7 @@ 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 {
|
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 {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -707,7 +707,7 @@ func (m *Manager) send(ctx context.Context, l *live, text string, imgs []image)
|
|||||||
return m.store.MarkSessionDelivered(ctx, l.id)
|
return m.store.MarkSessionDelivered(ctx, l.id)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Manager) recordMessage(ctx context.Context, sess api.AgentSession, task api.Task, actor, text string, images []string) ([]image, bool, error) {
|
func (m *Manager) recordMessage(ctx context.Context, sess api.AgentSession, task api.Task, actor, text, sendID string, images []string) ([]image, bool, error) {
|
||||||
implementer := sess.Role == api.Implementer
|
implementer := sess.Role == api.Implementer
|
||||||
if implementer && isMergeInPrompt(actor, text) {
|
if implementer && isMergeInPrompt(actor, text) {
|
||||||
m.ownMerge(sess.Id, task.Id, task.WorktreePath)
|
m.ownMerge(sess.Id, task.Id, task.WorktreePath)
|
||||||
@@ -738,6 +738,9 @@ func (m *Manager) recordMessage(ctx context.Context, sess api.AgentSession, task
|
|||||||
if isFindingsMessage(actor, text) {
|
if isFindingsMessage(actor, text) {
|
||||||
payload["findings"] = true
|
payload["findings"] = true
|
||||||
}
|
}
|
||||||
|
if sendID != "" {
|
||||||
|
payload["send_id"] = sendID
|
||||||
|
}
|
||||||
err = m.store.RecordTurnMessage(ctx, sess.Id, newEvent(sess.Id, sess.Owner, task.ProjectId, task.Id, actor, "user.message", payload))
|
err = m.store.RecordTurnMessage(ctx, sess.Id, newEvent(sess.Id, sess.Owner, task.ProjectId, task.Id, actor, "user.message", payload))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, false, err
|
return nil, false, err
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ func (m *Manager) headStartMerge(ctx context.Context, task api.Task, user api.Us
|
|||||||
}
|
}
|
||||||
prompt := mergeInPrompt(target) + fmt.Sprintf(" This task's Merge is held while another task merges into %s; this turn gets a head start, and byre merges %s in again when the held merge starts. If you stop for substantive changes, call stop_merge first, so byre drops the held merge and nothing is pushed before the review.", target, target)
|
prompt := mergeInPrompt(target) + fmt.Sprintf(" This task's Merge is held while another task merges into %s; this turn gets a head start, and byre merges %s in again when the held merge starts. If you stop for substantive changes, call stop_merge first, so byre drops the held merge and nothing is pushed before the review.", target, target)
|
||||||
go func() {
|
go func() {
|
||||||
_, err := m.launch(context.Background(), sess, task, "byre", prompt, nil)
|
_, err := m.launch(context.Background(), sess, task, "byre", prompt, "", nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
slog.Error("start held merge head start", "task", task.Id, "err", err)
|
slog.Error("start held merge head start", "task", task.Id, "err", err)
|
||||||
}
|
}
|
||||||
@@ -64,9 +64,9 @@ func (m *Manager) sendMergeIn(ctx context.Context, task api.Task, user api.User,
|
|||||||
return api.AgentSession{}, err
|
return api.AgentSession{}, err
|
||||||
}
|
}
|
||||||
if found {
|
if found {
|
||||||
return m.Send(ctx, sess.Id, "byre", prompt, "")
|
return m.Send(ctx, sess.Id, "byre", prompt, "", "")
|
||||||
}
|
}
|
||||||
return m.create(ctx, task, user, sess.Member, prompt, "", "byre", nil)
|
return m.create(ctx, task, user, sess.Member, prompt, "", "byre", "", nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Manager) Merge(ctx context.Context, task api.Task, project api.Project, user api.User, title string) (api.Task, error) {
|
func (m *Manager) Merge(ctx context.Context, task api.Task, project api.Project, user api.User, title string) (api.Task, error) {
|
||||||
@@ -189,7 +189,7 @@ func (m *Manager) launchMerge(sess api.AgentSession, task api.Task, project api.
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (m *Manager) deliverMerge(ctx context.Context, sess api.AgentSession, task api.Task, project api.Project, prompt string) error {
|
func (m *Manager) deliverMerge(ctx context.Context, sess api.AgentSession, task api.Task, project api.Project, prompt string) error {
|
||||||
_, compacting, err := m.recordMessage(ctx, sess, task, "byre", prompt, nil)
|
_, compacting, err := m.recordMessage(ctx, sess, task, "byre", prompt, "", nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -223,7 +223,7 @@ func (m *Manager) MergeToMain(ctx context.Context, task api.Task, project api.Pr
|
|||||||
}
|
}
|
||||||
if err == nil {
|
if err == nil {
|
||||||
m.clearMergeAfterReview(ctx, task.Id)
|
m.clearMergeAfterReview(ctx, task.Id)
|
||||||
_, err = m.Send(ctx, sess.Id, "byre", landPrompt(project.DefaultBranch, title), "")
|
_, err = m.Send(ctx, sess.Id, "byre", landPrompt(project.DefaultBranch, title), "", "")
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
m.endLanding(task.Id, sess.Id)
|
m.endLanding(task.Id, sess.Id)
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ func (m *Manager) lockDelivery(sessionID string) func() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Manager) Message(ctx context.Context, sessionID, actor, text, model string, images ...string) (api.AgentSession, bool, error) {
|
func (m *Manager) Message(ctx context.Context, sessionID, actor, text, model, sendID string, images ...string) (api.AgentSession, bool, error) {
|
||||||
unlock := m.lockDelivery(sessionID)
|
unlock := m.lockDelivery(sessionID)
|
||||||
defer unlock()
|
defer unlock()
|
||||||
|
|
||||||
@@ -55,7 +55,7 @@ func (m *Manager) Message(ctx context.Context, sessionID, actor, text, model str
|
|||||||
}
|
}
|
||||||
held := sess.State == api.AgentSessionStateIdle && sess.Role == api.Implementer && (automatedActor(actor) && m.interrupted(sess.Id) || m.heldForLanding(sess.TaskId, sess.Id))
|
held := sess.State == api.AgentSessionStateIdle && sess.Role == api.Implementer && (automatedActor(actor) && m.interrupted(sess.Id) || m.heldForLanding(sess.TaskId, sess.Id))
|
||||||
if !held && (sess.State != api.AgentSessionStateRunning || sess.Role != api.Implementer) {
|
if !held && (sess.State != api.AgentSessionStateRunning || sess.Role != api.Implementer) {
|
||||||
sess, err = m.Send(ctx, sessionID, actor, text, model, images...)
|
sess, err = m.Send(ctx, sessionID, actor, text, model, sendID, images...)
|
||||||
return sess, false, err
|
return sess, false, err
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -75,7 +75,7 @@ func (m *Manager) Message(ctx context.Context, sessionID, actor, text, model str
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return api.AgentSession{}, false, err
|
return api.AgentSession{}, false, err
|
||||||
}
|
}
|
||||||
err = m.store.AddTaskInput(ctx, store.TaskInput{TaskID: sess.TaskId, Member: sess.Member, Actor: actor, Text: text, Images: images}, task.ProjectId, sess.Id)
|
err = m.store.AddTaskInput(ctx, store.TaskInput{TaskID: sess.TaskId, Member: sess.Member, Actor: actor, Text: text, Images: images, SendID: sendID}, task.ProjectId, sess.Id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return api.AgentSession{}, false, err
|
return api.AgentSession{}, false, err
|
||||||
}
|
}
|
||||||
@@ -139,7 +139,7 @@ func (m *Manager) deliverQueued(ctx context.Context, sess api.AgentSession) (boo
|
|||||||
}
|
}
|
||||||
|
|
||||||
in := inputs[0]
|
in := inputs[0]
|
||||||
_, err = m.Send(ctx, sess.Id, in.Actor, in.Text, "", in.Images...)
|
_, err = m.Send(ctx, sess.Id, in.Actor, in.Text, "", in.SendID, in.Images...)
|
||||||
if errors.Is(err, ErrConflict) {
|
if errors.Is(err, ErrConflict) {
|
||||||
slog.Info("queued message held: session changed state", "session", sess.Id, "task", task.Id, "err", err)
|
slog.Info("queued message held: session changed state", "session", sess.Id, "task", task.Id, "err", err)
|
||||||
return true, nil
|
return true, nil
|
||||||
@@ -171,8 +171,9 @@ func (m *Manager) deliverAllQueued(ctx context.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type queued struct {
|
type queued struct {
|
||||||
actor string
|
actor string
|
||||||
text string
|
text string
|
||||||
|
sendID string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Manager) takeQueuedMidTurn(ctx context.Context, l *live) []store.TaskInput {
|
func (m *Manager) takeQueuedMidTurn(ctx context.Context, l *live) []store.TaskInput {
|
||||||
@@ -260,6 +261,9 @@ func (m *Manager) recordQueued(ctx context.Context, l *live, msgs []queued) stri
|
|||||||
if isFindingsMessage(q.actor, q.text) {
|
if isFindingsMessage(q.actor, q.text) {
|
||||||
payload["findings"] = true
|
payload["findings"] = true
|
||||||
}
|
}
|
||||||
|
if q.sendID != "" {
|
||||||
|
payload["send_id"] = q.sendID
|
||||||
|
}
|
||||||
err := m.emit(ctx, l, q.actor, "user.message", payload)
|
err := m.emit(ctx, l, q.actor, "user.message", payload)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
slog.Error("record mid-turn message", "session", l.id, "err", err)
|
slog.Error("record mid-turn message", "session", l.id, "err", err)
|
||||||
|
|||||||
@@ -205,7 +205,7 @@ func (m *Manager) tryPromptReady(ctx context.Context, taskID string, fromReview
|
|||||||
if review.State == api.ReviewStateSkipped {
|
if review.State == api.ReviewStateSkipped {
|
||||||
prompt = readyPromptUnchanged
|
prompt = readyPromptUnchanged
|
||||||
}
|
}
|
||||||
_, err = m.Send(ctx, sess.Id, "byre", prompt, "")
|
_, err = m.Send(ctx, sess.Id, "byre", prompt, "", "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -217,7 +217,7 @@ func (m *Manager) startReviewer(ctx context.Context, task api.Task, snap api.Sna
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = m.turn(ctx, sess, task, "byre", prompt, nil)
|
_, err = m.turn(ctx, sess, task, "byre", prompt, "", nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
m.endReviewer(ctx, sess.Id, project, reviewID)
|
m.endReviewer(ctx, sess.Id, project, reviewID)
|
||||||
m.failReview(ctx, reviewID, "the reviewer didn't start: "+err.Error())
|
m.failReview(ctx, reviewID, "the reviewer didn't start: "+err.Error())
|
||||||
@@ -350,7 +350,7 @@ func (m *Manager) forwardFindings(taskID, reviewID string) {
|
|||||||
slog.Info("review findings not forwarded, task has no live agent", "review", reviewID, "task", taskID)
|
slog.Info("review findings not forwarded, task has no live agent", "review", reviewID, "task", taskID)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
_, _, err = m.Message(ctx, s.Id, "byre", findingsMessage(r), "")
|
_, _, err = m.Message(ctx, s.Id, "byre", findingsMessage(r), "", "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
slog.Error("forward review findings", "review", reviewID, "session", s.Id, "err", err)
|
slog.Error("forward review findings", "review", reviewID, "session", s.Id, "err", err)
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -73,7 +73,7 @@ func (m *Manager) subtaskTools(l *live) []mcp.Tool {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
_, err = m.create(ctx, task, creator, "", args.Prompt, m.modelOr(args.Model, l), agentActor, nil)
|
_, err = m.create(ctx, task, creator, "", args.Prompt, m.modelOr(args.Model, l), agentActor, "", nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", fmt.Errorf("subtask %s created, but its agent did not start: %w", task.Id, err)
|
return "", fmt.Errorf("subtask %s created, but its agent did not start: %w", task.Id, err)
|
||||||
}
|
}
|
||||||
@@ -294,10 +294,10 @@ func (m *Manager) messageTask(ctx context.Context, task api.Task, user api.User,
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if !found {
|
if !found {
|
||||||
_, err = m.create(ctx, task, user, sess.Member, text, "", actor, nil)
|
_, err = m.create(ctx, task, user, sess.Member, text, "", actor, "", nil)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
_, _, err = m.Message(ctx, sess.Id, actor, text, "")
|
_, _, err = m.Message(ctx, sess.Id, actor, text, "", "")
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -202,7 +202,7 @@ func (m *Manager) launchDiscard(ctx context.Context, taskID string) (bool, error
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
_, err = m.launch(ctx, sess, task, "byre", discardPrompt, nil)
|
_, err = m.launch(ctx, sess, task, "byre", discardPrompt, "", nil)
|
||||||
return err == nil, err
|
return err == nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ func (m *Manager) StartTask(ctx context.Context, project api.Project, creator ap
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return api.Task{}, fmt.Errorf("task %s created, but its agent did not start: %w", task.Id, err)
|
return api.Task{}, fmt.Errorf("task %s created, but its agent did not start: %w", task.Id, err)
|
||||||
}
|
}
|
||||||
imgs, compacting, err := m.recordMessage(ctx, sess, task, creator.Login, prompt, images)
|
imgs, compacting, err := m.recordMessage(ctx, sess, task, creator.Login, prompt, "", images)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
m.failStart(ctx, sess.Id, creator.Login, err)
|
m.failStart(ctx, sess.Id, creator.Login, err)
|
||||||
return api.Task{}, fmt.Errorf("task %s created, but its agent did not start: %w", task.Id, err)
|
return api.Task{}, fmt.Errorf("task %s created, but its agent did not start: %w", task.Id, err)
|
||||||
@@ -84,7 +84,7 @@ func (m *Manager) StartTodo(ctx context.Context, project api.Project, creator ap
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
_, err = m.create(ctx, task, creator, "", todoPrompt(item, discuss), model, actor, nil)
|
_, err = m.create(ctx, task, creator, "", todoPrompt(item, discuss), model, actor, "", nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("task %s created, but its agent did not start: %w", task.Id, err)
|
return nil, fmt.Errorf("task %s created, but its agent did not start: %w", task.Id, err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -95,7 +95,7 @@ func (m *Manager) startTeam(ctx context.Context, task api.Task, creator api.User
|
|||||||
}
|
}
|
||||||
failed := []string{}
|
failed := []string{}
|
||||||
for i, sess := range sessions {
|
for i, sess := range sessions {
|
||||||
_, err = m.turn(ctx, sess, task, actor, teamPrompt(shared, members[i]), nil)
|
_, err = m.turn(ctx, sess, task, actor, teamPrompt(shared, members[i]), "", nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
failed = append(failed, fmt.Sprintf("%q: %v", members[i].Name, err))
|
failed = append(failed, fmt.Sprintf("%q: %v", members[i].Name, err))
|
||||||
}
|
}
|
||||||
@@ -189,7 +189,7 @@ func (m *Manager) post(ctx context.Context, task api.Task, from poster, text str
|
|||||||
func (m *Manager) deliverPost(ctx context.Context, task api.Task, s api.AgentSession, msg string) {
|
func (m *Manager) deliverPost(ctx context.Context, task api.Task, s api.AgentSession, msg string) {
|
||||||
started, err := m.store.SessionStarted(ctx, s.Id)
|
started, err := m.store.SessionStarted(ctx, s.Id)
|
||||||
if err == nil && started {
|
if err == nil && started {
|
||||||
_, _, err = m.Message(ctx, s.Id, boardActor, msg, "")
|
_, _, err = m.Message(ctx, s.Id, boardActor, msg, "", "")
|
||||||
}
|
}
|
||||||
if err == nil && started {
|
if err == nil && started {
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -166,7 +166,7 @@ func (m *Manager) terminalTypeTool(l *live) mcp.Tool {
|
|||||||
func (m *Manager) wakeOnPrompt(l *live) func(string, []byte) {
|
func (m *Manager) wakeOnPrompt(l *live) func(string, []byte) {
|
||||||
return func(exit string, out []byte) {
|
return func(exit string, out []byte) {
|
||||||
text := fmt.Sprintf("The line you left unsent in the terminal was run, and the shell is back at its prompt with exit status %s (130 means it was cancelled with Ctrl-C). The terminal's output since you typed it:\n\n%s", exit, terminal.Text(out))
|
text := fmt.Sprintf("The line you left unsent in the terminal was run, and the shell is back at its prompt with exit status %s (130 means it was cancelled with Ctrl-C). The terminal's output since you typed it:\n\n%s", exit, terminal.Text(out))
|
||||||
_, _, err := m.Message(context.Background(), l.id, "byre", text, "")
|
_, _, err := m.Message(context.Background(), l.id, "byre", text, "", "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
slog.Error("wake agent on terminal prompt", "session", l.id, "task", l.taskID, "err", err)
|
slog.Error("wake agent on terminal prompt", "session", l.id, "task", l.taskID, "err", err)
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -269,7 +269,7 @@ func (m *Manager) beginStage(ctx context.Context, task api.Task, project api.Pro
|
|||||||
s := workflow.Get(frozen.Definition, stage)
|
s := workflow.Get(frozen.Definition, stage)
|
||||||
message := stageMessage(frozen, stage, parents, project.DefaultBranch)
|
message := stageMessage(frozen, stage, parents, project.DefaultBranch)
|
||||||
if s.Agents == nil {
|
if s.Agents == nil {
|
||||||
return m.create(ctx, task, creator, "", message, s.Model, "byre", nil)
|
return m.create(ctx, task, creator, "", message, s.Model, "byre", "", nil)
|
||||||
}
|
}
|
||||||
err = m.startTeam(ctx, task, creator, message, *s.Agents, "byre")
|
err = m.startTeam(ctx, task, creator, message, *s.Agents, "byre")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -516,7 +516,7 @@ func (m *Manager) resumeStage(ctx context.Context, task api.Task, project api.Pr
|
|||||||
}
|
}
|
||||||
message = teamPrompt(message, mem)
|
message = teamPrompt(message, mem)
|
||||||
}
|
}
|
||||||
_, err = m.turn(ctx, sess, task, "byre", message, nil)
|
_, err = m.turn(ctx, sess, task, "byre", message, "", nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return ids, err
|
return ids, err
|
||||||
}
|
}
|
||||||
|
|||||||
+233
-226
@@ -1014,6 +1014,9 @@ type MessageCreate struct {
|
|||||||
// Model Model for this turn and later ones. When it differs from the session's model, the session switches before the message is sent.
|
// Model Model for this turn and later ones. When it differs from the session's model, the session switches before the message is sent.
|
||||||
Model *string `json:"model,omitempty"`
|
Model *string `json:"model,omitempty"`
|
||||||
|
|
||||||
|
// SendId The caller's id for this send, carried on the user.queued and user.message events the message records.
|
||||||
|
SendId *string `json:"send_id,omitempty"`
|
||||||
|
|
||||||
// Text May be empty when images are given.
|
// Text May be empty when images are given.
|
||||||
Text string `json:"text"`
|
Text string `json:"text"`
|
||||||
}
|
}
|
||||||
@@ -1179,6 +1182,9 @@ type SessionCreate struct {
|
|||||||
Images *[]string `json:"images,omitempty"`
|
Images *[]string `json:"images,omitempty"`
|
||||||
Model *string `json:"model,omitempty"`
|
Model *string `json:"model,omitempty"`
|
||||||
Prompt string `json:"prompt"`
|
Prompt string `json:"prompt"`
|
||||||
|
|
||||||
|
// SendId The caller's id for this send, carried on the user.message event the prompt records.
|
||||||
|
SendId *string `json:"send_id,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Snapshot defines model for Snapshot.
|
// Snapshot defines model for Snapshot.
|
||||||
@@ -4240,232 +4246,233 @@ var swaggerSpec = []string{
|
|||||||
"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",
|
||||||
}
|
}
|
||||||
|
|
||||||
// decodeSpec returns the embedded OpenAPI spec as raw JSON bytes,
|
// decodeSpec returns the embedded OpenAPI spec as raw JSON bytes,
|
||||||
|
|||||||
@@ -62,8 +62,12 @@ func (s *Server) MessageLead(w http.ResponseWriter, r *http.Request) {
|
|||||||
if req.Model != nil {
|
if req.Model != nil {
|
||||||
model = *req.Model
|
model = *req.Model
|
||||||
}
|
}
|
||||||
|
sendID := ""
|
||||||
|
if req.SendId != nil {
|
||||||
|
sendID = *req.SendId
|
||||||
|
}
|
||||||
|
|
||||||
sess, err := s.agents.MessageLead(r.Context(), u, req.Text, model, images)
|
sess, err := s.agents.MessageLead(r.Context(), u, req.Text, model, sendID, images)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
writeAgentError(w, err)
|
writeAgentError(w, err)
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -886,8 +886,12 @@ func (s *Server) CreateSession(w http.ResponseWriter, r *http.Request, taskID st
|
|||||||
if req.Model != nil {
|
if req.Model != nil {
|
||||||
model = *req.Model
|
model = *req.Model
|
||||||
}
|
}
|
||||||
|
sendID := ""
|
||||||
|
if req.SendId != nil {
|
||||||
|
sendID = *req.SendId
|
||||||
|
}
|
||||||
|
|
||||||
sess, err := s.agents.Create(r.Context(), t, u, req.Prompt, model, images...)
|
sess, err := s.agents.Create(r.Context(), t, u, req.Prompt, model, sendID, images...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
writeAgentError(w, err)
|
writeAgentError(w, err)
|
||||||
return
|
return
|
||||||
@@ -924,8 +928,12 @@ func (s *Server) SendMessage(w http.ResponseWriter, r *http.Request, sessionID s
|
|||||||
if req.Model != nil {
|
if req.Model != nil {
|
||||||
model = *req.Model
|
model = *req.Model
|
||||||
}
|
}
|
||||||
|
sendID := ""
|
||||||
|
if req.SendId != nil {
|
||||||
|
sendID = *req.SendId
|
||||||
|
}
|
||||||
|
|
||||||
sess, _, err := s.agents.Message(r.Context(), sessionID, actor(r), req.Text, model, images...)
|
sess, _, err := s.agents.Message(r.Context(), sessionID, actor(r), req.Text, model, sendID, images...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
writeAgentError(w, err)
|
writeAgentError(w, err)
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ type LeadInput struct {
|
|||||||
Actor string
|
Actor string
|
||||||
Text string
|
Text string
|
||||||
Images []string
|
Images []string
|
||||||
|
SendID string
|
||||||
CreatedAt time.Time
|
CreatedAt time.Time
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -33,14 +34,14 @@ func (s *Store) AddLeadInput(ctx context.Context, in LeadInput) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return s.write(ctx, func(tx *sql.Tx, emit emitFunc) error {
|
return s.write(ctx, func(tx *sql.Tx, emit emitFunc) error {
|
||||||
_, err := tx.ExecContext(ctx, `INSERT INTO lead_inputs (owner, kind, actor, text, images, created_at) VALUES (?, ?, ?, ?, ?, ?)`,
|
_, err := tx.ExecContext(ctx, `INSERT INTO lead_inputs (owner, kind, actor, text, images, send_id, created_at) VALUES (?, ?, ?, ?, ?, ?, ?)`,
|
||||||
in.Owner, string(in.Kind), in.Actor, in.Text, string(images), now())
|
in.Owner, string(in.Kind), in.Actor, in.Text, string(images), in.SendID, now())
|
||||||
return err
|
return err
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Store) LeadInputs(ctx context.Context, owner string) ([]LeadInput, error) {
|
func (s *Store) LeadInputs(ctx context.Context, owner string) ([]LeadInput, error) {
|
||||||
rows, err := s.db.QueryContext(ctx, `SELECT id, owner, kind, actor, text, images, created_at FROM lead_inputs WHERE owner = ? ORDER BY id`, owner)
|
rows, err := s.db.QueryContext(ctx, `SELECT id, owner, kind, actor, text, images, send_id, created_at FROM lead_inputs WHERE owner = ? ORDER BY id`, owner)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -52,7 +53,7 @@ func (s *Store) LeadInputs(ctx context.Context, owner string) ([]LeadInput, erro
|
|||||||
kind := ""
|
kind := ""
|
||||||
images := ""
|
images := ""
|
||||||
createdAt := ""
|
createdAt := ""
|
||||||
err = rows.Scan(&in.ID, &in.Owner, &kind, &in.Actor, &in.Text, &images, &createdAt)
|
err = rows.Scan(&in.ID, &in.Owner, &kind, &in.Actor, &in.Text, &images, &in.SendID, &createdAt)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -77,8 +78,8 @@ func (s *Store) RestoreLeadInputs(ctx context.Context, inputs []LeadInput) error
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
_, err = tx.ExecContext(ctx, `INSERT INTO lead_inputs (id, owner, kind, actor, text, images, created_at) VALUES (?, ?, ?, ?, ?, ?, ?)`,
|
_, err = tx.ExecContext(ctx, `INSERT INTO lead_inputs (id, owner, kind, actor, text, images, send_id, created_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?)`,
|
||||||
in.ID, in.Owner, string(in.Kind), in.Actor, in.Text, string(images), formatTime(in.CreatedAt))
|
in.ID, in.Owner, string(in.Kind), in.Actor, in.Text, string(images), in.SendID, formatTime(in.CreatedAt))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,33 @@
|
|||||||
|
ALTER TABLE task_inputs ADD COLUMN send_id TEXT NOT NULL DEFAULT '';
|
||||||
|
ALTER TABLE lead_inputs ADD COLUMN send_id TEXT NOT NULL DEFAULT '';
|
||||||
|
|
||||||
|
UPDATE task_inputs SET send_id = 'queued-' || id;
|
||||||
|
|
||||||
|
UPDATE events SET payload = json_set(payload, '$.send_id', 'queued-' || json_extract(payload, '$.id'))
|
||||||
|
WHERE kind = 'user.queued';
|
||||||
|
|
||||||
|
-- The n-th delivered queue entry of a task, sender and text pairs with the n-th later user.message of the same.
|
||||||
|
WITH q AS (
|
||||||
|
SELECT e.seq, e.task_id, e.actor, json_extract(e.payload, '$.text') AS text, json_extract(e.payload, '$.send_id') AS send_id,
|
||||||
|
row_number() OVER (PARTITION BY e.task_id, e.actor, json_extract(e.payload, '$.text') ORDER BY e.seq) AS n
|
||||||
|
FROM events AS e
|
||||||
|
WHERE e.kind = 'user.queued'
|
||||||
|
AND json_extract(e.payload, '$.id') NOT IN (SELECT id FROM task_inputs)
|
||||||
|
AND NOT EXISTS (
|
||||||
|
SELECT 1 FROM events AS u
|
||||||
|
WHERE u.kind = 'user.unqueued' AND u.task_id = e.task_id AND json_extract(u.payload, '$.id') = json_extract(e.payload, '$.id')
|
||||||
|
)
|
||||||
|
), first AS (
|
||||||
|
SELECT task_id, actor, text, min(seq) AS seq FROM q GROUP BY task_id, actor, text
|
||||||
|
), m AS (
|
||||||
|
SELECT e.seq, e.task_id, e.actor, json_extract(e.payload, '$.text') AS text,
|
||||||
|
row_number() OVER (PARTITION BY e.task_id, e.actor, json_extract(e.payload, '$.text') ORDER BY e.seq) AS n
|
||||||
|
FROM events AS e
|
||||||
|
JOIN first AS f ON f.task_id = e.task_id AND f.actor = e.actor AND f.text = json_extract(e.payload, '$.text') AND e.seq > f.seq
|
||||||
|
WHERE e.kind = 'user.message'
|
||||||
|
), pairs AS (
|
||||||
|
SELECT m.seq, q.send_id FROM m
|
||||||
|
JOIN q ON q.task_id = m.task_id AND q.actor = m.actor AND q.text = m.text AND q.n = m.n AND q.seq < m.seq
|
||||||
|
)
|
||||||
|
UPDATE events SET payload = json_set(payload, '$.send_id', (SELECT send_id FROM pairs WHERE pairs.seq = events.seq))
|
||||||
|
WHERE seq IN (SELECT seq FROM pairs);
|
||||||
@@ -15,6 +15,7 @@ type TaskInput struct {
|
|||||||
Actor string
|
Actor string
|
||||||
Text string
|
Text string
|
||||||
Images []string
|
Images []string
|
||||||
|
SendID string
|
||||||
CreatedAt time.Time
|
CreatedAt time.Time
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -23,13 +24,16 @@ func (s *Store) AddTaskInput(ctx context.Context, in TaskInput, projectID, sessi
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
payload := map[string]any{"text": in.Text}
|
if in.SendID == "" {
|
||||||
|
in.SendID = NewID()
|
||||||
|
}
|
||||||
|
payload := map[string]any{"text": in.Text, "send_id": in.SendID}
|
||||||
if len(in.Images) > 0 {
|
if len(in.Images) > 0 {
|
||||||
payload["images"] = in.Images
|
payload["images"] = in.Images
|
||||||
}
|
}
|
||||||
return s.write(ctx, func(tx *sql.Tx, emit emitFunc) error {
|
return s.write(ctx, func(tx *sql.Tx, emit emitFunc) error {
|
||||||
res, err := tx.ExecContext(ctx, `INSERT INTO task_inputs (task_id, member, actor, text, images, created_at) VALUES (?, ?, ?, ?, ?, ?)`,
|
res, err := tx.ExecContext(ctx, `INSERT INTO task_inputs (task_id, member, actor, text, images, send_id, created_at) VALUES (?, ?, ?, ?, ?, ?, ?)`,
|
||||||
in.TaskID, in.Member, in.Actor, in.Text, string(images), now())
|
in.TaskID, in.Member, in.Actor, in.Text, string(images), in.SendID, now())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -62,7 +66,7 @@ func (s *Store) WithdrawTaskInput(ctx context.Context, taskID string, id int64,
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *Store) TaskInputs(ctx context.Context, taskID, member string) ([]TaskInput, error) {
|
func (s *Store) TaskInputs(ctx context.Context, taskID, member string) ([]TaskInput, error) {
|
||||||
rows, err := s.db.QueryContext(ctx, `SELECT id, task_id, member, actor, text, images, created_at FROM task_inputs WHERE task_id = ? AND member = ? ORDER BY id`, taskID, member)
|
rows, err := s.db.QueryContext(ctx, `SELECT id, task_id, member, actor, text, images, send_id, created_at FROM task_inputs WHERE task_id = ? AND member = ? ORDER BY id`, taskID, member)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -73,7 +77,7 @@ func (s *Store) TaskInputs(ctx context.Context, taskID, member string) ([]TaskIn
|
|||||||
in := TaskInput{}
|
in := TaskInput{}
|
||||||
images := ""
|
images := ""
|
||||||
createdAt := ""
|
createdAt := ""
|
||||||
err = rows.Scan(&in.ID, &in.TaskID, &in.Member, &in.Actor, &in.Text, &images, &createdAt)
|
err = rows.Scan(&in.ID, &in.TaskID, &in.Member, &in.Actor, &in.Text, &images, &in.SendID, &createdAt)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -97,8 +101,8 @@ func (s *Store) RestoreTaskInputs(ctx context.Context, inputs []TaskInput) error
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
_, err = tx.ExecContext(ctx, `INSERT INTO task_inputs (id, task_id, member, actor, text, images, created_at) VALUES (?, ?, ?, ?, ?, ?, ?)`,
|
_, err = tx.ExecContext(ctx, `INSERT INTO task_inputs (id, task_id, member, actor, text, images, send_id, created_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?)`,
|
||||||
in.ID, in.TaskID, in.Member, in.Actor, in.Text, string(images), formatTime(in.CreatedAt))
|
in.ID, in.TaskID, in.Member, in.Actor, in.Text, string(images), in.SendID, formatTime(in.CreatedAt))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -60,7 +60,7 @@ final class ChatModel {
|
|||||||
case .task(let id): id.flatMap { store?.currentSession($0) }
|
case .task(let id): id.flatMap { store?.currentSession($0) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
builder = TranscriptBuilder(taskId: taskId, worktree: { [weak store] in
|
builder = TranscriptBuilder(taskId: taskId, local: store.sends(taskId ?? "lead"), worktree: { [weak store] in
|
||||||
taskId.flatMap { store?.findTask($0)?.worktreePath } ?? ""
|
taskId.flatMap { store?.findTask($0)?.worktreePath } ?? ""
|
||||||
}, running: {
|
}, running: {
|
||||||
session()?.state == .running
|
session()?.state == .running
|
||||||
@@ -458,11 +458,11 @@ final class ChatModel {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func send(sessionId: String?, text: String, images: [String]?, model: String?, firstModel: String) async throws {
|
func send(sessionId: String?, text: String, images: [String]?, model: String?, firstModel: String, sendID: String) async throws {
|
||||||
let api = store.api.client
|
let api = store.api.client
|
||||||
switch kind {
|
switch kind {
|
||||||
case .lead:
|
case .lead:
|
||||||
_ = try await api.messageLead(body: .json(.init(text: text, images: images, model: sessionId != nil ? model : firstModel))).accepted
|
_ = try await api.messageLead(body: .json(.init(text: text, images: images, model: sessionId != nil ? model : firstModel, sendId: sendID))).accepted
|
||||||
case .task(let id):
|
case .task(let id):
|
||||||
if team {
|
if team {
|
||||||
if images != nil {
|
if images != nil {
|
||||||
@@ -472,10 +472,10 @@ final class ChatModel {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
if let sessionId {
|
if let sessionId {
|
||||||
_ = try await api.sendMessage(path: .init(sessionId: sessionId), body: .json(.init(text: text, images: images, model: model))).accepted
|
_ = try await api.sendMessage(path: .init(sessionId: sessionId), body: .json(.init(text: text, images: images, model: model, sendId: sendID))).accepted
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
_ = try await api.createSession(path: .init(taskId: id ?? ""), body: .json(.init(prompt: text, model: firstModel, images: images))).created
|
_ = try await api.createSession(path: .init(taskId: id ?? ""), body: .json(.init(prompt: text, model: firstModel, images: images, sendId: sendID))).created
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -519,16 +519,21 @@ struct ComposerView: View {
|
|||||||
let imgs = images.take(draftKey)
|
let imgs = images.take(draftKey)
|
||||||
text = ""
|
text = ""
|
||||||
let sent = drafts.startSend(draftKey, value, imgs.count)
|
let sent = drafts.startSend(draftKey, value, imgs.count)
|
||||||
let cancel = chat.team ? {} : chat.builder.showPending(value, imgs)
|
let sendID = UUID().uuidString
|
||||||
|
let cancel = chat.team ? { true } : chat.builder.showPending(value, imgs, sendID)
|
||||||
Task {
|
Task {
|
||||||
do {
|
do {
|
||||||
let ids = try await uploadImages(store.api, imgs)
|
let ids = try await uploadImages(store.api, imgs)
|
||||||
try await chat.send(sessionId: m.live?.id, text: value, images: ids, model: m.selected != m.current ? m.selected : nil, firstModel: m.selected)
|
try await chat.send(sessionId: m.live?.id, text: value, images: ids, model: m.selected != m.current ? m.selected : nil, firstModel: m.selected, sendID: sendID)
|
||||||
modelChoices[m.modelKey] = nil
|
modelChoices[m.modelKey] = nil
|
||||||
pick = nil
|
pick = nil
|
||||||
drafts.endSend(sent, true)
|
drafts.endSend(sent, true)
|
||||||
} catch {
|
} catch {
|
||||||
cancel()
|
guard cancel() else {
|
||||||
|
log.notice("send failed after the server confirmed it: \(errorText(error), privacy: .public)")
|
||||||
|
drafts.endSend(sent, true)
|
||||||
|
return
|
||||||
|
}
|
||||||
drafts.endSend(sent, false)
|
drafts.endSend(sent, false)
|
||||||
text = drafts.composerText(draftKey)
|
text = drafts.composerText(draftKey)
|
||||||
images.restore(draftKey, imgs)
|
images.restore(draftKey, imgs)
|
||||||
|
|||||||
@@ -88,6 +88,7 @@ final class Store {
|
|||||||
@ObservationIgnored private var leadStream: EventStream?
|
@ObservationIgnored private var leadStream: EventStream?
|
||||||
@ObservationIgnored private var stopped = false
|
@ObservationIgnored private var stopped = false
|
||||||
@ObservationIgnored var changesMemory = ChangesMemory()
|
@ObservationIgnored var changesMemory = ChangesMemory()
|
||||||
|
@ObservationIgnored private var localSends: [String: LocalSends] = [:]
|
||||||
|
|
||||||
init(api: API, images: ImageDrafts, forwards: Forwards) {
|
init(api: API, images: ImageDrafts, forwards: Forwards) {
|
||||||
self.api = api
|
self.api = api
|
||||||
@@ -98,6 +99,22 @@ final class Store {
|
|||||||
remote = Remote(api: api)
|
remote = Remote(api: api)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func sends(_ key: String) -> LocalSends {
|
||||||
|
if let sends = localSends[key] {
|
||||||
|
return sends
|
||||||
|
}
|
||||||
|
let sends = LocalSends()
|
||||||
|
localSends[key] = sends
|
||||||
|
return sends
|
||||||
|
}
|
||||||
|
|
||||||
|
private func confirmSend(_ key: String, _ ev: ByreEvent) {
|
||||||
|
guard ev.kind == "user.queued" || ev.kind == "user.message", let sendID = str(ev.payload.value, "send_id") else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
localSends[key]?.items.removeAll { $0.sendID == sendID }
|
||||||
|
}
|
||||||
|
|
||||||
func stop() async {
|
func stop() async {
|
||||||
stopped = true
|
stopped = true
|
||||||
globalStream?.stop()
|
globalStream?.stop()
|
||||||
@@ -569,6 +586,7 @@ final class Store {
|
|||||||
leadMore = page.more
|
leadMore = page.more
|
||||||
leadStream = EventStream(api: api, after: page.events.last?.seq ?? 0, lead: true, onEvent: { [weak self] ev in
|
leadStream = EventStream(api: api, after: page.events.last?.seq ?? 0, lead: true, onEvent: { [weak self] ev in
|
||||||
self?.leadFeed.push(ev)
|
self?.leadFeed.push(ev)
|
||||||
|
self?.confirmSend("lead", ev)
|
||||||
}, onState: { _ in }, onReady: { [weak self] in
|
}, onState: { _ in }, onReady: { [weak self] in
|
||||||
self?.leadFeed.load(page.events)
|
self?.leadFeed.load(page.events)
|
||||||
})
|
})
|
||||||
@@ -603,6 +621,7 @@ final class Store {
|
|||||||
func handleEvent(_ ev: ByreEvent) {
|
func handleEvent(_ ev: ByreEvent) {
|
||||||
if let taskId = ev.taskId {
|
if let taskId = ev.taskId {
|
||||||
taskFeeds[taskId]?.push(ev)
|
taskFeeds[taskId]?.push(ev)
|
||||||
|
confirmSend(taskId, ev)
|
||||||
}
|
}
|
||||||
if ev.seq == 0 {
|
if ev.seq == 0 {
|
||||||
events.send(ev)
|
events.send(ev)
|
||||||
|
|||||||
@@ -151,9 +151,18 @@ struct PendingSend: Identifiable {
|
|||||||
let local: Bool
|
let local: Bool
|
||||||
let queued: Bool
|
let queued: Bool
|
||||||
let md: [MDBlock]
|
let md: [MDBlock]
|
||||||
|
let sendID: String
|
||||||
var inputId: Int? = nil
|
var inputId: Int? = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Sends not yet confirmed by the server, kept by the Store so they outlive the transcript showing them.
|
||||||
|
@MainActor
|
||||||
|
@Observable
|
||||||
|
final class LocalSends {
|
||||||
|
var items: [PendingSend] = []
|
||||||
|
@ObservationIgnored var ids = 0
|
||||||
|
}
|
||||||
|
|
||||||
@MainActor
|
@MainActor
|
||||||
private final class Run {
|
private final class Run {
|
||||||
let first: Row
|
let first: Row
|
||||||
@@ -202,7 +211,7 @@ private final class Ctx {
|
|||||||
@Observable
|
@Observable
|
||||||
final class TranscriptBuilder {
|
final class TranscriptBuilder {
|
||||||
let root = RowList()
|
let root = RowList()
|
||||||
private(set) var pending: [PendingSend] = []
|
private(set) var queued: [PendingSend] = []
|
||||||
private(set) var context: [String: Usage] = [:]
|
private(set) var context: [String: Usage] = [:]
|
||||||
private(set) var open = false
|
private(set) var open = false
|
||||||
private(set) var lastMergeAfterReview: Int?
|
private(set) var lastMergeAfterReview: Int?
|
||||||
@@ -216,14 +225,20 @@ final class TranscriptBuilder {
|
|||||||
@ObservationIgnored private let worktree: () -> String
|
@ObservationIgnored private let worktree: () -> String
|
||||||
@ObservationIgnored private let running: () -> Bool
|
@ObservationIgnored private let running: () -> Bool
|
||||||
@ObservationIgnored private var pendingIds = 0
|
@ObservationIgnored private var pendingIds = 0
|
||||||
|
@ObservationIgnored private let local: LocalSends
|
||||||
|
|
||||||
init(taskId: String?, worktree: @escaping () -> String, running: @escaping () -> Bool) {
|
init(taskId: String?, local: LocalSends, worktree: @escaping () -> String, running: @escaping () -> Bool) {
|
||||||
self.taskId = taskId
|
self.taskId = taskId
|
||||||
|
self.local = local
|
||||||
rowTask = taskId
|
rowTask = taskId
|
||||||
self.worktree = worktree
|
self.worktree = worktree
|
||||||
self.running = running
|
self.running = running
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var pending: [PendingSend] {
|
||||||
|
queued + local.items
|
||||||
|
}
|
||||||
|
|
||||||
func stop() {
|
func stop() {
|
||||||
dropStream()
|
dropStream()
|
||||||
}
|
}
|
||||||
@@ -245,25 +260,23 @@ final class TranscriptBuilder {
|
|||||||
root.rows.insert(contentsOf: box.rows, at: 0)
|
root.rows.insert(contentsOf: box.rows, at: 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
func showPending(_ text: String, _ images: [PendingImage]) -> () -> Void {
|
// The returned closure drops the send and says whether it was still unconfirmed.
|
||||||
pendingIds += 1
|
func showPending(_ text: String, _ images: [PendingImage], _ sendID: String) -> () -> Bool {
|
||||||
let entry = PendingSend(id: pendingIds, text: text, images: images, imageIds: [], local: true, queued: running(), md: trimmed(text).isEmpty ? [] : parseMarkdown(text))
|
local.ids -= 1
|
||||||
pending.append(entry)
|
let entry = PendingSend(id: local.ids, text: text, images: images, imageIds: [], local: true, queued: running(), md: trimmed(text).isEmpty ? [] : parseMarkdown(text), sendID: sendID)
|
||||||
|
local.items.append(entry)
|
||||||
ctx.run = nil
|
ctx.run = nil
|
||||||
return { [weak self] in
|
return { [local] in
|
||||||
self?.pending.removeAll { $0.id == entry.id }
|
guard let i = local.items.firstIndex(where: { $0.id == entry.id }) else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
local.items.remove(at: i)
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func withdrawn(_ inputId: Int) {
|
func withdrawn(_ inputId: Int) {
|
||||||
pending.removeAll { $0.inputId == inputId }
|
queued.removeAll { $0.inputId == inputId }
|
||||||
}
|
|
||||||
|
|
||||||
private func confirmPending(_ text: String, queued: Bool, local: Bool) {
|
|
||||||
let i = queued ? pending.firstIndex { !$0.local && $0.text == text } : nil
|
|
||||||
if let i = i ?? (local ? pending.firstIndex { $0.local && $0.text == text } : nil) {
|
|
||||||
pending.remove(at: i)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private func release() {
|
private func release() {
|
||||||
@@ -441,19 +454,18 @@ final class TranscriptBuilder {
|
|||||||
if sink == nil && ((ev.kind == "agent.message" && parent.isEmpty) || ev.kind == "turn.ended" || ev.kind == "user.message") {
|
if sink == nil && ((ev.kind == "agent.message" && parent.isEmpty) || ev.kind == "turn.ended" || ev.kind == "user.message") {
|
||||||
dropStream()
|
dropStream()
|
||||||
}
|
}
|
||||||
if sink == nil && own && ev.kind == "user.message" {
|
if sink == nil && own && ev.kind == "user.message", let sendID = str(p, "send_id") {
|
||||||
confirmPending(str(p, "text") ?? "", queued: true, local: ev.actor != "byre")
|
queued.removeAll { $0.sendID == sendID }
|
||||||
}
|
}
|
||||||
if sink == nil && own && (ev.kind == "task.merged" || ev.kind == "task.discarded") {
|
if sink == nil && own && (ev.kind == "task.merged" || ev.kind == "task.discarded") {
|
||||||
pending.removeAll { !$0.local }
|
queued.removeAll()
|
||||||
}
|
}
|
||||||
if ev.kind == "user.queued" {
|
if ev.kind == "user.queued" {
|
||||||
if sink == nil && own {
|
if sink == nil && own {
|
||||||
let text = str(p, "text") ?? ""
|
let text = str(p, "text") ?? ""
|
||||||
confirmPending(text, queued: false, local: true)
|
|
||||||
pendingIds += 1
|
pendingIds += 1
|
||||||
let images = (p["images"] as? [(any Sendable)?] ?? []).map { jsString($0) }
|
let images = (p["images"] as? [(any Sendable)?] ?? []).map { jsString($0) }
|
||||||
pending.append(PendingSend(id: pendingIds, text: text, images: [], imageIds: images, local: false, queued: true, md: trimmed(text).isEmpty ? [] : parseMarkdown(text), inputId: int(p, "id")))
|
queued.append(PendingSend(id: pendingIds, text: text, images: [], imageIds: images, local: false, queued: true, md: trimmed(text).isEmpty ? [] : parseMarkdown(text), sendID: str(p, "send_id") ?? "", inputId: int(p, "id")))
|
||||||
}
|
}
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user