Commit Graph

238 Commits

Author SHA1 Message Date
Dhruv Sharma ab1e4fb069
Merge pull request #1180 from yyovil/add/type-resolution
Add node type resolution for non-web packages
2026-04-13 19:56:36 +05:30
yyovil 9bed49d453 fix: scope node types to node packages 2026-04-13 18:25:21 +05:30
i-trytoohard 36a64c98b7
chore: bump all package versions to 0.2.5 (#1190)
* chore: release 0.2.5

Realign main with npm registry after off-branch publish of 0.2.3/0.2.4.
Bump all 21 linked packages to 0.2.5 and cherry-pick the startup-grace-period
fix for #989 (was in 5e4244a8 but never merged to main).

Also sync non-linked plugin versions (notifier-discord, notifier-openclaw,
scm-gitlab, tracker-gitlab) to their current npm versions.

* Revert "chore: release 0.2.5"

This reverts commit eb17f32834.

* chore: bump all package versions to 0.2.5, remove release workflow

- Bump all 25 packages to 0.2.5 to realign with npm registry
- Update package-version test to expect 0.2.5
- Remove stale .changeset/linear-spawn-branch-name.md
- Delete .github/workflows/release.yml (changesets-based NPM publish)

---------

Co-authored-by: Prateek <karnalprateek@gmail.com>
Co-authored-by: AO Bot <ao-bot@composio.dev>
2026-04-13 05:47:08 +05:30
Dhruv Sharma 663c61e16d fix: merge main and address review findings for prompt-driven spawn
- Merge main to pick up @composio/ao-core → @aoagents/ao-core rename
  and decomposer removal (#1104)
- Fix @composio/ao-core import in prompt-spawn.test.ts → @aoagents/ao-core
- Remove unreachable dead code in CLI spawn (empty-string guard after || undefined)
- Update format.ts JSDoc to document 8-item fallback chain including userPrompt
- Add clarifying comment on validation/sanitization separation in spawn route
- Integrate userPrompt display into redesigned SessionCard footer

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-11 21:44:57 +05:30
Atul Pandey 31fc9ef274
Merge pull request #1112 from ComposioHQ/fix/activity-state-terminal-status-1081
fix(core): derive activity from runtime probe, not terminal status
2026-04-11 20:39:26 +05:30
Ashish Huddar af2af115bd
style(design): design review fixes + fresh Warm Terminal design system (#927)
* style(design): FINDING-001 — add prefers-reduced-motion support

All animations and transitions are disabled when the user's system
requests reduced motion, per DESIGN.md accessibility requirements.

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

* style(design): FINDING-003 — remove concurrent breathe animations

Status pills had two animations: a breathe animation on the pill
and a dot-pulse on the child dot. DESIGN.md says "one animation per
element, one purpose" and "keep dot pulse, remove border heartbeat."

Removed all three breathe keyframes, kept dot-pulse only.

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

* style(design): FINDING-004 — fix dashboard title weight and tracking

DESIGN.md specifies display headings at weight 680 and letter-spacing
-0.035em. The dashboard title was using 600 / -0.05em.

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

* style(design): FINDING-005 — fix detail-card text to blue-tinted graphite

Detail cards overrode text-secondary and text-tertiary with neutral
grays (#9898a0, #5c5c66). DESIGN.md specifies blue-tinted graphite
palette (#a5afc4, #6f7c94) for dark mode text.

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

* style(design): FINDING-008 — add text-wrap: balance on headings

Dashboard title and kanban column titles now use text-wrap: balance
for more even line breaks on narrow viewports.

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

* style(design): FINDING-006/009 — fix section label semantics and spacing

Changed "Attention Board" from <h2> to <div role="heading"> since it's
styled as a 12px uppercase label, not a heading. Also fixed letter-spacing
from 0.16em to 0.06em per DESIGN.md UI/Labels spec.

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

* style(design): FINDING-007 — contextual empty state messages

Empty kanban columns now show context-specific messages instead of
generic "No sessions" text. Each column's empty state reflects its
purpose: "No agents need your input" (Respond), "No code waiting
for review" (Review), etc.

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

* docs(design): fresh design system — Warm Terminal

Complete redesign from Industrial Precision (blue-tinted) to Warm Terminal
(brown-tinted). Key changes:

- Warm charcoal surfaces (#121110, #1a1918, #222120) replace blue-gray
- Cream text (#f0ece8) replaces blue-white (#eef3ff)
- Warm periwinkle accent (#8b9cf7) replaces cool blue (#5B7EF8)
- Berkeley Mono for display headlines (mono cohesion)
- Added: Accessibility section (44px touch targets, WCAG AA, focus-visible)
- Added: Component anatomy (button states, card structure, input fields)
- Added: Light mode design rationale (warm parchment, not clinical white)

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

* docs(design): swap Berkeley Mono for JetBrains Mono (free)

Berkeley Mono is a paid font ($75). JetBrains Mono is free, open source,
already loaded in the project, and the mono-for-headlines concept works
the same way with it.

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

* docs(design): fix light mode contrast failures

Light mode text-tertiary #a8a29e failed WCAG AA at 2.5:1 on white.
Darkened to #736e6b (5.0:1). Light mode accent #6b73c4 was borderline
at 4.3:1, darkened to #5c64b5 (5.3:1). All pairs now pass AA.

Added verified contrast ratios for both modes to accessibility section.

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

* chore: gitignore .gstack/ directory

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

* docs(design): add design audit report and screenshots

Design review audit report with before/after screenshots for all
dashboard pages (kanban, session detail, PRs) across desktop, tablet,
and mobile viewports in both light and dark mode.

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

* fix(design): address PR review comments

- Fix mobile test expecting removed "No sessions" text. The merge zone
  emptyMessage is now "Nothing cleared to land yet." (Bugbot comment #1)
- Remove no-op .dark .detail-card override that duplicated global dark
  values after FINDING-005 fix aligned them (Bugbot comment #2)

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

* fix: gitignore .gstack-report/ and remove from tracking

The .gstack-report/ directory contains local audit artifacts with
filesystem paths. Should not be tracked in the repository.

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

* fix(design): align dashboard title CSS to new DESIGN.md spec

Dashboard title was using old Geist Sans values (weight 680, -0.035em).
New spec is JetBrains Mono, weight 500, letter-spacing -0.02em.
Added font-family: var(--font-mono) to match.

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

* fix: use native h2 element for Attention Board section heading

Replace ARIA role="heading" div with semantic h2 per ARIA first rule — native elements are preferred over ARIA roles for actual headings.

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

* fix: pre-landing review fixes — a11y, dead code, test coverage

- Add aria-controls + id to accordion button/body pair in AttentionZone
- Wrap empty-state messages in aria-live="polite" regions for AT announcements
- Remove dead message prop and isDefault from EmptyState (Skeleton.tsx)
- Add parameterized test covering all 6 zone-specific empty messages

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

* fix: revert aria-live on empty states — causes false AT announcements

Codex review identified that role="status" aria-live on static empty-state
text causes burst announcements on page load (all empty columns fire) and
announces in collapsed mobile sections that aren't visible. Empty states are
static text, not dynamic transitions. The aria-controls fix is kept.

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

* chore: bump version and changelog (v0.0.1.0)

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

* chore: remove .gstack-report/ from .gitignore

* chore: remove VERSION and CHANGELOG (not used in this project)

* style(design): warm terminal color migration + inline style removal

Migrate all CSS tokens from cool blue-tinted graphite to warm
brown-tinted terminal aesthetic per DESIGN.md spec. Replace inline
style color mappings in ActivityDot, AttentionZone, Dashboard,
ProjectSidebar, and SessionCard with data-attribute CSS selectors.
Fix duplicate className bug on SessionCard done-title element.

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

* fix: pre-landing review fixes — activity dot fallback + review stat color

Add base CSS fallback for activity-dot, activity-pill, and
activity-pill__text so null/unknown activity states render visibly
(gray) instead of invisible. Fix review stat card to use accent-orange
(matching kanban/sidebar/mobile review indicators) instead of cyan.

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

* chore: checkpoint current design branch state

* design changes

* feat(web): redesign session detail page — compact PR card, identity strip, layout reorder

- Redesign SessionTopStrip with simplified breadcrumbs, action buttons (Message/Kill)
- Replace stacked PR card with compact inline layout: title row + blocker/CI chips + collapsible comments
- Move PR card above terminal for better information hierarchy
- Replace vertical IssuesList with inline buildBlockerChips helper
- Add ~200 lines of new CSS classes for compact PR card design system
- Add changedFiles field to DashboardPR type

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

* feat(web): design system tokens, sidebar redesign, and component primitives

- Align all color tokens (status, bg, border, text) across three HTML mockups
- Rewrite ProjectSidebar to match finalized.html: rotation chevron, session status text, border-bottom project separators, 224px width
- Add packages/web/DESIGN.md: agent-readable reference for tokens, typography, component patterns, anti-patterns
- Add Badge.tsx: generic badge/chip/pill primitive with status/outline/default variants
- Add Button.tsx: ghost/primary/danger button primitive
- Update CLAUDE.md to reference DESIGN.md as required pre-read for web UI work

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

* style(design): FINDING-001 — card border-radius 0 → 6px to match mockup

* style(design): FINDING-002 — column border-radius 0 → 7px, border subtle to match mockup

* style(design): FINDING-003 — column header mono font, 500 weight, muted color to match mockup

* style(design): FINDING-004/005/006 — fix accent-blue/yellow/purple tokens to match mockup

* style(design): FINDING-007 — add --color-bg-card token (light #fff, dark #1c1b19)

* Refine dashboard design system and remove fixture flow

* Fix respond status colors in dashboard indicators

* Align tests with updated dashboard and metadata behavior

* fix(core): register notifier aliases consistently

* chore(web): drop uncovered showcase routes

* Add desktop PullRequestsPage coverage tests

* Remove generated coverage artifact

* Consolidate web design guidance into the root design system

* Fix working and ready status color tokens

* Fix sidebar collapse and inline kill confirmation

* fix(qa): ISSUE-001 - show all mobile filter chips

* Restore full title contrast in session cards

* Fix review feedback in dashboard state styling

* style: implement mobile responsive designs (feed, terminal-first, dense PRs)

Dashboard: replace accordion with urgency-sorted priority feed, horizontal scroll filter pills.
Session Detail: terminal-first layout with floating header, status pill, PR bottom sheet.
PRs: dense rows with CI dots, grouped sections, muted merged/closed rows.
Update tests to match new mobile layouts.

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

* Fix mobile terminal padding with PR sheet layout

* Polish mobile feed and session detail styling

* Align mobile dashboard layouts with gstack designs

* Fix mobile terminal actions and PR review labels

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-11 16:30:03 +05:30
Dhruv Sharma d908500791
Merge pull request #882 from DNX/feat/linear-spawn-branch-name
Honor Linear branchName as single source of truth for spawn worktree branches
2026-04-11 14:45:57 +05:30
Dhruv Sharma ba899f9c30
Merge pull request #1104 from ComposioHQ/feat/issue-1045
refactor: remove ao spawn --decompose feature
2026-04-11 10:22:22 +05:30
Dhruv Sharma b7464e5657 fix(spawn): persist userPrompt to metadata and prevent injection
writeMetadata() used a hard-coded field whitelist that silently dropped
userPrompt. The in-memory Session.metadata also never included it, so
the spawn response always returned null. Additionally, prompts containing
newlines could inject arbitrary key=value pairs into the flat-file
metadata format.

Fixes:
- Add userPrompt to writeMetadata() whitelist and readMetadata() return
- Populate session.metadata with userPrompt during spawn
- Strip newlines from prompts in both web API and CLI (defense-in-depth)
- Harden serializeMetadata() to replace newlines in all values
- Add prompt length validation (4096 max) in CLI to match web API

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-10 18:54:48 +05:30
prod-blip b88b664dbb fix(core): derive activity from runtime probe, not terminal status (#1081)
Previously, sessions with terminal statuses (merged, done, killed, etc.)
had their activity unconditionally set to "exited" without checking if
the agent process was actually still running. This caused the dashboard
to show incorrect activity state for sessions where the PR was merged
but the agent was still alive in tmux.

Now the runtime liveness check runs first for all sessions, and activity
is derived from the actual process state via getActivityState(). Terminal
status no longer forces activity to "exited".

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-04-10 16:20:26 +05:30
Harsh Batheja 1a476a415c test(core): fix stale @composio scope refs in plugin-registry tests
Two notifier alias tests in plugin-registry.test.ts still referenced
@composio/ao-plugin-notifier-slack after the @composio→@aoagents
rename in #983. The mock fallback hit the throw branch, the slack
plugin never got registered, and the assertions failed with
"undefined" / "called 0 times". CI on main has been red on these
two tests since #967 merged.

Other tests in the same file (webhook line 231, openclaw line 333)
already use @aoagents — these two were missed.
2026-04-10 13:09:30 +05:30
Harsh Batheja 3fb2ddf06d refactor: remove --decompose feature entirely
The --decompose flag and supporting decomposer module had two
unfixable bugs (#1045): it crashed when ANTHROPIC_API_KEY was unset,
and it created multiple branches/PRs per issue, fragmenting history
and complicating review/merge. Removing the feature instead of
patching either bug.

- Delete packages/core/src/decomposer.ts and all re-exports
- Drop @anthropic-ai/sdk dependency from @composio/ao-core
- Remove --decompose / --max-depth options from ao spawn
- Remove decomposer field from ProjectConfig (zod default-strip
  silently ignores existing decomposer: blocks in user yaml)
- Remove lineage / siblings from SessionSpawnConfig and the
  prompt-builder Layer 4 block (only used by decomposer)
- Gut the matching backlog reactor branch in web/services.ts
  so the polling path no longer hits the same bugs
- Remove decompose parameter from openclaw-plugin ao_spawn tool
- Drop decomposer mocks from web services.test.ts
2026-04-10 13:02:52 +05:30
harshitsinghbhandari f154f87add fix: merge upstream main and resolve conflicts for cursor agent
- Resolve @composio → @aoagents package renaming conflicts
- Add cursor agent to BUILTIN_PLUGINS in plugin-registry.ts
- Add cursor agent to AGENT_PLUGINS in detect-agent.ts
- Add cursor agent import and registration in plugins.ts
- Add cursor agent dependency and import in web services.ts
- Update cursor plugin package naming to @aoagents/ao-plugin-agent-cursor
- Add cursor agent to changeset linked group
- Fix test imports to use new @aoagents package naming

Co-Authored-By: Claude <noreply@anthropic.com>
2026-04-10 11:48:27 +05:30
Dhruv Sharma c9c41ad6dc
Merge pull request #967 from ChiragArora31/fix/notifier-alias-lifecycle-routing
fix(core): resolve notifier aliases in lifecycle routing
2026-04-10 03:02:36 +05:30
ChiragArora31 8ea1c38532 Merge upstream/main into fix/notifier-alias-lifecycle-routing 2026-04-09 21:55:05 +05:30
Prateek 27712442f8 fix: restore GitHub repo URLs to ComposioHQ/agent-orchestrator — only npm scope changed 2026-04-09 16:00:32 +00:00
Prateek 967e864f5a chore: rename @composio scope to @aoagents across all packages
Renames all npm package scopes from @composio/* to @aoagents/* and
updates GitHub repo references from ComposioHQ/agent-orchestrator
to aoagents/ao throughout the codebase.

- All package.json names and dependencies
- README badges, links, and install instructions
- Documentation references
- Changeset config
- Source code imports and test files
2026-04-09 15:59:33 +00:00
ChiragArora31 49df74172b fix(core): preserve alias-specific notifier instances 2026-04-09 19:47:26 +05:30
harshitsinghbhandari 04f3a7c27f fix(cursor): register plugin in all discovery/resolution layers
Address PR review feedback from #1076 to make Cursor agent fully functional.

## Changes

### Plugin Registration (High Severity Fixes)
- **core/plugin-registry.ts**: Add cursor to BUILTIN_PLUGINS array for config-based resolution
- **cli/plugins.ts**: Import and register cursor in agentPlugins map for getAgent/getAgentByName
- **cli/detect-agent.ts**: Add cursor to AGENT_PLUGINS array for detectAvailableAgents()
- **web/services.ts**: Import and register cursor plugin for dashboard SessionManager/LifecycleManager
- **web/package.json**: Add @composio/ao-plugin-agent-cursor dependency

### Activity Detection Fix (Medium Severity)
- **cursor/index.ts**: Remove overly broad blocked detection patterns (error:/failed:)
  - Compiler errors, test failures, and linter output are normal tool output
  - Terminal-based detection can't distinguish between actionable agent errors and normal output
  - Follow Aider/OpenCode pattern: only Claude Code detects blocked (has native JSONL)
  - Added comment explaining why blocked detection is removed
- **cursor/index.test.ts**: Remove blocked detection test cases

## Why These Changes Are Needed

Before these fixes:
- `defaults.agent: cursor` in config would throw "Unknown agent plugin: cursor"
- `ao spawn --agent cursor` would fail
- Dashboard couldn't resolve cursor agent (SessionManager/LifecycleManager failures)
- `detectAvailableAgents()` never discovered Cursor
- False "blocked" states when agent encountered normal compiler errors

After these fixes:
- Cursor agent fully discoverable and usable via config and CLI
- Dashboard can spawn and manage Cursor sessions
- Activity detection matches other terminal-based agents (Aider, OpenCode)
- No false positives from normal tool output

## Testing
-  cursor plugin tests: 51/51 passing (reduced from 52 due to removed blocked tests)
-  core typecheck: clean
-  web typecheck: clean
-  CLI can import cursor plugin without errors

Addresses: ComposioHQ/agent-orchestrator#1076

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-04-09 19:17:41 +05:30
Dhruv Sharma aa2177698a feat(spawn): support prompt-driven sessions without a tracker issue (#974)
- Add --prompt <text> flag to `ao spawn` CLI
- Accept prompt field in POST /api/spawn web route
- Persist userPrompt to session metadata on spawn
- Add userPrompt to DashboardSession type and serialize.ts mapping
- Show userPrompt in SessionCard footer for prompt-only sessions
- Include userPrompt in SessionDetail headline fallback chain
- Update orchestrator-prompt.ts with prompt-driven spawn examples

Closes #974
2026-04-09 18:43:55 +05:30
Gaurav Bhola 0613ac2970 Merge remote-tracking branch 'upstream/main' into feat/single-socket
# Conflicts:
#	packages/cli/__tests__/commands/start.test.ts
#	packages/cli/src/commands/start.ts
#	packages/core/src/__tests__/lifecycle-manager.test.ts
#	packages/core/src/agent-selection.ts
#	packages/core/src/lifecycle-manager.ts
#	packages/core/src/plugin-registry.ts
#	packages/core/src/recovery/validator.ts
#	packages/core/src/session-manager.ts
#	packages/web/src/__tests__/api-routes.test.ts
#	packages/web/src/app/sessions/[id]/page.tsx
#	packages/web/src/components/Dashboard.tsx
#	packages/web/src/components/PullRequestsPage.tsx
#	packages/web/src/components/__tests__/Dashboard.doneBar.test.tsx
#	packages/web/src/hooks/useSessionEvents.ts
2026-04-07 03:37:36 -07:00
Gaurav Bhola b4c108338f fix(core): allow external notifier with manifest name matching built-in
When an external notifier is loaded via path and its manifest.name
collides with a built-in (e.g. 'slack'), prepareConfig previously threw
because isBuiltin was true and the path field was present. Skip the
built-in guard for external loads — the path field in that case is the
loading path, not a stray user config value.
2026-04-06 23:25:42 -07:00
Gaurav Bhola 1531a914c0 fix(core): forward allSessionPrefixes in resolveSessionRole
Prevents cross-project misclassification where a worker session like
'app-orchestrator-1' of prefix 'app-orchestrator' could be matched as
an orchestrator for a shorter prefix 'app'.
2026-04-06 23:11:59 -07:00
Harsh Batheja f65e7d95bf 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 22:52:47 -07:00
Harsh Batheja 649fc67f6a 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 22:52:47 -07:00
harshitsinghbhandari 49426ccddb fix comments 2026-04-06 22:48:01 -07:00
harshitsinghbhandari 10163f0ec2 fix tests 2026-04-06 22:48:01 -07:00
Harsh Batheja 7e53542f9d 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-06 22:48:01 -07:00
harshitsinghbhandari c92c5ebaff fix: address illegalcall review comments
- Fix bad temp name for non-standard packages: use full packageName
  instead of last hyphen-segment to avoid collisions between packages
  like "custom-tracker-plugin" and "my-custom-plugin"

- Fix disabled top-level plugin blocking inline loading: when an
  existing plugin entry has `enabled: false`, it's now set to `true`
  when there's an inline tracker/scm/notifier reference for the same
  package/path

- Create validatePluginConfigFields helper to deduplicate the
  TrackerConfigSchema, SCMConfigSchema, and NotifierConfigSchema
  validation logic (was duplicated thrice)

- Fix multi-project external plugin order-dependency: tracker and SCM
  plugins now receive no project-level config at create() time, making
  them consistent with built-in plugins. Project-specific config is
  passed per-call via ProjectConfig argument, avoiding first-project
  wins behavior

- Document post-validation invariant for plugin field: added
  documentation clarifying that `plugin` is always populated after
  validateConfig() to help downstream consumers understand they can
  safely assume non-null after validation

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-04-06 22:46:29 -07:00
harshitsinghbhandari 8ffd6a6298 comments fixed 2026-04-06 22:46:29 -07:00
harshitsinghbhandari ee7f08ada7 performed review and fixed 2026-04-06 22:46:29 -07:00
harshitsinghbhandari 23cb1ee7a8 fix load builtin error 2026-04-06 22:46:29 -07:00
harshitsinghbhandari 3e6d2f97be feat(core): unify plugin config cleaning and enforce reserved fields for tracker and scm 2026-04-06 22:46:29 -07:00
harshitsinghbhandari 4645f6574b feat(core): enforce reserved fields in notifier configs and improve loader error handling 2026-04-06 22:46:29 -07:00
harshitsinghbhandari ff47d363c1 add error handling for path in config 2026-04-06 22:46:29 -07:00
harshitsinghbhandari e799c01086 fix(core): ensure consistent temp plugin name generation 2026-04-06 22:46:29 -07:00
harshitsinghbhandari 986c499acb fix: ensure notifier config is passed when manifest name differs
extractPluginConfig was being called with mod.manifest.name before the
config was updated from the temp name. When manifest name differs from
temp name (e.g., temp "teams" vs manifest "ms-teams"), extractPluginConfig
found no match and returned undefined, causing the plugin to be registered
without its configuration (webhook URLs, tokens, etc.).

Fix by reordering operations:
1. Validate and update configs with manifest.name FIRST
2. Extract plugin config AFTER (now finds the config by manifest.name)
3. Register plugin with its correct configuration

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-04-06 22:46:29 -07:00
harshitsinghbhandari 1396d07900 fix: prevent one project's bad config from breaking shared plugin
When multiple projects share the same external plugin, a misconfiguration
in one project (wrong expectedPluginName) would prevent the plugin from
being registered at all, silently breaking it for all projects.

Fix by:
1. Register the plugin FIRST, before validating individual project configs
2. Validate each project's config in its own try-catch
3. Log validation errors but continue processing other projects

This ensures:
- Plugin is always registered if it loads successfully
- Projects with correct config get updated
- Projects with bad config get a warning but don't break others
- Misconfigured projects fail at point of use with clearer errors

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-04-06 22:46:29 -07:00
harshitsinghbhandari b936114e52 fix: preserve multi-word plugin names from package specifiers
generateTempPluginName was splitting package names on both hyphens and
slashes, which truncated multi-word names like "jira-cloud" to just
"cloud".

Fix by first extracting the package name without scope, then using a
regex to match the common ao-plugin-{slot}-{name} pattern which
preserves the full plugin name including hyphens.

Examples:
- @acme/ao-plugin-tracker-jira-cloud -> jira-cloud (was: cloud)
- ao-plugin-scm-azure-devops -> azure-devops (was: devops)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-04-06 22:46:29 -07:00
harshitsinghbhandari 40f4efaca4 fix: address cursor bugbot review comments
- Strip package and path loading metadata from notifier config passed
  to plugin create(). These fields are used for plugin resolution, not
  plugin-specific configuration. Prevents filesystem paths from leaking
  into plugin config where they could conflict with plugin-specific
  fields of the same name.

- Remove collectExternalPluginConfigs from public API exports since it
  is only used internally within config.ts during validateConfig.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-04-06 22:46:29 -07:00
harshitsinghbhandari 8e4f4f6380 fix: address remaining PR review comments
- Fix multiple projects sharing same external plugin: change
  findExternalPluginEntry to findAllExternalPluginEntries which returns
  all matching entries, allowing all projects to be updated with the
  actual manifest.name

- Fix path without slashes misidentified as npm package: use separate
  pkg and path parameters in generateTempPluginName instead of combining
  them and checking for slashes. Local paths like "my-tracker" (no
  slashes) are now correctly returned as-is

- Replace console.warn with process.stderr.write for structured logging
  in plugin-registry.ts

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-04-06 22:46:29 -07:00
harshitsinghbhandari dfe879d7c2 fix: remove unused import and add missing location field
- Remove unused collectExternalPluginConfigs import from tests
- Add missing location field to _externalPluginEntries in test

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-04-06 22:46:29 -07:00
harshitsinghbhandari 470e53780d fix: address Copilot review feedback on external plugin support
1. Only set expectedPluginName when user explicitly specifies `plugin`
   - Auto-generated temp names no longer block manifest name inference
   - External plugins like @acme/ao-plugin-tracker-jira-cloud with
     manifest.name "jira-cloud" will now load correctly

2. Add structured location data to ExternalPluginEntryRef
   - New ExternalPluginLocation type with kind/projectId/configType
   - Avoids fragile string parsing of source (project/notifier keys
     can legally contain dots)

3. Expand home paths (~/) in inline plugin configs
   - Apply expandHome() to tracker/scm/notifier path fields
   - Consistent behavior with config.plugins path expansion

4. Improve error messages
   - Use "package" vs "path" in manifest mismatch errors instead
     of always saying "package"

5. Fix tests to use config._externalPluginEntries
   - Tests now check entries stored by validateConfig() instead
     of calling collectExternalPluginConfigs() again on modified config

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-04-06 22:46:29 -07:00
harshitsinghbhandari ebe6408586 feat(core): support external tracker, scm, and notifier plugins from config
Add support for specifying external plugins inline in tracker, scm, and
notifier configs using optional `package` or `path` fields.

**Config schema changes:**
- TrackerConfig, SCMConfig, NotifierConfig now accept optional `package`
  (npm) or `path` (local) fields alongside the existing `plugin` field
- When only `package`/`path` is specified, plugin name is auto-inferred
- When both `plugin` and `package`/`path` are specified, manifest.name
  is validated against the declared plugin name

**Example usage:**
```yaml
projects:
  my-app:
    tracker:
      plugin: jira  # optional - validates against manifest.name
      package: "@acme/ao-plugin-tracker-jira"
      teamId: "TEAM-123"
    scm:
      path: ./plugins/my-scm  # local plugin

notifiers:
  teams:
    package: "@acme/ao-plugin-notifier-teams"
```

**Implementation details:**
- collectExternalPluginConfigs() extracts external plugin entries from
  tracker/scm/notifier configs during config validation
- Auto-generates InstalledPluginConfig entries merged into config.plugins
- Plugin registry validates manifest.name matches expectedPluginName and
  updates config with actual manifest.name after loading
- Warns when plugin slot doesn't match configured slot

Closes #736

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-04-06 22:46:29 -07:00
Harsh Batheja 41c967a396 fix: use TERMINAL_STATUSES in lifecycle poll accounting
Replace hardcoded "merged"/"killed" checks in pollAll() with the
canonical TERMINAL_STATUSES set from types.ts. This ensures sessions
in done, terminated, cleanup, or errored states are correctly treated
as inactive for polling, active-count, and all-complete detection.

Closes #752
2026-04-06 22:46:29 -07:00
Your Name f07aedfa58 fix: resolve prompt delivery persistence and false warnings 2026-04-06 22:46:29 -07:00
Your Name 3d1913e690 fix: robust prompt delivery with retries and CLI warnings 2026-04-06 22:46:29 -07:00
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 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
ChiragArora31 4d22931180 fix(core): resolve notifier aliases in lifecycle routing 2026-04-07 08:23:03 +05:30