Skip to content

pr/8dd0619a7 featcoc read only native copilot cli ses#327

Merged
plusplusoneplusplus merged 37 commits into
mainfrom
pr/8dd0619a7-featcoc-read-only-native-copilot-cli-ses
Jun 14, 2026
Merged

pr/8dd0619a7 featcoc read only native copilot cli ses#327
plusplusoneplusplus merged 37 commits into
mainfrom
pr/8dd0619a7-featcoc-read-only-native-copilot-cli-ses

Conversation

@plusplusoneplusplus

Copy link
Copy Markdown
Owner
  • feat(coc): read-only native Copilot CLI sessions dashboard view
  • Improve Copilot Sessions panel UI
  • Make Copilot Sessions panel more compact
  • Dedup native Copilot sessions against CoC process store
  • Deep-link Copilot Sessions and hide background-job sessions
  • feat(coc): add compact LLM tool parameter summarization util + contract types
  • feat(coc): wire compact LLM tool param metadata into workspace config API
  • feat(coc): compact expandable param summaries in LLM Tools settings
  • feat(coc): parse native Copilot session-state events.jsonl into rich turns
  • feat(coc): reconstruct native Copilot session detail conversation (AC-02)
  • feat(coc): render native Copilot session detail as a rich chat transcript (AC-03)
  • feat(coc): add Dreams tab to Knowledge nav group (AC-01)
  • feat(coc): relocate Dreams provider activity into the Dreams tab (AC-05)
  • feat(coc): move dreams.enabled toggle into the Dreams tab (AC-03)
  • Add Dreams idle interval admin setting
  • Add Dreams run defaults to admin tab
  • Fix Dreams admin defaults dirty tracking
  • test(coc): cover LLM tools param affordance keyboard a11y + narrow-screen layout
  • docs(coc-knowledge): refresh Copilot Sessions tab for rich-transcript reconstruction
  • test(coc): render real ConversationTurnBubble for native reconstructed turns
  • test(coc): cover panel→mapper→real-bubble native transcript render path
  • fix(coc): make embedded Dreams admin panel scrollable
  • Hide summary-only Copilot title sessions
  • Add native CLI session parsers
  • Add native CLI session providers
  • Wire native CLI session routes
  • Add CLI sessions dashboard tab
  • Harden Codex native session image parsing
  • Deduplicate native CLI session list rows
  • Consolidate native CLI sessions flag
  • Tighten Claude native session workspace scoping
  • Default CLI sessions to Copilot
  • Test native CLI session HTML escaping
  • Move CLI Sessions sub-tab between Activity and Git
  • Capture Codex sub-agent tool calls
  • Capture Claude sub-agent tool calls

plusplusoneplusplus and others added 30 commits June 14, 2026 05:39
Add a disabled-by-default features.nativeCopilotSessions flag that exposes a
workspace-scoped, read-only "Copilot Sessions" repo tab over the server
user's native GitHub Copilot CLI session store (~/.copilot/session-store.db).

Server:
- New native-copilot-sessions query service: short-lived read-only SQLite
  connections, workspace scoping by native cwd (normalized prefix) or
  repository (origin-remote owner/repo, case-insensitive), parameterized
  metadata filters, literal-quoted FTS text search with snippets, newest
  updated_at sorting, pagination, turn counts, and typed
  db-missing/db-invalid unavailable states that never crash the dashboard.
- GET /api/workspaces/:id/native-copilot-sessions (+ /:sessionId detail with
  ordered turns, char counts, and search-index diagnostics), live
  feature-guarded; disabled flag returns 200 {enabled:false}.

Client/SPA:
- coc-client nativeCopilotSessions domain with typed contracts.
- Copilot Sessions tab gated by the runtime flag; two-pane read-only panel
  with search/sessionId/branch/date filters, disabled/unavailable/loading/
  empty/error states, "Native Copilot CLI session" labels, read-only badge
  with helper copy, "No assistant response stored" labeling, plain
  pre-wrapped text rendering (stored HTML/scripts never execute), and no CoC
  chat actions.

Tests:
- Server route + service tests using synthetic temp SQLite fixtures only:
  disabled flag, missing/invalid DB, two-repo workspace filtering, sorting,
  pagination, turn counts, FTS hits/snippets, combined filters, no-result
  state, hostile filter input parameterization, detail ordering/diagnostics,
  and workspace-scoped 404s.
- SPA panel tests for all UX states, read-only labeling, inert script text,
  filter wiring, and absence of CoC chat action controls.

Docs: coc-knowledge references (rest-api, server-architecture,
dashboard-spa) and packages/coc/AGENTS.md read-only invariant.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Fix the broken master-detail width (nested flex-1 + max-w-[46%] collapsed
the list to ~23% leaving a large empty gap) by giving the list a clamped
~42% column and letting the detail fill the rest. Remove the redundant
per-row 'Native Copilot CLI session' badge that caused horizontal column
clipping, and replace the cramped 4-column table with a single-column list
of session cards (ID chip, timestamp, summary, repo, turn/branch pills,
selected-row accent bar). Polish the filter bar (full-width search with
icon, focus rings) and the empty detail state (icon + centered copy).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Tighten the panel density: reduce header/filter/row/detail padding and
gaps, shrink control heights (h-8 -> h-7), drop font sizes to 10-13px,
and shrink badges/labels and pre block max-heights so more sessions and
turns fit on screen without horizontal or vertical waste.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The Copilot SDK/CLI writes one session id per conversation that equals the
native session-store id (~/.copilot/session-store.db sessions.id) and the
value CoC persists as processes.sdk_session_id. The read-only Copilot
Sessions tab previously listed native sessions independently, so sessions
already tracked in the Activity tab appeared in both places.

Add ProcessStore.getSdkSessionIds(workspaceId) (SQLite-backed, one indexed
query over processes.workspace_id) returning the distinct set of recorded
sdk_session_ids. The native session list route passes that set as
excludeSessionIds; the service hides matching sessions during its existing
in-memory workspace filter (O(1) per row) and returns deduplicatedCount.
The panel shows a 'N sessions hidden — already tracked in CoC Activity'
hint.

Tests: forge getSdkSessionIds unit tests; service-level exclusion and
count; route-level dedup via SqliteProcessStore; SPA hint presence/absence.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Deep-link support: the selected native session is reflected in the URL
hash (#repos/{wsId}/copilot-sessions/{sessionId}) via new Router helpers
parseNativeCopilotSessionDeepLink/buildNativeCopilotSessionHash. The panel
syncs selection to the hash on select/clear and restores it on mount and
hashchange, so selections survive refresh/back-forward and are shareable.

Background-job filtering: the native session list now hides automated
background sessions whose first turn matches BACKGROUND_JOB_PROMPT_PREFIXES
(e.g. 'Summarise the following conversation as a short title') — these are
Copilot CLI title-generation jobs, not user conversations (603 of them in
a real store). Detection is a chunked indexed query over turns.turn_index=0;
the hidden count is returned as backgroundJobCount and surfaced as a panel
hint. Opt out via the includeBackgroundJobs service option.

Also fix a stale Router test count guard (REPO_SUB_TAB_VALUES is 17 since
copilot-sessions was added; the assertion still expected 16).

Tests: Router deep-link parse/build, panel select/restore/cross-workspace
deep-link sync, panel background-hidden hint, service + route background-job
exclusion and includeBackgroundJobs passthrough.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…ct types

Foundation for the LLM Tools settings page showing each tool's input
parameters compactly (AC-01/AC-02 format).

- Add additive `LlmToolParam` type and optional `params?` field to
  `LlmToolMeta` in both the server registry and the coc-client contract.
  Existing clients reading only name/label/description/enabledByDefault are
  unaffected.
- Add pure `summarizeToolParameters()` / `compactParamType()` helpers that
  compress a tool's JSON-schema `parameters` into `{name,type,required}`
  entries: primitives keep their type, nested objects -> `{...}`, arrays ->
  `[...]`, typeless enums -> `enum`. Returns `[]` for a no-parameter schema
  and `undefined` when no JSON schema is available, so callers can render
  "No parameters" vs "Parameters unavailable".
- Unit tests covering type compaction, required/optional flags, ordering,
  empty-schema, and unavailable-schema cases.

No tool behavior, routing, or persisted preference changes.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… API

Source each toggleable tool's input-schema structure via a display-only
mirror (`LLM_TOOL_PARAMETER_SCHEMAS`) and attach the additive `params`
summary to GET/PUT `/api/workspaces/:id/llm-tools-config` responses through
`withToolParameterMetadata`.

The mirror avoids instantiating tool factories at route time (some build
heavyweight deps like FileWorkItemStore) and is display-only — it never
affects tool execution, validation, provider routing, or persisted prefs.
Tools without a locally-declared schema (e.g. the built-in `memory` tool)
omit `params` so clients can render "parameters unavailable". Existing
`name`/`label`/`description`/`enabledByDefault` fields are unchanged.

A drift-guard test compares the mirror's summary against each live tool
schema for every cheaply-constructible factory, plus a completeness check
that every registry tool is either mirrored or explicitly excluded.

Completes AC-01.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Render each tool's input parameters in the workspace LLM Tools panel as a
compact, scannable affordance. Collapsed rows show only a "N parameters"
button (keyboard-accessible, aria-expanded/aria-controls); expanding inline
shows `name: type*` for required and `name?: type` for optional params, with
nested `{...}`/`[...]` shapes left collapsed. Tools with `params: []` show
"No parameters" and tools without a schema show "Parameters unavailable".

The params button lives outside the toggle <label> so activating it never
flips the enable/disable checkbox; toggle save behavior is unchanged.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…turns

AC-01 of the native Copilot session chat reconstruction: add a read-only
parser for the per-session log at ~/.copilot/session-state/<id>/events.jsonl,
which is the rich source for messages, tool calls + results, model reasoning,
images, and errors (the on-disk session-state is a directory, not a file; the
sibling session.db holds only todos/inbox, and forge_trajectory_events in the
main store is empty).

- session-state-parser.ts: parseNativeSessionState() resolves the
  cross-platform path, reads events.jsonl, and reconstructs ordered
  ReconstructedConversationTurn[] — coalescing assistant messages by turnId,
  correlating tool start/complete by toolCallId, capturing reasoningText
  (thinking), model, base64 image attachments, skills, and tool errors. Returns
  null (never throws) on missing/malformed/empty input so callers fall back to
  the flat session-store.db turns. Rejects unsafe session ids (path traversal).
- types.ts: add ReconstructedConversationTurn / ReconstructedToolCall /
  ReconstructedTimelineItem mirroring the SPA ClientConversationTurn subset.
- test: 11 cases over a faithful synthetic events.jsonl (rich path with a tool
  call carrying name+args+result, failed-tool error capture, image data URLs,
  malformed/empty/missing → null, partial-line resilience, traversal guard,
  mid-stream tool, turnId coalescing). Validated manually against real sessions
  (52/87 turns, 46/217 tool calls) and confirmed c3a97a05 (empty SDK job) → null.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…-02)

Backend mapping + DB fallback for the native Copilot session detail view.

- NativeCopilotSessionDetail gains an always-present `conversation`
  (ReconstructedConversationTurn[]). getSession prefers the rich
  session-state/<id>/events.jsonl parser output; when it returns null
  (missing/malformed/empty log) it maps the flat session-store.db turns
  into text-only user/assistant turns via buildFallbackConversation.
- Service accepts sessionStateDir/parseSessionState overrides; plumb a
  nativeCopilotSessionStateDir option through server + route options for
  hermetic tests. Strictly read-only — no writes to ~/.copilot.
- Mirror ReconstructedToolCall/TimelineItem/ConversationTurn + the
  conversation field into coc-client contracts.
- Tests: rich-path (parser output preferred over DB), DB-fallback mapping
  (role/content/turnIndex sequencing, empty-message skipping, timeline
  shape), empty-session, real events.jsonl wiring via sessionStateDir, and
  an HTTP assertion that conversation flows through the detail endpoint.
- Update packages/coc/AGENTS.md and coc-knowledge rest-api.md reference.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ript (AC-03)

Reuse the existing read-only chat ConversationTurnBubble to render the
reconstructed native-session conversation instead of the flat <pre> dump.
A new SPA-local nativeConversationTurns mapper converts the backend
ReconstructedConversationTurn[] into ClientConversationTurn[], folding
assistant `thinking` into the content timeline as a markdown blockquote
(the chat turn shape has no reasoning field — no component fork). The
metadata header is preserved; no input box, streaming, resume, or per-turn
(pin/archive/delete) actions are wired. Empty conversations show the
existing no-turns state.

Tests: pure mapper unit test (field passthrough, thinking fold into
timeline+content, fallback) + panel test rewritten to assert the transcript
renders one bubble per turn with tool calls/images/model/provider, stored
HTML stays inert, and no chat-action controls exist.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Register a new 'dreams-admin' DashboardTab and wire it through the
existing tool-nav pattern: ALL_TOOL_NAV_ITEMS entry, TOOL_TAB_GROUP_LABELS
-> 'Knowledge', toolNavItem in the Knowledge group, hash route in
tabFromHash + Router switch, and a lazy-rendered DreamsView shell.

This is the foundational scaffold for the Dreams admin tab; subsequent
slices relocate the enable toggle, running-interval, provider/model/timeout
defaults, and the provider-activity section into it.

Tests: nav-item + group-label assertions, tabFromHash route, sidebar
group/data-tab rendering.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Move the "Dreams provider activity" queue + history out of the AI Provider
page and into the admin Dreams tab, reused rather than duplicated.

- Extract shared provider brand visuals (PROVIDER_LABELS, ProviderAvatar,
  brand SVG icons) into shared/providerVisuals.tsx so both the AI Provider
  page and the activity section share one source of truth.
- Move ProviderActivitySection into its own features/dreams component and
  render it from DreamsView (new optional providerActivity/error/onRefresh
  props). Refresh control preserved.
- Remove the section, its three props, and the now-dead providerActivity
  imports from AIProviderPage; AdminPanel passes the existing dream activity
  state/refresh to DreamsView instead, and the auto-refresh effect now loads
  dream activity when the Dreams tab is the active route (quota still loads
  on the AI Provider sub-tab).
- Tests: new DreamsView.test.tsx (attribution, empty, error, Refresh-preserved,
  no-handler) and a regression test that the section is gone from the AI
  Provider page. Update dashboard-spa knowledge reference.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Render the global `dreams.enabled` toggle in the admin Dreams tab and
remove it from the general Settings → Features grid. The runtime flag
(`dreamsEnabled`) and PUT /api/admin/config validation are unchanged.

- admin-setting-definitions: drop the `ui` block from `dreams.enabled`
  so it no longer renders on the Features card (key/runtime untouched).
- Extract the shared admin row primitives (SourceBadge, AdminRow,
  AdminToggle, AdminSeg, AdminInputSuffix) into admin/adminControls.tsx
  so the Dreams tab can reuse them instead of duplicating markup.
- AdminPanel owns the Dreams config form (loaded with the rest of the
  admin config) and a Save handler that persists to global config,
  invalidates display settings, and applies the runtime patch — same
  flow the Features card used. Passed into DreamsView as props.
- DreamsView renders the toggle inside a dirty-tracked SettingsCard.
- Tests: DreamsView toggle reflects/drives config + Save/Cancel wiring;
  contract test guards dreams.enabled stays admin-editable but off the
  Features card.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…reen layout

Adds the final AC-03 verification tests for the LlmToolsPanel parameter
affordance: an explicit keyboard-activation assertion (native button is
focusable, Enter expands and Space collapses, with accessible
label/state) and a narrow-screen layout assertion (single-column grid by
default, wrapping param tokens, fit-width affordance). Verified the
production SPA build compiles the Tailwind arbitrary values (pl-[46px] ->
padding-left:46px).

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

The dashboard-spa.md "Copilot Sessions Tab" section still described the old
plain <pre> text dump ("renders all stored text as plain pre-wrapped text",
per-turn "No assistant response stored" / "Indexed (N chars)" diagnostics),
all of which were removed when the detail view was reconstructed as a rich CoC
chat transcript. Update it to document the backend `conversation:
ReconstructedConversationTurn[]` (rich session-state/<id>/events.jsonl via
parseNativeSessionState, DB fallback via buildFallbackConversation), the SPA
toClientConversationTurns thinking-fold, one read-only ConversationTurnBubble
per turn under the native-session-conversation card, and the strictly
read-only guarantee.

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

The NativeCopilotSessionsPanel suite stubs ConversationTurnBubble, so no
test proved the real chat bubble renders a native-reconstructed transcript.
Add a hermetic render integration test that feeds a reconstructed
conversation (tool-call timeline items, model reasoning fold, a user image,
a failed tool call, and a turn-level error) through the real
toClientConversationTurns mapper into the real ConversationTurnBubble, and
asserts tool-call cards (name + result), assistant markdown, the reasoning
fold, the image gallery, the failed-tool error text, and the turn-level
error strip all reach the DOM — closing the AC-03 "tool-call cards/markdown/
images render" DoD gap with permanent regression coverage.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The native Copilot session detail render had a tested seam gap: the panel
suite stubs ConversationTurnBubble (proving fetch→prop wiring only) while the
bubble suite feeds toClientConversationTurns directly (bypassing the panel).
Neither exercises the production path AC-03's "demo shows a rich transcript"
relies on: panel → cocClient.get() → SessionDetailView →
toClientConversationTurns(detail.conversation) → real ConversationTurnBubble.

Add an end-to-end render integration test that drives the REAL panel with the
REAL bubble (no component stub) and a mocked detail fetch returning a rich
reconstructed conversation (modeled on real session 09c6d69e), asserting
genuine tool-call cards, the tool result + error text, assistant markdown, the
folded reasoning blockquote, and the user image gallery all reach the DOM —
the deterministic equivalent of the live screenshot. Read-only is re-asserted
(no chat-action controls). Uses the real `ui` barrel; only cocClient,
MarkdownView, and useDisplaySettings are mocked.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The Dreams admin tab reuses the AI-provider `.aip-page` shell, but unlike
that page (which scrolls via `.ar-main`), it renders inside `.ar-tool-embed`
where `.ar-main--embed` suppresses the outer scroller. The Skills/Memory
embeds own an inner scroll region; the `.aip-page` grid has none, so its
content — notably the provider-activity queue — overflowed unreachably and
the panel could not scroll.

Give the embedded `.aip-page` its own scroll region (`overflow-y: auto`)
plus page padding mirroring `.ar-page`, scoped to `.ar-tool-embed > .aip-page`
so the non-embedded AI-provider page that shares the class is untouched.
Add a matching narrow-screen padding override.

Extend the admin fit-to-viewport CSS-contract test to lock in that the
embedded `.aip-page` owns its scroll region and that Dreams renders inside
`.ar-tool-embed` using that shell, so the regression can't silently return.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Collapse duplicate filesystem-backed native CLI transcript records with the same provider session ID to the newest metadata record so list rows and detail deep links remain stable. Add regression coverage for duplicate Claude transcript files and keep documentation current.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Gate legacy Copilot session compatibility routes with features.nativeCliSessions and remove the old nativeCopilotSessions runtime/admin flag so CLI Sessions has one operational switch.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
plusplusoneplusplus and others added 6 commits June 14, 2026 05:39
Require Claude native session records with cwd metadata to all remain under the active workspace root before listing or serving detail, preventing mixed-cwd transcripts from leaking across workspaces.

Add regression coverage for mixed-cwd Claude transcripts and document the stricter native CLI session scoping rule.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Keep the unified CLI Sessions tab and bare cli-sessions deep links aligned with the legacy Copilot Sessions behavior and the REST provider default. Add regression coverage for Copilot-default routing plus provider switching.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Reorder the repo sub-tab strip so CLI Sessions sits immediately after the
Activity/Chats tab and before Git in both the classic and dev-workflow
layouts. CLI Sessions also moves into the Activity/Git/Terminal divider
group so it renders without divider-flanked isolation.

Update RepoDetail SUB_TABS unit tests to assert the new ordering and group
placement, and add a regression block covering CLI Sessions placement in
SUB_TABS, VISIBLE_SUB_TABS, the divider group, and the dev-workflow order.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The merged native-CLI-sessions, Dreams admin, and LLM-tool-param features
left several deterministic test failures uncovered by the sharded suite:

- llm-tool-parameter-schemas: mirror the three canvas tools (write_canvas,
  read_canvas, extension_canvas) so the registry-completeness guard passes.
- shared barrel: re-export providerVisuals so barrel-completeness passes.
- config "all fields overridden": set the new dreams.provider, dreams.model,
  and features.nativeCliSessions keys in the fixture; refresh the inline
  resolved-config/source snapshot for features.nativeCliSessions.
- terminal-tab-integration: update the visibleSubTabs dependency-array
  assertion for the added nativeCliSessionsEnabled dep.
- admin e2e: expect Dreams in the Knowledge nav group.

Also fix a Windows-only regression: dashEncodeWorkspaceRoot kept the
drive-letter colon (C:\... -> C:-...), an invalid Windows path segment that
broke ClaudeNativeSessionProvider directory reads. Encode colons to dashes
(C--...), export the helper, and add cross-platform regression tests.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@plusplusoneplusplus plusplusoneplusplus merged commit be77658 into main Jun 14, 2026
36 checks passed
@plusplusoneplusplus plusplusoneplusplus deleted the pr/8dd0619a7-featcoc-read-only-native-copilot-cli-ses branch June 14, 2026 06:18
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