diff --git a/apps/desktop/src/app/session/hooks/use-session-list-actions.ts b/apps/desktop/src/app/session/hooks/use-session-list-actions.ts index 611484e0d7..d7ab9ea091 100644 --- a/apps/desktop/src/app/session/hooks/use-session-list-actions.ts +++ b/apps/desktop/src/app/session/hooks/use-session-list-actions.ts @@ -29,12 +29,13 @@ import { } from '@/store/session' import { $workingSessionIds, getRecentlySettledSessionIds } from '@/store/session-states' -// The recents list is local-only: cron rows have their own section, and each -// messaging platform (telegram, discord, …) is fetched separately into its own -// self-managed sidebar section (refreshMessagingSessions). Excluding both here -// keeps "Load more" paging through interactive local chats instead of +// The recents list is local-only: cron rows have their own section, kanban +// dispatcher workers are read on the board, and each messaging platform +// (telegram, discord, …) is fetched separately into its own self-managed +// sidebar section (refreshMessagingSessions). Excluding them here keeps +// "Load more" paging through interactive local chats instead of // interleaving gateway threads that bury them. -const SIDEBAR_EXCLUDED_SOURCES = ['cron', 'subagent', 'tool', ...MESSAGING_SESSION_SOURCE_IDS] +const SIDEBAR_EXCLUDED_SOURCES = ['cron', 'kanban', 'subagent', 'tool', ...MESSAGING_SESSION_SOURCE_IDS] // The messaging slice is the inverse: drop cron + every local source so only // external-platform conversations remain, then split per platform in the UI. const MESSAGING_EXCLUDED_SOURCES = ['cron', ...LOCAL_SESSION_SOURCE_IDS] diff --git a/apps/desktop/src/lib/session-source.ts b/apps/desktop/src/lib/session-source.ts index 6958c62611..38fc60cded 100644 --- a/apps/desktop/src/lib/session-source.ts +++ b/apps/desktop/src/lib/session-source.ts @@ -9,6 +9,7 @@ const SOURCE_LABELS: Record = { discord: 'Discord', email: 'Email', gateway: 'Gateway', + kanban: 'Kanban', local: 'Local', matrix: 'Matrix', mattermost: 'Mattermost', @@ -42,7 +43,7 @@ const SOURCE_ALIASES: Record = { // platform. A handoff *from* one of these isn't a platform origin worth a badge. // Exported so the recents fetch can keep these in the main list while the // messaging fetch excludes them. -export const LOCAL_SESSION_SOURCE_IDS = ['cli', 'codex', 'desktop', 'gateway', 'local', 'tui'] +export const LOCAL_SESSION_SOURCE_IDS = ['cli', 'codex', 'desktop', 'gateway', 'kanban', 'local', 'tui'] const LOCAL_SOURCE_IDS = new Set(LOCAL_SESSION_SOURCE_IDS) // External messaging platforms that each get their own self-managed sidebar diff --git a/cli.py b/cli.py index 5b4bef350b..2b86f97458 100644 --- a/cli.py +++ b/cli.py @@ -7712,7 +7712,7 @@ class HermesCLI(CLIAgentSetupMixin, CLICommandsMixin, CLIBillingMixin): include_all_sources=False, include_unnamed=True, limit=limit, - exclude_sources=["tool"], + exclude_sources=["kanban", "tool"], ) except Exception: return [] diff --git a/hermes_cli/console_engine.py b/hermes_cli/console_engine.py index 4b1eddbaf8..50a4592fc0 100644 --- a/hermes_cli/console_engine.py +++ b/hermes_cli/console_engine.py @@ -1324,7 +1324,7 @@ def _sessions_list(_engine: HermesConsoleEngine, args: list[str]) -> str: db = SessionDB() try: sessions = db.list_sessions_rich( - exclude_sources=["tool"], + exclude_sources=["kanban", "tool"], limit=ns.limit, order_by_last_active=True, ) @@ -1340,7 +1340,7 @@ def _sessions_stats(_engine: HermesConsoleEngine, args: list[str]) -> str: db = SessionDB() try: total = db.session_count() - listable = db.session_count(exclude_children=True, exclude_sources=["tool"]) + listable = db.session_count(exclude_children=True, exclude_sources=["kanban", "tool"]) messages = db.message_count() lines = [ f"Total sessions: {total}", diff --git a/tools/session_search_tool.py b/tools/session_search_tool.py index ed9fd49502..be063a83e4 100644 --- a/tools/session_search_tool.py +++ b/tools/session_search_tool.py @@ -35,9 +35,9 @@ from typing import Any, Dict, List, Optional, Union # Sources that are excluded from session browsing/searching by default. # Third-party integrations tag their sessions with HERMES_SESSION_SOURCE=tool; -# delegate subagent runs are tagged "subagent" — neither belongs in the -# user's session history. -_HIDDEN_SESSION_SOURCES = ("subagent", "tool") +# delegate subagent runs are tagged "subagent"; kanban dispatcher workers are +# tagged "kanban" — none belongs in the user's session history. +_HIDDEN_SESSION_SOURCES = ("kanban", "subagent", "tool") # Automation sources that are kept searchable but DEMOTED below interactive # sessions in discover ranking. Cron jobs run on a schedule and accumulate diff --git a/tui_gateway/methods_session.py b/tui_gateway/methods_session.py index 5b00a42a51..68210a4c8a 100644 --- a/tui_gateway/methods_session.py +++ b/tui_gateway/methods_session.py @@ -170,10 +170,11 @@ def _(rid, params: dict) -> dict: # ones not enumerated here), ACP adapter clients, webhook sessions, # custom `HERMES_SESSION_SOURCE` values, and older installs with # different source labels. We deny-list only the noisy internal - # sources (``tool`` sub-agent runs) rather than allow-listing a - # fixed set of platform names that goes stale whenever a new - # platform is added or a user names their own source. - deny = frozenset({"tool"}) + # sources (``tool`` sub-agent runs and ``kanban`` dispatcher + # workers) rather than allow-listing a fixed set of platform names + # that goes stale whenever a new platform is added or a user names + # their own source. + deny = frozenset({"kanban", "tool"}) limit = int(params.get("limit", 200) or 200) # Over-fetch modestly so per-source filtering doesn't leave us @@ -215,7 +216,7 @@ def _(rid, params: dict) -> dict: """Return the most recent human-facing session id, or ``None``. Mirrors ``session.list``'s deny-list behaviour (drops ``tool`` - sub-agent rows). Used by TUI auto-resume when + sub-agent rows and ``kanban`` worker rows). Used by TUI auto-resume when ``display.tui_auto_resume_recent`` is on; the field is also handy for any CLI tooling that wants "latest session" without paginating the full list. @@ -232,7 +233,7 @@ def _(rid, params: dict) -> dict: if db is None: return _ok(rid, {"session_id": None}) try: - deny = frozenset({"tool"}) + deny = frozenset({"kanban", "tool"}) # Over-fetch by a generous bounded amount so heavy sub-agent # users (lots of recent ``tool`` rows) don't get a false # "no eligible session" answer. ``session.list`` uses a diff --git a/tui_gateway/server.py b/tui_gateway/server.py index ca5876ac00..2ecb4c3ed9 100644 --- a/tui_gateway/server.py +++ b/tui_gateway/server.py @@ -618,7 +618,7 @@ def _transfer_active_session_slot( # TUI backend itself creates ("tui", plus whatever a client passes as its # own ``source``) and the CLI's own sessions are NOT gateway-owned. _NON_GATEWAY_SOURCES = frozenset({ - "", "tui", "cli", "webui", "desktop", "cron", "subagent", "test", + "", "tui", "cli", "webui", "desktop", "cron", "kanban", "subagent", "test", "local", "acp", "webhook", "api_server", "msgraph_webhook", }) @@ -11033,10 +11033,11 @@ def _discover_repos_payload( return out -# Sources excluded from the project tree: cron runs and tool/subagent children -# are not user conversations. Subagent/compression children are already dropped -# by list_sessions_rich(include_children=False); cron has its own section. -_PROJECT_TREE_EXCLUDED_SOURCES = ["cron"] +# Sources excluded from the project tree: cron runs, and kanban dispatcher +# workers, are not user conversations. Subagent/compression children are +# already dropped by list_sessions_rich(include_children=False); cron has its +# own section, and kanban runs are read on the board. +_PROJECT_TREE_EXCLUDED_SOURCES = ["cron", "kanban"] def _project_tree_row(r: dict) -> dict: