@teamlearners/clawops 0.30.0 → 0.32.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/dist/agent/index.cjs +143 -74
- package/dist/agent/index.cjs.map +1 -1
- package/dist/agent/index.d.cts +15 -2
- package/dist/agent/index.d.ts +15 -2
- package/dist/agent/index.js +73 -4
- package/dist/agent/index.js.map +1 -1
- package/dist/agent/livekit/index.cjs +8 -8
- package/dist/agent/livekit/index.d.cts +1 -1
- package/dist/agent/livekit/index.d.ts +1 -1
- package/dist/agent/livekit/index.js +1 -1
- package/dist/{base-Cje1TDJi.d.cts → base-B6S9WQRN.d.cts} +21 -0
- package/dist/{base-Cje1TDJi.d.ts → base-B6S9WQRN.d.ts} +21 -0
- package/dist/{chunk-WKGTY7QT.js → chunk-4YLSDMLL.js} +10 -4
- package/dist/chunk-4YLSDMLL.js.map +1 -0
- package/dist/{chunk-IGFGK3XZ.cjs → chunk-HKXRJRAW.cjs} +3 -3
- package/dist/{chunk-IGFGK3XZ.cjs.map → chunk-HKXRJRAW.cjs.map} +1 -1
- package/dist/{chunk-622J6BJ5.js → chunk-MXJ2LM6R.js} +3 -3
- package/dist/{chunk-622J6BJ5.js.map → chunk-MXJ2LM6R.js.map} +1 -1
- package/dist/{chunk-XAOA3XFQ.cjs → chunk-QD2ZY4UP.cjs} +10 -4
- package/dist/chunk-QD2ZY4UP.cjs.map +1 -0
- package/dist/index.cjs +35 -35
- package/dist/index.js +2 -2
- package/package.json +1 -1
- package/dist/chunk-WKGTY7QT.js.map +0 -1
- package/dist/chunk-XAOA3XFQ.cjs.map +0 -1
package/dist/agent/index.d.cts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { F as FunctionTool, S as Session, B as BuiltinTool, C as CallSession, a as STT, L as LLM, T as TTS, b as ToolRegistry, c as SessionTelemetry, d as SpeechEvent, e as ConversationMessage, f as LLMChunk } from '../base-
|
|
2
|
-
export { g as CallDirection, h as CallStatus, O as OpenAIToolDefinition, i as ToolCallRequest, j as functionTool, z as zodToToolParams } from '../base-
|
|
1
|
+
import { F as FunctionTool, S as Session, B as BuiltinTool, C as CallSession, a as STT, L as LLM, T as TTS, b as ToolRegistry, c as SessionTelemetry, d as SpeechEvent, e as ConversationMessage, f as LLMChunk } from '../base-B6S9WQRN.cjs';
|
|
2
|
+
export { g as CallDirection, h as CallStatus, O as OpenAIToolDefinition, i as ToolCallRequest, j as functionTool, z as zodToToolParams } from '../base-B6S9WQRN.cjs';
|
|
3
3
|
import { Logger } from 'pino';
|
|
4
4
|
export { Logger } from 'pino';
|
|
5
5
|
import 'zod';
|
|
@@ -164,6 +164,8 @@ declare class ClawOpsAgent {
|
|
|
164
164
|
private _recording;
|
|
165
165
|
private _recordingPath;
|
|
166
166
|
private _activeSessions;
|
|
167
|
+
/** 미디어 정리를 마친 통화가 서버 종료 프레임을 기다리는 자리. callId → resolve. */
|
|
168
|
+
private _terminalWaiters;
|
|
167
169
|
private _builtinTools;
|
|
168
170
|
private _passiveDtmfDebounceMs;
|
|
169
171
|
private _passiveDtmfBuffer;
|
|
@@ -224,6 +226,17 @@ declare class ClawOpsAgent {
|
|
|
224
226
|
machineDetection?: 'Enable' | 'Hangup';
|
|
225
227
|
}): Promise<CallSession>;
|
|
226
228
|
private _handleIncoming;
|
|
229
|
+
/**
|
|
230
|
+
* 서버 종료 프레임을 짧게 기다린다. 미디어 정리 직후에만 부른다.
|
|
231
|
+
*
|
|
232
|
+
* 왜 기다리나: `call_end` 는 인바운드 사용자가 통화 결과를 받는 **유일한 통로**인데, 서버는
|
|
233
|
+
* 미디어 WS 를 먼저 닫고 자원 정리를 마친 뒤에야 control 종료 프레임을 보낸다. 안 기다리면
|
|
234
|
+
* 그 핸들러 안에서 `endedDuration` 은 영영 null 이다.
|
|
235
|
+
*
|
|
236
|
+
* 상한을 다 쓰는 경우는 제어 연결이 죽어 프레임이 아예 안 오는 때뿐이다 — 그 프레임은
|
|
237
|
+
* 예전부터 늘 오던 것이고(이번에 바뀐 건 내용뿐), 정상 경로에서는 밀리초 안에 풀린다.
|
|
238
|
+
*/
|
|
239
|
+
private _awaitServerTerminal;
|
|
227
240
|
private _handleEnded;
|
|
228
241
|
/**
|
|
229
242
|
* Drop prewarm bookkeeping for a callId. Used on hangup/failure paths.
|
package/dist/agent/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { F as FunctionTool, S as Session, B as BuiltinTool, C as CallSession, a as STT, L as LLM, T as TTS, b as ToolRegistry, c as SessionTelemetry, d as SpeechEvent, e as ConversationMessage, f as LLMChunk } from '../base-
|
|
2
|
-
export { g as CallDirection, h as CallStatus, O as OpenAIToolDefinition, i as ToolCallRequest, j as functionTool, z as zodToToolParams } from '../base-
|
|
1
|
+
import { F as FunctionTool, S as Session, B as BuiltinTool, C as CallSession, a as STT, L as LLM, T as TTS, b as ToolRegistry, c as SessionTelemetry, d as SpeechEvent, e as ConversationMessage, f as LLMChunk } from '../base-B6S9WQRN.js';
|
|
2
|
+
export { g as CallDirection, h as CallStatus, O as OpenAIToolDefinition, i as ToolCallRequest, j as functionTool, z as zodToToolParams } from '../base-B6S9WQRN.js';
|
|
3
3
|
import { Logger } from 'pino';
|
|
4
4
|
export { Logger } from 'pino';
|
|
5
5
|
import 'zod';
|
|
@@ -164,6 +164,8 @@ declare class ClawOpsAgent {
|
|
|
164
164
|
private _recording;
|
|
165
165
|
private _recordingPath;
|
|
166
166
|
private _activeSessions;
|
|
167
|
+
/** 미디어 정리를 마친 통화가 서버 종료 프레임을 기다리는 자리. callId → resolve. */
|
|
168
|
+
private _terminalWaiters;
|
|
167
169
|
private _builtinTools;
|
|
168
170
|
private _passiveDtmfDebounceMs;
|
|
169
171
|
private _passiveDtmfBuffer;
|
|
@@ -224,6 +226,17 @@ declare class ClawOpsAgent {
|
|
|
224
226
|
machineDetection?: 'Enable' | 'Hangup';
|
|
225
227
|
}): Promise<CallSession>;
|
|
226
228
|
private _handleIncoming;
|
|
229
|
+
/**
|
|
230
|
+
* 서버 종료 프레임을 짧게 기다린다. 미디어 정리 직후에만 부른다.
|
|
231
|
+
*
|
|
232
|
+
* 왜 기다리나: `call_end` 는 인바운드 사용자가 통화 결과를 받는 **유일한 통로**인데, 서버는
|
|
233
|
+
* 미디어 WS 를 먼저 닫고 자원 정리를 마친 뒤에야 control 종료 프레임을 보낸다. 안 기다리면
|
|
234
|
+
* 그 핸들러 안에서 `endedDuration` 은 영영 null 이다.
|
|
235
|
+
*
|
|
236
|
+
* 상한을 다 쓰는 경우는 제어 연결이 죽어 프레임이 아예 안 오는 때뿐이다 — 그 프레임은
|
|
237
|
+
* 예전부터 늘 오던 것이고(이번에 바뀐 건 내용뿐), 정상 경로에서는 밀리초 안에 풀린다.
|
|
238
|
+
*/
|
|
239
|
+
private _awaitServerTerminal;
|
|
227
240
|
private _handleEnded;
|
|
228
241
|
/**
|
|
229
242
|
* Drop prewarm bookkeeping for a callId. Used on hangup/failure paths.
|
package/dist/agent/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { DEFAULT_BASE_URL, AgentError, AgentConnectionError, VERSION } from '../chunk-
|
|
2
|
-
import { NOOP_LOGGER, resolveBuiltinTools, createAgentLogger, createPipelineLogger, BufferingCall, attachBuffered, ulawToPcm16, resamplePcm16, getBuiltinToolSchemas, BUILTIN_TOOL_NAMES, executeBuiltinTool, CALL_NOT_READY_RESULT, pcm16ToUlaw, applyUlawGain } from '../chunk-
|
|
3
|
-
export { BUILTIN_TOOL_NAMES, BuiltinTool, DECODE_TABLE, createAgentLogger, createPipelineLogger, executeBuiltinTool, getBuiltinToolSchemas, isBuiltinTool, pcm16ToUlaw, resamplePcm16, ulawToPcm16 } from '../chunk-
|
|
1
|
+
import { DEFAULT_BASE_URL, AgentError, AgentConnectionError, VERSION } from '../chunk-MXJ2LM6R.js';
|
|
2
|
+
import { NOOP_LOGGER, resolveBuiltinTools, createAgentLogger, createPipelineLogger, BufferingCall, attachBuffered, ulawToPcm16, resamplePcm16, getBuiltinToolSchemas, BUILTIN_TOOL_NAMES, executeBuiltinTool, CALL_NOT_READY_RESULT, pcm16ToUlaw, applyUlawGain } from '../chunk-4YLSDMLL.js';
|
|
3
|
+
export { BUILTIN_TOOL_NAMES, BuiltinTool, DECODE_TABLE, createAgentLogger, createPipelineLogger, executeBuiltinTool, getBuiltinToolSchemas, isBuiltinTool, pcm16ToUlaw, resamplePcm16, ulawToPcm16 } from '../chunk-4YLSDMLL.js';
|
|
4
4
|
import * as fs from 'fs';
|
|
5
5
|
import * as path from 'path';
|
|
6
6
|
import os from 'os';
|
|
@@ -740,6 +740,7 @@ var CallSession = class {
|
|
|
740
740
|
metadata;
|
|
741
741
|
_status;
|
|
742
742
|
_endedStatus = null;
|
|
743
|
+
_endedDuration = null;
|
|
743
744
|
_sendAudioFn = null;
|
|
744
745
|
_clearAudioFn = null;
|
|
745
746
|
_hangupFn = null;
|
|
@@ -799,9 +800,22 @@ var CallSession = class {
|
|
|
799
800
|
get endedStatus() {
|
|
800
801
|
return this._endedStatus;
|
|
801
802
|
}
|
|
803
|
+
/**
|
|
804
|
+
* 서버가 확정한 통화 시간(초). 통화가 끝나기 전에는 `null`.
|
|
805
|
+
*
|
|
806
|
+
* ⚠️ `duration` 과 다르다 — 그쪽은 SDK 가 로컬 시계로 재는 **경과 시간**이라 통화 중에도
|
|
807
|
+
* 읽히고, 세션이 붙기 전후의 오차를 포함한다. 기록·정산에 쓸 값은 이쪽이다.
|
|
808
|
+
*/
|
|
809
|
+
get endedDuration() {
|
|
810
|
+
return this._endedDuration;
|
|
811
|
+
}
|
|
802
812
|
get duration() {
|
|
803
813
|
return (Date.now() - this.startTime.getTime()) / 1e3;
|
|
804
814
|
}
|
|
815
|
+
/** @internal 서버가 통보한 통화 시간을 확정한다. */
|
|
816
|
+
_setEndedDuration(seconds) {
|
|
817
|
+
this._endedDuration = seconds;
|
|
818
|
+
}
|
|
805
819
|
get metrics() {
|
|
806
820
|
return this._metrics;
|
|
807
821
|
}
|
|
@@ -929,11 +943,27 @@ var CallSession = class {
|
|
|
929
943
|
* directly to a SIP endpoint without going through the PSTN carrier. Requires the
|
|
930
944
|
* account to have an active `sip_trunk` add-on; otherwise the transfer fails and
|
|
931
945
|
* the call continues with the AI (result `{ status: 'failed', ... }`).
|
|
946
|
+
*
|
|
947
|
+
* 전환받는 쪽에 표시되는 발신번호는 기본이 **계정 보유번호**(인바운드면 착신 070)다.
|
|
948
|
+
*
|
|
949
|
+
* `callerIdMode: 'original'` 은 인바운드 통화의 **원 발신자 번호를 승계하려는 선호**다.
|
|
950
|
+
* 승계할 수 없는 통화(KCT 직결 인입이 아니거나 국내 번호로 정규화되지 않는 발신번호)면
|
|
951
|
+
* 조용히 계정 보유번호로 내려앉고 **전환은 그대로 성사된다**.
|
|
952
|
+
*
|
|
953
|
+
* `callerId` 는 번호를 직접 주는 **지시**라 성격이 다르다. 허용 범위(계정 보유번호 또는
|
|
954
|
+
* KCT 직결 인입의 원 발신자)를 벗어나면 전환 자체가 실패한다. 둘 다 주면 `callerId` 가
|
|
955
|
+
* 이기고 `callerIdMode` 는 무시된다 — 우선순위 판단은 서버가 한다.
|
|
932
956
|
*/
|
|
933
957
|
async transfer(to, options) {
|
|
934
958
|
if (!this._transferFn) {
|
|
935
959
|
throw new Error("transfer not available");
|
|
936
960
|
}
|
|
961
|
+
const callerIdMode = options?.callerIdMode;
|
|
962
|
+
if (callerIdMode !== void 0 && callerIdMode !== "account" && callerIdMode !== "original") {
|
|
963
|
+
throw new Error(
|
|
964
|
+
`callerIdMode must be 'account' or 'original', got ${JSON.stringify(callerIdMode)}`
|
|
965
|
+
);
|
|
966
|
+
}
|
|
937
967
|
return this._transferFn({
|
|
938
968
|
to,
|
|
939
969
|
destinationType: options?.destinationType ?? "pstn",
|
|
@@ -943,7 +973,9 @@ var CallSession = class {
|
|
|
943
973
|
whisper: options?.whisper ?? null,
|
|
944
974
|
context: options?.context ?? null,
|
|
945
975
|
callerId: options?.callerId ?? null,
|
|
946
|
-
timeout: options?.timeout ?? 30
|
|
976
|
+
timeout: options?.timeout ?? 30,
|
|
977
|
+
// 안 주면 키를 붙이지 않는다 — 구 서버와 기존 동작을 그대로 둔다(additive).
|
|
978
|
+
...callerIdMode !== void 0 ? { callerIdMode } : {}
|
|
947
979
|
});
|
|
948
980
|
}
|
|
949
981
|
/** Register an event handler. */
|
|
@@ -1373,6 +1405,7 @@ var ATTR_CALL_DIRECTION = "clawops.call.direction";
|
|
|
1373
1405
|
var ATTR_AGENT_ID = "clawops.agent.id";
|
|
1374
1406
|
|
|
1375
1407
|
// src/agent/agent.ts
|
|
1408
|
+
var TERMINAL_FRAME_GRACE_MS = 2e3;
|
|
1376
1409
|
var ClawOpsAgent = class _ClawOpsAgent {
|
|
1377
1410
|
_apiKey;
|
|
1378
1411
|
_accountId;
|
|
@@ -1386,6 +1419,8 @@ var ClawOpsAgent = class _ClawOpsAgent {
|
|
|
1386
1419
|
_recording;
|
|
1387
1420
|
_recordingPath;
|
|
1388
1421
|
_activeSessions = /* @__PURE__ */ new Map();
|
|
1422
|
+
/** 미디어 정리를 마친 통화가 서버 종료 프레임을 기다리는 자리. callId → resolve. */
|
|
1423
|
+
_terminalWaiters = /* @__PURE__ */ new Map();
|
|
1389
1424
|
_builtinTools;
|
|
1390
1425
|
_passiveDtmfDebounceMs;
|
|
1391
1426
|
_passiveDtmfBuffer = [];
|
|
@@ -1535,6 +1570,7 @@ var ClawOpsAgent = class _ClawOpsAgent {
|
|
|
1535
1570
|
this._controlWs.close();
|
|
1536
1571
|
this._controlWs = null;
|
|
1537
1572
|
}
|
|
1573
|
+
for (const wake of [...this._terminalWaiters.values()]) wake();
|
|
1538
1574
|
for (const session of this._activeSessions.values()) {
|
|
1539
1575
|
session._markEnded();
|
|
1540
1576
|
}
|
|
@@ -1622,12 +1658,43 @@ var ClawOpsAgent = class _ClawOpsAgent {
|
|
|
1622
1658
|
this._safeStartCallSession(session, mediaUrl, callId);
|
|
1623
1659
|
}
|
|
1624
1660
|
}
|
|
1661
|
+
/**
|
|
1662
|
+
* 서버 종료 프레임을 짧게 기다린다. 미디어 정리 직후에만 부른다.
|
|
1663
|
+
*
|
|
1664
|
+
* 왜 기다리나: `call_end` 는 인바운드 사용자가 통화 결과를 받는 **유일한 통로**인데, 서버는
|
|
1665
|
+
* 미디어 WS 를 먼저 닫고 자원 정리를 마친 뒤에야 control 종료 프레임을 보낸다. 안 기다리면
|
|
1666
|
+
* 그 핸들러 안에서 `endedDuration` 은 영영 null 이다.
|
|
1667
|
+
*
|
|
1668
|
+
* 상한을 다 쓰는 경우는 제어 연결이 죽어 프레임이 아예 안 오는 때뿐이다 — 그 프레임은
|
|
1669
|
+
* 예전부터 늘 오던 것이고(이번에 바뀐 건 내용뿐), 정상 경로에서는 밀리초 안에 풀린다.
|
|
1670
|
+
*/
|
|
1671
|
+
async _awaitServerTerminal(session) {
|
|
1672
|
+
if (session.endedDuration !== null) return;
|
|
1673
|
+
await new Promise((resolve) => {
|
|
1674
|
+
const done = () => {
|
|
1675
|
+
clearTimeout(timer);
|
|
1676
|
+
this._terminalWaiters.delete(session.callId);
|
|
1677
|
+
resolve();
|
|
1678
|
+
};
|
|
1679
|
+
const timer = setTimeout(() => {
|
|
1680
|
+
this._log.debug(
|
|
1681
|
+
"\uC11C\uBC84 \uC885\uB8CC \uD504\uB808\uC784\uC774 %dms \uC548\uC5D0 \uC624\uC9C0 \uC54A\uC558\uB2E4 \u2014 endedDuration \uC5C6\uC774 \uC9C4\uD589: %s",
|
|
1682
|
+
TERMINAL_FRAME_GRACE_MS,
|
|
1683
|
+
session.callId
|
|
1684
|
+
);
|
|
1685
|
+
done();
|
|
1686
|
+
}, TERMINAL_FRAME_GRACE_MS);
|
|
1687
|
+
this._terminalWaiters.set(session.callId, done);
|
|
1688
|
+
});
|
|
1689
|
+
}
|
|
1625
1690
|
_handleEnded(event) {
|
|
1626
1691
|
const callId = event["callId"];
|
|
1627
1692
|
const status = event["status"] || "completed";
|
|
1693
|
+
const endedDuration = event["duration"];
|
|
1628
1694
|
const session = this._activeSessions.get(callId);
|
|
1629
1695
|
if (session) {
|
|
1630
1696
|
this._log.info("Call ended (server): %s (status=%s)", callId, status);
|
|
1697
|
+
if (typeof endedDuration === "number") session._setEndedDuration(endedDuration);
|
|
1631
1698
|
if (status !== "completed") {
|
|
1632
1699
|
this._log.info("Outbound call not connected: %s (%s)", callId, status);
|
|
1633
1700
|
session._emit("call_failed", status);
|
|
@@ -1635,6 +1702,7 @@ var ClawOpsAgent = class _ClawOpsAgent {
|
|
|
1635
1702
|
session._markEnded(status);
|
|
1636
1703
|
this._activeSessions.delete(callId);
|
|
1637
1704
|
}
|
|
1705
|
+
this._terminalWaiters.get(callId)?.();
|
|
1638
1706
|
void this._cleanupPrewarm(callId);
|
|
1639
1707
|
}
|
|
1640
1708
|
/**
|
|
@@ -1989,6 +2057,7 @@ var ClawOpsAgent = class _ClawOpsAgent {
|
|
|
1989
2057
|
this._controlWs?.send({ event: "call.metrics", callId: session.callId, metrics: session.metrics });
|
|
1990
2058
|
} catch {
|
|
1991
2059
|
}
|
|
2060
|
+
await this._awaitServerTerminal(session);
|
|
1992
2061
|
session._emit("call_end");
|
|
1993
2062
|
session._markEnded();
|
|
1994
2063
|
this._activeSessions.delete(session.callId);
|