Fork tasks from snapshots; snapshot only when text follows tool calls
This commit is contained in:
+50
-15
@@ -86,6 +86,8 @@ type live struct {
|
||||
wantCompact bool
|
||||
rateLimited map[string]any
|
||||
|
||||
toolsSinceText bool
|
||||
|
||||
contextModel string
|
||||
contextUsed int
|
||||
contextWindow int
|
||||
@@ -608,6 +610,7 @@ func (m *Manager) message(ctx context.Context, l *live, actor, text string, imag
|
||||
l.wantCompact = false
|
||||
l.compacting = compacting
|
||||
l.rateLimited = nil
|
||||
l.toolsSinceText = false
|
||||
m.mu.Unlock()
|
||||
|
||||
return l.sess.Send(text, imgs...)
|
||||
@@ -643,7 +646,10 @@ func (m *Manager) ensureLive(ctx context.Context, sess api.AgentSession, task ap
|
||||
owner: sess.Owner,
|
||||
}
|
||||
|
||||
g := taskGuidance(task)
|
||||
g, err := m.taskGuidance(ctx, task)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
tools := []mcp.Tool{m.askTool(l), m.cancelQuestionTool(l), m.createTaskTool(l), m.setMergeTitleTool(l)}
|
||||
hooks := map[string][]claude.HookMatcher{
|
||||
"UserPromptSubmit": {{Func: remindGuidance(g)}},
|
||||
@@ -675,9 +681,15 @@ func (m *Manager) ensureLive(ctx context.Context, sess api.AgentSession, task ap
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if started {
|
||||
switch {
|
||||
case started:
|
||||
opts.Resume = sess.VendorSessionId
|
||||
} else {
|
||||
case sess.ForkedFrom != nil:
|
||||
opts.SessionID = sess.VendorSessionId
|
||||
opts.Resume = *sess.ForkedFrom
|
||||
opts.ResumeAt = *sess.ForkedAt
|
||||
opts.ForkSession = true
|
||||
default:
|
||||
opts.SessionID = sess.VendorSessionId
|
||||
}
|
||||
|
||||
@@ -691,9 +703,6 @@ func (m *Manager) ensureLive(ctx context.Context, sess api.AgentSession, task ap
|
||||
|
||||
m.live[sess.Id] = l
|
||||
go m.pump(l)
|
||||
if l.owner == "" {
|
||||
go m.snapshotTimer(l)
|
||||
}
|
||||
return l, nil
|
||||
}
|
||||
|
||||
@@ -764,11 +773,29 @@ func (m *Manager) handle(ctx context.Context, l *live, ev claude.Event) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
uuid, err := ev.UUID()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, b := range blocks {
|
||||
err = m.handleAssistantBlock(ctx, l, b, parent)
|
||||
err = m.handleAssistantBlock(ctx, l, b, parent, uuid)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if parent != "" || l.owner != "" {
|
||||
continue
|
||||
}
|
||||
switch {
|
||||
case b.Type == "tool_use":
|
||||
l.toolsSinceText = true
|
||||
case b.Type == "text" && l.toolsSinceText:
|
||||
l.toolsSinceText = false
|
||||
seq, err := m.store.LastTaskSeq(ctx, l.taskID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
m.snapshotSoon(l, api.SnapshotTriggerMessage, seq)
|
||||
}
|
||||
}
|
||||
if parent != "" {
|
||||
return nil
|
||||
@@ -792,6 +819,10 @@ func (m *Manager) handle(ctx context.Context, l *live, ev claude.Event) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
uuid, err := ev.UUID()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, b := range blocks {
|
||||
if b.Type != "tool_result" {
|
||||
continue
|
||||
@@ -804,7 +835,7 @@ func (m *Manager) handle(ctx context.Context, l *live, ev claude.Event) error {
|
||||
"tool_use_id": b.ToolUseID,
|
||||
"is_error": b.IsError,
|
||||
"content": content,
|
||||
}, parent))
|
||||
}, parent, uuid))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -838,7 +869,7 @@ func (m *Manager) handle(ctx context.Context, l *live, ev claude.Event) error {
|
||||
if sha != "" {
|
||||
payload["commit"] = sha
|
||||
}
|
||||
m.snapshotSoon(l, api.SnapshotTriggerTurnEnd)
|
||||
m.snapshotSoon(l, api.SnapshotTriggerTurnEnd, 0)
|
||||
switch {
|
||||
case handoff:
|
||||
payload["handoff"] = true
|
||||
@@ -957,16 +988,16 @@ func (m *Manager) updateContext(ctx context.Context, l *live, used, window int)
|
||||
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, parent string) error {
|
||||
func (m *Manager) handleAssistantBlock(ctx context.Context, l *live, b claude.Block, parent, uuid string) error {
|
||||
switch b.Type {
|
||||
case "text":
|
||||
return m.emit(ctx, l, agentActor, "agent.message", withParent(map[string]any{"text": b.Text}, parent))
|
||||
return m.emit(ctx, l, agentActor, "agent.message", withParent(map[string]any{"text": b.Text}, parent, uuid))
|
||||
|
||||
case "thinking":
|
||||
if b.Thinking == "" {
|
||||
return nil
|
||||
}
|
||||
return m.emit(ctx, l, agentActor, "agent.thinking", withParent(map[string]any{"text": b.Thinking}, parent))
|
||||
return m.emit(ctx, l, agentActor, "agent.thinking", withParent(map[string]any{"text": b.Thinking}, parent, uuid))
|
||||
|
||||
case "tool_use":
|
||||
input, err := decode(b.Input)
|
||||
@@ -977,15 +1008,19 @@ func (m *Manager) handleAssistantBlock(ctx context.Context, l *live, b claude.Bl
|
||||
"id": b.ID,
|
||||
"name": b.Name,
|
||||
"input": input,
|
||||
}, parent))
|
||||
}, parent, uuid))
|
||||
}
|
||||
|
||||
return m.emit(ctx, l, agentActor, "agent.vendor", withParent(store.ToPayload(b), parent))
|
||||
return m.emit(ctx, l, agentActor, "agent.vendor", withParent(store.ToPayload(b), parent, uuid))
|
||||
}
|
||||
|
||||
func withParent(payload map[string]any, parent string) map[string]any {
|
||||
func withParent(payload map[string]any, parent, uuid string) map[string]any {
|
||||
if parent != "" {
|
||||
payload["parent_tool_use_id"] = parent
|
||||
return payload
|
||||
}
|
||||
if uuid != "" {
|
||||
payload["vendor_uuid"] = uuid
|
||||
}
|
||||
return payload
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user