[aidan] feat/workflows-canvas: tag run and edit-chat sessions to suppress duplicate cards

This commit is contained in:
abccodes
2026-06-26 06:06:22 -07:00
parent 7cf88589e5
commit 9c5e20e4ef
6 changed files with 29 additions and 3 deletions
+6
View File
@@ -13,6 +13,8 @@ class AgentConfig(BaseModel):
max_turns: Optional[int] = None
target_directory: Optional[str] = None
dashboard_id: Optional[str] = None
workflow_run_id: Optional[str] = None
workflow_edit_id: Optional[str] = None
# App cards the user picked to edit. When exactly one resolves, launch binds the chat's cwd to that app instead of seeding a new "Untitled App".
selected_app_output_ids: Optional[list[str]] = None
@@ -110,6 +112,10 @@ class AgentSession(BaseModel):
dashboard_id: Optional[str] = None
browser_id: Optional[str] = None
parent_session_id: Optional[str] = None
# Set when this session IS a workflow run's agent; the run renders in the Workflows monitor card, so the canvas suppresses the duplicate standalone agent card.
workflow_run_id: Optional[str] = None
# Set when this session IS a workflow's embedded edit/compose chat; it lives in the Workflows hub window, so the canvas suppresses its standalone card and docks its browser below the hub.
workflow_edit_id: Optional[str] = None
workflow_test_state: Optional[Literal["running", "complete", "error"]] = None
# Browser memory signals, drive the subtle "remembered/learned" card chip so the user feels the agent getting smarter without lifting a finger.
memory_recalled: bool = False
@@ -115,6 +115,8 @@ class AgentLaunch(AgentManagerProtocol):
repo_url=repo_url,
branch=branch_name,
dashboard_id=config.dashboard_id,
workflow_run_id=config.workflow_run_id,
workflow_edit_id=config.workflow_edit_id,
thinking_level=getattr(global_settings, "default_thinking_level", "auto"),
)
apply_context_window(session, global_settings)
+3 -2
View File
@@ -64,7 +64,7 @@ def _resolve_allowed_tools(wf: Workflow) -> Optional[list[str]]:
return list(wf.actions.configured_sets)
def p_resolve_run_dashboard_id(wf: Workflow) -> Optional[str]:
def resolve_workflow_dashboard_id(wf: Workflow) -> Optional[str]:
"""Pick the dashboard this run's agent attaches to, so browser tools work like in chat.
Browser cards render only on the dashboard the renderer is currently showing, so we
@@ -262,7 +262,8 @@ async def execute(
allowed_tools=resolved_allowed_tools if resolved_allowed_tools is not None else [
"Read", "Edit", "Write", "Bash", "Glob", "Grep", "AskUserQuestion",
],
dashboard_id=p_resolve_run_dashboard_id(wf),
dashboard_id=resolve_workflow_dashboard_id(wf),
workflow_run_id=run.id,
)
session = await agent_manager.launch_agent(config)
@@ -279,7 +279,7 @@ export function useDashboardLifecycle({
useEffect(() => {
if (!layoutInitialized) return;
const dashboardSessionIds = Object.values(sessions)
.filter((s) => s.dashboard_id === dashboardId && s.mode !== 'browser-agent' && s.mode !== 'invoked-agent' && s.mode !== 'sub-agent')
.filter((s) => s.dashboard_id === dashboardId && !s.workflow_run_id && !s.workflow_edit_id && s.mode !== 'browser-agent' && s.mode !== 'invoked-agent' && s.mode !== 'sub-agent')
.map((s) => s.id);
const liveIds = dashboardSessionIds.sort().join(',');
if (liveIds === prevSessionIdsRef.current) return;
@@ -49,6 +49,18 @@ export function useDashboardController(dashboardId: string, isActive: boolean) {
!!workflowsMonitorId && s.workflows.active.some((a) => a.workflow_id === workflowsMonitorId));
const workflowsMonitorLabel = monitorIsLive ? 'Watching' : 'Viewing';
// The session id of the run the monitor is showing, mirroring RunMonitor's pinned-or-latest pick, so its browser tether can anchor to the monitor card.
const workflowsMonitorRunId = useAppSelector((s) => s.dashboardLayout.workflowsMonitorRunId);
const monitorRuns = useAppSelector((s) => (workflowsMonitorId ? s.workflows.runs[workflowsMonitorId] : undefined));
const allRuns = useAppSelector((s) => s.workflows.allRuns);
const monitorRunSessionId = useMemo(() => {
if (!workflowsMonitorId) return null;
const run = workflowsMonitorRunId
? (monitorRuns || []).find((r) => r.id === workflowsMonitorRunId) || allRuns.find((r) => r.id === workflowsMonitorRunId)
: (monitorRuns && monitorRuns[0]) || allRuns.find((r) => r.workflow_id === workflowsMonitorId);
return run?.session_id || null;
}, [workflowsMonitorId, workflowsMonitorRunId, monitorRuns, allRuns]);
const contentBounds = useMemo(
() => computeContentBounds(cards, viewCards, browserCards, workflowCards, workflowsHub),
[cards, viewCards, browserCards, workflowCards, workflowsHub],
@@ -295,6 +307,7 @@ export function useDashboardController(dashboardId: string, isActive: boolean) {
workflowsHub,
workflowsMonitorCard,
workflowsMonitorLabel,
monitorRunSessionId,
});
return {
+4
View File
@@ -93,6 +93,10 @@ export interface AgentSession {
dashboard_id?: string;
browser_id?: string | null;
parent_session_id?: string | null;
/** Set when this session IS a workflow run's agent; the run shows in the Workflows monitor, so it gets no standalone canvas card. */
workflow_run_id?: string | null;
/** Set when this session IS a workflow's embedded edit/compose chat; it lives in the Workflows hub, so it gets no standalone card and its browser docks below the hub. */
workflow_edit_id?: string | null;
/** Browser memory signals that drive the subtle "Remembered"/"Learned" card chip. */
memory_recalled?: boolean;
memory_learned?: boolean;