Skip to content

feat(session-summary): give the session summary a right-sidebar home and its own model setting - #56

Merged
nplez1 merged 10 commits into
nplez1/mainfrom
Notes-Issue
Oct 7, 2026
Merged

nplez1 merged 10 commits into
nplez1/mainfrom
Notes-Issue

Conversation

@nplez1

@nplez1 nplez1 commented Oct 7, 2026 •

Copy link
Copy Markdown
Owner
Files Added Deleted Net
Test 12 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​931 $\color{#cf222e}{\Huge{\mathbf{−}}}$​19 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​912
Prod 32 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​1354 $\color{#cf222e}{\Huge{\mathbf{−}}}$​427 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​927

ELI5

Orca can write a short brief about what an agent session did: what it is working on, what it finished, what is blocking it, and a timeline of highlights. That feature was merged, but it never appeared anywhere — the panel only existed inside a page the app can no longer open. This puts it in the right sidebar, next to Agents, and gives it its own agent/model/effort setting so its cost can be tuned on its own.

What Changed

Before: there was no way to see a session summary. The panel was mounted only inside the Activity page's detail pane, and nothing has opened that page since the agent list became a sidebar tab (upstream stablyai#18222), so no build ever rendered it. Its model settings were also borrowed from the commit-message Source Control AI recipe: editing commit-message AI silently changed summary cost, and editing the commit prompt looked like it should change summaries when it could not.

After:

  • A Summary tab in the right sidebar (info icon), beside Agents. It briefs the agent session you have focused in the active workspace and nothing else; with nothing focused it asks you to focus an agent session.
  • Settings → Agents → Session summaries owns its agent, model, thinking effort, and custom command. Unset means your default agent with its default model and effort, so summaries work without configuring anything.
  • Agents whose model list only exists in their CLI (Pi) now list real models with their effort levels — 63 of them on this machine, from pi --list-models. Copilot's hosted Claude models expose effort like its GPT models already did.
  • The effort dropdown is always visible: enabled with the model's levels, or disabled with This model has no effort setting.
  • A finished or hibernated session still summarizes when you focus its pane.

Mechanism:

  • A new static summary tab renders the panel in RightSidebarPanelContent. The ledger view and header state moved out of the Activity strip (activity/session-summary-ledger-view.tsx, activity/session-summary-presentation.ts) so both hosts render one component instead of two copies.
  • The subject is getFocusedAgentPaneKeyForWorktree (right-sidebar/session-summary-subject.ts). The pane keys on the pane and its provider session, because a new session can start in the pane you are already focused on.
  • The fold reads the new sessionSummaryAi block (shared/session-summary-ai.ts) instead of Source Control AI's commitMessage operation. Resolution and the picker share one rule for the effective model and effort, so the dropdown always names what the CLI is actually given.
  • Dynamic agents are probed once through the existing git:discoverCommitMessageModels path; only a real probe result is cached, keyed by agent, with a Refresh models control.
  • Laziness, incrementality and caching are unchanged from feat(session-summary): on-view session summaries for agent sessions #51: a fold runs only while the pane is open, only messages past the ledger's cursor are extracted, and the cached brief is shown immediately.

Why

  • A right-sidebar tab rather than resurrecting the Activity page. That page is unreachable, and the other thing it held (the agent list) has already moved to the sidebar. A tab puts the panel where agents now are.
  • Its own setting. A summary is not a source-control action: it feeds no command template and no PR/branch flow. Borrowing the commit-message recipe made its cost untunable on its own. The shape is reused (agent, per-model effort, custom command) — including the selection code, which the AI-author card now shares instead of duplicating.
  • Local-only discovery, host-keyed storage. The fold spawns the agent CLI locally and reads the transcript from the local filesystem, so a remote host's model list would offer models that spawn cannot run. The list is stored per host so a future host-aware fold needs no migration, but resolution reads the local slot.
  • Per-model effort, always visible. Effort is stored per model (selectedThinkingByModel), so a model that advertises none cannot be given one; hiding the control made that read as a missing feature.

Linked Issue

None — fork-local feature work (no external issue exists; happy to attach one if the maintainer wants tracking).

Fixes #

Visual Proof

A "before" capture is impossible: the panel rendered in no build, so there was no screen to photograph. Evidence instead, both from a hidden dev instance over CDP:

  • After — the new tab and pane (info icon, focus prompt when nothing is focused):

    summary pane

  • After — the new setting with a probed dynamic agent (Pi, Deepseek Deepseek Flash, effort Low, Refresh models):

    summary settings

The setting's before/after is real and visible: Pi previously offered a single Config default entry and no effort control at all, because its model list exists only in its CLI output.

Testing

  • pnpm tc clean. Changed-code quality gate, localization coverage/catalogs/extraction, and the fork's newer gates (check:upstream-identity-literals, check:max-lines-ratchet, check:dead-classes) all pass.

  • 10,438 tests pass across 1,140 files in the touched areas (settings, right-sidebar, lib, shared, main/session-summary). New tests cover: refusing an unusable agent instead of substituting one, creating the fold's scratch directory, honouring a probed model's effort, keeping the spec default in the picker list, the session-identity remount, the missing-bridge pane state, and the fields component's three effort states.

  • Rendered checks (hidden dev instance, no window shown): the tab and pane render; the setting drives sessionSummaryAi while commitMessageAi stays untouched; Pi's probe caches 63 models in both the agent slot and the host-keyed slot; a thinking: no Pi model shows the disabled effort control with its explanation; the value persists in profile state.

  • macOS only. No Windows/Linux/SSH run, and no real end-to-end fold against a live agent CLI — the fold's own behaviour is unchanged from feat(session-summary): on-view session summaries for agent sessions #51 and covered by its unit tests.

  • I manually tested these changes locally

  • Automated tests added/updated, or explained why not below

AI Disclosure

Authored with the pi coding agent (xiaomi/mimo workhorse models) under human direction, then reviewed by github-copilot/gpt-6.1-sol at high effort. The review raised two blockers and several should-fix findings, all fixed in the last three commits:

  1. The fold's scratch directory (<tmpdir>/orca-session-summary) was never created, so every fold would have failed with ENOENT — created before spawning.
  2. An unusable agent setting silently ran the default agent (transcript to a provider the user did not choose) — now fails with a message naming the setting.
  3. A picked effort was dropped for probed models, and the displayed model could differ from the executed one — one rule now serves both sides.
  4. A new session in the same pane kept the previous session's brief — the pane keys on the provider session too.
  5. Discovery was cached forever — added Refresh models.
  6. A paired web client offered a tab it cannot run — the tab is omitted when the bridge is missing.

Agent skill upstream boundary

  • Not applicable, or this change follows docs/reference/agent-skill-sharing-upstream-boundary.md and copies or mechanically translates no upstream skill-installer source, tests, fixtures, registry entries, path tables, comments, or documentation.

Notes

  • Security: a fold runs only the agent configured for summaries (or your default agent); an unusable configuration fails rather than substituting a provider, so a transcript is never sent somewhere the user did not choose.
  • Cross-platform / SSH: the spawn path is the existing cross-platform spawnSourceControlAgent; no bare rg, no shell-string spawning, no new path handling. The right sidebar and settings are desktop surfaces.
  • Backwards compatibility: sessionSummaryAi is an optional settings key that older builds ignore, and the new summary tab is dropped by an older host's tolerant ui.set path (tolerateUnknownValues) rather than failing the batch.
  • Performance: no polling and no background folds; one probe per dynamic agent, cached, refreshable on demand.
  • Known and deliberately left open: while the pane stays open, new messages do not trigger another fold (re-folding on status changes would spend tokens continuously for a pane the user may be leaving); the feature-wall AI-author card does not probe dynamic agents, so Pi still shows its stub there; and parsePiModels double-labels Pi models ("Deepseek Deepseek Flash"), which Native Chat's picker shares.

Checklist

  • This PR is small and focused
  • I explained what changed and why (ELI5, the user-facing before/after, the mechanism, and why over the alternatives)
  • Before/after screenshots or videos attached for UI changes, or N/A with reason
  • Self-reviewed for correctness, security, and performance
  • Cross-platform, SSH/remote, and path/shortcut impact considered (or N/A)
  • pnpm lint, pnpm typecheck, pnpm test, and pnpm build pass (or CI will cover; local preferred)

nplez1 added 10 commits October 7, 2026 00:06
The brief merged in #51 could never appear. It mounted only inside
ActivityThreadDetailPane, which only ActivityPrototypePage renders, and nothing
has called openActivityPage since the agent list became a sidebar tab (upstream
stablyai#18222). The panel was dead UI in every build.

Give it a live surface: a static right-sidebar `summary` tab, following the agent
session focused in the active workspace and nothing else.

- Strictly focus-driven (`getFocusedAgentPaneKeyForWorktree`). Another agent in
  the same workspace is not this pane's subject: showing the most recently active
  one reads as though it were the session the user is looking at. With nothing
  focused the pane asks the user to focus an agent session.
- Finished sessions already qualify, now pinned by a test: the fold resolves its
  transcript from a provider session, and main retains a `providerSessionOnly`
  remnant row for a dropped status row (`server-cleanup.ts`), so a hibernated pane
  the user focuses summarizes like a live one.
- The ledger view and header state are extracted from the activity strip
  (session-summary-ledger-view.tsx, session-summary-presentation.ts) so both hosts
  render one component instead of two copies.
- The tab is declared in the three allowlists a static tab needs: the shared
  union, the client-ui schema, and the main-process persistence normalizer.
  Dropping any one makes the tab vanish or reset to Explorer on restart, so the
  regression test moves onto the new value.

Wire-safe in both directions: an older host drops a value it cannot express
(tolerateUnknownValues) instead of failing the whole ui.set batch, and an older
client normalizes an unknown tab to Explorer.

Also adds docs/reference/session-summary.md, which two source files already
referenced but which did not exist.
A summary borrowed the commit-message Source Control AI recipe, so its cost could
not be changed without changing commit messages — and editing the commit prompt
looked like it should affect summaries when it never could. Give the fold its own
setting, same shape, separate storage:

- `sessionSummaryAi` (Settings → Agents → Session summaries): agent, model,
  per-model thinking effort, and the custom command for a custom agent. Resolved
  by `shared/session-summary-ai.ts`; the fold now reads it instead of operation
  'commitMessage'. Unset means the default agent and that agent's default model
  and effort, so nothing has to be configured for summaries to work.
- The selection rules (capability lookup, model/effort seeding on switch, patch
  building) move to `lib/ai-generation-settings.ts`, and the picker fields to
  `settings/ai-generation-fields.tsx`, so the AI-author card and this block share
  one implementation instead of two. The block is also registered in settings
  search, which is how the Agents pane is reached.
- Primitive typography is left to the primitives: the moved fields no longer pass
  `text-xs` to <Label>/<SelectTrigger> or `font-mono text-xs` to <Input> (the
  design-system gate forbids restyling them), and the labels sit above their
  controls instead of in a 92px column that only fitted the smaller type.

Tests: resolver units including "ignores the source-control AI choice entirely",
the fold-params contract, and the settings block's own behaviour.

Verified in the running app: the block shows Codex / GPT-5.4 / High from
`sessionSummaryAi` while `commitMessageAi` stays null, and the value persists in
profile state.
Copilot's hosted Claude entries listed no thinking levels, so the model/effort
pickers hid the effort control for exactly the models users pick when they want
reasoning — while the CLI accepts `--effort` (an alias of `--reasoning-effort`)
for any of its models and validates the value list globally, not per model.

Declare the same low/medium/high/xhigh levels the GPT entries already use, with
the same 'low' default, so those entries expose effort in Settings and in the
per-action recipes.
The picker could not offer a model for a dynamic agent: Pi advertises a single
`Config default` stub, and its real list only exists in its CLI output. Selecting
Pi therefore gave no model choice and no effort control at all, even though the
fold passes `--model` and `--thinking` and `parsePiModels` reports 60+ models with
effort levels.

Probe once per agent and cache the answer:

- Resolve the selected agent through the spec, then replace its model list with
  whatever a probe reported for that agent (`discoveredModelsByAgent`, keyed by
  agent so another agent's probe can never be applied to this one). The existing
  panel only ever read discovered models in the resolver, never in the picker.
- Probe through `git:discoverCommitMessageModels` with no worktree or connection:
  host-native, which is where the fold spawns the CLI. Cache only a real probe —
  a `spec`-origin result is the built-in list and caching it would retire the
  probe for that agent permanently.
- Storage stays host-keyed (`discoveredModelsByAgentByHost.local`) while the pane
  resolves the local slot, because the fold reads the transcript locally too; a
  host-aware fold can then land without a migration.
- The pane says `Discovering models…` while the probe runs and falls back to the
  built-in list with an explanation when it fails, instead of silently showing a
  one-entry list.

Verified in the running app: selecting Pi exposes 63 probed models with effort
levels (Model: Deepseek Deepseek Flash, Thinking effort: Low) and caches them in
both slots.
The effort dropdown rendered only when the selected model declared thinking
levels, so it silently vanished for models that take none — Pi's four
`thinking: no` rows, Copilot's `auto` and `gpt-4.1` — which reads as a missing
feature rather than as a property of the model.

Render the row whenever a model is selected: enabled with that model's levels, or
disabled showing `—` with a one-line explanation. Levels stay model-scoped, which
is how `selectedThinkingByModel` is modelled.

Verified in the running app with Pi: `deepseek/deepseek-flash` shows an enabled
Low/…/Xhigh control, `omlx/Laguna-S-2.1-oQ4e` shows the disabled control and the
explanation, and the Agent and Model dropdowns stay independent of it.
…usable agent

Two ways a fold could not do what the user asked:

- The scratch cwd (`<tmpdir>/orca-session-summary`) was never created, and the
  launcher passes it straight to spawn, so every fold failed with ENOENT on a
  machine that had never had that directory. The brain now creates it, because it
  is the component that requires it.
- A resolver that reported "nothing usable is configured" (Custom with an empty
  command, or every candidate agent disabled) was turned into the default agent.
  That runs a provider the user did not choose — possibly one they disabled — with
  their transcript. It now fails the fold with a message naming the setting.
The pane could show one model while the fold ran another, and an effort the user
picked could be dropped:

- Effort was resolved through the spec/synthesis helper, which guesses thinking
  support from the model id (`withOpenAiThinking` matches gpt-5/codex). A probed
  Pi model therefore produced no thinking level, so picking High changed nothing.
  Resolution now prefers the probed model's own levels.
- A model a probe no longer lists was displayed as replaced while execution still
  passed the stale id; conversely, an unset choice displayed the probe's first
  model while execution passed `default`. The spec default now stays in the list,
  and a stored id is validated against the probe (or, for a static agent, the spec).

Both sides now read one rule, so what the dropdown says is what the CLI is given.
…it can do

- The pane keyed only on the pane key, so a new session started in the pane the
  user was already focused on kept the previous session's brief. The key now
  includes the provider session id, which makes the service reset the ledger.
- The tab was offered in every window, but a paired web client composes no
  `sessionSummary` bridge, so the pane would sit on its loading state forever.
  The activity bar now omits the tab when the bridge is missing, and the pane
  says so if it is somehow opened.
- A successful probe was cached for the life of the install with no way to list
  again, so models the CLI began reporting were unreachable. The pane offers
  Refresh models for a dynamic agent.
@nplez1
nplez1 merged commit ed0be20 into nplez1/main Oct 7, 2026
39 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant