feat(ui): move agents library to Intelligence tab (#3438)

This commit is contained in:
Steven Enamakel
2026-06-06 14:44:57 -04:00
committed by GitHub
parent 0ac6e6a5bc
commit 7fa3c215ea
20 changed files with 359 additions and 143 deletions
@@ -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(
+4 -1
View File
@@ -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.',
+4 -1
View File
@@ -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.',
+4 -1
View File
@@ -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.',
+4 -1
View File
@@ -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.',
+4 -1
View File
@@ -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.',
+4 -1
View File
@@ -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.',
+4 -1
View File
@@ -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.',
+4 -1
View File
@@ -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.',
+4 -1
View File
@@ -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.',
+4 -1
View File
@@ -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.',
+5 -2
View File
@@ -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':
+4 -1
View File
@@ -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.',
+4 -1
View File
@@ -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.',
+4 -1
View File
@@ -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.',
+1 -1
View File
@@ -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 {
+7 -2
View File
@@ -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 />}