refactor(server): move VoiceSession into session/voice/ to match CheckoutSession (#1645)
#1640 extracted the voice subsystem to server/voice/ (a peer of session.ts); #1644 extracted the checkout read side to session/checkout/. The two carves landed in inconsistent homes, so there was no single place to find "what has session.ts been carved into." Relocate server/voice/ -> server/session/voice/ so all session subsystems share one home alongside session/checkout/. Pure relocation: git-tracked renames with import-depth bumped one level (../ -> ../../); the only external touch point is the import path in session.ts. Behavior preserved verbatim — the 17 voice unit tests pass unchanged before and after. Also record the actual shipped layout and the next carve (the checkout mutation handlers still inline in session.ts -> session/checkout/checkout-session.ts) in the decomposition plan, and fix its now-stale voice path.
This commit is contained in:
@@ -13,6 +13,15 @@ Each domain becomes a controller class in its own file with the **exact** contra
|
||||
|
||||
Session shrinks to a connection/dispatch shell: it keeps `handleMessage`, the `??` chain (1739-1751), `emit`/`emitBinary`, `sessionLogger`, connection identity, inflight metrics, lifecycle intents, and the **ordered** `cleanup()`. Each `dispatchXMessage` collapses to `return this.xController.dispatch(msg)`.
|
||||
|
||||
## Progress (shipped — diverged from the original filenames)
|
||||
|
||||
The first carves shipped as **deep modules with a narrow Host seam**, not the `dispatch(msg)`-owned-set controllers sketched below: `session.ts` keeps each `dispatchXMessage` switch and delegates per case to the subsystem. Home convention that emerged: session subsystems live at **`session/<domain>/`**, with `session.ts` as the orchestrator shell.
|
||||
|
||||
- **#1640 — VoiceSession** (`session/voice/voice-session.ts`, seam `VoiceSessionHost`): the STT/TTS/dictation/turn-detection subsystem. _(Originally landed at `server/voice/`; relocated under `session/` so all session subsystems share one home.)_
|
||||
- **#1644 — CheckoutSession, read side** (`session/checkout/checkout-session.ts`, seam `CheckoutSessionHost`, port `CheckoutDiffSubscriber`): status, branch validate/suggest, diff subscribe/unsubscribe, manual refresh. The workspace-git observer already delegates `emitStatusUpdate`/`scheduleDiffRefresh` to it.
|
||||
|
||||
**Next carve — CheckoutSession mutation side (Slice 4 below).** The 17 checkout _write_ handlers still inline in `session.ts` (`dispatchCheckoutMessage`, ~2010) — branch switch/rename, commit, merge, merge-from-base, pull, push, PR create/merge, github set-auto-merge/get-check-details, PR status, PR timeline, github search, stash save/pop/list — move into the existing `session/checkout/checkout-session.ts` behind `CheckoutSessionHost`. The Slice-3 observer entanglement the table fears is already resolved: #1644 moved the status/diff read side into CheckoutSession, so the workspace observer delegates today and this no longer blocks on the WorkspaceController split.
|
||||
|
||||
### Why this is safe at the dispatch seam (verified)
|
||||
|
||||
`dispatchInboundMessage` builds `a() ?? b() ?? ... ?? dispatchMiscMessage()` and short-circuits on the first non-`undefined` **Promise object** (not its resolved value). Message-type spaces are **disjoint** (no duplicate `case` labels across switches), so at most one dispatcher matches any message — collapsing to delegation cannot change which handler runs. `dispatchTerminalMessage` (2150-2153) already proves this. Two quirks preserved verbatim: schedule/\* is reached via the chat dispatcher's OWN `default` arm (2183), not the top-level `??`; and `start_workspace_script_request` (a workspace type) is special-cased before terminal delegation (2150).
|
||||
@@ -117,7 +126,7 @@ In-place, separately reviewable. Split the `audio_output` TTS-debug branch out o
|
||||
|
||||
## Slice 7 — VoiceSessionController (XL)
|
||||
|
||||
**Move:** voice handlers + ~25 voice fields + the TTS-debug hook (Slice 6) + `voiceModeAgentId`/`isVoiceMode` + the `shouldAutoAllowVoicePermission` predicate (Slice 3) → `packages/server/src/server/voice/voice-session-controller.ts`. Carve voice types out of `dispatchVoiceAndControlMessage`, leaving infra (restart/shutdown/heartbeat/ping/abort) on the shell.
|
||||
**Move:** voice handlers + ~25 voice fields + the TTS-debug hook (Slice 6) + `voiceModeAgentId`/`isVoiceMode` + the `shouldAutoAllowVoicePermission` predicate (Slice 3) → the existing `packages/server/src/server/session/voice/voice-session.ts` (see Progress above). Carve voice types out of `dispatchVoiceAndControlMessage`, leaving infra (restart/shutdown/heartbeat/ping/abort) on the shell.
|
||||
|
||||
**SessionContext surface:** pure `emit`, `emitBinary`, `hasBinaryChannel`, `sessionLogger`/`sessionId`/`paseoHome`, `getSpeechReadiness`, agent-control port `{ loadAgent, reloadWithSystemPrompt, interruptIfRunning, isRunning, sendSpokenText, buildAgentPrompt }`, `getSignal`/`abortCurrent` (Slice 6).
|
||||
|
||||
|
||||
@@ -167,7 +167,7 @@ import {
|
||||
} from "./workspace-registry.js";
|
||||
import { wrapSpokenInput } from "./voice-config.js";
|
||||
import { isVoicePermissionAllowed } from "./voice-permission-policy.js";
|
||||
import { VoiceSession } from "./voice/voice-session.js";
|
||||
import { VoiceSession } from "./session/voice/voice-session.js";
|
||||
import { CheckoutSession } from "./session/checkout/checkout-session.js";
|
||||
import {
|
||||
listDirectoryEntries,
|
||||
|
||||
+4
-4
@@ -3,18 +3,18 @@ import pino from "pino";
|
||||
import { describe, expect, test, vi } from "vitest";
|
||||
|
||||
import { VoiceSession, type VoiceSessionHost } from "./voice-session.js";
|
||||
import type { ManagedAgent } from "../agent/agent-manager.js";
|
||||
import type { SessionOutboundMessage } from "../messages.js";
|
||||
import type { ManagedAgent } from "../../agent/agent-manager.js";
|
||||
import type { SessionOutboundMessage } from "../../messages.js";
|
||||
import type {
|
||||
SpeechToTextProvider,
|
||||
StreamingTranscriptionCommittedEvent,
|
||||
StreamingTranscriptionEvent,
|
||||
StreamingTranscriptionSession,
|
||||
} from "../speech/speech-provider.js";
|
||||
} from "../../speech/speech-provider.js";
|
||||
import type {
|
||||
TurnDetectionProvider,
|
||||
TurnDetectionSession,
|
||||
} from "../speech/turn-detection-provider.js";
|
||||
} from "../../speech/turn-detection-provider.js";
|
||||
|
||||
const VOICE_AGENT_ID = "11111111-1111-4111-8111-111111111111";
|
||||
|
||||
+15
-15
@@ -2,25 +2,25 @@ import { v4 as uuidv4 } from "uuid";
|
||||
import { z } from "zod";
|
||||
import type pino from "pino";
|
||||
import { getErrorMessage } from "@getpaseo/protocol/error-utils";
|
||||
import type { SessionInboundMessage, SessionOutboundMessage } from "../messages.js";
|
||||
import { TTSManager } from "../agent/tts-manager.js";
|
||||
import { STTManager } from "../agent/stt-manager.js";
|
||||
import type { SpeechToTextProvider, TextToSpeechProvider } from "../speech/speech-provider.js";
|
||||
import type { TurnDetectionProvider } from "../speech/turn-detection-provider.js";
|
||||
import { maybePersistTtsDebugAudio } from "../agent/tts-debug.js";
|
||||
import { isPaseoDictationDebugEnabled } from "../agent/recordings-debug.js";
|
||||
import type { SessionInboundMessage, SessionOutboundMessage } from "../../messages.js";
|
||||
import { TTSManager } from "../../agent/tts-manager.js";
|
||||
import { STTManager } from "../../agent/stt-manager.js";
|
||||
import type { SpeechToTextProvider, TextToSpeechProvider } from "../../speech/speech-provider.js";
|
||||
import type { TurnDetectionProvider } from "../../speech/turn-detection-provider.js";
|
||||
import { maybePersistTtsDebugAudio } from "../../agent/tts-debug.js";
|
||||
import { isPaseoDictationDebugEnabled } from "../../agent/recordings-debug.js";
|
||||
import {
|
||||
DictationStreamManager,
|
||||
type DictationStreamOutboundMessage,
|
||||
} from "../dictation/dictation-stream-manager.js";
|
||||
} from "../../dictation/dictation-stream-manager.js";
|
||||
import { createVoiceTurnController, type VoiceTurnController } from "./voice-turn-controller.js";
|
||||
import { buildVoiceModeSystemPrompt, stripVoiceModeSystemPrompt } from "../voice-config.js";
|
||||
import type { VoiceCallerContext, VoiceSpeakHandler } from "../voice-types.js";
|
||||
import type { ManagedAgent } from "../agent/agent-manager.js";
|
||||
import type { AgentSessionConfig } from "../agent/agent-sdk-types.js";
|
||||
import type { LocalSpeechModelId } from "../speech/providers/local/models.js";
|
||||
import { toResolver, type Resolvable } from "../speech/provider-resolver.js";
|
||||
import type { SpeechReadinessSnapshot, SpeechReadinessState } from "../speech/speech-runtime.js";
|
||||
import { buildVoiceModeSystemPrompt, stripVoiceModeSystemPrompt } from "../../voice-config.js";
|
||||
import type { VoiceCallerContext, VoiceSpeakHandler } from "../../voice-types.js";
|
||||
import type { ManagedAgent } from "../../agent/agent-manager.js";
|
||||
import type { AgentSessionConfig } from "../../agent/agent-sdk-types.js";
|
||||
import type { LocalSpeechModelId } from "../../speech/providers/local/models.js";
|
||||
import { toResolver, type Resolvable } from "../../speech/provider-resolver.js";
|
||||
import type { SpeechReadinessSnapshot, SpeechReadinessState } from "../../speech/speech-runtime.js";
|
||||
|
||||
const PCM_SAMPLE_RATE = 16000;
|
||||
const PCM_CHANNELS = 1;
|
||||
+2
-2
@@ -7,11 +7,11 @@ import type {
|
||||
StreamingTranscriptionCommittedEvent,
|
||||
StreamingTranscriptionEvent,
|
||||
StreamingTranscriptionSession,
|
||||
} from "../speech/speech-provider.js";
|
||||
} from "../../speech/speech-provider.js";
|
||||
import type {
|
||||
TurnDetectionProvider,
|
||||
TurnDetectionSession,
|
||||
} from "../speech/turn-detection-provider.js";
|
||||
} from "../../speech/turn-detection-provider.js";
|
||||
import { createVoiceTurnController } from "./voice-turn-controller.js";
|
||||
|
||||
class FakeTurnDetectionSession extends EventEmitter implements TurnDetectionSession {
|
||||
+4
-4
@@ -2,14 +2,14 @@ import { Buffer } from "node:buffer";
|
||||
import type { Logger } from "pino";
|
||||
import { v4 as uuidv4 } from "uuid";
|
||||
|
||||
import { Pcm16MonoResampler } from "../agent/pcm16-resampler.js";
|
||||
import { parsePcmRateFromFormat } from "../speech/audio.js";
|
||||
import { Pcm16MonoResampler } from "../../agent/pcm16-resampler.js";
|
||||
import { parsePcmRateFromFormat } from "../../speech/audio.js";
|
||||
import type {
|
||||
SpeechToTextProvider,
|
||||
StreamingTranscriptionEvent,
|
||||
StreamingTranscriptionSession,
|
||||
} from "../speech/speech-provider.js";
|
||||
import type { TurnDetectionProvider } from "../speech/turn-detection-provider.js";
|
||||
} from "../../speech/speech-provider.js";
|
||||
import type { TurnDetectionProvider } from "../../speech/turn-detection-provider.js";
|
||||
|
||||
const VOICE_FINAL_TRANSCRIPT_TIMEOUT_MS = 10_000;
|
||||
|
||||
Reference in New Issue
Block a user