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 }