Skip to content
mercury framework
GitHub

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

on GitHub

Minor Changes

  • 96eb1a8: - mfw create offers 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 in locales/en.json.

@mercury-fw/plugin-confluence 0.1.0

on GitHub

Minor Changes

  • 96eb1a8: First release: Confluence, read-only, through the pinned confluence CLI.

    • 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

on GitHub

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

on GitHub

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

on GitHub

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

on GitHub

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-issues text is in locales/en.json only 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

on GitHub

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

on GitHub

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

on GitHub

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-issues text is in locales/en.json only 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

on GitHub

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-issues text is in locales/en.json only 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

on GitHub

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-issues text is in locales/en.json only 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

on GitHub

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

on GitHub

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-issues text is in locales/en.json only 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

on GitHub

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

on GitHub

Patch Changes

  • 44f2540: Inside an app, mfw --version and mfw --help (and a bare mfw) 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, so mfw --version there 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). create and upgrade stay the global CLI’s. It’s the global mfw that hands over, so this takes effect once it’s updated: mfw upgrade.

Mercury Framework 0.59.0

on GitHub

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

on GitHub

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 abortSignal aborted) is never finalized, run through the post-turn guards or archived, and it resolves instead of throwing; the sink is still released with dispose. Before, the AI SDK returned what it had and the turn delivered it as the answer.
    • Channel contract: the abortSignal doc says what a cancelled turn does. No change in shape, the contract version stays 6.

Mercury Framework 0.57.0

on GitHub

Minor Changes

  • 377ce2a: mfw create gives the app a place for plugins of its own: plugins/<name>, each a package, as a workspace of the app ("workspaces": ["plugins/*"] in package.json), listed in dependencies as workspace:* and declared in mercury.config.ts. A bunfig.toml keeps Bun’s hoisted node_modules: with a workspaces key Bun would switch to its isolated linker, whose node_modules is made of links. The Dockerfile copies bunfig.toml with package.json, then plugins/ before bun install, so a plugin’s postinstall finds its own scripts, and puts on PATH the binaries a plugin’s postinstall writes to its bin/, as it does for the framework’s packages. .dockerignore leaves out every node_modules, a plugin’s own included, and the binaries a plugin’s postinstall wrote on the host, which .gitignore keeps out of the repository too. package.json gets a test script (bun test), which runs the plugins’ tests too.

    An existing app keeps working as it is. To give it the same place:

    1. In package.json, add "workspaces": ["plugins/*"] and "test": "bun test" under scripts.

    2. Add a bunfig.toml next to it:

      [install]
      linker = "hoisted"
    3. In the Dockerfile, change COPY --chown=mercury:mercury package.json bun.lock* ./ to COPY --chown=mercury:mercury package.json bun.lock* bunfig.toml ./, add COPY --chown=mercury:mercury plugin[s] ./plugins/ right after COPY --chown=mercury:mercury .pack[s] ./.packs/, and replace the line RUN 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/ \;
    4. In .dockerignore, change node_modules to **/node_modules and add plugins/*/bin.

    5. In .gitignore, add plugins/*/bin.

Mercury Framework 0.56.0

on GitHub

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 new i18n.timeZone in mercury.config.ts, else the machine’s. A timeZone that isn’t a zone’s name stops the start.
    • The person saves theirs by telling the assistant (the new set_time_zone tool), or from a channel’s settings.
    • Channel contract: InboundTurn.timeZoneHint, and ctx.settings.setTimeZone for a channel with a settings area; reads.me also returns the saved timeZone. Both are optional additions: the contract version stays 6.
    • HTTP channel: the X-Time-Zone header on POST /turn (allowed by CORS), timeZone in GET /me, and PATCH /me with { timeZone } to save it. Needs the framework at 0.56 or later; an older core ignores the header and offers no PATCH /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) to i18n in mercury.config.ts; without it the machine’s zone is used, often UTC inside a container.

Mercury Framework 0.55.0

on GitHub

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-types starts there.

    A plugin written for contract 5 isn’t loaded by this core, and the log says why: add a description and rebuild it against PLUGIN_API_VERSION 6.

Mercury Framework 0.54.0

on GitHub

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-Language header (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 through ctx.i18n.forRequest(principal, hint).
    • The replies to a confirmation are the core’s, in the person’s language: ctx.confirm returns { text, resume? } and ctx.resolveConfirmation the outcome with its text. A confirmed action reads “Confirmed and done.” instead of its raw result as JSON. confirmReply, RESUME_AFTER_CONFIRM and PENDING_CONFIRMATION_NOTE are gone from the contract, and tryConfirm from @mercury-fw/confirm-engine (resolveConfirmation reports 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-Language on /turn, /confirm and /login/callback; Content-Language on the responses of /turn and /confirm; the final event carries pendingNote; 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’s common.userLocale is 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 through final’s pendingNote, 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 against CHANNEL_API_VERSION 6.

  • 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 } } in mercury.config.ts, each file mapping keys to texts. A person picks their language by asking the agent, which saves it with set_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 gets defaultLocale, then English. A key a language’s file lacks shows as the key itself, and the log names the missing keys at startup. GET /me on 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:

    1. access.unknownMessage is gone. To keep a custom refusal, put it in locales/en.json as { "core.access.unknown": "…" }, and in mercury.config.ts import it (import en from "./locales/en.json";) and set i18n: { locales: { en } }.
    2. To keep the status and terminal lines in Italian, add locales/it.json with the core. keys translated (start it as {}: the log lists the missing keys at startup), and in mercury.config.ts import it (import it from "./locales/it.json";) and set i18n: { defaultLocale: "it", locales: { it } } (locales: { en, it } when you did step 1 too).
    3. Whenever the config imports a file from locales/ (steps 1 and 2), the image needs the folder: add COPY --chown=mercury:mercury locales ./locales after the mercury.config.ts line of the Dockerfile, or the app fails at startup.
    4. For the terminal (mfw repl) to follow the language too, add texts: app.texts, to createTerminalProvider({ … }) in src/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 named core or app, 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: in SessionToolContext.t, as the second argument of a status describer (toolStatusDescribers, StatusDescriber), the third of a post-processor (CliPostProcessor), and in a post-turn guard, whose statusLabel is now (t) => string and whose run gets t as its third argument. Messages, Params and Translate are 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-bare without a subcommand (“Running jira”), and an unreadable command’s is core.status.command-unknown. createCliTool (@mercury-fw/cli-engine) takes the turn’s t among its options, handed to the post-processor: a CLI plugin passes sctx.t; createCliStatusDescriber’s describer takes it as its second argument.
    • The formatter hands a rule t as its second argument, and an empty text can be (t) => string; existing rules keep working as they are. An app’s own texts go under app., in its locales/en.json, which declares them (the startup log no longer lists them as unknown); mfw create writes the Jira rule’s empty text as app.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_VERSION 5 (a status describer, a guard’s statusLabel and run, a post-processor take the new t). An app that wants the web search and status lines back in Italian adds their keys to locales/it.json (the log lists the missing ones at startup).

Mercury Framework 0.53.2

on GitHub

Patch Changes

  • 4a9a528: mfw create writes the whole persona inside mercury.config.ts: the assistant’s name once, as a const, referred to by the identity and by the tone, which is written out in full, one string per line. There are no persona/identity.md and persona/tone.md files anymore, no markdown.d.ts, and the Dockerfile no longer copies them.

    An existing app keeps working as it is. To move it to the new layout:

    1. In mercury.config.ts, drop the two import … from "./persona/…md" lines, add const name = "<the assistant's name>"; and set persona: { 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).
    2. Delete persona/ and markdown.d.ts.
    3. In the Dockerfile, change COPY --chown=mercury:mercury mercury.config.ts markdown.d.ts ./ to COPY --chown=mercury:mercury mercury.config.ts ./ and delete COPY --chown=mercury:mercury persona ./persona.

Mercury Framework 0.53.1

on GitHub

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’s Fonti: list replaces it, and web_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 through resolveConfirmation.
    • @mercury-fw/confirm-engine: tryConfirm answers with confirmReply, same texts as before.

Mercury Framework 0.53.0

on GitHub

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 within access.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 person all 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.
  • c83b129: - New plugin @mercury-fw/plugin-web-search: web_search through a backend package of the app’s choice, and web_fetch to 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 shouldRun gets the turn’s steps as a second argument.
  • 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-search adds the web search plugin with the SearXNG backend, a searxng service in the compose file (on the compose network only), its searxng/settings.yml with the JSON format on, and SEARXNG_URL and SEARXNG_SECRET in the env example.

Mercury Framework 0.52.0

on GitHub

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 before build(): 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/core exports OPERATOR_PRINCIPAL, the terminal’s principal, for a caller that drives handleTurn itself as the operator.
    • @mercury-fw/cli/e2e exports turnFromDump and createExpect, so a tool running Mercury turns of its own reads and checks them the way mfw e2e does.

Mercury Framework 0.51.0

on GitHub

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 stageChoices of SessionToolContext, 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.
    • detectPendingConfirmation returns the choices a staging offers; a plain staging is one choice labelled Conferma.
    • A confirmed action can hand its result back to the model (resume on 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.resumeAfterConfirm and RESUME_AFTER_CONFIRM (channel contract) carry the resumed turn; ConfirmOutcome has a resume status.
    • 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 pending event carries choices; a token sent to POST /turn that resumes streams the model’s answer on the same request; POST /confirm answers resume: true, and POST /turn { resume: true, conversationId } runs that turn. The channel now needs channel-types 0.51.0 or later.

Mercury Framework 0.50.4

on GitHub

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

on GitHub

Patch Changes

  • 1b786af: - A turn on a streaming channel (the HTTP surface) whose model sends nothing for TURN_STALL_TIMEOUT_MS (default two minutes, 0 turns 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 create lists TURN_STALL_TIMEOUT_MS in a new app’s env example.

Mercury Framework 0.50.2

on GitHub

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

on GitHub

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

on GitHub

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 present as long as the conversation, previously five minutes, at most the ten most recent per conversation.
    • stashDisplay takes 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.

Mercury Framework 0.49.1

on GitHub

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

on GitHub

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_name tool. It’s a setting kept in .mercury/profiles.json on 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.me says how the caller asked to be called, or null.
    • HTTP: POST /turn takes { opening: true, conversationId }, and GET /me answers { name }. A UI asks for an opening right after a sign-in, when the name is null. The channel now needs channel-types 0.49.0 or later.

Mercury Framework 0.48.0

on GitHub

Minor Changes

  • 62bb3a3: - persona.name is 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 assistantName in their build() 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. The mercury.act-as-self roles are unchanged.
    • mfw create writes the assistant’s name as persona.name, with persona/identity.md and persona/tone.md referring to it. An existing app keeps working as it is: to rename its assistant everywhere, add name to its persona and write {{assistant}} in its persona files.

Mercury Framework 0.47.1

on GitHub

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

on GitHub

Minor Changes

  • 3d73e59: - auth in mercury.config.ts takes 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 by OIDC_<NAME>_ISSUER and OIDC_<NAME>_AUDIENCE, so an instance can accept several issuers. oidcAuth is unchanged.
    • A principal’s provider can be oidc-<name>; each provider’s people keep their own keys.
    • zitadelDirectory takes ZITADEL_AUTH_PROVIDER, the auth provider whose subjects are its users (default oidc).

Mercury Framework 0.46.1

on GitHub

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 /conversation and 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

on GitHub

Minor Changes

  • 4184ea6: - loginLinkText words 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-chat needs @mercury-fw/channel-types 0.46.0 or later.

Mercury Framework 0.45.0

on GitHub

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_CHARS sets how much comes back (default 30000, 0 turns it off); HTTP_SURFACE_RESUME_MAX_CHARS sets it for the HTTP surface only.
    • InboundTurn.resume lets a channel mark a conversation its client reopens.
    • The episodic collection gets a sessionKey index.
    • mfw create lists HTTP_SURFACE_RESUME_MAX_CHARS for the HTTP channel.
    • Core’s README explains how Mercury remembers a conversation.

Mercury Framework 0.44.1

on GitHub

Patch Changes

  • c24de78: A Google Chat sender is recognised as their ZITADEL user.

    • channel-google-chat passes 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.
    • zitadelDirectory resolves 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 create lists ZITADEL_GOOGLE_IDP_ID in the env example of an app with the ZITADEL directory.

Mercury Framework 0.44.0

on GitHub

Minor Changes

  • 2c63bbf: One person across accounts: linking them by a code.

    • POST /link on 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 | unlink lets 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 gets ctx.linking.
    • Fixed: ctx.confirm refused 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

on GitHub

Minor Changes

  • 07eb858: ZITADEL as the user directory.

    • @mercury-fw/plugin-zitadel exports zitadelDirectory. Someone calling with a ZITADEL token is the ZITADEL user the token names, with the role keys of their active role assignments on the project ZITADEL_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> (static or zitadel) gives the new app a user directory, and the wizard asks for one.

Mercury Framework 0.42.0

on GitHub

Minor Changes

  • a7f2f13: A user directory decides who people are and what they may make Mercury do.

    • An app can declare a directory in mercury.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.unknown is "allow"; access.unknownMessage sets 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) or mercury.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.plugins restricts 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 in DIRECTORY_STATIC_PEOPLE.
    • The channel contract is at version 5:
      • ChannelRuntimeContext.admit says whether the core talks to a caller;
      • the reads are per person and async, the manifest included;
      • DirectoryPlugin is the directory contract;
      • Principal no longer carries roles.
    • channel-http answers 403 to a caller the directory doesn’t know and 503 when it can’t be reached, on every route.
    • auth-static refuses roles in AUTH_STATIC_TOKENS: roles come from the directory.

Mercury Framework 0.41.0

on GitHub

Minor Changes

  • 2755f61: New plugin: ZITADEL through the zitadel CLI.

    • 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-zitadel sets up the service user the terminal runs as; with --user-app it sets up the Native app people log in through.
    • mfw create offers it as zitadel.

Mercury Framework 0.40.0

on GitHub

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 a login; its tools get the turn’s person and requireLogin in 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.
    • createCliTool adds --user with 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. createCliPersonLogin builds a plugin’s login on a CLI’s two-step remote login. A failed runCli carries exitCode.
    • Channel contract version 4: channels get logins (accept a callback URL, complete a login) and detectLoginRequired.
    • The HTTP channel, with HTTP_SURFACE_PUBLIC_URL set, streams a login event with the link to open and takes the person back at GET /login/callback, public and protected by the single-use state.
    • mercury.cliCredentials can declare userSetup, run by mfw 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-app and register <HTTP_SURFACE_PUBLIC_URL>/login/callback on it. Jira’s skill now says currentUser() is the person.
    • plugin-atlassian-admin acts as Mercury, on CLI 0.2.0, whose init asks for the key without echoing it: until permissions exist it’s available on the terminal only.
    • A third-party plugin needs apiVersion: 4 to load, and actsAs: "person" with a login to be offered to people.

Mercury Framework 0.39.0

on GitHub

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.cliCredentials the 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 set is removed, and the core no longer unpacks *_CONFIG_TAR_B64 variables at startup: it reports a declared login that isn’t set up yet, and still links a login kept outside ~/.config to the volume.
    • A command the model writes with --user is 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_B64 line from the env file and run mfw credentials setup once; on Jira, Mercury now runs as an Atlassian Service Account.
    • plugin-atlassian-admin keeps its CLI at 0.1.2, whose init doesn’t prompt for the organization key: until it does, write the key from mfw shell with atlassian-admin init --api-key <KEY> --org-id <ORG_ID> (the plugin’s README says so).

Mercury Framework 0.38.1

on GitHub

Patch Changes

  • 972a17f: - mfw local-packages and mfw create --local-packages name 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

on GitHub

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 /turn on 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.md updates 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 in lanes that 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

on GitHub

Minor Changes

  • fbaa7aa: The admin panel is gone.

    • Breaking, @mercury-fw/core: composeMercury no longer returns startAdmin, and ADMIN_PANEL_ENABLED/ADMIN_PANEL_PORT do nothing. An existing app removes the two lines of its src/index.ts that start and stop it (const adminServer = app.startAdmin(); and adminServer?.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 memory for the operator.

Mercury Framework 0.36.0

on GitHub

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 as personal/. Listing, reading and grepping reach the common area and the caller’s own area, nothing else.
    • Breaking: the model’s write_file writes only under personal/notes/. The new promote_note tool copies one of the person’s notes into curated/, 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. confirm and resolveConfirmation take the caller’s Principal, and so does every read getter except manifest and health. Channels written for contract 2 are refused.
    • @mercury-fw/confirm-engine: the store’s stage, take and pending take 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’s conversationIds and /conversations lists them by conversationId. /wiki/read answers 404 outside what the caller can see, and /memory/scroll answers 400 for 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 read refuses 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.

Mercury Framework 0.35.0

on GitHub

Minor Changes

  • 8d3e5af: Authentication for the HTTP surface.

    • An app declares an auth provider as auth in mercury.config.ts. The core builds it and hands channels ctx.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_VERSION and Authenticate. PrincipalProvider gains oidc and static.
    • New package @mercury-fw/auth-oidc verifies an OpenID Connect bearer token against the issuer’s keys and checks iss, aud and expiry. It reads OIDC_ISSUER and OIDC_AUDIENCE.
    • New package @mercury-fw/auth-static maps fixed test tokens from AUTH_STATIC_TOKENS to 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 except GET /openapi.yaml and the OPTIONS preflights needs Authorization: Bearer <token>, and a missing or refused token gets 401 before 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 tool event carries the name of the tool the model called. TurnSink.onToolStart gets it as an optional fourth argument.
    • mfw create pairs the HTTP channel with an auth provider: --auth oidc|static, which the wizard asks for once HTTP is chosen.
    • mfw e2e runs cases on the HTTP surface (channel: "http") as one of the test’s users, 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, as mfw local-packages does on an existing one. Versions come from the tarballs, so a package that isn’t published yet works too.

Mercury Framework 0.34.0

on GitHub

Minor Changes

  • 8bed46b: - A turn carries a Principal (the person behind it and the provider that vouched for them) instead of the opaque userId and wikiUserId; 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 conversationId on /turn and /confirm only made of letters, digits, - and _, up to 128 characters once trimmed, and answers 400 otherwise: 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. /conversation keeps opening any session key /conversations lists, 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_VERSION is 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

on GitHub

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-jira or jira-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 create no longer writes docker-entrypoint.sh or the credentials variables in the env example, and always mounts the cli-credentials volume; 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.

Mercury Framework 0.32.0

on GitHub

Minor Changes

  • 1fa3a97: - Every tool plugin declared in mercury.config.ts loads: MERCURY_CLIS is 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.

Mercury Framework 0.31.1

on GitHub

Patch Changes

  • 7926aa2: - Wiki grep ignores case, for the agent, the nightly review, mfw vault grep and the HTTP /wiki/grep route.
    • The agent’s write_file and the nightly review’s write_curated take a path starting with curated/, as listing, reading and grepping give it, instead of writing under curated/curated/.

Mercury Framework 0.31.0

on GitHub

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; --off goes back to the registry.
    • mfw e2e runs 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 in e2e/results/.
    • Test files declare their cases with e2e() from @mercury-fw/cli/e2e.
    • A new app’s Dockerfile copies .packs/ when present, and its .gitignore leaves out .packs/ and e2e/results/.
    • In the REPL, /dump after a confirmation no longer writes the turn before it.

Mercury Framework 0.30.0

on GitHub

Minor Changes

  • 238d4dc: - mfw create runs bun install in the new app, showing its output as it goes (--no-install to skip it).
    • mfw create creates a git repository on main with a first commit, lockfile included (--no-git to 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 create adds origin when given one, in the wizard or with --git-remote; nothing is pushed.
    • The message at the end of mfw create lists what each step did and only the steps left to run.

Mercury Framework 0.29.4

on GitHub

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 --help already 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.

Mercury Framework 0.29.3

on GitHub

Patch Changes

  • bd643ab: - A scaffolded app with Jira marks JIRA_SITE_URL as required in its env example.

Mercury Framework 0.29.2

on GitHub

Patch Changes

  • cc31536: - mfw create ends by saying that, without a global mfw, the app’s commands run as bunx mfw <command>, and lists the global install as an optional step.

Mercury Framework 0.29.1

on GitHub

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=Mercury matches both.
    • The Jira issue-list extractor’s docs use a generic example site.

Mercury Framework 0.29.0

on GitHub

Minor Changes

  • e498dad: mfw is meant to be installed globally (bun add -g @mercury-fw/cli) and used as a plain command. Inside an app, a global mfw at another version hands the command over to the app’s own CLI, so the app’s commands always match its framework. mfw upgrade installs the registry’s latest globally, and mfw create run by a stale mfw says to. The CLI’s messages, the generated app and the docs say mfw instead of bunx mfw.

Mercury Framework 0.28.4

on GitHub

Patch Changes

  • 65e78fe: mfw create (and bun create mercury-agent) checks the registry for a newer @mercury-fw/cli first, 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

on GitHub

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

on GitHub

Patch Changes

  • 0c5bb5b: Dependencies at their latest minor: ai 7.0.126, ai-sdk-ollama 4.4.0, zod 4.6.5, yaml 2.9.1, shell-quote 1.11.0. A new app from mfw create runs Qdrant on its v1 tag, 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

on GitHub

Patch Changes

  • ab52977: An app created with the Google Chat channel trusts protobufjs in trustedDependencies, 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

on GitHub

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-written sed/printf step.

Mercury Framework 0.27.1

on GitHub

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

on GitHub

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; --print prints 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

on GitHub

Minor Changes

  • 8b92abb: - mfw operates an app from inside its folder: start, stop and restart (with --no-cache), logs, repl, shell, all as docker compose calls.
    • mfw vault maintains the wiki vault (list, read, grep, write-curated, write-raw) in a one-off container.
    • mfw memory list and mfw memory read <collection> read the memory on Qdrant, newest first where the collection has a timestamp index.
    • mfw reset memory and mfw reset wiki delete 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: --help at every level, a suggestion for a mistyped command, and every argument checked before anything runs.
    • A scaffolded app lists @mercury-fw/cli among its devDependencies, at the framework’s version, and its README runs it through bunx mfw.
    • A scaffolded app’s @types/bun and typescript use 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 what mfw memory runs.
    • 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

on GitHub

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.yml restarts the app service unless it was stopped.

Mercury Framework 0.25.0

on GitHub

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-agent scaffolds an app, and the CLI command is now mfw (@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.