deviceagent

package
v0.51.1 Latest Latest
Warning

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

Go to latest
Published: Jul 22, 2026 License: Apache-2.0 Imports: 32 Imported by: 0

Documentation

Index

Constants

View Source
const (
	BoxMediaTurnPath = "/v1/box-media/turn"

	BoxMediaDeviceIDHeader     = "X-SpeechKit-Device-ID"
	BoxMediaPairingIDHeader    = "X-SpeechKit-Pairing-ID"
	BoxMediaRequestIDHeader    = "X-SpeechKit-Request-ID"
	BoxMediaInputSHA256Header  = "X-SpeechKit-Input-Audio-SHA256"
	BoxMediaOutputSHA256Header = "X-SpeechKit-Output-Audio-SHA256"
	BoxMediaReplayHeader       = "X-SpeechKit-Replayed"
)
View Source
const (
	ClaimDispatchNew     = "dispatch_new"
	ClaimReplayCompleted = "replay_completed"
	ClaimIndeterminate   = "indeterminate"
	ClaimDigestConflict  = "digest_conflict"
	ClaimNotFound        = "not_found"
)
View Source
const (
	ActionTurnOn  = "turn_on"
	ActionTurnOff = "turn_off"

	ExpectedStateOn  = "on"
	ExpectedStateOff = "off"

	PolicyErrorCodeDenied = "local_policy_denied"

	PolicyReasonRuleNotFound   = "local_rule_not_found"
	PolicyReasonDeviceMismatch = "local_rule_device_mismatch"
	PolicyReasonRoomMismatch   = "local_rule_room_mismatch"
	PolicyReasonTimeInvalid    = "local_rule_time_invalid"
	PolicyReasonNotYetValid    = "local_rule_not_yet_valid"
	PolicyReasonExpired        = "local_rule_expired"
	PolicyReasonTextMismatch   = "local_rule_text_mismatch"
	PolicyReasonLocaleMismatch = "local_rule_locale_mismatch"
)

Variables

View Source
var (
	ErrBoxMediaBridgeRequired   = errors.New("box media: device-agent bridge is required")
	ErrBoxMediaLocalSTTRequired = errors.New("box media: a proven local-only STT transcriber is required")
	ErrBoxMediaTokenInvalid     = errors.New("box media: an independent pairing token is required")
	ErrBoxMediaRuleInvalid      = errors.New("box media: the single transcript mapping is invalid")
)
View Source
var (
	ErrPolicyRuleInvalid   = errors.New("device-agent local policy: invalid rule")
	ErrPolicyRuleDuplicate = errors.New("device-agent local policy: duplicate rule id")
)

Functions

This section is empty.

Types

type AuthorizedCommand

type AuthorizedCommand struct {
	RuleID        string `json:"rule_id"`
	Utterance     string `json:"utterance"`
	Locale        string `json:"locale"`
	EntityID      string `json:"entity_id"`
	ExpectedState string `json:"expected_state"`
}

AuthorizedCommand is assembled exclusively from the configured rule after every binding has matched. Utterance is the canonical server-owned trigger, never the caller-provided string.

type BoxMediaHandler

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

BoxMediaHandler accepts one complete L16 turn over a dedicated HTTPS listener. It is intentionally not mounted by Bridge.Mount, keeping the existing exactly-four speechkit.device_agent.v1 routes unchanged.

func NewBoxMediaHandler

func NewBoxMediaHandler(opts BoxMediaHandlerOptions) (*BoxMediaHandler, error)

func (*BoxMediaHandler) ServeHTTP

func (h *BoxMediaHandler) ServeHTTP(w http.ResponseWriter, r *http.Request)

type BoxMediaHandlerOptions

type BoxMediaHandlerOptions struct {
	Bridge            *Bridge
	LocalSTT          BoxMediaLocalTranscriber
	MediaPairingToken string
	Rule              BoxMediaRuleOptions
	STTTimeout        time.Duration
	TurnTimeout       time.Duration
}

type BoxMediaLocalTranscriber

type BoxMediaLocalTranscriber interface {
	LocalOnly() bool
	TranscribeLocal(context.Context, []byte, string) (*BoxMediaTranscript, error)
}

BoxMediaLocalTranscriber deliberately differs from the general STT router: LocalOnly must attest that this dependency has no cloud or dynamic fallback. Production wiring passes the router's concrete Local() provider directly.

type BoxMediaRuleOptions

type BoxMediaRuleOptions struct {
	DeviceID   string
	PairingID  string
	RoomID     string
	Transcript string
	CommandID  string
	Locale     string
}

BoxMediaRuleOptions defines exactly one server-owned transcript-to-command mapping for the Phase-A representative light. There is no rule collection, fuzzy matching, search, intent inference, or fallback.

type BoxMediaTLSConfig

type BoxMediaTLSConfig struct {
	ListenAddr      string
	CertificateFile string
	PrivateKeyFile  string
}

BoxMediaTLSConfig is the explicit bootstrap contract for the dedicated Box listener. Certificate provisioning is intentionally out of scope: the host supplies an existing absolute-path key pair, and the Box must separately pin the issuing local CA. There is no plaintext listener or HTTP redirect mode.

type BoxMediaTLSRuntime

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

func (*BoxMediaTLSRuntime) Addr

func (r *BoxMediaTLSRuntime) Addr() net.Addr

func (*BoxMediaTLSRuntime) Errors

func (r *BoxMediaTLSRuntime) Errors() <-chan error

func (*BoxMediaTLSRuntime) Shutdown

func (r *BoxMediaTLSRuntime) Shutdown(ctx context.Context) error

type BoxMediaTLSServer

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

BoxMediaTLSServer owns only the exact Box media handler. It never mounts the general SpeechKit mux, device-agent v1 routes, MCP, Gateway, or proxy paths.

func NewBoxMediaTLSServer

func NewBoxMediaTLSServer(config BoxMediaTLSConfig, handler http.Handler) (*BoxMediaTLSServer, error)

func (*BoxMediaTLSServer) Start

Start binds synchronously so configuration, certificate, and port failures are returned before any caller can claim the endpoint is available.

type BoxMediaTranscript

type BoxMediaTranscript struct {
	Text     string
	Provider string
}

BoxMediaTranscript is the bounded result returned by the host's local STT dependency. Provider identifies the local implementation for evidence only; it is never returned to the Box.

type Bridge

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

func NewBridge

func NewBridge(opts BridgeOptions) (*Bridge, error)

func (*Bridge) ExecuteTurn

func (b *Bridge) ExecuteTurn(ctx context.Context, request TurnRequest) (*TurnResult, error)

ExecuteTurn runs the real, durable local HA G0 plus claim-bound TTS path without opening another authority surface. It deliberately reuses the existing v1 handlers in-process; the four public speechkit.device_agent.v1 routes and their JSON contracts remain unchanged.

func (*Bridge) Mount

func (b *Bridge) Mount(mux *http.ServeMux)

type BridgeOptions

type BridgeOptions struct {
	ServerInstanceID   string
	Bindings           []DeviceBindingOptions
	HomeAssistant      HomeAssistant
	TTS                TTSSynthesizer
	TTSReady           bool
	Claims             ClaimLedger
	Policy             *Policy
	MaxRequestAge      time.Duration
	FutureSkew         time.Duration
	ProbeTimeout       time.Duration
	StateVerifyTimeout time.Duration
	Now                func() time.Time
}

type ClaimDecision

type ClaimDecision struct {
	Disposition string
	Result      *StoredResult
}

type ClaimKey

type ClaimKey struct {
	PairingID string
	RequestID string
}

type ClaimLedger

type ClaimLedger interface {
	Claim(context.Context, ClaimKey, [32]byte, time.Time) (ClaimDecision, error)
	Lookup(context.Context, ClaimKey, time.Time) (ClaimDecision, error)
	Complete(context.Context, ClaimKey, [32]byte, StoredResult, time.Time) error
	MarkIndeterminate(context.Context, ClaimKey, [32]byte, string, time.Time) error
}

ClaimLedger is a durable at-most-once ledger. Claim must commit a new row before returning ClaimDispatchNew. Implementations must never turn an existing nonterminal claim back into a dispatch decision.

type Denial

type Denial struct {
	ErrorCode    string `json:"error_code"`
	ReasonCode   string `json:"reason_code"`
	UserGuidance string `json:"user_guidance"`
}

Denial is the stable fail-closed reason envelope returned for every request that does not match one active local rule exactly.

type DeviceBindingOptions

type DeviceBindingOptions struct {
	PairingID          string
	DeviceID           string
	RoomID             string
	Token              string
	AllowedClientCIDRs []string
}

type DurableClaimLedger

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

DurableClaimLedger adapts the single-purpose SQLite ledger to the bridge contract. Handles exist only for claims won by this process; after a restart every previously nonterminal claim remains indeterminate and cannot be dispatched again.

func NewDurableClaimLedger

func NewDurableClaimLedger(store *claimstore.Ledger) (*DurableClaimLedger, error)

func (*DurableClaimLedger) Claim

func (l *DurableClaimLedger) Claim(ctx context.Context, key ClaimKey, digest [32]byte, now time.Time) (ClaimDecision, error)

func (*DurableClaimLedger) Complete

func (l *DurableClaimLedger) Complete(ctx context.Context, key ClaimKey, digest [32]byte, result StoredResult, now time.Time) error

func (*DurableClaimLedger) Lookup

func (l *DurableClaimLedger) Lookup(ctx context.Context, key ClaimKey, now time.Time) (ClaimDecision, error)

func (*DurableClaimLedger) MarkIndeterminate

func (l *DurableClaimLedger) MarkIndeterminate(ctx context.Context, key ClaimKey, digest [32]byte, reason string, now time.Time) error

type HomeAssistant

type HomeAssistant interface {
	Probe(context.Context) error
	Converse(context.Context, string, string) (*HomeAssistantResult, error)
	VerifyState(context.Context, string, string) error
}

type HomeAssistantClient

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

func NewHomeAssistantClient

func NewHomeAssistantClient(opts HomeAssistantOptions) (*HomeAssistantClient, error)

func (*HomeAssistantClient) Converse

func (c *HomeAssistantClient) Converse(ctx context.Context, text, locale string) (*HomeAssistantResult, error)

func (*HomeAssistantClient) Probe

func (c *HomeAssistantClient) Probe(ctx context.Context) error

func (*HomeAssistantClient) VerifyState

func (c *HomeAssistantClient) VerifyState(ctx context.Context, entityID, expectedState string) error

VerifyState reads the documented Home Assistant state resource from the already-validated origin and proves both the entity identity and exact state.

type HomeAssistantDispatchError

type HomeAssistantDispatchError struct {
	ReasonCode     string
	Retryable      bool
	ActionExecuted string // no | unknown
	Cause          error
}

HomeAssistantDispatchError carries only stable bridge classification. It deliberately excludes response bodies and credentials from Error().

func (*HomeAssistantDispatchError) Error

func (*HomeAssistantDispatchError) Unwrap

func (e *HomeAssistantDispatchError) Unwrap() error

type HomeAssistantOptions

type HomeAssistantOptions struct {
	BaseURL  string
	Token    string
	AgentID  string
	Language string
	Timeout  time.Duration
}

type HomeAssistantResult

type HomeAssistantResult struct {
	ConversationID string
	ResponseType   string
	Speech         string
	Language       string
	SuccessTargets []HomeAssistantTarget
	FailedTargets  []HomeAssistantTarget
	ErrorCode      string
	ReasonCode     string
	ActionExecuted string // yes | no | not_applicable | unknown
}

type HomeAssistantTarget

type HomeAssistantTarget struct {
	Name string `json:"name"`
	Type string `json:"type"`
	ID   string `json:"id,omitempty"`
}

HomeAssistantTarget is the documented target projection returned in the Conversation API response data.success and data.failed arrays.

type Policy

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

Policy is immutable after construction and therefore safe for concurrent authorization checks. An empty or nil Policy denies every request.

func NewPolicy

func NewPolicy(options ...RuleOptions) (*Policy, error)

NewPolicy validates and copies a static local allowlist. Only reversible Tier-1 light turn_on/turn_off rules are accepted.

func (*Policy) Authorize

func (p *Policy) Authorize(deviceID, roomID, ruleID, clientText, locale string, now time.Time) (AuthorizedCommand, *Denial)

Authorize resolves one request against one explicitly named local rule. It performs no NLP, fuzzy matching, intent inference, fallback, or rule search. A nil Denial means the command is authorized; otherwise the zero command is returned and callers must not dispatch an action.

type RuleOptions

type RuleOptions struct {
	RuleID      string
	DeviceID    string
	RoomID      string
	TriggerText string
	Locale      string
	Action      string
	EntityID    string
	NotBefore   time.Time
	ExpiresAt   time.Time
}

RuleOptions defines one static, server-owned G0 authorization rule. A rule is a local per-request allowlist entry, not a Workbench approval, Cloud standing grant, delegated identity, or federation capability.

type StoredResult

type StoredResult struct {
	Status         string
	ConversationID string
	ResponseType   string
	Speech         string
	Language       string
	ErrorCode      string
	ReasonCode     string
	Retryable      bool
	ActionExecuted string
}

type TTSSynthesizer

type TTSSynthesizer interface {
	Synthesize(context.Context, string, tts.SynthesizeOpts) (*tts.Result, error)
	ReadyHealthCheck(context.Context) map[string]error
}

type TurnExecutionError

type TurnExecutionError struct {
	StatusCode int
	Envelope   wire.ErrorEnvelope
}

TurnExecutionError preserves the stable error/reason/guidance envelope from the existing device-agent bridge when an in-process adapter executes a turn.

func (*TurnExecutionError) Error

func (e *TurnExecutionError) Error() string

type TurnRequest

type TurnRequest struct {
	RequestID    string
	SessionID    string
	CommandID    string
	DeviceID     string
	PairingID    string
	RoomID       string
	PairingToken string
	Text         string
	Locale       string
	InputSHA256  string
}

TurnRequest is the authenticated, host-side execution contract used by bounded media ingress adapters. PairingToken is consumed only for a constant-time binding check and is never serialized or returned.

InputSHA256 is optional for existing non-media callers. A media adapter must supply the lowercase digest it independently verified over the exact input bytes. The digest joins the durable HA claim, preventing a request ID from being reused with different audio.

type TurnResult

type TurnResult struct {
	Assist     wire.AssistResponse
	Audio      []byte
	Format     string
	SampleRate int
	DurationMS int64
	Provider   string
	Voice      string
}

TurnResult combines the existing durable Home Assistant result with the existing claim-bound local TTS result. Audio is the validated provider payload returned by the v1 TTS implementation (currently WAV); media adapters remain responsible for their own final wire encoding.

Directories

Path Synopsis
Package claimstore provides the durable at-most-once request ledger used by the local SpeechKit to Home Assistant bridge.
Package claimstore provides the durable at-most-once request ledger used by the local SpeechKit to Home Assistant bridge.

Jump to

Keyboard shortcuts

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