// actionTable dispatches applyAction's per-intent bodies. Each of the 7 // intents (fact, reminder, note, query, act, chat, system) has one handler // here with the signature: // // func(h *reactiveHandler, ctx context.Context, dec router.Decision) string // // same contract as applyAction itself: "" means "let the Replier phrase the // reply", a non-empty string OVERRIDES it. This is a straight extraction of // applyAction's old switch cases (formerly ~300 lines in voice.go) — no // reordering of side effects, no new abstractions inside a handler. // // What does NOT belong in this table, because it is not per-intent: // // - the dec.Clarify short-circuit ("" when the router's stage-3 fired) — // stays in applyAction, before dispatch, since it applies to every // intent identically. // - the destructive-act confirm gate (park / resolveConfirm / confirmTTL) // and the enabled-tool allowlist. Both live entirely inside // actionAct/handleAct in actions_act.go, exactly where they lived in the old // switch's IntentAct case — they are act-specific (a fact or a note // can't be destructive), not shared across intents, so they do not need // to move to a separate layer. The important invariant, preserved // as-is: applyAction runs identically whether dec came from a fresh // route or from a completed clarify answer (see finishClarified in // clarify.go and its comment "filling in an argument never grants // authority") — a handler must never special-case a clarify-completed // decision to skip the confirm gate or the allowlist. // - detectPattern and dialogue-session bookkeeping (rememberTurn, // followUpMerge) run in the callers (runTurn, // finishClarified), not per-intent, and are untouched by this slice. // // Each handler lives in actions_.go; the small ones (chat, system) // and the table itself stay here. // // Adding an intent: write its handler in its own file, add one line to // actionHandlers. Do not grow applyAction's switch back. package main import ( "context" "log" "github.com/kami/maven/internal/phraser" "github.com/kami/maven/internal/router" ) // actionHandlers is the per-intent dispatch table used by applyAction. var actionHandlers = map[router.Intent]func(*reactiveHandler, context.Context, router.Decision) string{ router.IntentFact: (*reactiveHandler).actionFact, router.IntentReminder: (*reactiveHandler).actionReminder, router.IntentAct: (*reactiveHandler).actionAct, router.IntentChat: (*reactiveHandler).actionChat, router.IntentSystem: (*reactiveHandler).actionSystem, router.IntentNote: (*reactiveHandler).actionNote, router.IntentQuery: (*reactiveHandler).actionQuery, } func (h *reactiveHandler) actionChat(ctx context.Context, dec router.Decision) string { // Conversational: build history from dialogue session (prior user turns) // and let the LLM respond from general knowledge + context. if h.phraser == nil { return "поговорили." } history := h.chatHistory(ctx) // The phraser hands back its own fallback text alongside the error, so the // turn survives a dead server and the failure still reaches the log. reply, err := h.phraser.PhraseChat(ctx, dec.Utterance, history) if err != nil { log.Printf("voice: chat: %v", err) } if reply == "" { return phraser.ChatFallback() } return reply } func (h *reactiveHandler) actionSystem(ctx context.Context, dec router.Decision) string { return h.replySystem(ctx, dec) }