-
Notifications
You must be signed in to change notification settings - Fork 3k
feat(cli): reference prior sessions via @ and add completion tabs #7302
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
39 commits
Select commit
Hold shift + click to select a range
821979e
docs: design spec for @ session reference + tabbed completion UI
LaZzyMan 8a6c19d
docs: implementation plan for @ session reference + tabbed completion UI
LaZzyMan d03b338
feat(cli): add @session: mention ref parser
LaZzyMan c8c67af
feat(core): add SessionReferenceService for slimmed session injection
LaZzyMan 5fe3554
feat(cli): inject slimmed prior-session context on @session: mention
LaZzyMan fea0161
feat(cli): surface prior sessions as @ completion suggestions
LaZzyMan 36004b5
feat(cli): tabbed category layout for @ completion dropdown
LaZzyMan c3f482a
feat(cli): ←/→ tab switching for @ completion categories
LaZzyMan 05b876c
docs: mark @ session reference design as implemented
LaZzyMan dfd60e8
fix(cli): preserve assistant text on tool-call turns, keep newest tur…
LaZzyMan 2214de2
docs(core): clarify SessionReferenceService slimming rules
LaZzyMan 08bbbc2
perf(cli): remove @ completion input latency from session listing
LaZzyMan 9083aab
Revert "perf(cli): remove @ completion input latency from session lis…
LaZzyMan fdbb7e4
Merge remote-tracking branch 'origin/main' into lazzy/at-session-ref
LaZzyMan 0e81ea7
Merge remote-tracking branch 'origin/main' into lazzy/at-session-ref
LaZzyMan 0b6c474
fix(cli): harden session ref resolution and i18n tab labels (#7302)
qwen-code-ci-bot d7c341f
fix(cli): guard session ref I/O errors to never abort the turn (#7302)
ffd6645
fix(cli): strip session: prefix in completion filter and align tab gu…
40cc4cc
fix(cli): address review feedback on session refs and tab tests (#7302)
qwen-code-ci-bot 645c8a3
fix(cli): use SESSION_MENTION_PREFIX constant in completion filter (#…
qwen-code-ci-bot a50b760
fix(cli): address review feedback on session refs and tab tests (#7302)
34e57e8
fix(cli): address review feedback on session refs and tab tests (#7302)
e9e4bcf
fix(cli): surface real session lookup errors and test left tab switch…
58f29ea
fix(cli): reset suggestion indices when active category tab disappear…
d7eb219
fix(cli): address review feedback on session refs docs and tests (#7302)
7d90d1f
fix(cli): address review feedback on session refs and completion orde…
cb9341b
docs(cli): clarify title matching semantics in session reference desi…
015f283
Merge branch 'main' into lazzy/at-session-ref
wenshao c9be8c9
Merge branch 'main' into lazzy/at-session-ref
wenshao 5605c03
test(cli): assert error card in ambiguous session title test (#7302)
91d60e9
Merge branch 'main' into lazzy/at-session-ref
wenshao 7a54bfc
fix(core,cli): address review feedback on session refs (#7302)
qwen-code-ci-bot 65023a2
fix(core,cli): address review feedback on session refs (#7302)
c995c6d
fix(core,cli): address review feedback on session refs (#7302)
1e32b91
fix(core): address review feedback on session refs (#7302)
4f655fb
fix(core,cli): address review feedback on session refs (#7302)
qwen-code-ci-bot 37b9d99
fix(core): address review feedback on session refs (#7302)
74881a1
fix(cli): address review feedback on session refs (#7302)
eade9fd
fix(core,cli): address review feedback on session refs (#7302)
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
1,136 changes: 1,136 additions & 0 deletions
1,136
docs/superpowers/plans/2026-07-17-at-session-reference.md
Large diffs are not rendered by default.
Oops, something went wrong.
193 changes: 193 additions & 0 deletions
193
docs/superpowers/specs/2026-07-17-at-session-reference-design.md
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,193 @@ | ||
| # `@` Session Reference + Tabbed Completion UI — Design | ||
|
|
||
| Date: 2026-07-17 | ||
| Branch: `lazzy/at-session-ref` | ||
| Status: Implemented | ||
|
|
||
| ## 1. Goal | ||
|
|
||
| Two related enhancements to the interactive `@` mention feature: | ||
|
|
||
| 1. **Reference prior sessions via `@`.** Let a user pull a _condensed_ copy of an | ||
| earlier chat session's history into the current context as reference material — | ||
| without having to `fork` the session. The reference is injected as read-only | ||
| context, not as a resumed/forked timeline. | ||
| 2. **Tabbed `@` completion UI.** Because `@` now surfaces more categories | ||
| (files, directories, sessions, MCP servers/resources, extensions), redesign | ||
| the suggestion dropdown into a **tab-switched** layout so it stays usable. | ||
|
|
||
| ## 2. Decisions (locked) | ||
|
|
||
| | Dimension | Decision | | ||
| | ---------------------------- | --------------------------------------------------------------------------------------------------------------------------- | | ||
| | How the session is condensed | **Mechanical slimming, NO LLM call** — deterministic | | ||
| | What slimming keeps | **user + assistant text + a one-line summary per tool call** (name + status; no bulky tool results) | | ||
| | Referenceable scope | **Current project only** (project-hash scoped, same as `resume`) | | ||
| | How `@` surfaces sessions | **Bare `@` shows a Sessions group** (no prefix required to discover); a `@session:` prefix also works for direct addressing | | ||
| | Completion UI shape | **Tab switch** — top tab bar (All / Files / Sessions / MCP / Extensions), single list below shows the active tab | | ||
| | Injected size cap | **Fixed token budget with tail-retention** — drop oldest turns first, mark as truncated | | ||
|
|
||
| Explicitly rejected: LLM summarization (conflicts with the "inject slimmed | ||
| original, no model call" decision), reusing `ChatCompressionService` (it calls | ||
| the model and is coupled to a live `GeminiChat`), cross-project referencing. | ||
|
|
||
| ## 3. Architecture (Approach 1 — core service + localized UI changes) | ||
|
|
||
| Split into two independent halves: **backend** (parse + load + slim + inject) | ||
| lives in core and is a pure, unit-testable function; **frontend** (tabbed | ||
| dropdown) is a localized render-layer + keybinding change. | ||
|
|
||
| ``` | ||
| packages/core/src/services/ | ||
| sessionReferenceService.ts [NEW] load → slim → budget-trim → injectable text | ||
|
|
||
| packages/cli/src/ui/hooks/ | ||
| sessionMentionRef.ts [NEW] parse/build/validate @session:<id|title> | ||
| atCommandProcessor.ts [EDIT] new @session: routing branch → service → Part | ||
| useAtCompletion.ts [EDIT] new session-suggestion producer (category tag) | ||
| useCompletion.ts [EDIT] track activeCategory; reset index on tab switch | ||
|
|
||
| packages/cli/src/ui/components/ | ||
| SuggestionsDisplay.tsx [EDIT] tab bar + filter rows by active category | ||
| InputPrompt.tsx [EDIT] new keybinding to switch tab (←/→) | ||
| ``` | ||
|
|
||
| ### Module responsibilities | ||
|
|
||
| | Unit | Responsibility | In → Out | Reuses | | ||
| | --------------------------- | ---------------------------------------------- | ----------------------------------------------- | ----------------------------------------------------------------------- | | ||
| | `SessionReferenceService` | Turn a session id into injectable slimmed text | `sessionId` → `{ text, meta, truncated }` | `SessionService.loadSession`, `filterToDialog`, `estimateContentTokens` | | ||
| | `sessionMentionRef.ts` | Parse/build/validate `@session:` refs | string ↔ `{ id?, title? }` | — | | ||
| | `atCommandProcessor` (edit) | Route `@session:` → service → injected part | ref token → `Part` | `SessionReferenceService` | | ||
| | session producer (edit) | List referenceable sessions as suggestions | pattern → `Suggestion[]` (`category:'session'`) | `SessionService.listSessions` | | ||
| | `SuggestionsDisplay` (edit) | Render category tabs; show active tab's rows | `Suggestion[]` + `activeCategory` → TUI | — | | ||
|
|
||
| ## 4. Backend: session → injectable text | ||
|
|
||
| 1. **Resolve ref.** `@session:<arg>` where `<arg>` is a session UUID or a | ||
| (custom) title. UUID → direct; title → `SessionService.findSessionsByTitle` | ||
| (active, current-project only). Ambiguous title (>1 match) → the completion | ||
| UI already disambiguates; at submit time a still-ambiguous title is reported | ||
| as an unresolved mention (left as literal text, with a note), not guessed. | ||
| Note: title matching compares against the session's explicit `customTitle` | ||
| (set by auto-title or `/rename`), not the first-prompt label shown in the | ||
| completion dropdown. The picker always inserts `@session:<uuid>`, so the | ||
| mainline path is unaffected; a hand-typed `@session:<free text>` resolves | ||
| only when it case-insensitively equals an existing session title. | ||
| 2. **Load.** `SessionService.loadSession(id)` → `ConversationRecord.messages` | ||
| (`ChatRecord[]`). Guard: `sessionBelongsToCurrentProject` (already enforced | ||
| inside `loadSession`) — cross-project ids resolve to "not found". | ||
| 3. **Slim (deterministic, no model call).** | ||
| - Reuse `filterToDialog` to keep user + assistant visible text, dropping | ||
| thoughts. | ||
| - For records that are tool calls (`message.parts` functionCall / | ||
| `toolCallResult`), emit a single line: `[tool: <displayName> — <status>]`. | ||
| Do **not** include tool result bodies. | ||
| - Preserve chronological order; render as a labeled block: | ||
| `--- Referenced session "<title>" (slimmed, read-only) ---\n<body>`. | ||
| 4. **Budget-trim.** Estimate with `estimateContentTokens`. Cap at a fixed budget | ||
| (default `SESSION_REF_TOKEN_BUDGET`, ~8k, configurable). If over budget, | ||
| **drop oldest turns first** (tail-retention) and prepend a | ||
| `[earlier turns omitted]` marker so the model knows it is truncated. | ||
| Return `truncated: true` in meta. | ||
| 5. **Fast path.** If the loaded records contain a `chat_compression` record with | ||
| `systemPayload.compressedHistory`, that snapshot MAY be used as the slimmed | ||
| body directly (already condensed) — optional optimization, not required for v1. | ||
|
|
||
| Output shape: | ||
|
|
||
| ```ts | ||
| interface SlimmedSessionReference { | ||
| text: string; // labeled, budget-trimmed block | ||
| meta: { | ||
| sessionId: string; | ||
| title: string; | ||
| messageCount: number; | ||
| approxTokens: number; | ||
| }; | ||
| truncated: boolean; | ||
| } | ||
| ``` | ||
|
|
||
| ### Injection (atCommandProcessor) | ||
|
|
||
| New routing branch, ordered **after** extension/MCP refs and **before** the | ||
| filesystem path fall-through (so `session:` with its `:` is never mistaken for a | ||
| path). Resolved session text is added to `scopedMentionParts` (same bucket as | ||
| MCP-server context), so final assembly stays grouped-by-type. The `@session:…` | ||
| token is left verbatim in the prompt text; the model correlates it with the | ||
| `--- Referenced session … ---` block. A tool-call display card | ||
| ("Referenced session") is emitted, mirroring the existing Read File / Activate | ||
| Extension cards. Unresolved / not-found / cross-project refs fall back to literal | ||
| text with a surfaced note (never silently dropped). | ||
|
|
||
| ## 5. Frontend: tabbed completion UI | ||
|
|
||
| - **Suggestion type.** Add `category?: SuggestionCategory` to `Suggestion` | ||
| (`'file' | 'session' | 'mcp' | 'extension'`). Existing `sourceBadge` stays for | ||
| inline labels; `category` drives tab grouping. Files with no tag default to | ||
| `'file'`. | ||
| - **Producer.** In `useAtCompletion.ts` add a session producer that calls | ||
| `SessionService.listSessions` (current project), maps each to a `Suggestion` | ||
| with `category:'session'`, `label` = title (fallback: first user prompt, | ||
| truncated), `value` = `@session:<id>`, `description` = first user prompt | ||
| (when session has a custom title; otherwise omitted). Shown on **bare `@`** (like extensions) and filtered by pattern. | ||
| - **Rendering.** `SuggestionsDisplay.tsx` gains an optional top tab bar modeled on | ||
| `StatsDialog`'s `StatsTabs` / `handleTabChange` / `useKeypress` trio: tabs are | ||
| `All` + each non-empty category. `All` shows every suggestion (current | ||
| behavior); a specific tab filters to that category. | ||
| When only one category is present, the tab bar is hidden (no regression for | ||
| plain file completion). | ||
| - **Keyboard.** `↑/↓` selects within the active tab (unchanged). Tab **switching** | ||
| uses `←/→` (and/or `Shift+Tab`) via a **new keybinding Command**, because | ||
| `Command.ACCEPT_SUGGESTION` already binds BOTH `Tab` and `Enter` in | ||
| `InputPrompt.tsx` — reusing `Tab` would collide. `useCompletion` tracks | ||
| `activeCategory` and resets `activeSuggestionIndex` / scroll on switch. | ||
| - **State ownership.** `activeCategory` lives in `useCompletion` (alongside | ||
| `activeSuggestionIndex`), so accept/scroll logic stays in one place. | ||
|
|
||
| ## 6. Error handling | ||
|
|
||
| | Case | Behavior | | ||
| | -------------------------------------- | ----------------------------------------------------------------- | | ||
| | Session id not found / cross-project | Ref left as literal text + surfaced note; no throw | | ||
| | Title matches >1 session at submit | Reported as ambiguous unresolved mention (literal text + note) | | ||
| | Loaded history empty after slimming | Inject a short `(no textual content)` note instead of empty block | | ||
| | Over token budget | Tail-retain, prepend `[earlier turns omitted]`, `truncated:true` | | ||
| | `listSessions` fails (I/O) in producer | Session tab shows empty/"unavailable"; other tabs unaffected | | ||
| | Only one category available | Tab bar hidden; behaves exactly as today | | ||
|
|
||
| ## 7. Testing | ||
|
|
||
| **Core (unit, no model):** | ||
|
|
||
| - `sessionReferenceService`: slimming keeps user+assistant text; tool records | ||
| collapse to one-line summaries; tool result bodies excluded; chronological | ||
| order; budget trim drops oldest first + sets `truncated`; empty-history note; | ||
| cross-project id → not found. | ||
| - `sessionMentionRef`: parse/build/validate round-trip; UUID vs title; malformed. | ||
|
|
||
| **CLI (component / hook):** | ||
|
|
||
| - `atCommandProcessor`: `@session:<id>` routes to service and injects into | ||
| `scopedMentionParts`; ordering vs MCP/ext/file; unresolved → literal fallback; | ||
| display card emitted. | ||
| - `useAtCompletion`: session producer appears on bare `@`; filters by pattern; | ||
| current-project scoping. | ||
| - `SuggestionsDisplay`: tab bar renders per non-empty category; `All` shows all; | ||
| filtering by active category; single-category hides tab bar. | ||
| - `InputPrompt` / keybinding: `←/→` switches tab without triggering accept; | ||
| `Tab`/`Enter` still accept; index resets on switch. | ||
|
|
||
| **Manual / e2e:** type `@`, switch to Sessions tab, pick a prior session, submit, | ||
| confirm the slimmed block reaches the model as a labeled context part and a | ||
| "Referenced session" card is shown. | ||
|
|
||
| ## 8. Out of scope (v1) | ||
|
|
||
| - LLM-generated summaries of referenced sessions. | ||
| - Cross-project / cross-workspace session referencing. | ||
| - Referencing archived sessions by `@` (title search is active-only, matching | ||
| existing `findSessionsByTitle`). | ||
| - Left/right split-column dropdown layout (chose tab switch instead). | ||
| - Referencing a _range_ / specific messages within a session (whole-session only). |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.