@teamlearners/clawops 0.5.3 → 0.5.5

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,4 +1,5 @@
1
1
  import { DEFAULT_BASE_URL, AgentError, AgentConnectionError } from '../chunk-7S2OEBS6.js';
2
+ import pino from 'pino';
2
3
  import * as fs from 'fs';
3
4
  import * as path from 'path';
4
5
 
@@ -322,10 +323,19 @@ function resamplePcm16(pcm, fromRate, toRate) {
322
323
  }
323
324
  return out;
324
325
  }
326
+ var DEFAULT_LOGGER = pino({ name: "clawops.agent" });
327
+ var NOOP_LOGGER = pino({ level: "silent" });
328
+ function createAgentLogger(userLogger) {
329
+ return userLogger ?? DEFAULT_LOGGER;
330
+ }
331
+ function createPipelineLogger(parent) {
332
+ return parent.child({ module: "pipeline" });
333
+ }
325
334
 
326
335
  // src/agent/control-ws.ts
327
336
  var INITIAL_RECONNECT_DELAY = 1e3;
328
337
  var MAX_RECONNECT_DELAY = 3e4;
338
+ var PING_TIMEOUT = 6e4;
329
339
  function buildControlWsUrl(options) {
330
340
  const scheme = options.baseUrl.startsWith("https") ? "wss" : "ws";
331
341
  const host = options.baseUrl.replace(/^https?:\/\//, "").replace(/\/$/, "");
@@ -350,6 +360,11 @@ var ControlWebSocket = class {
350
360
  _closed = false;
351
361
  _connectedResolve = null;
352
362
  _connectedPromise;
363
+ _log = NOOP_LOGGER;
364
+ _pingTimer = null;
365
+ setLogger(logger) {
366
+ this._log = logger;
367
+ }
353
368
  /** Register an event handler for a specific event type. */
354
369
  on(event, handler) {
355
370
  let list = this._handlers.get(event);
@@ -377,6 +392,7 @@ var ControlWebSocket = class {
377
392
  /** Close the WebSocket and stop reconnecting. */
378
393
  close() {
379
394
  this._closed = true;
395
+ this._clearPingTimer();
380
396
  if (this._ws) {
381
397
  this._ws.close();
382
398
  this._ws = null;
@@ -393,26 +409,32 @@ var ControlWebSocket = class {
393
409
  this._ws = ws;
394
410
  ws.on("open", () => {
395
411
  this._reconnectDelay = INITIAL_RECONNECT_DELAY;
412
+ this._resetPingTimer();
396
413
  if (this._connectedResolve) {
397
414
  this._connectedResolve();
398
415
  this._connectedResolve = null;
399
416
  }
417
+ this._log.info("Control WS connected: %s", this._url);
418
+ });
419
+ ws.on("ping", () => {
420
+ this._resetPingTimer();
400
421
  });
401
422
  ws.on("message", (data) => {
402
423
  try {
403
424
  const msg = JSON.parse(data.toString());
404
425
  this._dispatchEvent(msg);
405
426
  } catch {
406
- console.error("[ControlWebSocket] Failed to parse message");
427
+ this._log.warn("Control WS parse error");
407
428
  }
408
429
  });
409
430
  ws.on("close", () => {
431
+ this._clearPingTimer();
410
432
  if (!this._closed) {
411
433
  this._scheduleReconnect();
412
434
  }
413
435
  });
414
436
  ws.on("error", (err) => {
415
- console.error("[ControlWebSocket] Error:", err.message);
437
+ this._log.warn("Control WS error: %s", err.message);
416
438
  });
417
439
  }
418
440
  _dispatchEvent(event) {
@@ -423,22 +445,38 @@ var ControlWebSocket = class {
423
445
  const result = handler(event);
424
446
  if (result && typeof result.catch === "function") {
425
447
  result.catch((err) => {
426
- console.error(`[ControlWebSocket] Error in handler for ${event.event}:`, err);
448
+ this._log.error({ err }, "Control WS handler error: %s", event.event);
427
449
  });
428
450
  }
429
451
  } catch (err) {
430
- console.error(`[ControlWebSocket] Error in handler for ${event.event}:`, err);
452
+ this._log.error({ err }, "Control WS handler error: %s", event.event);
431
453
  }
432
454
  }
433
455
  }
434
456
  }
457
+ _resetPingTimer() {
458
+ this._clearPingTimer();
459
+ this._pingTimer = setTimeout(() => {
460
+ this._log.warn("Control WS ping timeout, closing connection");
461
+ if (this._ws) {
462
+ this._ws.terminate();
463
+ }
464
+ }, PING_TIMEOUT);
465
+ }
466
+ _clearPingTimer() {
467
+ if (this._pingTimer) {
468
+ clearTimeout(this._pingTimer);
469
+ this._pingTimer = null;
470
+ }
471
+ }
435
472
  _scheduleReconnect() {
436
473
  const delay = this._reconnectDelay;
437
474
  this._reconnectDelay = Math.min(this._reconnectDelay * 2, MAX_RECONNECT_DELAY);
475
+ this._log.info("Control WS reconnecting in %ds...", delay / 1e3);
438
476
  setTimeout(() => {
439
477
  if (!this._closed) {
440
478
  this._doConnect().catch((err) => {
441
- console.error("[ControlWebSocket] Reconnect failed:", err);
479
+ this._log.warn({ err }, "Control WS reconnect failed");
442
480
  this._scheduleReconnect();
443
481
  });
444
482
  }
@@ -450,6 +488,10 @@ var ControlWebSocket = class {
450
488
  var MCPClient = class {
451
489
  _servers = /* @__PURE__ */ new Map();
452
490
  _clients = /* @__PURE__ */ new Map();
491
+ _log = NOOP_LOGGER;
492
+ setLogger(logger) {
493
+ this._log = logger;
494
+ }
453
495
  /** Add an MCP server configuration. */
454
496
  addServer(name, config) {
455
497
  this._servers.set(name, config);
@@ -462,7 +504,7 @@ var MCPClient = class {
462
504
  const tools = await this._connectServer(name, config);
463
505
  allTools.push(...tools);
464
506
  } catch (err) {
465
- console.error(`[MCPClient] Failed to connect to server '${name}':`, err);
507
+ this._log.error({ err }, "MCP connection failed: %s", name);
466
508
  }
467
509
  }
468
510
  return allTools;
@@ -471,12 +513,13 @@ var MCPClient = class {
471
513
  async disconnect() {
472
514
  for (const [name, client] of this._clients) {
473
515
  try {
516
+ this._log.debug("MCP closing: %s", name);
474
517
  const c = client;
475
518
  if (c.close) {
476
519
  await c.close();
477
520
  }
478
521
  } catch (err) {
479
- console.error(`[MCPClient] Error disconnecting from '${name}':`, err);
522
+ this._log.error({ err }, "MCP disconnect error: %s", name);
480
523
  }
481
524
  }
482
525
  this._clients.clear();
@@ -485,6 +528,11 @@ var MCPClient = class {
485
528
  const sdk = await import('@modelcontextprotocol/sdk/client/index.js');
486
529
  const { Client } = sdk;
487
530
  const client = new Client({ name: `clawops-${name}`, version: "1.0.0" });
531
+ if (config.type === "stdio") {
532
+ this._log.debug("MCP connecting (stdio): %s", config["command"]);
533
+ } else if (config.type === "http") {
534
+ this._log.debug("MCP connecting (http): %s", config["url"]);
535
+ }
488
536
  let transport;
489
537
  if (config.type === "stdio") {
490
538
  const { StdioClientTransport } = await import('@modelcontextprotocol/sdk/client/stdio.js');
@@ -513,6 +561,7 @@ var MCPClient = class {
513
561
  parameters: inputSchema.properties ?? {},
514
562
  required: inputSchema.required ?? [],
515
563
  handler: async (args) => {
564
+ this._log.debug("MCP call_tool: %s", toolDef.name);
516
565
  const result = await client.callTool({
517
566
  name: toolDef.name,
518
567
  arguments: args
@@ -525,6 +574,8 @@ var MCPClient = class {
525
574
  }
526
575
  });
527
576
  }
577
+ this._log.info("MCP server connected: %d tools found", tools.length);
578
+ this._log.debug("MCP tools: %s", tools.map((t) => t.name));
528
579
  return tools;
529
580
  }
530
581
  };
@@ -579,6 +630,10 @@ var MediaWebSocket = class {
579
630
  _onClose = null;
580
631
  _onDtmf = null;
581
632
  _markWaiters = /* @__PURE__ */ new Map();
633
+ _log = NOOP_LOGGER;
634
+ setLogger(logger) {
635
+ this._log = logger;
636
+ }
582
637
  /** Set the handler for inbound audio data. */
583
638
  onAudio(handler) {
584
639
  this._onAudio = handler;
@@ -620,6 +675,7 @@ var MediaWebSocket = class {
620
675
  ws.on("open", () => {
621
676
  this._startSendLoop();
622
677
  resolve();
678
+ this._log.info("Media WS connected: %s", url);
623
679
  });
624
680
  ws.on("message", (data) => {
625
681
  try {
@@ -638,7 +694,7 @@ var MediaWebSocket = class {
638
694
  if (!this._ws) {
639
695
  reject(err);
640
696
  }
641
- console.error("[MediaWebSocket] Error:", err.message);
697
+ this._log.error({ err }, "Media WS error");
642
698
  });
643
699
  });
644
700
  }
@@ -799,6 +855,10 @@ var AudioRecorder = class {
799
855
  _mixWritten = 0;
800
856
  _startTime = 0;
801
857
  _started = false;
858
+ _log = NOOP_LOGGER;
859
+ setLogger(logger) {
860
+ this._log = logger;
861
+ }
802
862
  constructor(recordingPath, callId) {
803
863
  this._dir = path.join(recordingPath, callId);
804
864
  }
@@ -813,6 +873,7 @@ var AudioRecorder = class {
813
873
  fs.writeSync(this._fdMix, header);
814
874
  this._startTime = performance.now();
815
875
  this._started = true;
876
+ this._log.info("Recording started: %s", this._dir);
816
877
  }
817
878
  _expectedBytes() {
818
879
  const elapsed = (performance.now() - this._startTime) / 1e3;
@@ -865,7 +926,7 @@ var AudioRecorder = class {
865
926
  this._inWritten += pcm16_8k.length;
866
927
  this._writeToMix(pcm16_8k, posBefore);
867
928
  } catch (err) {
868
- console.error("Error writing inbound audio:", err);
929
+ this._log.error({ err }, "Recording write error (inbound)");
869
930
  }
870
931
  }
871
932
  writeOutbound(pcm16_8k) {
@@ -878,7 +939,7 @@ var AudioRecorder = class {
878
939
  this._outWritten += pcm16_8k.length;
879
940
  this._writeToMix(pcm16_8k, posBefore);
880
941
  } catch (err) {
881
- console.error("Error writing outbound audio:", err);
942
+ this._log.error({ err }, "Recording write error (outbound)");
882
943
  }
883
944
  }
884
945
  stop() {
@@ -899,8 +960,10 @@ var AudioRecorder = class {
899
960
  fs.writeSync(fd, makeWavHeader(maxWritten), 0, 44, 0);
900
961
  fs.closeSync(fd);
901
962
  }
963
+ const maxSec = maxWritten / 16e3;
964
+ this._log.info("Recording stopped: %s (%ds)", this._dir, maxSec);
902
965
  } catch (err) {
903
- console.error("Error stopping recorder:", err);
966
+ this._log.error({ err }, "Recording stop error");
904
967
  } finally {
905
968
  this._fdIn = null;
906
969
  this._fdOut = null;
@@ -930,6 +993,7 @@ var CallSession = class {
930
993
  _dtmfCollectorActive = false;
931
994
  _dtmfResolvers = [];
932
995
  _dtmfBuffer = [];
996
+ _log = NOOP_LOGGER;
933
997
  _handlers = /* @__PURE__ */ new Map();
934
998
  _endedPromise;
935
999
  _resolveEnded;
@@ -946,6 +1010,9 @@ var CallSession = class {
946
1010
  this._resolveEnded = resolve;
947
1011
  });
948
1012
  }
1013
+ setLogger(logger) {
1014
+ this._log = logger;
1015
+ }
949
1016
  get status() {
950
1017
  return this._status;
951
1018
  }
@@ -1021,7 +1088,9 @@ var CallSession = class {
1021
1088
  this._dtmfResolvers = [];
1022
1089
  this._dtmfBuffer = [];
1023
1090
  }
1024
- return collected.join("");
1091
+ const result = collected.join("");
1092
+ this._log.info("DTMF collected: %s", result);
1093
+ return result;
1025
1094
  }
1026
1095
  /** Send a sequence of DTMF digits. */
1027
1096
  async sendDtmfSequence(digits) {
@@ -1073,11 +1142,11 @@ var CallSession = class {
1073
1142
  const result = handler(this, ...args);
1074
1143
  if (result && typeof result.catch === "function") {
1075
1144
  result.catch((err) => {
1076
- console.error(`[CallSession] Error in ${event} handler:`, err);
1145
+ this._log.error({ err }, "CallSession handler error: %s", event);
1077
1146
  });
1078
1147
  }
1079
1148
  } catch (err) {
1080
- console.error(`[CallSession] Error in ${event} handler:`, err);
1149
+ this._log.error({ err }, "CallSession handler error: %s", event);
1081
1150
  }
1082
1151
  }
1083
1152
  }
@@ -1325,6 +1394,9 @@ var ClawOpsAgent = class {
1325
1394
  _passiveDtmfTimer = null;
1326
1395
  _passiveDtmfCallId = null;
1327
1396
  _callSessions = /* @__PURE__ */ new Map();
1397
+ _log;
1398
+ _pipelineLog;
1399
+ _isPipelineSession = false;
1328
1400
  constructor(options) {
1329
1401
  this._apiKey = options.apiKey ?? process.env["CLAWOPS_API_KEY"] ?? "";
1330
1402
  this._accountId = options.accountId ?? process.env["CLAWOPS_ACCOUNT_ID"] ?? "";
@@ -1339,6 +1411,9 @@ var ClawOpsAgent = class {
1339
1411
  if (options.tracing) {
1340
1412
  setTracingConfig(options.tracing);
1341
1413
  }
1414
+ this._log = createAgentLogger(options.logger);
1415
+ this._pipelineLog = createPipelineLogger(this._log);
1416
+ this._isPipelineSession = "_stt" in this._session && "_llm" in this._session;
1342
1417
  }
1343
1418
  /**
1344
1419
  * Register a function tool.
@@ -1398,6 +1473,7 @@ var ClawOpsAgent = class {
1398
1473
  accountId: this._accountId,
1399
1474
  number: this._fromNumber
1400
1475
  });
1476
+ this._controlWs.setLogger(this._log);
1401
1477
  this._controlWs.on("call.incoming", (event) => this._handleIncoming(event));
1402
1478
  this._controlWs.on("call.ended", (event) => this._handleEnded(event));
1403
1479
  this._controlWs.on("call.outbound_ready", (event) => this._handleOutboundReady(event));
@@ -1411,7 +1487,7 @@ var ClawOpsAgent = class {
1411
1487
  `Failed to connect to ClawOps: ${err instanceof Error ? err.message : String(err)}`
1412
1488
  );
1413
1489
  }
1414
- console.log(`[ClawOpsAgent] Connected on ${this._fromNumber}`);
1490
+ this._log.info("ClawOpsAgent connected on %s", this._fromNumber);
1415
1491
  }
1416
1492
  /**
1417
1493
  * Connect and block until disconnected.
@@ -1438,7 +1514,7 @@ var ClawOpsAgent = class {
1438
1514
  }
1439
1515
  this._activeSessions.clear();
1440
1516
  this._callSessions.clear();
1441
- console.log("[ClawOpsAgent] Disconnected");
1517
+ this._log.info("ClawOpsAgent disconnected");
1442
1518
  }
1443
1519
  /**
1444
1520
  * Initiate an outbound call.
@@ -1473,10 +1549,9 @@ var ClawOpsAgent = class {
1473
1549
  callSession.on(evt, handler);
1474
1550
  }
1475
1551
  }
1552
+ callSession.setLogger(this._log);
1476
1553
  this._activeSessions.set(callSession.callId, callSession);
1477
- console.log(
1478
- `[ClawOpsAgent] Outbound call initiated: ${this._fromNumber} -> ${to} (${callSession.callId})`
1479
- );
1554
+ this._log.info("Outbound call initiated: %s -> %s (%s)", this._fromNumber, to, callSession.callId);
1480
1555
  return callSession;
1481
1556
  }
1482
1557
  _handleIncoming(event) {
@@ -1495,13 +1570,15 @@ var ClawOpsAgent = class {
1495
1570
  session.on(evt, handler);
1496
1571
  }
1497
1572
  }
1573
+ session.setLogger(this._log);
1498
1574
  this._activeSessions.set(callId, session);
1575
+ this._log.info("Incoming call: %s -> %s (%s)", fromNumber, this._fromNumber, callId);
1499
1576
  if (this._controlWs) {
1500
1577
  this._controlWs.send({ event: "call.accept", callId });
1501
1578
  }
1502
1579
  if (mediaUrl) {
1503
1580
  this._startCallSession(session, mediaUrl).catch((err) => {
1504
- console.error(`[ClawOpsAgent] Error in call session ${callId}:`, err);
1581
+ this._log.error({ err }, "Call session error: %s", callId);
1505
1582
  });
1506
1583
  }
1507
1584
  }
@@ -1509,6 +1586,7 @@ var ClawOpsAgent = class {
1509
1586
  const callId = event["callId"];
1510
1587
  const session = this._activeSessions.get(callId);
1511
1588
  if (session) {
1589
+ this._log.info("Call ended (server): %s", callId);
1512
1590
  session._markEnded();
1513
1591
  this._activeSessions.delete(callId);
1514
1592
  }
@@ -1525,6 +1603,7 @@ var ClawOpsAgent = class {
1525
1603
  accountId: this._accountId,
1526
1604
  direction: "outbound"
1527
1605
  });
1606
+ session.setLogger(this._log);
1528
1607
  for (const [evt, handlers] of this._handlers) {
1529
1608
  for (const handler of handlers) {
1530
1609
  session.on(evt, handler);
@@ -1533,8 +1612,9 @@ var ClawOpsAgent = class {
1533
1612
  this._activeSessions.set(callId, session);
1534
1613
  }
1535
1614
  if (mediaUrl) {
1615
+ this._log.info("Outbound call answered: %s -> %s (%s)", this._fromNumber, session.toNumber, callId);
1536
1616
  this._startCallSession(session, mediaUrl).catch((err) => {
1537
- console.error(`[ClawOpsAgent] Error in call session ${callId}:`, err);
1617
+ this._log.error({ err }, "Call session error: %s", callId);
1538
1618
  });
1539
1619
  }
1540
1620
  }
@@ -1542,13 +1622,14 @@ var ClawOpsAgent = class {
1542
1622
  const callId = event["callId"];
1543
1623
  const session = this._activeSessions.get(callId);
1544
1624
  if (session) {
1545
- console.log(`[ClawOpsAgent] Outbound call ringing: ${callId}`);
1625
+ this._log.info("Outbound call ringing: %s", callId);
1546
1626
  }
1547
1627
  }
1548
1628
  _handleFailed(event) {
1549
1629
  const callId = event["callId"];
1550
1630
  const session = this._activeSessions.get(callId);
1551
1631
  if (session) {
1632
+ this._log.info("Outbound call failed: %s (%s)", callId, event["reason"] ?? "failed");
1552
1633
  session._emit("call_failed", event["reason"] ?? "failed");
1553
1634
  session._markEnded();
1554
1635
  this._activeSessions.delete(callId);
@@ -1573,7 +1654,7 @@ var ClawOpsAgent = class {
1573
1654
  this._passiveDtmfCallId = null;
1574
1655
  if (digits && sessionHandler && sessionHandler.feedDtmf) {
1575
1656
  sessionHandler.feedDtmf(digits).catch((err) => {
1576
- console.error("[ClawOpsAgent] feedDtmf error:", err);
1657
+ this._log.error({ err }, "DTMF feed error");
1577
1658
  });
1578
1659
  }
1579
1660
  }, this._passiveDtmfDebounceMs);
@@ -1592,22 +1673,25 @@ var ClawOpsAgent = class {
1592
1673
  if (this._mcpServers.length > 0) {
1593
1674
  for (const serverConfig of this._mcpServers) {
1594
1675
  const client = new MCPClient();
1676
+ client.setLogger(this._log);
1595
1677
  client.addServer("mcp", serverConfig);
1596
1678
  try {
1597
1679
  const tools = await client.connect();
1598
1680
  sessionTools.registerMcpTools(tools);
1599
1681
  mcpClients.push(client);
1600
1682
  } catch (err) {
1601
- console.error("[ClawOpsAgent] MCP connection error:", err);
1683
+ this._log.error({ err }, "MCP connection error");
1602
1684
  }
1603
1685
  }
1604
1686
  }
1605
1687
  let recorder = null;
1606
1688
  if (this._recording) {
1607
1689
  recorder = new AudioRecorder(this._recordingPath, session.callId);
1690
+ recorder.setLogger(this._log);
1608
1691
  recorder.start();
1609
1692
  }
1610
1693
  const mediaWs = new MediaWebSocket();
1694
+ mediaWs.setLogger(this._log);
1611
1695
  session._bindTransport(
1612
1696
  (audio) => {
1613
1697
  mediaWs.sendAudio(audio.toString("base64"));
@@ -1637,6 +1721,9 @@ var ClawOpsAgent = class {
1637
1721
  if ("setBuiltinTools" in sessionHandler && typeof sessionHandler.setBuiltinTools === "function") {
1638
1722
  sessionHandler.setBuiltinTools(this._builtinTools);
1639
1723
  }
1724
+ if ("setLogger" in sessionHandler && typeof sessionHandler.setLogger === "function") {
1725
+ sessionHandler.setLogger(this._isPipelineSession ? this._pipelineLog : this._log);
1726
+ }
1640
1727
  this._callSessions.set(session.callId, sessionHandler);
1641
1728
  mediaWs.onAudio((ulawAudio, _timestamp) => {
1642
1729
  if (sessionHandler) {
@@ -1650,6 +1737,7 @@ var ClawOpsAgent = class {
1650
1737
  this._onDtmfEvent(session, digit);
1651
1738
  });
1652
1739
  mediaWs.onClose(() => {
1740
+ this._log.info("Media stream stopped: %s", session.callId);
1653
1741
  if (recorder) {
1654
1742
  recorder.stop();
1655
1743
  }
@@ -1658,11 +1746,12 @@ var ClawOpsAgent = class {
1658
1746
  session._emit("call_start");
1659
1747
  try {
1660
1748
  await mediaWs.connect(mediaWsUrl, this._apiKey);
1749
+ this._log.info("Media stream started: %s", session.callId);
1661
1750
  await sessionHandler.start(session, sessionTools);
1662
1751
  await session.wait();
1663
1752
  await sessionHandler.stop();
1664
1753
  } catch (err) {
1665
- console.error(`[ClawOpsAgent] Call session error:`, err);
1754
+ this._log.error({ err }, "Call session error: %s", session.callId);
1666
1755
  } finally {
1667
1756
  if (mcpClients.length > 0) {
1668
1757
  sessionTools.clearMcpTools();
@@ -1690,7 +1779,11 @@ var HANG_UP_TOOL = {
1690
1779
  type: "function",
1691
1780
  name: "hang_up",
1692
1781
  description: "End the phone call. Use when the conversation is finished or the caller says goodbye.",
1693
- parameters: { type: "object", properties: {}, required: [] }
1782
+ parameters: {
1783
+ type: "object",
1784
+ properties: {},
1785
+ required: []
1786
+ }
1694
1787
  };
1695
1788
  var COLLECT_DTMF_TOOL = {
1696
1789
  type: "function",
@@ -1713,7 +1806,10 @@ var SEND_DTMF_TOOL = {
1713
1806
  parameters: {
1714
1807
  type: "object",
1715
1808
  properties: {
1716
- digits: { type: "string", description: "\uC804\uC1A1\uD560 \uBC88\uD638 (0-9, *, #). 'w'\uB294 500ms \uB300\uAE30, 'W'\uB294 1000ms \uB300\uAE30." }
1809
+ digits: {
1810
+ type: "string",
1811
+ description: "\uC804\uC1A1\uD560 \uBC88\uD638 (0-9, *, #). 'w'\uB294 500ms \uB300\uAE30, 'W'\uB294 1000ms \uB300\uAE30."
1812
+ }
1717
1813
  },
1718
1814
  required: ["digits"]
1719
1815
  }
@@ -1726,7 +1822,11 @@ var OpenAIRealtime = class {
1726
1822
  _language;
1727
1823
  _eagerness;
1728
1824
  _greeting;
1825
+ _log = NOOP_LOGGER;
1729
1826
  _builtinTools = null;
1827
+ setLogger(logger) {
1828
+ this._log = logger;
1829
+ }
1730
1830
  setBuiltinTools(tools) {
1731
1831
  this._builtinTools = tools;
1732
1832
  }
@@ -1783,6 +1883,7 @@ var OpenAIRealtime = class {
1783
1883
  const ws = this._ws;
1784
1884
  ws.on("open", () => {
1785
1885
  this._sendSessionUpdate();
1886
+ this._log.info("OpenAI Realtime connected");
1786
1887
  if (this._greeting) {
1787
1888
  this._send({ type: "response.create" });
1788
1889
  }
@@ -1802,7 +1903,7 @@ var OpenAIRealtime = class {
1802
1903
  if (!this._ws) {
1803
1904
  reject(err);
1804
1905
  }
1805
- console.error("[OpenAIRealtime] WebSocket error:", err.message);
1906
+ this._log.error({ err }, "OpenAI Realtime WS error");
1806
1907
  });
1807
1908
  });
1808
1909
  }
@@ -1836,9 +1937,12 @@ var OpenAIRealtime = class {
1836
1937
  _sendSessionUpdate() {
1837
1938
  if (!this._ws || this._ws.readyState !== 1) return;
1838
1939
  const toolSchemas = this._tools ? this._tools.toOpenAITools().map((t) => ({ type: "function", ...t.function })) : [];
1839
- if (!this._builtinTools || this._builtinTools.has("hang_up" /* HANG_UP */)) toolSchemas.push(HANG_UP_TOOL);
1840
- if (!this._builtinTools || this._builtinTools.has("collect_dtmf" /* COLLECT_DTMF */)) toolSchemas.push(COLLECT_DTMF_TOOL);
1841
- if (!this._builtinTools || this._builtinTools.has("send_dtmf" /* SEND_DTMF */)) toolSchemas.push(SEND_DTMF_TOOL);
1940
+ if (!this._builtinTools || this._builtinTools.has("hang_up" /* HANG_UP */))
1941
+ toolSchemas.push(HANG_UP_TOOL);
1942
+ if (!this._builtinTools || this._builtinTools.has("collect_dtmf" /* COLLECT_DTMF */))
1943
+ toolSchemas.push(COLLECT_DTMF_TOOL);
1944
+ if (!this._builtinTools || this._builtinTools.has("send_dtmf" /* SEND_DTMF */))
1945
+ toolSchemas.push(SEND_DTMF_TOOL);
1842
1946
  this._send({
1843
1947
  type: "session.update",
1844
1948
  session: {
@@ -1848,7 +1952,7 @@ var OpenAIRealtime = class {
1848
1952
  input_audio_format: "g711_ulaw",
1849
1953
  output_audio_format: "g711_ulaw",
1850
1954
  input_audio_transcription: {
1851
- model: "gpt-4o-mini-transcribe",
1955
+ model: "whisper-1",
1852
1956
  language: this._language
1853
1957
  },
1854
1958
  input_audio_noise_reduction: { type: "far_field" },
@@ -1919,7 +2023,7 @@ var OpenAIRealtime = class {
1919
2023
  break;
1920
2024
  }
1921
2025
  case "error": {
1922
- console.error("[OpenAIRealtime] API error:", msg["error"]);
2026
+ this._log.error({ apiError: msg["error"] }, "OpenAI error");
1923
2027
  break;
1924
2028
  }
1925
2029
  }
@@ -1969,6 +2073,7 @@ var OpenAIRealtime = class {
1969
2073
  async _handleToolCall(item) {
1970
2074
  const funcName = item["name"];
1971
2075
  const callId = item["call_id"];
2076
+ this._log.info("Tool call: %s", funcName);
1972
2077
  if (funcName === "hang_up") {
1973
2078
  if (this._call) {
1974
2079
  await this._call.hangup();
@@ -2025,7 +2130,7 @@ var OpenAIRealtime = class {
2025
2130
  return;
2026
2131
  }
2027
2132
  if (!this._tools || !this._tools.has(funcName)) {
2028
- console.error(`[OpenAIRealtime] Unknown tool: ${funcName}`);
2133
+ this._log.error("Unknown tool: %s", funcName);
2029
2134
  return;
2030
2135
  }
2031
2136
  let result;
@@ -2033,7 +2138,7 @@ var OpenAIRealtime = class {
2033
2138
  const args = JSON.parse(item["arguments"] ?? "{}");
2034
2139
  result = await this._tools.call(funcName, args);
2035
2140
  } catch (err) {
2036
- console.error(`[OpenAIRealtime] Tool call failed: ${funcName}:`, err);
2141
+ this._log.error({ err }, "Tool call failed: %s", funcName);
2037
2142
  result = `Error: ${err}`;
2038
2143
  }
2039
2144
  await this._waitForResponseDone();
@@ -2182,6 +2287,7 @@ var GeminiRealtime = class {
2182
2287
  _audioRemainder = Buffer.alloc(0);
2183
2288
  _builtinTools = null;
2184
2289
  _toolCallInProgress = false;
2290
+ _log = NOOP_LOGGER;
2185
2291
  constructor(options = {}) {
2186
2292
  this._apiKey = options.apiKey ?? process.env["GOOGLE_API_KEY"] ?? "";
2187
2293
  this._systemPrompt = options.systemPrompt ?? "";
@@ -2201,6 +2307,9 @@ var GeminiRealtime = class {
2201
2307
  setBuiltinTools(tools) {
2202
2308
  this._builtinTools = tools;
2203
2309
  }
2310
+ setLogger(logger) {
2311
+ this._log = logger;
2312
+ }
2204
2313
  async start(callSession, tools) {
2205
2314
  this._call = callSession;
2206
2315
  if (tools) this._tools = tools;
@@ -2237,12 +2346,10 @@ var GeminiRealtime = class {
2237
2346
  callbacks: {
2238
2347
  onmessage: (msg) => this._handleMessage(msg),
2239
2348
  onerror: (err) => {
2240
- console.error("[GeminiRealtime] SDK error:", err);
2349
+ this._log.error({ err }, "Gemini SDK error");
2241
2350
  },
2242
2351
  onclose: (ev) => {
2243
- console.log(
2244
- `[GeminiRealtime] Connection closed: code=${ev?.code ?? "unknown"}`
2245
- );
2352
+ this._log.info({ code: ev?.code ?? "unknown" }, "Gemini connection closed");
2246
2353
  this._closed = true;
2247
2354
  }
2248
2355
  }
@@ -2322,11 +2429,11 @@ var GeminiRealtime = class {
2322
2429
  }
2323
2430
  }
2324
2431
  if (serverContent.turnComplete) {
2325
- console.log("[GeminiRealtime] Turn complete");
2432
+ this._log.debug("Turn complete");
2326
2433
  this._flushAudioRemainder();
2327
2434
  }
2328
2435
  if (serverContent.interrupted) {
2329
- console.log("[GeminiRealtime] Barge-in detected");
2436
+ this._log.info("Barge-in detected");
2330
2437
  if (this._call) {
2331
2438
  this._call.clearAudio();
2332
2439
  }
@@ -2335,12 +2442,12 @@ var GeminiRealtime = class {
2335
2442
  }
2336
2443
  const inputText = serverContent.inputTranscription?.text;
2337
2444
  if (inputText && this._call) {
2338
- console.log(`[GeminiRealtime] [TRANSCRIPT-USER] ${inputText}`);
2445
+ this._log.info("User: %s", inputText);
2339
2446
  this._call._emit("transcript", "user", inputText);
2340
2447
  }
2341
2448
  const outputText = serverContent.outputTranscription?.text;
2342
2449
  if (outputText && this._call) {
2343
- console.log(`[GeminiRealtime] [TRANSCRIPT-ASSISTANT] ${outputText}`);
2450
+ this._log.info("Assistant: %s", outputText);
2344
2451
  this._call._emit("transcript", "assistant", outputText);
2345
2452
  }
2346
2453
  }
@@ -2349,9 +2456,7 @@ var GeminiRealtime = class {
2349
2456
  }
2350
2457
  const toolCancellation = msg["toolCallCancellation"];
2351
2458
  if (toolCancellation) {
2352
- console.log(
2353
- `[GeminiRealtime] Tool call cancelled: ${(toolCancellation.ids ?? []).join(", ")}`
2354
- );
2459
+ this._log.info({ ids: toolCancellation.ids }, "Tool call cancelled");
2355
2460
  }
2356
2461
  }
2357
2462
  _handleAudioData(b64Data) {
@@ -2386,17 +2491,14 @@ var GeminiRealtime = class {
2386
2491
  const functionCalls = toolCall.functionCalls;
2387
2492
  if (!functionCalls) return;
2388
2493
  this._toolCallInProgress = true;
2389
- console.log(
2390
- `[GeminiRealtime] toolCall: ${functionCalls.map((fc) => fc.name).join(", ")}`
2391
- );
2392
2494
  const responses = [];
2393
2495
  for (const fc of functionCalls) {
2394
2496
  const name = fc.name ?? "";
2395
2497
  const fcId = fc.id ?? "";
2396
2498
  const args = fc.args ?? {};
2397
- console.log(`[GeminiRealtime] Tool call: ${name}(${JSON.stringify(args)})`);
2499
+ this._log.info({ tool: name, args }, "Tool call: %s", name);
2398
2500
  if (name === "hang_up") {
2399
- console.log("[GeminiRealtime] hang_up: ending call");
2501
+ this._log.info("hang_up: ending call");
2400
2502
  if (this._call) {
2401
2503
  await this._call.hangup();
2402
2504
  }
@@ -2406,17 +2508,15 @@ var GeminiRealtime = class {
2406
2508
  if (this._call) {
2407
2509
  let result;
2408
2510
  try {
2409
- console.log(
2410
- `[GeminiRealtime] collect_dtmf: waiting for digits (maxDigits=${args["max_digits"] ?? 4}, timeout=${args["timeout"] ?? 5})`
2411
- );
2511
+ this._log.info({ maxDigits: args["max_digits"] ?? 4, timeout: args["timeout"] ?? 5 }, "collect_dtmf: waiting for digits");
2412
2512
  result = await this._call.collectDtmf({
2413
2513
  maxDigits: args["max_digits"] ?? 4,
2414
2514
  finishOnKey: args["finish_on_key"] ?? "#",
2415
2515
  timeout: args["timeout"] ?? 5
2416
2516
  });
2417
- console.log(`[GeminiRealtime] DTMF collected: ${result || "(empty)"}`);
2517
+ this._log.info("DTMF collected: %s", result || "(empty)");
2418
2518
  } catch (err) {
2419
- console.error(`[GeminiRealtime] collect_dtmf error:`, err);
2519
+ this._log.error({ err }, "collect_dtmf error");
2420
2520
  result = `Error: ${err}`;
2421
2521
  }
2422
2522
  responses.push({
@@ -2431,12 +2531,12 @@ var GeminiRealtime = class {
2431
2531
  if (this._call) {
2432
2532
  let result;
2433
2533
  try {
2434
- console.log(`[GeminiRealtime] send_dtmf: digits="${args["digits"] ?? ""}"`);
2534
+ this._log.info('send_dtmf: digits="%s"', args["digits"] ?? "");
2435
2535
  await this._call.sendDtmfSequence(args["digits"] ?? "");
2436
2536
  result = "sent";
2437
- console.log(`[GeminiRealtime] send_dtmf: sent`);
2537
+ this._log.info("send_dtmf: sent");
2438
2538
  } catch (err) {
2439
- console.error(`[GeminiRealtime] send_dtmf error:`, err);
2539
+ this._log.error({ err }, "send_dtmf error");
2440
2540
  result = `Error: ${err}`;
2441
2541
  }
2442
2542
  responses.push({ id: fcId, name, response: { result } });
@@ -2444,21 +2544,21 @@ var GeminiRealtime = class {
2444
2544
  continue;
2445
2545
  }
2446
2546
  if (!this._tools || !this._tools.has(name)) {
2447
- console.error(`[GeminiRealtime] Unknown tool: ${name}`);
2547
+ this._log.error("Unknown tool: %s", name);
2448
2548
  responses.push({ id: fcId, name, response: { error: `Unknown tool: ${name}` } });
2449
2549
  continue;
2450
2550
  }
2451
2551
  try {
2452
2552
  const result = await this._tools.call(name, args);
2453
2553
  const resultStr = typeof result === "string" ? result : JSON.stringify(result);
2454
- console.log(`[GeminiRealtime] Tool result: ${name} -> ${resultStr.substring(0, 200)}`);
2554
+ this._log.info("Tool result: %s -> %s", name, resultStr.substring(0, 200));
2455
2555
  responses.push({
2456
2556
  id: fcId,
2457
2557
  name,
2458
2558
  response: { result: resultStr }
2459
2559
  });
2460
2560
  } catch (err) {
2461
- console.error(`[GeminiRealtime] Tool call failed: ${name}:`, err);
2561
+ this._log.error({ err }, "Tool call failed: %s", name);
2462
2562
  responses.push({
2463
2563
  id: fcId,
2464
2564
  name,
@@ -2467,7 +2567,7 @@ var GeminiRealtime = class {
2467
2567
  }
2468
2568
  }
2469
2569
  if (responses.length > 0 && this._session) {
2470
- console.log(`[GeminiRealtime] Sending ${responses.length} tool response(s)`);
2570
+ this._log.debug("Sending %d tool response(s)", responses.length);
2471
2571
  this._session.sendToolResponse({
2472
2572
  functionResponses: responses
2473
2573
  });
@@ -2520,6 +2620,7 @@ var PipelineSession = class {
2520
2620
  _running = false;
2521
2621
  _speaking = false;
2522
2622
  _builtinTools = null;
2623
+ _log = NOOP_LOGGER;
2523
2624
  constructor(options) {
2524
2625
  this._stt = options.stt;
2525
2626
  this._llm = options.llm;
@@ -2543,10 +2644,20 @@ var PipelineSession = class {
2543
2644
  setBuiltinTools(tools) {
2544
2645
  this._builtinTools = tools;
2545
2646
  }
2647
+ setLogger(logger) {
2648
+ this._log = logger;
2649
+ if ("setLogger" in this._stt && typeof this._stt.setLogger === "function") {
2650
+ this._stt.setLogger(logger);
2651
+ }
2652
+ if ("setLogger" in this._tts && typeof this._tts.setLogger === "function") {
2653
+ this._tts.setLogger(logger);
2654
+ }
2655
+ }
2546
2656
  async start(callSession, tools) {
2547
2657
  this._callSession = callSession;
2548
2658
  this._tools = tools ?? null;
2549
2659
  this._running = true;
2660
+ this._log.info("PipelineSession started");
2550
2661
  this._conversation = [];
2551
2662
  if (this._systemPrompt) {
2552
2663
  this._conversation.push({
@@ -2556,11 +2667,11 @@ var PipelineSession = class {
2556
2667
  }
2557
2668
  if (this._greeting) {
2558
2669
  this._generateGreeting().catch((err) => {
2559
- console.error("[PipelineSession] Greeting error:", err);
2670
+ this._log.error({ err }, "Greeting error");
2560
2671
  });
2561
2672
  }
2562
2673
  this._runSttLoop().catch((err) => {
2563
- console.error("[PipelineSession] STT loop error:", err);
2674
+ this._log.error({ err }, "STT loop error");
2564
2675
  });
2565
2676
  }
2566
2677
  feedAudio(audio) {
@@ -2577,6 +2688,7 @@ var PipelineSession = class {
2577
2688
  }
2578
2689
  async stop() {
2579
2690
  this._running = false;
2691
+ this._log.info("PipelineSession stopped");
2580
2692
  this._audioBuffer = [];
2581
2693
  }
2582
2694
  async _runSttLoop() {
@@ -2590,8 +2702,10 @@ var PipelineSession = class {
2590
2702
  if (this._callSession) {
2591
2703
  this._callSession.clearAudio();
2592
2704
  }
2705
+ this._log.info('Barge-in: "%s"', event.transcript.substring(0, 30));
2593
2706
  }
2594
2707
  if (event.type === "final" && event.transcript.trim()) {
2708
+ this._log.info("STT: %s", event.transcript);
2595
2709
  await this._handleUserSpeech(event.transcript);
2596
2710
  }
2597
2711
  }
@@ -2660,6 +2774,7 @@ var PipelineSession = class {
2660
2774
  }
2661
2775
  }
2662
2776
  if (fullResponse.trim()) {
2777
+ this._log.info("Assistant: %s", fullResponse.substring(0, 100));
2663
2778
  this._conversation.push({ role: "assistant", content: fullResponse });
2664
2779
  await this._synthesizeAndSend(fullResponse);
2665
2780
  }
@@ -2727,7 +2842,7 @@ var PipelineSession = class {
2727
2842
  await this._synthesizeAndSend(followUpText);
2728
2843
  }
2729
2844
  } catch (err) {
2730
- console.error(`[PipelineSession] Tool call error for ${name}:`, err);
2845
+ this._log.error({ err }, "Tool call failed: %s", name);
2731
2846
  }
2732
2847
  }
2733
2848
  async _synthesizeAndSend(text) {
@@ -2753,7 +2868,7 @@ var PipelineSession = class {
2753
2868
  }
2754
2869
  }
2755
2870
  } catch (err) {
2756
- console.error("[PipelineSession] TTS error:", err);
2871
+ this._log.error({ err }, "TTS error");
2757
2872
  } finally {
2758
2873
  this._speaking = false;
2759
2874
  }
@@ -2763,6 +2878,10 @@ var PipelineSession = class {
2763
2878
  // src/agent/pipeline/deepgram-stt.ts
2764
2879
  var DeepgramSTT = class {
2765
2880
  _options;
2881
+ _log = NOOP_LOGGER;
2882
+ setLogger(logger) {
2883
+ this._log = logger;
2884
+ }
2766
2885
  constructor(options = {}) {
2767
2886
  this._options = {
2768
2887
  model: "nova-3",
@@ -2832,7 +2951,7 @@ var DeepgramSTT = class {
2832
2951
  }
2833
2952
  });
2834
2953
  ws.on("error", (err) => {
2835
- console.error("[DeepgramSTT] WebSocket error:", err.message);
2954
+ this._log.error({ err }, "Deepgram STT error");
2836
2955
  done = true;
2837
2956
  if (resolveWait) {
2838
2957
  resolveWait();
@@ -2843,6 +2962,7 @@ var DeepgramSTT = class {
2843
2962
  ws.on("open", resolve);
2844
2963
  ws.on("error", reject);
2845
2964
  });
2965
+ this._log.info("Deepgram STT connected");
2846
2966
  const feedPromise = (async () => {
2847
2967
  try {
2848
2968
  for await (const chunk of audioStream) {
@@ -2879,6 +2999,10 @@ var DeepgramSTT = class {
2879
2999
  // src/agent/pipeline/elevenlabs-tts.ts
2880
3000
  var ElevenLabsTTS = class {
2881
3001
  _options;
3002
+ _log = NOOP_LOGGER;
3003
+ setLogger(logger) {
3004
+ this._log = logger;
3005
+ }
2882
3006
  constructor(options = {}) {
2883
3007
  this._options = {
2884
3008
  voiceId: "EXAVITQu4vr4xnSDxMaL",
@@ -2978,7 +3102,7 @@ var ElevenLabsTTS = class {
2978
3102
  }
2979
3103
  });
2980
3104
  ws.on("error", (err) => {
2981
- console.error("[ElevenLabsTTS] WebSocket error:", err.message);
3105
+ this._log.error({ err }, "ElevenLabs TTS error");
2982
3106
  done = true;
2983
3107
  if (resolveWait) {
2984
3108
  resolveWait();
@@ -2989,16 +3113,19 @@ var ElevenLabsTTS = class {
2989
3113
  ws.on("open", resolve);
2990
3114
  ws.on("error", reject);
2991
3115
  });
3116
+ this._log.info("ElevenLabs TTS connected");
2992
3117
  const feedPromise = (async () => {
2993
3118
  try {
2994
3119
  for await (const chunk of textStream) {
2995
3120
  if (done) break;
2996
3121
  if (ws.readyState === 1) {
3122
+ this._log.debug("ElevenLabs sending text: %s", chunk.substring(0, 60));
2997
3123
  ws.send(JSON.stringify({ text: chunk }));
2998
3124
  }
2999
3125
  }
3000
3126
  } finally {
3001
3127
  if (ws.readyState === 1) {
3128
+ this._log.debug("ElevenLabs sending EOS");
3002
3129
  ws.send(JSON.stringify({ text: "" }));
3003
3130
  }
3004
3131
  }
@@ -3536,6 +3663,6 @@ function mcpServerHTTP(options) {
3536
3663
  };
3537
3664
  }
3538
3665
 
3539
- export { AnthropicLLM, AudioRecorder, BuiltinTool, CallSession, ClawOpsAgent, DECODE_TABLE, DeepSeekLLM, DeepgramSTT, ElevenLabsTTS, FireworksLLM, GeminiLLM, GeminiRealtime, GroqLLM, MCPClient, MistralLLM, OllamaLLM, OpenAICompatLLM, OpenAILLM, OpenAIRealtime, PerplexityLLM, PipelineSession, TogetherLLM, ToolRegistry, XaiLLM, functionTool, getTracingConfig, mcpServerHTTP, mcpServerStdio, pcm16ToUlaw, resamplePcm16, resetTracingConfig, setTracingConfig, ulawToPcm16, zodToToolParams };
3666
+ export { AnthropicLLM, AudioRecorder, BuiltinTool, CallSession, ClawOpsAgent, DECODE_TABLE, DeepSeekLLM, DeepgramSTT, ElevenLabsTTS, FireworksLLM, GeminiLLM, GeminiRealtime, GroqLLM, MCPClient, MistralLLM, OllamaLLM, OpenAICompatLLM, OpenAILLM, OpenAIRealtime, PerplexityLLM, PipelineSession, TogetherLLM, ToolRegistry, XaiLLM, createAgentLogger, createPipelineLogger, functionTool, getTracingConfig, mcpServerHTTP, mcpServerStdio, pcm16ToUlaw, resamplePcm16, resetTracingConfig, setTracingConfig, ulawToPcm16, zodToToolParams };
3540
3667
  //# sourceMappingURL=index.js.map
3541
3668
  //# sourceMappingURL=index.js.map