Repository navigation
v1.6.0
Added
-
User guide for personal access tokens (#235) -
docs/user/personal-access-tokens.mdexplains who can have a token, how to create, use and revoke one, and how to use it as an OpenAI-compatible model or from an MCP client. Before, only the developer guides, which non-admins cannot open, explained them, and the help panel answered questions about tokens with the MCP server configuration instead. Users and roles and Tools and MCP servers link to it. -
Cost per helpful answer (#275) - the Usage page's Satisfaction by Agent table shows what each answer rated helpful cost: the cost of every answer rated in the period, not helpful ones included, divided by the number rated helpful. A rating is joined to its answer's token rows through the invocation id, so the cost includes the sub-agents the answer handed to. Calls without a price are left out and counted, and an agent with fewer than 10 rated answers is shown in grey. The Evals Playground shows Cost per passed case per variant: its whole cost over the cases it passed, or no passes. See
docs/user/usage-and-audit.mdanddocs/user/evals.md -
Trace Replay shows which version of an agent answered (#274) - when an agent is built, MATE records the config it was built from: a short hash, and the id and number of the stored version that holds it, if one does. Each model call's
gen_ai.inferencespan carries them (mate.agent.config_hash,mate.agent.config_version_id,mate.agent.config_version). Trace Replay shows the version on each agent node and in the step list. Clicking it opens the agent's version history at that version. A run on an older version than the agent has now is marked in amber, and a config no saved version holds (changed by an agent, a template import or the API) shows its hash, so two replays on different configs can be told apart. Seedocs/user/trace-replay.mdanddocs/dev/tracing.md -
Route inventory test (#276, 1.6.0 item 1) -
shared/test/test_route_inventory.pylists every/dashboard/api/route of the real app from its router, those of included routers too (FastAPI keeps those out ofapp.routes), and requests each as a non-admin. It fails when a route answers anything but the middleware's 403, or when the allowlists inserver/dashboard_authz.pylet through a route the test does not list as reviewed. The four routes a non-admin may call today (reading and sending ratings, reading their own tokens, renaming a conversation) are the reviewed ones. The test is the skeleton of the 2.0 cross-workspace test. Seedocs/dev/auth-and-access.md -
Secrets encrypted at rest (#276, 1.6.0 item 4) - with the new
MATE_SECRET_KEY(a Fernet key, separate fromSECRET_KEY), the secrets MATE stores and reads back are encrypted in the database: a Slack integration's bot token and signing secret, a webhook trigger's signing secret, and an agent's literal Model API Key, in its version history too. The columns use a newEncryptedTexttype (shared/utils/secret_box.py) that encrypts on write and decrypts on read, so nothing else changes. When the key is set on an existing installation, the auth server encrypts the values still stored in the clear at its next start. Without the key everything works as before, and the auth server warns at startup with the number of secrets stored in the clear; 2.0 will require it in production. A key that is not a Fernet key stops the auth server at startup. Keep the key: a secret stored with it reads as empty without it, and has to be entered again. Migration V045 makes the four columnsTEXT, since an encrypted value no longer fitsVARCHAR(255).cryptographyis now a direct dependency. Checked on SQLite, PostgreSQL 16 and MySQL 8.0 by upgrading a database written by 1.6.0's previous step. Seedocs/dev/deployment.md
Changed
-
One request context for dashboard routes (#276, 1.6.0 item 2) - a dashboard route now learns who is calling from one dependency,
get_request_context(server/request_context.py), which gives it the user and their role, instead of resolving the user and calling_get_is_adminitself. Every dashboard route uses it: the 24 pages, the API routes ofdashboard_server.py, and the docs, widget-key and chat-integration routes, which takeget_api_request_contextso that a signed-out caller still gets 401 there rather than a redirect._get_is_adminand_get_auth_user_dependencyare gone, and 2.0 adds the workspace to the context. Nothing changes for users: a signed-out caller is still sent to the login page, and a non-admin to the Work Room. The route inventory test now also fails on a dashboard route that does not take the context. Seedocs/dev/auth-and-access.md -
Log rows carry their project (#276, 1.6.0 item 3) -
token_usage_logs,guardrail_logs,agent_responsesandaudit_logsget aproject_id(migration V044), written with the row: the agent's project, or for an audit entry the project of the agent or project it names. The Usage page's cost by project reads it, so a call stays with the project it was made in when its agent is moved, renamed or deleted; before, every call was counted under the agent's current project, and under No project once the agent was deleted. Rows from before the upgrade are filled in from each agent's current project by a backfill the auth server runs once in the background, 10,000 rows at a time, so a large table is never locked for long. Checked on SQLite, PostgreSQL 16 and MySQL 8.0, and on a copy of a real database, where cost by project came out the same as before. Seedocs/dev/database-and-migrations.md -
create_model()takes the caller's context (#276, 1.6.0 item 5) -create_model(), andcreate_chat_model()on the LangGraph runtime, take aModelContext: the agent and project a model is built for. An agent's model and its fallback model get the agent, a subagent gets the agent that started it, and the fallback agent and the compaction summarizer get the installation's empty context. Provider keys still come from the environment, so nothing changes for users; the context appears in the "Creating ... model" log line, and 2.0 looks up a workspace's provider keys from it.shared/test/test_model_context.pyfails on a call to either function that does not pass a context. Seedocs/dev/agent-runtime.md
Fixed
-
An agent created with the name of a deleted one inherited what was left of it (#276, found in the reviews of 1.5.1) - many tables refer to an agent by name, with no foreign key to cascade along, and deleting an agent left those rows behind. A new agent with the same name, perhaps in another project, then took them over:
- its file-search store links, possibly to another project's stores;
- its widget keys, which exposed the new agent publicly, and its Slack integrations and triggers;
- its budgets, alert rules and eval test cases;
- its users' stored OAuth credentials, their conversations on both runtimes, the memory built from them, and app-wide state.
Every way of deleting an agent now removes them, in the same transaction: the dashboard's agent and project delete, the Create Agent tool and the wizard's trial cleanup (
shared/utils/agent_cleanup.py). Usage, guardrail, feedback and audit logs stay. Checked on SQLite, PostgreSQL 16 and MySQL 8.0 against the session tables ADK creates. Seedocs/user/agents.md -
A signed-in user could use up another user's request allowance (#276, found in the review of 1.5.1 C) - the rate-limit middleware counted a
/run_sserequest against the user id in its body before the proxy checked that id, so requests naming another user, which the proxy then refused with 403, still counted against that user's per-minute limit. A request that is refused is no longer counted. Seedocs/user/rate-limits.md -
A LangGraph session created with the id of a deleted one resumed its conversation (#276, found in the review of #302) - deleting a session on
AGENT_FRAMEWORK=langgraphremoved its rows but not its checkpointed graph state. Deleting a session now deletes its checkpoint, and creating a session with a given id deletes any checkpoint left under it. Seedocs/dev/langgraph-runtime.md -
The LangGraph runtime skipped remote MCP servers (#289) - on
AGENT_FRAMEWORK=langgraph, an MCP server given byurlwas dropped with the warning "missing command or args", and the agent ran without its tools. It is now reached as on ADK: over streamable HTTP, or SSE with"transport": "sse", withheaders,timeoutandconnect_timeout;${NAME}in the url and headers is read from the environment and an unset one skips the server; onlyhttpandhttpsare accepted; and an entry with bothurlandcommanduses the url.${NAME}in a stdio server'scommand,argsandenvis now read from the environment too, as on ADK, instead of being passed on as written. A server that cannot be reached now costs only its own tools, not those of every server of the agent. Seedocs/user/tools-and-mcp.md -
Slack users from different Slack workspaces shared one MATE user (#276, 1.6.0 item 6) - a Slack user's MATE id was
slack_<user>, without the workspace, so the same Slack user id in two workspaces had one set of roles, one profile, one memory and one store of connected accounts (OAuth credentials). The id is nowslack_<team>_<user>. On upgrade:- A Slack user's first message creates the new id with the roles of the old one, so no one loses access to a restricted agent, and the audit log records it.
- The bot starts existing threads and direct messages afresh.
- The old id keeps its conversations, usage and audit entries, which stay readable in the dashboard; its profile, memory and connected accounts are not carried over.
See
docs/user/slack.md -
LangGraph checked roles only for the agent requested, not the one that answered (#276, 1.6.0 item 7) - on
AGENT_FRAMEWORK=langgraph, a conversation transferred to a sub-agent goes on there in later turns, but each turn checked the user's roles only for the root agent requested. A user who lost the sub-agent's role, or whose sub-agent's allowed roles changed after the transfer, kept talking to it. Each turn now also checks the agent it resumes in and refuses it as ADK does; the user starts a new conversation to continue. A transfer with no run context, and so no user to check, was let through and is now refused, except in the graph built for LangGraph Studio. Seedocs/dev/langgraph-runtime.md -
A subagent could send the provider's key to the parent agent's endpoint - a subagent started by
subagent_delegationon its parent's model took the parent'smodel_base_urlandmodel_api_keyas stored, withoutresolve_agent_endpoint(), which builds the parent itself. With a base URL and no key, LiteLLM then sent the provider's key from the server environment to the host the base URL names, and a${VAR}key was sent as the literal text${VAR}. The subagent now reads the endpoint as the parent does: the variable is resolved, an unset one fails the subtask, and an endpoint without a key gets a placeholder. A subagent on another model given asdefault_modelalso took the parent's endpoint and key; now only a subagent on the parent's own model does. Seedocs/user/subagent-delegation.md -
Only admins could create a personal access token (#276) - the API and the MCP servers accept a token of any user with a role in
ALLOWED_API_ROLES(defaultadmin,developer), and the account menu showed Personal Tokens to every signed-in user, but the dashboard's access check refused creating and revoking a token to everyone but admins, and the menu said only "Failed to generate token". A user with such a role now creates and revokes their own tokens. The menu shows Personal Tokens only to admins and those users, and creating a token refuses anyone else with 403, by the same rule the API applies when the token is used. Anexpires_in_daysthat is not a positive number now gets 400 instead of 500. Seedocs/dev/auth-and-access.md -
Dashboard pages had up to three names (#288) - a page's sidebar entry, browser tab title and heading could each name it differently, such as Rate Limits, "Rate Limits & Budgets" and "Database Migrations". The tab title and heading now match the sidebar on every page; Usage is My Usage for non-admins, as in their sidebar. Thanks to @bernalalexis-try
-
Browser dialogs instead of the dashboard's own (#244) - actions that ask for confirmation on Sessions, Users, Memory Blocks, Migrations, Evals, the Work Room, the wizard pages, Integrations, Triggers and Widget Keys used the browser's
confirm(), and the Migrations page had its own alert banner. They now use the dashboard's confirmation dialog and notifications, which follow the theme. Somealert()calls remain (#244). Thanks to @CRYPTONIKAV -
Icon-only buttons and fields without a name for screen readers (#246) - the close buttons of every modal, the project edit and delete buttons, the copy buttons, the parent agents search clear button and the Rate Limits page's fields now have accessible names, and the Rate Limits labels are linked to their fields. Other pages follow in #246. Thanks to @Fire162 and @aoblak