@yeaft/webchat-agent 0.1.420 → 0.1.422
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/conversation.js +5 -18
- package/package.json +1 -1
- package/unify/web-bridge.js +0 -204
package/conversation.js
CHANGED
|
@@ -6,7 +6,6 @@ import { query } from './sdk/index.js';
|
|
|
6
6
|
import { loadSessionHistory } from './history.js';
|
|
7
7
|
import { startClaudeQuery } from './claude.js';
|
|
8
8
|
import { crewSessions, loadCrewIndex } from './crew.js';
|
|
9
|
-
import { handleUnifyInput } from './unify/web-bridge.js';
|
|
10
9
|
|
|
11
10
|
// 不支持的斜杠命令(真正需要交互式 CLI 的命令)
|
|
12
11
|
const UNSUPPORTED_SLASH_COMMANDS = ['/help', '/bug', '/login', '/logout', '/terminal-setup', '/vim', '/config'];
|
|
@@ -294,8 +293,7 @@ export async function sendConversationList() {
|
|
|
294
293
|
createdAt: state.createdAt,
|
|
295
294
|
processing: !!state.turnActive,
|
|
296
295
|
userId: state.userId,
|
|
297
|
-
username: state.username
|
|
298
|
-
mode: state.mode || null
|
|
296
|
+
username: state.username
|
|
299
297
|
};
|
|
300
298
|
list.push(entry);
|
|
301
299
|
}
|
|
@@ -356,7 +354,7 @@ export function sendError(conversationId, message) {
|
|
|
356
354
|
|
|
357
355
|
// 创建新的 conversation (延迟启动 Claude,等待用户发送第一条消息)
|
|
358
356
|
export async function createConversation(msg) {
|
|
359
|
-
const { conversationId, workDir, userId, username, disallowedTools
|
|
357
|
+
const { conversationId, workDir, userId, username, disallowedTools } = msg;
|
|
360
358
|
const effectiveWorkDir = workDir || ctx.CONFIG.workDir;
|
|
361
359
|
|
|
362
360
|
console.log(`Creating conversation: ${conversationId} in ${effectiveWorkDir} (lazy start)`);
|
|
@@ -377,7 +375,6 @@ export async function createConversation(msg) {
|
|
|
377
375
|
userId,
|
|
378
376
|
username,
|
|
379
377
|
disallowedTools: disallowedTools || null, // null = 使用全局默认
|
|
380
|
-
mode: mode || 'claude', // 'claude' (default) or 'unify'
|
|
381
378
|
usage: {
|
|
382
379
|
inputTokens: 0,
|
|
383
380
|
outputTokens: 0,
|
|
@@ -393,8 +390,7 @@ export async function createConversation(msg) {
|
|
|
393
390
|
workDir: effectiveWorkDir,
|
|
394
391
|
userId,
|
|
395
392
|
username,
|
|
396
|
-
disallowedTools: disallowedTools || null
|
|
397
|
-
mode: mode || 'claude'
|
|
393
|
+
disallowedTools: disallowedTools || null
|
|
398
394
|
});
|
|
399
395
|
|
|
400
396
|
// 立即发送 agent 级别的 MCP servers 列表(从 ~/.claude.json 读取的)
|
|
@@ -417,11 +413,8 @@ export async function createConversation(msg) {
|
|
|
417
413
|
sendConversationList();
|
|
418
414
|
|
|
419
415
|
// ★ Prestart Claude CLI in background to eagerly fetch skills/tools/model
|
|
420
|
-
//
|
|
421
|
-
|
|
422
|
-
// Fire-and-forget: failure just degrades to lazy-start behavior
|
|
423
|
-
prestartClaude(conversationId, effectiveWorkDir, null);
|
|
424
|
-
}
|
|
416
|
+
// Fire-and-forget: failure just degrades to lazy-start behavior
|
|
417
|
+
prestartClaude(conversationId, effectiveWorkDir, null);
|
|
425
418
|
}
|
|
426
419
|
|
|
427
420
|
// Resume 历史 conversation (延迟启动 Claude,等待用户发送第一条消息)
|
|
@@ -669,12 +662,6 @@ export async function handleCancelExecution(msg) {
|
|
|
669
662
|
export async function handleUserInput(msg) {
|
|
670
663
|
const { conversationId, prompt, workDir, claudeSessionId } = msg;
|
|
671
664
|
|
|
672
|
-
// ★ Unify mode: route to Unify Engine instead of Claude CLI
|
|
673
|
-
const state = ctx.conversations.get(conversationId);
|
|
674
|
-
if (state?.mode === 'unify') {
|
|
675
|
-
return handleUnifyInput(msg);
|
|
676
|
-
}
|
|
677
|
-
|
|
678
665
|
// 解析斜杠命令
|
|
679
666
|
const slashCommand = parseSlashCommand(prompt);
|
|
680
667
|
|
package/package.json
CHANGED
package/unify/web-bridge.js
DELETED
|
@@ -1,204 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* web-bridge.js — Bridge between Yeaft Unify Engine and the Web UI
|
|
3
|
-
*
|
|
4
|
-
* Translates Unify Engine streaming events into the sendOutput wire format
|
|
5
|
-
* that the existing web client already understands. This means the UI needs
|
|
6
|
-
* zero rendering changes — Unify conversations look identical to Claude ones.
|
|
7
|
-
*
|
|
8
|
-
* Architecture: Agent-side only. UI and Server are transparent relays.
|
|
9
|
-
*
|
|
10
|
-
* Lifecycle per conversation:
|
|
11
|
-
* 1. First user message → lazy-load a Unify Session (loadSession)
|
|
12
|
-
* 2. Each user message → engine.query({ prompt }) → stream events → sendOutput
|
|
13
|
-
* 3. On turn complete → send turn_completed so UI exits processing state
|
|
14
|
-
*/
|
|
15
|
-
|
|
16
|
-
import ctx from '../context.js';
|
|
17
|
-
import { sendOutput, sendConversationList } from '../conversation.js';
|
|
18
|
-
import { loadSession } from './session.js';
|
|
19
|
-
|
|
20
|
-
/**
|
|
21
|
-
* Handle a user message for a Unify-mode conversation.
|
|
22
|
-
*
|
|
23
|
-
* Called from conversation.js handleUserInput() when state.mode === 'unify'.
|
|
24
|
-
* Reuses the same sendOutput() wire format as Claude conversations so the
|
|
25
|
-
* web client renders messages identically.
|
|
26
|
-
*
|
|
27
|
-
* @param {{ conversationId: string, prompt: string }} msg
|
|
28
|
-
*/
|
|
29
|
-
export async function handleUnifyInput(msg) {
|
|
30
|
-
const { conversationId, prompt } = msg;
|
|
31
|
-
const state = ctx.conversations.get(conversationId);
|
|
32
|
-
|
|
33
|
-
if (!state) {
|
|
34
|
-
console.error(`[Unify] No conversation state for ${conversationId}`);
|
|
35
|
-
return;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
// ─── Lazy-init Unify Session ──────────────────────────────
|
|
39
|
-
if (!state._unifySession) {
|
|
40
|
-
try {
|
|
41
|
-
console.log(`[Unify] Loading session for ${conversationId}...`);
|
|
42
|
-
state._unifySession = await loadSession({
|
|
43
|
-
skipMCP: true,
|
|
44
|
-
skipSkills: true,
|
|
45
|
-
});
|
|
46
|
-
state._unifyMessages = [];
|
|
47
|
-
console.log(`[Unify] Session loaded for ${conversationId} (model: ${state._unifySession.config.model})`);
|
|
48
|
-
} catch (err) {
|
|
49
|
-
console.error(`[Unify] Failed to load session for ${conversationId}:`, err);
|
|
50
|
-
sendOutput(conversationId, {
|
|
51
|
-
type: 'assistant',
|
|
52
|
-
message: {
|
|
53
|
-
role: 'assistant',
|
|
54
|
-
content: [{
|
|
55
|
-
type: 'text',
|
|
56
|
-
text: `Unify session initialization failed: ${err.message}`
|
|
57
|
-
}]
|
|
58
|
-
}
|
|
59
|
-
});
|
|
60
|
-
ctx.sendToServer({
|
|
61
|
-
type: 'turn_completed',
|
|
62
|
-
conversationId,
|
|
63
|
-
workDir: state.workDir
|
|
64
|
-
});
|
|
65
|
-
return;
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
const session = state._unifySession;
|
|
70
|
-
|
|
71
|
-
// ─── Send user message to UI ──────────────────────────────
|
|
72
|
-
sendOutput(conversationId, {
|
|
73
|
-
type: 'user',
|
|
74
|
-
message: {
|
|
75
|
-
role: 'user',
|
|
76
|
-
content: prompt
|
|
77
|
-
}
|
|
78
|
-
});
|
|
79
|
-
|
|
80
|
-
// Track messages for multi-turn context
|
|
81
|
-
state._unifyMessages.push({ role: 'user', content: prompt });
|
|
82
|
-
|
|
83
|
-
// ─── Run Engine query and stream back ─────────────────────
|
|
84
|
-
let fullText = '';
|
|
85
|
-
let hasError = false;
|
|
86
|
-
|
|
87
|
-
try {
|
|
88
|
-
for await (const event of session.engine.query({
|
|
89
|
-
prompt,
|
|
90
|
-
messages: state._unifyMessages.slice(0, -1), // pass history (excluding current prompt which engine adds)
|
|
91
|
-
})) {
|
|
92
|
-
switch (event.type) {
|
|
93
|
-
case 'text_delta':
|
|
94
|
-
fullText += event.text;
|
|
95
|
-
sendOutput(conversationId, {
|
|
96
|
-
type: 'assistant',
|
|
97
|
-
message: {
|
|
98
|
-
role: 'assistant',
|
|
99
|
-
content: [{
|
|
100
|
-
type: 'text',
|
|
101
|
-
text: event.text
|
|
102
|
-
}]
|
|
103
|
-
},
|
|
104
|
-
subtype: 'text_delta'
|
|
105
|
-
});
|
|
106
|
-
break;
|
|
107
|
-
|
|
108
|
-
case 'tool_call':
|
|
109
|
-
sendOutput(conversationId, {
|
|
110
|
-
type: 'assistant',
|
|
111
|
-
message: {
|
|
112
|
-
role: 'assistant',
|
|
113
|
-
content: [{
|
|
114
|
-
type: 'tool_use',
|
|
115
|
-
id: event.id,
|
|
116
|
-
name: event.name,
|
|
117
|
-
input: event.input
|
|
118
|
-
}]
|
|
119
|
-
}
|
|
120
|
-
});
|
|
121
|
-
break;
|
|
122
|
-
|
|
123
|
-
case 'tool_result':
|
|
124
|
-
sendOutput(conversationId, {
|
|
125
|
-
type: 'assistant',
|
|
126
|
-
message: {
|
|
127
|
-
role: 'tool',
|
|
128
|
-
tool_use_id: event.toolCallId,
|
|
129
|
-
content: typeof event.result === 'string'
|
|
130
|
-
? event.result
|
|
131
|
-
: JSON.stringify(event.result)
|
|
132
|
-
}
|
|
133
|
-
});
|
|
134
|
-
break;
|
|
135
|
-
|
|
136
|
-
case 'usage':
|
|
137
|
-
// Update conversation usage tracking
|
|
138
|
-
if (state.usage) {
|
|
139
|
-
state.usage.inputTokens += event.inputTokens || 0;
|
|
140
|
-
state.usage.outputTokens += event.outputTokens || 0;
|
|
141
|
-
}
|
|
142
|
-
break;
|
|
143
|
-
|
|
144
|
-
case 'error':
|
|
145
|
-
hasError = true;
|
|
146
|
-
sendOutput(conversationId, {
|
|
147
|
-
type: 'assistant',
|
|
148
|
-
message: {
|
|
149
|
-
role: 'assistant',
|
|
150
|
-
content: [{
|
|
151
|
-
type: 'text',
|
|
152
|
-
text: `Error: ${event.error?.message || 'Unknown error'}`
|
|
153
|
-
}]
|
|
154
|
-
}
|
|
155
|
-
});
|
|
156
|
-
break;
|
|
157
|
-
|
|
158
|
-
case 'stop':
|
|
159
|
-
case 'turn_start':
|
|
160
|
-
case 'turn_end':
|
|
161
|
-
case 'recall':
|
|
162
|
-
case 'consolidate':
|
|
163
|
-
case 'fallback':
|
|
164
|
-
case 'thinking_delta':
|
|
165
|
-
// These are engine-internal events; no UI action needed
|
|
166
|
-
break;
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
} catch (err) {
|
|
170
|
-
hasError = true;
|
|
171
|
-
console.error(`[Unify] Query error for ${conversationId}:`, err);
|
|
172
|
-
sendOutput(conversationId, {
|
|
173
|
-
type: 'assistant',
|
|
174
|
-
message: {
|
|
175
|
-
role: 'assistant',
|
|
176
|
-
content: [{
|
|
177
|
-
type: 'text',
|
|
178
|
-
text: `Unify query error: ${err.message}`
|
|
179
|
-
}]
|
|
180
|
-
}
|
|
181
|
-
});
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
// ─── Track assistant response for multi-turn ──────────────
|
|
185
|
-
if (fullText) {
|
|
186
|
-
state._unifyMessages.push({ role: 'assistant', content: fullText });
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
// ─── Send result + turn_completed ─────────────────────────
|
|
190
|
-
// Emit a result message to match Claude's wire format (UI uses this for state management)
|
|
191
|
-
sendOutput(conversationId, {
|
|
192
|
-
type: 'result',
|
|
193
|
-
subtype: hasError ? 'error' : 'success',
|
|
194
|
-
result_text: fullText
|
|
195
|
-
});
|
|
196
|
-
|
|
197
|
-
ctx.sendToServer({
|
|
198
|
-
type: 'turn_completed',
|
|
199
|
-
conversationId,
|
|
200
|
-
workDir: state.workDir
|
|
201
|
-
});
|
|
202
|
-
|
|
203
|
-
sendConversationList();
|
|
204
|
-
}
|