refactor(desktop): split kernel_commands/pipeline_commands into modules, add SaaS client libs and gateway modules
Split monolithic kernel_commands.rs (2185 lines) and pipeline_commands.rs (1391 lines) into focused sub-modules under kernel_commands/ and pipeline_commands/ directories. Add gateway module (commands, config, io, runtime), health_check, and 15 new TypeScript client libraries for SaaS relay, auth, admin, telemetry, and kernel sub-systems (a2a, agent, chat, hands, skills, triggers). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
202
desktop/src/lib/kernel-chat.ts
Normal file
202
desktop/src/lib/kernel-chat.ts
Normal file
@@ -0,0 +1,202 @@
|
||||
/**
|
||||
* kernel-chat.ts - Chat & streaming methods for KernelClient
|
||||
*
|
||||
* Installed onto KernelClient.prototype via installChatMethods().
|
||||
*/
|
||||
|
||||
import { invoke } from '@tauri-apps/api/core';
|
||||
import { listen, type UnlistenFn } from '@tauri-apps/api/event';
|
||||
import { createLogger } from './logger';
|
||||
import type { KernelClient } from './kernel-client';
|
||||
import type { ChatResponse, StreamCallbacks, StreamChunkPayload } from './kernel-types';
|
||||
|
||||
const log = createLogger('KernelClient');
|
||||
|
||||
export function installChatMethods(ClientClass: { prototype: KernelClient }): void {
|
||||
const proto = ClientClass.prototype as any;
|
||||
|
||||
/**
|
||||
* Send a message and get a response
|
||||
*/
|
||||
proto.chat = async function (
|
||||
this: KernelClient,
|
||||
message: string,
|
||||
opts?: {
|
||||
sessionKey?: string;
|
||||
agentId?: string;
|
||||
}
|
||||
): Promise<{ runId: string; sessionId?: string; response?: string }> {
|
||||
const agentId = opts?.agentId || this.getDefaultAgentId();
|
||||
|
||||
if (!agentId) {
|
||||
throw new Error('No agent available');
|
||||
}
|
||||
|
||||
const response = await invoke<ChatResponse>('agent_chat', {
|
||||
request: {
|
||||
agentId,
|
||||
message,
|
||||
},
|
||||
});
|
||||
|
||||
return {
|
||||
runId: `run_${Date.now()}`,
|
||||
sessionId: opts?.sessionKey,
|
||||
response: response.content,
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Send a message with streaming response via Tauri events
|
||||
*/
|
||||
proto.chatStream = async function (
|
||||
this: KernelClient,
|
||||
message: string,
|
||||
callbacks: StreamCallbacks,
|
||||
opts?: {
|
||||
sessionKey?: string;
|
||||
agentId?: string;
|
||||
}
|
||||
): Promise<{ runId: string }> {
|
||||
const runId = crypto.randomUUID();
|
||||
const sessionId = opts?.sessionKey || runId;
|
||||
const agentId = opts?.agentId || this.getDefaultAgentId();
|
||||
|
||||
if (!agentId) {
|
||||
callbacks.onError('No agent available');
|
||||
return { runId };
|
||||
}
|
||||
|
||||
let unlisten: UnlistenFn | null = null;
|
||||
|
||||
try {
|
||||
// Set up event listener for stream chunks
|
||||
unlisten = await listen<StreamChunkPayload>('stream:chunk', (event) => {
|
||||
const payload = event.payload;
|
||||
|
||||
// Only process events for this session
|
||||
if (payload.sessionId !== sessionId) {
|
||||
return;
|
||||
}
|
||||
|
||||
const streamEvent = payload.event;
|
||||
|
||||
switch (streamEvent.type) {
|
||||
case 'delta':
|
||||
callbacks.onDelta(streamEvent.delta);
|
||||
break;
|
||||
|
||||
case 'tool_start':
|
||||
log.debug('Tool started:', streamEvent.name, streamEvent.input);
|
||||
if (callbacks.onTool) {
|
||||
callbacks.onTool(
|
||||
streamEvent.name,
|
||||
JSON.stringify(streamEvent.input),
|
||||
''
|
||||
);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'tool_end':
|
||||
log.debug('Tool ended:', streamEvent.name, streamEvent.output);
|
||||
if (callbacks.onTool) {
|
||||
callbacks.onTool(
|
||||
streamEvent.name,
|
||||
'',
|
||||
JSON.stringify(streamEvent.output)
|
||||
);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'handStart':
|
||||
log.debug('Hand started:', streamEvent.name, streamEvent.params);
|
||||
if (callbacks.onHand) {
|
||||
callbacks.onHand(streamEvent.name, 'running', undefined);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'handEnd':
|
||||
log.debug('Hand ended:', streamEvent.name, streamEvent.result);
|
||||
if (callbacks.onHand) {
|
||||
callbacks.onHand(streamEvent.name, 'completed', streamEvent.result);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'iteration_start':
|
||||
log.debug('Iteration started:', streamEvent.iteration, '/', streamEvent.maxIterations);
|
||||
// Don't need to notify user about iterations
|
||||
break;
|
||||
|
||||
case 'complete':
|
||||
log.debug('Stream complete:', streamEvent.inputTokens, streamEvent.outputTokens);
|
||||
callbacks.onComplete(streamEvent.inputTokens, streamEvent.outputTokens);
|
||||
// Clean up listener
|
||||
if (unlisten) {
|
||||
unlisten();
|
||||
unlisten = null;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'error':
|
||||
log.error('Stream error:', streamEvent.message);
|
||||
callbacks.onError(streamEvent.message);
|
||||
// Clean up listener
|
||||
if (unlisten) {
|
||||
unlisten();
|
||||
unlisten = null;
|
||||
}
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
// Invoke the streaming command
|
||||
await invoke('agent_chat_stream', {
|
||||
request: {
|
||||
agentId,
|
||||
sessionId,
|
||||
message,
|
||||
},
|
||||
});
|
||||
} catch (err: unknown) {
|
||||
const errorMessage = err instanceof Error ? err.message : String(err);
|
||||
callbacks.onError(errorMessage);
|
||||
|
||||
// Clean up listener on error
|
||||
if (unlisten) {
|
||||
unlisten();
|
||||
}
|
||||
}
|
||||
|
||||
return { runId };
|
||||
};
|
||||
|
||||
/**
|
||||
* Cancel a stream (no-op for internal kernel)
|
||||
*/
|
||||
proto.cancelStream = function (this: KernelClient, _runId: string): void {
|
||||
// No-op: internal kernel doesn't support stream cancellation
|
||||
};
|
||||
|
||||
// ─── Default Agent ───
|
||||
|
||||
/**
|
||||
* Fetch default agent ID (returns current default)
|
||||
*/
|
||||
proto.fetchDefaultAgentId = async function (this: KernelClient): Promise<string | null> {
|
||||
return this.getDefaultAgentId();
|
||||
};
|
||||
|
||||
/**
|
||||
* Set default agent ID
|
||||
*/
|
||||
proto.setDefaultAgentId = function (this: KernelClient, agentId: string): void {
|
||||
(this as any).defaultAgentId = agentId;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get default agent ID
|
||||
*/
|
||||
proto.getDefaultAgentId = function (this: KernelClient): string {
|
||||
return (this as any).defaultAgentId || '';
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user