Improve the transcript: tool grouping, report auto-ack, context meter, spinner, Enter to send
This commit is contained in:
@@ -170,12 +170,14 @@ func (m *Manager) validateAsk(ctx context.Context, l *live, args askArgs) error
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *Manager) fileReport(l *live, r report.Report) {
|
||||
func (m *Manager) fileReport(l *live, r report.Report) string {
|
||||
id := store.NewID()
|
||||
err := m.store.CreateInboxItem(context.Background(), m.newItem(l, id, id, api.Report, api.Fyi, false, store.ToPayload(r)))
|
||||
if err != nil {
|
||||
slog.Error("file report", "session", l.id, "err", err)
|
||||
return ""
|
||||
}
|
||||
return id
|
||||
}
|
||||
|
||||
func (m *Manager) fileError(l *live, msg string) {
|
||||
|
||||
@@ -59,6 +59,10 @@ type live struct {
|
||||
stopping bool
|
||||
interrupted bool
|
||||
rateLimited map[string]any
|
||||
|
||||
contextModel string
|
||||
contextUsed int
|
||||
contextWindow int
|
||||
}
|
||||
|
||||
func New(st *store.Store, limits Limits) *Manager {
|
||||
@@ -455,11 +459,12 @@ func (m *Manager) ensureLive(ctx context.Context, sess api.AgentSession, task ap
|
||||
|
||||
l = &live{id: sess.Id, vendor: sess.Vendor, taskID: task.Id, projectID: task.ProjectId}
|
||||
l.gate = &report.Gate{OnReport: func(r report.Report) {
|
||||
err := m.emit(context.Background(), l, agentActor, "agent.report", store.ToPayload(r))
|
||||
payload := store.ToPayload(r)
|
||||
payload["inbox_item_id"] = m.fileReport(l, r)
|
||||
err := m.emit(context.Background(), l, agentActor, "agent.report", payload)
|
||||
if err != nil {
|
||||
slog.Error("record report", "session", l.id, "err", err)
|
||||
}
|
||||
m.fileReport(l, r)
|
||||
}}
|
||||
|
||||
opts := claude.Options{
|
||||
@@ -549,7 +554,15 @@ func (m *Manager) handle(ctx context.Context, l *live, ev claude.Event) error {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
usage, err := ev.ContextUsage()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if usage.Tokens == 0 {
|
||||
return nil
|
||||
}
|
||||
l.contextModel = usage.Model
|
||||
return m.updateContext(ctx, l, usage.Tokens, l.contextWindow)
|
||||
|
||||
case "user":
|
||||
blocks, err := ev.Blocks()
|
||||
@@ -585,6 +598,14 @@ func (m *Manager) handle(ctx context.Context, l *live, ev claude.Event) error {
|
||||
return err
|
||||
}
|
||||
|
||||
window := res.ModelUsage[l.contextModel].ContextWindow
|
||||
if window > 0 {
|
||||
err = m.updateContext(ctx, l, l.contextUsed, window)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
m.mu.Lock()
|
||||
interrupted := l.interrupted
|
||||
rateLimited := l.rateLimited
|
||||
@@ -644,6 +665,15 @@ func (m *Manager) handle(ctx context.Context, l *live, ev claude.Event) error {
|
||||
return m.emit(ctx, l, agentActor, "agent.vendor", raw)
|
||||
}
|
||||
|
||||
func (m *Manager) updateContext(ctx context.Context, l *live, used, window int) error {
|
||||
if used == l.contextUsed && window == l.contextWindow {
|
||||
return nil
|
||||
}
|
||||
l.contextUsed = used
|
||||
l.contextWindow = window
|
||||
return m.emit(ctx, l, agentActor, "session.context", map[string]any{"model": l.contextModel, "used": used, "window": window})
|
||||
}
|
||||
|
||||
func (m *Manager) handleAssistantBlock(ctx context.Context, l *live, b claude.Block) error {
|
||||
switch b.Type {
|
||||
case "text":
|
||||
|
||||
@@ -156,12 +156,22 @@ func TestTurnAndFollowUp(t *testing.T) {
|
||||
}
|
||||
|
||||
events := e.waitIdle()
|
||||
for _, kind := range []string{"session.created", "user.message", "agent.tool_call", "agent.tool_result", "agent.report", "turn.ended"} {
|
||||
for _, kind := range []string{"session.created", "user.message", "agent.tool_call", "agent.tool_result", "agent.report", "turn.ended", "session.context"} {
|
||||
if !has(events, kind) {
|
||||
t.Fatalf("missing %s in %v", kind, kinds(events))
|
||||
}
|
||||
}
|
||||
|
||||
window := 0
|
||||
for _, ev := range events {
|
||||
if ev.Kind == "session.context" {
|
||||
window = ev.Payload["window"].(int)
|
||||
}
|
||||
}
|
||||
if window <= 0 {
|
||||
t.Fatalf("no context window reported after the turn ended")
|
||||
}
|
||||
|
||||
log := gitOut(t, e.task.WorktreePath, "log", "-1", "--format=%an <%ae>")
|
||||
if log != gitName+" <"+gitEmail+">" {
|
||||
t.Fatalf("agent committed as %q", log)
|
||||
|
||||
Reference in New Issue
Block a user