Help us shape the future of Go! Take the 2026 Go Developer Survey

handler

package
v0.1.0 Latest Latest
Warning

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

Go to latest
Published: Aug 30, 2026 License: MIT Imports: 19 Imported by: 0

Documentation

Overview

Package handler turns inbound Zulip events into ACP prompts and streams the agent's answer back into the originating topic.

The unit of conversation is a Zulip TOPIC, scoped by its channel. The topic string itself is never used as a key — see internal/journal for why — so everything here works in terms of the journal's stable conv-id.

Index

Constants

View Source
const InterruptedMarker = "\n\n*(relay restarted — turn interrupted)*"

InterruptedMarker is appended to a tail message the relay was still streaming into when it stopped. A restart kills the child agent, so the turn is dead regardless; the correct behaviour is to say so and let the next message start cleanly, never to try to continue a half-streamed message.

View Source
const OutboxDir = "outbox"

OutboxDir is the per-conversation directory an agent writes files into to have them uploaded to Zulip. Relative to the session cwd.

Variables

This section is empty.

Functions

This section is empty.

Types

type Agent

type Agent interface {
	Prompt(ctx context.Context, sid acp.SessionId, prompt []acp.ContentBlock) (acp.StopReason, error)
	Models() (models []client.ModelInfo, currentID string)
}

Agent is the subset of *client.AgentProc the handler drives directly. Session lifecycle lives in Sessions.

type Config

type Config struct {
	Client   Poster
	Agent    Agent
	Sessions Sessions
	Journal  *journal.Journal

	// BotUserID is the relay's own Zulip user id. Messages from it are
	// refused unconditionally: the relay must never feed its own
	// output back into the agent.
	BotUserID int64
	// BotFullName is the bot's display name, used to recognise
	// @-mentions in raw markdown.
	BotFullName string
	// BotSenderIDs are user ids whose messages are never treated as a
	// human turn: every bot in the realm, snapshotted at startup. A
	// bot that appears later is not in the set, but the cross-realm
	// system bots — the ones that actually post unprompted — are
	// caught by the SenderRealm check instead.
	BotSenderIDs map[int64]struct{}

	// Channels is the resolved allowlist: stream id → channel name.
	// The relay answers nowhere else.
	Channels map[int64]string
	// AllowedUsers, if non-nil, restricts who the relay answers.
	AllowedUsers map[int64]struct{}

	// PromptTimeout caps one agent turn.
	PromptTimeout time.Duration
	// EditInterval coalesces streaming edits.
	EditInterval time.Duration

	// Budget, SealMarker and ContinuationMarker configure the splitter.
	Budget             int
	SealMarker         string
	ContinuationMarker string

	// SilentSentinel lets the agent decline to answer an ambient turn.
	SilentSentinel string
	// HideThinking suppresses thought chunks on the streamed path.
	HideThinking bool

	// Logf receives operational messages.
	Logf func(format string, args ...any)
}

Config configures a Handler.

type Handler

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

Handler implements the event side of the relay.

func New

func New(cfg Config) (*Handler, error)

New constructs a Handler.

func (*Handler) Handle

func (h *Handler) Handle(ctx context.Context, ev zulipproto.Event)

Handle is the zulipproto.EventHandler entry point.

func (*Handler) MarkInterrupted

func (h *Handler) MarkInterrupted(ctx context.Context)

MarkInterrupted annotates every message the relay was streaming into when it last stopped. A restart kills the child agent, so the turn is dead; saying so beats leaving a truncated answer that looks complete.

It never attempts to continue a half-streamed message.

func (*Handler) WaitIdle

func (h *Handler) WaitIdle(ctx context.Context) error

WaitIdle blocks until no turn is in flight or ctx is done. Used for graceful shutdown and to synchronise tests without polling.

type Poster

type Poster interface {
	SendMessage(ctx context.Context, streamID int64, topic, content string) (int64, error)
	EditMessage(ctx context.Context, id int64, content string) error
	GetMessage(ctx context.Context, id int64) (zulipproto.Message, error)
	Upload(ctx context.Context, filename string, r io.Reader) (string, error)
}

Poster is the Zulip surface the handler writes to.

type Sessions

type Sessions interface {
	GetOrCreate(ctx context.Context, key string, sink client.SessionUpdateSink) (*state.Session, error)
	Touch(s *state.Session)
	Cancel(ctx context.Context, key string)
	TakePendingSystemPrompt(s *state.Session) string
}

Sessions is the subset of *state.Manager the handler uses.

Jump to

Keyboard shortcuts

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