bus

package
v0.1.6 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: May 7, 2026 License: Apache-2.0 Imports: 6 Imported by: 0

Documentation

Overview

Package bus provides a typed event bus for inter-component communication. Based on opencode's bus system.

Index

Constants

View Source
const (
	// Question events
	QuestionAsked    = "question.asked"
	QuestionReplied  = "question.replied"
	QuestionRejected = "question.rejected"

	// Permission events
	PermissionAsked    = "permission.asked"
	PermissionReplied  = "permission.replied"
	PermissionRejected = "permission.rejected"

	// Session events
	SessionCreated   = "session.created"
	SessionUpdated   = "session.updated"
	SessionCompacted = "session.compacted"

	// Aliases for code clarity
	EventSessionUpdated   = SessionUpdated
	EventSessionCompacted = SessionCompacted

	// Message events
	MessageCreated = "message.created"
	MessageUpdated = "message.updated"

	// Tool events
	ToolStarted   = "tool.started"
	ToolCompleted = "tool.completed"

	// Stream events (published by runner during LLM streaming)
	StreamTextDelta    = "stream.text_delta"
	StreamToolCall     = "stream.tool_call"
	StreamToolResult   = "stream.tool_result"
	StreamStepComplete = "stream.step_complete"
)

Event type constants - matches opencode's event types.

Variables

This section is empty.

Functions

func WithBus

func WithBus(ctx context.Context, b *Bus) context.Context

WithBus returns a context with the given Bus.

func WithPermissionManager

func WithPermissionManager(ctx context.Context, pm *PermissionManager) context.Context

WithPermissionManager returns a context with the given PermissionManager.

func WithQuestionManager

func WithQuestionManager(ctx context.Context, qm *QuestionManager) context.Context

WithQuestionManager returns a context with the given QuestionManager.

Types

type AskInput

type AskInput struct {
	SessionID string
	Questions []QuestionInfo
	Tool      *ToolContext
}

AskInput is the input for asking questions.

type Bus

type Bus struct {
	// contains filtered or unexported fields
}

Bus is the main event bus for publishing and subscribing to events.

func BusFromContext

func BusFromContext(ctx context.Context) *Bus

BusFromContext extracts a Bus from context. Panics if not set — the runner must inject it via WithBus.

func New

func New() *Bus

New creates a new Bus instance.

func (*Bus) Once

func (b *Bus) Once(eventType string, callback func(Event) bool)

Once subscribes to an event and automatically unsubscribes after the callback returns true.

func (*Bus) Publish

func (b *Bus) Publish(eventType string, properties any)

Publish sends an event to all subscribers of that event type and wildcard subscribers.

func (*Bus) Subscribe

func (b *Bus) Subscribe(eventType string, callback Subscription) func()

Subscribe registers a callback for a specific event type. Returns an unsubscribe function.

func (*Bus) SubscribeAll

func (b *Bus) SubscribeAll(callback Subscription) func()

SubscribeAll registers a callback for all events (wildcard).

type ErrPermissionNeeded

type ErrPermissionNeeded struct {
	Permission string         `json:"permission"`
	Patterns   []string       `json:"patterns"`
	ToolCallID string         `json:"toolCallID"`
	Metadata   map[string]any `json:"metadata,omitempty"`
}

ErrPermissionNeeded is returned when no permission rule matches. Implements FatalToolError so the executor propagates it up.

func (*ErrPermissionNeeded) Error

func (e *ErrPermissionNeeded) Error() string

func (*ErrPermissionNeeded) FatalToolError

func (e *ErrPermissionNeeded) FatalToolError() bool

type ErrQuestionNeeded

type ErrQuestionNeeded struct {
	Questions  []QuestionInfo `json:"questions"`
	ToolCallID string         `json:"toolCallID"`
}

ErrQuestionNeeded is returned when no pre-loaded answer is available.

func (*ErrQuestionNeeded) Error

func (e *ErrQuestionNeeded) Error() string

func (*ErrQuestionNeeded) FatalToolError

func (e *ErrQuestionNeeded) FatalToolError() bool

type Event

type Event struct {
	Type       string
	Properties any
}

Event represents a published event with type and properties.

type PermissionAskedPayload

type PermissionAskedPayload struct {
	ID         string         `json:"id"`
	SessionID  string         `json:"sessionID"`
	Permission string         `json:"permission"`
	Patterns   []string       `json:"patterns"`
	Always     []string       `json:"always,omitempty"`
	Metadata   map[string]any `json:"metadata,omitempty"`
	ToolCallID string         `json:"toolCallId,omitempty"`
}

PermissionAskedPayload is the payload for permission.asked events.

type PermissionDeniedError

type PermissionDeniedError struct {
	Permission string
	Pattern    string
	Reason     string
}

PermissionDeniedError is returned when permission is denied by a rule.

func (*PermissionDeniedError) Error

func (e *PermissionDeniedError) Error() string

type PermissionManager

type PermissionManager struct {
	// contains filtered or unexported fields
}

PermissionManager handles permission evaluation via rules. Non-blocking: Ask() returns immediately with either nil (allowed), PermissionDeniedError (denied by rule), or ErrPermissionNeeded (no rule matches).

func NewPermissionManager

func NewPermissionManager(b *Bus) *PermissionManager

NewPermissionManager creates a new permission manager. The bus parameter is required — there is no global bus.

func PermissionManagerFromContext

func PermissionManagerFromContext(ctx context.Context) *PermissionManager

PermissionManagerFromContext extracts a PermissionManager from context. Panics if not set — the runner must inject it via WithPermissionManager.

func (*PermissionManager) AddRule

func (pm *PermissionManager) AddRule(rule PermissionRule)

AddRule adds a permission rule.

func (*PermissionManager) Ask

Ask evaluates permission rules and returns immediately. Returns nil if all patterns are allowed, PermissionDeniedError if denied, or ErrPermissionNeeded if no rule matches (caller should suspend).

func (*PermissionManager) SetRules

func (pm *PermissionManager) SetRules(rules []PermissionRule)

SetRules sets the permission rules.

type PermissionRepliedPayload

type PermissionRepliedPayload struct {
	SessionID string `json:"sessionID"`
	RequestID string `json:"requestID"`
	Response  string `json:"response"` // "once", "always", "reject"
}

PermissionRepliedPayload is the payload for permission.replied events.

type PermissionRequest

type PermissionRequest struct {
	ID         string
	SessionID  string
	Permission string         // Permission type (e.g., "edit", "bash", "read")
	Patterns   []string       // Patterns being accessed (e.g., file paths)
	Always     []string       // Patterns to approve for "always" response
	Metadata   map[string]any // Additional metadata (e.g., diff for edits)
	ToolCallID string         // The tool call ID
}

PermissionRequest represents a permission request.

type PermissionRule

type PermissionRule struct {
	Permission string `json:"permission"` // Permission type to match (supports wildcards)
	Pattern    string `json:"pattern"`    // Pattern to match (supports wildcards)
	Action     string `json:"action"`     // "allow", "deny", or "ask"
}

PermissionRule defines an auto-approve or auto-deny rule.

type QuestionAskedPayload

type QuestionAskedPayload struct {
	ID        string         `json:"id"`
	SessionID string         `json:"sessionID"`
	Questions []QuestionInfo `json:"questions"`
	Tool      *ToolContext   `json:"tool,omitempty"`
}

QuestionAskedPayload is the payload for question.asked events.

type QuestionInfo

type QuestionInfo struct {
	Question string           `json:"question"`
	Header   string           `json:"header"`
	Options  []QuestionOption `json:"options"`
	Multiple bool             `json:"multiple,omitempty"`
	Custom   bool             `json:"custom,omitempty"`
}

QuestionInfo represents a single question.

type QuestionManager

type QuestionManager struct {
	// contains filtered or unexported fields
}

QuestionManager handles question ask/reply lifecycle. Non-blocking: Ask() checks pre-loaded answers or auto-answer mode, returning ErrQuestionNeeded if no answer is available.

func NewQuestionManager

func NewQuestionManager(b *Bus) *QuestionManager

NewQuestionManager creates a new question manager. The bus parameter is required — there is no global bus.

func QuestionManagerFromContext

func QuestionManagerFromContext(ctx context.Context) *QuestionManager

QuestionManagerFromContext extracts a QuestionManager from context. Panics if not set — the runner must inject it via WithQuestionManager.

func (*QuestionManager) Ask

func (qm *QuestionManager) Ask(ctx context.Context, input AskInput) ([][]string, error)

Ask checks pre-loaded answers or auto-answer mode. Returns ErrQuestionNeeded if no answer is available (caller should suspend).

func (*QuestionManager) PushAnswers

func (qm *QuestionManager) PushAnswers(answers [][]string)

PushAnswers adds a batch of pre-resolved answers to the FIFO queue.

func (*QuestionManager) PushScriptedAnswers

func (qm *QuestionManager) PushScriptedAnswers(answers []string)

PushScriptedAnswers adds raw string answers that get resolved at ask time. Numeric strings (e.g. "6") are resolved to the corresponding 1-based option label. Non-numeric strings are used as-is (custom answers).

func (*QuestionManager) SetAutoAnswer

func (qm *QuestionManager) SetAutoAnswer(auto bool)

SetAutoAnswer enables or disables auto-answer mode. When enabled, empty answers are returned when the queue is exhausted.

type QuestionOption

type QuestionOption struct {
	Label       string `json:"label"`
	Description string `json:"description"`
}

QuestionOption represents a choice option.

type QuestionRejectedPayload

type QuestionRejectedPayload struct {
	SessionID string `json:"sessionID"`
	RequestID string `json:"requestID"`
}

QuestionRejectedPayload is the payload for question.rejected events.

type QuestionRepliedPayload

type QuestionRepliedPayload struct {
	SessionID string     `json:"sessionID"`
	RequestID string     `json:"requestID"`
	Answers   [][]string `json:"answers"`
}

QuestionRepliedPayload is the payload for question.replied events.

type SessionCompactedPayload

type SessionCompactedPayload struct {
	SessionID string `json:"sessionID"`
}

SessionCompactedPayload is the payload for session.compacted events.

type SessionUpdatedPayload

type SessionUpdatedPayload struct {
	SessionID string `json:"sessionID"`
}

SessionUpdatedPayload is the payload for session.updated events.

type Subscription

type Subscription func(Event)

Subscription is a callback that receives events.

type ToolContext

type ToolContext struct {
	MessageID string `json:"messageID"`
	CallID    string `json:"callID"`
}

ToolContext provides context about which tool call triggered the question.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL