@teamlearners/clawops 0.25.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 chunkIVJXHNUN_cjs = require('../chunk-IVJXHNUN.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: chunkIVJXHNUN_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
  };
@@ -788,7 +788,7 @@ var CallSession = class {
788
788
  _dtmfCollectorActive = false;
789
789
  _dtmfResolvers = [];
790
790
  _dtmfBuffer = [];
791
- _log = chunkU4UFTVLX_cjs.NOOP_LOGGER;
791
+ _log = chunkXAOA3XFQ_cjs.NOOP_LOGGER;
792
792
  _handlers = /* @__PURE__ */ new Map();
793
793
  _endedPromise;
794
794
  _resolveEnded;
@@ -1105,7 +1105,7 @@ function generateComfortTone(volume = 0.12) {
1105
1105
  }
1106
1106
  }
1107
1107
  const pcm = int16ArrayToBuffer(concatInt16Arrays(...parts));
1108
- const ulaw = chunkU4UFTVLX_cjs.pcm16ToUlaw(pcm);
1108
+ const ulaw = chunkXAOA3XFQ_cjs.pcm16ToUlaw(pcm);
1109
1109
  const chunks = [];
1110
1110
  for (let i = 0; i < ulaw.length; i += CHUNK_SIZE) {
1111
1111
  chunks.push(ulaw.subarray(i, i + CHUNK_SIZE));
@@ -1162,9 +1162,9 @@ function loadHoldAudio(source) {
1162
1162
  pcmData = mono;
1163
1163
  }
1164
1164
  if (sampleRate !== SAMPLE_RATE2) {
1165
- pcmData = chunkU4UFTVLX_cjs.resamplePcm16(pcmData, sampleRate, SAMPLE_RATE2);
1165
+ pcmData = chunkXAOA3XFQ_cjs.resamplePcm16(pcmData, sampleRate, SAMPLE_RATE2);
1166
1166
  }
1167
- const ulaw = chunkU4UFTVLX_cjs.pcm16ToUlaw(pcmData);
1167
+ const ulaw = chunkXAOA3XFQ_cjs.pcm16ToUlaw(pcmData);
1168
1168
  const chunks = [];
1169
1169
  for (let i = 0; i < ulaw.length; i += CHUNK_SIZE) {
1170
1170
  chunks.push(ulaw.subarray(i, i + CHUNK_SIZE));
@@ -1433,13 +1433,13 @@ var ClawOpsAgent = class _ClawOpsAgent {
1433
1433
  constructor(options) {
1434
1434
  this._apiKey = options.apiKey ?? process.env["CLAWOPS_API_KEY"] ?? "";
1435
1435
  this._accountId = options.accountId ?? process.env["CLAWOPS_ACCOUNT_ID"] ?? "";
1436
- this._baseUrl = options.baseUrl ?? chunkIVJXHNUN_cjs.DEFAULT_BASE_URL;
1436
+ this._baseUrl = options.baseUrl ?? chunkM4QUDFGP_cjs.DEFAULT_BASE_URL;
1437
1437
  this._fromNumber = options.from;
1438
1438
  this._session = options.session;
1439
1439
  this._recording = options.recording ?? false;
1440
1440
  this._recordingPath = options.recordingPath ?? "./recordings";
1441
1441
  this._mcpServers = options.mcpServers ?? [];
1442
- this._builtinTools = chunkU4UFTVLX_cjs.resolveBuiltinTools(options.builtinTools ?? "all" /* ALL */);
1442
+ this._builtinTools = chunkXAOA3XFQ_cjs.resolveBuiltinTools(options.builtinTools ?? "all" /* ALL */);
1443
1443
  this._passiveDtmfDebounceMs = options.passiveDtmfDebounceMs ?? 500;
1444
1444
  this._rxGain = _ClawOpsAgent._validateGain("rxGain", options.rxGain ?? 1);
1445
1445
  this._txGain = _ClawOpsAgent._validateGain("txGain", options.txGain ?? 1);
@@ -1448,8 +1448,8 @@ var ClawOpsAgent = class _ClawOpsAgent {
1448
1448
  if (options.tracing) {
1449
1449
  setTracingConfig(options.tracing);
1450
1450
  }
1451
- this._log = chunkU4UFTVLX_cjs.createAgentLogger(options.logger);
1452
- this._pipelineLog = chunkU4UFTVLX_cjs.createPipelineLogger(this._log);
1451
+ this._log = chunkXAOA3XFQ_cjs.createAgentLogger(options.logger);
1452
+ this._pipelineLog = chunkXAOA3XFQ_cjs.createPipelineLogger(this._log);
1453
1453
  this._isPipelineSession = "_stt" in this._session && "_llm" in this._session;
1454
1454
  if (options.toolConfig?.holdAudio) {
1455
1455
  this._holdAudioChunks = loadHoldAudio(options.toolConfig.holdAudio);
@@ -1457,7 +1457,7 @@ var ClawOpsAgent = class _ClawOpsAgent {
1457
1457
  }
1458
1458
  static _validateGain(name, gain) {
1459
1459
  if (typeof gain !== "number" || !Number.isFinite(gain) || gain < 0) {
1460
- throw new chunkIVJXHNUN_cjs.AgentError(`${name}=${gain} must be a finite number >= 0`);
1460
+ throw new chunkM4QUDFGP_cjs.AgentError(`${name}=${gain} must be a finite number >= 0`);
1461
1461
  }
1462
1462
  return gain;
1463
1463
  }
@@ -1471,7 +1471,7 @@ var ClawOpsAgent = class _ClawOpsAgent {
1471
1471
  tool(nameOrTool, description, parameters, handler) {
1472
1472
  if (typeof nameOrTool === "string") {
1473
1473
  if (!description || !parameters || !handler) {
1474
- throw new chunkIVJXHNUN_cjs.AgentError(
1474
+ throw new chunkM4QUDFGP_cjs.AgentError(
1475
1475
  "tool(name, description, parameters, handler) requires all arguments."
1476
1476
  );
1477
1477
  }
@@ -1507,10 +1507,10 @@ var ClawOpsAgent = class _ClawOpsAgent {
1507
1507
  async connect() {
1508
1508
  if (this._controlWs) return;
1509
1509
  if (!this._apiKey) {
1510
- throw new chunkIVJXHNUN_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.");
1511
1511
  }
1512
1512
  if (!this._accountId) {
1513
- throw new chunkIVJXHNUN_cjs.AgentError(
1513
+ throw new chunkM4QUDFGP_cjs.AgentError(
1514
1514
  "Account ID is required. Set CLAWOPS_ACCOUNT_ID or pass accountId option."
1515
1515
  );
1516
1516
  }
@@ -1534,7 +1534,7 @@ var ClawOpsAgent = class _ClawOpsAgent {
1534
1534
  } catch {
1535
1535
  }
1536
1536
  } catch (err) {
1537
- throw new chunkIVJXHNUN_cjs.AgentConnectionError(
1537
+ throw new chunkM4QUDFGP_cjs.AgentConnectionError(
1538
1538
  `Failed to connect to ClawOps: ${err instanceof Error ? err.message : String(err)}`
1539
1539
  );
1540
1540
  }
@@ -1598,7 +1598,7 @@ var ClawOpsAgent = class _ClawOpsAgent {
1598
1598
  });
1599
1599
  if (resp.status !== 201) {
1600
1600
  const error = await resp.json();
1601
- throw new chunkIVJXHNUN_cjs.AgentError(`\uBC1C\uC2E0 \uC2E4\uD328 (${resp.status}): ${error["error"] ?? ""}`);
1601
+ throw new chunkM4QUDFGP_cjs.AgentError(`\uBC1C\uC2E0 \uC2E4\uD328 (${resp.status}): ${error["error"] ?? ""}`);
1602
1602
  }
1603
1603
  const data = await resp.json();
1604
1604
  const callSession = new CallSession({
@@ -1722,10 +1722,43 @@ var ClawOpsAgent = class _ClawOpsAgent {
1722
1722
  * multiple times — only the first invocation starts the task. Failures are
1723
1723
  * recorded in _prewarmFailed so the call-session path can fall back to start().
1724
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
+ }
1725
1750
  _startPrewarm(callId) {
1726
1751
  if (this._prewarmTasks.has(callId)) return;
1727
1752
  const sessionHandler = this._session;
1728
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());
1729
1762
  const PREWARM_TIMEOUT_MS = 1e4;
1730
1763
  const t0 = Date.now();
1731
1764
  this._log.info(`[PREWARM-T] start call_id=${callId} t=${(t0 / 1e3).toFixed(3)}`);
@@ -1863,9 +1896,9 @@ var ClawOpsAgent = class _ClawOpsAgent {
1863
1896
  let latestMediaTs = 0;
1864
1897
  session._bindTransport(
1865
1898
  (audio) => {
1866
- const gained = chunkU4UFTVLX_cjs.applyUlawGain(audio, this._txGain);
1899
+ const gained = chunkXAOA3XFQ_cjs.applyUlawGain(audio, this._txGain);
1867
1900
  if (recorder) {
1868
- recorder.writeOutbound(chunkU4UFTVLX_cjs.ulawToPcm16(gained), latestMediaTs);
1901
+ recorder.writeOutbound(chunkXAOA3XFQ_cjs.ulawToPcm16(gained), latestMediaTs);
1869
1902
  }
1870
1903
  mediaWs.sendAudio(gained.toString("base64"));
1871
1904
  session.recordFirstResponse();
@@ -1891,27 +1924,13 @@ var ClawOpsAgent = class _ClawOpsAgent {
1891
1924
  session._waitForMark = (name, timeoutMs) => mediaWs.waitForMark(name, timeoutMs);
1892
1925
  session._flushTransport = () => mediaWs.flush();
1893
1926
  const sessionHandler = this._session;
1894
- if ("setToolRegistry" in sessionHandler && typeof sessionHandler.setToolRegistry === "function") {
1895
- sessionHandler.setToolRegistry(sessionTools);
1896
- }
1897
- if (recorder && "setRecorder" in sessionHandler && typeof sessionHandler.setRecorder === "function") {
1898
- sessionHandler.setRecorder(recorder);
1899
- }
1900
- if ("setBuiltinTools" in sessionHandler && typeof sessionHandler.setBuiltinTools === "function") {
1901
- sessionHandler.setBuiltinTools(this._builtinTools);
1902
- }
1903
- if ("setLogger" in sessionHandler && typeof sessionHandler.setLogger === "function") {
1904
- sessionHandler.setLogger(this._isPipelineSession ? this._pipelineLog : this._log);
1905
- }
1906
- if (this._holdAudioChunks && "setHoldAudio" in sessionHandler && typeof sessionHandler.setHoldAudio === "function") {
1907
- sessionHandler.setHoldAudio(this._holdAudioChunks);
1908
- }
1927
+ this._injectSessionDeps(sessionTools, recorder);
1909
1928
  this._callSessions.set(session.callId, sessionHandler);
1910
1929
  mediaWs.onAudio((ulawAudio, timestamp) => {
1911
1930
  latestMediaTs = timestamp;
1912
- const gained = chunkU4UFTVLX_cjs.applyUlawGain(ulawAudio, this._rxGain);
1931
+ const gained = chunkXAOA3XFQ_cjs.applyUlawGain(ulawAudio, this._rxGain);
1913
1932
  if (recorder) {
1914
- recorder.writeInbound(chunkU4UFTVLX_cjs.ulawToPcm16(gained), timestamp);
1933
+ recorder.writeInbound(chunkXAOA3XFQ_cjs.ulawToPcm16(gained), timestamp);
1915
1934
  }
1916
1935
  if (sessionHandler) {
1917
1936
  sessionHandler.feedAudio(gained, timestamp);
@@ -2026,7 +2045,7 @@ var PipelineSession = class {
2026
2045
  _speaking = false;
2027
2046
  _builtinTools = null;
2028
2047
  _holdAudioChunks = null;
2029
- _log = chunkU4UFTVLX_cjs.NOOP_LOGGER;
2048
+ _log = chunkXAOA3XFQ_cjs.NOOP_LOGGER;
2030
2049
  constructor(options) {
2031
2050
  this._stt = options.stt;
2032
2051
  this._llm = options.llm;
@@ -2088,7 +2107,7 @@ var PipelineSession = class {
2088
2107
  */
2089
2108
  async prewarm(tools) {
2090
2109
  if (tools) this._tools = tools;
2091
- this._callSession = new chunkU4UFTVLX_cjs.BufferingCall();
2110
+ this._callSession = new chunkXAOA3XFQ_cjs.BufferingCall();
2092
2111
  this._running = true;
2093
2112
  this._conversation = [];
2094
2113
  this._log.info("PipelineSession prewarmed");
@@ -2111,7 +2130,7 @@ var PipelineSession = class {
2111
2130
  async attach(callSession) {
2112
2131
  const prev = this._callSession;
2113
2132
  this._callSession = callSession;
2114
- chunkU4UFTVLX_cjs.attachBuffered(prev, callSession);
2133
+ chunkXAOA3XFQ_cjs.attachBuffered(prev, callSession);
2115
2134
  }
2116
2135
  async start(callSession, tools) {
2117
2136
  this._tools = tools ?? null;
@@ -2158,8 +2177,8 @@ var PipelineSession = class {
2158
2177
  while (this._running) {
2159
2178
  if (this._audioBuffer.length > 0) {
2160
2179
  const ulaw = this._audioBuffer.shift();
2161
- const pcm8k = chunkU4UFTVLX_cjs.ulawToPcm16(ulaw);
2162
- const pcm16k = chunkU4UFTVLX_cjs.resamplePcm16(pcm8k, 8e3, 16e3);
2180
+ const pcm8k = chunkXAOA3XFQ_cjs.ulawToPcm16(ulaw);
2181
+ const pcm16k = chunkXAOA3XFQ_cjs.resamplePcm16(pcm8k, 8e3, 16e3);
2163
2182
  yield pcm16k;
2164
2183
  } else {
2165
2184
  await new Promise((resolve) => setTimeout(resolve, 20));
@@ -2175,7 +2194,7 @@ var PipelineSession = class {
2175
2194
  await this._respond();
2176
2195
  }
2177
2196
  _buildEffectiveTools() {
2178
- const builtinSchemas = chunkU4UFTVLX_cjs.getBuiltinToolSchemas(this._builtinTools, "chat");
2197
+ const builtinSchemas = chunkXAOA3XFQ_cjs.getBuiltinToolSchemas(this._builtinTools, "chat");
2179
2198
  const dtmfSchemas = builtinSchemas.filter((s) => {
2180
2199
  const name = s["function"]?.["name"];
2181
2200
  return name === "collect_dtmf" || name === "send_dtmf";
@@ -2224,8 +2243,8 @@ var PipelineSession = class {
2224
2243
  const { id, name, arguments: argsStr } = chunk.toolCall;
2225
2244
  try {
2226
2245
  const args = JSON.parse(argsStr);
2227
- if (chunkU4UFTVLX_cjs.BUILTIN_TOOL_NAMES.has(name) && this._callSession && !(this._callSession instanceof chunkU4UFTVLX_cjs.BufferingCall)) {
2228
- 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);
2229
2248
  if (result2 !== null) {
2230
2249
  if (name === "hang_up") return;
2231
2250
  this._conversation.push({ role: "tool", content: result2, tool_call_id: id, name });
@@ -2233,9 +2252,20 @@ var PipelineSession = class {
2233
2252
  return;
2234
2253
  }
2235
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
+ }
2236
2266
  if (!this._tools) return;
2237
2267
  this._callSession?.recordToolCall();
2238
- 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;
2239
2269
  player?.start();
2240
2270
  let result;
2241
2271
  try {
@@ -2286,8 +2316,8 @@ var PipelineSession = class {
2286
2316
  sampleRate: this._sampleRate
2287
2317
  })) {
2288
2318
  if (!this._running || !this._speaking) break;
2289
- const pcm8k = chunkU4UFTVLX_cjs.resamplePcm16(audioChunk, this._sampleRate, 8e3);
2290
- const ulaw = chunkU4UFTVLX_cjs.pcm16ToUlaw(pcm8k);
2319
+ const pcm8k = chunkXAOA3XFQ_cjs.resamplePcm16(audioChunk, this._sampleRate, 8e3);
2320
+ const ulaw = chunkXAOA3XFQ_cjs.pcm16ToUlaw(pcm8k);
2291
2321
  for (let off = 0; off < ulaw.length; off += 160) {
2292
2322
  let chunk = ulaw.subarray(off, off + 160);
2293
2323
  if (chunk.length < 160) {
@@ -2314,8 +2344,13 @@ var OpenAIRealtime = class {
2314
2344
  _language;
2315
2345
  _turnDetection;
2316
2346
  _greeting;
2317
- _log = chunkU4UFTVLX_cjs.NOOP_LOGGER;
2347
+ _log = chunkXAOA3XFQ_cjs.NOOP_LOGGER;
2318
2348
  _builtinTools = null;
2349
+ /**
2350
+ * 마지막 session.update 로 LLM 에 알린 tool 이름들. attach() 가 이 값과 현재
2351
+ * registry 를 비교해 달라졌을 때만(=MCP 도구 등 뒤늦게 붙은 경우) 재전송한다.
2352
+ */
2353
+ _sentToolNames = null;
2319
2354
  setLogger(logger) {
2320
2355
  this._log = logger;
2321
2356
  }
@@ -2386,7 +2421,7 @@ var OpenAIRealtime = class {
2386
2421
  /** Open WS + session.update + (optional) response.create without a CallSession. */
2387
2422
  async prewarm(tools) {
2388
2423
  if (tools) this._tools = tools;
2389
- this._call = new chunkU4UFTVLX_cjs.BufferingCall();
2424
+ this._call = new chunkXAOA3XFQ_cjs.BufferingCall();
2390
2425
  this._closed = false;
2391
2426
  this._playback = null;
2392
2427
  this._latestMediaTs = 0;
@@ -2425,9 +2460,10 @@ var OpenAIRealtime = class {
2425
2460
  }
2426
2461
  /** Attach a real CallSession to the prewarmed session and flush buffered audio. */
2427
2462
  async attach(callSession) {
2463
+ this._resyncTools();
2428
2464
  const prev = this._call;
2429
2465
  this._call = callSession;
2430
- chunkU4UFTVLX_cjs.attachBuffered(prev, callSession);
2466
+ chunkXAOA3XFQ_cjs.attachBuffered(prev, callSession);
2431
2467
  }
2432
2468
  async start(callSession, tools) {
2433
2469
  if (tools) this._tools = tools;
@@ -2485,10 +2521,36 @@ var OpenAIRealtime = class {
2485
2521
  });
2486
2522
  }
2487
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
+ }
2488
2550
  _sendSessionUpdate() {
2489
2551
  if (!this._ws || this._ws.readyState !== 1) return;
2490
- const toolSchemas = this._tools ? this._tools.toOpenAITools().map((t) => ({ type: "function", ...t.function })) : [];
2491
- toolSchemas.push(...chunkU4UFTVLX_cjs.getBuiltinToolSchemas(this._builtinTools, "realtime"));
2552
+ const toolSchemas = this._currentToolSchemas();
2553
+ this._sentToolNames = toolSchemas.map((t) => String(t["name"] ?? ""));
2492
2554
  this._send({
2493
2555
  type: "session.update",
2494
2556
  session: {
@@ -2633,9 +2695,9 @@ var OpenAIRealtime = class {
2633
2695
  const controller = new AbortController();
2634
2696
  this._pendingToolCalls.set(callId, controller);
2635
2697
  try {
2636
- 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)) {
2637
2699
  const args = JSON.parse(item["arguments"] ?? "{}");
2638
- const result2 = await chunkU4UFTVLX_cjs.executeBuiltinTool(funcName, args, this._call);
2700
+ const result2 = await chunkXAOA3XFQ_cjs.executeBuiltinTool(funcName, args, this._call);
2639
2701
  if (result2 !== null) {
2640
2702
  if (funcName === "hang_up") return;
2641
2703
  if (controller.signal.aborted) return;
@@ -2652,6 +2714,20 @@ var OpenAIRealtime = class {
2652
2714
  return;
2653
2715
  }
2654
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
+ }
2655
2731
  if (!this._tools || !this._tools.has(funcName)) {
2656
2732
  this._log.error("Unknown tool: %s", funcName);
2657
2733
  await this._waitForResponseDone();
@@ -2667,7 +2743,7 @@ var OpenAIRealtime = class {
2667
2743
  return;
2668
2744
  }
2669
2745
  let result;
2670
- 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;
2671
2747
  player?.start();
2672
2748
  try {
2673
2749
  const args = JSON.parse(item["arguments"] ?? "{}");
@@ -2789,6 +2865,13 @@ function sanitizeSchemaForGemini(schema, defs, depth = 0) {
2789
2865
  return result;
2790
2866
  }
2791
2867
  var GeminiRealtime = class _GeminiRealtime {
2868
+ /**
2869
+ * Gemini Live 는 connect 시점의 config 로 도구가 고정되어 세션 도중 변경할 수 없다.
2870
+ *
2871
+ * 통화 시작 시점에야 붙는 MCP 도구는 prewarm 된 세션에 넣을 방법이 없으므로,
2872
+ * `ClawOpsAgent` 가 MCP 서버 설정과 이 플래그를 보고 prewarm 을 건너뛴다.
2873
+ */
2874
+ toolsFrozenAfterPrewarm = true;
2792
2875
  _apiKey;
2793
2876
  _systemPrompt;
2794
2877
  _model;
@@ -2809,7 +2892,7 @@ var GeminiRealtime = class _GeminiRealtime {
2809
2892
  _toolDrainTimer = null;
2810
2893
  _lastAudioTime = 0;
2811
2894
  _holdAudioChunks = null;
2812
- _log = chunkU4UFTVLX_cjs.NOOP_LOGGER;
2895
+ _log = chunkXAOA3XFQ_cjs.NOOP_LOGGER;
2813
2896
  constructor(options = {}) {
2814
2897
  this._apiKey = options.apiKey ?? process.env["GOOGLE_API_KEY"] ?? "";
2815
2898
  this._systemPrompt = options.systemPrompt ?? "";
@@ -2855,7 +2938,7 @@ var GeminiRealtime = class _GeminiRealtime {
2855
2938
  /** Open Live session (no CallSession). Audio deltas accumulate into BufferingCall until attach(). */
2856
2939
  async prewarm(tools) {
2857
2940
  if (tools) this._tools = tools;
2858
- this._call = new chunkU4UFTVLX_cjs.BufferingCall();
2941
+ this._call = new chunkXAOA3XFQ_cjs.BufferingCall();
2859
2942
  this._closed = false;
2860
2943
  this._sentAudioChunks = 0;
2861
2944
  this._audioRemainder = Buffer.alloc(0);
@@ -2912,7 +2995,7 @@ var GeminiRealtime = class _GeminiRealtime {
2912
2995
  async attach(callSession) {
2913
2996
  const prev = this._call;
2914
2997
  this._call = callSession;
2915
- chunkU4UFTVLX_cjs.attachBuffered(prev, callSession);
2998
+ chunkXAOA3XFQ_cjs.attachBuffered(prev, callSession);
2916
2999
  }
2917
3000
  async start(callSession, tools) {
2918
3001
  if (tools) this._tools = tools;
@@ -2921,8 +3004,8 @@ var GeminiRealtime = class _GeminiRealtime {
2921
3004
  }
2922
3005
  feedAudio(audio) {
2923
3006
  if (this._session && !this._closed) {
2924
- const pcm8k = chunkU4UFTVLX_cjs.ulawToPcm16(audio);
2925
- const pcm16k = chunkU4UFTVLX_cjs.resamplePcm16(pcm8k, 8e3, 16e3);
3007
+ const pcm8k = chunkXAOA3XFQ_cjs.ulawToPcm16(audio);
3008
+ const pcm16k = chunkXAOA3XFQ_cjs.resamplePcm16(pcm8k, 8e3, 16e3);
2926
3009
  this._session.sendRealtimeInput({
2927
3010
  audio: {
2928
3011
  data: Buffer.from(pcm16k).toString("base64"),
@@ -2966,7 +3049,7 @@ var GeminiRealtime = class _GeminiRealtime {
2966
3049
  t.function.parameters ?? { type: "object", properties: {} }
2967
3050
  )
2968
3051
  })) : [];
2969
- toolDefs.push(...chunkU4UFTVLX_cjs.getBuiltinToolSchemas(this._builtinTools, "gemini"));
3052
+ toolDefs.push(...chunkXAOA3XFQ_cjs.getBuiltinToolSchemas(this._builtinTools, "gemini"));
2970
3053
  return toolDefs;
2971
3054
  }
2972
3055
  _handleMessage(msg) {
@@ -3026,8 +3109,8 @@ var GeminiRealtime = class _GeminiRealtime {
3026
3109
  _handleAudioData(b64Data) {
3027
3110
  if (!this._call) return;
3028
3111
  const pcm24k = Buffer.from(b64Data, "base64");
3029
- const pcm8k = chunkU4UFTVLX_cjs.resamplePcm16(pcm24k, 24e3, 8e3);
3030
- const ulaw = chunkU4UFTVLX_cjs.pcm16ToUlaw(pcm8k);
3112
+ const pcm8k = chunkXAOA3XFQ_cjs.resamplePcm16(pcm24k, 24e3, 8e3);
3113
+ const ulaw = chunkXAOA3XFQ_cjs.pcm16ToUlaw(pcm8k);
3031
3114
  const combined = Buffer.concat([this._audioRemainder, ulaw]);
3032
3115
  const chunkSize = 160;
3033
3116
  const fullEnd = Math.floor(combined.length / chunkSize) * chunkSize;
@@ -3070,7 +3153,7 @@ var GeminiRealtime = class _GeminiRealtime {
3070
3153
  const functionCalls = toolCall.functionCalls;
3071
3154
  if (!functionCalls) return;
3072
3155
  const responses = [];
3073
- 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;
3074
3157
  player?.start();
3075
3158
  try {
3076
3159
  for (const fc of functionCalls) {
@@ -3078,8 +3161,8 @@ var GeminiRealtime = class _GeminiRealtime {
3078
3161
  const fcId = fc.id ?? "";
3079
3162
  const args = fc.args ?? {};
3080
3163
  this._log.info({ tool: name, args }, "Tool call: %s", name);
3081
- if (chunkU4UFTVLX_cjs.BUILTIN_TOOL_NAMES.has(name) && this._call && !(this._call instanceof chunkU4UFTVLX_cjs.BufferingCall)) {
3082
- 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(
3083
3166
  name,
3084
3167
  args,
3085
3168
  this._call
@@ -3094,6 +3177,11 @@ var GeminiRealtime = class _GeminiRealtime {
3094
3177
  continue;
3095
3178
  }
3096
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
+ }
3097
3185
  if (!this._tools || !this._tools.has(name)) {
3098
3186
  this._log.error("Unknown tool: %s", name);
3099
3187
  responses.push({ id: fcId, name, response: { error: `Unknown tool: ${name}` } });
@@ -3136,7 +3224,7 @@ var GeminiRealtime = class _GeminiRealtime {
3136
3224
  // src/agent/pipeline/stt/deepgram-stt.ts
3137
3225
  var DeepgramSTT = class {
3138
3226
  _options;
3139
- _log = chunkU4UFTVLX_cjs.NOOP_LOGGER;
3227
+ _log = chunkXAOA3XFQ_cjs.NOOP_LOGGER;
3140
3228
  get provider() {
3141
3229
  return "deepgram";
3142
3230
  }
@@ -3263,7 +3351,7 @@ var DeepgramSTT = class {
3263
3351
  // src/agent/pipeline/tts/elevenlabs-tts.ts
3264
3352
  var ElevenLabsTTS = class {
3265
3353
  _options;
3266
- _log = chunkU4UFTVLX_cjs.NOOP_LOGGER;
3354
+ _log = chunkXAOA3XFQ_cjs.NOOP_LOGGER;
3267
3355
  get provider() {
3268
3356
  return "elevenlabs";
3269
3357
  }
@@ -4009,47 +4097,47 @@ function mcpServerHTTP(options) {
4009
4097
 
4010
4098
  Object.defineProperty(exports, "BUILTIN_TOOL_NAMES", {
4011
4099
  enumerable: true,
4012
- get: function () { return chunkU4UFTVLX_cjs.BUILTIN_TOOL_NAMES; }
4100
+ get: function () { return chunkXAOA3XFQ_cjs.BUILTIN_TOOL_NAMES; }
4013
4101
  });
4014
4102
  Object.defineProperty(exports, "BuiltinTool", {
4015
4103
  enumerable: true,
4016
- get: function () { return chunkU4UFTVLX_cjs.BuiltinTool; }
4104
+ get: function () { return chunkXAOA3XFQ_cjs.BuiltinTool; }
4017
4105
  });
4018
4106
  Object.defineProperty(exports, "DECODE_TABLE", {
4019
4107
  enumerable: true,
4020
- get: function () { return chunkU4UFTVLX_cjs.DECODE_TABLE; }
4108
+ get: function () { return chunkXAOA3XFQ_cjs.DECODE_TABLE; }
4021
4109
  });
4022
4110
  Object.defineProperty(exports, "createAgentLogger", {
4023
4111
  enumerable: true,
4024
- get: function () { return chunkU4UFTVLX_cjs.createAgentLogger; }
4112
+ get: function () { return chunkXAOA3XFQ_cjs.createAgentLogger; }
4025
4113
  });
4026
4114
  Object.defineProperty(exports, "createPipelineLogger", {
4027
4115
  enumerable: true,
4028
- get: function () { return chunkU4UFTVLX_cjs.createPipelineLogger; }
4116
+ get: function () { return chunkXAOA3XFQ_cjs.createPipelineLogger; }
4029
4117
  });
4030
4118
  Object.defineProperty(exports, "executeBuiltinTool", {
4031
4119
  enumerable: true,
4032
- get: function () { return chunkU4UFTVLX_cjs.executeBuiltinTool; }
4120
+ get: function () { return chunkXAOA3XFQ_cjs.executeBuiltinTool; }
4033
4121
  });
4034
4122
  Object.defineProperty(exports, "getBuiltinToolSchemas", {
4035
4123
  enumerable: true,
4036
- get: function () { return chunkU4UFTVLX_cjs.getBuiltinToolSchemas; }
4124
+ get: function () { return chunkXAOA3XFQ_cjs.getBuiltinToolSchemas; }
4037
4125
  });
4038
4126
  Object.defineProperty(exports, "isBuiltinTool", {
4039
4127
  enumerable: true,
4040
- get: function () { return chunkU4UFTVLX_cjs.isBuiltinTool; }
4128
+ get: function () { return chunkXAOA3XFQ_cjs.isBuiltinTool; }
4041
4129
  });
4042
4130
  Object.defineProperty(exports, "pcm16ToUlaw", {
4043
4131
  enumerable: true,
4044
- get: function () { return chunkU4UFTVLX_cjs.pcm16ToUlaw; }
4132
+ get: function () { return chunkXAOA3XFQ_cjs.pcm16ToUlaw; }
4045
4133
  });
4046
4134
  Object.defineProperty(exports, "resamplePcm16", {
4047
4135
  enumerable: true,
4048
- get: function () { return chunkU4UFTVLX_cjs.resamplePcm16; }
4136
+ get: function () { return chunkXAOA3XFQ_cjs.resamplePcm16; }
4049
4137
  });
4050
4138
  Object.defineProperty(exports, "ulawToPcm16", {
4051
4139
  enumerable: true,
4052
- get: function () { return chunkU4UFTVLX_cjs.ulawToPcm16; }
4140
+ get: function () { return chunkXAOA3XFQ_cjs.ulawToPcm16; }
4053
4141
  });
4054
4142
  exports.AnthropicLLM = AnthropicLLM;
4055
4143
  exports.AudioRecorder = AudioRecorder;