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 <noreply@anthropic.com>
This commit is contained in:
parent
5b7ad30573
commit
c6a41018b6
3 changed files with 41 additions and 7 deletions
|
|
@ -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<typeof setInterval> | 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();
|
||||
});
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
<script lang="ts">
|
||||
import { onMount, onDestroy } from 'svelte';
|
||||
import { onMount, onDestroy, untrack } from 'svelte';
|
||||
import { getAllWorkItems, getActiveProjectId, setActiveProject } from '../../stores/workspace.svelte';
|
||||
import ProjectBox from './ProjectBox.svelte';
|
||||
|
||||
|
|
@ -15,14 +15,16 @@
|
|||
// Track slot elements for auto-scroll
|
||||
let slotEls = $state<Record<string, HTMLElement>>({});
|
||||
|
||||
// Auto-scroll to active project when it changes
|
||||
// Auto-scroll to active project only when activeProjectId changes
|
||||
// (untrack slotEls so agent re-renders don't trigger unwanted scrolls)
|
||||
$effect(() => {
|
||||
const id = activeProjectId;
|
||||
if (!id) return;
|
||||
const el = slotEls[id];
|
||||
if (!el) return;
|
||||
// Use smooth scroll; block: nearest avoids jumping if already visible
|
||||
el.scrollIntoView({ behavior: 'smooth', block: 'nearest', inline: 'nearest' });
|
||||
untrack(() => {
|
||||
const el = slotEls[id];
|
||||
if (!el) return;
|
||||
el.scrollIntoView({ behavior: 'smooth', block: 'nearest', inline: 'nearest' });
|
||||
});
|
||||
});
|
||||
|
||||
let observer: ResizeObserver | undefined;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue