Commit Graph

702 Commits

Author SHA1 Message Date
Ashish Huddar 1bb80ef67c
feat: stable session titles via pinnedSummary metadata (#946)
* feat(core): pin first quality summary for stable session titles

When a session's first non-fallback summary is obtained, persist it to
metadata as pinnedSummary. This prevents the dashboard title from drifting
as the agent generates new summaries each turn.

Changes:
- Add pinnedSummary to SessionMetadata type
- Serialize/deserialize in writeMetadata/readMetadata
- Pin in lifecycle manager's checkSession (correct write path)
- Propagate through restore path

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* feat(web): stable session titles via unified getSessionTitle

Reorder title fallback: PR > issue > branch > summary (was: summary > issue > branch).
Replace inline getSessionHeadline with shared getSessionTitle in both
SessionDetail and page.tsx for consistent behavior across all components.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* test: coverage for pinnedSummary and title fallback chain

9 new tests:
- 3 serialize tests: pinnedSummary priority over agent/metadata summary
- 5 lifecycle-manager tests: pinning logic guards and error handling
- 1 metadata test: pinnedSummary serialization in writeMetadata

Updated existing tests to match new title fallback order.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* test: fix mobile title expectations

* fix(web): use pinnedSummary only for title selection, not dashboard.summary

Previously, pinnedSummary was assigned to dashboard.summary in
sessionToDashboard(), causing two problems:
1. enrichSessionAgentSummary() never fetched newer summaries because
   dashboard.summary was already set
2. Every UI surface rendering session.summary showed the stale pinned
   value instead of live agent output

Fix: pinnedSummary is now read only by getSessionTitle() via
session.metadata["pinnedSummary"], keeping title stable without
freezing the live summary field. dashboard.summary always reflects the
current agentInfo or metadata summary.

Addresses review comment on PR #946.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-07 09:52:48 +05:30
Ashish Huddar d8b8645bc7
fix(cli): ao start navigates to session page instead of orchestrator selection (#958)
* fix(cli): ao start navigates to session page instead of orchestrator selection

When ao start finds existing orchestrators, it now auto-selects the most
recently active one and opens /sessions/{id} directly, rather than the
/orchestrators selection page. This matches the behavior users expect:
landing on the agent terminal immediately on startup.

Closes #954

* fix(cli): navigate to session page for single orchestrator, selection page for multiple

When ao start finds existing orchestrators:
- 1 orchestrator: navigate directly to /sessions/{id} (fix for #954)
- 2+ orchestrators: keep /orchestrators?project={id} selection page so users
  can choose among sessions or spawn a new one — the main dashboard only
  links one orchestrator per project, making the selection page the only
  startup path for multi-orchestrator projects.

Addresses review feedback on #958.
2026-04-07 09:48:41 +05:30
Ashish Huddar 30ee327daf
fix(notifier): remove desktop notifications from default configs (#962)
* fix(notifier): remove desktop notifications from all default configs (#960)

Desktop notifications via osascript open blank Finder windows on macOS
without providing useful information. Remove 'desktop' from all default
notifier lists so new users are not affected. Existing configs that
explicitly list 'desktop' continue to work unchanged.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* test(cli): add coverage for autoCreateConfig default notifiers

Line 585 in start.ts (`notifiers: []`) was not covered by the diff
coverage check. Added a test that exercises `autoCreateConfig` via
`createConfigOnly()`, verifying that the generated config does not
include 'desktop' in `defaults.notifiers`.

Required infrastructure additions:
- Mock `node:process` so `import { cwd } from "node:process"` in
  start.ts can be overridden per-test via `mockProcessCwd`
- Set `detectProjectType` mock to return `{ languages: [], frameworks: [] }`
  instead of null to avoid crash in autoCreateConfig

Closes #960

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(config): disable all notifications by default — fully opt-in

Change DefaultPluginsSchema.notifiers default from ["composio"] to []
and notificationRouting defaults to all empty arrays, so no notifications
are sent unless the user explicitly configures them. Addresses reviewer
feedback that composio notifier should not be active out of the box.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(config): use empty object as notificationRouting default

Empty array entries like { urgent: [] } block the ?? fallback in
notifyHuman (lifecycle-manager.ts:1169) because [] is not null/undefined.
Using {} lets unlisted priorities fall back to defaults.notifiers, so
explicit per-user notifier config is respected.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-07 08:59:51 +05:30
Harsh Batheja e25e9b4967
fix(lifecycle): reduce GitHub API rate limiting from batch enrichment bypass (#906)
* fix(lifecycle): reduce GitHub API rate limiting from batch enrichment bypass

Three optimizations to prevent API storms in the lifecycle manager poll cycle:

1. **CRITICAL - maybeDispatchMergeConflicts**: Gate the getMergeability()
   fallback to only run when batch enrichment didn't run at all. Previously
   it called getMergeability() (3 REST calls) whenever hasConflicts was
   undefined, even when the batch had already fetched PR data. Now uses
   cachedData.hasConflicts ?? false when the batch ran.

2. **HIGH - maybeDispatchCIFailureDetails**: Use batch enrichment ciChecks
   when available instead of calling getCIChecks() (separate REST call)
   on every poll. The GraphQL batch query now fetches statusCheckRollup
   contexts (individual check names, statuses, URLs) alongside the rollup
   state. Falls back to getCIChecks() only when batch didn't run.

3. **MEDIUM - maybeDispatchReviewBacklog**: Throttle getPendingComments +
   getAutomatedComments API calls to at most once per 2 minutes per session.
   These were called every 30s even when nothing had changed.

Impact: ~8-10 API calls/PR/poll reduced to ~2-4, enabling 3-4x more
concurrent sessions before hitting GitHub's 5,000/hr REST limit.

Also extends PREnrichmentData with ciChecks?: CICheck[] and adds
parseCheckContexts() helper to graphql-batch.ts for parsing CheckRun
and StatusContext nodes from the GraphQL statusCheckRollup.contexts field.

* fix(scm-github): fall back to getCIChecks() when contexts list is truncated

When a PR has >20 CI checks, contexts(first: 20) silently truncates the
list. Setting ciChecks to undefined when pageInfo.hasNextPage is true
ensures maybeDispatchCIFailureDetails falls back to the getCIChecks()
REST call, which returns all checks without truncation.

Also adds pageInfo { hasNextPage } to the contexts GraphQL query so
truncation can be detected.

* fix(lifecycle): prune lastReviewBacklogCheckAt in pollAll cleanup loop

Add the new throttle map to the existing pruning loop that removes stale
entries for sessions no longer in the session list. Previously the map
was only cleared on terminal status transitions, leaving orphaned entries
for sessions removed externally (killed + cleaned up without transition).

* fix(lifecycle): bypass throttle on review transition; fix StatusContext conclusion

Two fixes for automated review findings:

1. Bypass review backlog throttle when a transition reaction just fired for
   humanReactionKey or automatedReactionKey. The transitionReaction branch
   needs to read the current fingerprint via the API to record
   lastPendingReviewDispatchHash. Without bypassing, the throttle prevents
   this write and the next unthrottled poll sees a stale (empty) hash,
   clears the reaction tracker, and fires a duplicate dispatch.

2. Set conclusion on StatusContext nodes in parseCheckContexts() to match
   the REST getCIChecksFromStatusRollup() format (rawState.toUpperCase()).
   The CI failure fingerprint includes c.conclusion ?? '', so inconsistent
   conclusion values between GraphQL and REST paths caused phantom fingerprint
   changes when switching sources, triggering duplicate dispatches.

* fix(scm-github): normalize CheckRun conclusion and map NEUTRAL to skipped

Two consistency fixes in parseCheckContexts() vs the REST path:

1. NEUTRAL conclusion: was mapped to 'passed' (with SUCCESS), but
   mapRawCheckStateToStatus() in the REST path maps NEUTRAL to 'skipped'.
   Changed to treat NEUTRAL the same as SKIPPED.

2. CheckRun conclusion: was stored as the raw GraphQL string (may be
   lowercase). REST getCIChecks/getCIChecksFromStatusRollup always store
   conclusion as rawState.toUpperCase(). Now stores rawConclusion which
   is already uppercased during the status branching logic.

Both fixes prevent phantom fingerprint changes when maybeDispatchCIFailureDetails
switches between GraphQL batch and REST fallback across poll cycles.

* fix(scm-github): map STALE/NOT_REQUIRED/NONE conclusions to skipped

parseCheckContexts() was mapping these conclusions to 'failed' via the
else fallback, while mapRawCheckStateToStatus() in the REST path
explicitly maps all of them to 'skipped'. Added them to the skipped
branch alongside SKIPPED and NEUTRAL to fully mirror the REST mapping.

* fix(scm-github): map QUEUED/WAITING to pending not running

parseCheckContexts() mapped QUEUED and WAITING CheckRun statuses to
'running', but mapRawCheckStateToStatus() in the REST path maps both
to 'pending'. Only IN_PROGRESS maps to 'running' in the REST path.

Fixes fingerprint inconsistency when switching between GraphQL batch
and REST fallback across poll cycles.

* fix(scm-github): map STARTUP_FAILURE to skipped; guard null pageInfo

- STARTUP_FAILURE conclusion now falls through to the "skipped" branch
  (matching mapRawCheckStateToStatus() REST default) instead of the
  explicit failure enumeration catch-all
- Null pageInfo guard prevents TypeError from typeof null === "object"
  JavaScript quirk when accessing hasNextPage on a null pageInfo field
- Tests added for both cases

* fix(scm-github): map COMPLETED+null conclusion to skipped not passed

When a CheckRun has status COMPLETED and conclusion null, the REST path's
mapRawCheckStateToStatus() converts it to "" which maps to "skipped".
The GraphQL path was incorrectly mapping it to "passed" via !rawConclusion.
Fix: only map rawConclusion === "SUCCESS" to "passed"; null falls through
to the else branch → "skipped", matching the REST path exactly.
2026-04-06 14:59:00 +05:30
Harsh Batheja 9ac659066a
revert: remove model rate-limit pause functionality (PR #367) (#908)
* Revert "fix: pause workers on model limits and stabilize session visibility (#367)"

This reverts commit 003eb78adb.

* fix: resolve post-revert type errors and broken test references

- Fix dangling import { in session-manager.ts (leftover from removing globalPause imports)
- Fix duplicate registerStop body in start.ts (leftover parent-version code after catch block)
- Remove --keep-session flag from stop command (was added by #367)
- Fix PullRequestsPage.tsx useSessionEvents call removing dropped initialGlobalPause arg
- Remove globalPause test cases from spawn.test.ts, communication.test.ts, api-routes.test.ts
- Remove unused updateMetadata imports from test files

* fix: remove unused allSessions variable after globalPause removal
2026-04-06 14:58:41 +05:30
Ashish Huddar b662274d15
Merge pull request #934 from ComposioHQ/ashish921998/restore-done-sessions
fix: restore done/terminated sessions column on kanban board
2026-04-06 13:35:38 +05:30
Ashish Huddar bfb3a32786
Merge pull request #929 from ComposioHQ/ashish921998/404-loading-pages
Add loading and not-found pages for app and sessions
2026-04-06 13:01:48 +05:30
Ashish Huddar 2e309d2a45 style: align done bar typography with board section head
Match font-size (12px), font-weight (700), and letter-spacing (0.16em)
to the existing board-section-head__title pattern for visual consistency.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-06 11:55:57 +05:30
Ashish Huddar 57667a96b5 fix: remove done from kanban/mobile arrays to prevent double rendering
Done sessions should only render in the collapsible done-bar, not also
in the kanban columns and mobile accordion. Reverts KANBAN_LEVELS,
MOBILE_KANBAN_ORDER, and MOBILE_FILTERS to exclude "done", and restores
the handler guards that prevent accordion/pill interaction for done level.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-06 11:54:15 +05:30
Ashish Huddar 651ffa592e test: add done bar coverage for diff-coverage CI gate
Add Dashboard.doneBar.test.tsx covering the collapsible done/terminated
bar: renders when done sessions exist, expands on click to show session
cards, and suppresses the empty state when only done sessions are present.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-06 11:48:12 +05:30
Ashish Huddar 61721f8e69 fix: restore done/terminated sessions column on kanban board
Add a collapsible "Done / Terminated" bar below the kanban board that
expands to show completed session cards in a grid. Sessions with terminal
states (merged, killed, done, terminated) appear here with their full
card UI including restore button. Also fixes the empty state to not show
when only done sessions exist.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-06 11:37:17 +05:30
Ashish Huddar 0e3f6c8698 Merge remote-tracking branch 'origin/main' into ashish921998/404-loading-pages 2026-04-06 09:10:23 +05:30
Ashish Huddar 156006764b fix(web): guard polling 404 with hasLoadedSessionRef and add missing link mock
Transient 404 during polling no longer kicks user off an active session
view. Also added missing next/link mock in session error test.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-06 09:01:55 +05:30
Ashish Huddar 7ea30205b5 fix(web): preserve session view on polling errors and add missing link mock
Polling errors no longer throw to the error boundary when a session is
already loaded, preventing transient network blips from unmounting the
view. Added missing next/link mock in error.test.tsx.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-06 00:53:45 +05:30
Ashish Huddar 52bcce8f2b feat(web): add dashboard error boundaries 2026-04-06 00:32:53 +05:30
Ashish Huddar 79b0cabb83
Merge pull request #923 from ComposioHQ/ashish921998/fix-session-cleanup
Improve dashboard fast-path session hydration
2026-04-05 22:54:44 +05:30
Harshit Singh Bhandari 35eca3107c
Merge pull request #909 from harshitsinghbhandari/fix/prevent-duplicate-orchestrators
fix: prevent ao start from spawning duplicate orchestrators
2026-04-05 22:47:06 +05:30
Ashish Huddar 64e27a6b70 fix: prevent retry storms and stale refresh on project switch
- Update lastRefreshAtRef on non-OK /api/sessions responses to prevent
  infinite 5-second retry loops when the API is unhealthy
- Reset lastRefreshAtRef when project changes so each project gets its
  own immediate first refresh instead of inheriting the prior project's
  cooldown timestamp
- Add test for project-switch refresh behavior

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 22:39:03 +05:30
Ashish Huddar 129149e166 fix(cli): resolve ao-core vitest subpaths 2026-04-05 22:38:55 +05:30
Ashish Huddar 1d30731247 fix(cli,web): stabilize isolated workspace checks 2026-04-05 22:28:12 +05:30
Ashish Huddar 9e87c04fac test: improve diff coverage for TTFB fast-path changes
Cover enriched done-card PR detail panel (SessionCard lines 361-377),
non-OK response timestamp update (useSessionEvents lines 180-181),
and enrichSessionsMetadataFast function (serialize lines 369-383).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 21:00:14 +05:30
Ashish Huddar 2ce2f484a0 fix: polish shimmer states, terminal PR inference, and gitignore .gstack
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 20:52:00 +05:30
Ashish Huddar 631eabf09a feat(web): add session-specific not-found.tsx for 404 handling
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 20:41:37 +05:30
Ashish Huddar e30e5dbf08 feat(web): add global not-found.tsx for 404 handling
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 20:40:39 +05:30
Ashish Huddar d5966c528a feat(web): add session loading.tsx with spinner for route transitions
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 20:39:35 +05:30
Ashish Huddar 49817315bf feat(web): add global loading.tsx with spinner for route transitions
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 20:38:23 +05:30
harshitsinghbhandari 478ce2cc81 fix: replicate terminal session filtering 2026-04-05 19:03:52 +05:30
harshitsinghbhandari e2b43ec0b2 fix comments 2026-04-05 18:41:46 +05:30
Ashish Huddar cfb7689f5e refactor: extract settlesWithin to shared async-utils module
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 18:13:09 +05:30
Ashish Huddar 25179a8ecf Restore SSR metadata timeout 2026-04-05 17:51:23 +05:30
Ashish Huddar f12500545e Infer terminal PR state without SCM 2026-04-05 17:42:59 +05:30
Ashish Huddar 80d6e76bda Restore parallel session metadata enrichment 2026-04-05 17:35:14 +05:30
Ashish Huddar e3f7d8aa29 Add diff coverage for dashboard fast path 2026-04-05 17:33:18 +05:30
Ashish Huddar db8c69da72 Merge origin/main into ashish921998/fix-session-cleanup 2026-04-05 17:24:38 +05:30
Ashish Huddar 661e654901 Improve dashboard fast-path session hydration
Treat unenriched PRs as a first-class state in the dashboard so SSR can
return fast-path session data without showing misleading PR status, size,
or alert states. Add shimmer placeholders for partial PR data, avoid
classifying unenriched PRs as merge/review/pending work, and ensure the
first SSE snapshot triggers an immediate refresh without failure loops.
2026-04-05 17:18:36 +05:30
suraj_markup 86ac556e8e
Merge pull request #864 from yyovil/emdash/fix-ao-version-p0h
fix(cli): ao's version mismatch problem
2026-04-04 23:41:13 +05:30
harshitsinghbhandari 5e19249ec3 fix: clean up OrchestratorSelector tests and add spawn resilience
- Remove phantom `projects` prop from test setup (was dead code)
- Deduplicate test coverage: consolidate component tests into
  OrchestratorSelector.test.tsx, keep only page route tests in
  orchestrators.test.tsx
- Fix useRouter mock to capture and assert router.push calls
- Add finally block to handleSpawnNew for resilience (ensures
  isSpawning is reset even if router.push fails)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-04-04 23:21:22 +05:30
harshitsinghbhandari a21b97656b fix: correct tmux target fallback and deduplicate orchestrator listing
- Fix tmux target to use session ID when runtimeHandle is missing,
  preventing references to non-existent sessions
- Extract mapSessionsToOrchestrators helper to eliminate duplicate
  orchestrator listing logic between page.tsx and API route

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-04-04 22:43:11 +05:30
harshitsinghbhandari 5255f997a1 refactor: deduplicate Orchestrator interface
Export Orchestrator interface from OrchestratorSelector component
and import it in page.tsx to avoid silent divergence risk.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-04-04 22:21:26 +05:30
harshitsinghbhandari 25b3f31a1c test: update start command tests for auto-select behavior
Update existing test to verify the new --no-dashboard auto-select
behavior and add a separate test for dashboard-enabled selection
message.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-04-04 22:10:11 +05:30
harshitsinghbhandari 4e60238d5d test: add edge case tests for formatRelativeTime
Add tests to verify proper handling of:
- Invalid date strings (returns "Unknown")
- Future timestamps (returns "Just now")
- Null dates (returns "Unknown")

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-04-04 22:01:09 +05:30
harshitsinghbhandari f9abcdf32f fix: auto-select orchestrator when --no-dashboard is used
When --no-dashboard is used and existing orchestrators are found,
auto-select the most recently active one instead of deferring to
dashboard selection (which isn't available). This ensures the CLI
remains usable without the dashboard.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-04-04 21:57:56 +05:30
harshitsinghbhandari d54ca026c1 fix: wrap session listing in try/catch for proper error handling
The sm.list() call was outside the existing try/catch that handles
orchestrator setup failures. If listing sessions throws, the spinner
could be left running and the dashboard process could leak.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-04-04 21:56:53 +05:30
harshitsinghbhandari e0b5aa0975 fix: remove unused projects prop from OrchestratorSelector
The projects prop was declared and passed but never used, causing
unnecessary data fetching and serialization. Removed the prop from
the component interface, page, and tests.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-04-04 21:56:29 +05:30
harshitsinghbhandari 2a0f27a940 fix: handle invalid and future dates in formatRelativeTime
Guard against NaN timestamps from invalid ISO date strings and handle
future timestamps gracefully by returning "Just now" instead of
negative values.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-04-04 21:54:47 +05:30
harshitsinghbhandari 2a1b513e7d fix tests 2026-04-04 21:52:50 +05:30
harshitsinghbhandari 71bdf75f53 add missing mock list 2026-04-04 19:32:01 +05:30
harshitsinghbhandari 0b7b3153a9 fix: prevent ao start from spawning duplicate orchestrators
When ao start is run and existing orchestrator sessions exist for the
project, the CLI now skips spawning a new one. Instead:

- Detects existing orchestrator sessions before spawning
- Opens the dashboard to a new /orchestrators page for session selection
- Users can resume an existing orchestrator or start a new one

Changes:
- packages/cli/src/commands/start.ts: Check for existing orchestrators,
  redirect to selection page if found
- packages/web/src/app/api/orchestrators/route.ts: Add GET endpoint to
  list orchestrators for a project
- packages/web/src/app/orchestrators/page.tsx: New page for orchestrator
  selection
- packages/web/src/components/OrchestratorSelector.tsx: UI component for
  selecting or spawning orchestrators
- packages/web/src/components/__tests__/OrchestratorSelector.test.tsx:
  Tests for the selector component

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-04-04 19:05:51 +05:30
Harsh Batheja 34bc5bb2d5
feat: support multiple concurrent orchestrators with isolated worktrees (#870)
* feat: support multiple concurrent orchestrators with isolated worktrees

- Each orchestrator session gets a numbered ID ({prefix}-orchestrator-N)
  and an isolated git worktree, replacing the single shared orchestrator
- reserveNextOrchestratorIdentity atomically reserves the next available
  number and detects conflicting project prefix configurations early
- isOrchestratorSession / isOrchestratorSessionName accept optional
  allSessionPrefixes for cross-project false-positive prevention
- getProjectPause and resolveGlobalPause track the longest active pause
  across all concurrent orchestrators instead of returning the first found
- cleanupWorktreeAndMetadata helper used consistently on all failure paths
  including post-launch; system prompt file included in cleanup
- pollBacklog, status.ts, ProjectSidebar, sessions page, global-pause,
  project-utils, serialize, and sessions API route all pass
  allSessionPrefixes to isOrchestratorSession
- Web sessions/[id]/page.tsx fetches project prefix map and passes it
  through prefixByProjectRef to avoid stale closure in fetchProjectSessions

* fix: seed sseAttentionLevels from fresh sessions on full refresh

When scheduleRefresh dispatches a reset action, derive sseAttentionLevels
from the freshly fetched sessions using getAttentionLevel. This clears
phantom entries for removed sessions and seeds correct levels for new
sessions, preventing stale favicon color and attention counts until the
next SSE snapshot.

* fix: merge duplicate @/lib/types imports into single import statement

Combining the separate import type and value import from @/lib/types
into one import to satisfy the no-duplicate-imports lint rule.
2026-04-04 12:30:51 +05:30
Dhruv Sharma ed187d976c
Merge pull request #873 from harshitsinghbhandari/feat/736
feat(core): support external tracker, scm, and notifier plugins from config
2026-04-04 02:50:19 +05:30