♻️ refactor: add more tools in lobe-agent-manangerment(modify、update、delete) (#13842)

* feat: add more tools in lobe-agent-manangerment

* feat: add the ensureAgentLoaded to modify it

* feat: add the update prompt tools
This commit is contained in:
LiJian 2026-04-15 17:57:05 +08:00 committed by GitHub
parent e5be1801a1
commit 15fcce97c9
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
24 changed files with 1214 additions and 45 deletions

View file

@ -40,16 +40,29 @@
"builtins.lobe-agent-management.apiName.callAgent": "Call agent",
"builtins.lobe-agent-management.apiName.createAgent": "Create agent",
"builtins.lobe-agent-management.apiName.deleteAgent": "Delete agent",
"builtins.lobe-agent-management.apiName.duplicateAgent": "Duplicate agent",
"builtins.lobe-agent-management.apiName.getAgentDetail": "Get agent detail",
"builtins.lobe-agent-management.apiName.installPlugin": "Install plugin",
"builtins.lobe-agent-management.apiName.searchAgent": "Search agents",
"builtins.lobe-agent-management.apiName.updateAgent": "Update agent",
"builtins.lobe-agent-management.apiName.updatePrompt": "Update prompt",
"builtins.lobe-agent-management.inspector.callAgent.sync": "Calling:",
"builtins.lobe-agent-management.inspector.callAgent.task": "Assigning task to:",
"builtins.lobe-agent-management.inspector.createAgent.title": "Creating agent:",
"builtins.lobe-agent-management.inspector.duplicateAgent.title": "Duplicating agent:",
"builtins.lobe-agent-management.inspector.getAgentDetail.title": "Getting details:",
"builtins.lobe-agent-management.inspector.installPlugin.title": "Installing plugin:",
"builtins.lobe-agent-management.inspector.searchAgent.all": "Search agents:",
"builtins.lobe-agent-management.inspector.searchAgent.market": "Search market:",
"builtins.lobe-agent-management.inspector.searchAgent.results": "{{count}} results",
"builtins.lobe-agent-management.inspector.searchAgent.user": "Search my agents:",
"builtins.lobe-agent-management.inspector.updateAgent.title": "Updating agent:",
"builtins.lobe-agent-management.inspector.updatePrompt.title": "Updating prompt:",
"builtins.lobe-agent-management.render.duplicateAgent.newId": "New Agent ID",
"builtins.lobe-agent-management.render.duplicateAgent.sourceId": "Source Agent ID",
"builtins.lobe-agent-management.render.installPlugin.failed": "Installation failed",
"builtins.lobe-agent-management.render.installPlugin.plugin": "Plugin",
"builtins.lobe-agent-management.render.installPlugin.success": "Installed successfully",
"builtins.lobe-agent-management.title": "Agent Manager",
"builtins.lobe-cloud-sandbox.apiName.editLocalFile": "Edit file",
"builtins.lobe-cloud-sandbox.apiName.executeCode": "Execute code",

View file

@ -40,16 +40,29 @@
"builtins.lobe-agent-management.apiName.callAgent": "呼叫代理",
"builtins.lobe-agent-management.apiName.createAgent": "创建代理",
"builtins.lobe-agent-management.apiName.deleteAgent": "删除代理",
"builtins.lobe-agent-management.apiName.duplicateAgent": "复制代理",
"builtins.lobe-agent-management.apiName.getAgentDetail": "获取代理详情",
"builtins.lobe-agent-management.apiName.installPlugin": "安装插件",
"builtins.lobe-agent-management.apiName.searchAgent": "搜索代理",
"builtins.lobe-agent-management.apiName.updateAgent": "更新代理",
"builtins.lobe-agent-management.apiName.updatePrompt": "更新提示词",
"builtins.lobe-agent-management.inspector.callAgent.sync": "正在呼叫:",
"builtins.lobe-agent-management.inspector.callAgent.task": "分配任务给:",
"builtins.lobe-agent-management.inspector.createAgent.title": "正在创建代理:",
"builtins.lobe-agent-management.inspector.duplicateAgent.title": "正在复制代理:",
"builtins.lobe-agent-management.inspector.getAgentDetail.title": "正在获取详情:",
"builtins.lobe-agent-management.inspector.installPlugin.title": "正在安装插件:",
"builtins.lobe-agent-management.inspector.searchAgent.all": "搜索代理:",
"builtins.lobe-agent-management.inspector.searchAgent.market": "搜索市场:",
"builtins.lobe-agent-management.inspector.searchAgent.results": "{{count}} 个结果",
"builtins.lobe-agent-management.inspector.searchAgent.user": "搜索我的代理:",
"builtins.lobe-agent-management.inspector.updateAgent.title": "正在更新代理:",
"builtins.lobe-agent-management.inspector.updatePrompt.title": "正在更新提示词:",
"builtins.lobe-agent-management.render.duplicateAgent.newId": "新代理 ID",
"builtins.lobe-agent-management.render.duplicateAgent.sourceId": "源代理 ID",
"builtins.lobe-agent-management.render.installPlugin.failed": "安装失败",
"builtins.lobe-agent-management.render.installPlugin.plugin": "插件",
"builtins.lobe-agent-management.render.installPlugin.success": "安装成功",
"builtins.lobe-agent-management.title": "代理管理器",
"builtins.lobe-cloud-sandbox.apiName.editLocalFile": "编辑文件",
"builtins.lobe-cloud-sandbox.apiName.executeCode": "执行代码",

View file

@ -116,6 +116,9 @@ export class AgentManagerRuntime {
params: UpdateAgentConfigParams,
): Promise<BuiltinToolResult> {
try {
// Ensure agent is loaded in store before reading its config
await this.ensureAgentLoaded(agentId);
const state = getAgentStoreState();
const agentStore = getAgentStoreState();
const resultState: UpdateAgentConfigState = { success: true };
@ -124,13 +127,33 @@ export class AgentManagerRuntime {
// Get current config for merging
const previousConfig = agentSelectors.getAgentConfigById(agentId)(state);
// Guard against LLM double-encoding: if config/meta is a JSON string, parse it.
// Use `as any` to bypass TS narrowing — at runtime LLMs can send strings for
// typed object params.
let rawConfig: any = params.config;
if (typeof rawConfig === 'string') {
try {
rawConfig = JSON.parse(rawConfig);
} catch {
rawConfig = undefined;
}
}
let rawMeta: any = params.meta;
if (typeof rawMeta === 'string') {
try {
rawMeta = JSON.parse(rawMeta);
} catch {
rawMeta = undefined;
}
}
// Build the final config update, merging togglePlugin into config.plugins
let finalConfig = params.config ? { ...params.config } : {};
let finalConfig = rawConfig ? { ...rawConfig } : {};
// Handle togglePlugin - merge into config.plugins
if (params.togglePlugin) {
const { pluginId, enabled } = params.togglePlugin;
const currentPlugins = previousConfig.plugins || [];
const currentPlugins = previousConfig?.plugins || [];
const isCurrentlyEnabled = currentPlugins.includes(pluginId);
const shouldEnable = enabled !== undefined ? enabled : !isCurrentlyEnabled;
@ -152,6 +175,12 @@ export class AgentManagerRuntime {
contentParts.push(`plugin ${pluginId} ${shouldEnable ? 'enabled' : 'disabled'}`);
}
// When systemRole is updated, clear editorData so the UI
// doesn't show stale rich-text content that contradicts the new prompt
if ('systemRole' in finalConfig && !('editorData' in finalConfig)) {
finalConfig = { ...finalConfig, editorData: null };
}
// Handle config update
if (Object.keys(finalConfig).length > 0) {
const configUpdatedFields = Object.keys(finalConfig);
@ -183,19 +212,19 @@ export class AgentManagerRuntime {
}
// Handle meta update
if (params.meta && Object.keys(params.meta).length > 0) {
if (rawMeta && Object.keys(rawMeta).length > 0) {
const previousMeta = agentSelectors.getAgentMetaById(agentId)(state);
const metaUpdatedFields = Object.keys(params.meta);
const metaUpdatedFields = Object.keys(rawMeta);
const metaPreviousValues: Record<string, unknown> = {};
for (const field of metaUpdatedFields) {
metaPreviousValues[field] = (previousMeta as unknown as Record<string, unknown>)[field];
}
await agentStore.optimisticUpdateAgentMeta(agentId, params.meta);
await agentStore.optimisticUpdateAgentMeta(agentId, rawMeta);
resultState.meta = {
newValues: params.meta,
newValues: rawMeta,
previousValues: metaPreviousValues as Record<string, unknown>,
updatedFields: metaUpdatedFields,
};
@ -242,6 +271,100 @@ export class AgentManagerRuntime {
}
}
/**
* Get detailed agent configuration by ID
*/
async getAgentDetail(agentId: string): Promise<BuiltinToolResult> {
try {
const config = await this.agentService.getAgentConfigById(agentId);
if (!config) {
return {
content: `Agent "${agentId}" not found.`,
success: false,
};
}
// The merged config may contain extra fields from the DB agent row
// (e.g., description, tags) that aren't on LobeAgentConfig type
const raw = config as Record<string, any>;
const detail = {
config: {
model: config.model,
openingMessage: config.openingMessage,
openingQuestions: config.openingQuestions,
plugins: config.plugins,
provider: config.provider,
systemRole: config.systemRole,
},
meta: {
avatar: config.avatar,
backgroundColor: config.backgroundColor,
description: raw.description as string | undefined,
tags: raw.tags as string[] | undefined,
title: config.title,
},
};
const parts: string[] = [];
if (detail.meta.title) parts.push(`**${detail.meta.title}**`);
if (detail.meta.description) parts.push(detail.meta.description);
if (detail.config.model)
parts.push(`Model: ${detail.config.provider || ''}/${detail.config.model}`);
if (detail.config.plugins?.length) parts.push(`Plugins: ${detail.config.plugins.join(', ')}`);
if (detail.config.systemRole) {
const truncated =
detail.config.systemRole.length > 200
? detail.config.systemRole.slice(0, 200) + '...'
: detail.config.systemRole;
parts.push(`System Prompt: ${truncated}`);
}
return {
content:
parts.length > 0 ? parts.join('\n') : `Agent "${agentId}" found (no details available).`,
state: {
agentId,
config: detail.config,
meta: detail.meta,
success: true,
},
success: true,
};
} catch (error) {
return this.handleError(error, 'Failed to get agent detail');
}
}
/**
* Duplicate an existing agent
*/
async duplicateAgent(agentId: string, newTitle?: string): Promise<BuiltinToolResult> {
try {
const result = await this.agentService.duplicateAgent(agentId, newTitle);
if (!result) {
return {
content: `Failed to duplicate agent "${agentId}". Agent may not exist.`,
success: false,
};
}
return {
content: `Successfully duplicated agent. New agent ID: ${result.agentId}${newTitle ? ` with title "${newTitle}"` : ''}`,
state: {
newAgentId: result.agentId,
sourceAgentId: agentId,
success: true,
},
success: true,
};
} catch (error) {
return this.handleError(error, 'Failed to duplicate agent');
}
}
// ==================== Search ====================
/**
@ -380,14 +503,16 @@ export class AgentManagerRuntime {
*/
async updatePrompt(agentId: string, params: UpdatePromptParams): Promise<BuiltinToolResult> {
try {
await this.ensureAgentLoaded(agentId);
const state = getAgentStoreState();
const previousConfig = agentSelectors.getAgentConfigById(agentId)(state);
const previousPrompt = previousConfig.systemRole;
const previousPrompt = previousConfig?.systemRole;
if (params.streaming) {
await this.streamUpdatePrompt(agentId, params.prompt);
} else {
await getAgentStoreState().optimisticUpdateAgentConfig(agentId, {
editorData: null,
systemRole: params.prompt,
});
}
@ -427,7 +552,6 @@ export class AgentManagerRuntime {
getAgentStoreState().appendStreamingSystemRole(chunk);
if (i + chunkSize < prompt.length) {
// eslint-disable-next-line no-promise-executor-return
await new Promise((resolve) => setTimeout(resolve, delay));
}
}
@ -545,15 +669,7 @@ export class AgentManagerRuntime {
const builtinTool = builtinTools.find((t) => t.identifier === identifier);
if (builtinTool) {
const agentState = getAgentStoreState();
const currentPlugins =
agentSelectors.getAgentConfigById(agentId)(agentState).plugins || [];
if (!currentPlugins.includes(identifier)) {
await getAgentStoreState().optimisticUpdateAgentConfig(agentId, {
plugins: [...currentPlugins, identifier],
});
}
await this.enablePluginForAgent(agentId, identifier);
return {
content: `Successfully enabled builtin tool: ${builtinTool.meta?.title || identifier}`,
@ -594,6 +710,22 @@ export class AgentManagerRuntime {
// ==================== Private Helper Methods ====================
/**
* Ensure the agent config is loaded into the Zustand store.
* When operating on agents that aren't currently open/active,
* their config won't be in the agentMap. This fetches and dispatches it.
*/
private async ensureAgentLoaded(agentId: string): Promise<void> {
const state = getAgentStoreState();
const existing = state.agentMap[agentId];
if (existing) return;
const config = await this.agentService.getAgentConfigById(agentId);
if (config) {
getAgentStoreState().internal_dispatchAgentMap(agentId, config);
}
}
private async handleKlavisInstall(
agentId: string,
identifier: string,
@ -856,8 +988,9 @@ export class AgentManagerRuntime {
}
private async enablePluginForAgent(agentId: string, pluginId: string): Promise<void> {
await this.ensureAgentLoaded(agentId);
const agentState = getAgentStoreState();
const currentPlugins = agentSelectors.getAgentConfigById(agentId)(agentState).plugins || [];
const currentPlugins = agentSelectors.getAgentConfigById(agentId)(agentState)?.plugins || [];
if (!currentPlugins.includes(pluginId)) {
await getAgentStoreState().optimisticUpdateAgentConfig(agentId, {

View file

@ -6,6 +6,8 @@ import type { IAgentService, IDiscoverService } from '../types';
// Create mock services
const mockAgentService: IAgentService = {
createAgent: vi.fn(),
duplicateAgent: vi.fn(),
getAgentConfigById: vi.fn(),
queryAgents: vi.fn(),
removeAgent: vi.fn(),
};
@ -401,6 +403,79 @@ describe('AgentManagerRuntime', () => {
});
});
describe('getAgentDetail', () => {
it('should get agent detail successfully', async () => {
vi.mocked(mockAgentService.getAgentConfigById).mockResolvedValue({
avatar: '🤖',
chatConfig: {} as any,
description: 'A test agent',
model: 'gpt-4o',
params: {} as any,
plugins: ['web-search'],
provider: 'openai',
systemRole: 'You are helpful',
title: 'Test Agent',
} as any);
const result = await runtime.getAgentDetail('agent-id');
expect(result.success).toBe(true);
expect(result.content).toContain('Test Agent');
expect(result.state).toMatchObject({
agentId: 'agent-id',
success: true,
meta: expect.objectContaining({ title: 'Test Agent' }),
config: expect.objectContaining({ model: 'gpt-4o' }),
});
});
it('should return not found for missing agent', async () => {
vi.mocked(mockAgentService.getAgentConfigById).mockResolvedValue(null);
const result = await runtime.getAgentDetail('missing-id');
expect(result.success).toBe(false);
expect(result.content).toContain('not found');
});
});
describe('duplicateAgent', () => {
it('should duplicate agent successfully', async () => {
vi.mocked(mockAgentService.duplicateAgent).mockResolvedValue({
agentId: 'new-agent-id',
});
const result = await runtime.duplicateAgent('source-id', 'Copy of Agent');
expect(result.success).toBe(true);
expect(result.content).toContain('Successfully duplicated agent');
expect(result.content).toContain('new-agent-id');
expect(result.state).toMatchObject({
newAgentId: 'new-agent-id',
sourceAgentId: 'source-id',
success: true,
});
});
it('should handle null result', async () => {
vi.mocked(mockAgentService.duplicateAgent).mockResolvedValue(null);
const result = await runtime.duplicateAgent('missing-id');
expect(result.success).toBe(false);
expect(result.content).toContain('Failed to duplicate agent');
});
it('should handle error', async () => {
vi.mocked(mockAgentService.duplicateAgent).mockRejectedValue(new Error('DB error'));
const result = await runtime.duplicateAgent('agent-id');
expect(result.success).toBe(false);
expect(result.content).toContain('Failed to duplicate agent');
});
});
describe('installPlugin', () => {
it('should install builtin tool', async () => {
const result = await runtime.installPlugin('agent-id', {

View file

@ -12,6 +12,8 @@ export interface IAgentService {
agentId?: string;
sessionId?: string;
}>;
duplicateAgent: (agentId: string, newTitle?: string) => Promise<{ agentId: string } | null>;
getAgentConfigById: (agentId: string) => Promise<LobeAgentConfig | null>;
queryAgents: (params: { keyword?: string; limit?: number }) => Promise<
Array<{
avatar?: string | null;

View file

@ -0,0 +1,60 @@
'use client';
import type { BuiltinInspectorProps } from '@lobechat/types';
import { Flexbox } from '@lobehub/ui';
import { createStaticStyles, cx } from 'antd-style';
import { memo } from 'react';
import { useTranslation } from 'react-i18next';
import { highlightTextStyles, shinyTextStyles } from '@/styles';
import type { DuplicateAgentParams } from '../../../types';
const styles = createStaticStyles(({ css, cssVar }) => ({
root: css`
overflow: hidden;
display: flex;
gap: 8px;
align-items: center;
`,
title: css`
flex-shrink: 0;
color: ${cssVar.colorTextSecondary};
white-space: nowrap;
`,
}));
export const DuplicateAgentInspector = memo<BuiltinInspectorProps<DuplicateAgentParams>>(
({ args, partialArgs, isArgumentsStreaming }) => {
const { t } = useTranslation('plugin');
const agentId = args?.agentId || partialArgs?.agentId;
const newTitle = args?.newTitle || partialArgs?.newTitle;
if (isArgumentsStreaming && !agentId) {
return (
<div className={cx(styles.root, shinyTextStyles.shinyText)}>
<span>{t('builtins.lobe-agent-management.apiName.duplicateAgent')}</span>
</div>
);
}
return (
<Flexbox
horizontal
align={'center'}
className={cx(styles.root, isArgumentsStreaming && shinyTextStyles.shinyText)}
gap={8}
>
<span className={styles.title}>
{t('builtins.lobe-agent-management.inspector.duplicateAgent.title')}
</span>
<span className={highlightTextStyles.primary}>{newTitle || agentId}</span>
</Flexbox>
);
},
);
DuplicateAgentInspector.displayName = 'DuplicateAgentInspector';
export default DuplicateAgentInspector;

View file

@ -0,0 +1,59 @@
'use client';
import type { BuiltinInspectorProps } from '@lobechat/types';
import { Flexbox } from '@lobehub/ui';
import { createStaticStyles, cx } from 'antd-style';
import { memo } from 'react';
import { useTranslation } from 'react-i18next';
import { highlightTextStyles, shinyTextStyles } from '@/styles';
import type { GetAgentDetailParams } from '../../../types';
const styles = createStaticStyles(({ css, cssVar }) => ({
root: css`
overflow: hidden;
display: flex;
gap: 8px;
align-items: center;
`,
title: css`
flex-shrink: 0;
color: ${cssVar.colorTextSecondary};
white-space: nowrap;
`,
}));
export const GetAgentDetailInspector = memo<BuiltinInspectorProps<GetAgentDetailParams>>(
({ args, partialArgs, isArgumentsStreaming }) => {
const { t } = useTranslation('plugin');
const agentId = args?.agentId || partialArgs?.agentId;
if (isArgumentsStreaming && !agentId) {
return (
<div className={cx(styles.root, shinyTextStyles.shinyText)}>
<span>{t('builtins.lobe-agent-management.apiName.getAgentDetail')}</span>
</div>
);
}
return (
<Flexbox
horizontal
align={'center'}
className={cx(styles.root, isArgumentsStreaming && shinyTextStyles.shinyText)}
gap={8}
>
<span className={styles.title}>
{t('builtins.lobe-agent-management.inspector.getAgentDetail.title')}
</span>
{agentId && <span className={highlightTextStyles.primary}>{agentId}</span>}
</Flexbox>
);
},
);
GetAgentDetailInspector.displayName = 'GetAgentDetailInspector';
export default GetAgentDetailInspector;

View file

@ -0,0 +1,59 @@
'use client';
import type { BuiltinInspectorProps } from '@lobechat/types';
import { Flexbox } from '@lobehub/ui';
import { createStaticStyles, cx } from 'antd-style';
import { memo } from 'react';
import { useTranslation } from 'react-i18next';
import { highlightTextStyles, shinyTextStyles } from '@/styles';
import type { InstallPluginParams } from '../../../types';
const styles = createStaticStyles(({ css, cssVar }) => ({
root: css`
overflow: hidden;
display: flex;
gap: 8px;
align-items: center;
`,
title: css`
flex-shrink: 0;
color: ${cssVar.colorTextSecondary};
white-space: nowrap;
`,
}));
export const InstallPluginInspector = memo<BuiltinInspectorProps<InstallPluginParams>>(
({ args, partialArgs, isArgumentsStreaming }) => {
const { t } = useTranslation('plugin');
const identifier = args?.identifier || partialArgs?.identifier;
if (isArgumentsStreaming && !identifier) {
return (
<div className={cx(styles.root, shinyTextStyles.shinyText)}>
<span>{t('builtins.lobe-agent-management.apiName.installPlugin')}</span>
</div>
);
}
return (
<Flexbox
horizontal
align={'center'}
className={cx(styles.root, isArgumentsStreaming && shinyTextStyles.shinyText)}
gap={8}
>
<span className={styles.title}>
{t('builtins.lobe-agent-management.inspector.installPlugin.title')}
</span>
{identifier && <span className={highlightTextStyles.primary}>{identifier}</span>}
</Flexbox>
);
},
);
InstallPluginInspector.displayName = 'InstallPluginInspector';
export default InstallPluginInspector;

View file

@ -0,0 +1,59 @@
'use client';
import type { BuiltinInspectorProps } from '@lobechat/types';
import { Flexbox } from '@lobehub/ui';
import { createStaticStyles, cx } from 'antd-style';
import { memo } from 'react';
import { useTranslation } from 'react-i18next';
import { highlightTextStyles, shinyTextStyles } from '@/styles';
import type { UpdatePromptParams } from '../../../types';
const styles = createStaticStyles(({ css, cssVar }) => ({
root: css`
overflow: hidden;
display: flex;
gap: 8px;
align-items: center;
`,
title: css`
flex-shrink: 0;
color: ${cssVar.colorTextSecondary};
white-space: nowrap;
`,
}));
export const UpdatePromptInspector = memo<BuiltinInspectorProps<UpdatePromptParams>>(
({ args, partialArgs, isArgumentsStreaming }) => {
const { t } = useTranslation('plugin');
const agentId = args?.agentId || partialArgs?.agentId;
if (isArgumentsStreaming && !agentId) {
return (
<div className={cx(styles.root, shinyTextStyles.shinyText)}>
<span>{t('builtins.lobe-agent-management.apiName.updatePrompt')}</span>
</div>
);
}
return (
<Flexbox
horizontal
align={'center'}
className={cx(styles.root, isArgumentsStreaming && shinyTextStyles.shinyText)}
gap={8}
>
<span className={styles.title}>
{t('builtins.lobe-agent-management.inspector.updatePrompt.title')}
</span>
{agentId && <span className={highlightTextStyles.primary}>{agentId}</span>}
</Flexbox>
);
},
);
UpdatePromptInspector.displayName = 'UpdatePromptInspector';
export default UpdatePromptInspector;

View file

@ -3,8 +3,12 @@ import { type BuiltinInspector } from '@lobechat/types';
import { AgentManagementApiName } from '../../types';
import { CallAgentInspector } from './CallAgent';
import { CreateAgentInspector } from './CreateAgent';
import { DuplicateAgentInspector } from './DuplicateAgent';
import { GetAgentDetailInspector } from './GetAgentDetail';
import { InstallPluginInspector } from './InstallPlugin';
import { SearchAgentInspector } from './SearchAgent';
import { UpdateAgentInspector } from './UpdateAgent';
import { UpdatePromptInspector } from './UpdatePrompt';
/**
* Agent Management Inspector Components Registry
@ -15,6 +19,10 @@ import { UpdateAgentInspector } from './UpdateAgent';
export const AgentManagementInspectors: Record<string, BuiltinInspector> = {
[AgentManagementApiName.callAgent]: CallAgentInspector as BuiltinInspector,
[AgentManagementApiName.createAgent]: CreateAgentInspector as BuiltinInspector,
[AgentManagementApiName.duplicateAgent]: DuplicateAgentInspector as BuiltinInspector,
[AgentManagementApiName.getAgentDetail]: GetAgentDetailInspector as BuiltinInspector,
[AgentManagementApiName.installPlugin]: InstallPluginInspector as BuiltinInspector,
[AgentManagementApiName.searchAgent]: SearchAgentInspector as BuiltinInspector,
[AgentManagementApiName.updateAgent]: UpdateAgentInspector as BuiltinInspector,
[AgentManagementApiName.updatePrompt]: UpdatePromptInspector as BuiltinInspector,
};

View file

@ -0,0 +1,56 @@
'use client';
import type { BuiltinRenderProps } from '@lobechat/types';
import { Flexbox } from '@lobehub/ui';
import { createStaticStyles } from 'antd-style';
import { memo } from 'react';
import { useTranslation } from 'react-i18next';
import type { DuplicateAgentParams, DuplicateAgentState } from '../../../types';
const styles = createStaticStyles(({ css, cssVar }) => ({
container: css`
padding: 12px;
border-radius: 8px;
background: ${cssVar.colorFillQuaternary};
`,
label: css`
font-size: 12px;
font-weight: 500;
color: ${cssVar.colorTextSecondary};
`,
value: css`
font-size: 13px;
`,
}));
export const DuplicateAgentRender = memo<
BuiltinRenderProps<DuplicateAgentParams, DuplicateAgentState>
>(({ pluginState }) => {
const { t } = useTranslation('plugin');
if (!pluginState?.success) return null;
return (
<div className={styles.container}>
<Flexbox gap={8}>
<Flexbox gap={2}>
<span className={styles.label}>
{t('builtins.lobe-agent-management.render.duplicateAgent.sourceId')}
</span>
<span className={styles.value}>{pluginState.sourceAgentId}</span>
</Flexbox>
<Flexbox gap={2}>
<span className={styles.label}>
{t('builtins.lobe-agent-management.render.duplicateAgent.newId')}
</span>
<span className={styles.value}>{pluginState.newAgentId}</span>
</Flexbox>
</Flexbox>
</div>
);
});
DuplicateAgentRender.displayName = 'DuplicateAgentRender';
export default DuplicateAgentRender;

View file

@ -0,0 +1,119 @@
'use client';
import { DEFAULT_AVATAR } from '@lobechat/const';
import type { BuiltinRenderProps } from '@lobechat/types';
import { Avatar, Block, Flexbox, Markdown, Tag } from '@lobehub/ui';
import { createStaticStyles, useTheme } from 'antd-style';
import { memo } from 'react';
import type { GetAgentDetailParams, GetAgentDetailState } from '../../../types';
const styles = createStaticStyles(({ css, cssVar }) => ({
container: css`
padding: 12px;
border-radius: 8px;
background: ${cssVar.colorFillQuaternary};
`,
field: css`
margin-block-end: 8px;
&:last-child {
margin-block-end: 0;
}
`,
header: css`
display: flex;
gap: 12px;
align-items: center;
margin-block-end: 12px;
padding-block-end: 12px;
border-block-end: 1px solid ${cssVar.colorBorderSecondary};
`,
label: css`
margin-block-end: 4px;
font-size: 12px;
font-weight: 500;
color: ${cssVar.colorTextSecondary};
`,
title: css`
font-size: 14px;
font-weight: 600;
`,
value: css`
font-size: 13px;
`,
}));
export const GetAgentDetailRender = memo<
BuiltinRenderProps<GetAgentDetailParams, GetAgentDetailState>
>(({ pluginState }) => {
const theme = useTheme();
const meta = pluginState?.meta;
const config = pluginState?.config;
if (!meta && !config) return null;
return (
<div className={styles.container}>
{meta && (
<div className={styles.header}>
<Avatar
avatar={meta.avatar || DEFAULT_AVATAR}
background={meta.backgroundColor || theme.colorBgContainer}
shape={'square'}
size={36}
title={meta.title || undefined}
/>
<Flexbox gap={2}>
<span className={styles.title}>{meta.title || 'Untitled'}</span>
{meta.description && <span className={styles.value}>{meta.description}</span>}
</Flexbox>
</div>
)}
{(config?.model || config?.provider) && (
<div className={styles.field}>
<div className={styles.label}>Model</div>
<div className={styles.value}>
{config.provider && `${config.provider}/`}
{config.model}
</div>
</div>
)}
{config?.plugins && config.plugins.length > 0 && (
<div className={styles.field}>
<div className={styles.label}>Plugins</div>
<Flexbox horizontal gap={4} wrap={'wrap'}>
{config.plugins.map((plugin) => (
<Tag key={plugin}>{plugin}</Tag>
))}
</Flexbox>
</div>
)}
{meta?.tags && meta.tags.length > 0 && (
<div className={styles.field}>
<div className={styles.label}>Tags</div>
<Flexbox horizontal gap={4} wrap={'wrap'}>
{meta.tags.map((tag) => (
<Tag key={tag}>{tag}</Tag>
))}
</Flexbox>
</div>
)}
{config?.systemRole && (
<div className={styles.field}>
<div className={styles.label}>System Prompt</div>
<Block paddingBlock={8} paddingInline={12} variant={'outlined'} width="100%">
<Markdown fontSize={13} variant={'chat'}>
{config.systemRole}
</Markdown>
</Block>
</div>
)}
</div>
);
});
GetAgentDetailRender.displayName = 'GetAgentDetailRender';
export default GetAgentDetailRender;

View file

@ -0,0 +1,65 @@
'use client';
import type { BuiltinRenderProps } from '@lobechat/types';
import { Flexbox, Tag } from '@lobehub/ui';
import { createStaticStyles } from 'antd-style';
import { memo } from 'react';
import { useTranslation } from 'react-i18next';
import type { InstallPluginParams, InstallPluginState } from '../../../types';
const styles = createStaticStyles(({ css, cssVar }) => ({
container: css`
padding: 12px;
border-radius: 8px;
background: ${cssVar.colorFillQuaternary};
`,
label: css`
font-size: 12px;
font-weight: 500;
color: ${cssVar.colorTextSecondary};
`,
statusFail: css`
font-size: 13px;
font-weight: 500;
color: ${cssVar.colorError};
`,
statusSuccess: css`
font-size: 13px;
font-weight: 500;
color: ${cssVar.colorSuccess};
`,
value: css`
font-size: 13px;
`,
}));
export const InstallPluginRender = memo<
BuiltinRenderProps<InstallPluginParams, InstallPluginState>
>(({ pluginState }) => {
const { t } = useTranslation('plugin');
if (!pluginState) return null;
return (
<div className={styles.container}>
<Flexbox gap={8}>
<Flexbox horizontal align={'center'} gap={8}>
<span className={styles.label}>
{t('builtins.lobe-agent-management.render.installPlugin.plugin')}
</span>
<Tag>{pluginState.pluginName || pluginState.pluginId}</Tag>
</Flexbox>
<span className={pluginState.installed ? styles.statusSuccess : styles.statusFail}>
{pluginState.installed
? t('builtins.lobe-agent-management.render.installPlugin.success')
: t('builtins.lobe-agent-management.render.installPlugin.failed')}
</span>
</Flexbox>
</div>
);
});
InstallPluginRender.displayName = 'InstallPluginRender';
export default InstallPluginRender;

View file

@ -31,8 +31,23 @@ const styles = createStaticStyles(({ css, cssVar }) => ({
`,
}));
const safeParse = (val: unknown): Record<string, any> | undefined => {
if (!val) return undefined;
if (typeof val === 'object') return val as Record<string, any>;
if (typeof val === 'string') {
try {
const parsed = JSON.parse(val);
return typeof parsed === 'object' ? parsed : undefined;
} catch {
return undefined;
}
}
return undefined;
};
export const UpdateAgentRender = memo<BuiltinRenderProps<UpdateAgentParams>>(({ args }) => {
const { config, meta } = args || {};
const config = safeParse(args?.config);
const meta = safeParse(args?.meta);
const hasConfig = config && Object.keys(config).length > 0;
const hasMeta = meta && Object.keys(meta).length > 0;

View file

@ -0,0 +1,43 @@
'use client';
import type { BuiltinRenderProps } from '@lobechat/types';
import { Block, Markdown } from '@lobehub/ui';
import { createStaticStyles } from 'antd-style';
import { memo } from 'react';
import type { UpdatePromptParams } from '../../../types';
const styles = createStaticStyles(({ css, cssVar }) => ({
container: css`
padding: 12px;
border-radius: 8px;
background: ${cssVar.colorFillQuaternary};
`,
label: css`
margin-block-end: 4px;
font-size: 12px;
font-weight: 500;
color: ${cssVar.colorTextSecondary};
`,
}));
export const UpdatePromptRender = memo<BuiltinRenderProps<UpdatePromptParams>>(({ args }) => {
const prompt = args?.prompt;
if (!prompt) return null;
return (
<div className={styles.container}>
<div className={styles.label}>System Prompt</div>
<Block paddingBlock={8} paddingInline={12} variant={'outlined'} width="100%">
<Markdown fontSize={13} variant={'chat'}>
{prompt}
</Markdown>
</Block>
</div>
);
});
UpdatePromptRender.displayName = 'UpdatePromptRender';
export default UpdatePromptRender;

View file

@ -1,8 +1,12 @@
import { AgentManagementApiName } from '../../types';
import CallAgentRender from './CallAgent';
import CreateAgentRender from './CreateAgent';
import DuplicateAgentRender from './DuplicateAgent';
import GetAgentDetailRender from './GetAgentDetail';
import InstallPluginRender from './InstallPlugin';
import SearchAgentRender from './SearchAgent';
import UpdateAgentRender from './UpdateAgent';
import UpdatePromptRender from './UpdatePrompt';
/**
* Agent Management Tool Render Components Registry
@ -10,11 +14,19 @@ import UpdateAgentRender from './UpdateAgent';
export const AgentManagementRenders = {
[AgentManagementApiName.callAgent]: CallAgentRender,
[AgentManagementApiName.createAgent]: CreateAgentRender,
[AgentManagementApiName.duplicateAgent]: DuplicateAgentRender,
[AgentManagementApiName.getAgentDetail]: GetAgentDetailRender,
[AgentManagementApiName.installPlugin]: InstallPluginRender,
[AgentManagementApiName.searchAgent]: SearchAgentRender,
[AgentManagementApiName.updateAgent]: UpdateAgentRender,
[AgentManagementApiName.updatePrompt]: UpdatePromptRender,
};
export { default as CallAgentRender } from './CallAgent';
export { default as CreateAgentRender } from './CreateAgent';
export { default as DuplicateAgentRender } from './DuplicateAgent';
export { default as GetAgentDetailRender } from './GetAgentDetail';
export { default as InstallPluginRender } from './InstallPlugin';
export { default as SearchAgentRender } from './SearchAgent';
export { default as UpdateAgentRender } from './UpdateAgent';
export { default as UpdatePromptRender } from './UpdatePrompt';

View file

@ -27,8 +27,12 @@ import {
type CallAgentState,
type CreateAgentParams,
type DeleteAgentParams,
type DuplicateAgentParams,
type GetAgentDetailParams,
type InstallPluginParams,
type SearchAgentParams,
type UpdateAgentParams,
type UpdatePromptParams,
} from './types';
const runtime = new AgentManagerRuntime({
@ -47,7 +51,24 @@ class AgentManagementExecutor extends BaseExecutor<typeof AgentManagementApiName
};
updateAgent = async (params: UpdateAgentParams): Promise<BuiltinToolResult> => {
const { agentId, config, meta } = params;
const { agentId } = params;
// LLMs sometimes double-encode JSON, sending config/meta as stringified JSON
// instead of objects. Parse them defensively before passing to runtime.
let { config, meta } = params;
if (typeof config === 'string') {
try {
config = JSON.parse(config);
} catch {
/* ignore */
}
}
if (typeof meta === 'string') {
try {
meta = JSON.parse(meta);
} catch {
/* ignore */
}
}
return runtime.updateAgentConfig(agentId, { config, meta });
};
@ -55,6 +76,25 @@ class AgentManagementExecutor extends BaseExecutor<typeof AgentManagementApiName
return runtime.deleteAgent(params.agentId);
};
getAgentDetail = async (params: GetAgentDetailParams): Promise<BuiltinToolResult> => {
return runtime.getAgentDetail(params.agentId);
};
duplicateAgent = async (params: DuplicateAgentParams): Promise<BuiltinToolResult> => {
return runtime.duplicateAgent(params.agentId, params.newTitle);
};
updatePrompt = async (params: UpdatePromptParams): Promise<BuiltinToolResult> => {
return runtime.updatePrompt(params.agentId, { prompt: params.prompt });
};
installPlugin = async (params: InstallPluginParams): Promise<BuiltinToolResult> => {
return runtime.installPlugin(params.agentId, {
identifier: params.identifier,
source: params.source,
});
};
// ==================== Search ====================
searchAgent = async (params: SearchAgentParams): Promise<BuiltinToolResult> => {

View file

@ -157,6 +157,88 @@ export const AgentManagementManifest: BuiltinToolManifest = {
},
},
{
description:
'Get the detailed configuration and metadata of an agent, including its system prompt, model, provider, plugins, and other settings.',
name: AgentManagementApiName.getAgentDetail,
parameters: {
properties: {
agentId: {
description: 'The ID of the agent to get details for',
type: 'string',
},
},
required: ['agentId'],
type: 'object',
},
},
{
description:
'Duplicate an existing agent to create a copy with the same configuration. Optionally provide a new title for the duplicated agent.',
name: AgentManagementApiName.duplicateAgent,
parameters: {
properties: {
agentId: {
description: 'The ID of the agent to duplicate',
type: 'string',
},
newTitle: {
description:
'Optional new title for the duplicated agent. If not provided, the original title with a "Copy" suffix will be used.',
type: 'string',
},
},
required: ['agentId'],
type: 'object',
},
},
{
description:
"Install a plugin/tool for an agent. Use 'official' source for builtin tools, Klavis integrations, and LobehubSkill providers. Use 'market' source for MCP marketplace plugins.",
name: AgentManagementApiName.installPlugin,
parameters: {
properties: {
agentId: {
description: 'The ID of the agent to install the plugin for',
type: 'string',
},
identifier: {
description: 'The plugin identifier to install',
type: 'string',
},
source: {
description:
"Plugin source: 'official' (builtin tools, Klavis, LobehubSkill) or 'market' (MCP marketplace)",
enum: ['official', 'market'],
type: 'string',
},
},
required: ['agentId', 'identifier', 'source'],
type: 'object',
},
},
// ==================== Prompt ====================
{
description:
"Update an agent's system prompt. Use this instead of updateAgent when you only need to change the system prompt — it's simpler, avoids nested config objects, and clears stale editor data automatically.",
name: AgentManagementApiName.updatePrompt,
parameters: {
properties: {
agentId: {
description: 'The ID of the agent to update the prompt for',
type: 'string',
},
prompt: {
description: 'The new system prompt content',
type: 'string',
},
},
required: ['agentId', 'prompt'],
type: 'object',
},
},
// ==================== Search ====================
{
description:

View file

@ -13,10 +13,18 @@ export const systemPrompt = `You have Agent Management tools to create, configur
- **createAgent**: Create a new agent with custom configuration (title, description, systemRole, model, provider, plugins, avatar, etc.)
- **updateAgent**: Modify an existing agent's settings
- **deleteAgent**: Remove an agent from the workspace
- **getAgentDetail**: Retrieve the full configuration and metadata of an agent
- **duplicateAgent**: Create a copy of an existing agent
**Discovery:**
- **searchAgent**: Find agents in user's workspace or marketplace
**Prompt:**
- **updatePrompt**: Update an agent's system prompt directly (preferred over updateAgent when only changing the prompt)
**Plugin Management:**
- **installPlugin**: Install a plugin/tool for an agent (builtin, Klavis, LobehubSkill, or MCP marketplace)
**Execution:**
- **callAgent**: Invoke an agent to handle a task (synchronously or as async background task)
</core_capabilities>
@ -25,6 +33,7 @@ export const systemPrompt = `You have Agent Management tools to create, configur
## Available Resources
When this tool is enabled, you will receive contextual information about:
- **Current Agent**: Your own agent ID (in the \`<current_agent>\` tag). Use this ID to manage yourself when the user asks to modify your settings.
- **Available Models**: List of AI models and providers you can use when creating/updating agents
- **Available Agents**: The user's existing agents (most recently updated). You can call them directly via callAgent without first running searchAgent when one of them clearly matches the user's request.
- **Available Plugins**: List of plugins (builtin tools, Klavis integrations, LobehubSkill providers) you can enable for agents
@ -32,6 +41,25 @@ When this tool is enabled, you will receive contextual information about:
This information is automatically injected into the conversation context. Use the exact IDs from the context when specifying model/provider/plugins/agentId parameters. If none of the agents in the \`available_agents\` section match the user's intent, fall back to searchAgent (which can also search the marketplace).
</context_injection>
<self_management>
## Self-Management
You can manage yourself using the same Agent Management tools. Your own agent ID is provided in the \`<current_agent>\` tag in the injected context.
**When the user asks to modify YOUR settings** (e.g., "change your model", "add search plugin to you", "update your system prompt"), use your own agent ID with:
- **getAgentDetail**: Check your current configuration
- **updatePrompt**: Update your system prompt (preferred for prompt-only changes)
- **updateAgent**: Change your model, provider, or other config/meta fields
- **installPlugin**: Add new plugins/tools to yourself
- **duplicateAgent**: Create a copy of yourself
**Tool selection for prompt changes**: When only the system prompt needs updating, always use \`updatePrompt\` instead of \`updateAgent\`. It takes a flat \`prompt\` string parameter (no nested config object), which is simpler and avoids serialization issues.
**Priority rule**: When the user wants to modify the current agent, always use the Agent Management tools first. Only fall back to other tools (e.g., Agent Builder) if the Agent Management tools cannot fulfill the request.
**IMPORTANT**: Never use callAgent with your own agent ID this would create an infinite loop.
</self_management>
<agent_creation_guide>
## Creating Effective Agents
@ -112,6 +140,59 @@ Refer to the injected context for available plugin IDs and descriptions.
- **openingQuestions**: Array of suggested questions to help users start (e.g., ["What can you help me with?"])
</agent_creation_guide>
<agent_detail_guide>
## Getting Agent Details
Use getAgentDetail to inspect an agent's full configuration before making decisions:
**When to use:**
- Before calling an agent, to understand its capabilities
- Before updating an agent, to see current settings
- To check what model, plugins, or system prompt an agent uses
\`\`\`
getAgentDetail(agentId)
\`\`\`
Returns the agent's complete configuration including system prompt, model, provider, plugins, and metadata.
</agent_detail_guide>
<duplicate_guide>
## Duplicating Agents
Use duplicateAgent to create a copy of an existing agent:
**When to use:**
- Creating a variant of an existing agent with slight modifications
- Backing up an agent before making major changes
- Using an existing agent as a template
\`\`\`
duplicateAgent(agentId, newTitle?)
\`\`\`
The duplicated agent inherits all configuration from the original. After duplication, use updateAgent to customize the copy.
</duplicate_guide>
<install_plugin_guide>
## Installing Plugins
Use installPlugin to add tools/plugins to an agent:
**Plugin Sources:**
- **official**: Builtin tools (e.g., web search, code sandbox), Klavis integrations (e.g., Gmail, Google Calendar), and LobehubSkill providers
- **market**: MCP marketplace plugins
\`\`\`
installPlugin(agentId, identifier, source)
\`\`\`
**Notes:**
- Some official plugins (Klavis, LobehubSkill) may require OAuth authorization
- Use the available plugins from the injected context to find valid plugin identifiers
- After installation, the plugin is automatically enabled for the specified agent
</install_plugin_guide>
<search_guide>
## Finding the Right Agent
@ -178,6 +259,20 @@ The agent will work in the background and return results upon completion.
1. Create a specialized agent for a specific task
2. Immediately call the agent to execute the task
3. Refine agent configuration based on results
### Pattern 5: Inspect and Decide
1. Use getAgentDetail to inspect an agent's current configuration
2. Decide whether to call it, update it, or duplicate it based on the details
### Pattern 6: Duplicate and Customize
1. Find an existing agent that's close to what's needed
2. Use duplicateAgent to create a copy
3. Use updateAgent to customize the copy for the new use case
### Pattern 7: Equip with Plugins
1. Create or select an agent
2. Use installPlugin to add necessary tools/integrations
3. Call the agent with instructions that leverage the installed plugins
</workflow_patterns>
<best_practices>

View file

@ -10,40 +10,35 @@ export const AgentManagementIdentifier = 'lobe-agent-management';
* Agent Management API Names
*/
export const AgentManagementApiName = {
// ==================== Execution ====================
/** Call an agent to handle a task */
callAgent: 'callAgent',
/** Call an agent to handle a task */
callAgent: 'callAgent',
// ==================== Agent CRUD ====================
/** Create a new agent */
createAgent: 'createAgent',
// ==================== Agent CRUD ====================
/** Create a new agent */
createAgent: 'createAgent',
/** Delete an agent */
deleteAgent: 'deleteAgent',
/** Duplicate an existing agent */
duplicateAgent: 'duplicateAgent',
/** Delete an agent */
deleteAgent: 'deleteAgent',
/** Get detailed configuration of an agent */
getAgentDetail: 'getAgentDetail',
/** Install a plugin for an agent */
installPlugin: 'installPlugin',
// ==================== Search ====================
/** Search agents (user's own and marketplace) */
searchAgent: 'searchAgent',
// ==================== Search ====================
/** Search agents (user's own and marketplace) */
searchAgent: 'searchAgent',
/** Update an existing agent */
updateAgent: 'updateAgent',
/** Update an existing agent */
updateAgent: 'updateAgent',
/** Update an agent's system prompt */
updatePrompt: 'updatePrompt',
} as const;
export type AgentManagementApiNameType =
@ -250,6 +245,19 @@ export interface SearchAgentState {
totalCount: number;
}
// ==================== Update Prompt ====================
export interface UpdatePromptParams {
/**
* The agent ID to update the prompt for
*/
agentId: string;
/**
* The new system prompt content
*/
prompt: string;
}
// ==================== Call Agent ====================
export interface CallAgentParams {
@ -280,6 +288,115 @@ export interface CallAgentParams {
timeout?: number;
}
// ==================== Get Agent Detail ====================
export interface GetAgentDetailParams {
/**
* The agent ID to get details for
*/
agentId: string;
}
export interface GetAgentDetailState {
/**
* The agent ID
*/
agentId: string;
/**
* Agent configuration
*/
config?: {
model?: string;
openingMessage?: string;
openingQuestions?: string[];
plugins?: string[];
provider?: string;
systemRole?: string;
};
/**
* Agent metadata
*/
meta?: {
avatar?: string;
backgroundColor?: string;
description?: string;
tags?: string[];
title?: string;
};
/**
* Whether the retrieval was successful
*/
success: boolean;
}
// ==================== Duplicate Agent ====================
export interface DuplicateAgentParams {
/**
* The agent ID to duplicate
*/
agentId: string;
/**
* Optional new title for the duplicated agent
*/
newTitle?: string;
}
export interface DuplicateAgentState {
/**
* The new agent's ID
*/
newAgentId?: string;
/**
* The original agent ID
*/
sourceAgentId: string;
/**
* Whether the duplication was successful
*/
success: boolean;
}
// ==================== Install Plugin ====================
export type InstallPluginSource = 'official' | 'market';
export interface InstallPluginParams {
/**
* The agent ID to install the plugin for
*/
agentId: string;
/**
* The plugin identifier to install
*/
identifier: string;
/**
* Plugin source: 'official' (builtin/klavis/lobehub-skill) or 'market' (MCP marketplace)
*/
source: InstallPluginSource;
}
export interface InstallPluginState {
/**
* Whether the plugin was installed successfully
*/
installed: boolean;
/**
* The plugin identifier
*/
pluginId: string;
/**
* The plugin display name
*/
pluginName?: string;
/**
* Whether the operation was successful
*/
success: boolean;
}
// ==================== Call Agent ====================
export interface CallAgentState {
/**
* The agent ID being called

View file

@ -88,6 +88,12 @@ export interface AgentManagementContext {
availablePlugins?: AvailablePluginInfo[];
/** Available providers and models */
availableProviders?: AvailableProviderInfo[];
/**
* The current responding agent's id and title.
* Exposed so the model can use Agent Management tools (updateAgent, getAgentDetail,
* installPlugin, etc.) to manage itself when the user asks to modify the current agent.
*/
currentAgent?: { id: string; title?: string };
/** Agents @mentioned by the user — supervisor should delegate to these via callAgent */
mentionedAgents?: RuntimeMentionedAgent[];
}
@ -107,6 +113,14 @@ export interface AgentManagementContextInjectorConfig {
const defaultFormatContext = (context: AgentManagementContext): string => {
const parts: string[] = [];
// Add current agent identity so the model can self-manage
if (context.currentAgent) {
const titleAttr = context.currentAgent.title
? ` title="${escapeXml(context.currentAgent.title)}"`
: '';
parts.push(`<current_agent id="${escapeXml(context.currentAgent.id)}"${titleAttr} />`);
}
// Add available models section
if (context.availableProviders && context.availableProviders.length > 0) {
const providersXml = context.availableProviders
@ -203,6 +217,11 @@ const defaultFormatContext = (context: AgentManagementContext): string => {
const hasAgents = context.availableAgents && context.availableAgents.length > 0;
const instructionParts: string[] = [];
if (context.currentAgent) {
instructionParts.push(
'The `current_agent` tag is YOU — your own agent ID. When the user asks to modify your settings (model, plugins, system prompt, etc.), use this ID with updateAgent, getAgentDetail, installPlugin, or other Agent Management tools to manage yourself. Do NOT call yourself via callAgent.',
);
}
if (hasModelsOrPlugins) {
instructionParts.push(
'When creating or updating agents using the Agent Management tools, you can select from these available models and plugins. Use the exact IDs from this context when specifying model/provider/plugins parameters.',

View file

@ -27,16 +27,29 @@ export default {
'builtins.lobe-agent-management.apiName.callAgent': 'Call agent',
'builtins.lobe-agent-management.apiName.createAgent': 'Create agent',
'builtins.lobe-agent-management.apiName.deleteAgent': 'Delete agent',
'builtins.lobe-agent-management.apiName.duplicateAgent': 'Duplicate agent',
'builtins.lobe-agent-management.apiName.getAgentDetail': 'Get agent detail',
'builtins.lobe-agent-management.apiName.installPlugin': 'Install plugin',
'builtins.lobe-agent-management.apiName.searchAgent': 'Search agents',
'builtins.lobe-agent-management.apiName.updateAgent': 'Update agent',
'builtins.lobe-agent-management.apiName.updatePrompt': 'Update prompt',
'builtins.lobe-agent-management.inspector.callAgent.sync': 'Calling:',
'builtins.lobe-agent-management.inspector.callAgent.task': 'Assigning task to:',
'builtins.lobe-agent-management.inspector.createAgent.title': 'Creating agent:',
'builtins.lobe-agent-management.inspector.duplicateAgent.title': 'Duplicating agent:',
'builtins.lobe-agent-management.inspector.getAgentDetail.title': 'Getting details:',
'builtins.lobe-agent-management.inspector.installPlugin.title': 'Installing plugin:',
'builtins.lobe-agent-management.inspector.searchAgent.all': 'Search agents:',
'builtins.lobe-agent-management.inspector.searchAgent.market': 'Search market:',
'builtins.lobe-agent-management.inspector.searchAgent.results': '{{count}} results',
'builtins.lobe-agent-management.inspector.searchAgent.user': 'Search my agents:',
'builtins.lobe-agent-management.inspector.updateAgent.title': 'Updating agent:',
'builtins.lobe-agent-management.inspector.updatePrompt.title': 'Updating prompt:',
'builtins.lobe-agent-management.render.duplicateAgent.newId': 'New Agent ID',
'builtins.lobe-agent-management.render.duplicateAgent.sourceId': 'Source Agent ID',
'builtins.lobe-agent-management.render.installPlugin.failed': 'Installation failed',
'builtins.lobe-agent-management.render.installPlugin.plugin': 'Plugin',
'builtins.lobe-agent-management.render.installPlugin.success': 'Installed successfully',
'builtins.lobe-agent-management.title': 'Agent Manager',
'builtins.lobe-cloud-sandbox.apiName.editLocalFile': 'Edit file',
'builtins.lobe-cloud-sandbox.apiName.executeCode': 'Execute code',

View file

@ -781,6 +781,12 @@ export class AiAgentService {
agentManagementContext = {
availableAgents,
availableAgentsHasMore: hasMoreAgents,
...(resolvedAgentId && {
currentAgent: {
id: resolvedAgentId,
title: agentConfig.title ?? undefined,
},
}),
};
}

View file

@ -440,6 +440,12 @@ export const contextEngineering = async ({
agentManagementContext = {
availableAgents,
availableAgentsHasMore: hasMoreAgents,
...(agentId && {
currentAgent: {
id: agentId,
title: agentSelectors.getAgentMetaById(agentId)(agentStoreState)?.title ?? undefined,
},
}),
};
log('availableAgents fetched: %d agents (hasMore=%s)', availableAgents.length, hasMoreAgents);
} catch (error) {