Compare commits

..

46 Commits

Author SHA1 Message Date
Haileyesus
0595ba8fed refactor: add gemini jsonl session support 2026-04-29 19:21:57 +03:00
Haileyesus
c7d4fa915e refactor: directly use getProjectPathById from projectsDb 2026-04-29 18:40:21 +03:00
Haileyesus
5352582fe5 refactor: remove dead code 2026-04-29 18:33:27 +03:00
Haileyesus
5b9108ac18 fix(codex): preserve reasoning entries as thinking blocks
Codex history normalization was downgrading reasoning into plain assistant text
because of branch ordering, not because the raw data was missing.

Why this mattered:
- Codex reasoning JSONL entries are intentionally mapped to history items with
  type thinking, but they also carry message.role assistant.
- normalizeHistoryEntry evaluated the assistant-role branch before the
  thinking branch.
- As a result, reasoning content matched the assistant-text path first and was
  emitted as kind text instead of kind thinking.
- This collapses semantic intent, so UI and downstream features that rely on
  thinking blocks (separate rendering, filtering, and interpretation of model
  thought process vs final answer) receive the wrong message kind.

What changed:
- Prioritized thinking detection (raw.type === thinking or raw.isReasoning)
  before role-based assistant normalization.
- Kept a non-empty content guard for thinking payloads to avoid emitting empty
  artifacts.

Impact:
- Reasoning entries from persisted Codex JSONL now remain thinking blocks
  end-to-end.
- Regular assistant text normalization behavior remains unchanged.
2026-04-29 18:23:14 +03:00
Haileyesus
cd3e8986d7 refactor(database): make session id the primary key in sessions table 2026-04-29 18:06:58 +03:00
Haileyesus
f175d20c4e refactor: normalize project paths across database and service modules 2026-04-29 18:02:33 +03:00
Haileyesus
0f93ef2781 refactor: improve session limit and offset validation in provider routes 2026-04-29 11:46:53 +03:00
Haileyesus
10f35c238d refactor(session-synchronizer): update last scanned timestamp based on synchronization results 2026-04-29 11:35:59 +03:00
Haileyesus
805e283fb6 fix(migrations,projects,clone): normalize legacy schema before writes and harden conflict detection
Why

- Legacy installs can have a sessions table shape that predates provider/custom_name columns. Running migrateLegacySessionNames first caused its INSERT OR REPLACE INTO sessions (...) to target columns that may not exist and fail during startup migration.

- Some upgraded databases had projects.project_id as plain TEXT instead of a real PRIMARY KEY. That breaks assumptions used by id-based lookups and can allow invalid/duplicate identity semantics over time.

- projectsDb.createProjectPath inferred outcomes from 
ow.isArchived, but the upsert path always returns the post-update row with isArchived=0, so archived-reactivation and fresh-create could be misclassified.

- git clone accepted user-controlled URLs directly in argv position, so inputs beginning with - could be interpreted as options instead of a repository argument.

What

- Added 
ebuildProjectsTableWithPrimaryKeySchema in migrations: detect table shape via getTableInfo('projects'), verify project_id has pk=1, and rebuild when missing.

- Rebuild flow now creates a canonical projects__new table (project_id TEXT PRIMARY KEY), copies rows with transformation, backfills empty ids via SQLITE_UUID_SQL, deduplicates conflicting ids/paths, then swaps tables inside a transaction.

- Replaced the prior ddColumnToTableIfNotExists(...) + UPDATE project_id sequence with PK-aware detection/rebuild logic so legacy DBs converge to the required schema.

- Reordered migration sequence to run 
ebuildSessionsTableWithProjectSchema before migrateLegacySessionNames, ensuring sessions is normalized before legacy session_names merge writes execute.

- Updated projectsDb.createProjectPath to generate an ttemptedId before insert, pass it into the prepared statement, and classify outcomes by comparing returned 
ow.project_id to ttemptedId (created vs 
eactivated_archived), with no-row remaining ctive_conflict.

- Hardened clone execution by inserting -- before clone URL in git argv and rejecting normalized GitHub URLs that start with - in startCloneProject.

Tests

- Added integration coverage for projectsDb.createProjectPath branches: fresh insert, archived reactivation, and active conflict.

- Added clone service test for option-prefixed githubUrl rejection (INVALID_GITHUB_URL).
2026-04-28 17:15:13 +03:00
Haileyesus
8570bd7bab refactor: add handling for internal Codex metadata in conversation search 2026-04-27 22:30:31 +03:00
Haileyesus
5af2b719e2 fix: search performance 2026-04-27 22:23:50 +03:00
Haileyesus
9684aa0941 Merge branch 'refactor/use-database-for-session-managemnet' of https://github.com/siteboon/claudecodeui into refactor/use-database-for-session-managemnet 2026-04-27 21:39:56 +03:00
Haileyesus
50ee3c7548 refactor: move search to module 2026-04-27 21:39:48 +03:00
Haileyesus
9a8fb116ef refactor: implement pagination for project sessions loading 2026-04-27 21:22:56 +03:00
Haileyesus
14e6b5b7b2 refactor(projects/sidebar): remove temp snapshot side-effects and simplify session metadata UX
Why this change was needed:
- Project listing had an implicit side effect: every fetch wrote a debug snapshot under `.tmp/project-dumps`.
  That added unnecessary disk I/O to a hot path, introduced hidden runtime behavior, and created maintenance
  overhead for code that was not part of product functionality.
- Keeping snapshot-specific exports/tests around made the projects module API broader than needed and coupled
  tests to temporary/debug behavior instead of user-visible behavior.
- Codex sessions could remain stuck with a placeholder name (`Untitled Codex Session`) even after a real title
  became available from newer sync data, which degraded session discoverability in the UI.
- Sidebar session rows showed duplicated provider branding and long-form relative times, which added visual noise
  and reduced scan speed when many sessions are listed.

What changed:
- Removed temporary projects snapshot dumping from `projects-with-sessions-fetch.service.ts`:
  - deleted snapshot types/helpers and file-write flow
  - removed the write call from `getProjectsWithSessions`
- Removed snapshot-related surface area from `projects/index.ts`.
- Removed the snapshot-focused test `projects.service.test.ts` that only validated removed debug behavior.
- Updated `codex-session-synchronizer.provider.ts` to upgrade session names when an existing session still has
  the placeholder title but a real parsed name is now available.
- Updated `SidebarSessionItem.tsx`:
  - removed duplicate provider logo rendering in each session row
  - moved age indicator to the right side
  - made age indicator fade on hover to prioritize action controls
  - switched to compact relative time format (`<1m`, `Xm`, `Xhr`, `Xd`) for faster list scanning

Outcome:
- Lower overhead and fewer hidden side effects in project fetches.
- Cleaner module boundaries in projects.
- Better Codex session naming consistency after sync.
- Cleaner sidebar density and clearer hover/action behavior.
2026-04-27 21:07:54 +03:00
Haile
714c9214e6 Potential fix for pull request finding 'Useless assignment to local variable'
Co-authored-by: Copilot Autofix powered by AI <223894421+github-code-quality[bot]@users.noreply.github.com>
2026-04-27 15:21:53 +03:00
Haile
c027dc0813 fix: remove unused var
Co-authored-by: Copilot Autofix powered by AI <223894421+github-code-quality[bot]@users.noreply.github.com>
2026-04-27 15:21:05 +03:00
Haileyesus
16954c883b refactor: move fetching messages to module 2026-04-27 14:30:09 +03:00
Haileyesus
9663f08fcb refactor: move rename and delete sessions to modules 2026-04-27 14:21:03 +03:00
Haileyesus
7ceaa9e326 refactor: move projects provider specific logic into respective session providers 2026-04-27 13:46:19 +03:00
Haileyesus
d3adc7afb8 refactor: optimize project auto-expand logic 2026-04-25 21:49:09 +03:00
Haileyesus
360aa514f9 fix(projects-state): stop websocket message reprocessing loop
The websocket projects effect in useProjectsState could re-handle the same
latestMessage after local state writes triggered re-renders.

Under bursty websocket traffic, this created an update feedback cycle
that surfaced as 'Maximum update depth exceeded', often from Sidebar.

What changed:
- Added lastHandledMessageRef so each latestMessage object is handled once.
- Added an early return guard when the current message was already handled.
- Made projects updates idempotent by comparing previous and merged payloads
  before calling setProjects.

Result:
- Breaks the effect -> state update -> effect re-entry cycle.
- Reduces redundant renders during rapid projects_updated traffic while
  preserving normal project/session synchronization.
2026-04-25 21:45:30 +03:00
Haileyesus
68123dcc33 feat: optimistic update for session watcher 2026-04-25 21:32:24 +03:00
Haileyesus
edc7d6d184 refactor: implement optimistic UI for project star state management 2026-04-25 21:09:25 +03:00
Haileyesus
113c7631b8 refactor: move project star state from localStorage to backend 2026-04-25 21:08:16 +03:00
Haileyesus
7a82fb54dc refactor: move project deletion to module 2026-04-25 20:45:24 +03:00
Haileyesus
447f352e7b refactor: move project rename to module 2026-04-25 20:28:58 +03:00
Haileyesus
3188ef5fee refactor: remove loading sessions logic from sidebar 2026-04-25 19:56:50 +03:00
Haileyesus
bb86236520 refactor: modularize project services, and wizard create/clone flow
Restructure project creation, listing, GitHub clone progress, and TaskMaster
details behind a dedicated TypeScript module under server/modules/projects/,
and align the client wizard with a single path-based flow.

Server / routing
- Remove server/routes/projects.js and mount server/modules/projects/
  projects.routes.ts at /api/projects (still behind authenticateToken).
- Drop duplicate handlers from server/index.js for GET /api/projects and
  GET /api/projects/:projectId/taskmaster; those live on the new router.
- Import WORKSPACES_ROOT and validateWorkspacePath from shared utils in
  index.js instead of the deleted projects route module.

Projects router (projects.routes.ts)
- GET /: list projects with sessions (existing snapshot behavior).
- POST /create-project: validate body, reject legacy workspaceType and
  mixed clone fields, delegate to createProject service, return distinct
  success copy when an archived path is reactivated.
- GET /clone-progress: Server-Sent Events for clone progress/complete/error;
  requires authenticated user id for token resolution; wires startCloneProject.
- GET /:projectId/taskmaster: delegates to getProjectTaskMaster.

Services (new)
- project-management.service.ts: path validation, workspace directory
  creation, persistence via projectsDb.createProjectPath, mapping to API
  project shape; surfaces AppError for validation, conflict, and not-found
  cases; optional dependency injection for tests.
- project-clone.service.ts: validates workspace, resolves GitHub auth
  (stored token or inline token), runs git clone with progress callbacks,
  registers project via createProject on success; sanitizes errors and
  supports cancellation; injectable dependencies for tests.
- projects-has-taskmaster.service.ts: moves TaskMaster detection and
  normalization out of server/projects.js; resolve-by-id and public
  getProjectTaskMaster with structured AppError responses.

Persistence and shared types
- projectsDb.createProjectPath now returns CreateProjectPathResult
  (created | reactivated_archived | active_conflict) using INSERT … ON
  CONFLICT with selective update when the row is archived; normalizes
  display name from path or custom name; repository row typing moves to
  shared ProjectRepositoryRow.
- getProjectPaths() returns only non-archived rows (isArchived = 0).
- shared/types.ts: ProjectRepositoryRow, CreateProjectPathResult/outcome,
  WorkspacePathValidationResult.
- shared/utils.ts: WORKSPACES_ROOT, forbidden path lists, validateWorkspacePath,
  asyncHandler for Express async routes.

Legacy cleanup
- server/projects.js: remove detectTaskMasterFolder, normalizeTaskMasterInfo,
  and getProjectTaskMasterById (logic lives in the new service).
- server/routes/agent.js: register external API project paths with
  projectsDb.createProjectPath instead of addProjectManually try/catch;
  treat active_conflict as an existing registration and continue.

Tests
- Add Node test suites for project-management, project-clone, and
  projects-has-taskmaster services; update projects.service test import
  for renamed projects-with-sessions-fetch.service.ts.

Rename
- projects.service.ts → projects-with-sessions-fetch.service.ts;
  re-export from modules/projects/index.ts.

Client (project creation wizard)
- Remove StepTypeSelection and workspaceType from form state and types;
  wizard is two steps (configure path/GitHub auth, then review).
- createWorkspaceRequest → createProjectRequest; clone vs create-only
  inferred from githubUrl (pathUtils / isCloneWorkflow).
- Adjust step indices, WizardProgress, StepConfiguration/Review,
  WorkspacePathField, and src/utils/api.js as needed for the new API.

Docs
- Minor websocket README touch-up.

Net: ~1.6k insertions / ~0.9k deletions across 29 files; behavior is
centralized in typed services with explicit HTTP errors and test seams.
2026-04-25 19:36:47 +03:00
Haileyesus
7023a8cf7b refactor: remove unnecessary GitHub cloning logic from create-workspace endpoint 2026-04-25 17:56:14 +03:00
Haileyesus
5d7d6e478e refactor(index.js): fix import order 2026-04-25 16:17:21 +03:00
Haileyesus
1083746df5 refactor(index.js): reorganize code structure 2026-04-25 16:02:35 +03:00
Haileyesus
eec9701679 refactor(sessions-watcher): remove redundant logging after session sync completion 2026-04-25 15:44:26 +03:00
Haileyesus
2323a576a6 refactor(websocket): move websocket logic to its own module 2026-04-25 15:39:22 +03:00
Haileyesus
3fd2353ffe refactor: use updated session watcher
In addition, for projects_updated websocket response, send the sessionId instead
2026-04-24 21:56:19 +03:00
Haileyesus
b3445508e9 refactor: update import path for getProjectsWithSessions to include file extension 2026-04-24 19:09:30 +03:00
Haileyesus
c412aac8fb refactor(projects): rename getProjects with getProjectsWithSessions 2026-04-24 19:08:27 +03:00
Haileyesus
18e5a88c48 refactor(projects): reorganize project-related logic into dedicated modules 2026-04-24 19:06:40 +03:00
Haileyesus
dc5d73936a refactor(projects): identify projects by DB projectId instead of folder-derived name
GET /api/projects used to scan ~/.claude/projects/ on every request, derive
each project's identity from the encoded folder name, and re-parse JSONL
files to build session lists. Using the folder-derived name as the project
identifier leaked the Claude CLI's on-disk encoding into every API route,
forced every downstream endpoint to re-resolve a real path via JSONL
'cwd' inspection, and made the project list endpoint O(projects x sessions)
on disk I/O.

This change switches the entire API surface to identify projects by the
stable primary key from the 'projects' table and drives the listing
straight from the DB:

- Add projectsDb.getProjectPathById as the canonical projectId -> path
  resolver so routes no longer need to touch the filesystem to figure out
  where a project lives.

- Rewrite getProjects so it reads the project list from the 'projects'
  table and the per-project session list from the 'sessions' table (one
  SELECT per project). No filesystem scanning happens for this endpoint
  anymore, which removes the dependency on ~/.claude/projects existing,
  on Cursor's MD5-hashed chat folders being discoverable, and on Codex's
  JSONL history being on disk. Per the migration spec each session now
  exposes 'summary' sourced from sessions.custom_name, 'messageCount' = 0
  (message counting is not implemented), and sessionMeta.hasMore is
  pinned to false since this endpoint doesn't drive session pagination.

- Introduce id-based wrappers (getSessionsById, renameProjectById,
  deleteSessionById, deleteProjectById, getProjectTaskMasterById) so
  every caller can pass projectId and resolve the real path through the
  DB. renameProjectById also writes to projects.custom_project_name so
  the DB-driven getProjects response reflects renames immediately; it
  keeps project-config.json in sync for any legacy reader that still
  consults the JSON file.

- Migrate every /api/projects/:projectName route in server/index.js,
  server/routes/taskmaster.js, and server/routes/messages.js to
  :projectId, and change server/routes/git.js so the 'project'
  query/body parameter carries a projectId that is resolved through the
  DB before any git command runs. TaskMaster WebSocket broadcasts emit
  'projectId' for the same reason so the frontend can match
  notifications against its current selection without another lookup.

- Delete helpers that existed only to feed the old getProjects path
  (getCursorSessions, getGeminiCliSessions, getProjectTaskMaster) along
  with their unused imports (better-sqlite3's Database,
  applyCustomSessionNames). The legacy folder-name helpers (getSessions,
  renameProject, deleteSession, deleteProject, extractProjectDirectory)
  are kept as internal implementation details of the id-based wrappers
  and of destructive cleanup / conversation search, but they are no
  longer re-exported.

- searchConversations still walks JSONL to produce match snippets (that
  data doesn't live in the DB), but it now includes the resolved
  projectId in each result so the sidebar can cross-reference hits with
  its already loaded project list without a second round-trip.

Frontend migration:

- Project.name is replaced by Project.projectId in src/types/app.ts, and
  ProjectSession.__projectName becomes __projectId so session tagging
  and sidebar state keys stay aligned with the backend identifier.
  Settings continues to use SettingsProject.name for legacy consumers,
  but it is populated from projectId by normalizeProjectForSettings.

- All places that previously indexed per-project state by project.name
  (sidebar expanded/starred/loading/deletingProjects sets,
  additionalSessions map, projectHasMoreOverrides, starredProjects
  localStorage, command history and draft-input localStorage,
  TaskMaster caches) now key on projectId so state survives
  display-name edits and is consistent across the app.

- src/utils/api.js renames every endpoint parameter to projectId, the
  unified messages endpoint takes projectId in its query string, and
  useSessionStore forwards projectId on fetchFromServer / fetchMore /
  refreshFromServer. Git panel, file tree, code editor, PRD editor,
  plugins context, MCP server flows and TaskMaster hooks are all
  updated to pass projectId.

- DEFAULT_PROJECT_FOR_EMPTY_SHELL is updated to carry a 'default'
  projectId sentinel so the empty-shell placeholder still satisfies the
  Project contract.

Bug fix bundled in:

- sessionsDb.setName no longer bumps updated_at when a row already
  exists. Renaming is a label change, not activity, so there is no
  reason for it to reset 'last activity' in the sidebar. It also no
  longer relies on SQLite's CURRENT_TIMESTAMP, which stores a naive
  'YYYY-MM-DD HH:MM:SS' value that JavaScript parses as local time and
  caused renamed sessions to appear shifted backwards by the client's
  UTC offset. When an INSERT actually happens it now writes ISO-8601
  UTC with a 'Z' suffix.

- buildSessionsByProviderFromDb normalizes any legacy naive timestamps
  in the sessions table to ISO-8601 UTC on the way out so rows written
  before this change also render correctly on the client.

Other cleanup:

- Removed the filesystem-first project-discovery comment block at the
  top of server/projects.js and replaced it with a short note that
  describes the new DB-driven flow and lists the few remaining
  filesystem-dependent helpers (message reads, search, destructive
  delete, manual project registration).

- server/modules/providers/index.ts is added as a small barrel so the
  providers module exposes a stable public surface.

Made-with: Cursor
2026-04-24 18:12:10 +03:00
Haileyesus
4bd07c3ece refactor: update import paths for database modules and remove legacy db.js and schema.js files 2026-04-24 15:11:25 +03:00
Haileyesus
15171e1428 perf(projects): lazy-load TaskMaster metadata per selected project
Why:

- /api/projects is a hot path (initial load, sidebar refresh, websocket sync).

- Scanning .taskmaster for every project on each call added avoidable fs I/O and payload size.

- TaskMaster metadata is only needed after selecting a specific project.

- Moving it to a project-scoped endpoint makes loading cost match user intent.

- The UI now hydrates TaskMaster state on selection and keeps it across refresh events.

- This prevents status flicker/regression while still removing global scan overhead.

- Selection fetches are sequence-guarded to block stale async responses on fast switching.

- isManuallyAdded was removed from responses to keep the public project contract minimal.

- Project dumps now use incrementing snapshot files to preserve history for debugging.

What changed:

- Added GET /api/projects/:projectName/taskmaster and getProjectTaskMaster().

- Removed TaskMaster detection from bulk getProjects().

- Added api.projectTaskmaster(...) plus selection-time hydration in frontend contexts.

- Merged cached taskmaster values into refreshed project lists for continuity.

- Removed isManuallyAdded from manual project payloads.
2026-04-24 13:30:04 +03:00
Haileyesus
f99af1ff67 feat: added session indexer logic 2026-04-23 17:32:08 +03:00
Haileyesus
7b75ed0b72 refactor(database): move db into typescript
- Implemented githubTokensDb for managing GitHub tokens with CRUD operations.
- Created
otificationPreferencesDb to handle user notification preferences.
- Added projectsDb for project path management and related operations.
- Introduced pushSubscriptionsDb for managing browser push subscriptions.
- Developed scanStateDb to track the last scanned timestamp.
- Established sessionsDb for session management with CRUD functionalities.
- Created userDb for user management, including authentication and onboarding.
- Implemented apidKeysDb for storing and managing VAPID keys.

feat(database): define schema for new database tables

- Added SQL schema definitions for users, API keys, user credentials, notification preferences, VAPID keys, push subscriptions, projects, sessions, scan state, and app configuration.
- Included necessary indexes for performance optimization.

refactor(shared): enhance type definitions and utility functions

- Updated shared types and interfaces for improved clarity and consistency.
- Added new types for credential management and provider-specific operations.
- Refined utility functions for better error handling and message normalization.
2026-04-23 16:59:45 +03:00
Haileyesus
2e326214e1 refactor: rename session_names table and related code to sessions for clarity and consistency 2026-04-22 13:43:16 +03:00
Haileyesus
295b8846a7 refactor: remove unused fields from project and session objects 2026-04-22 13:28:44 +03:00
Haileyesus
80d010126f refactor: remove unused exports 2026-04-22 13:01:55 +03:00
3 changed files with 2 additions and 234 deletions

View File

@@ -1,231 +0,0 @@
# Providers Module: How To Add a New Provider
This guide is the canonical checklist for adding a provider to the unified provider system.
The goal is to make provider onboarding deterministic for both humans and AI agents.
## Architecture Summary
Each provider is composed of 3 sub-capabilities behind one wrapper:
- `auth` (`IProviderAuth`): install/auth status
- `mcp` (`IProviderMcp`): MCP server read/write/list for provider-native config files
- `sessions` (`IProviderSessions`): normalize live events and fetch persisted history
Main interfaces:
- `server/shared/interfaces.ts`
- `server/shared/types.ts`
- `server/modules/providers/shared/base/abstract.provider.ts`
- `server/modules/providers/shared/mcp/mcp.provider.ts`
Main registry/services:
- `server/modules/providers/provider.registry.ts`
- `server/modules/providers/services/provider-auth.service.ts`
- `server/modules/providers/services/mcp.service.ts`
- `server/modules/providers/services/sessions.service.ts`
## Files You Must Add
Create `server/modules/providers/list/<provider>/` with:
- `<provider>.provider.ts`
- `<provider>-auth.provider.ts`
- `<provider>-mcp.provider.ts`
- `<provider>-sessions.provider.ts`
Follow the existing structure in `claude`, `codex`, `cursor`, or `gemini`.
## Step-by-Step Checklist
1. Add provider id to shared union types.
- Update `server/shared/types.ts` `LLMProvider`.
- Also update `src/types/app.ts` `LLMProvider` (frontend type).
2. Implement the provider wrapper.
- Extend `AbstractProvider`.
- Expose `readonly auth`, `readonly mcp`, and `readonly sessions`.
- Call `super('<provider>')`.
3. Implement auth provider (`<provider>-auth.provider.ts`).
- Implement `IProviderAuth#getStatus()`.
- Return `{ installed, provider, authenticated, email, method, error? }`.
- Use existing helpers from `server/shared/utils.ts` (`readObjectRecord`, `readOptionalString`, etc.) where relevant.
4. Implement MCP provider (`<provider>-mcp.provider.ts`).
- Extend `McpProvider`.
- Define supported scopes/transports in `super('<provider>', scopes, transports)`.
- Implement:
- `readScopedServers(...)`
- `writeScopedServers(...)`
- `buildServerConfig(...)`
- `normalizeServerConfig(...)`
- Reuse shared validation behavior in `McpProvider` (scope/transport checks).
5. Implement sessions provider (`<provider>-sessions.provider.ts`).
- Implement `IProviderSessions`:
- `normalizeMessage(raw, sessionId)`
- `fetchHistory(sessionId, options)`
- Normalize to `NormalizedMessage` using `createNormalizedMessage(...)`.
- For filesystem-backed sessions, sanitize path inputs (`sessionId`, workspace paths) before reading files/databases.
- Keep pagination semantics consistent:
- `limit: null` means unbounded
- `limit: 0` means empty page
- include `total`, `hasMore`, `offset`, `limit` correctly
- Ensure normalized message ids are unique per output message.
6. Register provider in backend registry/router.
- `server/modules/providers/provider.registry.ts`:
- import the new provider class
- add it to the `providers` map
- `server/modules/providers/provider.routes.ts`:
- update `parseProvider(...)` whitelist
7. Wire runtime execution path (outside this module).
If the provider should run live chat commands, also update runtime routing:
- `server/routes/agent.js` provider validation and dispatch
- `server/index.js` provider routing/command handling/valid provider lists
- Add or wire provider runtime implementation module (similar to `claude-sdk.js`, `cursor-cli.js`, `openai-codex.js`, `gemini-cli.js`)
8. Add model constants and UI integration (outside this module).
- `shared/modelConstants.js` provider model list + default
- Provider selection and state hooks:
- `src/components/chat/hooks/useChatProviderState.ts`
- `src/components/chat/view/subcomponents/ProviderSelectionEmptyState.tsx`
- Auth/login modal command text:
- `src/components/provider-auth/view/ProviderLoginModal.tsx`
## Minimal Templates
Use these as a starting point.
```ts
// <provider>.provider.ts
import { AbstractProvider } from '@/modules/providers/shared/base/abstract.provider.js';
import { <Provider>AuthProvider } from './<provider>-auth.provider.js';
import { <Provider>McpProvider } from './<provider>-mcp.provider.js';
import { <Provider>SessionsProvider } from './<provider>-sessions.provider.js';
import type { IProviderAuth, IProviderSessions } from '@/shared/interfaces.js';
export class <Provider>Provider extends AbstractProvider {
readonly mcp = new <Provider>McpProvider();
readonly auth: IProviderAuth = new <Provider>AuthProvider();
readonly sessions: IProviderSessions = new <Provider>SessionsProvider();
constructor() {
super('<provider>');
}
}
```
```ts
// <provider>-sessions.provider.ts
import type { IProviderSessions } from '@/shared/interfaces.js';
import type { FetchHistoryOptions, FetchHistoryResult, NormalizedMessage } from '@/shared/types.js';
import { createNormalizedMessage, readObjectRecord } from '@/shared/utils.js';
const PROVIDER = '<provider>';
export class <Provider>SessionsProvider implements IProviderSessions {
normalizeMessage(rawMessage: unknown, sessionId: string | null): NormalizedMessage[] {
const raw = readObjectRecord(rawMessage);
if (!raw) {
return [];
}
return [createNormalizedMessage({
provider: PROVIDER,
kind: 'text',
role: 'assistant',
sessionId,
content: String(raw.content ?? ''),
})];
}
async fetchHistory(
sessionId: string,
options: FetchHistoryOptions = {},
): Promise<FetchHistoryResult> {
const { limit = null, offset = 0 } = options;
const all: NormalizedMessage[] = [];
if (limit === null) {
return { messages: all.slice(offset), total: all.length, hasMore: false, offset, limit: null };
}
const start = Math.max(0, offset);
const safeLimit = Math.max(0, limit);
const page = safeLimit === 0 ? [] : all.slice(start, start + safeLimit);
return {
messages: page,
total: all.length,
hasMore: safeLimit === 0 ? start < all.length : start + safeLimit < all.length,
offset: start,
limit: safeLimit,
};
}
}
```
## AI Prompt Template
Use this prompt for AI-assisted implementation:
```text
Add a new provider "<provider>" using the provider module architecture.
Requirements:
1) Create:
- server/modules/providers/list/<provider>/<provider>.provider.ts
- server/modules/providers/list/<provider>/<provider>-auth.provider.ts
- server/modules/providers/list/<provider>/<provider>-mcp.provider.ts
- server/modules/providers/list/<provider>/<provider>-sessions.provider.ts
2) Register in:
- server/modules/providers/provider.registry.ts
- server/modules/providers/provider.routes.ts (parseProvider whitelist)
- server/shared/types.ts LLMProvider
- src/types/app.ts LLMProvider
3) Reuse helper utilities and follow existing style from codex/claude/cursor/gemini.
4) Ensure sessions:
- unique normalized message IDs
- safe path handling for disk/db session sources
- correct pagination for limit=null and limit=0
5) Run:
- npx eslint <touched server files>
- npx tsc --noEmit -p server/tsconfig.json
```
## Validation Checklist
Run these after implementation:
```bash
npx eslint server/modules/providers/**/*.ts server/shared/types.ts server/shared/interfaces.ts
npx tsc --noEmit -p server/tsconfig.json
```
Quick API smoke tests:
- `GET /api/providers/<provider>/auth/status`
- `GET /api/providers/<provider>/mcp/servers`
- `POST /api/providers/<provider>/mcp/servers`
- `GET /api/sessions/<sessionId>/messages?provider=<provider>&limit=50&offset=0`
## Common Mistakes
- Adding provider files but forgetting `provider.registry.ts`.
- Updating backend `LLMProvider` but not frontend `src/types/app.ts`.
- Hardcoding provider whitelists in routes and missing one location.
- Returning duplicate message ids in `normalizeMessage`.
- Treating `limit === 0` as unbounded instead of empty page.
- Building file paths from raw `sessionId` without validation.

View File

@@ -320,7 +320,7 @@ Custom commands can be created in:
packageName, packageName,
uptime: uptimeFormatted, uptime: uptimeFormatted,
uptimeSeconds: Math.floor(uptime), uptimeSeconds: Math.floor(uptime),
model: context?.model || CLAUDE_MODELS.DEFAULT, model: context?.model || 'claude-sonnet-4.5',
provider: context?.provider || 'claude', provider: context?.provider || 'claude',
nodeVersion: process.version, nodeVersion: process.version,
platform: process.platform platform: process.platform

View File

@@ -13,8 +13,8 @@
export const CLAUDE_MODELS = { export const CLAUDE_MODELS = {
// Models in SDK format (what the actual SDK accepts) // Models in SDK format (what the actual SDK accepts)
OPTIONS: [ OPTIONS: [
{ value: "opus", label: "Opus" },
{ value: "sonnet", label: "Sonnet" }, { value: "sonnet", label: "Sonnet" },
{ value: "opus", label: "Opus" },
{ value: "haiku", label: "Haiku" }, { value: "haiku", label: "Haiku" },
{ value: "claude-opus-4-6", label: "Opus 4.6" }, { value: "claude-opus-4-6", label: "Opus 4.6" },
{ value: "opusplan", label: "Opus Plan" }, { value: "opusplan", label: "Opus Plan" },
@@ -59,7 +59,6 @@ export const CURSOR_MODELS = {
*/ */
export const CODEX_MODELS = { export const CODEX_MODELS = {
OPTIONS: [ OPTIONS: [
{ value: "gpt-5.5", label: "GPT-5.5" },
{ value: "gpt-5.4", label: "GPT-5.4" }, { value: "gpt-5.4", label: "GPT-5.4" },
{ value: "gpt-5.4-mini", label: "GPT-5.4 mini" }, { value: "gpt-5.4-mini", label: "GPT-5.4 mini" },
{ value: "gpt-5.3-codex", label: "GPT-5.3 Codex" }, { value: "gpt-5.3-codex", label: "GPT-5.3 Codex" },