Changelog
The framework (core, CLI, contracts) is released as a whole and called by one version, Mercury Framework x.y.z. Plugins, channels and auth providers are released on their own, each with its own version. Both are listed here, newest first: the framework from 0.25.0, its first version on npm, the others from their releases after 0.59.1. For their earlier versions, see each package's CHANGELOG.md in the repository.
Mercury Framework 0.62.0
Minor Changes
- 96eb1a8: -
mfw createoffers the Confluence plugin (--plugins confluence), wrapped in the formatter with a starting rule for its search results and one for a page’s children, and their empty texts inlocales/en.json.
@mercury-fw/plugin-confluence 0.1.0
Minor Changes
-
96eb1a8: First release: Confluence, read-only, through the pinned
confluenceCLI.- Searches pages with Confluence CQL, reads a page as Markdown, lists a page’s children and ancestors, reads its comments, and reads spaces; the CLI’s commands that create, change or delete pages never run.
- Acts as the person, logged in through Mercury; on the terminal, as the app’s Service Account.
- Hands search results and a page’s children over as typed lists (
ConfluenceDisplays:page-list,child-pages) for the app’s formatter, with links to the pages. - Needs no environment variable: the links come from the CLI.
Mercury Framework 0.59.2
Patch Changes
- f631dd5: README: a link to the package’s page on Mercury’s documentation site (https://lucabro81.github.io/mercury-fw/), published from this release on. The HTTP channel’s README points to the HTTP API reference there, and the core’s memory section to the page that explains it.
- 3b67dd6:
mfw repl: the turn’s log line ([turn] done …) starts on a line of its own instead of landing glued to the end of the answer. The terminal ends the answer’s line once the answer is final, and doesn’t end it a second time before the prompt, a display or an error. A turn that fails or stalls halfway through an answer still logs right after the partial text.
@mercury-fw/auth-oidc 0.2.1
Patch Changes
- f631dd5: README: a link to the package’s page on Mercury’s documentation site (https://lucabro81.github.io/mercury-fw/), published from this release on. The HTTP channel’s README points to the HTTP API reference there, and the core’s memory section to the page that explains it.
@mercury-fw/auth-static 0.2.2
Patch Changes
- f631dd5: README: a link to the package’s page on Mercury’s documentation site (https://lucabro81.github.io/mercury-fw/), published from this release on. The HTTP channel’s README points to the HTTP API reference there, and the core’s memory section to the page that explains it.
@mercury-fw/channel-google-chat 0.11.1
Patch Changes
-
c7806ea: README: the setup steps on each service’s console follow its official documentation and link it.
- Google Chat: the Chat app’s configuration uses Google’s current labels (Enable interactive features, Add a command, Slash command name, a Command ID from 1 to 1000), says Visibility takes up to five people or groups, and links Google’s guides to the consent screen, the Pub/Sub quickstart, the Chat API configuration and commands.
- Jira: links Atlassian’s guides to service account credentials and to OAuth 2.0 (3LO) apps, and says the
app.jira.no-issuestext is inlocales/en.jsononly in an app created with Jira. - Atlassian admin: where an organization admin creates the API key, that it’s shown once and expires, and Atlassian’s guide.
- Bitbucket: where a workspace’s OAuth consumer is added, and Atlassian’s guide.
- ZITADEL: ZITADEL’s guides to a service account’s key and to applications.
-
f631dd5: README: a link to the package’s page on Mercury’s documentation site (https://lucabro81.github.io/mercury-fw/), published from this release on. The HTTP channel’s README points to the HTTP API reference there, and the core’s memory section to the page that explains it.
@mercury-fw/channel-http 0.13.1
Patch Changes
- f631dd5: README: a link to the package’s page on Mercury’s documentation site (https://lucabro81.github.io/mercury-fw/), published from this release on. The HTTP channel’s README points to the HTTP API reference there, and the core’s memory section to the page that explains it.
@mercury-fw/directory-static 0.1.2
Patch Changes
- f631dd5: README: a link to the package’s page on Mercury’s documentation site (https://lucabro81.github.io/mercury-fw/), published from this release on. The HTTP channel’s README points to the HTTP API reference there, and the core’s memory section to the page that explains it.
@mercury-fw/plugin-atlassian-admin 0.5.1
Patch Changes
-
c7806ea: README: the setup steps on each service’s console follow its official documentation and link it.
- Google Chat: the Chat app’s configuration uses Google’s current labels (Enable interactive features, Add a command, Slash command name, a Command ID from 1 to 1000), says Visibility takes up to five people or groups, and links Google’s guides to the consent screen, the Pub/Sub quickstart, the Chat API configuration and commands.
- Jira: links Atlassian’s guides to service account credentials and to OAuth 2.0 (3LO) apps, and says the
app.jira.no-issuestext is inlocales/en.jsononly in an app created with Jira. - Atlassian admin: where an organization admin creates the API key, that it’s shown once and expires, and Atlassian’s guide.
- Bitbucket: where a workspace’s OAuth consumer is added, and Atlassian’s guide.
- ZITADEL: ZITADEL’s guides to a service account’s key and to applications.
-
f631dd5: README: a link to the package’s page on Mercury’s documentation site (https://lucabro81.github.io/mercury-fw/), published from this release on. The HTTP channel’s README points to the HTTP API reference there, and the core’s memory section to the page that explains it.
@mercury-fw/plugin-bitbucket 0.6.1
Patch Changes
-
c7806ea: README: the setup steps on each service’s console follow its official documentation and link it.
- Google Chat: the Chat app’s configuration uses Google’s current labels (Enable interactive features, Add a command, Slash command name, a Command ID from 1 to 1000), says Visibility takes up to five people or groups, and links Google’s guides to the consent screen, the Pub/Sub quickstart, the Chat API configuration and commands.
- Jira: links Atlassian’s guides to service account credentials and to OAuth 2.0 (3LO) apps, and says the
app.jira.no-issuestext is inlocales/en.jsononly in an app created with Jira. - Atlassian admin: where an organization admin creates the API key, that it’s shown once and expires, and Atlassian’s guide.
- Bitbucket: where a workspace’s OAuth consumer is added, and Atlassian’s guide.
- ZITADEL: ZITADEL’s guides to a service account’s key and to applications.
-
f631dd5: README: a link to the package’s page on Mercury’s documentation site (https://lucabro81.github.io/mercury-fw/), published from this release on. The HTTP channel’s README points to the HTTP API reference there, and the core’s memory section to the page that explains it.
@mercury-fw/plugin-jira 0.8.1
Patch Changes
-
c7806ea: README: the setup steps on each service’s console follow its official documentation and link it.
- Google Chat: the Chat app’s configuration uses Google’s current labels (Enable interactive features, Add a command, Slash command name, a Command ID from 1 to 1000), says Visibility takes up to five people or groups, and links Google’s guides to the consent screen, the Pub/Sub quickstart, the Chat API configuration and commands.
- Jira: links Atlassian’s guides to service account credentials and to OAuth 2.0 (3LO) apps, and says the
app.jira.no-issuestext is inlocales/en.jsononly in an app created with Jira. - Atlassian admin: where an organization admin creates the API key, that it’s shown once and expires, and Atlassian’s guide.
- Bitbucket: where a workspace’s OAuth consumer is added, and Atlassian’s guide.
- ZITADEL: ZITADEL’s guides to a service account’s key and to applications.
-
f631dd5: README: a link to the package’s page on Mercury’s documentation site (https://lucabro81.github.io/mercury-fw/), published from this release on. The HTTP channel’s README points to the HTTP API reference there, and the core’s memory section to the page that explains it.
@mercury-fw/plugin-web-search 0.4.1
Patch Changes
- f631dd5: README: a link to the package’s page on Mercury’s documentation site (https://lucabro81.github.io/mercury-fw/), published from this release on. The HTTP channel’s README points to the HTTP API reference there, and the core’s memory section to the page that explains it.
@mercury-fw/plugin-zitadel 0.5.1
Patch Changes
-
c7806ea: README: the setup steps on each service’s console follow its official documentation and link it.
- Google Chat: the Chat app’s configuration uses Google’s current labels (Enable interactive features, Add a command, Slash command name, a Command ID from 1 to 1000), says Visibility takes up to five people or groups, and links Google’s guides to the consent screen, the Pub/Sub quickstart, the Chat API configuration and commands.
- Jira: links Atlassian’s guides to service account credentials and to OAuth 2.0 (3LO) apps, and says the
app.jira.no-issuestext is inlocales/en.jsononly in an app created with Jira. - Atlassian admin: where an organization admin creates the API key, that it’s shown once and expires, and Atlassian’s guide.
- Bitbucket: where a workspace’s OAuth consumer is added, and Atlassian’s guide.
- ZITADEL: ZITADEL’s guides to a service account’s key and to applications.
-
f631dd5: README: a link to the package’s page on Mercury’s documentation site (https://lucabro81.github.io/mercury-fw/), published from this release on. The HTTP channel’s README points to the HTTP API reference there, and the core’s memory section to the page that explains it.
@mercury-fw/web-search-searxng 0.1.1
Patch Changes
- f631dd5: README: a link to the package’s page on Mercury’s documentation site (https://lucabro81.github.io/mercury-fw/), published from this release on. The HTTP channel’s README points to the HTTP API reference there, and the core’s memory section to the page that explains it.
Mercury Framework 0.59.1
Patch Changes
- 44f2540: Inside an app,
mfw --versionandmfw --help(and a baremfw) come from the app’s own CLI too when the global one is another version, like every app command: before, they stayed the global one’s, somfw --versionthere showed the global version and a mismatch with the app’s went unnoticed. The line saying it hands over names both versions (mfw 0.53.2: running the app's 0.58.0).createandupgradestay the global CLI’s. It’s the globalmfwthat hands over, so this takes effect once it’s updated:mfw upgrade.
Mercury Framework 0.59.0
Minor Changes
- 7c9a038: - Channels get the framework’s version on their runtime context (
ctx.frameworkVersion,major.minor.patch), for a channel that tells people which Mercury they’re talking to.
Mercury Framework 0.58.0
Minor Changes
-
e3eb49d: A turn its channel cancels isn’t an answer anymore.
- An aborted model call no longer leaves half an answer in the conversation history: the person’s message stays, followed by a note that the answer was stopped, so the next message keeps its context. The same goes for a turn the stall watchdog stops.
- A cancelled turn (its
abortSignalaborted) is never finalized, run through the post-turn guards or archived, and it resolves instead of throwing; the sink is still released withdispose. Before, the AI SDK returned what it had and the turn delivered it as the answer. - Channel contract: the
abortSignaldoc says what a cancelled turn does. No change in shape, the contract version stays 6.
Mercury Framework 0.57.0
Minor Changes
-
377ce2a:
mfw creategives the app a place for plugins of its own:plugins/<name>, each a package, as a workspace of the app ("workspaces": ["plugins/*"]inpackage.json), listed independenciesasworkspace:*and declared inmercury.config.ts. Abunfig.tomlkeeps Bun’s hoistednode_modules: with a workspaces key Bun would switch to its isolated linker, whosenode_modulesis made of links. The Dockerfile copiesbunfig.tomlwithpackage.json, thenplugins/beforebun install, so a plugin’s postinstall finds its own scripts, and puts on PATH the binaries a plugin’s postinstall writes to itsbin/, as it does for the framework’s packages..dockerignoreleaves out everynode_modules, a plugin’s own included, and the binaries a plugin’s postinstall wrote on the host, which.gitignorekeeps out of the repository too.package.jsongets atestscript (bun test), which runs the plugins’ tests too.An existing app keeps working as it is. To give it the same place:
-
In
package.json, add"workspaces": ["plugins/*"]and"test": "bun test"underscripts. -
Add a
bunfig.tomlnext to it:[install] linker = "hoisted" -
In the
Dockerfile, changeCOPY --chown=mercury:mercury package.json bun.lock* ./toCOPY --chown=mercury:mercury package.json bun.lock* bunfig.toml ./, addCOPY --chown=mercury:mercury plugin[s] ./plugins/right afterCOPY --chown=mercury:mercury .pack[s] ./.packs/, and replace the lineRUN find /app/node_modules -path '*/@mercury-fw/*/bin/*' -type f -exec ln -sf {} /usr/local/bin/ \;with:RUN mkdir -p /app/plugins \ && find /app/node_modules/@mercury-fw /app/plugins -mindepth 3 -maxdepth 3 -path '*/bin/*' -type f -exec ln -sf {} /usr/local/bin/ \; -
In
.dockerignore, changenode_modulesto**/node_modulesand addplugins/*/bin. -
In
.gitignore, addplugins/*/bin.
-
Mercury Framework 0.56.0
Minor Changes
-
1126106: Every turn tells the model when and where the person wrote it.
- Each message reaches the model after a line with the weekday, date, time and time zone it was written in (
[Wednesday 2026-10-21 14:32 Europe/Rome]), and the system prompt says what that line is. The conversation’s archive and what a UI shows of it keep the message as written. - A resumed conversation’s messages come back stamped from the archive’s timestamps.
- The Layer 1 summary keeps when things were said; the episodic summary quotes a date only from those lines, never guessing one. The semantic-fact extractor and the capture card’s preview leave the line out.
- The time zone, always an IANA name (
Europe/Rome), is the one the person saved, else the one their client is in when the channel passes it on, else the newi18n.timeZoneinmercury.config.ts, else the machine’s. AtimeZonethat isn’t a zone’s name stops the start. - The person saves theirs by telling the assistant (the new
set_time_zonetool), or from a channel’s settings. - Channel contract:
InboundTurn.timeZoneHint, andctx.settings.setTimeZonefor a channel with a settings area;reads.mealso returns the savedtimeZone. Both are optional additions: the contract version stays 6. - HTTP channel: the
X-Time-Zoneheader onPOST /turn(allowed by CORS),timeZoneinGET /me, andPATCH /mewith{ timeZone }to save it. Needs the framework at 0.56 or later; an older core ignores the header and offers noPATCH /me. - Google Chat channel: the client’s time zone (
common.timeZone), when the event carries it, on messages and on the turn a click resumes.
To set a default for an app’s people, add
timeZone: "Europe/Rome"(their zone) toi18ninmercury.config.ts; without it the machine’s zone is used, often UTC inside a container. - Each message reaches the model after a line with the weekday, date, time and time zone it was written in (
Mercury Framework 0.55.0
Minor Changes
-
eac72fa: Every plugin says what it lets the agent do, and the agent can tell. Plugin contract version 6:
- A plugin declares a
description: one sentence, in English, for the model, on what it lets the assistant do ({{assistant}}stands for its name). How to use it stays in its fragment and skills. - The core doesn’t load a plugin whose description is missing, blank or more than one line, and the log says why.
- The system prompt lists the plugins offered to the person, each with its description, right after the persona’s identity, and tells the model to answer “what can you do?” from that list. A plugin the person may not use isn’t listed; with no plugin offered there’s no list.
- The first-party plugins released with this change carry a description each, are written for contract 6 and need the framework at 0.55.0 or later: the peer range on
@mercury-fw/plugin-typesstarts there.
A plugin written for contract 5 isn’t loaded by this core, and the log says why: add a
descriptionand rebuild it againstPLUGIN_API_VERSION6. - A plugin declares a
Mercury Framework 0.54.0
Minor Changes
-
e00ff73: The channels’ texts come out in the person’s language too, and the language their client asks for counts. Channel contract version 6:
- The person’s language is the one they saved, else the one their client asks for, else the app’s default. The HTTP channel passes on the request’s
Accept-Languageheader (what browsers send), Google Chat the language of the person’s Chat client when its event carries one (InboundTurn.languageHint). Someone the agent refuses gets it too. - A channel declares its own texts in English in
messages, under its name (google-chat.,http.), checked like a plugin’s; it gets them throughctx.i18n.forRequest(principal, hint). - The replies to a confirmation are the core’s, in the person’s language:
ctx.confirmreturns{ text, resume? }andctx.resolveConfirmationthe outcome with itstext. A confirmed action reads “Confirmed and done.” instead of its raw result as JSON.confirmReply,RESUME_AFTER_CONFIRMandPENDING_CONFIRMATION_NOTEare gone from the contract, andtryConfirmfrom@mercury-fw/confirm-engine(resolveConfirmationreports the outcome; the core words it). - The stand-in for an answer the model didn’t write because the turn staged an action (
core.pending.note, “Action pending, waiting for confirmation.”) is no longer streamed as text:finalize(text, { pendingNote })says the text begins with it, and channels drop it through that, never by comparing text. - A plain staging’s single choice has an empty label: each channel words its own button (“Confirm” on Google Chat, in the person’s language).
loginLinkText(login, t)takes the person’s texts. - HTTP:
Accept-Languageon/turn,/confirmand/login/callback;Content-Languageon the responses of/turnand/confirm; thefinalevent carriespendingNote; the login page reads in the browser’s language. - Google Chat: the cards’ titles, status lines and confirm button are
google-chat.*texts; the client’scommon.userLocaleis the hint when the event carries it. - The terminal’s replies to a confirmation read in the terminal person’s language, like its other lines.
The texts that were in Italian (the cards, the replies to a confirmation, the pending note) are now English by default. An app that wants them in Italian adds their keys to
locales/it.json(core.pending.note,core.confirm.*,core.login.link*,google-chat.*,http.*; the log lists the missing ones at startup). A UI on the HTTP channel can hide the pending note throughfinal’spendingNote, and words the button of a choice with an empty label itself. A channel written for contract 5 isn’t loaded by this core, and the log says why: rebuild it againstCHANNEL_API_VERSION6. - The person’s language is the one they saved, else the one their client asks for, else the app’s default. The HTTP channel passes on the request’s
-
ebde9e2: The texts a person sees can be in their language. The core’s own fixed texts (the refusal of someone the agent doesn’t know, the account-linking replies, the login errors, the tool status lines, the terminal’s own lines) are English, each under a key (
core.access.unknown, …), and an app adds a language with a file of its own:i18n: { defaultLocale, locales: { it } }inmercury.config.ts, each file mapping keys to texts. A person picks their language by asking the agent, which saves it withset_preferred_language(only the languages the app ships; not offered when it ships English alone) and answers in it from then on. Whoever hasn’t picked one getsdefaultLocale, then English. A key a language’s file lacks shows as the key itself, and the log names the missing keys at startup.GET /meon the HTTP channel also says the caller’s language.The texts that were in Italian (tool status lines, the terminal’s lines) are now English by default, and the language is no longer one of the facts inferred from conversations.
An existing app keeps working, with these changes:
access.unknownMessageis gone. To keep a custom refusal, put it inlocales/en.jsonas{ "core.access.unknown": "…" }, and inmercury.config.tsimport it (import en from "./locales/en.json";) and seti18n: { locales: { en } }.- To keep the status and terminal lines in Italian, add
locales/it.jsonwith thecore.keys translated (start it as{}: the log lists the missing keys at startup), and inmercury.config.tsimport it (import it from "./locales/it.json";) and seti18n: { defaultLocale: "it", locales: { it } }(locales: { en, it }when you did step 1 too). - Whenever the config imports a file from
locales/(steps 1 and 2), the image needs the folder: addCOPY --chown=mercury:mercury locales ./localesafter themercury.config.tsline of theDockerfile, or the app fails at startup. - For the terminal (
mfw repl) to follow the language too, addtexts: app.texts,tocreateTerminalProvider({ … })insrc/repl.ts; without it the terminal’s own lines stay English.
-
5015642: The plugins’ texts a person sees come out in their language, like the core’s. Plugin contract version 5:
- A plugin declares its texts in English in
messages, every key under its own name (web-search.choice.once); the core refuses a plugin with a key outside it (and a plugin namedcoreorapp, whose keys are the core’s and the app’s), and the app’s language files translate them. The startup log names the missing keys of the plugins too. - While a turn runs, a plugin gets the texts of its person as
t: inSessionToolContext.t, as the second argument of a status describer (toolStatusDescribers,StatusDescriber), the third of a post-processor (CliPostProcessor), and in a post-turn guard, whosestatusLabelis now(t) => stringand whoserungetstas its third argument.Messages,ParamsandTranslateare part of the contract. - The default status line of a CLI command is the core’s
core.status.command(“Running jira issue search”),core.status.command-barewithout a subcommand (“Running jira”), and an unreadable command’s iscore.status.command-unknown.createCliTool(@mercury-fw/cli-engine) takes the turn’stamong its options, handed to the post-processor: a CLI plugin passessctx.t;createCliStatusDescriber’s describer takes it as its second argument. - The formatter hands a rule
tas its second argument, and anemptytext can be(t) => string; existing rules keep working as they are. An app’s own texts go underapp., in itslocales/en.json, which declares them (the startup log no longer lists them as unknown);mfw createwrites the Jira rule’s empty text asapp.jira.no-issues. - Web search: the two choices (“Only this time”, “Always for <host>”), the status lines and the sources heading (“Sources”) are
web-search.*texts, English by default; a list of sources the model writes under the heading in the person’s language, or the English one, is still replaced by the plugin’s.
The first-party plugins released with this change are written for contract 5 and need the core released with it: an older core skips them and says why in the log. A plugin written for contract 4 isn’t loaded by this core either, and the log says why: rebuild it against
PLUGIN_API_VERSION5 (a status describer, a guard’sstatusLabelandrun, a post-processor take the newt). An app that wants the web search and status lines back in Italian adds their keys tolocales/it.json(the log lists the missing ones at startup). - A plugin declares its texts in English in
Mercury Framework 0.53.2
Patch Changes
-
4a9a528:
mfw createwrites the whole persona insidemercury.config.ts: the assistant’s name once, as aconst, referred to by the identity and by the tone, which is written out in full, one string per line. There are nopersona/identity.mdandpersona/tone.mdfiles anymore, nomarkdown.d.ts, and the Dockerfile no longer copies them.An existing app keeps working as it is. To move it to the new layout:
- In
mercury.config.ts, drop the twoimport … from "./persona/…md"lines, addconst name = "<the assistant's name>";and setpersona: { name, identity: "…", tone: "…" }with the text of the two files (${name}in a template literal where they say{{assistant}}, or keep{{assistant}}: the core still replaces it). - Delete
persona/andmarkdown.d.ts. - In the
Dockerfile, changeCOPY --chown=mercury:mercury mercury.config.ts markdown.d.ts ./toCOPY --chown=mercury:mercury mercury.config.ts ./and deleteCOPY --chown=mercury:mercury persona ./persona.
- In
Mercury Framework 0.53.1
Patch Changes
- edfebb5: -
@mercury-fw/plugin-web-search: an answer no longer lists its sources twice. When the model writes its own list of sources at the end of the answer, the plugin’sFonti:list replaces it, andweb_fetch’s description tells the model the pages it reads are listed for it.@mercury-fw/channel-google-chat: once a choice on a confirmation card is taken, the card shows it (“Scelto: …”) instead of the buttons. A click that confirms nothing (expired, or someone else’s in a shared space) leaves the card as it is.@mercury-fw/channel-types:confirmReply(outcome), the reply to each confirmation outcome, for a channel that resolves a token throughresolveConfirmation.@mercury-fw/confirm-engine:tryConfirmanswers withconfirmReply, same texts as before.
Mercury Framework 0.53.0
Minor Changes
- 56e4ed5: - A plugin can act as no one (
actsAs: "anyone"): for a public service with no login, such as web search. It is offered to everyone (still withinaccess.plugins), never with a variant acting as Mercury, and the operator gets it under its own names.- The tools of such a plugin get the turn’s
personall the same (the operator’s key on the terminal), to keep what belongs to each person; they can’t start a login. - An older core reads
"anyone"as"mercury": the plugin is offered only to whoever may make Mercury act as itself.
- The tools of such a plugin get the turn’s
- c83b129: - New plugin
@mercury-fw/plugin-web-search:web_searchthrough a backend package of the app’s choice, andweb_fetchto read a page and hand the model its title and text.- A page is read right away only on a host the instance allows (
allowedHosts) or the person allowed; for any other host the person chooses between reading that page only and allowing the host for good (their own list, kept in the plugin’s folder), and the page goes back to the model either way. - Internal addresses are never read, redirects included, and only HTML and plain text are, up to 2 MB and 20,000 characters.
- An answer built on pages ends with the list of the pages read (
Fonti:). - A post-turn guard’s
shouldRungets the turn’s steps as a second argument.
- A page is read right away only on a host the instance allows (
- 79a232e: - New package
@mercury-fw/web-search-searxng: a SearXNG instance as the web search plugin’s backend, through its JSON format.mfw create --plugins web-searchadds the web search plugin with the SearXNG backend, asearxngservice in the compose file (on the compose network only), itssearxng/settings.ymlwith the JSON format on, andSEARXNG_URLandSEARXNG_SECRETin the env example.
Mercury Framework 0.52.0
Minor Changes
- 678c1cc: - A plugin gets a folder of its own on the vault’s volume,
PluginRuntimeContext.dataDir(<vault>/.mercury/plugins/<name>/), created by the core beforebuild(): data it keeps there survives a restart, and never reaches the model or the wiki.- A plugin whose name isn’t a safe folder name, or whose folder can’t be created, gets none (the core logs why) and loads anyway.
- 7939ff2: -
@mercury-fw/coreexportsOPERATOR_PRINCIPAL, the terminal’s principal, for a caller that driveshandleTurnitself as the operator.@mercury-fw/cli/e2eexportsturnFromDumpandcreateExpect, so a tool running Mercury turns of its own reads and checks them the waymfw e2edoes.
Mercury Framework 0.51.0
Minor Changes
- b2f8cfb: - A confirmation can offer several choices (for example “only this time” / “always for this host”): a plugin tool stages them with the new
stageChoicesofSessionToolContext, and gets one token per choice. The person takes one; the others die with it, and they all expire together.- The paper-trail note of a group of choices is one, under its first token, and says which choice was taken.
detectPendingConfirmationreturns thechoicesa staging offers; a plain staging is one choice labelledConferma.- A confirmed action can hand its result back to the model (
resumeon a choice): instead of showing the person the raw result, the channel resumes the turn and the model answers on it, the result reaching it as the tool result of the call that staged it, never stored in the history. - A resumed turn answers the person who confirmed even in a shared space, and the line standing for the confirmation isn’t archived as something they said. A result nobody resumes within five minutes (the life of a confirmation token) is forgotten, and a restart loses it.
InboundTurn.resumeAfterConfirmandRESUME_AFTER_CONFIRM(channel contract) carry the resumed turn;ConfirmOutcomehas aresumestatus.- Terminal: every choice is printed on its own line with its token; a confirmation that resumes runs the turn again.
- Google Chat: the confirmation card has one button per choice; a typed token or a clicked choice that resumes runs the turn again, in the session’s queue. The channel now needs channel-types 0.51.0 or later.
- HTTP: the
pendingevent carrieschoices; a token sent toPOST /turnthat resumes streams the model’s answer on the same request;POST /confirmanswersresume: true, andPOST /turn { resume: true, conversationId }runs that turn. The channel now needs channel-types 0.51.0 or later.
Mercury Framework 0.50.4
Patch Changes
- ef59194: - The turn log says how the model’s generation ended (
finish stop, for instance).- A turn whose generation ended on an error or at a token limit fails with an error instead of passing a cut answer off as complete.
Mercury Framework 0.50.3
Patch Changes
- 1b786af: - A turn on a streaming channel (the HTTP surface) whose model sends nothing for
TURN_STALL_TIMEOUT_MS(default two minutes,0turns it off) is stopped with the error “the model sent nothing for N seconds”, logged as stalled. It used to hang, holding its conversation, until the service restarted. A tool that takes long doesn’t count: the wait pauses while a tool runs.mfw createlistsTURN_STALL_TIMEOUT_MSin a new app’s env example.
Mercury Framework 0.50.2
Patch Changes
- 4e44ff8: - Every turn is logged: when it starts, when it ends (time, steps, tool calls, how much reasoning and answer text), when it fails or is cancelled, and every minute while it is still running. A turn with no tool calls, or one stuck in its reasoning, used to leave no trace.
Mercury Framework 0.50.1
Patch Changes
- d93e06a: - A new conversation on a channel where the person opens and reopens conversations (the HTTP surface) no longer starts with a recap of their last one: nothing from another conversation comes in unless they ask.
- On a channel with one running stream (Google Chat), the recap of the last conversation reaches the model as background in its prompt, framed as possibly out of date. It used to arrive as the model’s own message, which made it pick that thread back up, and the next summary saved it again.
- Episodic summaries keep what was asked and decided, not the data a tool returned.
Mercury Framework 0.50.0
Minor Changes
- ae495c7: - The model is told, at the end of every prompt, which lists it already showed in the conversation: each one’s ref and the command that built it, never the list itself. It can show one again with
present, or run the command again for fresh data, instead of retyping it from memory.- A list shown in a conversation stays available for
presentas long as the conversation, previously five minutes, at most the ten most recent per conversation. stashDisplaytakes an optional label, and the cli-engine passes the command that built the list with its number of items.- The jira skill points to the lists already shown, and keeps a new search for fresh data.
- A list shown in a conversation stays available for
Mercury Framework 0.49.1
Patch Changes
- 5d3bfdf: - The opening greeting asks for the person’s name in words a model can’t turn around: a small model had asked “how would I like you to call me?”.
Mercury Framework 0.49.0
Minor Changes
- b7438a6: - The assistant remembers how each person wants to be called: they say it in conversation and the model saves it with the new
set_preferred_nametool. It’s a setting kept in.mercury/profiles.jsonon the vault’s volume, not a wiki note, and every one-to-one turn tells the model the name.- A turn can be an opening (
opening: true, with no text): the assistant speaks first, but only to someone whose name it doesn’t know, introducing itself and asking how to call them. For anyone else, and in a shared space, it says nothing and keeps nothing. ChannelHostReads.mesays how the caller asked to be called, ornull.- HTTP:
POST /turntakes{ opening: true, conversationId }, andGET /meanswers{ name }. A UI asks for an opening right after a sign-in, when the name isnull. The channel now needs channel-types 0.49.0 or later.
- A turn can be an opening (
Mercury Framework 0.48.0
Minor Changes
- 62bb3a3: -
persona.nameis the assistant’s name, Mercury when left out. A name that’s blank, longer than 40 characters or on more than one line stops the app at startup.- Every text the model reads refers to the name as
{{assistant}}, and the core puts the persona’s name there: the persona’s identity and tone, the system prompt, plugin fragments, skills and tool descriptions. The model no longer takes “Mercury” for its own name when the app calls its assistant something else. - Where the one acting is the system around the model, the texts say “the system” or “this instance” instead of Mercury: the wiki tools, the cli-engine’s errors, the jira skill and its issue lists.
- Plugins get
assistantNamein theirbuild()context, and channels in their runtime context, for texts they build while running. The login messages, the linking reply and the HTTP login page name the assistant that way. - The tools a person runs as the assistant itself are
<tool>AsSelf, previously<tool>AsMercury. Themercury.act-as-selfroles are unchanged. mfw createwrites the assistant’s name aspersona.name, withpersona/identity.mdandpersona/tone.mdreferring to it. An existing app keeps working as it is: to rename its assistant everywhere, addnameto its persona and write{{assistant}}in its persona files.
- Every text the model reads refers to the name as
Mercury Framework 0.47.1
Patch Changes
- e005ca4: -
mfw e2e: a REPL that exits before its stderr has been read no longer reports “exited with code N:” without the reason.
Mercury Framework 0.47.0
Minor Changes
- 3d73e59: -
authinmercury.config.tstakes a list of providers as well as one: they’re tried in order and the first that recognizes the caller decides who they are. One that fails to load, or fails on a request, is logged and the others still serve; a name taken twice is refused.createOidcAuth({ name })is an OIDC provider of its own (oidc-<name>), configured byOIDC_<NAME>_ISSUERandOIDC_<NAME>_AUDIENCE, so an instance can accept several issuers.oidcAuthis unchanged.- A principal’s provider can be
oidc-<name>; each provider’s people keep their own keys. zitadelDirectorytakesZITADEL_AUTH_PROVIDER, the auth provider whose subjects are its users (defaultoidc).
Mercury Framework 0.46.1
Patch Changes
- a0dc010: - A reopened conversation shows the lists a turn showed: the verbatim archive keeps them next to the model’s answer, and
GET /conversationand the conversation list serve each answer as the person saw it.- A turn whose answer is only a shown list is archived too.
- What goes back to the model (resuming a conversation,
recall_verbatim) is still the model’s own answer only.
Mercury Framework 0.46.0
Minor Changes
- 4184ea6: -
loginLinkTextwords a login link the same way on every channel, and the contract states that a login link is shown only to the person it’s for.- Google Chat shows the login to a service a plugin acts on as the person (Jira, Bitbucket) as a button that opens it, visible only to them: in a shared space it’s a private message to whoever asked.
@mercury-fw/channel-google-chatneeds@mercury-fw/channel-types0.46.0 or later.
Mercury Framework 0.45.0
Minor Changes
- 812c9c5: - A conversation reopened on the HTTP surface after Mercury freed its history comes back to the model: the summaries of its older part plus its newest messages from the archive, within a budget.
- What a resumed conversation brings back isn’t captured to long-term memory again, only the new messages are.
SESSION_RESUME_MAX_CHARSsets how much comes back (default 30000,0turns it off);HTTP_SURFACE_RESUME_MAX_CHARSsets it for the HTTP surface only.InboundTurn.resumelets a channel mark a conversation its client reopens.- The episodic collection gets a
sessionKeyindex. mfw createlistsHTTP_SURFACE_RESUME_MAX_CHARSfor the HTTP channel.- Core’s README explains how Mercury remembers a conversation.
Mercury Framework 0.44.1
Patch Changes
-
c24de78: A Google Chat sender is recognised as their ZITADEL user.
channel-google-chatpasses the sender’s email on as a claim (claims.email), on messages and on card clicks, when Google vouches for it: a person’s (HUMAN) only.zitadelDirectoryresolves a Google Chat sender to the one active ZITADEL user with that email, verified, whose links to the Google identity provider (ZITADEL_GOOGLE_IDP_ID, new and optional) include the sender’s own Google account. Without that variable nobody is recognised from Chat. Anyone else on Chat stays unknown and can link their account with a code.mfw createlistsZITADEL_GOOGLE_IDP_IDin the env example of an app with the ZITADEL directory.
Mercury Framework 0.44.0
Minor Changes
-
2c63bbf: One person across accounts: linking them by a code.
POST /linkon the HTTP surface hands the caller a one-time code (xxxx-xxxx-xxxx, ten minutes, single use). They send it from the other account, on any channel, twice. The first time Mercury says whom the account would be linked to, the second time it links it.- From then on the account is the person it’s linked to: one private area, one memory, the same roles. Its earlier area stays where it was, and undoing the link gives it back.
- A link never touches the terminal, never chains, and is refused for an account the directory already knows as someone else.
- The links live on the vault’s volume, out of the vault’s git and out of reach of the model and the HTTP reads.
mfw identity links | link | unlinklets the operator manage them, for instances where nobody can be shown a code.- Channels get the code through the confirmation hook they already call (
ctx.confirm), and the HTTP surface getsctx.linking. - Fixed:
ctx.confirmrefused someone the core doesn’t admit before checking that their text was a token at all. On Google Chat, every message from an unknown sender came back as the refusal.
Mercury Framework 0.43.0
Minor Changes
-
07eb858: ZITADEL as the user directory.
@mercury-fw/plugin-zitadelexportszitadelDirectory. Someone calling with a ZITADEL token is the ZITADEL user the token names, with the role keys of their active role assignments on the projectZITADEL_PROJECT_ID. A user ZITADEL doesn’t find, or one that isn’t active, is unknown. It reads through the zitadel CLI’s service user, from code.mfw create --directory <id>(staticorzitadel) gives the new app a user directory, and the wizard asks for one.
Mercury Framework 0.42.0
Minor Changes
-
a7f2f13: A user directory decides who people are and what they may make Mercury do.
- An app can declare a
directoryinmercury.config.ts. Given whoever a channel says is talking, it returns the person with their roles. The person’s key is<directory name>:<id>, so one person reached from several channels keeps one private area, one memory and one set of confirmations. - With a directory the instance is closed. Someone it doesn’t know gets a refusal and nothing runs, unless
access.unknownis"allow";access.unknownMessagesets what they’re told. A directory that can’t be reached, or fails to load, refuses everyone but the terminal. Answers are cached for five minutes, so a revoked role stops counting within that time. mercury.act-as-self(every plugin) ormercury.act-as-self.<plugin>(one) lets a person make Mercury act with its own identity.- A plugin acting as Mercury, such as atlassian-admin, is offered only to those people, and on the terminal.
- A plugin acting as the person also gives them each tool a second time, as
<tool>AsMercury. - Every call made as Mercury for someone is logged with who asked.
access.pluginsrestricts a plugin to some roles. Prompts, skills and the manifest follow what each person is offered.- New package
@mercury-fw/directory-static: people, their identities on each channel and their roles, listed inDIRECTORY_STATIC_PEOPLE. - The channel contract is at version 5:
ChannelRuntimeContext.admitsays whether the core talks to a caller;- the reads are per person and async, the manifest included;
DirectoryPluginis the directory contract;Principalno longer carriesroles.
channel-httpanswers403to a caller the directory doesn’t know and503when it can’t be reached, on every route.auth-staticrefusesrolesinAUTH_STATIC_TOKENS: roles come from the directory.
- An app can declare a
Mercury Framework 0.41.0
Minor Changes
-
2755f61: New plugin: ZITADEL through the
zitadelCLI.- New package
@mercury-fw/plugin-zitadel. It reads users (by email, username or id), their project roles and identity provider links, organizations and projects, as the person Mercury is talking to, who logs in to ZITADEL through Mercury. Nothing it runs changes ZITADEL. - The CLI is pinned at 2.6.0.
mfw credentials setup @mercury-fw/plugin-zitadelsets up the service user the terminal runs as; with--user-appit sets up the Native app people log in through. mfw createoffers it aszitadel.
- New package
Mercury Framework 0.40.0
Minor Changes
-
0739f77: A plugin acts as the person Mercury is talking to, who logs in to the service through Mercury.
- Plugin contract version 4: a plugin declares
actsAs: "person" | "mercury", and one acting as the person contributes alogin; its tools get the turn’spersonandrequireLoginin their context. - A person is offered only the plugins acting as the person: their prompt fragments, skills and tools. A plugin acting as Mercury, or declaring nothing, is offered on the terminal only until people can be allowed to make Mercury act as itself; the startup log names it.
createCliTooladds--userwith the person’s id to every command, staged confirmations included, and when the CLI exits with code 3 (not logged in) returns their login instead.createCliPersonLoginbuilds a plugin’sloginon a CLI’s two-step remote login. A failedrunClicarriesexitCode.- Channel contract version 4: channels get
logins(accepta callback URL,completea login) anddetectLoginRequired. - The HTTP channel, with
HTTP_SURFACE_PUBLIC_URLset, streams aloginevent with the link to open and takes the person back atGET /login/callback, public and protected by the single-use state. mercury.cliCredentialscan declareuserSetup, run bymfw credentials setup <plugin> --user-app: the app people log in through, set up without logging anyone in.- plugin-jira and plugin-bitbucket act as the person, on CLIs 2.3.0: set up their people’s app with
--user-appand register<HTTP_SURFACE_PUBLIC_URL>/login/callbackon it. Jira’s skill now sayscurrentUser()is the person. - plugin-atlassian-admin acts as Mercury, on CLI 0.2.0, whose
initasks for the key without echoing it: until permissions exist it’s available on the terminal only. - A third-party plugin needs
apiVersion: 4to load, andactsAs: "person"with aloginto be offered to people.
- Plugin contract version 4: a plugin declares
Mercury Framework 0.39.0
Minor Changes
-
881835c: A CLI’s login is set up inside the app’s container, no longer carried in the env file.
- A plugin declares in
mercury.cliCredentialsthe commands that set its CLI’s login up (setup, required), check it (check) and log an identity out (logout), each a binary followed by its arguments. mfw credentials setup <plugin>runs the declared setup in a one-off container on your terminal, so the CLI writes its login straight onto the credentials volume;mfw credentials check <plugin>runs the declared check.mfw credentials reset <plugin> [--user <key>]runs the declared logout, of the identity Mercury runs as or of one person, instead of deleting the CLI’s folder.mfw credentials setis removed, and the core no longer unpacks*_CONFIG_TAR_B64variables at startup: it reports a declared login that isn’t set up yet, and still links a login kept outside~/.configto the volume.- A command the model writes with
--useris refused: which person a CLI acts as is never the model’s to pick. cliUserId(@mercury-fw/utils) maps a user key to the id a CLI knows a person by.- plugin-jira and plugin-bitbucket pin their CLIs at 2.1.0, which keep the service identity and each person apart and refuse the old login folder. After updating, remove the plugin’s
*_CONFIG_TAR_B64line from the env file and runmfw credentials setuponce; on Jira, Mercury now runs as an Atlassian Service Account. - plugin-atlassian-admin keeps its CLI at 0.1.2, whose
initdoesn’t prompt for the organization key: until it does, write the key frommfw shellwithatlassian-admin init --api-key <KEY> --org-id <ORG_ID>(the plugin’s README says so).
- A plugin declares in
Mercury Framework 0.38.1
Patch Changes
- 972a17f: -
mfw local-packagesandmfw create --local-packagesname each tarball in.packs/after its content, so a package repacked at the same version is installed anew instead of kept from Bun’s cache or refused by the lockfile’s integrity check
Mercury Framework 0.38.0
Minor Changes
-
de1ee52: Several people talking at once.
@mercury-fw/core: turns of one conversation run one after the other, on every channel. Before, two HTTP/turnon the same conversation id ran together and mixed up its history.@mercury-fw/core: a turn waiting behind another one on the same conversation is dropped as soon as its client goes away.@mercury-fw/core: the idle capture waits for a running turn before it closes a conversation, and a sweep never starts while the previous one is still running.@mercury-fw/core: the capture a history compression triggers no longer leaves the capture marker wrong, which could make a later capture take the same messages twice or skip new ones.@mercury-fw/core: the tool log is kept per person, so a busy person no longer pushes everyone else’s calls out of it.@mercury-fw/core: consolidation,index.mdupdates and confirmed promotions compare against the current file in the same queue as the write, so a concurrent update is never lost.@mercury-fw/core: vault files are written through a temporary file and a rename, so a reader never sees one half written.@mercury-fw/core: a vault file that exists but can’t be read fails a write that depends on it, instead of counting as missing.@mercury-fw/core: the nightly review rewrites or deletes only the version of a document it read, and rereads it when it changed meanwhile.@mercury-fw/core: each pass of the nightly review gets up to 100 steps, up from 15.@mercury-fw/cli: an e2e case on HTTP can send its turns inlanesthat run at the same time, each as its own user, on a conversation of its own or one they share.
Mercury Framework 0.37.0
Minor Changes
-
fbaa7aa: The admin panel is gone.
- Breaking,
@mercury-fw/core:composeMercuryno longer returnsstartAdmin, andADMIN_PANEL_ENABLED/ADMIN_PANEL_PORTdo nothing. An existing app removes the two lines of itssrc/index.tsthat start and stop it (const adminServer = app.startAdmin();andadminServer?.stop();). - New apps don’t start it.
- What it showed is in the HTTP surface’s read routes, scoped to the caller, and in
mfw vault/mfw memoryfor the operator.
- Breaking,
Mercury Framework 0.36.0
Minor Changes
-
521db42: Isolation between the people an agent talks to.
- The core keeps one identity per person,
<provider>:<id>, so two providers issuing the same id are two people. Memory, the conversation archive, the tool log and pending confirmations are keyed on it. - The wiki has a common area (
curated/) everyone reads and an area per person (users/<key>/), which the model sees aspersonal/. Listing, reading and grepping reach the common area and the caller’s own area, nothing else. - Breaking: the model’s
write_filewrites only underpersonal/notes/. The newpromote_notetool copies one of the person’s notes intocurated/, and only once they confirm it with the token. - A confirmation can only be confirmed by whoever staged it, in the same session. Its note is written once, in the person’s area: a staged note no longer stays “pending” after it’s resolved, for ids such as Google Chat’s
users/<n>. - Breaking,
@mercury-fw/channel-types: channel contract 3.confirmandresolveConfirmationtake the caller’sPrincipal, and so does every read getter exceptmanifestandhealth. Channels written for contract 2 are refused. @mercury-fw/confirm-engine: the store’sstage,takeandpendingtake the owner’s key.- Breaking,
@mercury-fw/channel-http: every read route returns only the caller’s data./conversation?id=takes one of the caller’sconversationIds and/conversationslists them byconversationId./wiki/readanswers404outside what the caller can see, and/memory/scrollanswers400for a collection that isn’t kept per person. @mercury-fw/channel-google-chat: on channel contract 3.- At startup, an instance’s existing data moves to the new layout. Google Chat and terminal notes go into their areas, and Qdrant’s ids go onto the new keys. Ids that don’t say which provider they came from are left in place and logged.
mfw vault readrefuses a path outside the vault.- The admin panel’s wiki box edits the common area with the operator’s tools (list, read, grep,
write_curated), the same the nightly review uses.
- The core keeps one identity per person,
Mercury Framework 0.35.0
Minor Changes
-
8d3e5af: Authentication for the HTTP surface.
- An app declares an auth provider as
authinmercury.config.ts. The core builds it and hands channelsctx.authenticate. A provider that fails to load leaves the channel without it, so the surface stays closed. - New contract in
@mercury-fw/channel-types:AuthPlugin,AUTH_API_VERSIONandAuthenticate.PrincipalProvidergainsoidcandstatic. - New package
@mercury-fw/auth-oidcverifies an OpenID Connect bearer token against the issuer’s keys and checksiss,audand expiry. It readsOIDC_ISSUERandOIDC_AUDIENCE. - New package
@mercury-fw/auth-staticmaps fixed test tokens fromAUTH_STATIC_TOKENSto users, for test apps and e2e tests. - Breaking,
@mercury-fw/channel-http: the channel doesn’t start without an auth provider. @mercury-fw/channel-http: every route exceptGET /openapi.yamland theOPTIONSpreflights needsAuthorization: Bearer <token>, and a missing or refused token gets401before anything runs.- Breaking,
@mercury-fw/channel-http: a conversation belongs to whoever opened it. The session key is<caller id>:<conversationId>, so the same id from someone else is a conversation of their own. Existing HTTP conversations stay readable but can’t be continued. @mercury-fw/channel-http: a confirmation token is checked against the caller’s own session, so it can’t be confirmed from someone else’s.@mercury-fw/channel-http: authenticated callers get their own episodic memory and wiki area.@mercury-fw/channel-http: the read routes (/conversations,/conversation,/wiki/*,/memory/scroll,/tool-log,/confirmations) need an authenticated caller, but still show every person’s data. Scoping them per person comes next, so don’t give the surface to people who shouldn’t see each other’s conversations yet. Confirmation tokens are never listed.- The HTTP
toolevent carries the name of the tool the model called.TurnSink.onToolStartgets it as an optional fourth argument. mfw createpairs the HTTP channel with an auth provider:--auth oidc|static, which the wizard asks for once HTTP is chosen.mfw e2eruns cases on the HTTP surface (channel: "http") as one of the test’susers, each a token the app accepts. A turn can name its own user, and it reports the response’s status.mfw create --local-packages <folder>makes an app on packed tarballs, asmfw local-packagesdoes on an existing one. Versions come from the tarballs, so a package that isn’t published yet works too.
- An app declares an auth provider as
Mercury Framework 0.34.0
Minor Changes
- 8bed46b: - A turn carries a
Principal(the person behind it and the provider that vouched for them) instead of the opaqueuserIdandwikiUserId; the core derives every per-person id from it.- Episodic memory follows the principal, not the channel: a session is captured whenever a provider vouched for the person. Google Chat keeps the ids it had; the terminal and the HTTP channel (until it authenticates its callers) send a principal nobody vouched for, so they stay out of episodic memory as before.
- The HTTP channel accepts a
conversationIdon/turnand/confirmonly made of letters, digits,-and_, up to 128 characters once trimmed, and answers400otherwise: a client-chosen id can no longer reach another user’s wiki notes, forge log lines or break the turn. An existing HTTP conversation whose id has other characters can still be read with/conversation, but not continued./conversationkeeps opening any session key/conversationslists, whatever channel it came from. - The model’s wiki read tools refuse a per-user id that isn’t one path segment, as writes already did.
CHANNEL_API_VERSIONis 2: a channel written for version 1 is refused at load.- The first-party channels declare their channel api version as a literal, so an older channel can’t claim a newer contract it doesn’t implement.
Mercury Framework 0.33.0
Minor Changes
- a493d9b: - A plugin whose CLI keeps its login in a folder declares it in its
package.json(mercury.cliCredentials):{ folder }under~/.config, the default, or{ path }anywhere else under the home. Any plugin’s CLI gets the login mechanism, not only the first-party ones.- The core unpacks each declared login from its env variable onto the credentials volume (
~/.config) at startup, only when the folder isn’t there yet, for the service and the REPL alike; a folder declared elsewhere in the home lives on the volume under~/.config/mercury-home, linked from its usual place. It warns about a declared folder with neither the folder nor the variable. mfw credentials set|reset <plugin>names the plugin by its package or its CLI’s folder (@mercury-fw/plugin-jiraorjira-cli), read from the app’s installed plugins; the short name (jira) is no longer accepted, and reset asks for the folder’s name.mfw createno longer writesdocker-entrypoint.shor the credentials variables in the env example, and always mounts thecli-credentialsvolume; an existing app’s entrypoint keeps working alongside.- The generated README explains how a plugin’s CLI gets its login without listing plugins.
- jira, bitbucket and atlassian-admin declare their CLI’s login folder; their READMEs point to
mfw credentials set.
- The core unpacks each declared login from its env variable onto the credentials volume (
Mercury Framework 0.32.0
Minor Changes
- 1fa3a97: - Every tool plugin declared in
mercury.config.tsloads:MERCURY_CLISis no longer read. An app that used it to keep a declared plugin off now gets that plugin on; remove the plugin from the config instead.- A new app’s env example has no
MERCURY_CLIS, and its config’s comment says every declared plugin and channel is active. - A plugin caught in a dependency cycle is always reported at startup.
- The plugins’ READMEs no longer ask to list them in
MERCURY_CLIS.
- A new app’s env example has no
Mercury Framework 0.31.1
Patch Changes
- 7926aa2: - Wiki grep ignores case, for the agent, the nightly review,
mfw vault grepand the HTTP/wiki/greproute.- The agent’s
write_fileand the nightly review’swrite_curatedtake a path starting withcurated/, as listing, reading and grepping give it, instead of writing undercurated/curated/.
- The agent’s
Mercury Framework 0.31.0
Minor Changes
- 6ae8133: -
mfw local-packages <folder>makes an app install@mercury-fw/*packages from local tarballs (bun pm pack) instead of the registry, transitive dependencies included;--offgoes back to the registry.mfw e2eruns end-to-end tests against an app’s real model through its REPL: cases of turns with checks on the tool calls and the answer, repeated runs, results kept ine2e/results/.- Test files declare their cases with
e2e()from@mercury-fw/cli/e2e. - A new app’s Dockerfile copies
.packs/when present, and its.gitignoreleaves out.packs/ande2e/results/. - In the REPL,
/dumpafter a confirmation no longer writes the turn before it.
Mercury Framework 0.30.0
Minor Changes
- 238d4dc: -
mfw createrunsbun installin the new app, showing its output as it goes (--no-installto skip it).mfw createcreates a git repository onmainwith a first commit, lockfile included (--no-gitto skip it); inside another repository it is skipped, and when git refuses a step or is missing, the message reports why and lists the commands to finish by hand.mfw createaddsoriginwhen given one, in the wizard or with--git-remote; nothing is pushed.- The message at the end of
mfw createlists what each step did and only the steps left to run.
Mercury Framework 0.29.4
Patch Changes
- 63f3da2: - Before writing to the wiki, the agent looks for a document on the same topic and updates it, instead of adding a new one each time.
- The agent no longer writes wiki notes restating a CLI’s syntax or flags, which the plugin’s skill and
--helpalready cover. - A learned command correction is keyed by the flag or subcommand it is about, without the tool’s name, so corrections on the same flag land in the same note.
- The prompts that extract command corrections and facts about the user are in English.
- The agent no longer writes wiki notes restating a CLI’s syntax or flags, which the plugin’s skill and
Mercury Framework 0.29.3
Patch Changes
- bd643ab: - A scaffolded app with Jira marks
JIRA_SITE_URLas required in its env example.
Mercury Framework 0.29.2
Patch Changes
- cc31536: -
mfw createends by saying that, without a globalmfw, the app’s commands run asbunx mfw <command>, and lists the global install as an optional step.
Mercury Framework 0.29.1
Patch Changes
- 60916cb: - The wiki vault’s automated commits are authored as
Mercury <mercury@mercury.local>. Commits written before keep their old address;git log --author=Mercurymatches both.- The Jira issue-list extractor’s docs use a generic example site.
Mercury Framework 0.29.0
Minor Changes
- e498dad:
mfwis meant to be installed globally (bun add -g @mercury-fw/cli) and used as a plain command. Inside an app, a globalmfwat another version hands the command over to the app’s own CLI, so the app’s commands always match its framework.mfw upgradeinstalls the registry’s latest globally, andmfw createrun by a stalemfwsays to. The CLI’s messages, the generated app and the docs saymfwinstead ofbunx mfw.
Mercury Framework 0.28.4
Patch Changes
- 65e78fe:
mfw create(andbun create mercury-agent) checks the registry for a newer@mercury-fw/clifirst, and when it’s behind, as a copy left in Bun’s bunx cache can be, it runs the same command through the newer version instead of writing an outdated app.
Mercury Framework 0.28.3
Patch Changes
- 5175e10: An app created with the HTTP channel publishes the surface’s port on the host (
HTTP_SURFACE_PORT, 4100 when unset), so it’s reachable from outside the container; its README says where it listens and that it has no authentication.
Mercury Framework 0.28.2
Patch Changes
- 0c5bb5b: Dependencies at their latest minor:
ai7.0.126,ai-sdk-ollama4.4.0,zod4.6.5,yaml2.9.1,shell-quote1.11.0. A new app frommfw createruns Qdrant on itsv1tag, which follows every 1.x release, instead of a fixed 1.19.0, and gets@types/bun^1.4.2.
Mercury Framework 0.28.1
Patch Changes
- ab52977: An app created with the Google Chat channel trusts
protobufjsintrustedDependencies, so its install no longer reports that package’s postinstall as blocked. The channel’s README says to do the same when adding it by hand.
Mercury Framework 0.28.0
Minor Changes
- 51bd439:
mfw google-chat set-key <key-file> [--subscription <name>]writes the Google Chat channel’s service account key (and its subscription) into the app’s env file, the key on one line the way the channel reads it and never printed. The channel’s setup uses it in place of the hand-writtensed/printfstep.
Mercury Framework 0.27.1
Patch Changes
- 2c0e020: The REPL no longer prints the answer a second time, under “risposta corretta rispetto a quanto già mostrato sopra”, after a turn that reasoned or called a tool.
Mercury Framework 0.27.0
Minor Changes
- d2a6be5: - A scaffolded app with tool plugins gets a
docker-entrypoint.sh: at the first start without a CLI’s config folder on the credentials volume, it unpacks that CLI’s variable from the env file there, then starts the service. What the CLI refreshes afterwards stays on the volume.- The scaffolded env example lists each tool plugin’s credentials variable, and the README explains the flow.
mfw credentials set <plugin>packs a CLI’s config folder (~/.config/<cli>, or--from <dir>) into its variable in the app’s env file, never printing it;--printprints the line to paste elsewhere.mfw credentials reset <plugin>clears a CLI’s folder from the credentials volume after you type the plugin’s name, so a corrected variable is unpacked at the next start.
Mercury Framework 0.26.0
Minor Changes
- 8b92abb: -
mfwoperates an app from inside its folder:start,stopandrestart(with--no-cache),logs,repl,shell, all asdocker composecalls.mfw vaultmaintains the wiki vault (list,read,grep,write-curated,write-raw) in a one-off container.mfw memory listandmfw memory read <collection>read the memory on Qdrant, newest first where the collection has a timestamp index.mfw reset memoryandmfw reset wikidelete a memory volume after you type the app’s name, then bring the service back up empty; after a memory reset a running app is restarted, so it sets up its collections again.- The command line is declared with commander:
--helpat every level, a suggestion for a mistyped command, and every argument checked before anything runs. - A scaffolded app lists
@mercury-fw/cliamong its devDependencies, at the framework’s version, and its README runs it throughbunx mfw. - A scaffolded app’s
@types/bunandtypescriptuse caret ranges instead of exact versions. - The core ships a read-only memory CLI (
src/memory/memory-cli.ts) next to the vault one, which is whatmfw memoryruns. - The vault CLI says so when asked to read a note that doesn’t exist, instead of printing a stack trace.
Mercury Framework 0.25.1
Patch Changes
- 5613e5e: - Mercury starts even when Qdrant isn’t answering yet: the memory collections are set up in the background and retried until Qdrant is reachable, instead of crashing the process at startup.
- A new session’s context primer goes on without the last-session recap when Qdrant doesn’t answer, instead of failing the turn.
- The scaffolded app’s
docker-compose.ymlrestarts the app service unless it was stopped.
Mercury Framework 0.25.0
Minor Changes
- c114e34: - Mercury is published on npm as
@mercury-fw/*: the framework packages move together under one version, plugins and channels have versions of their own.bun create mercury-agent my-agentscaffolds an app, and the CLI command is nowmfw(@mercury-fw/cli).- Packages ship type declarations, so a new app type-checks against the framework without re-checking its source.
- External dependencies use version ranges instead of exact pins, so an app shares them with the framework.
- Every package has its own README, and the repo README describes the framework.
- MIT license.