Add ask_human and the inbox with batched responses and delivery to agents
This commit is contained in:
@@ -32,15 +32,18 @@ type Manager struct {
|
||||
mu sync.Mutex
|
||||
live map[string]*live
|
||||
closing bool
|
||||
|
||||
deliverMu sync.Mutex
|
||||
}
|
||||
|
||||
type live struct {
|
||||
sess *claude.Session
|
||||
gate *report.Gate
|
||||
id string
|
||||
taskID string
|
||||
projectID string
|
||||
stopping bool
|
||||
sess *claude.Session
|
||||
gate *report.Gate
|
||||
id string
|
||||
taskID string
|
||||
projectID string
|
||||
stopping bool
|
||||
interrupted bool
|
||||
}
|
||||
|
||||
func New(st *store.Store) *Manager {
|
||||
@@ -119,6 +122,10 @@ func (m *Manager) Interrupt(ctx context.Context, sessionID string) (api.AgentSes
|
||||
return api.AgentSession{}, fmt.Errorf("%w: session %s has no turn running", ErrConflict, sess.Id)
|
||||
}
|
||||
|
||||
m.mu.Lock()
|
||||
l.interrupted = true
|
||||
m.mu.Unlock()
|
||||
|
||||
ictx, cancel := context.WithTimeout(ctx, 10*time.Second)
|
||||
defer cancel()
|
||||
|
||||
@@ -219,7 +226,10 @@ func (m *Manager) turn(ctx context.Context, sess api.AgentSession, task api.Task
|
||||
return api.AgentSession{}, err
|
||||
}
|
||||
|
||||
err = m.store.SetSessionState(ctx, sess.Id, api.AgentSessionStateRunning, actor, nil)
|
||||
err = m.store.TransitionSession(ctx, sess.Id, api.AgentSessionStateIdle, api.AgentSessionStateRunning, actor)
|
||||
if errors.Is(err, store.ErrStateChanged) {
|
||||
return api.AgentSession{}, fmt.Errorf("%w: %w", ErrConflict, err)
|
||||
}
|
||||
if err != nil {
|
||||
return api.AgentSession{}, err
|
||||
}
|
||||
@@ -238,6 +248,10 @@ func (m *Manager) message(ctx context.Context, l *live, actor, text string) erro
|
||||
return err
|
||||
}
|
||||
|
||||
m.mu.Lock()
|
||||
l.interrupted = false
|
||||
m.mu.Unlock()
|
||||
|
||||
l.gate.Reset()
|
||||
return l.sess.Send(text)
|
||||
}
|
||||
@@ -266,6 +280,7 @@ func (m *Manager) ensureLive(ctx context.Context, sess api.AgentSession, task ap
|
||||
if err != nil {
|
||||
slog.Error("record report", "session", l.id, "err", err)
|
||||
}
|
||||
m.fileReport(l, r)
|
||||
}}
|
||||
|
||||
opts := claude.Options{
|
||||
@@ -273,7 +288,7 @@ func (m *Manager) ensureLive(ctx context.Context, sess api.AgentSession, task ap
|
||||
Model: sess.Model,
|
||||
GitName: creator.GitName,
|
||||
GitEmail: creator.GitEmail,
|
||||
MCPServers: []*mcp.Server{{Name: "byre", Version: "0.0.0", Tools: []mcp.Tool{l.gate.Tool()}}},
|
||||
MCPServers: []*mcp.Server{{Name: "byre", Version: "0.0.0", Tools: []mcp.Tool{l.gate.Tool(), m.askTool(l)}}},
|
||||
Hooks: map[string][]claude.HookMatcher{
|
||||
"Stop": {{Func: l.gate.StopHook}},
|
||||
},
|
||||
@@ -330,6 +345,7 @@ func (m *Manager) pump(l *live) {
|
||||
if err != nil {
|
||||
slog.Error("record session exit", "session", l.id, "err", err)
|
||||
}
|
||||
m.fileError(l, "The agent process exited mid-turn. Send a message to resume it.")
|
||||
}
|
||||
|
||||
func (m *Manager) handle(ctx context.Context, l *live, ev claude.Event) error {
|
||||
@@ -380,7 +396,27 @@ func (m *Manager) handle(ctx context.Context, l *live, ev claude.Event) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return m.store.SetSessionState(ctx, l.id, api.AgentSessionStateIdle, agentActor, nil)
|
||||
|
||||
m.mu.Lock()
|
||||
interrupted := l.interrupted
|
||||
m.mu.Unlock()
|
||||
|
||||
if res.IsError && !interrupted {
|
||||
m.fileError(l, fmt.Sprintf("The turn ended with an error (%s): %s", res.Subtype, res.Result))
|
||||
}
|
||||
|
||||
err = m.store.SetSessionState(ctx, l.id, api.AgentSessionStateIdle, agentActor, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
go func() {
|
||||
err := m.Deliver(context.Background(), l.id)
|
||||
if err != nil {
|
||||
slog.Error("deliver inbox responses", "session", l.id, "err", err)
|
||||
}
|
||||
}()
|
||||
return nil
|
||||
|
||||
case "system":
|
||||
if ev.Subtype == "init" {
|
||||
|
||||
Reference in New Issue
Block a user