@teamlearners/clawops 0.16.3 → 0.16.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 +62 -32
- package/dist/agent/index.cjs.map +1 -1
- package/dist/agent/index.d.cts +8 -0
- package/dist/agent/index.d.ts +8 -0
- package/dist/agent/index.js +54 -24
- package/dist/agent/index.js.map +1 -1
- package/dist/{chunk-5IYJOP2D.js → chunk-BHGDB52N.js} +3 -3
- package/dist/{chunk-5IYJOP2D.js.map → chunk-BHGDB52N.js.map} +1 -1
- package/dist/{chunk-4QQXMZ76.cjs → chunk-TFYE5DWP.cjs} +3 -3
- package/dist/{chunk-4QQXMZ76.cjs.map → chunk-TFYE5DWP.cjs.map} +1 -1
- package/dist/index.cjs +35 -35
- package/dist/index.js +2 -2
- package/package.json +1 -1
package/dist/agent/index.cjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var
|
|
3
|
+
var chunkTFYE5DWP_cjs = require('../chunk-TFYE5DWP.cjs');
|
|
4
4
|
var pino = require('pino');
|
|
5
5
|
var fs = require('fs');
|
|
6
6
|
var path = require('path');
|
|
@@ -1079,7 +1079,7 @@ var MAX_ERROR_MESSAGE_LENGTH = 200;
|
|
|
1079
1079
|
function getSdkInfo() {
|
|
1080
1080
|
return {
|
|
1081
1081
|
name: "clawops-node",
|
|
1082
|
-
version:
|
|
1082
|
+
version: chunkTFYE5DWP_cjs.VERSION,
|
|
1083
1083
|
runtime: `node/${process.versions.node}`,
|
|
1084
1084
|
os: `${process.platform}/${os__default.default.arch()}`
|
|
1085
1085
|
};
|
|
@@ -1762,7 +1762,7 @@ var ClawOpsAgent = class _ClawOpsAgent {
|
|
|
1762
1762
|
constructor(options) {
|
|
1763
1763
|
this._apiKey = options.apiKey ?? process.env["CLAWOPS_API_KEY"] ?? "";
|
|
1764
1764
|
this._accountId = options.accountId ?? process.env["CLAWOPS_ACCOUNT_ID"] ?? "";
|
|
1765
|
-
this._baseUrl = options.baseUrl ??
|
|
1765
|
+
this._baseUrl = options.baseUrl ?? chunkTFYE5DWP_cjs.DEFAULT_BASE_URL;
|
|
1766
1766
|
this._fromNumber = options.from;
|
|
1767
1767
|
this._session = options.session;
|
|
1768
1768
|
this._recording = options.recording ?? false;
|
|
@@ -1784,7 +1784,7 @@ var ClawOpsAgent = class _ClawOpsAgent {
|
|
|
1784
1784
|
}
|
|
1785
1785
|
static _validateGain(name, gain) {
|
|
1786
1786
|
if (typeof gain !== "number" || !Number.isFinite(gain) || gain < 0) {
|
|
1787
|
-
throw new
|
|
1787
|
+
throw new chunkTFYE5DWP_cjs.AgentError(`${name}=${gain} must be a finite number >= 0`);
|
|
1788
1788
|
}
|
|
1789
1789
|
return gain;
|
|
1790
1790
|
}
|
|
@@ -1798,7 +1798,7 @@ var ClawOpsAgent = class _ClawOpsAgent {
|
|
|
1798
1798
|
tool(nameOrTool, description, parameters, handler) {
|
|
1799
1799
|
if (typeof nameOrTool === "string") {
|
|
1800
1800
|
if (!description || !parameters || !handler) {
|
|
1801
|
-
throw new
|
|
1801
|
+
throw new chunkTFYE5DWP_cjs.AgentError(
|
|
1802
1802
|
"tool(name, description, parameters, handler) requires all arguments."
|
|
1803
1803
|
);
|
|
1804
1804
|
}
|
|
@@ -1834,10 +1834,10 @@ var ClawOpsAgent = class _ClawOpsAgent {
|
|
|
1834
1834
|
async connect() {
|
|
1835
1835
|
if (this._controlWs) return;
|
|
1836
1836
|
if (!this._apiKey) {
|
|
1837
|
-
throw new
|
|
1837
|
+
throw new chunkTFYE5DWP_cjs.AgentError("API key is required. Set CLAWOPS_API_KEY or pass apiKey option.");
|
|
1838
1838
|
}
|
|
1839
1839
|
if (!this._accountId) {
|
|
1840
|
-
throw new
|
|
1840
|
+
throw new chunkTFYE5DWP_cjs.AgentError(
|
|
1841
1841
|
"Account ID is required. Set CLAWOPS_ACCOUNT_ID or pass accountId option."
|
|
1842
1842
|
);
|
|
1843
1843
|
}
|
|
@@ -1861,7 +1861,7 @@ var ClawOpsAgent = class _ClawOpsAgent {
|
|
|
1861
1861
|
} catch {
|
|
1862
1862
|
}
|
|
1863
1863
|
} catch (err) {
|
|
1864
|
-
throw new
|
|
1864
|
+
throw new chunkTFYE5DWP_cjs.AgentConnectionError(
|
|
1865
1865
|
`Failed to connect to ClawOps: ${err instanceof Error ? err.message : String(err)}`
|
|
1866
1866
|
);
|
|
1867
1867
|
}
|
|
@@ -1912,7 +1912,7 @@ var ClawOpsAgent = class _ClawOpsAgent {
|
|
|
1912
1912
|
});
|
|
1913
1913
|
if (resp.status !== 201) {
|
|
1914
1914
|
const error = await resp.json();
|
|
1915
|
-
throw new
|
|
1915
|
+
throw new chunkTFYE5DWP_cjs.AgentError(`\uBC1C\uC2E0 \uC2E4\uD328 (${resp.status}): ${error["error"] ?? ""}`);
|
|
1916
1916
|
}
|
|
1917
1917
|
const data = await resp.json();
|
|
1918
1918
|
const callSession = new CallSession({
|
|
@@ -1955,9 +1955,7 @@ var ClawOpsAgent = class _ClawOpsAgent {
|
|
|
1955
1955
|
this._controlWs.send({ event: "call.accept", callId });
|
|
1956
1956
|
}
|
|
1957
1957
|
if (mediaUrl) {
|
|
1958
|
-
this.
|
|
1959
|
-
this._log.error({ err }, "Call session error: %s", callId);
|
|
1960
|
-
});
|
|
1958
|
+
this._safeStartCallSession(session, mediaUrl, callId);
|
|
1961
1959
|
}
|
|
1962
1960
|
}
|
|
1963
1961
|
_handleEnded(event) {
|
|
@@ -1991,9 +1989,7 @@ var ClawOpsAgent = class _ClawOpsAgent {
|
|
|
1991
1989
|
}
|
|
1992
1990
|
if (mediaUrl) {
|
|
1993
1991
|
this._log.info("Outbound call answered: %s -> %s (%s)", this._fromNumber, session.toNumber, callId);
|
|
1994
|
-
this.
|
|
1995
|
-
this._log.error({ err }, "Call session error: %s", callId);
|
|
1996
|
-
});
|
|
1992
|
+
this._safeStartCallSession(session, mediaUrl, callId);
|
|
1997
1993
|
}
|
|
1998
1994
|
}
|
|
1999
1995
|
_handleRinging(event) {
|
|
@@ -2037,6 +2033,32 @@ var ClawOpsAgent = class _ClawOpsAgent {
|
|
|
2037
2033
|
}
|
|
2038
2034
|
}, this._passiveDtmfDebounceMs);
|
|
2039
2035
|
}
|
|
2036
|
+
/**
|
|
2037
|
+
* _startCallSession 의 예외를 잡아 control WS 로 call.session_failed 전송한다.
|
|
2038
|
+
*
|
|
2039
|
+
* OpenAI/Gemini API 키 누락 등 session.start() 단계 실패는 media WS connect 에
|
|
2040
|
+
* 도달하지 못해 call-engine 이 30 초간 무음 통화를 유지하게 만든다. 서버에 즉시
|
|
2041
|
+
* 알려서 fail-fast 시키고 _activeSessions 에서 정리한다.
|
|
2042
|
+
*/
|
|
2043
|
+
_safeStartCallSession(session, mediaWsUrl, callId) {
|
|
2044
|
+
this._startCallSession(session, mediaWsUrl).catch((err) => {
|
|
2045
|
+
const error = err;
|
|
2046
|
+
this._log.error({ err }, "Session start failed for %s", callId);
|
|
2047
|
+
if (this._controlWs) {
|
|
2048
|
+
try {
|
|
2049
|
+
this._controlWs.send({
|
|
2050
|
+
event: "call.session_failed",
|
|
2051
|
+
callId,
|
|
2052
|
+
reason: error?.name ?? "Error",
|
|
2053
|
+
message: error?.message ?? String(err)
|
|
2054
|
+
});
|
|
2055
|
+
} catch {
|
|
2056
|
+
}
|
|
2057
|
+
}
|
|
2058
|
+
this._activeSessions.delete(callId);
|
|
2059
|
+
this._callSessions.delete(callId);
|
|
2060
|
+
});
|
|
2061
|
+
}
|
|
2040
2062
|
async _startCallSession(session, mediaWsUrl) {
|
|
2041
2063
|
await withSpan(
|
|
2042
2064
|
"clawops.call_session",
|
|
@@ -2660,6 +2682,11 @@ var OpenAIRealtime = class {
|
|
|
2660
2682
|
_holdAudioChunks = null;
|
|
2661
2683
|
constructor(options = {}) {
|
|
2662
2684
|
this._apiKey = options.apiKey ?? process.env["OPENAI_API_KEY"] ?? "";
|
|
2685
|
+
if (!this._apiKey) {
|
|
2686
|
+
throw new Error(
|
|
2687
|
+
"OpenAI API key is required. Set OPENAI_API_KEY env var or pass apiKey option."
|
|
2688
|
+
);
|
|
2689
|
+
}
|
|
2663
2690
|
this._systemPrompt = options.systemPrompt ?? "";
|
|
2664
2691
|
this._model = options.model ?? "gpt-realtime-2";
|
|
2665
2692
|
this._voice = options.voice ?? "marin";
|
|
@@ -2685,15 +2712,11 @@ var OpenAIRealtime = class {
|
|
|
2685
2712
|
this._closed = false;
|
|
2686
2713
|
this._playback = null;
|
|
2687
2714
|
this._latestMediaTs = 0;
|
|
2688
|
-
if (!this._apiKey) {
|
|
2689
|
-
throw new Error("OpenAI API key is required. Set OPENAI_API_KEY or pass apiKey option.");
|
|
2690
|
-
}
|
|
2691
2715
|
const { WebSocket } = await import('ws');
|
|
2692
2716
|
const url = `${OPENAI_REALTIME_URL}${this._model}`;
|
|
2693
2717
|
this._ws = new WebSocket(url, {
|
|
2694
2718
|
headers: {
|
|
2695
|
-
Authorization: `Bearer ${this._apiKey}
|
|
2696
|
-
"OpenAI-Beta": "realtime=v1"
|
|
2719
|
+
Authorization: `Bearer ${this._apiKey}`
|
|
2697
2720
|
}
|
|
2698
2721
|
});
|
|
2699
2722
|
return new Promise((resolve, reject) => {
|
|
@@ -2763,17 +2786,24 @@ var OpenAIRealtime = class {
|
|
|
2763
2786
|
this._send({
|
|
2764
2787
|
type: "session.update",
|
|
2765
2788
|
session: {
|
|
2766
|
-
|
|
2767
|
-
|
|
2789
|
+
type: "realtime",
|
|
2790
|
+
output_modalities: ["audio"],
|
|
2768
2791
|
instructions: this._systemPrompt,
|
|
2769
|
-
|
|
2770
|
-
|
|
2771
|
-
|
|
2772
|
-
|
|
2773
|
-
|
|
2792
|
+
audio: {
|
|
2793
|
+
input: {
|
|
2794
|
+
format: { type: "audio/pcmu" },
|
|
2795
|
+
noise_reduction: { type: "far_field" },
|
|
2796
|
+
transcription: {
|
|
2797
|
+
model: "gpt-realtime-whisper",
|
|
2798
|
+
language: this._language
|
|
2799
|
+
},
|
|
2800
|
+
turn_detection: this._turnDetection
|
|
2801
|
+
},
|
|
2802
|
+
output: {
|
|
2803
|
+
format: { type: "audio/pcmu" },
|
|
2804
|
+
voice: this._voice
|
|
2805
|
+
}
|
|
2774
2806
|
},
|
|
2775
|
-
input_audio_noise_reduction: { type: "far_field" },
|
|
2776
|
-
turn_detection: this._turnDetection,
|
|
2777
2807
|
tools: toolSchemas
|
|
2778
2808
|
}
|
|
2779
2809
|
});
|
|
@@ -2781,11 +2811,11 @@ var OpenAIRealtime = class {
|
|
|
2781
2811
|
_handleMessage(msg) {
|
|
2782
2812
|
const type = msg["type"];
|
|
2783
2813
|
switch (type) {
|
|
2784
|
-
case "response.
|
|
2814
|
+
case "response.output_audio.delta": {
|
|
2785
2815
|
this._handleAudioDelta(msg);
|
|
2786
2816
|
break;
|
|
2787
2817
|
}
|
|
2788
|
-
case "response.
|
|
2818
|
+
case "response.output_audio.done": {
|
|
2789
2819
|
if (this._playback) {
|
|
2790
2820
|
this._playback.generating = false;
|
|
2791
2821
|
if (this._playback.audioRemainder.length > 0) {
|
|
@@ -2819,7 +2849,7 @@ var OpenAIRealtime = class {
|
|
|
2819
2849
|
}
|
|
2820
2850
|
break;
|
|
2821
2851
|
}
|
|
2822
|
-
case "response.
|
|
2852
|
+
case "response.output_audio_transcript.done": {
|
|
2823
2853
|
if (this._call) {
|
|
2824
2854
|
this._call._emit("transcript", "assistant", msg["transcript"] ?? "");
|
|
2825
2855
|
}
|