mirror of
https://github.com/sipeed/picoclaw.git
synced 2026-06-12 18:08:54 +00:00
fix(agent): fix subturn panic result, hard abort rollback, and drain bus exit
- spawnSubTurn: set result=nil on panic instead of constructing a non-nil ToolResult - HardAbort: roll back session history to initialHistoryLength after Finish() - drainBusToSteering: switch to non-blocking reads after first message so function returns promptly when the inbound channel is empty - remove obsolete documentation files
This commit is contained in:
+27
-9
@@ -509,21 +509,39 @@ func (al *AgentLoop) Run(ctx context.Context) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// drainBusToSteering continuously consumes inbound messages and redirects
|
||||
// messages from the active scope into the steering queue. Messages from other
|
||||
// scopes are requeued so they can be processed normally after the active turn.
|
||||
// drainBusToSteering consumes inbound messages and redirects messages from the
|
||||
// active scope into the steering queue. Messages from other scopes are requeued
|
||||
// so they can be processed normally after the active turn. It drains all
|
||||
// immediately available messages, blocking for the first one until ctx is done.
|
||||
func (al *AgentLoop) drainBusToSteering(ctx context.Context, activeScope, activeAgentID string) {
|
||||
blocking := true
|
||||
for {
|
||||
var msg bus.InboundMessage
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return
|
||||
case m, ok := <-al.bus.InboundChan():
|
||||
if !ok {
|
||||
|
||||
if blocking {
|
||||
// Block waiting for the first available message or ctx cancellation.
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return
|
||||
case m, ok := <-al.bus.InboundChan():
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
msg = m
|
||||
}
|
||||
} else {
|
||||
// Non-blocking: drain any remaining queued messages, return when empty.
|
||||
select {
|
||||
case m, ok := <-al.bus.InboundChan():
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
msg = m
|
||||
default:
|
||||
return
|
||||
}
|
||||
msg = m
|
||||
}
|
||||
blocking = false
|
||||
|
||||
msgScope, _, scopeOK := al.resolveSteeringTarget(msg)
|
||||
if !scopeOK || msgScope != activeScope {
|
||||
|
||||
@@ -460,6 +460,14 @@ func (al *AgentLoop) HardAbort(sessionKey string) error {
|
||||
// Use isHardAbort=true for hard abort to immediately cancel all children.
|
||||
ts.Finish(true)
|
||||
|
||||
// Roll back session history to the state before the turn started.
|
||||
if ts.session != nil {
|
||||
history := ts.session.GetHistory(sessionKey)
|
||||
if ts.initialHistoryLength < len(history) {
|
||||
ts.session.SetHistory(sessionKey, history[:ts.initialHistoryLength])
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -428,19 +428,12 @@ func spawnSubTurn(
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
err = fmt.Errorf("subturn panicked: %v", r)
|
||||
result = nil
|
||||
logger.ErrorCF("subturn", "SubTurn panicked", map[string]any{
|
||||
"child_id": childID,
|
||||
"parent_id": parentTS.turnID,
|
||||
"panic": r,
|
||||
})
|
||||
|
||||
// Ensure result is not nil to prevent panic during event emission
|
||||
if result == nil {
|
||||
result = &tools.ToolResult{
|
||||
Err: err,
|
||||
ForLLM: fmt.Sprintf("SubTurn panicked: %v", r),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Result Delivery Strategy (Async vs Sync)
|
||||
|
||||
Reference in New Issue
Block a user