mirror of
https://github.com/siteboon/claudecodeui.git
synced 2026-05-17 09:30:05 +00:00
feat: add chat unifier setup
This commit is contained in:
@@ -93,6 +93,10 @@ export abstract class AbstractProvider implements IProvider {
|
||||
timestamp: new Date().toISOString(),
|
||||
channel: 'system',
|
||||
message: 'Session stop requested.',
|
||||
data: {
|
||||
sessionId,
|
||||
sessionStatus: 'SESSION_ABORTED',
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
@@ -220,6 +224,16 @@ export abstract class AbstractProvider implements IProvider {
|
||||
thinkingMode: input.thinkingMode,
|
||||
});
|
||||
|
||||
this.appendEvent(session, {
|
||||
timestamp: session.startedAt,
|
||||
channel: 'system',
|
||||
message: 'Session started.',
|
||||
data: {
|
||||
sessionId,
|
||||
sessionStatus: 'STARTED',
|
||||
},
|
||||
});
|
||||
|
||||
return session;
|
||||
}
|
||||
|
||||
|
||||
@@ -240,6 +240,15 @@ export abstract class BaseCliProvider extends AbstractProvider {
|
||||
|
||||
if (code === 0) {
|
||||
this.updateSessionStatus(session, 'completed');
|
||||
this.appendEvent(session, {
|
||||
timestamp: new Date().toISOString(),
|
||||
channel: 'system',
|
||||
message: 'Session completed.',
|
||||
data: {
|
||||
sessionId: session.sessionId,
|
||||
sessionStatus: 'COMPLETED',
|
||||
},
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@ import type { LLMProvider } from '@/shared/types/app.js';
|
||||
type CreateSdkExecutionInput = StartSessionInput & {
|
||||
sessionId: string;
|
||||
isResume: boolean;
|
||||
emitEvent?: (event: ProviderSessionEvent) => void;
|
||||
};
|
||||
|
||||
type SdkExecution = {
|
||||
@@ -86,6 +87,9 @@ export abstract class BaseSdkProvider extends AbstractProvider {
|
||||
...input,
|
||||
model: effectiveModel,
|
||||
thinkingMode: effectiveThinking,
|
||||
emitEvent: (event) => {
|
||||
this.appendEvent(session, event);
|
||||
},
|
||||
});
|
||||
} catch (error) {
|
||||
const message = error instanceof Error ? error.message : 'Failed to start SDK session';
|
||||
@@ -123,6 +127,15 @@ export abstract class BaseSdkProvider extends AbstractProvider {
|
||||
|
||||
if (session.status === 'running') {
|
||||
this.updateSessionStatus(session, 'completed');
|
||||
this.appendEvent(session, {
|
||||
timestamp: new Date().toISOString(),
|
||||
channel: 'system',
|
||||
message: 'Session completed.',
|
||||
data: {
|
||||
sessionId: session.sessionId,
|
||||
sessionStatus: 'COMPLETED',
|
||||
},
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
const message = error instanceof Error ? error.message : 'Unknown SDK execution failure';
|
||||
|
||||
@@ -18,6 +18,7 @@ import type {
|
||||
type ClaudeExecutionInput = StartSessionInput & {
|
||||
sessionId: string;
|
||||
isResume: boolean;
|
||||
emitEvent?: (event: ProviderSessionEvent) => void;
|
||||
};
|
||||
|
||||
const CLAUDE_THINKING_LEVELS = new Set(['low', 'medium', 'high', 'max']);
|
||||
@@ -52,6 +53,18 @@ type ClaudeUserPromptMessage = {
|
||||
timestamp: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* Safely reads one optional string value from unknown data.
|
||||
*/
|
||||
const readString = (value: unknown): string | undefined => {
|
||||
if (typeof value !== 'string') {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const normalized = value.trim();
|
||||
return normalized.length ? normalized : undefined;
|
||||
};
|
||||
|
||||
/**
|
||||
* Claude SDK provider implementation.
|
||||
*/
|
||||
@@ -97,7 +110,7 @@ export class ClaudeProvider extends BaseSdkProvider {
|
||||
cwd: input.workspacePath,
|
||||
model: input.model,
|
||||
effort: this.resolveClaudeEffort(input.thinkingMode),
|
||||
canUseTool: this.resolvePermissionHandler(input.runtimePermissionMode),
|
||||
canUseTool: this.resolvePermissionHandler(input.runtimePermissionMode, input.emitEvent),
|
||||
};
|
||||
|
||||
if (input.isResume) {
|
||||
@@ -232,20 +245,59 @@ export class ClaudeProvider extends BaseSdkProvider {
|
||||
/**
|
||||
* Builds a runtime permission callback when explicit allow/deny is requested.
|
||||
*/
|
||||
private resolvePermissionHandler(mode?: RuntimePermissionMode): CanUseTool | undefined {
|
||||
private resolvePermissionHandler(
|
||||
mode?: RuntimePermissionMode,
|
||||
emitEvent?: (event: ProviderSessionEvent) => void,
|
||||
): CanUseTool | undefined {
|
||||
if (!mode || mode === 'ask') {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
if (mode === 'allow') {
|
||||
return async () => ({ behavior: 'allow' });
|
||||
return async (toolName, input, options) => {
|
||||
const optionsRecord = options as Record<string, unknown>;
|
||||
emitEvent?.({
|
||||
timestamp: new Date().toISOString(),
|
||||
channel: 'system',
|
||||
message: `Tool permission requested for "${toolName}".`,
|
||||
data: {
|
||||
type: 'tool_use_request',
|
||||
toolName,
|
||||
input,
|
||||
toolUseID: options.toolUseID,
|
||||
title: readString(optionsRecord.title),
|
||||
displayName: readString(optionsRecord.displayName),
|
||||
description: readString(optionsRecord.description),
|
||||
blockedPath: options.blockedPath,
|
||||
},
|
||||
});
|
||||
return { behavior: 'allow' };
|
||||
};
|
||||
}
|
||||
|
||||
return async () => ({
|
||||
behavior: 'deny',
|
||||
message: 'Permission denied by runtime permission mode.',
|
||||
interrupt: false,
|
||||
});
|
||||
return async (toolName, input, options) => {
|
||||
const optionsRecord = options as Record<string, unknown>;
|
||||
emitEvent?.({
|
||||
timestamp: new Date().toISOString(),
|
||||
channel: 'system',
|
||||
message: `Tool permission denied for "${toolName}".`,
|
||||
data: {
|
||||
type: 'tool_use_request',
|
||||
toolName,
|
||||
input,
|
||||
toolUseID: options.toolUseID,
|
||||
title: readString(optionsRecord.title),
|
||||
displayName: readString(optionsRecord.displayName),
|
||||
description: readString(optionsRecord.description),
|
||||
blockedPath: options.blockedPath,
|
||||
},
|
||||
});
|
||||
return {
|
||||
behavior: 'deny',
|
||||
message: 'Permission denied by runtime permission mode.',
|
||||
interrupt: false,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user