Commit Graph
724 Commits
Author SHA1 Message Date
Sergey KozyrenkoandClaude Opus 4.7 96e91d2a2a fix(frontend): make mobile period triggers compact icon buttons
The mobile period switcher swap landed in 28c98a8 but kept the
text-button paddings (px-3), so each "icon-only" trigger was still
~36 px wide and the group looked stretched on a 375 px viewport.

Added \`aspect-square px-0\` to each TabsTrigger (with the desktop
overrides reset above the \`sm\` breakpoint) so the buttons collapse
to a 1:1 ratio on mobile — three tight squares matching shadcn's
icon-button proportions — while desktop keeps the original
horizontal padding.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-19 09:39:23 +07:00
Sergey KozyrenkoandClaude Opus 4.7 e02da42f39 feat(frontend): collapse Dashboard period switcher to icons on mobile
On screens narrower than the `sm` breakpoint (640 px) the
Week / Month / Quarter triggers were forcing the period switcher to
crowd the Analytics / Overview tabs into the next line. Replaced
the labels with lucide calendar icons (CalendarDays / Calendar /
CalendarRange) that swap in below `sm` while the full text comes
back above it.

Accessibility preserved — each TabsTrigger keeps an explicit
aria-label and the icons are aria-hidden so screen readers still
announce "Week" / "Month" / "Quarter" instead of duplicate icon
descriptions.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-19 09:39:23 +07:00
Sergey KozyrenkoandClaude Opus 4.7 58d01e0a67 perf(frontend): defer heavy Dashboard tab switches via useTransition
Both tab groups on /dashboard trigger expensive re-renders:
- Analytics ↔ Overview swaps the entire content subtree (Analytics
  alone pulls in a ~386 kB chunk with four Recharts views).
- Week / Month / Quarter invalidates the analytics query and
  re-paints every chart with the new dataset.

Wrapped both setState calls in startTransition so the trigger
buttons update on the urgent track while the heavy work commits as
low-priority. React will discard intermediate frames if the user
clicks again before the previous transition finishes.

Surfaces the combined isPending state via a subtle opacity-60 dim
plus aria-busy on the content region — gives users feedback that
the heavier render is in flight without a spinner flash.

Verified: build, lint (0/44 baseline), 475/475 tests, browser smoke
on /dashboard with no console errors after switching periods.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-19 09:39:22 +07:00
Sergey KozyrenkoandClaude Opus 4.7 dea9e38107 refactor(frontend): unify the whole codebase on function declarations
Brings every top-level React component and hook outside of
src/components/{ui,shared} (those were already migrated in 9d5daf7)
in line with the shadcn/ui new-york-v4 canonical style:

- features/* (flows, knowledges, resources, authentication, …)
- pages/* (dashboard, flows, knowledges, settings, templates, login,
  oauth-result, resources)
- providers/* (all eleven context providers + their useXxx hooks)
- components/icons/* (every SVG icon + provider-icon, flow-status-*)
- components/layouts/* and components/routes/*
- hooks/* (use-* hooks in their own files)
- Calendar UI component (missed in the earlier pass — also dropped
  the redundant `displayName = 'Calendar'` and the empty
  `CalendarProps = DayPickerProps` alias)

Net diff is +50 lines (1842 ins / 1792 del — the lines that were
removed are the `const X = (…) =>` declarations and the matching
closing `;`, plus a handful of explicit `React.FC<…>` type
annotations that no longer add anything; the lines added are the
`function X(…) {`/`}` envelopes).

What we deliberately did NOT touch:
- Inner components/hooks defined inside other functions — keep their
  arrow form so the local-scope intent stays visible.
- React.memo / React.lazy wrappers — `const X = memo(InnerX)` and
  `const X = lazy(() => …)` aren't components themselves.
- Render-callback props (renderFlowItem, renderItem, …).
- Plain utilities, helpers, factories, REST/GraphQL builders and
  every file under lib/, models/, schemas/, types/, graphql/types.ts.
- The few callback-ref-composing components (autocomplete, textarea,
  markdown-editor) keep their explicit `ref?: Ref<CustomShape>`
  because ComponentProps can't express their custom imperative shape.

Bonus cleanup along the way: removed the now-unused
`import * as React` from settings-prompt.tsx (was only there for the
deleted `React.FC` typing).

Verified: pnpm run build, lint (0 errors / 44 warnings unchanged
baseline) and 475/475 vitest tests.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-19 09:39:22 +07:00
Sergey KozyrenkoandClaude Opus 4.7 39adab6f8c chore(frontend): drop redundant displayName outside components/ui
Removes two leftover `X.displayName = 'X'` lines that just duplicated
the function's own name:
- src/components/shared/terminal/terminal.tsx
- src/components/shared/monaco-terminal.tsx

Two other displayName usages in the wider codebase are kept on
purpose:
- markdown.tsx — factory creates an inner `Renderer` and stamps it
  `Highlighted(${ComponentName})` so each instance shows up
  distinctly in React DevTools.
- file-manager-row.tsx — the function is named `FileManagerRowImpl`
  and re-exported through React.memo as `FileManagerRow`; the
  displayName carries the public name across the memo wrapper.

The five `.displayName` mentions in src/pages/settings/settings-prompt*
are property reads (`promptInfo.displayName`, `row.original.displayName`)
on domain data, not component metadata — untouched.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-19 09:39:22 +07:00
Sergey KozyrenkoandClaude Opus 4.7 99f8485ae0 refactor(frontend): adopt shadcn/ui new-york-v4 component style
Brings all 32 components in src/components/{ui,shared} in line with
the current shadcn/ui new-york-v4 canon (cross-checked against the
upstream registry tooltip / button / input / dialog sources). Net
diff is -637 lines of boilerplate.

Four shape changes per component:
1. function declaration instead of `const X = (...) =>`. Slightly
   cleaner stack traces and Function.name is set automatically.
2. React.ComponentProps<typeof X> (or React.ComponentProps<"button">
   for native elements) instead of
   React.ComponentPropsWithoutRef<typeof X> & { ref?: React.Ref<...> }.
   In React 19 ref is already part of ComponentProps; the explicit
   intersection was leftover noise.
3. `ref` is no longer destructured — it flows through `{...props}`
   spread into the underlying primitive. Exceptions kept verbatim:
   textarea.tsx (custom TextareaRef shape with useImperativeHandle),
   autocomplete.tsx (callback-ref composition in AutocompleteInput
   and AutocompleteContent), markdown-editor.tsx (MarkdownEditorHandle
   with useImperativeHandle).
4. displayName removed everywhere — Function.name covers React DevTools
   now that there is no forwardRef wrapper to hide it.

Verified: pnpm run build, lint (0 errors / 44 warnings unchanged
baseline) and 475/475 vitest tests.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-19 09:39:22 +07:00
Sergey KozyrenkoandClaude Opus 4.7 b5b3cbe99b feat(frontend): per-route document titles via React 19 metadata
Adds a tiny <PageTitle> wrapper around React 19's native <title>
element (which React hoists into <head> automatically) and uses it
from every page-level route. Browser tabs, history, and shareable
URLs now reflect the actual page instead of the static "PentAGI"
that index.html ships.

- Static titles for list and form pages (Dashboard, Flows, New flow,
  Flow report, Templates, Knowledges, Resources, Providers, Prompts,
  API tokens, Login, OAuth).
- Dynamic titles for detail pages — flow title + id, knowledge
  question, template name, provider name, prompt display name — with
  graceful fallbacks while data is still loading.
- Added to every render branch (loading / error / empty / main) so
  the tab title stays correct even before data lands.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-19 09:39:22 +07:00
Sergey KozyrenkoandClaude Opus 4.7 53c140dd70 refactor(frontend): drop React.forwardRef for ref-as-prop (React 19)
Migrates all 32 components in src/components/{ui,shared} from
React.forwardRef<RefT, PropsT> to plain functional components that
take `ref?: React.Ref<RefT>` as a regular prop. React 19 treats ref
like any other prop, so the forwardRef wrapper is no longer needed
and only added boilerplate + an extra type parameter.

Highlights:
- All 32 files keep their displayName so React DevTools and Radix
  Slot composition still surface readable component names.
- Custom-shape refs (TextareaRef in textarea.tsx and
  MarkdownEditorHandle in markdown-editor.tsx) plus the callback-ref
  composition inside autocomplete.tsx are preserved verbatim — only
  the wrapper changes.
- sidebar.tsx contained 22 forwardRef wrappers in one file; all
  converted in the same pass.
- No behaviour, classes, attributes or imports of `import * as React`
  are touched. pnpm run lint:fix afterwards reformatted the auto-added
  blank lines so we are back to 0 errors / 44 warnings (baseline).

Verified: pnpm run build, lint, and 475/475 vitest tests.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-19 09:39:21 +07:00
Sergey KozyrenkoandClaude Opus 4.7 de39cf1065 perf(frontend): defer DataTable filter pipeline via useDeferredValue
Wraps the composite globalFilter ({ columns, query }) in
React.useDeferredValue before handing it to TanStack's state. The
filter input itself stays urgent (already debounced to 300 ms), so
keystrokes still feel instant; what becomes low-priority is the
heavy getFilteredRowModel() recomputation that runs over the full
dataset — ≈353 flows, 180 knowledge documents, ~50 templates and
API tokens. On those screens React can now drop intermediate filter
states when the user is still typing, instead of committing each
debounce tick on the urgent track.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-19 09:39:21 +07:00
Sergey KozyrenkoandClaude Opus 4.7 450e374166 style(frontend): apply prettier formatting fixes
- detail-navigation-buttons.tsx: wrap long cn() call across lines
- knowledge-header.tsx: alphabetize lucide-react named imports
- flow.tsx: reorder Tailwind classes per prettier-plugin-tailwindcss
  canonical order

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-19 09:39:21 +07:00
Sergey KozyrenkoandClaude Opus 4.7 827fd46c81 chore(frontend): silence 5 long-standing lint errors with targeted disables
Drops pnpm run lint from 5 errors to 0 (warnings unchanged at 44).
Each disable is the smallest local suppression with a one-line reason
on the same comment, so the next reader sees why the rule was muted:

- src/components/ui/textarea.tsx (line 79): the effect intentionally
  syncs the internal auto-size trigger with the controlled value prop
  every time the parent changes it.
- src/features/flows/files/use-flow-container-files.ts (line 107): the
  effect calls an async fetcher (fetchListing) whose setState runs
  after await — not synchronously inside the effect body; pathsKey is
  intentionally used in place of the paths array reference.
- src/pages/settings/settings-prompt.tsx (line 344): the useMemo
  branches on data.settingsPrompts which the react-compiler can't
  statically prove stable.
- src/providers/resources-provider.tsx (line 70): intentional
  mount-time loading flag for the REST hydration path.
- src/providers/user-provider.tsx (line 144): refreshAuthInfo's
  setState runs after an async /auth/info fetch, not synchronously.

Build, 475/475 vitest tests, and runtime smoke (dashboard, flows,
knowledges, settings/providers) all pass.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-19 09:39:21 +07:00
Sergey KozyrenkoandClaude Opus 4.7 b713d6d004 chore(frontend): update remaining deps to latest within caret ranges
Runs pnpm update + bumps prettier-plugin-tailwindcss 0.7.4 → 0.8.0
(the 0.8 release is the one that pairs with Tailwind v4).

After this commit, pnpm outdated only reports three deliberately
pinned packages: eslint / @eslint/js (held on 9.x while
eslint-plugin-react still targets eslint ^9) and @types/node (held on
24.x to match the Node 24.12 runtime; npm latest is 25.x for Node 25).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-19 09:39:21 +07:00
Sergey KozyrenkoandClaude Opus 4.7 bd6f3b1c43 chore(frontend): align @types/node with node 24.12
Bumps @types/node 22.19.19 → 24.12.4 to match the actual Node 24.12
runtime the project is built and run on. The latest published tag is
25.x (for the not-yet-released Node 25 line); we deliberately stay on
24.x.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-19 09:39:21 +07:00
Sergey KozyrenkoandClaude Opus 4.7 5ac904d740 chore(frontend): upgrade commitlint to v21
Bumps @commitlint/cli and @commitlint/config-conventional from 20.5.3
to 21.0.1. Dev-only — affects the commit-message hook, not the app.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-19 09:39:20 +07:00
Sergey KozyrenkoandClaude Opus 4.7 41d5277216 chore(frontend): upgrade typescript to v6
Bumps typescript 5.9.3 → 6.0.3.

Adds compilerOptions.ignoreDeprecations = "6.0" to tsconfig.json so the
existing baseUrl/paths setup keeps working — TS 6 has flagged baseUrl
as deprecated (it will be removed in 7.0). The vite-tsconfig-paths
plugin still consumes the paths mapping; nothing else in the codebase
needed changes.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-19 09:39:20 +07:00
Sergey KozyrenkoandClaude Opus 4.7 f7e61b4fe5 chore(frontend): upgrade eslint-plugin-perfectionist to v5 (eslint kept on v9)
Bumps eslint-plugin-perfectionist 4.15.1 → 5.9.0.

eslint and @eslint/js are intentionally kept on 9.39.4 (the latest
9.x): eslint 10 changes the rule context API
(contextOrFilename.getFilename is gone) and eslint-plugin-react still
ships only versions targeting eslint ^9 — the v10 upgrade currently
crashes the lint run with "Error while loading rule
'react/display-name'". Once eslint-plugin-react publishes a v10-aware
release we can finish this bump.

perfectionist v5 already supports the eslint ^8.45 || ^9 || ^10 peer
range, so it ships cleanly today and gives us its v5 rule changes
without dragging eslint along.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-19 09:39:20 +07:00
Sergey KozyrenkoandClaude Opus 4.7 1fa879839e chore(frontend): upgrade vite to v8
Bumps vite 7.3.3 → 8.0.13 and vite-tsconfig-paths 5.1.4 → 6.1.1.

Vite 8 ships rolldown (the Rust bundler that replaced rollup) and that
required two fixes:

1. build.rollupOptions.output.manualChunks: rolldown no longer accepts
   the { chunkName: [packages] } static object form, so it is now an
   (id) => string callback that matches paths under node_modules.

2. src/components/ui/resizable.tsx: switched from the namespace import
   `import * as ResizablePrimitive` to named `{ Group, Panel, Separator }`
   from react-resizable-panels. The v4 release of that library renamed
   PanelGroup → Group and PanelResizeHandle → Separator; rollup's
   loose tree-shaking happened to keep the old names working at
   build-time, but rolldown enforces the export list and the page
   failed at runtime with "Element type is invalid". Public wrapper
   exports (ResizableHandle, ResizablePanel, ResizablePanelGroup) are
   unchanged so the rest of the app keeps working.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-19 09:39:20 +07:00
Sergey KozyrenkoandClaude Opus 4.7 8a3d4e951d chore(frontend): upgrade graphql-codegen toolchain
Bumps the codegen stack: cli 6→7, client-preset 5→6, typescript 5→6,
typescript-operations 5→6 (typescript-react-apollo and
near-operation-file-preset only moved within their minor range).

Configures typescript-react-apollo to emit Apollo v4-compatible imports
via apolloReactCommonImportFrom / apolloReactHooksImportFrom set to
@apollo/client/react, so re-running pnpm run graphql:generate now
produces ApolloReactCommon / ApolloReactHooks namespaces from the v4
subpath instead of the v3 single-entry import.

src/graphql/types.ts is fully regenerated under the new config.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-19 09:39:20 +07:00
Sergey KozyrenkoandClaude Opus 4.7 b6d9983563 chore(frontend): upgrade @apollo/client to v4
Bumps @apollo/client from 3.14.1 to 4.1.9.

Apollo v4 moved React-only exports to the @apollo/client/react
subpath. Updated the three import sites that consume them:
- ApolloProvider in app.tsx
- useApolloClient in providers/resources-provider.tsx
- the Apollo namespace import in graphql/types.ts (covers useQuery,
  useMutation, useSubscription, useLazyQuery, useSuspenseQuery,
  skipToken, *HookOptions, *QueryResult)

Everything else (gql, ApolloClient, ApolloLink, createHttpLink,
InMemoryCache, Observable, split, NetworkStatus, getMainDefinition,
onError, GraphQLWsLink) keeps its existing import path in v4.

Build output shrunk from 223 kB to 191 kB for the apollo-client chunk.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-19 09:39:20 +07:00
Sergey KozyrenkoandClaude Opus 4.7 de18fe2c31 chore(frontend): upgrade zod to v4 with hookform/resolvers v5
Bumps zod 3.25.76 → 4.4.3 and @hookform/resolvers 3.10.0 → 5.2.2
(the resolvers v5 requires zod v4).

zod is used in 25 source files (schemas, form validators, table state
parsers). Build passes without changes — the project relies on the
shared subset of the v3/v4 API (z.object/z.string/z.number/z.enum,
z.infer, .min/.max/.optional, refinements). Runtime form validation
keeps disabling submit until required fields are filled.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-19 09:39:19 +07:00
Sergey KozyrenkoandClaude Opus 4.7 6d227e1d7d chore(frontend): upgrade react-resizable-panels to v4
Bumps react-resizable-panels from 3.0.6 to 4.11.1. The wrapper in
src/components/ui/resizable.tsx only re-exports PanelGroup, Panel and
PanelResizeHandle; their props remain compatible across the major.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-19 09:39:19 +07:00
Sergey KozyrenkoandClaude Opus 4.7 45e509ff48 chore(frontend): upgrade react-day-picker to v10
Bumps react-day-picker from 9.14.0 to 10.0.1. The Calendar wrapper in
src/components/ui/calendar.tsx uses the v9 classNames keys
(button_next/previous, day_button, range_*, week, weekday, weekdays)
and the components.Chevron API; all of these are preserved in v10.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-19 09:39:19 +07:00
Sergey KozyrenkoandClaude Opus 4.7 6c8e4ea35a chore(frontend): upgrade marked to v18
Bumps marked from 17.0.6 to 18.0.3. Used only in
src/lib/report/report-pdf.tsx for PDF generation via marked.lexer();
that API is compatible across the major.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-19 09:39:19 +07:00
Sergey KozyrenkoandClaude Opus 4.7 f655ad872d chore(frontend): upgrade lucide-react to 1.x
Bumps lucide-react from 0.553.0 to 1.16.0. The 0.x → 1.x jump is a
versioning policy change, not a breaking API change — all icon imports
keep working as before.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-19 09:39:19 +07:00
Sergey KozyrenkoandClaude Opus 4.7 8c346275d9 chore(frontend): bump react to 19.2.6 and align @types
Pin React/React-DOM to 19.2.6 and @types/react* to 19.2.0 in
package.json — lockfile already resolved the latest 19.x; this just
makes the minimum explicit so future installs don't regress.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-19 09:39:18 +07:00
Dmitry Ng 39f122467d feat(config): add new embedding and rename database connection pool settings
- Introduced `EMBEDDING_MAX_TEXT_BYTES` to limit the maximum byte size of text sent to the embedding model.
- Renamed database connection pool settings: `DATABASE_MAX_OPEN_CONNS`, `DATABASE_MAX_IDLE_CONNS`, and `DATABASE_VECTOR_MAX_CONNS` for improved PostgreSQL connection management.
- Updated relevant documentation to reflect these new configuration options and their usage.
- Adjusted various components to utilize the new settings for enhanced performance and resource management.
2026-05-18 18:26:52 +03:00
Dmitry Ng 2ce863ec1a feat(toolcall): implement ToolCall logging functionality
- Added ToolCallLogProvider interface with methods for logging tool calls, updating success and failure statuses.
- Introduced proxyToolCallLogProvider to handle ToolCall logging operations.
- Updated flow execution components to integrate ToolCall logging, including flow workers and controllers.
- Enhanced GraphQL schema to support ToolCall logs, including queries and subscriptions for real-time updates.
- Updated documentation to reflect the new ToolCall logging features and their usage.
2026-05-18 11:21:56 +03:00
Dmitry Ng 077ddce476 feat(database): enhance PostgreSQL connection pooling and configuration
- Introduced shared connection pooling for PostgreSQL using `*sql.DB` for sqlc and GORM, optimizing resource usage.
- Added new environment variables: `DB_MAX_OPEN_CONNS`, `DB_MAX_IDLE_CONNS`, and `DB_VECTOR_MAX_CONNS` for configurable connection limits.
- Updated documentation to reflect new connection pooling strategy and provide operational commands for monitoring.
- Implemented shared `pgxpool` for pgvector stores to reduce connection overhead and improve performance.
- Adjusted various components to utilize the new connection pooling setup, ensuring efficient database interactions.
2026-05-18 11:13:43 +03:00
Dmitry Ng f970922ea5 Merge remote-tracking branch 'origin/feature/frontend-next' into feature/next-release 2026-05-16 22:54:28 +03:00
Dmitry Ng 1bb7f8a9a0 feat(flow): add WaitTaskCompletion method and associated tools for assistant
- Introduced WaitTaskCompletion method in FlowWorker interface to block until the current task completes or the context expires.
- Implemented signalTaskComplete to manage task completion signaling across goroutines.
- Added waitFlowCompletion tool to handle waiting for task completion with configurable timeout.
- Updated assistant provider to include wait functionality for flow completion.
- Enhanced templates and tool registry to support new wait functionality.
2026-05-16 22:52:51 +03:00
Dmitry Ng 548c54c761 fix(controller): remove close(aw.input) to prevent nil channel deadlock on assistant finish 2026-05-16 22:51:07 +03:00
Dmitry Ng 888f7e2a4f fix(subscriptions): drop events for slow/disconnected subscribers after 5s timeout 2026-05-16 22:49:49 +03:00
Sergey KozyrenkoandClaude Opus 4.7 31803630d3 feat(frontend): add Anonymize action to knowledge document form
Gated behind the `anonymize.call` privilege from `useUser()`; runs the
current `content` through the `anonymizeText` GraphQL mutation and writes
the result back. Desktop renders the button before Save; mobile collapses
it into the first item of the actions dropdown (the dropdown now appears
for new documents too when the privilege is present).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-17 02:31:47 +07:00
Sergey KozyrenkoandClaude Opus 4.7 35e077fd8b fix(frontend): always refetch knowledgeDocuments on provider remount
The Apollo client's global nextFetchPolicy is 'cache-first', so returning
to /knowledges via SPA navigation would serve potentially stale cache.
Subscriptions are now scoped to /knowledges* and detach on other pages,
so changes from AI agents during flow runs would be missed until a full
reload. Override nextFetchPolicy to keep cache-and-network for this query.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-16 23:26:44 +07:00
Sergey KozyrenkoandClaude Opus 4.7 258b677c01 fix(frontend): scope KnowledgesProvider to /knowledges routes
KnowledgesProvider sat in RootLayout, so knowledgeDocuments (with full
content) and its 3 subscriptions fired on every authenticated page —
a ~2.1 MB payload on /flows, /dashboard, etc. All consumers of
useKnowledges live under /knowledges*, so wrap those routes in a
dedicated layout.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-16 22:45:48 +07:00
Sergey KozyrenkoandClaude Opus 4.7 cc6b525f57 fix(frontend): cap DataTable filter input at 200 chars
A paste of multi-KB content into the filter would land in the URL
verbatim (`?q=` plus the raw blob), which exceeds the practical
reverse-proxy limit (~2–4 KB) and breaks the share-link experience.
The user-facing entry point is a single `<input>`, so a DOM-level
`maxLength` is sufficient — it truncates both typing and paste before
the value ever reaches React state, the URL, or localStorage. 200 chars
is well above any realistic search term while leaving plenty of
headroom for non-ASCII expansion under percent-encoding.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-16 21:41:42 +07:00
Sergey KozyrenkoandClaude Opus 4.7 022e8f3df0 refactor(frontend): unify table URL state under useTableState
The split `useTableQueryFilter` + `usePagination` pair had a latent batching
race: react-router v6 feeds every functional `setSearchParams(updater)`
queued in a single React tick the same pre-batch snapshot, so a `setFilter`
+ `setPage` issued from the same event handler (e.g. a debounced filter
commit landing alongside a paging click) would collapse — the second
write erased the first and `q` disappeared from the URL. The earlier
window.location workaround papered over the symptom for the typical case
but didn't remove the underlying possibility.

Replace both hooks with `useTableState`, which owns filter + pageIndex +
storage roundtrip together and routes every URL write through a single
microtask-coalesced `update(patch)`. When multiple `update` calls fire in
the same tick — `setFilter` and `setPage`, two synchronous handlers, an
effect and a click — they merge into one navigation rather than racing.
Replace conflicts resolve in favour of push, so intentional history
entries (paging) survive coalescence with replace-only updates (filter
typing). The `MemoryRouter` fallback (latest snapshot via ref) is kept
only as a defensive read; the coalescence itself makes it redundant.

A regression test (`two top-level updaters firing in the same tick keep
both params`) locks this in: the previous behaviour failed it, the new
implementation passes it without touching `window.location`.

Read-only siblings stay on `useTableQueryFilterReader` — detail pages
don't write the URL, so no race surface to remove. `usePagination`
deleted entirely; its callers migrated to `useTableState`.

474/474 tests pass.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-16 21:26:14 +07:00
Sergey KozyrenkoandClaude Opus 4.7 019ddcb1a7 fix(frontend): preserve URL params across batched setSearchParams updates
When a debounced filter commit and a paging button click land in the same
React tick, react-router v6 feeds the functional updater of every queued
`setSearchParams` call the same pre-batch snapshot. The second write
overwrites the first instead of stacking, so `setFilter('foo')` followed
by `setPage(5)` collapses to `?page=6` and `q` is lost. The QA pass
caught this on /flows: clicking >> right after typing in the filter
silently dropped `?q=` from the share-able URL.

Build the next `URLSearchParams` from `window.location.search` instead,
since that's the freshest source of truth under BrowserRouter and
sidesteps react-router's batching entirely. Fall back to the latest
react-router snapshot (stashed in a render-synced ref) under
MemoryRouter, which the hook tests use — `MemoryRouter` doesn't sync
its in-memory history to `window.location`.

478/478 existing tests still pass; the browser repro that previously
produced `?page=6` now correctly produces `?q=bypass&page=6`.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-16 20:48:06 +07:00
Sergey Kozyrenko cd0fff34f3 fix 2026-05-16 20:30:38 +07:00
Sergey KozyrenkoandClaude Opus 4.7 ca32c199e8 refactor(frontend): compose DataTable storageKey via usePageStorageKeys
Hard-coding the full `table_4_/settings/prompts:agents` / `:tools` keys in
the page duplicated the route prefix that `usePageStorageKeys` already
computes — if the prefix ever bumps (e.g. `table_4_` → `table_5_` on a
storage migration), every call site silently goes stale. Read the route
base from the hook instead and only own the per-table suffix in the page.
Updated the `storageKey` JSDoc on DataTable to document this composition
pattern.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-16 20:28:15 +07:00
Sergey KozyrenkoandClaude Opus 4.7 de695ec1c5 docs(frontend): DataTable QA scenarios and findings
Capture the QA pass we did against the multi-column search rollout:
INP baselines per page, the four bugs we hit (two fixed in the
follow-up commit, two left as backlog), the negative results from
the adversarial input pass (XSS / SQLi / regex meta / unicode /
prototype pollution via localStorage), and the regression scenarios
(S1–S11) so the next round of changes has a concrete checklist
instead of "run it and see if it feels OK".

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-16 20:22:32 +07:00
Sergey KozyrenkoandClaude Opus 4.7 1d81974fa3 fix(frontend): unique DataTable input id and per-instance storageKey
Two issues uncovered by the QA pass over /settings/prompts, where one page
mounts two DataTable instances:

1. Both inputs shared a hard-coded `id="data-table-search"` and
   `name="search"`. `document.getElementById` only ever found the first
   one, screen readers couldn't disambiguate the two filter inputs, and
   any test selector keyed on the id silently picked the wrong field.
   Switch to `useId()` so the id and name are per-instance, non-empty,
   and stable across renders.

2. Both tables persisted sorting / column visibility / search-column
   narrowing into the same `table_4_/settings/prompts` slot. The last
   writer won the race; on reload the loser inherited the winner's
   state. Add a `storageKey?: string` prop to DataTable (mirroring the
   existing `useTableQueryFilter` API) and pass `…:agents` / `…:tools`
   for the two tables on /settings/prompts so they get distinct slots.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-16 20:22:15 +07:00
Sergey KozyrenkoandClaude Opus 4.7 f12b469199 feat(frontend): extend Settings/Prompts search across both tables
For both the Agent Prompts and Tool Prompts tables, mark every accessor
column with `meta.searchable: true` (name + status fields) and drop the
explicit `filterColumn="displayName"`. Typing "Custom" now surfaces
every prompt currently overridden — a much more useful starting point
than scanning the column visually. Added `columnMenuLabel` overrides
where the underlying accessorKey wasn't human-readable on its own.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-16 17:27:10 +07:00
Sergey KozyrenkoandClaude Opus 4.7 eedc6ece41 feat(frontend): extend Settings/API Tokens search to id and status
Mark `name`, `tokenId`, and `status` with `meta.searchable: true` and
drop the explicit `filterColumn="name"`. The bigger wins are tokenId
(operators often paste a fragment from logs or a request header to find
the owning token) and status (typing "revoked" surfaces every revoked
token without scrolling). Date columns stay excluded.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-16 17:25:03 +07:00
Sergey KozyrenkoandClaude Opus 4.7 3a61d601e0 feat(frontend): extend Settings/Providers search to name and type
Mark `name` and `type` with `meta.searchable: true` and drop the
explicit `filterColumn="name"` so users can also narrow by provider
type — typing "anthropic" surfaces every provider of that type without
having to remember its display name. Updated the placeholder to match
the broader scope.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-16 17:23:31 +07:00
Sergey KozyrenkoandClaude Opus 4.7 8bdc10a41f feat(frontend): extend Templates search to title and text
Mark both accessor columns with `meta.searchable: true` and drop the
explicit `filterColumn="title"` so the picker offers both fields and
OR-matches across them. Searching the body text is the bigger win —
template titles are short, so the previous single-column behaviour
missed templates that referenced the same topic in their body.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-16 17:22:02 +07:00
Sergey KozyrenkoandClaude Opus 4.7 b740c4769e feat(frontend): extend Knowledges search to type, question, and preview
Mark `docType`, `question`, and `content` with `meta.searchable: true` so
the multi-column picker offers all three as candidates and OR-matches
across them. Drop the explicit `filterColumn="question"` so the
zero-config path (search across all `meta.searchable` columns) takes
over. The `Flags` column has no accessor and would need a custom string
extractor to participate — skipped for now.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-16 17:20:37 +07:00
Sergey KozyrenkoandClaude Opus 4.7 fd0edebf83 feat(frontend): extend Flows search to provider and terminals
Switch the `provider` and `terminals` columns from `accessorKey` to
`accessorFn` so the global filter receives plain strings — the provider
name and the joined list of terminal images — instead of the raw object
or array, which the predicate would just stringify to `[object Object]`.
The cell renderers keep reading `row.original` directly, so the visible
output is unchanged.

Dates stay excluded: substring matching against formatted timestamps
gives unpredictable hits (e.g. typing "3" suddenly matches every row
whose ISO contains 03) and date filtering belongs in a range picker.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-16 17:18:54 +07:00
Sergey KozyrenkoandClaude Opus 4.7 63d059834f perf(frontend): debounce DataTable filter input commits
Typing into the Flows search felt sluggish — Chrome reported INP 271 ms with input delay 253 ms, meaning every keystroke had to wait for the previous keystroke's render to complete before the event handler could even run. The chain on every keypress was: `setSearchParams` → react-router rerenders the whole route subtree → TanStack re-derives the global filter → 91 rows × 3 predicate calls → reconcile ~70 cells (Tooltip + Badge + ProviderIcon). At ~250 ms per cycle a fast typist queues several keystrokes behind a running render, so the input appeared to lag visibly.

Split the input value from the upstream commit: `DataTableFilter` now owns a local string state that updates synchronously on every keystroke, while a 150 ms debounce mirrors the value into `onQueryChange`. The router / TanStack cascade now fires once per typing pause instead of once per keystroke, and the input never has to wait its turn behind an in-flight reconciliation. After the change Chrome reports INP 43 ms with input delay 2 ms — typing feels instant.

Externally visible behaviour is unchanged: the X button clears immediately (no debounce on explicit clears), and external `query` resets (back button, programmatic clear) sync down through a guarded effect that ignores the value we just emitted ourselves. Vitest tests that previously asserted against the synchronous commit now wait for the debounced row-model update via `findBy*` / `waitFor`.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-16 16:50:20 +07:00
Sergey KozyrenkoandClaude Opus 4.7 38bc1deda9 refactor(frontend): inline getColumnId into data-table.tsx
`getColumnId` only has one consumer — `lib/` is for utilities shared across the codebase, not for a single-call helper. Move it next to `columnPickerLabel` as a module-level function inside `data-table.tsx`.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-16 16:18:42 +07:00