@teamlearners/clawops 0.24.0 → 0.25.1
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 +208 -90
- package/dist/agent/index.cjs.map +1 -1
- package/dist/agent/index.d.cts +31 -2
- package/dist/agent/index.d.ts +31 -2
- package/dist/agent/index.js +145 -27
- 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-Z-uZdd27.d.cts → base-Cje1TDJi.d.cts} +19 -2
- package/dist/{base-Z-uZdd27.d.ts → base-Cje1TDJi.d.ts} +19 -2
- package/dist/{chunk-7P2HGIPS.js → chunk-2QE6GHWC.js} +3 -3
- package/dist/{chunk-7P2HGIPS.js.map → chunk-2QE6GHWC.js.map} +1 -1
- package/dist/{chunk-LUA53NPZ.cjs → chunk-M4QUDFGP.cjs} +3 -3
- package/dist/{chunk-LUA53NPZ.cjs.map → chunk-M4QUDFGP.cjs.map} +1 -1
- package/dist/{chunk-2BAYUQ7O.js → chunk-WKGTY7QT.js} +4 -3
- package/dist/chunk-WKGTY7QT.js.map +1 -0
- package/dist/{chunk-U4UFTVLX.cjs → chunk-XAOA3XFQ.cjs} +4 -2
- package/dist/chunk-XAOA3XFQ.cjs.map +1 -0
- package/dist/index.cjs +52 -36
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +21 -3
- package/dist/index.d.ts +21 -3
- package/dist/index.js +19 -3
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/dist/chunk-2BAYUQ7O.js.map +0 -1
- package/dist/chunk-U4UFTVLX.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-Cje1TDJi.cjs';
|
|
2
|
+
export { g as CallDirection, h as CallStatus, O as OpenAIToolDefinition, i as ToolCallRequest, j as functionTool, z as zodToToolParams } from '../base-Cje1TDJi.cjs';
|
|
3
3
|
import { Logger } from 'pino';
|
|
4
4
|
export { Logger } from 'pino';
|
|
5
5
|
import 'zod';
|
|
@@ -242,6 +242,14 @@ declare class ClawOpsAgent {
|
|
|
242
242
|
* multiple times — only the first invocation starts the task. Failures are
|
|
243
243
|
* recorded in _prewarmFailed so the call-session path can fall back to start().
|
|
244
244
|
*/
|
|
245
|
+
/**
|
|
246
|
+
* 세션에 콜별 의존성(도구·내장도구·hold audio·recorder·logger)을 주입한다.
|
|
247
|
+
*
|
|
248
|
+
* prewarm 과 _startCallSession 양쪽에서 부른다. prewarm 은 세션이 LLM 에 보낼
|
|
249
|
+
* tool 스키마를 그 시점에 확정하므로(OpenAI session.update / Gemini live connect
|
|
250
|
+
* config), prewarm **전에** 최소 한 번은 도구가 들어가 있어야 한다.
|
|
251
|
+
*/
|
|
252
|
+
private _injectSessionDeps;
|
|
245
253
|
private _startPrewarm;
|
|
246
254
|
private _handleRinging;
|
|
247
255
|
private _handleFailed;
|
|
@@ -533,6 +541,11 @@ declare class OpenAIRealtime implements Session {
|
|
|
533
541
|
private _greeting;
|
|
534
542
|
private _log;
|
|
535
543
|
private _builtinTools;
|
|
544
|
+
/**
|
|
545
|
+
* 마지막 session.update 로 LLM 에 알린 tool 이름들. attach() 가 이 값과 현재
|
|
546
|
+
* registry 를 비교해 달라졌을 때만(=MCP 도구 등 뒤늦게 붙은 경우) 재전송한다.
|
|
547
|
+
*/
|
|
548
|
+
private _sentToolNames;
|
|
536
549
|
setLogger(logger: Logger): void;
|
|
537
550
|
setBuiltinTools(tools: Set<BuiltinTool>): void;
|
|
538
551
|
getTelemetry(): SessionTelemetry;
|
|
@@ -562,6 +575,15 @@ declare class OpenAIRealtime implements Session {
|
|
|
562
575
|
feedDtmf(digits: string): Promise<void>;
|
|
563
576
|
feedAudio(audio: Buffer, timestamp?: number): void;
|
|
564
577
|
stop(): Promise<void>;
|
|
578
|
+
/** Current tool schemas: user tools + builtin tools (flat realtime format). */
|
|
579
|
+
private _currentToolSchemas;
|
|
580
|
+
/**
|
|
581
|
+
* prewarm 이후 도구가 바뀌었으면 session.update 로 재전송한다.
|
|
582
|
+
*
|
|
583
|
+
* MCP 도구는 통화 시작 시점에 registry 에 붙으므로 prewarm 시점의 스키마에는
|
|
584
|
+
* 없다. 여기서 맞춰주지 않으면 발신 통화에서 MCP 도구를 영영 못 쓴다.
|
|
585
|
+
*/
|
|
586
|
+
private _resyncTools;
|
|
565
587
|
private _sendSessionUpdate;
|
|
566
588
|
private _handleMessage;
|
|
567
589
|
private _handleAudioDelta;
|
|
@@ -595,6 +617,13 @@ interface GeminiRealtimeOptions {
|
|
|
595
617
|
realtimeInputConfig?: Record<string, unknown>;
|
|
596
618
|
}
|
|
597
619
|
declare class GeminiRealtime implements Session {
|
|
620
|
+
/**
|
|
621
|
+
* Gemini Live 는 connect 시점의 config 로 도구가 고정되어 세션 도중 변경할 수 없다.
|
|
622
|
+
*
|
|
623
|
+
* 통화 시작 시점에야 붙는 MCP 도구는 prewarm 된 세션에 넣을 방법이 없으므로,
|
|
624
|
+
* `ClawOpsAgent` 가 MCP 서버 설정과 이 플래그를 보고 prewarm 을 건너뛴다.
|
|
625
|
+
*/
|
|
626
|
+
readonly toolsFrozenAfterPrewarm = true;
|
|
598
627
|
private _apiKey;
|
|
599
628
|
private _systemPrompt;
|
|
600
629
|
private _model;
|
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-Cje1TDJi.js';
|
|
2
|
+
export { g as CallDirection, h as CallStatus, O as OpenAIToolDefinition, i as ToolCallRequest, j as functionTool, z as zodToToolParams } from '../base-Cje1TDJi.js';
|
|
3
3
|
import { Logger } from 'pino';
|
|
4
4
|
export { Logger } from 'pino';
|
|
5
5
|
import 'zod';
|
|
@@ -242,6 +242,14 @@ declare class ClawOpsAgent {
|
|
|
242
242
|
* multiple times — only the first invocation starts the task. Failures are
|
|
243
243
|
* recorded in _prewarmFailed so the call-session path can fall back to start().
|
|
244
244
|
*/
|
|
245
|
+
/**
|
|
246
|
+
* 세션에 콜별 의존성(도구·내장도구·hold audio·recorder·logger)을 주입한다.
|
|
247
|
+
*
|
|
248
|
+
* prewarm 과 _startCallSession 양쪽에서 부른다. prewarm 은 세션이 LLM 에 보낼
|
|
249
|
+
* tool 스키마를 그 시점에 확정하므로(OpenAI session.update / Gemini live connect
|
|
250
|
+
* config), prewarm **전에** 최소 한 번은 도구가 들어가 있어야 한다.
|
|
251
|
+
*/
|
|
252
|
+
private _injectSessionDeps;
|
|
245
253
|
private _startPrewarm;
|
|
246
254
|
private _handleRinging;
|
|
247
255
|
private _handleFailed;
|
|
@@ -533,6 +541,11 @@ declare class OpenAIRealtime implements Session {
|
|
|
533
541
|
private _greeting;
|
|
534
542
|
private _log;
|
|
535
543
|
private _builtinTools;
|
|
544
|
+
/**
|
|
545
|
+
* 마지막 session.update 로 LLM 에 알린 tool 이름들. attach() 가 이 값과 현재
|
|
546
|
+
* registry 를 비교해 달라졌을 때만(=MCP 도구 등 뒤늦게 붙은 경우) 재전송한다.
|
|
547
|
+
*/
|
|
548
|
+
private _sentToolNames;
|
|
536
549
|
setLogger(logger: Logger): void;
|
|
537
550
|
setBuiltinTools(tools: Set<BuiltinTool>): void;
|
|
538
551
|
getTelemetry(): SessionTelemetry;
|
|
@@ -562,6 +575,15 @@ declare class OpenAIRealtime implements Session {
|
|
|
562
575
|
feedDtmf(digits: string): Promise<void>;
|
|
563
576
|
feedAudio(audio: Buffer, timestamp?: number): void;
|
|
564
577
|
stop(): Promise<void>;
|
|
578
|
+
/** Current tool schemas: user tools + builtin tools (flat realtime format). */
|
|
579
|
+
private _currentToolSchemas;
|
|
580
|
+
/**
|
|
581
|
+
* prewarm 이후 도구가 바뀌었으면 session.update 로 재전송한다.
|
|
582
|
+
*
|
|
583
|
+
* MCP 도구는 통화 시작 시점에 registry 에 붙으므로 prewarm 시점의 스키마에는
|
|
584
|
+
* 없다. 여기서 맞춰주지 않으면 발신 통화에서 MCP 도구를 영영 못 쓴다.
|
|
585
|
+
*/
|
|
586
|
+
private _resyncTools;
|
|
565
587
|
private _sendSessionUpdate;
|
|
566
588
|
private _handleMessage;
|
|
567
589
|
private _handleAudioDelta;
|
|
@@ -595,6 +617,13 @@ interface GeminiRealtimeOptions {
|
|
|
595
617
|
realtimeInputConfig?: Record<string, unknown>;
|
|
596
618
|
}
|
|
597
619
|
declare class GeminiRealtime implements Session {
|
|
620
|
+
/**
|
|
621
|
+
* Gemini Live 는 connect 시점의 config 로 도구가 고정되어 세션 도중 변경할 수 없다.
|
|
622
|
+
*
|
|
623
|
+
* 통화 시작 시점에야 붙는 MCP 도구는 prewarm 된 세션에 넣을 방법이 없으므로,
|
|
624
|
+
* `ClawOpsAgent` 가 MCP 서버 설정과 이 플래그를 보고 prewarm 을 건너뛴다.
|
|
625
|
+
*/
|
|
626
|
+
readonly toolsFrozenAfterPrewarm = true;
|
|
598
627
|
private _apiKey;
|
|
599
628
|
private _systemPrompt;
|
|
600
629
|
private _model;
|
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, 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-2QE6GHWC.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-WKGTY7QT.js';
|
|
3
|
+
export { BUILTIN_TOOL_NAMES, BuiltinTool, DECODE_TABLE, createAgentLogger, createPipelineLogger, executeBuiltinTool, getBuiltinToolSchemas, isBuiltinTool, pcm16ToUlaw, resamplePcm16, ulawToPcm16 } from '../chunk-WKGTY7QT.js';
|
|
4
4
|
import * as fs from 'fs';
|
|
5
5
|
import * as path from 'path';
|
|
6
6
|
import os from 'os';
|
|
@@ -739,6 +739,7 @@ var CallSession = class {
|
|
|
739
739
|
startTime;
|
|
740
740
|
metadata;
|
|
741
741
|
_status;
|
|
742
|
+
_endedStatus = null;
|
|
742
743
|
_sendAudioFn = null;
|
|
743
744
|
_clearAudioFn = null;
|
|
744
745
|
_hangupFn = null;
|
|
@@ -787,6 +788,17 @@ var CallSession = class {
|
|
|
787
788
|
get status() {
|
|
788
789
|
return this._status;
|
|
789
790
|
}
|
|
791
|
+
/**
|
|
792
|
+
* 서버가 통보한 최종 종료 상태. 통화가 끝나기 전에는 null.
|
|
793
|
+
*
|
|
794
|
+
* `completed`(성사) / `no-answer`(벨은 울렸으나 무응답) / `busy`(통화중) /
|
|
795
|
+
* `rejected`(수신 거절) / `canceled`(응답 전 발신 측 취소) / `failed`(시스템·망 오류).
|
|
796
|
+
* `status` 는 SDK 내부 수명주기(ringing→active→ended)이므로 통화 성사 여부는
|
|
797
|
+
* 이 값이나 `call_failed` 이벤트로 판단한다.
|
|
798
|
+
*/
|
|
799
|
+
get endedStatus() {
|
|
800
|
+
return this._endedStatus;
|
|
801
|
+
}
|
|
790
802
|
get duration() {
|
|
791
803
|
return (Date.now() - this.startTime.getTime()) / 1e3;
|
|
792
804
|
}
|
|
@@ -947,8 +959,20 @@ var CallSession = class {
|
|
|
947
959
|
async wait() {
|
|
948
960
|
return this._endedPromise;
|
|
949
961
|
}
|
|
950
|
-
/**
|
|
951
|
-
|
|
962
|
+
/**
|
|
963
|
+
* Mark the session as ended (called internally).
|
|
964
|
+
*
|
|
965
|
+
* @param status 서버가 통보한 최종 상태(completed/no-answer/busy/rejected/canceled/
|
|
966
|
+
* failed). 주어지면 `endedStatus` 로 확정한다 — 상대가 받지 않은 통화를 성사된 통화와
|
|
967
|
+
* 구분하기 위함이다. 생략하면 미디어 세션 정리 경로에서의 호출이므로, 아직 종료 전일
|
|
968
|
+
* 때만 'completed' 로 채우고 이미 확정된 서버 상태는 덮어쓰지 않는다.
|
|
969
|
+
*/
|
|
970
|
+
_markEnded(status) {
|
|
971
|
+
if (status !== void 0) {
|
|
972
|
+
this._endedStatus = status;
|
|
973
|
+
} else if (this._status !== "ended") {
|
|
974
|
+
this._endedStatus = "completed";
|
|
975
|
+
}
|
|
952
976
|
this._status = "ended";
|
|
953
977
|
this._resolveEnded();
|
|
954
978
|
}
|
|
@@ -1600,10 +1624,15 @@ var ClawOpsAgent = class _ClawOpsAgent {
|
|
|
1600
1624
|
}
|
|
1601
1625
|
_handleEnded(event) {
|
|
1602
1626
|
const callId = event["callId"];
|
|
1627
|
+
const status = event["status"] || "completed";
|
|
1603
1628
|
const session = this._activeSessions.get(callId);
|
|
1604
1629
|
if (session) {
|
|
1605
|
-
this._log.info("Call ended (server): %s", callId);
|
|
1606
|
-
|
|
1630
|
+
this._log.info("Call ended (server): %s (status=%s)", callId, status);
|
|
1631
|
+
if (status !== "completed") {
|
|
1632
|
+
this._log.info("Outbound call not connected: %s (%s)", callId, status);
|
|
1633
|
+
session._emit("call_failed", status);
|
|
1634
|
+
}
|
|
1635
|
+
session._markEnded(status);
|
|
1607
1636
|
this._activeSessions.delete(callId);
|
|
1608
1637
|
}
|
|
1609
1638
|
void this._cleanupPrewarm(callId);
|
|
@@ -1668,10 +1697,43 @@ var ClawOpsAgent = class _ClawOpsAgent {
|
|
|
1668
1697
|
* multiple times — only the first invocation starts the task. Failures are
|
|
1669
1698
|
* recorded in _prewarmFailed so the call-session path can fall back to start().
|
|
1670
1699
|
*/
|
|
1700
|
+
/**
|
|
1701
|
+
* 세션에 콜별 의존성(도구·내장도구·hold audio·recorder·logger)을 주입한다.
|
|
1702
|
+
*
|
|
1703
|
+
* prewarm 과 _startCallSession 양쪽에서 부른다. prewarm 은 세션이 LLM 에 보낼
|
|
1704
|
+
* tool 스키마를 그 시점에 확정하므로(OpenAI session.update / Gemini live connect
|
|
1705
|
+
* config), prewarm **전에** 최소 한 번은 도구가 들어가 있어야 한다.
|
|
1706
|
+
*/
|
|
1707
|
+
_injectSessionDeps(tools, recorder) {
|
|
1708
|
+
const sessionHandler = this._session;
|
|
1709
|
+
if ("setToolRegistry" in sessionHandler && typeof sessionHandler.setToolRegistry === "function") {
|
|
1710
|
+
sessionHandler.setToolRegistry(tools);
|
|
1711
|
+
}
|
|
1712
|
+
if (recorder && "setRecorder" in sessionHandler && typeof sessionHandler.setRecorder === "function") {
|
|
1713
|
+
sessionHandler.setRecorder(recorder);
|
|
1714
|
+
}
|
|
1715
|
+
if ("setBuiltinTools" in sessionHandler && typeof sessionHandler.setBuiltinTools === "function") {
|
|
1716
|
+
sessionHandler.setBuiltinTools(this._builtinTools);
|
|
1717
|
+
}
|
|
1718
|
+
if ("setLogger" in sessionHandler && typeof sessionHandler.setLogger === "function") {
|
|
1719
|
+
sessionHandler.setLogger(this._isPipelineSession ? this._pipelineLog : this._log);
|
|
1720
|
+
}
|
|
1721
|
+
if (this._holdAudioChunks && "setHoldAudio" in sessionHandler && typeof sessionHandler.setHoldAudio === "function") {
|
|
1722
|
+
sessionHandler.setHoldAudio(this._holdAudioChunks);
|
|
1723
|
+
}
|
|
1724
|
+
}
|
|
1671
1725
|
_startPrewarm(callId) {
|
|
1672
1726
|
if (this._prewarmTasks.has(callId)) return;
|
|
1673
1727
|
const sessionHandler = this._session;
|
|
1674
1728
|
if (typeof sessionHandler.prewarm !== "function") return;
|
|
1729
|
+
if (this._mcpServers.length > 0 && sessionHandler.toolsFrozenAfterPrewarm) {
|
|
1730
|
+
this._log.info(
|
|
1731
|
+
{ callId },
|
|
1732
|
+
"Skipping prewarm \u2014 \uC138\uC158\uC774 \uC5F0\uACB0 \uD6C4 \uB3C4\uAD6C \uBCC0\uACBD\uC744 \uC9C0\uC6D0\uD558\uC9C0 \uC54A\uC544 MCP \uB3C4\uAD6C\uAC00 \uB204\uB77D\uB41C\uB2E4."
|
|
1733
|
+
);
|
|
1734
|
+
return;
|
|
1735
|
+
}
|
|
1736
|
+
this._injectSessionDeps(this._tools.fork());
|
|
1675
1737
|
const PREWARM_TIMEOUT_MS = 1e4;
|
|
1676
1738
|
const t0 = Date.now();
|
|
1677
1739
|
this._log.info(`[PREWARM-T] start call_id=${callId} t=${(t0 / 1e3).toFixed(3)}`);
|
|
@@ -1715,9 +1777,10 @@ var ClawOpsAgent = class _ClawOpsAgent {
|
|
|
1715
1777
|
const callId = event["callId"];
|
|
1716
1778
|
const session = this._activeSessions.get(callId);
|
|
1717
1779
|
if (session) {
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
session.
|
|
1780
|
+
const reason = event["reason"] ?? "failed";
|
|
1781
|
+
this._log.info("Outbound call failed: %s (%s)", callId, reason);
|
|
1782
|
+
session._emit("call_failed", reason);
|
|
1783
|
+
session._markEnded(reason);
|
|
1721
1784
|
this._activeSessions.delete(callId);
|
|
1722
1785
|
}
|
|
1723
1786
|
void this._cleanupPrewarm(callId);
|
|
@@ -1836,21 +1899,7 @@ var ClawOpsAgent = class _ClawOpsAgent {
|
|
|
1836
1899
|
session._waitForMark = (name, timeoutMs) => mediaWs.waitForMark(name, timeoutMs);
|
|
1837
1900
|
session._flushTransport = () => mediaWs.flush();
|
|
1838
1901
|
const sessionHandler = this._session;
|
|
1839
|
-
|
|
1840
|
-
sessionHandler.setToolRegistry(sessionTools);
|
|
1841
|
-
}
|
|
1842
|
-
if (recorder && "setRecorder" in sessionHandler && typeof sessionHandler.setRecorder === "function") {
|
|
1843
|
-
sessionHandler.setRecorder(recorder);
|
|
1844
|
-
}
|
|
1845
|
-
if ("setBuiltinTools" in sessionHandler && typeof sessionHandler.setBuiltinTools === "function") {
|
|
1846
|
-
sessionHandler.setBuiltinTools(this._builtinTools);
|
|
1847
|
-
}
|
|
1848
|
-
if ("setLogger" in sessionHandler && typeof sessionHandler.setLogger === "function") {
|
|
1849
|
-
sessionHandler.setLogger(this._isPipelineSession ? this._pipelineLog : this._log);
|
|
1850
|
-
}
|
|
1851
|
-
if (this._holdAudioChunks && "setHoldAudio" in sessionHandler && typeof sessionHandler.setHoldAudio === "function") {
|
|
1852
|
-
sessionHandler.setHoldAudio(this._holdAudioChunks);
|
|
1853
|
-
}
|
|
1902
|
+
this._injectSessionDeps(sessionTools, recorder);
|
|
1854
1903
|
this._callSessions.set(session.callId, sessionHandler);
|
|
1855
1904
|
mediaWs.onAudio((ulawAudio, timestamp) => {
|
|
1856
1905
|
latestMediaTs = timestamp;
|
|
@@ -2178,6 +2227,17 @@ var PipelineSession = class {
|
|
|
2178
2227
|
return;
|
|
2179
2228
|
}
|
|
2180
2229
|
}
|
|
2230
|
+
if (BUILTIN_TOOL_NAMES.has(name) && this._callSession instanceof BufferingCall) {
|
|
2231
|
+
this._log.warn("Builtin tool %s called before answer \u2014 deferring", name);
|
|
2232
|
+
this._conversation.push({
|
|
2233
|
+
role: "tool",
|
|
2234
|
+
content: CALL_NOT_READY_RESULT,
|
|
2235
|
+
tool_call_id: id,
|
|
2236
|
+
name
|
|
2237
|
+
});
|
|
2238
|
+
await this._respond();
|
|
2239
|
+
return;
|
|
2240
|
+
}
|
|
2181
2241
|
if (!this._tools) return;
|
|
2182
2242
|
this._callSession?.recordToolCall();
|
|
2183
2243
|
const player = this._holdAudioChunks && this._callSession && !(this._callSession instanceof BufferingCall) ? new HoldAudioPlayer(this._callSession, this._holdAudioChunks) : null;
|
|
@@ -2261,6 +2321,11 @@ var OpenAIRealtime = class {
|
|
|
2261
2321
|
_greeting;
|
|
2262
2322
|
_log = NOOP_LOGGER;
|
|
2263
2323
|
_builtinTools = null;
|
|
2324
|
+
/**
|
|
2325
|
+
* 마지막 session.update 로 LLM 에 알린 tool 이름들. attach() 가 이 값과 현재
|
|
2326
|
+
* registry 를 비교해 달라졌을 때만(=MCP 도구 등 뒤늦게 붙은 경우) 재전송한다.
|
|
2327
|
+
*/
|
|
2328
|
+
_sentToolNames = null;
|
|
2264
2329
|
setLogger(logger) {
|
|
2265
2330
|
this._log = logger;
|
|
2266
2331
|
}
|
|
@@ -2370,6 +2435,7 @@ var OpenAIRealtime = class {
|
|
|
2370
2435
|
}
|
|
2371
2436
|
/** Attach a real CallSession to the prewarmed session and flush buffered audio. */
|
|
2372
2437
|
async attach(callSession) {
|
|
2438
|
+
this._resyncTools();
|
|
2373
2439
|
const prev = this._call;
|
|
2374
2440
|
this._call = callSession;
|
|
2375
2441
|
attachBuffered(prev, callSession);
|
|
@@ -2430,10 +2496,36 @@ var OpenAIRealtime = class {
|
|
|
2430
2496
|
});
|
|
2431
2497
|
}
|
|
2432
2498
|
}
|
|
2433
|
-
|
|
2434
|
-
|
|
2499
|
+
/** Current tool schemas: user tools + builtin tools (flat realtime format). */
|
|
2500
|
+
_currentToolSchemas() {
|
|
2435
2501
|
const toolSchemas = this._tools ? this._tools.toOpenAITools().map((t) => ({ type: "function", ...t.function })) : [];
|
|
2436
2502
|
toolSchemas.push(...getBuiltinToolSchemas(this._builtinTools, "realtime"));
|
|
2503
|
+
return toolSchemas;
|
|
2504
|
+
}
|
|
2505
|
+
/**
|
|
2506
|
+
* prewarm 이후 도구가 바뀌었으면 session.update 로 재전송한다.
|
|
2507
|
+
*
|
|
2508
|
+
* MCP 도구는 통화 시작 시점에 registry 에 붙으므로 prewarm 시점의 스키마에는
|
|
2509
|
+
* 없다. 여기서 맞춰주지 않으면 발신 통화에서 MCP 도구를 영영 못 쓴다.
|
|
2510
|
+
*/
|
|
2511
|
+
_resyncTools() {
|
|
2512
|
+
if (!this._ws || this._ws.readyState !== 1) return;
|
|
2513
|
+
const toolSchemas = this._currentToolSchemas();
|
|
2514
|
+
const names = toolSchemas.map((t) => String(t["name"] ?? ""));
|
|
2515
|
+
if (this._sentToolNames && names.length === this._sentToolNames.length && names.every((n, i) => n === this._sentToolNames[i])) {
|
|
2516
|
+
return;
|
|
2517
|
+
}
|
|
2518
|
+
this._send({
|
|
2519
|
+
type: "session.update",
|
|
2520
|
+
session: { type: "realtime", tools: toolSchemas }
|
|
2521
|
+
});
|
|
2522
|
+
this._sentToolNames = names;
|
|
2523
|
+
this._log.info("Tool schema resynced after prewarm: %s", names.join(", "));
|
|
2524
|
+
}
|
|
2525
|
+
_sendSessionUpdate() {
|
|
2526
|
+
if (!this._ws || this._ws.readyState !== 1) return;
|
|
2527
|
+
const toolSchemas = this._currentToolSchemas();
|
|
2528
|
+
this._sentToolNames = toolSchemas.map((t) => String(t["name"] ?? ""));
|
|
2437
2529
|
this._send({
|
|
2438
2530
|
type: "session.update",
|
|
2439
2531
|
session: {
|
|
@@ -2597,6 +2689,20 @@ var OpenAIRealtime = class {
|
|
|
2597
2689
|
return;
|
|
2598
2690
|
}
|
|
2599
2691
|
}
|
|
2692
|
+
if (BUILTIN_TOOL_NAMES.has(funcName) && this._call instanceof BufferingCall) {
|
|
2693
|
+
this._log.warn("Builtin tool %s called before answer \u2014 deferring", funcName);
|
|
2694
|
+
await this._waitForResponseDone();
|
|
2695
|
+
this._send({
|
|
2696
|
+
type: "conversation.item.create",
|
|
2697
|
+
item: {
|
|
2698
|
+
type: "function_call_output",
|
|
2699
|
+
call_id: callId,
|
|
2700
|
+
output: CALL_NOT_READY_RESULT
|
|
2701
|
+
}
|
|
2702
|
+
});
|
|
2703
|
+
this._send({ type: "response.create" });
|
|
2704
|
+
return;
|
|
2705
|
+
}
|
|
2600
2706
|
if (!this._tools || !this._tools.has(funcName)) {
|
|
2601
2707
|
this._log.error("Unknown tool: %s", funcName);
|
|
2602
2708
|
await this._waitForResponseDone();
|
|
@@ -2734,6 +2840,13 @@ function sanitizeSchemaForGemini(schema, defs, depth = 0) {
|
|
|
2734
2840
|
return result;
|
|
2735
2841
|
}
|
|
2736
2842
|
var GeminiRealtime = class _GeminiRealtime {
|
|
2843
|
+
/**
|
|
2844
|
+
* Gemini Live 는 connect 시점의 config 로 도구가 고정되어 세션 도중 변경할 수 없다.
|
|
2845
|
+
*
|
|
2846
|
+
* 통화 시작 시점에야 붙는 MCP 도구는 prewarm 된 세션에 넣을 방법이 없으므로,
|
|
2847
|
+
* `ClawOpsAgent` 가 MCP 서버 설정과 이 플래그를 보고 prewarm 을 건너뛴다.
|
|
2848
|
+
*/
|
|
2849
|
+
toolsFrozenAfterPrewarm = true;
|
|
2737
2850
|
_apiKey;
|
|
2738
2851
|
_systemPrompt;
|
|
2739
2852
|
_model;
|
|
@@ -3039,6 +3152,11 @@ var GeminiRealtime = class _GeminiRealtime {
|
|
|
3039
3152
|
continue;
|
|
3040
3153
|
}
|
|
3041
3154
|
}
|
|
3155
|
+
if (BUILTIN_TOOL_NAMES.has(name) && this._call instanceof BufferingCall) {
|
|
3156
|
+
this._log.warn("Builtin tool %s called before answer \u2014 deferring", name);
|
|
3157
|
+
responses.push({ id: fcId, name, response: { result: CALL_NOT_READY_RESULT } });
|
|
3158
|
+
continue;
|
|
3159
|
+
}
|
|
3042
3160
|
if (!this._tools || !this._tools.has(name)) {
|
|
3043
3161
|
this._log.error("Unknown tool: %s", name);
|
|
3044
3162
|
responses.push({ id: fcId, name, response: { error: `Unknown tool: ${name}` } });
|