refactor: Rename message stream handler and update session creation logic

This commit is contained in:
Vaayne
2025-09-17 14:10:10 +08:00
parent 219d162e1a
commit d1ff8591a6
20 changed files with 147 additions and 269 deletions
@@ -23,7 +23,7 @@ import { useTimer } from '@renderer/hooks/useTimer'
import { EVENT_NAMES, EventEmitter } from '@renderer/services/EventService'
import { AgentEntity, AgentType, isAgentType } from '@renderer/types'
import { uuid } from '@renderer/utils'
import { ChangeEvent, FormEvent, ReactNode, useCallback, useMemo, useRef, useState } from 'react'
import { ChangeEvent, FormEvent, ReactNode, useCallback, useEffect, useMemo, useRef, useState } from 'react'
import { useTranslation } from 'react-i18next'
import { ErrorBoundary } from '../ErrorBoundary'
@@ -45,13 +45,23 @@ interface AgentTypeOption extends Option {
type ModelOption = Option
type AgentForm = {
type: AgentEntity['type']
name: AgentEntity['name']
description?: AgentEntity['description']
instructions?: AgentEntity['instructions']
model?: AgentEntity['model']
type: AgentType
name: string
description?: string
instructions?: string
model: string
accessible_paths: string[]
}
const buildAgentForm = (existing?: AgentEntity): AgentForm => ({
type: existing?.type ?? 'claude-code',
name: existing?.name ?? 'Claude Code',
description: existing?.description,
instructions: existing?.instructions,
model: existing?.model ?? 'claude-4-sonnet',
accessible_paths: existing?.accessible_paths ? [...existing.accessible_paths] : []
})
interface BaseProps {
agent?: AgentEntity
}
@@ -88,16 +98,13 @@ export const AgentModal: React.FC<Props> = ({ agent, trigger, isOpen: _isOpen, o
const { addAgent, updateAgent } = useAgents()
const isEditing = (agent?: AgentEntity) => agent !== undefined
// default values. may change to undefined.
const [form, setForm] = useState<AgentForm>(
isEditing(agent)
? agent
: {
type: 'claude-code',
name: 'Claude Code',
model: 'claude-4-sonnet'
}
)
const [form, setForm] = useState<AgentForm>(() => buildAgentForm(agent))
useEffect(() => {
if (isOpen) {
setForm(buildAgentForm(agent))
}
}, [agent, isOpen])
const Option = useCallback(
({ option }: { option?: Option | null }) => {
@@ -222,44 +229,51 @@ export const AgentModal: React.FC<Props> = ({ agent, trigger, isOpen: _isOpen, o
// Additional validation check besides native HTML validation to ensure security
if (!isAgentType(form.type)) {
window.toast.error(t('agent.add.error.invalid_agent'))
loadingRef.current = false
return
}
if (form.model === undefined) {
if (!form.model) {
window.toast.error(t('error.model.not_exists'))
loadingRef.current = false
return
}
let _agent: AgentEntity
let resultAgent: AgentEntity
if (isEditing(agent)) {
_agent = {
...agent,
// type: form.type,
if (!agent) {
throw new Error('Agent is required for editing mode')
}
const updatePayload: Partial<AgentEntity> & { id: string } = {
id: agent.id,
name: form.name,
description: form.description,
instructions: form.instructions,
updated_at: new Date().toISOString(),
model: form.model
// avatar: getAvatar(form.type)
} satisfies AgentEntity
updateAgent(_agent)
}
updateAgent(updatePayload)
resultAgent = { ...agent, ...updatePayload }
window.toast.success(t('common.update_success'))
} else {
_agent = {
const now = new Date().toISOString()
resultAgent = {
id: uuid(),
type: form.type,
name: form.name,
description: form.description,
instructions: form.instructions,
created_at: new Date().toISOString(),
updated_at: new Date().toISOString(),
created_at: now,
updated_at: now,
model: form.model,
avatar: getAvatar(form.type)
} satisfies AgentEntity
addAgent(_agent)
accessible_paths: [...form.accessible_paths]
}
addAgent(resultAgent)
window.toast.success(t('common.add_success'))
}
logger.debug('Agent', _agent)
logger.debug('Agent mutation payload', { agent: resultAgent })
loadingRef.current = false
setTimeoutTimer('onCreateAgent', () => EventEmitter.emit(EVENT_NAMES.SHOW_ASSISTANTS), 0)
@@ -271,6 +285,7 @@ export const AgentModal: React.FC<Props> = ({ agent, trigger, isOpen: _isOpen, o
form.name,
form.description,
form.instructions,
form.accessible_paths,
agent,
setTimeoutTimer,
onClose,
@@ -339,8 +354,16 @@ export const AgentModal: React.FC<Props> = ({ agent, trigger, isOpen: _isOpen, o
</SelectItem>
)}
</Select>
<Textarea label={t('common.description')} value={form.description} onValueChange={onDescChange} />
<Textarea label={t('common.prompt')} value={form.instructions} onValueChange={onInstChange} />
<Textarea
label={t('common.description')}
value={form.description ?? ''}
onValueChange={onDescChange}
/>
<Textarea
label={t('common.prompt')}
value={form.instructions ?? ''}
onValueChange={onInstChange}
/>
</ModalBody>
<ModalFooter className="w-full">
<Button onPress={onClose}>{t('common.close')}</Button>
@@ -356,11 +379,3 @@ export const AgentModal: React.FC<Props> = ({ agent, trigger, isOpen: _isOpen, o
</ErrorBoundary>
)
}
const getAvatar = (type: AgentType) => {
switch (type) {
case 'claude-code':
return ClaudeIcon
}
return undefined
}
+3 -1
View File
@@ -1,7 +1,9 @@
import { AgentBase } from '@renderer/types'
// base agent config. no default config for now.
const DEFAULT_AGENT_CONFIG: Omit<AgentBase, 'model'> = {} as const
const DEFAULT_AGENT_CONFIG: Omit<AgentBase, 'model'> = {
accessible_paths: []
} as const
// no default config for now.
export const DEFAULT_CLAUDE_CODE_CONFIG: Omit<AgentBase, 'model'> = {
@@ -6,8 +6,9 @@ export const useUpdateAgent = () => {
// TODO: use api
return useMutation({
// @ts-expect-error not-implemented
mutationFn: async ({}: Partial<AgentEntity> & { id: string }) => {},
mutationFn: async (agentUpdate: Partial<AgentEntity> & { id: string }) => {
throw new Error(`useUpdateAgent mutationFn not implemented for agent ${agentUpdate.id}`)
},
onSuccess: (updated: AgentEntity) => {
qc.setQueryData<AgentEntity[]>(['todos'], (old) =>
old ? old.map((t) => (t.id === updated.id ? updated : t)) : []
@@ -22,13 +22,14 @@ const AgentItem: FC<AgentItemProps> = ({ agent, isActive, onDelete }) => {
// const { agents } = useAgents()
const AgentLabel = useCallback(() => {
const displayName = agent.name ?? agent.id
return (
<>
{agent.avatar && <Avatar className="h-6 w-6" src={agent.avatar} />}
<span className="text-sm">{agent.name}</span>
<Avatar className="h-6 w-6" name={displayName} />
<span className="text-sm">{displayName}</span>
</>
)
}, [agent.avatar, agent.name])
}, [agent.id, agent.name])
const handleClick = () => logger.debug('not implemented')
@@ -37,7 +38,7 @@ const AgentItem: FC<AgentItemProps> = ({ agent, isActive, onDelete }) => {
<ContextMenu modal={false}>
<ContextMenuTrigger>
<Container onClick={handleClick} className={isActive ? 'active' : ''}>
<AssistantNameRow className="name" title={agent.name}>
<AssistantNameRow className="name" title={agent.name ?? agent.id}>
<AgentLabel />
</AssistantNameRow>
</Container>
+3 -4
View File
@@ -2,7 +2,7 @@
* Database entity types for Agent, Session, and SessionMessage
* Shared between main and renderer processes
*/
import { TextStreamPart, UIMessageChunk, ModelMessage } from 'ai'
import { ModelMessage, TextStreamPart, UIMessageChunk } from 'ai'
export type PermissionMode = 'default' | 'acceptEdits' | 'bypassPermissions' | 'plan'
export type SessionMessageRole = ModelMessage['role']
export type AgentType = 'claude-code'
@@ -75,15 +75,14 @@ export interface ListOptions {
export interface AgentSessionEntity extends AgentBase {
id: string
agent_id: string // Primary agent ID for the session
agent_type: AgentType
// sub_agent_ids?: string[] // Array of sub-agent IDs involved in the session
created_at: string
updated_at: string
}
export interface CreateSessionRequest extends AgentBase {
agent_id: string // Primary agent ID for the session
}
export type CreateSessionRequest = AgentBase
export interface UpdateSessionRequest extends Partial<AgentBase> {}