Skip to content

Releases

v0.8.4

Choose a tag to compare

@github-actions github-actions released this 23 Sep 18:15

Fixed

  • agents: namespaces written by 0.8.2 keep their people after the upgrade.
    Namespaces now list people by e-mail address, but a record 0.8.2 wrote names
    its creator and members by user id — and those entries matched nobody, so
    everyone lost access to their namespaces, with no way to repair it in the
    UI. An entry without an @ is read as a user id again and matches the
    account that signs in with it. Likewise, mindconnect.namespace-admins set
    on an installation whose default namespace already had a record added
    nobody to it and locked everyone out; the named admins are now added to the
    existing record.

What's Changed

  • Namespaces: records from 0.8.2 keep their members and admins after the upgrade by @beisdog in #165

Full Changelog: v0.8.3...v0.8.4

v0.8.3

Choose a tag to compare

@github-actions github-actions released this 23 Sep 18:00

Added

  • agents: a module can add its screen to the admin UI's sidebar. A jar on
    the classpath that serves a page of its own had no way into the navigation —
    the menu was a fixed list, so the page was reachable by URL only. It now
    registers an AdminMenuContribution bean and says which entries to show,
    for an admin of the namespace and for a plain user of it separately, so a
    contribution never offers a viewer a route the server would refuse. An entry
    is a link or a group of links, like Install; groups with one id from several
    modules become one. The entries follow the shipped sections, before the
    Install group.
  • agents: a module on the classpath ships its own agents, skills and workflows. The
    seeds were read from the app's own jar only: initial-data/ in a second jar
    was never scanned, so a module that brings an agent had to install it by
    hand. The loaders — and the Migrations screen — now look through every jar
    on the classpath, so a module's initial-data/agent-definitions/*.json,
    skills/*.md and workflows/*.json are seeded and migrated exactly like the
    bundled ones.
  • agents: a turn that waits for an approval can say so. A tool marked
    needsApproval used to leave the caller waiting for the final answer until
    somebody answered the card elsewhere. AgentRuntime.send(…) returns as soon
    as a call waits, with a TurnResult that is INCOMPLETE and lists the open
    questions; approve(…) and deny(…) continue the same turn and return its
    answer or the next question. Underneath, ChatTurnHandle.outcome() completes
    at the question, and AgentChatService.sendChat, approve and deny are the
    non-blocking forms. Over REST the chat stream ends a waiting stretch with an
    incomplete frame, and POST /api/sessions/{id}/approvals/{callId}/continue
    answers and streams the turn on. The runtime protocol backend ends such a
    response INCOMPLETE(WAITING_FOR_APPROVAL) with an ApprovalRequest item,
    and an ApprovalResponse as the next input continues the turn.
  • agents: the Admin UI can carry your own name, mark and stylesheet.
    mindconnect.branding.* sets the heading in the header, the logo beside it
    and where it leads, the browser tab's title and icon, the look the shell
    opens in, and any number of stylesheets that are linked after the shipped
    ones so their rules win. assets-dir serves a directory next to the running
    app at /branding/**, so an installation brands itself by dropping files in
    rather than by building its own image; the login page follows the same
    settings. Unset, everything looks exactly as before — except the browser tab,
    which now says "Mindconnect Agent Runtime" instead of "Agent Admin".
    switch takes it one step further: named variants with a url-pattern each,
    so one deployment behind two host names wears two brands, decided per request
    rather than per process. style-picker says what the header's theme picker
    may offer — every shipped theme, a named few, or none at all, and with none
    the look is genuinely fixed. See
    Branding the app.
  • agents: the calendar is a port too, and CalDAV comes with it.
    mc-calendar-core has CalendarStore — the calendars, a period of
    appointments, reading one, creating, changing and deleting — and the
    CalendarProvider seam a kind of account plugs into. mc-calendar-caldav
    brings CalDAV, which nearly every mail provider and every self-hosted
    calendar speaks: a card with the CalDAV address, user and password, a Test
    button that signs in and says which calendars it found, and as much
    iCalendar as an appointment needs. mc-agent-tools-calendar brings
    calendar_calendars, calendar_events, calendar_read, calendar_create,
    calendar_update and calendar_delete, which name an account
    provider.key (caldav.web) or ask all — one set of tools whatever kind
    of calendar a user connected, and each change a tool of its own so a binding
    can make it ask for approval.
  • agents: code_execute runs python in an image with the office libraries.
    The default python image is now ghcr.io/mindconnect-ai/code-exec-python:
    Python 3.12 with python-pptx, python-docx, openpyxl, matplotlib and pandas, and
    the mc_office package whose pptx_builder, docx_builder and xlsx_builder
    build a deck, a Word document or a workbook from a small spec. The office
    skills used to carry those generators as text the model had to copy into every
    call — tens of kilobytes that crowded a local model's context before the first
    slide was written. The tool tells the model what the image carries. It is
    published for amd64 and arm64; mindconnect.code-exec.languages=python=python:3.12-slim
    goes back to the plain image.
  • agents: a connection made by signing in is named after the account. After
    "Connect with Microsoft" the card said "Microsoft account", and with two of them
    nobody could tell which was which — nor which mailbox a mail would leave from. The
    connection is now tried out the moment it arrives, and when the source's test says
    whose account it is, the connection takes that name: the address, as a rule. The
    toast after signing in says what the test found. A ConnectionTest can carry the
    account it saw; sources that do not say leave the card's name in place.
  • agents: a connection can be tried out from the Connections page. A
    tool source that declares a ConnectionSpec may offer a ConnectionTester
    (connectionTester() on ToolFactory and MultiToolProvider); the page
    then shows a Test button on each of that provider's rows, runs the test
    right after a connection is added or edited — while the person who typed
    the password is still looking — and writes the verdict into the Status
    column, so a wrong password reads "error — the server refused the account"
    instead of every tool call failing the same way. An OAuth token is renewed
    before the test, as it would be before a tool call. ToolRegistry
    gained connectionTesterOf(provider).
  • agents: a jar on the classpath declares what it brings, and a namespace can switch it off.
    A module that ships tools, agents or a screen was loaded by whatever
    ServiceLoader found, with no record of what it brought and no way to keep
    it out of one namespace. It now carries a manifest,
    META-INF/mindconnect/extension.json — id, vendor, version, and under
    contributes its tools (as name patterns), agents, menu entries, routes,
    decorated ports and more. The host reads every manifest at start and the
    new Extensions screen (Install → Extensions) lists them; an admin
    switches an extension off for the namespace at hand, and its tools leave
    the catalogs and its sidebar entries the menu there. From a brand's own
    namespace the decision can be taken for the whole brand, locked against
    the namespaces' own say or not; mindconnect.extensions.disabled lets the
    operator switch an extension off everywhere. Decisions are stored per
    namespace (system/extensions/ on files, mc_extension_activation in
    Postgres) and per brand (system/extension-brands/,
    mc_extension_brand_activation). Menu entries a manifest declares with a
    label and href are rendered by the host, into a shipped group or a new
    one; the routes it declares are sections of the admin UI — shell, layout
    and a 404 where the extension is off — so a jar's own screen needs no
    filter of its own; and beside every declared tool pattern and provider
    class the screen says whether the classpath actually has it. A worked
    example ships with the admin UI app, switched off: mc-extension-demo, a
    short adventure with the LLM as game master — a dice tool, an agent, a
    decorator around the trace store, a store of its own for scenarios, saved
    games and rolls, and two screens; switch it on under Install → Extensions. Problems — two manifests with one id, an unreadable one, two
    extensions replacing the same bean — and jars that bring providers without
    a manifest are logged and shown; mindconnect.extensions.strict=true
    refuses to start on them instead.
  • agents: the chat's Files dialog opens, edits and deletes files. A file
    opens in place of the tree: text up to 1 MB in an editor with Save, images
    scaled to fit, PDFs in the browser's viewer; Office documents and other
    binaries are offered as a download. Files and folders can be deleted, a folder
    with everything in it, after a confirmation; a directory of the chat itself
    never can, and a link is removed without touching what it points to. Behind
    it are GET …/files/view, POST …/files/save and POST …/files/delete under
    /admin/api/sessions/{id}, for the session's owner only. Workspaces on a
    virtual environment server work the same way; WorkspaceFiles gained
    delete(Path).
  • agents: a folder in the chat's Files dialog downloads as a zip. Every
    folder, each of the chat's directories included, has a download button that
    packs everything below it into one zip, instead of fetching file by file. The
    zip holds only what the dialog would list, so a link out of the directory
    stays out. A folder with more than 10,000 files or more than 1 GB is refused
    (413). Behind it is GET /admin/api/sessions/{id}/files/zip?root=…&path=…,
    for the session's owner only, like the rest of the dialog.
  • agents: A folder's newest thousand messages are kept, and a list or a search answers from there. mc-mail-index holds a window per folder somebody looks at — headers only, in a file per user, account and folder — filled on the first look, compared with the provider's top page when older than two minutes, widened when a page below it is read, and written through by every delete, move and read flag. The folder views and a gathered list read from it; `m...
Read more

Snapshot main · 0.8.3-SNAPSHOT (2026-09-23)

Choose a tag to compare

@github-actions github-actions released this 23 Sep 17:49
4d4a563

Version 0.8.3-SNAPSHOT, built from main at 4d4a563.

Not a release: these assets are replaced in place whenever the snapshot workflow runs for this branch. main publishes to snapshot; any other branch to snapshot-<branch>, with / in the name turned into -. For something that stays put, take a version from Maven Central or a tagged release.

curl -LO https://github.com/mindconnect-ai/mindconnect/releases/download/snapshot/mc-agent-admin-ui-app-0.8.3-SNAPSHOT-exec.jar
java -jar mc-agent-admin-ui-app-0.8.3-SNAPSHOT-exec.jar

The CLI is mc-agent-cli-0.8.3-SNAPSHOT-exec.jar, next to it. Both need a JDK 21. The file names carry the development version, so they change when a release opens the next one — snapshot.txt names the current one, and the release API lists the assets.

v0.8.2

Choose a tag to compare

@github-actions github-actions released this 16 Sep 12:06

Added

  • agents: namespaces — a server now serves any number of them, and a
    user works in one at a time. Everything lives inside a namespace (agents,
    LLM configs, skills, sessions, files, vector stores, workflows, MCP servers,
    tool settings, MCP registrations); only users, API tokens and the namespaces
    themselves are installation-wide. Workflows, MCP servers and vector stores
    follow the namespace of the request like every other store; a new namespace
    starts without any of them (the bundled MCP registrations are seeded into the
    default namespace only). The helper agents a namespace needs — the title
    generator, the summarizers — are created there on first use, on the
    namespace's agent-default (or first) LLM config. A chat's title is now
    generated by a task of its own (agent.title), submitted together with the
    first turn and run in parallel with it from the user's message alone: it
    shows in the task monitor, survives a restart, runs in the turn's namespace,
    and the turn's handle resolves once both are done — so the title is there
    when the answer is. The task monitor — the header badge, its
    dialog and the live updates — shows the tasks of the namespace you are in. In the Admin UI the header shows the current namespace:
    switch from the menu, create a new empty one and fill it from the Registry,
    or open Namespaces & members to see who works where. Your profile lists
    the namespaces you may work in, with Switch to and Invite… on each row;
    the invite dialog takes the other user's user name (someone who never
    signed in is refused). Only the creator of a namespace invites, removes members, renames
    it — and deletes it, with everything in it: the directory under the data
    directory, or every row of it in Postgres and its pgvector tables. Any
    other member can leave. Whoever loses a namespace this way is back in the
    default one. A namespace cannot be called system, ns, api, admin or
    v1. The header switch is a POST. Fixes from a review of the branch: the
    live task stream no longer shows a namespace its viewer was removed from;
    the working-directory root follows the namespace of the call instead of
    the one at start-up; a turn waits at most three hours again; MCP tool catalogs and pooled
    connections are kept per namespace, so editing a server in one namespace
    no longer drops another's connections; a malformed X-Mindconnect-Namespace
    header is a 400. Two users creating a namespace with the same id at the
    same moment get one namespace, not the second record over the first; and
    a store's embedding config is always read in the store's own namespace.
    A call to the REST API or the Responses API without a
    namespace works in the default one — never in the one its user last chose
    in the Admin UI; only the Admin UI follows the session and the remembered
    choice. The choice is
    remembered per user, so it survives a new browser session and a restart. The default namespace
    (mindconnect.namespace, local) is open to every signed-in user, so an
    installation that never creates another behaves as before. API clients name
    the namespace in the address — https://host/ns/acme/api/… and, for
    OpenAI-compatible clients, the base URL https://host/ns/acme — or in an
    X-Mindconnect-Namespace header; the server answers with the namespace it
    worked in under the same header, and with 403 when the caller is not a
    member. Embedders see none of this: the builder still takes one namespace.
    Storage moved: users and API tokens now live under
    <data.base-dir>/system/ (Postgres: without a namespace column) instead of
    inside local/; on an existing install sign in again and re-issue tokens.
    No migration is shipped: a Postgres database from 0.8.0 or 0.8.1 still
    has mc_user and mc_api_token with the namespace in their primary key,
    and the new code cannot write to those tables — every sign-in record,
    namespace switch and token creation fails. Drop both tables before starting
    this version (DROP TABLE mc_user, mc_api_token;); they are recreated
    empty, users reappear on their next sign-in.

  • agents: the model's reasoning is shown in the chat — as a collapsed
    card above the answer, like a tool call, whose header runs while the model
    thinks and reads "thought for 4.2 s" once the first token arrives. Open it
    to read along. A Qwen3, DeepSeek-R1 or gpt-oss model on LM Studio, Ollama,
    vLLM or Groq used to leave the chat sitting on "AI is thinking" with nothing
    to show for it — the OpenAI-compatible gateway now picks the reasoning up
    from the reasoning_content and reasoning fields as well as from inline
    <think> tags in the content (which no longer leak into the answer). The
    card comes back on reload, duration included: the message keeps its
    reasoning in metadata.thinking and thinkingMs, on an answer and on a
    tool-call turn alike. The REST stream carries it as a thinking frame, the Responses API as a reasoning
    item with response.reasoning_summary_text.delta events; the CLI stays
    quiet. Claude's adaptive thinking shows the same way when the config asks
    for it (thinkingDisplay: summarized).

  • agents: reasoning_effort on every OpenAI-compatible LLM config
    (OpenAI, Azure, Groq, xAI, OpenRouter, Ollama, LM Studio, …) — a select in
    the Admin UI form next to the provider's other parameters, or
    "additionalParams": {"reasoning_effort": "high"} in the config JSON. Which
    levels apply depends on the model. Local servers ignore the field; OpenAI
    is only sent it with a reasoning model (gpt-5, o-series), since it rejects
    it on any other.

  • agents: an LLM config can name fallback models. When the provider
    rate-limits it (HTTP 429) or reports itself overloaded (529) and the config's
    own retries are used up, the same request is re-sent through the next config
    in fallbackModels, in order, until one answers — normally a config at
    another provider, whose limit is a different limit. Configure it in the Admin
    UI (Fallback models (on rate limit) in the chat settings: tick chat
    configs and move them into the order they are tried) or as a
    "fallbackModels": ["openai-default", "gemini-default"] list in the config
    JSON. Omitting it keeps the previous behaviour: the rate-limit error reaches
    the caller. A fallback is only taken before anything has streamed, so a
    partial answer is never duplicated.

  • agents: two providers more — XAI (the Grok models) and
    MOONSHOT (the Kimi models). Both speak the OpenAI API, so they arrive
    with everything the others have: endpoint prefilled (https://api.x.ai,
    https://api.moonshot.ai — set the base URL to https://api.moonshot.cn for
    Moonshot's mainland-China endpoint), model list in the form, retry, rate
    limit and fallbacks. The provider dropdown now names the models a vendor is
    known for, so Grok and Kimi are findable under XAI and MOONSHOT. The apps
    register the OpenAI-compatible gateway as the default for every provider
    rather than listing them one by one, so the next one is a single enum
    constant.

  • agents: the Admin UI's LLM-config form reads the model list from the
    provider
    , so a model is picked rather than typed. LM Studio already did
    this through its native API; now every provider that publishes a listing does
    — OpenAI, Groq, Mistral, DeepSeek, Together, OpenRouter, Perplexity,
    Fireworks and Ollama through /v1/models, Anthropic and Gemini through their
    equivalents. Only models fitting the config type are offered — image,
    text-to-speech, moderation and legacy completion models are left out — a
    model the provider no longer lists stays selectable, and a listing that fails
    leaves a text field carrying the reason. OpenRouter's list needs no API key.
    Azure OpenAI keeps free text: its configs name a deployment, not a model.

  • agents: registries — a Registry screen that installs LLM configs,
    agents, workflows and whole packages from a GitHub project. A registry is a
    repository, not a server: an index (registry.json) and the entity files it
    points at, added by its owner/repo (owner/repo@v1.2.0 pins a tag, a
    private one names the environment variable holding its token). Browse it
    under Agents, LLM configs, Workflows and Packages, import — a package
    installs the agent, its sub-agents, the workflow they run and the LLM config
    they share, dependencies first and each entity once, with a report that says
    line by line what was imported, updated, removed, skipped or failed. A
    package's Contents tab lists everything it would install, marks what is
    already here, and lets you untick entries to leave them out; Remove takes
    a package out again, deleting the included entries and keeping the rest. An
    entry that something outside the package still uses — the alias every agent
    runs on, the config behind it — says Used by … and starts unticked.
    Importing keeps what is already here unless overwrite is chosen, an overwrite
    keeps local ids so that sessions and aliases keep working, and an API key
    that came with a registry file is dropped rather than used. An application
    ships registries as files under initial-data/registries/; the admin UI
    comes with mindconnect-ai/mc-registry,
    the default agents, LLM configs and workflows plus an example package. Off
    for an installation that wants no outbound calls
    (mindconnect.registry.enabled=false); format and settings in the docs under
    Registry.

  • agents: skills — know-how written down once and loaded when it is needed,
    instead of carried in a system prompt from the first token on. Only a skill's
    name and description stand in the prompt, a line each; the instructions
    arrive when the model calls the new skill tool for that name, so ten skills
    cost ten lines until one is use...

Read more

Snapshot feature/namespace-supplier · 0.8.2-feature-namespace-supplier-SNAPSHOT (2026-09-15)

Choose a tag to compare

Version 0.8.2-feature-namespace-supplier-SNAPSHOT, built from feature/namespace-supplier at 7cf6e0b.

Not a release: these assets are replaced in place whenever the snapshot workflow runs for this branch. main publishes to snapshot; any other branch to snapshot-<branch>, with / in the name turned into -. For something that stays put, take a version from Maven Central or a tagged release.

curl -LO https://github.com/mindconnect-ai/mindconnect/releases/download/snapshot-feature-namespace-supplier/mc-agent-admin-ui-app-0.8.2-feature-namespace-supplier-SNAPSHOT-exec.jar
java -jar mc-agent-admin-ui-app-0.8.2-feature-namespace-supplier-SNAPSHOT-exec.jar

The CLI is mc-agent-cli-0.8.2-feature-namespace-supplier-SNAPSHOT-exec.jar, next to it. Both need a JDK 21. The file names carry the development version, so they change when a release opens the next one — snapshot.txt names the current one, and the release API lists the assets.

v0.8.1

Choose a tag to compare

@github-actions github-actions released this 12 Sep 00:08

Fixed

  • agents: a file attached to a chat is findable on disk, not just by
    search. The document tools said in their own description that an attached
    file "has no path, use vector_search for it" — it has one since the chat
    keeps its uploads in its own directory, and a model follows the tool's
    description over the prompt, so it never opened the file. The prompt now
    also names the path the tools take (uploads/<name> from the working
    directory) beside the absolute one, glob says that it returns files and
    never directories — * in a directory of sub-directories finds nothing —
    and the bundled coding-assistant gets file_list, which it was missing.
  • agents: vector_search no longer claims to be the only way to an
    attached file. Its description said in capitals that attached files are not
    on the filesystem, which outweighed both the prompt and the file tools; it
    now says what search is good for — a passage in a long document, several
    files at once, a knowledge store — and that a file the prompt names with a
    path is better opened. The bundled default-chat says the same.
  • agents: an image attached to a chat is a file like any other. It was
    only recorded on the session and handed to the model with the next message,
    so file_list did not show it and no tool could open it; it is now copied
    into the chat's uploads directory as well, named in the prompt with its
    path, and removed again when the attachment is removed. Its content is
    still not indexed — an image is not text to search.
  • agents: attaching a file activates file_read and file_list for the
    chat, next to vector_search. An agent that finds its tools by searching
    had the search tool in front of it and the file tools only behind a
    tool_search call, so it searched an attachment it was told to open.

What's Changed

  • An attached file is a file on disk, and the tools say so by @beisdog in #79
  • An attached image is a file on disk too, and the tools that read it are switched on by @beisdog in #80

Full Changelog: v0.8.0...v0.8.1

v0.8.0

Choose a tag to compare

@github-actions github-actions released this 11 Sep 23:09

Added

  • agents: the REST API (/api/**) and the OpenAI Responses API (/v1/**)
    accept a bearer token once authentication is on: a Keycloak access token,
    checked against KC_ISSUER_URI (MC_JWT_AUDIENCES can require an audience),
    or a personal API token. A user creates API tokens on the new profile page
    behind the avatar in the admin UI's header; the secret is shown once, only its
    hash is stored, and a token can be revoked at any time. A request without an
    accepted token gets 401 with a JSON body saying why. Before, the API only
    answered to a browser session with a CSRF token — no program could call it on
    a secured installation; now it takes bearer tokens only, the browser session
    no longer counts there, and Swagger UI's Authorize takes a token for
    Try it out. The agent server (mc-agent-api-app) is secured the
    same way with MC_AUTH_ENABLED=true; it had no authentication at all. Users
    are recorded on sign-in (<data>/<namespace>/system/users/ or the mc_user
    table). New modules: mc-user-core, mc-user, mc-user-pg,
    mc-agent-security.

  • agents: MCP servers as registered tools. An operator registers a server
    under Admin → MCP Servers — a local process, a Docker image or a remote HTTP
    endpoint — tries it out with Test connection, and its tools appear in the
    tool catalog as <prefix>_<tool> (the prefix is fixed once registered, so
    agent definitions keep working). Values in env and headers are used as
    entered; a ${NAME} placeholder is refused until users can set their own
    variables — the environment of the server process is never read.
    process and docker targets start only where
    mindconnect.mcp.allow-process / mindconnect.mcp.allow-docker allow it;
    unset, both are on while sign-in is off and off once it is on, because
    registering asks for a login, not an admin role. A started server inherits
    only PATH, HOME, locale, proxy and container-daemon variables from the
    admin UI. Failed MCP calls report Error: …, so the chat and a workflow's
    tool step see them as failures.
    Discovered tools are cached on disk, so a restart does not start every server
    again. Registrations live in <data>/<namespace>/system/mcp-servers/, one
    JSON file per server; a Gmail registration ships disabled. The modules sit in
    agents/mcp.

  • agents: tool settings in the admin UI: a tool can be switched off or given
    a different description for this installation, without touching any agent
    definition. What an agent definition says about a tool still wins for that
    agent. Stored in <data>/<namespace>/system/tool-settings.json.

  • workflow: run attributes. WorkflowContext carries values the host
    hands one run (getAttribute / setAttribute), passed in with
    WorkflowExecutorService.executeWorkflow(workflow, params, attributes) or
    WorkflowRunService.runWithAttributes(...). Every step of the run sees them,
    parallel for-each blocks and called workflows included; a resumed run starts
    without them.

  • agents: ToolCallScope.rootSessionId, the chat at the top of a sub-agent
    chain, and ToolInvoker.call(tool, arguments, scope), which the tool-call
    workflow step now calls with the scope found on its run.

  • agents: ToolRegistry.releaseSession(sessionId) and
    MultiToolProvider.releaseSession(sessionId): whoever ends a session lets
    the tools go of what they hold for it. The tool test bench calls it after
    every test, so testing an MCP tool no longer leaves a connection — or a
    container — behind per click.

  • common: mc-file-repo, the file counterpart of mc-jdbc for stores that
    keep one JSON document per file. Documents reads without locking and lets
    one writer at a time change a file — across the whole JVM, so two store
    instances on the same directory take turns too — with create,
    createIfAbsent and update(key, change) for changes that must not
    overwrite each other. Writing while already holding a write lock fails at
    once instead of risking a deadlock, a lock that stays taken times out naming
    its holder, and a second process on the same partition (namespace) of a data
    directory is refused — processes serving different namespaces share the
    directory as before. The file session store is the first to use it.

  • common: RecordLog in mc-file-repo — an ordered log of JSON records
    per parent (the messages of a conversation), one file per record, with an
    in-memory index of the file names: append hands out the next key under
    the directory's lock, pages read only their records, and a deleted tail's
    keys are never handed out again.

  • agents: MessageRepository.update(conversation, id, change) changes a
    stored message in one step.

  • common: DocumentTable.insert (writes a new row, false when the key
    exists) and DocumentTable.update(key, change), which reads the row
    FOR UPDATE and writes the change in the same transaction.

  • common: Versions and StaleVersionException in mc-common for such
    checks, and compute(key, change) in Documents and DocumentTable: decide
    on the stored document, or its absence, and write — under one lock.

  • agents: a session has a working directory. AgentSession.workingDir
    is the directory the user is in — the CLI sets it to where it was
    launched (local mode) and changes it with /cd (a relative path taken
    from the session's current directory), the chat's composer
    has a folder button that names it and opens a chooser, POST /api/sessions takes
    workingDir and PUT /api/sessions/{id}/working-dir changes it, an
    embedding calls AgentRuntime.openSession(agent, user, path). The
    file-rooted tools (file_*, glob, bash, the document tools,
    vector_ingest_file) take it as their base directory ahead of a tool's
    baseDir override and the configured default; the system prompt names
    it; a sub-agent called from the session inherits it. glob searches a
    working directory from . and no longer requires path there. The
    directory must exist and, on a server, lie under the new
    mindconnect.tools.working-dir-root (default: mindconnect.tools.base-dir;
    the CLI sets /). Sessions written before read as having none, so
    nothing changes for them.

  • agents: a session can reach additional directories beside its working
    directory (AgentSession.additionalDirs): /add-dir and /dirs in the
    CLI, Additional directories in the chat's chooser, additionalDirs on
    POST /api/sessions and PUT /api/sessions/{id}/working-dir,
    AgentRuntime.addDirectory in an embedding. Relative paths keep meaning
    the working directory; an absolute path into an additional directory is
    allowed, anything outside the session's directories is refused with an
    error naming them. FileRoots in mc-agent-tool-spi is the one sandbox
    check the file, glob and document tools and vector_ingest_file now share
    (ToolCallScope.fileRoots), and each of those tools takes a FileRoots
    beside its Path constructor.

  • agents: the chat's folder button opens a directory chooser that works
    like the operating system's: one path field, the folders inside to step
    into, Use this folder takes what the field says. It browses the server's
    tree under mindconnect.tools.working-dir-root and nothing beyond it; the
    additional directories are listed beneath with a Remove each and an
    Add this folder. GET /api/directories?path= is the same listing over REST,
    over the caller's own tree, and PUT /api/sessions/{id}/working-dir changes
    a session of theirs — like every session endpoint, someone else's answers 404. The
    root may carry {user} (/srv/mindconnect/users/{user}): then every
    user has a root of their own, created on first use, and picks from and
    works in that tree only — the way to run this on a multi-user server.

  • agents: every user has a directory on the server, and every session
    one of its own. mindconnect.users.home (default <data.base-dir>/<namespace>/home/{user})
    is where a user's directories live; a session opened without a working
    directory works in sessions/<id> under it, the files attached to a chat
    are put in that directory's uploads/ — the system prompt names each
    file's path, so file_read, the document tools and bash open them —
    and when the session moves to a project, or a file is attached while it
    works in one, its own directory stays
    reachable as an additional directory. The users' home is also the
    default root a working directory must lie under
    (mindconnect.tools.working-dir-root), so the chat's chooser opens in
    the user's own tree. An embedding sets it with
    AgentRuntimeBuilder.usersHome. Sessions written before keep working as
    they did: none of them gets a directory after the fact. A user id with
    characters other than letters, digits, ., - and _ gets a short hash
    on its directory name, so two such ids never share a home.

  • agents: AttachedFile.path — where the copy of an attached file lies
    on disk, when there is one; ToolCallScope.runWith binds a scope to the
    running thread, and a workflow tool-call step resolves its tools in that
    scope, so an ingestion workflow runs against the session's own files.

  • agents: a bundled coding-assistant agent beside default-chat: it
    works in the project chosen with the chat's folder button — grep and
    glob to orient, file_read with line numbers, file_edit for exact
    changes, file_write for new files, bash (with approval) for the build
    and tests, todo_write for the plan, and the explorer sub-agent for a
    sweep of an unfamiliar codebase. Forty tool rounds per turn, auto-compact
    memory.

  • agents: two file tools for editing code: file_edit replaces one
    exact passage of a file (old_string → new_string; the passage must
    be unique unless replace_all) and returns a unified ...

Read more

v0.7.0

Choose a tag to compare

@github-actions github-actions released this 11 Sep 16:03

Added

  • agents: a container image for the admin UI, published to GHCR on every
    push to main as ghcr.io/mindconnect-ai/mc-agent-admin-ui. It is a layered
    Spring Boot image, so a new version is a ~3 MB application layer on top of an
    unchanged dependency layer instead of a fresh 344 MB — which is what makes
    redeploying onto a small VPS practical. Chromium ships in the image, so the
    Playwright-backed browser tools work without downloading a browser at
    runtime. deploy/ holds a single-host Compose setup to run it behind Caddy,
    which obtains and renews its TLS certificates itself, next to Postgres and
    Keycloak.

Changed

  • agents: every id is a typed record — AgentId, SessionId,
    ConversationId, MessageId, ChatTurnId, ParticipantId,
    ConversationSummaryId, TraceId, LlmConfigId, FileId, AgentToolId
    and friends — instead of a UUID or a bare String. Ports, services,
    repositories, the tool SPI, the builder facade and the CLI client take these
    types, so a findById(AgentId) refuses a SessionId at compile time. Id
    values are lower-case, file-name-safe strings: a readable web-researcher
    is as valid as a UUID. In JSON an id is still its plain string.
    AgentTool no longer points back at its agent: the agentDefinitionId
    field is gone, AgentTool.of(name, …) takes no agent id, and older agent
    files that still carry the field load as before.

  • agents: the namespace is no longer part of any domain object, id, port
    or service, and AuthenticationInfo carries only the user and roles.
    Persistence adapters are bound to one namespace when they are built, and a
    process serves exactly one: the Spring starters read mindconnect.namespace
    (default local; MC_NAMESPACE in the apps), the builder takes
    namespace(...), and adapters take it as their last constructor argument.
    Everything a namespace stores on disk sits in one directory,
    <mindconnect.data.base-dir>/<namespace>/ — data/local/ by default, with
    files, workflows and vector-stores beside the rest — so the properties
    mindconnect.file-store.dir, mindconnect.workflow-admin.dir and
    mindconnect.vector-store.dir are gone, and existing data has to move into
    that directory (or start with an empty one).
    On Postgres every mc_* table carries a namespace column in its primary
    key and the workflow tables a partition_key, and pgvector tables are named
    vs_<namespace>__<store>; there is no migration, so drop the mc_* tables
    of an existing database before starting this version (existing vs_*
    stores are not picked up either). The REST API loses its namespace
    parameters — the query parameter of the agent endpoints and of
    GET /api/sessions, and the field in the bodies of POST /api/agents and
    POST /api/sessions — agent, session and conversation JSON lose their
    namespace field, AgentDefinition.create(...) its namespace argument, and
    the property mindconnect.agent.stateless.namespace is gone.

  • agents: the protocol (mc-agent-protocol) no longer names a tenant:
    Sessions.open(agentName) and Conversations.create() lose their
    namespace argument, and Session and Conversation their namespace
    field. AgentRuntimeBackend needs none either — the runtime it wraps is
    bound to one.

  • agents: Namespace and AuthenticationInfo moved from mc-common
    (package ai.mindconnect.common) into the new dependency-free module
    mc-agent-domain, package ai.mindconnect.agent, together with UserId,
    which AuthenticationInfo.userId() now returns instead of a String.
    Embedders repoint two imports and wrap the user id in UserId.of(...); the
    agents modules pull the new module in transitively.
    mc-common is tenant-free again, which is what the workflow and taskqueue
    areas expect from it; its unused DomainEvent record went with the move.

  • agents: the runtime's packages moved one level down, from
    ai.mindconnect.agent.{domain,port,service,memory,tools,adapter} to
    ai.mindconnect.agent.runtime.{domain,port,service,memory,tools,adapter},
    so that ai.mindconnect.agent itself is left to the shared vocabulary.
    Unchanged: the tool SPI ai.mindconnect.agent.tool, the tool modules
    ai.mindconnect.agent.tools.* and the memory strategies
    ai.mindconnect.agent.memory.strategy. In the same spirit the Postgres
    file store moved from ai.mindconnect.filestore.pg to
    ai.mindconnect.filestore.adapter.pg. Embedders repoint their imports;
    logger names configured for the old packages (for example
    ai.mindconnect.agent.service) need the runtime segment too.

  • agents: the bundled agents default to OpenAI instead of a local LM
    Studio server. agent-default, the LLM config every shipped agent
    references, is now an alias that delegates to openai-default, so a fresh
    install works as soon as OPENAI_API_KEY is set — which is what most
    people have — instead of requiring a 120B model on localhost:1234.
    url-reader follows suit and no longer needs the local gemma-reader
    model. Repointing the alias (in agent-default.json or the Admin UI)
    moves every bundled agent to another provider at once; lm-studio-default
    still carries the previous local settings. Existing installations are not
    changed behind your back: a stored agent-default that differs from the
    seed shows up as a pending migration in the Admin UI, and the CLI logs the
    difference and keeps what it has.

Fixed

  • agents: a store file of the memory vector-store backend loads whatever
    its formatting. It is written one chunk per line, and it was read that way
    too, so a file someone had pretty-printed to read it counted as unreadable —
    and the chat's attachment list then showed no chunks without saying why. The
    file is now read as a sequence of JSON objects, and an unreadable session
    store is logged as a warning.

  • agents: a tool call no longer fails now and then with "tool call was
    interrupted before completion: tool task ended FAILED without a result".
    With the file persistence, a session was saved by truncating and rewriting
    session.json; a tool task reading the session at that moment found an
    empty file and failed before the tool ran. Sessions are now written to a
    temporary file and moved into place in one step.

  • agents: a sub-agent's approval question is answered only through the
    chat that shows its card. The open questions were keyed by the tool call's
    id alone, which the model provider hands out and which is unique only within
    one response: an answer sent through another session reached the waiting
    tool all the same, and two chats that got the same call id shared one entry,
    so one card never appeared. POST /api/sessions/{id}/approvals/{callId} now
    answers 404 when that session has no such open question.

  • agents: the admin UI's Logout button no longer ends with "The backend is
    unreachable". The header's logout is a link, and the SPA router turned every
    such click into a fetch — but logout answers with a redirect to Keycloak on
    another host, which a fetch cannot follow. The session was destroyed all the
    same, so it logged you out and showed an error while doing it. Logout is now
    an ordinary page navigation.

  • agents: a tool provider that cannot say whether it is ready no longer
    takes the catalogue with it. isAvailable() became a per-lookup call when
    provider binding moved off the startup path; a provider throwing there would
    have failed every turn and every catalogue render, for every provider. It is
    now caught and logged, and the bundle counts as unavailable. The
    documentation for tool authors says what bind and isAvailable may expect
    of their surroundings — both run more often, and in more places, than they
    used to.

  • agents: the Spring runtime starts the tool warm-up once its context is
    refreshed rather than while the registry is being built. Binding asks the
    environment for services, and that environment resolves them from the
    application context: doing it from a warm-up thread mid-refresh meant
    queueing behind the very startup that was waiting for the first round, so a
    provider that should have been ready in microseconds could miss its window
    for no reason. SpiToolRegistry.deferred(...) is the registry without the
    warm-up for a host that starts in phases; embedders that build it directly
    are unaffected.

Changed

  • agents: resolving tools no longer holds up the start. Providers that
    reach outside the process — the Gmail tools spawn an MCP server in a
    container — used to bind on the startup thread, so a machine without a
    container runtime paid the full initialization timeout before the
    application served its first request; here that was 47 seconds against 2.
    Every provider now binds on its own virtual thread, the first round gets a
    short grace period so in-process providers are ready when the runtime is,
    and anything slower joins the catalog the moment it is ready. A provider
    that fails for a passing reason is tried again a few times over about a
    minute, which means a container runtime started after the application no
    longer needs a restart to be noticed. SpiToolRegistry is AutoCloseable;
    closing it stops the warm-up.

Fixed

  • agents: an MCP server whose command cannot be run says so at once
    instead of holding the application back. The Gmail tools spawn their server
    through docker; on a machine without it — or with a docker link left behind
    by an uninstalled Docker Desktop — startup used to stall for the full
    initialization timeout and log a dropped reactive error before deciding the
    provider was unavailable. The proxy now checks that the command resolves to
    something executable before spawning, and names it when it does not.

  • agents: a transcription job asked to wait no longer answers with a
    server ...

Read more

v0.6.0

Choose a tag to compare

@github-actions github-actions released this 10 Sep 02:18

Added

  • agents: a bundled openai-embeddings LLM config — OpenAI's
    text-embedding-3-small (override with OPENAI_EMBEDDING_MODEL) with
    OPENAI_API_KEY, for vector stores and file ingestion when no local
    embedding server is running. Choose it in a store template or via
    mindconnect.vector-store.embedding-config; the local embeddings config
    stays the default.

  • agents: transcription over the REST API, as a job.
    POST /api/transcriptions takes a recording as multipart, stores it, queues
    the work on the task queue and answers 202 with the job id plus both URLs
    to follow it: one to poll, one to stream. GET /api/transcriptions/{id}
    reports the status and, once done, the transcript with the detected language
    and the recording's length; ?wait= holds the request until the job ends,
    for a caller who wants the synchronous feel without a loop.
    GET /api/transcriptions/{id}/events is Server-Sent Events from the job's
    channel — queued, running, the transcript as it forms in delta events,
    then completed with the whole text — opening with the state as it is now,
    so a late arrival is told what it missed, and ?afterSeq= resumes a dropped
    connection without a gap. The queue retries a provider hiccup, and model
    names an LLM config whose alias is followed. The recording stays in the file
    store, so a transcript keeps the audio behind it: its id and URL come back
    with the job, and DELETE /api/files/{id} disposes of it when the caller
    says so. Whether the text arrives word by word depends on the model —
    OpenAI's transcribe models stream it, whisper-1 answers in one piece and
    sends one delta — and the events are the same either way, so a client does
    not branch on the model.

  • agents: speech to text — a Whisper model as a normal LLM config, and a
    microphone in the chat. A config's type can now be SPEECH_TO_TEXT next to
    CHAT and EMBEDDING, with the same provider, key, encryption and admin
    form as every other model. LlmTranscription is the port callers use: they
    name a config and get a transcript, so an alias is followed exactly as it is
    for a chat model — point speech-to-text at whichever config should serve
    and swap it later without touching a caller. Behind it,
    OpenAiTranscriptionGateway calls the OpenAI-compatible
    /v1/audio/transcriptions endpoint, so OpenAI, Groq and a local Whisper
    server all work by pointing the base URL at them; language, prompt and
    response format are provider parameters on the config, and the LM Studio
    model picker says it serves no transcription models rather than offering
    chat ones.

    In the chat, the composer's new microphone records in the browser and puts
    the transcript into the input, after anything already typed — nothing is
    sent without the person who spoke it pressing Send. Dictating while a turn
    is still streaming brings the words back as a sticky toast, since there is
    no input to fill at that moment. The admin UI tests a
    speech config the same way: drop an audio file into the test dialog, or
    press Record, speak and press Stop. Both need a microphone and a
    secure context (localhost or HTTPS); where the browser refuses, uploading
    a file still works.

    A new installation is seeded with a speech-to-text config (whisper-1
    behind ${OPENAI_API_KEY}, overridable through SPEECH_TO_TEXT_MODEL and
    SPEECH_TO_TEXT_BASE_URL); an existing one gets it on the next start, since
    seeding checks each entry by id.

Changed

  • agents: AgentLoop.run takes the tokens spent by earlier attempts as a
    further argument, and StreamEvent has a new TurnUsage variant. Code
    embedding mc-agent-runtime-core that calls run directly, or that
    switches exhaustively over StreamEvent, needs the extra argument and the
    extra arm. StreamEvent.Done is unchanged.

Fixed

  • agents: the Responses API reports what a turn cost. usage on
    /v1/responses was always zero: the runtime summed the providers' token
    counts per round but dropped them at the turn boundary, so every response
    claimed nought input and nought output tokens. The counts now ride with the
    turn — across a suspend on a tool call and back — and reach the response
    object and the response.completed frame. They arrive with the new
    StreamEvent.TurnUsage, sent whenever the loop hands back control, so a
    turn that fails or is cancelled reports what it burned instead of zero,
    and the extra model call that answers after the round cap is counted too.
    The same numbers ride the mc-agent-api-rest session stream as a
    turn_usage frame; a client talking to an older server sees none, which
    reads as untracked rather than as a measured zero. Nothing on that API
    carried a turn's cost before — the /memory endpoint's totalTokens
    sizes the next prompt's context window, which is not what a turn spent,
    and the per-call traces that do record it are rendered only in the admin
    UI.

  • agents: the Responses API stream announces and closes its content
    parts. It sent output_item.added, the text deltas and output_item.done,
    but none of content_part.added, output_text.done or content_part.done
    — so a client had nothing telling it that the deltas and the text in the
    finished item are one content part, and one that assumed otherwise rendered
    the answer twice. A tool call now also gets the
    function_call_arguments.done a client may wait for before dispatching, an
    item opens in_progress and empty rather than already complete, and
    sequence_number counts the frames actually written, so it no longer skips
    the numbers of protocol events this layer does not send.

  • agents: a Responses API stream no longer closes a content part it
    never opened. When text reaches the runtime finished rather than as tokens
    — a reviewer's replacement answer — the item was closed without ever being
    announced, and a client resolving those frames against
    response.output[output_index] aborted the stream on the missing entry
    (the official OpenAI SDK raises IndexError). Such an item is now
    announced before it closes.

  • agents: a Responses API stream can no longer start out of order. A
    subscriber was registered while the backlog it had yet to receive was
    handed over outside the lock, so an event emitted in between overtook it: a
    reader could see a later delta before an earlier one, and a terminal event
    that won the race closed the stream on a backlog never written. The backlog
    is now delivered under the same lock that registers the subscriber.

  • agents: two messages appended at the same moment no longer claim the
    same place in a conversation. The sequence number was read and written in
    two steps, so a turn finishing several tool calls together could hand the
    same number to two messages and skip the next, which throws off anything
    cutting by it: delete-from-here, regenerate, and the tool cards a turn is
    folded into. Handing out the number is now the store's own doing, in one
    step: Postgres reserves it in a counter row, the file and in-memory stores
    take a lock per conversation. MessageRepository gains append and loses
    countByConversationId, whose only caller this was.

  • agents: a chat longer than 200 messages no longer hides its newest
    ones. A session's history was loaded as the first 200 messages, so once
    a conversation passed that the chat kept showing its opening and silently
    dropped everything after it, the message just sent included. It looked
    like the message disappearing and the view jumping back a turn, while the
    agent went on answering it, because the runtime had always read the
    conversation entire. The history now comes back entire too, in the chat,
    the admin UI's chat, the attached-files list and regenerate.

  • agents: editing the built-in default vector-store template in the Admin
    UI no longer pretends to save. The built-in template is the
    mindconnect.vector-store.* properties; its edit form now says so and saves
    the values as a copy under a name of your own, and saving or deleting it
    under its own name reports why nothing happened instead of staying silent.

What's Changed

  • A long chat shows its newest messages, and two appends get two numbers by @beisdog in #50
  • An OpenAI embedding config, and a template form that admits what it cannot save by @beisdog in #51
  • Fix token streaming and accounting on the Responses API by @beisdog in #52
  • Speech to text: a Whisper config, and a microphone in the chat by @beisdog in #53

Full Changelog: v0.5.2...v0.6.0

Snapshot fix/tool-warmup-review · 0.6.1-fix-tool-warmup-review-SNAPSHOT (2026-09-10)

Choose a tag to compare

Version 0.6.1-fix-tool-warmup-review-SNAPSHOT, built from fix/tool-warmup-review at 67594cc.

Not a release: these assets are replaced in place whenever the snapshot workflow runs for this branch. main publishes to snapshot; any other branch to snapshot-<branch>, with / in the name turned into -. For something that stays put, take a version from Maven Central or a tagged release.

curl -LO https://github.com/mindconnect-ai/mindconnect/releases/download/snapshot-fix-tool-warmup-review/mc-agent-admin-ui-app-0.6.1-fix-tool-warmup-review-SNAPSHOT-exec.jar
java -jar mc-agent-admin-ui-app-0.6.1-fix-tool-warmup-review-SNAPSHOT-exec.jar

The CLI is mc-agent-cli-0.6.1-fix-tool-warmup-review-SNAPSHOT-exec.jar, next to it. Both need a JDK 21. The file names carry the development version, so they change when a release opens the next one — snapshot.txt names the current one, and the release API lists the assets.