feat(desktop): DeerFlow visual redesign + stream hang fix + intelligence client
Some checks failed
CI / Lint & TypeCheck (push) Has been cancelled
CI / Unit Tests (push) Has been cancelled
CI / Build Frontend (push) Has been cancelled
CI / Rust Check (push) Has been cancelled
CI / Security Scan (push) Has been cancelled
CI / E2E Tests (push) Has been cancelled
Some checks failed
CI / Lint & TypeCheck (push) Has been cancelled
CI / Unit Tests (push) Has been cancelled
CI / Build Frontend (push) Has been cancelled
CI / Rust Check (push) Has been cancelled
CI / Security Scan (push) Has been cancelled
CI / E2E Tests (push) Has been cancelled
DeerFlow frontend visual overhaul: - Card-style input box (white rounded card, textarea top, actions bottom) - Dropdown mode selector (闪速/思考/Pro/Ultra with icons+descriptions) - Colored quick-action chips (小惊喜/写作/研究/收集/学习) - Minimal top bar (title + token count + export) - Warm gray color system (#faf9f6 bg, #f5f4f1 sidebar, #e8e6e1 border) - DeerFlow-style sidebar (新对话/对话/智能体 nav) - Reasoning block, tool call chain, task progress visualization - Streaming text, model selector, suggestion chips components - Resizable artifact panel with drag handle - Virtualized message list for 100+ messages Bug fixes: - Stream hang: GatewayClient onclose code 1000 now calls onComplete - WebView2 textarea border: CSS !important override for UA styles - Gateway stream event handling (response/phase/tool_call types) Intelligence client: - Unified client with fallback drivers (compactor/heartbeat/identity/memory/reflection) - Gateway API types and type conversions
This commit is contained in:
@@ -473,6 +473,9 @@ export class GatewayClient {
|
||||
opts?: {
|
||||
sessionKey?: string;
|
||||
agentId?: string;
|
||||
thinking_enabled?: boolean;
|
||||
reasoning_effort?: string;
|
||||
plan_mode?: boolean;
|
||||
}
|
||||
): Promise<{ runId: string }> {
|
||||
const agentId = opts?.agentId || this.defaultAgentId;
|
||||
@@ -482,11 +485,16 @@ export class GatewayClient {
|
||||
// If no agent ID, try to fetch from ZCLAW status (async, but we'll handle it in connectZclawStream)
|
||||
if (!agentId) {
|
||||
// Try to get default agent asynchronously
|
||||
const chatModeOpts = {
|
||||
thinking_enabled: opts?.thinking_enabled,
|
||||
reasoning_effort: opts?.reasoning_effort,
|
||||
plan_mode: opts?.plan_mode,
|
||||
};
|
||||
this.fetchDefaultAgentId().then(() => {
|
||||
const resolvedAgentId = this.defaultAgentId;
|
||||
if (resolvedAgentId) {
|
||||
this.streamCallbacks.set(runId, callbacks);
|
||||
this.connectZclawStream(resolvedAgentId, runId, sessionId, message);
|
||||
this.connectZclawStream(resolvedAgentId, runId, sessionId, message, chatModeOpts);
|
||||
} else {
|
||||
callbacks.onError('No agent available. Please ensure ZCLAW has at least one agent.');
|
||||
callbacks.onComplete();
|
||||
@@ -502,7 +510,11 @@ export class GatewayClient {
|
||||
this.streamCallbacks.set(runId, callbacks);
|
||||
|
||||
// Connect to ZCLAW WebSocket if not connected
|
||||
this.connectZclawStream(agentId, runId, sessionId, message);
|
||||
this.connectZclawStream(agentId, runId, sessionId, message, {
|
||||
thinking_enabled: opts?.thinking_enabled,
|
||||
reasoning_effort: opts?.reasoning_effort,
|
||||
plan_mode: opts?.plan_mode,
|
||||
});
|
||||
|
||||
return { runId };
|
||||
}
|
||||
@@ -512,7 +524,12 @@ export class GatewayClient {
|
||||
agentId: string,
|
||||
runId: string,
|
||||
sessionId: string,
|
||||
message: string
|
||||
message: string,
|
||||
chatModeOpts?: {
|
||||
thinking_enabled?: boolean;
|
||||
reasoning_effort?: string;
|
||||
plan_mode?: boolean;
|
||||
}
|
||||
): void {
|
||||
// Close existing connection if any
|
||||
if (this.zclawWs && this.zclawWs.readyState !== WebSocket.CLOSED) {
|
||||
@@ -539,11 +556,20 @@ export class GatewayClient {
|
||||
this.zclawWs.onopen = () => {
|
||||
this.log('info', 'ZCLAW WebSocket connected');
|
||||
// Send chat message using ZCLAW actual protocol
|
||||
const chatRequest = {
|
||||
const chatRequest: Record<string, unknown> = {
|
||||
type: 'message',
|
||||
content: message,
|
||||
session_id: sessionId,
|
||||
};
|
||||
if (chatModeOpts?.thinking_enabled !== undefined) {
|
||||
chatRequest.thinking_enabled = chatModeOpts.thinking_enabled;
|
||||
}
|
||||
if (chatModeOpts?.reasoning_effort !== undefined) {
|
||||
chatRequest.reasoning_effort = chatModeOpts.reasoning_effort;
|
||||
}
|
||||
if (chatModeOpts?.plan_mode !== undefined) {
|
||||
chatRequest.plan_mode = chatModeOpts.plan_mode;
|
||||
}
|
||||
this.zclawWs?.send(JSON.stringify(chatRequest));
|
||||
};
|
||||
|
||||
@@ -569,8 +595,13 @@ export class GatewayClient {
|
||||
this.zclawWs.onclose = (event) => {
|
||||
this.log('info', `ZCLAW WebSocket closed: ${event.code} ${event.reason}`);
|
||||
const callbacks = this.streamCallbacks.get(runId);
|
||||
if (callbacks && event.code !== 1000) {
|
||||
callbacks.onError(`Connection closed: ${event.reason || 'unknown'}`);
|
||||
if (callbacks) {
|
||||
if (event.code !== 1000) {
|
||||
callbacks.onError(`Connection closed: ${event.reason || 'unknown'}`);
|
||||
} else {
|
||||
// Normal closure — ensure stream is completed even if no done event was sent
|
||||
callbacks.onComplete();
|
||||
}
|
||||
}
|
||||
this.streamCallbacks.delete(runId);
|
||||
this.zclawWs = null;
|
||||
@@ -614,8 +645,9 @@ export class GatewayClient {
|
||||
case 'response':
|
||||
// Final response with tokens info
|
||||
if (data.content) {
|
||||
// If we haven't received any deltas yet, send the full response
|
||||
// This handles non-streaming responses
|
||||
// Forward the full response content via onDelta
|
||||
// This handles non-streaming responses from the server
|
||||
callbacks.onDelta(data.content);
|
||||
}
|
||||
// Mark complete if phase done wasn't sent
|
||||
callbacks.onComplete();
|
||||
|
||||
Reference in New Issue
Block a user