Move <title> ownership out of detail-page components into the app
shell. The four detail routes — templates/:templateId,
knowledges/:knowledgeId, settings/providers/:providerId,
flows/:flowId — now expose a titleComponent via react-router
handle. A new <DocumentTitle/> in RootLayout walks useMatches()
deepest-first and renders the matched title component. Each one
subscribes to its resource via Apollo with fetchPolicy:
'cache-only', so it reacts to the destination page's own fetch
without issuing a duplicate request.
Fixes the root cause behind bbf943e's sticky workaround: navigation
between sibling documents (DetailNavigation prev/next) tore down
the page-level state that PageTitle held, flashing a generic
fallback during data fetch. The shell-level <DocumentTitle/>
survives the remount, so the title resolves from cache before the
new page has finished mounting.
- Add src/components/shared/document-title.tsx with DocumentTitle
plus FlowTitle / KnowledgeTitle / ProviderTitle / TemplateTitle.
- Wire <DocumentTitle/> into RootLayout and attach
handle={{ titleComponent: ... }} on the four detail routes.
- Remove <PageTitle> calls and now-redundant <> wrappers from the
four page components.
- Drop the sticky setState-during-render hack from PageTitle —
listing pages that still use it never flickered, so the simple
passthrough is enough.
Lint + tsc clean, all 475 tests pass. Listing routes (Dashboard,
Flows, Templates, Knowledges, etc.) keep using PageTitle and can
migrate route-by-route without coordination.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Replace the two ad-hoc useState form objects (createFormData /
editFormData) in settings-api-tokens.tsx with parent-level useForm
hooks per inline row, each backed by a zod schema. Controller in
each DataTable cell re-subscribes to the parent form state on
remount, so subscription-driven row remounts no longer drop user
input — same root cause that 04c6825 fixed, but solved at the
architectural level instead of controlling each Input manually.
- tokenNameSchema (trim + max 255), createTokenFormSchema with
required expiresAt via refine, editTokenFormSchema with status.
- <Input>, <Select> and <Calendar> cells rendered through
<Controller>; RHF owns the values, not React state in the parent.
- CreateRowActions / EditRowActions subcomponents own the
useFormState({ control }).isValid subscription so the Save button
re-renders only the small action subtree on keystrokes instead of
the whole table.
- Ad-hoc required/trim checks dropped from handlers; the schema +
isValid gate handle them. Submit still applies trim() || null
because the GraphQL mutation treats null as "no name."
All 18 test files / 475 tests pass; lint + tsc clean.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Manual formatting pass plus a UI tweak that turns the DataTable
"Columns" trigger into an icon-only ColumnsSettings button to match the
other toolbar controls' compact look. The button keeps its accessible
name via aria-label="Columns" so screen readers and
data-table.test.tsx (`getByRole('button', { name: /Columns/ })`) still
find it. Other touched files are pure prettier reformatting.
All 18 test files / 475 tests pass.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The /providers/new, /knowledges/new and /templates/new routes shared the
generic fallback used during data loading on their respective detail
pages, so the browser tab read "Provider — PentAGI", "Knowledge —
PentAGI" or "Template — PentAGI" while the user was clearly creating a
new entity. /flows/new already did this correctly ("New flow — PentAGI")
and serves as the reference.
Each detail page already exposes an `isNew` flag derived from the route
param, so the fix is a one-line branch in each PageTitle call site:
/settings/providers/new → "New provider — PentAGI"
/knowledges/new → "New knowledge — PentAGI"
/templates/new → "New template — PentAGI"
Verified in chrome-devtools MCP — all three tabs now show the new
titles.
While here, give PageTitle a sticky fallback: once a non-empty title is
rendered, briefly empty children (e.g. a data refetch on the same
route) keep showing the previous value instead of flashing the generic
"Provider/Knowledge/Template — PentAGI" fallback. This helps in-route
loading transitions; it intentionally does not survive route remounts
(navigating Prev/Next between sibling documents tears down the Template
component and the local state with it). A complete fix for the inter-
route flicker requires app-level state or an optimistic Apollo cache
read on mount and is out of scope here.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The Analytics tab on /dashboard renders 4 Recharts plus a Flow
Execution Details list with 130+ Collapsible rows. Switching the period
(Week/Month/Quarter) re-rendered everything synchronously: the
pointerdown→paint interaction had INP=434ms (Google "Needs Improvement"
zone), with processing duration alone accounting for 403ms.
Four small changes, no new dependencies:
1. useMemo on usageChartData / toolcallsChartData / flowsChartData so
Recharts receives stable references when only an unrelated piece
of parent state changes (e.g. tooltip hover state).
2. useDeferredValue(executionStats) so a period switch can repaint
the charts first and reconcile the long list as a low-priority
follow-up. While the deferred value is stale we dim the list to
60% opacity, matching the existing dashboard.tsx convention.
3. content-visibility: auto + contain-intrinsic-size on every
FlowExecutionItem. Off-screen rows skip layout and paint entirely
— this is the cheapest possible virtualization, with no deps,
measurement, or scroll math.
4. React.memo on FlowExecutionItem and TaskExecutionItem so parent
re-renders don't traverse the entire 130-row tree.
Measured via chrome-devtools MCP performance trace:
Before: INP 434ms — input 1ms / processing 403ms / present 29ms
After: INP 134ms — input 1ms / processing 112ms / present 21ms
CLS stays at 0.00.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The inline create/edit row in the API tokens table used uncontrolled
<Input ref={…} defaultValue="…" /> and read the value back from the ref
inside the submit handler. When the apiTokens subscription fired during
the user's interaction, DataTable would reorder rows and the input would
re-mount with defaultValue="" (or the original name), discarding what
the user just typed. The mutation then sent name: null and the freshly
created token appeared as "(unnamed)".
Move both create and edit name inputs to controlled state, drop the
two refs, and read the value from form state at submit time so it
survives any number of re-mounts. While here, give each input a stable
useId() for id, an explicit name="token-name", and autoComplete="off"
to silence the "A form field element should have an id or name
attribute" Chrome DevTools issue that this form was triggering.
Verified via chrome-devtools MCP: creating a token named "qa-pr1-verify"
now stores and displays the name correctly across the
useApiTokenCreatedSubscription refetch.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Two long-running mutations now flip the UI on the urgent track and
reconcile against the server in the background, so the user no longer
sees the latency between a confirming click and the network round-trip.
pages/flows/flow.tsx — inline rename
- New optimistic title state seeded from `flow.title` and updated via
`setOptimisticFlowTitle(next)` at the start of the save handler.
- Mutation runs inside `startTransition(async () => …)` so React keeps
the optimistic title visible while awaiting the rename. On success
the Apollo cache update lands and useOptimistic falls back to the
fresh `actualFlowTitle`; on failure the toast surfaces and the value
rolls back to the cached title automatically.
providers/favorites-provider.tsx — toggle favorite
- `useOptimistic` over the favorite-ids array with an `add`/`remove`
action reducer. addFavoriteFlow and removeFavoriteFlow wrap their
Apollo mutations in `startTransition` and call
`applyOptimisticFavorite` first, so the sidebar's Favorite Flows
list and the row's star both flip instantly.
- Subscription-driven cache refresh still arrives a moment later and
becomes the new `actualFavoriteFlowIds`; React's transition exits
without a visible flicker because the optimistic value already
matches what the cache now holds.
Verified: build, lint (0/44 baseline), 475/475 tests, browser smoke —
clicking the favorite star on /flows immediately moves the row into
"Favorite Flows" in the sidebar and back, no console errors.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Reworks the FormSubmitButton so it slots into every Button use-case
in the codebase, not just the simplest text-only submits:
- Adds an `icon` prop. While the form is idle the icon renders to the
left of `children`; while submitting it is swapped for the spinner
in place, so the button keeps its layout. Plain text buttons stay
the same — the spinner is prepended only during submit.
- Adds an optional `loading` prop. When provided it wins over
`formState.isSubmitting`, which unlocks two real cases we
previously had to skip: submits attached via the HTML `form="…"`
attribute that live outside the FormProvider tree, and submits
whose pending state is the union of several mutation flags.
- Reads `useFormContext()` defensively — it returns null outside
a FormProvider, so the component degrades to "controlled by
`loading` prop only" instead of throwing. That keeps the API
honest for both in-tree and out-of-tree submits.
All other Button props (variant, size, className, onClick,
aria-label, form, …) pass through unchanged because the type
extends `React.ComponentProps<typeof Button>`.
Applied to two more previously-skipped sites:
- settings-provider.tsx Save button (form="provider-form",
loading=isLoading union, Save icon, dynamic label).
- settings-prompt.tsx system and human Save buttons (same shape).
resources-mkdir-dialog now passes FolderPlus via the new `icon`
prop instead of putting it inside children, so the dialog's submit
spinner replaces the folder icon in place during submit.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
New shared component src/components/ui/form-submit-button.tsx. It is
the react-hook-form-flavoured analogue of React 19's useFormStatus:
the button subscribes to the nearest FormProvider via useFormContext
and reads formState.isSubmitting / isValid / isSubmitted itself, so
the surrounding form no longer has to thread loading flags down.
Shows a Loader2 spinner while submitting and disables itself once
the form is dirty-and-invalid (opt out with requireValid={false}).
Applied to the three forms where the submit is a plain
<Button type="submit"> living inside a <Form {...form}> wrapper:
- login-form.tsx — also dropped the manual isSubmitting toggles
inside the form handler (RHF already tracks that through
form.handleSubmit). Kept the OAuth-flow useState because the
provider login does not go through form.handleSubmit; combined
the two states on the OAuth buttons so they also disable while
the form is submitting.
- password-change-form.tsx — dropped the manual isSubmitting state
entirely; the only submit goes through form.handleSubmit. Cancel
and Skip buttons stay as plain Buttons.
- resources-mkdir-dialog.tsx — replaced the submit button; kept the
isCreating flag because Cancel and the Input still need to react
to the mutation in flight.
Five other forms intentionally kept their current submit:
- knowledge-form.tsx uses our HeaderButton (responsive icon/label)
with its own Spinner, not a plain Button.
- flow-form.tsx and templates/template.tsx use InputGroupButton
(icon-only, sits inside an InputGroupAddon).
- settings-prompt.tsx and settings-provider.tsx attach their submit
to the form via the HTML form="…" attribute, so the button is
outside the FormProvider tree — useFormContext would throw.
These also drive disabled state from multiple mutation flags
(create/update/delete/validate) that aren't reducible to a single
isSubmitting.
Verified: pnpm run build, lint (0/44 baseline unchanged), 475/475 tests.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Three nearly-identical calendar icons (CalendarDays / Calendar /
CalendarRange) didn't convey Week vs Month vs Quarter clearly on a
375 px viewport — the differences between the glyphs are too subtle
at 16 px. Swapped them for the first letter of each label (W / M / Q)
with the same compact `size-7` button shape; full label stays on
sm+ as before.
Kept aria-label on the trigger and aria-hidden on the letter span so
screen readers still announce the full word.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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>
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>
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>
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>
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>
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>
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>
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>
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>
- 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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
- 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.
- 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.
- 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.
- 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.
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>
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>
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>
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>
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>
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>
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>