Skip to content

feat(history): replace backends.json path hints with history-sources.json - #84

Merged
vriesdemichael merged 2 commits into
mainfrom
77-history-sources-config
Apr 2, 2026
Merged

feat(history): replace backends.json path hints with history-sources.json#84
vriesdemichael merged 2 commits into
mainfrom
77-history-sources-config

Conversation

@vriesdemichael

Copy link
Copy Markdown
Owner

Summary

  • Removes historyPathHints/cliHistoryPathHints from BackendDefinitionRecord and BackendSummary; backends.json now only stores agent registry data
  • Introduces backend/src/history/sources-config.ts with a dedicated history-sources.json config file, GET /api/history-sources and PATCH /api/history-sources/:provider routes
  • Updates the settings UI with a new History Sources section separate from the ACP Backends section, allowing path hints to be configured per provider

Closes #77

…json

Removes historyPathHints/cliHistoryPathHints from BackendDefinitionRecord
and BackendSummary. Introduces a dedicated history-sources.json config file
with read/write helpers and adds GET /api/history-sources + PATCH
/api/history-sources/:provider routes. Updates the settings UI with a new
History Sources section. Closes #77.
Copilot AI review requested due to automatic review settings April 2, 2026 21:54

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR splits history path-hint configuration out of backends.json into a dedicated history-sources.json, exposing it via new backend routes and a new Settings UI section so backend connection settings and history discovery settings are managed independently.

Changes:

  • Remove historyPathHints/cliHistoryPathHints from backend config and backend summaries, and route history discovery hint lookups through history-sources.json.
  • Add GET /api/history-sources and PATCH /api/history-sources/:provider endpoints backed by a new backend/src/history/sources-config.ts module.
  • Update the Settings UI + tests to edit history source paths per provider in a separate “History Sources” section.

Reviewed changes

Copilot reviewed 11 out of 11 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
frontend/src/routes/settings.tsx Adds History Sources section and card UI; removes history-hint editing from backend cards.
frontend/src/router.test.tsx Updates router test fetch mocks/assertions for the new history-sources UI.
frontend/src/hooks/useBackendSettings.ts Introduces useHistorySources() hook for loading/saving history-sources.json.
backend/src/routes/agents.ts Adds /history-sources API routes alongside existing agent/backend routes.
backend/src/routes/agents.test.ts Adds integration tests for history-sources routes and updates backend stubs.
backend/src/history/sources-config.ts New config module for reading/writing/updating history-sources.json and providing hint lookups.
backend/src/history/sources-config.test.ts Unit tests for the new history-sources config module.
backend/src/agents/types.ts Adds HistorySourceConfig API shape; removes history hint fields from BackendSummary.
backend/src/agents/registry.ts Switches session/history discovery to use getHistoryHintsForProvider() lookups instead of backend config fields.
backend/src/agents/registry.test.ts Updates mocks/expectations for hint lookups now sourced from history-sources.json.
backend/src/agents/config.ts Removes history hint fields from backend config records and updates legacy Copilot backend migration notes/logic.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines 82 to 90
/**
* Migrate old split Copilot backend records (copilot-cli-wsl, copilot-cli-host,
* copilot-vscode-host, copilot-vscode-wsl) into a single `copilot` backend.
* Any user-customized command/args from the CLI backends are preserved.
* All historyPathHints are merged into `historyPathHints` (VS Code roots).
* Paths that look like CLI session-state directories (absolute paths containing
* '.copilot' but not 'workspaceStorage') are also placed in `cliHistoryPathHints`.
* History path hints previously stored on these records are no longer part of
* `BackendDefinitionRecord` — they live in `history-sources.json` now.
* Non-Copilot backends are unchanged.
* Does not delete unknown custom user backends.
*/

Copilot AI Apr 2, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

readBackendConfig()/migrateLegacyCopilotBackends() no longer migrates legacy historyPathHints/cliHistoryPathHints out of existing backends.json. Because normalizeBackendRecord() now drops those fields and history-sources.json is initialized to empty defaults, upgrading from an older config will silently lose user-configured history roots. Consider extracting legacy hint fields from the raw backend config file (before normalization) and writing them into history-sources.json (only if that file is missing or still default), mapping gemini-cligemini etc., so existing installs keep their configured history paths.

Copilot uses AI. Check for mistakes.
Comment thread backend/src/history/sources-config.ts Outdated
Comment on lines +29 to +38
export function readHistorySourcesConfig(): HistorySourceRecord[] {
ensureHistorySourcesConfigExists()
const file = readHistorySourcesConfigFile()
const configured = file.sources

if (!configured || configured.length === 0) {
return DEFAULT_SOURCES
}

return configured.map(normalizeHistorySourceRecord)

Copilot AI Apr 2, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

readHistorySourcesConfig() returns only the configured sources entries and does not merge in defaults for missing providers. This means a partially-populated history-sources.json (or an older file after adding a new provider) will cause the API/UI to omit providers entirely. Consider merging DEFAULT_SOURCES with configured records by provider (configured values win) so the API always returns a stable, complete set of providers.

Suggested change
export function readHistorySourcesConfig(): HistorySourceRecord[] {
ensureHistorySourcesConfigExists()
const file = readHistorySourcesConfigFile()
const configured = file.sources
if (!configured || configured.length === 0) {
return DEFAULT_SOURCES
}
return configured.map(normalizeHistorySourceRecord)
function mergeHistorySourcesWithDefaults(sources?: HistorySourceRecord[]): HistorySourceRecord[] {
if (!sources || sources.length === 0) {
return DEFAULT_SOURCES.map(normalizeHistorySourceRecord)
}
const configuredByProvider = new Map<HistoryProvider, HistorySourceRecord>(
sources
.map(normalizeHistorySourceRecord)
.map((source) => [source.provider, source] as const)
)
return DEFAULT_SOURCES.map((source) => configuredByProvider.get(source.provider) ?? normalizeHistorySourceRecord(source))
}
export function readHistorySourcesConfig(): HistorySourceRecord[] {
ensureHistorySourcesConfigExists()
const file = readHistorySourcesConfigFile()
return mergeHistorySourcesWithDefaults(file.sources)

Copilot uses AI. Check for mistakes.
Comment thread backend/src/history/sources-config.ts Outdated
Comment on lines +124 to +130
// Only include cliPaths for copilot; include it (even empty) when present so
// the key is persisted.
if (record.provider === 'copilot' || record.cliPaths !== undefined) {
normalized.cliPaths = Array.isArray(record.cliPaths)
? record.cliPaths.filter((p): p is string => typeof p === 'string')
: []
}

Copilot AI Apr 2, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

normalizeHistorySourceRecord() currently includes cliPaths for non-copilot providers whenever the input record has cliPaths defined (record.provider === 'copilot' || record.cliPaths !== undefined). This contradicts the comment and the intended schema (“only meaningful for copilot”), and allows /api/history-sources/:provider to persist cliPaths for gemini/opencode. Consider restricting cliPaths normalization/persistence to record.provider === 'copilot' only (and ignoring cliPaths for other providers).

Copilot uses AI. Check for mistakes.
}

const updated = (await response.json()) as HistorySourceConfig
setSources((current) => current.map((s) => (s.provider === provider ? updated : s)))

Copilot AI Apr 2, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

saveSource() updates local state with current.map(...), which will be a no-op if the provider isn’t already present in sources (e.g., if the backend starts returning additional providers or if the config file is missing an entry). Consider upserting: replace when found, otherwise append the returned record so the UI stays in sync with the backend response.

Suggested change
setSources((current) => current.map((s) => (s.provider === provider ? updated : s)))
setSources((current) => {
const exists = current.some((s) => s.provider === provider)
return exists
? current.map((s) => (s.provider === provider ? updated : s))
: [...current, updated]
})

Copilot uses AI. Check for mistakes.
Comment thread backend/src/routes/agents.ts Outdated
Comment on lines +77 to +82
const body = await c.req.json<{
paths?: string[]
cliPaths?: string[]
}>()

try {

Copilot AI Apr 2, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In PATCH /history-sources/:provider, await c.req.json() is executed outside the try/catch. If the client sends invalid JSON (or no body with a JSON Content-Type), this will throw and likely surface as a 500 instead of a 400. Consider wrapping the JSON parse in the existing try/catch (or handling parse errors explicitly) and returning a 400 with a clear error message.

Suggested change
const body = await c.req.json<{
paths?: string[]
cliPaths?: string[]
}>()
try {
try {
const body = await c.req.json<{
paths?: string[]
cliPaths?: string[]
}>()

Copilot uses AI. Check for mistakes.
Comment on lines +164 to +170
describe('getHistoryHintsForProvider', () => {
it('returns empty arrays for unknown provider', () => {
readHistorySourcesConfig() // init defaults

const result = getHistoryHintsForProvider('gemini')
expect(result).toEqual({ historyPathHints: [], cliHistoryPathHints: [] })
})

Copilot AI Apr 2, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Test name is misleading: it says “unknown provider” but calls getHistoryHintsForProvider('gemini'), which is a known provider in the type and defaults. Consider renaming the test to reflect the actual behavior being validated (e.g., “returns empty arrays when no paths are configured”).

Copilot uses AI. Check for mistakes.
- readHistorySourcesConfig: merge configured sources with DEFAULT_SOURCES
  by provider so partial files always return a complete provider set
- normalizeHistorySourceRecord: restrict cliPaths to copilot provider only
- PATCH /history-sources/:provider: move JSON parse inside try/catch so
  malformed request bodies return 400 instead of 500
- readBackendConfig: migrate legacy historyPathHints/cliHistoryPathHints
  from backends.json into history-sources.json on first upgrade; make
  BACKEND_CONFIG_PATH lazy (getBackendConfigPath()) to support test isolation
- useHistorySources saveSource: upsert provider in local state rather than
  map-only so a new provider returned by the server is kept in sync
- Rename misleading test 'returns empty arrays for unknown provider' to
  'returns empty arrays when no paths are configured for a provider'
- Add tests for mergeWithDefaults, cliPaths stripping, and legacy migration
@vriesdemichael
vriesdemichael merged commit 0cbb5b2 into main Apr 2, 2026
4 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.

Backend: replace backends.json with history-sources.json config

2 participants