@teamlearners/clawops 0.5.8 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,7 +1,8 @@
1
- import { DEFAULT_BASE_URL, AgentError, AgentConnectionError } from '../chunk-7S2OEBS6.js';
1
+ import { DEFAULT_BASE_URL, AgentError, AgentConnectionError, VERSION } from '../chunk-2B74QZZJ.js';
2
2
  import pino from 'pino';
3
3
  import * as fs from 'fs';
4
4
  import * as path from 'path';
5
+ import os from 'os';
5
6
 
6
7
  // src/agent/audio.ts
7
8
  var _BIAS = 132;
@@ -972,6 +973,36 @@ var AudioRecorder = class {
972
973
  }
973
974
  }
974
975
  };
976
+ var MAX_ERRORS = 20;
977
+ var MAX_ERROR_MESSAGE_LENGTH = 200;
978
+ function getSdkInfo() {
979
+ return {
980
+ name: "clawops-node",
981
+ version: VERSION,
982
+ runtime: `node/${process.versions.node}`,
983
+ os: `${process.platform}/${os.arch()}`
984
+ };
985
+ }
986
+ function createCallMetrics() {
987
+ return {
988
+ firstResponseMs: null,
989
+ turnCount: 0,
990
+ toolCallCount: 0,
991
+ toolErrorCount: 0,
992
+ bargeInCount: 0,
993
+ endReason: null,
994
+ errors: []
995
+ };
996
+ }
997
+ function addMetricError(metrics, err) {
998
+ metrics.toolErrorCount++;
999
+ if (metrics.errors.length < MAX_ERRORS) {
1000
+ metrics.errors.push({
1001
+ type: err.name || "Error",
1002
+ message: (err.message || "").slice(0, MAX_ERROR_MESSAGE_LENGTH)
1003
+ });
1004
+ }
1005
+ }
975
1006
 
976
1007
  // src/agent/session.ts
977
1008
  var CallSession = class {
@@ -997,6 +1028,8 @@ var CallSession = class {
997
1028
  _handlers = /* @__PURE__ */ new Map();
998
1029
  _endedPromise;
999
1030
  _resolveEnded;
1031
+ _metrics = createCallMetrics();
1032
+ _firstResponseSent = false;
1000
1033
  constructor(options) {
1001
1034
  this.callId = options.callId;
1002
1035
  this.fromNumber = options.fromNumber;
@@ -1019,6 +1052,30 @@ var CallSession = class {
1019
1052
  get duration() {
1020
1053
  return (Date.now() - this.startTime.getTime()) / 1e3;
1021
1054
  }
1055
+ get metrics() {
1056
+ return this._metrics;
1057
+ }
1058
+ recordFirstResponse() {
1059
+ if (!this._firstResponseSent) {
1060
+ this._firstResponseSent = true;
1061
+ this._metrics.firstResponseMs = Date.now() - this.startTime.getTime();
1062
+ }
1063
+ }
1064
+ recordTurn() {
1065
+ this._metrics.turnCount++;
1066
+ }
1067
+ recordToolCall() {
1068
+ this._metrics.toolCallCount++;
1069
+ }
1070
+ recordToolError(err) {
1071
+ addMetricError(this._metrics, err);
1072
+ }
1073
+ recordBargeIn() {
1074
+ this._metrics.bargeInCount++;
1075
+ }
1076
+ recordEndReason(reason) {
1077
+ this._metrics.endReason = reason;
1078
+ }
1022
1079
  /** Bind transport functions (called internally by the agent). */
1023
1080
  _bindTransport(send, clear, hangup, sendDtmf, isConnected) {
1024
1081
  this._sendAudioFn = send;
@@ -1483,6 +1540,10 @@ var ClawOpsAgent = class {
1483
1540
  try {
1484
1541
  await this._controlWs.connect();
1485
1542
  await this._controlWs.waitConnected();
1543
+ try {
1544
+ this._controlWs.send({ event: "agent.hello", sdk: getSdkInfo() });
1545
+ } catch {
1546
+ }
1486
1547
  } catch (err) {
1487
1548
  throw new AgentConnectionError(
1488
1549
  `Failed to connect to ClawOps: ${err instanceof Error ? err.message : String(err)}`
@@ -1696,9 +1757,11 @@ var ClawOpsAgent = class {
1696
1757
  session._bindTransport(
1697
1758
  (audio) => {
1698
1759
  mediaWs.sendAudio(audio.toString("base64"));
1760
+ session.recordFirstResponse();
1699
1761
  },
1700
1762
  () => {
1701
1763
  mediaWs.sendClear();
1764
+ session.recordBargeIn();
1702
1765
  },
1703
1766
  async () => {
1704
1767
  await mediaWs.flush();
@@ -1749,10 +1812,22 @@ var ClawOpsAgent = class {
1749
1812
  await mediaWs.connect(mediaWsUrl, this._apiKey);
1750
1813
  this._log.info("Media stream started: %s", session.callId);
1751
1814
  await sessionHandler.start(session, sessionTools);
1815
+ const telemetry = sessionHandler.getTelemetry?.() ?? null;
1816
+ if (telemetry) {
1817
+ telemetry.toolCount = sessionTools?.size ?? 0;
1818
+ telemetry.mcpServerCount = this._mcpServers?.length ?? 0;
1819
+ telemetry.builtinTools = this._builtinTools ? [...this._builtinTools].map((t) => t.toString()) : [];
1820
+ telemetry.recordingEnabled = this._recording;
1821
+ try {
1822
+ this._controlWs.send({ event: "call.telemetry", callId: session.callId, telemetry });
1823
+ } catch {
1824
+ }
1825
+ }
1752
1826
  await session.wait();
1753
1827
  await sessionHandler.stop();
1754
1828
  } catch (err) {
1755
1829
  this._log.error({ err }, "Call session error: %s", session.callId);
1830
+ session.recordEndReason("error");
1756
1831
  } finally {
1757
1832
  if (mcpClients.length > 0) {
1758
1833
  sessionTools.clearMcpTools();
@@ -1764,6 +1839,13 @@ var ClawOpsAgent = class {
1764
1839
  if (recorder) {
1765
1840
  recorder.stop();
1766
1841
  }
1842
+ if (!session.metrics.endReason) {
1843
+ session.recordEndReason(session.status === "ended" ? "user_hangup" : "agent_hangup");
1844
+ }
1845
+ try {
1846
+ this._controlWs?.send({ event: "call.metrics", callId: session.callId, metrics: session.metrics });
1847
+ } catch {
1848
+ }
1767
1849
  session._emit("call_end");
1768
1850
  session._markEnded();
1769
1851
  this._activeSessions.delete(session.callId);
@@ -1930,6 +2012,24 @@ var PipelineSession = class {
1930
2012
  setBuiltinTools(tools) {
1931
2013
  this._builtinTools = tools;
1932
2014
  }
2015
+ getTelemetry() {
2016
+ const llm = this._llm;
2017
+ const stt = this._stt;
2018
+ const tts = this._tts;
2019
+ return {
2020
+ sessionType: "pipeline",
2021
+ llm: llm.provider && llm.model ? { provider: llm.provider, model: llm.model } : null,
2022
+ stt: stt.provider && stt.model ? { provider: stt.provider, model: stt.model } : null,
2023
+ tts: tts.provider && tts.model ? { provider: tts.provider, model: tts.model } : null,
2024
+ voice: tts.voiceId ?? null,
2025
+ language: this._language,
2026
+ greetingEnabled: this._greeting,
2027
+ recordingEnabled: !!this._recorder,
2028
+ toolCount: this._tools?.size ?? 0,
2029
+ mcpServerCount: 0,
2030
+ builtinTools: []
2031
+ };
2032
+ }
1933
2033
  setLogger(logger) {
1934
2034
  this._log = logger;
1935
2035
  if ("setLogger" in this._stt && typeof this._stt.setLogger === "function") {
@@ -2076,6 +2176,7 @@ var PipelineSession = class {
2076
2176
  }
2077
2177
  }
2078
2178
  if (!this._tools) return;
2179
+ this._callSession?.recordToolCall();
2079
2180
  const result = await this._tools.call(name, args);
2080
2181
  this._conversation.push({
2081
2182
  role: "assistant",
@@ -2107,6 +2208,9 @@ var PipelineSession = class {
2107
2208
  }
2108
2209
  } catch (err) {
2109
2210
  this._log.error({ err }, "Tool call failed: %s", name);
2211
+ if (err instanceof Error) {
2212
+ this._callSession?.recordToolError(err);
2213
+ }
2110
2214
  }
2111
2215
  }
2112
2216
  async _synthesizeAndSend(text) {
@@ -2157,6 +2261,21 @@ var OpenAIRealtime = class {
2157
2261
  setBuiltinTools(tools) {
2158
2262
  this._builtinTools = tools;
2159
2263
  }
2264
+ getTelemetry() {
2265
+ return {
2266
+ sessionType: "openai_realtime",
2267
+ llm: { provider: "openai", model: this._model },
2268
+ stt: null,
2269
+ tts: null,
2270
+ voice: this._voice,
2271
+ language: this._language,
2272
+ greetingEnabled: this._greeting,
2273
+ recordingEnabled: !!this._recorder,
2274
+ toolCount: this._tools?.size ?? 0,
2275
+ mcpServerCount: 0,
2276
+ builtinTools: []
2277
+ };
2278
+ }
2160
2279
  _ws = null;
2161
2280
  _call = null;
2162
2281
  _tools = null;
@@ -2444,9 +2563,13 @@ var OpenAIRealtime = class {
2444
2563
  let result;
2445
2564
  try {
2446
2565
  const args = JSON.parse(item["arguments"] ?? "{}");
2566
+ this._call?.recordToolCall();
2447
2567
  result = await this._tools.call(funcName, args);
2448
2568
  } catch (err) {
2449
2569
  this._log.error({ err }, "Tool call failed: %s", funcName);
2570
+ if (err instanceof Error) {
2571
+ this._call?.recordToolError(err);
2572
+ }
2450
2573
  result = `Error: ${err}`;
2451
2574
  }
2452
2575
  if (controller.signal.aborted) {
@@ -2595,6 +2718,21 @@ var GeminiRealtime = class {
2595
2718
  setLogger(logger) {
2596
2719
  this._log = logger;
2597
2720
  }
2721
+ getTelemetry() {
2722
+ return {
2723
+ sessionType: "gemini_realtime",
2724
+ llm: { provider: "gemini", model: this._model },
2725
+ stt: null,
2726
+ tts: null,
2727
+ voice: this._voice,
2728
+ language: this._language,
2729
+ greetingEnabled: this._greeting,
2730
+ recordingEnabled: !!this._recorder,
2731
+ toolCount: this._tools?.size ?? 0,
2732
+ mcpServerCount: 0,
2733
+ builtinTools: []
2734
+ };
2735
+ }
2598
2736
  async start(callSession, tools) {
2599
2737
  this._call = callSession;
2600
2738
  if (tools) this._tools = tools;
@@ -2798,6 +2936,7 @@ var GeminiRealtime = class {
2798
2936
  continue;
2799
2937
  }
2800
2938
  try {
2939
+ this._call?.recordToolCall();
2801
2940
  const result = await this._tools.call(name, args);
2802
2941
  const resultStr = typeof result === "string" ? result : JSON.stringify(result);
2803
2942
  this._log.info("Tool result: %s -> %s", name, resultStr.substring(0, 200));
@@ -2808,6 +2947,9 @@ var GeminiRealtime = class {
2808
2947
  });
2809
2948
  } catch (err) {
2810
2949
  this._log.error({ err }, "Tool call failed: %s", name);
2950
+ if (err instanceof Error) {
2951
+ this._call?.recordToolError(err);
2952
+ }
2811
2953
  responses.push({
2812
2954
  id: fcId,
2813
2955
  name,
@@ -2829,6 +2971,12 @@ var GeminiRealtime = class {
2829
2971
  var DeepgramSTT = class {
2830
2972
  _options;
2831
2973
  _log = NOOP_LOGGER;
2974
+ get provider() {
2975
+ return "deepgram";
2976
+ }
2977
+ get model() {
2978
+ return this._options.model ?? "nova-3";
2979
+ }
2832
2980
  setLogger(logger) {
2833
2981
  this._log = logger;
2834
2982
  }
@@ -2950,6 +3098,15 @@ var DeepgramSTT = class {
2950
3098
  var ElevenLabsTTS = class {
2951
3099
  _options;
2952
3100
  _log = NOOP_LOGGER;
3101
+ get provider() {
3102
+ return "elevenlabs";
3103
+ }
3104
+ get model() {
3105
+ return this._options.model ?? "eleven_flash_v2_5";
3106
+ }
3107
+ get voiceId() {
3108
+ return this._options.voiceId ?? "EXAVITQu4vr4xnSDxMaL";
3109
+ }
2953
3110
  setLogger(logger) {
2954
3111
  this._log = logger;
2955
3112
  }
@@ -3102,6 +3259,12 @@ var ElevenLabsTTS = class {
3102
3259
  // src/agent/pipeline/llm/openai-llm.ts
3103
3260
  var OpenAILLM = class {
3104
3261
  _options;
3262
+ get provider() {
3263
+ return "openai";
3264
+ }
3265
+ get model() {
3266
+ return this._options.model;
3267
+ }
3105
3268
  constructor(options = {}) {
3106
3269
  this._options = {
3107
3270
  model: "gpt-4o-mini",
@@ -3188,6 +3351,12 @@ var OpenAILLM = class {
3188
3351
  // src/agent/pipeline/llm/anthropic-llm.ts
3189
3352
  var AnthropicLLM = class {
3190
3353
  _options;
3354
+ get provider() {
3355
+ return "anthropic";
3356
+ }
3357
+ get model() {
3358
+ return this._options.model;
3359
+ }
3191
3360
  constructor(options = {}) {
3192
3361
  this._options = {
3193
3362
  model: "claude-sonnet-4-6",
@@ -3293,6 +3462,12 @@ var AnthropicLLM = class {
3293
3462
  // src/agent/pipeline/llm/gemini-llm.ts
3294
3463
  var GeminiLLM = class {
3295
3464
  _options;
3465
+ get provider() {
3466
+ return "gemini";
3467
+ }
3468
+ get model() {
3469
+ return this._options.model;
3470
+ }
3296
3471
  constructor(options = {}) {
3297
3472
  this._options = {
3298
3473
  model: "gemini-2.5-flash",
@@ -3377,6 +3552,12 @@ var GeminiLLM = class {
3377
3552
  // src/agent/pipeline/llm/openai-compat-llm.ts
3378
3553
  var OpenAICompatLLM = class {
3379
3554
  _options;
3555
+ get provider() {
3556
+ return "openai_compatible";
3557
+ }
3558
+ get model() {
3559
+ return this._options.model;
3560
+ }
3380
3561
  constructor(options) {
3381
3562
  this._options = options;
3382
3563
  }
@@ -3460,6 +3641,12 @@ var OpenAICompatLLM = class {
3460
3641
  // src/agent/pipeline/llm/ollama-llm.ts
3461
3642
  var OllamaLLM = class {
3462
3643
  _inner;
3644
+ get provider() {
3645
+ return "ollama";
3646
+ }
3647
+ get model() {
3648
+ return this._inner.model;
3649
+ }
3463
3650
  constructor(options = {}) {
3464
3651
  const baseUrl = options.baseUrl ?? process.env["OLLAMA_BASE_URL"] ?? "http://localhost:11434/v1";
3465
3652
  this._inner = new OpenAICompatLLM({
@@ -3478,6 +3665,12 @@ var OllamaLLM = class {
3478
3665
  // src/agent/pipeline/llm/mistral-llm.ts
3479
3666
  var MistralLLM = class {
3480
3667
  _inner;
3668
+ get provider() {
3669
+ return "mistral";
3670
+ }
3671
+ get model() {
3672
+ return this._inner.model;
3673
+ }
3481
3674
  constructor(options = {}) {
3482
3675
  this._inner = new OpenAICompatLLM({
3483
3676
  apiKey: options.apiKey ?? process.env["MISTRAL_API_KEY"],
@@ -3495,6 +3688,12 @@ var MistralLLM = class {
3495
3688
  // src/agent/pipeline/llm/groq-llm.ts
3496
3689
  var GroqLLM = class {
3497
3690
  _inner;
3691
+ get provider() {
3692
+ return "groq";
3693
+ }
3694
+ get model() {
3695
+ return this._inner.model;
3696
+ }
3498
3697
  constructor(options = {}) {
3499
3698
  this._inner = new OpenAICompatLLM({
3500
3699
  apiKey: options.apiKey ?? process.env["GROQ_API_KEY"],
@@ -3512,6 +3711,12 @@ var GroqLLM = class {
3512
3711
  // src/agent/pipeline/llm/perplexity-llm.ts
3513
3712
  var PerplexityLLM = class {
3514
3713
  _inner;
3714
+ get provider() {
3715
+ return "perplexity";
3716
+ }
3717
+ get model() {
3718
+ return this._inner.model;
3719
+ }
3515
3720
  constructor(options = {}) {
3516
3721
  this._inner = new OpenAICompatLLM({
3517
3722
  apiKey: options.apiKey ?? process.env["PERPLEXITY_API_KEY"],
@@ -3529,6 +3734,12 @@ var PerplexityLLM = class {
3529
3734
  // src/agent/pipeline/llm/together-llm.ts
3530
3735
  var TogetherLLM = class {
3531
3736
  _inner;
3737
+ get provider() {
3738
+ return "together";
3739
+ }
3740
+ get model() {
3741
+ return this._inner.model;
3742
+ }
3532
3743
  constructor(options = {}) {
3533
3744
  this._inner = new OpenAICompatLLM({
3534
3745
  apiKey: options.apiKey ?? process.env["TOGETHER_API_KEY"],
@@ -3546,6 +3757,12 @@ var TogetherLLM = class {
3546
3757
  // src/agent/pipeline/llm/fireworks-llm.ts
3547
3758
  var FireworksLLM = class {
3548
3759
  _inner;
3760
+ get provider() {
3761
+ return "fireworks";
3762
+ }
3763
+ get model() {
3764
+ return this._inner.model;
3765
+ }
3549
3766
  constructor(options = {}) {
3550
3767
  this._inner = new OpenAICompatLLM({
3551
3768
  apiKey: options.apiKey ?? process.env["FIREWORKS_API_KEY"],
@@ -3563,6 +3780,12 @@ var FireworksLLM = class {
3563
3780
  // src/agent/pipeline/llm/deepseek-llm.ts
3564
3781
  var DeepSeekLLM = class {
3565
3782
  _inner;
3783
+ get provider() {
3784
+ return "deepseek";
3785
+ }
3786
+ get model() {
3787
+ return this._inner.model;
3788
+ }
3566
3789
  constructor(options = {}) {
3567
3790
  this._inner = new OpenAICompatLLM({
3568
3791
  apiKey: options.apiKey ?? process.env["DEEPSEEK_API_KEY"],
@@ -3580,6 +3803,12 @@ var DeepSeekLLM = class {
3580
3803
  // src/agent/pipeline/llm/xai-llm.ts
3581
3804
  var XaiLLM = class {
3582
3805
  _inner;
3806
+ get provider() {
3807
+ return "xai";
3808
+ }
3809
+ get model() {
3810
+ return this._inner.model;
3811
+ }
3583
3812
  constructor(options = {}) {
3584
3813
  this._inner = new OpenAICompatLLM({
3585
3814
  apiKey: options.apiKey ?? process.env["XAI_API_KEY"],