Compare commits
5 Commits
feat/sub_a
...
betterSqli
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
00754f3644 | ||
|
|
36a9af3e6b | ||
|
|
67e032344b | ||
|
|
59a8f3c47d | ||
|
|
fadb436c7d |
@@ -77,7 +77,9 @@
|
||||
"release:aicore:alpha": "yarn workspace @cherrystudio/ai-core version prerelease --preid alpha --immediate && yarn workspace @cherrystudio/ai-core build && yarn workspace @cherrystudio/ai-core npm publish --tag alpha --access public",
|
||||
"release:aicore:beta": "yarn workspace @cherrystudio/ai-core version prerelease --preid beta --immediate && yarn workspace @cherrystudio/ai-core build && yarn workspace @cherrystudio/ai-core npm publish --tag beta --access public",
|
||||
"release:aicore": "yarn workspace @cherrystudio/ai-core version patch --immediate && yarn workspace @cherrystudio/ai-core build && yarn workspace @cherrystudio/ai-core npm publish --access public",
|
||||
"release:ai-sdk-provider": "yarn workspace @cherrystudio/ai-sdk-provider version patch --immediate && yarn workspace @cherrystudio/ai-sdk-provider build && yarn workspace @cherrystudio/ai-sdk-provider npm publish --access public"
|
||||
"release:ai-sdk-provider": "yarn workspace @cherrystudio/ai-sdk-provider version patch --immediate && yarn workspace @cherrystudio/ai-sdk-provider build && yarn workspace @cherrystudio/ai-sdk-provider npm publish --access public",
|
||||
"rebuild": "electron-rebuild -f -w better-sqlite3",
|
||||
"postinstall": "electron-builder install-app-deps"
|
||||
},
|
||||
"dependencies": {
|
||||
"@anthropic-ai/claude-agent-sdk": "patch:@anthropic-ai/claude-agent-sdk@npm%3A0.1.30#~/.yarn/patches/@anthropic-ai-claude-agent-sdk-npm-0.1.30-b50a299674.patch",
|
||||
@@ -86,6 +88,7 @@
|
||||
"@napi-rs/system-ocr": "patch:@napi-rs/system-ocr@npm%3A1.0.2#~/.yarn/patches/@napi-rs-system-ocr-npm-1.0.2-59e7a78e8b.patch",
|
||||
"@paymoapp/electron-shutdown-handler": "^1.1.2",
|
||||
"@strongtz/win32-arm64-msvc": "^0.4.7",
|
||||
"better-sqlite3": "12.4.1",
|
||||
"emoji-picker-element-data": "^1",
|
||||
"express": "^5.1.0",
|
||||
"font-list": "^2.0.0",
|
||||
@@ -201,6 +204,7 @@
|
||||
"@tiptap/y-tiptap": "^3.0.0",
|
||||
"@truto/turndown-plugin-gfm": "^1.0.2",
|
||||
"@tryfabric/martian": "^1.2.4",
|
||||
"@types/better-sqlite3": "^7.6.12",
|
||||
"@types/cli-progress": "^3",
|
||||
"@types/content-type": "^1.1.9",
|
||||
"@types/cors": "^2.8.19",
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
ALTER TABLE `agents` ADD `sub_agents` text;--> statement-breakpoint
|
||||
ALTER TABLE `sessions` ADD `sub_agents` text;
|
||||
@@ -1,360 +0,0 @@
|
||||
{
|
||||
"version": "6",
|
||||
"dialect": "sqlite",
|
||||
"id": "9aeb5f21-fed7-4dbf-973d-c344681b71c2",
|
||||
"prevId": "0cf3d79e-69bf-4dba-8df4-996b9b67d2e8",
|
||||
"tables": {
|
||||
"agents": {
|
||||
"name": "agents",
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "text",
|
||||
"primaryKey": true,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"type": {
|
||||
"name": "type",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"name": {
|
||||
"name": "name",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"description": {
|
||||
"name": "description",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"accessible_paths": {
|
||||
"name": "accessible_paths",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"instructions": {
|
||||
"name": "instructions",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"model": {
|
||||
"name": "model",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"plan_model": {
|
||||
"name": "plan_model",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"small_model": {
|
||||
"name": "small_model",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"mcps": {
|
||||
"name": "mcps",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"allowed_tools": {
|
||||
"name": "allowed_tools",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"sub_agents": {
|
||||
"name": "sub_agents",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"configuration": {
|
||||
"name": "configuration",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"created_at": {
|
||||
"name": "created_at",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"updated_at": {
|
||||
"name": "updated_at",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
}
|
||||
},
|
||||
"indexes": {},
|
||||
"foreignKeys": {},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {},
|
||||
"checkConstraints": {}
|
||||
},
|
||||
"session_messages": {
|
||||
"name": "session_messages",
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "integer",
|
||||
"primaryKey": true,
|
||||
"notNull": true,
|
||||
"autoincrement": true
|
||||
},
|
||||
"session_id": {
|
||||
"name": "session_id",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"role": {
|
||||
"name": "role",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"content": {
|
||||
"name": "content",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"agent_session_id": {
|
||||
"name": "agent_session_id",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false,
|
||||
"default": "''"
|
||||
},
|
||||
"metadata": {
|
||||
"name": "metadata",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"created_at": {
|
||||
"name": "created_at",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"updated_at": {
|
||||
"name": "updated_at",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
}
|
||||
},
|
||||
"indexes": {},
|
||||
"foreignKeys": {},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {},
|
||||
"checkConstraints": {}
|
||||
},
|
||||
"migrations": {
|
||||
"name": "migrations",
|
||||
"columns": {
|
||||
"version": {
|
||||
"name": "version",
|
||||
"type": "integer",
|
||||
"primaryKey": true,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"tag": {
|
||||
"name": "tag",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"executed_at": {
|
||||
"name": "executed_at",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
}
|
||||
},
|
||||
"indexes": {},
|
||||
"foreignKeys": {},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {},
|
||||
"checkConstraints": {}
|
||||
},
|
||||
"sessions": {
|
||||
"name": "sessions",
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "text",
|
||||
"primaryKey": true,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"agent_type": {
|
||||
"name": "agent_type",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"agent_id": {
|
||||
"name": "agent_id",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"name": {
|
||||
"name": "name",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"description": {
|
||||
"name": "description",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"accessible_paths": {
|
||||
"name": "accessible_paths",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"instructions": {
|
||||
"name": "instructions",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"model": {
|
||||
"name": "model",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"plan_model": {
|
||||
"name": "plan_model",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"small_model": {
|
||||
"name": "small_model",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"mcps": {
|
||||
"name": "mcps",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"allowed_tools": {
|
||||
"name": "allowed_tools",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"sub_agents": {
|
||||
"name": "sub_agents",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"slash_commands": {
|
||||
"name": "slash_commands",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"configuration": {
|
||||
"name": "configuration",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"created_at": {
|
||||
"name": "created_at",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"updated_at": {
|
||||
"name": "updated_at",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
}
|
||||
},
|
||||
"indexes": {},
|
||||
"foreignKeys": {},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {},
|
||||
"checkConstraints": {}
|
||||
}
|
||||
},
|
||||
"views": {},
|
||||
"enums": {},
|
||||
"_meta": {
|
||||
"schemas": {},
|
||||
"tables": {},
|
||||
"columns": {}
|
||||
},
|
||||
"internal": {
|
||||
"indexes": {}
|
||||
}
|
||||
}
|
||||
@@ -22,13 +22,6 @@
|
||||
"when": 1762526423527,
|
||||
"tag": "0002_wealthy_naoko",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 3,
|
||||
"version": "6",
|
||||
"when": 1763500397620,
|
||||
"tag": "0003_smooth_talkback",
|
||||
"breakpoints": true
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -849,7 +849,7 @@ class FileStorage {
|
||||
const resolvedPath = path.resolve(dirPath)
|
||||
|
||||
const stat = await fs.promises.stat(resolvedPath).catch((error) => {
|
||||
logger.error(`Failed to access directory: ${resolvedPath}`, error as Error)
|
||||
logger.error(`[IPC - Error] Failed to access directory: ${resolvedPath}`, error as Error)
|
||||
throw error
|
||||
})
|
||||
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import { type Client, createClient } from '@libsql/client'
|
||||
import { loggerService } from '@logger'
|
||||
import { mcpApiService } from '@main/apiServer/services/mcp'
|
||||
import type { ModelValidationError } from '@main/apiServer/utils'
|
||||
import { validateModelId } from '@main/apiServer/utils'
|
||||
import type { AgentType, MCPTool, SlashCommand, Tool } from '@types'
|
||||
import { objectKeys } from '@types'
|
||||
import { drizzle, type LibSQLDatabase } from 'drizzle-orm/libsql'
|
||||
import Database from 'better-sqlite3'
|
||||
import { type BetterSQLite3Database, drizzle } from 'drizzle-orm/better-sqlite3'
|
||||
import fs from 'fs'
|
||||
import path from 'path'
|
||||
|
||||
@@ -32,8 +32,8 @@ const logger = loggerService.withContext('BaseService')
|
||||
* - Connection retry logic with exponential backoff
|
||||
*/
|
||||
export abstract class BaseService {
|
||||
protected static client: Client | null = null
|
||||
protected static db: LibSQLDatabase<typeof schema> | null = null
|
||||
protected static client: Database.Database | null = null
|
||||
protected static db: BetterSQLite3Database<typeof schema> | null = null
|
||||
protected static isInitialized = false
|
||||
protected static initializationPromise: Promise<void> | null = null
|
||||
protected jsonFields: string[] = [
|
||||
@@ -42,7 +42,6 @@ export abstract class BaseService {
|
||||
'configuration',
|
||||
'accessible_paths',
|
||||
'allowed_tools',
|
||||
'sub_agents',
|
||||
'slash_commands'
|
||||
]
|
||||
|
||||
@@ -117,9 +116,7 @@ export abstract class BaseService {
|
||||
fs.mkdirSync(dbDir, { recursive: true })
|
||||
}
|
||||
|
||||
BaseService.client = createClient({
|
||||
url: `file:${dbPath}`
|
||||
})
|
||||
BaseService.client = new Database(dbPath)
|
||||
|
||||
BaseService.db = drizzle(BaseService.client, { schema })
|
||||
|
||||
@@ -166,12 +163,12 @@ export abstract class BaseService {
|
||||
}
|
||||
}
|
||||
|
||||
protected get database(): LibSQLDatabase<typeof schema> {
|
||||
protected get database(): BetterSQLite3Database<typeof schema> {
|
||||
this.ensureInitialized()
|
||||
return BaseService.db!
|
||||
}
|
||||
|
||||
protected get rawClient(): Client {
|
||||
protected get rawClient(): Database.Database {
|
||||
this.ensureInitialized()
|
||||
return BaseService.client!
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { type Client } from '@libsql/client'
|
||||
import { loggerService } from '@logger'
|
||||
import { getResourcePath } from '@main/utils'
|
||||
import { type LibSQLDatabase } from 'drizzle-orm/libsql'
|
||||
import type Database from 'better-sqlite3'
|
||||
import { type BetterSQLite3Database } from 'drizzle-orm/better-sqlite3'
|
||||
import fs from 'fs'
|
||||
import path from 'path'
|
||||
|
||||
@@ -23,11 +23,11 @@ interface MigrationJournal {
|
||||
}
|
||||
|
||||
export class MigrationService {
|
||||
private db: LibSQLDatabase<typeof schema>
|
||||
private client: Client
|
||||
private db: BetterSQLite3Database<typeof schema>
|
||||
private client: Database.Database
|
||||
private migrationDir: string
|
||||
|
||||
constructor(db: LibSQLDatabase<typeof schema>, client: Client) {
|
||||
constructor(db: BetterSQLite3Database<typeof schema>, client: Database.Database) {
|
||||
this.db = db
|
||||
this.client = client
|
||||
this.migrationDir = path.join(getResourcePath(), 'database', 'drizzle')
|
||||
@@ -88,8 +88,8 @@ export class MigrationService {
|
||||
|
||||
private async migrationsTableExists(): Promise<boolean> {
|
||||
try {
|
||||
const table = await this.client.execute(`SELECT name FROM sqlite_master WHERE type='table' AND name='migrations'`)
|
||||
return table.rows.length > 0
|
||||
const rows = this.client.prepare(`SELECT name FROM sqlite_master WHERE type='table' AND name='migrations'`).all()
|
||||
return rows.length > 0
|
||||
} catch (error) {
|
||||
logger.error('Failed to check migrations table status:', { error })
|
||||
throw error
|
||||
@@ -136,7 +136,7 @@ export class MigrationService {
|
||||
|
||||
// Read and execute SQL
|
||||
const sqlContent = fs.readFileSync(sqlFilePath, 'utf-8')
|
||||
await this.client.executeMultiple(sqlContent)
|
||||
this.client.exec(sqlContent)
|
||||
|
||||
// Record migration as applied (store journal idx as version for tracking)
|
||||
const newMigration: NewMigration = {
|
||||
|
||||
@@ -19,7 +19,6 @@ export const agentsTable = sqliteTable('agents', {
|
||||
|
||||
mcps: text('mcps'), // JSON array of MCP tool IDs
|
||||
allowed_tools: text('allowed_tools'), // JSON array of allowed tool IDs (whitelist)
|
||||
sub_agents: text('sub_agents'), // JSON array of sub-agent IDs
|
||||
|
||||
configuration: text('configuration'), // JSON, extensible settings
|
||||
|
||||
|
||||
@@ -22,7 +22,6 @@ export const sessionsTable = sqliteTable('sessions', {
|
||||
|
||||
mcps: text('mcps'), // JSON array of MCP tool IDs
|
||||
allowed_tools: text('allowed_tools'), // JSON array of allowed tool IDs (whitelist)
|
||||
sub_agents: text('sub_agents'), // JSON array of sub-agent IDs
|
||||
slash_commands: text('slash_commands'), // JSON array of slash command objects from SDK init
|
||||
|
||||
configuration: text('configuration'), // JSON, extensible settings
|
||||
|
||||
@@ -91,17 +91,18 @@ class AgentMessageRepository extends BaseService {
|
||||
return tx ?? this.database
|
||||
}
|
||||
|
||||
private async findExistingMessageRow(
|
||||
private findExistingMessageRow(
|
||||
writer: TxClient,
|
||||
sessionId: string,
|
||||
role: string,
|
||||
messageId: string
|
||||
): Promise<SessionMessageRow | null> {
|
||||
const candidateRows: SessionMessageRow[] = await writer
|
||||
): SessionMessageRow | null {
|
||||
const candidateRows: SessionMessageRow[] = writer
|
||||
.select()
|
||||
.from(sessionMessagesTable)
|
||||
.where(and(eq(sessionMessagesTable.session_id, sessionId), eq(sessionMessagesTable.role, role)))
|
||||
.orderBy(asc(sessionMessagesTable.created_at))
|
||||
.all()
|
||||
|
||||
for (const row of candidateRows) {
|
||||
if (!row?.content) continue
|
||||
@@ -119,12 +120,9 @@ class AgentMessageRepository extends BaseService {
|
||||
return null
|
||||
}
|
||||
|
||||
private async upsertMessage(
|
||||
private upsertMessageSync(
|
||||
params: PersistUserMessageParams | PersistAssistantMessageParams
|
||||
): Promise<AgentSessionMessageEntity> {
|
||||
await AgentMessageRepository.initialize()
|
||||
this.ensureInitialized()
|
||||
|
||||
): AgentSessionMessageEntity {
|
||||
const { sessionId, agentSessionId = '', payload, metadata, createdAt, tx } = params
|
||||
|
||||
if (!payload?.message?.role) {
|
||||
@@ -140,13 +138,13 @@ class AgentMessageRepository extends BaseService {
|
||||
const serializedPayload = this.serializeMessage(payload)
|
||||
const serializedMetadata = this.serializeMetadata(metadata)
|
||||
|
||||
const existingRow = await this.findExistingMessageRow(writer, sessionId, payload.message.role, payload.message.id)
|
||||
const existingRow = this.findExistingMessageRow(writer, sessionId, payload.message.role, payload.message.id)
|
||||
|
||||
if (existingRow) {
|
||||
const metadataToPersist = serializedMetadata ?? existingRow.metadata ?? undefined
|
||||
const agentSessionToPersist = agentSessionId || existingRow.agent_session_id || ''
|
||||
|
||||
await writer
|
||||
writer
|
||||
.update(sessionMessagesTable)
|
||||
.set({
|
||||
content: serializedPayload,
|
||||
@@ -155,6 +153,7 @@ class AgentMessageRepository extends BaseService {
|
||||
updated_at: now
|
||||
})
|
||||
.where(eq(sessionMessagesTable.id, existingRow.id))
|
||||
.run()
|
||||
|
||||
return this.deserialize({
|
||||
...existingRow,
|
||||
@@ -175,11 +174,19 @@ class AgentMessageRepository extends BaseService {
|
||||
updated_at: now
|
||||
}
|
||||
|
||||
const [saved] = await writer.insert(sessionMessagesTable).values(insertData).returning()
|
||||
const [saved] = writer.insert(sessionMessagesTable).values(insertData).returning().all()
|
||||
|
||||
return this.deserialize(saved)
|
||||
}
|
||||
|
||||
private async upsertMessage(
|
||||
params: PersistUserMessageParams | PersistAssistantMessageParams
|
||||
): Promise<AgentSessionMessageEntity> {
|
||||
await AgentMessageRepository.initialize()
|
||||
this.ensureInitialized()
|
||||
return this.upsertMessageSync(params)
|
||||
}
|
||||
|
||||
async persistUserMessage(params: PersistUserMessageParams): Promise<AgentSessionMessageEntity> {
|
||||
return this.upsertMessage({ ...params, agentSessionId: params.agentSessionId ?? '' })
|
||||
}
|
||||
@@ -194,11 +201,11 @@ class AgentMessageRepository extends BaseService {
|
||||
|
||||
const { sessionId, agentSessionId, user, assistant } = params
|
||||
|
||||
const result = await this.database.transaction(async (tx) => {
|
||||
const result = this.database.transaction((tx) => {
|
||||
const exchangeResult: PersistExchangeResult = {}
|
||||
|
||||
if (user?.payload) {
|
||||
exchangeResult.userMessage = await this.persistUserMessage({
|
||||
exchangeResult.userMessage = this.upsertMessageSync({
|
||||
sessionId,
|
||||
agentSessionId,
|
||||
payload: user.payload,
|
||||
@@ -209,7 +216,7 @@ class AgentMessageRepository extends BaseService {
|
||||
}
|
||||
|
||||
if (assistant?.payload) {
|
||||
exchangeResult.assistantMessage = await this.persistAssistantMessage({
|
||||
exchangeResult.assistantMessage = this.upsertMessageSync({
|
||||
sessionId,
|
||||
agentSessionId,
|
||||
payload: assistant.payload,
|
||||
|
||||
@@ -24,7 +24,7 @@ export default defineConfig({
|
||||
schema: './src/main/services/agents/database/schema/index.ts',
|
||||
out: './resources/database/drizzle',
|
||||
dbCredentials: {
|
||||
url: `file:${resolvedDbPath}`
|
||||
url: resolvedDbPath
|
||||
},
|
||||
verbose: true,
|
||||
strict: true
|
||||
|
||||
@@ -117,19 +117,6 @@ export class AgentService extends BaseService {
|
||||
return agent
|
||||
}
|
||||
|
||||
async getAgentConfigForSDK(id: string): Promise<AgentEntity | null> {
|
||||
this.ensureInitialized()
|
||||
|
||||
const result = await this.database.select().from(agentsTable).where(eq(agentsTable.id, id)).limit(1)
|
||||
|
||||
if (!result[0]) {
|
||||
return null
|
||||
}
|
||||
|
||||
const agent = this.deserializeJsonFields(result[0]) as AgentEntity
|
||||
return agent
|
||||
}
|
||||
|
||||
async listAgents(options: ListOptions = {}): Promise<{ agents: AgentEntity[]; total: number }> {
|
||||
this.ensureInitialized() // Build query with pagination
|
||||
|
||||
@@ -215,9 +202,9 @@ export class AgentService extends BaseService {
|
||||
async deleteAgent(id: string): Promise<boolean> {
|
||||
this.ensureInitialized()
|
||||
|
||||
const result = await this.database.delete(agentsTable).where(eq(agentsTable.id, id))
|
||||
const result = this.database.delete(agentsTable).where(eq(agentsTable.id, id)).run()
|
||||
|
||||
return result.rowsAffected > 0
|
||||
return result.changes > 0
|
||||
}
|
||||
|
||||
async agentExists(id: string): Promise<boolean> {
|
||||
|
||||
@@ -148,11 +148,12 @@ export class SessionMessageService extends BaseService {
|
||||
async deleteSessionMessage(sessionId: string, messageId: number): Promise<boolean> {
|
||||
this.ensureInitialized()
|
||||
|
||||
const result = await this.database
|
||||
const result = this.database
|
||||
.delete(sessionMessagesTable)
|
||||
.where(and(eq(sessionMessagesTable.id, messageId), eq(sessionMessagesTable.session_id, sessionId)))
|
||||
.run()
|
||||
|
||||
return result.rowsAffected > 0
|
||||
return result.changes > 0
|
||||
}
|
||||
|
||||
async createSessionMessage(
|
||||
|
||||
@@ -130,7 +130,6 @@ export class SessionService extends BaseService {
|
||||
small_model: serializedData.small_model || null,
|
||||
mcps: serializedData.mcps || null,
|
||||
allowed_tools: serializedData.allowed_tools || null,
|
||||
sub_agents: serializedData.sub_agents || null,
|
||||
configuration: serializedData.configuration || null,
|
||||
created_at: now,
|
||||
updated_at: now
|
||||
@@ -170,22 +169,6 @@ export class SessionService extends BaseService {
|
||||
session.slash_commands = await this.listSlashCommands(session.agent_type, agentId)
|
||||
}
|
||||
|
||||
// Load installed plugins from cache file
|
||||
const workdir = session.accessible_paths?.[0]
|
||||
if (workdir) {
|
||||
try {
|
||||
session.plugins = await pluginService.listInstalledFromCache(workdir)
|
||||
} catch (error) {
|
||||
logger.warn(`Failed to load installed plugins for session ${id}`, {
|
||||
workdir,
|
||||
error: error instanceof Error ? error.message : String(error)
|
||||
})
|
||||
session.plugins = []
|
||||
}
|
||||
} else {
|
||||
session.plugins = []
|
||||
}
|
||||
|
||||
return session
|
||||
}
|
||||
|
||||
@@ -287,11 +270,12 @@ export class SessionService extends BaseService {
|
||||
async deleteSession(agentId: string, id: string): Promise<boolean> {
|
||||
this.ensureInitialized()
|
||||
|
||||
const result = await this.database
|
||||
const result = this.database
|
||||
.delete(sessionsTable)
|
||||
.where(and(eq(sessionsTable.id, id), eq(sessionsTable.agent_id, agentId)))
|
||||
.run()
|
||||
|
||||
return result.rowsAffected > 0
|
||||
return result.changes > 0
|
||||
}
|
||||
|
||||
async sessionExists(agentId: string, id: string): Promise<boolean> {
|
||||
|
||||
@@ -2,13 +2,7 @@
|
||||
import { EventEmitter } from 'node:events'
|
||||
import { createRequire } from 'node:module'
|
||||
|
||||
import type {
|
||||
AgentDefinition,
|
||||
CanUseTool,
|
||||
McpHttpServerConfig,
|
||||
Options,
|
||||
SDKMessage
|
||||
} from '@anthropic-ai/claude-agent-sdk'
|
||||
import type { CanUseTool, McpHttpServerConfig, Options, SDKMessage } from '@anthropic-ai/claude-agent-sdk'
|
||||
import { query } from '@anthropic-ai/claude-agent-sdk'
|
||||
import { loggerService } from '@logger'
|
||||
import { config as apiConfigService } from '@main/apiServer/config'
|
||||
@@ -16,7 +10,7 @@ import { validateModelId } from '@main/apiServer/utils'
|
||||
import getLoginShellEnvironment from '@main/utils/shell-env'
|
||||
import { app } from 'electron'
|
||||
|
||||
import { agentService, type GetAgentSessionResponse } from '../..'
|
||||
import type { GetAgentSessionResponse } from '../..'
|
||||
import type { AgentServiceInterface, AgentStream, AgentStreamEvent } from '../../interfaces/AgentStreamInterface'
|
||||
import { sessionService } from '../SessionService'
|
||||
import { buildNamespacedToolCallId } from './claude-stream-state'
|
||||
@@ -163,32 +157,6 @@ class ClaudeCodeService implements AgentServiceInterface {
|
||||
})
|
||||
}
|
||||
|
||||
const subAgents: Record<string, AgentDefinition> = {}
|
||||
if (session.sub_agents && session.sub_agents.length > 0) {
|
||||
for (const subAgentId of session.sub_agents) {
|
||||
try {
|
||||
const agentConfig = await agentService.getAgentConfigForSDK(subAgentId)
|
||||
if (agentConfig) {
|
||||
subAgents[subAgentId] = {
|
||||
// TODO: support custom model for sub-agents
|
||||
model: 'inherit',
|
||||
description: agentConfig.description ?? '',
|
||||
prompt: agentConfig.instructions ?? '',
|
||||
tools: agentConfig.allowed_tools
|
||||
}
|
||||
logger.info('Loaded sub-agent', { subAgentId })
|
||||
} else {
|
||||
logger.warn('Sub-agent not found', { subAgentId })
|
||||
}
|
||||
} catch (error) {
|
||||
logger.error('Failed to load sub-agent config', {
|
||||
subAgentId,
|
||||
error: error instanceof Error ? error.message : String(error)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Build SDK options from parameters
|
||||
const options: Options = {
|
||||
abortController,
|
||||
|
||||
@@ -156,12 +156,6 @@
|
||||
"uninstalling": "Uninstalling..."
|
||||
},
|
||||
"prompt": "Prompt Settings",
|
||||
"sub_agents": {
|
||||
"placeholder": "Select sub agents",
|
||||
"tab": "Sub Agents",
|
||||
"title": "Sub Agents",
|
||||
"tooltip": "Select other agents that can be delegated tasks by this agent"
|
||||
},
|
||||
"tooling": {
|
||||
"mcp": {
|
||||
"description": "Connect MCP servers to unlock additional tools you can approve above.",
|
||||
@@ -647,7 +641,6 @@
|
||||
"description": "No files available in accessible directories",
|
||||
"label": "No File Found"
|
||||
},
|
||||
"sub_agent": "Sub-Agent",
|
||||
"title": "Activity Directory"
|
||||
},
|
||||
"auto_resize": "Auto resize height",
|
||||
|
||||
@@ -156,12 +156,6 @@
|
||||
"uninstalling": "卸载中..."
|
||||
},
|
||||
"prompt": "提示词设置",
|
||||
"sub_agents": {
|
||||
"placeholder": "选择子智能体",
|
||||
"tab": "子智能体",
|
||||
"title": "子智能体",
|
||||
"tooltip": "选择可以被此智能体委派任务的其他智能体"
|
||||
},
|
||||
"tooling": {
|
||||
"mcp": {
|
||||
"description": "连接 MCP 服务器即可解锁更多可在上方预先授权的工具。",
|
||||
@@ -647,7 +641,6 @@
|
||||
"description": "可访问目录中没有可用文件",
|
||||
"label": "未找到文件"
|
||||
},
|
||||
"sub_agent": "子代理",
|
||||
"title": "活动目录"
|
||||
},
|
||||
"auto_resize": "自动调整高度",
|
||||
|
||||
@@ -156,12 +156,6 @@
|
||||
"uninstalling": "解除安裝中..."
|
||||
},
|
||||
"prompt": "提示設定",
|
||||
"sub_agents": {
|
||||
"placeholder": "選擇子助手",
|
||||
"tab": "子助手",
|
||||
"title": "子助手",
|
||||
"tooltip": "選擇可以被此助手委派任務的其他助手"
|
||||
},
|
||||
"tooling": {
|
||||
"mcp": {
|
||||
"description": "連線 MCP 伺服器即可解鎖更多可在上方預先授權的工具。",
|
||||
@@ -647,7 +641,6 @@
|
||||
"description": "可存取的目錄中沒有檔案",
|
||||
"label": "找不到檔案"
|
||||
},
|
||||
"sub_agent": "子代理",
|
||||
"title": "活動目錄"
|
||||
},
|
||||
"auto_resize": "自動調整高度",
|
||||
|
||||
@@ -156,12 +156,6 @@
|
||||
"uninstalling": "Deinstallation läuft..."
|
||||
},
|
||||
"prompt": "Prompt-Einstellungen",
|
||||
"sub_agents": {
|
||||
"placeholder": "[to be translated]:Select sub agents",
|
||||
"tab": "[to be translated]:Sub Agents",
|
||||
"title": "[to be translated]:Sub Agents",
|
||||
"tooltip": "[to be translated]:Select other agents that can be delegated tasks by this agent"
|
||||
},
|
||||
"tooling": {
|
||||
"mcp": {
|
||||
"description": "Verbinden Sie MCP-Server, um weitere Tools freizuschalten, die oben vorab autorisiert werden können.",
|
||||
|
||||
@@ -156,12 +156,6 @@
|
||||
"uninstalling": "Απεγκατάσταση..."
|
||||
},
|
||||
"prompt": "Ρυθμίσεις Προτροπής",
|
||||
"sub_agents": {
|
||||
"placeholder": "[to be translated]:Select sub agents",
|
||||
"tab": "[to be translated]:Sub Agents",
|
||||
"title": "[to be translated]:Sub Agents",
|
||||
"tooltip": "[to be translated]:Select other agents that can be delegated tasks by this agent"
|
||||
},
|
||||
"tooling": {
|
||||
"mcp": {
|
||||
"description": "Συνδέστε διακομιστές MCP για να ξεκλειδώσετε πρόσθετα εργαλεία που μπορείτε να εγκρίνετε παραπάνω.",
|
||||
|
||||
@@ -156,12 +156,6 @@
|
||||
"uninstalling": "Desinstalando..."
|
||||
},
|
||||
"prompt": "Configuración de indicaciones",
|
||||
"sub_agents": {
|
||||
"placeholder": "[to be translated]:Select sub agents",
|
||||
"tab": "[to be translated]:Sub Agents",
|
||||
"title": "[to be translated]:Sub Agents",
|
||||
"tooltip": "[to be translated]:Select other agents that can be delegated tasks by this agent"
|
||||
},
|
||||
"tooling": {
|
||||
"mcp": {
|
||||
"description": "Conecta servidores MCP para desbloquear herramientas adicionales que puedes aprobar arriba.",
|
||||
|
||||
@@ -156,12 +156,6 @@
|
||||
"uninstalling": "Désinstallation en cours..."
|
||||
},
|
||||
"prompt": "Paramètres de l'invite",
|
||||
"sub_agents": {
|
||||
"placeholder": "[to be translated]:Select sub agents",
|
||||
"tab": "[to be translated]:Sub Agents",
|
||||
"title": "[to be translated]:Sub Agents",
|
||||
"tooltip": "[to be translated]:Select other agents that can be delegated tasks by this agent"
|
||||
},
|
||||
"tooling": {
|
||||
"mcp": {
|
||||
"description": "Connectez des serveurs MCP pour débloquer des outils supplémentaires que vous pouvez approuver ci-dessus.",
|
||||
|
||||
@@ -156,12 +156,6 @@
|
||||
"uninstalling": "アンインストール中..."
|
||||
},
|
||||
"prompt": "プロンプト設定",
|
||||
"sub_agents": {
|
||||
"placeholder": "[to be translated]:Select sub agents",
|
||||
"tab": "[to be translated]:Sub Agents",
|
||||
"title": "[to be translated]:Sub Agents",
|
||||
"tooltip": "[to be translated]:Select other agents that can be delegated tasks by this agent"
|
||||
},
|
||||
"tooling": {
|
||||
"mcp": {
|
||||
"description": "MCPサーバーを接続して、上で承認できる追加ツールを解放します。",
|
||||
|
||||
@@ -156,12 +156,6 @@
|
||||
"uninstalling": "Desinstalando..."
|
||||
},
|
||||
"prompt": "Configurações de Prompt",
|
||||
"sub_agents": {
|
||||
"placeholder": "[to be translated]:Select sub agents",
|
||||
"tab": "[to be translated]:Sub Agents",
|
||||
"title": "[to be translated]:Sub Agents",
|
||||
"tooltip": "[to be translated]:Select other agents that can be delegated tasks by this agent"
|
||||
},
|
||||
"tooling": {
|
||||
"mcp": {
|
||||
"description": "Conecte servidores MCP para desbloquear ferramentas adicionais que você pode aprovar acima.",
|
||||
|
||||
@@ -156,12 +156,6 @@
|
||||
"uninstalling": "Удаление..."
|
||||
},
|
||||
"prompt": "Настройки подсказки",
|
||||
"sub_agents": {
|
||||
"placeholder": "[to be translated]:Select sub agents",
|
||||
"tab": "[to be translated]:Sub Agents",
|
||||
"title": "[to be translated]:Sub Agents",
|
||||
"tooltip": "[to be translated]:Select other agents that can be delegated tasks by this agent"
|
||||
},
|
||||
"tooling": {
|
||||
"mcp": {
|
||||
"description": "Подключите серверы MCP, чтобы разблокировать дополнительные инструменты, которые вы можете одобрить выше.",
|
||||
|
||||
@@ -103,23 +103,12 @@ const AgentSessionInputbar: FC<Props> = ({ agentId, sessionId }) => {
|
||||
// Prepare session data for tools
|
||||
const sessionData = useMemo(() => {
|
||||
if (!session) return undefined
|
||||
|
||||
// Get installed agent plugins from session.plugins
|
||||
const agentPlugins = (session.plugins ?? [])
|
||||
.filter((plugin) => plugin.type === 'agent')
|
||||
.map((plugin) => ({
|
||||
id: plugin.filename,
|
||||
name: plugin.metadata.name ?? plugin.filename.replace(/\.md$/i, ''),
|
||||
description: plugin.metadata.description
|
||||
}))
|
||||
|
||||
return {
|
||||
agentId,
|
||||
sessionId,
|
||||
slashCommands: session.slash_commands,
|
||||
tools: session.tools,
|
||||
accessiblePaths: session.accessible_paths ?? [],
|
||||
subAgents: agentPlugins
|
||||
accessiblePaths: session.accessible_paths ?? []
|
||||
}
|
||||
}, [session, agentId, sessionId])
|
||||
|
||||
@@ -169,8 +158,6 @@ interface InnerProps {
|
||||
sessionId?: string
|
||||
slashCommands?: Array<{ command: string; description?: string }>
|
||||
tools?: Array<{ id: string; name: string; type: string; description?: string }>
|
||||
accessiblePaths?: string[]
|
||||
subAgents?: Array<{ id: string; name: string; description?: string }>
|
||||
}
|
||||
actionsRef: React.MutableRefObject<{
|
||||
resizeTextArea: () => void
|
||||
|
||||
@@ -25,12 +25,11 @@ const activityDirectoryTool = defineTool({
|
||||
const { quickPanel, quickPanelController, actions, session } = context
|
||||
const { onTextChange } = actions
|
||||
|
||||
// Get accessible paths and sub-agents from session data
|
||||
// Get accessible paths from session data
|
||||
const accessiblePaths = session?.accessiblePaths ?? []
|
||||
const subAgents = session?.subAgents ?? []
|
||||
|
||||
// Only render if we have accessible paths or sub-agents
|
||||
if (accessiblePaths.length === 0 && subAgents.length === 0) {
|
||||
// Only render if we have accessible paths
|
||||
if (accessiblePaths.length === 0) {
|
||||
return null
|
||||
}
|
||||
|
||||
@@ -39,7 +38,6 @@ const activityDirectoryTool = defineTool({
|
||||
quickPanel={quickPanel}
|
||||
quickPanelController={quickPanelController}
|
||||
accessiblePaths={accessiblePaths}
|
||||
subAgents={subAgents}
|
||||
setText={onTextChange as React.Dispatch<React.SetStateAction<string>>}
|
||||
/>
|
||||
)
|
||||
|
||||
@@ -13,17 +13,10 @@ interface Props {
|
||||
quickPanel: ToolQuickPanelApi
|
||||
quickPanelController: ToolQuickPanelController
|
||||
accessiblePaths: string[]
|
||||
subAgents?: Array<{ id: string; name: string; description?: string }>
|
||||
setText: React.Dispatch<React.SetStateAction<string>>
|
||||
}
|
||||
|
||||
const ActivityDirectoryButton: FC<Props> = ({
|
||||
quickPanel,
|
||||
quickPanelController,
|
||||
accessiblePaths,
|
||||
subAgents,
|
||||
setText
|
||||
}) => {
|
||||
const ActivityDirectoryButton: FC<Props> = ({ quickPanel, quickPanelController, accessiblePaths, setText }) => {
|
||||
const { t } = useTranslation()
|
||||
|
||||
const { handleOpenQuickPanel } = useActivityDirectoryPanel(
|
||||
@@ -31,7 +24,6 @@ const ActivityDirectoryButton: FC<Props> = ({
|
||||
quickPanel,
|
||||
quickPanelController,
|
||||
accessiblePaths,
|
||||
subAgents,
|
||||
setText
|
||||
},
|
||||
'button'
|
||||
|
||||
@@ -15,9 +15,8 @@ const ActivityDirectoryQuickPanelManager = ({ context }: ManagerProps) => {
|
||||
session
|
||||
} = context
|
||||
|
||||
// Get accessible paths and sub-agents from session data
|
||||
// Get accessible paths from session data
|
||||
const accessiblePaths = session?.accessiblePaths ?? []
|
||||
const subAgents = session?.subAgents ?? []
|
||||
|
||||
// Always call hooks unconditionally (React rules)
|
||||
useActivityDirectoryPanel(
|
||||
@@ -25,7 +24,6 @@ const ActivityDirectoryQuickPanelManager = ({ context }: ManagerProps) => {
|
||||
quickPanel,
|
||||
quickPanelController,
|
||||
accessiblePaths,
|
||||
subAgents,
|
||||
setText: onTextChange as React.Dispatch<React.SetStateAction<string>>
|
||||
},
|
||||
'manager'
|
||||
|
||||
@@ -2,7 +2,7 @@ import { loggerService } from '@logger'
|
||||
import type { QuickPanelListItem } from '@renderer/components/QuickPanel'
|
||||
import { QuickPanelReservedSymbol } from '@renderer/components/QuickPanel'
|
||||
import type { ToolQuickPanelApi, ToolQuickPanelController } from '@renderer/pages/home/Inputbar/types'
|
||||
import { Bot, File, Folder } from 'lucide-react'
|
||||
import { File, Folder } from 'lucide-react'
|
||||
import type React from 'react'
|
||||
import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
@@ -25,22 +25,15 @@ export type ActivityDirectoryTriggerInfo = {
|
||||
symbol?: QuickPanelReservedSymbol
|
||||
}
|
||||
|
||||
interface SubAgentInfo {
|
||||
id: string
|
||||
name: string
|
||||
description?: string
|
||||
}
|
||||
|
||||
interface Params {
|
||||
quickPanel: ToolQuickPanelApi
|
||||
quickPanelController: ToolQuickPanelController
|
||||
accessiblePaths: string[]
|
||||
subAgents?: SubAgentInfo[]
|
||||
setText: React.Dispatch<React.SetStateAction<string>>
|
||||
}
|
||||
|
||||
export const useActivityDirectoryPanel = (params: Params, role: 'button' | 'manager' = 'button') => {
|
||||
const { quickPanel, quickPanelController, accessiblePaths, subAgents = [], setText } = params
|
||||
const { quickPanel, quickPanelController, accessiblePaths, setText } = params
|
||||
const { registerTrigger, registerRootMenu } = quickPanel
|
||||
const { open, close, updateList, isVisible, symbol } = quickPanelController
|
||||
const { t } = useTranslation()
|
||||
@@ -245,68 +238,6 @@ export const useActivityDirectoryPanel = (params: Params, role: 'button' | 'mana
|
||||
[close, insertFilePath]
|
||||
)
|
||||
|
||||
/**
|
||||
* Insert sub-agent name at @ position
|
||||
*/
|
||||
const insertSubAgentName = useCallback(
|
||||
(agentName: string, triggerInfo?: ActivityDirectoryTriggerInfo) => {
|
||||
setText((currentText) => {
|
||||
const symbol = triggerInfo?.symbol ?? QuickPanelReservedSymbol.MentionModels
|
||||
const triggerIndex =
|
||||
triggerInfo?.position !== undefined
|
||||
? triggerInfo.position
|
||||
: symbol === QuickPanelReservedSymbol.Root
|
||||
? currentText.lastIndexOf('/')
|
||||
: currentText.lastIndexOf('@')
|
||||
|
||||
if (triggerIndex !== -1) {
|
||||
let endPos = triggerIndex + 1
|
||||
while (endPos < currentText.length && !/\s/.test(currentText[endPos])) {
|
||||
endPos++
|
||||
}
|
||||
return currentText.slice(0, triggerIndex) + agentName + ' ' + currentText.slice(endPos)
|
||||
}
|
||||
|
||||
// If no trigger found, append at end
|
||||
return currentText + ' ' + agentName + ' '
|
||||
})
|
||||
},
|
||||
[setText]
|
||||
)
|
||||
|
||||
/**
|
||||
* Handle sub-agent selection
|
||||
*/
|
||||
const onSelectSubAgent = useCallback(
|
||||
(agentName: string) => {
|
||||
const trigger = triggerInfoRef.current
|
||||
insertSubAgentName(agentName, trigger)
|
||||
close()
|
||||
},
|
||||
[close, insertSubAgentName]
|
||||
)
|
||||
|
||||
/**
|
||||
* Create sub-agent list items for QuickPanel
|
||||
*/
|
||||
const createSubAgentItems = useCallback(
|
||||
(agents: SubAgentInfo[]): QuickPanelListItem[] => {
|
||||
if (agents.length === 0) {
|
||||
return []
|
||||
}
|
||||
|
||||
return agents.map((agent) => ({
|
||||
label: agent.name,
|
||||
description: agent.description || t('chat.input.activity_directory.sub_agent'),
|
||||
icon: <Bot size={16} />,
|
||||
filterText: `${agent.name} ${agent.description || ''} ${agent.id}`,
|
||||
action: () => onSelectSubAgent(agent.name),
|
||||
isSelected: false
|
||||
}))
|
||||
},
|
||||
[onSelectSubAgent, t]
|
||||
)
|
||||
|
||||
/**
|
||||
* Create file list items for QuickPanel from a file list
|
||||
*/
|
||||
@@ -360,18 +291,12 @@ export const useActivityDirectoryPanel = (params: Params, role: 'button' | 'mana
|
||||
)
|
||||
|
||||
/**
|
||||
* Create combined list items for QuickPanel (sub-agents + files)
|
||||
* Create file list items for QuickPanel (for current state)
|
||||
*/
|
||||
const combinedItems = useMemo<QuickPanelListItem[]>(() => {
|
||||
const agentItems = createSubAgentItems(subAgents)
|
||||
const files = createFileItems(fileList, isLoading)
|
||||
|
||||
// Combine: sub-agents first, then files
|
||||
return [...agentItems, ...files]
|
||||
}, [createSubAgentItems, subAgents, createFileItems, fileList, isLoading])
|
||||
|
||||
// Keep fileItems for backward compatibility
|
||||
const fileItems = combinedItems
|
||||
const fileItems = useMemo<QuickPanelListItem[]>(
|
||||
() => createFileItems(fileList, isLoading),
|
||||
[createFileItems, fileList, isLoading]
|
||||
)
|
||||
|
||||
/**
|
||||
* Handle search text change - load files and update list
|
||||
@@ -386,13 +311,11 @@ export const useActivityDirectoryPanel = (params: Params, role: 'button' | 'mana
|
||||
|
||||
const hasChanged = updateFileListState(newFiles)
|
||||
if (hasChanged) {
|
||||
// Combine sub-agents and files
|
||||
const agentItems = createSubAgentItems(subAgents)
|
||||
const fileItems = createFileItems(newFiles, false)
|
||||
updateList([...agentItems, ...fileItems])
|
||||
const newItems = createFileItems(newFiles, false)
|
||||
updateList(newItems)
|
||||
}
|
||||
},
|
||||
[loadFiles, createFileItems, createSubAgentItems, subAgents, updateList, updateFileListState]
|
||||
[loadFiles, createFileItems, updateList, updateFileListState]
|
||||
)
|
||||
|
||||
/**
|
||||
@@ -413,10 +336,8 @@ export const useActivityDirectoryPanel = (params: Params, role: 'button' | 'mana
|
||||
const files = await loadFiles()
|
||||
updateFileListState(files)
|
||||
|
||||
// Create items from sub-agents and loaded files immediately
|
||||
const agentItems = createSubAgentItems(subAgents)
|
||||
const fileItems = createFileItems(files, false)
|
||||
const items = [...agentItems, ...fileItems]
|
||||
// Create items from the loaded files immediately
|
||||
const items = createFileItems(files, false)
|
||||
|
||||
open({
|
||||
title: t('chat.input.activity_directory.description'),
|
||||
@@ -456,18 +377,7 @@ export const useActivityDirectoryPanel = (params: Params, role: 'button' | 'mana
|
||||
onSearchChange: handleSearchChange
|
||||
})
|
||||
},
|
||||
[
|
||||
loadFiles,
|
||||
open,
|
||||
removeTriggerSymbolAndText,
|
||||
setText,
|
||||
t,
|
||||
handleSearchChange,
|
||||
createFileItems,
|
||||
createSubAgentItems,
|
||||
subAgents,
|
||||
updateFileListState
|
||||
]
|
||||
[loadFiles, open, removeTriggerSymbolAndText, setText, t, handleSearchChange, createFileItems, updateFileListState]
|
||||
)
|
||||
|
||||
/**
|
||||
|
||||
@@ -68,7 +68,6 @@ export interface ToolContext {
|
||||
slashCommands?: Array<{ command: string; description?: string }>
|
||||
tools?: Array<{ id: string; name: string; type: string; description?: string }>
|
||||
accessiblePaths?: string[]
|
||||
subAgents?: Array<{ id: string; name: string; description?: string }>
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -11,7 +11,6 @@ import EssentialSettings from './EssentialSettings'
|
||||
import PluginSettings from './PluginSettings'
|
||||
import PromptSettings from './PromptSettings'
|
||||
import { AgentLabel, LeftMenu, Settings, StyledMenu, StyledModal } from './shared'
|
||||
import SubAgentsSettings from './SubAgentsSettings'
|
||||
import ToolingSettings from './ToolingSettings'
|
||||
|
||||
interface AgentSettingPopupShowParams {
|
||||
@@ -23,7 +22,7 @@ interface AgentSettingPopupParams extends AgentSettingPopupShowParams {
|
||||
resolve: () => void
|
||||
}
|
||||
|
||||
type AgentSettingPopupTab = 'essential' | 'prompt' | 'tooling' | 'advanced' | 'plugins' | 'sub-agents' | 'session-mcps'
|
||||
type AgentSettingPopupTab = 'essential' | 'prompt' | 'tooling' | 'advanced' | 'plugins' | 'session-mcps'
|
||||
|
||||
const AgentSettingPopupContainer: React.FC<AgentSettingPopupParams> = ({ tab, agentId, resolve }) => {
|
||||
const [open, setOpen] = useState(true)
|
||||
@@ -63,10 +62,6 @@ const AgentSettingPopupContainer: React.FC<AgentSettingPopupParams> = ({ tab, ag
|
||||
key: 'plugins',
|
||||
label: t('agent.settings.plugins.tab', 'Plugins')
|
||||
},
|
||||
{
|
||||
key: 'sub-agents',
|
||||
label: t('agent.settings.sub_agents.tab', 'Sub-agents')
|
||||
},
|
||||
{
|
||||
key: 'advanced',
|
||||
label: t('agent.settings.advance.title', 'Advanced Settings')
|
||||
@@ -112,7 +107,6 @@ const AgentSettingPopupContainer: React.FC<AgentSettingPopupParams> = ({ tab, ag
|
||||
{menu === 'prompt' && <PromptSettings agentBase={agent} update={updateAgent} />}
|
||||
{menu === 'tooling' && <ToolingSettings agentBase={agent} update={updateAgent} />}
|
||||
{menu === 'plugins' && <PluginSettings agentBase={agent} update={updateAgent} />}
|
||||
{menu === 'sub-agents' && <SubAgentsSettings agentBase={agent} update={updateAgent} />}
|
||||
{menu === 'advanced' && <AdvancedSettings agentBase={agent} update={updateAgent} />}
|
||||
</Settings>
|
||||
</div>
|
||||
|
||||
@@ -1,57 +0,0 @@
|
||||
import { useAgents } from '@renderer/hooks/agents/useAgents'
|
||||
import type { GetAgentResponse, GetAgentSessionResponse, UpdateAgentFunctionUnion } from '@renderer/types'
|
||||
import { Form, Select, Spin } from 'antd'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
|
||||
interface SubAgentsSettingsProps {
|
||||
agentBase: GetAgentResponse | GetAgentSessionResponse | undefined | null
|
||||
update: UpdateAgentFunctionUnion
|
||||
}
|
||||
|
||||
const SubAgentsSettings: React.FC<SubAgentsSettingsProps> = ({ agentBase, update }) => {
|
||||
const { t } = useTranslation()
|
||||
const [form] = Form.useForm()
|
||||
const { agents, isLoading } = useAgents()
|
||||
|
||||
if (!agentBase) return
|
||||
|
||||
const handleValuesChange = (changedValues: { sub_agents: string[] }) => {
|
||||
update({
|
||||
id: agentBase.id,
|
||||
...changedValues
|
||||
})
|
||||
}
|
||||
|
||||
if (isLoading) {
|
||||
return <Spin />
|
||||
}
|
||||
|
||||
const availableAgents = agents?.filter((agent) => agent.id !== agentBase.id) || []
|
||||
|
||||
return (
|
||||
<Form
|
||||
form={form}
|
||||
layout="vertical"
|
||||
initialValues={{ sub_agents: agentBase.sub_agents || [] }}
|
||||
onValuesChange={handleValuesChange}
|
||||
style={{ maxWidth: 600 }}>
|
||||
<Form.Item
|
||||
name="sub_agents"
|
||||
label={t('agent.settings.sub_agents.title')}
|
||||
tooltip={t('agent.settings.sub_agents.tooltip')}>
|
||||
<Select
|
||||
mode="multiple"
|
||||
placeholder={t('agent.settings.sub_agents.placeholder')}
|
||||
loading={isLoading}
|
||||
options={availableAgents.map((agent) => ({
|
||||
label: agent.name,
|
||||
value: agent.id
|
||||
}))}
|
||||
filterOption={(input, option) => (option?.label ?? '').toLowerCase().includes(input.toLowerCase())}
|
||||
/>
|
||||
</Form.Item>
|
||||
</Form>
|
||||
)
|
||||
}
|
||||
|
||||
export default SubAgentsSettings
|
||||
@@ -82,7 +82,6 @@ export const AgentBaseSchema = z.object({
|
||||
// Tools
|
||||
mcps: z.array(z.string()).optional(), // Array of MCP tool IDs
|
||||
allowed_tools: z.array(z.string()).optional(), // Array of allowed tool IDs (whitelist)
|
||||
sub_agents: z.array(z.string()).optional(), // Array of sub-agent IDs
|
||||
slash_commands: z.array(SlashCommandSchema).optional(), // Array of slash commands merged from builtin and SDK
|
||||
|
||||
// Configuration
|
||||
@@ -133,7 +132,7 @@ export const AgentSessionEntitySchema = AgentBaseSchema.extend({
|
||||
id: z.string(),
|
||||
agent_id: z.string(), // Primary agent ID for the session
|
||||
agent_type: AgentTypeSchema,
|
||||
sub_agents: z.array(z.string()).optional(), // Array of sub-agent IDs involved in the session
|
||||
// sub_agent_ids?: string[] // Array of sub-agent IDs involved in the session
|
||||
|
||||
created_at: z.iso.datetime(),
|
||||
updated_at: z.iso.datetime()
|
||||
@@ -206,7 +205,6 @@ export type BaseAgentForm = {
|
||||
model: string
|
||||
accessible_paths: string[]
|
||||
allowed_tools: string[]
|
||||
sub_agents?: string[]
|
||||
mcps?: string[]
|
||||
configuration?: AgentConfiguration
|
||||
}
|
||||
@@ -288,7 +286,6 @@ export interface UpdateSessionRequest extends Partial<AgentBase> {}
|
||||
|
||||
export const GetAgentSessionResponseSchema = AgentSessionEntitySchema.extend({
|
||||
tools: z.array(ToolSchema).optional(), // All tools available to the session (including built-in and custom)
|
||||
sub_agents: z.array(z.string()).optional(), // Array of sub-agent IDs
|
||||
messages: z.array(AgentSessionMessageEntitySchema).optional(), // Messages in the session
|
||||
plugins: z
|
||||
.array(
|
||||
|
||||
40
yarn.lock
40
yarn.lock
@@ -8082,6 +8082,15 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@types/better-sqlite3@npm:^7.6.12":
|
||||
version: 7.6.13
|
||||
resolution: "@types/better-sqlite3@npm:7.6.13"
|
||||
dependencies:
|
||||
"@types/node": "npm:*"
|
||||
checksum: 10c0/c4336e7b92343eb0e988ded007c53fa9887b98a38d61175226e86124a1a2c28b1a4e3892873c5041e350b7bfa2901f85c82db1542c4f0eed1d3a899682c92106
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@types/body-parser@npm:*":
|
||||
version: 1.19.6
|
||||
resolution: "@types/body-parser@npm:1.19.6"
|
||||
@@ -10000,6 +10009,7 @@ __metadata:
|
||||
"@tiptap/y-tiptap": "npm:^3.0.0"
|
||||
"@truto/turndown-plugin-gfm": "npm:^1.0.2"
|
||||
"@tryfabric/martian": "npm:^1.2.4"
|
||||
"@types/better-sqlite3": "npm:^7.6.12"
|
||||
"@types/cli-progress": "npm:^3"
|
||||
"@types/content-type": "npm:^1.1.9"
|
||||
"@types/cors": "npm:^2.8.19"
|
||||
@@ -10043,6 +10053,7 @@ __metadata:
|
||||
archiver: "npm:^7.0.1"
|
||||
async-mutex: "npm:^0.5.0"
|
||||
axios: "npm:^1.7.3"
|
||||
better-sqlite3: "npm:12.4.1"
|
||||
browser-image-compression: "npm:^2.0.2"
|
||||
chardet: "npm:^2.1.0"
|
||||
check-disk-space: "npm:3.4.0"
|
||||
@@ -10902,6 +10913,17 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"better-sqlite3@npm:12.4.1":
|
||||
version: 12.4.1
|
||||
resolution: "better-sqlite3@npm:12.4.1"
|
||||
dependencies:
|
||||
bindings: "npm:^1.5.0"
|
||||
node-gyp: "npm:latest"
|
||||
prebuild-install: "npm:^7.1.1"
|
||||
checksum: 10c0/88773a75d996b4171e5690a38459b05dc814a792701b224bd9909ee084dc0b4c64aaffbdbcf4bbbc6d4e247faf19e91b2a56cf4175d746d3bd9ff14764eb05aa
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"bignumber.js@npm:^9.0.0":
|
||||
version: 9.2.1
|
||||
resolution: "bignumber.js@npm:9.2.1"
|
||||
@@ -10916,6 +10938,15 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"bindings@npm:^1.5.0":
|
||||
version: 1.5.0
|
||||
resolution: "bindings@npm:1.5.0"
|
||||
dependencies:
|
||||
file-uri-to-path: "npm:1.0.0"
|
||||
checksum: 10c0/3dab2491b4bb24124252a91e656803eac24292473e56554e35bbfe3cc1875332cfa77600c3bac7564049dc95075bf6fcc63a4609920ff2d64d0fe405fcf0d4ba
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"birecord@npm:^0.1.1":
|
||||
version: 0.1.1
|
||||
resolution: "birecord@npm:0.1.1"
|
||||
@@ -14930,6 +14961,13 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"file-uri-to-path@npm:1.0.0":
|
||||
version: 1.0.0
|
||||
resolution: "file-uri-to-path@npm:1.0.0"
|
||||
checksum: 10c0/3b545e3a341d322d368e880e1c204ef55f1d45cdea65f7efc6c6ce9e0c4d22d802d5629320eb779d006fe59624ac17b0e848d83cc5af7cd101f206cb704f5519
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"filelist@npm:^1.0.4":
|
||||
version: 1.0.4
|
||||
resolution: "filelist@npm:1.0.4"
|
||||
@@ -20660,7 +20698,7 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"prebuild-install@npm:^7.1.2":
|
||||
"prebuild-install@npm:^7.1.1, prebuild-install@npm:^7.1.2":
|
||||
version: 7.1.3
|
||||
resolution: "prebuild-install@npm:7.1.3"
|
||||
dependencies:
|
||||
|
||||
Reference in New Issue
Block a user