interaction

package
v0.260507.1 Latest Latest
Warning

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

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

Documentation

Overview

Package interaction defines the domain-neutral request / reply / result types that flow between scenarios (back-end content providers) and channels (human-facing surfaces) inside the remote middle layer.

All types are JSON-friendly so a future out-of-process plugin transport (HTTP / gRPC) can use the same contract without redesign.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Interaction

type Interaction struct {
	ID      string         `json:"id"`
	Kind    Kind           `json:"kind"`
	Title   string         `json:"title"`
	Body    string         `json:"body,omitempty"`
	Options []Option       `json:"options,omitempty"`
	Timeout time.Duration  `json:"timeout,omitempty"`
	Meta    map[string]any `json:"meta,omitempty"`
}

Interaction is the domain-neutral request a scenario sends to a human through a Channel. Channels are responsible for rendering it natively (buttons on Telegram, blocks on Slack, plain numbered text fallback).

type Kind

type Kind string

Kind classifies how an interaction should be presented and answered.

const (
	// KindNotify is a one-way push (no reply expected).
	KindNotify Kind = "notify"
	// KindConfirm is a yes/no/cancel-style approval.
	KindConfirm Kind = "confirm"
	// KindChoose is a "pick one of N" selection.
	KindChoose Kind = "choose"
	// KindAsk is a free-form text question.
	KindAsk Kind = "ask"
)

type Notification

type Notification struct {
	Title string         `json:"title,omitempty"`
	Body  string         `json:"body"`
	Meta  map[string]any `json:"meta,omitempty"`
}

Notification is a one-way push (no reply expected). Title may be empty; channels should default to a sensible header.

type Option

type Option struct {
	Value string `json:"value"`
	Label string `json:"label"`
	// Style is an optional render hint: "default" | "primary" | "danger".
	Style string `json:"style,omitempty"`
}

Option is a selectable choice presented to the human.

type Registry

type Registry[T any] struct {
	// contains filtered or unexported fields
}

Registry owns the pending / await / resolve / cache lifecycle for long-running interactions whose result is delivered out-of-band.

The registry is generic over the result type so different scenarios can keep their own native types (e.g. Result for the wait HTTP endpoint; an internal value type for fully in-process flows). Phase 1 uses Registry[Result] for the Claude Code wait endpoint.

Concurrency model:

  • Producer side calls Begin (mark inflight) then eventually Resolve.
  • Consumer side (long-poll) calls Await to obtain the receive channel and either picks up the cached answer or blocks until Resolve fires.
  • Recently-resolved values are cached for answerTTL so a script reconnect that races with the producer still gets a deterministic answer instead of an "unknown id" 404.

func New

func New[T any](answerTTL time.Duration) *Registry[T]

New creates a registry. answerTTL controls how long resolved values remain in the cache for late reconnects (default 30s if <=0).

func (*Registry[T]) Await

func (r *Registry[T]) Await(id string) (<-chan T, bool)

Await returns the receive channel for id. ok is true if id is inflight or has a cached answer (the caller may safely block on the channel); false means the id is unknown or already evicted and the channel is nil.

If a cached answer exists, the returned channel is buffered and pre-loaded so a select returns immediately.

func (*Registry[T]) Begin

func (r *Registry[T]) Begin(id string) bool

Begin marks id as inflight. Returns true if the caller should run the producer (newly added) or false if id is already inflight or already cached. Idempotent — duplicate triggers reuse the existing producer instead of double-prompting the human.

func (*Registry[T]) Cancel

func (r *Registry[T]) Cancel(id string)

Cancel removes id from inflight and drops any waiter channel without delivering. Existing select-blocked goroutines fall through to their own ctx / timeout path.

func (*Registry[T]) Forget

func (r *Registry[T]) Forget(id string)

Forget removes any cached answer.

func (*Registry[T]) IsInflight

func (r *Registry[T]) IsInflight(id string) bool

IsInflight reports whether id has an active producer.

func (*Registry[T]) Resolve

func (r *Registry[T]) Resolve(id string, value T)

Resolve removes id from inflight, delivers value to any waiter (non-blocking), and caches value for answerTTL.

type Reply

type Reply struct {
	InteractionID string `json:"interaction_id"`
	Status        Status `json:"status"`
	// Selected carries the chosen Option.Value when applicable.
	Selected string `json:"selected,omitempty"`
	// FreeText carries free-form replies (KindAsk) or supplementary input.
	FreeText string `json:"free_text,omitempty"`
	// Meta is scenario-specific extra data the channel surfaced.
	Meta map[string]any `json:"meta,omitempty"`
}

Reply is what the human sent back through a Channel.

type Result

type Result struct {
	Status   Status         `json:"status"`
	Decision map[string]any `json:"decision,omitempty"`
	Reason   string         `json:"reason,omitempty"`
}

Result is the final outcome a scenario delivers to the long-poll waiter. Decision is scenario-defined (e.g. Claude Code's hookSpecificOutput map).

type Status

type Status string

Status reports how an interaction concluded.

const (
	// StatusAnswered means the human selected an option or replied.
	StatusAnswered Status = "answered"
	// StatusCancelled means the human explicitly cancelled.
	StatusCancelled Status = "cancelled"
	// StatusTimeout means the budget elapsed before any reply.
	StatusTimeout Status = "timeout"
	// StatusError means the channel failed to deliver or read a reply.
	StatusError Status = "error"
)

Jump to

Keyboard shortcuts

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