Skip to content

fix(session-summary): make the summary panel work, and stop reporting an empty fold as up to date - #57

Merged
nplez1 merged 3 commits into
nplez1/mainfrom
nplez1/Notes-Issues
Oct 9, 2026
Merged

nplez1 merged 3 commits into
nplez1/mainfrom
nplez1/Notes-Issues

Conversation

@nplez1

@nplez1 nplez1 commented Oct 9, 2026 •

Copy link
Copy Markdown
Owner

ELI5

The Summary tab next to the Agents list is supposed to be a quick written brief of what an agent session is doing, so you don't have to scroll back through a terminal. It never showed a brief — not once, for any session — and it often claimed it was "Up to date" while the pane was empty. Separately, picking GitHub Copilot as the agent that writes those briefs offered a model list that stopped somewhere in the past. This PR fixes all three, plus the tab's icon.

What Changed

Before: opening the Summary tab on an agent session showed a loading skeleton forever, then settled on "Up to date" with nothing under it. Hiding and re-showing the panel changed nothing. Choosing Copilot in Settings → Agents → Session summaries listed models no longer offered and never listed current ones.

After: the tab produces a real brief for a session, using the transcript of the session itself — goal, what the agent is doing now, what it has finished, blockers, decisions, and a timeline. Copilot's model list is read from the installed CLI, so it always matches what that CLI actually accepts.

There are three independent causes, and each is fixed at its own layer.

1. The brief could not find the session's transcript

A summary is built from the agent CLI's own transcript file, not by re-reading the terminal. The code that located that file only knew the "native chat" agents — Claude, Codex, Grok, OMP, OpenCode. For anything else it returned nothing, so the pane settled on "No readable transcript for this session yet." and stayed there permanently.

On the machine this was reported from, the sessions are Pi (462), Copilot (16), OpenCode (11) and Cursor (2) — so almost every session was one the brief could not read. That is why it never worked.

It now also resolves:

  • Pi / Prime Agent — the hook already reports the exact session file, and Orca only exposes their session identity at all when that file exists, so there is nothing to search for.
  • Copilot — reports no file path, but its session id is the name of its session directory, so the transcript is $COPILOT_HOME/session-state/<id>/events.jsonl. The id is validated before being joined onto that path, so a malformed one cannot escape the sessions root.
  • A hook-reported path that no longer exists, or that points at a directory, is no longer mistaken for a transcript.

The decoding side already understood every one of these formats, so no parser changed.

2. "Up to date" was reported for folds that were running, or had failed

Two bugs in the header line:

  • The panel's "a fold is running" flag was only set once the first chunk of text came back from the model. So for the whole of a first fold — the entire window a user actually watches — the header said "Up to date" over a skeleton, and a fold that could not start never set the flag at all. It is now set when the fold starts.
  • The header text ignored the fold's own status, so "no readable transcript" and "the summarizer failed" both fell through to "Up to date". They now say what happened. A failure over a session that already has a good brief keeps counting how many events are unfolded instead, because the old brief is still on screen.

3. Copilot's model list was a hand-written snapshot

An array pinned 16 model ids, ending at gpt-5.5. The installed CLI offers 31, including claude-sonnet-5.5, claude-opus-5.5, gpt-6.1-sol, gpt-6-luna, grok-4.7 and kimi-k3. Anything released after that array was written was unreachable, and retired ids stayed on offer.

The CLI has no "list models" command and its interactive picker is filtered per account, but copilot help config documents the model setting as the list of ids it accepts — the CLI's own catalog. The probe now reads that list. An unparseable help format parses to nothing, which falls back to a refreshed pinned copy and is not cached, so the next attempt probes again rather than retiring the probe forever.

4. The icon

The tab and the pane header used a generic information circle — the same icon the app uses for notices, so the tab read as a warning rather than a place to read a brief. Both now use a sticky note.

Why

Why probe help config rather than refresh the array. A refreshed array is stale again the next time GitHub ships a model; the CLI's own catalog is the thing that tracks that, and we already have a probe pipeline for every other dynamic agent.

Why not fix only Copilot's list. The reporting user's own settings select Pi as the summarizing agent, and the sessions were Pi. The model list and the missing brief are genuinely separate defects that happened to be reported together.

Why the header changes are in the same commit as the transcript coverage. They are the two halves of one symptom: a user cannot tell "nothing is here yet" from "everything is done" if the header lies. Fixing only the first would have replaced a silent empty pane with a pane that still claimed to be up to date whenever a fold failed.

Linked Issue

N/A — this is a fork PR against nplez1/main, reported directly by the fork owner rather than filed as an issue first.

Visual Proof

Not attached: this work was done headlessly in a worktree, and the rendered check was not run. Recording it needs the app driven by CDP on a focused agent session plus a completed fold, which is more than the change warrants; say the word and I'll capture it before this goes anywhere near upstream.

The visual deltas are exact and pinned by tests, so a reviewer can see them without a screenshot:

  • Icon — the Summary tab and the pane header change from lucide-info to lucide-sticky-note (SessionSummaryPane.test.tsx asserts the class).
  • Header text — new strings for a session with no readable transcript ("No readable transcript") and for a failed fold ("Could not summarize this session"), replacing "Up to date" in both cases.
  • No layout, spacing, colour or typography change.

Testing

  • Automated tests added/updated
  • Manually exercised in the running app — the feature path was driven end to end in the main process (below), but no rendered-UI check was run

Full runs, against the pushed head

Every command below was run in this worktree against f6adc4ca02 — the head of this PR — after the commits were made, so the evidence describes exactly what is being reviewed. An earlier, narrower run during development produced 80 failures; 42 of them were phantom, because a bare pnpm exec vitest run does not export ORCA_BACKGROUND_LAUNCH=1 the way the repo's pnpm test wrapper does, which turned 41 green browser-manager cases red. Running the documented commands is what separated the real signal from that noise.

Command Result
pnpm tc clean
pnpm lint exit 0 — all 19 steps, including repo-wide oxlint with its max-lines rule
pnpm test 113,874 tests — 112,887 passed, 57 failed across 14 files, 1 expected failure, 929 skipped

All 57 failures are pre-existing and unrelated to this change. None of the 14 failing files is one this PR touches, and none of them has been modified since the upstream merge (git log 7c0a53a148..HEAD -- <paths> is empty, so they arrived red and unchanged). They fall into three classes:

Class Files Why they are red
Upstream-red logic — agent-session-*, agent-launch-*, structured-agent-session-* 9 Recorded in LOCAL-PATCHES.md as "proved not ours"; they reproduce on pre-merge 807aa6062c
Relay region correction 1 Needs the uninstalled cloud/ workspace (pg)
PTY / subprocess suites that only run red under the bun project 4 daemon/repro-13767-shell-ready-marker-lost-to-exec, daemon/shell-ready, shell-startup-feature-channel, orcad-bundled-runtime.integration produce no child output under the bun runtime and pass under the node project — a test-project assignment gap, the same class upstream re-seated in stablyai#26028

The third class only becomes visible with bun installed (pinned at config/.bun-version, 1.4.2); it is a property of this test environment, not of this diff — none of those four files is touched here.

Focused tests added or updated for this change

  • New src/main/session-summary/session-summary-transcript-source.test.ts (5 cases): a Pi session read from its hook-reported file, a Copilot session read from its session-state directory, a traversal-shaped Copilot id refused, a directory refused as a transcript, and an agent the fold cannot parse returning nothing.
  • session-summary-service.test.ts: the fold reports itself in flight before its first chunk returns.
  • session-summary-presentation.test.ts: "no readable transcript" and the failure case are named instead of "Up to date"; a failed fold over an existing brief still counts its backlog.
  • commit-message-agent-spec.test.ts: Copilot probes help config; the id parser reads only the model setting's block, adds auto back, and parses to nothing on a format change.
  • Changed-lines gate pnpm run check:code-quality:changed and the localization verifiers pass.

End to end

On the reporting machine, with the reporter's own settings (sessionSummaryAi = Pi / deepseek/deepseek-flash) and a real Pi transcript: the service reaches ready in 7.4s with a populated brief. Before this change the same call returned "no readable transcript". The Copilot probe was likewise run through the real main-process discovery path, returning 31 models with catalogOrigin: 'probe'.

Platforms actually exercised: macOS only, local worktree, no SSH. The Copilot path is macOS-verified (/opt/homebrew/bin/copilot); the Pi path depends only on the hook's reported path, which is platform-neutral, but Windows and Linux were not run.

Notes

  • Remote SSH / mobile: unaffected in the sense that nothing new crosses the wire — sessionSummary is local-only, and a paired web client still gets no tab. Transcript resolution deliberately stays local because the fold spawns the agent CLI locally.
  • Backwards compatibility: an unchanged or older host behaviour is preserved for every agent type that resolved before; agents still unresolvable return nothing and the pane says so, which is now an honest message rather than "Up to date".
  • Performance: one stat per open, plus one spawn of copilot help config when the probe runs (it reads local documentation, makes no network call, and is cached).
  • Known gaps, unchanged here: Pi transcripts decode only their text blocks for this consumer — Pi's thinking and tool-call blocks use shapes the shared decoder does not read, so a Pi brief is text-only. That decoder is shared with Claude, Codex and OpenCode, so it is deliberately out of scope. OpenCode sessions still need a hook-supplied transcript path.

AI Disclosure

Written by an AI agent (Pi, running a github-copilot model) working directly in this worktree, under the fork owner's direction and review.

Review

Agent skill upstream boundary

  • Not applicable — no skill installer source, tests, fixtures, registry entries, path tables, comments or documentation touched.

nplez1 added 3 commits October 8, 2026 20:45
…'s real state

The panel could not summarize the sessions this reported from, and when it could not
it said it was up to date. Both are fixed here, because either alone still leaves a
reader with an empty pane and a false assurance.

Transcripts the fold could never resolve. `readSessionSummaryTranscriptEvents`
resolved a file only through the native-chat resolver, which names Claude/OpenClaude,
Codex, Grok, OMP and OpenCode — so a Pi or Copilot session got `null`, the service
settled on `unavailable`, and the pane kept the empty state for the life of the
install. On the machine this was reported from that is 462 Pi sessions and 16 Copilot
ones against 11 OpenCode. Both now resolve:

- Pi and Prime Agent post `session_file` on every hook, and `agent-session-resume.ts`
  only returns their provider session at all when that path exists, so the hook's path
  *is* the transcript and there is nothing to search for.
- Copilot reports no path, but its `session_id` is the name of its session directory,
  so the file is `$COPILOT_HOME/session-state/<id>/events.jsonl`. The id is shape
  checked, because joining a hook string onto a root path unchecked is a traversal;
  a hook path that exists is preferred, and a directory is never mistaken for a
  transcript.
- `COPILOT_SESSIONS_DIR` is exported rather than re-derived, so the AI Vault scanner
  and this reader cannot drift apart on where Copilot keeps sessions.

Everything downstream already worked: `parseAgentSessionFile` dispatches on
`candidate.agent`, and both parsers publish into the consumer channel, so the fold
needed only a path and a parser name. Agents with neither still return null, and the
pane says so.

A fold reported "Up to date" while it was running, and after it failed. Two causes:

- `fold.running` was set only once a chunk came back from the model, so the entire
  first fold — the whole window a reader actually watches — rendered "Up to date"
  over a loading skeleton, and a fold that could not start never set it at all. It is
  now set when the fold starts.
- The header hint ignored `status`, so `unavailable` and `failed` both fell through to
  "Up to date". They now name what happened. A failed fold over a session that already
  has a good ledger keeps counting its backlog instead, because that ledger is still
  on screen and the events behind it are genuinely unfolded.

Verified: `pnpm tc` clean; 2150 tests across session-summary, activity, right-sidebar,
settings and text-generation; `check:code-quality:changed`, oxlint and the localization
verifiers pass. End to end on this machine with the reporter's own settings
(`sessionSummaryAi` = pi/deepseek-flash) and a real Pi transcript, the service reaches
`ready` in 7.4s with a populated ledger, which it could not do before this change.
Copilot's entries in the agent model picker were a hand-written array frozen at the
CLI build the list was read from. It ended at `gpt-5.5` and `claude-opus-4.7` while
the installed CLI offers 31 ids — `claude-sonnet-5.5`, `claude-opus-5.5`,
`gpt-6.1-sol`, `gpt-6-luna`, `grok-4.7`, `kimi-k3` among them — so the picker could
offer retired models and could never offer current ones. Every model released after
that array was written was unreachable.

The CLI publishes no `models` subcommand (`copilot help models` is an unknown topic,
and the interactive picker is policy-filtered per account), but `copilot help config`
documents the `model` setting as the list of ids it accepts. That list is the CLI's
own catalog, so probing it is what keeps the picker current. The parser reads only
that setting's bullet list, stops at the next documented setting, and adds `auto`
back: `--model` documents it as "let Copilot pick automatically" while the setting's
value list omits it.

Two things that would otherwise regress:

- Model discovery replaces the spec's list wholesale rather than merging metadata
  into it, so the parsed models carry the same low/medium/high/xhigh levels and `low`
  default the static entries did. Without that, every Copilot model would lose its
  effort control.
- The array stays as `COPILOT_FALLBACK_MODELS` for a machine with no `copilot` on
  PATH, refreshed to the catalog the CLI ships now. A help format change parses to
  nothing, which falls back to it and is not cached, so the next probe tries again
  instead of retiring the probe for the life of the install.

Also spells the effort flag the documented way. The earlier Copilot effort commit
relied on `--effort` being an alias of `--reasoning-effort`; that still works, but it
is absent from both `--help` and the CLI's own shell completion, so a fold would break
silently if the alias were dropped. Verified with a live one-shot prompt, not only
with `--help`.

Copilot's spec is the Source Control AI catalog as well, so commit-message model
selection gets the same live list. The probe lives in its own module because
`commit-message-model-parsers.ts` was already at its `max-lines` cap — extracted, not
suppressed.

Verified: the real main-process discovery path (`discoverCommitMessageModelsLocal`)
spawns `copilot help config` and returns 31 models with `catalogOrigin: 'probe'`,
carrying labels and effort levels; `pnpm tc` clean; the agent-spec, session-summary-ai,
ai-generation-settings and text-generation suites pass.
The Summary tab and the pane header both used a generic "info" circle, which is the
icon the app already uses for informational notices — it says nothing about a written
brief, and it made the tab read like a warning rather than a place to catch up on a
session.

Both now use a sticky note, the same icon the sidebar already uses for a worktree's
notes. SSR test updated from `lucide-info` to `lucide-sticky-note`.
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