Compare commits
4 Commits
libsql
...
feat/sub_a
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3da8b63673 | ||
|
|
dcdd1bf852 | ||
|
|
a12b6bfeca | ||
|
|
1175823ab8 |
@@ -81,11 +81,12 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"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",
|
"@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",
|
||||||
"@libsql/client": "0.15.15",
|
"@libsql/client": "0.14.0",
|
||||||
"@libsql/win32-x64-msvc": "^0.5.22",
|
"@libsql/win32-x64-msvc": "^0.4.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",
|
"@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",
|
"@paymoapp/electron-shutdown-handler": "^1.1.2",
|
||||||
"@strongtz/win32-arm64-msvc": "^0.4.7",
|
"@strongtz/win32-arm64-msvc": "^0.4.7",
|
||||||
|
"emoji-picker-element-data": "^1",
|
||||||
"express": "^5.1.0",
|
"express": "^5.1.0",
|
||||||
"font-list": "^2.0.0",
|
"font-list": "^2.0.0",
|
||||||
"graceful-fs": "^4.2.11",
|
"graceful-fs": "^4.2.11",
|
||||||
@@ -386,10 +387,10 @@
|
|||||||
"@codemirror/lint": "6.8.5",
|
"@codemirror/lint": "6.8.5",
|
||||||
"@codemirror/view": "6.38.1",
|
"@codemirror/view": "6.38.1",
|
||||||
"@langchain/core@npm:^0.3.26": "patch:@langchain/core@npm%3A1.0.2#~/.yarn/patches/@langchain-core-npm-1.0.2-183ef83fe4.patch",
|
"@langchain/core@npm:^0.3.26": "patch:@langchain/core@npm%3A1.0.2#~/.yarn/patches/@langchain-core-npm-1.0.2-183ef83fe4.patch",
|
||||||
"@libsql/client": "0.15.15",
|
|
||||||
"atomically@npm:^1.7.0": "patch:atomically@npm%3A1.7.0#~/.yarn/patches/atomically-npm-1.7.0-e742e5293b.patch",
|
"atomically@npm:^1.7.0": "patch:atomically@npm%3A1.7.0#~/.yarn/patches/atomically-npm-1.7.0-e742e5293b.patch",
|
||||||
"esbuild": "^0.25.0",
|
"esbuild": "^0.25.0",
|
||||||
"file-stream-rotator@npm:^0.6.1": "patch:file-stream-rotator@npm%3A0.6.1#~/.yarn/patches/file-stream-rotator-npm-0.6.1-eab45fb13d.patch",
|
"file-stream-rotator@npm:^0.6.1": "patch:file-stream-rotator@npm%3A0.6.1#~/.yarn/patches/file-stream-rotator-npm-0.6.1-eab45fb13d.patch",
|
||||||
|
"libsql@npm:^0.4.4": "patch:libsql@npm%3A0.4.7#~/.yarn/patches/libsql-npm-0.4.7-444e260fb1.patch",
|
||||||
"node-abi": "4.24.0",
|
"node-abi": "4.24.0",
|
||||||
"openai@npm:^4.77.0": "npm:@cherrystudio/openai@6.5.0",
|
"openai@npm:^4.77.0": "npm:@cherrystudio/openai@6.5.0",
|
||||||
"openai@npm:^4.87.3": "npm:@cherrystudio/openai@6.5.0",
|
"openai@npm:^4.87.3": "npm:@cherrystudio/openai@6.5.0",
|
||||||
|
|||||||
2
resources/database/drizzle/0003_smooth_talkback.sql
Normal file
2
resources/database/drizzle/0003_smooth_talkback.sql
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
ALTER TABLE `agents` ADD `sub_agents` text;--> statement-breakpoint
|
||||||
|
ALTER TABLE `sessions` ADD `sub_agents` text;
|
||||||
360
resources/database/drizzle/meta/0003_snapshot.json
Normal file
360
resources/database/drizzle/meta/0003_snapshot.json
Normal file
@@ -0,0 +1,360 @@
|
|||||||
|
{
|
||||||
|
"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,6 +22,13 @@
|
|||||||
"when": 1762526423527,
|
"when": 1762526423527,
|
||||||
"tag": "0002_wealthy_naoko",
|
"tag": "0002_wealthy_naoko",
|
||||||
"breakpoints": true
|
"breakpoints": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idx": 3,
|
||||||
|
"version": "6",
|
||||||
|
"when": 1763500397620,
|
||||||
|
"tag": "0003_smooth_talkback",
|
||||||
|
"breakpoints": true
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -104,12 +104,6 @@ const router = express
|
|||||||
logger.warn('No models available from providers', { filter })
|
logger.warn('No models available from providers', { filter })
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.info('Models response ready', {
|
|
||||||
filter,
|
|
||||||
total: response.total,
|
|
||||||
modelIds: response.data.map((m) => m.id)
|
|
||||||
})
|
|
||||||
|
|
||||||
return res.json(response satisfies ApiModelsResponse)
|
return res.json(response satisfies ApiModelsResponse)
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
logger.error('Error fetching models', { error })
|
logger.error('Error fetching models', { error })
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ export class ModelsService {
|
|||||||
|
|
||||||
for (const model of models) {
|
for (const model of models) {
|
||||||
const provider = providers.find((p) => p.id === model.provider)
|
const provider = providers.find((p) => p.id === model.provider)
|
||||||
logger.debug(`Processing model ${model.id}`)
|
// logger.debug(`Processing model ${model.id}`)
|
||||||
if (!provider) {
|
if (!provider) {
|
||||||
logger.debug(`Skipping model ${model.id} . Reason: Provider not found.`)
|
logger.debug(`Skipping model ${model.id} . Reason: Provider not found.`)
|
||||||
continue
|
continue
|
||||||
|
|||||||
@@ -849,7 +849,7 @@ class FileStorage {
|
|||||||
const resolvedPath = path.resolve(dirPath)
|
const resolvedPath = path.resolve(dirPath)
|
||||||
|
|
||||||
const stat = await fs.promises.stat(resolvedPath).catch((error) => {
|
const stat = await fs.promises.stat(resolvedPath).catch((error) => {
|
||||||
logger.error(`[IPC - Error] Failed to access directory: ${resolvedPath}`, error as Error)
|
logger.error(`Failed to access directory: ${resolvedPath}`, error as Error)
|
||||||
throw error
|
throw error
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -42,6 +42,7 @@ export abstract class BaseService {
|
|||||||
'configuration',
|
'configuration',
|
||||||
'accessible_paths',
|
'accessible_paths',
|
||||||
'allowed_tools',
|
'allowed_tools',
|
||||||
|
'sub_agents',
|
||||||
'slash_commands'
|
'slash_commands'
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ export const agentsTable = sqliteTable('agents', {
|
|||||||
|
|
||||||
mcps: text('mcps'), // JSON array of MCP tool IDs
|
mcps: text('mcps'), // JSON array of MCP tool IDs
|
||||||
allowed_tools: text('allowed_tools'), // JSON array of allowed tool IDs (whitelist)
|
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
|
configuration: text('configuration'), // JSON, extensible settings
|
||||||
|
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ export const sessionsTable = sqliteTable('sessions', {
|
|||||||
|
|
||||||
mcps: text('mcps'), // JSON array of MCP tool IDs
|
mcps: text('mcps'), // JSON array of MCP tool IDs
|
||||||
allowed_tools: text('allowed_tools'), // JSON array of allowed tool IDs (whitelist)
|
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
|
slash_commands: text('slash_commands'), // JSON array of slash command objects from SDK init
|
||||||
|
|
||||||
configuration: text('configuration'), // JSON, extensible settings
|
configuration: text('configuration'), // JSON, extensible settings
|
||||||
|
|||||||
@@ -117,6 +117,19 @@ export class AgentService extends BaseService {
|
|||||||
return agent
|
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 }> {
|
async listAgents(options: ListOptions = {}): Promise<{ agents: AgentEntity[]; total: number }> {
|
||||||
this.ensureInitialized() // Build query with pagination
|
this.ensureInitialized() // Build query with pagination
|
||||||
|
|
||||||
|
|||||||
@@ -130,6 +130,7 @@ export class SessionService extends BaseService {
|
|||||||
small_model: serializedData.small_model || null,
|
small_model: serializedData.small_model || null,
|
||||||
mcps: serializedData.mcps || null,
|
mcps: serializedData.mcps || null,
|
||||||
allowed_tools: serializedData.allowed_tools || null,
|
allowed_tools: serializedData.allowed_tools || null,
|
||||||
|
sub_agents: serializedData.sub_agents || null,
|
||||||
configuration: serializedData.configuration || null,
|
configuration: serializedData.configuration || null,
|
||||||
created_at: now,
|
created_at: now,
|
||||||
updated_at: now
|
updated_at: now
|
||||||
@@ -169,6 +170,22 @@ export class SessionService extends BaseService {
|
|||||||
session.slash_commands = await this.listSlashCommands(session.agent_type, agentId)
|
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
|
return session
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -21,6 +21,11 @@ describe('stripLocalCommandTags', () => {
|
|||||||
'<local-command-stdout>line1</local-command-stdout>\nkeep\n<local-command-stderr>Error</local-command-stderr>'
|
'<local-command-stdout>line1</local-command-stdout>\nkeep\n<local-command-stderr>Error</local-command-stderr>'
|
||||||
expect(stripLocalCommandTags(input)).toBe('line1\nkeep\nError')
|
expect(stripLocalCommandTags(input)).toBe('line1\nkeep\nError')
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('if no tags present, returns original string', () => {
|
||||||
|
const input = 'just some normal text'
|
||||||
|
expect(stripLocalCommandTags(input)).toBe(input)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('Claude → AiSDK transform', () => {
|
describe('Claude → AiSDK transform', () => {
|
||||||
@@ -188,6 +193,111 @@ describe('Claude → AiSDK transform', () => {
|
|||||||
expect(toolResult.output).toBe('ok')
|
expect(toolResult.output).toBe('ok')
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('handles tool calls without streaming events (no content_block_start/stop)', () => {
|
||||||
|
const state = new ClaudeStreamState({ agentSessionId: '12344' })
|
||||||
|
const parts: ReturnType<typeof transformSDKMessageToStreamParts>[number][] = []
|
||||||
|
|
||||||
|
const messages: SDKMessage[] = [
|
||||||
|
{
|
||||||
|
...baseStreamMetadata,
|
||||||
|
type: 'assistant',
|
||||||
|
uuid: uuid(20),
|
||||||
|
message: {
|
||||||
|
id: 'msg-tool-no-stream',
|
||||||
|
type: 'message',
|
||||||
|
role: 'assistant',
|
||||||
|
model: 'claude-test',
|
||||||
|
content: [
|
||||||
|
{
|
||||||
|
type: 'tool_use',
|
||||||
|
id: 'tool-read',
|
||||||
|
name: 'Read',
|
||||||
|
input: { file_path: '/test.txt' }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'tool_use',
|
||||||
|
id: 'tool-bash',
|
||||||
|
name: 'Bash',
|
||||||
|
input: { command: 'ls -la' }
|
||||||
|
}
|
||||||
|
],
|
||||||
|
stop_reason: 'tool_use',
|
||||||
|
stop_sequence: null,
|
||||||
|
usage: {
|
||||||
|
input_tokens: 10,
|
||||||
|
output_tokens: 20
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} as unknown as SDKMessage,
|
||||||
|
{
|
||||||
|
...baseStreamMetadata,
|
||||||
|
type: 'user',
|
||||||
|
uuid: uuid(21),
|
||||||
|
message: {
|
||||||
|
role: 'user',
|
||||||
|
content: [
|
||||||
|
{
|
||||||
|
type: 'tool_result',
|
||||||
|
tool_use_id: 'tool-read',
|
||||||
|
content: 'file contents',
|
||||||
|
is_error: false
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
} as SDKMessage,
|
||||||
|
{
|
||||||
|
...baseStreamMetadata,
|
||||||
|
type: 'user',
|
||||||
|
uuid: uuid(22),
|
||||||
|
message: {
|
||||||
|
role: 'user',
|
||||||
|
content: [
|
||||||
|
{
|
||||||
|
type: 'tool_result',
|
||||||
|
tool_use_id: 'tool-bash',
|
||||||
|
content: 'total 42\n...',
|
||||||
|
is_error: false
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
} as SDKMessage
|
||||||
|
]
|
||||||
|
|
||||||
|
for (const message of messages) {
|
||||||
|
const transformed = transformSDKMessageToStreamParts(message, state)
|
||||||
|
parts.push(...transformed)
|
||||||
|
}
|
||||||
|
|
||||||
|
const types = parts.map((part) => part.type)
|
||||||
|
expect(types).toEqual(['tool-call', 'tool-call', 'tool-result', 'tool-result'])
|
||||||
|
|
||||||
|
const toolCalls = parts.filter((part) => part.type === 'tool-call') as Extract<
|
||||||
|
(typeof parts)[number],
|
||||||
|
{ type: 'tool-call' }
|
||||||
|
>[]
|
||||||
|
expect(toolCalls).toHaveLength(2)
|
||||||
|
expect(toolCalls[0].toolName).toBe('Read')
|
||||||
|
expect(toolCalls[0].toolCallId).toBe('12344:tool-read')
|
||||||
|
expect(toolCalls[1].toolName).toBe('Bash')
|
||||||
|
expect(toolCalls[1].toolCallId).toBe('12344:tool-bash')
|
||||||
|
|
||||||
|
const toolResults = parts.filter((part) => part.type === 'tool-result') as Extract<
|
||||||
|
(typeof parts)[number],
|
||||||
|
{ type: 'tool-result' }
|
||||||
|
>[]
|
||||||
|
expect(toolResults).toHaveLength(2)
|
||||||
|
// This is the key assertion - toolName should NOT be 'unknown'
|
||||||
|
expect(toolResults[0].toolName).toBe('Read')
|
||||||
|
expect(toolResults[0].toolCallId).toBe('12344:tool-read')
|
||||||
|
expect(toolResults[0].input).toEqual({ file_path: '/test.txt' })
|
||||||
|
expect(toolResults[0].output).toBe('file contents')
|
||||||
|
|
||||||
|
expect(toolResults[1].toolName).toBe('Bash')
|
||||||
|
expect(toolResults[1].toolCallId).toBe('12344:tool-bash')
|
||||||
|
expect(toolResults[1].input).toEqual({ command: 'ls -la' })
|
||||||
|
expect(toolResults[1].output).toBe('total 42\n...')
|
||||||
|
})
|
||||||
|
|
||||||
it('handles streaming text completion', () => {
|
it('handles streaming text completion', () => {
|
||||||
const state = new ClaudeStreamState({ agentSessionId: baseStreamMetadata.session_id })
|
const state = new ClaudeStreamState({ agentSessionId: baseStreamMetadata.session_id })
|
||||||
const parts: ReturnType<typeof transformSDKMessageToStreamParts>[number][] = []
|
const parts: ReturnType<typeof transformSDKMessageToStreamParts>[number][] = []
|
||||||
@@ -300,4 +410,87 @@ describe('Claude → AiSDK transform', () => {
|
|||||||
expect(finishStep.finishReason).toBe('stop')
|
expect(finishStep.finishReason).toBe('stop')
|
||||||
expect(finishStep.usage).toEqual({ inputTokens: 2, outputTokens: 4, totalTokens: 6 })
|
expect(finishStep.usage).toEqual({ inputTokens: 2, outputTokens: 4, totalTokens: 6 })
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('emits fallback text when Claude sends a snapshot instead of deltas', () => {
|
||||||
|
const state = new ClaudeStreamState({ agentSessionId: '12344' })
|
||||||
|
const parts: ReturnType<typeof transformSDKMessageToStreamParts>[number][] = []
|
||||||
|
|
||||||
|
const messages: SDKMessage[] = [
|
||||||
|
{
|
||||||
|
...baseStreamMetadata,
|
||||||
|
type: 'stream_event',
|
||||||
|
uuid: uuid(30),
|
||||||
|
event: {
|
||||||
|
type: 'message_start',
|
||||||
|
message: {
|
||||||
|
id: 'msg-fallback',
|
||||||
|
type: 'message',
|
||||||
|
role: 'assistant',
|
||||||
|
model: 'claude-test',
|
||||||
|
content: [],
|
||||||
|
stop_reason: null,
|
||||||
|
stop_sequence: null,
|
||||||
|
usage: {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} as unknown as SDKMessage,
|
||||||
|
{
|
||||||
|
...baseStreamMetadata,
|
||||||
|
type: 'stream_event',
|
||||||
|
uuid: uuid(31),
|
||||||
|
event: {
|
||||||
|
type: 'content_block_start',
|
||||||
|
index: 0,
|
||||||
|
content_block: {
|
||||||
|
type: 'text',
|
||||||
|
text: ''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} as unknown as SDKMessage,
|
||||||
|
{
|
||||||
|
...baseStreamMetadata,
|
||||||
|
type: 'assistant',
|
||||||
|
uuid: uuid(32),
|
||||||
|
message: {
|
||||||
|
id: 'msg-fallback-content',
|
||||||
|
type: 'message',
|
||||||
|
role: 'assistant',
|
||||||
|
model: 'claude-test',
|
||||||
|
content: [
|
||||||
|
{
|
||||||
|
type: 'text',
|
||||||
|
text: 'Final answer without streaming deltas.'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
stop_reason: 'end_turn',
|
||||||
|
stop_sequence: null,
|
||||||
|
usage: {
|
||||||
|
input_tokens: 3,
|
||||||
|
output_tokens: 7
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} as unknown as SDKMessage
|
||||||
|
]
|
||||||
|
|
||||||
|
for (const message of messages) {
|
||||||
|
const transformed = transformSDKMessageToStreamParts(message, state)
|
||||||
|
parts.push(...transformed)
|
||||||
|
}
|
||||||
|
|
||||||
|
const types = parts.map((part) => part.type)
|
||||||
|
expect(types).toEqual(['start-step', 'text-start', 'text-delta', 'text-end', 'finish-step'])
|
||||||
|
|
||||||
|
const delta = parts.find((part) => part.type === 'text-delta') as Extract<
|
||||||
|
(typeof parts)[number],
|
||||||
|
{ type: 'text-delta' }
|
||||||
|
>
|
||||||
|
expect(delta.text).toBe('Final answer without streaming deltas.')
|
||||||
|
|
||||||
|
const finish = parts.find((part) => part.type === 'finish-step') as Extract<
|
||||||
|
(typeof parts)[number],
|
||||||
|
{ type: 'finish-step' }
|
||||||
|
>
|
||||||
|
expect(finish.usage).toEqual({ inputTokens: 3, outputTokens: 7, totalTokens: 10 })
|
||||||
|
expect(finish.finishReason).toBe('stop')
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -153,6 +153,20 @@ export class ClaudeStreamState {
|
|||||||
return this.blocksByIndex.get(index)
|
return this.blocksByIndex.get(index)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getFirstOpenTextBlock(): TextBlockState | undefined {
|
||||||
|
const candidates: TextBlockState[] = []
|
||||||
|
for (const block of this.blocksByIndex.values()) {
|
||||||
|
if (block.kind === 'text') {
|
||||||
|
candidates.push(block)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (candidates.length === 0) {
|
||||||
|
return undefined
|
||||||
|
}
|
||||||
|
candidates.sort((a, b) => a.index - b.index)
|
||||||
|
return candidates[0]
|
||||||
|
}
|
||||||
|
|
||||||
getToolBlockById(toolCallId: string): ToolBlockState | undefined {
|
getToolBlockById(toolCallId: string): ToolBlockState | undefined {
|
||||||
const index = this.toolIndexByNamespacedId.get(toolCallId)
|
const index = this.toolIndexByNamespacedId.get(toolCallId)
|
||||||
if (index === undefined) return undefined
|
if (index === undefined) return undefined
|
||||||
@@ -217,10 +231,10 @@ export class ClaudeStreamState {
|
|||||||
* Persists the final input payload for a tool block once the provider signals
|
* Persists the final input payload for a tool block once the provider signals
|
||||||
* completion so that downstream tool results can reference the original call.
|
* completion so that downstream tool results can reference the original call.
|
||||||
*/
|
*/
|
||||||
completeToolBlock(toolCallId: string, input: unknown, providerMetadata?: ProviderMetadata): void {
|
completeToolBlock(toolCallId: string, toolName: string, input: unknown, providerMetadata?: ProviderMetadata): void {
|
||||||
const block = this.getToolBlockByRawId(toolCallId)
|
const block = this.getToolBlockByRawId(toolCallId)
|
||||||
this.registerToolCall(toolCallId, {
|
this.registerToolCall(toolCallId, {
|
||||||
toolName: block?.toolName ?? 'unknown',
|
toolName,
|
||||||
input,
|
input,
|
||||||
providerMetadata
|
providerMetadata
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -2,7 +2,13 @@
|
|||||||
import { EventEmitter } from 'node:events'
|
import { EventEmitter } from 'node:events'
|
||||||
import { createRequire } from 'node:module'
|
import { createRequire } from 'node:module'
|
||||||
|
|
||||||
import type { CanUseTool, McpHttpServerConfig, Options, SDKMessage } from '@anthropic-ai/claude-agent-sdk'
|
import type {
|
||||||
|
AgentDefinition,
|
||||||
|
CanUseTool,
|
||||||
|
McpHttpServerConfig,
|
||||||
|
Options,
|
||||||
|
SDKMessage
|
||||||
|
} from '@anthropic-ai/claude-agent-sdk'
|
||||||
import { query } from '@anthropic-ai/claude-agent-sdk'
|
import { query } from '@anthropic-ai/claude-agent-sdk'
|
||||||
import { loggerService } from '@logger'
|
import { loggerService } from '@logger'
|
||||||
import { config as apiConfigService } from '@main/apiServer/config'
|
import { config as apiConfigService } from '@main/apiServer/config'
|
||||||
@@ -10,7 +16,7 @@ import { validateModelId } from '@main/apiServer/utils'
|
|||||||
import getLoginShellEnvironment from '@main/utils/shell-env'
|
import getLoginShellEnvironment from '@main/utils/shell-env'
|
||||||
import { app } from 'electron'
|
import { app } from 'electron'
|
||||||
|
|
||||||
import type { GetAgentSessionResponse } from '../..'
|
import { agentService, type GetAgentSessionResponse } from '../..'
|
||||||
import type { AgentServiceInterface, AgentStream, AgentStreamEvent } from '../../interfaces/AgentStreamInterface'
|
import type { AgentServiceInterface, AgentStream, AgentStreamEvent } from '../../interfaces/AgentStreamInterface'
|
||||||
import { sessionService } from '../SessionService'
|
import { sessionService } from '../SessionService'
|
||||||
import { buildNamespacedToolCallId } from './claude-stream-state'
|
import { buildNamespacedToolCallId } from './claude-stream-state'
|
||||||
@@ -157,6 +163,32 @@ 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
|
// Build SDK options from parameters
|
||||||
const options: Options = {
|
const options: Options = {
|
||||||
abortController,
|
abortController,
|
||||||
@@ -414,23 +446,6 @@ class ClaudeCodeService implements AgentServiceInterface {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (message.type === 'assistant' || message.type === 'user') {
|
|
||||||
logger.silly('claude response', {
|
|
||||||
message,
|
|
||||||
content: JSON.stringify(message.message.content)
|
|
||||||
})
|
|
||||||
} else if (message.type === 'stream_event') {
|
|
||||||
// logger.silly('Claude stream event', {
|
|
||||||
// message,
|
|
||||||
// event: JSON.stringify(message.event)
|
|
||||||
// })
|
|
||||||
} else {
|
|
||||||
logger.silly('Claude response', {
|
|
||||||
message,
|
|
||||||
event: JSON.stringify(message)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
const chunks = transformSDKMessageToStreamParts(message, streamState)
|
const chunks = transformSDKMessageToStreamParts(message, streamState)
|
||||||
for (const chunk of chunks) {
|
for (const chunk of chunks) {
|
||||||
stream.emit('data', {
|
stream.emit('data', {
|
||||||
|
|||||||
@@ -110,7 +110,7 @@ const sdkMessageToProviderMetadata = (message: SDKMessage): ProviderMetadata =>
|
|||||||
* blocks across calls so that incremental deltas can be correlated correctly.
|
* blocks across calls so that incremental deltas can be correlated correctly.
|
||||||
*/
|
*/
|
||||||
export function transformSDKMessageToStreamParts(sdkMessage: SDKMessage, state: ClaudeStreamState): AgentStreamPart[] {
|
export function transformSDKMessageToStreamParts(sdkMessage: SDKMessage, state: ClaudeStreamState): AgentStreamPart[] {
|
||||||
logger.silly('Transforming SDKMessage', { message: sdkMessage })
|
logger.silly('Transforming SDKMessage', { message: JSON.stringify(sdkMessage) })
|
||||||
switch (sdkMessage.type) {
|
switch (sdkMessage.type) {
|
||||||
case 'assistant':
|
case 'assistant':
|
||||||
return handleAssistantMessage(sdkMessage, state)
|
return handleAssistantMessage(sdkMessage, state)
|
||||||
@@ -186,14 +186,13 @@ function handleAssistantMessage(
|
|||||||
|
|
||||||
for (const block of content) {
|
for (const block of content) {
|
||||||
switch (block.type) {
|
switch (block.type) {
|
||||||
case 'text':
|
case 'text': {
|
||||||
if (!isStreamingActive) {
|
const sanitizedText = stripLocalCommandTags(block.text)
|
||||||
const sanitizedText = stripLocalCommandTags(block.text)
|
if (sanitizedText) {
|
||||||
if (sanitizedText) {
|
textBlocks.push(sanitizedText)
|
||||||
textBlocks.push(sanitizedText)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
|
}
|
||||||
case 'tool_use':
|
case 'tool_use':
|
||||||
handleAssistantToolUse(block as ToolUseContent, providerMetadata, state, chunks)
|
handleAssistantToolUse(block as ToolUseContent, providerMetadata, state, chunks)
|
||||||
break
|
break
|
||||||
@@ -203,7 +202,16 @@ function handleAssistantMessage(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isStreamingActive && textBlocks.length > 0) {
|
if (textBlocks.length === 0) {
|
||||||
|
return chunks
|
||||||
|
}
|
||||||
|
|
||||||
|
const combinedText = textBlocks.join('')
|
||||||
|
if (!combinedText) {
|
||||||
|
return chunks
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isStreamingActive) {
|
||||||
const id = message.uuid?.toString() || generateMessageId()
|
const id = message.uuid?.toString() || generateMessageId()
|
||||||
state.beginStep()
|
state.beginStep()
|
||||||
chunks.push({
|
chunks.push({
|
||||||
@@ -219,7 +227,7 @@ function handleAssistantMessage(
|
|||||||
chunks.push({
|
chunks.push({
|
||||||
type: 'text-delta',
|
type: 'text-delta',
|
||||||
id,
|
id,
|
||||||
text: textBlocks.join(''),
|
text: combinedText,
|
||||||
providerMetadata
|
providerMetadata
|
||||||
})
|
})
|
||||||
chunks.push({
|
chunks.push({
|
||||||
@@ -230,7 +238,27 @@ function handleAssistantMessage(
|
|||||||
return finalizeNonStreamingStep(message, state, chunks)
|
return finalizeNonStreamingStep(message, state, chunks)
|
||||||
}
|
}
|
||||||
|
|
||||||
return chunks
|
const existingTextBlock = state.getFirstOpenTextBlock()
|
||||||
|
const fallbackId = existingTextBlock?.id || message.uuid?.toString() || generateMessageId()
|
||||||
|
if (!existingTextBlock) {
|
||||||
|
chunks.push({
|
||||||
|
type: 'text-start',
|
||||||
|
id: fallbackId,
|
||||||
|
providerMetadata
|
||||||
|
})
|
||||||
|
}
|
||||||
|
chunks.push({
|
||||||
|
type: 'text-delta',
|
||||||
|
id: fallbackId,
|
||||||
|
text: combinedText,
|
||||||
|
providerMetadata
|
||||||
|
})
|
||||||
|
chunks.push({
|
||||||
|
type: 'text-end',
|
||||||
|
id: fallbackId,
|
||||||
|
providerMetadata
|
||||||
|
})
|
||||||
|
return finalizeNonStreamingStep(message, state, chunks)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -252,7 +280,7 @@ function handleAssistantToolUse(
|
|||||||
providerExecuted: true,
|
providerExecuted: true,
|
||||||
providerMetadata
|
providerMetadata
|
||||||
})
|
})
|
||||||
state.completeToolBlock(block.id, block.input, providerMetadata)
|
state.completeToolBlock(block.id, block.name, block.input, providerMetadata)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -459,6 +487,9 @@ function handleStreamEvent(
|
|||||||
}
|
}
|
||||||
|
|
||||||
case 'message_stop': {
|
case 'message_stop': {
|
||||||
|
if (!state.hasActiveStep()) {
|
||||||
|
break
|
||||||
|
}
|
||||||
const pending = state.getPendingUsage()
|
const pending = state.getPendingUsage()
|
||||||
chunks.push({
|
chunks.push({
|
||||||
type: 'finish-step',
|
type: 'finish-step',
|
||||||
|
|||||||
@@ -1,35 +1,120 @@
|
|||||||
|
import 'emoji-picker-element'
|
||||||
|
|
||||||
import TwemojiCountryFlagsWoff2 from '@renderer/assets/fonts/country-flag-fonts/TwemojiCountryFlags.woff2?url'
|
import TwemojiCountryFlagsWoff2 from '@renderer/assets/fonts/country-flag-fonts/TwemojiCountryFlags.woff2?url'
|
||||||
import { useTheme } from '@renderer/context/ThemeProvider'
|
import { useTheme } from '@renderer/context/ThemeProvider'
|
||||||
|
import type { LanguageVarious } from '@renderer/types'
|
||||||
import { polyfillCountryFlagEmojis } from 'country-flag-emoji-polyfill'
|
import { polyfillCountryFlagEmojis } from 'country-flag-emoji-polyfill'
|
||||||
|
// i18n translations from emoji-picker-element
|
||||||
|
import de from 'emoji-picker-element/i18n/de'
|
||||||
|
import en from 'emoji-picker-element/i18n/en'
|
||||||
|
import es from 'emoji-picker-element/i18n/es'
|
||||||
|
import fr from 'emoji-picker-element/i18n/fr'
|
||||||
|
import ja from 'emoji-picker-element/i18n/ja'
|
||||||
|
import pt_PT from 'emoji-picker-element/i18n/pt_PT'
|
||||||
|
import ru_RU from 'emoji-picker-element/i18n/ru_RU'
|
||||||
|
import zh_CN from 'emoji-picker-element/i18n/zh_CN'
|
||||||
|
import type Picker from 'emoji-picker-element/picker'
|
||||||
|
import type { EmojiClickEvent, NativeEmoji } from 'emoji-picker-element/shared'
|
||||||
|
// Emoji data from emoji-picker-element-data (local, no CDN)
|
||||||
|
// Using CLDR format for full multi-language search support (28 languages)
|
||||||
|
import dataDE from 'emoji-picker-element-data/de/cldr/data.json?url'
|
||||||
|
import dataEN from 'emoji-picker-element-data/en/cldr/data.json?url'
|
||||||
|
import dataES from 'emoji-picker-element-data/es/cldr/data.json?url'
|
||||||
|
import dataFR from 'emoji-picker-element-data/fr/cldr/data.json?url'
|
||||||
|
import dataJA from 'emoji-picker-element-data/ja/cldr/data.json?url'
|
||||||
|
import dataPT from 'emoji-picker-element-data/pt/cldr/data.json?url'
|
||||||
|
import dataRU from 'emoji-picker-element-data/ru/cldr/data.json?url'
|
||||||
|
import dataZH from 'emoji-picker-element-data/zh/cldr/data.json?url'
|
||||||
|
import dataZH_HANT from 'emoji-picker-element-data/zh-hant/cldr/data.json?url'
|
||||||
import type { FC } from 'react'
|
import type { FC } from 'react'
|
||||||
import { useEffect, useRef } from 'react'
|
import { useEffect, useRef } from 'react'
|
||||||
|
import { useTranslation } from 'react-i18next'
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
onEmojiClick: (emoji: string) => void
|
onEmojiClick: (emoji: string) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Mapping from app locale to emoji-picker-element i18n
|
||||||
|
const i18nMap: Record<LanguageVarious, typeof en> = {
|
||||||
|
'en-US': en,
|
||||||
|
'zh-CN': zh_CN,
|
||||||
|
'zh-TW': zh_CN, // Closest available
|
||||||
|
'de-DE': de,
|
||||||
|
'el-GR': en, // No Greek available, fallback to English
|
||||||
|
'es-ES': es,
|
||||||
|
'fr-FR': fr,
|
||||||
|
'ja-JP': ja,
|
||||||
|
'pt-PT': pt_PT,
|
||||||
|
'ru-RU': ru_RU
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mapping from app locale to emoji data URL
|
||||||
|
// Using CLDR format provides native language search support for all locales
|
||||||
|
const dataSourceMap: Record<LanguageVarious, string> = {
|
||||||
|
'en-US': dataEN,
|
||||||
|
'zh-CN': dataZH,
|
||||||
|
'zh-TW': dataZH_HANT,
|
||||||
|
'de-DE': dataDE,
|
||||||
|
'el-GR': dataEN, // No Greek CLDR available, fallback to English
|
||||||
|
'es-ES': dataES,
|
||||||
|
'fr-FR': dataFR,
|
||||||
|
'ja-JP': dataJA,
|
||||||
|
'pt-PT': dataPT,
|
||||||
|
'ru-RU': dataRU
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mapping from app locale to emoji-picker-element locale string
|
||||||
|
// Must match the data source locale for proper IndexedDB caching
|
||||||
|
const localeMap: Record<LanguageVarious, string> = {
|
||||||
|
'en-US': 'en',
|
||||||
|
'zh-CN': 'zh',
|
||||||
|
'zh-TW': 'zh-hant',
|
||||||
|
'de-DE': 'de',
|
||||||
|
'el-GR': 'en',
|
||||||
|
'es-ES': 'es',
|
||||||
|
'fr-FR': 'fr',
|
||||||
|
'ja-JP': 'ja',
|
||||||
|
'pt-PT': 'pt',
|
||||||
|
'ru-RU': 'ru'
|
||||||
|
}
|
||||||
|
|
||||||
const EmojiPicker: FC<Props> = ({ onEmojiClick }) => {
|
const EmojiPicker: FC<Props> = ({ onEmojiClick }) => {
|
||||||
const { theme } = useTheme()
|
const { theme } = useTheme()
|
||||||
const ref = useRef<HTMLDivElement>(null)
|
const { i18n } = useTranslation()
|
||||||
|
const ref = useRef<Picker>(null)
|
||||||
|
const currentLocale = i18n.language as LanguageVarious
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
polyfillCountryFlagEmojis('Twemoji Mozilla', TwemojiCountryFlagsWoff2)
|
polyfillCountryFlagEmojis('Twemoji Mozilla', TwemojiCountryFlagsWoff2)
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
|
// Configure picker with i18n and dataSource
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const refValue = ref.current
|
const picker = ref.current
|
||||||
|
if (picker) {
|
||||||
|
picker.i18n = i18nMap[currentLocale] || en
|
||||||
|
picker.dataSource = dataSourceMap[currentLocale] || dataEN
|
||||||
|
picker.locale = localeMap[currentLocale] || 'en'
|
||||||
|
}
|
||||||
|
}, [currentLocale])
|
||||||
|
|
||||||
if (refValue) {
|
useEffect(() => {
|
||||||
const handleEmojiClick = (event: any) => {
|
const picker = ref.current
|
||||||
|
|
||||||
|
if (picker) {
|
||||||
|
const handleEmojiClick = (event: EmojiClickEvent) => {
|
||||||
event.stopPropagation()
|
event.stopPropagation()
|
||||||
onEmojiClick(event.detail.unicode || event.detail.emoji.unicode)
|
const { detail } = event
|
||||||
|
// Use detail.unicode (processed with skin tone) or fallback to emoji's unicode for native emoji
|
||||||
|
const unicode = detail.unicode || ('unicode' in detail.emoji ? (detail.emoji as NativeEmoji).unicode : '')
|
||||||
|
onEmojiClick(unicode)
|
||||||
}
|
}
|
||||||
// 添加事件监听器
|
// 添加事件监听器
|
||||||
refValue.addEventListener('emoji-click', handleEmojiClick)
|
picker.addEventListener('emoji-click', handleEmojiClick)
|
||||||
|
|
||||||
// 清理事件监听器
|
// 清理事件监听器
|
||||||
return () => {
|
return () => {
|
||||||
refValue.removeEventListener('emoji-click', handleEmojiClick)
|
picker.removeEventListener('emoji-click', handleEmojiClick)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -156,6 +156,12 @@
|
|||||||
"uninstalling": "Uninstalling..."
|
"uninstalling": "Uninstalling..."
|
||||||
},
|
},
|
||||||
"prompt": "Prompt Settings",
|
"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": {
|
"tooling": {
|
||||||
"mcp": {
|
"mcp": {
|
||||||
"description": "Connect MCP servers to unlock additional tools you can approve above.",
|
"description": "Connect MCP servers to unlock additional tools you can approve above.",
|
||||||
@@ -641,6 +647,7 @@
|
|||||||
"description": "No files available in accessible directories",
|
"description": "No files available in accessible directories",
|
||||||
"label": "No File Found"
|
"label": "No File Found"
|
||||||
},
|
},
|
||||||
|
"sub_agent": "Sub-Agent",
|
||||||
"title": "Activity Directory"
|
"title": "Activity Directory"
|
||||||
},
|
},
|
||||||
"auto_resize": "Auto resize height",
|
"auto_resize": "Auto resize height",
|
||||||
|
|||||||
@@ -156,6 +156,12 @@
|
|||||||
"uninstalling": "卸载中..."
|
"uninstalling": "卸载中..."
|
||||||
},
|
},
|
||||||
"prompt": "提示词设置",
|
"prompt": "提示词设置",
|
||||||
|
"sub_agents": {
|
||||||
|
"placeholder": "选择子智能体",
|
||||||
|
"tab": "子智能体",
|
||||||
|
"title": "子智能体",
|
||||||
|
"tooltip": "选择可以被此智能体委派任务的其他智能体"
|
||||||
|
},
|
||||||
"tooling": {
|
"tooling": {
|
||||||
"mcp": {
|
"mcp": {
|
||||||
"description": "连接 MCP 服务器即可解锁更多可在上方预先授权的工具。",
|
"description": "连接 MCP 服务器即可解锁更多可在上方预先授权的工具。",
|
||||||
@@ -641,6 +647,7 @@
|
|||||||
"description": "可访问目录中没有可用文件",
|
"description": "可访问目录中没有可用文件",
|
||||||
"label": "未找到文件"
|
"label": "未找到文件"
|
||||||
},
|
},
|
||||||
|
"sub_agent": "子代理",
|
||||||
"title": "活动目录"
|
"title": "活动目录"
|
||||||
},
|
},
|
||||||
"auto_resize": "自动调整高度",
|
"auto_resize": "自动调整高度",
|
||||||
|
|||||||
@@ -156,6 +156,12 @@
|
|||||||
"uninstalling": "解除安裝中..."
|
"uninstalling": "解除安裝中..."
|
||||||
},
|
},
|
||||||
"prompt": "提示設定",
|
"prompt": "提示設定",
|
||||||
|
"sub_agents": {
|
||||||
|
"placeholder": "選擇子助手",
|
||||||
|
"tab": "子助手",
|
||||||
|
"title": "子助手",
|
||||||
|
"tooltip": "選擇可以被此助手委派任務的其他助手"
|
||||||
|
},
|
||||||
"tooling": {
|
"tooling": {
|
||||||
"mcp": {
|
"mcp": {
|
||||||
"description": "連線 MCP 伺服器即可解鎖更多可在上方預先授權的工具。",
|
"description": "連線 MCP 伺服器即可解鎖更多可在上方預先授權的工具。",
|
||||||
@@ -641,6 +647,7 @@
|
|||||||
"description": "可存取的目錄中沒有檔案",
|
"description": "可存取的目錄中沒有檔案",
|
||||||
"label": "找不到檔案"
|
"label": "找不到檔案"
|
||||||
},
|
},
|
||||||
|
"sub_agent": "子代理",
|
||||||
"title": "活動目錄"
|
"title": "活動目錄"
|
||||||
},
|
},
|
||||||
"auto_resize": "自動調整高度",
|
"auto_resize": "自動調整高度",
|
||||||
|
|||||||
@@ -156,6 +156,12 @@
|
|||||||
"uninstalling": "Deinstallation läuft..."
|
"uninstalling": "Deinstallation läuft..."
|
||||||
},
|
},
|
||||||
"prompt": "Prompt-Einstellungen",
|
"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": {
|
"tooling": {
|
||||||
"mcp": {
|
"mcp": {
|
||||||
"description": "Verbinden Sie MCP-Server, um weitere Tools freizuschalten, die oben vorab autorisiert werden können.",
|
"description": "Verbinden Sie MCP-Server, um weitere Tools freizuschalten, die oben vorab autorisiert werden können.",
|
||||||
|
|||||||
@@ -156,6 +156,12 @@
|
|||||||
"uninstalling": "Απεγκατάσταση..."
|
"uninstalling": "Απεγκατάσταση..."
|
||||||
},
|
},
|
||||||
"prompt": "Ρυθμίσεις Προτροπής",
|
"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": {
|
"tooling": {
|
||||||
"mcp": {
|
"mcp": {
|
||||||
"description": "Συνδέστε διακομιστές MCP για να ξεκλειδώσετε πρόσθετα εργαλεία που μπορείτε να εγκρίνετε παραπάνω.",
|
"description": "Συνδέστε διακομιστές MCP για να ξεκλειδώσετε πρόσθετα εργαλεία που μπορείτε να εγκρίνετε παραπάνω.",
|
||||||
|
|||||||
@@ -156,6 +156,12 @@
|
|||||||
"uninstalling": "Desinstalando..."
|
"uninstalling": "Desinstalando..."
|
||||||
},
|
},
|
||||||
"prompt": "Configuración de indicaciones",
|
"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": {
|
"tooling": {
|
||||||
"mcp": {
|
"mcp": {
|
||||||
"description": "Conecta servidores MCP para desbloquear herramientas adicionales que puedes aprobar arriba.",
|
"description": "Conecta servidores MCP para desbloquear herramientas adicionales que puedes aprobar arriba.",
|
||||||
|
|||||||
@@ -156,6 +156,12 @@
|
|||||||
"uninstalling": "Désinstallation en cours..."
|
"uninstalling": "Désinstallation en cours..."
|
||||||
},
|
},
|
||||||
"prompt": "Paramètres de l'invite",
|
"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": {
|
"tooling": {
|
||||||
"mcp": {
|
"mcp": {
|
||||||
"description": "Connectez des serveurs MCP pour débloquer des outils supplémentaires que vous pouvez approuver ci-dessus.",
|
"description": "Connectez des serveurs MCP pour débloquer des outils supplémentaires que vous pouvez approuver ci-dessus.",
|
||||||
|
|||||||
@@ -156,6 +156,12 @@
|
|||||||
"uninstalling": "アンインストール中..."
|
"uninstalling": "アンインストール中..."
|
||||||
},
|
},
|
||||||
"prompt": "プロンプト設定",
|
"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": {
|
"tooling": {
|
||||||
"mcp": {
|
"mcp": {
|
||||||
"description": "MCPサーバーを接続して、上で承認できる追加ツールを解放します。",
|
"description": "MCPサーバーを接続して、上で承認できる追加ツールを解放します。",
|
||||||
|
|||||||
@@ -156,6 +156,12 @@
|
|||||||
"uninstalling": "Desinstalando..."
|
"uninstalling": "Desinstalando..."
|
||||||
},
|
},
|
||||||
"prompt": "Configurações de Prompt",
|
"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": {
|
"tooling": {
|
||||||
"mcp": {
|
"mcp": {
|
||||||
"description": "Conecte servidores MCP para desbloquear ferramentas adicionais que você pode aprovar acima.",
|
"description": "Conecte servidores MCP para desbloquear ferramentas adicionais que você pode aprovar acima.",
|
||||||
|
|||||||
@@ -156,6 +156,12 @@
|
|||||||
"uninstalling": "Удаление..."
|
"uninstalling": "Удаление..."
|
||||||
},
|
},
|
||||||
"prompt": "Настройки подсказки",
|
"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": {
|
"tooling": {
|
||||||
"mcp": {
|
"mcp": {
|
||||||
"description": "Подключите серверы MCP, чтобы разблокировать дополнительные инструменты, которые вы можете одобрить выше.",
|
"description": "Подключите серверы MCP, чтобы разблокировать дополнительные инструменты, которые вы можете одобрить выше.",
|
||||||
|
|||||||
@@ -103,12 +103,23 @@ const AgentSessionInputbar: FC<Props> = ({ agentId, sessionId }) => {
|
|||||||
// Prepare session data for tools
|
// Prepare session data for tools
|
||||||
const sessionData = useMemo(() => {
|
const sessionData = useMemo(() => {
|
||||||
if (!session) return undefined
|
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 {
|
return {
|
||||||
agentId,
|
agentId,
|
||||||
sessionId,
|
sessionId,
|
||||||
slashCommands: session.slash_commands,
|
slashCommands: session.slash_commands,
|
||||||
tools: session.tools,
|
tools: session.tools,
|
||||||
accessiblePaths: session.accessible_paths ?? []
|
accessiblePaths: session.accessible_paths ?? [],
|
||||||
|
subAgents: agentPlugins
|
||||||
}
|
}
|
||||||
}, [session, agentId, sessionId])
|
}, [session, agentId, sessionId])
|
||||||
|
|
||||||
@@ -158,6 +169,8 @@ interface InnerProps {
|
|||||||
sessionId?: string
|
sessionId?: string
|
||||||
slashCommands?: Array<{ command: string; description?: string }>
|
slashCommands?: Array<{ command: string; description?: string }>
|
||||||
tools?: Array<{ id: string; name: string; type: 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<{
|
actionsRef: React.MutableRefObject<{
|
||||||
resizeTextArea: () => void
|
resizeTextArea: () => void
|
||||||
|
|||||||
@@ -25,11 +25,12 @@ const activityDirectoryTool = defineTool({
|
|||||||
const { quickPanel, quickPanelController, actions, session } = context
|
const { quickPanel, quickPanelController, actions, session } = context
|
||||||
const { onTextChange } = actions
|
const { onTextChange } = actions
|
||||||
|
|
||||||
// Get accessible paths from session data
|
// Get accessible paths and sub-agents from session data
|
||||||
const accessiblePaths = session?.accessiblePaths ?? []
|
const accessiblePaths = session?.accessiblePaths ?? []
|
||||||
|
const subAgents = session?.subAgents ?? []
|
||||||
|
|
||||||
// Only render if we have accessible paths
|
// Only render if we have accessible paths or sub-agents
|
||||||
if (accessiblePaths.length === 0) {
|
if (accessiblePaths.length === 0 && subAgents.length === 0) {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -38,6 +39,7 @@ const activityDirectoryTool = defineTool({
|
|||||||
quickPanel={quickPanel}
|
quickPanel={quickPanel}
|
||||||
quickPanelController={quickPanelController}
|
quickPanelController={quickPanelController}
|
||||||
accessiblePaths={accessiblePaths}
|
accessiblePaths={accessiblePaths}
|
||||||
|
subAgents={subAgents}
|
||||||
setText={onTextChange as React.Dispatch<React.SetStateAction<string>>}
|
setText={onTextChange as React.Dispatch<React.SetStateAction<string>>}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -13,10 +13,17 @@ interface Props {
|
|||||||
quickPanel: ToolQuickPanelApi
|
quickPanel: ToolQuickPanelApi
|
||||||
quickPanelController: ToolQuickPanelController
|
quickPanelController: ToolQuickPanelController
|
||||||
accessiblePaths: string[]
|
accessiblePaths: string[]
|
||||||
|
subAgents?: Array<{ id: string; name: string; description?: string }>
|
||||||
setText: React.Dispatch<React.SetStateAction<string>>
|
setText: React.Dispatch<React.SetStateAction<string>>
|
||||||
}
|
}
|
||||||
|
|
||||||
const ActivityDirectoryButton: FC<Props> = ({ quickPanel, quickPanelController, accessiblePaths, setText }) => {
|
const ActivityDirectoryButton: FC<Props> = ({
|
||||||
|
quickPanel,
|
||||||
|
quickPanelController,
|
||||||
|
accessiblePaths,
|
||||||
|
subAgents,
|
||||||
|
setText
|
||||||
|
}) => {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
|
|
||||||
const { handleOpenQuickPanel } = useActivityDirectoryPanel(
|
const { handleOpenQuickPanel } = useActivityDirectoryPanel(
|
||||||
@@ -24,6 +31,7 @@ const ActivityDirectoryButton: FC<Props> = ({ quickPanel, quickPanelController,
|
|||||||
quickPanel,
|
quickPanel,
|
||||||
quickPanelController,
|
quickPanelController,
|
||||||
accessiblePaths,
|
accessiblePaths,
|
||||||
|
subAgents,
|
||||||
setText
|
setText
|
||||||
},
|
},
|
||||||
'button'
|
'button'
|
||||||
|
|||||||
@@ -15,8 +15,9 @@ const ActivityDirectoryQuickPanelManager = ({ context }: ManagerProps) => {
|
|||||||
session
|
session
|
||||||
} = context
|
} = context
|
||||||
|
|
||||||
// Get accessible paths from session data
|
// Get accessible paths and sub-agents from session data
|
||||||
const accessiblePaths = session?.accessiblePaths ?? []
|
const accessiblePaths = session?.accessiblePaths ?? []
|
||||||
|
const subAgents = session?.subAgents ?? []
|
||||||
|
|
||||||
// Always call hooks unconditionally (React rules)
|
// Always call hooks unconditionally (React rules)
|
||||||
useActivityDirectoryPanel(
|
useActivityDirectoryPanel(
|
||||||
@@ -24,6 +25,7 @@ const ActivityDirectoryQuickPanelManager = ({ context }: ManagerProps) => {
|
|||||||
quickPanel,
|
quickPanel,
|
||||||
quickPanelController,
|
quickPanelController,
|
||||||
accessiblePaths,
|
accessiblePaths,
|
||||||
|
subAgents,
|
||||||
setText: onTextChange as React.Dispatch<React.SetStateAction<string>>
|
setText: onTextChange as React.Dispatch<React.SetStateAction<string>>
|
||||||
},
|
},
|
||||||
'manager'
|
'manager'
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { loggerService } from '@logger'
|
|||||||
import type { QuickPanelListItem } from '@renderer/components/QuickPanel'
|
import type { QuickPanelListItem } from '@renderer/components/QuickPanel'
|
||||||
import { QuickPanelReservedSymbol } from '@renderer/components/QuickPanel'
|
import { QuickPanelReservedSymbol } from '@renderer/components/QuickPanel'
|
||||||
import type { ToolQuickPanelApi, ToolQuickPanelController } from '@renderer/pages/home/Inputbar/types'
|
import type { ToolQuickPanelApi, ToolQuickPanelController } from '@renderer/pages/home/Inputbar/types'
|
||||||
import { File, Folder } from 'lucide-react'
|
import { Bot, File, Folder } from 'lucide-react'
|
||||||
import type React from 'react'
|
import type React from 'react'
|
||||||
import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
|
import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
@@ -25,15 +25,22 @@ export type ActivityDirectoryTriggerInfo = {
|
|||||||
symbol?: QuickPanelReservedSymbol
|
symbol?: QuickPanelReservedSymbol
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface SubAgentInfo {
|
||||||
|
id: string
|
||||||
|
name: string
|
||||||
|
description?: string
|
||||||
|
}
|
||||||
|
|
||||||
interface Params {
|
interface Params {
|
||||||
quickPanel: ToolQuickPanelApi
|
quickPanel: ToolQuickPanelApi
|
||||||
quickPanelController: ToolQuickPanelController
|
quickPanelController: ToolQuickPanelController
|
||||||
accessiblePaths: string[]
|
accessiblePaths: string[]
|
||||||
|
subAgents?: SubAgentInfo[]
|
||||||
setText: React.Dispatch<React.SetStateAction<string>>
|
setText: React.Dispatch<React.SetStateAction<string>>
|
||||||
}
|
}
|
||||||
|
|
||||||
export const useActivityDirectoryPanel = (params: Params, role: 'button' | 'manager' = 'button') => {
|
export const useActivityDirectoryPanel = (params: Params, role: 'button' | 'manager' = 'button') => {
|
||||||
const { quickPanel, quickPanelController, accessiblePaths, setText } = params
|
const { quickPanel, quickPanelController, accessiblePaths, subAgents = [], setText } = params
|
||||||
const { registerTrigger, registerRootMenu } = quickPanel
|
const { registerTrigger, registerRootMenu } = quickPanel
|
||||||
const { open, close, updateList, isVisible, symbol } = quickPanelController
|
const { open, close, updateList, isVisible, symbol } = quickPanelController
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
@@ -238,6 +245,68 @@ export const useActivityDirectoryPanel = (params: Params, role: 'button' | 'mana
|
|||||||
[close, insertFilePath]
|
[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
|
* Create file list items for QuickPanel from a file list
|
||||||
*/
|
*/
|
||||||
@@ -291,12 +360,18 @@ export const useActivityDirectoryPanel = (params: Params, role: 'button' | 'mana
|
|||||||
)
|
)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create file list items for QuickPanel (for current state)
|
* Create combined list items for QuickPanel (sub-agents + files)
|
||||||
*/
|
*/
|
||||||
const fileItems = useMemo<QuickPanelListItem[]>(
|
const combinedItems = useMemo<QuickPanelListItem[]>(() => {
|
||||||
() => createFileItems(fileList, isLoading),
|
const agentItems = createSubAgentItems(subAgents)
|
||||||
[createFileItems, fileList, isLoading]
|
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
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handle search text change - load files and update list
|
* Handle search text change - load files and update list
|
||||||
@@ -311,11 +386,13 @@ export const useActivityDirectoryPanel = (params: Params, role: 'button' | 'mana
|
|||||||
|
|
||||||
const hasChanged = updateFileListState(newFiles)
|
const hasChanged = updateFileListState(newFiles)
|
||||||
if (hasChanged) {
|
if (hasChanged) {
|
||||||
const newItems = createFileItems(newFiles, false)
|
// Combine sub-agents and files
|
||||||
updateList(newItems)
|
const agentItems = createSubAgentItems(subAgents)
|
||||||
|
const fileItems = createFileItems(newFiles, false)
|
||||||
|
updateList([...agentItems, ...fileItems])
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[loadFiles, createFileItems, updateList, updateFileListState]
|
[loadFiles, createFileItems, createSubAgentItems, subAgents, updateList, updateFileListState]
|
||||||
)
|
)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -336,8 +413,10 @@ export const useActivityDirectoryPanel = (params: Params, role: 'button' | 'mana
|
|||||||
const files = await loadFiles()
|
const files = await loadFiles()
|
||||||
updateFileListState(files)
|
updateFileListState(files)
|
||||||
|
|
||||||
// Create items from the loaded files immediately
|
// Create items from sub-agents and loaded files immediately
|
||||||
const items = createFileItems(files, false)
|
const agentItems = createSubAgentItems(subAgents)
|
||||||
|
const fileItems = createFileItems(files, false)
|
||||||
|
const items = [...agentItems, ...fileItems]
|
||||||
|
|
||||||
open({
|
open({
|
||||||
title: t('chat.input.activity_directory.description'),
|
title: t('chat.input.activity_directory.description'),
|
||||||
@@ -377,7 +456,18 @@ export const useActivityDirectoryPanel = (params: Params, role: 'button' | 'mana
|
|||||||
onSearchChange: handleSearchChange
|
onSearchChange: handleSearchChange
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
[loadFiles, open, removeTriggerSymbolAndText, setText, t, handleSearchChange, createFileItems, updateFileListState]
|
[
|
||||||
|
loadFiles,
|
||||||
|
open,
|
||||||
|
removeTriggerSymbolAndText,
|
||||||
|
setText,
|
||||||
|
t,
|
||||||
|
handleSearchChange,
|
||||||
|
createFileItems,
|
||||||
|
createSubAgentItems,
|
||||||
|
subAgents,
|
||||||
|
updateFileListState
|
||||||
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -68,6 +68,7 @@ export interface ToolContext {
|
|||||||
slashCommands?: Array<{ command: string; description?: string }>
|
slashCommands?: Array<{ command: string; description?: string }>
|
||||||
tools?: Array<{ id: string; name: string; type: string; description?: string }>
|
tools?: Array<{ id: string; name: string; type: string; description?: string }>
|
||||||
accessiblePaths?: string[]
|
accessiblePaths?: string[]
|
||||||
|
subAgents?: Array<{ id: string; name: string; description?: string }>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import type { CollapseProps } from 'antd'
|
import type { CollapseProps } from 'antd'
|
||||||
import { Tag } from 'antd'
|
import { Tag } from 'antd'
|
||||||
import { CheckCircle, Terminal, XCircle } from 'lucide-react'
|
import { CheckCircle, Terminal, XCircle } from 'lucide-react'
|
||||||
import { useMemo } from 'react'
|
|
||||||
|
|
||||||
import { ToolTitle } from './GenericTools'
|
import { ToolTitle } from './GenericTools'
|
||||||
import type { BashOutputToolInput, BashOutputToolOutput } from './types'
|
import type { BashOutputToolInput, BashOutputToolOutput } from './types'
|
||||||
@@ -16,6 +15,63 @@ interface ParsedBashOutput {
|
|||||||
tool_use_error?: string
|
tool_use_error?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const parseBashOutput = (output?: BashOutputToolOutput): ParsedBashOutput | null => {
|
||||||
|
if (!output) return null
|
||||||
|
|
||||||
|
try {
|
||||||
|
const parser = new DOMParser()
|
||||||
|
const hasToolError = output.includes('<tool_use_error>')
|
||||||
|
const xmlStr = output.includes('<status>') || hasToolError ? `<root>${output}</root>` : output
|
||||||
|
const xmlDoc = parser.parseFromString(xmlStr, 'application/xml')
|
||||||
|
const parserError = xmlDoc.querySelector('parsererror')
|
||||||
|
if (parserError) return null
|
||||||
|
|
||||||
|
const getElementText = (tagName: string): string | undefined => {
|
||||||
|
const element = xmlDoc.getElementsByTagName(tagName)[0]
|
||||||
|
return element?.textContent?.trim()
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
status: getElementText('status'),
|
||||||
|
exit_code: getElementText('exit_code') ? parseInt(getElementText('exit_code')!) : undefined,
|
||||||
|
stdout: getElementText('stdout'),
|
||||||
|
stderr: getElementText('stderr'),
|
||||||
|
timestamp: getElementText('timestamp'),
|
||||||
|
tool_use_error: getElementText('tool_use_error')
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const getStatusConfig = (parsedOutput: ParsedBashOutput | null) => {
|
||||||
|
if (!parsedOutput) return null
|
||||||
|
|
||||||
|
if (parsedOutput.tool_use_error) {
|
||||||
|
return {
|
||||||
|
color: 'danger',
|
||||||
|
icon: <XCircle className="h-3.5 w-3.5" />,
|
||||||
|
text: 'Error'
|
||||||
|
} as const
|
||||||
|
}
|
||||||
|
|
||||||
|
const isCompleted = parsedOutput.status === 'completed'
|
||||||
|
const isSuccess = parsedOutput.exit_code === 0
|
||||||
|
|
||||||
|
return {
|
||||||
|
color: isCompleted && isSuccess ? 'success' : isCompleted && !isSuccess ? 'danger' : 'warning',
|
||||||
|
icon:
|
||||||
|
isCompleted && isSuccess ? (
|
||||||
|
<CheckCircle className="h-3.5 w-3.5" />
|
||||||
|
) : isCompleted && !isSuccess ? (
|
||||||
|
<XCircle className="h-3.5 w-3.5" />
|
||||||
|
) : (
|
||||||
|
<Terminal className="h-3.5 w-3.5" />
|
||||||
|
),
|
||||||
|
text: isCompleted ? (isSuccess ? 'Success' : 'Failed') : 'Running'
|
||||||
|
} as const
|
||||||
|
}
|
||||||
|
|
||||||
export function BashOutputTool({
|
export function BashOutputTool({
|
||||||
input,
|
input,
|
||||||
output
|
output
|
||||||
@@ -23,73 +79,8 @@ export function BashOutputTool({
|
|||||||
input: BashOutputToolInput
|
input: BashOutputToolInput
|
||||||
output?: BashOutputToolOutput
|
output?: BashOutputToolOutput
|
||||||
}): NonNullable<CollapseProps['items']>[number] {
|
}): NonNullable<CollapseProps['items']>[number] {
|
||||||
// 解析 XML 输出
|
const parsedOutput = parseBashOutput(output)
|
||||||
const parsedOutput = useMemo(() => {
|
const statusConfig = getStatusConfig(parsedOutput)
|
||||||
if (!output) return null
|
|
||||||
|
|
||||||
try {
|
|
||||||
const parser = new DOMParser()
|
|
||||||
// 检查是否包含 tool_use_error 标签
|
|
||||||
const hasToolError = output.includes('<tool_use_error>')
|
|
||||||
// 包装成有效的 XML(如果还没有根元素)
|
|
||||||
const xmlStr = output.includes('<status>') || hasToolError ? `<root>${output}</root>` : output
|
|
||||||
const xmlDoc = parser.parseFromString(xmlStr, 'application/xml')
|
|
||||||
|
|
||||||
// 检查是否有解析错误
|
|
||||||
const parserError = xmlDoc.querySelector('parsererror')
|
|
||||||
if (parserError) {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
const getElementText = (tagName: string): string | undefined => {
|
|
||||||
const element = xmlDoc.getElementsByTagName(tagName)[0]
|
|
||||||
return element?.textContent?.trim()
|
|
||||||
}
|
|
||||||
|
|
||||||
const result: ParsedBashOutput = {
|
|
||||||
status: getElementText('status'),
|
|
||||||
exit_code: getElementText('exit_code') ? parseInt(getElementText('exit_code')!) : undefined,
|
|
||||||
stdout: getElementText('stdout'),
|
|
||||||
stderr: getElementText('stderr'),
|
|
||||||
timestamp: getElementText('timestamp'),
|
|
||||||
tool_use_error: getElementText('tool_use_error')
|
|
||||||
}
|
|
||||||
|
|
||||||
return result
|
|
||||||
} catch {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
}, [output])
|
|
||||||
|
|
||||||
// 获取状态配置
|
|
||||||
const statusConfig = useMemo(() => {
|
|
||||||
if (!parsedOutput) return null
|
|
||||||
|
|
||||||
// 如果有 tool_use_error,直接显示错误状态
|
|
||||||
if (parsedOutput.tool_use_error) {
|
|
||||||
return {
|
|
||||||
color: 'danger',
|
|
||||||
icon: <XCircle className="h-3.5 w-3.5" />,
|
|
||||||
text: 'Error'
|
|
||||||
} as const
|
|
||||||
}
|
|
||||||
|
|
||||||
const isCompleted = parsedOutput.status === 'completed'
|
|
||||||
const isSuccess = parsedOutput.exit_code === 0
|
|
||||||
|
|
||||||
return {
|
|
||||||
color: isCompleted && isSuccess ? 'success' : isCompleted && !isSuccess ? 'danger' : 'warning',
|
|
||||||
icon:
|
|
||||||
isCompleted && isSuccess ? (
|
|
||||||
<CheckCircle className="h-3.5 w-3.5" />
|
|
||||||
) : isCompleted && !isSuccess ? (
|
|
||||||
<XCircle className="h-3.5 w-3.5" />
|
|
||||||
) : (
|
|
||||||
<Terminal className="h-3.5 w-3.5" />
|
|
||||||
),
|
|
||||||
text: isCompleted ? (isSuccess ? 'Success' : 'Failed') : 'Running'
|
|
||||||
} as const
|
|
||||||
}, [parsedOutput])
|
|
||||||
|
|
||||||
const children = parsedOutput ? (
|
const children = parsedOutput ? (
|
||||||
<div className="flex flex-col gap-4">
|
<div className="flex flex-col gap-4">
|
||||||
|
|||||||
@@ -1,12 +1,47 @@
|
|||||||
import type { CollapseProps } from 'antd'
|
import type { CollapseProps } from 'antd'
|
||||||
import { FileText } from 'lucide-react'
|
import { FileText } from 'lucide-react'
|
||||||
import { useMemo } from 'react'
|
|
||||||
import ReactMarkdown from 'react-markdown'
|
import ReactMarkdown from 'react-markdown'
|
||||||
|
|
||||||
import { ToolTitle } from './GenericTools'
|
import { ToolTitle } from './GenericTools'
|
||||||
import type { ReadToolInput as ReadToolInputType, ReadToolOutput as ReadToolOutputType, TextOutput } from './types'
|
import type { ReadToolInput as ReadToolInputType, ReadToolOutput as ReadToolOutputType, TextOutput } from './types'
|
||||||
import { AgentToolsType } from './types'
|
import { AgentToolsType } from './types'
|
||||||
|
|
||||||
|
const removeSystemReminderTags = (text: string): string => {
|
||||||
|
return text.replace(/<system-reminder>[\s\S]*?<\/system-reminder>/gi, '')
|
||||||
|
}
|
||||||
|
|
||||||
|
const normalizeOutputString = (output?: ReadToolOutputType): string | null => {
|
||||||
|
if (!output) return null
|
||||||
|
|
||||||
|
const toText = (item: TextOutput) => removeSystemReminderTags(item.text)
|
||||||
|
|
||||||
|
if (Array.isArray(output)) {
|
||||||
|
return output
|
||||||
|
.filter((item): item is TextOutput => item.type === 'text')
|
||||||
|
.map(toText)
|
||||||
|
.join('')
|
||||||
|
}
|
||||||
|
|
||||||
|
return removeSystemReminderTags(output)
|
||||||
|
}
|
||||||
|
|
||||||
|
const getOutputStats = (outputString: string | null) => {
|
||||||
|
if (!outputString) return null
|
||||||
|
|
||||||
|
const bytes = new Blob([outputString]).size
|
||||||
|
const formatSize = (size: number) => {
|
||||||
|
if (size < 1024) return `${size} B`
|
||||||
|
if (size < 1024 * 1024) return `${(size / 1024).toFixed(1)} KB`
|
||||||
|
return `${(size / (1024 * 1024)).toFixed(1)} MB`
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
lineCount: outputString.split('\n').length,
|
||||||
|
fileSize: bytes,
|
||||||
|
formatSize
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export function ReadTool({
|
export function ReadTool({
|
||||||
input,
|
input,
|
||||||
output
|
output
|
||||||
@@ -14,50 +49,8 @@ export function ReadTool({
|
|||||||
input: ReadToolInputType
|
input: ReadToolInputType
|
||||||
output?: ReadToolOutputType
|
output?: ReadToolOutputType
|
||||||
}): NonNullable<CollapseProps['items']>[number] {
|
}): NonNullable<CollapseProps['items']>[number] {
|
||||||
// 移除 system-reminder 标签及其内容的辅助函数
|
const outputString = normalizeOutputString(output)
|
||||||
const removeSystemReminderTags = (text: string): string => {
|
const stats = getOutputStats(outputString)
|
||||||
// 使用正则表达式匹配 <system-reminder> 标签及其内容,包括换行符
|
|
||||||
return text.replace(/<system-reminder>[\s\S]*?<\/system-reminder>/gi, '')
|
|
||||||
}
|
|
||||||
|
|
||||||
// 将 output 统一转换为字符串
|
|
||||||
const outputString = useMemo(() => {
|
|
||||||
if (!output) return null
|
|
||||||
|
|
||||||
let processedOutput: string
|
|
||||||
|
|
||||||
// 如果是 TextOutput[] 类型,提取所有 text 内容
|
|
||||||
if (Array.isArray(output)) {
|
|
||||||
processedOutput = output
|
|
||||||
.filter((item): item is TextOutput => item.type === 'text')
|
|
||||||
.map((item) => removeSystemReminderTags(item.text))
|
|
||||||
.join('')
|
|
||||||
} else {
|
|
||||||
// 如果是字符串,直接使用
|
|
||||||
processedOutput = output
|
|
||||||
}
|
|
||||||
|
|
||||||
// 移除 system-reminder 标签及其内容
|
|
||||||
return removeSystemReminderTags(processedOutput)
|
|
||||||
}, [output])
|
|
||||||
|
|
||||||
// 如果有输出,计算统计信息
|
|
||||||
const stats = useMemo(() => {
|
|
||||||
if (!outputString) return null
|
|
||||||
|
|
||||||
const bytes = new Blob([outputString]).size
|
|
||||||
const formatSize = (bytes: number) => {
|
|
||||||
if (bytes < 1024) return `${bytes} B`
|
|
||||||
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`
|
|
||||||
return `${(bytes / (1024 * 1024)).toFixed(1)} MB`
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
lineCount: outputString.split('\n').length,
|
|
||||||
fileSize: bytes,
|
|
||||||
formatSize
|
|
||||||
}
|
|
||||||
}, [outputString])
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
key: AgentToolsType.Read,
|
key: AgentToolsType.Read,
|
||||||
|
|||||||
@@ -11,11 +11,24 @@ interface UnknownToolProps {
|
|||||||
output?: unknown
|
output?: unknown
|
||||||
}
|
}
|
||||||
|
|
||||||
export function UnknownToolRenderer({
|
const getToolDisplayName = (name: string) => {
|
||||||
toolName = '',
|
if (name.startsWith('mcp__')) {
|
||||||
input,
|
const parts = name.substring(5).split('__')
|
||||||
output
|
if (parts.length >= 2) {
|
||||||
}: UnknownToolProps): NonNullable<CollapseProps['items']>[number] {
|
return `${parts[0]}:${parts.slice(1).join(':')}`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return name
|
||||||
|
}
|
||||||
|
|
||||||
|
const getToolDescription = (toolName: string) => {
|
||||||
|
if (toolName.startsWith('mcp__')) {
|
||||||
|
return 'MCP Server Tool'
|
||||||
|
}
|
||||||
|
return 'Tool'
|
||||||
|
}
|
||||||
|
|
||||||
|
const UnknownToolContent = ({ input, output }: { input?: unknown; output?: unknown }) => {
|
||||||
const { highlightCode } = useCodeStyle()
|
const { highlightCode } = useCodeStyle()
|
||||||
const [inputHtml, setInputHtml] = useState<string>('')
|
const [inputHtml, setInputHtml] = useState<string>('')
|
||||||
const [outputHtml, setOutputHtml] = useState<string>('')
|
const [outputHtml, setOutputHtml] = useState<string>('')
|
||||||
@@ -34,58 +47,49 @@ export function UnknownToolRenderer({
|
|||||||
}
|
}
|
||||||
}, [output, highlightCode])
|
}, [output, highlightCode])
|
||||||
|
|
||||||
const getToolDisplayName = (name: string) => {
|
if (input === undefined && output === undefined) {
|
||||||
if (name.startsWith('mcp__')) {
|
return <div className="text-foreground-500 text-xs">No data available for this tool</div>
|
||||||
const parts = name.substring(5).split('__')
|
|
||||||
if (parts.length >= 2) {
|
|
||||||
return `${parts[0]}:${parts.slice(1).join(':')}`
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return name
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const getToolDescription = () => {
|
return (
|
||||||
if (toolName.startsWith('mcp__')) {
|
<div className="space-y-3">
|
||||||
return 'MCP Server Tool'
|
{input !== undefined && (
|
||||||
}
|
<div>
|
||||||
return 'Tool'
|
<div className="mb-1 font-semibold text-foreground-600 text-xs dark:text-foreground-400">Input:</div>
|
||||||
}
|
<div
|
||||||
|
className="overflow-x-auto rounded bg-gray-50 dark:bg-gray-900"
|
||||||
|
dangerouslySetInnerHTML={{ __html: inputHtml }}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{output !== undefined && (
|
||||||
|
<div>
|
||||||
|
<div className="mb-1 font-semibold text-foreground-600 text-xs dark:text-foreground-400">Output:</div>
|
||||||
|
<div
|
||||||
|
className="rounded bg-gray-50 dark:bg-gray-900 [&>*]:whitespace-pre-line"
|
||||||
|
dangerouslySetInnerHTML={{ __html: outputHtml }}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function UnknownToolRenderer({
|
||||||
|
toolName = '',
|
||||||
|
input,
|
||||||
|
output
|
||||||
|
}: UnknownToolProps): NonNullable<CollapseProps['items']>[number] {
|
||||||
return {
|
return {
|
||||||
key: 'unknown-tool',
|
key: 'unknown-tool',
|
||||||
label: (
|
label: (
|
||||||
<ToolTitle
|
<ToolTitle
|
||||||
icon={<Wrench className="h-4 w-4" />}
|
icon={<Wrench className="h-4 w-4" />}
|
||||||
label={getToolDisplayName(toolName)}
|
label={getToolDisplayName(toolName)}
|
||||||
params={getToolDescription()}
|
params={getToolDescription(toolName)}
|
||||||
/>
|
/>
|
||||||
),
|
),
|
||||||
children: (
|
children: <UnknownToolContent input={input} output={output} />
|
||||||
<div className="space-y-3">
|
|
||||||
{input !== undefined && (
|
|
||||||
<div>
|
|
||||||
<div className="mb-1 font-semibold text-foreground-600 text-xs dark:text-foreground-400">Input:</div>
|
|
||||||
<div
|
|
||||||
className="overflow-x-auto rounded bg-gray-50 dark:bg-gray-900"
|
|
||||||
dangerouslySetInnerHTML={{ __html: inputHtml }}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{output !== undefined && (
|
|
||||||
<div>
|
|
||||||
<div className="mb-1 font-semibold text-foreground-600 text-xs dark:text-foreground-400">Output:</div>
|
|
||||||
<div
|
|
||||||
className="rounded bg-gray-50 dark:bg-gray-900 [&>*]:whitespace-pre-line"
|
|
||||||
dangerouslySetInnerHTML={{ __html: outputHtml }}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{input === undefined && output === undefined && (
|
|
||||||
<div className="text-foreground-500 text-xs">No data available for this tool</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,8 +6,6 @@ import { Collapse } from 'antd'
|
|||||||
// 导出所有类型
|
// 导出所有类型
|
||||||
export * from './types'
|
export * from './types'
|
||||||
|
|
||||||
import { useMemo } from 'react'
|
|
||||||
|
|
||||||
// 导入所有渲染器
|
// 导入所有渲染器
|
||||||
import ToolPermissionRequestCard from '../ToolPermissionRequestCard'
|
import ToolPermissionRequestCard from '../ToolPermissionRequestCard'
|
||||||
import { BashOutputTool } from './BashOutputTool'
|
import { BashOutputTool } from './BashOutputTool'
|
||||||
@@ -57,22 +55,19 @@ export function isValidAgentToolsType(toolName: unknown): toolName is AgentTools
|
|||||||
return typeof toolName === 'string' && Object.values(AgentToolsType).includes(toolName as AgentToolsType)
|
return typeof toolName === 'string' && Object.values(AgentToolsType).includes(toolName as AgentToolsType)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 统一的渲染函数
|
// 统一的渲染组件
|
||||||
function renderToolContent(toolName: AgentToolsType, input: ToolInput, output?: ToolOutput) {
|
function ToolContent({ toolName, input, output }: { toolName: AgentToolsType; input: ToolInput; output?: ToolOutput }) {
|
||||||
const Renderer = toolRenderers[toolName]
|
const Renderer = toolRenderers[toolName]
|
||||||
|
const renderedItem = Renderer
|
||||||
|
? Renderer({ input: input as any, output: output as any })
|
||||||
|
: UnknownToolRenderer({ input: input as any, output: output as any, toolName })
|
||||||
|
|
||||||
// eslint-disable-next-line react-hooks/rules-of-hooks
|
const toolContentItem: NonNullable<CollapseProps['items']>[number] = {
|
||||||
const toolContentItem = useMemo(() => {
|
...renderedItem,
|
||||||
const rendered = Renderer
|
classNames: {
|
||||||
? Renderer({ input: input as any, output: output as any })
|
body: 'bg-foreground-50 p-2 text-foreground-900 dark:bg-foreground-100 max-h-96 p-2 overflow-scroll'
|
||||||
: UnknownToolRenderer({ input: input as any, output: output as any, toolName })
|
}
|
||||||
return {
|
}
|
||||||
...rendered,
|
|
||||||
classNames: {
|
|
||||||
body: 'bg-foreground-50 p-2 text-foreground-900 dark:bg-foreground-100 max-h-96 p-2 overflow-scroll'
|
|
||||||
} as NonNullable<CollapseProps['items']>[number]['classNames']
|
|
||||||
} as NonNullable<CollapseProps['items']>[number]
|
|
||||||
}, [Renderer, input, output, toolName])
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Collapse
|
<Collapse
|
||||||
@@ -98,5 +93,7 @@ export function MessageAgentTools({ toolResponse }: { toolResponse: NormalToolRe
|
|||||||
return <ToolPermissionRequestCard toolResponse={toolResponse} />
|
return <ToolPermissionRequestCard toolResponse={toolResponse} />
|
||||||
}
|
}
|
||||||
|
|
||||||
return renderToolContent(tool.name as AgentToolsType, args as ToolInput, response as ToolOutput)
|
return (
|
||||||
|
<ToolContent toolName={tool.name as AgentToolsType} input={args as ToolInput} output={response as ToolOutput} />
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import EssentialSettings from './EssentialSettings'
|
|||||||
import PluginSettings from './PluginSettings'
|
import PluginSettings from './PluginSettings'
|
||||||
import PromptSettings from './PromptSettings'
|
import PromptSettings from './PromptSettings'
|
||||||
import { AgentLabel, LeftMenu, Settings, StyledMenu, StyledModal } from './shared'
|
import { AgentLabel, LeftMenu, Settings, StyledMenu, StyledModal } from './shared'
|
||||||
|
import SubAgentsSettings from './SubAgentsSettings'
|
||||||
import ToolingSettings from './ToolingSettings'
|
import ToolingSettings from './ToolingSettings'
|
||||||
|
|
||||||
interface AgentSettingPopupShowParams {
|
interface AgentSettingPopupShowParams {
|
||||||
@@ -22,7 +23,7 @@ interface AgentSettingPopupParams extends AgentSettingPopupShowParams {
|
|||||||
resolve: () => void
|
resolve: () => void
|
||||||
}
|
}
|
||||||
|
|
||||||
type AgentSettingPopupTab = 'essential' | 'prompt' | 'tooling' | 'advanced' | 'plugins' | 'session-mcps'
|
type AgentSettingPopupTab = 'essential' | 'prompt' | 'tooling' | 'advanced' | 'plugins' | 'sub-agents' | 'session-mcps'
|
||||||
|
|
||||||
const AgentSettingPopupContainer: React.FC<AgentSettingPopupParams> = ({ tab, agentId, resolve }) => {
|
const AgentSettingPopupContainer: React.FC<AgentSettingPopupParams> = ({ tab, agentId, resolve }) => {
|
||||||
const [open, setOpen] = useState(true)
|
const [open, setOpen] = useState(true)
|
||||||
@@ -62,6 +63,10 @@ const AgentSettingPopupContainer: React.FC<AgentSettingPopupParams> = ({ tab, ag
|
|||||||
key: 'plugins',
|
key: 'plugins',
|
||||||
label: t('agent.settings.plugins.tab', 'Plugins')
|
label: t('agent.settings.plugins.tab', 'Plugins')
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
key: 'sub-agents',
|
||||||
|
label: t('agent.settings.sub_agents.tab', 'Sub-agents')
|
||||||
|
},
|
||||||
{
|
{
|
||||||
key: 'advanced',
|
key: 'advanced',
|
||||||
label: t('agent.settings.advance.title', 'Advanced Settings')
|
label: t('agent.settings.advance.title', 'Advanced Settings')
|
||||||
@@ -107,6 +112,7 @@ const AgentSettingPopupContainer: React.FC<AgentSettingPopupParams> = ({ tab, ag
|
|||||||
{menu === 'prompt' && <PromptSettings agentBase={agent} update={updateAgent} />}
|
{menu === 'prompt' && <PromptSettings agentBase={agent} update={updateAgent} />}
|
||||||
{menu === 'tooling' && <ToolingSettings agentBase={agent} update={updateAgent} />}
|
{menu === 'tooling' && <ToolingSettings agentBase={agent} update={updateAgent} />}
|
||||||
{menu === 'plugins' && <PluginSettings 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} />}
|
{menu === 'advanced' && <AdvancedSettings agentBase={agent} update={updateAgent} />}
|
||||||
</Settings>
|
</Settings>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -0,0 +1,57 @@
|
|||||||
|
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
|
||||||
@@ -1,5 +1,3 @@
|
|||||||
import 'emoji-picker-element'
|
|
||||||
|
|
||||||
import { CheckOutlined, LoadingOutlined, RollbackOutlined, ThunderboltOutlined } from '@ant-design/icons'
|
import { CheckOutlined, LoadingOutlined, RollbackOutlined, ThunderboltOutlined } from '@ant-design/icons'
|
||||||
import { loggerService } from '@logger'
|
import { loggerService } from '@logger'
|
||||||
import EmojiPicker from '@renderer/components/EmojiPicker'
|
import EmojiPicker from '@renderer/components/EmojiPicker'
|
||||||
|
|||||||
@@ -585,9 +585,11 @@ const fetchAndProcessAgentResponseImpl = async (
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Only mark as cleared if there was a previous session ID (not initial assignment)
|
||||||
|
sessionWasCleared = !!latestAgentSessionId
|
||||||
|
|
||||||
latestAgentSessionId = sessionId
|
latestAgentSessionId = sessionId
|
||||||
agentSession.agentSessionId = sessionId
|
agentSession.agentSessionId = sessionId
|
||||||
sessionWasCleared = true
|
|
||||||
|
|
||||||
logger.debug(`Agent session ID updated`, {
|
logger.debug(`Agent session ID updated`, {
|
||||||
topicId,
|
topicId,
|
||||||
|
|||||||
@@ -82,6 +82,7 @@ export const AgentBaseSchema = z.object({
|
|||||||
// Tools
|
// Tools
|
||||||
mcps: z.array(z.string()).optional(), // Array of MCP tool IDs
|
mcps: z.array(z.string()).optional(), // Array of MCP tool IDs
|
||||||
allowed_tools: z.array(z.string()).optional(), // Array of allowed tool IDs (whitelist)
|
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
|
slash_commands: z.array(SlashCommandSchema).optional(), // Array of slash commands merged from builtin and SDK
|
||||||
|
|
||||||
// Configuration
|
// Configuration
|
||||||
@@ -132,7 +133,7 @@ export const AgentSessionEntitySchema = AgentBaseSchema.extend({
|
|||||||
id: z.string(),
|
id: z.string(),
|
||||||
agent_id: z.string(), // Primary agent ID for the session
|
agent_id: z.string(), // Primary agent ID for the session
|
||||||
agent_type: AgentTypeSchema,
|
agent_type: AgentTypeSchema,
|
||||||
// sub_agent_ids?: string[] // Array of sub-agent IDs involved in the session
|
sub_agents: z.array(z.string()).optional(), // Array of sub-agent IDs involved in the session
|
||||||
|
|
||||||
created_at: z.iso.datetime(),
|
created_at: z.iso.datetime(),
|
||||||
updated_at: z.iso.datetime()
|
updated_at: z.iso.datetime()
|
||||||
@@ -205,6 +206,7 @@ export type BaseAgentForm = {
|
|||||||
model: string
|
model: string
|
||||||
accessible_paths: string[]
|
accessible_paths: string[]
|
||||||
allowed_tools: string[]
|
allowed_tools: string[]
|
||||||
|
sub_agents?: string[]
|
||||||
mcps?: string[]
|
mcps?: string[]
|
||||||
configuration?: AgentConfiguration
|
configuration?: AgentConfiguration
|
||||||
}
|
}
|
||||||
@@ -286,6 +288,7 @@ export interface UpdateSessionRequest extends Partial<AgentBase> {}
|
|||||||
|
|
||||||
export const GetAgentSessionResponseSchema = AgentSessionEntitySchema.extend({
|
export const GetAgentSessionResponseSchema = AgentSessionEntitySchema.extend({
|
||||||
tools: z.array(ToolSchema).optional(), // All tools available to the session (including built-in and custom)
|
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
|
messages: z.array(AgentSessionMessageEntitySchema).optional(), // Messages in the session
|
||||||
plugins: z
|
plugins: z
|
||||||
.array(
|
.array(
|
||||||
|
|||||||
153
yarn.lock
153
yarn.lock
@@ -4558,38 +4558,38 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"@libsql/client@npm:0.15.15":
|
"@libsql/client@npm:0.14.0, @libsql/client@npm:^0.14.0":
|
||||||
version: 0.15.15
|
version: 0.14.0
|
||||||
resolution: "@libsql/client@npm:0.15.15"
|
resolution: "@libsql/client@npm:0.14.0"
|
||||||
dependencies:
|
dependencies:
|
||||||
"@libsql/core": "npm:^0.15.14"
|
"@libsql/core": "npm:^0.14.0"
|
||||||
"@libsql/hrana-client": "npm:^0.7.0"
|
"@libsql/hrana-client": "npm:^0.7.0"
|
||||||
js-base64: "npm:^3.7.5"
|
js-base64: "npm:^3.7.5"
|
||||||
libsql: "npm:^0.5.22"
|
libsql: "npm:^0.4.4"
|
||||||
promise-limit: "npm:^2.7.0"
|
promise-limit: "npm:^2.7.0"
|
||||||
checksum: 10c0/1ae67280ebe27903ff142b07e2a256c22ef5ada65185286a72823e8eae8d9d2602e0d72e423d3bd64ae57494791bfffff946aa0fc7c2378b55a227ff63f8df69
|
checksum: 10c0/9c6bab468453df765f647422c772af3578f1e108b663a80b99063f47ed3542db26ae0fcdba2e153d72e6d5089c5caeba947a167a6c065b0191a0832621539335
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"@libsql/core@npm:^0.15.14":
|
"@libsql/core@npm:^0.14.0":
|
||||||
version: 0.15.15
|
version: 0.14.0
|
||||||
resolution: "@libsql/core@npm:0.15.15"
|
resolution: "@libsql/core@npm:0.14.0"
|
||||||
dependencies:
|
dependencies:
|
||||||
js-base64: "npm:^3.7.5"
|
js-base64: "npm:^3.7.5"
|
||||||
checksum: 10c0/0a619689c9504f4239d9745882a128b81e2f6c0547352bbb0d36932261c053bbcbea4435a17f91abe61556bb791f2f1203b36c36b2d4b4f369953d7949bdc40e
|
checksum: 10c0/327bb991cf191d5a9a9fc0cc1a17123f7ca88f222187a3bde845fbad8ceaeaa1f139882080e4b2969da57b83e576c52702572e2838d1743c6bff75f95e6f774a
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"@libsql/darwin-arm64@npm:0.5.22":
|
"@libsql/darwin-arm64@npm:0.4.7":
|
||||||
version: 0.5.22
|
version: 0.4.7
|
||||||
resolution: "@libsql/darwin-arm64@npm:0.5.22"
|
resolution: "@libsql/darwin-arm64@npm:0.4.7"
|
||||||
conditions: os=darwin & cpu=arm64
|
conditions: os=darwin & cpu=arm64
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"@libsql/darwin-x64@npm:0.5.22":
|
"@libsql/darwin-x64@npm:0.4.7":
|
||||||
version: 0.5.22
|
version: 0.4.7
|
||||||
resolution: "@libsql/darwin-x64@npm:0.5.22"
|
resolution: "@libsql/darwin-x64@npm:0.4.7"
|
||||||
conditions: os=darwin & cpu=x64
|
conditions: os=darwin & cpu=x64
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
@@ -4623,52 +4623,38 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"@libsql/linux-arm-gnueabihf@npm:0.5.22":
|
"@libsql/linux-arm64-gnu@npm:0.4.7":
|
||||||
version: 0.5.22
|
version: 0.4.7
|
||||||
resolution: "@libsql/linux-arm-gnueabihf@npm:0.5.22"
|
resolution: "@libsql/linux-arm64-gnu@npm:0.4.7"
|
||||||
conditions: os=linux & cpu=arm
|
|
||||||
languageName: node
|
|
||||||
linkType: hard
|
|
||||||
|
|
||||||
"@libsql/linux-arm-musleabihf@npm:0.5.22":
|
|
||||||
version: 0.5.22
|
|
||||||
resolution: "@libsql/linux-arm-musleabihf@npm:0.5.22"
|
|
||||||
conditions: os=linux & cpu=arm
|
|
||||||
languageName: node
|
|
||||||
linkType: hard
|
|
||||||
|
|
||||||
"@libsql/linux-arm64-gnu@npm:0.5.22":
|
|
||||||
version: 0.5.22
|
|
||||||
resolution: "@libsql/linux-arm64-gnu@npm:0.5.22"
|
|
||||||
conditions: os=linux & cpu=arm64
|
conditions: os=linux & cpu=arm64
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"@libsql/linux-arm64-musl@npm:0.5.22":
|
"@libsql/linux-arm64-musl@npm:0.4.7":
|
||||||
version: 0.5.22
|
version: 0.4.7
|
||||||
resolution: "@libsql/linux-arm64-musl@npm:0.5.22"
|
resolution: "@libsql/linux-arm64-musl@npm:0.4.7"
|
||||||
conditions: os=linux & cpu=arm64
|
conditions: os=linux & cpu=arm64
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"@libsql/linux-x64-gnu@npm:0.5.22":
|
"@libsql/linux-x64-gnu@npm:0.4.7":
|
||||||
version: 0.5.22
|
version: 0.4.7
|
||||||
resolution: "@libsql/linux-x64-gnu@npm:0.5.22"
|
resolution: "@libsql/linux-x64-gnu@npm:0.4.7"
|
||||||
conditions: os=linux & cpu=x64
|
conditions: os=linux & cpu=x64
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"@libsql/linux-x64-musl@npm:0.5.22":
|
"@libsql/linux-x64-musl@npm:0.4.7":
|
||||||
version: 0.5.22
|
version: 0.4.7
|
||||||
resolution: "@libsql/linux-x64-musl@npm:0.5.22"
|
resolution: "@libsql/linux-x64-musl@npm:0.4.7"
|
||||||
conditions: os=linux & cpu=x64
|
conditions: os=linux & cpu=x64
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"@libsql/win32-x64-msvc@npm:0.5.22, @libsql/win32-x64-msvc@npm:^0.5.22":
|
"@libsql/win32-x64-msvc@npm:0.4.7, @libsql/win32-x64-msvc@npm:^0.4.7":
|
||||||
version: 0.5.22
|
version: 0.4.7
|
||||||
resolution: "@libsql/win32-x64-msvc@npm:0.5.22"
|
resolution: "@libsql/win32-x64-msvc@npm:0.4.7"
|
||||||
checksum: 10c0/1bb2730563c603c03a229faa352897685648659d85ba0872dda60cc02abc469fbd55539ffd8b86c81d00230d76292e5a4d2a763fe44c05694612ce6db6e929aa
|
checksum: 10c0/2fcb8715b6f0571dec145eaaf3fd53c7c5aa5bf408fe1be9d84b10adc8a909bb6ee60b45e0d7052b0c1722c30ac212356a3f1adcdf7f57d5a59b48f36ca5bdf5
|
||||||
conditions: os=win32 & cpu=x64
|
conditions: os=win32 & cpu=x64
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
@@ -9967,8 +9953,8 @@ __metadata:
|
|||||||
"@langchain/community": "npm:^1.0.0"
|
"@langchain/community": "npm:^1.0.0"
|
||||||
"@langchain/core": "patch:@langchain/core@npm%3A1.0.2#~/.yarn/patches/@langchain-core-npm-1.0.2-183ef83fe4.patch"
|
"@langchain/core": "patch:@langchain/core@npm%3A1.0.2#~/.yarn/patches/@langchain-core-npm-1.0.2-183ef83fe4.patch"
|
||||||
"@langchain/openai": "patch:@langchain/openai@npm%3A1.0.0#~/.yarn/patches/@langchain-openai-npm-1.0.0-474d0ad9d4.patch"
|
"@langchain/openai": "patch:@langchain/openai@npm%3A1.0.0#~/.yarn/patches/@langchain-openai-npm-1.0.0-474d0ad9d4.patch"
|
||||||
"@libsql/client": "npm:0.15.15"
|
"@libsql/client": "npm:0.14.0"
|
||||||
"@libsql/win32-x64-msvc": "npm:^0.5.22"
|
"@libsql/win32-x64-msvc": "npm:^0.4.7"
|
||||||
"@mistralai/mistralai": "npm:^1.7.5"
|
"@mistralai/mistralai": "npm:^1.7.5"
|
||||||
"@modelcontextprotocol/sdk": "npm:^1.17.5"
|
"@modelcontextprotocol/sdk": "npm:^1.17.5"
|
||||||
"@mozilla/readability": "npm:^0.6.0"
|
"@mozilla/readability": "npm:^0.6.0"
|
||||||
@@ -10088,6 +10074,7 @@ __metadata:
|
|||||||
electron-window-state: "npm:^5.0.3"
|
electron-window-state: "npm:^5.0.3"
|
||||||
emittery: "npm:^1.0.3"
|
emittery: "npm:^1.0.3"
|
||||||
emoji-picker-element: "npm:^1.22.1"
|
emoji-picker-element: "npm:^1.22.1"
|
||||||
|
emoji-picker-element-data: "npm:^1"
|
||||||
epub: "patch:epub@npm%3A1.3.0#~/.yarn/patches/epub-npm-1.3.0-8325494ffe.patch"
|
epub: "patch:epub@npm%3A1.3.0#~/.yarn/patches/epub-npm-1.3.0-8325494ffe.patch"
|
||||||
eslint: "npm:^9.22.0"
|
eslint: "npm:^9.22.0"
|
||||||
eslint-plugin-import-zod: "npm:^1.2.0"
|
eslint-plugin-import-zod: "npm:^1.2.0"
|
||||||
@@ -13669,6 +13656,13 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"emoji-picker-element-data@npm:^1":
|
||||||
|
version: 1.8.0
|
||||||
|
resolution: "emoji-picker-element-data@npm:1.8.0"
|
||||||
|
checksum: 10c0/c8976b636205a0cc90d2690859a1193add71a948dadf743962b47c338a4c3715768404d0ccbc02156608b44abf41f3e1d51756e06f1bbed9d164dd4cb1752103
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"emoji-picker-element@npm:^1.22.1":
|
"emoji-picker-element@npm:^1.22.1":
|
||||||
version: 1.26.3
|
version: 1.26.3
|
||||||
resolution: "emoji-picker-element@npm:1.26.3"
|
resolution: "emoji-picker-element@npm:1.26.3"
|
||||||
@@ -17281,19 +17275,17 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"libsql@npm:^0.5.22":
|
"libsql@npm:0.4.7":
|
||||||
version: 0.5.22
|
version: 0.4.7
|
||||||
resolution: "libsql@npm:0.5.22"
|
resolution: "libsql@npm:0.4.7"
|
||||||
dependencies:
|
dependencies:
|
||||||
"@libsql/darwin-arm64": "npm:0.5.22"
|
"@libsql/darwin-arm64": "npm:0.4.7"
|
||||||
"@libsql/darwin-x64": "npm:0.5.22"
|
"@libsql/darwin-x64": "npm:0.4.7"
|
||||||
"@libsql/linux-arm-gnueabihf": "npm:0.5.22"
|
"@libsql/linux-arm64-gnu": "npm:0.4.7"
|
||||||
"@libsql/linux-arm-musleabihf": "npm:0.5.22"
|
"@libsql/linux-arm64-musl": "npm:0.4.7"
|
||||||
"@libsql/linux-arm64-gnu": "npm:0.5.22"
|
"@libsql/linux-x64-gnu": "npm:0.4.7"
|
||||||
"@libsql/linux-arm64-musl": "npm:0.5.22"
|
"@libsql/linux-x64-musl": "npm:0.4.7"
|
||||||
"@libsql/linux-x64-gnu": "npm:0.5.22"
|
"@libsql/win32-x64-msvc": "npm:0.4.7"
|
||||||
"@libsql/linux-x64-musl": "npm:0.5.22"
|
|
||||||
"@libsql/win32-x64-msvc": "npm:0.5.22"
|
|
||||||
"@neon-rs/load": "npm:^0.0.4"
|
"@neon-rs/load": "npm:^0.0.4"
|
||||||
detect-libc: "npm:2.0.2"
|
detect-libc: "npm:2.0.2"
|
||||||
dependenciesMeta:
|
dependenciesMeta:
|
||||||
@@ -17301,10 +17293,6 @@ __metadata:
|
|||||||
optional: true
|
optional: true
|
||||||
"@libsql/darwin-x64":
|
"@libsql/darwin-x64":
|
||||||
optional: true
|
optional: true
|
||||||
"@libsql/linux-arm-gnueabihf":
|
|
||||||
optional: true
|
|
||||||
"@libsql/linux-arm-musleabihf":
|
|
||||||
optional: true
|
|
||||||
"@libsql/linux-arm64-gnu":
|
"@libsql/linux-arm64-gnu":
|
||||||
optional: true
|
optional: true
|
||||||
"@libsql/linux-arm64-musl":
|
"@libsql/linux-arm64-musl":
|
||||||
@@ -17315,8 +17303,41 @@ __metadata:
|
|||||||
optional: true
|
optional: true
|
||||||
"@libsql/win32-x64-msvc":
|
"@libsql/win32-x64-msvc":
|
||||||
optional: true
|
optional: true
|
||||||
checksum: 10c0/6c34f08fc7408ebee16708ba12e5def9d1b2a4fa166070c956a120133ba9be68ec532e2d0b76bdc7005ef9ef69bf70d2ba7208ed824c4288c2a3d881edd5eaf6
|
checksum: 10c0/351952440e6bad3477e5f1bb1b9d6570d16e403b894f4a13c5c7e183a1307b2fb04a2fa902728cb8594a259e1726c51c61b822d545bbc88319b126ad15468a87
|
||||||
conditions: (os=darwin | os=linux | os=win32) & (cpu=x64 | cpu=arm64 | cpu=wasm32 | cpu=arm)
|
conditions: (os=darwin | os=linux | os=win32) & (cpu=x64 | cpu=arm64 | cpu=wasm32)
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
|
"libsql@patch:libsql@npm%3A0.4.7#~/.yarn/patches/libsql-npm-0.4.7-444e260fb1.patch":
|
||||||
|
version: 0.4.7
|
||||||
|
resolution: "libsql@patch:libsql@npm%3A0.4.7#~/.yarn/patches/libsql-npm-0.4.7-444e260fb1.patch::version=0.4.7&hash=972e11"
|
||||||
|
dependencies:
|
||||||
|
"@libsql/darwin-arm64": "npm:0.4.7"
|
||||||
|
"@libsql/darwin-x64": "npm:0.4.7"
|
||||||
|
"@libsql/linux-arm64-gnu": "npm:0.4.7"
|
||||||
|
"@libsql/linux-arm64-musl": "npm:0.4.7"
|
||||||
|
"@libsql/linux-x64-gnu": "npm:0.4.7"
|
||||||
|
"@libsql/linux-x64-musl": "npm:0.4.7"
|
||||||
|
"@libsql/win32-x64-msvc": "npm:0.4.7"
|
||||||
|
"@neon-rs/load": "npm:^0.0.4"
|
||||||
|
detect-libc: "npm:2.0.2"
|
||||||
|
dependenciesMeta:
|
||||||
|
"@libsql/darwin-arm64":
|
||||||
|
optional: true
|
||||||
|
"@libsql/darwin-x64":
|
||||||
|
optional: true
|
||||||
|
"@libsql/linux-arm64-gnu":
|
||||||
|
optional: true
|
||||||
|
"@libsql/linux-arm64-musl":
|
||||||
|
optional: true
|
||||||
|
"@libsql/linux-x64-gnu":
|
||||||
|
optional: true
|
||||||
|
"@libsql/linux-x64-musl":
|
||||||
|
optional: true
|
||||||
|
"@libsql/win32-x64-msvc":
|
||||||
|
optional: true
|
||||||
|
checksum: 10c0/6098770dc6c31ae0dbfe0821719d184d9bb353ac92553923096f6e3420d3786f240f0b3858f519af0aeada93beb4aa83cb9a9a1a6aa18d625511b484dcb53d07
|
||||||
|
conditions: (os=darwin | os=linux | os=win32) & (cpu=x64 | cpu=arm64 | cpu=wasm32)
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user