Merge feature/next-release into main (pre-2.2.0) - #384
Merged
Conversation
…servation The roving-tabindex MutationObserver watched childList:true/subtree:true, justified by a comment claiming "the table control swaps button↔menu". Verified against the source: the toolbar's item set is static (every control renders unconditionally; state only drives pressed/disabled props), and each menu keeps its `data-toolbar-item` trigger in the bar while swapping its content in a Radix body portal — outside the observed subtree. So childList observed nothing. Keep only the load-bearing attributeFilter:['disabled'] + subtree (a disabled toggle changes the roving set), and correct the comment. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…text A paragraph line starting with "# " or "> " (reachable by Shift+Enter then such a line — the heading input-rule only fires at a block start, not after a soft break) serialized verbatim through the identity encoder and re-parsed as a heading / blockquote on the next load, silently changing the block TYPE of body text. Fix it as a symmetric pair: the paragraph serializer escapes a line-leading `# `/`> ` to `\# `/`\> `, and the tuned marked Lexer's escape tokenizer — otherwise off to keep `\d`/`\|`/`\\` literal — unescapes exactly `\#`/`\>` back on load. Only these two markers are handled; `-`/`*`/`+`/`1.`/fences overlap with literal regex/glob/backref escapes (`\*`, `\1`, `\|`) the editor must preserve, so they are left alone. Real headings/blockquotes, regex/glob literals, and mid-line `#` are unchanged (full byte-fidelity corpus stays green); adds pin tests. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…g on any edit The heading input-rule fires only on the keystroke that types `# ` at a block start. A block that comes to start with `# ` any other way — deleting the text before an existing `#`, pressing Enter in front of it, pasting — stayed a paragraph, diverging from CommonMark (a leading ATX marker IS a heading) and from what the same text becomes on reload. HeadingAutoformat, a ProseMirror appendTransaction plugin, promotes such a paragraph to the matching heading (stripping the marker) on every doc-changing transaction. It keys off the block's first child, not textContent, so a `# ` after a hardBreak (Shift+Enter) correctly stays body text — that is a soft break inside the paragraph, so the block does not start with `#`. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…ti-line promotion Adds markdown-editor-heading-autoformat.test.ts (57 cases): levels, marker stripping, negatives, hardBreak protection, every trigger path (delete/split/merge/insertText/ setContent/insertContent), the canReplaceWith container gate, multi-target ordering, mark/variable/tag preservation, byte-fidelity interaction, and re-entrancy. The 6 ad-hoc Case B tests move here from the extensions suite. Writing the coverage surfaced a soundness bug: promoting a MULTI-LINE paragraph whose first line starts with `# ` (e.g. `# a`+Shift+Enter+`# b`, then deleting the lead-in) emitted a heading containing a hardBreak, which re-parsed as TWO headings on the next load. HeadingAutoformat now skips any paragraph that contains a hardBreak — a heading is single-line — so such a block stays body text and escapeLineLeadingBlockMarkers round-trips it. Single-line promotion is unchanged. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…ight → panel/content Renames the shared two-pane detail layout to DetailSplitLayout (file detail-split-layout.tsx) and its slots left/right/rightClassName to panel/content/contentClassName, which name the actual roles — a form/meta panel beside the main editor content — instead of bare position. Updates all four consumers (knowledge form, template, settings prompt, settings provider). Also drops a dead GripVertical child passed to <ResizableHandle withHandle /> (the handle renders its own grip and ignores children) and gives the left panel a bg-card fill so the gutters around the centered card match the card colour. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…on desktop) The Diff dialog passed a bare `max-w-7xl`, but DialogContent's default already sets `sm:max-w-lg`. tailwind-merge keeps both (different variants), and at ≥sm the `sm:` rule wins the cascade — so the dialog was pinned at 512px and the split diff overflowed with a horizontal scrollbar. Use `sm:max-w-7xl` so it overrides the default at the same breakpoint, matching the app's other wide dialogs. Verified live: the real Diff dialog now renders at 1280px with no horizontal scroll. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…-lg default The prompt-validation dialog (max-w-2xl) and the provider-test-results dialog (max-w-3xl) had the same latent bug as the Diff dialog: a bare max-w-* cannot override DialogContent's default sm:max-w-lg at ≥sm, so both rendered at 512px instead of 672/768px. Prefix with sm: so they reach their intended width. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Every consumer embedded MarkdownEditorField in a `flex min-h-0 flex-col` column and repeated `min-h-0 flex-1` to make it fill. Bake that default into the field (applied first so a consumer can still override the height), and drop the boilerplate: settings-prompt and template pass no className now, and the knowledge field only keeps its one deviation — a fixed `min-h-[calc(100dvh-5rem)]` for the mobile stack where it isn't inside a flex box. Live-verified all four surfaces: rich editor fills (flex-1, min-h 0) on prompt/template/knowledge-desktop, and knowledge-mobile keeps the fixed 100dvh-5rem height. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
… consumer The mobile fixed height was knowledge-only; make it the field's job for all. The field now derives its own height from useBreakpoint — the SAME hook the layouts switch on — so it fills its flex parent on desktop and takes a near-viewport fixed height on mobile/tablet, everywhere. Knowledge drops its last height className. This also fixes the settings-prompt and template editors, which had no mobile height and were cramped in their stacked mobile layouts. Live-verified all three on desktop (fills) and mobile (min-h calc(100dvh-5rem)). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…lls inside The mobile/tablet branch used `min-h-[calc(100dvh-5rem)]`, only a floor — the stacked forms have no flex parent to cap it, so a long document (a big agent prompt is ~10k px of text) stretched the editor to its full content height and the whole page grew with it. Make it a FIXED `h-[calc(100dvh-5rem)]` so the box stays one viewport tall and the content scrolls inside it. Desktop (fills its pane) is unchanged. Live-verified: the Adviser prompt editor is now 1194px with an internal scrollbar instead of 10.6k px. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…g gotchas Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…tory) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…height goes pure CSS The JS "desktop" threshold was 1200px, which is no Tailwind breakpoint, so anything mixing useBreakpoint with a CSS `md:`/`xl:` variant for the same decision could drift in the 1024–1279 zone. Move the threshold to Tailwind `xl` (1280) — mobile already sat on `md` (768) — so JS layout switches and CSS variants now share the same values. With the breakpoint a real token, MarkdownEditorField drops useBreakpoint and expresses its height in plain CSS: fixed `h-[calc(100dvh-5rem)]` below xl (stacked forms, internal scroll), `xl:min-h-0 xl:flex-1` to fill its pane on the desktop split. No JS hook, no comment. Note: the split view now appears at ≥1280 instead of ≥1200. Verified <xl live (fixed 1194px, internal scroll); the ≥1280 branch resolves to the same `min-h-0 flex-1` fill already measured at 1440px — this browser window caps at ~1141px so the wide layout could not be re-observed here. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…nd md/xl) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…gotchas Re-reviewed every frontend comment against the "names a concrete wrong action" rubric. Cut pure restatements, change-narration, self-defense and bug-history; trimmed mixed comments down to their load-bearing gotcha/contract; kept genuine framework/API/security notes. Relocated a misplaced JSDoc in resources-provider that sat on `error` but described `resources` (fields are alphabetically sorted). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…bers
react-resizable-panels v4 reads numeric size props as PIXELS, not percent
("Numeric values are assumed to be pixels"). So `minSize={30}` was a 30px
floor — a user could drag either panel down to a ~30px sliver (~2.5% on a
1184px group) instead of the intended 30%. `defaultSize={45/55}` were px too
(they only rendered ~45/55% because default sizes normalize to a ratio;
minSize is an absolute per-panel constraint and is not normalized).
Use string percentages so the constraints mean what they read: minSize "30%",
defaultSize "45%"/"55%".
Verified live (chrome-devtools @1440px): separator aria-valuemin 2.536 -> 30;
dragging a panel to its minimum now floors at 29.97% (355px) instead of 30px.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Same react-resizable-panels v4 pixels-not-percent pitfall as detail-split-layout (a55cac3): flow.tsx's desktop two-panel split passed minSize={30}/defaultSize={50} as numbers, so the 30 was a 30px floor — a user could drag either the central-tabs panel or the detail panel down to a ~30px sliver instead of the intended 30%. Use string percentages: minSize "30%", defaultSize "50%". Verified live (chrome-devtools @1440px, dev server on a real flow): separator aria-valuemin=30, dragging a panel to its minimum floors at 29.97% (355px). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Follow-up to a55cac3 / 2a3a553. A percentage minSize scales with the screen: 30% is ~700px on a 2560px display and ~1000px on an ultra-wide — far too large a floor. Give both resizable splits (DetailSplitLayout + flow.tsx) a fixed 390px per-panel minimum (numeric = px in react-resizable-panels v4). defaultSize stays a percentage: flow 50%, DetailSplitLayout 45%/55%. Verified live (chrome-devtools, dev server, real flow + settings-prompt): - 1280px (narrowest desktop): group 1024px, both mins 390px — no collision (2x390 < 1024), drag range 390..633px. - 2560px: min stays 390px (16.9%), not 691px (30%). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The inline "Create Token" row is a data row prepended at index 0, so an active
table filter (globalFilterFn) or a non-first page hid it — clicking Create Token
appeared to do nothing. handleCreateNew now calls setFilter(''), which clears the
filter and resets pageIndex to 0 (clearPageOnFilterChange default), so the create
row is always visible.
Live-verified (docker/HEAD backend): with a non-matching filter, Create Token now
shows the create row and clears the filter.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The rich editor is a contenteditable div with role="textbox", which is NOT a labelable element — a sibling `<FormLabel htmlFor>` can't name it, and two of the three consumers (settings-prompt, template) render no visible label at all, so the field had no accessible name (Lighthouse A5). Thread `aria-label` through MarkdownEditorField to the contenteditable (via the same view.dom passthrough as aria-describedby/aria-invalid) and to the raw textarea, with each consumer supplying its name. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…branch) - vite.config.ts: drop the manualChunks 'monaco' branch — monaco was removed in the monaco→tiptap migration (82b82c5), so the regex never matches. - use-file-manager-selection.ts: remove `setSelection` — no caller destructures it (file-manager.tsx never did); all writes go through rawSelectedPaths. - settings-prompt.tsx: remove the unreachable `isNew` (promptId === 'new') branch — nothing routes to /settings/prompts/new, and it would render a "Create Prompt" header over a "Prompt not found" card. The real create path is `isUpdate === false`. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Subscribing to `formState.isValid` via useFormState flips RHF's internal `_proxyFormState.isValid`, which makes the zod resolver re-run over the WHOLE form (13 agent accordions × ~19 fields + refines) on every keystroke — even in onSubmit mode. `isValid` is only needed to gate the unsaved-changes dialog, so validate lazily when that dialog opens instead of subscribing. Live INP on the Name field: steady-state per-keystroke processing 152ms worst-case → ~5ms. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Both provider components built their context `value` as a fresh object literal each render (providers-provider also re-sorted into a new array and recreated its setter), forcing every consumer to re-render on each provider render. Memoize the sorted array, the setter, and both value objects — the two non-memoized outliers among the app's context providers. No behavior change. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- main-sidebar.tsx: the three icon-only "New" quick-action links wrapped a bare <Plus/> with no accessible name (WCAG 2.4.4/4.1.2) — add aria-label "New flow"/"New template"/"New knowledge". - editor toolbar heading/list/table-align menus: single-select options marked the active one with only a visual <Check>. Add role="menuitemradio" + aria-checked (mirroring the shipped Header-row menuitemcheckbox) so screen readers announce which option is active. Visual affordance unchanged. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…roduction
`updateMessage` only mutated a ref, so the visible status ("Authentication in
progress...") never changed in production — it appeared to work only in dev via
StrictMode's double-mounted useLayoutEffect. Call setStatusMessage directly and
drop the dead ref + layout-effect machinery. The OAuth flow itself was already
correct (close/redirect use locals); only the visible text was frozen.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The multi-drag "N items" drag image set `hsl(var(--primary))`, but the theme tokens are OKLCH values — `hsl(oklch(...))` is invalid CSS and dropped, leaving a transparent badge with default text. Reference the tokens directly via `var(--primary)` / `var(--primary-foreground)` (theme-aware, valid). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Drop comments that restate the code they sit on (public-route auth block, app.tsx catch-all-route label, image-handle overlay description) per the project's default-zero comment policy. Kept the load-bearing inline note that password_change_required is local-users-only. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…e under 200 The streaming ZIP refactor made `ZipResources` open each blob inline while `streamZipArchive`'s writer commits HTTP 200 on the first byte. A blob missing on disk (a DB record whose blob file is gone) failed mid-stream, so the client received a 200 with a central-directory-valid but incomplete archive — the missing files silently dropped (regression vs main, which buffered then sent). Stat every blob up front; a missing one now returns before any byte is written, so `streamZipArchive` emits a clean structured error instead. Keeps the streaming memory benefit. Tests (both proven fail-on-unfixed / pass-on-fixed): a unit test asserts the writer stays empty when a blob is missing, and a download-handler test asserts a missing blob in a multi-file ZIP returns a clean 500, not a truncated 200. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The renameFlow / putUserInput / callAssistant GraphQL resolvers passed the title/input straight to the controller with no non-empty check, while the equivalent REST handlers reject them with 400. An empty flow title in particular then fails the Flow model's `required` invariant and breaks the REST GET /flows listing for that user. Mirror the REST guard at the resolver boundary, matching the existing createFlow "... is required" checks. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The REST handlers reject empty or oversized knowledge fields and over-long API-token names through their request-model validate tags, but the GraphQL mutations — the path the web UI uses — accepted them unchecked, so the same entity could be stored past its documented limits depending on the caller. Mirror the REST caps at the resolver boundary: - createKnowledgeDocument / updateKnowledgeDocument: require content (and question on create), and cap content/question/description/codeLang lengths. - createAPIToken / updateAPIToken: cap the token-name length. Limits are kept in sync with server/models via mirror comments. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…leanup features - Updated the worker node guide to include a hardened Docker-in-Docker (dind) configuration, emphasizing security measures such as OPA authorization and seccomp profiles. - Introduced new scripts for managing dind containers, including `dind-cleanup.sh` for removing stale containers and `run-dind.sh` for starting the dind container with authorization. - Added configuration files for the dind setup, including `authz.rego`, `seccomp.json`, and `daemon.json`, to enforce strict security policies. - Implemented a systemd timer and service for regular cleanup of nested containers, ensuring efficient resource management. - Expanded documentation to clarify the purpose and usage of new scripts and configuration files, enhancing user understanding of the setup process.
- Refactored state initialization in tests to use NewState instead of New for clarity and consistency. - Ensured that the changes maintain the functionality of the tests while improving code readability.
…t report - Changed the model version from `claude-opus-5` to `claude-opus-4-8` in the configuration file for both generator and refiner. - Updated the test report to reflect the new model version, including adjustments to success rates and average latencies for the generator and refiner agents. - Revised overall average latency in the test report to account for the updated model performance.
- Updated the documentation in `docker.md` to provide a detailed explanation of the explicit capability allow-list used for primary containers, emphasizing the rationale behind the selected capabilities and the deliberate omission of `MKNOD`. - Revised comments in `client.go` and `tools.go` to reflect the decision against using `no-new-privileges`, clarifying its impact on privilege escalation testing and container security. - Enhanced the overall clarity and completeness of the capability management section to aid understanding of security measures in the Docker-in-Docker setup.
…upport - Added new environment variables `DATABASE_EXTENSIONS_SCHEMA` and `DATABASE_SEARCH_PATH_VIA_OPTIONS` to `.env.example` and `docker-compose.yml` for better management of PostgreSQL schemas in multi-tenant deployments. - Updated the README and configuration documentation to explain the purpose and usage of the new variables, particularly for setups using Supabase. - Implemented schema verification and initialization logic in the backend to ensure proper handling of tenant-specific schemas during database connections. - Enhanced the installer and server settings forms to include the new configuration options, improving user experience and clarity.
- Added new environment variables to `.env.example` and `docker-compose-graphiti.yml` for Graphiti, including `GRAPHITI_CPUS`, `GRAPHITI_MEMORY`, and various ingestion and logging settings. - Updated the README to clarify the optional nature of the Graphiti integration and its configuration requirements. - Enhanced the backend installer to support new Graphiti configuration options, ensuring proper handling of the Graphiti stack. - Improved the user interface for Graphiti settings in the installer wizard, allowing for better configuration management. - Revised documentation to reflect changes in Graphiti's deployment modes and integration capabilities.
…roviders - Replaced direct error logging calls with a centralized `obs.LogErrorOrCancel` function to ensure consistent logging behavior for errors, particularly in cases of context cancellation. - Updated various controllers and providers to utilize the new logging method, enhancing maintainability and clarity in error handling. - Introduced new methods for invalidating subtasks and tasks in the flow and subtask controllers to improve resource management and prevent stale references. - Enhanced the handling of database errors in task and subtask status updates to treat missing records as completed, ensuring idempotent task shutdowns.
- Revised the worker node guide to clarify the use of TCP over TLS for worker connections, addressing potential failure modes associated with socket mounts. - Updated environment variable configurations to include `METRICS_IP`, ensuring proper binding for Docker metrics. - Enhanced the `policy-tests.sh` script to improve transport resolution for the DinD API, allowing for better handling of both TCP and Unix socket connections. - Improved documentation for the metrics endpoint and security model, ensuring clearer instructions for users.
…rker node - Introduced a new section in the worker node guide detailing the deployment of a browser scraper on the worker node's host Docker, including setup instructions and security considerations. - Added a script for launching the scraper with appropriate configurations, including environment variables for credentials and resource limits. - Updated firewall requirements to include access for the scraper service, ensuring clarity on network configurations. - Enhanced documentation to guide users on verifying the scraper's readiness and integrating it with the main PentAGI node.
- Increased temperature settings for `simple` and `simple_json` models from 0.7 to 1.0 for enhanced variability. - Changed model references from `gemini-3.5-flash` to `gemini-3.5-flash-lite` for `reflector`, `searcher`, and `enricher`, optimizing for cost and performance. - Updated `coder`, `installer`, and `pentester` models to `gemini-3.6-flash`, reflecting the latest version with improved pricing and capabilities. - Revised test report to reflect new model configurations, including updated success rates and average latencies, ensuring accurate performance metrics. - Enhanced documentation to clarify model descriptions and pricing adjustments for better user understanding.
- Updated the model references in `gemini.yaml` from `gemini/gemini-2.5-flash-lite` to `gemini-3.5-flash-lite` and `gemini/gemini-2.5-flash-lite` to `gemini-3.1-flash-lite` for improved performance. - Changed the model references in `openai.yaml` from `openai/gpt-5-mini` to `gpt-5.6-luna` and `openai/gpt-5.4-nano` to `gpt-5-nano`, reflecting the latest versions for better capabilities.
…integration - Added new environment variables to `.env.example` for Neo4j memory and transaction settings, including `NEO4J_HEAP_INITIAL_SIZE`, `NEO4J_HEAP_MAX_SIZE`, `NEO4J_PAGECACHE_SIZE`, and `NEO4J_TRANSACTION_MAX`. - Updated `docker-compose-graphiti.yml` to mount Neo4j configuration and plugin directories, ensuring proper integration with the Graphiti stack. - Introduced static configuration files for Neo4j and APOC in the `examples/neo4j/conf` directory, providing a clear structure for user-editable settings. - Enhanced the README to document the new configuration options and directory structure for Neo4j, improving user guidance for setup and customization.
…ker node - Updated `METRICS_IP` documentation to reflect new metrics ports (8080, 9100) for cAdvisor and node-exporter. - Added detailed instructions for deploying cAdvisor and node-exporter on the worker node's host Docker, including scripts for setup. - Enhanced the security and firewall configuration section to include new metrics endpoints. - Improved overall documentation clarity for metrics integration with PentAGI's observability stack.
…list A markdown table with a long unbreakable cell (e.g. a FLAG hash) overflowed the message bubble and dragged the entire message list into a horizontal scroll, cutting off the left edge of every line. Wrap tables in an overflow-x-auto container so wide content scrolls inside its own box while the list scrolls only vertically. Covers both automation and assistant, which render through the same Markdown component. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> (cherry picked from commit 8745c51)
- Updated the success rate calculation in `PrintAgentResults`, `PrintSummaryReport`, and `WriteReportToFile` functions to handle cases where total tests are zero, preventing division by zero errors. - Enhanced comments in `convertToAgentResults` to clarify the impact of unsupported capabilities on overall success rates and average latency.
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using default effort and found 2 potential issues.
There are 4 total unresolved issues (including 2 from previous reviews).
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 5610cd9. Configure here.
fix(flows): allow pending trace IDs in flow responses
…umentation - Updated the API documentation in `docs.go`, `swagger.json`, and `swagger.yaml` to remove the maxLength constraint for password fields, simplifying the validation requirements. - Added `github.com/docker/go-units` as a direct dependency in `go.mod` to support updated functionality.
…imum connections limit - Modified the minimum connections setting in the connection pool configuration to ensure it does not exceed the maximum connections limit, enhancing resource management and preventing potential connection issues.
… the database - Added a specific error check to handle cases where the provider is not found in the database, providing clearer feedback on retrieval failures.
- Consolidated sentences in the README and installation configuration guide for better readability. - Removed unnecessary line breaks and ensured consistent formatting for installation instructions and requirements. - Enhanced links to related documentation for a smoother user experience.
…ance - Renamed the test function to reflect a broader focus on CLI argument guidance rather than specific XSStrike flags. - Enhanced test descriptions and guidance to cover common AI-agent mistakes, ensuring clarity and tool-agnostic advice. - Updated the template to remove specific tool references, promoting a more generalized approach to command-line argument handling.
… calls - Introduced a new test case to verify that multiple nil FunctionCall tool calls are correctly removed from the message content, ensuring only relevant text parts are retained. - Removed the previous test file `newbodypair_reg_test.go` as its functionality is now covered in the updated test suite.
- Updated the README to include xAI as a supported provider, along with pre-configured provider files for testing. - Added a new configuration file for xAI with detailed model settings and pricing. - Created a testing report for xAI, summarizing the performance and success rates of various agents. - Enhanced the VSCode launch configuration to include xAI provider options for testing.
- Added `RenameFlowsProvider` and `ResetFlowsProviderToDefault` methods to the `FlowController` to handle renaming of user-defined providers and resetting to built-in defaults. - Introduced SQL queries for bulk updating flow and assistant provider names based on user actions, ensuring idempotency and safe retries. - Enhanced error handling and logging for provider updates, ensuring that flows and assistants remain valid after provider changes. - Added unit tests to verify the correct behavior of provider renaming and resetting functionalities.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.

Description of the Change
This PR merges the accumulated
feature/next-releasebranch intomain, forming the 2.2.0 release: 559 commits, 730 files, +81649 / −27334 sincev2.1.0. The summary is organized by theme rather than by commit.On the commit count. 559 = 533 non-merge + 26 merges, but only 25 non-merge commits were authored directly on this branch.
feature/frontendwas merged twice without squashing (123 commits) and a staging branchintegrate/open-prs-v2brought 344 more, so 436 of the 533 (82%) are frontend commits from one author. A further 26 commit subjects appear twice — that staging branch cherry-picked the open external PRs, which were then merged again from their own fork branches — leaving 507 unique subjects.Problem
After 2.1.0, PentAGI could only ever be one installation per set of backing services — two instances sharing a PostgreSQL, a worker node or a Graphiti would collide on flow ids, container names, host ports and sessions. Agents chose their own search engine from seven separate tools, each of which swallowed its failures into a prose string the LLM could not distinguish from a result — and Google Custom Search had been sending unauthenticated requests for every deployment. Sandbox containers inherited Docker's implicit default capability set with the host socket mounted by default. The prompt/template/knowledge editor corrupted the very documents the backend parses as Go templates. The web UI had no automated end-to-end coverage at all, its CI was almost entirely advisory, and a backend that was down rendered as a pristine empty account. Several fail-fast paths — an unreachable telemetry collector, one unreadable directory entry, a deadlocking work queue — could take down startup or a whole request.
Solution
Multi-instance deployment —
TENANT_ID(new) Several installations can now share one PostgreSQL, one worker-node Docker daemon, one Neo4j/Graphiti and one Langfuse. A single helper module namespaces the PostgreSQL schema, worker container/volume names and labels, Graphiti group ids, auth key derivation and cookie names, and telemetry identity. Empty value is a strict no-op — every helper degrades to an identity function and created objects are byte-identical to 2.1.0, enforced in one place and covered by a backward-compatibility contract test. The tenant schema is created at boot, the DSN is rewritten once so sqlc/GORM/goose/pgvector all follow, and startup refuses to boot ifcurrent_schema()is not the tenant's.DATABASE_EXTENSIONS_SCHEMAandDATABASE_SEARCH_PATH_VIA_OPTIONScover managed Postgres and poolers (Supabase, PgBouncer, Supavisor). Migrations now run under an advisory lock against a schema-qualified goose version table — which also fixes a pre-existing race between two single-instance deployments booting against one database.Graphiti/Neo4j resource limits and LLM presets (new) Both containers gain explicit sizing (
GRAPHITI_CPUS/_MEMORY,NEO4J_CPUS/_MEMORY/_SHM_SIZE/_NOFILE) instead of running unbounded. Graphiti's own LLM client is now a selectable preset (GRAPHITI_LLM_CLIENT_TYPE: openai/gemini/custom/litellm) loaded fromexamples/graphiti/<provider>.yaml, replacing the singleGRAPHITI_MODEL_NAMEvariable, and gains explicit ingestion-policy, worker-pool and extraction/taxonomy tuning knobs. Neo4j ships a user-editableexamples/neo4j/conf/(neo4j.conf,apoc.conf) plus a bundled APOC plugin, mounted read-only viadocker-compose-graphiti.yml; the installer wizard's Graphiti step was rewritten to configure all of it.One
web_searchtool replacing seven engine tools Agents now supply a query plus an intent mode (links/answer/research/exploit) and never name an engine; a per-mode fallback table is the single source of priority. Engines moved into a neutralsearcherspackage behind aSearcherinterface returning typed retryable/fatal errors, so the orchestrator can retry, fall back, or degrade. Previously every engine returned its failure as a result string with a nil error, and its Langfuse event was literally named "search engine error swallowed". Two new engines: Firecrawl (external contribution) and an opt-in internal analytics engine that scrapes and summarizes pages with no paid API. Search-log rows are now written once per call, attributed to the engine that actually served the result.Sandbox capability model The primary terminal container moved from Docker's implicit defaults to
CapDrop: ALLplus an explicit 14-capability allow-list — MKNOD deliberately omitted (block-devicemknod+debugfsis this project's own identified primary escape vector) and SYS_PTRACE deliberately added for exploit-development tooling. Every container getsPidsLimit=2048. NewDOCKER_INSIDE_HOST/_TLS_VERIFY/_CERT_PATHseparate which daemon a sandbox may reach from which daemon PentAGI uses, so an operator can point agents at a dedicated dind endpoint instead of bind-mounting the host socket; setting the host also stops autodetecting and mounting it. A 12-file hardened Docker-in-Docker kit (fail-closed OPA policy, seccomp profile blocking block-devicemknoddaemon-wide, cleanup timer, 1374-line isolation suite) ships underexamples/guides/worker_node/as operator-deployed guidance — not enforced by the application. The guide itself grew two new sections — deploying the browser scraper and Prometheus exporters (cAdvisor/node-exporter) on the worker node's host Docker — plus a clarified TCP-over-TLS failure-mode writeup for worker connections.Providers, models and reasoning MiniMax is the tenth first-class provider. A capability model (
ReasoningModeadaptive/budget/off plus per-modelefforts/supported/cannotDisable/defaultOn, derived at runtime from the langchaingo reasoning tables rather than model-name regexes) lets the Settings UI offer only thinking controls a model actually honours, andxhigh/maxnow reach the wire. Adaptive-only models can no longer be sent budget thinking and get a 400. The Bedrock/Anthropic request-body rewriting middleware is deleted — langchaingo emits adaptive natively. Five near-duplicate OpenAI-compatible providers collapsed onto a shared base (~190 → ~78 lines each) and five per-type switches onto a table-driven registry, so a new provider can no longer return 422 from the REST API for want of a whitelist edit. A price audit against official sources corrected real overstatements (deepseek-v4-pro was 4× the official rate; one Bedrock Mistral model 8×), guarded by a new drift test. Gemini's default catalog was retuned too:coder/installer/pentestermoved togemini-3.6-flashandreflector/searcher/enrichertogemini-3.5-flash-lite, trading a little accuracy for cost — the refreshed ctester report still clears 94% overall.Markdown editor rebuilt (Monaco removed) A 47-module subsystem on official
@tiptap/markdownis now the single surface for the three places users author markdown the backend consumes. This matters becausegetMarkdown()output is the Gotext/templatethat gets stored and server-validated: a table pipe inside a{{ … }}pipeline made prompts impossible to save at all, a code-fence detection bug cost one real knowledge document 556 words, and marked's default escaping corrupted regex/glob/UNC backslashes on first load. Four ReDoS paths on stored content were rewritten to linear form (one measured 4.9 s of frozen main thread at 250 KB, now 0.48 ms), and two perf fixes cut 1 MB document load 69 s → 4 s.Web UI platform Self-healing recovery from the two SPA crashes reproduced live (stale hashed chunks after a redeploy; DOM desync from an extension or auto-translation). Route payloads cut by removing an unused ~198 KB gzip of recharts and highlight.js from every route including
/login(/settings/account502 → 305 KB). Row virtualization on/flows(41 657 → 1 121 DOM elements at 1116 rows). A failed load no longer renders as an empty account, and detail pages now distinguish not-found from an authorization denial, a partial error, and a failed background refetch. TypeScript now gates the build — 76 type errors had accumulated unnoticed becausevite buildstrips types and the old baretscchecked nothing. Accessibility was run as a program: focus return, accessible names, table/listbox semantics, and every coloured badge retuned to clear WCAG AA in both themes.End-to-end suite and CI (new) Three tiers: a hermetic mock tier (cassette-driven mock of the whole API surface, run against the production bundle — no backend, no secrets, fork-safe, the PR gate), a local tier (branch-built image in an isolated compose stack driven by a mock LLM, exercising the real agent loop), and a stand tier (LLM-independent smoke against a live deployment behind a label gate plus a protected Environment). 39 spec files / 125 spec cases, 20 container-generated visual baselines, 8 e2e unit-test files. A second pass was explicitly about falsifiability — several gates were written so they could not fail and several CI signals reported green without reading anything; closing those holes is what surfaced most of the UI defects above.
Reliability A deadlocking work queue (found via a file-manager request that never returned) fixed three times and then deleted in favour of a bounded errgroup. An unreachable telemetry collector no longer blocks
main()forever or kills the process, and telemetry is now actually flushed on SIGTERM. Container listings return partial success instead of a 500 that blanked the file browser — and were rewritten offlsparsing under a TTY, which made listings entirely unreadable on alpine/busybox images. ZIP downloads stream instead of buffering whole archives. A flow whose worker fails to start is no longer left visible in the listing. Graphiti's health check retries before permanently disabling the knowledge graph for the process lifetime, and its 5xx responses degrade to a soft result instead of burning tool-call retries. Error logging across controllers and providers was centralized behindobs.LogErrorOrCancelso context-cancellation noise no longer masks real failures, and task/subtask status updates now treat an already-missing database row as completed, making shutdown idempotent instead of erroring on a stale reference.A first-deployment guide, six troubleshooting sections written against real user issues, deep operational docs for the three deployment-affecting features, and five docs-only design RFCs (Kubernetes, Vertex AI, BrowserOS MCP, tool/model fallback, headless crawler) — none of which ship runtime code, env vars or migrations.
Closes #61, #335, #338
Refs #249, #268, #279, #288, #309, #310, #313, #321, #322, #324, #336, #337, #341, #342, #344, #380
Type of Change
Areas Affected
Testing and Verification
Test Configuration
Test Steps
cd backend && go build ./... && go vet ./... && go test ./...cd frontend && pnpm install && pnpm run lint && pnpm typescript && pnpm run testdocker compose up -don a clean database; confirm both migrations apply and the startupInstance identityline reports the expectedtenant_id/data_dir/schema/installation_id.TENANT_IDempty, confirm container names, the PostgreSQL schema, Graphiti group ids and the session cookie are unchanged from 2.1.0.TENANT_ID,DATA_DIRandDOCKER_PORTS_BASEagainst one PostgreSQL and one worker node; run a flow in each and confirm no collision on flow ids, container names, host ports or sessions, and that a session from one is rejected by the other.web_searchin each of the four modes; verify onesearchlogsrow per call attributed to the winning engine, and that an unconfigured engine is skipped rather than failing the chain.{{ .Var | urlquery }}pipeline, an indented code fence and regex backslashes; save through rich mode, reload, save again — confirm it converges and passes server-side template validation.pnpm run e2e(mock tier), then the local tier against a branch-built image.docker inspect) and thatPidsLimitis 2048.Test Results
go build ./...,go vet ./...clean;gofmtclean on every touched file;go test ./...— 40 packages ok, 0 failures.pnpm run lintclean at--max-warnings 0.ctesterreports regenerated against the new default catalogs and committed underexamples/tests/— 14 refreshed (OpenAI goes 281/281 on o3/o4-mini/gpt-4.1-mini → 303/304 on gpt-5.6-terra/gpt-5.4-mini/gpt-5.4-nano) and 4 new (hcnsec 283/295, minimax 275/295, vllm-mixed 295/295, vllm-qwen3.6-27b-fp8 295/295). These are the empirical backing for the provider and model-catalog claims.integrate/open-prs-v2merge silently dropped 13 test files covering code that does ship (docker container listing/stat fan-out, graph validation, observability shutdown drain, provider build/get/price consistency, openai-compat adaptive thinking, pconfig reasoning effort/off, static serving). Those areas ship with their regression suites deleted — see Additional Notes.Security Considerations
Sandbox containment. The primary terminal container now runs
CapDrop: ALLwith an explicit allow-list rather than inheriting Docker's implicit defaults, with MKNOD withheld to close the block-devicemknod+debugfshost-disk read path, andPidsLimit=2048as a fork-bomb guard on every container.DOCKER_INSIDEnow defaults tofalsein.env.examplewith the risk spelled out:truebind-mounts the host Docker socket into every sandbox, which prompt injection can use to escape to the host (issue #337).DOCKER_INSIDE_HOSTlets an operator designate a dedicated daemon and stops the host socket being mounted at all.Be accurate about what did not ship: the
no-new-privileges:truehalf of PR #355 was deliberately reverted — the capability bounding set already caps what any process can gain, while the flag unconditionally broke SUID/SGID privilege-escalation testing andsudo/sufrom a non-root shell, both routine pentest workflows. The shipped mitigation is the capability allow-list plus PidsLimit. Everything stronger (OPA authz, custom seccomp, no host bind-mounts) is operator-deployed guidance underexamples/guides/worker_node/and is not enforced, verified, or even detectable by the application.OAuth account linking — audit before enabling. OAuth login now matches users by email alone (the
type = oauthfilter was removed) and issues the session with the matched account's actual role privileges (previously hardcodedRoleUser). This fixes a 500 when an OAuth email collided with a local account, but it means if a local admin account's email address can be registered or controlled at the configured IdP, that is a privilege-escalation path. Audit local account emails before enabling Google/GitHub OAuth. Two properties bound the risk: logins are rejected when the provider reports the email unverified (the resolver now returns(email, verified, err), so a provider that forgets to report fails closed), and the match is exact-case, so a case-mismatched IdP address creates a second account rather than linking into the local one.Email case sensitivity. Emails are now deliberately case-sensitive on every path against
UNIQUE(mail), and the shared validator was widened (case-insensitive, TLD{2,}), soAdmin@x.comandadmin@x.comare distinct accounts — newly reachable. A fully case-insensitive scheme (citext/UNIQUE(lower(mail))) is an explicit open follow-up.Multi-tenant isolation. With
TENANT_IDset, cookie and API-token signing keys derive fromCOOKIE_SIGNING_SALTplus the tenant and three cookies are renamed (session plus the OAuth CSRF pair), so a session or token minted by one instance is cryptographically rejected by another even with an identical salt. An invalid tenant id aborts startup rather than being normalized, because collapsing two tenants onto one namespace is precisely the collision tenancy exists to prevent.Other hardening. Passwords are capped at the 72 bytes bcrypt can actually hash (previously 73–100 characters passed validation and then 500'd inside the hash call). Four ReDoS paths in editor regexes running synchronously over stored, LLM-influenceable content were rewritten to linear form. Authoring-time URL/image protocol allowlists prevent
javascript:/data:text/html/data:image/svg+xmlbeing persisted into a document. Container-listing responses no longer leak raw Docker-layer text (container ids, daemon address) in a 200 body, and the request path count is bounded at 128 so an attacker-chosen count cannot multiply the per-path entry cap into a large fan-out. pprof no longer listens on a hardcoded:7777in every deployment..env.*files are excluded from the Docker build context, and stand e2e reports are redacted before their public artifact upload.Performance Impact
Measured improvements:
/settings/account502 KB → 305 KB,/dashboard474 → 378 KB, by removing ~198 KB gzip of recharts and highlight.js that every route —/loginincluded — was downloading unused./flowsat 1116 rows: DOM elements 41 657 → 1 121, tbody rows 1116 → 22, a11y tree ~485 KB → ~80 KB. Detail-navigation sheet ~1800 → ~32 nodes.Costs to be aware of:
PERPLEXITY_MODELdefault moved to the more expensivesonar-protier.effort=highon the wire; the only escape is an explicitreasoning: {mode: off}.Documentation Updates
backend/docs/config.mdmulti-instance, worker Docker access, new engines, Graphiti/Neo4j resource limits and LLM presets)Mapscalar,renameKnowledgeDocument, minimax)examples/guides/installation_configuration.md; a 12-file hardened dind kit underexamples/guides/worker_node/;frontend/docs/e2e.md;frontend/docs/list_detail_pages.md; rewrittenbackend/docs/{database,docker,flow_execution}.md; five docs-only RFCs underexamples/proposals/;CLAUDE.mdpassword policy and search-engine contributor guide;examples/neo4j/conf/reference configsDeployment Notes
Deployment steps
main.docker compose build(requires Node 24.17.0 / pnpm 11.8.0 — both pinned;pnpm buildis nowtsc -b && vite build, so a TypeScript error fails the image build).docker compose up -d— both migrations apply on boot.Leave
TENANT_IDempty when upgrading. Setting it points the instance at a new, empty PostgreSQL schema; data inpublicis not migrated and will appear to be gone.Migrations (2).
20260621_120000_add_minimax_provider.sqladdsminimaxtoPROVIDER_TYPE— its down migration is destructive (DELETE FROM providers/flows/assistants WHERE type = 'minimax').20260716_120000_add_firecrawl_search_type.sqladdsfirecrawltoSEARCHENGINE_TYPE; its down migration is non-destructive but lossy (remapsfirecrawlsearch logs totavilybefore narrowing the enum).Actions an existing deployment may need to take
.env.examplenow setsDOCKER_INSIDE=false. Existing.envfiles are unaffected, but a newly bootstrapped install gives agents no nested Docker until explicitly enabled.PPROF_ADDR=:7777to restore the previously always-on listener. Any scrape job assuming:7777breaks without it.{{.GoogleToolName}},{{.TavilyToolName}},{{.DuckDuckGoToolName}},{{.TraversaalToolName}}or{{.PerplexityToolName}}now fail validation and must be edited to{{.WebSearchToolName}}.claude-sonnet-5/claude-opus-4-8and OpenAI to the GPT-5.4/5.6 families. An API key without access to those tiers fails on the default config. Eight model ids were removed from the catalogs (existing pinned agent configs keep their stored string).BEDROCK_CONFIG_PATH: with it set and Bedrock credentials present, startup upserts the Bedrock provider row for every user from the YAML — any per-agent config edited in the Settings UI is replaced on every restart. With this variable set, the YAML is authoritative.PERPLEXITY_MODEL: the application default moved tosonar-pro, butdocker-compose.ymlstill pins${PERPLEXITY_MODEL:-sonar}— Docker and bare-metal defaults now disagree. Set the variable explicitly./assets/*must return 404, not a rewrite toindex.html, or the stale-chunk recovery re-breaks after a redeploy.langfuse telemetry disabled/opentelemetry disabledwarnings instead of relying on a boot failure.ci.ymlnow triggers onpull_requestplus push tomain/tags, not every branch push. The stand e2e tier needs a protectede2e-standEnvironment with required reviewers, threeE2E_STAND_*secrets and ane2e:standlabel. Branch protection should requiree2e-mockandlint-and-test;e2e-visualis advisory by design and must not be required.GRAPHITI_MODEL_NAMEwas removed — an existing.envthat still sets it is harmless (ignored), but the model is now selected viaGRAPHITI_LLM_CLIENT_TYPEplusexamples/graphiti/<provider>.yaml; setGRAPHITI_CPUS/GRAPHITI_MEMORY/NEO4J_CPUS/NEO4J_MEMORYexplicitly if the previous unbounded resource usage was relied upon.One environment variable was removed:
GRAPHITI_MODEL_NAME, superseded by theGRAPHITI_LLM_CLIENT_TYPE/GRAPHITI_CONFIG_PATHprovider-preset system above. New:TENANT_ID,DATABASE_EXTENSIONS_SCHEMA,DATABASE_SEARCH_PATH_VIA_OPTIONS,DOCKER_PORTS_BASE,PPROF_ADDR,DOCKER_INSIDE_HOST,DOCKER_INSIDE_TLS_VERIFY,DOCKER_INSIDE_CERT_PATH,BEDROCK_CONFIG_PATH,PENTAGI_BEDROCK_CONFIG_PATH,MINIMAX_*,FIRECRAWL_*,WEB_SEARCH_INTERNAL_*,GRAPHITI_CPUS,GRAPHITI_MEMORY,GRAPHITI_CONFIG_PATH,GRAPHITI_CONFIG_DIR,GRAPHITI_LLM_CLIENT_TYPE,GRAPHITI_SEPARATE_EMBEDDING,GRAPHITI_INGEST_*,GRAPHITI_ANCHOR_*,NEO4J_CPUS,NEO4J_MEMORY,NEO4J_SHM_SIZE,NEO4J_NOFILE.Checklist
Code Quality
go fmtandgo vet(for Go code)npm run lint(for TypeScript/JavaScript code)Security
Compatibility
Documentation
Additional Notes
External contributions
21 GitHub PRs from 9 external contributors were merged into this branch. Credits use git identities verbatim and they/them, since no contributor's pronouns or verified full name appear in the repository.
BEDROCK_CONFIG_PATH, following the existingLLM_SERVER_CONFIG_PATH/OLLAMA_SERVER_CONFIG_PATHpattern.CapDrop ALLwith an allow-list,PidsLimit, a boot warning and the correctedDOCKER_INSIDEdefault. Theno-new-privilegeshalf was deliberately reverted afterwards./settings/account.cast.NewBodyPairforward-order deletion bug that could drop a valid text part while keeping an invalid tool call in the message chain.PR #332 (Atlas Cloud) was not merged; it was reimplemented as
examples/configs/atlas.provider.yml, dropping the vendor banner and UTM links and correcting advice that would have broken/modelsdiscovery.Core work by @asdek (multi-instance deployment, the search orchestrator, providers/registry, sandbox capabilities, reliability) and @sirozha (the markdown editor, the frontend platform and feature pages, the e2e suite and CI gates).
Not in this release — do not announce
integrate/open-prs-v2merge droppedbackend/pkg/tools/evidence_receipts.goandEVIDENCE_RECEIPTS_ENABLEDappears nowhere.examples/proposals/evidence_chain.mdis pre-existing and unchanged.no-new-privilegessandbox hardening — reverted; it survives only as an explanatory comment.PROVIDER_TYPEmigration ship.BEDROCK_MODELS_PATH— in-branch churn, absent at the tip.Known issues to resolve before tagging
The merge topology cost us a few things that are worth fixing in this PR rather than after the tag:
README.md:1473contradictsREADME.md:2866and the code. Commit2a4ff3d6removed a bullet claiming image selection fails as a tool call (it is a plain text completion of thesimpleagent); re-merging PR docs: add tool-call parser troubleshooting for custom LLM backends #330 re-applied the original branch and put it back. Worth auditing the other 21 duplicated doc commits the same way.CONTRIBUTORS.mdhas a zero diff across the whole range and still reads "370+ commits over 18 months" — none of the nine contributors credited above appear in it.claude-opus-5entirely.backend/docs/config.mddocuments three tool constructors this release deleted (NewFirecrawlTool/NewSploitusTool/NewSearxngTool), andbackend/docs/flow_execution.md:231-234documents fallback chains that omit Firecrawl from three of four modes — while its own prose names the code as the source of truth.README.md:3190hasdeepinfra.provider.ymllin a copy-pasteable config comment.Review focus
backend/pkg/config/tenant.go+backend/pkg/database/tenant.go— the no-op contract whenTENANT_IDis empty is what protects every existing deployment; also confirm no code path opens a connection beforeEnsureTenantSchema.backend/pkg/providers/providers.go:673—SeedDefaultProviderswriting from a read-only resolver.backend/pkg/server/services/auth.go— OAuth link-by-email plus role-privilege inheritance.backend/pkg/tools/web_search.go:82-109— the fallback table is the only place engine priority lives.frontend/src/components/shared/markdown-editor/markdown-editor-table-pipes.ts— hand-reimplements marked's GFM table grammar; its scope must match marked's row set exactly.backend/pkg/tools/file_diff.go—edit_fileapplies diffs through a fuzzy matcher; "every hunk applied" does not mean "applied where the model meant".continue-on-error: true, including the linux/amd64 + linux/arm64 cross-compile, and golangci-lint runs with--issues-exit-code=0. A backend build break does not fail the gate. Worth deciding whether that changes with this release.Note
High Risk
The release touches tenant isolation, OAuth linking, sandbox Docker exposure, auth/signing, and many breaking env/template defaults across a very large diff—misconfiguration or partial upgrades could cause data isolation failures or privilege issues.
Overview
This is the 2.2.0 integration of
feature/next-release: a large cross-cutting release spanning backend, frontend, Docker, CI, and documentation.Multi-instance and data plane. Optional
TENANT_IDnamespaces PostgreSQL schema, worker containers, Graphiti groups, auth cookies/tokens, and telemetry; empty tenant id stays a no-op for single-instance upgrades. Utility binaries (etester,ftester) now run the same tenant schema bootstrap andsearch_pathverification as the main server..env.exampledocumentsDATABASE_EXTENSIONS_SCHEMA,DATABASE_SEARCH_PATH_VIA_OPTIONS,DOCKER_PORTS_BASE, and related deployment knobs.Search and integrations. Agents move to a single
web_searchtool with intent modes and orchestrated fallbacks (docs and env add Firecrawl, optional internal browser analytics, and expanded Graphiti/Neo4j/LLM preset configuration). MiniMax is wired intoctesterwith capability-gated test reporting (Unsupportedno longer counts as failure).Security and sandbox.
DOCKER_INSIDEdefaults to false in.env.examplewith explicit socket-risk notes;DOCKER_INSIDE_HOST/ TLS cert paths separate sandbox Docker access from the worker daemon.PPROF_ADDRreplaces an always-on debug listener.CI and quality gates.
ci.ymlruns on pull requests, pins Node via.nvmrc, removes permissivecontinue-on-erroron frontend steps, adds TypeScript check and a GraphQL codegen freshness gate (.github/scripts/codegen-inputs-changed.sh). New E2E workflows: fork-safe mock tier, visual snapshots (advisory), local nightly tier, label-gated stand tier with secret redaction, ande2e-report.ymlsticky PR comments viaworkflow_run.Build and packaging. Dockerfile bumps to Node 24.17.0, expands shipped provider YAMLs, and
.dockerignoreexcludes e2e artifacts and local env files from the image context.Documentation. README, CLAUDE.md, CONTRIBUTING, and Graphiti sections are rewritten for tenancy, worker Docker guidance, new providers/models, tool-call troubleshooting, and offline E2E contributor flow.
Reviewed by Cursor Bugbot for commit db68cde. Bugbot is set up for automated code reviews on this repo. Configure here.