mood

package
v0.22.0 Latest Latest
Warning

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

Go to latest
Published: Aug 16, 2026 License: MIT Imports: 6 Imported by: 0

Documentation

Overview

Package mood detects interaction *friction* — how a user is engaging with the agent — from their terminal input, using a deterministic, dependency-free lexical heuristic. No model, no network, no surprise deps (consistent with memcode's local-first doctrine and the BM25-over-embeddings choice for recall).

This is NOT a personality judgment about the user. It is a control signal: a rising friction reading (caps, expletives, "still broken", repeated corrections, interrupts, denials) usually means the agent is on the wrong track and should change strategy. And the *intensity* of a direction is part of its meaning — an instruction given with force ("do NOT add a paid vendor") is a stronger, more durable constraint than a calm aside, so memcode records the intensity alongside the direction and weighs it later.

Index

Constants

This section is empty.

Variables

View Source
var W = struct {
	Caps, Expl, Neg, Punct, Rep, RepWord, Density, Combo, Directed, Pos float64
}{
	Caps: 0.5, Expl: 0.65, Neg: 0.55, Punct: 0.4, Rep: 0.3, RepWord: 0.25,
	Density: 0.2, Combo: 0.25, Directed: 0.1, Pos: 0.7,
}

W is the tunable feature-weight table for friction scoring — kept in one place (not scattered through conditionals) so the model can be calibrated easily.

Functions

func Behavior

func Behavior(s State) string

Behavior is the recommended agent strategy for a state — the whole point of reading friction: change approach before the user has to escalate further.

func Friction

func Friction(s State) string

Friction collapses a state into the product-facing gauge level shown in the TUI: low (proceed normally), elevated (slow down / clarify), high (stop and repair). Deliberately not an emoji mood — it's about the work, not the person.

Types

type Cadence

type Cadence struct {
	InterMessageMs  int64 `json:"inter_message_ms"`           // gap since the previous turn (0 = first)
	Chars           int   `json:"chars"`                      // message length
	Words           int   `json:"words"`                      //
	Burst           bool  `json:"burst,omitempty"`            // arrived right after the previous turn
	RapidCorrection bool  `json:"rapid_correction,omitempty"` // a quick corrective follow-up → repair
	LongPause       bool  `json:"long_pause,omitempty"`       // big gap → possibly a new topic
}

Cadence is the timing shape of a turn — a subtle but real interaction signal. Three short messages fired off in five seconds is a different state than one calm, considered prompt. We capture only DERIVED aggregates (never raw keystrokes — that gets creepy fast): the gap since the last turn, size, and a few booleans the runtime uses for routing + friction.

type CadenceTracker

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

CadenceTracker derives per-turn timing features from message arrival times.

func NewCadenceTracker

func NewCadenceTracker() *CadenceTracker

NewCadenceTracker returns a tracker using the wall clock.

func (*CadenceTracker) Observe

func (c *CadenceTracker) Observe(text string, corrective bool) Cadence

Observe records a turn's arrival and returns its cadence. corrective signals the turn is a correction/negation (from routing or friction), so a quick follow-up is flagged as a rapid correction (a strong "we're off track" cue).

type Reading

type Reading struct {
	Valence     float64  `json:"valence"`
	Intensity   float64  `json:"intensity"`
	Frustration float64  `json:"frustration"`
	State       State    `json:"state"`
	Signals     []string `json:"signals,omitempty"`
}

Reading is the friction charge of one piece of text (or a smoothed aggregate). Valence is -1..+1; Intensity and Frustration are 0..1.

func Score

func Score(text string) Reading

Score reads a single turn of text.

func (Reading) Behavior

func (r Reading) Behavior() string

func (Reading) FrictionLevel

func (r Reading) FrictionLevel() string

Friction returns the gauge level for r's state, and Behavior the strategy.

type State

type State string

State is the interaction state inferred for a turn (or the running aggregate).

const (
	Calm        State = "calm"        // positive / satisfied
	Focused     State = "focused"     // neutral, working — the default
	Curious     State = "curious"     // exploratory: "what if", "I wonder", "how does X work"
	Confused    State = "confused"    // uncertainty, questions, "what / huh / unclear"
	Frustrated  State = "frustrated"  // negative, exasperated
	Angry       State = "angry"       // peak friction: caps + expletives + blame
	Urgent      State = "urgent"      // time pressure: now / asap / immediately
	Discouraged State = "discouraged" // low-energy defeat about the WORK ("stuck", "give up")
)

type Tracker

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

Tracker keeps a smoothed running friction across a session so the gauge reflects the general interaction rather than one stray message, and detects repeated corrections (the same complaint twice = the agent really isn't listening, which should raise friction).

func NewTracker

func NewTracker() *Tracker

NewTracker returns a Tracker with sensible smoothing.

func (*Tracker) Bump

func (t *Tracker) Bump(amount float64, signal string) Reading

Bump nudges friction up for a non-textual signal the runtime observes (an interrupt route, an approval denial) and returns the updated aggregate.

func (*Tracker) Current

func (t *Tracker) Current() Reading

Current returns the smoothed reading.

func (*Tracker) Observe

func (t *Tracker) Observe(r Reading, raw string) Reading

Observe folds a turn into the running aggregate. raw is the original text, used to detect repeated corrections. Returns the smoothed reading.

Jump to

Keyboard shortcuts

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