@bytexbyte/nxtlinq-ai-agent-core-development 0.2.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.
Files changed (113) hide show
  1. package/dist/agent/ChatOrchestrator.d.ts +48 -0
  2. package/dist/agent/ChatOrchestrator.d.ts.map +1 -0
  3. package/dist/agent/ChatOrchestrator.js +311 -0
  4. package/dist/agent/NxtlinqAgent.d.ts +65 -0
  5. package/dist/agent/NxtlinqAgent.d.ts.map +1 -0
  6. package/dist/agent/NxtlinqAgent.js +256 -0
  7. package/dist/agent/errors.d.ts +4 -0
  8. package/dist/agent/errors.d.ts.map +1 -0
  9. package/dist/agent/errors.js +6 -0
  10. package/dist/agent/extractReplyText.d.ts +3 -0
  11. package/dist/agent/extractReplyText.d.ts.map +1 -0
  12. package/dist/agent/extractReplyText.js +16 -0
  13. package/dist/api/hosts.d.ts +4 -0
  14. package/dist/api/hosts.d.ts.map +1 -0
  15. package/dist/api/hosts.js +18 -0
  16. package/dist/api/nxtlinq-api.d.ts +9 -0
  17. package/dist/api/nxtlinq-api.d.ts.map +1 -0
  18. package/dist/api/nxtlinq-api.js +499 -0
  19. package/dist/api/parse-sse.d.ts +9 -0
  20. package/dist/api/parse-sse.d.ts.map +1 -0
  21. package/dist/api/parse-sse.js +97 -0
  22. package/dist/api/tts.d.ts +19 -0
  23. package/dist/api/tts.d.ts.map +1 -0
  24. package/dist/api/tts.js +46 -0
  25. package/dist/constants/storageKeys.d.ts +6 -0
  26. package/dist/constants/storageKeys.d.ts.map +1 -0
  27. package/dist/constants/storageKeys.js +5 -0
  28. package/dist/history/messageHistory.d.ts +18 -0
  29. package/dist/history/messageHistory.d.ts.map +1 -0
  30. package/dist/history/messageHistory.js +48 -0
  31. package/dist/index.d.ts +25 -0
  32. package/dist/index.d.ts.map +1 -0
  33. package/dist/index.js +26 -0
  34. package/dist/ports/HttpPort.d.ts +6 -0
  35. package/dist/ports/HttpPort.d.ts.map +1 -0
  36. package/dist/ports/HttpPort.js +3 -0
  37. package/dist/ports/PlatformPorts.d.ts +12 -0
  38. package/dist/ports/PlatformPorts.d.ts.map +1 -0
  39. package/dist/ports/PlatformPorts.js +1 -0
  40. package/dist/ports/StoragePort.d.ts +10 -0
  41. package/dist/ports/StoragePort.d.ts.map +1 -0
  42. package/dist/ports/StoragePort.js +33 -0
  43. package/dist/ports/WebRTCPort.d.ts +68 -0
  44. package/dist/ports/WebRTCPort.d.ts.map +1 -0
  45. package/dist/ports/WebRTCPort.js +10 -0
  46. package/dist/ports/createBrowserWebRTCPort.d.ts +7 -0
  47. package/dist/ports/createBrowserWebRTCPort.d.ts.map +1 -0
  48. package/dist/ports/createBrowserWebRTCPort.js +140 -0
  49. package/dist/ports/index.d.ts +5 -0
  50. package/dist/ports/index.d.ts.map +1 -0
  51. package/dist/ports/index.js +4 -0
  52. package/dist/types/agent-config.d.ts +40 -0
  53. package/dist/types/agent-config.d.ts.map +1 -0
  54. package/dist/types/agent-config.js +1 -0
  55. package/dist/types/ait-api.d.ts +393 -0
  56. package/dist/types/ait-api.d.ts.map +1 -0
  57. package/dist/types/ait-api.js +1 -0
  58. package/dist/voice/app-channel-dispatcher.d.ts +14 -0
  59. package/dist/voice/app-channel-dispatcher.d.ts.map +1 -0
  60. package/dist/voice/app-channel-dispatcher.js +171 -0
  61. package/dist/voice/create-voice-session.d.ts +8 -0
  62. package/dist/voice/create-voice-session.d.ts.map +1 -0
  63. package/dist/voice/create-voice-session.js +37 -0
  64. package/dist/voice/output-audio-level.d.ts +26 -0
  65. package/dist/voice/output-audio-level.d.ts.map +1 -0
  66. package/dist/voice/output-audio-level.js +132 -0
  67. package/dist/voice/remote-audio-gain.d.ts +10 -0
  68. package/dist/voice/remote-audio-gain.d.ts.map +1 -0
  69. package/dist/voice/remote-audio-gain.js +19 -0
  70. package/dist/voice/start-voice-session.d.ts +13 -0
  71. package/dist/voice/start-voice-session.d.ts.map +1 -0
  72. package/dist/voice/start-voice-session.js +303 -0
  73. package/dist/voice/transcript.d.ts +10 -0
  74. package/dist/voice/transcript.d.ts.map +1 -0
  75. package/dist/voice/transcript.js +50 -0
  76. package/dist/voice/trigger-voice-greeting.d.ts +14 -0
  77. package/dist/voice/trigger-voice-greeting.d.ts.map +1 -0
  78. package/dist/voice/trigger-voice-greeting.js +28 -0
  79. package/dist/voice/types.d.ts +138 -0
  80. package/dist/voice/types.d.ts.map +1 -0
  81. package/dist/voice/types.js +1 -0
  82. package/dist/voice/voice-user-input.d.ts +19 -0
  83. package/dist/voice/voice-user-input.d.ts.map +1 -0
  84. package/dist/voice/voice-user-input.js +10 -0
  85. package/package.json +41 -0
  86. package/src/agent/ChatOrchestrator.ts +380 -0
  87. package/src/agent/NxtlinqAgent.ts +325 -0
  88. package/src/agent/errors.ts +6 -0
  89. package/src/agent/extractReplyText.ts +22 -0
  90. package/src/api/hosts.ts +20 -0
  91. package/src/api/nxtlinq-api.ts +656 -0
  92. package/src/api/parse-sse.ts +104 -0
  93. package/src/api/tts.ts +69 -0
  94. package/src/constants/storageKeys.ts +5 -0
  95. package/src/history/messageHistory.ts +65 -0
  96. package/src/index.ts +70 -0
  97. package/src/ports/HttpPort.ts +12 -0
  98. package/src/ports/PlatformPorts.ts +12 -0
  99. package/src/ports/StoragePort.ts +37 -0
  100. package/src/ports/WebRTCPort.ts +54 -0
  101. package/src/ports/createBrowserWebRTCPort.ts +163 -0
  102. package/src/ports/index.ts +4 -0
  103. package/src/types/agent-config.ts +51 -0
  104. package/src/types/ait-api.ts +303 -0
  105. package/src/voice/app-channel-dispatcher.ts +201 -0
  106. package/src/voice/create-voice-session.ts +53 -0
  107. package/src/voice/output-audio-level.ts +153 -0
  108. package/src/voice/remote-audio-gain.ts +31 -0
  109. package/src/voice/start-voice-session.ts +369 -0
  110. package/src/voice/transcript.ts +44 -0
  111. package/src/voice/trigger-voice-greeting.ts +47 -0
  112. package/src/voice/types.ts +154 -0
  113. package/src/voice/voice-user-input.ts +32 -0
@@ -0,0 +1,138 @@
1
+ import type { IceServerConfig } from '../ports/WebRTCPort';
2
+ import type { Attachment } from '../types/ait-api';
3
+ import type { VoiceUserInputOptions } from './voice-user-input';
4
+ export type { VoiceUserInputOptions };
5
+ export type VoicePhase = 'vad_start' | 'vad_end' | 'stt_done' | 'llm_start' | 'llm_first_token' | 'llm_end' | 'tts_audio_start' | 'tts_audio_end';
6
+ export type VoiceStatus = 'listening' | 'transcribing' | 'thinking' | 'generating' | 'speaking' | 'idle';
7
+ export type VoicePhaseExtra = {
8
+ turnId?: string;
9
+ };
10
+ export type VoiceTranscriptEvent = {
11
+ role: 'user' | 'assistant';
12
+ text: string;
13
+ interim: boolean;
14
+ };
15
+ export type VoiceToolCallEvent = {
16
+ name: string;
17
+ args: Record<string, unknown>;
18
+ toolCallId?: string;
19
+ bridgePhrase?: string;
20
+ };
21
+ export type VoiceToolResultEvent = {
22
+ name: string;
23
+ handled: boolean;
24
+ toolCallType: 'backend' | 'frontend' | null;
25
+ response?: unknown;
26
+ };
27
+ export type VoicePermissionRequiredEvent = {
28
+ toolName: string;
29
+ permission: string;
30
+ };
31
+ export type VoiceBillingBlockedEvent = {
32
+ reason: 'no_credits' | 'inactive' | 'suspended' | 'canceled' | 'grace_period_expired' | string;
33
+ message: string;
34
+ balanceCents?: number;
35
+ costCents?: number;
36
+ };
37
+ export type VoiceTtsWordEvent = {
38
+ text: string;
39
+ ptsMs: number;
40
+ ts: number;
41
+ };
42
+ export type VoiceDoneEvent = {
43
+ replyText?: string;
44
+ userMessageId?: string | null;
45
+ assistantMessageId?: string | null;
46
+ toolCall?: {
47
+ name: string;
48
+ };
49
+ timings?: {
50
+ totalDurationMs: number;
51
+ stepTimings: Array<{
52
+ name: string;
53
+ durationMs: number;
54
+ }>;
55
+ };
56
+ guardrailsBlocked?: boolean;
57
+ billingBlocked?: boolean;
58
+ billingReason?: VoiceBillingBlockedEvent['reason'];
59
+ error?: string;
60
+ };
61
+ export type StartVoiceSessionOptions = {
62
+ apiKey: string;
63
+ apiSecret: string;
64
+ pseudoId: string;
65
+ externalId?: string;
66
+ aitId?: string;
67
+ walletAddress?: string;
68
+ aitToken?: string;
69
+ metadata?: Record<string, unknown>;
70
+ voiceMode?: 'cascade' | 'realtime';
71
+ startWithMicMuted?: boolean;
72
+ /**
73
+ * Remote assistant playback gain (react-native-webrtc `_setVolume`, 0–10).
74
+ * @default 10 in {@link DEFAULT_REMOTE_AUDIO_GAIN}
75
+ */
76
+ remoteAudioGain?: number;
77
+ baseUrl?: string;
78
+ onOpen?: () => void;
79
+ onPhase?: (phase: VoicePhase, ts: number, extra?: VoicePhaseExtra) => void;
80
+ onStatus?: (status: VoiceStatus, ts: number) => void;
81
+ onTranscript?: (event: VoiceTranscriptEvent) => void;
82
+ onToolCall?: (event: VoiceToolCallEvent) => void;
83
+ onToolResult?: (event: VoiceToolResultEvent) => void;
84
+ onPermissionRequired?: (event: VoicePermissionRequiredEvent) => void;
85
+ onBillingBlocked?: (event: VoiceBillingBlockedEvent) => void;
86
+ onTtsWord?: (event: VoiceTtsWordEvent) => void;
87
+ onDone?: (event: VoiceDoneEvent) => void;
88
+ onError?: (error: Error) => void;
89
+ onClose?: (reason: string) => void;
90
+ };
91
+ /** Active voice call handle (platform layer attaches real MediaStream). */
92
+ export type VoiceSession = {
93
+ id: string;
94
+ getRemoteAudioStream(): MediaStream | null;
95
+ /** Assistant playback RMS in 0–1 (poll from UI animation loop). */
96
+ getOutputAudioLevel(): number;
97
+ /** Whether the `app` data channel is open for client→server messages. */
98
+ isAppChannelOpen(): boolean;
99
+ /**
100
+ * Inject a text and/or image user turn during an active voice session
101
+ * (OpenAI Realtime–style conversation.item.create).
102
+ */
103
+ sendVoiceUserInput(options: VoiceUserInputOptions): void;
104
+ muteMic(muted: boolean): void;
105
+ interrupt(): void;
106
+ stop(reason?: string): Promise<void>;
107
+ };
108
+ export type VoiceGreetingOptions = {
109
+ text: string;
110
+ attachments?: Attachment[];
111
+ /**
112
+ * When true, do not append a user bubble in local chat state
113
+ * (Berify `_localOnly` auto-greet).
114
+ */
115
+ skipUserMessage?: boolean;
116
+ clientMessageId?: string;
117
+ /** Default false for opening greet (avoid barge-in on idle session). */
118
+ interruptFirst?: boolean;
119
+ };
120
+ export type VoiceSessionInitResponse = {
121
+ voiceSessionId: string;
122
+ signalingUrl: string;
123
+ iceServers: IceServerConfig[];
124
+ expiresAt: string;
125
+ mode?: 'cascade' | 'realtime';
126
+ };
127
+ export type CreateVoiceSessionParams = {
128
+ apiKey: string;
129
+ apiSecret: string;
130
+ pseudoId: string;
131
+ externalId?: string;
132
+ aitId?: string;
133
+ walletAddress?: string;
134
+ aitToken?: string;
135
+ voiceMode?: 'cascade' | 'realtime';
136
+ metadata?: Record<string, unknown>;
137
+ };
138
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/voice/types.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,19 @@
1
+ import type { Attachment } from '../types/ait-api';
2
+ export type VoiceUserInputOptions = {
3
+ /** User text; omit or empty for image-only turns. */
4
+ text?: string;
5
+ attachments?: Attachment[];
6
+ /** Correlates with client UI message id (e.g. GiftedChat _id). */
7
+ clientMessageId?: string;
8
+ /** Send `interrupt` on the app channel before user input (default true). */
9
+ interruptFirst?: boolean;
10
+ };
11
+ export type AppChannelUserInputPayload = {
12
+ type: 'user_input';
13
+ text?: string;
14
+ attachments?: Attachment[];
15
+ clientMessageId?: string;
16
+ ts: number;
17
+ };
18
+ export declare function buildUserInputPayload(options: VoiceUserInputOptions): AppChannelUserInputPayload;
19
+ //# sourceMappingURL=voice-user-input.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"voice-user-input.d.ts","sourceRoot":"","sources":["../../src/voice/voice-user-input.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAEnD,MAAM,MAAM,qBAAqB,GAAG;IAClC,qDAAqD;IACrD,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,UAAU,EAAE,CAAC;IAC3B,kEAAkE;IAClE,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,4EAA4E;IAC5E,cAAc,CAAC,EAAE,OAAO,CAAC;CAC1B,CAAC;AAEF,MAAM,MAAM,0BAA0B,GAAG;IACvC,IAAI,EAAE,YAAY,CAAC;IACnB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,UAAU,EAAE,CAAC;IAC3B,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,EAAE,EAAE,MAAM,CAAC;CACZ,CAAC;AAEF,wBAAgB,qBAAqB,CACnC,OAAO,EAAE,qBAAqB,GAC7B,0BAA0B,CAS5B"}
@@ -0,0 +1,10 @@
1
+ export function buildUserInputPayload(options) {
2
+ const text = typeof options.text === 'string' ? options.text.trim() : '';
3
+ return {
4
+ type: 'user_input',
5
+ ...(text ? { text } : {}),
6
+ ...(options.attachments?.length ? { attachments: options.attachments } : {}),
7
+ ...(options.clientMessageId ? { clientMessageId: options.clientMessageId } : {}),
8
+ ts: Date.now(),
9
+ };
10
+ }
package/package.json ADDED
@@ -0,0 +1,41 @@
1
+ {
2
+ "name": "@bytexbyte/nxtlinq-ai-agent-core-development",
3
+ "version": "0.2.0",
4
+ "description": "Platform-agnostic nxtlinq AI Agent core — API client, types, and orchestration ports",
5
+ "main": "dist/index.js",
6
+ "react-native": "src/index.ts",
7
+ "types": "dist/index.d.ts",
8
+ "files": [
9
+ "dist",
10
+ "src"
11
+ ],
12
+ "scripts": {
13
+ "build": "tsc",
14
+ "clean": "rm -rf dist && find src -type f \\( -name '*.js' -o -name '*.d.ts' -o -name '*.js.map' -o -name '*.d.ts.map' \\) -delete 2>/dev/null || true",
15
+ "prepublishOnly": "yarn build"
16
+ },
17
+ "keywords": [
18
+ "nxtlinq",
19
+ "ai-agent",
20
+ "sdk"
21
+ ],
22
+ "author": "ByteXByte",
23
+ "license": "MIT",
24
+ "repository": {
25
+ "type": "git",
26
+ "url": "https://dev.azure.com/nxtlinqLLC/nxtlinq/_git/nxtlinq-AI-Agent-SDK",
27
+ "directory": "packages/ai-agent-core"
28
+ },
29
+ "publishConfig": {
30
+ "access": "public",
31
+ "registry": "https://registry.npmjs.org/"
32
+ },
33
+ "sideEffects": false,
34
+ "dependencies": {
35
+ "fast-json-stable-stringify": "^2.1.0",
36
+ "uuid": "^11.1.0"
37
+ },
38
+ "devDependencies": {
39
+ "typescript": "^5.4.2"
40
+ }
41
+ }
@@ -0,0 +1,380 @@
1
+ import type { CoreAITApi } from '../api/nxtlinq-api';
2
+ import {
3
+ appendServerHistoryIntoMessages,
4
+ mapServerHistoryToMessages,
5
+ VOICE_TURN_SYNC_LAST,
6
+ type ServerHistoryMessage,
7
+ } from '../history/messageHistory';
8
+ import type { AgentResponse, Attachment, Message } from '../types/ait-api';
9
+ import type { AgentConfig, ToolUse } from '../types/agent-config';
10
+ import { extractReplyText } from './extractReplyText';
11
+
12
+ export type SendMessageOptions = {
13
+ text: string;
14
+ attachments?: Attachment[];
15
+ /** Defaults to `config.defaultModel` or `open-ai`. */
16
+ model?: string;
17
+ /** Skip appending a user bubble (retries / preset messages). */
18
+ skipUserMessage?: boolean;
19
+ isRetry?: boolean;
20
+ clientPipelineTimings?: Array<{ name: string; durationMs: number }>;
21
+ };
22
+
23
+ type AgentErrorResponse = AgentResponse & {
24
+ requiresWalletConnection?: boolean;
25
+ requiresPermissionUpdate?: boolean;
26
+ message?: string;
27
+ toolName?: string;
28
+ requiredPermission?: string;
29
+ };
30
+
31
+ /**
32
+ * Platform-agnostic text chat orchestration (no wallet UI, no TTS, no PII UI).
33
+ */
34
+ export class ChatOrchestrator {
35
+ private messages: Message[] = [];
36
+ private isLoading = false;
37
+
38
+ constructor(
39
+ private readonly config: AgentConfig,
40
+ private readonly api: CoreAITApi,
41
+ private readonly onUpdate: () => void,
42
+ ) {}
43
+
44
+ getMessages(): Message[] {
45
+ return this.messages;
46
+ }
47
+
48
+ getIsLoading(): boolean {
49
+ return this.isLoading;
50
+ }
51
+
52
+ setMessages(messages: Message[]): void {
53
+ this.messages = messages;
54
+ this.onUpdate();
55
+ }
56
+
57
+ private emit(): void {
58
+ this.onUpdate();
59
+ }
60
+
61
+ private requirePseudoId(): string {
62
+ const id = this.config.pseudoId;
63
+ if (!id) {
64
+ throw new Error('pseudoId is required — set AgentConfig.pseudoId before chatting');
65
+ }
66
+ return id;
67
+ }
68
+
69
+ private resolveModel(model?: string): string {
70
+ return model ?? this.config.defaultModel ?? 'open-ai';
71
+ }
72
+
73
+ private patchMessage(id: string, patch: Partial<Message>): void {
74
+ this.messages = this.messages.map((m) =>
75
+ m.id === id ? { ...m, ...patch } : m,
76
+ );
77
+ this.emit();
78
+ }
79
+
80
+ private appendMessage(message: Message): void {
81
+ this.messages = [...this.messages, message];
82
+ this.config.onMessage?.(message);
83
+ this.emit();
84
+ }
85
+
86
+ async loadHistory(options?: { last?: number }): Promise<void> {
87
+ const pseudoId = this.requirePseudoId();
88
+ const result = await this.api.agent.getMessageHistory({
89
+ apiKey: this.config.apiKey,
90
+ apiSecret: this.config.apiSecret,
91
+ pseudoId,
92
+ externalId: this.config.externalId,
93
+ last: options?.last,
94
+ });
95
+
96
+ if (result && typeof result === 'object' && 'error' in result && result.error) {
97
+ throw new Error(String(result.error));
98
+ }
99
+
100
+ const rows = (result as { messages?: ServerHistoryMessage[] }).messages ?? [];
101
+ this.messages = mapServerHistoryToMessages(rows);
102
+ this.emit();
103
+ }
104
+
105
+ /**
106
+ * Fetch the latest voice-turn rows from the server and merge by message id
107
+ * (same behavior as web SDK `refreshMessageHistory`).
108
+ */
109
+ async syncVoiceTurnHistory(options?: { last?: number }): Promise<void> {
110
+ const pseudoId = this.requirePseudoId();
111
+ const result = await this.api.agent.getMessageHistory({
112
+ apiKey: this.config.apiKey,
113
+ apiSecret: this.config.apiSecret,
114
+ pseudoId,
115
+ externalId: this.config.externalId,
116
+ last: options?.last ?? VOICE_TURN_SYNC_LAST,
117
+ });
118
+
119
+ if (result && typeof result === 'object' && 'error' in result && result.error) {
120
+ throw new Error(String(result.error));
121
+ }
122
+
123
+ const rows = (result as { messages?: ServerHistoryMessage[] }).messages ?? [];
124
+ this.messages = appendServerHistoryIntoMessages(this.messages, rows);
125
+ this.emit();
126
+ }
127
+
128
+ async sendMessage(options: SendMessageOptions): Promise<void> {
129
+ const text = options.text?.trim() ?? '';
130
+ const hasContent = Boolean(text || (options.attachments && options.attachments.length > 0));
131
+ if (!hasContent || this.isLoading) return;
132
+
133
+ const model = this.resolveModel(options.model);
134
+ const pseudoId = this.requirePseudoId();
135
+
136
+ if (!options.skipUserMessage && !options.isRetry) {
137
+ this.appendMessage({
138
+ id: `${Date.now()}`,
139
+ content:
140
+ text
141
+ || (options.attachments?.length
142
+ ? `Uploaded ${options.attachments.length} file(s)`
143
+ : ''),
144
+ role: 'user',
145
+ timestamp: new Date().toISOString(),
146
+ attachments: options.attachments,
147
+ metadata: { model },
148
+ });
149
+ }
150
+
151
+ this.isLoading = true;
152
+ this.emit();
153
+
154
+ const isStreamModel = model.endsWith('-stream');
155
+ let streamAssistantId: string | null = null;
156
+ let streamedReplyBuffer = '';
157
+
158
+ if (isStreamModel && !options.isRetry) {
159
+ streamAssistantId = `stream-assistant-${Date.now()}`;
160
+ this.appendMessage({
161
+ id: streamAssistantId,
162
+ role: 'assistant',
163
+ content: '',
164
+ partialContent: '',
165
+ isStreaming: true,
166
+ timestamp: new Date().toISOString(),
167
+ metadata: { model, agentLlmStream: true },
168
+ });
169
+ }
170
+
171
+ try {
172
+ const apiAttachments = options.attachments?.map((att) => ({
173
+ type: att.type,
174
+ url: att.url,
175
+ name: att.name,
176
+ mimeType: att.mimeType,
177
+ }));
178
+
179
+ const response = (await this.api.agent.sendMessage({
180
+ model,
181
+ apiKey: this.config.apiKey,
182
+ apiSecret: this.config.apiSecret,
183
+ pseudoId,
184
+ externalId: this.config.externalId,
185
+ customUserInfo: this.config.customUserInfo,
186
+ customUsername: this.config.customUsername,
187
+ message:
188
+ text
189
+ || (options.attachments?.length
190
+ ? `Uploaded ${options.attachments.length} file(s)`
191
+ : ''),
192
+ attachments: apiAttachments,
193
+ isRetry: options.isRetry,
194
+ ...(options.clientPipelineTimings?.length
195
+ ? { clientPipelineTimings: options.clientPipelineTimings }
196
+ : {}),
197
+ onStreamDelta: streamAssistantId
198
+ ? (delta: string) => {
199
+ streamedReplyBuffer += delta;
200
+ const current = this.messages.find((m) => m.id === streamAssistantId);
201
+ this.patchMessage(streamAssistantId!, {
202
+ partialContent: `${current?.partialContent ?? ''}${delta}`,
203
+ });
204
+ if (delta) {
205
+ this.isLoading = false;
206
+ this.emit();
207
+ }
208
+ }
209
+ : undefined,
210
+ })) as AgentErrorResponse;
211
+
212
+ const actualModel = response.model ?? model;
213
+
214
+ if (response.error || response.result === 'error') {
215
+ if (response.requiresWalletConnection || response.requiresPermissionUpdate) {
216
+ const msg =
217
+ response.message
218
+ ?? response.error
219
+ ?? 'Additional setup is required before continuing.';
220
+ this.appendMessage({
221
+ id: `${Date.now()}-err`,
222
+ content: msg,
223
+ role: 'assistant',
224
+ timestamp: new Date().toISOString(),
225
+ metadata: {
226
+ model: actualModel,
227
+ toolName: response.toolName,
228
+ requiredPermission: response.requiredPermission,
229
+ },
230
+ });
231
+ return;
232
+ }
233
+ throw new Error(response.error ?? response.message ?? 'Failed to send message');
234
+ }
235
+
236
+ if (streamAssistantId && response.toolCall?.toolUse) {
237
+ this.messages = this.messages.filter((m) => m.id !== streamAssistantId);
238
+ streamAssistantId = null;
239
+ this.emit();
240
+ }
241
+
242
+ if (streamAssistantId && !response.toolCall?.toolUse) {
243
+ const replyText = extractReplyText(response, streamedReplyBuffer);
244
+ this.patchMessage(streamAssistantId, {
245
+ content: replyText,
246
+ isStreaming: false,
247
+ partialContent: undefined,
248
+ metadata: { model: actualModel, agentLlmStream: true },
249
+ piiProtection: response.piiProtection?.anonymizedReply
250
+ ? {
251
+ anonymizedContent: response.piiProtection.anonymizedReply ?? undefined,
252
+ mapping: response.piiProtection.mapping ?? undefined,
253
+ }
254
+ : undefined,
255
+ });
256
+ return;
257
+ }
258
+
259
+ if (response.toolCall?.toolUse) {
260
+ await this.handleToolCall(response, actualModel);
261
+ return;
262
+ }
263
+
264
+ const replyText = extractReplyText(response, streamedReplyBuffer);
265
+ this.appendMessage({
266
+ id: `${Date.now() + 1}`,
267
+ content: replyText,
268
+ role: 'assistant',
269
+ timestamp: new Date().toISOString(),
270
+ metadata: { model: actualModel },
271
+ piiProtection: response.piiProtection?.anonymizedReply
272
+ ? {
273
+ anonymizedContent: response.piiProtection.anonymizedReply ?? undefined,
274
+ mapping: response.piiProtection.mapping ?? undefined,
275
+ }
276
+ : undefined,
277
+ });
278
+
279
+ if (response.piiProtection?.anonymizedUserMessage) {
280
+ for (let i = this.messages.length - 1; i >= 0; i--) {
281
+ if (this.messages[i].role === 'user') {
282
+ this.patchMessage(this.messages[i].id, {
283
+ piiProtection: {
284
+ anonymizedContent: response.piiProtection!.anonymizedUserMessage!,
285
+ mapping: response.piiProtection!.mapping ?? undefined,
286
+ },
287
+ piiStatus: 'complete',
288
+ });
289
+ break;
290
+ }
291
+ }
292
+ }
293
+ } catch (error) {
294
+ const err = error instanceof Error ? error : new Error(String(error));
295
+ this.config.onError?.(err);
296
+ if (streamAssistantId) {
297
+ this.messages = this.messages.filter((m) => m.id !== streamAssistantId);
298
+ }
299
+ this.appendMessage({
300
+ id: `${Date.now()}-err`,
301
+ content: err.message,
302
+ role: 'assistant',
303
+ timestamp: new Date().toISOString(),
304
+ error: err.message,
305
+ });
306
+ } finally {
307
+ this.isLoading = false;
308
+ this.emit();
309
+ }
310
+ }
311
+
312
+ private async handleToolCall(
313
+ response: AgentResponse,
314
+ actualModel: string,
315
+ ): Promise<void> {
316
+ const toolUse = response.toolCall!.toolUse as ToolUse;
317
+ const streamingId = `streaming-${Date.now()}`;
318
+
319
+ this.appendMessage({
320
+ id: streamingId,
321
+ content: '',
322
+ role: 'assistant',
323
+ timestamp: new Date().toISOString(),
324
+ isStreaming: true,
325
+ streamingToolName: toolUse.name,
326
+ streamingStatus: `Starting ${toolUse.name}...`,
327
+ streamingProgress: 0,
328
+ metadata: { model: actualModel, toolUse },
329
+ });
330
+
331
+ let finalContent = `Tool ${toolUse.name} executed successfully`;
332
+
333
+ if (this.config.onToolUse) {
334
+ const toolResult = await this.config.onToolUse(toolUse, (update) => {
335
+ const current = this.messages.find((m) => m.id === streamingId);
336
+ this.patchMessage(streamingId, {
337
+ content: update.partialResult ?? current?.content ?? '',
338
+ partialContent: update.partialContent,
339
+ streamingProgress: update.progress,
340
+ streamingStatus: update.status,
341
+ streamingSteps: update.steps ?? current?.streamingSteps,
342
+ });
343
+ });
344
+ if (toolResult?.content) {
345
+ finalContent = toolResult.content;
346
+ }
347
+ }
348
+
349
+ const replyText = extractReplyText(response);
350
+ if (replyText && replyText !== 'Sorry, I cannot understand your question') {
351
+ finalContent = `${finalContent}\n\n${replyText}`;
352
+ }
353
+
354
+ this.patchMessage(streamingId, {
355
+ content: finalContent,
356
+ isStreaming: false,
357
+ streamingProgress: 100,
358
+ metadata: { model: actualModel, toolUse },
359
+ piiProtection: response.piiProtection?.anonymizedReply
360
+ ? {
361
+ anonymizedContent: response.piiProtection.anonymizedReply ?? undefined,
362
+ mapping: response.piiProtection.mapping ?? undefined,
363
+ }
364
+ : undefined,
365
+ });
366
+
367
+ if (response.assistantMessageId) {
368
+ try {
369
+ await this.api.agent.updateMessageContent({
370
+ apiKey: this.config.apiKey,
371
+ apiSecret: this.config.apiSecret,
372
+ messageId: response.assistantMessageId,
373
+ content: finalContent,
374
+ });
375
+ } catch {
376
+ /* best-effort */
377
+ }
378
+ }
379
+ }
380
+ }