@yushaw/sanqian-chat 0.1.1 → 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.
- package/README.md +116 -0
- package/dist/core/index.d.mts +180 -6
- package/dist/core/index.d.ts +180 -6
- package/dist/core/index.js +548 -64
- package/dist/core/index.mjs +534 -63
- package/dist/main/index.d.mts +320 -5
- package/dist/main/index.d.ts +320 -5
- package/dist/main/index.js +466 -39
- package/dist/main/index.mjs +454 -38
- package/dist/preload/index.d.ts +88 -0
- package/dist/preload/index.js +5 -1
- package/dist/renderer/index.d.mts +609 -18
- package/dist/renderer/index.d.ts +609 -18
- package/dist/renderer/index.js +7870 -537
- package/dist/renderer/index.mjs +7854 -538
- package/package.json +16 -4
- package/src/renderer/styles/baseStyles.ts +74 -0
- package/src/renderer/styles/chat.css +3674 -0
- package/src/renderer/styles/chatCss.ts +11 -0
- package/src/renderer/styles/coreCss.ts +3237 -0
- package/src/renderer/styles/preflightCss.ts +454 -0
- package/src/renderer/styles/safe.css +3227 -0
- package/src/renderer/styles/safeCss.ts +10 -0
- package/src/renderer/styles/tailwind.css +683 -0
- package/src/renderer/styles/variables.css +704 -0
package/README.md
ADDED
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
# @yushaw/sanqian-chat
|
|
2
|
+
|
|
3
|
+
Sanqian chat UI components and hooks for third-party Electron apps.
|
|
4
|
+
|
|
5
|
+
## Phase 1 (Core Chat)
|
|
6
|
+
- Streaming chat messages
|
|
7
|
+
- Thinking section
|
|
8
|
+
- Tool call timeline
|
|
9
|
+
- HITL prompts
|
|
10
|
+
- Simplified input (text + send + stop)
|
|
11
|
+
|
|
12
|
+
## Install
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
npm install @yushaw/sanqian-chat
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## Publish
|
|
19
|
+
|
|
20
|
+
From the repo root:
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
npm publish --workspace=@yushaw/sanqian-chat
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
Or from the package directory:
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
cd packages/chat
|
|
30
|
+
npm publish
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## Usage (Renderer)
|
|
34
|
+
|
|
35
|
+
```tsx
|
|
36
|
+
import { SanqianChat, createSdkAdapter } from '@yushaw/sanqian-chat/renderer';
|
|
37
|
+
import { SanqianSDK } from '@yushaw/sanqian-sdk';
|
|
38
|
+
|
|
39
|
+
const sdk = new SanqianSDK({
|
|
40
|
+
appName: 'my-app',
|
|
41
|
+
appVersion: '0.1.0',
|
|
42
|
+
tools: [],
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
const adapter = createSdkAdapter({
|
|
46
|
+
getSdk: () => sdk,
|
|
47
|
+
getAgentId: () => 'assistant',
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
export function App() {
|
|
51
|
+
return (
|
|
52
|
+
<div className="h-full">
|
|
53
|
+
<SanqianChat
|
|
54
|
+
adapter={adapter}
|
|
55
|
+
config={{
|
|
56
|
+
theme: 'auto',
|
|
57
|
+
accentColor: '#e85d75',
|
|
58
|
+
locale: 'zh',
|
|
59
|
+
}}
|
|
60
|
+
/>
|
|
61
|
+
</div>
|
|
62
|
+
);
|
|
63
|
+
}
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## API Reference
|
|
67
|
+
|
|
68
|
+
See `packages/chat/API.md` and `packages/chat/API.zh-CN.md`.
|
|
69
|
+
|
|
70
|
+
## Notes
|
|
71
|
+
- **Style modes**: Default is `'safe'` (no Tailwind preflight, safe for embedding). Use `'full'` for standalone windows.
|
|
72
|
+
- Set `window.__SANQIAN_CHAT_STYLE_MODE__ = 'full'` for full styles including preflight
|
|
73
|
+
- Set `window.__SANQIAN_CHAT_DISABLE_STYLES__ = true` to disable auto-injection
|
|
74
|
+
- **CSS files**:
|
|
75
|
+
- `@yushaw/sanqian-chat/renderer/styles/safe.css` - Safe (no preflight, includes utilities)
|
|
76
|
+
- `@yushaw/sanqian-chat/renderer/styles/chat.css` - Full (includes Tailwind preflight)
|
|
77
|
+
- `@yushaw/sanqian-chat/renderer/styles/variables.css` - Variables only
|
|
78
|
+
- Sanqian should be running when the SDK connects.
|
|
79
|
+
- Additional capabilities (history, agents, files, settings) will be added in later phases.
|
|
80
|
+
|
|
81
|
+
## Developing Styles
|
|
82
|
+
|
|
83
|
+
CSS uses a modular structure with a single source of truth:
|
|
84
|
+
|
|
85
|
+
```
|
|
86
|
+
src/renderer/styles/
|
|
87
|
+
├── coreCss.ts # ← EDIT THIS (CSS variables + utilities)
|
|
88
|
+
├── preflightCss.ts # Tailwind preflight (don't edit)
|
|
89
|
+
├── safeCss.ts # Auto-generated (imports coreCss)
|
|
90
|
+
├── chatCss.ts # Auto-generated (imports coreCss + preflight)
|
|
91
|
+
├── safe.css # Auto-generated
|
|
92
|
+
└── chat.css # Auto-generated
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
**To modify styles:**
|
|
96
|
+
1. Edit `coreCss.ts`
|
|
97
|
+
2. Run `npm run build:css` to regenerate all files
|
|
98
|
+
3. Or just run `npm run build` (includes CSS generation)
|
|
99
|
+
|
|
100
|
+
## Changelog
|
|
101
|
+
|
|
102
|
+
### 2025-01-03
|
|
103
|
+
- **Added**: Focus persistence - input keeps focus when clicking empty areas (unless selecting text)
|
|
104
|
+
- **Added**: `Cmd+N` (macOS) / `Ctrl+N` (Windows) keyboard shortcut for new chat
|
|
105
|
+
- **Added**: Hover tooltip with shortcut hint on new chat button
|
|
106
|
+
|
|
107
|
+
### 2025-01-02
|
|
108
|
+
- **Changed**: CSS modular structure - single source of truth (`coreCss.ts`)
|
|
109
|
+
- **Changed**: `.prose` uses `font-size: inherit` for external control
|
|
110
|
+
- **Changed**: IntermediateSteps uses relative font sizes (rem instead of px)
|
|
111
|
+
- **Fixed**: `position` default value conflict with `rememberWindowState`
|
|
112
|
+
- **Fixed**: IPC now forwards `start` event (with `run_id`) to renderer
|
|
113
|
+
- **Fixed**: Center position calculation uses `workArea` (supports multi-monitor/dock)
|
|
114
|
+
- **Fixed**: `console.log` only outputs in `devMode`
|
|
115
|
+
- **Fixed**: Global style pollution - default to `'safe'` mode (no Tailwind preflight)
|
|
116
|
+
- **Fixed**: `cancelStream` now calls `sdk.cancelRun()` for real cancellation
|
package/dist/core/index.d.mts
CHANGED
|
@@ -7,16 +7,80 @@ export { ChatStreamEvent, HitlInterruptPayload, HitlInterruptType, HitlResponse,
|
|
|
7
7
|
* Re-exports SDK types + chat-specific types
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
|
-
type
|
|
10
|
+
type Locale = 'en' | 'zh';
|
|
11
|
+
interface ChatUiStrings {
|
|
12
|
+
inputPlaceholder: string;
|
|
13
|
+
inputSend: string;
|
|
14
|
+
inputStop: string;
|
|
15
|
+
chat: string;
|
|
16
|
+
recentChats: string;
|
|
17
|
+
newChat: string;
|
|
18
|
+
selectConversation: string;
|
|
19
|
+
noHistory: string;
|
|
20
|
+
loadMore: string;
|
|
21
|
+
today: string;
|
|
22
|
+
yesterday: string;
|
|
23
|
+
delete: string;
|
|
24
|
+
dismiss: string;
|
|
25
|
+
close: string;
|
|
26
|
+
pin: string;
|
|
27
|
+
unpin: string;
|
|
28
|
+
conversationUntitled: string;
|
|
29
|
+
conversationDeleteConfirm: string;
|
|
30
|
+
messageThinking: string;
|
|
31
|
+
messageError: string;
|
|
32
|
+
messageLoading: string;
|
|
33
|
+
connectionConnecting: string;
|
|
34
|
+
connectionConnected: string;
|
|
35
|
+
connectionDisconnected: string;
|
|
36
|
+
connectionReconnecting: string;
|
|
37
|
+
connectionError: string;
|
|
38
|
+
steps: string;
|
|
39
|
+
executing: string;
|
|
40
|
+
thinking: string;
|
|
41
|
+
thinkingStreaming: string;
|
|
42
|
+
thinkingPaused: string;
|
|
43
|
+
hitlApprove: string;
|
|
44
|
+
hitlReject: string;
|
|
45
|
+
hitlSubmit: string;
|
|
46
|
+
hitlCancel: string;
|
|
47
|
+
hitlRememberChoice: string;
|
|
48
|
+
hitlRequiredField: string;
|
|
49
|
+
hitlTimeoutIn: string;
|
|
50
|
+
hitlSeconds: string;
|
|
51
|
+
hitlExecuteTool: string;
|
|
52
|
+
hitlToolLabel: string;
|
|
53
|
+
hitlArgsLabel: string;
|
|
54
|
+
hitlDefaultPrefix: string;
|
|
55
|
+
hitlEnterResponse: string;
|
|
56
|
+
hitlApprovalRequest: string;
|
|
57
|
+
hitlInputRequest: string;
|
|
58
|
+
hitlApprovalRequired: string;
|
|
59
|
+
hitlInputRequired: string;
|
|
60
|
+
}
|
|
61
|
+
type ChatThemeMode = 'light' | 'dark' | 'auto';
|
|
62
|
+
type ChatFontSize = 'small' | 'normal' | 'large' | 'extra-large';
|
|
63
|
+
interface ChatUiConfigSerializable {
|
|
64
|
+
logo?: string;
|
|
65
|
+
theme?: ChatThemeMode;
|
|
66
|
+
/** Font size scale: small (13px), normal (14px), large (16px), extra-large (18px) */
|
|
67
|
+
fontSize?: ChatFontSize;
|
|
68
|
+
accentColor?: string;
|
|
69
|
+
locale?: Locale;
|
|
70
|
+
strings?: Partial<ChatUiStrings>;
|
|
71
|
+
alwaysOnTop?: boolean;
|
|
72
|
+
}
|
|
73
|
+
type MessageRole = 'user' | 'assistant' | 'system' | 'tool';
|
|
11
74
|
type ToolCallStatus = 'pending' | 'running' | 'completed' | 'error' | 'cancelled';
|
|
12
75
|
type ConnectionStatus = 'disconnected' | 'connecting' | 'connected' | 'reconnecting' | 'error';
|
|
13
76
|
type ConnectionErrorCode = 'NOT_FOUND' | 'CONNECTION_FAILED' | 'WEBSOCKET_ERROR' | 'AUTH_ERROR' | 'TIMEOUT' | 'UNKNOWN';
|
|
14
77
|
/** Tool call for UI rendering (extended from SDK) */
|
|
15
78
|
interface ToolCall {
|
|
16
|
-
id
|
|
79
|
+
id?: string;
|
|
17
80
|
name: string;
|
|
18
|
-
|
|
19
|
-
|
|
81
|
+
args?: Record<string, unknown>;
|
|
82
|
+
argsRaw?: string;
|
|
83
|
+
status?: ToolCallStatus;
|
|
20
84
|
result?: unknown;
|
|
21
85
|
error?: string;
|
|
22
86
|
}
|
|
@@ -27,9 +91,11 @@ interface MessageBlock {
|
|
|
27
91
|
timestamp: number;
|
|
28
92
|
toolName?: string;
|
|
29
93
|
toolArgs?: Record<string, unknown>;
|
|
94
|
+
toolArgsRaw?: string;
|
|
30
95
|
toolCallId?: string;
|
|
31
96
|
toolStatus?: ToolCallStatus;
|
|
32
97
|
isIntermediate?: boolean;
|
|
98
|
+
fromSubagent?: boolean;
|
|
33
99
|
}
|
|
34
100
|
/** Chat message for UI rendering */
|
|
35
101
|
interface ChatMessage {
|
|
@@ -42,8 +108,12 @@ interface ChatMessage {
|
|
|
42
108
|
thinking?: string;
|
|
43
109
|
currentThinking?: string;
|
|
44
110
|
isThinkingStreaming?: boolean;
|
|
111
|
+
isThinkingPaused?: boolean;
|
|
112
|
+
isToolCallsStreaming?: boolean;
|
|
45
113
|
blocks?: MessageBlock[];
|
|
114
|
+
finalContent?: string;
|
|
46
115
|
isComplete?: boolean;
|
|
116
|
+
filePaths?: string[];
|
|
47
117
|
}
|
|
48
118
|
/** Conversation info for UI */
|
|
49
119
|
interface ConversationInfo {
|
|
@@ -52,6 +122,7 @@ interface ConversationInfo {
|
|
|
52
122
|
createdAt: string;
|
|
53
123
|
updatedAt: string;
|
|
54
124
|
messageCount: number;
|
|
125
|
+
agentId?: string | null;
|
|
55
126
|
}
|
|
56
127
|
interface ConversationDetail extends ConversationInfo {
|
|
57
128
|
messages: ChatMessage[];
|
|
@@ -67,9 +138,21 @@ interface FloatingWindowConfig {
|
|
|
67
138
|
position?: WindowPosition;
|
|
68
139
|
width?: number;
|
|
69
140
|
height?: number;
|
|
141
|
+
/** Minimum window width (default: 320) */
|
|
142
|
+
minWidth?: number;
|
|
143
|
+
/** Minimum window height (default: 420) */
|
|
144
|
+
minHeight?: number;
|
|
70
145
|
alwaysOnTop?: boolean;
|
|
71
146
|
showInTaskbar?: boolean;
|
|
72
147
|
theme?: 'light' | 'dark' | 'system';
|
|
148
|
+
/** Optional UI config for renderer (serializable) */
|
|
149
|
+
uiConfig?: ChatUiConfigSerializable;
|
|
150
|
+
/** Persist size/position across sessions (stored under ~/.sanqian-chat) */
|
|
151
|
+
rememberWindowState?: boolean;
|
|
152
|
+
/** Optional storage key to avoid conflicts between apps */
|
|
153
|
+
windowStateKey?: string;
|
|
154
|
+
/** Optional full path for window state file */
|
|
155
|
+
windowStatePath?: string;
|
|
73
156
|
}
|
|
74
157
|
|
|
75
158
|
/**
|
|
@@ -81,6 +164,10 @@ interface FloatingWindowConfig {
|
|
|
81
164
|
|
|
82
165
|
/** Stream event callback */
|
|
83
166
|
type StreamEvent = {
|
|
167
|
+
type: 'start';
|
|
168
|
+
run_id: string;
|
|
169
|
+
conversationId?: string;
|
|
170
|
+
} | {
|
|
84
171
|
type: 'text';
|
|
85
172
|
content: string;
|
|
86
173
|
} | {
|
|
@@ -95,6 +182,16 @@ type StreamEvent = {
|
|
|
95
182
|
arguments: string;
|
|
96
183
|
};
|
|
97
184
|
};
|
|
185
|
+
} | {
|
|
186
|
+
type: 'tool_args_chunk';
|
|
187
|
+
tool_call_id: string;
|
|
188
|
+
tool_name?: string;
|
|
189
|
+
chunk: string;
|
|
190
|
+
} | {
|
|
191
|
+
type: 'tool_args';
|
|
192
|
+
tool_call_id: string;
|
|
193
|
+
tool_name?: string;
|
|
194
|
+
args: Record<string, unknown>;
|
|
98
195
|
} | {
|
|
99
196
|
type: 'tool_result';
|
|
100
197
|
tool_call_id: string;
|
|
@@ -111,6 +208,9 @@ type StreamEvent = {
|
|
|
111
208
|
interrupt_type: string;
|
|
112
209
|
interrupt_payload: HitlInterruptData;
|
|
113
210
|
run_id?: string;
|
|
211
|
+
} | {
|
|
212
|
+
type: 'cancelled';
|
|
213
|
+
run_id?: string;
|
|
114
214
|
};
|
|
115
215
|
/** Message to send */
|
|
116
216
|
interface SendMessage {
|
|
@@ -135,7 +235,9 @@ interface ChatAdapter {
|
|
|
135
235
|
messageLimit?: number;
|
|
136
236
|
}): Promise<ConversationDetail>;
|
|
137
237
|
deleteConversation(id: string): Promise<void>;
|
|
138
|
-
chatStream(messages: SendMessage[], conversationId: string | undefined, onEvent: (event: StreamEvent) => void
|
|
238
|
+
chatStream(messages: SendMessage[], conversationId: string | undefined, onEvent: (event: StreamEvent) => void, options?: {
|
|
239
|
+
agentId?: string | null;
|
|
240
|
+
}): Promise<{
|
|
139
241
|
cancel: () => void;
|
|
140
242
|
}>;
|
|
141
243
|
sendHitlResponse?(response: HitlResponse, runId?: string): void;
|
|
@@ -148,9 +250,81 @@ interface SdkAdapterConfig {
|
|
|
148
250
|
/** Agent ID getter */
|
|
149
251
|
getAgentId: () => string | null;
|
|
150
252
|
}
|
|
253
|
+
/** Simple chat adapter config (no SDK knowledge required) */
|
|
254
|
+
interface ChatAdapterConfig {
|
|
255
|
+
/** App name for registration */
|
|
256
|
+
appName: string;
|
|
257
|
+
/** App version */
|
|
258
|
+
appVersion: string;
|
|
259
|
+
/** Agent ID to chat with */
|
|
260
|
+
agentId: string;
|
|
261
|
+
/** Whether to persist conversation history (default: false) */
|
|
262
|
+
persistHistory?: boolean;
|
|
263
|
+
/** Tools to expose to the agent */
|
|
264
|
+
tools?: Array<{
|
|
265
|
+
name: string;
|
|
266
|
+
description: string;
|
|
267
|
+
parameters?: Record<string, unknown>;
|
|
268
|
+
handler?: (args: Record<string, unknown>) => Promise<unknown>;
|
|
269
|
+
}>;
|
|
270
|
+
}
|
|
271
|
+
/**
|
|
272
|
+
* Create a chat adapter with simple config (recommended for most use cases)
|
|
273
|
+
*
|
|
274
|
+
* This is the easiest way to integrate with Sanqian. Just provide your app info
|
|
275
|
+
* and agent ID, and the adapter handles SDK lifecycle automatically.
|
|
276
|
+
*
|
|
277
|
+
* @example
|
|
278
|
+
* ```typescript
|
|
279
|
+
* const adapter = createChatAdapter({
|
|
280
|
+
* appName: 'my-app',
|
|
281
|
+
* appVersion: '1.0.0',
|
|
282
|
+
* agentId: 'assistant',
|
|
283
|
+
* });
|
|
284
|
+
*
|
|
285
|
+
* <CompactChat adapter={adapter} />
|
|
286
|
+
* ```
|
|
287
|
+
*/
|
|
288
|
+
declare function createChatAdapter(config: ChatAdapterConfig): ChatAdapter;
|
|
151
289
|
/**
|
|
152
290
|
* Create adapter that wraps @yushaw/sanqian-sdk
|
|
291
|
+
*
|
|
292
|
+
* Use this when you need more control over the SDK instance.
|
|
293
|
+
* For simpler usage, prefer `createChatAdapter()`.
|
|
153
294
|
*/
|
|
154
295
|
declare function createSdkAdapter(config: SdkAdapterConfig): ChatAdapter;
|
|
155
296
|
|
|
156
|
-
|
|
297
|
+
/**
|
|
298
|
+
* Conversation history helpers
|
|
299
|
+
*
|
|
300
|
+
* Normalizes SDK conversation messages into ChatMessage[] with
|
|
301
|
+
* tool call blocks and merged assistant/tool rounds.
|
|
302
|
+
*
|
|
303
|
+
* NOTE: This logic is intentionally kept in sync with
|
|
304
|
+
* src/renderer/src/utils/chat-helpers.ts in the main app.
|
|
305
|
+
*/
|
|
306
|
+
|
|
307
|
+
interface ApiMessage {
|
|
308
|
+
id?: string;
|
|
309
|
+
role: string;
|
|
310
|
+
content: string;
|
|
311
|
+
created_at?: string;
|
|
312
|
+
timestamp?: string;
|
|
313
|
+
tool_calls?: unknown;
|
|
314
|
+
toolCalls?: unknown;
|
|
315
|
+
tool_call_id?: string | null;
|
|
316
|
+
thinking?: string | null;
|
|
317
|
+
filePaths?: string[];
|
|
318
|
+
message_id?: number;
|
|
319
|
+
}
|
|
320
|
+
/**
|
|
321
|
+
* Parse tool_calls from backend format (OpenAI or simplified).
|
|
322
|
+
*/
|
|
323
|
+
declare function parseToolCalls(toolCalls: unknown): ToolCall[] | undefined;
|
|
324
|
+
/**
|
|
325
|
+
* Merge consecutive assistant + tool messages into single assistant messages,
|
|
326
|
+
* preserving blocks for intermediate steps.
|
|
327
|
+
*/
|
|
328
|
+
declare function mergeConsecutiveAssistantMessages(rawMessages: ApiMessage[]): ChatMessage[];
|
|
329
|
+
|
|
330
|
+
export { type ApiMessage, type ChatAdapter, type ChatAdapterConfig, type ChatFontSize, type ChatMessage, type ChatThemeMode, type ChatUiConfigSerializable, type ChatUiStrings, type ConnectionErrorCode, type ConnectionStatus, type ConversationDetail, type ConversationInfo, type FloatingWindowConfig, type HitlInterruptData, type Locale, type MessageBlock, type MessageRole, type SdkAdapterConfig, type SendMessage, type StreamEvent, type ToolCall, type ToolCallStatus, type WindowPosition, createChatAdapter, createSdkAdapter, mergeConsecutiveAssistantMessages, parseToolCalls };
|
package/dist/core/index.d.ts
CHANGED
|
@@ -7,16 +7,80 @@ export { ChatStreamEvent, HitlInterruptPayload, HitlInterruptType, HitlResponse,
|
|
|
7
7
|
* Re-exports SDK types + chat-specific types
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
|
-
type
|
|
10
|
+
type Locale = 'en' | 'zh';
|
|
11
|
+
interface ChatUiStrings {
|
|
12
|
+
inputPlaceholder: string;
|
|
13
|
+
inputSend: string;
|
|
14
|
+
inputStop: string;
|
|
15
|
+
chat: string;
|
|
16
|
+
recentChats: string;
|
|
17
|
+
newChat: string;
|
|
18
|
+
selectConversation: string;
|
|
19
|
+
noHistory: string;
|
|
20
|
+
loadMore: string;
|
|
21
|
+
today: string;
|
|
22
|
+
yesterday: string;
|
|
23
|
+
delete: string;
|
|
24
|
+
dismiss: string;
|
|
25
|
+
close: string;
|
|
26
|
+
pin: string;
|
|
27
|
+
unpin: string;
|
|
28
|
+
conversationUntitled: string;
|
|
29
|
+
conversationDeleteConfirm: string;
|
|
30
|
+
messageThinking: string;
|
|
31
|
+
messageError: string;
|
|
32
|
+
messageLoading: string;
|
|
33
|
+
connectionConnecting: string;
|
|
34
|
+
connectionConnected: string;
|
|
35
|
+
connectionDisconnected: string;
|
|
36
|
+
connectionReconnecting: string;
|
|
37
|
+
connectionError: string;
|
|
38
|
+
steps: string;
|
|
39
|
+
executing: string;
|
|
40
|
+
thinking: string;
|
|
41
|
+
thinkingStreaming: string;
|
|
42
|
+
thinkingPaused: string;
|
|
43
|
+
hitlApprove: string;
|
|
44
|
+
hitlReject: string;
|
|
45
|
+
hitlSubmit: string;
|
|
46
|
+
hitlCancel: string;
|
|
47
|
+
hitlRememberChoice: string;
|
|
48
|
+
hitlRequiredField: string;
|
|
49
|
+
hitlTimeoutIn: string;
|
|
50
|
+
hitlSeconds: string;
|
|
51
|
+
hitlExecuteTool: string;
|
|
52
|
+
hitlToolLabel: string;
|
|
53
|
+
hitlArgsLabel: string;
|
|
54
|
+
hitlDefaultPrefix: string;
|
|
55
|
+
hitlEnterResponse: string;
|
|
56
|
+
hitlApprovalRequest: string;
|
|
57
|
+
hitlInputRequest: string;
|
|
58
|
+
hitlApprovalRequired: string;
|
|
59
|
+
hitlInputRequired: string;
|
|
60
|
+
}
|
|
61
|
+
type ChatThemeMode = 'light' | 'dark' | 'auto';
|
|
62
|
+
type ChatFontSize = 'small' | 'normal' | 'large' | 'extra-large';
|
|
63
|
+
interface ChatUiConfigSerializable {
|
|
64
|
+
logo?: string;
|
|
65
|
+
theme?: ChatThemeMode;
|
|
66
|
+
/** Font size scale: small (13px), normal (14px), large (16px), extra-large (18px) */
|
|
67
|
+
fontSize?: ChatFontSize;
|
|
68
|
+
accentColor?: string;
|
|
69
|
+
locale?: Locale;
|
|
70
|
+
strings?: Partial<ChatUiStrings>;
|
|
71
|
+
alwaysOnTop?: boolean;
|
|
72
|
+
}
|
|
73
|
+
type MessageRole = 'user' | 'assistant' | 'system' | 'tool';
|
|
11
74
|
type ToolCallStatus = 'pending' | 'running' | 'completed' | 'error' | 'cancelled';
|
|
12
75
|
type ConnectionStatus = 'disconnected' | 'connecting' | 'connected' | 'reconnecting' | 'error';
|
|
13
76
|
type ConnectionErrorCode = 'NOT_FOUND' | 'CONNECTION_FAILED' | 'WEBSOCKET_ERROR' | 'AUTH_ERROR' | 'TIMEOUT' | 'UNKNOWN';
|
|
14
77
|
/** Tool call for UI rendering (extended from SDK) */
|
|
15
78
|
interface ToolCall {
|
|
16
|
-
id
|
|
79
|
+
id?: string;
|
|
17
80
|
name: string;
|
|
18
|
-
|
|
19
|
-
|
|
81
|
+
args?: Record<string, unknown>;
|
|
82
|
+
argsRaw?: string;
|
|
83
|
+
status?: ToolCallStatus;
|
|
20
84
|
result?: unknown;
|
|
21
85
|
error?: string;
|
|
22
86
|
}
|
|
@@ -27,9 +91,11 @@ interface MessageBlock {
|
|
|
27
91
|
timestamp: number;
|
|
28
92
|
toolName?: string;
|
|
29
93
|
toolArgs?: Record<string, unknown>;
|
|
94
|
+
toolArgsRaw?: string;
|
|
30
95
|
toolCallId?: string;
|
|
31
96
|
toolStatus?: ToolCallStatus;
|
|
32
97
|
isIntermediate?: boolean;
|
|
98
|
+
fromSubagent?: boolean;
|
|
33
99
|
}
|
|
34
100
|
/** Chat message for UI rendering */
|
|
35
101
|
interface ChatMessage {
|
|
@@ -42,8 +108,12 @@ interface ChatMessage {
|
|
|
42
108
|
thinking?: string;
|
|
43
109
|
currentThinking?: string;
|
|
44
110
|
isThinkingStreaming?: boolean;
|
|
111
|
+
isThinkingPaused?: boolean;
|
|
112
|
+
isToolCallsStreaming?: boolean;
|
|
45
113
|
blocks?: MessageBlock[];
|
|
114
|
+
finalContent?: string;
|
|
46
115
|
isComplete?: boolean;
|
|
116
|
+
filePaths?: string[];
|
|
47
117
|
}
|
|
48
118
|
/** Conversation info for UI */
|
|
49
119
|
interface ConversationInfo {
|
|
@@ -52,6 +122,7 @@ interface ConversationInfo {
|
|
|
52
122
|
createdAt: string;
|
|
53
123
|
updatedAt: string;
|
|
54
124
|
messageCount: number;
|
|
125
|
+
agentId?: string | null;
|
|
55
126
|
}
|
|
56
127
|
interface ConversationDetail extends ConversationInfo {
|
|
57
128
|
messages: ChatMessage[];
|
|
@@ -67,9 +138,21 @@ interface FloatingWindowConfig {
|
|
|
67
138
|
position?: WindowPosition;
|
|
68
139
|
width?: number;
|
|
69
140
|
height?: number;
|
|
141
|
+
/** Minimum window width (default: 320) */
|
|
142
|
+
minWidth?: number;
|
|
143
|
+
/** Minimum window height (default: 420) */
|
|
144
|
+
minHeight?: number;
|
|
70
145
|
alwaysOnTop?: boolean;
|
|
71
146
|
showInTaskbar?: boolean;
|
|
72
147
|
theme?: 'light' | 'dark' | 'system';
|
|
148
|
+
/** Optional UI config for renderer (serializable) */
|
|
149
|
+
uiConfig?: ChatUiConfigSerializable;
|
|
150
|
+
/** Persist size/position across sessions (stored under ~/.sanqian-chat) */
|
|
151
|
+
rememberWindowState?: boolean;
|
|
152
|
+
/** Optional storage key to avoid conflicts between apps */
|
|
153
|
+
windowStateKey?: string;
|
|
154
|
+
/** Optional full path for window state file */
|
|
155
|
+
windowStatePath?: string;
|
|
73
156
|
}
|
|
74
157
|
|
|
75
158
|
/**
|
|
@@ -81,6 +164,10 @@ interface FloatingWindowConfig {
|
|
|
81
164
|
|
|
82
165
|
/** Stream event callback */
|
|
83
166
|
type StreamEvent = {
|
|
167
|
+
type: 'start';
|
|
168
|
+
run_id: string;
|
|
169
|
+
conversationId?: string;
|
|
170
|
+
} | {
|
|
84
171
|
type: 'text';
|
|
85
172
|
content: string;
|
|
86
173
|
} | {
|
|
@@ -95,6 +182,16 @@ type StreamEvent = {
|
|
|
95
182
|
arguments: string;
|
|
96
183
|
};
|
|
97
184
|
};
|
|
185
|
+
} | {
|
|
186
|
+
type: 'tool_args_chunk';
|
|
187
|
+
tool_call_id: string;
|
|
188
|
+
tool_name?: string;
|
|
189
|
+
chunk: string;
|
|
190
|
+
} | {
|
|
191
|
+
type: 'tool_args';
|
|
192
|
+
tool_call_id: string;
|
|
193
|
+
tool_name?: string;
|
|
194
|
+
args: Record<string, unknown>;
|
|
98
195
|
} | {
|
|
99
196
|
type: 'tool_result';
|
|
100
197
|
tool_call_id: string;
|
|
@@ -111,6 +208,9 @@ type StreamEvent = {
|
|
|
111
208
|
interrupt_type: string;
|
|
112
209
|
interrupt_payload: HitlInterruptData;
|
|
113
210
|
run_id?: string;
|
|
211
|
+
} | {
|
|
212
|
+
type: 'cancelled';
|
|
213
|
+
run_id?: string;
|
|
114
214
|
};
|
|
115
215
|
/** Message to send */
|
|
116
216
|
interface SendMessage {
|
|
@@ -135,7 +235,9 @@ interface ChatAdapter {
|
|
|
135
235
|
messageLimit?: number;
|
|
136
236
|
}): Promise<ConversationDetail>;
|
|
137
237
|
deleteConversation(id: string): Promise<void>;
|
|
138
|
-
chatStream(messages: SendMessage[], conversationId: string | undefined, onEvent: (event: StreamEvent) => void
|
|
238
|
+
chatStream(messages: SendMessage[], conversationId: string | undefined, onEvent: (event: StreamEvent) => void, options?: {
|
|
239
|
+
agentId?: string | null;
|
|
240
|
+
}): Promise<{
|
|
139
241
|
cancel: () => void;
|
|
140
242
|
}>;
|
|
141
243
|
sendHitlResponse?(response: HitlResponse, runId?: string): void;
|
|
@@ -148,9 +250,81 @@ interface SdkAdapterConfig {
|
|
|
148
250
|
/** Agent ID getter */
|
|
149
251
|
getAgentId: () => string | null;
|
|
150
252
|
}
|
|
253
|
+
/** Simple chat adapter config (no SDK knowledge required) */
|
|
254
|
+
interface ChatAdapterConfig {
|
|
255
|
+
/** App name for registration */
|
|
256
|
+
appName: string;
|
|
257
|
+
/** App version */
|
|
258
|
+
appVersion: string;
|
|
259
|
+
/** Agent ID to chat with */
|
|
260
|
+
agentId: string;
|
|
261
|
+
/** Whether to persist conversation history (default: false) */
|
|
262
|
+
persistHistory?: boolean;
|
|
263
|
+
/** Tools to expose to the agent */
|
|
264
|
+
tools?: Array<{
|
|
265
|
+
name: string;
|
|
266
|
+
description: string;
|
|
267
|
+
parameters?: Record<string, unknown>;
|
|
268
|
+
handler?: (args: Record<string, unknown>) => Promise<unknown>;
|
|
269
|
+
}>;
|
|
270
|
+
}
|
|
271
|
+
/**
|
|
272
|
+
* Create a chat adapter with simple config (recommended for most use cases)
|
|
273
|
+
*
|
|
274
|
+
* This is the easiest way to integrate with Sanqian. Just provide your app info
|
|
275
|
+
* and agent ID, and the adapter handles SDK lifecycle automatically.
|
|
276
|
+
*
|
|
277
|
+
* @example
|
|
278
|
+
* ```typescript
|
|
279
|
+
* const adapter = createChatAdapter({
|
|
280
|
+
* appName: 'my-app',
|
|
281
|
+
* appVersion: '1.0.0',
|
|
282
|
+
* agentId: 'assistant',
|
|
283
|
+
* });
|
|
284
|
+
*
|
|
285
|
+
* <CompactChat adapter={adapter} />
|
|
286
|
+
* ```
|
|
287
|
+
*/
|
|
288
|
+
declare function createChatAdapter(config: ChatAdapterConfig): ChatAdapter;
|
|
151
289
|
/**
|
|
152
290
|
* Create adapter that wraps @yushaw/sanqian-sdk
|
|
291
|
+
*
|
|
292
|
+
* Use this when you need more control over the SDK instance.
|
|
293
|
+
* For simpler usage, prefer `createChatAdapter()`.
|
|
153
294
|
*/
|
|
154
295
|
declare function createSdkAdapter(config: SdkAdapterConfig): ChatAdapter;
|
|
155
296
|
|
|
156
|
-
|
|
297
|
+
/**
|
|
298
|
+
* Conversation history helpers
|
|
299
|
+
*
|
|
300
|
+
* Normalizes SDK conversation messages into ChatMessage[] with
|
|
301
|
+
* tool call blocks and merged assistant/tool rounds.
|
|
302
|
+
*
|
|
303
|
+
* NOTE: This logic is intentionally kept in sync with
|
|
304
|
+
* src/renderer/src/utils/chat-helpers.ts in the main app.
|
|
305
|
+
*/
|
|
306
|
+
|
|
307
|
+
interface ApiMessage {
|
|
308
|
+
id?: string;
|
|
309
|
+
role: string;
|
|
310
|
+
content: string;
|
|
311
|
+
created_at?: string;
|
|
312
|
+
timestamp?: string;
|
|
313
|
+
tool_calls?: unknown;
|
|
314
|
+
toolCalls?: unknown;
|
|
315
|
+
tool_call_id?: string | null;
|
|
316
|
+
thinking?: string | null;
|
|
317
|
+
filePaths?: string[];
|
|
318
|
+
message_id?: number;
|
|
319
|
+
}
|
|
320
|
+
/**
|
|
321
|
+
* Parse tool_calls from backend format (OpenAI or simplified).
|
|
322
|
+
*/
|
|
323
|
+
declare function parseToolCalls(toolCalls: unknown): ToolCall[] | undefined;
|
|
324
|
+
/**
|
|
325
|
+
* Merge consecutive assistant + tool messages into single assistant messages,
|
|
326
|
+
* preserving blocks for intermediate steps.
|
|
327
|
+
*/
|
|
328
|
+
declare function mergeConsecutiveAssistantMessages(rawMessages: ApiMessage[]): ChatMessage[];
|
|
329
|
+
|
|
330
|
+
export { type ApiMessage, type ChatAdapter, type ChatAdapterConfig, type ChatFontSize, type ChatMessage, type ChatThemeMode, type ChatUiConfigSerializable, type ChatUiStrings, type ConnectionErrorCode, type ConnectionStatus, type ConversationDetail, type ConversationInfo, type FloatingWindowConfig, type HitlInterruptData, type Locale, type MessageBlock, type MessageRole, type SdkAdapterConfig, type SendMessage, type StreamEvent, type ToolCall, type ToolCallStatus, type WindowPosition, createChatAdapter, createSdkAdapter, mergeConsecutiveAssistantMessages, parseToolCalls };
|