Skip to content

Tool Reference

Set the active comedy mood for the session.

Input:

  • style (string, required) — one of: dry, roast, chaotic, cheeky, cynic, zoomer

Output:

{
"mood": "dry",
"description": "Deadpan, minimalist, says the obvious like devastating news",
"voice_notes": "Flat, weary, metronomic"
}

Read the current mood and session stats.

Input: none

Output:

{
"mood": "dry",
"description": "Deadpan, minimalist, says the obvious like devastating news",
"session_gag_count": 3
}

The core tool. Takes dry text and rewrites it with comedic delivery in the current mood.

Input:

  • text (string, required) — the dry statement to rewrite
  • technique (string, optional) — one of: rule-of-three, misdirection, escalation, callback, understatement, auto (default: auto)

Output:

{
"rewrite": "Forty-seven builds. A new personal record.",
"technique_used": "understatement",
"callback_source": null
}

When technique is callback and session has eligible recent bits, callback_source contains the text being referenced.

Affectionate burn on code, errors, ideas, or situations. Uses the verdict/label pattern.

Input:

  • target (string, required) — what to roast
  • context (string, optional) — one of: code, error, idea, situation

Output:

{
"roast": "Verdict: Monolithic state blob syndrome.",
"severity": 4,
"mood": "roast"
}

Severity scale: 1 = mild pattern, 5 = architectural crime.

Short, punchy reaction. The quick jab — no config needed.

Input:

  • target (string, required) — what to heckle

Output:

{
"heckle": "var in 2026.",
"mood": "dry"
}

Heckles are capped at ~20 words. They inherit the active mood.

Create a reusable catchphrase or recurring bit, stored in session.

Input:

  • context (string, optional) — what inspired the catchphrase

Output:

{
"phrase": "Verdict: Bug lottery ticket.",
"is_fresh": true
}

is_fresh is true on first generation, false when returning an existing phrase.

Retrieve and reuse the most-used catchphrase from the session.

Input: none

Output:

{
"phrase": "Verdict: Bug lottery ticket.",
"use_count": 3
}

Returns null if no catchphrases exist in the session yet.

Dump the current session state, mood config, and voice backend. Useful for inspecting state without enabling debug env vars.

Input: none

Output (does a live, bounded backend probe — never throws):

{
"mood": "dry",
"mood_description": "Deadpan, minimalist, says the obvious like it's devastating news",
"voice_notes": "One flat sentence of devastating observation...",
"turn_counter": 5,
"recent_bits_count": 3,
"running_gags_count": 1,
"catchphrase_count": 1,
"buffer_stats": { "recent_bits": 3, "max": 20, "running_gags": 1, "catchphrases": 1 },
"catchphrases": { "Ship it and pray.": 2 },
"voice_backend": "default (kokoro)",
"model": "qwen2.5:7b",
"ollama_host": "http://127.0.0.1:11434",
"ollama_api_key_set": false,
"timeout_ms": 30000,
"prompt_version": "1",
"active_prompt_key": "dry.v1",
"prompt_fingerprint": "3fba259a39b6",
"ollama_reachable": true,
"model_available": true,
"generation": { "total_calls": 12, "fallback_calls": 0, "safety_filter_fires": 0, "last_latency_ms": 380 },
"debug": false
}

Notable fields: generation.safety_filter_fires counts how often the safety floor substituted a line (a distinct signal from backend fallback_calls); prompt_fingerprint + active_prompt_key bind the active prompt text + model so output drift is attributable (and a silent prompt-version downgrade is visible — active_prompt_key is the resolved version, not the requested one); when ollama_reachable is false, an unreachable_reason field gives the classified cause (connection / auth / timeout).

Plant a recurring bit the sidekick can call back to later. This is the explicit seeding path for the callback mechanic — without it, running_gags is never populated and callbacks never fire.

Input: { setup: string, tag: string }

Output:

{ "tag": "deadbeef", "setup": "the deadbeef incident", "gag_count": 1, "created_turn": 4 }

Both setup and tag pass the terminal safety gate — a gag that trips the harsh/simile filter is refused (a stored, replayed gag must never carry a slur). A planted gag becomes a callback candidate only after SENSOR_HUMOR_GAG_MIN_DISTANCE turns (so the setup has time to land) and retires after SENSOR_HUMOR_GAG_MAX_FIRES fires — the inverted-U of humor repetition, where a running gag has a low peak. comic_timing with technique: "callback" references an eligible gag and is instructed to escalate it, never repeat it verbatim.

Return the last N per-call traces — one call reconstructs the generation pipeline for any recent output, so you can debug “why did this roast land weird?” without grepping logs.

Input: { limit?: number } (default 10, max 10)

Output: an array of trace entries, newest first:

[
{ "turn": 7, "tool": "roast", "mood": "roast", "input": "800-line function",
"prompt_fingerprint": "a1b2c3d4e5f6", "retries": 0, "validators_triggered": [],
"degraded_reason": null, "latency_ms": 812 }
]

The ring holds the last 10 calls and lives in memory only (never persisted). Set SENSOR_HUMOR_FULL_TRACE=true to additionally capture the full prompt text, raw model output, and parsed output in each entry — heavier, and off by default so raw prompt text never lands on disk.

Reset all session state — mood returns to dry, gags/bits/catchphrases/traces cleared, turn counter zeroed.

Input: none

Output:

{ "reset": true, "mood": "dry", "turn_counter": 0 }

When a tool can’t return a genuine model generation it still returns a usable in-voice line, plus a machine-readable signal so a consuming agent never mistakes a fallback for a real one:

  • degraded: true
  • degraded_reason — a closed enum a consumer can branch on exhaustively: safety-filter (a slur/simile/meta-leak was substituted), language (the model code-switched out of the Latin script and an English line was substituted — a conformance degrade, not a safety one), connection, timeout, model-not-found, auth, rate-limit, server, http, json-parse, validation, exhausted, unknown.

A genuine generation carries no degraded flag — its absence is the positive signal. All comedy tools carry this, including catchphrase_callback (a safety-substituted recall is flagged, never passed off as genuine).

The v1 mood prompts are frozen and pinned by tests — to change one you bump to a new version (SENSOR_HUMOR_PROMPT_VERSION=2 loads v2 prompts alongside v1, falling back to v1 per-mood). A regression scorecard guards drift: a deterministic form + safety golden set runs in npm test, and npm run scorecard runs the live statistical gate (per-mood conformance over N samples → a Wilson interval + three-valued PASS / FAIL / INCONCLUSIVE verdict + SPRT early-stopping). It measures structural conformance and safety, not “funniness.”

VariableDefaultDescription
SENSOR_HUMOR_DEBUGfalseVerbose logging of prompts, responses, and session updates
SENSOR_HUMOR_MODELqwen2.5:7bOllama model to use for comedy generation
SENSOR_HUMOR_TIMEOUT_MS30000Per-call Ollama timeout in ms (invalid values fall back to the default)
SENSOR_HUMOR_TEMPERATURE0.55Generation temperature, clamped 0.0–2.0 (invalid values fall back to the default)
SENSOR_HUMOR_PROMPT_VERSION1Prompt set version. dry.v2 ships as the exemplar; set 2 to load v2 prompts where they exist (other moods fall back to v1 per-mood). Malformed values fall back to v1
SENSOR_HUMOR_PERSISTfalsePersist session to ~/.sensor-humor/session.json so callbacks survive a restart (24h expiry)
SENSOR_HUMOR_SESSION_DIR~/.sensor-humorOverride the directory for the persisted session file
SENSOR_HUMOR_MAX_RETRIES1Ollama generation retries on bad output, clamped 0–3 (invalid values fall back to the default)
SENSOR_HUMOR_GAG_MIN_DISTANCE2Turns before a planted running_gag becomes a callback candidate
SENSOR_HUMOR_GAG_MAX_FIRES3Times a running gag can fire before it retires
SENSOR_HUMOR_FULL_TRACEfalsedebug_chain also captures full prompt/raw/parsed text (heavier)
OLLAMA_HOSThttp://127.0.0.1:11434Ollama API endpoint (may point to a remote/cloud Ollama)
OLLAMA_API_KEY(unset)Bearer token for a remote/cloud Ollama (e.g. https://ollama.com); sent only as an Authorization header, never logged

All tools share a single in-memory session per server instance:

  • mood — current active mood
  • running_gags — tagged setups with usage counts and last-used turn
  • recent_bits — ring buffer (max 20) of recent outputs for callback eligibility
  • catchphrases — phrase-to-count map
  • turn_counter — incremented on every tool call

The session is in-memory by default and resets when the server stops. Set SENSOR_HUMOR_PERSIST=true to persist it to ~/.sensor-humor/session.json (24h expiry) so callbacks survive a restart.