@teamlearners/clawops 0.16.3 → 0.16.4

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.
@@ -506,6 +506,14 @@ declare class ClawOpsAgent {
506
506
  private _handleRinging;
507
507
  private _handleFailed;
508
508
  private _onDtmfEvent;
509
+ /**
510
+ * _startCallSession 의 예외를 잡아 control WS 로 call.session_failed 전송한다.
511
+ *
512
+ * OpenAI/Gemini API 키 누락 등 session.start() 단계 실패는 media WS connect 에
513
+ * 도달하지 못해 call-engine 이 30 초간 무음 통화를 유지하게 만든다. 서버에 즉시
514
+ * 알려서 fail-fast 시키고 _activeSessions 에서 정리한다.
515
+ */
516
+ private _safeStartCallSession;
509
517
  private _startCallSession;
510
518
  }
511
519
 
@@ -506,6 +506,14 @@ declare class ClawOpsAgent {
506
506
  private _handleRinging;
507
507
  private _handleFailed;
508
508
  private _onDtmfEvent;
509
+ /**
510
+ * _startCallSession 의 예외를 잡아 control WS 로 call.session_failed 전송한다.
511
+ *
512
+ * OpenAI/Gemini API 키 누락 등 session.start() 단계 실패는 media WS connect 에
513
+ * 도달하지 못해 call-engine 이 30 초간 무음 통화를 유지하게 만든다. 서버에 즉시
514
+ * 알려서 fail-fast 시키고 _activeSessions 에서 정리한다.
515
+ */
516
+ private _safeStartCallSession;
509
517
  private _startCallSession;
510
518
  }
511
519
 
@@ -1,4 +1,4 @@
1
- import { DEFAULT_BASE_URL, AgentError, AgentConnectionError, VERSION } from '../chunk-5IYJOP2D.js';
1
+ import { DEFAULT_BASE_URL, AgentError, AgentConnectionError, VERSION } from '../chunk-B3SD2KKR.js';
2
2
  import pino from 'pino';
3
3
  import * as fs from 'fs';
4
4
  import * as path from 'path';
@@ -1928,9 +1928,7 @@ var ClawOpsAgent = class _ClawOpsAgent {
1928
1928
  this._controlWs.send({ event: "call.accept", callId });
1929
1929
  }
1930
1930
  if (mediaUrl) {
1931
- this._startCallSession(session, mediaUrl).catch((err) => {
1932
- this._log.error({ err }, "Call session error: %s", callId);
1933
- });
1931
+ this._safeStartCallSession(session, mediaUrl, callId);
1934
1932
  }
1935
1933
  }
1936
1934
  _handleEnded(event) {
@@ -1964,9 +1962,7 @@ var ClawOpsAgent = class _ClawOpsAgent {
1964
1962
  }
1965
1963
  if (mediaUrl) {
1966
1964
  this._log.info("Outbound call answered: %s -> %s (%s)", this._fromNumber, session.toNumber, callId);
1967
- this._startCallSession(session, mediaUrl).catch((err) => {
1968
- this._log.error({ err }, "Call session error: %s", callId);
1969
- });
1965
+ this._safeStartCallSession(session, mediaUrl, callId);
1970
1966
  }
1971
1967
  }
1972
1968
  _handleRinging(event) {
@@ -2010,6 +2006,32 @@ var ClawOpsAgent = class _ClawOpsAgent {
2010
2006
  }
2011
2007
  }, this._passiveDtmfDebounceMs);
2012
2008
  }
2009
+ /**
2010
+ * _startCallSession 의 예외를 잡아 control WS 로 call.session_failed 전송한다.
2011
+ *
2012
+ * OpenAI/Gemini API 키 누락 등 session.start() 단계 실패는 media WS connect 에
2013
+ * 도달하지 못해 call-engine 이 30 초간 무음 통화를 유지하게 만든다. 서버에 즉시
2014
+ * 알려서 fail-fast 시키고 _activeSessions 에서 정리한다.
2015
+ */
2016
+ _safeStartCallSession(session, mediaWsUrl, callId) {
2017
+ this._startCallSession(session, mediaWsUrl).catch((err) => {
2018
+ const error = err;
2019
+ this._log.error({ err }, "Session start failed for %s", callId);
2020
+ if (this._controlWs) {
2021
+ try {
2022
+ this._controlWs.send({
2023
+ event: "call.session_failed",
2024
+ callId,
2025
+ reason: error?.name ?? "Error",
2026
+ message: error?.message ?? String(err)
2027
+ });
2028
+ } catch {
2029
+ }
2030
+ }
2031
+ this._activeSessions.delete(callId);
2032
+ this._callSessions.delete(callId);
2033
+ });
2034
+ }
2013
2035
  async _startCallSession(session, mediaWsUrl) {
2014
2036
  await withSpan(
2015
2037
  "clawops.call_session",
@@ -2633,6 +2655,11 @@ var OpenAIRealtime = class {
2633
2655
  _holdAudioChunks = null;
2634
2656
  constructor(options = {}) {
2635
2657
  this._apiKey = options.apiKey ?? process.env["OPENAI_API_KEY"] ?? "";
2658
+ if (!this._apiKey) {
2659
+ throw new Error(
2660
+ "OpenAI API key is required. Set OPENAI_API_KEY env var or pass apiKey option."
2661
+ );
2662
+ }
2636
2663
  this._systemPrompt = options.systemPrompt ?? "";
2637
2664
  this._model = options.model ?? "gpt-realtime-2";
2638
2665
  this._voice = options.voice ?? "marin";
@@ -2658,9 +2685,6 @@ var OpenAIRealtime = class {
2658
2685
  this._closed = false;
2659
2686
  this._playback = null;
2660
2687
  this._latestMediaTs = 0;
2661
- if (!this._apiKey) {
2662
- throw new Error("OpenAI API key is required. Set OPENAI_API_KEY or pass apiKey option.");
2663
- }
2664
2688
  const { WebSocket } = await import('ws');
2665
2689
  const url = `${OPENAI_REALTIME_URL}${this._model}`;
2666
2690
  this._ws = new WebSocket(url, {