Skip to content

Merge feature/next-release into main (pre-2.2.0) - #384

Merged
asdek merged 571 commits into
mainfrom
feature/next-release
Aug 3, 2026
Merged

Merge feature/next-release into main (pre-2.2.0)#384
asdek merged 571 commits into
mainfrom
feature/next-release

Conversation

@asdek

@asdek asdek commented Aug 1, 2026

Copy link
Copy Markdown
Contributor

Description of the Change

This PR merges the accumulated feature/next-release branch into main, forming the 2.2.0 release: 559 commits, 730 files, +81649 / −27334 since v2.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/frontend was merged twice without squashing (123 commits) and a staging branch integrate/open-prs-v2 brought 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 if current_schema() is not the tenant's. DATABASE_EXTENSIONS_SCHEMA and DATABASE_SEARCH_PATH_VIA_OPTIONS cover 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 from examples/graphiti/<provider>.yaml, replacing the single GRAPHITI_MODEL_NAME variable, and gains explicit ingestion-policy, worker-pool and extraction/taxonomy tuning knobs. Neo4j ships a user-editable examples/neo4j/conf/ (neo4j.conf, apoc.conf) plus a bundled APOC plugin, mounted read-only via docker-compose-graphiti.yml; the installer wizard's Graphiti step was rewritten to configure all of it.

One web_search tool 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 neutral searchers package behind a Searcher interface 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: ALL plus an explicit 14-capability allow-list — MKNOD deliberately omitted (block-device mknod + debugfs is this project's own identified primary escape vector) and SYS_PTRACE deliberately added for exploit-development tooling. Every container gets PidsLimit=2048. New DOCKER_INSIDE_HOST / _TLS_VERIFY / _CERT_PATH separate 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-device mknod daemon-wide, cleanup timer, 1374-line isolation suite) ships under examples/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 (ReasoningMode adaptive/budget/off plus per-model efforts / 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, and xhigh/max now 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/pentester moved to gemini-3.6-flash and reflector/searcher/enricher to gemini-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/markdown is now the single surface for the three places users author markdown the backend consumes. This matters because getMarkdown() output is the Go text/template that 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/account 502 → 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 because vite build strips types and the old bare tsc checked 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 off ls parsing 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 behind obs.LogErrorOrCancel so 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

No commit in this range pairs a closing keyword with an issue number, so the Closes list is deliberately limited to the three where code or the specifically requested artifact shipped. #309 and #313 are documented here but their code fixes landed in main before this branch; #321/#324/#336/#341/#342 received docs-only RFCs; #337 is only partially addressed; #279 must not be claimed (see Additional Notes).

Type of Change

  • 🐛 Bug fix (non-breaking change which fixes an issue)
  • 🚀 New feature (non-breaking change which adds functionality)
  • 💥 Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • 📚 Documentation update
  • 🔧 Configuration change
  • 🧪 Test update
  • 🛡️ Security update

Areas Affected

  • Core Services (Frontend UI/Backend API)
  • AI Agents (Researcher/Developer/Executor)
  • Security Tools Integration
  • Memory System (Vector Store/Knowledge Base)
  • Monitoring Stack (Grafana/OpenTelemetry)
  • Analytics Platform (Langfuse)
  • External Integrations (LLM/Search APIs)
  • Documentation
  • Infrastructure/DevOps

Testing and Verification

Test Configuration

PentAGI Version: 2.2.0 (feature/next-release)
Docker Version: 24.0.x+
Host OS: macOS / Linux
LLM Provider: OpenAI, Anthropic, Bedrock, DeepSeek, GLM, Kimi, Qwen, MiniMax, vLLM, Ollama
Search Engines: Google, DuckDuckGo, Tavily, Firecrawl, Searxng, Perplexity, Traversaal, Sploitus, Internal
Node Version: 24.17.0 (pinned via frontend/.nvmrc)
pnpm Version: 11.8.0 (pinned via packageManager)
Enabled Features: Core, Graphiti, Langfuse, observability

Test Steps

  1. cd backend && go build ./... && go vet ./... && go test ./...
  2. cd frontend && pnpm install && pnpm run lint && pnpm typescript && pnpm run test
  3. docker compose up -d on a clean database; confirm both migrations apply and the startup Instance identity line reports the expected tenant_id / data_dir / schema / installation_id.
  4. Single-instance regression (the important one): with TENANT_ID empty, confirm container names, the PostgreSQL schema, Graphiti group ids and the session cookie are unchanged from 2.1.0.
  5. Multi-instance: two instances with distinct TENANT_ID, DATA_DIR and DOCKER_PORTS_BASE against 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.
  6. Run a flow and exercise web_search in each of the four modes; verify one searchlogs row per call attributed to the winning engine, and that an unconfigured engine is skipped rather than failing the chain.
  7. Author a prompt containing a markdown table with a {{ .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.
  8. pnpm run e2e (mock tier), then the local tier against a branch-built image.
  9. Verify the sandbox capability set on a running flow container (docker inspect) and that PidsLimit is 2048.

Test Results

  • Backend: go build ./..., go vet ./... clean; gofmt clean on every touched file; go test ./... — 40 packages ok, 0 failures.
  • Frontend: 1363/1363 Vitest tests pass (77 files); pnpm run lint clean at --max-warnings 0.
  • E2E: 39 spec files / 125 spec cases on the mock tier, 20 visual baselines, 8 e2e unit-test files — run in CI on this PR. Playwright was not run locally for this write-up; the counts are static and independently confirmed.
  • Migrations: both read in full and applied on a clean database.
  • Provider capability matrix: 18 ctester reports regenerated against the new default catalogs and committed under examples/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.
  • Known coverage gap: the integrate/open-prs-v2 merge 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: ALL with an explicit allow-list rather than inheriting Docker's implicit defaults, with MKNOD withheld to close the block-device mknod + debugfs host-disk read path, and PidsLimit=2048 as a fork-bomb guard on every container. DOCKER_INSIDE now defaults to false in .env.example with the risk spelled out: true bind-mounts the host Docker socket into every sandbox, which prompt injection can use to escape to the host (issue #337). DOCKER_INSIDE_HOST lets 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:true half 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 and sudo/su from 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 under examples/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 = oauth filter was removed) and issues the session with the matched account's actual role privileges (previously hardcoded RoleUser). 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,}), so Admin@x.com and admin@x.com are distinct accounts — newly reachable. A fully case-insensitive scheme (citext / UNIQUE(lower(mail))) is an explicit open follow-up.

Multi-tenant isolation. With TENANT_ID set, cookie and API-token signing keys derive from COOKIE_SIGNING_SALT plus 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+xml being 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 :7777 in 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:

  • Markdown editor: 1 MB document load 69 s → 4 s; the variable-panel ReDoS path 250 KB 4.9 s → 0.48 ms (~10 000×); a paste cue 120 k tokens 33 s → <1 ms.
  • Route payloads: /settings/account 502 KB → 305 KB, /dashboard 474 → 378 KB, by removing ~198 KB gzip of recharts and highlight.js that every route — /login included — was downloading unused.
  • /flows at 1116 rows: DOM elements 41 657 → 1 121, tbody rows 1116 → 22, a11y tree ~485 KB → ~80 KB. Detail-navigation sheet ~1800 → ~32 nodes.
  • Per-keystroke work on the edit pages: provider form 152 ms → ~5 ms (it was re-running the zod resolver over 13 agent accordions on every keystroke), knowledge form 58 → 21 ms.
  • PDF report: CJK font (8.1 MB) registered only when the report contains CJK — one flow's download traffic 22.6 MB → 13.8 MB.
  • ZIP downloads stream instead of buffering whole archives, so memory is proportional to one file's copy buffer.
  • RAM growth removed: a TTL-eviction goroutine was being spawned per short-lived flow/assistant provider; the embedded prompts FS was re-read and re-mapped on every call; the anonymizer was recompiled per flow executor.

Costs to be aware of:

  • Per-result raw search content 3000 → 8192 bytes — ~2.7× more source text to the summarizer and, on the non-summarized path, to the agent.
  • PERPLEXITY_MODEL default moved to the more expensive sonar-pro tier.
  • The summarizer cache shrank 1000 → 100 entries, so expect more summarization calls per flow.
  • Adaptive-only models assigned to a cheap utility role silently get effort=high on the wire; the only escape is an explicit reasoning: {mode: off}.
  • Graphiti enabled but unreachable adds ~4 s to startup (3 health-check attempts).

Documentation Updates

  • README.md updates (TENANT_ID, worker Docker access, MiniMax, Firecrawl, internal engine, model catalogs)
  • API documentation updates (Swagger regenerated for the container-files response shape)
  • Configuration documentation updates (backend/docs/config.md multi-instance, worker Docker access, new engines, Graphiti/Neo4j resource limits and LLM presets)
  • GraphQL schema updates (reasoning capability types, Map scalar, renameKnowledgeDocument, minimax)
  • Other: examples/guides/installation_configuration.md; a 12-file hardened dind kit under examples/guides/worker_node/; frontend/docs/e2e.md; frontend/docs/list_detail_pages.md; rewritten backend/docs/{database,docker,flow_execution}.md; five docs-only RFCs under examples/proposals/; CLAUDE.md password policy and search-engine contributor guide; examples/neo4j/conf/ reference configs

Deployment Notes

Deployment steps

  1. Pull the merged main.
  2. docker compose build (requires Node 24.17.0 / pnpm 11.8.0 — both pinned; pnpm build is now tsc -b && vite build, so a TypeScript error fails the image build).
  3. docker compose up -d — both migrations apply on boot.

Leave TENANT_ID empty when upgrading. Setting it points the instance at a new, empty PostgreSQL schema; data in public is not migrated and will appear to be gone.

Migrations (2). 20260621_120000_add_minimax_provider.sql adds minimax to PROVIDER_TYPE — its down migration is destructive (DELETE FROM providers/flows/assistants WHERE type = 'minimax'). 20260716_120000_add_firecrawl_search_type.sql adds firecrawl to SEARCHENGINE_TYPE; its down migration is non-destructive but lossy (remaps firecrawl search logs to tavily before narrowing the enum).

Actions an existing deployment may need to take

  1. Docker-in-Docker: a fresh copy of .env.example now sets DOCKER_INSIDE=false. Existing .env files are unaffected, but a newly bootstrapped install gives agents no nested Docker until explicitly enabled.
  2. pprof: set PPROF_ADDR=:7777 to restore the previously always-on listener. Any scrape job assuming :7777 breaks without it.
  3. Custom prompt overrides referencing {{.GoogleToolName}}, {{.TavilyToolName}}, {{.DuckDuckGoToolName}}, {{.TraversaalToolName}} or {{.PerplexityToolName}} now fail validation and must be edited to {{.WebSearchToolName}}.
  4. LLM access: the shipped Anthropic defaults moved to claude-sonnet-5 / claude-opus-4-8 and 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).
  5. 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.
  6. PERPLEXITY_MODEL: the application default moved to sonar-pro, but docker-compose.yml still pins ${PERPLEXITY_MODEL:-sonar} — Docker and bare-metal defaults now disagree. Set the variable explicitly.
  7. Reverse proxy / CDN: a missing /assets/* must return 404, not a rewrite to index.html, or the stale-chunk recovery re-breaks after a redeploy.
  8. OAuth: audit local account emails before enabling Google/GitHub (see Security Considerations).
  9. Passwords longer than 72 bytes are now rejected at validation time. No stored credential is affected — such a password could never have been hashed successfully.
  10. Monitoring: telemetry init failure is no longer fatal, so alert on the langfuse telemetry disabled / opentelemetry disabled warnings instead of relying on a boot failure.
  11. CI/repo configuration: ci.yml now triggers on pull_request plus push to main/tags, not every branch push. The stand e2e tier needs a protected e2e-stand Environment with required reviewers, three E2E_STAND_* secrets and an e2e:stand label. Branch protection should require e2e-mock and lint-and-test; e2e-visual is advisory by design and must not be required.
  12. GRAPHITI_MODEL_NAME was removed — an existing .env that still sets it is harmless (ignored), but the model is now selected via GRAPHITI_LLM_CLIENT_TYPE plus examples/graphiti/<provider>.yaml; set GRAPHITI_CPUS/GRAPHITI_MEMORY/NEO4J_CPUS/NEO4J_MEMORY explicitly if the previous unbounded resource usage was relied upon.

One environment variable was removed: GRAPHITI_MODEL_NAME, superseded by the GRAPHITI_LLM_CLIENT_TYPE / GRAPHITI_CONFIG_PATH provider-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

  • My code follows the project's coding standards
  • I have added/updated necessary documentation
  • I have added tests to cover my changes
  • All new and existing tests pass
  • I have run go fmt and go vet (for Go code)
  • I have run npm run lint (for TypeScript/JavaScript code)

Security

  • I have considered security implications
  • Changes maintain or improve the security model
  • Sensitive information has been properly handled

Compatibility

  • Changes are backward compatible
  • Breaking changes are clearly marked and documented
  • Dependencies are properly updated

Documentation

  • Documentation is clear and complete
  • Comments are added for non-obvious code
  • API changes are documented

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.

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 /models discovery.

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

  • Evidence receipt hash chain (feat: add evidence receipt hash chain prototype #279). Seven commits exist in history and none of it is in the tree — the integrate/open-prs-v2 merge dropped backend/pkg/tools/evidence_receipts.go and EVIDENCE_RECEIPTS_ENABLED appears nowhere. examples/proposals/evidence_chain.md is pre-existing and unchanged.
  • no-new-privileges sandbox hardening — reverted; it survives only as an explanatory comment.
  • Kubernetes, Vertex AI, BrowserOS MCP, headless crawler, tool/model fallback — all five are docs-only RFCs. None of their candidate env vars, config shapes or the Vertex PROVIDER_TYPE migration 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:

  1. README.md:1473 contradicts README.md:2866 and the code. Commit 2a4ff3d6 removed a bullet claiming image selection fails as a tool call (it is a plain text completion of the simple agent); 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.
  2. CONTRIBUTORS.md has 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.
  3. Bedrock docs are stale. The catalog ships 42 models; the README documents 24, still lists all four removed ids, and its "Tested but Incompatible Models" table declares eight models "not supported" that this release makes selectable. The Anthropic table documents 9 of 10 and omits claude-opus-5 entirely.
  4. backend/docs/config.md documents three tool constructors this release deleted (NewFirecrawlTool / NewSploitusTool / NewSearxngTool), and backend/docs/flow_execution.md:231-234 documents fallback chains that omit Firecrawl from three of four modes — while its own prose names the code as the source of truth.
  5. 13 test files for shipping code were dropped by the same merge (see Test Results).
  6. The Bedrock seeding variant is a merge accident, not a decision — the tree contains exactly the two pieces the reworker documented as deliberately dropped and lacks the one he added. Worth an explicit call.
  7. README.md:3190 has deepinfra.provider.ymll in a copy-pasteable config comment.

Review focus

  • backend/pkg/config/tenant.go + backend/pkg/database/tenant.go — the no-op contract when TENANT_ID is empty is what protects every existing deployment; also confirm no code path opens a connection before EnsureTenantSchema.
  • backend/pkg/providers/providers.go:673SeedDefaultProviders writing 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.goedit_file applies diffs through a fuzzy matcher; "every hunk applied" does not mean "applied where the model meant".
  • Backend CI is still advisory — all four backend steps carry 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_ID namespaces 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 and search_path verification as the main server. .env.example documents DATABASE_EXTENSIONS_SCHEMA, DATABASE_SEARCH_PATH_VIA_OPTIONS, DOCKER_PORTS_BASE, and related deployment knobs.

Search and integrations. Agents move to a single web_search tool 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 into ctester with capability-gated test reporting (Unsupported no longer counts as failure).

Security and sandbox. DOCKER_INSIDE defaults to false in .env.example with explicit socket-risk notes; DOCKER_INSIDE_HOST / TLS cert paths separate sandbox Docker access from the worker daemon. PPROF_ADDR replaces an always-on debug listener.

CI and quality gates. ci.yml runs on pull requests, pins Node via .nvmrc, removes permissive continue-on-error on 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, and e2e-report.yml sticky PR comments via workflow_run.

Build and packaging. Dockerfile bumps to Node 24.17.0, expands shipped provider YAMLs, and .dockerignore excludes 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.

sirozha and others added 30 commits July 7, 2026 21:24
…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>
asdek and others added 14 commits July 29, 2026 16:01
…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.
Comment thread backend/cmd/ctester/main.go
Comment thread backend/cmd/ctester/report.go
Comment thread backend/pkg/server/services/assistants.go Dismissed
Comment thread backend/pkg/server/services/flow_files.go Dismissed
sirozha and others added 2 commits August 2, 2026 02:53
…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.

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

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).

Fix All in Cursor

❌ 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.

Comment thread backend/pkg/providers/providers.go
Comment thread backend/cmd/etester/main.go Outdated
asdek added 9 commits August 3, 2026 15:44
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.
@asdek
asdek merged commit ca17c72 into main Aug 3, 2026
10 checks passed
@asdek
asdek deleted the feature/next-release branch August 3, 2026 22:22
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Enhancement]: Proper guide how to start

6 participants