mirror of
https://github.com/tinyhumansai/openhuman.git
synced 2026-07-27 21:08:00 +00:00
feat(ui): move agents library to Intelligence tab (#3438)
This commit is contained in:
@@ -0,0 +1,115 @@
|
||||
import debug from 'debug';
|
||||
import { useCallback, useEffect, useRef, useState } from 'react';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
|
||||
import { useT } from '../../lib/i18n/I18nContext';
|
||||
import type { AgentDefinitionDisplay } from '../../services/api/agentLibraryApi';
|
||||
import { threadApi } from '../../services/api/threadApi';
|
||||
import { chatSend } from '../../services/chatService';
|
||||
import { selectActiveAgentProfileId } from '../../store/agentProfileSlice';
|
||||
import { beginInferenceTurn, setToolTimelineForThread } from '../../store/chatRuntimeSlice';
|
||||
import { useAppDispatch, useAppSelector } from '../../store/hooks';
|
||||
import {
|
||||
loadThreadMessages,
|
||||
loadThreads,
|
||||
setActiveThread,
|
||||
setSelectedThread,
|
||||
} from '../../store/threadSlice';
|
||||
import type { ThreadMessage } from '../../types/thread';
|
||||
import AgentsLibraryPanel from './AgentsLibraryPanel';
|
||||
|
||||
const log = debug('intelligence:agents-tab');
|
||||
const AGENT_TASK_THREAD_LABEL = 'tasks';
|
||||
const CHAT_MODEL_ID = 'reasoning-v1';
|
||||
|
||||
function agentTaskThreadTitle(title: string): string {
|
||||
const trimmed = title.trim();
|
||||
const base = trimmed.length > 72 ? `${trimmed.slice(0, 69)}...` : trimmed;
|
||||
return `Agent task: ${base || 'Untitled task'}`;
|
||||
}
|
||||
|
||||
function buildExplicitAgentPrompt(agent: AgentDefinitionDisplay, task: string): string {
|
||||
return [
|
||||
`@agent:${agent.id}`,
|
||||
'',
|
||||
'Run this task with the explicitly selected agent above. Treat the selected agent id as the user routing choice when resolving delegation ambiguity.',
|
||||
'',
|
||||
task.trim(),
|
||||
].join('\n');
|
||||
}
|
||||
|
||||
export default function IntelligenceAgentsTab() {
|
||||
const { t } = useT();
|
||||
const dispatch = useAppDispatch();
|
||||
const navigate = useNavigate();
|
||||
const selectedAgentProfileId = useAppSelector(selectActiveAgentProfileId);
|
||||
const uiLocale = useAppSelector(state => state.locale?.current ?? 'en');
|
||||
const mountedRef = useRef(true);
|
||||
|
||||
useEffect(() => {
|
||||
return () => {
|
||||
mountedRef.current = false;
|
||||
};
|
||||
}, []);
|
||||
|
||||
const [runningAgentId, setRunningAgentId] = useState<string | null>(null);
|
||||
const [actionError, setActionError] = useState<string | null>(null);
|
||||
|
||||
const handleRunAgentTask = useCallback(
|
||||
async (agent: AgentDefinitionDisplay, task: string) => {
|
||||
if (runningAgentId) return;
|
||||
setRunningAgentId(agent.id);
|
||||
setActionError(null);
|
||||
const now = new Date().toISOString();
|
||||
const launchPrompt = buildExplicitAgentPrompt(agent, task);
|
||||
const titleBase = task.trim().slice(0, 64) || agent.display_name;
|
||||
try {
|
||||
const thread = await threadApi.createNewThread([AGENT_TASK_THREAD_LABEL, 'agent-library']);
|
||||
await threadApi.updateTitle(thread.id, agentTaskThreadTitle(titleBase));
|
||||
const userMessage: ThreadMessage = {
|
||||
id: `msg_${globalThis.crypto?.randomUUID ? globalThis.crypto.randomUUID() : `${Date.now()}`}`,
|
||||
content: launchPrompt,
|
||||
type: 'text',
|
||||
extraMetadata: { source: 'agent-library', explicitAgentId: agent.id },
|
||||
sender: 'user',
|
||||
createdAt: now,
|
||||
};
|
||||
await threadApi.appendMessage(thread.id, userMessage);
|
||||
|
||||
dispatch(setSelectedThread(thread.id));
|
||||
dispatch(setToolTimelineForThread({ threadId: thread.id, entries: [] }));
|
||||
dispatch(beginInferenceTurn({ threadId: thread.id }));
|
||||
dispatch(setActiveThread(thread.id));
|
||||
void dispatch(loadThreads());
|
||||
void dispatch(loadThreadMessages(thread.id));
|
||||
navigate('/chat');
|
||||
|
||||
await chatSend({
|
||||
threadId: thread.id,
|
||||
message: launchPrompt,
|
||||
model: CHAT_MODEL_ID,
|
||||
profileId: selectedAgentProfileId,
|
||||
locale: uiLocale,
|
||||
});
|
||||
} catch (err) {
|
||||
const msg = err instanceof Error ? err.message : String(err);
|
||||
log('run explicit agent task failed agent=%s: %s', agent.id, msg);
|
||||
if (mountedRef.current) setActionError(t('intelligence.agents.runFailed'));
|
||||
} finally {
|
||||
if (mountedRef.current) setRunningAgentId(null);
|
||||
}
|
||||
},
|
||||
[dispatch, navigate, runningAgentId, selectedAgentProfileId, t, uiLocale]
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="space-y-4">
|
||||
{actionError && (
|
||||
<div className="rounded-xl border border-coral-200 dark:border-coral-500/30 bg-coral-50 dark:bg-coral-500/10 px-4 py-3 text-sm text-coral-700 dark:text-coral-300">
|
||||
{actionError}
|
||||
</div>
|
||||
)}
|
||||
<AgentsLibraryPanel onRunAgentTask={handleRunAgentTask} runningAgentId={runningAgentId} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -29,7 +29,6 @@ import { useNavigate } from 'react-router-dom';
|
||||
import { useT } from '../../lib/i18n/I18nContext';
|
||||
import { TaskKanbanBoard } from '../../pages/conversations/components/TaskKanbanBoard';
|
||||
import { isTaskThread } from '../../pages/conversations/utils/threadFilter';
|
||||
import type { AgentDefinitionDisplay } from '../../services/api/agentLibraryApi';
|
||||
import { threadApi } from '../../services/api/threadApi';
|
||||
import {
|
||||
TASK_SOURCES_THREAD_ID,
|
||||
@@ -48,7 +47,6 @@ import {
|
||||
} from '../../store/threadSlice';
|
||||
import type { ThreadMessage } from '../../types/thread';
|
||||
import type { TaskBoard, TaskBoardCard, TaskBoardCardStatus } from '../../types/turnState';
|
||||
import AgentsLibraryPanel from './AgentsLibraryPanel';
|
||||
import { UserTaskComposer } from './UserTaskComposer';
|
||||
|
||||
const log = debug('intelligence:tasks');
|
||||
@@ -117,16 +115,6 @@ function buildAgentTaskPrompt(
|
||||
return lines.join('\n');
|
||||
}
|
||||
|
||||
function buildExplicitAgentPrompt(agent: AgentDefinitionDisplay, task: string): string {
|
||||
return [
|
||||
`@agent:${agent.id}`,
|
||||
'',
|
||||
'Run this task with the explicitly selected agent above. Treat the selected agent id as the user routing choice when resolving delegation ambiguity.',
|
||||
'',
|
||||
task.trim(),
|
||||
].join('\n');
|
||||
}
|
||||
|
||||
export default function IntelligenceTasksTab() {
|
||||
const { t } = useT();
|
||||
const dispatch = useAppDispatch();
|
||||
@@ -143,7 +131,6 @@ export default function IntelligenceTasksTab() {
|
||||
const [refiningCard, setRefiningCard] = useState<TaskBoardCard | null>(null);
|
||||
const [workingCardId, setWorkingCardId] = useState<string | null>(null);
|
||||
const [mutatingCardId, setMutatingCardId] = useState<string | null>(null);
|
||||
const [runningAgentId, setRunningAgentId] = useState<string | null>(null);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const [actionError, setActionError] = useState<string | null>(null);
|
||||
@@ -424,53 +411,6 @@ export default function IntelligenceTasksTab() {
|
||||
[dispatch, navigate, personalBoard, selectedAgentProfileId, t, uiLocale, workingCardId]
|
||||
);
|
||||
|
||||
const handleRunAgentTask = useCallback(
|
||||
async (agent: AgentDefinitionDisplay, task: string) => {
|
||||
if (runningAgentId) return;
|
||||
setRunningAgentId(agent.id);
|
||||
setActionError(null);
|
||||
const now = new Date().toISOString();
|
||||
const launchPrompt = buildExplicitAgentPrompt(agent, task);
|
||||
const titleBase = task.trim().slice(0, 64) || agent.display_name;
|
||||
try {
|
||||
const thread = await threadApi.createNewThread([AGENT_TASK_THREAD_LABEL, 'agent-library']);
|
||||
await threadApi.updateTitle(thread.id, agentTaskThreadTitle(titleBase));
|
||||
const userMessage: ThreadMessage = {
|
||||
id: `msg_${globalThis.crypto?.randomUUID ? globalThis.crypto.randomUUID() : `${Date.now()}`}`,
|
||||
content: launchPrompt,
|
||||
type: 'text',
|
||||
extraMetadata: { source: 'agent-library', explicitAgentId: agent.id },
|
||||
sender: 'user',
|
||||
createdAt: now,
|
||||
};
|
||||
await threadApi.appendMessage(thread.id, userMessage);
|
||||
|
||||
dispatch(setSelectedThread(thread.id));
|
||||
dispatch(setToolTimelineForThread({ threadId: thread.id, entries: [] }));
|
||||
dispatch(beginInferenceTurn({ threadId: thread.id }));
|
||||
dispatch(setActiveThread(thread.id));
|
||||
void dispatch(loadThreads());
|
||||
void dispatch(loadThreadMessages(thread.id));
|
||||
navigate('/chat');
|
||||
|
||||
await chatSend({
|
||||
threadId: thread.id,
|
||||
message: launchPrompt,
|
||||
model: CHAT_MODEL_ID,
|
||||
profileId: selectedAgentProfileId,
|
||||
locale: uiLocale,
|
||||
});
|
||||
} catch (err) {
|
||||
const msg = err instanceof Error ? err.message : String(err);
|
||||
log('run explicit agent task failed agent=%s: %s', agent.id, msg);
|
||||
if (mountedRef.current) setActionError(t('intelligence.agents.runFailed'));
|
||||
} finally {
|
||||
if (mountedRef.current) setRunningAgentId(null);
|
||||
}
|
||||
},
|
||||
[dispatch, navigate, runningAgentId, selectedAgentProfileId, t, uiLocale]
|
||||
);
|
||||
|
||||
const handleApproveSourcePlan = useCallback(
|
||||
async (sourceCard: TaskBoardCard, draft: RefinedTaskDraft) => {
|
||||
const now = new Date().toISOString();
|
||||
@@ -621,8 +561,6 @@ export default function IntelligenceTasksTab() {
|
||||
</div>
|
||||
)}
|
||||
|
||||
<AgentsLibraryPanel onRunAgentTask={handleRunAgentTask} runningAgentId={runningAgentId} />
|
||||
|
||||
{/* Personal board — always present so users can manage their own tasks. */}
|
||||
<section className="space-y-2">
|
||||
<div className="flex items-center gap-2">
|
||||
|
||||
@@ -0,0 +1,179 @@
|
||||
import { fireEvent, render, screen, waitFor } from '@testing-library/react';
|
||||
import { beforeEach, describe, expect, test, vi } from 'vitest';
|
||||
|
||||
const hoisted = vi.hoisted(() => ({
|
||||
createNewThread: vi.fn(),
|
||||
updateTitle: vi.fn(),
|
||||
appendMessage: vi.fn(),
|
||||
chatSend: vi.fn(),
|
||||
navigate: vi.fn(),
|
||||
dispatch: vi.fn(),
|
||||
selectorResult: {
|
||||
agentProfiles: { activeProfileId: 'agent-profile-1' },
|
||||
locale: { current: 'en' },
|
||||
},
|
||||
}));
|
||||
|
||||
vi.mock('../../../services/api/threadApi', () => ({
|
||||
threadApi: {
|
||||
createNewThread: hoisted.createNewThread,
|
||||
updateTitle: hoisted.updateTitle,
|
||||
appendMessage: hoisted.appendMessage,
|
||||
},
|
||||
}));
|
||||
|
||||
vi.mock('../../../services/chatService', () => ({ chatSend: hoisted.chatSend }));
|
||||
|
||||
vi.mock('../../../store/hooks', () => ({
|
||||
useAppSelector: (selector: (state: typeof hoisted.selectorResult) => unknown) =>
|
||||
selector(hoisted.selectorResult),
|
||||
useAppDispatch: () => hoisted.dispatch,
|
||||
}));
|
||||
|
||||
vi.mock('react-router-dom', async () => {
|
||||
const actual = await vi.importActual<typeof import('react-router-dom')>('react-router-dom');
|
||||
return { ...actual, useNavigate: () => hoisted.navigate };
|
||||
});
|
||||
|
||||
vi.mock('../AgentsLibraryPanel', () => ({
|
||||
default: ({
|
||||
onRunAgentTask,
|
||||
runningAgentId,
|
||||
}: {
|
||||
onRunAgentTask: (
|
||||
agent: {
|
||||
id: string;
|
||||
display_name: string;
|
||||
tier: string;
|
||||
model: { kind: string; value: string };
|
||||
direct_tool_count: number;
|
||||
direct_tool_names: string[];
|
||||
uses_wildcard_tools: boolean;
|
||||
subagent_ids: string[];
|
||||
includes_profile: boolean;
|
||||
includes_memory_md: boolean;
|
||||
includes_memory_context: boolean;
|
||||
can_run_as_user_facing_worker: boolean;
|
||||
write_capable: boolean;
|
||||
source: string;
|
||||
},
|
||||
task: string
|
||||
) => Promise<void>;
|
||||
runningAgentId?: string | null;
|
||||
}) => (
|
||||
<div>
|
||||
<span data-testid="running-agent">{runningAgentId ?? 'idle'}</span>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() =>
|
||||
onRunAgentTask(
|
||||
{
|
||||
id: 'researcher',
|
||||
display_name: 'Researcher',
|
||||
tier: 'worker',
|
||||
model: { kind: 'hint', value: 'reasoning' },
|
||||
direct_tool_count: 1,
|
||||
direct_tool_names: ['web_search'],
|
||||
uses_wildcard_tools: false,
|
||||
subagent_ids: [],
|
||||
includes_profile: false,
|
||||
includes_memory_md: false,
|
||||
includes_memory_context: false,
|
||||
can_run_as_user_facing_worker: true,
|
||||
write_capable: false,
|
||||
source: 'builtin',
|
||||
},
|
||||
'Find the current API docs'
|
||||
)
|
||||
}>
|
||||
run researcher
|
||||
</button>
|
||||
</div>
|
||||
),
|
||||
}));
|
||||
|
||||
async function importTab() {
|
||||
const mod = await import('../IntelligenceAgentsTab');
|
||||
return mod.default;
|
||||
}
|
||||
|
||||
describe('IntelligenceAgentsTab', () => {
|
||||
beforeEach(() => {
|
||||
vi.resetModules();
|
||||
hoisted.createNewThread.mockReset();
|
||||
hoisted.updateTitle.mockReset();
|
||||
hoisted.appendMessage.mockReset();
|
||||
hoisted.chatSend.mockReset();
|
||||
hoisted.navigate.mockReset();
|
||||
hoisted.dispatch.mockReset();
|
||||
hoisted.selectorResult.agentProfiles.activeProfileId = 'agent-profile-1';
|
||||
hoisted.selectorResult.locale.current = 'en';
|
||||
hoisted.createNewThread.mockResolvedValue({
|
||||
id: 'thread-agent-task',
|
||||
title: 'Agent task',
|
||||
labels: ['tasks', 'agent-library'],
|
||||
chatId: null,
|
||||
isActive: true,
|
||||
messageCount: 0,
|
||||
lastMessageAt: '2026-01-01T00:00:00Z',
|
||||
createdAt: '2026-01-01T00:00:00Z',
|
||||
});
|
||||
hoisted.updateTitle.mockResolvedValue({
|
||||
id: 'thread-agent-task',
|
||||
title: 'Agent task: Find the current API docs',
|
||||
labels: ['tasks', 'agent-library'],
|
||||
chatId: null,
|
||||
isActive: true,
|
||||
messageCount: 0,
|
||||
lastMessageAt: '2026-01-01T00:00:00Z',
|
||||
createdAt: '2026-01-01T00:00:00Z',
|
||||
});
|
||||
hoisted.appendMessage.mockResolvedValue({
|
||||
id: 'msg-1',
|
||||
content: '@agent:researcher',
|
||||
type: 'text',
|
||||
extraMetadata: {},
|
||||
sender: 'user',
|
||||
createdAt: '2026-01-01T00:00:00Z',
|
||||
});
|
||||
hoisted.chatSend.mockResolvedValue(undefined);
|
||||
});
|
||||
|
||||
test('starts a labeled task thread from an explicit library agent selection', async () => {
|
||||
const Tab = await importTab();
|
||||
render(<Tab />);
|
||||
|
||||
fireEvent.click(screen.getByRole('button', { name: /run researcher/i }));
|
||||
|
||||
await waitFor(() =>
|
||||
expect(hoisted.createNewThread).toHaveBeenCalledWith(['tasks', 'agent-library'])
|
||||
);
|
||||
expect(hoisted.updateTitle).toHaveBeenCalledWith(
|
||||
'thread-agent-task',
|
||||
'Agent task: Find the current API docs'
|
||||
);
|
||||
expect(hoisted.appendMessage).toHaveBeenCalledWith(
|
||||
'thread-agent-task',
|
||||
expect.objectContaining({
|
||||
content: expect.stringContaining('@agent:researcher'),
|
||||
sender: 'user',
|
||||
extraMetadata: expect.objectContaining({
|
||||
source: 'agent-library',
|
||||
explicitAgentId: 'researcher',
|
||||
}),
|
||||
})
|
||||
);
|
||||
await waitFor(() =>
|
||||
expect(hoisted.chatSend).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
threadId: 'thread-agent-task',
|
||||
message: expect.stringContaining('Find the current API docs'),
|
||||
model: 'reasoning-v1',
|
||||
profileId: 'agent-profile-1',
|
||||
locale: 'en',
|
||||
})
|
||||
)
|
||||
);
|
||||
expect(hoisted.navigate).toHaveBeenCalledWith('/chat');
|
||||
});
|
||||
});
|
||||
@@ -20,7 +20,6 @@ const hoisted = vi.hoisted(() => ({
|
||||
updateTitle: vi.fn(),
|
||||
appendMessage: vi.fn(),
|
||||
chatSend: vi.fn(),
|
||||
listAgentDefinitions: vi.fn(),
|
||||
todosList: vi.fn(),
|
||||
todosAdd: vi.fn(),
|
||||
todosEdit: vi.fn(),
|
||||
@@ -47,10 +46,6 @@ vi.mock('../../../services/api/threadApi', () => ({
|
||||
|
||||
vi.mock('../../../services/chatService', () => ({ chatSend: hoisted.chatSend }));
|
||||
|
||||
vi.mock('../../../services/api/agentLibraryApi', () => ({
|
||||
agentLibraryApi: { listDefinitions: hoisted.listAgentDefinitions },
|
||||
}));
|
||||
|
||||
vi.mock('../../../services/api/todosApi', () => ({
|
||||
TASK_SOURCES_THREAD_ID: 'task-sources',
|
||||
USER_TASKS_THREAD_ID: 'user-tasks',
|
||||
@@ -186,7 +181,6 @@ describe('IntelligenceTasksTab', () => {
|
||||
hoisted.updateTitle.mockReset();
|
||||
hoisted.appendMessage.mockReset();
|
||||
hoisted.chatSend.mockReset();
|
||||
hoisted.listAgentDefinitions.mockReset();
|
||||
hoisted.todosList.mockReset();
|
||||
hoisted.todosAdd.mockReset();
|
||||
hoisted.todosEdit.mockReset();
|
||||
@@ -234,25 +228,6 @@ describe('IntelligenceTasksTab', () => {
|
||||
// by default — otherwise the awaited `.catch()` chain stalls the handler
|
||||
// before it reaches chatSend.
|
||||
hoisted.todosSetSessionThread.mockResolvedValue(makeBoard('user-tasks', []));
|
||||
hoisted.listAgentDefinitions.mockResolvedValue([
|
||||
{
|
||||
id: 'researcher',
|
||||
display_name: 'Researcher',
|
||||
when_to_use: 'Use for focused research.',
|
||||
tier: 'worker',
|
||||
model: { kind: 'hint', value: 'reasoning' },
|
||||
direct_tool_count: 1,
|
||||
direct_tool_names: ['web_search'],
|
||||
uses_wildcard_tools: false,
|
||||
subagent_ids: [],
|
||||
includes_profile: false,
|
||||
includes_memory_md: false,
|
||||
includes_memory_context: false,
|
||||
can_run_as_user_facing_worker: true,
|
||||
write_capable: false,
|
||||
source: 'builtin',
|
||||
},
|
||||
]);
|
||||
hoisted.todosList.mockImplementation((threadId: string) =>
|
||||
Promise.resolve(makeBoard(threadId, []))
|
||||
);
|
||||
@@ -599,44 +574,6 @@ describe('IntelligenceTasksTab', () => {
|
||||
);
|
||||
});
|
||||
|
||||
test('starts a labeled task thread from an explicit library agent selection', async () => {
|
||||
hoisted.todosUpdateStatus.mockResolvedValue(makeBoard('user-tasks', []));
|
||||
|
||||
vi.resetModules();
|
||||
const Tab = await importTab();
|
||||
renderTab(Tab);
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText('Researcher')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
fireEvent.change(screen.getByPlaceholderText(/task for this agent/i), {
|
||||
target: { value: 'Find the current API docs' },
|
||||
});
|
||||
fireEvent.click(screen.getByRole('button', { name: /Run task/i }));
|
||||
|
||||
await waitFor(() =>
|
||||
expect(hoisted.createNewThread).toHaveBeenCalledWith(['tasks', 'agent-library'])
|
||||
);
|
||||
expect(hoisted.appendMessage).toHaveBeenCalledWith(
|
||||
'thread-agent-task',
|
||||
expect.objectContaining({
|
||||
content: expect.stringContaining('@agent:researcher'),
|
||||
extraMetadata: expect.objectContaining({
|
||||
source: 'agent-library',
|
||||
explicitAgentId: 'researcher',
|
||||
}),
|
||||
})
|
||||
);
|
||||
expect(hoisted.chatSend).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
threadId: 'thread-agent-task',
|
||||
message: expect.stringContaining('Find the current API docs'),
|
||||
model: 'reasoning-v1',
|
||||
profileId: 'agent-profile-1',
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
test('deletes a personal card via the todos RPC', async () => {
|
||||
hoisted.todosList.mockImplementation((threadId: string) =>
|
||||
Promise.resolve(
|
||||
|
||||
@@ -280,10 +280,13 @@ const messages: TranslationMap = {
|
||||
'memory.empty': 'لا توجد ذكريات بعد. تُنشأ الذكريات تلقائيًا أثناء تفاعلك.',
|
||||
'memory.tab.memory': 'الذاكرة',
|
||||
'memory.tab.memoryTree': 'شجرة الذاكرة',
|
||||
'memory.tab.tasks': 'مهام الوكيل',
|
||||
'memory.tab.tasks': 'المهام',
|
||||
'memory.tab.tasksDescription':
|
||||
'أنشئ المهام وتتبعها — مهامك الخاصة بالإضافة إلى اللوحات التي يبنيها وكلاؤك عبر المحادثات.',
|
||||
'memory.tab.subconscious': 'اللاوعي',
|
||||
'memory.tab.agents': 'المكتبة',
|
||||
'memory.tab.agentsDescription':
|
||||
'تصفّح وشغّل الوكلاء المتاحين — لكل منهم أدواته وقدراته ومجال تركيزه.',
|
||||
'memory.tab.workflows': 'Workflows',
|
||||
'memory.tab.workflowsDescription':
|
||||
'Lifecycle-bound rule sets that guide how the agent behaves during tasks.',
|
||||
|
||||
@@ -283,10 +283,13 @@ const messages: TranslationMap = {
|
||||
'এখনো কোনো মেমোরি নেই। আপনি যত ইন্টারঅ্যাক্ট করবেন, মেমোরি স্বয়ংক্রিয়ভাবে তৈরি হবে।',
|
||||
'memory.tab.memory': 'মেমোরি',
|
||||
'memory.tab.memoryTree': 'মেমোরি ট্রি',
|
||||
'memory.tab.tasks': 'এজেন্ট টাস্ক',
|
||||
'memory.tab.tasks': 'টাস্ক',
|
||||
'memory.tab.tasksDescription':
|
||||
'টাস্ক তৈরি করুন এবং ট্র্যাক করুন — আপনার নিজের কাজের তালিকা এবং এজেন্টরা কথোপকথন জুড়ে যে বোর্ডগুলি তৈরি করে।',
|
||||
'memory.tab.subconscious': 'সাবকনশাস',
|
||||
'memory.tab.agents': 'লাইব্রেরি',
|
||||
'memory.tab.agentsDescription':
|
||||
'আপনার উপলব্ধ এজেন্টগুলি ব্রাউজ করুন এবং চালান — প্রতিটির নিজস্ব টুল, ক্ষমতা এবং ফোকাস এলাকা রয়েছে।',
|
||||
'memory.tab.workflows': 'Workflows',
|
||||
'memory.tab.workflowsDescription':
|
||||
'Lifecycle-bound rule sets that guide how the agent behaves during tasks.',
|
||||
|
||||
@@ -292,10 +292,13 @@ const messages: TranslationMap = {
|
||||
'Noch keine Erinnerungen. Erinnerungen werden automatisch erstellt, während du interagierst.',
|
||||
'memory.tab.memory': 'Erinnerung',
|
||||
'memory.tab.memoryTree': 'Erinnerungsbaum',
|
||||
'memory.tab.tasks': 'Agent-Aufgaben',
|
||||
'memory.tab.tasks': 'Aufgaben',
|
||||
'memory.tab.tasksDescription':
|
||||
'Aufgaben erstellen und verfolgen – eigene To-dos sowie die Boards, die Agenten in Gesprächen anlegen.',
|
||||
'memory.tab.subconscious': 'Unterbewusstsein',
|
||||
'memory.tab.agents': 'Bibliothek',
|
||||
'memory.tab.agentsDescription':
|
||||
'Verfügbare Agenten durchsuchen und starten — jeder mit eigenen Werkzeugen, Fähigkeiten und Schwerpunkten.',
|
||||
'memory.tab.workflows': 'Workflows',
|
||||
'memory.tab.workflowsDescription':
|
||||
'Lifecycle-bound rule sets that guide how the agent behaves during tasks.',
|
||||
|
||||
@@ -308,10 +308,13 @@ const en: TranslationMap = {
|
||||
'memory.empty': 'No memories yet. Memories are created automatically as you interact.',
|
||||
'memory.tab.memory': 'Memory',
|
||||
'memory.tab.memoryTree': 'Memory Tree',
|
||||
'memory.tab.tasks': 'Agent Tasks',
|
||||
'memory.tab.tasks': 'Tasks',
|
||||
'memory.tab.tasksDescription':
|
||||
'Create and track tasks — your own to-dos plus the boards your agents build across conversations.',
|
||||
'memory.tab.subconscious': 'Subconscious',
|
||||
'memory.tab.agents': 'Library',
|
||||
'memory.tab.agentsDescription':
|
||||
'Browse and run your available agents — each with its own tools, capabilities, and focus area.',
|
||||
'memory.tab.workflows': 'Workflows',
|
||||
'memory.tab.workflowsDescription':
|
||||
'Reusable, runnable procedures — a goal plus the steps to reach it. Create one, install from a URL, or open a workflow to run it.',
|
||||
|
||||
@@ -292,10 +292,13 @@ const messages: TranslationMap = {
|
||||
'memory.empty': 'Sin recuerdos aún. Los recuerdos se crean automáticamente mientras interactúas.',
|
||||
'memory.tab.memory': 'Memoria',
|
||||
'memory.tab.memoryTree': 'Árbol de memoria',
|
||||
'memory.tab.tasks': 'Tareas del agente',
|
||||
'memory.tab.tasks': 'Tareas',
|
||||
'memory.tab.tasksDescription':
|
||||
'Crea y realiza un seguimiento de tareas: tus propios pendientes más los tableros que tus agentes crean a lo largo de las conversaciones.',
|
||||
'memory.tab.subconscious': 'Subconsciente',
|
||||
'memory.tab.agents': 'Biblioteca',
|
||||
'memory.tab.agentsDescription':
|
||||
'Explora y ejecuta los agentes disponibles — cada uno con sus propias herramientas, capacidades y área de enfoque.',
|
||||
'memory.tab.workflows': 'Workflows',
|
||||
'memory.tab.workflowsDescription':
|
||||
'Lifecycle-bound rule sets that guide how the agent behaves during tasks.',
|
||||
|
||||
@@ -291,10 +291,13 @@ const messages: TranslationMap = {
|
||||
"Aucun souvenir pour l'instant. Les souvenirs sont créés automatiquement au fil de tes interactions.",
|
||||
'memory.tab.memory': 'Mémoire',
|
||||
'memory.tab.memoryTree': 'Arbre de mémoire',
|
||||
'memory.tab.tasks': "Tâches de l'agent",
|
||||
'memory.tab.tasks': 'Tâches',
|
||||
'memory.tab.tasksDescription':
|
||||
'Créez et suivez des tâches — vos propres listes de choses à faire ainsi que les tableaux que vos agents construisent au fil des conversations.',
|
||||
'memory.tab.subconscious': 'Subconscient',
|
||||
'memory.tab.agents': 'Bibliothèque',
|
||||
'memory.tab.agentsDescription':
|
||||
'Parcourez et exécutez vos agents disponibles — chacun avec ses propres outils, capacités et domaine de spécialisation.',
|
||||
'memory.tab.workflows': 'Workflows',
|
||||
'memory.tab.workflowsDescription':
|
||||
'Lifecycle-bound rule sets that guide how the agent behaves during tasks.',
|
||||
|
||||
@@ -282,10 +282,13 @@ const messages: TranslationMap = {
|
||||
'memory.empty': 'अभी कोई मेमोरी नहीं है। बातचीत के दौरान मेमोरी अपने आप बनती है।',
|
||||
'memory.tab.memory': 'मेमोरी',
|
||||
'memory.tab.memoryTree': 'मेमोरी ट्री',
|
||||
'memory.tab.tasks': 'एजेंट कार्य',
|
||||
'memory.tab.tasks': 'कार्य',
|
||||
'memory.tab.tasksDescription':
|
||||
'कार्य बनाएं और ट्रैक करें — आपके अपने टू-डू और साथ ही वे बोर्ड जो आपके एजेंट वार्तालापों में बनाते हैं।',
|
||||
'memory.tab.subconscious': 'सबकॉन्शस',
|
||||
'memory.tab.agents': 'लाइब्रेरी',
|
||||
'memory.tab.agentsDescription':
|
||||
'अपने उपलब्ध एजेंट ब्राउज़ करें और चलाएं — प्रत्येक के पास अपने टूल, क्षमताएं और फोकस क्षेत्र हैं।',
|
||||
'memory.tab.workflows': 'Workflows',
|
||||
'memory.tab.workflowsDescription':
|
||||
'Lifecycle-bound rule sets that guide how the agent behaves during tasks.',
|
||||
|
||||
@@ -284,10 +284,13 @@ const messages: TranslationMap = {
|
||||
'memory.empty': 'Belum ada memori. Memori dibuat otomatis saat Anda berinteraksi.',
|
||||
'memory.tab.memory': 'Memori',
|
||||
'memory.tab.memoryTree': 'Pohon Memori',
|
||||
'memory.tab.tasks': 'Tugas Agen',
|
||||
'memory.tab.tasks': 'Tugas',
|
||||
'memory.tab.tasksDescription':
|
||||
'Buat dan lacak tugas — to-do Anda sendiri beserta papan yang dibangun agen Anda di berbagai percakapan.',
|
||||
'memory.tab.subconscious': 'Bawah sadar',
|
||||
'memory.tab.agents': 'Perpustakaan',
|
||||
'memory.tab.agentsDescription':
|
||||
'Telusuri dan jalankan agen yang tersedia — masing-masing dengan alat, kemampuan, dan area fokusnya sendiri.',
|
||||
'memory.tab.workflows': 'Workflows',
|
||||
'memory.tab.workflowsDescription':
|
||||
'Lifecycle-bound rule sets that guide how the agent behaves during tasks.',
|
||||
|
||||
@@ -289,10 +289,13 @@ const messages: TranslationMap = {
|
||||
'Nessuna memoria ancora. Le memorie vengono create automaticamente mentre interagisci.',
|
||||
'memory.tab.memory': 'Memoria',
|
||||
'memory.tab.memoryTree': 'Albero della memoria',
|
||||
'memory.tab.tasks': 'Attività agente',
|
||||
'memory.tab.tasks': 'Attività',
|
||||
'memory.tab.tasksDescription':
|
||||
'Crea e monitora le attività — i tuoi to-do personali e le board create dagli agenti nelle conversazioni.',
|
||||
'memory.tab.subconscious': 'Subconscio',
|
||||
'memory.tab.agents': 'Libreria',
|
||||
'memory.tab.agentsDescription':
|
||||
'Sfoglia e avvia gli agenti disponibili — ognuno con i propri strumenti, capacità e area di specializzazione.',
|
||||
'memory.tab.workflows': 'Workflows',
|
||||
'memory.tab.workflowsDescription':
|
||||
'Lifecycle-bound rule sets that guide how the agent behaves during tasks.',
|
||||
|
||||
@@ -282,10 +282,13 @@ const messages: TranslationMap = {
|
||||
'memory.empty': '아직 메모리가 없습니다. 메모리는 상호작용하면서 자동으로 생성됩니다.',
|
||||
'memory.tab.memory': '메모리',
|
||||
'memory.tab.memoryTree': '메모리 트리',
|
||||
'memory.tab.tasks': '에이전트 작업',
|
||||
'memory.tab.tasks': '작업',
|
||||
'memory.tab.tasksDescription':
|
||||
'작업을 만들고 추적하세요 — 본인의 할 일 목록과 에이전트가 대화를 통해 구성한 보드가 모두 포함됩니다.',
|
||||
'memory.tab.subconscious': '잠재의식',
|
||||
'memory.tab.agents': '라이브러리',
|
||||
'memory.tab.agentsDescription':
|
||||
'사용 가능한 에이전트를 탐색하고 실행하세요 — 각 에이전트는 고유한 도구, 기능 및 전문 분야를 갖추고 있습니다.',
|
||||
'memory.tab.workflows': 'Workflows',
|
||||
'memory.tab.workflowsDescription':
|
||||
'Lifecycle-bound rule sets that guide how the agent behaves during tasks.',
|
||||
|
||||
@@ -287,10 +287,13 @@ const messages: TranslationMap = {
|
||||
'Brak wspomnień. Wspomnienia powstają automatycznie podczas korzystania z aplikacji.',
|
||||
'memory.tab.memory': 'Pamięć',
|
||||
'memory.tab.memoryTree': 'Drzewo pamięci',
|
||||
'memory.tab.tasks': 'Zadania agenta',
|
||||
'memory.tab.tasks': 'Zadania',
|
||||
'memory.tab.tasksDescription':
|
||||
'Twórz i śledź zadania — własne listy do zrobienia oraz tablice, które Twoi agenci budują w rozmowach.',
|
||||
'memory.tab.subconscious': 'Podświadomość',
|
||||
'memory.tab.agents': 'Biblioteka',
|
||||
'memory.tab.agentsDescription':
|
||||
'Przeglądaj i uruchamiaj dostępnych agentów — każdy z własnymi narzędziami, możliwościami i obszarem specjalizacji.',
|
||||
'memory.tab.workflows': 'Workflows',
|
||||
'memory.tab.workflowsDescription':
|
||||
'Lifecycle-bound rule sets that guide how the agent behaves during tasks.',
|
||||
@@ -4716,7 +4719,7 @@ const messages: TranslationMap = {
|
||||
'intelligence.agents.allTools': 'Wszystkie narzędzia',
|
||||
'intelligence.agents.toolCountOne': '{count} narzędzie',
|
||||
'intelligence.agents.toolCountOther': '{count} narzędzi',
|
||||
'intelligence.agents.subagentCountOne': '{count} subagent',
|
||||
'intelligence.agents.subagentCountOne': '{count} podagent',
|
||||
'intelligence.agents.subagentCountOther': '{count} subagentów',
|
||||
'intelligence.agents.startChat': 'Rozpocznij czat',
|
||||
'intelligence.agents.startChatPrompt':
|
||||
|
||||
@@ -291,10 +291,13 @@ const messages: TranslationMap = {
|
||||
'Nenhuma memória ainda. As memórias são criadas automaticamente conforme você interage.',
|
||||
'memory.tab.memory': 'Memória',
|
||||
'memory.tab.memoryTree': 'Árvore de memória',
|
||||
'memory.tab.tasks': 'Tarefas do Agente',
|
||||
'memory.tab.tasks': 'Tarefas',
|
||||
'memory.tab.tasksDescription':
|
||||
'Crie e acompanhe tarefas — seus próprios afazeres e os painéis que seus agentes constroem ao longo das conversas.',
|
||||
'memory.tab.subconscious': 'Subconsciente',
|
||||
'memory.tab.agents': 'Biblioteca',
|
||||
'memory.tab.agentsDescription':
|
||||
'Navegue e execute os agentes disponíveis — cada um com suas próprias ferramentas, capacidades e área de foco.',
|
||||
'memory.tab.workflows': 'Workflows',
|
||||
'memory.tab.workflowsDescription':
|
||||
'Lifecycle-bound rule sets that guide how the agent behaves during tasks.',
|
||||
|
||||
@@ -284,10 +284,13 @@ const messages: TranslationMap = {
|
||||
'memory.empty': 'Воспоминаний пока нет. Они создаются автоматически в процессе общения.',
|
||||
'memory.tab.memory': 'Память',
|
||||
'memory.tab.memoryTree': 'Дерево памяти',
|
||||
'memory.tab.tasks': 'Задачи агента',
|
||||
'memory.tab.tasks': 'Задачи',
|
||||
'memory.tab.tasksDescription':
|
||||
'Создавайте и отслеживайте задачи — ваши личные дела и доски, которые агенты формируют в ходе разговоров.',
|
||||
'memory.tab.subconscious': 'Подсознание',
|
||||
'memory.tab.agents': 'Библиотека',
|
||||
'memory.tab.agentsDescription':
|
||||
'Просматривайте и запускайте доступных агентов — у каждого свои инструменты, возможности и область специализации.',
|
||||
'memory.tab.workflows': 'Workflows',
|
||||
'memory.tab.workflowsDescription':
|
||||
'Lifecycle-bound rule sets that guide how the agent behaves during tasks.',
|
||||
|
||||
@@ -271,10 +271,13 @@ const messages: TranslationMap = {
|
||||
'memory.empty': '暂无记忆。记忆将在你交互时自动创建。',
|
||||
'memory.tab.memory': '记忆',
|
||||
'memory.tab.memoryTree': '记忆树',
|
||||
'memory.tab.tasks': '智能体任务',
|
||||
'memory.tab.tasks': '任务',
|
||||
'memory.tab.tasksDescription':
|
||||
'创建并跟踪任务——包括您自己的待办事项以及智能体在对话中创建的看板。',
|
||||
'memory.tab.subconscious': '潜意识',
|
||||
'memory.tab.agents': '资料库',
|
||||
'memory.tab.agentsDescription':
|
||||
'浏览并运行可用的智能体——每个智能体都有自己的工具、能力和专注领域。',
|
||||
'memory.tab.workflows': 'Workflows',
|
||||
'memory.tab.workflowsDescription':
|
||||
'Lifecycle-bound rule sets that guide how the agent behaves during tasks.',
|
||||
|
||||
@@ -7,7 +7,7 @@ import Intelligence from './Intelligence';
|
||||
vi.mock('../lib/i18n/I18nContext', () => ({ useT: () => ({ t: (k: string) => k }) }));
|
||||
|
||||
// IS_DEV gates the dev-only "council" tab; default to a non-dev build.
|
||||
const isDev = { value: false };
|
||||
const isDev = vi.hoisted(() => ({ value: false }));
|
||||
vi.mock('../utils/config', async () => {
|
||||
const actual = await vi.importActual<typeof import('../utils/config')>('../utils/config');
|
||||
return {
|
||||
|
||||
@@ -2,6 +2,7 @@ import { useCallback, useEffect, useState } from 'react';
|
||||
import { useSearchParams } from 'react-router-dom';
|
||||
|
||||
import { ConfirmationModal } from '../components/intelligence/ConfirmationModal';
|
||||
import IntelligenceAgentsTab from '../components/intelligence/IntelligenceAgentsTab';
|
||||
import IntelligenceSubconsciousTab from '../components/intelligence/IntelligenceSubconsciousTab';
|
||||
import IntelligenceTasksTab from '../components/intelligence/IntelligenceTasksTab';
|
||||
import MemorySection from '../components/intelligence/MemorySection';
|
||||
@@ -21,12 +22,13 @@ import type {
|
||||
} from '../types/intelligence';
|
||||
import { IS_DEV } from '../utils/config';
|
||||
|
||||
type IntelligenceTab = 'memory' | 'subconscious' | 'tasks' | 'workflows' | 'council';
|
||||
type IntelligenceTab = 'memory' | 'subconscious' | 'tasks' | 'agents' | 'workflows' | 'council';
|
||||
|
||||
const INTELLIGENCE_TABS: IntelligenceTab[] = [
|
||||
'memory',
|
||||
'subconscious',
|
||||
'tasks',
|
||||
'agents',
|
||||
'workflows',
|
||||
'council',
|
||||
];
|
||||
@@ -134,13 +136,14 @@ export default function Intelligence() {
|
||||
description: t('memory.tab.workflowsDescription'),
|
||||
},
|
||||
{ id: 'council', label: t('memory.tab.council'), devOnly: true },
|
||||
{ id: 'agents', label: t('memory.tab.agents'), description: t('memory.tab.agentsDescription') },
|
||||
];
|
||||
const tabs = allTabs.filter(tab => !tab.devOnly || IS_DEV);
|
||||
const activeTabDef = tabs.find(tab => tab.id === activeTab);
|
||||
|
||||
return (
|
||||
<div className="min-h-full p-4 pt-6">
|
||||
<div className="max-w-2xl mx-auto space-y-4">
|
||||
<div className="max-w-4xl mx-auto space-y-4">
|
||||
<PillTabBar
|
||||
items={tabs.map(tab => ({ label: tab.label, value: tab.id }))}
|
||||
selected={activeTab}
|
||||
@@ -212,6 +215,8 @@ export default function Intelligence() {
|
||||
|
||||
{activeTab === 'tasks' && <IntelligenceTasksTab />}
|
||||
|
||||
{activeTab === 'agents' && <IntelligenceAgentsTab />}
|
||||
|
||||
{activeTab === 'workflows' && <WorkflowsTab />}
|
||||
|
||||
{activeTab === 'council' && <ModelCouncilTab />}
|
||||
|
||||
Reference in New Issue
Block a user