@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.
- package/dist/agent/index.cjs +201 -69
- package/dist/agent/index.cjs.map +1 -1
- package/dist/agent/index.d.cts +46 -1
- package/dist/agent/index.d.ts +46 -1
- package/dist/agent/index.js +197 -70
- package/dist/agent/index.js.map +1 -1
- package/package.json +12 -11
package/dist/agent/index.cjs
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
474
|
+
this._log.error({ err }, "Control WS handler error: %s", event.event);
|
|
450
475
|
});
|
|
451
476
|
}
|
|
452
477
|
} catch (err) {
|
|
453
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1171
|
+
this._log.error({ err }, "CallSession handler error: %s", event);
|
|
1100
1172
|
});
|
|
1101
1173
|
}
|
|
1102
1174
|
} catch (err) {
|
|
1103
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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: {
|
|
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: {
|
|
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
|
-
|
|
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 */))
|
|
1863
|
-
|
|
1864
|
-
if (!this._builtinTools || this._builtinTools.has("
|
|
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: "
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
2375
|
+
this._log.error({ err }, "Gemini SDK error");
|
|
2264
2376
|
},
|
|
2265
2377
|
onclose: (ev) => {
|
|
2266
|
-
|
|
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
|
-
|
|
2458
|
+
this._log.debug("Turn complete");
|
|
2349
2459
|
this._flushAudioRemainder();
|
|
2350
2460
|
}
|
|
2351
2461
|
if (serverContent.interrupted) {
|
|
2352
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
2525
|
+
this._log.info({ tool: name, args }, "Tool call: %s", name);
|
|
2421
2526
|
if (name === "hang_up") {
|
|
2422
|
-
|
|
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
|
-
|
|
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
|
-
|
|
2543
|
+
this._log.info("DTMF collected: %s", result || "(empty)");
|
|
2441
2544
|
} catch (err) {
|
|
2442
|
-
|
|
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
|
-
|
|
2560
|
+
this._log.info('send_dtmf: digits="%s"', args["digits"] ?? "");
|
|
2458
2561
|
await this._call.sendDtmfSequence(args["digits"] ?? "");
|
|
2459
2562
|
result = "sent";
|
|
2460
|
-
|
|
2563
|
+
this._log.info("send_dtmf: sent");
|
|
2461
2564
|
} catch (err) {
|
|
2462
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
2696
|
+
this._log.error({ err }, "Greeting error");
|
|
2583
2697
|
});
|
|
2584
2698
|
}
|
|
2585
2699
|
this._runSttLoop().catch((err) => {
|
|
2586
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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;
|