deviceagent

package
v0.54.1 Latest Latest
Warning

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

Go to latest
Published: Aug 12, 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 added in v0.51.0

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 added in v0.51.0

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

func (*BoxMediaHandler) ServeHTTP added in v0.51.0

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

type BoxMediaHandlerOptions added in v0.51.0

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

type BoxMediaLocalTranscriber added in v0.51.0

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 added in v0.51.0

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 added in v0.51.0

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 added in v0.51.0

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

func (*BoxMediaTLSRuntime) Addr added in v0.51.0

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

func (*BoxMediaTLSRuntime) Errors added in v0.51.0

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

func (*BoxMediaTLSRuntime) Shutdown added in v0.51.0

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

type BoxMediaTLSServer added in v0.51.0

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 added in v0.51.0

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

func (*BoxMediaTLSServer) CertificateChainDER added in v0.54.0

func (s *BoxMediaTLSServer) CertificateChainDER() [][]byte

CertificateChainDER returns a defensive snapshot of the exact certificate chain loaded into the dedicated TLS listener. Lifecycle policy can therefore bind the served leaf to the Box-pinned CA without reopening the certificate file and introducing a verify-then-load race.

func (*BoxMediaTLSServer) Start added in v0.51.0

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

type BoxMediaTranscript added in v0.51.0

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 added in v0.51.0

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 added in v0.51.0

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 added in v0.51.0

func (e *TurnExecutionError) Error() string

type TurnRequest added in v0.51.0

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 added in v0.51.0

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