diff --git a/packages/server/src/client/daemon-client-transport-types.ts b/packages/server/src/client/daemon-client-transport-types.ts index 8820c13df..92c043d0b 100644 --- a/packages/server/src/client/daemon-client-transport-types.ts +++ b/packages/server/src/client/daemon-client-transport-types.ts @@ -22,15 +22,15 @@ export interface WebSocketLike { send: (data: string | Uint8Array | ArrayBuffer) => void; close: (code?: number, reason?: string) => void; binaryType?: string; - on?: (event: string, listener: (...args: any[]) => void) => void; - off?: (event: string, listener: (...args: any[]) => void) => void; - removeListener?: (event: string, listener: (...args: any[]) => void) => void; - addEventListener?: (event: string, listener: (event: any) => void) => void; - removeEventListener?: (event: string, listener: (event: any) => void) => void; - onopen?: ((event: any) => void) | null; - onclose?: ((event: any) => void) | null; - onerror?: ((event: any) => void) | null; - onmessage?: ((event: any) => void) | null; + on?: (event: string, listener: (...args: unknown[]) => void) => void; + off?: (event: string, listener: (...args: unknown[]) => void) => void; + removeListener?: (event: string, listener: (...args: unknown[]) => void) => void; + addEventListener?: (event: string, listener: (event: unknown) => void) => void; + removeEventListener?: (event: string, listener: (event: unknown) => void) => void; + onopen?: ((event: unknown) => void) | null; + onclose?: ((event: unknown) => void) | null; + onerror?: ((event: unknown) => void) | null; + onmessage?: ((event: unknown) => void) | null; } export interface TransportLogger { diff --git a/packages/server/src/client/daemon-client-transport.test.ts b/packages/server/src/client/daemon-client-transport.test.ts index 3bb89903c..f6dd1bb1b 100644 --- a/packages/server/src/client/daemon-client-transport.test.ts +++ b/packages/server/src/client/daemon-client-transport.test.ts @@ -89,12 +89,12 @@ describe("daemon-client transport helpers", () => { }); test("createWebSocketTransportFactory binds and unbinds event listeners", () => { - const listeners = new Map void>(); + const listeners = new Map void>(); const ws = { readyState: 1, send: vi.fn(), close: vi.fn(), - addEventListener: vi.fn((event: string, handler: (...args: any[]) => void) => { + addEventListener: vi.fn((event: string, handler: (...args: unknown[]) => void) => { listeners.set(event, handler); }), removeEventListener: vi.fn((event: string) => { diff --git a/packages/server/src/client/daemon-client-websocket-transport.ts b/packages/server/src/client/daemon-client-websocket-transport.ts index fdf4a9a38..d11c3fe47 100644 --- a/packages/server/src/client/daemon-client-websocket-transport.ts +++ b/packages/server/src/client/daemon-client-websocket-transport.ts @@ -8,7 +8,7 @@ export function defaultWebSocketFactory( url: string, _options?: { headers?: Record }, ): WebSocketLike { - const globalWs = (globalThis as { WebSocket?: any }).WebSocket; + const globalWs = (globalThis as { WebSocket?: new (url: string) => WebSocketLike }).WebSocket; if (!globalWs) { throw new Error("WebSocket is not available in this runtime"); } @@ -102,7 +102,7 @@ function bindTemporaryEarlyCloseErrorHandler(ws: WebSocketLike): () => void { export function bindWsHandler( ws: WebSocketLike, event: "open" | "close" | "error" | "message", - handler: (...args: any[]) => void, + handler: (...args: unknown[]) => void, ): () => void { if (typeof ws.addEventListener === "function") { ws.addEventListener(event, handler); @@ -125,11 +125,12 @@ export function bindWsHandler( }; } const prop = `on${event}` as "onopen" | "onclose" | "onerror" | "onmessage"; - const previous = (ws as any)[prop]; - (ws as any)[prop] = handler; + const wsRecord = ws as unknown as Record; + const previous = wsRecord[prop]; + wsRecord[prop] = handler; return () => { - if ((ws as any)[prop] === handler) { - (ws as any)[prop] = previous ?? null; + if (wsRecord[prop] === handler) { + wsRecord[prop] = previous ?? null; } }; } diff --git a/packages/server/src/server/agent/agent-metadata-generator.unit.test.ts b/packages/server/src/server/agent/agent-metadata-generator.unit.test.ts index 718483cd4..35998690b 100644 --- a/packages/server/src/server/agent/agent-metadata-generator.unit.test.ts +++ b/packages/server/src/server/agent/agent-metadata-generator.unit.test.ts @@ -159,7 +159,10 @@ describe("agent metadata generator auto-title", () => { deps: { generateStructuredAgentResponseWithFallback: generateStructured, renameCurrentBranch, - workspaceGitService: workspaceGitService as any, + workspaceGitService: workspaceGitService as unknown as Pick< + import("../workspace-git-service.js").WorkspaceGitService, + "getSnapshot" + >, }, }); @@ -198,7 +201,10 @@ describe("agent metadata generator auto-title", () => { deps: { generateStructuredAgentResponseWithFallback: generateStructured, renameCurrentBranch, - workspaceGitService: workspaceGitService as any, + workspaceGitService: workspaceGitService as unknown as Pick< + import("../workspace-git-service.js").WorkspaceGitService, + "getSnapshot" + >, }, }); diff --git a/packages/server/src/server/agent/agent-projections.test.ts b/packages/server/src/server/agent/agent-projections.test.ts index fef36e1fa..f9f4f3b8a 100644 --- a/packages/server/src/server/agent/agent-projections.test.ts +++ b/packages/server/src/server/agent/agent-projections.test.ts @@ -2,6 +2,7 @@ import { describe, expect, it } from "vitest"; import { AGENT_LIFECYCLE_STATUSES, type AgentLifecycleStatus } from "./agent-manager.js"; import { toAgentPayload, toStoredAgentRecord, type ManagedAgent } from "./agent-projections.js"; +import type { AgentSession } from "./agent-sdk-types.js"; import type { AgentFeature, AgentPermissionRequest, @@ -40,7 +41,8 @@ function createManagedAgent(overrides: ManagedAgentOverrides = {}): ManagedAgent ...restOverrides } = overrides; - const sessionValue = lifecycle === "closed" ? null : (restOverrides.session ?? ({} as any)); + const sessionValue = + lifecycle === "closed" ? null : (restOverrides.session ?? ({} as AgentSession)); const activeForegroundTurnIdValue = restOverrides.activeForegroundTurnId ?? (lifecycle === "running" ? "test-turn-id" : null); const lastErrorValue = @@ -250,7 +252,7 @@ describe("toAgentPayload", () => { expect(payload.lastUsage).toEqual(agent.lastUsage); expect(payload.lastUsage).not.toBe(agent.lastUsage); expect(payload.lastError).toBe("boom"); - expect((payload as any).session).toBeUndefined(); + expect((payload as unknown as { session?: unknown }).session).toBeUndefined(); payload.availableModes[0].label = "Changed"; expect(agent.availableModes[0].label).toBe("Planning"); @@ -310,7 +312,7 @@ describe("toAgentPayload", () => { persistence: { provider: "codex", sessionId: "persist-99", - nativeHandle: { id: "native" } as any, + nativeHandle: { id: "native" } as unknown, metadata: { restored: new Date("2025-03-01T00:00:00.000Z"), empty: {} }, }, }); diff --git a/packages/server/src/server/agent/mcp-shared.test.ts b/packages/server/src/server/agent/mcp-shared.test.ts index e8b286a82..809217796 100644 --- a/packages/server/src/server/agent/mcp-shared.test.ts +++ b/packages/server/src/server/agent/mcp-shared.test.ts @@ -71,6 +71,10 @@ it("does not notify archived callers", async () => { expect(agentStorage.get).toHaveBeenCalledWith("caller-agent"); }); - expect((agentManager as any).streamAgent).not.toHaveBeenCalled(); - expect((agentManager as any).replaceAgentRun).not.toHaveBeenCalled(); + expect( + (agentManager as unknown as { streamAgent: ReturnType }).streamAgent, + ).not.toHaveBeenCalled(); + expect( + (agentManager as unknown as { replaceAgentRun: ReturnType }).replaceAgentRun, + ).not.toHaveBeenCalled(); }); diff --git a/packages/server/src/server/bootstrap.ts b/packages/server/src/server/bootstrap.ts index 8ee1e5aa5..c453b19fe 100644 --- a/packages/server/src/server/bootstrap.ts +++ b/packages/server/src/server/bootstrap.ts @@ -1,5 +1,5 @@ import express from "express"; -import { createServer as createHTTPServer } from "http"; +import { createServer as createHTTPServer, type IncomingMessage, type ServerResponse } from "http"; import { createReadStream, unlinkSync, existsSync } from "fs"; import { stat } from "fs/promises"; import { randomUUID } from "node:crypto"; @@ -644,7 +644,11 @@ export async function createPaseoDaemon( transport = await createAgentMcpTransport(callerAgentId); } - await transport.handleRequest(req as any, res as any, req.body); + await transport.handleRequest( + req as unknown as IncomingMessage, + res as unknown as ServerResponse, + req.body, + ); } catch (err) { logger.error({ err }, "Failed to handle Agent MCP request"); if (!res.headersSent) { diff --git a/packages/server/src/server/checkout-diff-manager.test.ts b/packages/server/src/server/checkout-diff-manager.test.ts index a14f0d386..fc3f6b1b8 100644 --- a/packages/server/src/server/checkout-diff-manager.test.ts +++ b/packages/server/src/server/checkout-diff-manager.test.ts @@ -10,7 +10,9 @@ vi.mock("./checkout-git-utils.js", () => ({ toCheckoutError: toCheckoutErrorMock, })); +import type pino from "pino"; import { CheckoutDiffManager } from "./checkout-diff-manager.js"; +import type { WorkspaceGitService } from "./workspace-git-service.js"; describe("CheckoutDiffManager", () => { beforeEach(() => { @@ -54,9 +56,9 @@ describe("CheckoutDiffManager", () => { }; const manager = new CheckoutDiffManager({ - logger: logger as any, + logger: logger as unknown as pino.Logger, paseoHome: "/tmp/paseo-test", - workspaceGitService: workspaceGitService as any, + workspaceGitService: workspaceGitService as unknown as WorkspaceGitService, }); return { diff --git a/packages/server/src/server/daemon-e2e/git-operations.e2e.test.ts b/packages/server/src/server/daemon-e2e/git-operations.e2e.test.ts index b4cd80300..9886a64af 100644 --- a/packages/server/src/server/daemon-e2e/git-operations.e2e.test.ts +++ b/packages/server/src/server/daemon-e2e/git-operations.e2e.test.ts @@ -26,7 +26,7 @@ function findTimelineToolCall( if (msg.payload.agentId !== agentId) { continue; } - const event = msg.payload.event as any; + const event = msg.payload.event as { type?: string; item?: AgentTimelineItem }; if (event?.type !== "timeline") { continue; } @@ -57,7 +57,7 @@ async function waitForTimelineToolCall( const msg = messages[i]; if (msg?.type !== "agent_stream") continue; if (msg.payload.agentId !== agentId) continue; - const event = msg.payload.event as any; + const event = msg.payload.event as { type?: string; item?: AgentTimelineItem }; if (event?.type !== "timeline") continue; const item = event.item as AgentTimelineItem; if (item?.type !== "tool_call") continue; diff --git a/packages/server/src/server/relay-transport.test.ts b/packages/server/src/server/relay-transport.test.ts index bd3d32d43..42ca80095 100644 --- a/packages/server/src/server/relay-transport.test.ts +++ b/packages/server/src/server/relay-transport.test.ts @@ -13,7 +13,7 @@ const wsMock = vi.hoisted(() => { readyState = MockWebSocket.CONNECTING; sent: string[] = []; terminateCalls = 0; - private listeners = new Map void>>(); + private listeners = new Map void>>(); constructor(url: string, options?: unknown) { this.url = url; @@ -25,15 +25,15 @@ const wsMock = vi.hoisted(() => { MockWebSocket.instances = []; } - on(event: string, listener: (...args: any[]) => void) { + on(event: string, listener: (...args: unknown[]) => void) { const handlers = this.listeners.get(event) ?? []; handlers.push(listener); this.listeners.set(event, handlers); return this; } - once(event: string, listener: (...args: any[]) => void) { - const wrapped = (...args: any[]) => { + once(event: string, listener: (...args: unknown[]) => void) { + const wrapped = (...args: unknown[]) => { this.off(event, wrapped); listener(...args); }; @@ -71,7 +71,7 @@ const wsMock = vi.hoisted(() => { this.emit("error", err); } - private off(event: string, listener: (...args: any[]) => void) { + private off(event: string, listener: (...args: unknown[]) => void) { const handlers = this.listeners.get(event) ?? []; this.listeners.set( event, @@ -79,7 +79,7 @@ const wsMock = vi.hoisted(() => { ); } - private emit(event: string, ...args: any[]) { + private emit(event: string, ...args: unknown[]) { const handlers = this.listeners.get(event) ?? []; for (const handler of [...handlers]) { handler(...args); @@ -92,6 +92,7 @@ const wsMock = vi.hoisted(() => { vi.mock("ws", () => ({ default: wsMock.MockWebSocket })); +import type pino from "pino"; import { startRelayTransport } from "./relay-transport"; function createMockLogger() { @@ -128,7 +129,7 @@ describe("relay-transport control lifecycle", () => { test("logs relay_control_connected only after first valid control message", () => { const logger = createMockLogger(); const controller = startRelayTransport({ - logger: logger as any, + logger: logger as unknown as pino.Logger, attachSocket: async () => {}, relayEndpoint: "relay.paseo.sh:443", serverId: "srv_test", @@ -150,7 +151,7 @@ describe("relay-transport control lifecycle", () => { vi.useFakeTimers(); const logger = createMockLogger(); const controller = startRelayTransport({ - logger: logger as any, + logger: logger as unknown as pino.Logger, attachSocket: async () => {}, relayEndpoint: "relay.paseo.sh:443", serverId: "srv_test", @@ -172,7 +173,7 @@ describe("relay-transport control lifecycle", () => { vi.useFakeTimers(); const logger = createMockLogger(); const controller = startRelayTransport({ - logger: logger as any, + logger: logger as unknown as pino.Logger, attachSocket: async () => {}, relayEndpoint: "relay.paseo.sh:443", serverId: "srv_test", @@ -193,7 +194,7 @@ describe("relay-transport control lifecycle", () => { const logger = createMockLogger(); const attachSocket = vi.fn(async () => {}); const controller = startRelayTransport({ - logger: logger as any, + logger: logger as unknown as pino.Logger, attachSocket, relayEndpoint: "relay.paseo.sh:443", serverId: "srv_test", diff --git a/packages/server/src/server/session.ts b/packages/server/src/server/session.ts index 30988de8d..cc58b2603 100644 --- a/packages/server/src/server/session.ts +++ b/packages/server/src/server/session.ts @@ -1642,7 +1642,7 @@ export class Session { ); try { await this.dispatchInboundMessage(msg); - } catch (error: any) { + } catch (error) { const err = error instanceof Error ? error : new Error(String(error)); this.sessionLogger.error({ err }, "Error handling message"); @@ -2131,7 +2131,7 @@ export class Session { try { await this.agentManager.closeAgent(agentId); - } catch (error: any) { + } catch (error) { this.sessionLogger.warn( { err: error, agentId }, `Failed to close agent ${agentId} during delete`, @@ -2145,7 +2145,7 @@ export class Session { try { await this.agentStorage.remove(agentId); await this.agentManager.deleteCommittedTimeline(agentId); - } catch (error: any) { + } catch (error) { this.sessionLogger.error({ err: error, agentId }, `Failed to fully delete agent ${agentId}`); } @@ -2281,7 +2281,7 @@ export class Session { for (const terminalId of msg.terminalIds) { try { terminals.push(this.killTerminalForClose(terminalId)); - } catch (error: any) { + } catch (error) { this.sessionLogger.warn( { err: error, terminalId, requestId: msg.requestId }, "Failed to kill terminal during close_items batch", @@ -2358,7 +2358,7 @@ export class Session { type: "update_agent_response", payload: { requestId, agentId, accepted: true, error: null }, }); - } catch (error: any) { + } catch (error) { this.sessionLogger.error( { err: error, agentId, requestId }, "session: update_agent_request error", @@ -2369,7 +2369,7 @@ export class Session { id: uuidv4(), timestamp: new Date(), type: "error", - content: `Failed to update agent: ${error.message}`, + content: `Failed to update agent: ${(error as Error).message}`, }, }); this.emit({ @@ -2378,7 +2378,9 @@ export class Session { requestId, agentId, accepted: false, - error: error?.message ? String(error.message) : "Failed to update agent", + error: (error as Error | undefined)?.message + ? String((error as Error).message) + : "Failed to update agent", }, }); } @@ -2943,7 +2945,7 @@ export class Session { { agentId: snapshot.id, provider: snapshot.provider }, `Created agent ${snapshot.id} (${snapshot.provider})`, ); - } catch (error: any) { + } catch (error) { const wireError = toWorktreeWireError(error); this.sessionLogger.error({ err: error }, "Failed to create agent"); if (requestId) { @@ -3052,7 +3054,7 @@ export class Session { }, }); } - } catch (error: any) { + } catch (error) { this.sessionLogger.error({ err: error }, "Failed to resume agent"); this.emit({ type: "activity_log", @@ -3060,7 +3062,7 @@ export class Session { id: uuidv4(), timestamp: new Date(), type: "error", - content: `Failed to resume agent: ${error.message}`, + content: `Failed to resume agent: ${(error as Error).message}`, }, }); } @@ -3113,7 +3115,7 @@ export class Session { }, }); } - } catch (error: any) { + } catch (error) { this.sessionLogger.error({ err: error, agentId }, `Failed to refresh agent ${agentId}`); this.emit({ type: "activity_log", @@ -3121,7 +3123,7 @@ export class Session { id: uuidv4(), timestamp: new Date(), type: "error", - content: `Failed to refresh agent: ${error.message}`, + content: `Failed to refresh agent: ${(error as Error).message}`, }, }); } @@ -3782,7 +3784,7 @@ export class Session { type: "set_agent_mode_response", payload: { requestId, agentId, accepted: true, error: null }, }); - } catch (error: any) { + } catch (error) { this.sessionLogger.error( { err: error, agentId, modeId, requestId }, "session: set_agent_mode_request error", @@ -3793,7 +3795,7 @@ export class Session { id: uuidv4(), timestamp: new Date(), type: "error", - content: `Failed to set agent mode: ${error.message}`, + content: `Failed to set agent mode: ${(error as Error).message}`, }, }); this.emit({ @@ -3802,7 +3804,9 @@ export class Session { requestId, agentId, accepted: false, - error: error?.message ? String(error.message) : "Failed to set agent mode", + error: (error as Error | undefined)?.message + ? String((error as Error).message) + : "Failed to set agent mode", }, }); } @@ -3825,7 +3829,7 @@ export class Session { type: "set_agent_model_response", payload: { requestId, agentId, accepted: true, error: null }, }); - } catch (error: any) { + } catch (error) { this.sessionLogger.error( { err: error, agentId, modelId, requestId }, "session: set_agent_model_request error", @@ -3836,7 +3840,7 @@ export class Session { id: uuidv4(), timestamp: new Date(), type: "error", - content: `Failed to set agent model: ${error.message}`, + content: `Failed to set agent model: ${(error as Error).message}`, }, }); this.emit({ @@ -3845,7 +3849,9 @@ export class Session { requestId, agentId, accepted: false, - error: error?.message ? String(error.message) : "Failed to set agent model", + error: (error as Error | undefined)?.message + ? String((error as Error).message) + : "Failed to set agent model", }, }); } @@ -3872,7 +3878,7 @@ export class Session { type: "set_agent_feature_response", payload: { requestId, agentId, accepted: true, error: null }, }); - } catch (error: any) { + } catch (error) { this.sessionLogger.error( { err: error, agentId, featureId, value, requestId }, "session: set_agent_feature_request error", @@ -3883,7 +3889,7 @@ export class Session { id: uuidv4(), timestamp: new Date(), type: "error", - content: `Failed to set agent feature: ${error.message}`, + content: `Failed to set agent feature: ${(error as Error).message}`, }, }); this.emit({ @@ -3892,7 +3898,9 @@ export class Session { requestId, agentId, accepted: false, - error: error?.message ? String(error.message) : "Failed to set agent feature", + error: (error as Error | undefined)?.message + ? String((error as Error).message) + : "Failed to set agent feature", }, }); } @@ -3918,7 +3926,7 @@ export class Session { type: "set_agent_thinking_response", payload: { requestId, agentId, accepted: true, error: null }, }); - } catch (error: any) { + } catch (error) { this.sessionLogger.error( { err: error, agentId, thinkingOptionId, requestId }, "session: set_agent_thinking_request error", @@ -3929,7 +3937,7 @@ export class Session { id: uuidv4(), timestamp: new Date(), type: "error", - content: `Failed to set agent thinking option: ${error.message}`, + content: `Failed to set agent thinking option: ${(error as Error).message}`, }, }); this.emit({ @@ -3938,7 +3946,9 @@ export class Session { requestId, agentId, accepted: false, - error: error?.message ? String(error.message) : "Failed to set agent thinking option", + error: (error as Error | undefined)?.message + ? String((error as Error).message) + : "Failed to set agent thinking option", }, }); } @@ -3973,7 +3983,7 @@ export class Session { }, }); } - } catch (error: any) { + } catch (error) { this.sessionLogger.error({ err: error, agentIds }, "Failed to clear agent attention"); // Don't throw - this is not critical } @@ -4072,14 +4082,14 @@ export class Session { requestId, }, }); - } catch (error: any) { + } catch (error) { this.sessionLogger.error({ err: error, agentId, draftConfig }, "Failed to list commands"); this.emit({ type: "list_commands_response", payload: { agentId, commands: [], - error: error.message, + error: (error as Error).message, requestId, }, }); @@ -4110,7 +4120,7 @@ export class Session { ); this.startAgentStream(agentId, result.followUpPrompt); } - } catch (error: any) { + } catch (error) { this.sessionLogger.error( { err: error, agentId, requestId }, "Failed to respond to permission", @@ -4121,7 +4131,7 @@ export class Session { id: uuidv4(), timestamp: new Date(), type: "error", - content: `Failed to respond to permission: ${error.message}`, + content: `Failed to respond to permission: ${(error as Error).message}`, }, }); throw error; @@ -5195,7 +5205,7 @@ export class Session { }, }); } - } catch (error: any) { + } catch (error) { this.sessionLogger.error( { err: error, cwd, path: requestedPath }, `Failed to fulfill file explorer request for workspace ${cwd}`, @@ -5208,7 +5218,7 @@ export class Session { mode, directory: null, file: null, - error: error.message, + error: (error as Error).message, requestId, }, }); @@ -5234,13 +5244,13 @@ export class Session { requestId, }, }); - } catch (error: any) { + } catch (error) { this.emit({ type: "project_icon_response", payload: { cwd, icon: null, - error: error.message, + error: (error as Error).message, requestId, }, }); @@ -5302,7 +5312,7 @@ export class Session { requestId, }, }); - } catch (error: any) { + } catch (error) { this.sessionLogger.error( { err: error, cwd, path: requestedPath }, `Failed to issue download token for workspace ${cwd}`, @@ -5316,7 +5326,7 @@ export class Session { fileName: null, mimeType: null, size: null, - error: error.message, + error: (error as Error).message, requestId, }, }); @@ -7746,7 +7756,7 @@ export class Session { format: result.format, debugRecordingPath: result.debugRecordingPath, }); - } catch (error: any) { + } catch (error) { this.setPhase("idle"); this.clearSpeechInProgress("transcription error"); await this.flushPendingAudioSegments("transcription error"); @@ -7756,7 +7766,7 @@ export class Session { id: uuidv4(), timestamp: new Date(), type: "error", - content: `Transcription error: ${error.message}`, + content: `Transcription error: ${(error as Error).message}`, }, }); throw error; @@ -8816,7 +8826,7 @@ export class Session { requestId: msg.requestId, }, }); - } catch (error: any) { + } catch (error) { this.sessionLogger.error({ err: error, cwd: msg.cwd }, "Failed to list terminals"); this.emit({ type: "list_terminals_response", @@ -8887,13 +8897,13 @@ export class Session { requestId: msg.requestId, }, }); - } catch (error: any) { + } catch (error) { this.sessionLogger.error({ err: error, cwd: msg.cwd }, "Failed to create terminal"); this.emit({ type: "create_terminal_response", payload: { terminal: null, - error: error.message, + error: (error as Error).message, requestId: msg.requestId, }, }); @@ -9081,7 +9091,7 @@ export class Session { requestId: msg.requestId, }, }); - } catch (error: any) { + } catch (error) { this.sessionLogger.error( { err: error, terminalId: msg.terminalId }, "Failed to capture terminal", diff --git a/packages/server/src/server/session.wait-for-finish.test.ts b/packages/server/src/server/session.wait-for-finish.test.ts index d84a5c1c8..8216d0c9d 100644 --- a/packages/server/src/server/session.wait-for-finish.test.ts +++ b/packages/server/src/server/session.wait-for-finish.test.ts @@ -1,13 +1,14 @@ import { describe, expect, test } from "vitest"; import { resolveWaitForFinishError } from "./session.js"; +import type { AgentSnapshotPayload } from "./messages.js"; describe("resolveWaitForFinishError", () => { test("returns the agent error when the wait result is an error", () => { expect( resolveWaitForFinishError({ status: "error", - final: { lastError: "invalid_json_schema" } as any, + final: { lastError: "invalid_json_schema" } as unknown as AgentSnapshotPayload, }), ).toBe("invalid_json_schema"); }); @@ -16,7 +17,7 @@ describe("resolveWaitForFinishError", () => { expect( resolveWaitForFinishError({ status: "error", - final: {} as any, + final: {} as unknown as AgentSnapshotPayload, }), ).toBe("Agent failed"); }); @@ -25,7 +26,7 @@ describe("resolveWaitForFinishError", () => { expect( resolveWaitForFinishError({ status: "idle", - final: { lastError: "should not surface" } as any, + final: { lastError: "should not surface" } as unknown as AgentSnapshotPayload, }), ).toBeNull(); }); diff --git a/packages/server/src/server/speech/providers/local/pocket/pocket-tts-onnx.ts b/packages/server/src/server/speech/providers/local/pocket/pocket-tts-onnx.ts index 4487dec73..6a7e7c668 100644 --- a/packages/server/src/server/speech/providers/local/pocket/pocket-tts-onnx.ts +++ b/packages/server/src/server/speech/providers/local/pocket/pocket-tts-onnx.ts @@ -52,17 +52,23 @@ function getSessionInputMeta( session: OrtSession, inputName: string, ): { type?: string; dims?: Array } | undefined { - const metaAny = (session as any).inputMetadata as unknown; + const metaAny = (session as unknown as { inputMetadata?: unknown }).inputMetadata; if (Array.isArray(metaAny)) { const entry = metaAny.find( - (m) => m && typeof m === "object" && (m as any).name === inputName, - ) as any; + (m) => m && typeof m === "object" && (m as { name?: string }).name === inputName, + ) as { type?: string; shape?: Array } | undefined; if (!entry) return undefined; return { type: entry.type, dims: entry.shape }; } - if (metaAny && typeof metaAny === "object" && inputName in (metaAny as any)) { - const entry = (metaAny as any)[inputName] as any; + if (metaAny && typeof metaAny === "object" && inputName in metaAny) { + const entry = (metaAny as Record)[inputName] as + | { + type?: string; + dimensions?: Array; + shape?: Array; + } + | undefined; return { type: entry?.type, dims: entry?.dimensions ?? entry?.shape }; } @@ -108,10 +114,19 @@ async function loadOrt(): Promise { async function loadSentencePiece(tokenizerModelPath: string): Promise { const mod = await import("@sctg/sentencepiece-js"); + const modRecord = mod as unknown as { + SentencePieceProcessor?: new () => SentencePieceProcessor; + default?: + | (new () => SentencePieceProcessor) + | { SentencePieceProcessor?: new () => SentencePieceProcessor }; + }; + const defaultValue = modRecord.default; const Processor = - (mod as any).SentencePieceProcessor ?? - (mod as any).default?.SentencePieceProcessor ?? - (mod as any).default; + modRecord.SentencePieceProcessor ?? + (defaultValue && typeof defaultValue === "object" && "SentencePieceProcessor" in defaultValue + ? defaultValue.SentencePieceProcessor + : undefined) ?? + (typeof defaultValue === "function" ? defaultValue : undefined); if (!Processor) { throw new Error("Failed to load SentencePiece processor from @sctg/sentencepiece-js"); @@ -166,7 +181,8 @@ function createZeroTensorForInput( function initState(session: OrtSession, ort: OrtModule): Record { const out: Record = {}; - for (const name of (session as any).inputNames as string[]) { + const inputNames = (session as unknown as { inputNames: string[] }).inputNames; + for (const name of inputNames) { if (name.startsWith("state_")) { out[name] = createZeroTensorForInput(ort, session, name); } @@ -188,7 +204,7 @@ function updateStateFromOutputs( } function tensorDataFloat32(t: OrtTensor): Float32Array { - const data = (t as any).data; + const data = (t as unknown as { data: unknown }).data; if (data instanceof Float32Array) return data; if (Array.isArray(data)) return Float32Array.from(data as number[]); throw new Error("Unexpected tensor data type (expected Float32Array)"); @@ -346,10 +362,11 @@ class PocketTtsOnnxEngine { const audioTensor = new ort.Tensor("float32", floatAudio, [1, 1, floatAudio.length]); const encoded = await mimiEncoder.run({ audio: audioTensor }); - const firstOutName = (mimiEncoder as any).outputNames?.[0] as string | undefined; + const firstOutName = (mimiEncoder as unknown as { outputNames?: string[] }).outputNames?.[0]; + const encodedRecord = encoded as unknown as Record; const voiceEmb = firstOutName - ? (encoded as any)[firstOutName] - : (Object.values(encoded)[0] as any); + ? encodedRecord[firstOutName] + : (Object.values(encodedRecord)[0] as OrtTensor | undefined); if (!voiceEmb) { throw new Error("PocketTTS mimi_encoder: missing output"); } @@ -382,9 +399,15 @@ class PocketTtsOnnxEngine { } private async runTextConditioner(tokenIds: OrtTensor): Promise { - const out = await this.textConditioner.run({ token_ids: tokenIds } as any); - const firstOutName = (this.textConditioner as any).outputNames?.[0] as string | undefined; - const t = firstOutName ? (out as any)[firstOutName] : (Object.values(out)[0] as any); + const out = await this.textConditioner.run({ + token_ids: tokenIds, + } as unknown as Record); + const firstOutName = (this.textConditioner as unknown as { outputNames?: string[] }) + .outputNames?.[0]; + const outRecord = out as unknown as Record; + const t = firstOutName + ? outRecord[firstOutName] + : (Object.values(outRecord)[0] as OrtTensor | undefined); if (!t) throw new Error("PocketTTS text_conditioner: missing output"); return t; } @@ -401,16 +424,16 @@ class PocketTtsOnnxEngine { sequence: emptySeq, text_embeddings: this.voiceEmbeddings, ...state, - } as any); - updateStateFromOutputs(state, resVoice as any); + } as unknown as Record); + updateStateFromOutputs(state, resVoice as unknown as Record); // Text conditioning pass const resText = await this.flowLmMain.run({ sequence: emptySeq, text_embeddings: textEmbeddings, ...state, - } as any); - updateStateFromOutputs(state, resText as any); + } as unknown as Record); + updateStateFromOutputs(state, resText as unknown as Record); // Autoregressive generation const curr = new Float32Array(32); @@ -425,18 +448,19 @@ class PocketTtsOnnxEngine { sequence: currTensor, text_embeddings: emptyText, ...state, - } as any); + } as unknown as Record); - const outputNames = (this.flowLmMain as any).outputNames as string[] | undefined; - const conditioningName = outputNames?.[0] ?? Object.keys(resStep)[0]!; - const eosName = outputNames?.[1] ?? Object.keys(resStep)[1]!; + const outputNames = (this.flowLmMain as unknown as { outputNames?: string[] }).outputNames; + const resStepRecord = resStep as unknown as Record; + const conditioningName = outputNames?.[0] ?? Object.keys(resStepRecord)[0]!; + const eosName = outputNames?.[1] ?? Object.keys(resStepRecord)[1]!; - const conditioning = (resStep as any)[conditioningName] as OrtTensor; - const eos = (resStep as any)[eosName] as OrtTensor; + const conditioning = resStepRecord[conditioningName]; + const eos = resStepRecord[eosName]; if (!conditioning || !eos) { throw new Error("PocketTTS flow_lm_main: missing conditioning/EOS outputs"); } - updateStateFromOutputs(state, resStep as any); + updateStateFromOutputs(state, resStepRecord); const eosData = tensorDataFloat32(eos); if (eosData[0]! > -4.0 && eosStep === null) { @@ -462,9 +486,12 @@ class PocketTtsOnnxEngine { s: st.s, t: st.t, x: xTensor, - } as any); - const first = (this.flowLmFlow as any).outputNames?.[0] as string | undefined; - const flowTensor = first ? (flowOut as any)[first] : (Object.values(flowOut)[0] as any); + } as unknown as Record); + const first = (this.flowLmFlow as unknown as { outputNames?: string[] }).outputNames?.[0]; + const flowOutRecord = flowOut as unknown as Record; + const flowTensor = first + ? flowOutRecord[first] + : (Object.values(flowOutRecord)[0] as OrtTensor | undefined); if (!flowTensor) throw new Error("PocketTTS flow_lm_flow: missing output"); const delta = tensorDataFloat32(flowTensor); for (let i = 0; i < x.length; i += 1) { @@ -489,11 +516,18 @@ class PocketTtsOnnxEngine { } const latent = new ort.Tensor("float32", flattened, [1, frameCount, 32]); - const out = await this.mimiDecoder.run({ latent, ...state } as any); - updateStateFromOutputs(state, out as any); + const out = await this.mimiDecoder.run({ + latent, + ...state, + } as unknown as Record); + const outRecord = out as unknown as Record; + updateStateFromOutputs(state, outRecord); - const firstOutName = (this.mimiDecoder as any).outputNames?.[0] as string | undefined; - const audioTensor = firstOutName ? (out as any)[firstOutName] : (Object.values(out)[0] as any); + const firstOutName = (this.mimiDecoder as unknown as { outputNames?: string[] }) + .outputNames?.[0]; + const audioTensor = firstOutName + ? outRecord[firstOutName] + : (Object.values(outRecord)[0] as OrtTensor | undefined); if (!audioTensor) { throw new Error("PocketTTS mimi_decoder: missing audio output"); } diff --git a/packages/server/src/server/speech/providers/local/sherpa/sherpa-offline-recognizer.ts b/packages/server/src/server/speech/providers/local/sherpa/sherpa-offline-recognizer.ts index f0f339f6d..cfde73add 100644 --- a/packages/server/src/server/speech/providers/local/sherpa/sherpa-offline-recognizer.ts +++ b/packages/server/src/server/speech/providers/local/sherpa/sherpa-offline-recognizer.ts @@ -28,8 +28,22 @@ export interface SherpaOfflineRecognizerConfig { maxActivePaths?: number; } +interface SherpaOfflineRecognizerNative { + config?: { featConfig?: { sampleRate?: number } }; + createStream: () => unknown; + decode: (stream: unknown) => void; + getResult: (stream: unknown) => { text?: string } | string | undefined; + free?: () => void; +} + +interface SherpaOfflineStreamNative { + acceptWaveform: ((arg: { samples: Float32Array; sampleRate: number }) => void) & + ((sampleRate: number, samples: Float32Array) => void); + free?: () => void; +} + export class SherpaOfflineRecognizerEngine { - public readonly recognizer: any; + public readonly recognizer: SherpaOfflineRecognizerNative; public readonly sampleRate: number; private readonly logger: pino.Logger; @@ -68,7 +82,11 @@ export class SherpaOfflineRecognizerEngine { maxActivePaths: config.maxActivePaths ?? 4, }; - this.recognizer = new sherpa.OfflineRecognizer(recognizerConfig); + this.recognizer = new ( + sherpa as unknown as { + OfflineRecognizer: new (config: unknown) => SherpaOfflineRecognizerNative; + } + ).OfflineRecognizer(recognizerConfig); const sr = this.recognizer?.config?.featConfig?.sampleRate; this.sampleRate = typeof sr === "number" && Number.isFinite(sr) && sr > 0 @@ -81,11 +99,15 @@ export class SherpaOfflineRecognizerEngine { ); } - createStream(): any { - return this.recognizer.createStream(); + createStream(): SherpaOfflineStreamNative { + return this.recognizer.createStream() as SherpaOfflineStreamNative; } - acceptWaveform(stream: any, sampleRate: number, samples: Float32Array): void { + acceptWaveform( + stream: SherpaOfflineStreamNative, + sampleRate: number, + samples: Float32Array, + ): void { if (!stream || typeof stream.acceptWaveform !== "function") { throw new Error("Unexpected sherpa offline stream: missing acceptWaveform()"); } diff --git a/packages/server/src/server/speech/providers/local/sherpa/sherpa-online-recognizer.ts b/packages/server/src/server/speech/providers/local/sherpa/sherpa-online-recognizer.ts index fb87cb5e2..6c728ec3c 100644 --- a/packages/server/src/server/speech/providers/local/sherpa/sherpa-online-recognizer.ts +++ b/packages/server/src/server/speech/providers/local/sherpa/sherpa-online-recognizer.ts @@ -74,8 +74,23 @@ function buildModelConfig(model: SherpaOnlineRecognizerModel): object { }; } +export interface SherpaOnlineStreamNative { + acceptWaveform: (sampleRate: number, samples: Float32Array) => void; + free?: () => void; +} + +export interface SherpaOnlineRecognizerNative { + config?: { featConfig?: { sampleRate?: number } }; + createStream: () => SherpaOnlineStreamNative; + isReady: (stream: SherpaOnlineStreamNative) => boolean; + decode: (stream: SherpaOnlineStreamNative) => void; + getResult: (stream: SherpaOnlineStreamNative) => { text?: string } | string | undefined; + reset?: (stream: SherpaOnlineStreamNative) => void; + free?: () => void; +} + export class SherpaOnlineRecognizerEngine { - public readonly recognizer: any; + public readonly recognizer: SherpaOnlineRecognizerNative; public readonly sampleRate: number; private readonly logger: pino.Logger; @@ -115,7 +130,9 @@ export class SherpaOnlineRecognizerEngine { rule3MinUtteranceLength: config.rule3MinUtteranceLength ?? 20, }; - this.recognizer = sherpa.createOnlineRecognizer(recognizerConfig); + this.recognizer = sherpa.createOnlineRecognizer( + recognizerConfig, + ) as SherpaOnlineRecognizerNative; const sr = this.recognizer?.config?.featConfig?.sampleRate; this.sampleRate = typeof sr === "number" && Number.isFinite(sr) && sr > 0 ? sr : featConfig.sampleRate; @@ -126,7 +143,7 @@ export class SherpaOnlineRecognizerEngine { ); } - createStream(): any { + createStream(): SherpaOnlineStreamNative { return this.recognizer.createStream(); } diff --git a/packages/server/src/server/speech/providers/local/sherpa/sherpa-onnx-loader.ts b/packages/server/src/server/speech/providers/local/sherpa/sherpa-onnx-loader.ts index 89189e53e..a8c25ba5f 100644 --- a/packages/server/src/server/speech/providers/local/sherpa/sherpa-onnx-loader.ts +++ b/packages/server/src/server/speech/providers/local/sherpa/sherpa-onnx-loader.ts @@ -1,9 +1,9 @@ import { createRequire } from "node:module"; export interface SherpaOnnxModule { - createOnlineRecognizer: (config: any) => any; - createOfflineRecognizer: (config: any) => any; - createOfflineTts: (config: any) => any; + createOnlineRecognizer: (config: unknown) => unknown; + createOfflineRecognizer: (config: unknown) => unknown; + createOfflineTts: (config: unknown) => unknown; } let cached: SherpaOnnxModule | null = null; diff --git a/packages/server/src/server/speech/providers/local/sherpa/sherpa-parakeet-realtime-session.ts b/packages/server/src/server/speech/providers/local/sherpa/sherpa-parakeet-realtime-session.ts index c0b1f38ef..a299b32be 100644 --- a/packages/server/src/server/speech/providers/local/sherpa/sherpa-parakeet-realtime-session.ts +++ b/packages/server/src/server/speech/providers/local/sherpa/sherpa-parakeet-realtime-session.ts @@ -147,7 +147,11 @@ export class SherpaParakeetRealtimeTranscriptionSession this.engine.acceptWaveform(stream, this.engine.sampleRate, floatSamples); this.engine.recognizer.decode(stream); const result = this.engine.recognizer.getResult(stream); - return String(result?.text ?? result ?? "").trim(); + return String( + (typeof result === "object" && result && "text" in result ? result.text : undefined) ?? + result ?? + "", + ).trim(); } finally { try { stream.free?.(); diff --git a/packages/server/src/server/speech/providers/local/sherpa/sherpa-parakeet-stt.ts b/packages/server/src/server/speech/providers/local/sherpa/sherpa-parakeet-stt.ts index bd6492caa..e4b0b1082 100644 --- a/packages/server/src/server/speech/providers/local/sherpa/sherpa-parakeet-stt.ts +++ b/packages/server/src/server/speech/providers/local/sherpa/sherpa-parakeet-stt.ts @@ -53,14 +53,14 @@ export class SherpaOnnxParakeetSTT implements SpeechToTextProvider { }, appendPcm16(chunk: Buffer) { if (!connected) { - (emitter as any).emit("error", new Error("STT session not connected")); + emitter.emit("error", new Error("STT session not connected")); return; } pcm16 = pcm16.length === 0 ? chunk : Buffer.concat([pcm16, chunk]); }, commit: () => { if (!connected) { - (emitter as any).emit("error", new Error("STT session not connected")); + emitter.emit("error", new Error("STT session not connected")); return; } @@ -70,7 +70,7 @@ export class SherpaOnnxParakeetSTT implements SpeechToTextProvider { previousSegmentId = committedId; segmentId = uuidv4(); pcm16 = Buffer.alloc(0); - (emitter as any).emit("committed", { segmentId: committedId, previousSegmentId: prev }); + emitter.emit("committed", { segmentId: committedId, previousSegmentId: prev }); void (async () => { try { @@ -78,7 +78,7 @@ export class SherpaOnnxParakeetSTT implements SpeechToTextProvider { committedPcm16, `audio/pcm;rate=${requiredSampleRate}`, ); - (emitter as any).emit("transcript", { + emitter.emit("transcript", { segmentId: committedId, transcript: rt.text, isFinal: true, @@ -88,7 +88,7 @@ export class SherpaOnnxParakeetSTT implements SpeechToTextProvider { isLowConfidence: rt.isLowConfidence, }); } catch (err) { - (emitter as any).emit("error", err); + emitter.emit("error", err); } finally { logger.debug({ bytes: committedPcm16.length }, "Parakeet session reset"); } @@ -102,8 +102,8 @@ export class SherpaOnnxParakeetSTT implements SpeechToTextProvider { connected = false; pcm16 = Buffer.alloc(0); }, - on(event: any, handler: any) { - emitter.on(event, handler); + on(event: "committed" | "transcript" | "error", handler: (payload: never) => void) { + emitter.on(event, handler as (...args: unknown[]) => void); return undefined; }, }; @@ -151,7 +151,11 @@ export class SherpaOnnxParakeetSTT implements SpeechToTextProvider { this.engine.acceptWaveform(stream, inputRate, floatSamples); this.engine.recognizer.decode(stream); const result = this.engine.recognizer.getResult(stream); - const text = String(result?.text ?? result ?? "").trim(); + const text = String( + (typeof result === "object" && result && "text" in result ? result.text : undefined) ?? + result ?? + "", + ).trim(); const duration = Date.now() - start; this.logger.debug({ duration, textLength: text.length }, "Parakeet transcription complete"); return { text, duration, ...(text.length === 0 ? { isLowConfidence: true } : {}) }; diff --git a/packages/server/src/server/speech/providers/local/sherpa/sherpa-realtime-session.ts b/packages/server/src/server/speech/providers/local/sherpa/sherpa-realtime-session.ts index bbc7fdc59..fbf449130 100644 --- a/packages/server/src/server/speech/providers/local/sherpa/sherpa-realtime-session.ts +++ b/packages/server/src/server/speech/providers/local/sherpa/sherpa-realtime-session.ts @@ -3,14 +3,17 @@ import { v4 as uuidv4 } from "uuid"; import type { StreamingTranscriptionSession } from "../../../speech-provider.js"; import { pcm16lePeakAbs, pcm16leToFloat32 } from "../../../audio.js"; -import { SherpaOnlineRecognizerEngine } from "./sherpa-online-recognizer.js"; +import { + SherpaOnlineRecognizerEngine, + type SherpaOnlineStreamNative, +} from "./sherpa-online-recognizer.js"; export class SherpaRealtimeTranscriptionSession extends EventEmitter implements StreamingTranscriptionSession { private readonly engine: SherpaOnlineRecognizerEngine; - private stream: any | null = null; + private stream: SherpaOnlineStreamNative | null = null; private connected = false; public readonly requiredSampleRate: number; @@ -55,7 +58,12 @@ export class SherpaRealtimeTranscriptionSession this.engine.recognizer.decode(this.stream); } - const text = String(this.engine.recognizer.getResult(this.stream)?.text ?? "").trim(); + const rawResult = this.engine.recognizer.getResult(this.stream); + const text = String( + (typeof rawResult === "object" && rawResult && "text" in rawResult + ? rawResult.text + : undefined) ?? "", + ).trim(); if (text !== this.lastPartialText) { this.lastPartialText = text; this.emit("transcript", { @@ -88,7 +96,12 @@ export class SherpaRealtimeTranscriptionSession this.engine.recognizer.decode(this.stream); } - const finalText = String(this.engine.recognizer.getResult(this.stream)?.text ?? "").trim(); + const rawFinal = this.engine.recognizer.getResult(this.stream); + const finalText = String( + (typeof rawFinal === "object" && rawFinal && "text" in rawFinal + ? rawFinal.text + : undefined) ?? "", + ).trim(); const segmentId = this.currentSegmentId; const previousSegmentId = this.previousSegmentId; @@ -98,7 +111,7 @@ export class SherpaRealtimeTranscriptionSession this.previousSegmentId = segmentId; this.currentSegmentId = uuidv4(); this.lastPartialText = ""; - this.engine.recognizer.reset(this.stream); + this.engine.recognizer.reset?.(this.stream); } catch (err) { this.emit("error", err instanceof Error ? err : new Error(String(err))); } @@ -109,7 +122,7 @@ export class SherpaRealtimeTranscriptionSession return; } try { - this.engine.recognizer.reset(this.stream); + this.engine.recognizer.reset?.(this.stream); this.currentSegmentId = uuidv4(); this.lastPartialText = ""; } catch (err) { diff --git a/packages/server/src/server/speech/providers/local/sherpa/sherpa-stt.ts b/packages/server/src/server/speech/providers/local/sherpa/sherpa-stt.ts index 1c4afa21c..37bf45fe1 100644 --- a/packages/server/src/server/speech/providers/local/sherpa/sherpa-stt.ts +++ b/packages/server/src/server/speech/providers/local/sherpa/sherpa-stt.ts @@ -56,25 +56,25 @@ export class SherpaOnnxSTT implements SpeechToTextProvider { }, appendPcm16(chunk: Buffer) { if (!connected) { - (emitter as any).emit("error", new Error("STT session not connected")); + emitter.emit("error", new Error("STT session not connected")); return; } pcm16 = pcm16.length === 0 ? chunk : Buffer.concat([pcm16, chunk]); }, commit: () => { if (!connected) { - (emitter as any).emit("error", new Error("STT session not connected")); + emitter.emit("error", new Error("STT session not connected")); return; } const committedId = segmentId; const prev = previousSegmentId; - (emitter as any).emit("committed", { segmentId: committedId, previousSegmentId: prev }); + emitter.emit("committed", { segmentId: committedId, previousSegmentId: prev }); void (async () => { try { const rt = await this.transcribeAudio(pcm16, `audio/pcm;rate=${requiredSampleRate}`); - (emitter as any).emit("transcript", { + emitter.emit("transcript", { segmentId: committedId, transcript: rt.text, isFinal: true, @@ -84,7 +84,7 @@ export class SherpaOnnxSTT implements SpeechToTextProvider { isLowConfidence: rt.isLowConfidence, }); } catch (err) { - (emitter as any).emit("error", err); + emitter.emit("error", err); } finally { previousSegmentId = committedId; segmentId = uuidv4(); @@ -100,8 +100,8 @@ export class SherpaOnnxSTT implements SpeechToTextProvider { connected = false; pcm16 = Buffer.alloc(0); }, - on(event: any, handler: any) { - emitter.on(event, handler); + on(event: "committed" | "transcript" | "error", handler: (payload: never) => void) { + emitter.on(event, handler as (...args: unknown[]) => void); return undefined; }, }; @@ -164,7 +164,12 @@ export class SherpaOnnxSTT implements SpeechToTextProvider { this.engine.recognizer.decode(stream); } - const text = String(this.engine.recognizer.getResult(stream)?.text ?? "").trim(); + const rawResult = this.engine.recognizer.getResult(stream); + const text = String( + (typeof rawResult === "object" && rawResult && "text" in rawResult + ? rawResult.text + : undefined) ?? "", + ).trim(); const duration = Date.now() - start; this.logger.debug({ duration, textLength: text.length }, "Sherpa transcription complete"); return { text, duration, ...(text.length === 0 ? { isLowConfidence: true } : {}) }; diff --git a/packages/server/src/server/speech/providers/local/sherpa/sherpa-tts.ts b/packages/server/src/server/speech/providers/local/sherpa/sherpa-tts.ts index 0498c9a2e..1d6f4ac3f 100644 --- a/packages/server/src/server/speech/providers/local/sherpa/sherpa-tts.ts +++ b/packages/server/src/server/speech/providers/local/sherpa/sherpa-tts.ts @@ -23,8 +23,19 @@ function assertFileExists(filePath: string, label: string): void { } } +interface SherpaOfflineTtsNative { + sampleRate?: number; + generate: (args: { + text: string; + sid: number; + speed: number; + enableExternalBuffer: boolean; + }) => { samples?: Float32Array | number[]; sampleRate?: number } | undefined; + free?: () => void; +} + export class SherpaOnnxTTS implements TextToSpeechProvider { - private readonly tts: any; + private readonly tts: SherpaOfflineTtsNative; private readonly speakerId: number; private readonly speed: number; private readonly logger: pino.Logger; @@ -81,7 +92,9 @@ export class SherpaOnnxTTS implements TextToSpeechProvider { maxNumSentences: 1, }; - this.tts = new sherpa.OfflineTts(offlineTtsConfig); + this.tts = new ( + sherpa as unknown as { OfflineTts: new (config: unknown) => SherpaOfflineTtsNative } + ).OfflineTts(offlineTtsConfig); this.logger.info( { preset: config.preset, modelDir: config.modelDir }, "Sherpa offline TTS initialized", diff --git a/packages/server/src/server/workspace-reconciliation-service.test.ts b/packages/server/src/server/workspace-reconciliation-service.test.ts index 99060b24f..45a845e2d 100644 --- a/packages/server/src/server/workspace-reconciliation-service.test.ts +++ b/packages/server/src/server/workspace-reconciliation-service.test.ts @@ -2,6 +2,7 @@ import { execSync } from "node:child_process"; import { mkdtempSync, realpathSync, rmSync, writeFileSync } from "node:fs"; import { tmpdir } from "node:os"; import path from "node:path"; +import type pino from "pino"; import { describe, expect, test, vi, afterEach } from "vitest"; import { createPersistedProjectRecord, @@ -69,7 +70,7 @@ function createTestLogger() { warn: vi.fn(), error: vi.fn(), }; - return logger as any; + return logger as unknown as pino.Logger; } function createWorkspaceGitServiceStub(