SIGN IN SIGN UP

AutoGPT is the vision of accessible AI for everyone, to use and to build on. Our mission is to provide the tools, so that you can focus on what matters.

0 0 73 Python

feat(copilot): autopilot UX polish + skills/followups LD kill-switches + unified Scheduled page (#13202)

## Why

Three things were left over after the autopilot consolidation shipped
(#13190 + #13195):

1. **Naming drift** — the briefing pill said "Copilot library" even
though the product is autopilot.
2. **Per-link hide on 0** — "1 skill · 0 follow-ups" reads as broken UX.
Either count being zero should hide just that link.
3. **No kill-switch** — skills + scheduled-followups inject a
`<available_skills>` / `<session_context>` block on every turn and gate
the four MCP tools. If something regresses we need to disable them
per-user without a redeploy.

Plus one piece of unification a sleep-late-Friday user asked for:
`/library/followups` should show **both** copilot followups AND
recurring agent schedules, since they're the same APScheduler primitive
with a `kind` discriminator and the UX of having two separate pages is
silly.

## What

### LD kill-switches (default-on)

Two new server-side LaunchDarkly flags, evaluated per-user, default
`true`:

- **`copilot-skills`** — gates the four MCP tools (`store_skill` /
`read_skill` / `delete_skill` / `list_skills`) and the per-turn
`<available_skills>` block. When off, `build_skills_context` returns
empty (no list query, no Redis hit) and every tool returns a structured
`feature_disabled` error.
- **`copilot-scheduled-followups`** — gates the `schedule_followup` MCP
tool and the `pending_followups` line inside `<session_context>`. When
off, the tool returns `feature_disabled` and `build_session_context`
collapses to the bare `session_id` line WITHOUT calling the scheduler
RPC.

Local override pattern unchanged: `FORCE_FLAG_COPILOT_SKILLS=false` /
`FORCE_FLAG_COPILOT_SCHEDULED_FOLLOWUPS=false` for dev.

### Briefing pill (`CopilotLibrarySummary`)

- Label: `Copilot library` → **`Autopilot library`**.
- Counts are independent now: shows `N skill(s)` only if N > 0, shows `M
scheduled` only if M > 0, drops the `·` separator when only one is
shown. Still hides the whole pill when both are 0 (no degenerate state).
- Scheduled count now sums **followups + graph schedules** so it mirrors
what `/library/followups` displays.

### Unified Scheduled page (`/library/followups`)

Route name unchanged for backward compat, but the page is now
"Scheduled":

- Title `Copilot follow-ups` → **`Scheduled`**.
- `useFollowupsPage` fetches BOTH `listCopilotFollowupSchedules` AND
`getV1ListExecutionSchedulesForAUser`, merges into a discriminated
`ScheduleItem[]`, sorts by `next_run_time` ascending.
- New `GraphScheduleListItem` component renders graph rows with an
emerald "Agent run" badge, agent name, cron label, View dialog showing
graph_id + schedule name, and a Delete flow that invalidates the
graph-schedules query.
- Existing `FollowupListItem` (copilot_turn) untouched — just rendered
as one branch of the polymorphic list.
- Empty-state copy: "No copilot follow-ups" → "Nothing scheduled" +
mention both surfaces.

## How (highlights)

- **Skills helper**: `is_skills_feature_enabled(user_id)` returns `True`
for anonymous (no user_id) so we don't surprise-break unauthenticated
paths; otherwise reads the flag with `default=True`.
- **Followups helper**: `is_followups_feature_enabled(user_id)` same
shape, lives in `session_context.py` so the gating function ships with
the consumer.
- **Cost half of the kill-switch**: when off, scheduler RPC / list-files
/ Redis fetches are all skipped — flipping the flag also drops the
per-turn cost to zero, not "still calls but ignores result".
- **Frontend merge over backend union**: kept the two existing typed
REST endpoints (`/schedules` returns `list[GraphExecutionJobInfo]`,
`/schedules/followups` returns `list[CopilotTurnJobInfo]`) and stitched
them on the client. A unified `/schedules/all` endpoint with a
discriminated union would have been cleaner but required a new model +
openapi regen for marginal benefit.

## Test coverage

- **Backend** (102 passed):
- `build_skills_context_empty_when_flag_disabled` — short-circuit
verified
- `build_skills_context_normal_when_flag_enabled` — happy path still
works
- `store_skill_returns_feature_disabled_when_flag_off` + `list_skills_…`
— tool gating
- `build_session_context_skips_scheduler_when_followups_disabled` —
verifies the scheduler RPC is NOT called when off (via
`AssertionError(...)` side-effect on the mock)
- `schedule_followup returns_feature_disabled_when_flag_off` — tool
gating + scheduler spy
- **Frontend**: existing skills + followups page suites should still
pass post-rename; will run via CI.
- **Live functional /pr-test**: planned on dev preview after the
redeploy that pulls this commit.

## Out of scope

- Unifying the MCP tool surface (one `schedule(kind=...)` instead of two
tools) — refactor deferred.
- New `/schedules/all` REST endpoint — frontend stitching is enough
today.
- Renaming the `/library/followups` route — backward compat with
existing deep-links from the briefing pill etc.

## Checklist

- [x] Code follows project style guidelines
- [x] Self-review performed
- [x] Tests added for new behaviors (flag-off paths)
- [x] No new warnings
- [x] Authorization unchanged — endpoints + helpers still scope by
`user_id` from auth
- [ ] CI green
- [ ] `/pr-test` on dev-preview validates pill + Scheduled page + flag
kill-switch
Z
Zamil Majdy committed
5a93de3f38b7c4e53fe5c3a44d35148b38beb43a
Parent: de39a05
Committed by GitHub <noreply@github.com> on 5/24/2026, 2:53:39 AM