@fishaudio/agent-client 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,251 @@
1
+ import { Room } from 'livekit-client';
2
+ import { ToolCallSource, SessionToken, SessionLanguage, SessionOverrides } from '@fishaudio/agent-protocol';
3
+ export { AgentSessionCreateRequest, SessionOverrides, SessionToken, ToolCallSource } from '@fishaudio/agent-protocol';
4
+
5
+ type FishAgentErrorCode = "session_request_failed" | "agent_not_public" | "origin_forbidden" | "unsupported_transport" | "mic_permission_denied"
6
+ /** A requested audio device could not be activated, or the browser does not support selecting it. */
7
+ | "device_change_failed" | "connection_failed" | "session_expired" | "tool_failed" | "tool_timeout"
8
+ /** An upstream model/speech provider failed during the session. */
9
+ | "provider_error"
10
+ /** The agent runtime hit an internal error. */
11
+ | "internal_error";
12
+ declare class FishAgentError extends Error {
13
+ readonly code: FishAgentErrorCode;
14
+ /** HTTP status of the failing session request, when the error originated there. */
15
+ readonly statusCode?: number;
16
+ constructor(code: FishAgentErrorCode, message: string, options?: {
17
+ statusCode?: number;
18
+ cause?: unknown;
19
+ });
20
+ }
21
+
22
+ type SessionStatus = "connecting" | "connected" | "reconnecting" | "ended";
23
+ type AgentMode = "listening" | "thinking" | "speaking";
24
+ type EndReason = "user_hangup" | "agent_hangup" | "connection_lost";
25
+ interface UserTranscriptEvent {
26
+ /** Groups updates of one utterance; interim updates replace, they never append. */
27
+ segmentId: string;
28
+ /** Full text of the utterance so far. */
29
+ text: string;
30
+ final: boolean;
31
+ }
32
+ interface AgentResponseDeltaEvent {
33
+ /** Groups streaming updates of one agent speech segment. */
34
+ segmentId: string;
35
+ delta: string;
36
+ /** Accumulated segment text including this delta. */
37
+ text: string;
38
+ }
39
+ interface AgentResponseEvent {
40
+ segmentId: string;
41
+ text: string;
42
+ }
43
+ /** One transcript segment; `text` is the whole segment so far, refined in place. */
44
+ interface TranscriptSegment {
45
+ segmentId: string;
46
+ role: "user" | "agent";
47
+ text: string;
48
+ final: boolean;
49
+ }
50
+ interface ConversationMessage {
51
+ role: "user" | "agent";
52
+ text: string;
53
+ }
54
+ /**
55
+ * Tool-call lifecycle, forwarded for every tool the agent runs (including client
56
+ * tools, which complete once this client returns their result). `callId` pairs one
57
+ * started event with exactly one completed/failed event; terminal events repeat
58
+ * name/source so a listener attached mid-call can still render a full item.
59
+ * Emission is a session-creation option (`toolEvents`/`tool_events`, default true).
60
+ */
61
+ interface ToolCallStartedEvent {
62
+ callId: string;
63
+ toolName: string;
64
+ source: ToolCallSource;
65
+ /** Tool arguments as a JSON string; parse only when `inputTruncated` is false. */
66
+ input: string;
67
+ inputTruncated: boolean;
68
+ }
69
+ interface ToolCallCompletedEvent {
70
+ callId: string;
71
+ toolName: string;
72
+ source: ToolCallSource;
73
+ /** Tool result as a JSON string; parse only when `outputTruncated` is false. */
74
+ output: string;
75
+ outputTruncated: boolean;
76
+ }
77
+ interface ToolCallFailedEvent {
78
+ callId: string;
79
+ toolName: string;
80
+ source: ToolCallSource;
81
+ error: string;
82
+ }
83
+ interface AgentSessionEvents {
84
+ connect: (event: {
85
+ sessionId: string;
86
+ }) => void;
87
+ disconnect: (event: {
88
+ reason: EndReason;
89
+ }) => void;
90
+ statusChange: (status: SessionStatus) => void;
91
+ modeChange: (mode: AgentMode) => void;
92
+ userTranscript: (event: UserTranscriptEvent) => void;
93
+ agentResponseDelta: (event: AgentResponseDeltaEvent) => void;
94
+ agentResponse: (event: AgentResponseEvent) => void;
95
+ /** Finalized messages only, in conversation order — the "just give me a chat log" event. */
96
+ message: (message: ConversationMessage) => void;
97
+ toolCallStarted: (event: ToolCallStartedEvent) => void;
98
+ toolCallCompleted: (event: ToolCallCompletedEvent) => void;
99
+ toolCallFailed: (event: ToolCallFailedEvent) => void;
100
+ error: (error: FishAgentError) => void;
101
+ }
102
+ type AgentSessionCallbacks = {
103
+ [K in keyof AgentSessionEvents as `on${Capitalize<K>}`]: AgentSessionEvents[K];
104
+ };
105
+
106
+ declare const DEFAULT_SERVER_URL = "https://api.fish.audio";
107
+ interface SessionRequestOptions {
108
+ /** Public agent: the SDK creates the session directly against the Fish API. */
109
+ agentId?: string;
110
+ /** Session token created by the host backend, passed through verbatim. */
111
+ sessionToken?: SessionToken;
112
+ serverUrl?: string;
113
+ /** Sugar for `overrides.language`; pair with a voice in that language. */
114
+ language?: SessionLanguage;
115
+ /**
116
+ * IANA timezone for the agent's sense of local time in this session. Omit to
117
+ * use the browser's timezone (sent automatically as a hint). With
118
+ * `sessionToken` auth the host backend chooses instead, via `timezone` on its
119
+ * session-creation call.
120
+ */
121
+ timezone?: string;
122
+ /**
123
+ * The agent knows the current date and time by default. Set false to withhold
124
+ * both from this session's prompt. With `sessionToken` auth the host backend
125
+ * chooses instead, via `world_context` on its session-creation call.
126
+ */
127
+ worldContext?: boolean;
128
+ /** Per-session config overrides; each field must be allow-listed on the agent. */
129
+ overrides?: SessionOverrides;
130
+ /** `{{name}}` template values for the agent's prompt and first message. */
131
+ dynamicVariables?: Record<string, string | number | boolean>;
132
+ endUserId?: string;
133
+ /** Caller-owned tag, stored and returned verbatim; the platform never interprets it. */
134
+ metadata?: Record<string, unknown>;
135
+ /**
136
+ * Stream the agent's tool-call lifecycle (toolCallStarted/Completed/Failed
137
+ * events, with payloads) to this session. Default true; set false when tool
138
+ * data must stay hidden from this client. With `sessionToken` auth the host
139
+ * backend chooses instead, via `tool_events` on its session-creation call.
140
+ */
141
+ toolEvents?: boolean;
142
+ }
143
+
144
+ type Listener = (...args: any[]) => void;
145
+ /** Minimal typed emitter; listener errors are isolated so one bad consumer can't break the session. */
146
+ declare class TypedEmitter<Events extends Record<keyof Events, Listener>> {
147
+ private readonly listeners;
148
+ on<K extends keyof Events>(event: K, listener: Events[K]): this;
149
+ off<K extends keyof Events>(event: K, listener: Events[K]): this;
150
+ once<K extends keyof Events>(event: K, listener: Events[K]): this;
151
+ protected emit<K extends keyof Events>(event: K, ...args: Parameters<Events[K]>): void;
152
+ }
153
+
154
+ type ClientToolHandler = (params: Record<string, unknown>, context: {
155
+ callId: string;
156
+ toolName: string;
157
+ }) => unknown | Promise<unknown>;
158
+
159
+ interface AgentSessionOptions extends SessionRequestOptions {
160
+ clientTools?: Record<string, ClientToolHandler>;
161
+ clientToolTimeoutMs?: number;
162
+ /**
163
+ * Capture the microphone on start. Default true. Set false to join without
164
+ * requesting it (text-first UIs); the first `setMicMuted(false)` then
165
+ * captures — the permission prompt happens there, so call it from a user
166
+ * gesture.
167
+ */
168
+ microphone?: boolean;
169
+ audio?: {
170
+ inputDeviceId?: string;
171
+ /** Playback device (`setSinkId`); start rejects with `device_change_failed`
172
+ * where output selection is unsupported (before creating a server session)
173
+ * or the device cannot be used (before connecting). */
174
+ outputDeviceId?: string;
175
+ };
176
+ /**
177
+ * Hold a screen wake lock while the session is live, so long calls survive
178
+ * the phone trying to sleep. Default true; denial is silent. Set false to
179
+ * leave screen policy to the page.
180
+ */
181
+ wakeLock?: boolean;
182
+ callbacks?: Partial<AgentSessionCallbacks>;
183
+ }
184
+ declare class AgentSession extends TypedEmitter<AgentSessionEvents> {
185
+ #private;
186
+ private constructor();
187
+ /**
188
+ * Obtain a session token (if needed), connect, publish the microphone and
189
+ * hand back a live session. Rejects with FishAgentError on session-request,
190
+ * permission or connect failures.
191
+ */
192
+ static start(options: AgentSessionOptions): Promise<AgentSession>;
193
+ get sessionId(): string;
194
+ get status(): SessionStatus;
195
+ get mode(): AgentMode;
196
+ get isSpeaking(): boolean;
197
+ get micMuted(): boolean;
198
+ get endReason(): EndReason | undefined;
199
+ /**
200
+ * Snapshot of every transcript segment so far, in conversation order. Seed a
201
+ * consumer that subscribes after events already fired (e.g. a component
202
+ * mounted right after start), then apply live events by segmentId.
203
+ */
204
+ getTranscript(): TranscriptSegment[];
205
+ /** `audio: false` asks the agent to answer this turn in text only (no TTS). */
206
+ sendUserMessage(text: string, options?: {
207
+ audio?: boolean;
208
+ }): void;
209
+ sendUserActivity(): void;
210
+ interrupt(): void;
211
+ registerClientTool(name: string, handler: ClientToolHandler): void;
212
+ /**
213
+ * After a `microphone: false` start, the first unmute captures and publishes
214
+ * the microphone — the permission prompt happens here. A denial rejects with
215
+ * `mic_permission_denied` and the session stays muted.
216
+ */
217
+ setMicMuted(muted: boolean): Promise<void>;
218
+ /** Call from a user gesture to satisfy browser autoplay policies. */
219
+ startAudio(): Promise<void>;
220
+ /**
221
+ * Switch the microphone mid-call. While the mic is not captured yet
222
+ * (`microphone: false` start, still muted) this records the preference for
223
+ * the first unmute. Rejects with `device_change_failed` when the device
224
+ * cannot be activated, switching back to the previous microphone (best
225
+ * effort — the transport stops the old capture before acquiring the new).
226
+ */
227
+ setInputDevice(deviceId: string): Promise<void>;
228
+ /**
229
+ * Route the agent's audio to an output device (`setSinkId`); pass `""` to
230
+ * return to the default device. Rejects with `device_change_failed` where
231
+ * the browser does not support output selection (common on mobile browsers)
232
+ * or the device cannot be used; playback stays on the previous device.
233
+ */
234
+ setOutputDevice(deviceId: string): Promise<void>;
235
+ setOutputVolume(volume: number): void;
236
+ getOutputVolume(): number;
237
+ getInputVolume(): number;
238
+ getOutputFrequencyData(): Uint8Array;
239
+ getInputFrequencyData(): Uint8Array;
240
+ /**
241
+ * Escape hatch: the underlying LiveKit `Room`, for needs the session API
242
+ * doesn't cover (connection-quality telemetry, publishing extra tracks).
243
+ * Code using it couples to this SDK's transport choice and livekit-client
244
+ * version — prefer the session API where one exists. `undefined` after the
245
+ * session ends, or when a custom transport doesn't expose a room.
246
+ */
247
+ getRoom(): Room | undefined;
248
+ end(): Promise<void>;
249
+ }
250
+
251
+ export { type AgentMode, type AgentResponseDeltaEvent, type AgentResponseEvent, AgentSession, type AgentSessionCallbacks, type AgentSessionEvents, type AgentSessionOptions, type ClientToolHandler, type ConversationMessage, DEFAULT_SERVER_URL, type EndReason, FishAgentError, type FishAgentErrorCode, type SessionStatus, type ToolCallCompletedEvent, type ToolCallFailedEvent, type ToolCallStartedEvent, type TranscriptSegment, type UserTranscriptEvent };