@teamlearners/clawops 0.5.8 → 0.8.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.
package/README.md CHANGED
@@ -128,6 +128,19 @@ const call = await client.calls.create({
128
128
  });
129
129
  console.log(call.callId);
130
130
 
131
+ // AI Completion 모드 — AI가 직접 통화를 처리
132
+ const aiCall = await client.calls.create({
133
+ to: '01012345678',
134
+ from: '07052358010',
135
+ ai: {
136
+ provider: 'openai',
137
+ model: 'gpt-realtime',
138
+ apiKey: process.env.OPENAI_API_KEY!,
139
+ voice: 'marin',
140
+ messages: [{ role: 'system', content: '당신은 예약 확인 AI입니다.' }],
141
+ },
142
+ });
143
+
131
144
  // 통화 목록 조회 (페이지네이션)
132
145
  const page = await client.calls.list({ status: 'completed', page: 0, pageSize: 20 });
133
146
  for (const call of page) {
@@ -1,9 +1,10 @@
1
1
  'use strict';
2
2
 
3
- var chunk6IQN5RQD_cjs = require('../chunk-6IQN5RQD.cjs');
3
+ var chunkR742GPVL_cjs = require('../chunk-R742GPVL.cjs');
4
4
  var pino = require('pino');
5
5
  var fs = require('fs');
6
6
  var path = require('path');
7
+ var os = require('os');
7
8
 
8
9
  function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
9
10
 
@@ -28,6 +29,7 @@ function _interopNamespace(e) {
28
29
  var pino__default = /*#__PURE__*/_interopDefault(pino);
29
30
  var fs__namespace = /*#__PURE__*/_interopNamespace(fs);
30
31
  var path__namespace = /*#__PURE__*/_interopNamespace(path);
32
+ var os__default = /*#__PURE__*/_interopDefault(os);
31
33
 
32
34
  // src/agent/audio.ts
33
35
  var _BIAS = 132;
@@ -998,6 +1000,36 @@ var AudioRecorder = class {
998
1000
  }
999
1001
  }
1000
1002
  };
1003
+ var MAX_ERRORS = 20;
1004
+ var MAX_ERROR_MESSAGE_LENGTH = 200;
1005
+ function getSdkInfo() {
1006
+ return {
1007
+ name: "clawops-node",
1008
+ version: chunkR742GPVL_cjs.VERSION,
1009
+ runtime: `node/${process.versions.node}`,
1010
+ os: `${process.platform}/${os__default.default.arch()}`
1011
+ };
1012
+ }
1013
+ function createCallMetrics() {
1014
+ return {
1015
+ firstResponseMs: null,
1016
+ turnCount: 0,
1017
+ toolCallCount: 0,
1018
+ toolErrorCount: 0,
1019
+ bargeInCount: 0,
1020
+ endReason: null,
1021
+ errors: []
1022
+ };
1023
+ }
1024
+ function addMetricError(metrics, err) {
1025
+ metrics.toolErrorCount++;
1026
+ if (metrics.errors.length < MAX_ERRORS) {
1027
+ metrics.errors.push({
1028
+ type: err.name || "Error",
1029
+ message: (err.message || "").slice(0, MAX_ERROR_MESSAGE_LENGTH)
1030
+ });
1031
+ }
1032
+ }
1001
1033
 
1002
1034
  // src/agent/session.ts
1003
1035
  var CallSession = class {
@@ -1023,6 +1055,8 @@ var CallSession = class {
1023
1055
  _handlers = /* @__PURE__ */ new Map();
1024
1056
  _endedPromise;
1025
1057
  _resolveEnded;
1058
+ _metrics = createCallMetrics();
1059
+ _firstResponseSent = false;
1026
1060
  constructor(options) {
1027
1061
  this.callId = options.callId;
1028
1062
  this.fromNumber = options.fromNumber;
@@ -1045,6 +1079,30 @@ var CallSession = class {
1045
1079
  get duration() {
1046
1080
  return (Date.now() - this.startTime.getTime()) / 1e3;
1047
1081
  }
1082
+ get metrics() {
1083
+ return this._metrics;
1084
+ }
1085
+ recordFirstResponse() {
1086
+ if (!this._firstResponseSent) {
1087
+ this._firstResponseSent = true;
1088
+ this._metrics.firstResponseMs = Date.now() - this.startTime.getTime();
1089
+ }
1090
+ }
1091
+ recordTurn() {
1092
+ this._metrics.turnCount++;
1093
+ }
1094
+ recordToolCall() {
1095
+ this._metrics.toolCallCount++;
1096
+ }
1097
+ recordToolError(err) {
1098
+ addMetricError(this._metrics, err);
1099
+ }
1100
+ recordBargeIn() {
1101
+ this._metrics.bargeInCount++;
1102
+ }
1103
+ recordEndReason(reason) {
1104
+ this._metrics.endReason = reason;
1105
+ }
1048
1106
  /** Bind transport functions (called internally by the agent). */
1049
1107
  _bindTransport(send, clear, hangup, sendDtmf, isConnected) {
1050
1108
  this._sendAudioFn = send;
@@ -1426,7 +1484,7 @@ var ClawOpsAgent = class {
1426
1484
  constructor(options) {
1427
1485
  this._apiKey = options.apiKey ?? process.env["CLAWOPS_API_KEY"] ?? "";
1428
1486
  this._accountId = options.accountId ?? process.env["CLAWOPS_ACCOUNT_ID"] ?? "";
1429
- this._baseUrl = options.baseUrl ?? chunk6IQN5RQD_cjs.DEFAULT_BASE_URL;
1487
+ this._baseUrl = options.baseUrl ?? chunkR742GPVL_cjs.DEFAULT_BASE_URL;
1430
1488
  this._fromNumber = options.from;
1431
1489
  this._session = options.session;
1432
1490
  this._recording = options.recording ?? false;
@@ -1451,7 +1509,7 @@ var ClawOpsAgent = class {
1451
1509
  tool(nameOrTool, description, parameters, handler) {
1452
1510
  if (typeof nameOrTool === "string") {
1453
1511
  if (!description || !parameters || !handler) {
1454
- throw new chunk6IQN5RQD_cjs.AgentError(
1512
+ throw new chunkR742GPVL_cjs.AgentError(
1455
1513
  "tool(name, description, parameters, handler) requires all arguments."
1456
1514
  );
1457
1515
  }
@@ -1487,10 +1545,10 @@ var ClawOpsAgent = class {
1487
1545
  async connect() {
1488
1546
  if (this._controlWs) return;
1489
1547
  if (!this._apiKey) {
1490
- throw new chunk6IQN5RQD_cjs.AgentError("API key is required. Set CLAWOPS_API_KEY or pass apiKey option.");
1548
+ throw new chunkR742GPVL_cjs.AgentError("API key is required. Set CLAWOPS_API_KEY or pass apiKey option.");
1491
1549
  }
1492
1550
  if (!this._accountId) {
1493
- throw new chunk6IQN5RQD_cjs.AgentError(
1551
+ throw new chunkR742GPVL_cjs.AgentError(
1494
1552
  "Account ID is required. Set CLAWOPS_ACCOUNT_ID or pass accountId option."
1495
1553
  );
1496
1554
  }
@@ -1509,8 +1567,12 @@ var ClawOpsAgent = class {
1509
1567
  try {
1510
1568
  await this._controlWs.connect();
1511
1569
  await this._controlWs.waitConnected();
1570
+ try {
1571
+ this._controlWs.send({ event: "agent.hello", sdk: getSdkInfo() });
1572
+ } catch {
1573
+ }
1512
1574
  } catch (err) {
1513
- throw new chunk6IQN5RQD_cjs.AgentConnectionError(
1575
+ throw new chunkR742GPVL_cjs.AgentConnectionError(
1514
1576
  `Failed to connect to ClawOps: ${err instanceof Error ? err.message : String(err)}`
1515
1577
  );
1516
1578
  }
@@ -1561,7 +1623,7 @@ var ClawOpsAgent = class {
1561
1623
  });
1562
1624
  if (resp.status !== 201) {
1563
1625
  const error = await resp.json();
1564
- throw new chunk6IQN5RQD_cjs.AgentError(`\uBC1C\uC2E0 \uC2E4\uD328 (${resp.status}): ${error["error"] ?? ""}`);
1626
+ throw new chunkR742GPVL_cjs.AgentError(`\uBC1C\uC2E0 \uC2E4\uD328 (${resp.status}): ${error["error"] ?? ""}`);
1565
1627
  }
1566
1628
  const data = await resp.json();
1567
1629
  const callSession = new CallSession({
@@ -1722,9 +1784,11 @@ var ClawOpsAgent = class {
1722
1784
  session._bindTransport(
1723
1785
  (audio) => {
1724
1786
  mediaWs.sendAudio(audio.toString("base64"));
1787
+ session.recordFirstResponse();
1725
1788
  },
1726
1789
  () => {
1727
1790
  mediaWs.sendClear();
1791
+ session.recordBargeIn();
1728
1792
  },
1729
1793
  async () => {
1730
1794
  await mediaWs.flush();
@@ -1775,10 +1839,22 @@ var ClawOpsAgent = class {
1775
1839
  await mediaWs.connect(mediaWsUrl, this._apiKey);
1776
1840
  this._log.info("Media stream started: %s", session.callId);
1777
1841
  await sessionHandler.start(session, sessionTools);
1842
+ const telemetry = sessionHandler.getTelemetry?.() ?? null;
1843
+ if (telemetry) {
1844
+ telemetry.toolCount = sessionTools?.size ?? 0;
1845
+ telemetry.mcpServerCount = this._mcpServers?.length ?? 0;
1846
+ telemetry.builtinTools = this._builtinTools ? [...this._builtinTools].map((t) => t.toString()) : [];
1847
+ telemetry.recordingEnabled = this._recording;
1848
+ try {
1849
+ this._controlWs.send({ event: "call.telemetry", callId: session.callId, telemetry });
1850
+ } catch {
1851
+ }
1852
+ }
1778
1853
  await session.wait();
1779
1854
  await sessionHandler.stop();
1780
1855
  } catch (err) {
1781
1856
  this._log.error({ err }, "Call session error: %s", session.callId);
1857
+ session.recordEndReason("error");
1782
1858
  } finally {
1783
1859
  if (mcpClients.length > 0) {
1784
1860
  sessionTools.clearMcpTools();
@@ -1790,6 +1866,13 @@ var ClawOpsAgent = class {
1790
1866
  if (recorder) {
1791
1867
  recorder.stop();
1792
1868
  }
1869
+ if (!session.metrics.endReason) {
1870
+ session.recordEndReason(session.status === "ended" ? "user_hangup" : "agent_hangup");
1871
+ }
1872
+ try {
1873
+ this._controlWs?.send({ event: "call.metrics", callId: session.callId, metrics: session.metrics });
1874
+ } catch {
1875
+ }
1793
1876
  session._emit("call_end");
1794
1877
  session._markEnded();
1795
1878
  this._activeSessions.delete(session.callId);
@@ -1956,6 +2039,24 @@ var PipelineSession = class {
1956
2039
  setBuiltinTools(tools) {
1957
2040
  this._builtinTools = tools;
1958
2041
  }
2042
+ getTelemetry() {
2043
+ const llm = this._llm;
2044
+ const stt = this._stt;
2045
+ const tts = this._tts;
2046
+ return {
2047
+ sessionType: "pipeline",
2048
+ llm: llm.provider && llm.model ? { provider: llm.provider, model: llm.model } : null,
2049
+ stt: stt.provider && stt.model ? { provider: stt.provider, model: stt.model } : null,
2050
+ tts: tts.provider && tts.model ? { provider: tts.provider, model: tts.model } : null,
2051
+ voice: tts.voiceId ?? null,
2052
+ language: this._language,
2053
+ greetingEnabled: this._greeting,
2054
+ recordingEnabled: !!this._recorder,
2055
+ toolCount: this._tools?.size ?? 0,
2056
+ mcpServerCount: 0,
2057
+ builtinTools: []
2058
+ };
2059
+ }
1959
2060
  setLogger(logger) {
1960
2061
  this._log = logger;
1961
2062
  if ("setLogger" in this._stt && typeof this._stt.setLogger === "function") {
@@ -2102,6 +2203,7 @@ var PipelineSession = class {
2102
2203
  }
2103
2204
  }
2104
2205
  if (!this._tools) return;
2206
+ this._callSession?.recordToolCall();
2105
2207
  const result = await this._tools.call(name, args);
2106
2208
  this._conversation.push({
2107
2209
  role: "assistant",
@@ -2133,6 +2235,9 @@ var PipelineSession = class {
2133
2235
  }
2134
2236
  } catch (err) {
2135
2237
  this._log.error({ err }, "Tool call failed: %s", name);
2238
+ if (err instanceof Error) {
2239
+ this._callSession?.recordToolError(err);
2240
+ }
2136
2241
  }
2137
2242
  }
2138
2243
  async _synthesizeAndSend(text) {
@@ -2183,6 +2288,21 @@ var OpenAIRealtime = class {
2183
2288
  setBuiltinTools(tools) {
2184
2289
  this._builtinTools = tools;
2185
2290
  }
2291
+ getTelemetry() {
2292
+ return {
2293
+ sessionType: "openai_realtime",
2294
+ llm: { provider: "openai", model: this._model },
2295
+ stt: null,
2296
+ tts: null,
2297
+ voice: this._voice,
2298
+ language: this._language,
2299
+ greetingEnabled: this._greeting,
2300
+ recordingEnabled: !!this._recorder,
2301
+ toolCount: this._tools?.size ?? 0,
2302
+ mcpServerCount: 0,
2303
+ builtinTools: []
2304
+ };
2305
+ }
2186
2306
  _ws = null;
2187
2307
  _call = null;
2188
2308
  _tools = null;
@@ -2470,9 +2590,13 @@ var OpenAIRealtime = class {
2470
2590
  let result;
2471
2591
  try {
2472
2592
  const args = JSON.parse(item["arguments"] ?? "{}");
2593
+ this._call?.recordToolCall();
2473
2594
  result = await this._tools.call(funcName, args);
2474
2595
  } catch (err) {
2475
2596
  this._log.error({ err }, "Tool call failed: %s", funcName);
2597
+ if (err instanceof Error) {
2598
+ this._call?.recordToolError(err);
2599
+ }
2476
2600
  result = `Error: ${err}`;
2477
2601
  }
2478
2602
  if (controller.signal.aborted) {
@@ -2621,6 +2745,21 @@ var GeminiRealtime = class {
2621
2745
  setLogger(logger) {
2622
2746
  this._log = logger;
2623
2747
  }
2748
+ getTelemetry() {
2749
+ return {
2750
+ sessionType: "gemini_realtime",
2751
+ llm: { provider: "gemini", model: this._model },
2752
+ stt: null,
2753
+ tts: null,
2754
+ voice: this._voice,
2755
+ language: this._language,
2756
+ greetingEnabled: this._greeting,
2757
+ recordingEnabled: !!this._recorder,
2758
+ toolCount: this._tools?.size ?? 0,
2759
+ mcpServerCount: 0,
2760
+ builtinTools: []
2761
+ };
2762
+ }
2624
2763
  async start(callSession, tools) {
2625
2764
  this._call = callSession;
2626
2765
  if (tools) this._tools = tools;
@@ -2824,6 +2963,7 @@ var GeminiRealtime = class {
2824
2963
  continue;
2825
2964
  }
2826
2965
  try {
2966
+ this._call?.recordToolCall();
2827
2967
  const result = await this._tools.call(name, args);
2828
2968
  const resultStr = typeof result === "string" ? result : JSON.stringify(result);
2829
2969
  this._log.info("Tool result: %s -> %s", name, resultStr.substring(0, 200));
@@ -2834,6 +2974,9 @@ var GeminiRealtime = class {
2834
2974
  });
2835
2975
  } catch (err) {
2836
2976
  this._log.error({ err }, "Tool call failed: %s", name);
2977
+ if (err instanceof Error) {
2978
+ this._call?.recordToolError(err);
2979
+ }
2837
2980
  responses.push({
2838
2981
  id: fcId,
2839
2982
  name,
@@ -2855,6 +2998,12 @@ var GeminiRealtime = class {
2855
2998
  var DeepgramSTT = class {
2856
2999
  _options;
2857
3000
  _log = NOOP_LOGGER;
3001
+ get provider() {
3002
+ return "deepgram";
3003
+ }
3004
+ get model() {
3005
+ return this._options.model ?? "nova-3";
3006
+ }
2858
3007
  setLogger(logger) {
2859
3008
  this._log = logger;
2860
3009
  }
@@ -2976,6 +3125,15 @@ var DeepgramSTT = class {
2976
3125
  var ElevenLabsTTS = class {
2977
3126
  _options;
2978
3127
  _log = NOOP_LOGGER;
3128
+ get provider() {
3129
+ return "elevenlabs";
3130
+ }
3131
+ get model() {
3132
+ return this._options.model ?? "eleven_flash_v2_5";
3133
+ }
3134
+ get voiceId() {
3135
+ return this._options.voiceId ?? "EXAVITQu4vr4xnSDxMaL";
3136
+ }
2979
3137
  setLogger(logger) {
2980
3138
  this._log = logger;
2981
3139
  }
@@ -3128,6 +3286,12 @@ var ElevenLabsTTS = class {
3128
3286
  // src/agent/pipeline/llm/openai-llm.ts
3129
3287
  var OpenAILLM = class {
3130
3288
  _options;
3289
+ get provider() {
3290
+ return "openai";
3291
+ }
3292
+ get model() {
3293
+ return this._options.model;
3294
+ }
3131
3295
  constructor(options = {}) {
3132
3296
  this._options = {
3133
3297
  model: "gpt-4o-mini",
@@ -3214,6 +3378,12 @@ var OpenAILLM = class {
3214
3378
  // src/agent/pipeline/llm/anthropic-llm.ts
3215
3379
  var AnthropicLLM = class {
3216
3380
  _options;
3381
+ get provider() {
3382
+ return "anthropic";
3383
+ }
3384
+ get model() {
3385
+ return this._options.model;
3386
+ }
3217
3387
  constructor(options = {}) {
3218
3388
  this._options = {
3219
3389
  model: "claude-sonnet-4-6",
@@ -3319,6 +3489,12 @@ var AnthropicLLM = class {
3319
3489
  // src/agent/pipeline/llm/gemini-llm.ts
3320
3490
  var GeminiLLM = class {
3321
3491
  _options;
3492
+ get provider() {
3493
+ return "gemini";
3494
+ }
3495
+ get model() {
3496
+ return this._options.model;
3497
+ }
3322
3498
  constructor(options = {}) {
3323
3499
  this._options = {
3324
3500
  model: "gemini-2.5-flash",
@@ -3403,6 +3579,12 @@ var GeminiLLM = class {
3403
3579
  // src/agent/pipeline/llm/openai-compat-llm.ts
3404
3580
  var OpenAICompatLLM = class {
3405
3581
  _options;
3582
+ get provider() {
3583
+ return "openai_compatible";
3584
+ }
3585
+ get model() {
3586
+ return this._options.model;
3587
+ }
3406
3588
  constructor(options) {
3407
3589
  this._options = options;
3408
3590
  }
@@ -3486,6 +3668,12 @@ var OpenAICompatLLM = class {
3486
3668
  // src/agent/pipeline/llm/ollama-llm.ts
3487
3669
  var OllamaLLM = class {
3488
3670
  _inner;
3671
+ get provider() {
3672
+ return "ollama";
3673
+ }
3674
+ get model() {
3675
+ return this._inner.model;
3676
+ }
3489
3677
  constructor(options = {}) {
3490
3678
  const baseUrl = options.baseUrl ?? process.env["OLLAMA_BASE_URL"] ?? "http://localhost:11434/v1";
3491
3679
  this._inner = new OpenAICompatLLM({
@@ -3504,6 +3692,12 @@ var OllamaLLM = class {
3504
3692
  // src/agent/pipeline/llm/mistral-llm.ts
3505
3693
  var MistralLLM = class {
3506
3694
  _inner;
3695
+ get provider() {
3696
+ return "mistral";
3697
+ }
3698
+ get model() {
3699
+ return this._inner.model;
3700
+ }
3507
3701
  constructor(options = {}) {
3508
3702
  this._inner = new OpenAICompatLLM({
3509
3703
  apiKey: options.apiKey ?? process.env["MISTRAL_API_KEY"],
@@ -3521,6 +3715,12 @@ var MistralLLM = class {
3521
3715
  // src/agent/pipeline/llm/groq-llm.ts
3522
3716
  var GroqLLM = class {
3523
3717
  _inner;
3718
+ get provider() {
3719
+ return "groq";
3720
+ }
3721
+ get model() {
3722
+ return this._inner.model;
3723
+ }
3524
3724
  constructor(options = {}) {
3525
3725
  this._inner = new OpenAICompatLLM({
3526
3726
  apiKey: options.apiKey ?? process.env["GROQ_API_KEY"],
@@ -3538,6 +3738,12 @@ var GroqLLM = class {
3538
3738
  // src/agent/pipeline/llm/perplexity-llm.ts
3539
3739
  var PerplexityLLM = class {
3540
3740
  _inner;
3741
+ get provider() {
3742
+ return "perplexity";
3743
+ }
3744
+ get model() {
3745
+ return this._inner.model;
3746
+ }
3541
3747
  constructor(options = {}) {
3542
3748
  this._inner = new OpenAICompatLLM({
3543
3749
  apiKey: options.apiKey ?? process.env["PERPLEXITY_API_KEY"],
@@ -3555,6 +3761,12 @@ var PerplexityLLM = class {
3555
3761
  // src/agent/pipeline/llm/together-llm.ts
3556
3762
  var TogetherLLM = class {
3557
3763
  _inner;
3764
+ get provider() {
3765
+ return "together";
3766
+ }
3767
+ get model() {
3768
+ return this._inner.model;
3769
+ }
3558
3770
  constructor(options = {}) {
3559
3771
  this._inner = new OpenAICompatLLM({
3560
3772
  apiKey: options.apiKey ?? process.env["TOGETHER_API_KEY"],
@@ -3572,6 +3784,12 @@ var TogetherLLM = class {
3572
3784
  // src/agent/pipeline/llm/fireworks-llm.ts
3573
3785
  var FireworksLLM = class {
3574
3786
  _inner;
3787
+ get provider() {
3788
+ return "fireworks";
3789
+ }
3790
+ get model() {
3791
+ return this._inner.model;
3792
+ }
3575
3793
  constructor(options = {}) {
3576
3794
  this._inner = new OpenAICompatLLM({
3577
3795
  apiKey: options.apiKey ?? process.env["FIREWORKS_API_KEY"],
@@ -3589,6 +3807,12 @@ var FireworksLLM = class {
3589
3807
  // src/agent/pipeline/llm/deepseek-llm.ts
3590
3808
  var DeepSeekLLM = class {
3591
3809
  _inner;
3810
+ get provider() {
3811
+ return "deepseek";
3812
+ }
3813
+ get model() {
3814
+ return this._inner.model;
3815
+ }
3592
3816
  constructor(options = {}) {
3593
3817
  this._inner = new OpenAICompatLLM({
3594
3818
  apiKey: options.apiKey ?? process.env["DEEPSEEK_API_KEY"],
@@ -3606,6 +3830,12 @@ var DeepSeekLLM = class {
3606
3830
  // src/agent/pipeline/llm/xai-llm.ts
3607
3831
  var XaiLLM = class {
3608
3832
  _inner;
3833
+ get provider() {
3834
+ return "xai";
3835
+ }
3836
+ get model() {
3837
+ return this._inner.model;
3838
+ }
3609
3839
  constructor(options = {}) {
3610
3840
  this._inner = new OpenAICompatLLM({
3611
3841
  apiKey: options.apiKey ?? process.env["XAI_API_KEY"],
@@ -3654,6 +3884,7 @@ exports.GeminiLLM = GeminiLLM;
3654
3884
  exports.GeminiRealtime = GeminiRealtime;
3655
3885
  exports.GroqLLM = GroqLLM;
3656
3886
  exports.MCPClient = MCPClient;
3887
+ exports.MediaWebSocket = MediaWebSocket;
3657
3888
  exports.MistralLLM = MistralLLM;
3658
3889
  exports.OllamaLLM = OllamaLLM;
3659
3890
  exports.OpenAICompatLLM = OpenAICompatLLM;