iop/packages/go/agentruntime/emitter.go

56 lines
1.3 KiB
Go

package agentruntime
import (
"context"
"sync"
)
// IsTerminalEvent reports whether an event closes one runtime execution.
func IsTerminalEvent(eventType EventType) bool {
switch eventType {
case EventTypeComplete, EventTypeError, EventTypeCancelled:
return true
default:
return false
}
}
// TerminalEmitter enforces at-most-one terminal event while preserving the
// wrapped sink's ordering. Duplicate terminal and post-terminal events are
// ignored so a host cannot expose more than one terminal boundary.
type TerminalEmitter struct {
sink EventSink
emitMu sync.Mutex
mu sync.Mutex
terminal bool
}
// NewTerminalEmitter wraps sink with terminal exactly-once protection.
func NewTerminalEmitter(sink EventSink) *TerminalEmitter {
return &TerminalEmitter{sink: sink}
}
// Emit forwards the event unless the terminal boundary was already observed.
func (e *TerminalEmitter) Emit(ctx context.Context, event RuntimeEvent) error {
e.emitMu.Lock()
defer e.emitMu.Unlock()
e.mu.Lock()
if e.terminal {
e.mu.Unlock()
return nil
}
if IsTerminalEvent(event.Type) {
e.terminal = true
}
e.mu.Unlock()
return e.sink.Emit(ctx, event)
}
// TerminalObserved reports whether a terminal event was accepted.
func (e *TerminalEmitter) TerminalObserved() bool {
e.mu.Lock()
defer e.mu.Unlock()
return e.terminal
}