Repository navigation
v1.5.0 #272
v1.5.0
#272
Replies: 0 comments
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Uh oh!
There was an error while loading. Please reload this page.
Every deployment should upgrade: the agent, Google Drive and image MCP servers
answered without a login, an empty
AUTH_PASSWORDlet anyone sign in with emptyfields, any signed-in user could fire a trigger, and a widget admin key could change
another agent's files. The release brings costs in US dollars with dollar budgets,
a Playground to compare models and instructions, Trace Replay on the Visual
Builder canvas, an image tool for any LiteLLM image model, alerts on fallback
model use and to Slack and Discord, and a Docker image on GHCR. MySQL works
for the first time, and A2A works again on a2a-sdk 1.x.
Before upgrading:
/agents/<name>/mcp,/gdrive/mcpand/images/mcpneedcredentials, for example a personal access token.
POST /triggers/{id}/firewithout the trigger's key needs an admin.CSP_MODE=enforce, inline scripts need the nonce;report-only, the default,blocks nothing.
SUPABASE_BUCKETdefaults toartifactseverywhere; set it if you relied onpublic-bucket.Migrations V037 to V043 run on startup.
Added
trace_spansrows recorded withOTEL_TRACING_ENABLED=true(ADK runtime only), through two new admin routes,GET /dashboard/api/traces/sessionsandGET /dashboard/api/traces/replay. Thegen_ai.inferencespan now recordsmate.model.fallbackand the fallback asgen_ai.response.modelwhen the fallback answered. Seedocs/user/trace-replay.mdghcr.io/antiv/mateforlinux/amd64andlinux/arm64, tagged1.5.0,1.5andlatest, so MATE runs with onedocker runand no clone or local build. The workflow starts the image and checks/healthbefore it pushes anything. Seedocs/dev/deployment.mdask_<agent>, which asks the agent a question through its tool, and the resourcemate://agents/<agent>/about, with the agent's name, description and how to call it (not its instruction).initializeannounces both capabilities. Seedocs/dev/mcp-servers.mdGET /mcplists the agents exposed over MCP (Require a caller on the agent and Google Drive MCP servers; add GET /mcp #231) - with a personal access token (or any other way the agent servers accept), it returns the agents inMCP_EXPOSED_AGENTSthat the caller may use, each with its name, description and full MCP URL, so a client can be set up without knowing the names. Seedocs/dev/mcp-servers.mdinitializeresponse of/agents/<name>/mcpnow carries the agent's description inserverInfo(title,description) and asinstructions. MCP registries read it: without it the LobeHub market CLI (lhm plugin init) refused the server with "Could not infer a plugin description"token_usage_logs.cost_usd), and the Usage page shows the period's cost, the cost per agent and per project, and what agents' fallback models cost apart (calls a fallback answered are now marked,is_fallback). Prices come from a price set on the new Model prices part of the Settings page, then foropenrouter/models OpenRouter's own price list (LiteLLM has no entry for most of them, and reports 0 for those), then LiteLLM's. A model none of them prices has no cost rather than a cost of $0, and the Usage page shows the share of tokens left out. Setting a price reprices the model's past calls; Fill in missing costs prices calls logged before their model had a price, andopenrouter/calls logged before OpenRouter's list arrived are priced as soon as it does. Costs are list prices, without discounts for cached input. Rate Limits takes budgets in dollars per day and per month for a user, agent or project (usd_per_day,usd_per_month), each measured against that scope's own spend, with the same warn or block actions, and the Budget threshold alert can be in dollars, with its own limit or the Rate Limits one. Calls without a price do not count toward a dollar budgetimages.generateand a separate Nano Banana path through OpenRouter chat, so Imagen, FLUX, Stability, Recraft, xAI or Bedrock models could not be used. Generation now goes throughlitellm.aimage_generation, except for OpenRouter models, which go through its chat completions asking for an image (modalities), since LiteLLM's image call for OpenRouter leaves that out and the model then answers in text only. Models are named as for agents (gemini/gemini-2.5-flash-image,black_forest_labs/flux-pro-1.1, ...) and each provider's usual key; Gemini also takesGOOGLE_API_KEY. Image Model in the agent form is a text field with suggestions. The default for agents that name none is set on a new Settings page in the dashboard (Control Room → System), or with the newIMAGE_MODEL, elsedall-e-3; the page shows which applies and whether its provider's key is set, and every change is audited. The page stores its values in a newsystem_settingstable (migration V040). Existing configs keep working: bare OpenAI names with the same defaults and key fallbacks,nano-banana, and the tool namegenerate_image_nano_banana. Images are saved with the type they came in (JPEG, WebP, ...) instead of always as PNG; only PNGs carry the Art. 50 AI-generated marker. The setup check behind/images/mcp/healthnow looks for the default model's key instead of listing OpenAI models. Seedocs/dev/image-generation.mdagent.model_fallbackaudit entries both runtimes write. Its webhook event ismodel_fallback_alert. Seedocs/user/alerts.mdtext,content) rather than MATE's payload. Alert rules can now name a Slack (incoming webhook) or Discord (webhook) destination, which posts the alert message as text. Slack's<>&are escaped and Discord's mentions turned off, so a rule or agent name cannot ping the channel, and the rule list shows the destination type instead of the webhook URL. Seedocs/user/alerts.mdMATE_FEEDBACK_URLandMATE_FEEDBACK_KEY(the standalone feedback key of a widget key for the same agent, shown under Embed Code) in the build's.env, the chat shows the buttons and the note after a thumbs-down, and the build forwards each rating to that MATE, where it lands in Evals → Rated Down with Add to evals and Suggest a fix. The central server cannot read the build's sessions, so the build sends the rated question and answer, read from its own session store, never from the browser; they are stored on the rating (migration V038) and shown as sent by a standalone build, and used only when the server has no session for it. The server takes them only with the feedback key (X-Widget-Feedback-Key, migration V039 generates one for every widget key): with the public key, which is in every page that embeds the widget, the rating is recorded and the text ignored, so a visitor cannot feed made-up exchanges to Suggest a fix. A rating can no longer be changed through another agent's widget key. The build's/feedbackroute takes at most 30 ratings a minute per client, and forgets idle clients. Off by default. Seedocs/dev/standalone-build.md(feat: collect response ratings from standalone builds #125)Changed
latestonly when it is the highest finalvX.Y.Ztag, so av1.5.1cut from arelease/1.5branch afterv2.0.0leaves both on 2.0.0. CI also runs on pushes torelease/**. How releases and maintenance branches work:docs/dev/releases.mdopenrouter/models while OpenRouter's price list can't be fetched, local or new models) added nothing to the spend, a database error while summing counted as $0 and let the request through, and an agent's budget left out what its sub-agents spent. Each Rate Limits config now has Treat calls without a price as over the dollar budget (block_unpriced) and, for agents, Include sub-agents (include_sub_agents, counting the agent and every agent below it in its daily token limit and dollar budgets); both are off by default (migration V043). A Block dollar budget now refuses the request when its spend cannot be read. The Usage vs Limits panel warns when the scope had calls without a price, and a dollar Budget threshold alert says how many calls it left out/run_ssenow cancels a run when its client disconnects, and a new index on the session events table is added on startup. Only ADK moves: the rest of the lock waits for the monthly upgrade, which takes releases at least a week old. Checked with the unit tests and a real conversation through the auth server on both runtimes, on SQLite and on PostgreSQL 16, which also ran migrations V037 to V040 there for the first timerequirements.lockon the 1st of each month with the newest allowed releases at least a week old (a package the lock already has at a newer release stays there rather than moving back), runs the tests on it in a job with a read-only token and no stored credentials, and opens a pull request listing what moved from a second job that installs nothing; actions are pinned by commit; Dependabot keeps the GitHub Actions current. See the README's Dependencies sectionalert_rules.condition_typeanddestination_type(SQLite rebuilds the table, keeping every rule), which refused any type added after them. The dashboard API validates both lists, and tables created by SQLAlchemy never had the constraintsSUPABASE_BUCKETdefaults toartifactseverywhere. The Supabase storage tools usedpublic-bucket, so with the variable unset they and the artifact service wrote to different buckets; if you relied on that, setSUPABASE_BUCKET. Image links for Supabase artifacts are now built with that default too, where they used to be left out. Because the tools and the artifacts now share a bucket by default, the Supabase storage tools refuse paths underpublic/, where the artifact service keeps every user's chat artifacts; an agent steered by a chat message could otherwise read, overwrite or delete them. This applied already to deployments that set one bucket for both, as the old.env.exampledid.DB_NAMEdefaults tomate_agentfor the application database as it already did for sessions, so PostgreSQL and MySQL need onlyDB_USERandDB_PASSWORD. Defaults now live inshared/utils/settings.pyget_otlp_endpoint()andget_otlp_protocol()are removed from the tracing config; nothing called them, and thehttp://localhost:4318default they advertised never applied: withoutOTEL_EXPORTER_OTLP_ENDPOINTno OTLP exporter is addedFixed
trace_spans.attributesisJSONBthere, which the driver returns already parsed, and the Traces page only parsed text, so every span showed none. It now takes bothadk_main.pywas written for a2a-sdk 0.3, whosea2a.server.appsno longer exists in 1.x. Starting with--a2a, which the dashboard always does, printed "A2A dependencies not available" and mounted no/a2a/*routes, so A2A calls failed, and so did agents exposed over MCP, which go through A2A.google.adk.a2a._compat(shared/utils/a2a_routes.py), which works on 0.3 and 1.x./a2a/{agent}accepts the 1.x methods (SendMessage,GetTask, ...) and the 0.3 ones (message/send,tasks/get, ...).supportedInterfaces). It now names the agent's own endpoint (/a2a/{agent}) instead of the server root. Its input and output modes aretext/plain, since the agent reads and writes text. Anagents/<name>/agent.jsonin the 0.3 form is still read and converted.TEXTcolumn only as an expression (DEFAULT ('x')), and thecredentialsunique key was longer than MySQL's 3072 bytes. Twenty later migrations then failed on the missing tables or onADD COLUMN IF NOT EXISTS, which MySQL does not have.credential_key.mysql+pymysql://session URL and fell back to an in-memory store, which also failed to start, so every new chat failed. Sessions are now stored in MySQL throughaiomysql.DB_TYPE=mysqlfailed at startup on a default install - MATE connects to MySQL withpymysql, and ADK keeps sessions on MySQL withaiomysql, but both were commented out inrequirements.txtand missing fromrequirements.lock, so the server stopped withNo module named 'pymysql'. Both are now installed, like the PostgreSQL drivers (Plan for 1.5.0 #178)shared/migrate.pywithout a.envlooked for PostgreSQL (Conflicting defaults for the same env var, and 55 variables missing from .env.example #157) - run on its own, the migration runner fell back toDB_TYPE=postgresqland, for SQLite, tomate_agent.dbrelative to the working directory, while the application uses SQLite andmy_agent_data.dbin the project root.migrate.py statusandruntherefore failed with "No database engine available", or migrated a different file. The runner, the database client, the session store and the dashboard now read the database settings from one placeDB_TYPE=mysqlandDB_PORTunset, the application database used 3306 but the ADK session store 5432messagetext of existing HTTP and email alerts changes accordingly;scopeandscope_idin the payload do not. The fallback alert's message now reads "The fallback model answered for … N times …"/run_ssereply,Content-Lengthincluded, and put its session event in front of the body, so an error reply ran past its length and was cut off, with an SSE line in front of the JSON. It no longer copiesContent-Length,Content-Encoding,Transfer-Encoding,ConnectionorKeep-Alive, and adds the session event only to a successful reply, so the widget can read an error's detail-p 9000:8000) the tab showed nothing. They now load from the auth server's own/docsand/redoc, which already forward to the agent server. Thanks to @ningyixihttps://dashboard/docs(Help panel: the help agent rewrites docs links into a dead https://dashboard/... URL #234) - the help agent often rewrote the/dashboard/docs?...links its tool gave it ashttps://dashboard/docs?..., which the panel treated as an external site and opened on a host that doesn't exist. Such links (and//dashboard/...,dashboard/...) are now turned back into/dashboard/docsbefore the answer is shown. Thanks to @ningyixishowNotificationalways closed after 5 seconds, so the Triggers page's test-fire result, meant to stay 8 seconds, went as fast as the rest. It now takes the duration, 5 seconds by default. Thanks to @kocaemrearia-label. Thanks to @CRYPTONIKAV/dashboard/integrationswas missing from the Studio pages inbase.html, so it opened with the Control Room menu and its own item was nowhere to be seen. Thanks to @sammist2Security
store_namefor an upload and deleted any file search document by id, so the admin key of one widget could add files to, or delete files from, any agent's stores, in any project.POST /widget/api/files/uploadnow refuses a store not linked to the key's agent (404), andDELETE /widget/api/files/{id}answers "File not found" for a file in such a storePOST /triggers/{id}/firewithout the trigger's key fell back to dashboard login, which accepts any user, not only admins, and the route sits outside/dashboard/api, where admin-only access is enforced. Without the key, firing a trigger now needs an admin (403otherwise); callers with the fire key are unchanged. Seedocs/dev/trigger-engine.mdshowConfirmput its title, message and button labels into the page as markup, and some messages carry a name someone else chose (an imported template, a rate-limit scope such as a widget visitor's user ID). They are set as text now/agents/<name>/mcpand/gdrive/mcpbut the health check answered without credentials. Anyone who reached the server could run the agents inMCP_EXPOSED_AGENTSon its provider keys, outside per-user budgets and the agent'sallowed_for_roles(the call reaches the agent over A2A without the caller's identity), and could list, search and read Google Drive files, sharing permissions included, through the server's service account. Both now take the callers the OpenAI-compatible API takes: a personal access token (Authorization: Bearer mate_pat_...), Basic auth, the login token or a dashboard session, of an admin or a user with a role inALLOWED_API_ROLES; an agent's routes also require a role the agent allows, and an agent with none is admin-only. Without credentials they answer 401, without access 403. MCP clients that connected without credentials, including other MATE instances that use these agents throughmcp_servers_config, now need a token;${VAR}inheaderskeeps it out of the config. Seedocs/dev/mcp-servers.mdAUTH_PASSWORDlet anyone log in with empty fields -.env.examplelistsAUTH_USERNAMEandAUTH_PASSWORDempty, andos.getenvreturns""for them rather than its default. So the Quick Start'scp .env.example .envconfigured an empty username and password, and both the login form and Basic auth (curl -u ":") were accepted, admin APIs included. The production check looked only for the default passwordmate, so it let an empty one through, also withMATE_ENV=production. Empty values now count as unset: the built-in account isadmin/mate, which development warns about and production refuses at startup. An empty password never matches, whatever is configured. The credentials are read in one place,shared/utils/settings.pyjsmodule acted as the person who clicked Run: an admin's session could create users or read agent configs, keys included. Python now runs on the sandboxed page too, where it can reach neither the dashboard nor MATE's storage; Open in new tab also runs Python there, instead of treating it as JavaScript.script-srcallowed the whole ofcdn.jsdelivr.netandcdnjs.cloudflare.com, which serve any npm package or GitHub repository, so an HTML injection could load the attacker's own script even underenforce. The policy now lists each library's path with its version (Chart.js, Monaco, React and React Flow, markdown-it, Swagger UI, Redoc, Ace), and a test fails on a template script it does not cover.'wasm-unsafe-eval'is gone from the dashboard's policy, since Pyodide no longer runs there. A custom template that loads another script from those CDNs needs its path inCSP_EXTRA_SOURCESevals.playground_runwith the admin who started it, the mode, and each variant's model and version; changed instructions are recorded by length and hash, not as text. The runs themselves are logged under the eval user, so this is what ties them to a personunpkg.comandd3js.org- the only page that loaded from them,templates/graph.html(a graph view no route served), is removed. A custom template that loads a script from either host now needs it inCSP_EXTRA_SOURCESenforcenow blocks inline scripts without the nonce, and inline handlers (Add a Content-Security-Policy header #124) - every response gets a fresh nonce, and the policy allows inline scripts only with it and no inlineonclick=handlers. Without'unsafe-inline', the policy stops an injected script rather than only limiting where it loads code from.CSP_MODE=enforceenforces it; in 1.4 it enforced a policy with'unsafe-inline'.CSP_MODE=report-only(the default) reports against it and blocks nothing; it is also the way back if a page breaks underenforce. ADK's dev UI, whose markup is not MATE's, keeps'unsafe-inline'. Handlers becomedata-clickactions (static/js/csp-actions.js), which call only functions the page allows. Converted: the layout every dashboard page shares, the login page, the overview, the Agents page and the Visual Builder with all their modals, the monitoring pages (alerts, audit logs, guardrail logs, sessions, traces, rate limits, integrations, triggers), evals, usage, users, migrations, docs, the template gallery and the wizard pages (leads, orders, pricing), and the Work Room, the widget chat and admin pages and the standalone chat: every page. The Work Room canvas runs agent code on a page of its own (/dashboard/workroom/canvas) whose policy sandboxes it, so that code keeps its inline scripts,onclick=and CDNs once the dashboard's policy is strict. A library from a CDN no list names (cdn.plot.ly) now loads in the canvas too. The admin Swagger UI's inline script gets the nonce, and the agent wizard (/wizard/embed) takes itsframe-ancestorsfrom the partner's allowed origins: it had the dashboard'sframe-ancestors 'self', so underCSP_MODE=enforceno partner site could frame it. Doing so also fixed:blob:URL. Ablob:page has the origin of the page that made it and no sandbox, so code an agent wrote, steered by a prompt injection for example, could call the dashboard API as whoever clicked. The tab now opens the sandboxed canvas page, whose code has no access to MATEtool_config- since the image tool moved to LiteLLM (unreleased), everytool_config.image_toolskey but a short denylist reachedlitellm.aimage_generation. An agent steered by a chat intoupdate_agentcould setmock_responseto an internal URL, which the tool then downloaded and saved as an artifact (cloud metadata, the ADK server), route every prompt to an outside logging host, or point Bedrock at its own endpoint. Only image parameters pass now (size,quality,n,style,aspect_ratio, ...), and an image URL from a provider is downloaded only over http(s), without redirects, at most 25 MB and not from a private address (IMAGE_ALLOW_PRIVATE_NETWORKallows it for a local model)/images/mcpneeded no login - its tools spend the server's provider keys, and the Nano Banana tool took the model and request parameters from the caller. Every route but the health check now needs a signed-in caller, callers can no longer choose the model, and the public health check no longer names the default model or missing keys. The image MCP server had also been off since the image tool change, whatever keys were set: its startup check read fields the setup check no longer returnsshowNotificationput its text into the page as markup, and some messages echo server or user input (an error naming the model you typed). It is set as text now, for every dashboard pageThis discussion was created from the release v1.5.0.
All reactions