1.2 line
June 2026 – present. The current line.
This is where Console grew from a gateway with a dashboard into a platform: the Agent Sandbox, the open-source split, a long security pass on tenant isolation, Web Search, MCP Hubs, and Cost & Optimization.
v1.2.40-community
24 August 2026
A capacity review closed five places the system had no ceiling under load, not a wrong one.
Provider calls now honor GATEWAY_REQUEST_TIMEOUT_MS. The setting had been defined and documented since early in the line but never actually read, and Fastify's own request timeouts are unset, so a stalled provider held its socket, request context and stream open indefinitely. withResilience now applies the budget per attempt and hands the operation an AbortSignal, which the chat paths pass through to LangChain, so a timeout aborts the upstream call instead of abandoning it.
Quota limit resolution is now cached for QUOTA_POLICY_CACHE_TTL_SECONDS (default 30). A single inference request had been resolving tenant limits three times before reaching the model, each one an uncached tenant read plus a policy read against a ten-connection pool. Only the inputs are cached — scope and domain filtering still run per request, so one caller can never receive another caller's limits — and a policy write invalidates the entry immediately.
Evicted provider runtimes now release their connections. Eviction used to be a bare map delete: the reference dropped while the underlying Postgres pool or Elasticsearch client kept every socket open, an unbounded file-descriptor leak across every tenant/provider pair that was ever evicted. Runtimes now opt in with close(), deferred by PROVIDER_RUNTIME_DISPOSE_GRACE_SECONDS so a request still holding the runtime is never cut off, and immediate at shutdown.
Agent, MCP, browser and crawler queue consumers now take a concurrency setting. All four had been fixed at one, so a single slow cross-node job head-of-line blocked every job behind it until the queue's 60-second invoke timeout failed the ones still waiting. Crawler's default sits lower than the others, since one crawl job already fans out to as many as sixteen fetches.
Knowledge Engine ingest can now run off the request path. Chunking, embedding and upserting a document had run inside the HTTP call with nothing bounding it. A caller opts in with async: true and gets a 202 with a pending document that a queue consumer indexes; the source is committed before the job is published, and a boot-time sweep re-publishes anything still pending, so a restart on the memory queue driver costs the indexing rather than the content. The default stays synchronous — the existing 201-with-indexed-document contract is what callers read chunkCount from and query immediately after.
MCP request logs stop leaking across recycled server names. Logs were joined to their server by serverKey, a slug derived from the server's name. Deleting a server never cleaned up its logs, and creating a new one under the same name regenerated the identical key — so a new server's Logs tab could silently show its deleted predecessor's history interleaved with its own. Logs now carry a durable serverId, matched primarily, falling back to serverKey (scoped by project and capped at the server's own createdAt) for rows written before the migration, so a recycled key can never pull in a predecessor's history.
reasoning_effort is dropped when a call carries function tools. gpt-5.6-terra was returning 400 on /v1/chat/completions whenever function tools and reasoning_effort were set together — the provider wants /v1/responses for that combination, or reasoning_effort: 'none'. Console doesn't proxy /v1/responses, so the gateway now drops reasoning/reasoning_effort itself whenever tools are present on the call, and keeps applying it normally on tool-free calls.
v1.2.39-community
22 August 2026
Internal-sourced MCP servers can now be published publicly. The gate that blocked publishing a tenant-internal-data server or composite on a public URL (assertPublicExposureAllowed) is gone — publishing that data publicly is now an intentional, supported choice, guarded by an orange warning in the create form and on the server's overview page rather than a hard block.
A server's public endpoint path is a caller-chosen value. endpointSlug used to be only a random 16-character slug; it can now be set at create time and renamed afterward, validated for format, length and tenant-wide uniqueness.
v1.2.38-community
21 August 2026
Azure AI Search rejected every Knowledge Engine write that needed key-encoding. The _b64_ marker introduced for ids Azure's document-key charset can't represent directly violated Azure's other key rule — a document key may not start with an underscore — so no id that needed encoding could ever be written, and every upsert or delete against it failed in production. The marker now starts with a letter (b64_); decoding still accepts the old form, since nothing was ever successfully written under it. The contract suite had asserted the same incomplete rule the code carried, so it stayed green while production rejected every batch — it now asserts the whole rule against the id shapes a real deployment sends.
v1.2.37-community
21 August 2026
The largest release in this batch: a full rebuild of the Knowledge Engine (RAG) from the chunker up, two further audit passes that found what the rebuild itself had broken, a tenant-isolation sweep across every by-id route in the product, and three pre-pen-test hardening fixes on the authentication and secrets path. A branch name in the history (claude/console-pen-test-prep-36fywd) places this work squarely ahead of a scheduled security engagement.
Knowledge Engine, rebuilt from the chunker up
Chunking now runs on one shared splitter/packer (src/lib/services/rag/chunking.ts) instead of three divergent strategies. token mode counts real tokens with gpt-tokenizer — previously a dependency but unused — rather than splitting on whitespace and ignoring the configured encoding. chunkSize is now a hard cap: text with no usable boundary (CJK, base64, a wide markdown table) used to be emitted whole and could overflow the embedding model. chunkOverlap >= chunkSize, reachable from the UI, used to spin the token splitter forever; it's rejected on write and clamped for existing modules. Three new strategies ship: markdown (never crosses a heading, carries the breadcrumb), sentence, and semantic (cuts on embedding distance), plus an optional contextual header — one LLM-written sentence per chunk situating it in its document, degrading to an unprefixed chunk on failure. Chunks now record charStart/charEnd/headingPath/tokenCount, which is what makes small-to-big parent-window resolution possible without duplicating text.
On top of that: every ingest stores the extracted text (inline under 250k characters, in the file bucket above it) and the original bytes when a bucket is configured, so re-ingest resolves original → stored text → the old lossy chunk join instead of rebuilding a document from its own overlapping chunks. Uploads de-duplicate on a content hash. Deleting a module now cascades to its documents, chunks, stored sources and vectors — it used to delete one row and orphan the rest. Changing chunkConfig or the embedding model marks the module and enqueues a resumable, checkpointed, cancellable re-index; a same-dimension model swap is now flagged instead of silently returning meaningless neighbours. Retrieval gains optional hybrid dense+keyword search with fused scores normalized back onto the similarity scale, and isolateByModule, which ANDs the module key into every query — on by default only when the vector index is actually shared. Query logs now record preFilterMatchCount, topScore, avgScore, minScoreApplied and hybrid, which is what tells "nothing was retrieved" apart from "the threshold discarded everything." Evaluations gain a rag target and three embedding-similarity scorers: context-recall, context-precision and groundedness.
A canonical metadata filter language now covers all vector providers. A filter had meant something different per driver — a where clause on Chroma, query DSL on Elasticsearch, ignored outright on nine drivers including Azure AI Search — so callers that filtered (Knowledge Engine queries, memory scope isolation) had been silently getting unfiltered results back. One provider-neutral DSL ($eq/$ne/$gt/$gte/$lt/$lte/$in/$nin/$exists, composing with $and/$or/$not, plus a $raw escape hatch) is now parsed once and translated per driver, and always pushed down — a driver that can't express an operator rejects the query with 400 rather than silently dropping it. Modules gain defaultFilter and filterableFields.
Vector query analytics actually have data now. The index detail page's daily-volume, latency, average-score and topK panels had been empty since they shipped — nothing ever wrote to vector_query_logs. queryVectorIndex now records one row per search, and the analytics endpoints were moved off a raw MongoDB client onto the database abstraction, so the panel no longer 503s for SQLite tenants. Vector searches are also now rolled into usage accounting, as a vector service event keyed by index key.
The 27 defects an adversarial review confirmed, and the defects the fixes introduced
Two independent reviewers, instructed to try to refute each finding, confirmed 27 defects in the rebuild above — spanning data loss (deleting a module by its non-unique-across-projects key destroyed same-named modules in other projects; documents ingested through /client/v1 were enumerated project-scoped but deleted unscoped), wrong retrieval (a malformed $and filter broke every isolated query on the SaaS default vector provider; minScore was compared against inconsistent scales across reranked and hybrid results), chunking (an unset chunkOverlap made chunks grow unboundedly, since undefined loses every numeric comparison), and operations (a production config failure was swallowed, leaving pods alive, "ready," and serving 503; re-index runs were claimed only in process memory, so a rolling restart re-embedded the same documents on every replica concurrently).
A second review pass then found that three of those 27 fixes were themselves wrong — two of them worse than what they replaced. Re-index had been left unable to run at all, because its cross-replica claim existed only as a TypeScript interface with no implementation on either database backend; both now have real claimRagReindexRun/touchRagReindexRunClaim/releaseRagReindexRunClaim primitives. A guard against storing a bad chunk-join reconstruction had been written so that it also removed a document's only copy when a pod was killed mid-document. And a 409 from the re-index endpoint — meaning a run for the previous config is still in flight — had been reported to the UI as success.
A third pass, after that, found the durability fix still hadn't persisted anything: for text under the inline cap, storeDocumentSource only builds an in-memory object, and the source fields reached the document row only in the success branch — after indexing had already deleted the old vectors and chunks. The source row is now written before any destructive step runs. 3733 tests were green by the end of the chain, none of them the same suite twice.
Tenant isolation: the project boundary, enforced on every by-id route
The MCP server routes had a defect worth naming on its own: findMcpServerById resolves only by tenant, so any project member could address another project's MCP server by id — read its config, flip it public, delete it, or POST /mcp/:id/execute to run its tools with that server's own stored upstream credentials. Fixed with a serverInProjectScope() guard across every by-id MCP route, returning the same 404 for an out-of-scope id as for a missing one.
The same defect turned out to repeat across the product — any handler that resolved an id by tenant alone, never checking the object's projectId against the caller's resolved project. Closed with the identical guard on: agents (get/update/delete/versions/publish, plus conversations/chat, which had let a caller drive another project's agent); tools (get/update/delete/logs, and execute, which issued outbound calls under the victim project's stored credentials); RAG (document get/delete/re-ingest, resolved by id alone while only the module was scoped); memory (item delete/update and bulk delete); guardrails (get/update/delete/evaluations, plus the word-list routes); all nine OCR-job by-id routes; and PII policy get/update/delete. Owners and admins keep the tenant-wide reach they already had. The guards live in the API plugins, not the service/database layer, which the commit flags as the durable fix still owed.
Pre-pen-test hardening
Three fixes on the authentication and secrets path, ahead of a scheduled penetration test. Production now refuses to boot when config validation reports errors, rather than only logging them — a deployment missing the required 32-character JWT_SECRET or an independent PROVIDER_ENCRYPTION_SECRET had been starting up on the shipped development defaults: a session token anyone could forge, and credentials encrypted under a publicly known key. Production also stops falling back to JWT_SECRET when encrypting provider credentials, so the signing key and the at-rest encryption key can never collide — decryption still tries both, so existing payloads keep working and are re-encrypted on their next save. And the password-reset link is now built from the configured app URL instead of the request's Origin/Host headers, which — on an unauthenticated endpoint — had let an attacker have a genuine reset token mailed to a victim pointing at a host the attacker controls.
Redis connections now identify their node. Cache and queue connections send CLIENT SETNAME as console:<node>:<role>, reusing the existing cluster node identity, so CLIENT LIST on the server can answer the first question asked when connections leak or maxclients fills up: which node, which role.
v1.2.36-community
20 August 2026
A streamed call's client disconnect was recorded as a provider error. A stream whose client hangs up lands in the same catch block as an actual provider failure, so every user who pressed stop was logged with status: 'error' and usage: {}. That miscounted two things: the model error rate — and the alerting on top of it — tracked user behavior rather than provider health, and the output the provider had already generated (and billed for) was recorded as zero tokens. A disconnect is now logged under a third status, cancelled, additive to both the SQLite and Mongo usage aggregations since they match success/error by exact value. The output already streamed is estimated from the streamed text using the same chars/4 rule the quota pre-flight uses, tagged output_tokens_estimated so an estimate is never mistaken for a measured count. The streaming output guardrail now also runs on a cancelled stream — those tokens still reached the caller, and auditing only completions that ran to the end had made hanging up a way to skip the audit.
Documentation caught up with the product. The README and site description still advertised the feature set from before MCP Hub, GPU Fleet, Agent Sandboxes, AI Red-Teaming, Realtime voice and Cost Intelligence shipped.
v1.2.35-community
20 August 2026
RAG uploads de-duplicate by file name. The crawler bridge can now look up an existing document by file name (findRagDocumentByFileName) and skip or replace a duplicate instead of re-ingesting it. Modules can also set defaultTopK/defaultMinScore for queries that omit them.
Vector index migrations moved onto the durable job queue. The batch-execution loop is now a queue-consumed job (vectorMigrationJob/vectorMigrationConsumer), started at bootstrap and resumable from its persisted cursor after a restart interrupts it mid-run. A new queued status and statuses[] filter track it.
MCP tools carry annotations for strict clients. Every tools/list entry now populates readOnlyHint/destructiveHint/idempotentHint/openWorldHint, derived from the HTTP method or source type when not set explicitly, and operators can override hints and descriptions per tool via PATCH toolAnnotations/toolDescriptions.
The form summary sidebar no longer pushes the form off-screen on mobile. FormShell's summary stacked below the form on narrow viewports and, left expanded, pushed the form itself out of view; it now collapses by default with a toggle. Pinch-zoom is also disabled, since the app has its own responsive layouts rather than a zoomable canvas.
A read-only data-footprint report script was added, estimating data volume per request/user/day across every request-generating collection — gateway, tracing, MCP, RAG, guardrails, web search, reranker, realtime.
v1.2.34-community
19 August 2026
A Knowledge Base can be published as an MCP tool without hand-writing an OpenAPI spec. The "Add MCP server" modal gains an "Internal service" source type, backed by an extensible internal MCP provider registry (src/lib/services/mcp/internal/) so future console-native capabilities can register the same way. The Knowledge Base provider exposes a single grounded "ask" tool (answerWithRag underneath) and, on the same create flow, can attach to an existing MCP Hub or create a default one when the enterprise MCP Hub module is licensed.
Agent Observability is a second internal MCP provider, registered next to Knowledge Base: a read-only reporting surface (list_agents, list_sessions, get_session_detail) backed by the tracing service, with no mutation tools. CreateMcpModal's "Internal service" section was generalized so a provider without a per-instance picker — this one covers a whole project, one server per instance — shows a short info note instead of the Knowledge Base instance/answer-model fields.
A MongoDB-backed vector store for Community deployments without Atlas. The built-in SQLite vector store persists to node-local disk with no cluster affinity, so in a multi-instance deployment a knowledge_search call could land on a different node than the one that indexed the documents and silently return zero matches. The new mongodb-community-vector provider scores candidates in application code against any MongoDB deployment — self-hosted Community or Atlas — instead of relying on an Atlas $vectorSearch index, so it works with the plain mongo:7 image and stays safe across instances since the data lives in shared MongoDB rather than on one node's disk. New tenant/project provisioning defaults to it when DB_PROVIDER=mongodb; existing provider records are untouched.
A single-select ChipPicker value for the MCP internal-provider picker is cast to a string, fixing a type mismatch that broke the picker. The Agent Sandbox docs also caught up with the template edit/delete row actions shipped on the enterprise side.
v1.2.32-community
19 August 2026
finishReason and reasoningTokens are now first-class trace and Model Hub fields, not values buried inside each event's metadata JSON blob. Both are real columns/fields across every write path (external HTTP and OTLP ingest, the internal agent sink, the Model Hub gateway), every read path (session/event APIs, the tracing UI, the Model Hub logs UI), and the usage_daily rollup. reasoningTokens stays strictly a subset of outputTokens — never billed or summed on top of it — and a shared src/lib/shared/finishReason.ts normalizes provider values (stop/length/tool_calls/…) consistently across server and client. Old rows fall back to their metadata location until scripts/backfill-trace-fields.ts is run, which migrates them in place.
Builds on v1.2.26
v1.2.26 first surfaced finishReason and reasoningTokens as trace event fields, logged for attribution. This release promotes both to queryable, aggregable columns rather than values you can only read off one event at a time.
v1.2.29-community
17 August 2026
A cleanup pass for sessions captured before the previous release's guard landed. Older agent-sdk versions (< 0.9.5) answered an absent tool-details payload with the literal string "undefined", which the ingest path then spread into a nine-key object of single characters ({0:'u',1:'n',...}) — v1.2.28 stopped new writes doing this, but every session captured in between still carries that object in the database, and the read path rendered it as-is: the Tool Details panel and the raw Metadata tab both showed the garbled object. Reads now strip it on the fly, matched by shape (keys exactly 0..n-1, every value a single character) rather than by a database migration, since no genuine tool-details record looks like that.
v1.2.28-community
17 August 2026
Support tickets now stay attached to the right account. Console had been sending its tenant slug as the CRM support-organization id. Ticket visibility is scoped to that organization, so renaming a slug would have silently created a second organization and hidden every earlier ticket from the customer — and because the key wasn't namespaced per issuer, a human-readable slug also risked colliding with another product's organization id. A Console tenant is both the customer and the workspace, so it now sends a null organization id and lets the CRM derive an issuer-scoped key from the tenant id instead, which also fixes the organization name never following a company rename. Support staff and notifications also greeted people by their email address rather than their name, unlike Studio and Pulse; it now reads the real name from the tenant database, falling back to the email if that lookup fails. The Help entry point used reachability rather than configurability to decide whether to show itself, so on a SaaS deployment with only SUPPORT_BASE_URL set the button appeared and every click ended in a 503; it now uses the same predicate Studio and Pulse already used. Diagnostics never actually reached the server — the dashboard error boundary can now report to Support with the error attached — and docker-compose passes the support environment variables through, since Console ships as a self-hosted image rather than a chart.
Session metadata is no longer silently dropped on SQLite. Session-level metadata, added in the previous release, was never persisted there — the agent_tracing_sessions table had no metadata column, so every session created on SQLite lost it on write; MongoDB's whole-document persistence had hidden the gap. The column is added (fresh-DB schema plus a migration for existing databases), wired through create/update/row-mapping, with a dual-backend regression test.
Metadata gets a UI. The session detail sidebar now shows it (clicking a value jumps to the sessions list pre-filtered on it), the sessions and threads list pages gained a metadata key/value filter, and the cost dashboard's per-model usage breakdown gained a "Metadata" group-by option alongside the existing Users/API keys toggle.
A tool-call event with no toolDetails had been rendering as the literal string "undefined" spread into single-character keys — the root cause is in agent-sdk, fixed there separately, but client-tracing.ts and the session detail page now also guard toolDetails through the same toRecord()/getRecord() check metadata.toolDetails already used, so a bad payload degrades to "no details" instead.
v1.2.27-community
17 August 2026
Tracing sessions accept a free-form metadata bag, attributable in spend and usage queries. Metadata (a sibling of agent) is sanitized at ingest and flows through recordUsageEvent into usage_daily as a new dimension keyed by its canonical serialization. spend/report and analytics/usage's group_by[_entity] now accept metadata.<key> for dynamic grouping, validated against an allowlist regex before it reaches a query. Both the SQLite and Mongo backends gain the metadata/metadataKey fields, with the unique-dimensions index bumped to v3 following the same pattern as the earlier agentKey v1→v2 bump. agent-sdk moves to 0.9.4 to pick up TracingConfig.metadata support, and Console's own internal-agent tracing sink now forwards it too.
Tracing navigation. The Agents sub-nav item (Overview/Sessions/Threads/Agents) replaces the old per-agent-name directory that had been rendered directly into the sidebar, backed by a new /dashboard/tracing/agents list page.
package-lock.json was also regenerated with npm 10 to match the Docker build image — no dependency change, just a lockfile format fix.
v1.2.26-community
16 August 2026
Traces now record what shape the answer was required to take. A model call has two halves that decide its output: the tools it was offered, and the structured-output contract it was held to. Only the first was ever captured. So a reply that was not valid JSON looked identical whether a schema had been enforced and the model failed it, or nothing had ever asked for JSON — and that is the difference between a bug and a design choice.
Every model-call event now carries a response_format section: the contract type, the schema name, whether it was strict, and the schema itself. It is recorded per call rather than per session, because an agent can enforce a schema on its final turn only. The tracing screen shows it next to the tool menu.
It arrives by every route. The Cognipeer agent-sdk emits it directly — for the native response_format path and for providers without one, where the SDK enforces the same contract through a forced tool call and now says so. OTLP ingestion synthesises it from OpenInference's llm.invocation_parameters and the OTel GenAI gen_ai.output.type + gen_ai.request.structured_output_schema pair. The @cognipeer/observability packages (JS and Python) map it from LangChain, the Vercel AI SDK, and OpenAI Agents.
Two logging fields that explain a bad answer. finishReason — length, meaning the model hit its output ceiling, is the single most common cause of truncated and unparseable JSON, and without it that failure is indistinguishable from a model that simply answered badly. It is shown as a badge on the event when the stop was abnormal. And reasoningTokens, a subset of the output count that is routinely most of the output bill on a reasoning model while being invisible in the response text. It is recorded for attribution only and deliberately not added to the bill — the tokens are already inside outputTokens, and counting them again would overcharge.
Gateway request logs keep the JSON schema, not just its name. The logged request contract recorded that a schema existed; a replay needs the schema itself. It is now kept under its own size budget, so an enormous schema drops without taking the messages down with it.
Why this matters beyond the trace screen
The captured contract flows onward. Traffic Snapshots copy it onto each dataset item, and evaluation suites and prompt-optimizer runs send it back on the wire. A test that replays production traffic without production's schema is measuring a looser system than the one you run — which is exactly how a JSON-shape regression survives a green suite.
Evaluation can replay a conversation turn by turn. A suite has always sent the whole recorded prefix and graded one answer. That isolates each decision against a known-good history — cheap, and the cleanest regression signal — but it structurally cannot catch drift: an agent that answers every turn correctly in isolation and still loses the thread once it is reading its own output passes with full marks. The new turn by turn mode drives the conversation and feeds the model its own answers back, at one model call per user turn. Recorded tool exchanges still replay verbatim — a tool result is a fact about the environment a test cannot regenerate — and only the final answer is graded, so scores stay comparable between the two modes. Single-turn datasets behave identically under both.
A JSON-shape scorer. Similarity scoring happily passes a reply that kept the gist and dropped required fields. This one grades structural conformance against the reference output.
Evaluation targets can override the system prompt. Snapshot items embed the prompt they were recorded with, so without an override every run re-tests the prompt already in production. A target now takes a literal prompt or a promptKey resolved per run — promote a new version and the next run tests it. Targets also carry their own response_format and output-token ceiling.
Datasets can be labeled by AI, and sliced by those labels. An analysis definition's field set is a label taxonomy; pointing a run at a dataset instead of the conversation corpus turns the same engine into a labeler. Labels land on each item as queryable key/value pairs, with a distribution panel and filters over them. A human edit always wins: reviewer corrections are stamped as such and later AI runs leave them alone.
Datasets can be cloned into golden sets. Label a captured corpus, correct what matters, then copy the reviewed slice into a new dataset — filtered by segment, by whether a human confirmed it, and by whether the item has anything to grade against. It is a copy rather than a view on purpose: a golden set has to be stable, or tomorrow's traffic capture silently changes what the regression suite means and two runs a week apart stop being comparable.
Prompt optimizer inherits the captured contract. With no response_format configured, a run now optimizes under the contract the captured traffic actually ran with, and records which contract it used — that fact changes what the scores mean, and nothing else on the run said it.
Catalog price fill is reachable from the model screens. The component shipped in 1.2.25 but was never wired into create or edit.
agent-sdk upgraded from 0.7.0 to 0.9.3
Console's own agent runtime moves two minor versions. The only behavioural change that reaches it is maxParallelTools on the balanced profile, rescaled 2 → 5 in agent-sdk 0.8.0 — Console pins the other rescaled limits explicitly. Agents with several tools may now execute more of them concurrently.
v1.2.25-community
16 August 2026
Edition change
Cost & Optimization is now an enterprise module in full. It had been shipping in the community edition; it should not have been. The screens, the services and the APIs all moved to the enterprise overlay — not gated at the API layer with the code left behind, but moved.
A community installation keeps the service-catalog entry with an upsell page behind it. /api/cost/* and /api/prescriptions/* answer HTTP 402 naming the module they require, rather than rendering a broken page.
If you are running the community edition and using these screens, this release removes them. Nothing else changes: spend is still recorded, usage_daily is still written, quotas and Model Hub figures are unaffected. Two pieces stayed behind deliberately, because other things depend on them — price resolution for externally-priced models, which runs on the ingest path that writes usage_daily, and the /api/model-price-catalog* route, which also backs the Model Hub.
Prescriptions stops calling itself Analysis. The report screens spoke entirely in the language of the Analysis workbench sitting one click away — "Analysis: <subject>", "New analysis", "Analysis window" — which made the two indistinguishable. "Analysis" now means the workbench; these are reports and say so.
Prescription narratives render as markdown. The narrator writes headings, lists and emphasis. Rendering that as preformatted text turned every report into one grey wall.
Reports can be focused on a single model or agent. A new Everything / By model / By agent control scopes the trend, the totals and both breakdowns together, so a report about one agent no longer sits above tables describing everything else.
Analysis no longer prescribes an SDK upgrade when no per-turn tool menu was recorded. Plenty of people trace through OpenTelemetry or their own ingest; it states the observation instead.
Dense tables became readable. Badge clusters were laid out without wrapping, which shrank each badge until its label ellipsised to something like "P.." — coloured, present and completely unreadable. They wrap now and refuse to shrink.
Release notes. This section. The old Changelog page had not been touched since June and documented a version numbering that was retired; it now points here.
v1.2.24-community
15 August 2026
The container image build stops dying silently. Builds had begun failing with no error output at all — the step just ended and the log came back empty. The cause was the build process sizing its memory from the host machine rather than from the container it was running in, growing past what was actually available, and being killed rather than reporting a problem. The build now declares an explicit memory ceiling below the container's limit, so it collects garbage before it hits the wall instead of dying at it.
No application code changed in this release.
v1.2.23-community
15 August 2026
Automated prescriptions. Cost & Optimization gained a recommendation engine that runs on a schedule instead of waiting to be asked. It reads your recorded traffic and produces a written report: which agents are costing more than their peers, where a prompt is carrying dead weight, which tool definitions are being paid for on every turn and never called. Seventeen detectors, robust statistics so a single outlier day does not drive a recommendation, and a narrator that writes the findings in prose rather than leaving you a table to interpret.
Workload signals. The same analysis surfaces what a workload actually demands — how long the inputs run, whether responses are structured, whether tools are involved, how much of the traffic is non-English. These are what make a model recommendation defensible rather than a guess based on price alone.
Documentation. Cost & Optimization and the Cost & Prescriptions API reference were written for this release.
v1.2.22-community
15 August 2026
Cost & Optimization: from production traffic to a measured model decision. The loop closes. You can take a deterministic sample of real production traffic, put it through a mandatory PII gate before it is stored, and then replay that sample against candidate models and score the results. The point is that the answer comes from a measurement, not a projection: a cheaper model that a spreadsheet says will save you 60% will show up here failing the tests, if that is what it does.
The PII gate is not optional and not a checkbox. Traffic is masked or replaced with pseudonyms before it lands in a snapshot; the pseudonym form preserves the fact that two mentions refer to the same entity, so a conversation still makes sense after redaction, and the salt used to generate them is never written down. Sampling is deterministic — a hash of each row — so the same window produces the same sample and a result can be reproduced.
Dataset import from OpenAI, gateway, Bedrock and Langfuse exports, for evaluation sets you already have.
A tool-call scorer. Scoring the trajectory — which tools the model chose and in what order — not only the final text.
Defects found by running the documented workflow
Writing the guides meant executing them, and executing them found five things that had shipped without working.
- Model Hub spend read zero on SQLite. The aggregate never selected any cost field, while the MongoDB implementation summed it correctly. Two database implementations, one of them silently returning a different answer.
- A snapshot from a LangChain agent had no reference answer. The recorded response was read from one envelope shape only, so serialized LangChain messages produced dataset items with nothing to compare against — silently.
- The tool-call scorer could not be selected. The scorer, its tests and the snapshot builder's expected-tool-call output all shipped, but the endpoint's list of valid scorers was a hand-kept copy that omitted it. It now derives from the supported list rather than duplicating it.
- An assertion scorer with no assertions passed everything. It reported success on every item rather than reporting that it had nothing to check.
- A collapsible section header was keyboard-inaccessible — it announced itself as a button but could not be focused or activated from the keyboard.
A How-To section. How-To is a set of task-shaped guides written for people who arrived wanting to do something specific rather than to read a reference page — connecting an OpenAI-compatible client, tracing an existing agent, crawling a site, automating a browser task, and a long walkthrough of optimizing token usage end to end.
Broken documentation links are now build failures. Dead-link checking had been disabled, and it was quietly hiding an entire sidebar section pointing at pages that did not exist. It is on, and the pages exist.
v1.2.21-community
15 August 2026
A defect-fix release, and a bigger one than its size suggests. Several of these were settings that appeared to work and did nothing.
Request parameters
The gateway narrowed every chat request down to four fields before sending it on. Two consequences: models that reject a particular parameter returned a 400 with no way to express that, and anything outside the OpenAI schema could not be sent at all.
- A registry of parameters each model rejects, per provider driver and model id, unioned with a manual per-model list and switchable off per model.
top_p,presence_penaltyandfrequency_penaltyare actually forwarded. They were collected from the request and logged as if they had been applied, then dropped — the Playground's Top P slider was a shipped no-op.- The token budget survives when a model rejects
max_tokensunder that name and wants a different key. - Per-model default parameters and opt-in caller passthrough, so fields specific to vLLM and SGLang can reach the upstream.
- The agent path uses the same resolver. It had hard-coded temperature 0.7, ignored the model's settings entirely, and passed
top_pandmax_tokensunder key names nothing read — so agent Top P and Max Tokens had never taken effect. - Retries stop multiplying. The client library's own retry budget nested inside Console's allowed up to 21 upstream calls for one request, 18 of them invisible to the circuit breaker.
Streaming
The OpenAI-compatible stream was non-compliant enough that strict clients could not consume it, and it was losing data on the way.
- Streamed requests were logged with zero tokens. Usage was being read from keys the streaming path does not populate — and because budget and rate-limit updates are gated on usage being present, they were skipped entirely for every streamed request.
- Tool calls appeared to be called with empty parameters whenever streaming was on. Argument fragments are now emitted as deltas as they arrive.
- One completion id across all frames, the assistant role on the opening frame, always a terminal finish reason, and string content.
- A mid-stream failure is delivered as an error frame followed by
[DONE]instead of destroying the socket, and the upstream request is aborted when the client disconnects.
If you bill or rate-limit on recorded usage and you serve streamed traffic, this is the release that makes those numbers correct.
v1.2.20-community
14 August 2026
Support handoff. Console had no route to Support, so operators fell back to email. A signed-in user can now be handed into Cognipeer Support with their identity already established. The server exchanges its own secret for a single-use code; the browser only ever receives the resulting URL. On-premises deployments that know the Support URL but hold no credentials fall back to the Support login rather than failing the action.
A release runbook. Releasing documents the order the two channels are released in and why it cannot be reversed.
v1.2.19-community
4 August 2026
A version alignment release. No application changes.
v1.2.16-community
3 August 2026
Vision inputs are normalized, and image data is redacted from logs. Images arriving in different shapes are normalized to one internal form, and the image payloads themselves no longer end up in persisted request logs — a base64 image in a log line is both a privacy problem and a storage problem.
v1.2.15-community
3 August 2026
OpenAI-compatible inference errors are normalized. Errors from OpenAI-compatible providers were reaching callers in whatever shape the upstream happened to use. They now come back in one consistent form.
v1.2.14-community
31 July 2026
The largest release in the line. It covers everything from the initial open-source release through the end of July, so it is grouped by theme rather than listed chronologically.
Breaking change
The JS Sandbox module has been removed. The in-process JavaScript execution runtime and its API endpoints are gone. Code execution moved to the Agent Sandbox, which runs work in a real container instead of inside the server process. If you were calling the JS Sandbox endpoints, migrate to the Agent Sandbox.
Alongside the removal, outbound HTTP from tools and integrations now goes through a shared SSRF guard — requests to private network ranges are refused unless the integration explicitly opts in.
Tenant isolation
The single largest thread in this release, and the reason to upgrade if you run more than one tenant. A series of defects allowed a request to be served against the wrong tenant's database under concurrency — not a broken permission check, but a request that never bound itself to a tenant in the first place and fell through to whatever the last global binding happened to be.
- Every client-API request, dashboard request, model route and inference plugin now binds its tenant explicitly and per request.
- Tenant binding was unified onto one canonical wrapper, and three copies that had drifted — one of which skipped the role check entirely — were deleted.
- Schedulers and queue consumers bind tenant scope too, which is what was letting background jobs write results into the wrong tenant.
- API-token access and the model/provider creation flows were hardened separately.
- A legacy data-backfill routine was assigning a default project to providers that were explicitly scoped to other projects, making them visible where they should not have been. Fixed, with a self-heal for records it had already touched.
- Secrets echoed back by upstream services are scrubbed before request logs are persisted.
Web Search
A new Web Search service: project-scoped search instances with pluggable engines, its own client API, and a dashboard. Six engines ship with it — Bing, Brave, Serper, Tavily, SearXNG and DuckDuckGo.
Knowledge Engine
RAG was renamed Knowledge Engine everywhere it is user-facing — the concept had outgrown the acronym. Module creation also gained reranker selection, so a knowledge module can rerank its own results without a separate step.
MCP
- MCP Hubs groundwork — the types, schema and licence rules for curated catalogs of MCP servers.
- Per-tool toggles — individual tools on an MCP server can be switched off rather than all-or-nothing.
- Import from OpenAPI and Postman — point Console at a spec and get a tool or MCP server out of it, with an import UI and a playground for trying calls.
- Editing a server no longer wipes its stored upstream secrets, spec re-import works from the edit form, and streamable HTTP is served correctly on
/sse. - Sandbox-backed MCP execution and Aegis policy enforcement are gated behind Enterprise, with a clear message in the UI rather than a silent failure.
Client API
- Admin-surface endpoints for providers, models, projects, members and licence.
- Observability and authoring endpoints, with per-token scopes so a token can be issued for exactly what it needs.
- The complete OpenAPI specification is published.
Agents and interoperability
- Runtime context header passthrough — headers from the caller can be forwarded to downstream tools, default-deny and explicitly allowlisted, so a tool can act with the end user's authorization rather than a service account's.
- Inbound A2A server — Console can be addressed as an agent by other agent-to-agent clients, with a published agent card.
- Tool-call progress — agent runs surface tool activity as it happens instead of going quiet until the answer arrives.
- Reasoning content is surfaced across streaming, the API, agents and the UI for models that emit it.
- Models declare explicit discovery capabilities rather than being probed.
Crawler
An extended hardening pass, driven by real crawls failing in production.
- Structure-preserving markdown conversion (
@cognipeer/to-markdown3.1.0) — tables and nested lists survive the conversion instead of collapsing. - Anti-bot challenge pages are detected rather than being stored as if they were content, and the headless fingerprint was reduced.
- SSRF-safe redirect handling, atomic job claiming, and a persisted cancel signal so a cancel survives a restart.
- Cancel actually stops the job, within about a second, aborting in-flight requests.
- Errors no longer consume the page budget, results are no longer dropped when the page limit is reached mid-batch, and all results are shown rather than the first 200.
- Jobs orphaned by a server restart are reconciled at boot.
- Page, webhook and Knowledge Engine errors are logged and shown in the Runs UI instead of failing invisibly.
- Markdown conversion is bounded by a timeout, so one pathological page cannot hold a job open forever.
- TLS chain and download fallbacks for sites that serve incomplete certificate chains or files the HTTP client cannot fetch directly.
Guardrails
Evaluation results were never being written — a guardrail could run for weeks with nothing to show for it. Logging works, and the release adds word filtering, a redact action, a configurable fail mode, and a more robust LLM evaluation path. The model selector is always visible, disabled with an explanation when the project has no model configured, rather than absent.
Red teaming and compliance
- An EU AI Act compliance report and the EU risk taxonomy.
- A system-prompt-leakage probe.
- A "Policy Probes" entry for red-teaming Aegis policies.
Providers and storage
- Zero-config built-in vector and file providers — a fresh install has working vector storage and file storage without configuring anything.
- S3-compatible custom endpoints, so MinIO and similar work with the
aws-s3-filesprovider.
Platform
- The container runs the server directly as PID 1. Previously the process manager sat in front of it and did not reliably forward the shutdown signal, so graceful shutdown never ran and any crawl in progress was hard-killed partway through.
- A per-request legacy backfill that was running a dozen full collection scans on nearly every authenticated request is gone; database indexes are created from a manifest at boot instead.
- Auth pages and transactional emails were rebuilt on the design system.
- Registration defaults to open rather than invitation-only.
- Agent tracing: a session's totals are no longer reset by its own later legs, and the recently-active-agents panel became a real table.
Earlier in the 1.2 line
These changes shipped between the open-source release and v1.2.14-community, and are included in it.
Agent Sandbox and GPU pools
1 June 2026
The Agent Sandbox arrived — a container runtime with its own APIs and dashboard, giving agents a real machine to work on. GPU pool management and terminal access over WebSocket landed at the same time.
The open-source split
5 June 2026
Console was split into a community edition under AGPL-3.0 and an enterprise overlay. See v1.0.0-community for what the first public release contained. A prompt optimizer module shipped alongside.
Realtime, groups and external auth
18 June 2026
- Realtime voice and chat — the schema and gateway routing landed in the community edition; the realtime service itself is an enterprise module.
- User groups with tenant and project grants, and permissions resolved as a union across a user's groups.
- An external authentication seam for directory and SSO providers, so LDAP and SSO can be attached without forking the login flow.
- Client API expansion — batch, moderation, spend and realtime endpoints.
- Red-team probes and an overview screen, and evaluation run comparison.
- Browser automation hardening — stale element references fall back to selectors, action and navigation timeouts are bounded, and a
browser_pdftool was added. - Azure's v1 audio endpoint does not route
/audio/*by model; requests are now deployment-scoped so audio works against Azure.
Provider scoping
19 June 2026
Providers became strictly project-scoped, with error messages that say so. A provider that is not assigned to your project cannot be used from it — and now tells you that instead of failing obscurely.

