@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,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,10 +349,19 @@ 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;
351
363
  var MAX_RECONNECT_DELAY = 3e4;
364
+ var PING_TIMEOUT = 6e4;
352
365
  function buildControlWsUrl(options) {
353
366
  const scheme = options.baseUrl.startsWith("https") ? "wss" : "ws";
354
367
  const host = options.baseUrl.replace(/^https?:\/\//, "").replace(/\/$/, "");
@@ -373,6 +386,11 @@ var ControlWebSocket = class {
373
386
  _closed = false;
374
387
  _connectedResolve = null;
375
388
  _connectedPromise;
389
+ _log = NOOP_LOGGER;
390
+ _pingTimer = null;
391
+ setLogger(logger) {
392
+ this._log = logger;
393
+ }
376
394
  /** Register an event handler for a specific event type. */
377
395
  on(event, handler) {
378
396
  let list = this._handlers.get(event);
@@ -400,6 +418,7 @@ var ControlWebSocket = class {
400
418
  /** Close the WebSocket and stop reconnecting. */
401
419
  close() {
402
420
  this._closed = true;
421
+ this._clearPingTimer();
403
422
  if (this._ws) {
404
423
  this._ws.close();
405
424
  this._ws = null;
@@ -416,26 +435,32 @@ var ControlWebSocket = class {
416
435
  this._ws = ws;
417
436
  ws.on("open", () => {
418
437
  this._reconnectDelay = INITIAL_RECONNECT_DELAY;
438
+ this._resetPingTimer();
419
439
  if (this._connectedResolve) {
420
440
  this._connectedResolve();
421
441
  this._connectedResolve = null;
422
442
  }
443
+ this._log.info("Control WS connected: %s", this._url);
444
+ });
445
+ ws.on("ping", () => {
446
+ this._resetPingTimer();
423
447
  });
424
448
  ws.on("message", (data) => {
425
449
  try {
426
450
  const msg = JSON.parse(data.toString());
427
451
  this._dispatchEvent(msg);
428
452
  } catch {
429
- console.error("[ControlWebSocket] Failed to parse message");
453
+ this._log.warn("Control WS parse error");
430
454
  }
431
455
  });
432
456
  ws.on("close", () => {
457
+ this._clearPingTimer();
433
458
  if (!this._closed) {
434
459
  this._scheduleReconnect();
435
460
  }
436
461
  });
437
462
  ws.on("error", (err) => {
438
- console.error("[ControlWebSocket] Error:", err.message);
463
+ this._log.warn("Control WS error: %s", err.message);
439
464
  });
440
465
  }
441
466
  _dispatchEvent(event) {
@@ -446,22 +471,38 @@ var ControlWebSocket = class {
446
471
  const result = handler(event);
447
472
  if (result && typeof result.catch === "function") {
448
473
  result.catch((err) => {
449
- console.error(`[ControlWebSocket] Error in handler for ${event.event}:`, err);
474
+ this._log.error({ err }, "Control WS handler error: %s", event.event);
450
475
  });
451
476
  }
452
477
  } catch (err) {
453
- console.error(`[ControlWebSocket] Error in handler for ${event.event}:`, err);
478
+ this._log.error({ err }, "Control WS handler error: %s", event.event);
454
479
  }
455
480
  }
456
481
  }
457
482
  }
483
+ _resetPingTimer() {
484
+ this._clearPingTimer();
485
+ this._pingTimer = setTimeout(() => {
486
+ this._log.warn("Control WS ping timeout, closing connection");
487
+ if (this._ws) {
488
+ this._ws.terminate();
489
+ }
490
+ }, PING_TIMEOUT);
491
+ }
492
+ _clearPingTimer() {
493
+ if (this._pingTimer) {
494
+ clearTimeout(this._pingTimer);
495
+ this._pingTimer = null;
496
+ }
497
+ }
458
498
  _scheduleReconnect() {
459
499
  const delay = this._reconnectDelay;
460
500
  this._reconnectDelay = Math.min(this._reconnectDelay * 2, MAX_RECONNECT_DELAY);
501
+ this._log.info("Control WS reconnecting in %ds...", delay / 1e3);
461
502
  setTimeout(() => {
462
503
  if (!this._closed) {
463
504
  this._doConnect().catch((err) => {
464
- console.error("[ControlWebSocket] Reconnect failed:", err);
505
+ this._log.warn({ err }, "Control WS reconnect failed");
465
506
  this._scheduleReconnect();
466
507
  });
467
508
  }
@@ -473,6 +514,10 @@ var ControlWebSocket = class {
473
514
  var MCPClient = class {
474
515
  _servers = /* @__PURE__ */ new Map();
475
516
  _clients = /* @__PURE__ */ new Map();
517
+ _log = NOOP_LOGGER;
518
+ setLogger(logger) {
519
+ this._log = logger;
520
+ }
476
521
  /** Add an MCP server configuration. */
477
522
  addServer(name, config) {
478
523
  this._servers.set(name, config);
@@ -485,7 +530,7 @@ var MCPClient = class {
485
530
  const tools = await this._connectServer(name, config);
486
531
  allTools.push(...tools);
487
532
  } catch (err) {
488
- console.error(`[MCPClient] Failed to connect to server '${name}':`, err);
533
+ this._log.error({ err }, "MCP connection failed: %s", name);
489
534
  }
490
535
  }
491
536
  return allTools;
@@ -494,12 +539,13 @@ var MCPClient = class {
494
539
  async disconnect() {
495
540
  for (const [name, client] of this._clients) {
496
541
  try {
542
+ this._log.debug("MCP closing: %s", name);
497
543
  const c = client;
498
544
  if (c.close) {
499
545
  await c.close();
500
546
  }
501
547
  } catch (err) {
502
- console.error(`[MCPClient] Error disconnecting from '${name}':`, err);
548
+ this._log.error({ err }, "MCP disconnect error: %s", name);
503
549
  }
504
550
  }
505
551
  this._clients.clear();
@@ -508,6 +554,11 @@ var MCPClient = class {
508
554
  const sdk = await import('@modelcontextprotocol/sdk/client/index.js');
509
555
  const { Client } = sdk;
510
556
  const client = new Client({ name: `clawops-${name}`, version: "1.0.0" });
557
+ if (config.type === "stdio") {
558
+ this._log.debug("MCP connecting (stdio): %s", config["command"]);
559
+ } else if (config.type === "http") {
560
+ this._log.debug("MCP connecting (http): %s", config["url"]);
561
+ }
511
562
  let transport;
512
563
  if (config.type === "stdio") {
513
564
  const { StdioClientTransport } = await import('@modelcontextprotocol/sdk/client/stdio.js');
@@ -536,6 +587,7 @@ var MCPClient = class {
536
587
  parameters: inputSchema.properties ?? {},
537
588
  required: inputSchema.required ?? [],
538
589
  handler: async (args) => {
590
+ this._log.debug("MCP call_tool: %s", toolDef.name);
539
591
  const result = await client.callTool({
540
592
  name: toolDef.name,
541
593
  arguments: args
@@ -548,6 +600,8 @@ var MCPClient = class {
548
600
  }
549
601
  });
550
602
  }
603
+ this._log.info("MCP server connected: %d tools found", tools.length);
604
+ this._log.debug("MCP tools: %s", tools.map((t) => t.name));
551
605
  return tools;
552
606
  }
553
607
  };
@@ -602,6 +656,10 @@ var MediaWebSocket = class {
602
656
  _onClose = null;
603
657
  _onDtmf = null;
604
658
  _markWaiters = /* @__PURE__ */ new Map();
659
+ _log = NOOP_LOGGER;
660
+ setLogger(logger) {
661
+ this._log = logger;
662
+ }
605
663
  /** Set the handler for inbound audio data. */
606
664
  onAudio(handler) {
607
665
  this._onAudio = handler;
@@ -643,6 +701,7 @@ var MediaWebSocket = class {
643
701
  ws.on("open", () => {
644
702
  this._startSendLoop();
645
703
  resolve();
704
+ this._log.info("Media WS connected: %s", url);
646
705
  });
647
706
  ws.on("message", (data) => {
648
707
  try {
@@ -661,7 +720,7 @@ var MediaWebSocket = class {
661
720
  if (!this._ws) {
662
721
  reject(err);
663
722
  }
664
- console.error("[MediaWebSocket] Error:", err.message);
723
+ this._log.error({ err }, "Media WS error");
665
724
  });
666
725
  });
667
726
  }
@@ -822,6 +881,10 @@ var AudioRecorder = class {
822
881
  _mixWritten = 0;
823
882
  _startTime = 0;
824
883
  _started = false;
884
+ _log = NOOP_LOGGER;
885
+ setLogger(logger) {
886
+ this._log = logger;
887
+ }
825
888
  constructor(recordingPath, callId) {
826
889
  this._dir = path__namespace.join(recordingPath, callId);
827
890
  }
@@ -836,6 +899,7 @@ var AudioRecorder = class {
836
899
  fs__namespace.writeSync(this._fdMix, header);
837
900
  this._startTime = performance.now();
838
901
  this._started = true;
902
+ this._log.info("Recording started: %s", this._dir);
839
903
  }
840
904
  _expectedBytes() {
841
905
  const elapsed = (performance.now() - this._startTime) / 1e3;
@@ -888,7 +952,7 @@ var AudioRecorder = class {
888
952
  this._inWritten += pcm16_8k.length;
889
953
  this._writeToMix(pcm16_8k, posBefore);
890
954
  } catch (err) {
891
- console.error("Error writing inbound audio:", err);
955
+ this._log.error({ err }, "Recording write error (inbound)");
892
956
  }
893
957
  }
894
958
  writeOutbound(pcm16_8k) {
@@ -901,7 +965,7 @@ var AudioRecorder = class {
901
965
  this._outWritten += pcm16_8k.length;
902
966
  this._writeToMix(pcm16_8k, posBefore);
903
967
  } catch (err) {
904
- console.error("Error writing outbound audio:", err);
968
+ this._log.error({ err }, "Recording write error (outbound)");
905
969
  }
906
970
  }
907
971
  stop() {
@@ -922,8 +986,10 @@ var AudioRecorder = class {
922
986
  fs__namespace.writeSync(fd, makeWavHeader(maxWritten), 0, 44, 0);
923
987
  fs__namespace.closeSync(fd);
924
988
  }
989
+ const maxSec = maxWritten / 16e3;
990
+ this._log.info("Recording stopped: %s (%ds)", this._dir, maxSec);
925
991
  } catch (err) {
926
- console.error("Error stopping recorder:", err);
992
+ this._log.error({ err }, "Recording stop error");
927
993
  } finally {
928
994
  this._fdIn = null;
929
995
  this._fdOut = null;
@@ -953,6 +1019,7 @@ var CallSession = class {
953
1019
  _dtmfCollectorActive = false;
954
1020
  _dtmfResolvers = [];
955
1021
  _dtmfBuffer = [];
1022
+ _log = NOOP_LOGGER;
956
1023
  _handlers = /* @__PURE__ */ new Map();
957
1024
  _endedPromise;
958
1025
  _resolveEnded;
@@ -969,6 +1036,9 @@ var CallSession = class {
969
1036
  this._resolveEnded = resolve;
970
1037
  });
971
1038
  }
1039
+ setLogger(logger) {
1040
+ this._log = logger;
1041
+ }
972
1042
  get status() {
973
1043
  return this._status;
974
1044
  }
@@ -1044,7 +1114,9 @@ var CallSession = class {
1044
1114
  this._dtmfResolvers = [];
1045
1115
  this._dtmfBuffer = [];
1046
1116
  }
1047
- return collected.join("");
1117
+ const result = collected.join("");
1118
+ this._log.info("DTMF collected: %s", result);
1119
+ return result;
1048
1120
  }
1049
1121
  /** Send a sequence of DTMF digits. */
1050
1122
  async sendDtmfSequence(digits) {
@@ -1096,11 +1168,11 @@ var CallSession = class {
1096
1168
  const result = handler(this, ...args);
1097
1169
  if (result && typeof result.catch === "function") {
1098
1170
  result.catch((err) => {
1099
- console.error(`[CallSession] Error in ${event} handler:`, err);
1171
+ this._log.error({ err }, "CallSession handler error: %s", event);
1100
1172
  });
1101
1173
  }
1102
1174
  } catch (err) {
1103
- console.error(`[CallSession] Error in ${event} handler:`, err);
1175
+ this._log.error({ err }, "CallSession handler error: %s", event);
1104
1176
  }
1105
1177
  }
1106
1178
  }
@@ -1348,6 +1420,9 @@ var ClawOpsAgent = class {
1348
1420
  _passiveDtmfTimer = null;
1349
1421
  _passiveDtmfCallId = null;
1350
1422
  _callSessions = /* @__PURE__ */ new Map();
1423
+ _log;
1424
+ _pipelineLog;
1425
+ _isPipelineSession = false;
1351
1426
  constructor(options) {
1352
1427
  this._apiKey = options.apiKey ?? process.env["CLAWOPS_API_KEY"] ?? "";
1353
1428
  this._accountId = options.accountId ?? process.env["CLAWOPS_ACCOUNT_ID"] ?? "";
@@ -1362,6 +1437,9 @@ var ClawOpsAgent = class {
1362
1437
  if (options.tracing) {
1363
1438
  setTracingConfig(options.tracing);
1364
1439
  }
1440
+ this._log = createAgentLogger(options.logger);
1441
+ this._pipelineLog = createPipelineLogger(this._log);
1442
+ this._isPipelineSession = "_stt" in this._session && "_llm" in this._session;
1365
1443
  }
1366
1444
  /**
1367
1445
  * Register a function tool.
@@ -1421,6 +1499,7 @@ var ClawOpsAgent = class {
1421
1499
  accountId: this._accountId,
1422
1500
  number: this._fromNumber
1423
1501
  });
1502
+ this._controlWs.setLogger(this._log);
1424
1503
  this._controlWs.on("call.incoming", (event) => this._handleIncoming(event));
1425
1504
  this._controlWs.on("call.ended", (event) => this._handleEnded(event));
1426
1505
  this._controlWs.on("call.outbound_ready", (event) => this._handleOutboundReady(event));
@@ -1434,7 +1513,7 @@ var ClawOpsAgent = class {
1434
1513
  `Failed to connect to ClawOps: ${err instanceof Error ? err.message : String(err)}`
1435
1514
  );
1436
1515
  }
1437
- console.log(`[ClawOpsAgent] Connected on ${this._fromNumber}`);
1516
+ this._log.info("ClawOpsAgent connected on %s", this._fromNumber);
1438
1517
  }
1439
1518
  /**
1440
1519
  * Connect and block until disconnected.
@@ -1461,7 +1540,7 @@ var ClawOpsAgent = class {
1461
1540
  }
1462
1541
  this._activeSessions.clear();
1463
1542
  this._callSessions.clear();
1464
- console.log("[ClawOpsAgent] Disconnected");
1543
+ this._log.info("ClawOpsAgent disconnected");
1465
1544
  }
1466
1545
  /**
1467
1546
  * Initiate an outbound call.
@@ -1496,10 +1575,9 @@ var ClawOpsAgent = class {
1496
1575
  callSession.on(evt, handler);
1497
1576
  }
1498
1577
  }
1578
+ callSession.setLogger(this._log);
1499
1579
  this._activeSessions.set(callSession.callId, callSession);
1500
- console.log(
1501
- `[ClawOpsAgent] Outbound call initiated: ${this._fromNumber} -> ${to} (${callSession.callId})`
1502
- );
1580
+ this._log.info("Outbound call initiated: %s -> %s (%s)", this._fromNumber, to, callSession.callId);
1503
1581
  return callSession;
1504
1582
  }
1505
1583
  _handleIncoming(event) {
@@ -1518,13 +1596,15 @@ var ClawOpsAgent = class {
1518
1596
  session.on(evt, handler);
1519
1597
  }
1520
1598
  }
1599
+ session.setLogger(this._log);
1521
1600
  this._activeSessions.set(callId, session);
1601
+ this._log.info("Incoming call: %s -> %s (%s)", fromNumber, this._fromNumber, callId);
1522
1602
  if (this._controlWs) {
1523
1603
  this._controlWs.send({ event: "call.accept", callId });
1524
1604
  }
1525
1605
  if (mediaUrl) {
1526
1606
  this._startCallSession(session, mediaUrl).catch((err) => {
1527
- console.error(`[ClawOpsAgent] Error in call session ${callId}:`, err);
1607
+ this._log.error({ err }, "Call session error: %s", callId);
1528
1608
  });
1529
1609
  }
1530
1610
  }
@@ -1532,6 +1612,7 @@ var ClawOpsAgent = class {
1532
1612
  const callId = event["callId"];
1533
1613
  const session = this._activeSessions.get(callId);
1534
1614
  if (session) {
1615
+ this._log.info("Call ended (server): %s", callId);
1535
1616
  session._markEnded();
1536
1617
  this._activeSessions.delete(callId);
1537
1618
  }
@@ -1548,6 +1629,7 @@ var ClawOpsAgent = class {
1548
1629
  accountId: this._accountId,
1549
1630
  direction: "outbound"
1550
1631
  });
1632
+ session.setLogger(this._log);
1551
1633
  for (const [evt, handlers] of this._handlers) {
1552
1634
  for (const handler of handlers) {
1553
1635
  session.on(evt, handler);
@@ -1556,8 +1638,9 @@ var ClawOpsAgent = class {
1556
1638
  this._activeSessions.set(callId, session);
1557
1639
  }
1558
1640
  if (mediaUrl) {
1641
+ this._log.info("Outbound call answered: %s -> %s (%s)", this._fromNumber, session.toNumber, callId);
1559
1642
  this._startCallSession(session, mediaUrl).catch((err) => {
1560
- console.error(`[ClawOpsAgent] Error in call session ${callId}:`, err);
1643
+ this._log.error({ err }, "Call session error: %s", callId);
1561
1644
  });
1562
1645
  }
1563
1646
  }
@@ -1565,13 +1648,14 @@ var ClawOpsAgent = class {
1565
1648
  const callId = event["callId"];
1566
1649
  const session = this._activeSessions.get(callId);
1567
1650
  if (session) {
1568
- console.log(`[ClawOpsAgent] Outbound call ringing: ${callId}`);
1651
+ this._log.info("Outbound call ringing: %s", callId);
1569
1652
  }
1570
1653
  }
1571
1654
  _handleFailed(event) {
1572
1655
  const callId = event["callId"];
1573
1656
  const session = this._activeSessions.get(callId);
1574
1657
  if (session) {
1658
+ this._log.info("Outbound call failed: %s (%s)", callId, event["reason"] ?? "failed");
1575
1659
  session._emit("call_failed", event["reason"] ?? "failed");
1576
1660
  session._markEnded();
1577
1661
  this._activeSessions.delete(callId);
@@ -1596,7 +1680,7 @@ var ClawOpsAgent = class {
1596
1680
  this._passiveDtmfCallId = null;
1597
1681
  if (digits && sessionHandler && sessionHandler.feedDtmf) {
1598
1682
  sessionHandler.feedDtmf(digits).catch((err) => {
1599
- console.error("[ClawOpsAgent] feedDtmf error:", err);
1683
+ this._log.error({ err }, "DTMF feed error");
1600
1684
  });
1601
1685
  }
1602
1686
  }, this._passiveDtmfDebounceMs);
@@ -1615,22 +1699,25 @@ var ClawOpsAgent = class {
1615
1699
  if (this._mcpServers.length > 0) {
1616
1700
  for (const serverConfig of this._mcpServers) {
1617
1701
  const client = new MCPClient();
1702
+ client.setLogger(this._log);
1618
1703
  client.addServer("mcp", serverConfig);
1619
1704
  try {
1620
1705
  const tools = await client.connect();
1621
1706
  sessionTools.registerMcpTools(tools);
1622
1707
  mcpClients.push(client);
1623
1708
  } catch (err) {
1624
- console.error("[ClawOpsAgent] MCP connection error:", err);
1709
+ this._log.error({ err }, "MCP connection error");
1625
1710
  }
1626
1711
  }
1627
1712
  }
1628
1713
  let recorder = null;
1629
1714
  if (this._recording) {
1630
1715
  recorder = new AudioRecorder(this._recordingPath, session.callId);
1716
+ recorder.setLogger(this._log);
1631
1717
  recorder.start();
1632
1718
  }
1633
1719
  const mediaWs = new MediaWebSocket();
1720
+ mediaWs.setLogger(this._log);
1634
1721
  session._bindTransport(
1635
1722
  (audio) => {
1636
1723
  mediaWs.sendAudio(audio.toString("base64"));
@@ -1660,6 +1747,9 @@ var ClawOpsAgent = class {
1660
1747
  if ("setBuiltinTools" in sessionHandler && typeof sessionHandler.setBuiltinTools === "function") {
1661
1748
  sessionHandler.setBuiltinTools(this._builtinTools);
1662
1749
  }
1750
+ if ("setLogger" in sessionHandler && typeof sessionHandler.setLogger === "function") {
1751
+ sessionHandler.setLogger(this._isPipelineSession ? this._pipelineLog : this._log);
1752
+ }
1663
1753
  this._callSessions.set(session.callId, sessionHandler);
1664
1754
  mediaWs.onAudio((ulawAudio, _timestamp) => {
1665
1755
  if (sessionHandler) {
@@ -1673,6 +1763,7 @@ var ClawOpsAgent = class {
1673
1763
  this._onDtmfEvent(session, digit);
1674
1764
  });
1675
1765
  mediaWs.onClose(() => {
1766
+ this._log.info("Media stream stopped: %s", session.callId);
1676
1767
  if (recorder) {
1677
1768
  recorder.stop();
1678
1769
  }
@@ -1681,11 +1772,12 @@ var ClawOpsAgent = class {
1681
1772
  session._emit("call_start");
1682
1773
  try {
1683
1774
  await mediaWs.connect(mediaWsUrl, this._apiKey);
1775
+ this._log.info("Media stream started: %s", session.callId);
1684
1776
  await sessionHandler.start(session, sessionTools);
1685
1777
  await session.wait();
1686
1778
  await sessionHandler.stop();
1687
1779
  } catch (err) {
1688
- console.error(`[ClawOpsAgent] Call session error:`, err);
1780
+ this._log.error({ err }, "Call session error: %s", session.callId);
1689
1781
  } finally {
1690
1782
  if (mcpClients.length > 0) {
1691
1783
  sessionTools.clearMcpTools();
@@ -1713,7 +1805,11 @@ var HANG_UP_TOOL = {
1713
1805
  type: "function",
1714
1806
  name: "hang_up",
1715
1807
  description: "End the phone call. Use when the conversation is finished or the caller says goodbye.",
1716
- parameters: { type: "object", properties: {}, required: [] }
1808
+ parameters: {
1809
+ type: "object",
1810
+ properties: {},
1811
+ required: []
1812
+ }
1717
1813
  };
1718
1814
  var COLLECT_DTMF_TOOL = {
1719
1815
  type: "function",
@@ -1736,7 +1832,10 @@ var SEND_DTMF_TOOL = {
1736
1832
  parameters: {
1737
1833
  type: "object",
1738
1834
  properties: {
1739
- digits: { type: "string", description: "\uC804\uC1A1\uD560 \uBC88\uD638 (0-9, *, #). 'w'\uB294 500ms \uB300\uAE30, 'W'\uB294 1000ms \uB300\uAE30." }
1835
+ digits: {
1836
+ type: "string",
1837
+ description: "\uC804\uC1A1\uD560 \uBC88\uD638 (0-9, *, #). 'w'\uB294 500ms \uB300\uAE30, 'W'\uB294 1000ms \uB300\uAE30."
1838
+ }
1740
1839
  },
1741
1840
  required: ["digits"]
1742
1841
  }
@@ -1749,7 +1848,11 @@ var OpenAIRealtime = class {
1749
1848
  _language;
1750
1849
  _eagerness;
1751
1850
  _greeting;
1851
+ _log = NOOP_LOGGER;
1752
1852
  _builtinTools = null;
1853
+ setLogger(logger) {
1854
+ this._log = logger;
1855
+ }
1753
1856
  setBuiltinTools(tools) {
1754
1857
  this._builtinTools = tools;
1755
1858
  }
@@ -1806,6 +1909,7 @@ var OpenAIRealtime = class {
1806
1909
  const ws = this._ws;
1807
1910
  ws.on("open", () => {
1808
1911
  this._sendSessionUpdate();
1912
+ this._log.info("OpenAI Realtime connected");
1809
1913
  if (this._greeting) {
1810
1914
  this._send({ type: "response.create" });
1811
1915
  }
@@ -1825,7 +1929,7 @@ var OpenAIRealtime = class {
1825
1929
  if (!this._ws) {
1826
1930
  reject(err);
1827
1931
  }
1828
- console.error("[OpenAIRealtime] WebSocket error:", err.message);
1932
+ this._log.error({ err }, "OpenAI Realtime WS error");
1829
1933
  });
1830
1934
  });
1831
1935
  }
@@ -1859,9 +1963,12 @@ var OpenAIRealtime = class {
1859
1963
  _sendSessionUpdate() {
1860
1964
  if (!this._ws || this._ws.readyState !== 1) return;
1861
1965
  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);
1966
+ if (!this._builtinTools || this._builtinTools.has("hang_up" /* HANG_UP */))
1967
+ toolSchemas.push(HANG_UP_TOOL);
1968
+ if (!this._builtinTools || this._builtinTools.has("collect_dtmf" /* COLLECT_DTMF */))
1969
+ toolSchemas.push(COLLECT_DTMF_TOOL);
1970
+ if (!this._builtinTools || this._builtinTools.has("send_dtmf" /* SEND_DTMF */))
1971
+ toolSchemas.push(SEND_DTMF_TOOL);
1865
1972
  this._send({
1866
1973
  type: "session.update",
1867
1974
  session: {
@@ -1871,7 +1978,7 @@ var OpenAIRealtime = class {
1871
1978
  input_audio_format: "g711_ulaw",
1872
1979
  output_audio_format: "g711_ulaw",
1873
1980
  input_audio_transcription: {
1874
- model: "gpt-4o-mini-transcribe",
1981
+ model: "whisper-1",
1875
1982
  language: this._language
1876
1983
  },
1877
1984
  input_audio_noise_reduction: { type: "far_field" },
@@ -1942,7 +2049,7 @@ var OpenAIRealtime = class {
1942
2049
  break;
1943
2050
  }
1944
2051
  case "error": {
1945
- console.error("[OpenAIRealtime] API error:", msg["error"]);
2052
+ this._log.error({ apiError: msg["error"] }, "OpenAI error");
1946
2053
  break;
1947
2054
  }
1948
2055
  }
@@ -1992,6 +2099,7 @@ var OpenAIRealtime = class {
1992
2099
  async _handleToolCall(item) {
1993
2100
  const funcName = item["name"];
1994
2101
  const callId = item["call_id"];
2102
+ this._log.info("Tool call: %s", funcName);
1995
2103
  if (funcName === "hang_up") {
1996
2104
  if (this._call) {
1997
2105
  await this._call.hangup();
@@ -2048,7 +2156,7 @@ var OpenAIRealtime = class {
2048
2156
  return;
2049
2157
  }
2050
2158
  if (!this._tools || !this._tools.has(funcName)) {
2051
- console.error(`[OpenAIRealtime] Unknown tool: ${funcName}`);
2159
+ this._log.error("Unknown tool: %s", funcName);
2052
2160
  return;
2053
2161
  }
2054
2162
  let result;
@@ -2056,7 +2164,7 @@ var OpenAIRealtime = class {
2056
2164
  const args = JSON.parse(item["arguments"] ?? "{}");
2057
2165
  result = await this._tools.call(funcName, args);
2058
2166
  } catch (err) {
2059
- console.error(`[OpenAIRealtime] Tool call failed: ${funcName}:`, err);
2167
+ this._log.error({ err }, "Tool call failed: %s", funcName);
2060
2168
  result = `Error: ${err}`;
2061
2169
  }
2062
2170
  await this._waitForResponseDone();
@@ -2205,6 +2313,7 @@ var GeminiRealtime = class {
2205
2313
  _audioRemainder = Buffer.alloc(0);
2206
2314
  _builtinTools = null;
2207
2315
  _toolCallInProgress = false;
2316
+ _log = NOOP_LOGGER;
2208
2317
  constructor(options = {}) {
2209
2318
  this._apiKey = options.apiKey ?? process.env["GOOGLE_API_KEY"] ?? "";
2210
2319
  this._systemPrompt = options.systemPrompt ?? "";
@@ -2224,6 +2333,9 @@ var GeminiRealtime = class {
2224
2333
  setBuiltinTools(tools) {
2225
2334
  this._builtinTools = tools;
2226
2335
  }
2336
+ setLogger(logger) {
2337
+ this._log = logger;
2338
+ }
2227
2339
  async start(callSession, tools) {
2228
2340
  this._call = callSession;
2229
2341
  if (tools) this._tools = tools;
@@ -2260,12 +2372,10 @@ var GeminiRealtime = class {
2260
2372
  callbacks: {
2261
2373
  onmessage: (msg) => this._handleMessage(msg),
2262
2374
  onerror: (err) => {
2263
- console.error("[GeminiRealtime] SDK error:", err);
2375
+ this._log.error({ err }, "Gemini SDK error");
2264
2376
  },
2265
2377
  onclose: (ev) => {
2266
- console.log(
2267
- `[GeminiRealtime] Connection closed: code=${ev?.code ?? "unknown"}`
2268
- );
2378
+ this._log.info({ code: ev?.code ?? "unknown" }, "Gemini connection closed");
2269
2379
  this._closed = true;
2270
2380
  }
2271
2381
  }
@@ -2345,11 +2455,11 @@ var GeminiRealtime = class {
2345
2455
  }
2346
2456
  }
2347
2457
  if (serverContent.turnComplete) {
2348
- console.log("[GeminiRealtime] Turn complete");
2458
+ this._log.debug("Turn complete");
2349
2459
  this._flushAudioRemainder();
2350
2460
  }
2351
2461
  if (serverContent.interrupted) {
2352
- console.log("[GeminiRealtime] Barge-in detected");
2462
+ this._log.info("Barge-in detected");
2353
2463
  if (this._call) {
2354
2464
  this._call.clearAudio();
2355
2465
  }
@@ -2358,12 +2468,12 @@ var GeminiRealtime = class {
2358
2468
  }
2359
2469
  const inputText = serverContent.inputTranscription?.text;
2360
2470
  if (inputText && this._call) {
2361
- console.log(`[GeminiRealtime] [TRANSCRIPT-USER] ${inputText}`);
2471
+ this._log.info("User: %s", inputText);
2362
2472
  this._call._emit("transcript", "user", inputText);
2363
2473
  }
2364
2474
  const outputText = serverContent.outputTranscription?.text;
2365
2475
  if (outputText && this._call) {
2366
- console.log(`[GeminiRealtime] [TRANSCRIPT-ASSISTANT] ${outputText}`);
2476
+ this._log.info("Assistant: %s", outputText);
2367
2477
  this._call._emit("transcript", "assistant", outputText);
2368
2478
  }
2369
2479
  }
@@ -2372,9 +2482,7 @@ var GeminiRealtime = class {
2372
2482
  }
2373
2483
  const toolCancellation = msg["toolCallCancellation"];
2374
2484
  if (toolCancellation) {
2375
- console.log(
2376
- `[GeminiRealtime] Tool call cancelled: ${(toolCancellation.ids ?? []).join(", ")}`
2377
- );
2485
+ this._log.info({ ids: toolCancellation.ids }, "Tool call cancelled");
2378
2486
  }
2379
2487
  }
2380
2488
  _handleAudioData(b64Data) {
@@ -2409,17 +2517,14 @@ var GeminiRealtime = class {
2409
2517
  const functionCalls = toolCall.functionCalls;
2410
2518
  if (!functionCalls) return;
2411
2519
  this._toolCallInProgress = true;
2412
- console.log(
2413
- `[GeminiRealtime] toolCall: ${functionCalls.map((fc) => fc.name).join(", ")}`
2414
- );
2415
2520
  const responses = [];
2416
2521
  for (const fc of functionCalls) {
2417
2522
  const name = fc.name ?? "";
2418
2523
  const fcId = fc.id ?? "";
2419
2524
  const args = fc.args ?? {};
2420
- console.log(`[GeminiRealtime] Tool call: ${name}(${JSON.stringify(args)})`);
2525
+ this._log.info({ tool: name, args }, "Tool call: %s", name);
2421
2526
  if (name === "hang_up") {
2422
- console.log("[GeminiRealtime] hang_up: ending call");
2527
+ this._log.info("hang_up: ending call");
2423
2528
  if (this._call) {
2424
2529
  await this._call.hangup();
2425
2530
  }
@@ -2429,17 +2534,15 @@ var GeminiRealtime = class {
2429
2534
  if (this._call) {
2430
2535
  let result;
2431
2536
  try {
2432
- console.log(
2433
- `[GeminiRealtime] collect_dtmf: waiting for digits (maxDigits=${args["max_digits"] ?? 4}, timeout=${args["timeout"] ?? 5})`
2434
- );
2537
+ this._log.info({ maxDigits: args["max_digits"] ?? 4, timeout: args["timeout"] ?? 5 }, "collect_dtmf: waiting for digits");
2435
2538
  result = await this._call.collectDtmf({
2436
2539
  maxDigits: args["max_digits"] ?? 4,
2437
2540
  finishOnKey: args["finish_on_key"] ?? "#",
2438
2541
  timeout: args["timeout"] ?? 5
2439
2542
  });
2440
- console.log(`[GeminiRealtime] DTMF collected: ${result || "(empty)"}`);
2543
+ this._log.info("DTMF collected: %s", result || "(empty)");
2441
2544
  } catch (err) {
2442
- console.error(`[GeminiRealtime] collect_dtmf error:`, err);
2545
+ this._log.error({ err }, "collect_dtmf error");
2443
2546
  result = `Error: ${err}`;
2444
2547
  }
2445
2548
  responses.push({
@@ -2454,12 +2557,12 @@ var GeminiRealtime = class {
2454
2557
  if (this._call) {
2455
2558
  let result;
2456
2559
  try {
2457
- console.log(`[GeminiRealtime] send_dtmf: digits="${args["digits"] ?? ""}"`);
2560
+ this._log.info('send_dtmf: digits="%s"', args["digits"] ?? "");
2458
2561
  await this._call.sendDtmfSequence(args["digits"] ?? "");
2459
2562
  result = "sent";
2460
- console.log(`[GeminiRealtime] send_dtmf: sent`);
2563
+ this._log.info("send_dtmf: sent");
2461
2564
  } catch (err) {
2462
- console.error(`[GeminiRealtime] send_dtmf error:`, err);
2565
+ this._log.error({ err }, "send_dtmf error");
2463
2566
  result = `Error: ${err}`;
2464
2567
  }
2465
2568
  responses.push({ id: fcId, name, response: { result } });
@@ -2467,21 +2570,21 @@ var GeminiRealtime = class {
2467
2570
  continue;
2468
2571
  }
2469
2572
  if (!this._tools || !this._tools.has(name)) {
2470
- console.error(`[GeminiRealtime] Unknown tool: ${name}`);
2573
+ this._log.error("Unknown tool: %s", name);
2471
2574
  responses.push({ id: fcId, name, response: { error: `Unknown tool: ${name}` } });
2472
2575
  continue;
2473
2576
  }
2474
2577
  try {
2475
2578
  const result = await this._tools.call(name, args);
2476
2579
  const resultStr = typeof result === "string" ? result : JSON.stringify(result);
2477
- console.log(`[GeminiRealtime] Tool result: ${name} -> ${resultStr.substring(0, 200)}`);
2580
+ this._log.info("Tool result: %s -> %s", name, resultStr.substring(0, 200));
2478
2581
  responses.push({
2479
2582
  id: fcId,
2480
2583
  name,
2481
2584
  response: { result: resultStr }
2482
2585
  });
2483
2586
  } catch (err) {
2484
- console.error(`[GeminiRealtime] Tool call failed: ${name}:`, err);
2587
+ this._log.error({ err }, "Tool call failed: %s", name);
2485
2588
  responses.push({
2486
2589
  id: fcId,
2487
2590
  name,
@@ -2490,7 +2593,7 @@ var GeminiRealtime = class {
2490
2593
  }
2491
2594
  }
2492
2595
  if (responses.length > 0 && this._session) {
2493
- console.log(`[GeminiRealtime] Sending ${responses.length} tool response(s)`);
2596
+ this._log.debug("Sending %d tool response(s)", responses.length);
2494
2597
  this._session.sendToolResponse({
2495
2598
  functionResponses: responses
2496
2599
  });
@@ -2543,6 +2646,7 @@ var PipelineSession = class {
2543
2646
  _running = false;
2544
2647
  _speaking = false;
2545
2648
  _builtinTools = null;
2649
+ _log = NOOP_LOGGER;
2546
2650
  constructor(options) {
2547
2651
  this._stt = options.stt;
2548
2652
  this._llm = options.llm;
@@ -2566,10 +2670,20 @@ var PipelineSession = class {
2566
2670
  setBuiltinTools(tools) {
2567
2671
  this._builtinTools = tools;
2568
2672
  }
2673
+ setLogger(logger) {
2674
+ this._log = logger;
2675
+ if ("setLogger" in this._stt && typeof this._stt.setLogger === "function") {
2676
+ this._stt.setLogger(logger);
2677
+ }
2678
+ if ("setLogger" in this._tts && typeof this._tts.setLogger === "function") {
2679
+ this._tts.setLogger(logger);
2680
+ }
2681
+ }
2569
2682
  async start(callSession, tools) {
2570
2683
  this._callSession = callSession;
2571
2684
  this._tools = tools ?? null;
2572
2685
  this._running = true;
2686
+ this._log.info("PipelineSession started");
2573
2687
  this._conversation = [];
2574
2688
  if (this._systemPrompt) {
2575
2689
  this._conversation.push({
@@ -2579,11 +2693,11 @@ var PipelineSession = class {
2579
2693
  }
2580
2694
  if (this._greeting) {
2581
2695
  this._generateGreeting().catch((err) => {
2582
- console.error("[PipelineSession] Greeting error:", err);
2696
+ this._log.error({ err }, "Greeting error");
2583
2697
  });
2584
2698
  }
2585
2699
  this._runSttLoop().catch((err) => {
2586
- console.error("[PipelineSession] STT loop error:", err);
2700
+ this._log.error({ err }, "STT loop error");
2587
2701
  });
2588
2702
  }
2589
2703
  feedAudio(audio) {
@@ -2600,6 +2714,7 @@ var PipelineSession = class {
2600
2714
  }
2601
2715
  async stop() {
2602
2716
  this._running = false;
2717
+ this._log.info("PipelineSession stopped");
2603
2718
  this._audioBuffer = [];
2604
2719
  }
2605
2720
  async _runSttLoop() {
@@ -2613,8 +2728,10 @@ var PipelineSession = class {
2613
2728
  if (this._callSession) {
2614
2729
  this._callSession.clearAudio();
2615
2730
  }
2731
+ this._log.info('Barge-in: "%s"', event.transcript.substring(0, 30));
2616
2732
  }
2617
2733
  if (event.type === "final" && event.transcript.trim()) {
2734
+ this._log.info("STT: %s", event.transcript);
2618
2735
  await this._handleUserSpeech(event.transcript);
2619
2736
  }
2620
2737
  }
@@ -2683,6 +2800,7 @@ var PipelineSession = class {
2683
2800
  }
2684
2801
  }
2685
2802
  if (fullResponse.trim()) {
2803
+ this._log.info("Assistant: %s", fullResponse.substring(0, 100));
2686
2804
  this._conversation.push({ role: "assistant", content: fullResponse });
2687
2805
  await this._synthesizeAndSend(fullResponse);
2688
2806
  }
@@ -2750,7 +2868,7 @@ var PipelineSession = class {
2750
2868
  await this._synthesizeAndSend(followUpText);
2751
2869
  }
2752
2870
  } catch (err) {
2753
- console.error(`[PipelineSession] Tool call error for ${name}:`, err);
2871
+ this._log.error({ err }, "Tool call failed: %s", name);
2754
2872
  }
2755
2873
  }
2756
2874
  async _synthesizeAndSend(text) {
@@ -2776,7 +2894,7 @@ var PipelineSession = class {
2776
2894
  }
2777
2895
  }
2778
2896
  } catch (err) {
2779
- console.error("[PipelineSession] TTS error:", err);
2897
+ this._log.error({ err }, "TTS error");
2780
2898
  } finally {
2781
2899
  this._speaking = false;
2782
2900
  }
@@ -2786,6 +2904,10 @@ var PipelineSession = class {
2786
2904
  // src/agent/pipeline/deepgram-stt.ts
2787
2905
  var DeepgramSTT = class {
2788
2906
  _options;
2907
+ _log = NOOP_LOGGER;
2908
+ setLogger(logger) {
2909
+ this._log = logger;
2910
+ }
2789
2911
  constructor(options = {}) {
2790
2912
  this._options = {
2791
2913
  model: "nova-3",
@@ -2855,7 +2977,7 @@ var DeepgramSTT = class {
2855
2977
  }
2856
2978
  });
2857
2979
  ws.on("error", (err) => {
2858
- console.error("[DeepgramSTT] WebSocket error:", err.message);
2980
+ this._log.error({ err }, "Deepgram STT error");
2859
2981
  done = true;
2860
2982
  if (resolveWait) {
2861
2983
  resolveWait();
@@ -2866,6 +2988,7 @@ var DeepgramSTT = class {
2866
2988
  ws.on("open", resolve);
2867
2989
  ws.on("error", reject);
2868
2990
  });
2991
+ this._log.info("Deepgram STT connected");
2869
2992
  const feedPromise = (async () => {
2870
2993
  try {
2871
2994
  for await (const chunk of audioStream) {
@@ -2902,6 +3025,10 @@ var DeepgramSTT = class {
2902
3025
  // src/agent/pipeline/elevenlabs-tts.ts
2903
3026
  var ElevenLabsTTS = class {
2904
3027
  _options;
3028
+ _log = NOOP_LOGGER;
3029
+ setLogger(logger) {
3030
+ this._log = logger;
3031
+ }
2905
3032
  constructor(options = {}) {
2906
3033
  this._options = {
2907
3034
  voiceId: "EXAVITQu4vr4xnSDxMaL",
@@ -3001,7 +3128,7 @@ var ElevenLabsTTS = class {
3001
3128
  }
3002
3129
  });
3003
3130
  ws.on("error", (err) => {
3004
- console.error("[ElevenLabsTTS] WebSocket error:", err.message);
3131
+ this._log.error({ err }, "ElevenLabs TTS error");
3005
3132
  done = true;
3006
3133
  if (resolveWait) {
3007
3134
  resolveWait();
@@ -3012,16 +3139,19 @@ var ElevenLabsTTS = class {
3012
3139
  ws.on("open", resolve);
3013
3140
  ws.on("error", reject);
3014
3141
  });
3142
+ this._log.info("ElevenLabs TTS connected");
3015
3143
  const feedPromise = (async () => {
3016
3144
  try {
3017
3145
  for await (const chunk of textStream) {
3018
3146
  if (done) break;
3019
3147
  if (ws.readyState === 1) {
3148
+ this._log.debug("ElevenLabs sending text: %s", chunk.substring(0, 60));
3020
3149
  ws.send(JSON.stringify({ text: chunk }));
3021
3150
  }
3022
3151
  }
3023
3152
  } finally {
3024
3153
  if (ws.readyState === 1) {
3154
+ this._log.debug("ElevenLabs sending EOS");
3025
3155
  ws.send(JSON.stringify({ text: "" }));
3026
3156
  }
3027
3157
  }
@@ -3583,6 +3713,8 @@ exports.PipelineSession = PipelineSession;
3583
3713
  exports.TogetherLLM = TogetherLLM;
3584
3714
  exports.ToolRegistry = ToolRegistry;
3585
3715
  exports.XaiLLM = XaiLLM;
3716
+ exports.createAgentLogger = createAgentLogger;
3717
+ exports.createPipelineLogger = createPipelineLogger;
3586
3718
  exports.functionTool = functionTool;
3587
3719
  exports.getTracingConfig = getTracingConfig;
3588
3720
  exports.mcpServerHTTP = mcpServerHTTP;