adapters

package
v0.1.0-alpha Latest Latest
Warning

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

Go to latest
Published: Aug 28, 2026 License: MIT Imports: 18 Imported by: 0

Documentation

Overview

Package adapters defines backend-neutral agent events and the adapters that derive them from normalized terminal text.

Index

Constants

View Source
const (
	// ClaudeID identifies the experimental Claude Code adapter. Its vendor
	// rules are intentionally limited to anonymized prompts observed with the
	// version documented alongside the fixtures.
	ClaudeID = "claude"
)
View Source
const (
	// CodexID is experimental and intentionally limited to interactions backed
	// by the anonymized captures in testdata/codex.
	CodexID = "codex"
)
View Source
const GenericID = "generic"
View Source
const MaxLineBytes = 4096

MaxLineBytes is the maximum encoded line accepted by the safe line-input boundary, before its terminating carriage return is appended.

Variables

View Source
var (
	ErrUnknownAdapter      = errors.New("adaptateur inconnu")
	ErrAdapterUnavailable  = errors.New("adaptateur non implémenté")
	ErrDecisionUnsupported = errors.New("décision non prise en charge")
	ErrEventMismatch       = errors.New("événement en attente différent")
	ErrProcessorTerminated = errors.New("flux sémantique de l'adaptateur terminé")
)
View Source
var (
	// ErrEventPending reports that an actionable event must be resolved before
	// ordinary line input can be delivered.
	ErrEventPending = errors.New("événement actionable en attente")
	// ErrInvalidLine reports line input which cannot be encoded unambiguously.
	ErrInvalidLine = errors.New("ligne terminal invalide")
	// ErrLineUnsupported reports that a transport has no atomic line boundary.
	ErrLineUnsupported = errors.New("envoi de ligne non pris en charge")
	// ErrLineDeliveryUncertain reports that a transport was called but could
	// not prove whether zero, some, or all line bytes reached the target. The
	// original transport error is deliberately not retained: it may echo the
	// submitted text and must never escape through errors.Unwrap.
	ErrLineDeliveryUncertain = errors.New("livraison de ligne incertaine")
)

Functions

func IsSensitiveText

func IsSensitiveText(value string) bool

Types

type Adapter

type Adapter interface {
	ID() string
	Detect(state *DetectionState, chunk []byte) ([]Event, error)
	EncodeDecision(event Event, decision Decision, manualInput string) ([]byte, error)
}

Adapter interprets normalized, ANSI-free terminal text. Implementations do not own processes, PTYs, tmux sessions, rendered history or audit sinks.

type ClaudeAdapter

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

ClaudeAdapter recognizes only prompts backed by the anonymized Claude Code 2.1.59 fixtures. Configured intercept_patterns retain their configured order and take priority, preserving the semantics of existing configurations.

The adapter remains experimental: no automatic allow or deny byte sequence is claimed because the highlighted TUI selection can change independently of the prompt text visible to Relayer.

func NewClaudeAdapter

func NewClaudeAdapter(patterns []Pattern) (*ClaudeAdapter, error)

NewClaudeAdapter validates both the observed rules and every configured intercept_pattern before a session starts.

func (*ClaudeAdapter) Detect

func (a *ClaudeAdapter) Detect(state *DetectionState, chunk []byte) ([]Event, error)

Detect delegates bounded stream handling and legacy regex behavior to the generic adapter, then enriches only rules tied to real Claude Code fixtures.

func (*ClaudeAdapter) EncodeDecision

func (a *ClaudeAdapter) EncodeDecision(event Event, decision Decision, manualInput string) ([]byte, error)

EncodeDecision preserves exact manual input compatibility. Automatic allow and deny remain unsupported until selection-independent bytes are verified.

func (*ClaudeAdapter) ID

func (*ClaudeAdapter) ID() string

type CodexAdapter

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

CodexAdapter recognizes only prompts captured from the CLI version recorded in testdata/codex. Configured intercept_patterns remain a complete fallback; this wrapper does not silently replace or weaken them.

func NewCodexAdapter

func NewCodexAdapter(patterns []Pattern) (*CodexAdapter, error)

NewCodexAdapter validates and copies the legacy intercept_patterns used by the generic fallback.

func (*CodexAdapter) Detect

func (a *CodexAdapter) Detect(state *DetectionState, chunk []byte) ([]Event, error)

Detect gives configured intercept_patterns priority so enabling the Codex adapter cannot change an existing policy's event semantics. Verified Codex prompts are considered only when no configured pattern matches. Probes are value copies, so a chunk is committed to the bounded DetectionState exactly once.

func (*CodexAdapter) EncodeDecision

func (a *CodexAdapter) EncodeDecision(event Event, decision Decision, manualInput string) ([]byte, error)

EncodeDecision supports only byte sequences verified against the captured CLI. Generic fallback events retain the established manual-input behavior.

func (*CodexAdapter) ID

func (*CodexAdapter) ID() string

type Decision

type Decision string

Decision is deliberately limited to actions represented by current code. Adapters may reject allow or deny when they cannot encode the action reliably; callers must then retain the pending event for human input.

const (
	DecisionManual Decision = "manual"
	DecisionAllow  Decision = "allow"
	DecisionDeny   Decision = "deny"
)

type Descriptor

type Descriptor struct {
	ID          string
	Status      Status
	Implemented bool
	Executables []string
}

Descriptor is safe to display in diagnostics and documentation.

type DetectionState

type DetectionState struct {
	SessionID string
	AgentID   string
	AdapterID string
	// contains filtered or unexported fields
}

DetectionState is the bounded, per-session state supplied to an Adapter. A state must never be shared by two terminal sessions.

func NewDetectionState

func NewDetectionState(sessionID, agentID, adapterID string) *DetectionState

NewDetectionState creates independent state for one agent session.

func (*DetectionState) IsBlocked

func (s *DetectionState) IsBlocked() bool

IsBlocked reports whether an actionable event still awaits a decision.

func (*DetectionState) Pending

func (s *DetectionState) Pending() *Event

Pending returns a defensive copy of the current actionable event.

type Event

type Event struct {
	ID        string
	Signature string
	Sequence  uint64
	SessionID string
	AgentID   string
	Adapter   string
	Type      EventType
	Summary   string
	Match     string
	Sensitive bool
	Risk      RiskLevel
	Timestamp time.Time
	Metadata  map[string]string
}

Event is the single semantic representation shared by adapters, backends, snapshots and the TUI. ID identifies one occurrence; Signature is stable for equivalent normalized content and is used only while reconciling duplicates.

func NewProcessExitEvent

func NewProcessExitEvent(sessionID, agentID, adapterID string, sequence uint64, exitCode *int, failed bool) Event

NewProcessExitEvent creates the sole lifecycle event currently represented in the semantic stream. Metadata contains only a numeric exit code.

func (Event) Actionable

func (e Event) Actionable() bool

Actionable reports whether Relayer must pause for a human decision.

func (Event) Clone

func (e Event) Clone() Event

Clone returns an event whose mutable metadata cannot alias the source.

type EventType

type EventType string

EventType describes an observation with a concrete meaning in Relayer. Output invalidations and backend failures intentionally remain transport signals: they are not agent events.

const (
	EventConfirmation EventType = "confirmation"
	EventPermission   EventType = "permission"
	EventCredential   EventType = "credential"
	EventProcessExit  EventType = "process_exit"
)

type Factory

type Factory func() (Adapter, error)

Factory creates independent adapter state for one terminal session.

type GenericRegexAdapter

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

GenericRegexAdapter is the stable compatibility adapter for existing intercept_patterns. It receives only normalized text from Processor.

func NewGenericRegexAdapter

func NewGenericRegexAdapter(patterns []Pattern) (*GenericRegexAdapter, error)

NewGenericRegexAdapter validates and defensively copies all expressions.

func (*GenericRegexAdapter) Detect

func (a *GenericRegexAdapter) Detect(state *DetectionState, chunk []byte) ([]Event, error)

Detect preserves pattern order and reports at most one actionable event while another occurrence is pending. A match may span chunks, but it must reach the active terminal line affected by the newest normalized chunk.

func (*GenericRegexAdapter) EncodeDecision

func (*GenericRegexAdapter) EncodeDecision(event Event, decision Decision, manualInput string) ([]byte, error)

func (*GenericRegexAdapter) ID

type Hooks

type Hooks struct {
	OnOutput func()
	OnEvent  func(Event)
}

Hooks are invoked synchronously after Processor releases its state lock. OnEvent may inspect Processor state; process termination remains the responsibility of the owning backend rather than an event callback.

type Pattern

type Pattern struct {
	Name        string
	Description string
	Expression  string
	Sensitive   bool
}

Pattern is the backward-compatible representation of intercept_patterns.

func DefaultPatterns

func DefaultPatterns() []Pattern

type Processor

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

Processor separates raw transport bytes, normalized detection text and the bounded sanitized text rendered by Bubble Tea. Raw bytes are never retained.

func NewProcessor

func NewProcessor(adapter Adapter, state *DetectionState, capacity int, hooks Hooks) (*Processor, error)

func (*Processor) Acknowledge

func (p *Processor) Acknowledge(eventID string) error

func (*Processor) Consume

func (p *Processor) Consume(chunk []byte) error

Consume accepts raw terminal bytes, strips fragmented ANSI sequences and sends only normalized text to the adapter.

func (*Processor) DetectionWindowLen

func (p *Processor) DetectionWindowLen() int

func (*Processor) IsBlocked

func (p *Processor) IsBlocked() bool

func (*Processor) MarkProcessExitEvent

func (p *Processor) MarkProcessExitEvent(exitCode *int, failed bool) Event

MarkProcessExitEvent atomically terminates any pending interaction and reserves the next occurrence sequence under the same lock used by Detect and SendLine. It deliberately does not wait for earlier semantic hooks: a process owner can mark the transport closed immediately after Wait, perform bounded descendant cleanup, then call WaitSemanticEvents before publishing the returned process_exit event.

func (*Processor) NewProcessExitEvent

func (p *Processor) NewProcessExitEvent(exitCode *int, failed bool) Event

NewProcessExitEvent marks the processor terminated, then preserves the historical ordering guarantee that every earlier semantic hook completes before the process_exit event is returned to its caller.

func (*Processor) Output

func (p *Processor) Output() string

func (*Processor) Pending

func (p *Processor) Pending() *Event

func (*Processor) ReconcileSnapshot

func (p *Processor) ReconcileSnapshot(raw []byte) (*Event, bool, error)

ReconcileSnapshot uses the last active logical line for generic detection. A vendor adapter may additionally fingerprint only its verified prompt block so a resize remains idempotent while a successive prompt discovered after an attach receives a fresh occurrence ID. An unchanged snapshot after a successful acknowledgement cannot resurrect the old event.

func (*Processor) Resolve

func (p *Processor) Resolve(eventID string, deliver func() error) error

Resolve serializes a decision with detection. The pending occurrence is cleared only after delivery succeeds; terminal output arriving concurrently is processed afterwards, so an immediate second prompt cannot be overwritten by rollback of the first one.

func (*Processor) Restore

func (p *Processor) Restore(event Event) error

func (*Processor) Revision

func (p *Processor) Revision() uint64

Revision returns the latest semantic occurrence sequence for snapshots.

func (*Processor) Run

func (p *Processor) Run(ctx context.Context, reader io.Reader) error

func (*Processor) SendLine

func (p *Processor) SendLine(ctx context.Context, line string, deliver func([]byte) error) error

SendLine serializes ordinary line input with event detection and process termination. It never resolves or acknowledges an event. Delivery happens under the same lock as Detect, so either the line is sent first or a prompt becomes pending first; the two outcomes cannot race past one another.

func (*Processor) WaitSemanticEvents

func (p *Processor) WaitSemanticEvents()

WaitSemanticEvents waits until every actionable event reserved before process termination has reached its hook. It must stay outside p.mu because hooks are allowed to inspect Processor state.

type Registry

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

Registry resolves explicit adapters and executable-name hints. Generic is always installed as the final fallback. Claude and Codex remain experimental and recognize only interactions backed by anonymized fixtures.

func NewRegistry

func NewRegistry(patterns []Pattern) (*Registry, error)

NewRegistry builds the production registry around legacy regex patterns.

func (*Registry) Descriptors

func (r *Registry) Descriptors() []Descriptor

Descriptors returns a deterministic, defensive maturity inventory.

func (*Registry) Register

func (r *Registry) Register(descriptor Descriptor, factory Factory) error

Register adds an adapter implementation or an unavailable maturity descriptor. Executable hints are used only when factory is non-nil and the descriptor explicitly marks the adapter implemented.

func (*Registry) Resolve

func (r *Registry) Resolve(requestedID, executable string) (Adapter, Descriptor, error)

Resolve honors an explicit ID. With no explicit ID it considers executable hints only for implemented adapters, then falls back to generic.

type RiskLevel

type RiskLevel string

RiskLevel is descriptive audit metadata. It does not apply a policy.

const (
	RiskLow     RiskLevel = "low"
	RiskUnknown RiskLevel = "unknown"
	RiskHigh    RiskLevel = "high"
)

type Status

type Status string

Status exposes the maturity of a registered adapter without claiming that an experimental placeholder is implemented.

const (
	StatusStable       Status = "stable"
	StatusExperimental Status = "experimental"
)

Jump to

Keyboard shortcuts

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