/** * @module capture/proactive/fast-responder * * THE gate — one fast-model call that gates, reasons, and answers. * * The old pipeline made two separate calls (a gpt-oss triage gate, then a * responder). Owner's design: the gate IS the fast-mode agent. Every trigger * batch goes to a single Groq call with the FULL context; the model returns, * in one response: * - thinking — its inner process (why this does/doesn't deserve engagement), * surfaced in the debug streams * - pass — the gate decision (DEFAULT BLOCK for overheard/ambient noise) * - say — when passing, the instant reply itself (same call, no second * round-trip) * - command — optional settings change ("koi, speak mode on" → speak) * - delegate — true ONLY when the ask needs tools/web/files/email/code or * the person asks for deep work: `say` becomes a short ack and * the smart model takes over * * Returns null on ANY failure (no key, timeout, parse error) — the caller * fails closed for ambient noise and falls through to the smart actor for * direct addresses. */ import { type SKYKOIConfig } from "../../config/config.js"; export type FastGateDecision = { /** Inner process — why it engaged or stayed silent (debug streams). */ thinking: string; pass: boolean; /** One-line description of what happened (logs + smart handoff). */ summary: string; /** The instant reply — present whenever pass is true. */ say: string; /** Optional allowlisted settings change, e.g. {command:"speak"}. */ command: { command: string; value?: string; } | null; /** Hand the real work to the smart model (say becomes a short ack). */ delegate: boolean; totalMs: number; }; export type SpeakerSignal = { distinctSpeakers: number; ambient: boolean; wearerVerified: boolean; /** How the koi listens (infra/voice-mode): wake | conversation | ambient. */ engagement?: "wake" | "conversation" | "ambient"; /** conversation mode: the person addressed the koi moments ago. */ conversationLive?: boolean; /** A wearer-voiced vocative-shaped line that may be a misheard "koi" — * no textual koi mention, so the gate (not directKoi) decides. */ misheardCandidate?: boolean; }; /** Tolerant parse of the model's single-call decision. Null on garbage. */ export declare function parseFastGate(raw: string, totalMs: number): FastGateDecision | null; /** * ONE call: gate + reasoning + instant reply. `direct` marks a koi mention * (the model must pass; the pipeline additionally enforces it). */ export declare function fastGate(triggerText: string, fullContext: string, speaker: SpeakerSignal, direct: boolean, opts?: { apiKey?: string; fetchImpl?: typeof fetch; config?: SKYKOIConfig; }): Promise;