Skip to content

feat: headless agent pills in sessions view#49

Merged
jcanizalez merged 2 commits intomainfrom
main-worktree-b747cd4c
Mar 19, 2026
Merged

feat: headless agent pills in sessions view#49
jcanizalez merged 2 commits intomainfrom
main-worktree-b747cd4c

Conversation

@jcanizalez
Copy link
Copy Markdown
Owner

Summary

  • Headless agents spawned by workflows now appear as compact inline pills above the interactive session grid in the Sessions view
  • Each pill shows agent icon, name, branch, live duration timer, and exit code badge
  • Click to expand and see streaming log output; close button hides pill (process keeps running), kill button stops it
  • Exited sessions auto-prune after a configurable retention period (default 5 min)
  • New settings: "Show Headless Agents" toggle and "Completed Agent Retention" dropdown in General settings

Test plan

  • Launch app with no headless sessions → no "Headless Agents" section visible
  • Trigger a workflow with a headless agent → pill appears instantly with animated border
  • Verify pill shows agent icon, name, branch, and live ticking duration
  • Click pill → expands to show streaming log output
  • Hover → close (X) hides pill, kill (square) stops process
  • After agent exits → pill shows exit code badge, dims, auto-prunes after retention time
  • Settings → General → toggle "Show Headless Agents" off → section disappears
  • Settings → "Completed Agent Retention" → change value → verify prune timing
  • Project/workspace filter applies to headless pills
  • Status filter (running/error/idle) applies correctly

Copilot AI review requested due to automatic review settings March 17, 2026 03:26
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds end-to-end “headless agent session” tracking and UI so background (non-terminal) agent runs can be listed, monitored, and controlled from the renderer.

Changes:

  • Introduces renderer store state/actions for headless sessions (add/update/dismiss/prune + last-output tracking).
  • Adds UI for displaying headless sessions above the grid (including an expandable pill with live log streaming), plus settings for visibility and retention.
  • Adds IPC + server method support for listing headless sessions and polls/syncs them in the renderer.

Reviewed changes

Copilot reviewed 13 out of 13 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
src/renderer/stores/types.ts Extends TerminalsSlice with headless session tracking types.
src/renderer/stores/terminals-slice.ts Implements headless session state/actions in Zustand.
src/renderer/lib/workflow-execution.ts Immediately adds newly created headless sessions to the UI store.
src/renderer/global.css Adds borderPulse keyframes for headless pill styling.
src/renderer/components/settings/GeneralSettings.tsx Adds settings toggles for headless visibility + retention duration.
src/renderer/components/HeadlessPill.tsx New UI component to render headless session status/actions/logs.
src/renderer/components/GridView.tsx Renders a “Headless Agents” section above the session grid with filtering.
src/renderer/App.tsx Subscribes to headless data/exit, polls listHeadlessSessions, and prunes exited sessions.
src/preload/index.ts Exposes listHeadlessSessions to the renderer.
src/main/ipc-handlers.ts Registers IPC handler for headless:list.
packages/shared/src/types.ts Adds config defaults + IPC constant for headless list.
packages/shared/src/protocol.ts Adds JSON-RPC method typing for headless:list.
packages/server/src/register-methods.ts Registers server method headless:list.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

You can also share your feedback on Copilot code review. Take the survey.

Comment on lines +80 to +98
// Reconcile: merge with existing, don't add back dismissed
const dismissed = state.headlessDismissed
const existing = new Map(state.headlessSessions.map((s) => [s.id, s]))
for (const s of sessions) {
if (dismissed.has(s.id)) continue
const prev = existing.get(s.id)
if (prev) {
// Update status/exitCode/endedAt from server, keep local overrides
existing.set(s.id, {
...prev,
status: s.status,
exitCode: s.exitCode,
endedAt: s.endedAt
})
} else {
existing.set(s.id, s)
}
}
return { headlessSessions: Array.from(existing.values()) }
Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed: setHeadlessSessions now rebuilds from the server list, dropping sessions the server no longer reports. Locally-added running sessions (not yet known to server) are preserved. Output entries for removed sessions are also cleaned up.

Comment on lines +116 to +128
return {
headlessSessions: state.headlessSessions.filter((s) => s.id !== id),
headlessDismissed: dismissed
}
}),

pruneExitedHeadless: (retentionMs) =>
set((state) => {
const now = Date.now()
return {
headlessSessions: state.headlessSessions.filter(
(s) => s.status === 'running' || !s.endedAt || now - s.endedAt < retentionMs
)
Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed: headlessLastOutput entries are now deleted in dismissHeadlessSession, pruneExitedHeadless, and setHeadlessSessions (when sessions disappear from server). headlessDismissed is also cleaned up during pruning.

? 'border-red-500/25'
: 'border-white/[0.06]'

const opacityClass = !isRunning ? 'opacity-65' : ''
Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed: changed opacity-65 to opacity-[0.65] (Tailwind arbitrary value syntax).

hover:border-white/[0.12]
${borderClass} ${opacityClass}
${expanded ? 'flex-col !items-stretch w-[320px]' : ''}`}
style={isRunning ? { animation: 'borderPulse 2.5s ease-in-out infinite' } : undefined}
Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed: removed the duplicate inline style={{ animation }} — now only using the Tailwind animate-[borderPulse_...] class.

Headless agents spawned by workflows are now visible in the Sessions
view as compact inline pills above the interactive session grid.

- Add headless:list IPC to query active headless sessions from server
- Track headless sessions in Zustand store with real-time updates
- HeadlessPill component: status dot, agent icon, name, branch, live
  duration timer, exit code badge, expandable log output
- Close button hides pill (process continues), Kill button stops process
- Auto-prune exited sessions after configurable retention (default 5 min)
- Settings: toggle visibility + retention duration in General settings
- Project/workspace/status filters apply to headless pills
- Fix phantom sessions: setHeadlessSessions now removes sessions gone
  from server instead of only merging (keeps locally-added running ones)
- Fix memory leak: dismissHeadlessSession and pruneExitedHeadless now
  clean up headlessLastOutput and headlessDismissed maps
- Fix opacity-65 (not a default Tailwind class) to opacity-[0.65]
- Remove duplicate inline style animation (keep Tailwind class only)
@jcanizalez jcanizalez force-pushed the main-worktree-b747cd4c branch from e94a314 to 230ed24 Compare March 19, 2026 19:12
@jcanizalez jcanizalez merged commit f2948c2 into main Mar 19, 2026
1 check passed
@jcanizalez jcanizalez deleted the main-worktree-b747cd4c branch March 19, 2026 20:52
This was referenced Mar 19, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants