phonic 0.32.13 → 0.32.15

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.
Files changed (47) hide show
  1. package/dist/cjs/BaseClient.js +2 -2
  2. package/dist/cjs/api/resources/agents/client/requests/UpdateAgentRequest.d.ts +1 -1
  3. package/dist/cjs/api/resources/tools/client/requests/CreateToolRequest.d.ts +2 -0
  4. package/dist/cjs/api/resources/tools/client/requests/UpdateToolRequest.d.ts +2 -0
  5. package/dist/cjs/api/resources/tts/client/requests/StreamTtsRequest.d.ts +6 -0
  6. package/dist/cjs/api/types/Agent.d.ts +1 -1
  7. package/dist/cjs/api/types/BuiltInToolConfig.d.ts +1 -1
  8. package/dist/cjs/api/types/BuiltInToolConfigs.d.ts +5 -2
  9. package/dist/cjs/api/types/BuiltInToolDefinition.d.ts +8 -2
  10. package/dist/cjs/api/types/BuiltInToolDefinition.js +1 -0
  11. package/dist/cjs/api/types/ChooseNotToRespondToolConfig.d.ts +7 -0
  12. package/dist/cjs/api/types/ChooseNotToRespondToolConfig.js +3 -0
  13. package/dist/cjs/api/types/CreateAgentRequest.d.ts +1 -1
  14. package/dist/cjs/api/types/OutboundCallConfig.d.ts +1 -1
  15. package/dist/cjs/api/types/Tool.d.ts +2 -0
  16. package/dist/cjs/api/types/index.d.ts +1 -0
  17. package/dist/cjs/api/types/index.js +1 -0
  18. package/dist/cjs/custom/ReconnectableConversationsSocket.d.ts +16 -0
  19. package/dist/cjs/custom/ReconnectableConversationsSocket.js +127 -11
  20. package/dist/cjs/version.d.ts +1 -1
  21. package/dist/cjs/version.js +1 -1
  22. package/dist/esm/BaseClient.mjs +2 -2
  23. package/dist/esm/api/resources/agents/client/requests/UpdateAgentRequest.d.mts +1 -1
  24. package/dist/esm/api/resources/tools/client/requests/CreateToolRequest.d.mts +2 -0
  25. package/dist/esm/api/resources/tools/client/requests/UpdateToolRequest.d.mts +2 -0
  26. package/dist/esm/api/resources/tts/client/requests/StreamTtsRequest.d.mts +6 -0
  27. package/dist/esm/api/types/Agent.d.mts +1 -1
  28. package/dist/esm/api/types/BuiltInToolConfig.d.mts +1 -1
  29. package/dist/esm/api/types/BuiltInToolConfigs.d.mts +5 -2
  30. package/dist/esm/api/types/BuiltInToolDefinition.d.mts +8 -2
  31. package/dist/esm/api/types/BuiltInToolDefinition.mjs +1 -0
  32. package/dist/esm/api/types/ChooseNotToRespondToolConfig.d.mts +7 -0
  33. package/dist/esm/api/types/ChooseNotToRespondToolConfig.mjs +2 -0
  34. package/dist/esm/api/types/CreateAgentRequest.d.mts +1 -1
  35. package/dist/esm/api/types/OutboundCallConfig.d.mts +1 -1
  36. package/dist/esm/api/types/Tool.d.mts +2 -0
  37. package/dist/esm/api/types/index.d.mts +1 -0
  38. package/dist/esm/api/types/index.mjs +1 -0
  39. package/dist/esm/custom/ReconnectableConversationsSocket.d.mts +16 -0
  40. package/dist/esm/custom/ReconnectableConversationsSocket.mjs +127 -11
  41. package/dist/esm/version.d.mts +1 -1
  42. package/dist/esm/version.mjs +1 -1
  43. package/package.json +1 -1
  44. package/dist/index.d.mts +0 -240
  45. package/dist/index.d.ts +0 -240
  46. package/dist/index.js +0 -347
  47. package/dist/index.mjs +0 -310
@@ -43,8 +43,8 @@ function normalizeClientOptions(options) {
43
43
  const headers = (0, headers_js_1.mergeHeaders)({
44
44
  "X-Fern-Language": "JavaScript",
45
45
  "X-Fern-SDK-Name": "phonic",
46
- "X-Fern-SDK-Version": "0.32.13",
47
- "User-Agent": "phonic/0.32.13",
46
+ "X-Fern-SDK-Version": "0.32.15",
47
+ "User-Agent": "phonic/0.32.15",
48
48
  "X-Fern-Runtime": core.RUNTIME.type,
49
49
  "X-Fern-Runtime-Version": core.RUNTIME.version,
50
50
  }, options === null || options === void 0 ? void 0 : options.headers);
@@ -178,7 +178,7 @@ export declare namespace UpdateAgentRequest {
178
178
  }
179
179
  type Tools = Tools.Item[];
180
180
  namespace Tools {
181
- type Item = "keypad_input" | "natural_conversation_ending"
181
+ type Item = "keypad_input" | "natural_conversation_ending" | "choose_not_to_respond"
182
182
  /**
183
183
  * Custom tool */
184
184
  | string;
@@ -217,6 +217,8 @@ export interface CreateToolRequest {
217
217
  require_speech_before_tool_call?: boolean;
218
218
  /** For built_in_natural_conversation_ending and built_in_keypad_input tools. Whether the agent must speak before calling the tool ("required"), the model decides ("optional"), or the agent must stay silent ("suppressed"). Not used by other tool types. */
219
219
  speech_before_tool_call?: CreateToolRequest.SpeechBeforeToolCall;
220
+ /** For built_in_choose_not_to_respond tools. Number of seconds to wait after the tool fires before the agent speaks a follow-up if the user stays silent. When null, the agent stays silent (default). Not used by other tool types. */
221
+ respond_after_sec?: number | null;
220
222
  /** If true, the agent will wait to finish speaking before executing the tool. This is only available for custom_webhook and custom_websocket tools. */
221
223
  wait_for_speech_before_tool_call?: boolean;
222
224
  /** When true, forbids the agent from speaking after executing the tool. Available for custom_context, custom_webhook and custom_websocket tools. */
@@ -57,6 +57,8 @@ export interface UpdateToolRequest {
57
57
  require_speech_before_tool_call?: boolean;
58
58
  /** For built_in_natural_conversation_ending and built_in_keypad_input tools. Whether the agent must speak before calling the tool ("required"), the model decides ("optional"), or the agent must stay silent ("suppressed"). Not used by other tool types. */
59
59
  speech_before_tool_call?: UpdateToolRequest.SpeechBeforeToolCall;
60
+ /** For built_in_choose_not_to_respond tools. Number of seconds to wait after the tool fires before the agent speaks a follow-up if the user stays silent. When null, the agent stays silent (default). Not used by other tool types. */
61
+ respond_after_sec?: number | null;
60
62
  /** If true, the agent will wait to finish speaking before executing the tool. This is only available for custom_webhook and custom_websocket tools. */
61
63
  wait_for_speech_before_tool_call?: boolean;
62
64
  /** When true, forbids the agent from speaking after executing the tool. Available for custom_context, custom_webhook and custom_websocket tools. */
@@ -1,3 +1,4 @@
1
+ import type * as Phonic from "../../../../index.js";
1
2
  /**
2
3
  * @example
3
4
  * {
@@ -15,6 +16,11 @@ export interface StreamTtsRequest {
15
16
  voice_id?: string;
16
17
  /** The audio format to stream. */
17
18
  output_format?: StreamTtsRequest.OutputFormat;
19
+ /**
20
+ * Candidate languages for synthesis. An empty array defaults to English, one language
21
+ * selects it directly, and multiple languages let Phonic detect among those candidates.
22
+ */
23
+ languages?: Phonic.LanguageCode[];
18
24
  }
19
25
  export declare namespace StreamTtsRequest {
20
26
  /** The audio format to stream. */
@@ -127,7 +127,7 @@ export declare namespace Agent {
127
127
  }
128
128
  type Tools = Tools.Item[];
129
129
  namespace Tools {
130
- type Item = "keypad_input" | "natural_conversation_ending"
130
+ type Item = "keypad_input" | "natural_conversation_ending" | "choose_not_to_respond"
131
131
  /**
132
132
  * Custom tool */
133
133
  | string;
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Configuration for a simple built-in tool (`keypad_input`, `natural_conversation_ending`, or `choose_not_to_respond`).
2
+ * Configuration for a simple built-in tool (`keypad_input` or `natural_conversation_ending`).
3
3
  */
4
4
  export interface BuiltInToolConfig {
5
5
  /** Controls whether the assistant speaks before the tool is called. `required`: the assistant must speak first. `optional`: the model decides. `suppressed`: the assistant is strongly instructed to stay silent before the call (best effort). */
@@ -1,5 +1,8 @@
1
1
  import type * as Phonic from "../index.js";
2
2
  /**
3
- * Per-tool configuration overrides for built-in tools, keyed by built-in tool ID (e.g. `tool_natural_conversation_ending`). A built-in tool referenced in `tools` but absent from this map uses its default configuration.
3
+ * Per-tool configuration overrides for built-in tools, keyed by built-in tool ID. The value's shape depends on the tool: `tool_keypad_input` and `tool_natural_conversation_ending` take a `speech_before_tool_call` config; `tool_choose_not_to_respond` takes a `respond_after_sec` config. A built-in tool referenced in `tools` but absent from this map uses its default configuration.
4
4
  */
5
- export type BuiltInToolConfigs = Record<string, Phonic.BuiltInToolConfig>;
5
+ export type BuiltInToolConfigs = Record<string, BuiltInToolConfigs.Value>;
6
+ export declare namespace BuiltInToolConfigs {
7
+ type Value = Phonic.BuiltInToolConfig | Phonic.ChooseNotToRespondToolConfig;
8
+ }
@@ -1,18 +1,24 @@
1
1
  import type * as Phonic from "../index.js";
2
2
  /**
3
- * A built-in tool with an explicit configuration, as an alternative to referencing it by bare name (which uses the tool's default configuration). Only `keypad_input` and `natural_conversation_ending` accept configuration this way.
3
+ * A built-in tool with an explicit configuration, as an alternative to referencing it by bare name (which uses the tool's default configuration). `keypad_input` and `natural_conversation_ending` take a `speech_before_tool_call` config; `choose_not_to_respond` takes a `respond_after_sec` config.
4
4
  */
5
5
  export interface BuiltInToolDefinition {
6
6
  type: "built_in";
7
7
  /** The name of the built-in tool. */
8
8
  name: BuiltInToolDefinition.Name;
9
- tool_config: Phonic.BuiltInToolConfig;
9
+ /** The tool's configuration. Use `BuiltInToolConfig` for `keypad_input` and `natural_conversation_ending`, or `ChooseNotToRespondToolConfig` for `choose_not_to_respond`. */
10
+ tool_config: BuiltInToolDefinition.ToolConfig;
10
11
  }
11
12
  export declare namespace BuiltInToolDefinition {
12
13
  /** The name of the built-in tool. */
13
14
  const Name: {
14
15
  readonly KeypadInput: "keypad_input";
15
16
  readonly NaturalConversationEnding: "natural_conversation_ending";
17
+ readonly ChooseNotToRespond: "choose_not_to_respond";
16
18
  };
17
19
  type Name = (typeof Name)[keyof typeof Name];
20
+ /**
21
+ * The tool's configuration. Use `BuiltInToolConfig` for `keypad_input` and `natural_conversation_ending`, or `ChooseNotToRespondToolConfig` for `choose_not_to_respond`.
22
+ */
23
+ type ToolConfig = Phonic.BuiltInToolConfig | Phonic.ChooseNotToRespondToolConfig;
18
24
  }
@@ -8,5 +8,6 @@ var BuiltInToolDefinition;
8
8
  BuiltInToolDefinition.Name = {
9
9
  KeypadInput: "keypad_input",
10
10
  NaturalConversationEnding: "natural_conversation_ending",
11
+ ChooseNotToRespond: "choose_not_to_respond",
11
12
  };
12
13
  })(BuiltInToolDefinition || (exports.BuiltInToolDefinition = BuiltInToolDefinition = {}));
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Configuration for the `choose_not_to_respond` built-in tool.
3
+ */
4
+ export interface ChooseNotToRespondToolConfig {
5
+ /** Number of seconds to wait after the tool fires before the assistant speaks a follow-up if the user stays silent. When null, the assistant stays silent (default). */
6
+ respond_after_sec?: (number | null) | undefined;
7
+ }
@@ -0,0 +1,3 @@
1
+ "use strict";
2
+ // This file was auto-generated by Fern from our API Definition.
3
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -125,7 +125,7 @@ export declare namespace CreateAgentRequest {
125
125
  }
126
126
  type Tools = Tools.Item[];
127
127
  namespace Tools {
128
- type Item = "keypad_input" | "natural_conversation_ending"
128
+ type Item = "keypad_input" | "natural_conversation_ending" | "choose_not_to_respond"
129
129
  /**
130
130
  * Custom tool */
131
131
  | string;
@@ -97,7 +97,7 @@ export declare namespace OutboundCallConfig {
97
97
  }
98
98
  type Tools = Tools.Item[];
99
99
  namespace Tools {
100
- type Item = "keypad_input" | "natural_conversation_ending"
100
+ type Item = "keypad_input" | "natural_conversation_ending" | "choose_not_to_respond"
101
101
  /**
102
102
  * Custom tool */
103
103
  | string;
@@ -48,6 +48,8 @@ export interface Tool {
48
48
  require_speech_before_tool_call?: boolean | undefined;
49
49
  /** For built_in_natural_conversation_ending and built_in_keypad_input tools. Whether the agent must speak before calling the tool ("required"), the model decides ("optional"), or the agent must stay silent ("suppressed"). Not used by other tool types. */
50
50
  speech_before_tool_call?: Tool.SpeechBeforeToolCall | undefined;
51
+ /** For built_in_choose_not_to_respond tools. Number of seconds to wait after the tool fires before the agent speaks a follow-up if the user stays silent. When null, the agent stays silent (default). Not used by other tool types. */
52
+ respond_after_sec?: (number | null) | undefined;
51
53
  /** If true, the agent will wait to finish speaking before executing the tool. This is only available for custom_webhook and custom_websocket tools. */
52
54
  wait_for_speech_before_tool_call?: boolean | undefined;
53
55
  /** When true, forbids the agent from speaking after executing the tool. Available for custom_context, custom_webhook and custom_websocket tools. */
@@ -13,6 +13,7 @@ export * from "./BasicError.js";
13
13
  export * from "./BuiltInToolConfig.js";
14
14
  export * from "./BuiltInToolConfigs.js";
15
15
  export * from "./BuiltInToolDefinition.js";
16
+ export * from "./ChooseNotToRespondToolConfig.js";
16
17
  export * from "./ConfigOptions.js";
17
18
  export * from "./ConfigPayload.js";
18
19
  export * from "./Conversation.js";
@@ -29,6 +29,7 @@ __exportStar(require("./BasicError.js"), exports);
29
29
  __exportStar(require("./BuiltInToolConfig.js"), exports);
30
30
  __exportStar(require("./BuiltInToolConfigs.js"), exports);
31
31
  __exportStar(require("./BuiltInToolDefinition.js"), exports);
32
+ __exportStar(require("./ChooseNotToRespondToolConfig.js"), exports);
32
33
  __exportStar(require("./ConfigOptions.js"), exports);
33
34
  __exportStar(require("./ConfigPayload.js"), exports);
34
35
  __exportStar(require("./Conversation.js"), exports);
@@ -27,6 +27,11 @@ type EventHandlers = {
27
27
  * with a terminal close code (4800 session expired, 4801 invalid state),
28
28
  * the safety cap is reached, or the user calls close().
29
29
  *
30
+ * A reconnectable close is not surfaced to the user's close handler while the
31
+ * reconnect is in flight. The close is emitted only when reconnection has
32
+ * failed, meaning the conversation is closing. Closes that are not
33
+ * reconnectable (1000, 4000, etc) are not affected and will pass through.
34
+ *
30
35
  * Uses composition rather than inheritance to avoid coupling to the parent's
31
36
  * private event handler registration or ReconnectingWebSocket internals.
32
37
  */
@@ -41,6 +46,8 @@ export declare class ReconnectableConversationsSocket {
41
46
  private _cleanupWireListeners;
42
47
  private _pendingReconnect;
43
48
  private _pendingReplacement;
49
+ private _lastSuppressedClose;
50
+ private _gaveUp;
44
51
  constructor(args: ReconnectableConversationsSocketArgs);
45
52
  /** The conversation ID captured from the server's conversation_created message. */
46
53
  get conversationId(): string | null;
@@ -67,9 +74,18 @@ export declare class ReconnectableConversationsSocket {
67
74
  connect(): never;
68
75
  close(): void;
69
76
  waitForOpen(): Promise<core.ReconnectingWebSocket>;
77
+ private _willReconnectAfter;
78
+ private _giveUp;
70
79
  private _getReconnectDelay;
71
80
  /** Schedule a reconnection attempt after backoff delay. */
72
81
  private _scheduleReconnect;
82
+ /** Settle as soon as the abort signal fires rather than waiting on the
83
+ * factory, whose promise is caller-supplied (it awaits fresh auth) and
84
+ * may never settle — the suppressed close would stay hidden forever.
85
+ * Resolves null when aborted; a socket arriving afterwards is closed
86
+ * rather than leaked. The listener is removed once the factory settles,
87
+ * so a long-lived conversation does not accumulate them on the signal. */
88
+ private _createSocketOrAbort;
73
89
  /** Perform the actual reconnection attempt. */
74
90
  private _doReconnect;
75
91
  private _wireInner;
@@ -80,6 +80,11 @@ const MAX_RECONNECT_ATTEMPTS = 10;
80
80
  * with a terminal close code (4800 session expired, 4801 invalid state),
81
81
  * the safety cap is reached, or the user calls close().
82
82
  *
83
+ * A reconnectable close is not surfaced to the user's close handler while the
84
+ * reconnect is in flight. The close is emitted only when reconnection has
85
+ * failed, meaning the conversation is closing. Closes that are not
86
+ * reconnectable (1000, 4000, etc) are not affected and will pass through.
87
+ *
83
88
  * Uses composition rather than inheritance to avoid coupling to the parent's
84
89
  * private event handler registration or ReconnectingWebSocket internals.
85
90
  */
@@ -92,6 +97,8 @@ class ReconnectableConversationsSocket {
92
97
  this._cleanupWireListeners = null;
93
98
  this._pendingReconnect = null;
94
99
  this._pendingReplacement = false;
100
+ this._lastSuppressedClose = null;
101
+ this._gaveUp = false;
95
102
  this._createReconnectSocket = args.createReconnectSocket;
96
103
  this._abortSignal = args.abortSignal != undefined ? args.abortSignal : null;
97
104
  this._inner = new Socket_js_1.ConversationsSocket({ socket: args.socket });
@@ -170,15 +177,36 @@ class ReconnectableConversationsSocket {
170
177
  clearTimeout(this._pendingReconnect);
171
178
  this._pendingReconnect = null;
172
179
  }
180
+ // Close before detaching: ConversationsSocket.close() synthesizes a
181
+ // 1000 close event, and a user-initiated close should still surface one.
182
+ this._inner.close();
173
183
  (_a = this._cleanupWireListeners) === null || _a === void 0 ? void 0 : _a.call(this);
174
184
  this._cleanupWireListeners = null;
175
- this._inner.close();
176
185
  }
177
186
  waitForOpen() {
178
187
  return __awaiter(this, void 0, void 0, function* () {
179
188
  return this._inner.waitForOpen();
180
189
  });
181
190
  }
191
+ _willReconnectAfter(event) {
192
+ return (!this._isClosed
193
+ && !this._gaveUp
194
+ && this._conversationId !== null
195
+ && isReconnectableClose(event.code, event.reason));
196
+ }
197
+ _giveUp() {
198
+ var _a, _b;
199
+ this._pendingReplacement = false;
200
+ if (this._gaveUp || this._isClosed) {
201
+ return;
202
+ }
203
+ this._gaveUp = true;
204
+ const suppressed = this._lastSuppressedClose;
205
+ this._lastSuppressedClose = null;
206
+ if (suppressed !== null) {
207
+ (_b = (_a = this._handlers).close) === null || _b === void 0 ? void 0 : _b.call(_a, suppressed);
208
+ }
209
+ }
182
210
  _getReconnectDelay() {
183
211
  // Exponential backoff: 500ms, 1s, 2s, 4s, capped at 5s
184
212
  const delay = BASE_RECONNECT_DELAY_MS * Math.pow(2, this._reconnectAttempts - 1);
@@ -187,12 +215,18 @@ class ReconnectableConversationsSocket {
187
215
  /** Schedule a reconnection attempt after backoff delay. */
188
216
  _scheduleReconnect() {
189
217
  var _a, _b, _c;
190
- if (this._isClosed || this._conversationId === null || ((_a = this._abortSignal) === null || _a === void 0 ? void 0 : _a.aborted)) {
218
+ // With no conversation the close was never suppressed, so unlike the
219
+ // branches below there is nothing to emit.
220
+ if (this._isClosed || this._conversationId === null) {
221
+ return;
222
+ }
223
+ if ((_a = this._abortSignal) === null || _a === void 0 ? void 0 : _a.aborted) {
224
+ this._giveUp();
191
225
  return;
192
226
  }
193
227
  if (this._reconnectAttempts >= MAX_RECONNECT_ATTEMPTS) {
194
- this._pendingReplacement = false;
195
228
  (_c = (_b = this._handlers).error) === null || _c === void 0 ? void 0 : _c.call(_b, new Error("Max reconnect attempts reached"));
229
+ this._giveUp();
196
230
  return;
197
231
  }
198
232
  // Clear any existing timer to prevent orphaned timeouts
@@ -203,24 +237,70 @@ class ReconnectableConversationsSocket {
203
237
  const delay = this._getReconnectDelay();
204
238
  this._pendingReplacement = true;
205
239
  this._pendingReconnect = setTimeout(() => {
240
+ var _a;
206
241
  this._pendingReconnect = null;
207
242
  if (this._isClosed) {
208
243
  this._pendingReplacement = false;
209
244
  return;
210
245
  }
246
+ if ((_a = this._abortSignal) === null || _a === void 0 ? void 0 : _a.aborted) {
247
+ this._giveUp();
248
+ return;
249
+ }
211
250
  void this._doReconnect();
212
251
  }, delay);
213
252
  }
253
+ /** Settle as soon as the abort signal fires rather than waiting on the
254
+ * factory, whose promise is caller-supplied (it awaits fresh auth) and
255
+ * may never settle — the suppressed close would stay hidden forever.
256
+ * Resolves null when aborted; a socket arriving afterwards is closed
257
+ * rather than leaked. The listener is removed once the factory settles,
258
+ * so a long-lived conversation does not accumulate them on the signal. */
259
+ _createSocketOrAbort(created) {
260
+ const signal = this._abortSignal;
261
+ if (signal === null) {
262
+ return created;
263
+ }
264
+ const discardLate = () => {
265
+ void created.then((socket) => socket.close()).catch(() => undefined);
266
+ };
267
+ if (signal.aborted) {
268
+ discardLate();
269
+ return Promise.resolve(null);
270
+ }
271
+ return new Promise((resolve, reject) => {
272
+ const onAbort = () => {
273
+ discardLate();
274
+ resolve(null);
275
+ };
276
+ signal.addEventListener("abort", onAbort, { once: true });
277
+ created.then((socket) => {
278
+ signal.removeEventListener("abort", onAbort);
279
+ resolve(socket);
280
+ }, (error) => {
281
+ signal.removeEventListener("abort", onAbort);
282
+ reject(error);
283
+ });
284
+ });
285
+ }
214
286
  /** Perform the actual reconnection attempt. */
215
287
  _doReconnect() {
216
288
  return __awaiter(this, void 0, void 0, function* () {
217
289
  var _a, _b;
218
290
  try {
219
291
  const created = this._createReconnectSocket(this._conversationId);
220
- const newRawSocket = created instanceof Promise ? yield created : created;
292
+ const newRawSocket = created instanceof Promise ? yield this._createSocketOrAbort(created) : created;
293
+ if (newRawSocket === null) {
294
+ // Aborted while the factory was still in flight.
295
+ this._giveUp();
296
+ return;
297
+ }
221
298
  if (this._isClosed || ((_a = this._abortSignal) === null || _a === void 0 ? void 0 : _a.aborted)) {
222
299
  this._pendingReplacement = false;
223
300
  newRawSocket.close();
301
+ if (!this._isClosed) {
302
+ this._giveUp();
303
+ }
224
304
  return;
225
305
  }
226
306
  // Clean up the old socket: remove our custom listeners and close
@@ -233,7 +313,7 @@ class ReconnectableConversationsSocket {
233
313
  catch ( /* already closed from 1006 */_c) { /* already closed from 1006 */ }
234
314
  const newInner = new Socket_js_1.ConversationsSocket({ socket: newRawSocket });
235
315
  this._inner = newInner;
236
- this._wireInner(newInner, newRawSocket);
316
+ this._wireInner(newInner, newRawSocket, true);
237
317
  }
238
318
  catch (_d) {
239
319
  this._pendingReplacement = false;
@@ -244,17 +324,41 @@ class ReconnectableConversationsSocket {
244
324
  }
245
325
  });
246
326
  }
247
- _wireInner(inner, rawSocket) {
248
- // Forward events from the inner ConversationsSocket to user handlers.
249
- // Clear _pendingReplacement before calling the user's open handler
250
- // so that sends inside the handler are not silently dropped.
327
+ _wireInner(inner, rawSocket, isReplacement = false) {
328
+ // A replacement that closes before ever opening failed at the transport
329
+ // level, and its code says nothing about why: ReconnectingWebSocket
330
+ // synthesizes 1000 for connect errors and timeouts (_handleError ->
331
+ // _disconnect). Read it as a failed attempt rather than as a close.
332
+ // _isClosed excluded: close() also reaches this via the synthesized
333
+ // 1000, and a user-initiated close is a close, not a failed attempt.
334
+ let opened = false;
335
+ const isFailedAttempt = () => isReplacement && !opened && !this._isClosed;
336
+ // Clearing _pendingReplacement before the user's open handler keeps
337
+ // sends made inside that handler from being dropped.
251
338
  inner.on("open", () => {
252
339
  var _a, _b;
340
+ opened = true;
253
341
  this._pendingReplacement = false;
342
+ this._lastSuppressedClose = null;
254
343
  (_b = (_a = this._handlers).open) === null || _b === void 0 ? void 0 : _b.call(_a);
255
344
  });
256
345
  inner.on("message", (msg) => { var _a, _b; return (_b = (_a = this._handlers).message) === null || _b === void 0 ? void 0 : _b.call(_a, msg); });
257
- inner.on("close", (ev) => { var _a, _b; return (_b = (_a = this._handlers).close) === null || _b === void 0 ? void 0 : _b.call(_a, ev); });
346
+ // ConversationsSocket registers this in its constructor, so it runs
347
+ // before the rawSocket close listener below that schedules the
348
+ // reconnect — the decision cannot be read off reconnect state here.
349
+ inner.on("close", (ev) => {
350
+ var _a, _b;
351
+ // Leave _lastSuppressedClose holding the original drop, so _giveUp
352
+ // reports what went wrong rather than this attempt's code.
353
+ if (isFailedAttempt()) {
354
+ return;
355
+ }
356
+ if (this._willReconnectAfter(ev)) {
357
+ this._lastSuppressedClose = ev;
358
+ return;
359
+ }
360
+ (_b = (_a = this._handlers).close) === null || _b === void 0 ? void 0 : _b.call(_a, ev);
361
+ });
258
362
  inner.on("error", (err) => { var _a, _b; return (_b = (_a = this._handlers).error) === null || _b === void 0 ? void 0 : _b.call(_a, err); });
259
363
  // Intercept raw messages to capture conversation_id and reset reconnect counter
260
364
  const onMessage = (event) => {
@@ -274,8 +378,15 @@ class ReconnectableConversationsSocket {
274
378
  // ignore — inner socket handles parse errors
275
379
  }
276
380
  };
381
+ // After giving up this is a plain pass-through: the close was already
382
+ // forwarded above, and retrying would repeat the give-up error.
277
383
  const onClose = (event) => {
278
- if (this._isClosed) {
384
+ if (this._isClosed || this._gaveUp) {
385
+ return;
386
+ }
387
+ if (isFailedAttempt()) {
388
+ rawSocket.close();
389
+ this._scheduleReconnect();
279
390
  return;
280
391
  }
281
392
  if (isReconnectableClose(event.code, event.reason) && this._conversationId !== null) {
@@ -294,6 +405,11 @@ class ReconnectableConversationsSocket {
294
405
  rawSocket.addEventListener("message", onMessage);
295
406
  rawSocket.addEventListener("close", onClose);
296
407
  this._cleanupWireListeners = () => {
408
+ // ConversationsSocket.close() synthesizes a 1000 close event, so a
409
+ // discarded socket would otherwise report a spurious normal closure.
410
+ for (const event of ["open", "message", "close", "error"]) {
411
+ inner.on(event, undefined);
412
+ }
297
413
  rawSocket.removeEventListener("message", onMessage);
298
414
  rawSocket.removeEventListener("close", onClose);
299
415
  };
@@ -1 +1 @@
1
- export declare const SDK_VERSION = "0.32.13";
1
+ export declare const SDK_VERSION = "0.32.15";
@@ -1,4 +1,4 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.SDK_VERSION = void 0;
4
- exports.SDK_VERSION = "0.32.13";
4
+ exports.SDK_VERSION = "0.32.15";
@@ -6,8 +6,8 @@ export function normalizeClientOptions(options) {
6
6
  const headers = mergeHeaders({
7
7
  "X-Fern-Language": "JavaScript",
8
8
  "X-Fern-SDK-Name": "phonic",
9
- "X-Fern-SDK-Version": "0.32.13",
10
- "User-Agent": "phonic/0.32.13",
9
+ "X-Fern-SDK-Version": "0.32.15",
10
+ "User-Agent": "phonic/0.32.15",
11
11
  "X-Fern-Runtime": core.RUNTIME.type,
12
12
  "X-Fern-Runtime-Version": core.RUNTIME.version,
13
13
  }, options === null || options === void 0 ? void 0 : options.headers);
@@ -178,7 +178,7 @@ export declare namespace UpdateAgentRequest {
178
178
  }
179
179
  type Tools = Tools.Item[];
180
180
  namespace Tools {
181
- type Item = "keypad_input" | "natural_conversation_ending"
181
+ type Item = "keypad_input" | "natural_conversation_ending" | "choose_not_to_respond"
182
182
  /**
183
183
  * Custom tool */
184
184
  | string;
@@ -217,6 +217,8 @@ export interface CreateToolRequest {
217
217
  require_speech_before_tool_call?: boolean;
218
218
  /** For built_in_natural_conversation_ending and built_in_keypad_input tools. Whether the agent must speak before calling the tool ("required"), the model decides ("optional"), or the agent must stay silent ("suppressed"). Not used by other tool types. */
219
219
  speech_before_tool_call?: CreateToolRequest.SpeechBeforeToolCall;
220
+ /** For built_in_choose_not_to_respond tools. Number of seconds to wait after the tool fires before the agent speaks a follow-up if the user stays silent. When null, the agent stays silent (default). Not used by other tool types. */
221
+ respond_after_sec?: number | null;
220
222
  /** If true, the agent will wait to finish speaking before executing the tool. This is only available for custom_webhook and custom_websocket tools. */
221
223
  wait_for_speech_before_tool_call?: boolean;
222
224
  /** When true, forbids the agent from speaking after executing the tool. Available for custom_context, custom_webhook and custom_websocket tools. */
@@ -57,6 +57,8 @@ export interface UpdateToolRequest {
57
57
  require_speech_before_tool_call?: boolean;
58
58
  /** For built_in_natural_conversation_ending and built_in_keypad_input tools. Whether the agent must speak before calling the tool ("required"), the model decides ("optional"), or the agent must stay silent ("suppressed"). Not used by other tool types. */
59
59
  speech_before_tool_call?: UpdateToolRequest.SpeechBeforeToolCall;
60
+ /** For built_in_choose_not_to_respond tools. Number of seconds to wait after the tool fires before the agent speaks a follow-up if the user stays silent. When null, the agent stays silent (default). Not used by other tool types. */
61
+ respond_after_sec?: number | null;
60
62
  /** If true, the agent will wait to finish speaking before executing the tool. This is only available for custom_webhook and custom_websocket tools. */
61
63
  wait_for_speech_before_tool_call?: boolean;
62
64
  /** When true, forbids the agent from speaking after executing the tool. Available for custom_context, custom_webhook and custom_websocket tools. */
@@ -1,3 +1,4 @@
1
+ import type * as Phonic from "../../../../index.mjs";
1
2
  /**
2
3
  * @example
3
4
  * {
@@ -15,6 +16,11 @@ export interface StreamTtsRequest {
15
16
  voice_id?: string;
16
17
  /** The audio format to stream. */
17
18
  output_format?: StreamTtsRequest.OutputFormat;
19
+ /**
20
+ * Candidate languages for synthesis. An empty array defaults to English, one language
21
+ * selects it directly, and multiple languages let Phonic detect among those candidates.
22
+ */
23
+ languages?: Phonic.LanguageCode[];
18
24
  }
19
25
  export declare namespace StreamTtsRequest {
20
26
  /** The audio format to stream. */
@@ -127,7 +127,7 @@ export declare namespace Agent {
127
127
  }
128
128
  type Tools = Tools.Item[];
129
129
  namespace Tools {
130
- type Item = "keypad_input" | "natural_conversation_ending"
130
+ type Item = "keypad_input" | "natural_conversation_ending" | "choose_not_to_respond"
131
131
  /**
132
132
  * Custom tool */
133
133
  | string;
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Configuration for a simple built-in tool (`keypad_input`, `natural_conversation_ending`, or `choose_not_to_respond`).
2
+ * Configuration for a simple built-in tool (`keypad_input` or `natural_conversation_ending`).
3
3
  */
4
4
  export interface BuiltInToolConfig {
5
5
  /** Controls whether the assistant speaks before the tool is called. `required`: the assistant must speak first. `optional`: the model decides. `suppressed`: the assistant is strongly instructed to stay silent before the call (best effort). */
@@ -1,5 +1,8 @@
1
1
  import type * as Phonic from "../index.mjs";
2
2
  /**
3
- * Per-tool configuration overrides for built-in tools, keyed by built-in tool ID (e.g. `tool_natural_conversation_ending`). A built-in tool referenced in `tools` but absent from this map uses its default configuration.
3
+ * Per-tool configuration overrides for built-in tools, keyed by built-in tool ID. The value's shape depends on the tool: `tool_keypad_input` and `tool_natural_conversation_ending` take a `speech_before_tool_call` config; `tool_choose_not_to_respond` takes a `respond_after_sec` config. A built-in tool referenced in `tools` but absent from this map uses its default configuration.
4
4
  */
5
- export type BuiltInToolConfigs = Record<string, Phonic.BuiltInToolConfig>;
5
+ export type BuiltInToolConfigs = Record<string, BuiltInToolConfigs.Value>;
6
+ export declare namespace BuiltInToolConfigs {
7
+ type Value = Phonic.BuiltInToolConfig | Phonic.ChooseNotToRespondToolConfig;
8
+ }
@@ -1,18 +1,24 @@
1
1
  import type * as Phonic from "../index.mjs";
2
2
  /**
3
- * A built-in tool with an explicit configuration, as an alternative to referencing it by bare name (which uses the tool's default configuration). Only `keypad_input` and `natural_conversation_ending` accept configuration this way.
3
+ * A built-in tool with an explicit configuration, as an alternative to referencing it by bare name (which uses the tool's default configuration). `keypad_input` and `natural_conversation_ending` take a `speech_before_tool_call` config; `choose_not_to_respond` takes a `respond_after_sec` config.
4
4
  */
5
5
  export interface BuiltInToolDefinition {
6
6
  type: "built_in";
7
7
  /** The name of the built-in tool. */
8
8
  name: BuiltInToolDefinition.Name;
9
- tool_config: Phonic.BuiltInToolConfig;
9
+ /** The tool's configuration. Use `BuiltInToolConfig` for `keypad_input` and `natural_conversation_ending`, or `ChooseNotToRespondToolConfig` for `choose_not_to_respond`. */
10
+ tool_config: BuiltInToolDefinition.ToolConfig;
10
11
  }
11
12
  export declare namespace BuiltInToolDefinition {
12
13
  /** The name of the built-in tool. */
13
14
  const Name: {
14
15
  readonly KeypadInput: "keypad_input";
15
16
  readonly NaturalConversationEnding: "natural_conversation_ending";
17
+ readonly ChooseNotToRespond: "choose_not_to_respond";
16
18
  };
17
19
  type Name = (typeof Name)[keyof typeof Name];
20
+ /**
21
+ * The tool's configuration. Use `BuiltInToolConfig` for `keypad_input` and `natural_conversation_ending`, or `ChooseNotToRespondToolConfig` for `choose_not_to_respond`.
22
+ */
23
+ type ToolConfig = Phonic.BuiltInToolConfig | Phonic.ChooseNotToRespondToolConfig;
18
24
  }
@@ -5,5 +5,6 @@ export var BuiltInToolDefinition;
5
5
  BuiltInToolDefinition.Name = {
6
6
  KeypadInput: "keypad_input",
7
7
  NaturalConversationEnding: "natural_conversation_ending",
8
+ ChooseNotToRespond: "choose_not_to_respond",
8
9
  };
9
10
  })(BuiltInToolDefinition || (BuiltInToolDefinition = {}));
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Configuration for the `choose_not_to_respond` built-in tool.
3
+ */
4
+ export interface ChooseNotToRespondToolConfig {
5
+ /** Number of seconds to wait after the tool fires before the assistant speaks a follow-up if the user stays silent. When null, the assistant stays silent (default). */
6
+ respond_after_sec?: (number | null) | undefined;
7
+ }
@@ -0,0 +1,2 @@
1
+ // This file was auto-generated by Fern from our API Definition.
2
+ export {};
@@ -125,7 +125,7 @@ export declare namespace CreateAgentRequest {
125
125
  }
126
126
  type Tools = Tools.Item[];
127
127
  namespace Tools {
128
- type Item = "keypad_input" | "natural_conversation_ending"
128
+ type Item = "keypad_input" | "natural_conversation_ending" | "choose_not_to_respond"
129
129
  /**
130
130
  * Custom tool */
131
131
  | string;