@teamlearners/clawops 0.5.3 → 0.5.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,9 +1,12 @@
1
1
  'use strict';
2
2
 
3
3
  var chunk6IQN5RQD_cjs = require('../chunk-6IQN5RQD.cjs');
4
+ var pino = require('pino');
4
5
  var fs = require('fs');
5
6
  var path = require('path');
6
7
 
8
+ function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
9
+
7
10
  function _interopNamespace(e) {
8
11
  if (e && e.__esModule) return e;
9
12
  var n = Object.create(null);
@@ -22,6 +25,7 @@ function _interopNamespace(e) {
22
25
  return Object.freeze(n);
23
26
  }
24
27
 
28
+ var pino__default = /*#__PURE__*/_interopDefault(pino);
25
29
  var fs__namespace = /*#__PURE__*/_interopNamespace(fs);
26
30
  var path__namespace = /*#__PURE__*/_interopNamespace(path);
27
31
 
@@ -345,6 +349,14 @@ function resamplePcm16(pcm, fromRate, toRate) {
345
349
  }
346
350
  return out;
347
351
  }
352
+ var DEFAULT_LOGGER = pino__default.default({ name: "clawops.agent" });
353
+ var NOOP_LOGGER = pino__default.default({ level: "silent" });
354
+ function createAgentLogger(userLogger) {
355
+ return userLogger ?? DEFAULT_LOGGER;
356
+ }
357
+ function createPipelineLogger(parent) {
358
+ return parent.child({ module: "pipeline" });
359
+ }
348
360
 
349
361
  // src/agent/control-ws.ts
350
362
  var INITIAL_RECONNECT_DELAY = 1e3;
@@ -373,6 +385,10 @@ var ControlWebSocket = class {
373
385
  _closed = false;
374
386
  _connectedResolve = null;
375
387
  _connectedPromise;
388
+ _log = NOOP_LOGGER;
389
+ setLogger(logger) {
390
+ this._log = logger;
391
+ }
376
392
  /** Register an event handler for a specific event type. */
377
393
  on(event, handler) {
378
394
  let list = this._handlers.get(event);
@@ -420,13 +436,14 @@ var ControlWebSocket = class {
420
436
  this._connectedResolve();
421
437
  this._connectedResolve = null;
422
438
  }
439
+ this._log.info("Control WS connected: %s", this._url);
423
440
  });
424
441
  ws.on("message", (data) => {
425
442
  try {
426
443
  const msg = JSON.parse(data.toString());
427
444
  this._dispatchEvent(msg);
428
445
  } catch {
429
- console.error("[ControlWebSocket] Failed to parse message");
446
+ this._log.warn("Control WS parse error");
430
447
  }
431
448
  });
432
449
  ws.on("close", () => {
@@ -435,7 +452,7 @@ var ControlWebSocket = class {
435
452
  }
436
453
  });
437
454
  ws.on("error", (err) => {
438
- console.error("[ControlWebSocket] Error:", err.message);
455
+ this._log.warn("Control WS error: %s", err.message);
439
456
  });
440
457
  }
441
458
  _dispatchEvent(event) {
@@ -446,11 +463,11 @@ var ControlWebSocket = class {
446
463
  const result = handler(event);
447
464
  if (result && typeof result.catch === "function") {
448
465
  result.catch((err) => {
449
- console.error(`[ControlWebSocket] Error in handler for ${event.event}:`, err);
466
+ this._log.error({ err }, "Control WS handler error: %s", event.event);
450
467
  });
451
468
  }
452
469
  } catch (err) {
453
- console.error(`[ControlWebSocket] Error in handler for ${event.event}:`, err);
470
+ this._log.error({ err }, "Control WS handler error: %s", event.event);
454
471
  }
455
472
  }
456
473
  }
@@ -458,10 +475,11 @@ var ControlWebSocket = class {
458
475
  _scheduleReconnect() {
459
476
  const delay = this._reconnectDelay;
460
477
  this._reconnectDelay = Math.min(this._reconnectDelay * 2, MAX_RECONNECT_DELAY);
478
+ this._log.info("Control WS reconnecting in %ds...", delay / 1e3);
461
479
  setTimeout(() => {
462
480
  if (!this._closed) {
463
481
  this._doConnect().catch((err) => {
464
- console.error("[ControlWebSocket] Reconnect failed:", err);
482
+ this._log.warn({ err }, "Control WS reconnect failed");
465
483
  this._scheduleReconnect();
466
484
  });
467
485
  }
@@ -473,6 +491,10 @@ var ControlWebSocket = class {
473
491
  var MCPClient = class {
474
492
  _servers = /* @__PURE__ */ new Map();
475
493
  _clients = /* @__PURE__ */ new Map();
494
+ _log = NOOP_LOGGER;
495
+ setLogger(logger) {
496
+ this._log = logger;
497
+ }
476
498
  /** Add an MCP server configuration. */
477
499
  addServer(name, config) {
478
500
  this._servers.set(name, config);
@@ -485,7 +507,7 @@ var MCPClient = class {
485
507
  const tools = await this._connectServer(name, config);
486
508
  allTools.push(...tools);
487
509
  } catch (err) {
488
- console.error(`[MCPClient] Failed to connect to server '${name}':`, err);
510
+ this._log.error({ err }, "MCP connection failed: %s", name);
489
511
  }
490
512
  }
491
513
  return allTools;
@@ -494,12 +516,13 @@ var MCPClient = class {
494
516
  async disconnect() {
495
517
  for (const [name, client] of this._clients) {
496
518
  try {
519
+ this._log.debug("MCP closing: %s", name);
497
520
  const c = client;
498
521
  if (c.close) {
499
522
  await c.close();
500
523
  }
501
524
  } catch (err) {
502
- console.error(`[MCPClient] Error disconnecting from '${name}':`, err);
525
+ this._log.error({ err }, "MCP disconnect error: %s", name);
503
526
  }
504
527
  }
505
528
  this._clients.clear();
@@ -508,6 +531,11 @@ var MCPClient = class {
508
531
  const sdk = await import('@modelcontextprotocol/sdk/client/index.js');
509
532
  const { Client } = sdk;
510
533
  const client = new Client({ name: `clawops-${name}`, version: "1.0.0" });
534
+ if (config.type === "stdio") {
535
+ this._log.debug("MCP connecting (stdio): %s", config["command"]);
536
+ } else if (config.type === "http") {
537
+ this._log.debug("MCP connecting (http): %s", config["url"]);
538
+ }
511
539
  let transport;
512
540
  if (config.type === "stdio") {
513
541
  const { StdioClientTransport } = await import('@modelcontextprotocol/sdk/client/stdio.js');
@@ -536,6 +564,7 @@ var MCPClient = class {
536
564
  parameters: inputSchema.properties ?? {},
537
565
  required: inputSchema.required ?? [],
538
566
  handler: async (args) => {
567
+ this._log.debug("MCP call_tool: %s", toolDef.name);
539
568
  const result = await client.callTool({
540
569
  name: toolDef.name,
541
570
  arguments: args
@@ -548,6 +577,8 @@ var MCPClient = class {
548
577
  }
549
578
  });
550
579
  }
580
+ this._log.info("MCP server connected: %d tools found", tools.length);
581
+ this._log.debug("MCP tools: %s", tools.map((t) => t.name));
551
582
  return tools;
552
583
  }
553
584
  };
@@ -602,6 +633,10 @@ var MediaWebSocket = class {
602
633
  _onClose = null;
603
634
  _onDtmf = null;
604
635
  _markWaiters = /* @__PURE__ */ new Map();
636
+ _log = NOOP_LOGGER;
637
+ setLogger(logger) {
638
+ this._log = logger;
639
+ }
605
640
  /** Set the handler for inbound audio data. */
606
641
  onAudio(handler) {
607
642
  this._onAudio = handler;
@@ -643,6 +678,7 @@ var MediaWebSocket = class {
643
678
  ws.on("open", () => {
644
679
  this._startSendLoop();
645
680
  resolve();
681
+ this._log.info("Media WS connected: %s", url);
646
682
  });
647
683
  ws.on("message", (data) => {
648
684
  try {
@@ -661,7 +697,7 @@ var MediaWebSocket = class {
661
697
  if (!this._ws) {
662
698
  reject(err);
663
699
  }
664
- console.error("[MediaWebSocket] Error:", err.message);
700
+ this._log.error({ err }, "Media WS error");
665
701
  });
666
702
  });
667
703
  }
@@ -822,6 +858,10 @@ var AudioRecorder = class {
822
858
  _mixWritten = 0;
823
859
  _startTime = 0;
824
860
  _started = false;
861
+ _log = NOOP_LOGGER;
862
+ setLogger(logger) {
863
+ this._log = logger;
864
+ }
825
865
  constructor(recordingPath, callId) {
826
866
  this._dir = path__namespace.join(recordingPath, callId);
827
867
  }
@@ -836,6 +876,7 @@ var AudioRecorder = class {
836
876
  fs__namespace.writeSync(this._fdMix, header);
837
877
  this._startTime = performance.now();
838
878
  this._started = true;
879
+ this._log.info("Recording started: %s", this._dir);
839
880
  }
840
881
  _expectedBytes() {
841
882
  const elapsed = (performance.now() - this._startTime) / 1e3;
@@ -888,7 +929,7 @@ var AudioRecorder = class {
888
929
  this._inWritten += pcm16_8k.length;
889
930
  this._writeToMix(pcm16_8k, posBefore);
890
931
  } catch (err) {
891
- console.error("Error writing inbound audio:", err);
932
+ this._log.error({ err }, "Recording write error (inbound)");
892
933
  }
893
934
  }
894
935
  writeOutbound(pcm16_8k) {
@@ -901,7 +942,7 @@ var AudioRecorder = class {
901
942
  this._outWritten += pcm16_8k.length;
902
943
  this._writeToMix(pcm16_8k, posBefore);
903
944
  } catch (err) {
904
- console.error("Error writing outbound audio:", err);
945
+ this._log.error({ err }, "Recording write error (outbound)");
905
946
  }
906
947
  }
907
948
  stop() {
@@ -922,8 +963,10 @@ var AudioRecorder = class {
922
963
  fs__namespace.writeSync(fd, makeWavHeader(maxWritten), 0, 44, 0);
923
964
  fs__namespace.closeSync(fd);
924
965
  }
966
+ const maxSec = maxWritten / 16e3;
967
+ this._log.info("Recording stopped: %s (%ds)", this._dir, maxSec);
925
968
  } catch (err) {
926
- console.error("Error stopping recorder:", err);
969
+ this._log.error({ err }, "Recording stop error");
927
970
  } finally {
928
971
  this._fdIn = null;
929
972
  this._fdOut = null;
@@ -953,6 +996,7 @@ var CallSession = class {
953
996
  _dtmfCollectorActive = false;
954
997
  _dtmfResolvers = [];
955
998
  _dtmfBuffer = [];
999
+ _log = NOOP_LOGGER;
956
1000
  _handlers = /* @__PURE__ */ new Map();
957
1001
  _endedPromise;
958
1002
  _resolveEnded;
@@ -969,6 +1013,9 @@ var CallSession = class {
969
1013
  this._resolveEnded = resolve;
970
1014
  });
971
1015
  }
1016
+ setLogger(logger) {
1017
+ this._log = logger;
1018
+ }
972
1019
  get status() {
973
1020
  return this._status;
974
1021
  }
@@ -1044,7 +1091,9 @@ var CallSession = class {
1044
1091
  this._dtmfResolvers = [];
1045
1092
  this._dtmfBuffer = [];
1046
1093
  }
1047
- return collected.join("");
1094
+ const result = collected.join("");
1095
+ this._log.info("DTMF collected: %s", result);
1096
+ return result;
1048
1097
  }
1049
1098
  /** Send a sequence of DTMF digits. */
1050
1099
  async sendDtmfSequence(digits) {
@@ -1096,11 +1145,11 @@ var CallSession = class {
1096
1145
  const result = handler(this, ...args);
1097
1146
  if (result && typeof result.catch === "function") {
1098
1147
  result.catch((err) => {
1099
- console.error(`[CallSession] Error in ${event} handler:`, err);
1148
+ this._log.error({ err }, "CallSession handler error: %s", event);
1100
1149
  });
1101
1150
  }
1102
1151
  } catch (err) {
1103
- console.error(`[CallSession] Error in ${event} handler:`, err);
1152
+ this._log.error({ err }, "CallSession handler error: %s", event);
1104
1153
  }
1105
1154
  }
1106
1155
  }
@@ -1348,6 +1397,9 @@ var ClawOpsAgent = class {
1348
1397
  _passiveDtmfTimer = null;
1349
1398
  _passiveDtmfCallId = null;
1350
1399
  _callSessions = /* @__PURE__ */ new Map();
1400
+ _log;
1401
+ _pipelineLog;
1402
+ _isPipelineSession = false;
1351
1403
  constructor(options) {
1352
1404
  this._apiKey = options.apiKey ?? process.env["CLAWOPS_API_KEY"] ?? "";
1353
1405
  this._accountId = options.accountId ?? process.env["CLAWOPS_ACCOUNT_ID"] ?? "";
@@ -1362,6 +1414,9 @@ var ClawOpsAgent = class {
1362
1414
  if (options.tracing) {
1363
1415
  setTracingConfig(options.tracing);
1364
1416
  }
1417
+ this._log = createAgentLogger(options.logger);
1418
+ this._pipelineLog = createPipelineLogger(this._log);
1419
+ this._isPipelineSession = "_stt" in this._session && "_llm" in this._session;
1365
1420
  }
1366
1421
  /**
1367
1422
  * Register a function tool.
@@ -1421,6 +1476,7 @@ var ClawOpsAgent = class {
1421
1476
  accountId: this._accountId,
1422
1477
  number: this._fromNumber
1423
1478
  });
1479
+ this._controlWs.setLogger(this._log);
1424
1480
  this._controlWs.on("call.incoming", (event) => this._handleIncoming(event));
1425
1481
  this._controlWs.on("call.ended", (event) => this._handleEnded(event));
1426
1482
  this._controlWs.on("call.outbound_ready", (event) => this._handleOutboundReady(event));
@@ -1434,7 +1490,7 @@ var ClawOpsAgent = class {
1434
1490
  `Failed to connect to ClawOps: ${err instanceof Error ? err.message : String(err)}`
1435
1491
  );
1436
1492
  }
1437
- console.log(`[ClawOpsAgent] Connected on ${this._fromNumber}`);
1493
+ this._log.info("ClawOpsAgent connected on %s", this._fromNumber);
1438
1494
  }
1439
1495
  /**
1440
1496
  * Connect and block until disconnected.
@@ -1461,7 +1517,7 @@ var ClawOpsAgent = class {
1461
1517
  }
1462
1518
  this._activeSessions.clear();
1463
1519
  this._callSessions.clear();
1464
- console.log("[ClawOpsAgent] Disconnected");
1520
+ this._log.info("ClawOpsAgent disconnected");
1465
1521
  }
1466
1522
  /**
1467
1523
  * Initiate an outbound call.
@@ -1496,10 +1552,9 @@ var ClawOpsAgent = class {
1496
1552
  callSession.on(evt, handler);
1497
1553
  }
1498
1554
  }
1555
+ callSession.setLogger(this._log);
1499
1556
  this._activeSessions.set(callSession.callId, callSession);
1500
- console.log(
1501
- `[ClawOpsAgent] Outbound call initiated: ${this._fromNumber} -> ${to} (${callSession.callId})`
1502
- );
1557
+ this._log.info("Outbound call initiated: %s -> %s (%s)", this._fromNumber, to, callSession.callId);
1503
1558
  return callSession;
1504
1559
  }
1505
1560
  _handleIncoming(event) {
@@ -1518,13 +1573,15 @@ var ClawOpsAgent = class {
1518
1573
  session.on(evt, handler);
1519
1574
  }
1520
1575
  }
1576
+ session.setLogger(this._log);
1521
1577
  this._activeSessions.set(callId, session);
1578
+ this._log.info("Incoming call: %s -> %s (%s)", fromNumber, this._fromNumber, callId);
1522
1579
  if (this._controlWs) {
1523
1580
  this._controlWs.send({ event: "call.accept", callId });
1524
1581
  }
1525
1582
  if (mediaUrl) {
1526
1583
  this._startCallSession(session, mediaUrl).catch((err) => {
1527
- console.error(`[ClawOpsAgent] Error in call session ${callId}:`, err);
1584
+ this._log.error({ err }, "Call session error: %s", callId);
1528
1585
  });
1529
1586
  }
1530
1587
  }
@@ -1532,6 +1589,7 @@ var ClawOpsAgent = class {
1532
1589
  const callId = event["callId"];
1533
1590
  const session = this._activeSessions.get(callId);
1534
1591
  if (session) {
1592
+ this._log.info("Call ended (server): %s", callId);
1535
1593
  session._markEnded();
1536
1594
  this._activeSessions.delete(callId);
1537
1595
  }
@@ -1548,6 +1606,7 @@ var ClawOpsAgent = class {
1548
1606
  accountId: this._accountId,
1549
1607
  direction: "outbound"
1550
1608
  });
1609
+ session.setLogger(this._log);
1551
1610
  for (const [evt, handlers] of this._handlers) {
1552
1611
  for (const handler of handlers) {
1553
1612
  session.on(evt, handler);
@@ -1556,8 +1615,9 @@ var ClawOpsAgent = class {
1556
1615
  this._activeSessions.set(callId, session);
1557
1616
  }
1558
1617
  if (mediaUrl) {
1618
+ this._log.info("Outbound call answered: %s -> %s (%s)", this._fromNumber, session.toNumber, callId);
1559
1619
  this._startCallSession(session, mediaUrl).catch((err) => {
1560
- console.error(`[ClawOpsAgent] Error in call session ${callId}:`, err);
1620
+ this._log.error({ err }, "Call session error: %s", callId);
1561
1621
  });
1562
1622
  }
1563
1623
  }
@@ -1565,13 +1625,14 @@ var ClawOpsAgent = class {
1565
1625
  const callId = event["callId"];
1566
1626
  const session = this._activeSessions.get(callId);
1567
1627
  if (session) {
1568
- console.log(`[ClawOpsAgent] Outbound call ringing: ${callId}`);
1628
+ this._log.info("Outbound call ringing: %s", callId);
1569
1629
  }
1570
1630
  }
1571
1631
  _handleFailed(event) {
1572
1632
  const callId = event["callId"];
1573
1633
  const session = this._activeSessions.get(callId);
1574
1634
  if (session) {
1635
+ this._log.info("Outbound call failed: %s (%s)", callId, event["reason"] ?? "failed");
1575
1636
  session._emit("call_failed", event["reason"] ?? "failed");
1576
1637
  session._markEnded();
1577
1638
  this._activeSessions.delete(callId);
@@ -1596,7 +1657,7 @@ var ClawOpsAgent = class {
1596
1657
  this._passiveDtmfCallId = null;
1597
1658
  if (digits && sessionHandler && sessionHandler.feedDtmf) {
1598
1659
  sessionHandler.feedDtmf(digits).catch((err) => {
1599
- console.error("[ClawOpsAgent] feedDtmf error:", err);
1660
+ this._log.error({ err }, "DTMF feed error");
1600
1661
  });
1601
1662
  }
1602
1663
  }, this._passiveDtmfDebounceMs);
@@ -1615,22 +1676,25 @@ var ClawOpsAgent = class {
1615
1676
  if (this._mcpServers.length > 0) {
1616
1677
  for (const serverConfig of this._mcpServers) {
1617
1678
  const client = new MCPClient();
1679
+ client.setLogger(this._log);
1618
1680
  client.addServer("mcp", serverConfig);
1619
1681
  try {
1620
1682
  const tools = await client.connect();
1621
1683
  sessionTools.registerMcpTools(tools);
1622
1684
  mcpClients.push(client);
1623
1685
  } catch (err) {
1624
- console.error("[ClawOpsAgent] MCP connection error:", err);
1686
+ this._log.error({ err }, "MCP connection error");
1625
1687
  }
1626
1688
  }
1627
1689
  }
1628
1690
  let recorder = null;
1629
1691
  if (this._recording) {
1630
1692
  recorder = new AudioRecorder(this._recordingPath, session.callId);
1693
+ recorder.setLogger(this._log);
1631
1694
  recorder.start();
1632
1695
  }
1633
1696
  const mediaWs = new MediaWebSocket();
1697
+ mediaWs.setLogger(this._log);
1634
1698
  session._bindTransport(
1635
1699
  (audio) => {
1636
1700
  mediaWs.sendAudio(audio.toString("base64"));
@@ -1660,6 +1724,9 @@ var ClawOpsAgent = class {
1660
1724
  if ("setBuiltinTools" in sessionHandler && typeof sessionHandler.setBuiltinTools === "function") {
1661
1725
  sessionHandler.setBuiltinTools(this._builtinTools);
1662
1726
  }
1727
+ if ("setLogger" in sessionHandler && typeof sessionHandler.setLogger === "function") {
1728
+ sessionHandler.setLogger(this._isPipelineSession ? this._pipelineLog : this._log);
1729
+ }
1663
1730
  this._callSessions.set(session.callId, sessionHandler);
1664
1731
  mediaWs.onAudio((ulawAudio, _timestamp) => {
1665
1732
  if (sessionHandler) {
@@ -1673,6 +1740,7 @@ var ClawOpsAgent = class {
1673
1740
  this._onDtmfEvent(session, digit);
1674
1741
  });
1675
1742
  mediaWs.onClose(() => {
1743
+ this._log.info("Media stream stopped: %s", session.callId);
1676
1744
  if (recorder) {
1677
1745
  recorder.stop();
1678
1746
  }
@@ -1681,11 +1749,12 @@ var ClawOpsAgent = class {
1681
1749
  session._emit("call_start");
1682
1750
  try {
1683
1751
  await mediaWs.connect(mediaWsUrl, this._apiKey);
1752
+ this._log.info("Media stream started: %s", session.callId);
1684
1753
  await sessionHandler.start(session, sessionTools);
1685
1754
  await session.wait();
1686
1755
  await sessionHandler.stop();
1687
1756
  } catch (err) {
1688
- console.error(`[ClawOpsAgent] Call session error:`, err);
1757
+ this._log.error({ err }, "Call session error: %s", session.callId);
1689
1758
  } finally {
1690
1759
  if (mcpClients.length > 0) {
1691
1760
  sessionTools.clearMcpTools();
@@ -1713,7 +1782,11 @@ var HANG_UP_TOOL = {
1713
1782
  type: "function",
1714
1783
  name: "hang_up",
1715
1784
  description: "End the phone call. Use when the conversation is finished or the caller says goodbye.",
1716
- parameters: { type: "object", properties: {}, required: [] }
1785
+ parameters: {
1786
+ type: "object",
1787
+ properties: {},
1788
+ required: []
1789
+ }
1717
1790
  };
1718
1791
  var COLLECT_DTMF_TOOL = {
1719
1792
  type: "function",
@@ -1736,7 +1809,10 @@ var SEND_DTMF_TOOL = {
1736
1809
  parameters: {
1737
1810
  type: "object",
1738
1811
  properties: {
1739
- digits: { type: "string", description: "\uC804\uC1A1\uD560 \uBC88\uD638 (0-9, *, #). 'w'\uB294 500ms \uB300\uAE30, 'W'\uB294 1000ms \uB300\uAE30." }
1812
+ digits: {
1813
+ type: "string",
1814
+ description: "\uC804\uC1A1\uD560 \uBC88\uD638 (0-9, *, #). 'w'\uB294 500ms \uB300\uAE30, 'W'\uB294 1000ms \uB300\uAE30."
1815
+ }
1740
1816
  },
1741
1817
  required: ["digits"]
1742
1818
  }
@@ -1749,7 +1825,11 @@ var OpenAIRealtime = class {
1749
1825
  _language;
1750
1826
  _eagerness;
1751
1827
  _greeting;
1828
+ _log = NOOP_LOGGER;
1752
1829
  _builtinTools = null;
1830
+ setLogger(logger) {
1831
+ this._log = logger;
1832
+ }
1753
1833
  setBuiltinTools(tools) {
1754
1834
  this._builtinTools = tools;
1755
1835
  }
@@ -1806,6 +1886,7 @@ var OpenAIRealtime = class {
1806
1886
  const ws = this._ws;
1807
1887
  ws.on("open", () => {
1808
1888
  this._sendSessionUpdate();
1889
+ this._log.info("OpenAI Realtime connected");
1809
1890
  if (this._greeting) {
1810
1891
  this._send({ type: "response.create" });
1811
1892
  }
@@ -1825,7 +1906,7 @@ var OpenAIRealtime = class {
1825
1906
  if (!this._ws) {
1826
1907
  reject(err);
1827
1908
  }
1828
- console.error("[OpenAIRealtime] WebSocket error:", err.message);
1909
+ this._log.error({ err }, "OpenAI Realtime WS error");
1829
1910
  });
1830
1911
  });
1831
1912
  }
@@ -1859,9 +1940,12 @@ var OpenAIRealtime = class {
1859
1940
  _sendSessionUpdate() {
1860
1941
  if (!this._ws || this._ws.readyState !== 1) return;
1861
1942
  const toolSchemas = this._tools ? this._tools.toOpenAITools().map((t) => ({ type: "function", ...t.function })) : [];
1862
- if (!this._builtinTools || this._builtinTools.has("hang_up" /* HANG_UP */)) toolSchemas.push(HANG_UP_TOOL);
1863
- if (!this._builtinTools || this._builtinTools.has("collect_dtmf" /* COLLECT_DTMF */)) toolSchemas.push(COLLECT_DTMF_TOOL);
1864
- if (!this._builtinTools || this._builtinTools.has("send_dtmf" /* SEND_DTMF */)) toolSchemas.push(SEND_DTMF_TOOL);
1943
+ if (!this._builtinTools || this._builtinTools.has("hang_up" /* HANG_UP */))
1944
+ toolSchemas.push(HANG_UP_TOOL);
1945
+ if (!this._builtinTools || this._builtinTools.has("collect_dtmf" /* COLLECT_DTMF */))
1946
+ toolSchemas.push(COLLECT_DTMF_TOOL);
1947
+ if (!this._builtinTools || this._builtinTools.has("send_dtmf" /* SEND_DTMF */))
1948
+ toolSchemas.push(SEND_DTMF_TOOL);
1865
1949
  this._send({
1866
1950
  type: "session.update",
1867
1951
  session: {
@@ -1871,7 +1955,7 @@ var OpenAIRealtime = class {
1871
1955
  input_audio_format: "g711_ulaw",
1872
1956
  output_audio_format: "g711_ulaw",
1873
1957
  input_audio_transcription: {
1874
- model: "gpt-4o-mini-transcribe",
1958
+ model: "whisper-1",
1875
1959
  language: this._language
1876
1960
  },
1877
1961
  input_audio_noise_reduction: { type: "far_field" },
@@ -1942,7 +2026,7 @@ var OpenAIRealtime = class {
1942
2026
  break;
1943
2027
  }
1944
2028
  case "error": {
1945
- console.error("[OpenAIRealtime] API error:", msg["error"]);
2029
+ this._log.error({ apiError: msg["error"] }, "OpenAI error");
1946
2030
  break;
1947
2031
  }
1948
2032
  }
@@ -1992,6 +2076,7 @@ var OpenAIRealtime = class {
1992
2076
  async _handleToolCall(item) {
1993
2077
  const funcName = item["name"];
1994
2078
  const callId = item["call_id"];
2079
+ this._log.info("Tool call: %s", funcName);
1995
2080
  if (funcName === "hang_up") {
1996
2081
  if (this._call) {
1997
2082
  await this._call.hangup();
@@ -2048,7 +2133,7 @@ var OpenAIRealtime = class {
2048
2133
  return;
2049
2134
  }
2050
2135
  if (!this._tools || !this._tools.has(funcName)) {
2051
- console.error(`[OpenAIRealtime] Unknown tool: ${funcName}`);
2136
+ this._log.error("Unknown tool: %s", funcName);
2052
2137
  return;
2053
2138
  }
2054
2139
  let result;
@@ -2056,7 +2141,7 @@ var OpenAIRealtime = class {
2056
2141
  const args = JSON.parse(item["arguments"] ?? "{}");
2057
2142
  result = await this._tools.call(funcName, args);
2058
2143
  } catch (err) {
2059
- console.error(`[OpenAIRealtime] Tool call failed: ${funcName}:`, err);
2144
+ this._log.error({ err }, "Tool call failed: %s", funcName);
2060
2145
  result = `Error: ${err}`;
2061
2146
  }
2062
2147
  await this._waitForResponseDone();
@@ -2205,6 +2290,7 @@ var GeminiRealtime = class {
2205
2290
  _audioRemainder = Buffer.alloc(0);
2206
2291
  _builtinTools = null;
2207
2292
  _toolCallInProgress = false;
2293
+ _log = NOOP_LOGGER;
2208
2294
  constructor(options = {}) {
2209
2295
  this._apiKey = options.apiKey ?? process.env["GOOGLE_API_KEY"] ?? "";
2210
2296
  this._systemPrompt = options.systemPrompt ?? "";
@@ -2224,6 +2310,9 @@ var GeminiRealtime = class {
2224
2310
  setBuiltinTools(tools) {
2225
2311
  this._builtinTools = tools;
2226
2312
  }
2313
+ setLogger(logger) {
2314
+ this._log = logger;
2315
+ }
2227
2316
  async start(callSession, tools) {
2228
2317
  this._call = callSession;
2229
2318
  if (tools) this._tools = tools;
@@ -2260,12 +2349,10 @@ var GeminiRealtime = class {
2260
2349
  callbacks: {
2261
2350
  onmessage: (msg) => this._handleMessage(msg),
2262
2351
  onerror: (err) => {
2263
- console.error("[GeminiRealtime] SDK error:", err);
2352
+ this._log.error({ err }, "Gemini SDK error");
2264
2353
  },
2265
2354
  onclose: (ev) => {
2266
- console.log(
2267
- `[GeminiRealtime] Connection closed: code=${ev?.code ?? "unknown"}`
2268
- );
2355
+ this._log.info({ code: ev?.code ?? "unknown" }, "Gemini connection closed");
2269
2356
  this._closed = true;
2270
2357
  }
2271
2358
  }
@@ -2345,11 +2432,11 @@ var GeminiRealtime = class {
2345
2432
  }
2346
2433
  }
2347
2434
  if (serverContent.turnComplete) {
2348
- console.log("[GeminiRealtime] Turn complete");
2435
+ this._log.debug("Turn complete");
2349
2436
  this._flushAudioRemainder();
2350
2437
  }
2351
2438
  if (serverContent.interrupted) {
2352
- console.log("[GeminiRealtime] Barge-in detected");
2439
+ this._log.info("Barge-in detected");
2353
2440
  if (this._call) {
2354
2441
  this._call.clearAudio();
2355
2442
  }
@@ -2358,12 +2445,12 @@ var GeminiRealtime = class {
2358
2445
  }
2359
2446
  const inputText = serverContent.inputTranscription?.text;
2360
2447
  if (inputText && this._call) {
2361
- console.log(`[GeminiRealtime] [TRANSCRIPT-USER] ${inputText}`);
2448
+ this._log.info("User: %s", inputText);
2362
2449
  this._call._emit("transcript", "user", inputText);
2363
2450
  }
2364
2451
  const outputText = serverContent.outputTranscription?.text;
2365
2452
  if (outputText && this._call) {
2366
- console.log(`[GeminiRealtime] [TRANSCRIPT-ASSISTANT] ${outputText}`);
2453
+ this._log.info("Assistant: %s", outputText);
2367
2454
  this._call._emit("transcript", "assistant", outputText);
2368
2455
  }
2369
2456
  }
@@ -2372,9 +2459,7 @@ var GeminiRealtime = class {
2372
2459
  }
2373
2460
  const toolCancellation = msg["toolCallCancellation"];
2374
2461
  if (toolCancellation) {
2375
- console.log(
2376
- `[GeminiRealtime] Tool call cancelled: ${(toolCancellation.ids ?? []).join(", ")}`
2377
- );
2462
+ this._log.info({ ids: toolCancellation.ids }, "Tool call cancelled");
2378
2463
  }
2379
2464
  }
2380
2465
  _handleAudioData(b64Data) {
@@ -2409,17 +2494,14 @@ var GeminiRealtime = class {
2409
2494
  const functionCalls = toolCall.functionCalls;
2410
2495
  if (!functionCalls) return;
2411
2496
  this._toolCallInProgress = true;
2412
- console.log(
2413
- `[GeminiRealtime] toolCall: ${functionCalls.map((fc) => fc.name).join(", ")}`
2414
- );
2415
2497
  const responses = [];
2416
2498
  for (const fc of functionCalls) {
2417
2499
  const name = fc.name ?? "";
2418
2500
  const fcId = fc.id ?? "";
2419
2501
  const args = fc.args ?? {};
2420
- console.log(`[GeminiRealtime] Tool call: ${name}(${JSON.stringify(args)})`);
2502
+ this._log.info({ tool: name, args }, "Tool call: %s", name);
2421
2503
  if (name === "hang_up") {
2422
- console.log("[GeminiRealtime] hang_up: ending call");
2504
+ this._log.info("hang_up: ending call");
2423
2505
  if (this._call) {
2424
2506
  await this._call.hangup();
2425
2507
  }
@@ -2429,17 +2511,15 @@ var GeminiRealtime = class {
2429
2511
  if (this._call) {
2430
2512
  let result;
2431
2513
  try {
2432
- console.log(
2433
- `[GeminiRealtime] collect_dtmf: waiting for digits (maxDigits=${args["max_digits"] ?? 4}, timeout=${args["timeout"] ?? 5})`
2434
- );
2514
+ this._log.info({ maxDigits: args["max_digits"] ?? 4, timeout: args["timeout"] ?? 5 }, "collect_dtmf: waiting for digits");
2435
2515
  result = await this._call.collectDtmf({
2436
2516
  maxDigits: args["max_digits"] ?? 4,
2437
2517
  finishOnKey: args["finish_on_key"] ?? "#",
2438
2518
  timeout: args["timeout"] ?? 5
2439
2519
  });
2440
- console.log(`[GeminiRealtime] DTMF collected: ${result || "(empty)"}`);
2520
+ this._log.info("DTMF collected: %s", result || "(empty)");
2441
2521
  } catch (err) {
2442
- console.error(`[GeminiRealtime] collect_dtmf error:`, err);
2522
+ this._log.error({ err }, "collect_dtmf error");
2443
2523
  result = `Error: ${err}`;
2444
2524
  }
2445
2525
  responses.push({
@@ -2454,12 +2534,12 @@ var GeminiRealtime = class {
2454
2534
  if (this._call) {
2455
2535
  let result;
2456
2536
  try {
2457
- console.log(`[GeminiRealtime] send_dtmf: digits="${args["digits"] ?? ""}"`);
2537
+ this._log.info('send_dtmf: digits="%s"', args["digits"] ?? "");
2458
2538
  await this._call.sendDtmfSequence(args["digits"] ?? "");
2459
2539
  result = "sent";
2460
- console.log(`[GeminiRealtime] send_dtmf: sent`);
2540
+ this._log.info("send_dtmf: sent");
2461
2541
  } catch (err) {
2462
- console.error(`[GeminiRealtime] send_dtmf error:`, err);
2542
+ this._log.error({ err }, "send_dtmf error");
2463
2543
  result = `Error: ${err}`;
2464
2544
  }
2465
2545
  responses.push({ id: fcId, name, response: { result } });
@@ -2467,21 +2547,21 @@ var GeminiRealtime = class {
2467
2547
  continue;
2468
2548
  }
2469
2549
  if (!this._tools || !this._tools.has(name)) {
2470
- console.error(`[GeminiRealtime] Unknown tool: ${name}`);
2550
+ this._log.error("Unknown tool: %s", name);
2471
2551
  responses.push({ id: fcId, name, response: { error: `Unknown tool: ${name}` } });
2472
2552
  continue;
2473
2553
  }
2474
2554
  try {
2475
2555
  const result = await this._tools.call(name, args);
2476
2556
  const resultStr = typeof result === "string" ? result : JSON.stringify(result);
2477
- console.log(`[GeminiRealtime] Tool result: ${name} -> ${resultStr.substring(0, 200)}`);
2557
+ this._log.info("Tool result: %s -> %s", name, resultStr.substring(0, 200));
2478
2558
  responses.push({
2479
2559
  id: fcId,
2480
2560
  name,
2481
2561
  response: { result: resultStr }
2482
2562
  });
2483
2563
  } catch (err) {
2484
- console.error(`[GeminiRealtime] Tool call failed: ${name}:`, err);
2564
+ this._log.error({ err }, "Tool call failed: %s", name);
2485
2565
  responses.push({
2486
2566
  id: fcId,
2487
2567
  name,
@@ -2490,7 +2570,7 @@ var GeminiRealtime = class {
2490
2570
  }
2491
2571
  }
2492
2572
  if (responses.length > 0 && this._session) {
2493
- console.log(`[GeminiRealtime] Sending ${responses.length} tool response(s)`);
2573
+ this._log.debug("Sending %d tool response(s)", responses.length);
2494
2574
  this._session.sendToolResponse({
2495
2575
  functionResponses: responses
2496
2576
  });
@@ -2543,6 +2623,7 @@ var PipelineSession = class {
2543
2623
  _running = false;
2544
2624
  _speaking = false;
2545
2625
  _builtinTools = null;
2626
+ _log = NOOP_LOGGER;
2546
2627
  constructor(options) {
2547
2628
  this._stt = options.stt;
2548
2629
  this._llm = options.llm;
@@ -2566,10 +2647,20 @@ var PipelineSession = class {
2566
2647
  setBuiltinTools(tools) {
2567
2648
  this._builtinTools = tools;
2568
2649
  }
2650
+ setLogger(logger) {
2651
+ this._log = logger;
2652
+ if ("setLogger" in this._stt && typeof this._stt.setLogger === "function") {
2653
+ this._stt.setLogger(logger);
2654
+ }
2655
+ if ("setLogger" in this._tts && typeof this._tts.setLogger === "function") {
2656
+ this._tts.setLogger(logger);
2657
+ }
2658
+ }
2569
2659
  async start(callSession, tools) {
2570
2660
  this._callSession = callSession;
2571
2661
  this._tools = tools ?? null;
2572
2662
  this._running = true;
2663
+ this._log.info("PipelineSession started");
2573
2664
  this._conversation = [];
2574
2665
  if (this._systemPrompt) {
2575
2666
  this._conversation.push({
@@ -2579,11 +2670,11 @@ var PipelineSession = class {
2579
2670
  }
2580
2671
  if (this._greeting) {
2581
2672
  this._generateGreeting().catch((err) => {
2582
- console.error("[PipelineSession] Greeting error:", err);
2673
+ this._log.error({ err }, "Greeting error");
2583
2674
  });
2584
2675
  }
2585
2676
  this._runSttLoop().catch((err) => {
2586
- console.error("[PipelineSession] STT loop error:", err);
2677
+ this._log.error({ err }, "STT loop error");
2587
2678
  });
2588
2679
  }
2589
2680
  feedAudio(audio) {
@@ -2600,6 +2691,7 @@ var PipelineSession = class {
2600
2691
  }
2601
2692
  async stop() {
2602
2693
  this._running = false;
2694
+ this._log.info("PipelineSession stopped");
2603
2695
  this._audioBuffer = [];
2604
2696
  }
2605
2697
  async _runSttLoop() {
@@ -2613,8 +2705,10 @@ var PipelineSession = class {
2613
2705
  if (this._callSession) {
2614
2706
  this._callSession.clearAudio();
2615
2707
  }
2708
+ this._log.info('Barge-in: "%s"', event.transcript.substring(0, 30));
2616
2709
  }
2617
2710
  if (event.type === "final" && event.transcript.trim()) {
2711
+ this._log.info("STT: %s", event.transcript);
2618
2712
  await this._handleUserSpeech(event.transcript);
2619
2713
  }
2620
2714
  }
@@ -2683,6 +2777,7 @@ var PipelineSession = class {
2683
2777
  }
2684
2778
  }
2685
2779
  if (fullResponse.trim()) {
2780
+ this._log.info("Assistant: %s", fullResponse.substring(0, 100));
2686
2781
  this._conversation.push({ role: "assistant", content: fullResponse });
2687
2782
  await this._synthesizeAndSend(fullResponse);
2688
2783
  }
@@ -2750,7 +2845,7 @@ var PipelineSession = class {
2750
2845
  await this._synthesizeAndSend(followUpText);
2751
2846
  }
2752
2847
  } catch (err) {
2753
- console.error(`[PipelineSession] Tool call error for ${name}:`, err);
2848
+ this._log.error({ err }, "Tool call failed: %s", name);
2754
2849
  }
2755
2850
  }
2756
2851
  async _synthesizeAndSend(text) {
@@ -2776,7 +2871,7 @@ var PipelineSession = class {
2776
2871
  }
2777
2872
  }
2778
2873
  } catch (err) {
2779
- console.error("[PipelineSession] TTS error:", err);
2874
+ this._log.error({ err }, "TTS error");
2780
2875
  } finally {
2781
2876
  this._speaking = false;
2782
2877
  }
@@ -2786,6 +2881,10 @@ var PipelineSession = class {
2786
2881
  // src/agent/pipeline/deepgram-stt.ts
2787
2882
  var DeepgramSTT = class {
2788
2883
  _options;
2884
+ _log = NOOP_LOGGER;
2885
+ setLogger(logger) {
2886
+ this._log = logger;
2887
+ }
2789
2888
  constructor(options = {}) {
2790
2889
  this._options = {
2791
2890
  model: "nova-3",
@@ -2855,7 +2954,7 @@ var DeepgramSTT = class {
2855
2954
  }
2856
2955
  });
2857
2956
  ws.on("error", (err) => {
2858
- console.error("[DeepgramSTT] WebSocket error:", err.message);
2957
+ this._log.error({ err }, "Deepgram STT error");
2859
2958
  done = true;
2860
2959
  if (resolveWait) {
2861
2960
  resolveWait();
@@ -2866,6 +2965,7 @@ var DeepgramSTT = class {
2866
2965
  ws.on("open", resolve);
2867
2966
  ws.on("error", reject);
2868
2967
  });
2968
+ this._log.info("Deepgram STT connected");
2869
2969
  const feedPromise = (async () => {
2870
2970
  try {
2871
2971
  for await (const chunk of audioStream) {
@@ -2902,6 +3002,10 @@ var DeepgramSTT = class {
2902
3002
  // src/agent/pipeline/elevenlabs-tts.ts
2903
3003
  var ElevenLabsTTS = class {
2904
3004
  _options;
3005
+ _log = NOOP_LOGGER;
3006
+ setLogger(logger) {
3007
+ this._log = logger;
3008
+ }
2905
3009
  constructor(options = {}) {
2906
3010
  this._options = {
2907
3011
  voiceId: "EXAVITQu4vr4xnSDxMaL",
@@ -3001,7 +3105,7 @@ var ElevenLabsTTS = class {
3001
3105
  }
3002
3106
  });
3003
3107
  ws.on("error", (err) => {
3004
- console.error("[ElevenLabsTTS] WebSocket error:", err.message);
3108
+ this._log.error({ err }, "ElevenLabs TTS error");
3005
3109
  done = true;
3006
3110
  if (resolveWait) {
3007
3111
  resolveWait();
@@ -3012,16 +3116,19 @@ var ElevenLabsTTS = class {
3012
3116
  ws.on("open", resolve);
3013
3117
  ws.on("error", reject);
3014
3118
  });
3119
+ this._log.info("ElevenLabs TTS connected");
3015
3120
  const feedPromise = (async () => {
3016
3121
  try {
3017
3122
  for await (const chunk of textStream) {
3018
3123
  if (done) break;
3019
3124
  if (ws.readyState === 1) {
3125
+ this._log.debug("ElevenLabs sending text: %s", chunk.substring(0, 60));
3020
3126
  ws.send(JSON.stringify({ text: chunk }));
3021
3127
  }
3022
3128
  }
3023
3129
  } finally {
3024
3130
  if (ws.readyState === 1) {
3131
+ this._log.debug("ElevenLabs sending EOS");
3025
3132
  ws.send(JSON.stringify({ text: "" }));
3026
3133
  }
3027
3134
  }
@@ -3583,6 +3690,8 @@ exports.PipelineSession = PipelineSession;
3583
3690
  exports.TogetherLLM = TogetherLLM;
3584
3691
  exports.ToolRegistry = ToolRegistry;
3585
3692
  exports.XaiLLM = XaiLLM;
3693
+ exports.createAgentLogger = createAgentLogger;
3694
+ exports.createPipelineLogger = createPipelineLogger;
3586
3695
  exports.functionTool = functionTool;
3587
3696
  exports.getTracingConfig = getTracingConfig;
3588
3697
  exports.mcpServerHTTP = mcpServerHTTP;