Commit Graph

10 Commits

Author SHA1 Message Date
Dhruv Sharma 3ce3d7b0ab style: format merged main files 2026-07-05 18:22:32 +05:30
NIKHIL ACHALE cc75a519ab
fix: replace orchestrators through safe canonical branch handoff (#2338)
* fix: replace orchestrators through canonical handoff

* fix(tests): correct logic in TestRetireForReplacementCapturesAndReleasesWorkspace

* fix: enhance tests and add project config handling in service and workspace components

* fix: update project summary to include orchestrator agent and adjust related tests

* fix: block orchestrator replacement on runtime teardown failure

* fix: enhance orchestrator handling in ProjectSettingsForm and SessionsBoard components

* fix: rename parameter for clarity in projectConfigPtr function
2026-07-04 11:35:03 +05:30
Anirudh Sharma d18ea87f57
feat(tracker): GitHub issue intake (backend + dashboard) (#2325)
* feat(tracker): GitHub issue intake observer

Adds an opt-in daemon poll loop that spawns one worker session per
eligible open GitHub issue, keyed by the canonical "github:<native>"
id so restarts cannot double-spawn. Eligibility requires an explicit
label or assignee rule to avoid draining an entire backlog.

Provider dispatch goes through a TrackerResolver interface
(SingleTrackerResolver for now) so Linear/Jira can be added later as
new adapters plus a resolver-map entry, without touching the poll,
eligibility, or backoff logic. Reuses the existing rate-limit-aware
GitHub tracker adapter and backs off per-project on any poll failure
(including rate limits) instead of retrying in a tight loop.

Closes #2324.

* feat(frontend): surface GitHub tracker intake in project settings

Adds a Tracker Intake card to project settings (enable, repository
override, labels, assignee) with inline validation requiring at
least one label or assignee before intake can be saved. Session
cards and the inspector show the canonical "github:<native>" issue
id when a session was spawned by intake.

Regenerated frontend/src/api/schema.ts against the backend's
TrackerIntakeConfig. The provider is currently fixed to "github";
adding a picker for future providers is additive once the backend
enum grows.

Closes #2324.

* fix(tracker): start the intake observer unconditionally at boot

startTrackerIntake scanned projects once at daemon startup and skipped
starting the observer loop entirely when none had intake enabled yet.
Poll() already re-reads every project's config on each tick and skips
disabled projects there, so the boot-time gate only broke the common
case: enabling intake on a project after the daemon is already running
silently never got picked up until the next restart, with no error or
log line to explain why.

Always start the loop; the adapter (and its token resolution) stays
lazy regardless, so there's no added cost when intake is unused.

Found while manually verifying issue intake end-to-end against a live
dev daemon: enabled intake via the settings UI, saved successfully,
but no session ever spawned for a matching labeled issue.

* fix(frontend): show auto-detected repo link, hide labels input for now

The Electron app only registers git projects today, so the daemon
always has a usable git origin to derive owner/repo from when
trackerIntake.repo is unset (trackerRepo() in observer.go, already
covered by every Poll-level test in observer_test.go). Replace the
manual Repository input with a read-only link derived client-side
from the project's own git origin, purely for display — the daemon's
own derivation at poll time is unaffected either way.

Comment out the Labels input; Assignee is the only intake eligibility
rule editable from this form for now. form.intakeLabels/intakeRepo
stay wired into buildIntake so a value set via the CLI round-trips
on a UI save instead of being silently cleared.

* chore: format with prettier [skip ci]

* feat(frontend): offer issue intake at project creation

Add the GitHub tracker intake controls to the create-project sheet so a
project can opt into issue-driven worker spawning at creation time, not
only later via Settings.

- Extract the shared IntakeFields component (enable toggle, assignee
  input, validation, credential hint) plus buildIntake/intakeNeedsRule/
  deriveGitHubRepo helpers into IntakeFields.tsx, and consume it from
  ProjectSettingsForm so the two surfaces can't drift.
- CreateProjectAgentSheet renders IntakeFields (no repo-preview row,
  since the git origin isn't known there; the daemon derives the repo).
  The selection now carries an optional trackerIntake payload, gated by
  the same "requires a label or assignee" rule the backend enforces.
- Thread trackerIntake through Sidebar's onCreateProject into the
  POST /api/v1/projects config. No backend change: the endpoint already
  accepts a full ProjectConfig and the intake observer picks up newly
  enabled projects on its next tick.

Verified in the web preview: enabling reveals the assignee field and
gates submit until a rule is set; submit builds the intake payload.

* chore: format with prettier [skip ci]

* feat(frontend): simplify intake controls in the create-project sheet

Reduce the create sheet's tracker-intake block to the essentials: the
enable toggle plus an info icon whose hover tooltip explains what
enabling does, then the assignee input. Drop the descriptive intro
paragraph, the inline "requires a label or assignee" guard text, and
the credential hint — the sheet stays minimal and submit gating already
communicates the missing rule via the disabled button.

- IntakeFields gains a `compact` prop: hides the prose, folds the
  explanation into an Info tooltip, and drops the trailing help text.
  The tooltip is wrapped in its own TooltipProvider so the component is
  self-contained regardless of ancestor. The verbose settings card is
  unchanged (renders without `compact`).
- Assignee placeholder reworded to "type username or * for any".

Verified in the web preview: the info tooltip surfaces the one-line
description on hover, the assignee placeholder is updated, and no other
copy remains in the create sheet.

* perf(tracker): cache GitHub issue lists with ETag conditional requests

The intake observer polls Tracker.List for the same repo+filter every
tick, and each poll did an uncached GET + full JSON decode even when
nothing changed. Add HTTP conditional requests so unchanged polls are
cheap and don't consume the primary rate limit.

- Tracker holds a per-request-path cache of {etag, mapped issues},
  guarded by a mutex. The key space is bounded by intake-enabled
  repo/filter pairs, so no eviction is needed.
- List sends If-None-Match with the cached ETag (verbatim, preserving
  weak validators). On 304 it returns the cached issues without
  re-decoding (GitHub 304s don't count against the primary rate limit);
  a rotated ETag on the 304 is recorded. On 200 it stores the new
  {etag, issues}, or drops a stale entry if the response omits an ETag.
  A 304 without a prior validator falls back to an unconditional refetch.
- Extract the HTTP round-trip into roundTrip(); do() delegates to it so
  Get and Preflight keep byte-for-byte identical behavior. roundTrip
  owns If-None-Match, ETag capture, and 304 handling before
  classifyError.

Tests cover revalidation returning cached issues on 304, ETag rotation
on change, separate cache keys per filter, and no caching when the
response carries no ETag.

* feat(frontend): trim tracker intake copy in project settings

Reduce the settings Tracker Intake card to a one-line description
("Auto-spawn worker sessions from matching tracker issues.") and drop
the trailing credential/daemon-restart hint. The compact create sheet is
unaffected (it already renders neither).

* feat(tracker): scope v1 intake to assignee-only

Per PR review, labels were a persisted/API/CLI eligibility rule while the
UI only ever exposed assignee — surface beyond the intended v1 scope.
Remove labels from intake end-to-end; assignee becomes the required and
only eligibility rule.

- domain: drop TrackerIntakeConfig.Labels; Validate() now requires a
  non-empty assignee when enabled (was "labels or assignee").
- observer: pass only State+Assignee into ListFilter; drop the label
  match loop in issueMatchesConfig.
- CLI: remove --tracker-label and its plumbing.
- Regenerate openapi.yaml + schema.ts (labels gone from the schema).
- frontend: drop labels from IntakeForm/buildIntake/intakeNeedsRule and
  the settings form state; validation copy now "requires an assignee".
  Remove the label round-trip test; keep assignee coverage.

The generic domain.ListFilter.Labels adapter capability is retained;
only intake stops using it.

* fix(tracker): paginate GitHub issue listing with page-aware ETag cache

Per PR review, single-page listing is a correctness bug for intake, not
just a bounded v1 tradeoff: with more than a page of eligible open
issues, AO re-sees the same first page every poll, the persisted
issue_id dedupe skips the already-spawned first-page issues, and later
pages are never fetched or spawned.

- List now requests per_page=100 and follows the GitHub Link header
  rel="next" until no next link remains, accumulating issues across all
  pages. A maxListPages guard fails loud on a pathological Link cycle.
- The ETag cache is page-aware: each entry stores {etag, issues,
  nextPath} keyed by the per-page request path, so a 304 Not Modified
  still continues to the cached next page. roundTrip now surfaces the
  parsed next path alongside the ETag; do() delegates unchanged so
  Get/Preflight keep identical behavior.
- ListFilter.Limit becomes an optional total-result cap (page size is
  fixed at the provider max).

Tests cover multi-page accumulation, all-304 revalidation continuing the
cached chain, page-count shrink orphaning a stale entry, and Link
header parsing.

* style(session_manager): gofmt manager_test.go to unblock CI

The session-restart test carried a struct-literal alignment that gofmt
(and golangci-lint's goimports) reject, failing the build-test and lint
jobs. Whitespace-only reformat; no test logic changes.

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: whoisasx <adil.business4064@gmail.com>
2026-07-04 02:52:14 +05:30
Adil Shaikh 4557146681
fix: render raw activity state in inspector (#2278)
* fix: render raw session activity in inspector

* fix: hide unknown activity state in inspector
2026-07-04 00:29:32 +05:30
Adil Shaikh 6f8112e7b9
feat: surface SCM summaries in desktop (#263)
* feat: surface scm summaries in desktop

* chore: format with prettier [skip ci]

* fix: hydrate PR views from session facts

* chore: format with prettier [skip ci]

* fix: document PR summary DTOs

* fix: clean pr summary attention details

* chore: format with prettier [skip ci]

* fix: default codex sessions to bypass approvals

* fix: refine pr inspector summary

* chore: format with prettier [skip ci]

* fix: color pr diff metadata

* chore: format with prettier [skip ci]

* fix: move diff icon to file count

* fix: keep shell topbar on board routes

* fix: render all session prs on board

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-06-22 00:35:00 +05:30
swyam sharma 34a9fdb736
fix: surface session branch in frontend (#358)
Co-authored-by: Swyam Sharma <cruzer@Swyams-MacBook-Pro.local>
2026-06-21 14:57:37 +05:30
Vaibhaav Tiwari 708ec5db56
fix: recover terminal reattach after daemon idle (#354)
* fix: recover terminal reattach after daemon idle

* chore: format with prettier [skip ci]

* fix: harden daemon start recovery

* fix: cancel stale daemon start attempts

* fix: quarantine untrusted daemon base url

* chore: format with prettier [skip ci]

* fix: close daemon status race windows

* chore: format with prettier [skip ci]

* fix: bootstrap daemon trust before shell load

* test: trust mocked API base in PR hydration

---------

Co-authored-by: Vaibhaav <user@example.com>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-06-21 13:59:15 +05:30
Pritom Mazumdar b4a8fad215
feat(frontend): surface multiple PRs per session + reviewer in inspector (#335)
* feat(frontend): surface multiple PRs per session

Replace the single optional pullRequest on a session with a prs[] list and
render it across every PR surface: the inspector stacks one card per PR, the
PR board lists one row per attributed PR, and the board card shows a PR count
summary. useWorkspaceQuery now maps the live /api/v1/sessions prs[] into the
query (the frontend surfaced no PRs before this). Ordering is actionable-first
(open, draft, merged, closed).

Adds unit tests (SessionInspector, PullRequestsPage), a Playwright e2e spec,
and a multi-PR mock-data fixture.

* chore: format with prettier [skip ci]

* feat(frontend): swap inspector Changes tab for empty Reviews placeholder

The inspector rail's Summary tab already renders the multi-PR stack from
PR #237 work, so the Changes (Git rail) tab is the next inspector surface
to evolve. Reviews will land on its own backed by PR-scoped review data
(separate workstream); reserve the slot now with an empty placeholder so
the navigation lands before the data does.

- Tabs are now Summary, Reviews, Browser. The InspectorView union and
  the VIEWS array are updated; Reviews gets a message-bubble icon to
  distinguish it from Summary's list icon.
- ChangesView and its lucide imports (GitBranch, GitCommitHorizontal,
  Plus, Square, Trash2) and the unused Button import are removed; a
  small ReviewsView mirrors BrowserView's empty-state shape.
- styles.css drops the orphaned .inspector-changes__* block.
- SessionInspector.test.tsx asserts the new tab labels and that Reviews
  shows the empty placeholder.

* feat(frontend): wire the reviewer feature into the inspector Reviews tab

The multi-PR transplant left the Reviews tab an empty placeholder, which would
have regressed the existing session review feature. Move the reviewer panel
(trigger/re-run a review, open the reviewer terminal, surface verdict + status)
into the Reviews tab, gated on the multi-PR model: it reads the session's prs[]
to decide between the reviewer card and the "no PR yet" empty state, and pulls
review runs + project reviewer config straight from the daemon.

Reconciles the prs[]-from-session-list model across the suite: ShellTopbar and
pr-hydration fixtures carry prs[], useWorkspaceQuery tests drop the obsolete
per-session /pr fetch, and SessionsBoard restores the dropped board title.

Adds a Playwright reviews-tab e2e spec (reviewer card for a session with PRs;
empty state for one without).

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

* test(frontend): adapt multi-PR e2e specs to main's expanded fixture

The rebase pulled in main's larger mock-data fixture (4 workspaces, 13 PRs)
and its renamed refactor-mux title. Rewrite the PR-board assertion to verify
the actionable-first ordering invariant instead of a brittle full-list match,
and update the empty-state selector to the session's current title.

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

* chore: format with prettier [skip ci]

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-20 16:27:53 +05:30
neversettle 6ac65d2cd3
fix(frontend): populate session.pullRequest from /pr endpoint (#251) (#279)
* fix(frontend): populate session.pullRequest from /pr endpoint (#251)

WorkspaceSession.pullRequest was declared but never populated, so every
Boolean(session.pullRequest) check was dead: the Summary tab gated its PR
fetch on it (never ran), and the Board card and /prs page read it directly
(always empty).

Hydrate the field centrally in fetchWorkspaces — the single place that
builds session objects for the workspace, board, PR page, and sidebar — by
fetching GET /sessions/{sessionId}/pr per non-terminated session in parallel
and attaching {number, state}. A per-session fetch error degrades to "no PR"
rather than failing the whole workspace query; terminated sessions are
skipped. GET /sessions/{sessionId}/pr stays the single source of truth, so
no new backend endpoint and no changes to the consuming components.

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

* chore: format with prettier [skip ci]

* test(frontend): verify PR hydration end-to-end for a normal project (#251)

Drives the real useWorkspaceQuery + real SessionsBoard / PullRequestsPage
for an ordinary project (from /api/v1/projects) whose session has an open PR,
mocking only the HTTP client and router. Confirms PR facts fetched from
/sessions/{id}/pr flow through the shared workspace cache into both the Board
card ("PR #278 · open") and the PR page row.

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

* test(frontend): move PR hydration integration test into __tests__/integration

Cross-component integration test belongs in a dedicated folder, separate
from the co-located unit tests. No behavior change.

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

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-06-17 17:48:03 +05:30
yyovil c2c4404c7d
feat(frontend): scaffold for frontend with complimentary backend changes (#168)
* feat(frontend): rebuild Electron desktop UI as a React + Vite renderer

Replaces the skeleton Electron frontend with a full React 19 + TypeScript
renderer (Vite, electron-forge, contextBridge preload), plus the backend
additions it needs.

Renderer:
- TanStack Query + EventTransport (CDC SSE on /api/v1/events)
- TanStack Router file-system routing (hash history for the file:// origin)
- Tailwind + shadcn/ui, react-resizable-panels, Zustand UI state
- @xterm/xterm per-session PTY over /mux WebSocket + WebGL addon
- openapi-typescript + openapi-fetch types off openapi.yaml
- electron-forge packaging + update-electron-app auto-updater
- Vitest + RTL · Playwright

Backend:
- cors.go — allowlist-only CORS, handles Private Network Access preflight
  for app:// renderer -> loopback daemon
- session.TerminalHandleID exposed in domain + OpenAPI spec
- project.Path added to OpenAPI spec, service, store, and tests

DESIGN.md documents the emdash-matched dark UI (tokens, blue accent, status
glyph spec, orchestrator-led layout).

Co-authored-by: Ashish Huddar <ashish.hudar@gmail.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Entire-Checkpoint: b1e334c1e54a

* chore: format with prettier [skip ci]

---------

Co-authored-by: Ashish Huddar <ashish.hudar@gmail.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-06-10 11:40:17 +05:30