From 643c830e960195210dfd8027e13043b32cb14156 Mon Sep 17 00:00:00 2001
From: Cyrus Gray <144336577+graycyrus@users.noreply.github.com>
Date: Thu, 11 Jun 2026 17:43:37 +0530
Subject: [PATCH] =?UTF-8?q?refactor(settings):=20move=20Tasks=20tab=20from?=
=?UTF-8?q?=20Activity=20into=20Settings=20=E2=86=92=20Developer=20Options?=
=?UTF-8?q?=20(#3594)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../__tests__/useSettingsNavigation.test.tsx | 5 ++
.../settings/hooks/useSettingsNavigation.ts | 6 ++
.../settings/panels/DeveloperOptionsPanel.tsx | 16 ++++++
.../components/settings/panels/TasksPanel.tsx | 38 +++++++++++++
.../panels/__tests__/TasksPanel.test.tsx | 42 ++++++++++++++
app/src/lib/i18n/ar.ts | 3 +
app/src/lib/i18n/bn.ts | 3 +
app/src/lib/i18n/de.ts | 3 +
app/src/lib/i18n/en.ts | 3 +
app/src/lib/i18n/es.ts | 3 +
app/src/lib/i18n/fr.ts | 3 +
app/src/lib/i18n/hi.ts | 3 +
app/src/lib/i18n/id.ts | 3 +
app/src/lib/i18n/it.ts | 3 +
app/src/lib/i18n/ko.ts | 3 +
app/src/lib/i18n/pl.ts | 3 +
app/src/lib/i18n/pt.ts | 3 +
app/src/lib/i18n/ru.ts | 3 +
app/src/lib/i18n/zh-CN.ts | 3 +
app/src/pages/Activity.tsx | 22 +++----
app/src/pages/Settings.tsx | 2 +
app/src/pages/__tests__/Activity.test.tsx | 57 ++++++++-----------
22 files changed, 184 insertions(+), 46 deletions(-)
create mode 100644 app/src/components/settings/panels/TasksPanel.tsx
create mode 100644 app/src/components/settings/panels/__tests__/TasksPanel.test.tsx
diff --git a/app/src/components/settings/hooks/__tests__/useSettingsNavigation.test.tsx b/app/src/components/settings/hooks/__tests__/useSettingsNavigation.test.tsx
index 1a5c56d63..e2715e6a5 100644
--- a/app/src/components/settings/hooks/__tests__/useSettingsNavigation.test.tsx
+++ b/app/src/components/settings/hooks/__tests__/useSettingsNavigation.test.tsx
@@ -30,6 +30,11 @@ describe('useSettingsNavigation breadcrumbs', () => {
);
});
+ test('tasks returns Settings > Developer Options', () => {
+ renderWithProviders(, { initialEntries: ['/settings/tasks'] });
+ expect(screen.getByTestId('breadcrumbs')).toHaveTextContent('Settings > Developer Options');
+ });
+
test('developer-options returns Settings (section page)', () => {
renderWithProviders(, { initialEntries: ['/settings/developer-options'] });
expect(screen.getByTestId('breadcrumbs')).toHaveTextContent('Settings');
diff --git a/app/src/components/settings/hooks/useSettingsNavigation.ts b/app/src/components/settings/hooks/useSettingsNavigation.ts
index 2011b6c44..b6b511551 100644
--- a/app/src/components/settings/hooks/useSettingsNavigation.ts
+++ b/app/src/components/settings/hooks/useSettingsNavigation.ts
@@ -47,6 +47,7 @@ export type SettingsRoute =
| 'composio-triggers'
| 'composio-routing'
| 'task-sources'
+ | 'tasks'
| 'mcp-server'
| 'dev-workflow'
| 'sandbox-settings'
@@ -135,6 +136,10 @@ export const useSettingsNavigation = (): SettingsNavigationHook => {
if (path.includes('/settings/composio-triggers')) return 'composio-triggers';
if (path.includes('/settings/composio-routing')) return 'composio-routing';
if (path.includes('/settings/task-sources')) return 'task-sources';
+ // `tasks` is checked after `task-sources` so the longer, hyphenated route
+ // isn't shadowed (the two prefixes don't actually overlap, but ordering
+ // here keeps the intent obvious).
+ if (path.includes('/settings/tasks')) return 'tasks';
if (path.includes('/settings/intelligence')) return 'intelligence';
if (path.includes('/settings/crypto')) return 'crypto';
if (path.includes('/settings/recovery-phrase')) return 'recovery-phrase';
@@ -326,6 +331,7 @@ export const useSettingsNavigation = (): SettingsNavigationHook => {
case 'webhooks-triggers':
case 'composio-triggers':
case 'composio-routing':
+ case 'tasks':
case 'notification-routing':
case 'mcp-server':
case 'dev-workflow':
diff --git a/app/src/components/settings/panels/DeveloperOptionsPanel.tsx b/app/src/components/settings/panels/DeveloperOptionsPanel.tsx
index 0de78361c..55a333088 100644
--- a/app/src/components/settings/panels/DeveloperOptionsPanel.tsx
+++ b/app/src/components/settings/panels/DeveloperOptionsPanel.tsx
@@ -381,6 +381,22 @@ const modelsInferenceGroup: DevGroup = {
const automationIntegrationsGroup: DevGroup = {
labelKey: 'settings.devGroups.automationIntegrations',
items: [
+ {
+ id: 'tasks',
+ titleKey: 'settings.developerMenu.tasks.title',
+ descriptionKey: 'settings.developerMenu.tasks.desc',
+ route: 'tasks',
+ icon: (
+
+ ),
+ },
{
id: 'cron-jobs',
titleKey: 'settings.developerMenu.cronJobs.title',
diff --git a/app/src/components/settings/panels/TasksPanel.tsx b/app/src/components/settings/panels/TasksPanel.tsx
new file mode 100644
index 000000000..30e1c69c4
--- /dev/null
+++ b/app/src/components/settings/panels/TasksPanel.tsx
@@ -0,0 +1,38 @@
+import { useT } from '../../../lib/i18n/I18nContext';
+import IntelligenceTasksTab from '../../intelligence/IntelligenceTasksTab';
+import SettingsHeader from '../components/SettingsHeader';
+import { useSettingsNavigation } from '../hooks/useSettingsNavigation';
+
+/**
+ * Settings → Developer Options → Tasks.
+ *
+ * Hosts the {@link IntelligenceTasksTab} task-board surface that previously
+ * lived as a tab on the Activity page. The board (personal to-dos, task-source
+ * boards, and the per-agent boards built across conversations) is unchanged —
+ * this panel only re-homes it under the developer menu with the standard
+ * SettingsHeader + breadcrumb chrome.
+ */
+const TasksPanel = () => {
+ const { t } = useT();
+ const { navigateBack, breadcrumbs } = useSettingsNavigation();
+
+ return (
+
+
+
+
+
+ {t('memory.tab.tasksDescription')}
+
+
+
+
+ );
+};
+
+export default TasksPanel;
diff --git a/app/src/components/settings/panels/__tests__/TasksPanel.test.tsx b/app/src/components/settings/panels/__tests__/TasksPanel.test.tsx
new file mode 100644
index 000000000..401420650
--- /dev/null
+++ b/app/src/components/settings/panels/__tests__/TasksPanel.test.tsx
@@ -0,0 +1,42 @@
+import { render, screen } from '@testing-library/react';
+import { MemoryRouter } from 'react-router-dom';
+import { describe, expect, it, vi } from 'vitest';
+
+// i18n stub — return the key verbatim so we can assert on label keys.
+vi.mock('../../../../lib/i18n/I18nContext', () => ({ useT: () => ({ t: (key: string) => key }) }));
+
+// The task-board surface is heavy (Redux + RPC); stub it to a sentinel so the
+// panel test stays focused on the re-homing chrome.
+vi.mock('../../../intelligence/IntelligenceTasksTab', () => ({
+ default: () => ,
+}));
+
+const navigateBack = vi.fn();
+vi.mock('../../hooks/useSettingsNavigation', () => ({
+ useSettingsNavigation: () => ({
+ navigateBack,
+ breadcrumbs: [{ label: 'Settings' }, { label: 'Developer Options' }],
+ }),
+}));
+
+const TasksPanel = (await import('../TasksPanel')).default;
+
+function renderPanel() {
+ return render(
+
+
+
+ );
+}
+
+describe('TasksPanel', () => {
+ it('renders the panel shell, title, description and the task board', () => {
+ renderPanel();
+ expect(screen.getByTestId('tasks-panel')).toBeInTheDocument();
+ // SettingsHeader title + the descriptive blurb both come from i18n keys.
+ expect(screen.getAllByText('memory.tab.tasks').length).toBeGreaterThan(0);
+ expect(screen.getByText('memory.tab.tasksDescription')).toBeInTheDocument();
+ // The re-homed task board is mounted unchanged.
+ expect(screen.getByTestId('intelligence-tasks-tab')).toBeInTheDocument();
+ });
+});
diff --git a/app/src/lib/i18n/ar.ts b/app/src/lib/i18n/ar.ts
index 0984fcd30..875d69882 100644
--- a/app/src/lib/i18n/ar.ts
+++ b/app/src/lib/i18n/ar.ts
@@ -3838,6 +3838,9 @@ const messages: TranslationMap = {
'settings.devWorkflow.schedule.every2hours': 'كل ساعتين',
'settings.devWorkflow.schedule.every6hours': 'كل 6 ساعات',
'settings.devWorkflow.schedule.onceDaily': 'يوم واحد (9 صباحا)',
+ 'settings.developerMenu.tasks.title': 'المهام',
+ 'settings.developerMenu.tasks.desc':
+ 'تصفّح لوحات المهام وإدارتها — مهامك الخاصة إضافةً إلى اللوحات التي تنشئها الوكلاء عبر المحادثات.',
'settings.developerMenu.cronJobs.title': 'مهام Cron',
'settings.developerMenu.cronJobs.desc': 'عرض وتكوين المهام المجدولة لمهارات وقت التشغيل',
'settings.developerMenu.localModelDebug.title': 'تصحيح النموذج المحلي',
diff --git a/app/src/lib/i18n/bn.ts b/app/src/lib/i18n/bn.ts
index 33be093a8..bd63551e6 100644
--- a/app/src/lib/i18n/bn.ts
+++ b/app/src/lib/i18n/bn.ts
@@ -3914,6 +3914,9 @@ const messages: TranslationMap = {
'settings.devWorkflow.schedule.every2hours': 'প্রতি ২ ঘন্টা',
'settings.devWorkflow.schedule.every6hours': 'প্রতি ৬ ঘন্টা',
'settings.devWorkflow.schedule.onceDaily': 'প্রতিদিন (৯.',
+ 'settings.developerMenu.tasks.title': 'কাজ',
+ 'settings.developerMenu.tasks.desc':
+ 'টাস্ক বোর্ড ব্রাউজ ও পরিচালনা করুন — আপনার নিজের কাজ এবং কথোপকথন জুড়ে এজেন্টদের তৈরি করা বোর্ড।',
'settings.developerMenu.cronJobs.title': 'Cron জব',
'settings.developerMenu.cronJobs.desc':
'রানটাইম স্কিলের জন্য নির্ধারিত জব দেখুন এবং কনফিগার করুন',
diff --git a/app/src/lib/i18n/de.ts b/app/src/lib/i18n/de.ts
index f185f38b5..635683f56 100644
--- a/app/src/lib/i18n/de.ts
+++ b/app/src/lib/i18n/de.ts
@@ -4014,6 +4014,9 @@ const messages: TranslationMap = {
'settings.devWorkflow.schedule.every2hours': 'Alle 2 Stunden',
'settings.devWorkflow.schedule.every6hours': 'Alle 6 Stunden',
'settings.devWorkflow.schedule.onceDaily': 'Einmal täglich (09:00 Uhr)',
+ 'settings.developerMenu.tasks.title': 'Aufgaben',
+ 'settings.developerMenu.tasks.desc':
+ 'Aufgaben-Boards durchsuchen und verwalten — deine eigenen To-dos sowie die Boards, die Agenten über Unterhaltungen hinweg erstellen.',
'settings.developerMenu.cronJobs.title': 'Cron-Jobs',
'settings.developerMenu.cronJobs.desc':
'Zeige geplante Jobs für Laufzeitfähigkeiten an und konfiguriere sie',
diff --git a/app/src/lib/i18n/en.ts b/app/src/lib/i18n/en.ts
index bd9020fe7..b0ed65106 100644
--- a/app/src/lib/i18n/en.ts
+++ b/app/src/lib/i18n/en.ts
@@ -4457,6 +4457,9 @@ const en: TranslationMap = {
'settings.devWorkflow.schedule.every2hours': 'Every 2 hours',
'settings.devWorkflow.schedule.every6hours': 'Every 6 hours',
'settings.devWorkflow.schedule.onceDaily': 'Once daily (9 AM)',
+ 'settings.developerMenu.tasks.title': 'Tasks',
+ 'settings.developerMenu.tasks.desc':
+ 'Browse and manage task boards — your own to-dos plus the boards agents build across conversations.',
'settings.developerMenu.cronJobs.title': 'Cron Jobs',
'settings.developerMenu.cronJobs.desc': 'View and configure scheduled jobs for runtime skills',
'settings.developerMenu.localModelDebug.title': 'Local Model Debug',
diff --git a/app/src/lib/i18n/es.ts b/app/src/lib/i18n/es.ts
index 3f7b176a1..03183cc83 100644
--- a/app/src/lib/i18n/es.ts
+++ b/app/src/lib/i18n/es.ts
@@ -3988,6 +3988,9 @@ const messages: TranslationMap = {
'settings.devWorkflow.schedule.every2hours': 'Cada 2 horas',
'settings.devWorkflow.schedule.every6hours': 'Cada 6 horas',
'settings.devWorkflow.schedule.onceDaily': 'Una vez al día (9 AM)',
+ 'settings.developerMenu.tasks.title': 'Tareas',
+ 'settings.developerMenu.tasks.desc':
+ 'Explora y gestiona los tableros de tareas: tus pendientes y los tableros que los agentes crean en las conversaciones.',
'settings.developerMenu.cronJobs.title': 'Tareas cron',
'settings.developerMenu.cronJobs.desc':
'Ver y configurar tareas programadas para habilidades en tiempo de ejecución',
diff --git a/app/src/lib/i18n/fr.ts b/app/src/lib/i18n/fr.ts
index 1eba2a393..0bc68b560 100644
--- a/app/src/lib/i18n/fr.ts
+++ b/app/src/lib/i18n/fr.ts
@@ -4004,6 +4004,9 @@ const messages: TranslationMap = {
'settings.devWorkflow.schedule.every2hours': 'Toutes les 2 heures',
'settings.devWorkflow.schedule.every6hours': 'Toutes les 6 heures',
'settings.devWorkflow.schedule.onceDaily': 'Une fois par jour (9 h)',
+ 'settings.developerMenu.tasks.title': 'Tâches',
+ 'settings.developerMenu.tasks.desc':
+ 'Parcourez et gérez les tableaux de tâches — vos propres to-dos ainsi que les tableaux créés par les agents au fil des conversations.',
'settings.developerMenu.cronJobs.title': 'Tâches cron',
'settings.developerMenu.cronJobs.desc':
"Afficher et configurer les tâches planifiées des compétences d'exécution",
diff --git a/app/src/lib/i18n/hi.ts b/app/src/lib/i18n/hi.ts
index f2680e583..0d8ccaed1 100644
--- a/app/src/lib/i18n/hi.ts
+++ b/app/src/lib/i18n/hi.ts
@@ -3921,6 +3921,9 @@ const messages: TranslationMap = {
'settings.devWorkflow.schedule.every2hours': 'हर 2 घंटे',
'settings.devWorkflow.schedule.every6hours': '6 घंटे',
'settings.devWorkflow.schedule.onceDaily': 'एक बार दैनिक (9 AM)',
+ 'settings.developerMenu.tasks.title': 'कार्य',
+ 'settings.developerMenu.tasks.desc':
+ 'टास्क बोर्ड ब्राउज़ करें और प्रबंधित करें — आपके अपने काम और बातचीत के दौरान एजेंट द्वारा बनाए गए बोर्ड।',
'settings.developerMenu.cronJobs.title': 'Cron जॉब्स',
'settings.developerMenu.cronJobs.desc':
'रनटाइम स्किल्स के लिए शेड्यूल किए गए जॉब देखें और कॉन्फ़िगर करें',
diff --git a/app/src/lib/i18n/id.ts b/app/src/lib/i18n/id.ts
index 933098a95..06e6faafb 100644
--- a/app/src/lib/i18n/id.ts
+++ b/app/src/lib/i18n/id.ts
@@ -3932,6 +3932,9 @@ const messages: TranslationMap = {
'settings.devWorkflow.schedule.every2hours': 'Setiap 2 jam',
'settings.devWorkflow.schedule.every6hours': 'Setiap 6 jam',
'settings.devWorkflow.schedule.onceDaily': 'Setiap hari (jam 9 pagi)',
+ 'settings.developerMenu.tasks.title': 'Tugas',
+ 'settings.developerMenu.tasks.desc':
+ 'Jelajahi dan kelola papan tugas — daftar tugas Anda sendiri serta papan yang dibuat agen di seluruh percakapan.',
'settings.developerMenu.cronJobs.title': 'Pekerjaan Cron',
'settings.developerMenu.cronJobs.desc': 'Lihat dan atur pekerjaan terjadwal untuk skill runtime',
'settings.developerMenu.localModelDebug.title': 'Debug Model Lokal',
diff --git a/app/src/lib/i18n/it.ts b/app/src/lib/i18n/it.ts
index 505e30495..c9f710ac9 100644
--- a/app/src/lib/i18n/it.ts
+++ b/app/src/lib/i18n/it.ts
@@ -3982,6 +3982,9 @@ const messages: TranslationMap = {
'settings.devWorkflow.schedule.every2hours': 'Ogni 2 ore',
'settings.devWorkflow.schedule.every6hours': 'Ogni 6 ore',
'settings.devWorkflow.schedule.onceDaily': 'Una volta al giorno (ore 9)',
+ 'settings.developerMenu.tasks.title': 'Attività',
+ 'settings.developerMenu.tasks.desc':
+ 'Esplora e gestisci le bacheche delle attività — i tuoi to-do e le bacheche create dagli agenti nelle conversazioni.',
'settings.developerMenu.cronJobs.title': 'Processi cron',
'settings.developerMenu.cronJobs.desc':
'Visualizza e configura processi pianificati per le skill di runtime',
diff --git a/app/src/lib/i18n/ko.ts b/app/src/lib/i18n/ko.ts
index 0dcec2d23..13e43fd06 100644
--- a/app/src/lib/i18n/ko.ts
+++ b/app/src/lib/i18n/ko.ts
@@ -3881,6 +3881,9 @@ const messages: TranslationMap = {
'settings.devWorkflow.schedule.every2hours': '2시간마다',
'settings.devWorkflow.schedule.every6hours': '6시간마다',
'settings.devWorkflow.schedule.onceDaily': '하루 한 번(오전 9시)',
+ 'settings.developerMenu.tasks.title': '작업',
+ 'settings.developerMenu.tasks.desc':
+ '작업 보드를 둘러보고 관리하세요 — 내 할 일과 대화 전반에서 에이전트가 만든 보드를 함께 확인합니다.',
'settings.developerMenu.cronJobs.title': '크론 작업',
'settings.developerMenu.cronJobs.desc': '예약 보기 및 구성 런타임 기술용 작업',
'settings.developerMenu.localModelDebug.title': '로컬 모델 디버그',
diff --git a/app/src/lib/i18n/pl.ts b/app/src/lib/i18n/pl.ts
index be180eb20..fbdb95734 100644
--- a/app/src/lib/i18n/pl.ts
+++ b/app/src/lib/i18n/pl.ts
@@ -3980,6 +3980,9 @@ const messages: TranslationMap = {
'settings.devWorkflow.schedule.every2hours': 'Co 2 godziny',
'settings.devWorkflow.schedule.every6hours': 'Co 6 godzin',
'settings.devWorkflow.schedule.onceDaily': 'Raz dziennie (9:00)',
+ 'settings.developerMenu.tasks.title': 'Zadania',
+ 'settings.developerMenu.tasks.desc':
+ 'Przeglądaj tablice zadań i zarządzaj nimi — Twoje własne zadania oraz tablice tworzone przez agentów w rozmowach.',
'settings.developerMenu.cronJobs.title': 'Zadania cron',
'settings.developerMenu.cronJobs.desc':
'Przeglądaj i konfiguruj zaplanowane zadania dla umiejętności runtime',
diff --git a/app/src/lib/i18n/pt.ts b/app/src/lib/i18n/pt.ts
index 8bf34e9a3..53216707c 100644
--- a/app/src/lib/i18n/pt.ts
+++ b/app/src/lib/i18n/pt.ts
@@ -3982,6 +3982,9 @@ const messages: TranslationMap = {
'settings.devWorkflow.schedule.every2hours': 'A cada 2 horas',
'settings.devWorkflow.schedule.every6hours': 'A cada 6 horas',
'settings.devWorkflow.schedule.onceDaily': 'Uma vez ao dia (9h)',
+ 'settings.developerMenu.tasks.title': 'Tarefas',
+ 'settings.developerMenu.tasks.desc':
+ 'Navegue e gerencie os quadros de tarefas — suas próprias pendências e os quadros que os agentes criam nas conversas.',
'settings.developerMenu.cronJobs.title': 'Tarefas cron',
'settings.developerMenu.cronJobs.desc':
'Veja e configure tarefas agendadas para habilidades em tempo de execução',
diff --git a/app/src/lib/i18n/ru.ts b/app/src/lib/i18n/ru.ts
index ed3f9894e..d9bcc3214 100644
--- a/app/src/lib/i18n/ru.ts
+++ b/app/src/lib/i18n/ru.ts
@@ -3952,6 +3952,9 @@ const messages: TranslationMap = {
'settings.devWorkflow.schedule.every2hours': 'Каждые 2 часа',
'settings.devWorkflow.schedule.every6hours': 'Каждые 6 часов',
'settings.devWorkflow.schedule.onceDaily': 'Один раз в день (9 утра)',
+ 'settings.developerMenu.tasks.title': 'Задачи',
+ 'settings.developerMenu.tasks.desc':
+ 'Просматривайте доски задач и управляйте ими — ваши собственные дела и доски, которые агенты создают в разговорах.',
'settings.developerMenu.cronJobs.title': 'Задачи cron',
'settings.developerMenu.cronJobs.desc':
'Просмотр и настройка запланированных задач для runtime-навыков',
diff --git a/app/src/lib/i18n/zh-CN.ts b/app/src/lib/i18n/zh-CN.ts
index e63a0d543..71d3b08c3 100644
--- a/app/src/lib/i18n/zh-CN.ts
+++ b/app/src/lib/i18n/zh-CN.ts
@@ -3731,6 +3731,9 @@ const messages: TranslationMap = {
'settings.devWorkflow.schedule.every2hours': '每 2 小时',
'settings.devWorkflow.schedule.every6hours': '每 6 小时',
'settings.devWorkflow.schedule.onceDaily': '每天一次(上午 9 点)',
+ 'settings.developerMenu.tasks.title': '任务',
+ 'settings.developerMenu.tasks.desc':
+ '浏览和管理任务看板——你的待办事项以及智能体在对话中创建的看板。',
'settings.developerMenu.cronJobs.title': '定时任务',
'settings.developerMenu.cronJobs.desc': '查看并配置运行时技能的计划任务',
'settings.developerMenu.localModelDebug.title': '本地模型调试',
diff --git a/app/src/pages/Activity.tsx b/app/src/pages/Activity.tsx
index aaa5e7b08..459f8cf05 100644
--- a/app/src/pages/Activity.tsx
+++ b/app/src/pages/Activity.tsx
@@ -3,7 +3,6 @@ import { useSearchParams } from 'react-router-dom';
import { ConfirmationModal } from '../components/intelligence/ConfirmationModal';
import IntelligenceSubconsciousTab from '../components/intelligence/IntelligenceSubconsciousTab';
-import IntelligenceTasksTab from '../components/intelligence/IntelligenceTasksTab';
import { ToastContainer } from '../components/intelligence/Toast';
import WorkflowsTab from '../components/intelligence/WorkflowsTab';
import PillTabBar from '../components/PillTabBar';
@@ -20,18 +19,18 @@ import type {
import Notifications from './Notifications';
// Visible tab IDs for the Activity surface.
-// memory, agents, and council have moved to Settings → Developer & Diagnostics
-// (routes: /settings/intelligence, /settings/agents).
-// Back-compat: ?tab=memory / ?tab=agents / ?tab=council are unknown to the
-// visible set and therefore fall back to 'tasks' (see makeIsVisibleTab below).
-type ActivityTab = 'tasks' | 'automations' | 'backgroundActivity' | 'alerts';
+// memory, agents, council and tasks have moved to Settings → Developer & Diagnostics
+// (routes: /settings/intelligence, /settings/agents, /settings/tasks).
+// Back-compat: ?tab=memory / ?tab=agents / ?tab=council / ?tab=tasks are unknown
+// to the visible set and therefore fall back to 'automations' (see isVisibleTab).
+type ActivityTab = 'automations' | 'backgroundActivity' | 'alerts';
-const ACTIVITY_TABS: ActivityTab[] = ['tasks', 'automations', 'backgroundActivity', 'alerts'];
+const ACTIVITY_TABS: ActivityTab[] = ['automations', 'backgroundActivity', 'alerts'];
/**
* Returns a type-guard predicate for the currently visible tabs.
- * Unknown values (including old deep-link tabs like ?tab=memory) fall back to
- * the default tab rather than erroring.
+ * Unknown values (including old deep-link tabs like ?tab=memory or ?tab=tasks)
+ * fall back to the default tab rather than erroring.
*/
const isVisibleTab = (tab: string | null | undefined): tab is ActivityTab =>
(ACTIVITY_TABS as string[]).includes(tab ?? '');
@@ -43,7 +42,7 @@ export default function Activity() {
// restores the same tab. `replace` so switching tabs doesn't stack history.
const [searchParams, setSearchParams] = useSearchParams();
const tabParam = searchParams.get('tab');
- const activeTab: ActivityTab = isVisibleTab(tabParam) ? tabParam : 'tasks';
+ const activeTab: ActivityTab = isVisibleTab(tabParam) ? tabParam : 'automations';
const setActiveTab = useCallback(
(tab: ActivityTab) => {
setSearchParams(
@@ -95,7 +94,6 @@ export default function Activity() {
}, [socketConnected, socketManager]);
const tabs: { id: ActivityTab; label: string; description?: string; comingSoon?: boolean }[] = [
- { id: 'tasks', label: t('memory.tab.tasks'), description: t('memory.tab.tasksDescription') },
{
id: 'automations',
label: t('activity.tabs.automations'),
@@ -159,8 +157,6 @@ export default function Activity() {
{/* Tab content */}
- {activeTab === 'tasks' && }
-
{activeTab === 'automations' && }
{activeTab === 'backgroundActivity' && (
diff --git a/app/src/pages/Settings.tsx b/app/src/pages/Settings.tsx
index 8ed6fc491..016356c8b 100644
--- a/app/src/pages/Settings.tsx
+++ b/app/src/pages/Settings.tsx
@@ -47,6 +47,7 @@ import ScreenIntelligencePanel from '../components/settings/panels/ScreenIntelli
import SearchPanel from '../components/settings/panels/SearchPanel';
import SecurityPanel from '../components/settings/panels/SecurityPanel';
import TaskSourcesPanel from '../components/settings/panels/TaskSourcesPanel';
+import TasksPanel from '../components/settings/panels/TasksPanel';
import TeamInvitesPanel from '../components/settings/panels/TeamInvitesPanel';
import TeamManagementPanel from '../components/settings/panels/TeamManagementPanel';
import TeamMembersPanel from '../components/settings/panels/TeamMembersPanel';
@@ -634,6 +635,7 @@ const Settings = () => {
)} />
)} />
)} />
+ )} />
)} />
)} />
({ useT: () => ({ t: (key: string) =
vi.mock('../../components/intelligence/IntelligenceSubconsciousTab', () => ({
default: () => ,
}));
-vi.mock('../../components/intelligence/IntelligenceTasksTab', () => ({
- default: () => ,
-}));
vi.mock('../../components/intelligence/WorkflowsTab', () => ({
default: () => ,
}));
@@ -63,14 +60,9 @@ describe('Activity URL-backed tab', () => {
vi.clearAllMocks();
});
- it('defaults to the tasks tab when no ?tab is present', async () => {
+ it('defaults to the automations tab when no ?tab is present', async () => {
renderAt('/activity');
- await waitFor(() => expect(screen.getByTestId('tab-tasks')).toBeInTheDocument());
- });
-
- it('honours ?tab=tasks from the URL', async () => {
- renderAt('/activity?tab=tasks');
- await waitFor(() => expect(screen.getByTestId('tab-tasks')).toBeInTheDocument());
+ await waitFor(() => expect(screen.getByTestId('tab-automations')).toBeInTheDocument());
});
it('honours ?tab=automations from the URL', async () => {
@@ -88,28 +80,34 @@ describe('Activity URL-backed tab', () => {
await waitFor(() => expect(screen.getByTestId('tab-alerts')).toBeInTheDocument());
});
- it('falls back to tasks for an unknown ?tab value', async () => {
+ it('falls back to automations for an unknown ?tab value', async () => {
renderAt('/activity?tab=bogus');
- await waitFor(() => expect(screen.getByTestId('tab-tasks')).toBeInTheDocument());
+ await waitFor(() => expect(screen.getByTestId('tab-automations')).toBeInTheDocument());
});
- // Back-compat: old deep links (?tab=memory|agents|council) are no longer
- // visible Activity tabs — they should fall back to tasks rather than error.
- it('falls back to tasks for ?tab=memory (relocated to Settings)', async () => {
+ // Back-compat: old deep links (?tab=tasks|memory|agents|council) are no longer
+ // visible Activity tabs — they should fall back to automations rather than error.
+ it('falls back to automations for ?tab=tasks (relocated to Settings → Developer Options)', async () => {
+ renderAt('/activity?tab=tasks');
+ await waitFor(() => expect(screen.getByTestId('tab-automations')).toBeInTheDocument());
+ expect(screen.queryByTestId('tab-tasks')).not.toBeInTheDocument();
+ });
+
+ it('falls back to automations for ?tab=memory (relocated to Settings)', async () => {
renderAt('/activity?tab=memory');
- await waitFor(() => expect(screen.getByTestId('tab-tasks')).toBeInTheDocument());
+ await waitFor(() => expect(screen.getByTestId('tab-automations')).toBeInTheDocument());
expect(screen.queryByTestId('tab-memory')).not.toBeInTheDocument();
});
- it('falls back to tasks for ?tab=agents (relocated to Settings)', async () => {
+ it('falls back to automations for ?tab=agents (relocated to Settings)', async () => {
renderAt('/activity?tab=agents');
- await waitFor(() => expect(screen.getByTestId('tab-tasks')).toBeInTheDocument());
+ await waitFor(() => expect(screen.getByTestId('tab-automations')).toBeInTheDocument());
expect(screen.queryByTestId('tab-agents')).not.toBeInTheDocument();
});
- it('falls back to tasks for ?tab=council (relocated to Settings)', async () => {
+ it('falls back to automations for ?tab=council (relocated to Settings)', async () => {
renderAt('/activity?tab=council');
- await waitFor(() => expect(screen.getByTestId('tab-tasks')).toBeInTheDocument());
+ await waitFor(() => expect(screen.getByTestId('tab-automations')).toBeInTheDocument());
expect(screen.queryByTestId('tab-council')).not.toBeInTheDocument();
});
@@ -126,31 +124,24 @@ describe('Activity tab — tab set', () => {
vi.clearAllMocks();
});
- it('renders exactly four tab pills: tasks, automations, backgroundActivity, alerts', async () => {
+ it('renders exactly three tab pills: automations, backgroundActivity, alerts', async () => {
renderAt('/activity');
- await waitFor(() => screen.getByTestId('tab-tasks'));
+ await waitFor(() => screen.getByTestId('tab-automations'));
// Each label key is returned as-is by the stub t() function.
- expect(screen.getAllByText('memory.tab.tasks').length).toBeGreaterThan(0);
expect(screen.getAllByText('activity.tabs.automations').length).toBeGreaterThan(0);
expect(screen.getAllByText('activity.tabs.backgroundActivity').length).toBeGreaterThan(0);
expect(screen.getAllByText('activity.tabs.alerts').length).toBeGreaterThan(0);
});
- it('does not render memory, agents, or council pills', async () => {
+ it('does not render tasks, memory, agents, or council pills', async () => {
renderAt('/activity');
- await waitFor(() => screen.getByTestId('tab-tasks'));
+ await waitFor(() => screen.getByTestId('tab-automations'));
+ expect(screen.queryByText('memory.tab.tasks')).not.toBeInTheDocument();
expect(screen.queryByText('memory.tab.memory')).not.toBeInTheDocument();
expect(screen.queryByText('memory.tab.agents')).not.toBeInTheDocument();
expect(screen.queryByText('memory.tab.council')).not.toBeInTheDocument();
});
- it('clicking the automations pill switches to the automations tab', async () => {
- renderAt('/activity');
- await waitFor(() => screen.getAllByText('activity.tabs.automations'));
- fireEvent.click(screen.getAllByText('activity.tabs.automations')[0]);
- await waitFor(() => expect(screen.getByTestId('tab-automations')).toBeInTheDocument());
- });
-
it('clicking the backgroundActivity pill switches to the backgroundActivity tab', async () => {
renderAt('/activity');
await waitFor(() => screen.getAllByText('activity.tabs.backgroundActivity'));
@@ -169,6 +160,6 @@ describe('Activity tab — tab set', () => {
renderAt('/activity?tab=alerts');
await waitFor(() => expect(screen.getByTestId('tab-alerts')).toBeInTheDocument());
// The card wrapper is NOT rendered in the alerts tab.
- expect(screen.queryByTestId('tab-tasks')).not.toBeInTheDocument();
+ expect(screen.queryByTestId('tab-automations')).not.toBeInTheDocument();
});
});