mirror of
https://github.com/openswarm-ai/openswarm.git
synced 2026-09-06 17:57:43 +02:00
[Haik]: ckpt, auto spawn sub agent feature done and toggleable via settings
This commit is contained in:
@@ -23,4 +23,5 @@ class AppSettings(BaseModel):
|
||||
browser_homepage: str = "https://www.google.com"
|
||||
auto_select_mode_on_new_agent: bool = False
|
||||
expand_new_chats_in_dashboard: bool = False
|
||||
auto_reveal_sub_agents: bool = True
|
||||
dev_mode: bool = False
|
||||
|
||||
@@ -34,7 +34,9 @@ import {
|
||||
removeBrowserCard,
|
||||
pasteBrowserCard,
|
||||
placeCard,
|
||||
removeCard,
|
||||
setGlowingAgentCard,
|
||||
clearGlowingAgentCard,
|
||||
DEFAULT_CARD_W,
|
||||
DEFAULT_CARD_H,
|
||||
EXPANDED_CARD_MIN_H,
|
||||
@@ -97,6 +99,7 @@ const DashboardInner: React.FC = () => {
|
||||
const newAgentShortcut = useAppSelector((state) => state.settings.data.new_agent_shortcut);
|
||||
const browserHomepage = useAppSelector((state) => state.settings.data.browser_homepage);
|
||||
const expandNewChats = useAppSelector((state) => state.settings.data.expand_new_chats_in_dashboard);
|
||||
const autoRevealSubAgents = useAppSelector((state) => state.settings.data.auto_reveal_sub_agents);
|
||||
const outputs = useAppSelector((state) => state.outputs.items);
|
||||
const glowingAgentCards = useAppSelector((state) => state.dashboardLayout.glowingAgentCards);
|
||||
const sessionList = Object.values(sessions);
|
||||
@@ -357,6 +360,100 @@ const DashboardInner: React.FC = () => {
|
||||
dispatch(reconcileSessions({ sessionIds: dashboardSessionIds, expandedSessionIds }));
|
||||
}, [sessions, layoutInitialized, dispatch, dashboardId, expandedSessionIds]);
|
||||
|
||||
// ---- Auto-reveal / collapse / unreveal sub-agent cards ----
|
||||
const autoRevealedRef = useRef(new Set<string>());
|
||||
const prevSubStatusRef = useRef<Record<string, string>>({});
|
||||
const prevParentStatusRef = useRef<Record<string, string>>({});
|
||||
|
||||
useEffect(() => {
|
||||
if (!layoutInitialized || !autoRevealSubAgents) return;
|
||||
|
||||
const subSessions = Object.values(sessions).filter(
|
||||
(s) => (s.mode === 'sub-agent' || s.mode === 'invoked-agent') && s.parent_session_id,
|
||||
);
|
||||
|
||||
// 1) Auto-reveal newly spawned sub-agents (skip already-terminal ones on load)
|
||||
for (const sub of subSessions) {
|
||||
if (autoRevealedRef.current.has(sub.id)) continue;
|
||||
if (cards[sub.id]) {
|
||||
autoRevealedRef.current.add(sub.id);
|
||||
continue;
|
||||
}
|
||||
const parentCard = cards[sub.parent_session_id!];
|
||||
if (!parentCard) continue;
|
||||
|
||||
const isTerminal = sub.status === 'completed' || sub.status === 'error' || sub.status === 'stopped';
|
||||
const parentSession = sessions[sub.parent_session_id!];
|
||||
const parentTerminal = parentSession &&
|
||||
(parentSession.status === 'completed' || parentSession.status === 'error' || parentSession.status === 'stopped');
|
||||
if (isTerminal && parentTerminal) {
|
||||
autoRevealedRef.current.add(sub.id);
|
||||
continue;
|
||||
}
|
||||
|
||||
autoRevealedRef.current.add(sub.id);
|
||||
|
||||
const targetX = parentCard.x + parentCard.width + GRID_GAP * 12;
|
||||
let targetY = parentCard.y;
|
||||
const columnCards = Object.values(cards).filter(
|
||||
(c) => Math.abs(c.x - targetX) < 50 && c.session_id !== sub.id,
|
||||
);
|
||||
if (columnCards.length > 0) {
|
||||
const lowestBottom = Math.max(
|
||||
...columnCards.map((c) => c.y + Math.max(EXPANDED_CARD_MIN_H, c.height)),
|
||||
);
|
||||
targetY = lowestBottom + GRID_GAP;
|
||||
}
|
||||
|
||||
dispatch(placeCard({ sessionId: sub.id, x: targetX, y: targetY, width: DEFAULT_CARD_W, height: DEFAULT_CARD_H }));
|
||||
dispatch(expandSession(sub.id));
|
||||
const label = sub.mode === 'sub-agent' ? 'Create Agent' : 'Invoke Agent';
|
||||
dispatch(setGlowingAgentCard({ sessionId: sub.id, sourceId: sub.parent_session_id!, label }));
|
||||
|
||||
if (sub.status === 'completed' || sub.status === 'error' || sub.status === 'stopped') {
|
||||
const subId = sub.id;
|
||||
setTimeout(() => dispatch(collapseSession(subId)), 2000);
|
||||
}
|
||||
}
|
||||
|
||||
// 2) Auto-collapse sub-agents when they complete
|
||||
const TERMINAL = new Set(['completed', 'error', 'stopped']);
|
||||
for (const sub of subSessions) {
|
||||
const prev = prevSubStatusRef.current[sub.id];
|
||||
if (prev !== sub.status && TERMINAL.has(sub.status) && cards[sub.id]) {
|
||||
dispatch(collapseSession(sub.id));
|
||||
}
|
||||
}
|
||||
const newSubStatuses: Record<string, string> = {};
|
||||
for (const sub of subSessions) { newSubStatuses[sub.id] = sub.status; }
|
||||
prevSubStatusRef.current = newSubStatuses;
|
||||
|
||||
// 3) Unreveal all sub-agent cards when parent finishes output
|
||||
const parentIds = new Set(subSessions.map((s) => s.parent_session_id!));
|
||||
for (const pid of parentIds) {
|
||||
const parent = sessions[pid];
|
||||
if (!parent) continue;
|
||||
const prev = prevParentStatusRef.current[pid];
|
||||
if (prev !== parent.status && TERMINAL.has(parent.status)) {
|
||||
const children = subSessions.filter((s) => s.parent_session_id === pid);
|
||||
for (const child of children) {
|
||||
if (!cards[child.id]) continue;
|
||||
dispatch(collapseSession(child.id));
|
||||
dispatch(removeCard(child.id));
|
||||
setTimeout(() => {
|
||||
dispatch(clearGlowingAgentCard(child.id));
|
||||
}, 500);
|
||||
}
|
||||
}
|
||||
}
|
||||
const newParentStatuses: Record<string, string> = {};
|
||||
for (const pid of parentIds) {
|
||||
const parent = sessions[pid];
|
||||
if (parent) newParentStatuses[pid] = parent.status;
|
||||
}
|
||||
prevParentStatusRef.current = newParentStatuses;
|
||||
}, [sessions, cards, layoutInitialized, autoRevealSubAgents, dispatch]);
|
||||
|
||||
const skipInitialSave = useRef(true);
|
||||
const saveTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);
|
||||
const pendingSaveRef = useRef<Parameters<typeof saveLayout>[0] | null>(null);
|
||||
|
||||
@@ -569,7 +569,7 @@ const Settings: React.FC = () => {
|
||||
/>
|
||||
</Box>
|
||||
|
||||
<Box sx={inlineRowLastSx}>
|
||||
<Box sx={inlineRowSx}>
|
||||
<Box sx={{ mr: 3 }}>
|
||||
<Typography sx={labelSx}>Default agent spawn state in dashboard</Typography>
|
||||
<Typography sx={descSx}>When enabled, new agents spawn expanded instead of collapsed.</Typography>
|
||||
@@ -584,6 +584,21 @@ const Settings: React.FC = () => {
|
||||
/>
|
||||
</Box>
|
||||
|
||||
<Box sx={inlineRowLastSx}>
|
||||
<Box sx={{ mr: 3 }}>
|
||||
<Typography sx={labelSx}>Auto-reveal sub-agents on dashboard</Typography>
|
||||
<Typography sx={descSx}>Automatically show sub-agent cards (from CreateAgent / InvokeAgent) tethered to their parent on the dashboard.</Typography>
|
||||
</Box>
|
||||
<Switch
|
||||
checked={form.auto_reveal_sub_agents}
|
||||
onChange={(e) => setForm({ ...form, auto_reveal_sub_agents: e.target.checked })}
|
||||
sx={{
|
||||
'& .MuiSwitch-switchBase.Mui-checked': { color: c.accent.primary },
|
||||
'& .MuiSwitch-switchBase.Mui-checked + .MuiSwitch-track': { bgcolor: c.accent.primary },
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
|
||||
{/* ── Browser ── */}
|
||||
<Typography sx={{ ...sectionSx, mt: 3 }}>Browser</Typography>
|
||||
|
||||
|
||||
@@ -24,6 +24,7 @@ export interface AppSettings {
|
||||
browser_homepage: string;
|
||||
auto_select_mode_on_new_agent: boolean;
|
||||
expand_new_chats_in_dashboard: boolean;
|
||||
auto_reveal_sub_agents: boolean;
|
||||
dev_mode: boolean;
|
||||
}
|
||||
|
||||
@@ -55,6 +56,7 @@ const initialState: SettingsState = {
|
||||
browser_homepage: 'https://www.google.com',
|
||||
auto_select_mode_on_new_agent: false,
|
||||
expand_new_chats_in_dashboard: false,
|
||||
auto_reveal_sub_agents: true,
|
||||
dev_mode: false,
|
||||
},
|
||||
loading: false,
|
||||
|
||||
Reference in New Issue
Block a user