From c6a41018b6afc299655234aacd0fbdc9e396a7fa Mon Sep 17 00:00:00 2001 From: DexterFromLab Date: Thu, 12 Mar 2026 14:14:13 +0100 Subject: [PATCH] Auto-wake agents on btmsg and fix unwanted auto-scroll - Add btmsg inbox polling (10s) to AgentSession so agents wake when they receive messages from other agents (not just admin DMs) - Remove automatic setActiveProject on agent activation to prevent focus stealing from the user - Use untrack() in ProjectGrid scroll effect so agent re-renders don't trigger unwanted scrollIntoView Co-Authored-By: Claude Opus 4.6 --- .../components/Workspace/AgentSession.svelte | 33 +++++++++++++++++++ .../Workspace/GroupAgentsPanel.svelte | 1 - .../components/Workspace/ProjectGrid.svelte | 14 ++++---- 3 files changed, 41 insertions(+), 7 deletions(-) diff --git a/v2/src/lib/components/Workspace/AgentSession.svelte b/v2/src/lib/components/Workspace/AgentSession.svelte index a6f598a..2f2e736 100644 --- a/v2/src/lib/components/Workspace/AgentSession.svelte +++ b/v2/src/lib/components/Workspace/AgentSession.svelte @@ -27,6 +27,7 @@ import { getProvider, getDefaultProviderId } from '../../providers/registry.svelte'; import { loadAnchorsForProject } from '../../stores/anchors.svelte'; import { getSecret } from '../../adapters/secrets-bridge'; + import { getUnreadCount } from '../../adapters/btmsg-bridge'; import { getWakeEvent, consumeWakeEvent, updateManagerSession } from '../../stores/wake-scheduler.svelte'; import { SessionId, ProjectId } from '../../types/ids'; import AgentPane from '../Agent/AgentPane.svelte'; @@ -136,11 +137,43 @@ stopAgent(sessionId).catch(() => {}); }); + // btmsg inbox polling — auto-wake agent when it receives messages from other agents + let msgPollTimer: ReturnType | null = null; + let lastKnownUnread = 0; + + function startMsgPoll() { + if (msgPollTimer) clearInterval(msgPollTimer); + msgPollTimer = setInterval(async () => { + if (contextRefreshPrompt) return; // Don't queue if already has a pending prompt + try { + const count = await getUnreadCount(project.id as unknown as AgentId); + if (count > 0 && count > lastKnownUnread) { + lastKnownUnread = count; + const wakeMsg = project.isAgent + ? `[New Message] You have ${count} unread message(s). Check your inbox with \`btmsg inbox\` and respond appropriately.` + : `[New Message] You have ${count} unread message(s). Check your inbox and respond.`; + contextRefreshPrompt = wakeMsg; + logAuditEvent( + project.id as unknown as AgentId, + 'wake_event', + `Agent woken by ${count} unread btmsg message(s)`, + ).catch(() => {}); + } else if (count === 0) { + lastKnownUnread = 0; + } + } catch { + // btmsg not available, ignore + } + }, 10_000); // Check every 10s + } + // Start timer and clean up startReinjectionTimer(); + startMsgPoll(); onDestroy(() => { if (reinjectionTimer) clearInterval(reinjectionTimer); if (wakeCheckTimer) clearInterval(wakeCheckTimer); + if (msgPollTimer) clearInterval(msgPollTimer); unsubAgentStart(); unsubAgentStop(); }); diff --git a/v2/src/lib/components/Workspace/GroupAgentsPanel.svelte b/v2/src/lib/components/Workspace/GroupAgentsPanel.svelte index c4a9f49..a08ba89 100644 --- a/v2/src/lib/components/Workspace/GroupAgentsPanel.svelte +++ b/v2/src/lib/components/Workspace/GroupAgentsPanel.svelte @@ -64,7 +64,6 @@ await setAgentStatus(agent.id, newStatus); await pollBtmsg(); // Refresh immediately if (newStatus === 'active') { - setActiveProject(agent.id); emitAgentStart(agent.id); } else { emitAgentStop(agent.id); diff --git a/v2/src/lib/components/Workspace/ProjectGrid.svelte b/v2/src/lib/components/Workspace/ProjectGrid.svelte index b1c6e8e..d07aa78 100644 --- a/v2/src/lib/components/Workspace/ProjectGrid.svelte +++ b/v2/src/lib/components/Workspace/ProjectGrid.svelte @@ -1,5 +1,5 @@