@teamlearners/clawops 0.24.0 → 0.25.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.
@@ -1,7 +1,7 @@
1
1
  'use strict';
2
2
 
3
- var chunkLUA53NPZ_cjs = require('../chunk-LUA53NPZ.cjs');
4
- var chunkU4UFTVLX_cjs = require('../chunk-U4UFTVLX.cjs');
3
+ var chunkM4QUDFGP_cjs = require('../chunk-M4QUDFGP.cjs');
4
+ var chunkXAOA3XFQ_cjs = require('../chunk-XAOA3XFQ.cjs');
5
5
  var fs = require('fs');
6
6
  var path = require('path');
7
7
  var os = require('os');
@@ -59,7 +59,7 @@ var ControlWebSocket = class {
59
59
  _closed = false;
60
60
  _connectedResolve = null;
61
61
  _connectedPromise;
62
- _log = chunkU4UFTVLX_cjs.NOOP_LOGGER;
62
+ _log = chunkXAOA3XFQ_cjs.NOOP_LOGGER;
63
63
  _pingTimer = null;
64
64
  setLogger(logger) {
65
65
  this._log = logger;
@@ -224,7 +224,7 @@ var ControlWebSocket = class {
224
224
  var MCPClient = class {
225
225
  _servers = /* @__PURE__ */ new Map();
226
226
  _clients = /* @__PURE__ */ new Map();
227
- _log = chunkU4UFTVLX_cjs.NOOP_LOGGER;
227
+ _log = chunkXAOA3XFQ_cjs.NOOP_LOGGER;
228
228
  setLogger(logger) {
229
229
  this._log = logger;
230
230
  }
@@ -366,7 +366,7 @@ var MediaWebSocket = class {
366
366
  _onClose = null;
367
367
  _onDtmf = null;
368
368
  _markWaiters = /* @__PURE__ */ new Map();
369
- _log = chunkU4UFTVLX_cjs.NOOP_LOGGER;
369
+ _log = chunkXAOA3XFQ_cjs.NOOP_LOGGER;
370
370
  setLogger(logger) {
371
371
  this._log = logger;
372
372
  }
@@ -592,7 +592,7 @@ var AudioRecorder = class {
592
592
  _started = false;
593
593
  _baseTs = null;
594
594
  _outCursor = 0;
595
- _log = chunkU4UFTVLX_cjs.NOOP_LOGGER;
595
+ _log = chunkXAOA3XFQ_cjs.NOOP_LOGGER;
596
596
  setLogger(logger) {
597
597
  this._log = logger;
598
598
  }
@@ -728,7 +728,7 @@ var MAX_ERROR_MESSAGE_LENGTH = 200;
728
728
  function getSdkInfo() {
729
729
  return {
730
730
  name: "clawops-node",
731
- version: chunkLUA53NPZ_cjs.VERSION,
731
+ version: chunkM4QUDFGP_cjs.VERSION,
732
732
  runtime: `node/${process.versions.node}`,
733
733
  os: `${process.platform}/${os__default.default.arch()}`
734
734
  };
@@ -764,6 +764,7 @@ var CallSession = class {
764
764
  startTime;
765
765
  metadata;
766
766
  _status;
767
+ _endedStatus = null;
767
768
  _sendAudioFn = null;
768
769
  _clearAudioFn = null;
769
770
  _hangupFn = null;
@@ -787,7 +788,7 @@ var CallSession = class {
787
788
  _dtmfCollectorActive = false;
788
789
  _dtmfResolvers = [];
789
790
  _dtmfBuffer = [];
790
- _log = chunkU4UFTVLX_cjs.NOOP_LOGGER;
791
+ _log = chunkXAOA3XFQ_cjs.NOOP_LOGGER;
791
792
  _handlers = /* @__PURE__ */ new Map();
792
793
  _endedPromise;
793
794
  _resolveEnded;
@@ -812,6 +813,17 @@ var CallSession = class {
812
813
  get status() {
813
814
  return this._status;
814
815
  }
816
+ /**
817
+ * 서버가 통보한 최종 종료 상태. 통화가 끝나기 전에는 null.
818
+ *
819
+ * `completed`(성사) / `no-answer`(벨은 울렸으나 무응답) / `busy`(통화중) /
820
+ * `rejected`(수신 거절) / `canceled`(응답 전 발신 측 취소) / `failed`(시스템·망 오류).
821
+ * `status` 는 SDK 내부 수명주기(ringing→active→ended)이므로 통화 성사 여부는
822
+ * 이 값이나 `call_failed` 이벤트로 판단한다.
823
+ */
824
+ get endedStatus() {
825
+ return this._endedStatus;
826
+ }
815
827
  get duration() {
816
828
  return (Date.now() - this.startTime.getTime()) / 1e3;
817
829
  }
@@ -972,8 +984,20 @@ var CallSession = class {
972
984
  async wait() {
973
985
  return this._endedPromise;
974
986
  }
975
- /** Mark the session as ended (called internally). */
976
- _markEnded() {
987
+ /**
988
+ * Mark the session as ended (called internally).
989
+ *
990
+ * @param status 서버가 통보한 최종 상태(completed/no-answer/busy/rejected/canceled/
991
+ * failed). 주어지면 `endedStatus` 로 확정한다 — 상대가 받지 않은 통화를 성사된 통화와
992
+ * 구분하기 위함이다. 생략하면 미디어 세션 정리 경로에서의 호출이므로, 아직 종료 전일
993
+ * 때만 'completed' 로 채우고 이미 확정된 서버 상태는 덮어쓰지 않는다.
994
+ */
995
+ _markEnded(status) {
996
+ if (status !== void 0) {
997
+ this._endedStatus = status;
998
+ } else if (this._status !== "ended") {
999
+ this._endedStatus = "completed";
1000
+ }
977
1001
  this._status = "ended";
978
1002
  this._resolveEnded();
979
1003
  }
@@ -1081,7 +1105,7 @@ function generateComfortTone(volume = 0.12) {
1081
1105
  }
1082
1106
  }
1083
1107
  const pcm = int16ArrayToBuffer(concatInt16Arrays(...parts));
1084
- const ulaw = chunkU4UFTVLX_cjs.pcm16ToUlaw(pcm);
1108
+ const ulaw = chunkXAOA3XFQ_cjs.pcm16ToUlaw(pcm);
1085
1109
  const chunks = [];
1086
1110
  for (let i = 0; i < ulaw.length; i += CHUNK_SIZE) {
1087
1111
  chunks.push(ulaw.subarray(i, i + CHUNK_SIZE));
@@ -1138,9 +1162,9 @@ function loadHoldAudio(source) {
1138
1162
  pcmData = mono;
1139
1163
  }
1140
1164
  if (sampleRate !== SAMPLE_RATE2) {
1141
- pcmData = chunkU4UFTVLX_cjs.resamplePcm16(pcmData, sampleRate, SAMPLE_RATE2);
1165
+ pcmData = chunkXAOA3XFQ_cjs.resamplePcm16(pcmData, sampleRate, SAMPLE_RATE2);
1142
1166
  }
1143
- const ulaw = chunkU4UFTVLX_cjs.pcm16ToUlaw(pcmData);
1167
+ const ulaw = chunkXAOA3XFQ_cjs.pcm16ToUlaw(pcmData);
1144
1168
  const chunks = [];
1145
1169
  for (let i = 0; i < ulaw.length; i += CHUNK_SIZE) {
1146
1170
  chunks.push(ulaw.subarray(i, i + CHUNK_SIZE));
@@ -1409,13 +1433,13 @@ var ClawOpsAgent = class _ClawOpsAgent {
1409
1433
  constructor(options) {
1410
1434
  this._apiKey = options.apiKey ?? process.env["CLAWOPS_API_KEY"] ?? "";
1411
1435
  this._accountId = options.accountId ?? process.env["CLAWOPS_ACCOUNT_ID"] ?? "";
1412
- this._baseUrl = options.baseUrl ?? chunkLUA53NPZ_cjs.DEFAULT_BASE_URL;
1436
+ this._baseUrl = options.baseUrl ?? chunkM4QUDFGP_cjs.DEFAULT_BASE_URL;
1413
1437
  this._fromNumber = options.from;
1414
1438
  this._session = options.session;
1415
1439
  this._recording = options.recording ?? false;
1416
1440
  this._recordingPath = options.recordingPath ?? "./recordings";
1417
1441
  this._mcpServers = options.mcpServers ?? [];
1418
- this._builtinTools = chunkU4UFTVLX_cjs.resolveBuiltinTools(options.builtinTools ?? "all" /* ALL */);
1442
+ this._builtinTools = chunkXAOA3XFQ_cjs.resolveBuiltinTools(options.builtinTools ?? "all" /* ALL */);
1419
1443
  this._passiveDtmfDebounceMs = options.passiveDtmfDebounceMs ?? 500;
1420
1444
  this._rxGain = _ClawOpsAgent._validateGain("rxGain", options.rxGain ?? 1);
1421
1445
  this._txGain = _ClawOpsAgent._validateGain("txGain", options.txGain ?? 1);
@@ -1424,8 +1448,8 @@ var ClawOpsAgent = class _ClawOpsAgent {
1424
1448
  if (options.tracing) {
1425
1449
  setTracingConfig(options.tracing);
1426
1450
  }
1427
- this._log = chunkU4UFTVLX_cjs.createAgentLogger(options.logger);
1428
- this._pipelineLog = chunkU4UFTVLX_cjs.createPipelineLogger(this._log);
1451
+ this._log = chunkXAOA3XFQ_cjs.createAgentLogger(options.logger);
1452
+ this._pipelineLog = chunkXAOA3XFQ_cjs.createPipelineLogger(this._log);
1429
1453
  this._isPipelineSession = "_stt" in this._session && "_llm" in this._session;
1430
1454
  if (options.toolConfig?.holdAudio) {
1431
1455
  this._holdAudioChunks = loadHoldAudio(options.toolConfig.holdAudio);
@@ -1433,7 +1457,7 @@ var ClawOpsAgent = class _ClawOpsAgent {
1433
1457
  }
1434
1458
  static _validateGain(name, gain) {
1435
1459
  if (typeof gain !== "number" || !Number.isFinite(gain) || gain < 0) {
1436
- throw new chunkLUA53NPZ_cjs.AgentError(`${name}=${gain} must be a finite number >= 0`);
1460
+ throw new chunkM4QUDFGP_cjs.AgentError(`${name}=${gain} must be a finite number >= 0`);
1437
1461
  }
1438
1462
  return gain;
1439
1463
  }
@@ -1447,7 +1471,7 @@ var ClawOpsAgent = class _ClawOpsAgent {
1447
1471
  tool(nameOrTool, description, parameters, handler) {
1448
1472
  if (typeof nameOrTool === "string") {
1449
1473
  if (!description || !parameters || !handler) {
1450
- throw new chunkLUA53NPZ_cjs.AgentError(
1474
+ throw new chunkM4QUDFGP_cjs.AgentError(
1451
1475
  "tool(name, description, parameters, handler) requires all arguments."
1452
1476
  );
1453
1477
  }
@@ -1483,10 +1507,10 @@ var ClawOpsAgent = class _ClawOpsAgent {
1483
1507
  async connect() {
1484
1508
  if (this._controlWs) return;
1485
1509
  if (!this._apiKey) {
1486
- throw new chunkLUA53NPZ_cjs.AgentError("API key is required. Set CLAWOPS_API_KEY or pass apiKey option.");
1510
+ throw new chunkM4QUDFGP_cjs.AgentError("API key is required. Set CLAWOPS_API_KEY or pass apiKey option.");
1487
1511
  }
1488
1512
  if (!this._accountId) {
1489
- throw new chunkLUA53NPZ_cjs.AgentError(
1513
+ throw new chunkM4QUDFGP_cjs.AgentError(
1490
1514
  "Account ID is required. Set CLAWOPS_ACCOUNT_ID or pass accountId option."
1491
1515
  );
1492
1516
  }
@@ -1510,7 +1534,7 @@ var ClawOpsAgent = class _ClawOpsAgent {
1510
1534
  } catch {
1511
1535
  }
1512
1536
  } catch (err) {
1513
- throw new chunkLUA53NPZ_cjs.AgentConnectionError(
1537
+ throw new chunkM4QUDFGP_cjs.AgentConnectionError(
1514
1538
  `Failed to connect to ClawOps: ${err instanceof Error ? err.message : String(err)}`
1515
1539
  );
1516
1540
  }
@@ -1574,7 +1598,7 @@ var ClawOpsAgent = class _ClawOpsAgent {
1574
1598
  });
1575
1599
  if (resp.status !== 201) {
1576
1600
  const error = await resp.json();
1577
- throw new chunkLUA53NPZ_cjs.AgentError(`\uBC1C\uC2E0 \uC2E4\uD328 (${resp.status}): ${error["error"] ?? ""}`);
1601
+ throw new chunkM4QUDFGP_cjs.AgentError(`\uBC1C\uC2E0 \uC2E4\uD328 (${resp.status}): ${error["error"] ?? ""}`);
1578
1602
  }
1579
1603
  const data = await resp.json();
1580
1604
  const callSession = new CallSession({
@@ -1625,10 +1649,15 @@ var ClawOpsAgent = class _ClawOpsAgent {
1625
1649
  }
1626
1650
  _handleEnded(event) {
1627
1651
  const callId = event["callId"];
1652
+ const status = event["status"] || "completed";
1628
1653
  const session = this._activeSessions.get(callId);
1629
1654
  if (session) {
1630
- this._log.info("Call ended (server): %s", callId);
1631
- session._markEnded();
1655
+ this._log.info("Call ended (server): %s (status=%s)", callId, status);
1656
+ if (status !== "completed") {
1657
+ this._log.info("Outbound call not connected: %s (%s)", callId, status);
1658
+ session._emit("call_failed", status);
1659
+ }
1660
+ session._markEnded(status);
1632
1661
  this._activeSessions.delete(callId);
1633
1662
  }
1634
1663
  void this._cleanupPrewarm(callId);
@@ -1693,10 +1722,43 @@ var ClawOpsAgent = class _ClawOpsAgent {
1693
1722
  * multiple times — only the first invocation starts the task. Failures are
1694
1723
  * recorded in _prewarmFailed so the call-session path can fall back to start().
1695
1724
  */
1725
+ /**
1726
+ * 세션에 콜별 의존성(도구·내장도구·hold audio·recorder·logger)을 주입한다.
1727
+ *
1728
+ * prewarm 과 _startCallSession 양쪽에서 부른다. prewarm 은 세션이 LLM 에 보낼
1729
+ * tool 스키마를 그 시점에 확정하므로(OpenAI session.update / Gemini live connect
1730
+ * config), prewarm **전에** 최소 한 번은 도구가 들어가 있어야 한다.
1731
+ */
1732
+ _injectSessionDeps(tools, recorder) {
1733
+ const sessionHandler = this._session;
1734
+ if ("setToolRegistry" in sessionHandler && typeof sessionHandler.setToolRegistry === "function") {
1735
+ sessionHandler.setToolRegistry(tools);
1736
+ }
1737
+ if (recorder && "setRecorder" in sessionHandler && typeof sessionHandler.setRecorder === "function") {
1738
+ sessionHandler.setRecorder(recorder);
1739
+ }
1740
+ if ("setBuiltinTools" in sessionHandler && typeof sessionHandler.setBuiltinTools === "function") {
1741
+ sessionHandler.setBuiltinTools(this._builtinTools);
1742
+ }
1743
+ if ("setLogger" in sessionHandler && typeof sessionHandler.setLogger === "function") {
1744
+ sessionHandler.setLogger(this._isPipelineSession ? this._pipelineLog : this._log);
1745
+ }
1746
+ if (this._holdAudioChunks && "setHoldAudio" in sessionHandler && typeof sessionHandler.setHoldAudio === "function") {
1747
+ sessionHandler.setHoldAudio(this._holdAudioChunks);
1748
+ }
1749
+ }
1696
1750
  _startPrewarm(callId) {
1697
1751
  if (this._prewarmTasks.has(callId)) return;
1698
1752
  const sessionHandler = this._session;
1699
1753
  if (typeof sessionHandler.prewarm !== "function") return;
1754
+ if (this._mcpServers.length > 0 && sessionHandler.toolsFrozenAfterPrewarm) {
1755
+ this._log.info(
1756
+ { callId },
1757
+ "Skipping prewarm \u2014 \uC138\uC158\uC774 \uC5F0\uACB0 \uD6C4 \uB3C4\uAD6C \uBCC0\uACBD\uC744 \uC9C0\uC6D0\uD558\uC9C0 \uC54A\uC544 MCP \uB3C4\uAD6C\uAC00 \uB204\uB77D\uB41C\uB2E4."
1758
+ );
1759
+ return;
1760
+ }
1761
+ this._injectSessionDeps(this._tools.fork());
1700
1762
  const PREWARM_TIMEOUT_MS = 1e4;
1701
1763
  const t0 = Date.now();
1702
1764
  this._log.info(`[PREWARM-T] start call_id=${callId} t=${(t0 / 1e3).toFixed(3)}`);
@@ -1740,9 +1802,10 @@ var ClawOpsAgent = class _ClawOpsAgent {
1740
1802
  const callId = event["callId"];
1741
1803
  const session = this._activeSessions.get(callId);
1742
1804
  if (session) {
1743
- this._log.info("Outbound call failed: %s (%s)", callId, event["reason"] ?? "failed");
1744
- session._emit("call_failed", event["reason"] ?? "failed");
1745
- session._markEnded();
1805
+ const reason = event["reason"] ?? "failed";
1806
+ this._log.info("Outbound call failed: %s (%s)", callId, reason);
1807
+ session._emit("call_failed", reason);
1808
+ session._markEnded(reason);
1746
1809
  this._activeSessions.delete(callId);
1747
1810
  }
1748
1811
  void this._cleanupPrewarm(callId);
@@ -1833,9 +1896,9 @@ var ClawOpsAgent = class _ClawOpsAgent {
1833
1896
  let latestMediaTs = 0;
1834
1897
  session._bindTransport(
1835
1898
  (audio) => {
1836
- const gained = chunkU4UFTVLX_cjs.applyUlawGain(audio, this._txGain);
1899
+ const gained = chunkXAOA3XFQ_cjs.applyUlawGain(audio, this._txGain);
1837
1900
  if (recorder) {
1838
- recorder.writeOutbound(chunkU4UFTVLX_cjs.ulawToPcm16(gained), latestMediaTs);
1901
+ recorder.writeOutbound(chunkXAOA3XFQ_cjs.ulawToPcm16(gained), latestMediaTs);
1839
1902
  }
1840
1903
  mediaWs.sendAudio(gained.toString("base64"));
1841
1904
  session.recordFirstResponse();
@@ -1861,27 +1924,13 @@ var ClawOpsAgent = class _ClawOpsAgent {
1861
1924
  session._waitForMark = (name, timeoutMs) => mediaWs.waitForMark(name, timeoutMs);
1862
1925
  session._flushTransport = () => mediaWs.flush();
1863
1926
  const sessionHandler = this._session;
1864
- if ("setToolRegistry" in sessionHandler && typeof sessionHandler.setToolRegistry === "function") {
1865
- sessionHandler.setToolRegistry(sessionTools);
1866
- }
1867
- if (recorder && "setRecorder" in sessionHandler && typeof sessionHandler.setRecorder === "function") {
1868
- sessionHandler.setRecorder(recorder);
1869
- }
1870
- if ("setBuiltinTools" in sessionHandler && typeof sessionHandler.setBuiltinTools === "function") {
1871
- sessionHandler.setBuiltinTools(this._builtinTools);
1872
- }
1873
- if ("setLogger" in sessionHandler && typeof sessionHandler.setLogger === "function") {
1874
- sessionHandler.setLogger(this._isPipelineSession ? this._pipelineLog : this._log);
1875
- }
1876
- if (this._holdAudioChunks && "setHoldAudio" in sessionHandler && typeof sessionHandler.setHoldAudio === "function") {
1877
- sessionHandler.setHoldAudio(this._holdAudioChunks);
1878
- }
1927
+ this._injectSessionDeps(sessionTools, recorder);
1879
1928
  this._callSessions.set(session.callId, sessionHandler);
1880
1929
  mediaWs.onAudio((ulawAudio, timestamp) => {
1881
1930
  latestMediaTs = timestamp;
1882
- const gained = chunkU4UFTVLX_cjs.applyUlawGain(ulawAudio, this._rxGain);
1931
+ const gained = chunkXAOA3XFQ_cjs.applyUlawGain(ulawAudio, this._rxGain);
1883
1932
  if (recorder) {
1884
- recorder.writeInbound(chunkU4UFTVLX_cjs.ulawToPcm16(gained), timestamp);
1933
+ recorder.writeInbound(chunkXAOA3XFQ_cjs.ulawToPcm16(gained), timestamp);
1885
1934
  }
1886
1935
  if (sessionHandler) {
1887
1936
  sessionHandler.feedAudio(gained, timestamp);
@@ -1996,7 +2045,7 @@ var PipelineSession = class {
1996
2045
  _speaking = false;
1997
2046
  _builtinTools = null;
1998
2047
  _holdAudioChunks = null;
1999
- _log = chunkU4UFTVLX_cjs.NOOP_LOGGER;
2048
+ _log = chunkXAOA3XFQ_cjs.NOOP_LOGGER;
2000
2049
  constructor(options) {
2001
2050
  this._stt = options.stt;
2002
2051
  this._llm = options.llm;
@@ -2058,7 +2107,7 @@ var PipelineSession = class {
2058
2107
  */
2059
2108
  async prewarm(tools) {
2060
2109
  if (tools) this._tools = tools;
2061
- this._callSession = new chunkU4UFTVLX_cjs.BufferingCall();
2110
+ this._callSession = new chunkXAOA3XFQ_cjs.BufferingCall();
2062
2111
  this._running = true;
2063
2112
  this._conversation = [];
2064
2113
  this._log.info("PipelineSession prewarmed");
@@ -2081,7 +2130,7 @@ var PipelineSession = class {
2081
2130
  async attach(callSession) {
2082
2131
  const prev = this._callSession;
2083
2132
  this._callSession = callSession;
2084
- chunkU4UFTVLX_cjs.attachBuffered(prev, callSession);
2133
+ chunkXAOA3XFQ_cjs.attachBuffered(prev, callSession);
2085
2134
  }
2086
2135
  async start(callSession, tools) {
2087
2136
  this._tools = tools ?? null;
@@ -2128,8 +2177,8 @@ var PipelineSession = class {
2128
2177
  while (this._running) {
2129
2178
  if (this._audioBuffer.length > 0) {
2130
2179
  const ulaw = this._audioBuffer.shift();
2131
- const pcm8k = chunkU4UFTVLX_cjs.ulawToPcm16(ulaw);
2132
- const pcm16k = chunkU4UFTVLX_cjs.resamplePcm16(pcm8k, 8e3, 16e3);
2180
+ const pcm8k = chunkXAOA3XFQ_cjs.ulawToPcm16(ulaw);
2181
+ const pcm16k = chunkXAOA3XFQ_cjs.resamplePcm16(pcm8k, 8e3, 16e3);
2133
2182
  yield pcm16k;
2134
2183
  } else {
2135
2184
  await new Promise((resolve) => setTimeout(resolve, 20));
@@ -2145,7 +2194,7 @@ var PipelineSession = class {
2145
2194
  await this._respond();
2146
2195
  }
2147
2196
  _buildEffectiveTools() {
2148
- const builtinSchemas = chunkU4UFTVLX_cjs.getBuiltinToolSchemas(this._builtinTools, "chat");
2197
+ const builtinSchemas = chunkXAOA3XFQ_cjs.getBuiltinToolSchemas(this._builtinTools, "chat");
2149
2198
  const dtmfSchemas = builtinSchemas.filter((s) => {
2150
2199
  const name = s["function"]?.["name"];
2151
2200
  return name === "collect_dtmf" || name === "send_dtmf";
@@ -2194,8 +2243,8 @@ var PipelineSession = class {
2194
2243
  const { id, name, arguments: argsStr } = chunk.toolCall;
2195
2244
  try {
2196
2245
  const args = JSON.parse(argsStr);
2197
- if (chunkU4UFTVLX_cjs.BUILTIN_TOOL_NAMES.has(name) && this._callSession && !(this._callSession instanceof chunkU4UFTVLX_cjs.BufferingCall)) {
2198
- const result2 = await chunkU4UFTVLX_cjs.executeBuiltinTool(name, args, this._callSession);
2246
+ if (chunkXAOA3XFQ_cjs.BUILTIN_TOOL_NAMES.has(name) && this._callSession && !(this._callSession instanceof chunkXAOA3XFQ_cjs.BufferingCall)) {
2247
+ const result2 = await chunkXAOA3XFQ_cjs.executeBuiltinTool(name, args, this._callSession);
2199
2248
  if (result2 !== null) {
2200
2249
  if (name === "hang_up") return;
2201
2250
  this._conversation.push({ role: "tool", content: result2, tool_call_id: id, name });
@@ -2203,9 +2252,20 @@ var PipelineSession = class {
2203
2252
  return;
2204
2253
  }
2205
2254
  }
2255
+ if (chunkXAOA3XFQ_cjs.BUILTIN_TOOL_NAMES.has(name) && this._callSession instanceof chunkXAOA3XFQ_cjs.BufferingCall) {
2256
+ this._log.warn("Builtin tool %s called before answer \u2014 deferring", name);
2257
+ this._conversation.push({
2258
+ role: "tool",
2259
+ content: chunkXAOA3XFQ_cjs.CALL_NOT_READY_RESULT,
2260
+ tool_call_id: id,
2261
+ name
2262
+ });
2263
+ await this._respond();
2264
+ return;
2265
+ }
2206
2266
  if (!this._tools) return;
2207
2267
  this._callSession?.recordToolCall();
2208
- const player = this._holdAudioChunks && this._callSession && !(this._callSession instanceof chunkU4UFTVLX_cjs.BufferingCall) ? new HoldAudioPlayer(this._callSession, this._holdAudioChunks) : null;
2268
+ const player = this._holdAudioChunks && this._callSession && !(this._callSession instanceof chunkXAOA3XFQ_cjs.BufferingCall) ? new HoldAudioPlayer(this._callSession, this._holdAudioChunks) : null;
2209
2269
  player?.start();
2210
2270
  let result;
2211
2271
  try {
@@ -2256,8 +2316,8 @@ var PipelineSession = class {
2256
2316
  sampleRate: this._sampleRate
2257
2317
  })) {
2258
2318
  if (!this._running || !this._speaking) break;
2259
- const pcm8k = chunkU4UFTVLX_cjs.resamplePcm16(audioChunk, this._sampleRate, 8e3);
2260
- const ulaw = chunkU4UFTVLX_cjs.pcm16ToUlaw(pcm8k);
2319
+ const pcm8k = chunkXAOA3XFQ_cjs.resamplePcm16(audioChunk, this._sampleRate, 8e3);
2320
+ const ulaw = chunkXAOA3XFQ_cjs.pcm16ToUlaw(pcm8k);
2261
2321
  for (let off = 0; off < ulaw.length; off += 160) {
2262
2322
  let chunk = ulaw.subarray(off, off + 160);
2263
2323
  if (chunk.length < 160) {
@@ -2284,8 +2344,13 @@ var OpenAIRealtime = class {
2284
2344
  _language;
2285
2345
  _turnDetection;
2286
2346
  _greeting;
2287
- _log = chunkU4UFTVLX_cjs.NOOP_LOGGER;
2347
+ _log = chunkXAOA3XFQ_cjs.NOOP_LOGGER;
2288
2348
  _builtinTools = null;
2349
+ /**
2350
+ * 마지막 session.update 로 LLM 에 알린 tool 이름들. attach() 가 이 값과 현재
2351
+ * registry 를 비교해 달라졌을 때만(=MCP 도구 등 뒤늦게 붙은 경우) 재전송한다.
2352
+ */
2353
+ _sentToolNames = null;
2289
2354
  setLogger(logger) {
2290
2355
  this._log = logger;
2291
2356
  }
@@ -2356,7 +2421,7 @@ var OpenAIRealtime = class {
2356
2421
  /** Open WS + session.update + (optional) response.create without a CallSession. */
2357
2422
  async prewarm(tools) {
2358
2423
  if (tools) this._tools = tools;
2359
- this._call = new chunkU4UFTVLX_cjs.BufferingCall();
2424
+ this._call = new chunkXAOA3XFQ_cjs.BufferingCall();
2360
2425
  this._closed = false;
2361
2426
  this._playback = null;
2362
2427
  this._latestMediaTs = 0;
@@ -2395,9 +2460,10 @@ var OpenAIRealtime = class {
2395
2460
  }
2396
2461
  /** Attach a real CallSession to the prewarmed session and flush buffered audio. */
2397
2462
  async attach(callSession) {
2463
+ this._resyncTools();
2398
2464
  const prev = this._call;
2399
2465
  this._call = callSession;
2400
- chunkU4UFTVLX_cjs.attachBuffered(prev, callSession);
2466
+ chunkXAOA3XFQ_cjs.attachBuffered(prev, callSession);
2401
2467
  }
2402
2468
  async start(callSession, tools) {
2403
2469
  if (tools) this._tools = tools;
@@ -2455,10 +2521,36 @@ var OpenAIRealtime = class {
2455
2521
  });
2456
2522
  }
2457
2523
  }
2524
+ /** Current tool schemas: user tools + builtin tools (flat realtime format). */
2525
+ _currentToolSchemas() {
2526
+ const toolSchemas = this._tools ? this._tools.toOpenAITools().map((t) => ({ type: "function", ...t.function })) : [];
2527
+ toolSchemas.push(...chunkXAOA3XFQ_cjs.getBuiltinToolSchemas(this._builtinTools, "realtime"));
2528
+ return toolSchemas;
2529
+ }
2530
+ /**
2531
+ * prewarm 이후 도구가 바뀌었으면 session.update 로 재전송한다.
2532
+ *
2533
+ * MCP 도구는 통화 시작 시점에 registry 에 붙으므로 prewarm 시점의 스키마에는
2534
+ * 없다. 여기서 맞춰주지 않으면 발신 통화에서 MCP 도구를 영영 못 쓴다.
2535
+ */
2536
+ _resyncTools() {
2537
+ if (!this._ws || this._ws.readyState !== 1) return;
2538
+ const toolSchemas = this._currentToolSchemas();
2539
+ const names = toolSchemas.map((t) => String(t["name"] ?? ""));
2540
+ if (this._sentToolNames && names.length === this._sentToolNames.length && names.every((n, i) => n === this._sentToolNames[i])) {
2541
+ return;
2542
+ }
2543
+ this._send({
2544
+ type: "session.update",
2545
+ session: { type: "realtime", tools: toolSchemas }
2546
+ });
2547
+ this._sentToolNames = names;
2548
+ this._log.info("Tool schema resynced after prewarm: %s", names.join(", "));
2549
+ }
2458
2550
  _sendSessionUpdate() {
2459
2551
  if (!this._ws || this._ws.readyState !== 1) return;
2460
- const toolSchemas = this._tools ? this._tools.toOpenAITools().map((t) => ({ type: "function", ...t.function })) : [];
2461
- toolSchemas.push(...chunkU4UFTVLX_cjs.getBuiltinToolSchemas(this._builtinTools, "realtime"));
2552
+ const toolSchemas = this._currentToolSchemas();
2553
+ this._sentToolNames = toolSchemas.map((t) => String(t["name"] ?? ""));
2462
2554
  this._send({
2463
2555
  type: "session.update",
2464
2556
  session: {
@@ -2603,9 +2695,9 @@ var OpenAIRealtime = class {
2603
2695
  const controller = new AbortController();
2604
2696
  this._pendingToolCalls.set(callId, controller);
2605
2697
  try {
2606
- if (chunkU4UFTVLX_cjs.BUILTIN_TOOL_NAMES.has(funcName) && this._call && !(this._call instanceof chunkU4UFTVLX_cjs.BufferingCall)) {
2698
+ if (chunkXAOA3XFQ_cjs.BUILTIN_TOOL_NAMES.has(funcName) && this._call && !(this._call instanceof chunkXAOA3XFQ_cjs.BufferingCall)) {
2607
2699
  const args = JSON.parse(item["arguments"] ?? "{}");
2608
- const result2 = await chunkU4UFTVLX_cjs.executeBuiltinTool(funcName, args, this._call);
2700
+ const result2 = await chunkXAOA3XFQ_cjs.executeBuiltinTool(funcName, args, this._call);
2609
2701
  if (result2 !== null) {
2610
2702
  if (funcName === "hang_up") return;
2611
2703
  if (controller.signal.aborted) return;
@@ -2622,6 +2714,20 @@ var OpenAIRealtime = class {
2622
2714
  return;
2623
2715
  }
2624
2716
  }
2717
+ if (chunkXAOA3XFQ_cjs.BUILTIN_TOOL_NAMES.has(funcName) && this._call instanceof chunkXAOA3XFQ_cjs.BufferingCall) {
2718
+ this._log.warn("Builtin tool %s called before answer \u2014 deferring", funcName);
2719
+ await this._waitForResponseDone();
2720
+ this._send({
2721
+ type: "conversation.item.create",
2722
+ item: {
2723
+ type: "function_call_output",
2724
+ call_id: callId,
2725
+ output: chunkXAOA3XFQ_cjs.CALL_NOT_READY_RESULT
2726
+ }
2727
+ });
2728
+ this._send({ type: "response.create" });
2729
+ return;
2730
+ }
2625
2731
  if (!this._tools || !this._tools.has(funcName)) {
2626
2732
  this._log.error("Unknown tool: %s", funcName);
2627
2733
  await this._waitForResponseDone();
@@ -2637,7 +2743,7 @@ var OpenAIRealtime = class {
2637
2743
  return;
2638
2744
  }
2639
2745
  let result;
2640
- const player = this._holdAudioChunks && this._call && !(this._call instanceof chunkU4UFTVLX_cjs.BufferingCall) ? new HoldAudioPlayer(this._call, this._holdAudioChunks) : null;
2746
+ const player = this._holdAudioChunks && this._call && !(this._call instanceof chunkXAOA3XFQ_cjs.BufferingCall) ? new HoldAudioPlayer(this._call, this._holdAudioChunks) : null;
2641
2747
  player?.start();
2642
2748
  try {
2643
2749
  const args = JSON.parse(item["arguments"] ?? "{}");
@@ -2759,6 +2865,13 @@ function sanitizeSchemaForGemini(schema, defs, depth = 0) {
2759
2865
  return result;
2760
2866
  }
2761
2867
  var GeminiRealtime = class _GeminiRealtime {
2868
+ /**
2869
+ * Gemini Live 는 connect 시점의 config 로 도구가 고정되어 세션 도중 변경할 수 없다.
2870
+ *
2871
+ * 통화 시작 시점에야 붙는 MCP 도구는 prewarm 된 세션에 넣을 방법이 없으므로,
2872
+ * `ClawOpsAgent` 가 MCP 서버 설정과 이 플래그를 보고 prewarm 을 건너뛴다.
2873
+ */
2874
+ toolsFrozenAfterPrewarm = true;
2762
2875
  _apiKey;
2763
2876
  _systemPrompt;
2764
2877
  _model;
@@ -2779,7 +2892,7 @@ var GeminiRealtime = class _GeminiRealtime {
2779
2892
  _toolDrainTimer = null;
2780
2893
  _lastAudioTime = 0;
2781
2894
  _holdAudioChunks = null;
2782
- _log = chunkU4UFTVLX_cjs.NOOP_LOGGER;
2895
+ _log = chunkXAOA3XFQ_cjs.NOOP_LOGGER;
2783
2896
  constructor(options = {}) {
2784
2897
  this._apiKey = options.apiKey ?? process.env["GOOGLE_API_KEY"] ?? "";
2785
2898
  this._systemPrompt = options.systemPrompt ?? "";
@@ -2825,7 +2938,7 @@ var GeminiRealtime = class _GeminiRealtime {
2825
2938
  /** Open Live session (no CallSession). Audio deltas accumulate into BufferingCall until attach(). */
2826
2939
  async prewarm(tools) {
2827
2940
  if (tools) this._tools = tools;
2828
- this._call = new chunkU4UFTVLX_cjs.BufferingCall();
2941
+ this._call = new chunkXAOA3XFQ_cjs.BufferingCall();
2829
2942
  this._closed = false;
2830
2943
  this._sentAudioChunks = 0;
2831
2944
  this._audioRemainder = Buffer.alloc(0);
@@ -2882,7 +2995,7 @@ var GeminiRealtime = class _GeminiRealtime {
2882
2995
  async attach(callSession) {
2883
2996
  const prev = this._call;
2884
2997
  this._call = callSession;
2885
- chunkU4UFTVLX_cjs.attachBuffered(prev, callSession);
2998
+ chunkXAOA3XFQ_cjs.attachBuffered(prev, callSession);
2886
2999
  }
2887
3000
  async start(callSession, tools) {
2888
3001
  if (tools) this._tools = tools;
@@ -2891,8 +3004,8 @@ var GeminiRealtime = class _GeminiRealtime {
2891
3004
  }
2892
3005
  feedAudio(audio) {
2893
3006
  if (this._session && !this._closed) {
2894
- const pcm8k = chunkU4UFTVLX_cjs.ulawToPcm16(audio);
2895
- const pcm16k = chunkU4UFTVLX_cjs.resamplePcm16(pcm8k, 8e3, 16e3);
3007
+ const pcm8k = chunkXAOA3XFQ_cjs.ulawToPcm16(audio);
3008
+ const pcm16k = chunkXAOA3XFQ_cjs.resamplePcm16(pcm8k, 8e3, 16e3);
2896
3009
  this._session.sendRealtimeInput({
2897
3010
  audio: {
2898
3011
  data: Buffer.from(pcm16k).toString("base64"),
@@ -2936,7 +3049,7 @@ var GeminiRealtime = class _GeminiRealtime {
2936
3049
  t.function.parameters ?? { type: "object", properties: {} }
2937
3050
  )
2938
3051
  })) : [];
2939
- toolDefs.push(...chunkU4UFTVLX_cjs.getBuiltinToolSchemas(this._builtinTools, "gemini"));
3052
+ toolDefs.push(...chunkXAOA3XFQ_cjs.getBuiltinToolSchemas(this._builtinTools, "gemini"));
2940
3053
  return toolDefs;
2941
3054
  }
2942
3055
  _handleMessage(msg) {
@@ -2996,8 +3109,8 @@ var GeminiRealtime = class _GeminiRealtime {
2996
3109
  _handleAudioData(b64Data) {
2997
3110
  if (!this._call) return;
2998
3111
  const pcm24k = Buffer.from(b64Data, "base64");
2999
- const pcm8k = chunkU4UFTVLX_cjs.resamplePcm16(pcm24k, 24e3, 8e3);
3000
- const ulaw = chunkU4UFTVLX_cjs.pcm16ToUlaw(pcm8k);
3112
+ const pcm8k = chunkXAOA3XFQ_cjs.resamplePcm16(pcm24k, 24e3, 8e3);
3113
+ const ulaw = chunkXAOA3XFQ_cjs.pcm16ToUlaw(pcm8k);
3001
3114
  const combined = Buffer.concat([this._audioRemainder, ulaw]);
3002
3115
  const chunkSize = 160;
3003
3116
  const fullEnd = Math.floor(combined.length / chunkSize) * chunkSize;
@@ -3040,7 +3153,7 @@ var GeminiRealtime = class _GeminiRealtime {
3040
3153
  const functionCalls = toolCall.functionCalls;
3041
3154
  if (!functionCalls) return;
3042
3155
  const responses = [];
3043
- const player = this._holdAudioChunks && this._call && !(this._call instanceof chunkU4UFTVLX_cjs.BufferingCall) ? new HoldAudioPlayer(this._call, this._holdAudioChunks) : null;
3156
+ const player = this._holdAudioChunks && this._call && !(this._call instanceof chunkXAOA3XFQ_cjs.BufferingCall) ? new HoldAudioPlayer(this._call, this._holdAudioChunks) : null;
3044
3157
  player?.start();
3045
3158
  try {
3046
3159
  for (const fc of functionCalls) {
@@ -3048,8 +3161,8 @@ var GeminiRealtime = class _GeminiRealtime {
3048
3161
  const fcId = fc.id ?? "";
3049
3162
  const args = fc.args ?? {};
3050
3163
  this._log.info({ tool: name, args }, "Tool call: %s", name);
3051
- if (chunkU4UFTVLX_cjs.BUILTIN_TOOL_NAMES.has(name) && this._call && !(this._call instanceof chunkU4UFTVLX_cjs.BufferingCall)) {
3052
- const result = await chunkU4UFTVLX_cjs.executeBuiltinTool(
3164
+ if (chunkXAOA3XFQ_cjs.BUILTIN_TOOL_NAMES.has(name) && this._call && !(this._call instanceof chunkXAOA3XFQ_cjs.BufferingCall)) {
3165
+ const result = await chunkXAOA3XFQ_cjs.executeBuiltinTool(
3053
3166
  name,
3054
3167
  args,
3055
3168
  this._call
@@ -3064,6 +3177,11 @@ var GeminiRealtime = class _GeminiRealtime {
3064
3177
  continue;
3065
3178
  }
3066
3179
  }
3180
+ if (chunkXAOA3XFQ_cjs.BUILTIN_TOOL_NAMES.has(name) && this._call instanceof chunkXAOA3XFQ_cjs.BufferingCall) {
3181
+ this._log.warn("Builtin tool %s called before answer \u2014 deferring", name);
3182
+ responses.push({ id: fcId, name, response: { result: chunkXAOA3XFQ_cjs.CALL_NOT_READY_RESULT } });
3183
+ continue;
3184
+ }
3067
3185
  if (!this._tools || !this._tools.has(name)) {
3068
3186
  this._log.error("Unknown tool: %s", name);
3069
3187
  responses.push({ id: fcId, name, response: { error: `Unknown tool: ${name}` } });
@@ -3106,7 +3224,7 @@ var GeminiRealtime = class _GeminiRealtime {
3106
3224
  // src/agent/pipeline/stt/deepgram-stt.ts
3107
3225
  var DeepgramSTT = class {
3108
3226
  _options;
3109
- _log = chunkU4UFTVLX_cjs.NOOP_LOGGER;
3227
+ _log = chunkXAOA3XFQ_cjs.NOOP_LOGGER;
3110
3228
  get provider() {
3111
3229
  return "deepgram";
3112
3230
  }
@@ -3233,7 +3351,7 @@ var DeepgramSTT = class {
3233
3351
  // src/agent/pipeline/tts/elevenlabs-tts.ts
3234
3352
  var ElevenLabsTTS = class {
3235
3353
  _options;
3236
- _log = chunkU4UFTVLX_cjs.NOOP_LOGGER;
3354
+ _log = chunkXAOA3XFQ_cjs.NOOP_LOGGER;
3237
3355
  get provider() {
3238
3356
  return "elevenlabs";
3239
3357
  }
@@ -3979,47 +4097,47 @@ function mcpServerHTTP(options) {
3979
4097
 
3980
4098
  Object.defineProperty(exports, "BUILTIN_TOOL_NAMES", {
3981
4099
  enumerable: true,
3982
- get: function () { return chunkU4UFTVLX_cjs.BUILTIN_TOOL_NAMES; }
4100
+ get: function () { return chunkXAOA3XFQ_cjs.BUILTIN_TOOL_NAMES; }
3983
4101
  });
3984
4102
  Object.defineProperty(exports, "BuiltinTool", {
3985
4103
  enumerable: true,
3986
- get: function () { return chunkU4UFTVLX_cjs.BuiltinTool; }
4104
+ get: function () { return chunkXAOA3XFQ_cjs.BuiltinTool; }
3987
4105
  });
3988
4106
  Object.defineProperty(exports, "DECODE_TABLE", {
3989
4107
  enumerable: true,
3990
- get: function () { return chunkU4UFTVLX_cjs.DECODE_TABLE; }
4108
+ get: function () { return chunkXAOA3XFQ_cjs.DECODE_TABLE; }
3991
4109
  });
3992
4110
  Object.defineProperty(exports, "createAgentLogger", {
3993
4111
  enumerable: true,
3994
- get: function () { return chunkU4UFTVLX_cjs.createAgentLogger; }
4112
+ get: function () { return chunkXAOA3XFQ_cjs.createAgentLogger; }
3995
4113
  });
3996
4114
  Object.defineProperty(exports, "createPipelineLogger", {
3997
4115
  enumerable: true,
3998
- get: function () { return chunkU4UFTVLX_cjs.createPipelineLogger; }
4116
+ get: function () { return chunkXAOA3XFQ_cjs.createPipelineLogger; }
3999
4117
  });
4000
4118
  Object.defineProperty(exports, "executeBuiltinTool", {
4001
4119
  enumerable: true,
4002
- get: function () { return chunkU4UFTVLX_cjs.executeBuiltinTool; }
4120
+ get: function () { return chunkXAOA3XFQ_cjs.executeBuiltinTool; }
4003
4121
  });
4004
4122
  Object.defineProperty(exports, "getBuiltinToolSchemas", {
4005
4123
  enumerable: true,
4006
- get: function () { return chunkU4UFTVLX_cjs.getBuiltinToolSchemas; }
4124
+ get: function () { return chunkXAOA3XFQ_cjs.getBuiltinToolSchemas; }
4007
4125
  });
4008
4126
  Object.defineProperty(exports, "isBuiltinTool", {
4009
4127
  enumerable: true,
4010
- get: function () { return chunkU4UFTVLX_cjs.isBuiltinTool; }
4128
+ get: function () { return chunkXAOA3XFQ_cjs.isBuiltinTool; }
4011
4129
  });
4012
4130
  Object.defineProperty(exports, "pcm16ToUlaw", {
4013
4131
  enumerable: true,
4014
- get: function () { return chunkU4UFTVLX_cjs.pcm16ToUlaw; }
4132
+ get: function () { return chunkXAOA3XFQ_cjs.pcm16ToUlaw; }
4015
4133
  });
4016
4134
  Object.defineProperty(exports, "resamplePcm16", {
4017
4135
  enumerable: true,
4018
- get: function () { return chunkU4UFTVLX_cjs.resamplePcm16; }
4136
+ get: function () { return chunkXAOA3XFQ_cjs.resamplePcm16; }
4019
4137
  });
4020
4138
  Object.defineProperty(exports, "ulawToPcm16", {
4021
4139
  enumerable: true,
4022
- get: function () { return chunkU4UFTVLX_cjs.ulawToPcm16; }
4140
+ get: function () { return chunkXAOA3XFQ_cjs.ulawToPcm16; }
4023
4141
  });
4024
4142
  exports.AnthropicLLM = AnthropicLLM;
4025
4143
  exports.AudioRecorder = AudioRecorder;