@yushaw/sanqian-chat 0.1.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/core/index.d.mts +156 -0
- package/dist/core/index.d.ts +156 -0
- package/dist/core/index.js +176 -0
- package/dist/core/index.mjs +149 -0
- package/dist/main/index.d.mts +58 -0
- package/dist/main/index.d.ts +58 -0
- package/dist/main/index.js +299 -0
- package/dist/main/index.mjs +272 -0
- package/dist/preload/index.d.ts +67 -0
- package/dist/preload/index.js +38 -0
- package/dist/renderer/index.d.mts +340 -0
- package/dist/renderer/index.d.ts +340 -0
- package/dist/renderer/index.js +1171 -0
- package/dist/renderer/index.mjs +1131 -0
- package/package.json +84 -0
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
import { HitlInterruptPayload, HitlResponse, SanqianSDK } from '@yushaw/sanqian-sdk';
|
|
2
|
+
export { ChatStreamEvent, HitlInterruptPayload, HitlInterruptType, HitlResponse, HitlRiskLevel, ChatMessage as SdkChatMessage, ConversationDetail as SdkConversationDetail, ConversationInfo as SdkConversationInfo, ToolCall as SdkToolCall } from '@yushaw/sanqian-sdk';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* @yushaw/sanqian-chat Core Types
|
|
6
|
+
*
|
|
7
|
+
* Re-exports SDK types + chat-specific types
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
type MessageRole = 'user' | 'assistant' | 'system';
|
|
11
|
+
type ToolCallStatus = 'pending' | 'running' | 'completed' | 'error' | 'cancelled';
|
|
12
|
+
type ConnectionStatus = 'disconnected' | 'connecting' | 'connected' | 'reconnecting' | 'error';
|
|
13
|
+
type ConnectionErrorCode = 'NOT_FOUND' | 'CONNECTION_FAILED' | 'WEBSOCKET_ERROR' | 'AUTH_ERROR' | 'TIMEOUT' | 'UNKNOWN';
|
|
14
|
+
/** Tool call for UI rendering (extended from SDK) */
|
|
15
|
+
interface ToolCall {
|
|
16
|
+
id: string;
|
|
17
|
+
name: string;
|
|
18
|
+
arguments: Record<string, unknown>;
|
|
19
|
+
status: ToolCallStatus;
|
|
20
|
+
result?: unknown;
|
|
21
|
+
error?: string;
|
|
22
|
+
}
|
|
23
|
+
/** Message block for structured rendering */
|
|
24
|
+
interface MessageBlock {
|
|
25
|
+
type: 'thinking' | 'text' | 'tool_call' | 'tool_result';
|
|
26
|
+
content: string;
|
|
27
|
+
timestamp: number;
|
|
28
|
+
toolName?: string;
|
|
29
|
+
toolArgs?: Record<string, unknown>;
|
|
30
|
+
toolCallId?: string;
|
|
31
|
+
toolStatus?: ToolCallStatus;
|
|
32
|
+
isIntermediate?: boolean;
|
|
33
|
+
}
|
|
34
|
+
/** Chat message for UI rendering */
|
|
35
|
+
interface ChatMessage {
|
|
36
|
+
id: string;
|
|
37
|
+
role: MessageRole;
|
|
38
|
+
content: string;
|
|
39
|
+
timestamp: string;
|
|
40
|
+
isStreaming?: boolean;
|
|
41
|
+
toolCalls?: ToolCall[];
|
|
42
|
+
thinking?: string;
|
|
43
|
+
currentThinking?: string;
|
|
44
|
+
isThinkingStreaming?: boolean;
|
|
45
|
+
blocks?: MessageBlock[];
|
|
46
|
+
isComplete?: boolean;
|
|
47
|
+
}
|
|
48
|
+
/** Conversation info for UI */
|
|
49
|
+
interface ConversationInfo {
|
|
50
|
+
id: string;
|
|
51
|
+
title: string;
|
|
52
|
+
createdAt: string;
|
|
53
|
+
updatedAt: string;
|
|
54
|
+
messageCount: number;
|
|
55
|
+
}
|
|
56
|
+
interface ConversationDetail extends ConversationInfo {
|
|
57
|
+
messages: ChatMessage[];
|
|
58
|
+
}
|
|
59
|
+
/** HITL interrupt data for UI (alias for HitlInterruptPayload) */
|
|
60
|
+
type HitlInterruptData = HitlInterruptPayload;
|
|
61
|
+
type WindowPosition = 'center' | 'cursor' | 'remember' | {
|
|
62
|
+
x: number;
|
|
63
|
+
y: number;
|
|
64
|
+
};
|
|
65
|
+
interface FloatingWindowConfig {
|
|
66
|
+
shortcut?: string;
|
|
67
|
+
position?: WindowPosition;
|
|
68
|
+
width?: number;
|
|
69
|
+
height?: number;
|
|
70
|
+
alwaysOnTop?: boolean;
|
|
71
|
+
showInTaskbar?: boolean;
|
|
72
|
+
theme?: 'light' | 'dark' | 'system';
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Chat Adapter Interface
|
|
77
|
+
*
|
|
78
|
+
* Abstracts the connection to Sanqian backend.
|
|
79
|
+
* Default implementation uses @yushaw/sanqian-sdk directly.
|
|
80
|
+
*/
|
|
81
|
+
|
|
82
|
+
/** Stream event callback */
|
|
83
|
+
type StreamEvent = {
|
|
84
|
+
type: 'text';
|
|
85
|
+
content: string;
|
|
86
|
+
} | {
|
|
87
|
+
type: 'thinking';
|
|
88
|
+
content: string;
|
|
89
|
+
} | {
|
|
90
|
+
type: 'tool_call';
|
|
91
|
+
tool_call: {
|
|
92
|
+
id: string;
|
|
93
|
+
function: {
|
|
94
|
+
name: string;
|
|
95
|
+
arguments: string;
|
|
96
|
+
};
|
|
97
|
+
};
|
|
98
|
+
} | {
|
|
99
|
+
type: 'tool_result';
|
|
100
|
+
tool_call_id: string;
|
|
101
|
+
result: unknown;
|
|
102
|
+
} | {
|
|
103
|
+
type: 'done';
|
|
104
|
+
conversationId: string;
|
|
105
|
+
title?: string;
|
|
106
|
+
} | {
|
|
107
|
+
type: 'error';
|
|
108
|
+
error: string;
|
|
109
|
+
} | {
|
|
110
|
+
type: 'interrupt';
|
|
111
|
+
interrupt_type: string;
|
|
112
|
+
interrupt_payload: HitlInterruptData;
|
|
113
|
+
run_id?: string;
|
|
114
|
+
};
|
|
115
|
+
/** Message to send */
|
|
116
|
+
interface SendMessage {
|
|
117
|
+
role: 'user' | 'assistant';
|
|
118
|
+
content: string;
|
|
119
|
+
}
|
|
120
|
+
/** Chat adapter interface */
|
|
121
|
+
interface ChatAdapter {
|
|
122
|
+
connect(): Promise<void>;
|
|
123
|
+
disconnect(): Promise<void>;
|
|
124
|
+
isConnected(): boolean;
|
|
125
|
+
getConnectionStatus(): ConnectionStatus;
|
|
126
|
+
onConnectionChange(callback: (status: ConnectionStatus, error?: string, errorCode?: ConnectionErrorCode) => void): () => void;
|
|
127
|
+
listConversations(options?: {
|
|
128
|
+
limit?: number;
|
|
129
|
+
offset?: number;
|
|
130
|
+
}): Promise<{
|
|
131
|
+
conversations: ConversationInfo[];
|
|
132
|
+
total: number;
|
|
133
|
+
}>;
|
|
134
|
+
getConversation(id: string, options?: {
|
|
135
|
+
messageLimit?: number;
|
|
136
|
+
}): Promise<ConversationDetail>;
|
|
137
|
+
deleteConversation(id: string): Promise<void>;
|
|
138
|
+
chatStream(messages: SendMessage[], conversationId: string | undefined, onEvent: (event: StreamEvent) => void): Promise<{
|
|
139
|
+
cancel: () => void;
|
|
140
|
+
}>;
|
|
141
|
+
sendHitlResponse?(response: HitlResponse, runId?: string): void;
|
|
142
|
+
cleanup?(): void;
|
|
143
|
+
}
|
|
144
|
+
/** SDK adapter config */
|
|
145
|
+
interface SdkAdapterConfig {
|
|
146
|
+
/** SDK instance getter */
|
|
147
|
+
getSdk: () => SanqianSDK | null;
|
|
148
|
+
/** Agent ID getter */
|
|
149
|
+
getAgentId: () => string | null;
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* Create adapter that wraps @yushaw/sanqian-sdk
|
|
153
|
+
*/
|
|
154
|
+
declare function createSdkAdapter(config: SdkAdapterConfig): ChatAdapter;
|
|
155
|
+
|
|
156
|
+
export { type ChatAdapter, type ChatMessage, type ConnectionErrorCode, type ConnectionStatus, type ConversationDetail, type ConversationInfo, type FloatingWindowConfig, type HitlInterruptData, type MessageBlock, type MessageRole, type SdkAdapterConfig, type SendMessage, type StreamEvent, type ToolCall, type ToolCallStatus, type WindowPosition, createSdkAdapter };
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
import { HitlInterruptPayload, HitlResponse, SanqianSDK } from '@yushaw/sanqian-sdk';
|
|
2
|
+
export { ChatStreamEvent, HitlInterruptPayload, HitlInterruptType, HitlResponse, HitlRiskLevel, ChatMessage as SdkChatMessage, ConversationDetail as SdkConversationDetail, ConversationInfo as SdkConversationInfo, ToolCall as SdkToolCall } from '@yushaw/sanqian-sdk';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* @yushaw/sanqian-chat Core Types
|
|
6
|
+
*
|
|
7
|
+
* Re-exports SDK types + chat-specific types
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
type MessageRole = 'user' | 'assistant' | 'system';
|
|
11
|
+
type ToolCallStatus = 'pending' | 'running' | 'completed' | 'error' | 'cancelled';
|
|
12
|
+
type ConnectionStatus = 'disconnected' | 'connecting' | 'connected' | 'reconnecting' | 'error';
|
|
13
|
+
type ConnectionErrorCode = 'NOT_FOUND' | 'CONNECTION_FAILED' | 'WEBSOCKET_ERROR' | 'AUTH_ERROR' | 'TIMEOUT' | 'UNKNOWN';
|
|
14
|
+
/** Tool call for UI rendering (extended from SDK) */
|
|
15
|
+
interface ToolCall {
|
|
16
|
+
id: string;
|
|
17
|
+
name: string;
|
|
18
|
+
arguments: Record<string, unknown>;
|
|
19
|
+
status: ToolCallStatus;
|
|
20
|
+
result?: unknown;
|
|
21
|
+
error?: string;
|
|
22
|
+
}
|
|
23
|
+
/** Message block for structured rendering */
|
|
24
|
+
interface MessageBlock {
|
|
25
|
+
type: 'thinking' | 'text' | 'tool_call' | 'tool_result';
|
|
26
|
+
content: string;
|
|
27
|
+
timestamp: number;
|
|
28
|
+
toolName?: string;
|
|
29
|
+
toolArgs?: Record<string, unknown>;
|
|
30
|
+
toolCallId?: string;
|
|
31
|
+
toolStatus?: ToolCallStatus;
|
|
32
|
+
isIntermediate?: boolean;
|
|
33
|
+
}
|
|
34
|
+
/** Chat message for UI rendering */
|
|
35
|
+
interface ChatMessage {
|
|
36
|
+
id: string;
|
|
37
|
+
role: MessageRole;
|
|
38
|
+
content: string;
|
|
39
|
+
timestamp: string;
|
|
40
|
+
isStreaming?: boolean;
|
|
41
|
+
toolCalls?: ToolCall[];
|
|
42
|
+
thinking?: string;
|
|
43
|
+
currentThinking?: string;
|
|
44
|
+
isThinkingStreaming?: boolean;
|
|
45
|
+
blocks?: MessageBlock[];
|
|
46
|
+
isComplete?: boolean;
|
|
47
|
+
}
|
|
48
|
+
/** Conversation info for UI */
|
|
49
|
+
interface ConversationInfo {
|
|
50
|
+
id: string;
|
|
51
|
+
title: string;
|
|
52
|
+
createdAt: string;
|
|
53
|
+
updatedAt: string;
|
|
54
|
+
messageCount: number;
|
|
55
|
+
}
|
|
56
|
+
interface ConversationDetail extends ConversationInfo {
|
|
57
|
+
messages: ChatMessage[];
|
|
58
|
+
}
|
|
59
|
+
/** HITL interrupt data for UI (alias for HitlInterruptPayload) */
|
|
60
|
+
type HitlInterruptData = HitlInterruptPayload;
|
|
61
|
+
type WindowPosition = 'center' | 'cursor' | 'remember' | {
|
|
62
|
+
x: number;
|
|
63
|
+
y: number;
|
|
64
|
+
};
|
|
65
|
+
interface FloatingWindowConfig {
|
|
66
|
+
shortcut?: string;
|
|
67
|
+
position?: WindowPosition;
|
|
68
|
+
width?: number;
|
|
69
|
+
height?: number;
|
|
70
|
+
alwaysOnTop?: boolean;
|
|
71
|
+
showInTaskbar?: boolean;
|
|
72
|
+
theme?: 'light' | 'dark' | 'system';
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Chat Adapter Interface
|
|
77
|
+
*
|
|
78
|
+
* Abstracts the connection to Sanqian backend.
|
|
79
|
+
* Default implementation uses @yushaw/sanqian-sdk directly.
|
|
80
|
+
*/
|
|
81
|
+
|
|
82
|
+
/** Stream event callback */
|
|
83
|
+
type StreamEvent = {
|
|
84
|
+
type: 'text';
|
|
85
|
+
content: string;
|
|
86
|
+
} | {
|
|
87
|
+
type: 'thinking';
|
|
88
|
+
content: string;
|
|
89
|
+
} | {
|
|
90
|
+
type: 'tool_call';
|
|
91
|
+
tool_call: {
|
|
92
|
+
id: string;
|
|
93
|
+
function: {
|
|
94
|
+
name: string;
|
|
95
|
+
arguments: string;
|
|
96
|
+
};
|
|
97
|
+
};
|
|
98
|
+
} | {
|
|
99
|
+
type: 'tool_result';
|
|
100
|
+
tool_call_id: string;
|
|
101
|
+
result: unknown;
|
|
102
|
+
} | {
|
|
103
|
+
type: 'done';
|
|
104
|
+
conversationId: string;
|
|
105
|
+
title?: string;
|
|
106
|
+
} | {
|
|
107
|
+
type: 'error';
|
|
108
|
+
error: string;
|
|
109
|
+
} | {
|
|
110
|
+
type: 'interrupt';
|
|
111
|
+
interrupt_type: string;
|
|
112
|
+
interrupt_payload: HitlInterruptData;
|
|
113
|
+
run_id?: string;
|
|
114
|
+
};
|
|
115
|
+
/** Message to send */
|
|
116
|
+
interface SendMessage {
|
|
117
|
+
role: 'user' | 'assistant';
|
|
118
|
+
content: string;
|
|
119
|
+
}
|
|
120
|
+
/** Chat adapter interface */
|
|
121
|
+
interface ChatAdapter {
|
|
122
|
+
connect(): Promise<void>;
|
|
123
|
+
disconnect(): Promise<void>;
|
|
124
|
+
isConnected(): boolean;
|
|
125
|
+
getConnectionStatus(): ConnectionStatus;
|
|
126
|
+
onConnectionChange(callback: (status: ConnectionStatus, error?: string, errorCode?: ConnectionErrorCode) => void): () => void;
|
|
127
|
+
listConversations(options?: {
|
|
128
|
+
limit?: number;
|
|
129
|
+
offset?: number;
|
|
130
|
+
}): Promise<{
|
|
131
|
+
conversations: ConversationInfo[];
|
|
132
|
+
total: number;
|
|
133
|
+
}>;
|
|
134
|
+
getConversation(id: string, options?: {
|
|
135
|
+
messageLimit?: number;
|
|
136
|
+
}): Promise<ConversationDetail>;
|
|
137
|
+
deleteConversation(id: string): Promise<void>;
|
|
138
|
+
chatStream(messages: SendMessage[], conversationId: string | undefined, onEvent: (event: StreamEvent) => void): Promise<{
|
|
139
|
+
cancel: () => void;
|
|
140
|
+
}>;
|
|
141
|
+
sendHitlResponse?(response: HitlResponse, runId?: string): void;
|
|
142
|
+
cleanup?(): void;
|
|
143
|
+
}
|
|
144
|
+
/** SDK adapter config */
|
|
145
|
+
interface SdkAdapterConfig {
|
|
146
|
+
/** SDK instance getter */
|
|
147
|
+
getSdk: () => SanqianSDK | null;
|
|
148
|
+
/** Agent ID getter */
|
|
149
|
+
getAgentId: () => string | null;
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* Create adapter that wraps @yushaw/sanqian-sdk
|
|
153
|
+
*/
|
|
154
|
+
declare function createSdkAdapter(config: SdkAdapterConfig): ChatAdapter;
|
|
155
|
+
|
|
156
|
+
export { type ChatAdapter, type ChatMessage, type ConnectionErrorCode, type ConnectionStatus, type ConversationDetail, type ConversationInfo, type FloatingWindowConfig, type HitlInterruptData, type MessageBlock, type MessageRole, type SdkAdapterConfig, type SendMessage, type StreamEvent, type ToolCall, type ToolCallStatus, type WindowPosition, createSdkAdapter };
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/core/index.ts
|
|
21
|
+
var core_exports = {};
|
|
22
|
+
__export(core_exports, {
|
|
23
|
+
createSdkAdapter: () => createSdkAdapter
|
|
24
|
+
});
|
|
25
|
+
module.exports = __toCommonJS(core_exports);
|
|
26
|
+
|
|
27
|
+
// src/core/adapter.ts
|
|
28
|
+
function createSdkAdapter(config) {
|
|
29
|
+
let connectionStatus = "disconnected";
|
|
30
|
+
const connectionListeners = /* @__PURE__ */ new Set();
|
|
31
|
+
let currentRunId = null;
|
|
32
|
+
const updateStatus = (status, error, errorCode) => {
|
|
33
|
+
connectionStatus = status;
|
|
34
|
+
connectionListeners.forEach((cb) => cb(status, error, errorCode));
|
|
35
|
+
};
|
|
36
|
+
return {
|
|
37
|
+
async connect() {
|
|
38
|
+
const sdk = config.getSdk();
|
|
39
|
+
if (!sdk) throw new Error("SDK not available");
|
|
40
|
+
updateStatus("connecting");
|
|
41
|
+
try {
|
|
42
|
+
await sdk.ensureReady();
|
|
43
|
+
updateStatus("connected");
|
|
44
|
+
} catch (e) {
|
|
45
|
+
updateStatus("error", e instanceof Error ? e.message : "Connection failed", "CONNECTION_FAILED");
|
|
46
|
+
throw e;
|
|
47
|
+
}
|
|
48
|
+
},
|
|
49
|
+
async disconnect() {
|
|
50
|
+
updateStatus("disconnected");
|
|
51
|
+
},
|
|
52
|
+
isConnected() {
|
|
53
|
+
const sdk = config.getSdk();
|
|
54
|
+
return sdk?.isConnected() ?? false;
|
|
55
|
+
},
|
|
56
|
+
getConnectionStatus() {
|
|
57
|
+
return connectionStatus;
|
|
58
|
+
},
|
|
59
|
+
onConnectionChange(callback) {
|
|
60
|
+
connectionListeners.add(callback);
|
|
61
|
+
callback(connectionStatus);
|
|
62
|
+
return () => connectionListeners.delete(callback);
|
|
63
|
+
},
|
|
64
|
+
async listConversations(options) {
|
|
65
|
+
const sdk = config.getSdk();
|
|
66
|
+
const agentId = config.getAgentId();
|
|
67
|
+
if (!sdk || !agentId) throw new Error("SDK or agent not ready");
|
|
68
|
+
const result = await sdk.listConversations({ agentId, ...options });
|
|
69
|
+
return {
|
|
70
|
+
conversations: result.conversations.map((c) => ({
|
|
71
|
+
id: c.conversation_id,
|
|
72
|
+
title: c.title || "Untitled",
|
|
73
|
+
createdAt: c.created_at || "",
|
|
74
|
+
updatedAt: c.updated_at || "",
|
|
75
|
+
messageCount: c.message_count
|
|
76
|
+
})),
|
|
77
|
+
total: result.total
|
|
78
|
+
};
|
|
79
|
+
},
|
|
80
|
+
async getConversation(id, options) {
|
|
81
|
+
const sdk = config.getSdk();
|
|
82
|
+
if (!sdk) throw new Error("SDK not ready");
|
|
83
|
+
const detail = await sdk.getConversation(id, { messageLimit: options?.messageLimit });
|
|
84
|
+
return {
|
|
85
|
+
id: detail.conversation_id,
|
|
86
|
+
title: detail.title || "Untitled",
|
|
87
|
+
createdAt: detail.created_at || "",
|
|
88
|
+
updatedAt: detail.updated_at || "",
|
|
89
|
+
messageCount: detail.message_count,
|
|
90
|
+
messages: (detail.messages || []).map((m, i) => ({
|
|
91
|
+
id: `msg-${i}`,
|
|
92
|
+
role: m.role,
|
|
93
|
+
content: m.content,
|
|
94
|
+
timestamp: m.created_at || (/* @__PURE__ */ new Date()).toISOString()
|
|
95
|
+
}))
|
|
96
|
+
};
|
|
97
|
+
},
|
|
98
|
+
async deleteConversation(id) {
|
|
99
|
+
const sdk = config.getSdk();
|
|
100
|
+
if (!sdk) throw new Error("SDK not ready");
|
|
101
|
+
await sdk.deleteConversation(id);
|
|
102
|
+
},
|
|
103
|
+
async chatStream(messages, conversationId, onEvent) {
|
|
104
|
+
const sdk = config.getSdk();
|
|
105
|
+
const agentId = config.getAgentId();
|
|
106
|
+
if (!sdk || !agentId) throw new Error("SDK or agent not ready");
|
|
107
|
+
await sdk.ensureReady();
|
|
108
|
+
const sdkMessages = messages.map((m) => ({ role: m.role, content: m.content }));
|
|
109
|
+
const stream = sdk.chatStream(agentId, sdkMessages, { conversationId });
|
|
110
|
+
const controller = new AbortController();
|
|
111
|
+
const signal = controller.signal;
|
|
112
|
+
(async () => {
|
|
113
|
+
try {
|
|
114
|
+
for await (const event of stream) {
|
|
115
|
+
if (signal.aborted) break;
|
|
116
|
+
switch (event.type) {
|
|
117
|
+
case "text":
|
|
118
|
+
onEvent({ type: "text", content: event.content || "" });
|
|
119
|
+
break;
|
|
120
|
+
case "thinking":
|
|
121
|
+
onEvent({ type: "thinking", content: event.content || "" });
|
|
122
|
+
break;
|
|
123
|
+
case "tool_call":
|
|
124
|
+
if (event.tool_call) {
|
|
125
|
+
onEvent({ type: "tool_call", tool_call: event.tool_call });
|
|
126
|
+
}
|
|
127
|
+
break;
|
|
128
|
+
case "tool_result":
|
|
129
|
+
onEvent({ type: "tool_result", tool_call_id: event.tool_call_id || "", result: event.result });
|
|
130
|
+
break;
|
|
131
|
+
case "done":
|
|
132
|
+
onEvent({ type: "done", conversationId: event.conversationId || "", title: event.title });
|
|
133
|
+
break;
|
|
134
|
+
case "error":
|
|
135
|
+
onEvent({ type: "error", error: event.error || "Unknown error" });
|
|
136
|
+
break;
|
|
137
|
+
default:
|
|
138
|
+
const evt = event;
|
|
139
|
+
if (evt.type === "interrupt") {
|
|
140
|
+
currentRunId = evt.run_id || null;
|
|
141
|
+
onEvent({
|
|
142
|
+
type: "interrupt",
|
|
143
|
+
interrupt_type: evt.interrupt_type || "",
|
|
144
|
+
interrupt_payload: evt.interrupt_payload,
|
|
145
|
+
run_id: evt.run_id
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
break;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
} catch (e) {
|
|
152
|
+
if (!signal.aborted) {
|
|
153
|
+
onEvent({ type: "error", error: e instanceof Error ? e.message : "Stream error" });
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
})();
|
|
157
|
+
return {
|
|
158
|
+
cancel: () => {
|
|
159
|
+
controller.abort();
|
|
160
|
+
}
|
|
161
|
+
};
|
|
162
|
+
},
|
|
163
|
+
sendHitlResponse(response, runId) {
|
|
164
|
+
const sdk = config.getSdk();
|
|
165
|
+
if (!sdk) return;
|
|
166
|
+
const id = runId || currentRunId;
|
|
167
|
+
if (id) {
|
|
168
|
+
sdk.sendHitlResponse(id, response);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
174
|
+
0 && (module.exports = {
|
|
175
|
+
createSdkAdapter
|
|
176
|
+
});
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
// src/core/adapter.ts
|
|
2
|
+
function createSdkAdapter(config) {
|
|
3
|
+
let connectionStatus = "disconnected";
|
|
4
|
+
const connectionListeners = /* @__PURE__ */ new Set();
|
|
5
|
+
let currentRunId = null;
|
|
6
|
+
const updateStatus = (status, error, errorCode) => {
|
|
7
|
+
connectionStatus = status;
|
|
8
|
+
connectionListeners.forEach((cb) => cb(status, error, errorCode));
|
|
9
|
+
};
|
|
10
|
+
return {
|
|
11
|
+
async connect() {
|
|
12
|
+
const sdk = config.getSdk();
|
|
13
|
+
if (!sdk) throw new Error("SDK not available");
|
|
14
|
+
updateStatus("connecting");
|
|
15
|
+
try {
|
|
16
|
+
await sdk.ensureReady();
|
|
17
|
+
updateStatus("connected");
|
|
18
|
+
} catch (e) {
|
|
19
|
+
updateStatus("error", e instanceof Error ? e.message : "Connection failed", "CONNECTION_FAILED");
|
|
20
|
+
throw e;
|
|
21
|
+
}
|
|
22
|
+
},
|
|
23
|
+
async disconnect() {
|
|
24
|
+
updateStatus("disconnected");
|
|
25
|
+
},
|
|
26
|
+
isConnected() {
|
|
27
|
+
const sdk = config.getSdk();
|
|
28
|
+
return sdk?.isConnected() ?? false;
|
|
29
|
+
},
|
|
30
|
+
getConnectionStatus() {
|
|
31
|
+
return connectionStatus;
|
|
32
|
+
},
|
|
33
|
+
onConnectionChange(callback) {
|
|
34
|
+
connectionListeners.add(callback);
|
|
35
|
+
callback(connectionStatus);
|
|
36
|
+
return () => connectionListeners.delete(callback);
|
|
37
|
+
},
|
|
38
|
+
async listConversations(options) {
|
|
39
|
+
const sdk = config.getSdk();
|
|
40
|
+
const agentId = config.getAgentId();
|
|
41
|
+
if (!sdk || !agentId) throw new Error("SDK or agent not ready");
|
|
42
|
+
const result = await sdk.listConversations({ agentId, ...options });
|
|
43
|
+
return {
|
|
44
|
+
conversations: result.conversations.map((c) => ({
|
|
45
|
+
id: c.conversation_id,
|
|
46
|
+
title: c.title || "Untitled",
|
|
47
|
+
createdAt: c.created_at || "",
|
|
48
|
+
updatedAt: c.updated_at || "",
|
|
49
|
+
messageCount: c.message_count
|
|
50
|
+
})),
|
|
51
|
+
total: result.total
|
|
52
|
+
};
|
|
53
|
+
},
|
|
54
|
+
async getConversation(id, options) {
|
|
55
|
+
const sdk = config.getSdk();
|
|
56
|
+
if (!sdk) throw new Error("SDK not ready");
|
|
57
|
+
const detail = await sdk.getConversation(id, { messageLimit: options?.messageLimit });
|
|
58
|
+
return {
|
|
59
|
+
id: detail.conversation_id,
|
|
60
|
+
title: detail.title || "Untitled",
|
|
61
|
+
createdAt: detail.created_at || "",
|
|
62
|
+
updatedAt: detail.updated_at || "",
|
|
63
|
+
messageCount: detail.message_count,
|
|
64
|
+
messages: (detail.messages || []).map((m, i) => ({
|
|
65
|
+
id: `msg-${i}`,
|
|
66
|
+
role: m.role,
|
|
67
|
+
content: m.content,
|
|
68
|
+
timestamp: m.created_at || (/* @__PURE__ */ new Date()).toISOString()
|
|
69
|
+
}))
|
|
70
|
+
};
|
|
71
|
+
},
|
|
72
|
+
async deleteConversation(id) {
|
|
73
|
+
const sdk = config.getSdk();
|
|
74
|
+
if (!sdk) throw new Error("SDK not ready");
|
|
75
|
+
await sdk.deleteConversation(id);
|
|
76
|
+
},
|
|
77
|
+
async chatStream(messages, conversationId, onEvent) {
|
|
78
|
+
const sdk = config.getSdk();
|
|
79
|
+
const agentId = config.getAgentId();
|
|
80
|
+
if (!sdk || !agentId) throw new Error("SDK or agent not ready");
|
|
81
|
+
await sdk.ensureReady();
|
|
82
|
+
const sdkMessages = messages.map((m) => ({ role: m.role, content: m.content }));
|
|
83
|
+
const stream = sdk.chatStream(agentId, sdkMessages, { conversationId });
|
|
84
|
+
const controller = new AbortController();
|
|
85
|
+
const signal = controller.signal;
|
|
86
|
+
(async () => {
|
|
87
|
+
try {
|
|
88
|
+
for await (const event of stream) {
|
|
89
|
+
if (signal.aborted) break;
|
|
90
|
+
switch (event.type) {
|
|
91
|
+
case "text":
|
|
92
|
+
onEvent({ type: "text", content: event.content || "" });
|
|
93
|
+
break;
|
|
94
|
+
case "thinking":
|
|
95
|
+
onEvent({ type: "thinking", content: event.content || "" });
|
|
96
|
+
break;
|
|
97
|
+
case "tool_call":
|
|
98
|
+
if (event.tool_call) {
|
|
99
|
+
onEvent({ type: "tool_call", tool_call: event.tool_call });
|
|
100
|
+
}
|
|
101
|
+
break;
|
|
102
|
+
case "tool_result":
|
|
103
|
+
onEvent({ type: "tool_result", tool_call_id: event.tool_call_id || "", result: event.result });
|
|
104
|
+
break;
|
|
105
|
+
case "done":
|
|
106
|
+
onEvent({ type: "done", conversationId: event.conversationId || "", title: event.title });
|
|
107
|
+
break;
|
|
108
|
+
case "error":
|
|
109
|
+
onEvent({ type: "error", error: event.error || "Unknown error" });
|
|
110
|
+
break;
|
|
111
|
+
default:
|
|
112
|
+
const evt = event;
|
|
113
|
+
if (evt.type === "interrupt") {
|
|
114
|
+
currentRunId = evt.run_id || null;
|
|
115
|
+
onEvent({
|
|
116
|
+
type: "interrupt",
|
|
117
|
+
interrupt_type: evt.interrupt_type || "",
|
|
118
|
+
interrupt_payload: evt.interrupt_payload,
|
|
119
|
+
run_id: evt.run_id
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
break;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
} catch (e) {
|
|
126
|
+
if (!signal.aborted) {
|
|
127
|
+
onEvent({ type: "error", error: e instanceof Error ? e.message : "Stream error" });
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
})();
|
|
131
|
+
return {
|
|
132
|
+
cancel: () => {
|
|
133
|
+
controller.abort();
|
|
134
|
+
}
|
|
135
|
+
};
|
|
136
|
+
},
|
|
137
|
+
sendHitlResponse(response, runId) {
|
|
138
|
+
const sdk = config.getSdk();
|
|
139
|
+
if (!sdk) return;
|
|
140
|
+
const id = runId || currentRunId;
|
|
141
|
+
if (id) {
|
|
142
|
+
sdk.sendHitlResponse(id, response);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
export {
|
|
148
|
+
createSdkAdapter
|
|
149
|
+
};
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { BrowserWindow } from 'electron';
|
|
2
|
+
import { SanqianSDK } from '@yushaw/sanqian-sdk';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* @yushaw/sanqian-chat Core Types
|
|
6
|
+
*
|
|
7
|
+
* Re-exports SDK types + chat-specific types
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
type WindowPosition = 'center' | 'cursor' | 'remember' | {
|
|
11
|
+
x: number;
|
|
12
|
+
y: number;
|
|
13
|
+
};
|
|
14
|
+
interface FloatingWindowConfig {
|
|
15
|
+
shortcut?: string;
|
|
16
|
+
position?: WindowPosition;
|
|
17
|
+
width?: number;
|
|
18
|
+
height?: number;
|
|
19
|
+
alwaysOnTop?: boolean;
|
|
20
|
+
showInTaskbar?: boolean;
|
|
21
|
+
theme?: 'light' | 'dark' | 'system';
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* FloatingWindow - Main process module for floating chat window
|
|
26
|
+
*/
|
|
27
|
+
|
|
28
|
+
interface FloatingWindowOptions extends FloatingWindowConfig {
|
|
29
|
+
/** SDK instance getter */
|
|
30
|
+
getSdk: () => SanqianSDK | null;
|
|
31
|
+
/** Agent ID getter */
|
|
32
|
+
getAgentId: () => string | null;
|
|
33
|
+
/** Path to preload script */
|
|
34
|
+
preloadPath: string;
|
|
35
|
+
/** Path to renderer HTML or URL */
|
|
36
|
+
rendererPath: string;
|
|
37
|
+
/** Dev mode - load from URL instead of file */
|
|
38
|
+
devMode?: boolean;
|
|
39
|
+
}
|
|
40
|
+
declare class FloatingWindow {
|
|
41
|
+
private window;
|
|
42
|
+
private options;
|
|
43
|
+
private savedPosition;
|
|
44
|
+
private activeStreams;
|
|
45
|
+
constructor(options: FloatingWindowOptions);
|
|
46
|
+
private createWindow;
|
|
47
|
+
private getInitialPosition;
|
|
48
|
+
private registerShortcut;
|
|
49
|
+
private setupIpcHandlers;
|
|
50
|
+
show(): void;
|
|
51
|
+
hide(): void;
|
|
52
|
+
toggle(): void;
|
|
53
|
+
isVisible(): boolean;
|
|
54
|
+
destroy(): void;
|
|
55
|
+
getWindow(): BrowserWindow | null;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export { FloatingWindow, type FloatingWindowConfig, type FloatingWindowOptions, type WindowPosition };
|