Skip to content

v1.5.0

Latest

Choose a tag to compare

@github-actions github-actions released this 18 Oct 17:22
· 1 commit to main since this release

Added

  • A plugin's set of nested classes (contract ofClasses, 2026-10-16): Studio reads a holder of static final class X extends <element> classes, each with its singleton constant, its public final fields and its one-return
    setup method (project/managed/ManagedClasses). A field reference — Screens.MARKET.beerBuy, or MARKET.close
    inside the holder — reads as the plugin's value and is written back as Screens.MARKET.beerBuy. Fields are added,
    renamed, repointed and removed by binding like any set's constants; a class is added with its constant and an
    @Override setup, renamed with its constant (the class follows when it was named from it, FAVORED_SHOP →
    FavoredShop), and removed only while nothing outside it uses the constant, the class or a field. A bare close
    inside the class is not read as a reference yet: write MARKET.close.

  • Project ▸ Migrate… carries the bot over a plugin change by its author's @Migrate rules. Each row is an old
    element the bot still uses, with its uses (double-click one to go to it) and its rule's state. Write rule
    writes the element's signature and a default body into Migrations, marked to be written, and opens it in the
    editor. Preview lists each use before and after. Apply rewrites every use a written rule covers, as one
    version after a safety one. Remove finished rules removes each rule whose element the bot no longer uses,
    and the file when that empties it. A retired type's values still go through Copy values… (the migration
    window). Migrate compares the bot with the classpath the project loaded before its last change: an upgrade, a
    removal, or a dev build rebuilt under the same version. It also opens from a banner above the canvas when that
    change broke something, from a Review tab row, and from the upgrade's question, whose Migrate first moves the
    version and leaves the code for Migrate.

  • migration write (MCP): the assistant sets a seeded rule's body — one value, or one statement for a void
    rule, with the imports it names — checked to compile, and the seed's @Refactor mark goes. edit_code's values
    could not say self.markers(List.of(marker)), so a rule could be seeded and never written over MCP.

  • The assistant's migration tool is Migrate (MCP). list gives Migrate's rows, each element with its first
    uses and its rule, the rules naming nothing, the finished rules and the retired types' values. seed writes an
    element's rule, which the assistant then writes with edit_code. apply previews each use before and after,
    and with confirm writes them. remove_finished removes the finished rules. copy_values copies a retired
    type's values by the bot's own @Migrate method for the pair, and switch_uses and remove_migrated stay as
    they were. read and set, with their mapping lines, are gone: the rule is the mapping. plugins set_version
    takes rulesFirst, the upgrade's Migrate first: only the pom moves, and the answer counts what broke.

  • One migration engine behind @Migrate rules (migrate/). A bot's rule names the
    old method, constructor, field or whole type by its own signature, and Studio finds that element in the jars the
    bot used to compile against. It then rewrites every use in the project through the rule's body, keeping each
    use's own spelling. A chain such as named("home").marker("a").marker("b") comes out whole. Rules with neither
    or both of value and name, rules that match nothing, and two rules for one element are refused with a reason.
    With no rule, nothing is rewritten. A rewrite that would not compile is left as written and its function is
    marked @Refactor. migrate.Broken lists what the new jars break and whether a rule covers each element.

  • The old API survives a plugin change. Each load records the classpath it loaded, and one that differs keeps
    the last as the classpath before (~/.cache/botmaker/api). A -SNAPSHOT jar is recorded as a copy of the bytes
    loaded, so a dev reload can still read what the bot used to call.

  • One finder of uses (nav/Uses): calls, new, this(…)/super(…), method references, field reads and
    writes, case labels, type names and imports, matched by binding. Signature changes (MethodReferences) and
    migrations both use it.

  • The assistant's costly answers show up. tool-times.tsv in Studio's cache now also records how much text and
    how many images each tool call answered, so sort -k5 -n finds the replies that cost the most. A file written
    before these columns is moved aside to tool-times.old.tsv. At connect, the MCP server names the bot's plugins
    that have a guide and points to plugin_guide. It no longer pastes the guides into its instructions, which
    clients cut short.

  • A plugin's assistant tool may take nested values and one of several actions. Studio serves a nested record
    as an object, a list of records as an array of them, and an action tool as one flat object: action, whose
    description says what each action needs and does, and the actions' fields, each naming the actions that take
    it when not all do. No oneOf, which Gemini reads poorly.

  • Studio's own MCP tools are declared as a plugin's are: each an AssistantTool over a parameter record, in
    assist/FileTools, StudioTools, RunTools and ProjectTools; McpTools only serves them. The hand-written
    schemas and the text()/number()/flag() readers, which turned a missing value into "" without a word, are
    gone: a missing or ill-typed argument is refused, REFUSED: <tool>: 'value' is required. add_file's kind
    and set_setting's key are enums, sent as a JSON enum; add_method/edit_signature's params are typed.
    One wire change: each of apply_edits' edits names its kind as action (was op).
    edit_variable's name and type may be left out, as its description always said. An argument a tool does
    not take is refused, where a tool with none ignored it; an answer loses its trailing blank lines.

  • Studio serves 37 MCP tools where it served 58, and a third fewer characters (23,847, was 31,650 —
    McpBudgetTest's ceiling is now 26,000). Families of verbs became one tool of several actions, action
    picking one:

    • edit_code (insert, set, delete, move, add_argument, edit_variable) replaces insert_block, set_slot,
      delete_block, move_block, add_argument, edit_variable and apply_edits; one edit is a list of one;
    • review (list, mark, unmark, undo) replaces list_review, mark_reviewed, remove_mark, undo_change;
    • versions (list, checkpoint, revert), plugins (list, search, add, set_version, remove), migration (list,
      read, set, switch_uses, remove_migrated) and params (list, set_default, reset) replace their 16 tools.

    A field another action takes is refused, naming the action's own. While a plugin moves, plugins list,
    plugins search and versions list still answer. The descriptions are shorter; how a slot's value is spelt is
    said once, in edit_code's. Studio's answers name the new tools (versions revert, plugins list).

  • A plugin's value writes are undone with ↶. Each PluginValues write is one step of the editor's undo, named,
    and a plugin's History.batch makes several one step under its own label; ↶/↷ put every file it wrote back. Before,
    only a Project History safety version could take one back. A batch takes one safety version, not one per write.
    The history moved out of CodeEditorService into services/EditorHistory; plugin/HostHistory is the contract's
    History over it.

  • The assistant's undo and redo take the editor's last step back or do it again, as ↶/↷ do, and say which
    step and what is next each way. undo_change stays: it puts a review item's function back from the versions.

  • A plugin lists the bot's boolean methods and adds one (PluginValues.booleanMethods,
    createBooleanMethod): public static boolean name() { return false; } in the class it names, or a new one.

  • A tool's answer in several text parts reaches the client as one text, the parts a blank line apart.

  • quit_studio closes Studio as its window's close button does, half a second after it answers, so a client
    can restart it with the bridge's start_studio.

  • /mcp-hello proves to a client that it talks to this Studio before the client sends the token: it signs
    the client's nonce with the token (HMAC-SHA256) and says nothing of the token itself. Every other path still
    asks for the token.

Fixed

  • A fast scroll lagged and showed members being drawn (2026-10-18). Three causes, each fixed in
    EditorCanvas:
    • A scroll's draw ran after the frame that painted the new position, so nearly every frame of a fast scroll
      showed blanks. It now runs before the pulse lays the scene out.
    • Every forced layout called applyCss on the scroll pane. JavaFX restyles every node under a node
      applyCss is called on, clean or not. So each draw restyled every member drawn so far, and every render
      restyled the hidden kept roots too. Now only the new member (MemberPlaceholder.draw) or the new root is
      styled. A switch back went from 11 to 2 ms on the benchmark.
    • Once a render settles (300 ms), the members still blank are drawn about 6 ms per pulse, nearest the
      viewport first, the member at the top kept in place. A fast scroll after that costs about 8 ms a frame
      instead of 70.
  • The file tabs were sometimes hidden (2026-10-18). Since the canvas moved below the tab strip, a canvas
    or viewer asking for more height than the centre column had squeezed the strip to nothing, because a tab
    pane's minimum height is 0. The strip now keeps its height (CenterTabs), and the area below gives way,
    clipped.
  • Each render walked the whole bot again for its parameters and its sets' members (2026-10-18 ...