5839 Commits
Author SHA1 Message Date
38a65f4988 MUL-8425 feat(sidebar): right-click quick actions on sidebar issues (#9749)
* MUL-8425 feat(issues): add a sidebar variant of the issue context menu

Names the issue in a header, shows current status, priority and assignee
beside their entries, and keeps only the triage and navigation actions in
the full menu's order. Adds Copy identifier and, while an agent runs on
the issue, a confirmed Stop run entry.

Co-authored-by: Cursor <[email protected]>
Co-authored-by: multica-agent <[email protected]>

* MUL-8425 feat(sidebar): open the issue menu on right-click in the Work panel

Pinned issues and issues under expanded projects and views open the
sidebar variant; the right-clicked row stays lit while the menu is open.

Co-authored-by: Cursor <[email protected]>
Co-authored-by: multica-agent <[email protected]>

---------

Co-authored-by: Cursor <[email protected]>
Co-authored-by: multica-agent <[email protected]>
2026-10-31 16:37:21 +08:00
4bfa916450 MUL-8431 feat(layout): name the rail's work space Work instead of Issues (#9748)
The space holds Views, Pinned and Projects, and its id is already "work";
only the label said Issues, which made Projects read as part of Issues and
repeated the word three times (rail, panel title, All issues). The Issues
page and the issue noun keep their names. Other locales move from their
issue word to their existing work word (工作, 作業, 작업, Travail).

Co-authored-by: Cursor <[email protected]>
Co-authored-by: multica-agent <[email protected]>
2026-10-31 16:36:20 +08:00
e9ba02f6ff MUL-8424 fix(sidebar): light a clicked issue at once instead of All issues first (#9747)
* MUL-8424 fix(sidebar): light a clicked issue at once instead of All issues first

The Work panel learned the route issue's id only from the issue detail
cache, so until the issue page finished loading it no tree row matched
and All issues lit up for the /issues/... path. Take the id from a UUID
route or from the expanded tree lists (by id or identifier) instead, and
let a clicked tree row keep the highlight over a pin without waiting for
the load.

Co-authored-by: Cursor <[email protected]>
Co-authored-by: multica-agent <[email protected]>

* MUL-8424 test(sidebar): mock the auth store with its callable shape plus getState

Co-authored-by: Cursor <[email protected]>
Co-authored-by: multica-agent <[email protected]>

---------

Co-authored-by: Cursor <[email protected]>
Co-authored-by: multica-agent <[email protected]>
2026-10-31 16:15:04 +08:00
7c186016a5 feat(core): multica 1.0 part 1 (MUL-8317) (#9635)
* feat(core): add app space model for rail navigation

Co-authored-by: Cursor <[email protected]>
Co-authored-by: multica-agent <[email protected]>

* feat(layout): replace left sidebar with app rail and space panels

Co-authored-by: Cursor <[email protected]>
Co-authored-by: multica-agent <[email protected]>

* test(e2e): drive navigation through the app rail

Co-authored-by: Cursor <[email protected]>
Co-authored-by: multica-agent <[email protected]>

* feat(layout): finish the rail and panel shell against the mock

- The sidebar toggle always works: in Inbox, Chat and Settings it collapses
  the page's own list column instead of being disabled.
- Move create workspace from the rail into the account menu, and complete
  the menu: profile, theme and language submenus, shortcuts, docs, change
  log, Discord, desktop download, log out.
- Add an explicit New issue button to the Issues and My issues headers.
- Autopilot panel gets a create button and template shortcuts; Team panel
  gets an invite-member button for owners and admins.

Co-authored-by: Cursor <[email protected]>
Co-authored-by: multica-agent <[email protected]>

* feat(desktop): draw tabs as pills on the title bar

The active tab no longer merges into the content card: with the space panel
inside the card, the merged tab landed over whatever column happened to sit
below it. Every tab is now a pill on the title bar; the active one uses the
card's own surface, inactive ones a lighter tint.

Co-authored-by: Cursor <[email protected]>
Co-authored-by: multica-agent <[email protected]>

* feat(layout): give list columns the space panel's width

Inbox and Chat defaulted to 260px with their own saved widths, Settings was a
fixed 240px, and the space panel used the sidebar width. They now share one
ListColumn sized by --sidebar-width with the panel's resize edge, so the
divider stays in place across spaces and one drag resizes them all.

Co-authored-by: Cursor <[email protected]>
Co-authored-by: multica-agent <[email protected]>

* feat(layout): return each rail space to where you left it

Rail items always linked to the space's home, so switching spaces dropped
the project, squad, chat session or settings tab you had open. The shell
now remembers the last page (path and query) per space and workspace, and
the rail links there; the current space's item still leads home, so a second
click resets it.

Co-authored-by: Cursor <[email protected]>
Co-authored-by: multica-agent <[email protected]>

* feat(layout): animate the list column when the sidebar toggles

Inbox, Chat and Settings unmounted their list column on toggle, so it
snapped away while the space panel slid. ListColumn now stays mounted and
eases its width like the panel (200ms, off under reduced motion and while
dragging), which also keeps the list's scroll position across a collapse.

Co-authored-by: Cursor <[email protected]>
Co-authored-by: multica-agent <[email protected]>

* fix(layout): one title band for every left column

The space panels used PanelHeader (title-sm, no rule), Inbox and Chat used
the page header (body size, bottom border) and Settings its own 64px title.
PanelHeader now takes a heading level, a leading slot and a meta slot, and
Inbox, Chat and Settings render their list title with it.

Co-authored-by: Cursor <[email protected]>
Co-authored-by: multica-agent <[email protected]>

* fix(layout): collapse the whole sidebar with one toggle across spaces

Co-authored-by: Cursor <[email protected]>
Co-authored-by: multica-agent <[email protected]>

* feat(layout): switch workspaces from one rail tile

Co-authored-by: Cursor <[email protected]>
Co-authored-by: multica-agent <[email protected]>

* feat(layout): preview Inbox, Chat and Settings lists on rail hover

Co-authored-by: Cursor <[email protected]>
Co-authored-by: multica-agent <[email protected]>

* fix(inbox): keep the list column's own loading skeleton

Co-authored-by: Cursor <[email protected]>
Co-authored-by: multica-agent <[email protected]>

* fix(desktop): lay out the shell for the page on screen during rail switches (MUL-8317)

The tab session URL moves before the router commits the next page (it renders
in a transition), so for a few frames the shell already showed the incoming
space's layout around the outgoing page: switching into Inbox or Chat dropped
the Work panel and the old page's header icon jumped to the top-left.

The router now publishes its committed pathname from a layout effect and the
shell's panel and list columns follow it; space memory keeps following the
navigation URL.

Co-authored-by: Cursor <[email protected]>
Co-authored-by: multica-agent <[email protected]>

* feat(views): flatten the settings nav into one level of section headings (MUL-8317)

Scope header rows, sub-group headings and dividers stacked three levels into
the 240px list column. Every section is now a peer with the space panels'
section label and spacing; each page already names its scope in a badge.

Co-authored-by: Cursor <[email protected]>
Co-authored-by: multica-agent <[email protected]>

* feat(views): expand projects inline in the work panel (MUL-8317)

Project rows in the Issues panel get a chevron that lists the project's open issues in place, so the sidebar can move between issues without opening the project page. The issue on screen reveals its project and stays lit; expansion persists per workspace. A dedicated open-issues cache follows realtime title patches and refetches only when status or project membership moves.

Co-authored-by: Cursor <[email protected]>
Co-authored-by: multica-agent <[email protected]>

* feat(views): sidebar tree settings, load more, expandable views and project order (MUL-8317)

- Expanded projects and views list issues from the server (updated date,
  finished statuses hidden) with Load more / Show less.
- One shared issue display popover on hovered expanded projects; a project
  display popover on the Projects header (ordering, statuses, only mine,
  counts); drag to reorder; collapsible section; no project cap.
- Issue rows shimmer and show agent avatars while an agent works or waits.
- Settings, order and expansion persist per workspace.

Co-authored-by: Cursor <[email protected]>
Co-authored-by: multica-agent <[email protected]>

* fix(views): show sidebar status filters as checkboxes (MUL-8317)

Co-authored-by: Cursor <[email protected]>
Co-authored-by: multica-agent <[email protected]>

* fix(ui): auto-collapse the nav only below lg (MUL-8317)

The rail now stays on screen beside the space panel, so laptop-sized
windows (1024-1279px, just under the 1280 desktop default) have room for
the panel; they no longer open Issues with it collapsed.

Co-authored-by: Cursor <[email protected]>
Co-authored-by: multica-agent <[email protected]>

* feat(views): one Projects display popover, higher-contrast filter chips (MUL-8317)

Issue settings are shared by every project, so they move from each expanded
row into the Projects header popover beside the project settings. Status
filters go back to chips: a solid outline and tint when on, dashed and
faded when off.

Co-authored-by: Cursor <[email protected]>
Co-authored-by: multica-agent <[email protected]>

* feat(server): list views across scopes and store sidebar view order

Co-authored-by: Cursor <[email protected]>
Co-authored-by: multica-agent <[email protected]>

* feat(views): starred views sidebar, unified view header and All views page

- Sidebar Views lists only starred views (built-in and saved, user-ordered)
  with an All views row; order lives in a per-user sidebar preference.
- Issues, My issues and project pages share one header: view name menu,
  folding filter chips (+N), assignee-type / relation chip, Reset and Save.
- Saved-view filters are fully editable; new views are created directly
  and renamed inline instead of through the save dialog.
- New All views directory with tabs, search and star toggles.
- Project page drops the breadcrumb and hides the redundant project chip.
- Remove the view bar, save/manage dialogs and the old headers.

Co-authored-by: Cursor <[email protected]>
Co-authored-by: multica-agent <[email protected]>

* feat(inbox): switch between inbox and archive with header tabs

Replace the Archived row that trailed the notification list (and the back
row inside the archive) with Inbox | Archived tabs pinned under the list
header. The unread count moves onto the Inbox tab, and an empty archive no
longer says the inbox is empty.

Co-authored-by: Cursor <[email protected]>
Co-authored-by: multica-agent <[email protected]>

* Revert "feat(inbox): switch between inbox and archive with header tabs"

This reverts commit e8a8e1bcea1bf5b45c4e5830d654523a0b77daff.

Co-authored-by: multica-agent <[email protected]>

* fix(inbox): drop the count beside the no-agents-working filter

The count widened the whole filter menu for a toggle whose result is
visible in the list as soon as it is applied.

Co-authored-by: Cursor <[email protected]>
Co-authored-by: multica-agent <[email protected]>

* feat(inbox): switch to the archive from the page title

The Archived entry trailed the notification list and scrolled away with
it. The list title is now a menu (Inbox / Archived), so the switch stays
in reach without adding a row, and the archive's back row is gone. An
empty archive no longer says the inbox is empty.

Co-authored-by: Cursor <[email protected]>
Co-authored-by: multica-agent <[email protected]>

* fix(sidebar): keep a pinned issue lit after its URL turns canonical

The issue route replaces /issues/<uuid> with /issues/MUL-123, but a pin
only matched its own UUID href, so clicking a pinned issue lit the same
issue under its project instead. Issue pins now match the open issue by
id, a lit pin no longer auto-expands its project, and pinned issues show
the running agent the same way tree rows do.

Co-authored-by: Cursor <[email protected]>
Co-authored-by: multica-agent <[email protected]>

* fix(sidebar): line pinned agent avatars up with the tree rows

The avatar sat inside the gap reserved for the unpin button, so pinned
avatars landed left of the project tree's and the label lost room. A
pinned issue with agents now puts the avatar in the row's right edge and
hands that slot to the unpin button on hover.

Co-authored-by: Cursor <[email protected]>
Co-authored-by: multica-agent <[email protected]>

* fix(sidebar): raise the contrast of the working-agent shimmer

ShimmerText now reads --shimmer-text-base, --shimmer-text-highlight and --shimmer-text-spread, defaulting to the previous values. Sidebar issue rows (tree and Pinned) rest on faint-foreground and use a 22% highlight band so the sweep is visible at sidebar size; chat, the issue page and other shimmer hosts are unchanged.

Co-authored-by: Cursor <[email protected]>
Co-authored-by: multica-agent <[email protected]>

* fix(issues): keep the view header from overlapping on narrow widths

The view-name button kept the Button base shrink-0, so once the h1 hit its min width the name painted over the add-filter trigger (visible on a 390px web viewport). The button now shrinks and truncates, the add-filter label becomes screen-reader-only in a narrow header, and FoldingFilterChips re-measures its reserved width when its leading or trailing controls resize instead of only when the chips change.

Co-authored-by: Cursor <[email protected]>
Co-authored-by: multica-agent <[email protected]>

* feat(issues): one Filter dropdown and a header that adapts to its width

The view header no longer lays filters out as chips with a +N overflow. A single Filter button carries the active count and opens the filter menu, which now lists the dimensions already filtering first, each showing its values, with the scope axis (assignee type, relation to me) at the top. The split is taken when the menu opens so a row never moves mid-edit.

The header measures itself: when the title side runs out of room it first folds the display controls to icons, then the agents chip and New issue, before the title truncates, and only releases a step once the row is wider than the width that forced it. This fixes the overlap in saved views with table controls and after toggling agents working.

FoldingFilterChips and single-row-fit had no other users and are removed.

Co-authored-by: Cursor <[email protected]>
Co-authored-by: multica-agent <[email protected]>

* fix(sidebar): keep the highlight on the row that was clicked

An issue that is both pinned and listed in the tree always lit its pin, so clicking it under a project moved the highlight away from the row just clicked. The work panel now remembers which section a click on an issue link came from: a click in the tree keeps the tree row lit and the pin unlit, a click on the pin keeps the pin. Arriving any other way (search, a link, a shortcut) still lights the pin.

Co-authored-by: Cursor <[email protected]>
Co-authored-by: multica-agent <[email protected]>

* feat(sidebar): let Views and Pinned collapse like Projects

Co-authored-by: Cursor <[email protected]>
Co-authored-by: multica-agent <[email protected]>

* fix(sidebar): keep a dragged row at its own height while reordering (MUL-8354) (#9662)

Co-authored-by: Cursor <[email protected]>
Co-authored-by: multica-agent <[email protected]>

* feat(sidebar): create an issue in a project from its row

Co-authored-by: Cursor <[email protected]>
Co-authored-by: multica-agent <[email protected]>

* feat(issues): put the workflow beside the title and set the panel toggle apart

The project workflow now sits on the left with the title and Filter, in
the same borderless style. New issue matches Display's height, and a
divider separates the right-sidebar toggle from the header actions.

Co-authored-by: Cursor <[email protected]>
Co-authored-by: multica-agent <[email protected]>

* feat(issues): fold the layout switcher into the Display panel

One Display button whose icon follows the layout. The panel opens with
the layouts as a segmented row, then grouping, ordering and sub-issues,
then the current layout's own options. The table's grouping and column
pickers move in from the header.

Co-authored-by: Cursor <[email protected]>
Co-authored-by: multica-agent <[email protected]>

* fix(issues): keep hovered Display chips from looking selected

Light mode's muted and accent are the same grey, so a hovered chip
matched a pressed one. Hover now only darkens the text and border, and a
pressed chip fills with the stronger input tone.

Co-authored-by: Cursor <[email protected]>
Co-authored-by: multica-agent <[email protected]>

* feat(sidebar): give the Projects settings the Display panel's controls

Both panels now share one chip and one sort-direction button. The
sidebar popover drops its dividers and dashed off-chips, takes the same
section headings, and its selects match the Display panel's width.

Co-authored-by: Cursor <[email protected]>
Co-authored-by: multica-agent <[email protected]>

* feat(issues): quiet the issue view header down to its title

The header carried six pieces of text, half of which said nothing: the
project's workflow name, "0 agents working" and "Display".

- Workflow moves into the project title menu as a submenu; the properties
  panel keeps its row.
- Filter becomes an icon beside the title, followed by Reset / Save, so the
  count sits next to the actions it causes. Its tooltip names the active
  filter fields.
- Display and New issue become ghost icon buttons named by tooltips, with
  New issue showing its C shortcut.
- The agents chip shows only while someone is working or its filter is on,
  as avatars and a number.

With every control already an icon, the crowding steps are gone.

MUL-8361

Co-authored-by: Cursor <[email protected]>
Co-authored-by: multica-agent <[email protected]>

* feat(issues): is / is not per filter, Relation to me, and a hover summary (MUL-8353) (#9663)

* feat(issues): is / is not per filter, a Relation to me filter, and a hover summary (MUL-8353)

Co-authored-by: Cursor <[email protected]>
Co-authored-by: multica-agent <[email protected]>

* fix(issues): match the Relation filter's Chinese labels to My issues (MUL-8353)

Co-authored-by: Cursor <[email protected]>
Co-authored-by: multica-agent <[email protected]>

* feat(views): advanced filter mode, menu search, and due date filter

Advanced mode turns the simple filters into a rule tree (match all/any,
is/is not per condition, one level of groups) edited in a panel behind
the same Filter trigger. Switching back is offered when the tree is
something simple mode can say.

The filter menu gets a search box that matches fields and values; a
value hit reads as the condition it adds. Due date joins as a dimension
with relative buckets (overdue, today, this week, next 7 days, none).

The issue table query accepts filters.advanced, filters.due_dates and
filters.today, compiled to SQL with the same IS NOT TRUE negation.

Co-authored-by: Cursor <[email protected]>
Co-authored-by: multica-agent <[email protected]>

* test(e2e): menu search, due date and advanced filter

Co-authored-by: Cursor <[email protected]>
Co-authored-by: multica-agent <[email protected]>
Co-authored-by: Cursor <[email protected]>

* test(e2e): let menus settle before filter screenshots

Co-authored-by: Cursor <[email protected]>
Co-authored-by: multica-agent <[email protected]>

* fix(views): advanced filter keeps status columns, property filters and negated totals

- Columns follow a status the rule tree requires, and a status it names
  un-hides its column, so switching to advanced no longer drops rows.
- Status branch totals drop the excluded keys for "is not" instead of
  falling back to the loaded count.
- The advanced panel shows property filters as editable rows with an
  "Add property filter" entry, and Clear all clears them too.

Co-authored-by: Cursor <[email protected]>
Co-authored-by: multica-agent <[email protected]>

* feat(views): make custom properties ordinary advanced filter conditions

Properties are fields in the advanced tree: they sit in the field picker
after the built-in fields, can live in groups and follow all/any, and
edit their values with the property's own options. The server compiles a
property:<id> condition with the existing property predicate. Entering
advanced mode moves property filters into the tree, so the separate
property section and its Add property filter button are gone.

Co-authored-by: Cursor <[email protected]>
Co-authored-by: multica-agent <[email protected]>

---------

Co-authored-by: Cursor <[email protected]>
Co-authored-by: multica-agent <[email protected]>

* feat(views): typed filter operators and direct condition groups

- Add condition group inserts an empty group and opens its own add menu,
  instead of asking for a field first. The group's header carries its
  match ("Match any of these"); rows only show the joining word.
- A row's menu can turn it into a group, and a tree with groups reads
  back as one sentence under the rows.
- Operators follow the field's type: is / is not, includes any / none of
  for labels and multi-value properties, contains for text, comparisons
  for numbers, before / after for dates, and is empty / is not empty for
  anything that can be unset. Text, number and date values are typed in
  the row. Every operator is a reading of the stored negated + values, so
  saved views and the server query are unchanged.
- Actor value headings sit in a menu group, which Base UI requires.

Co-authored-by: Cursor <[email protected]>
Co-authored-by: multica-agent <[email protected]>

* feat(layout): New issue and search at the far end of the desktop title bar

New issue lived only as a pen in the Issues panel header, so Inbox, Team,
Chat and Auto had no way to start an issue but the C shortcut. Search sat
in the rail between the workspace tile and the spaces.

- Desktop: New issue and search are icon buttons at the right end of the
  title bar, styled like Back/Forward, with search last. The tabs give way
  to them. The rail drops search.
- Web has no title bar, so its rail keeps both under the workspace tile,
  New issue first.
- The button does what C does: a project page files the issue into that
  project. Both now share one helper.
- The Issues panel header loses its pen; the page header keeps New issue.

MUL-8359

Co-authored-by: Cursor <[email protected]>
Co-authored-by: multica-agent <[email protected]>

* fix(issues): remove horizontal scrollbar from the filter menu

Co-authored-by: Cursor <[email protected]>
Co-authored-by: multica-agent <[email protected]>

* feat(sidebar): every view expands in the sidebar

Saved views preview through the table query, which expresses every view filter (is not, relation, due date, rule trees, my-any). Built-in views expand too, scoped like their page and listed with the sidebar issue settings.

Co-authored-by: Cursor <[email protected]>
Co-authored-by: multica-agent <[email protected]>

* feat(sidebar): group expanded rows by spacing, folder icon for projects

Issues under an expanded row drop to 26px rows with no gap and a 12px status
icon, an open group ends with 8px of space, and the expanded row's name goes
full-strength. Sections sit 20px apart. Sidebar projects show a folder icon
instead of their emoji.

Co-authored-by: Cursor <[email protected]>
Co-authored-by: multica-agent <[email protected]>

* fix(sidebar): expandable rows give their label the full width

The hidden chevron, an empty spacer and their gaps reserved about 90px at the
end of every expandable row, so names truncated with space to spare. The
chevron now takes the icon's place on hover.

Co-authored-by: Cursor <[email protected]>
Co-authored-by: multica-agent <[email protected]>

* fix(sidebar): tree row chevron swaps back to the icon immediately

Drop the opacity fades, and show the chevron for keyboard focus only, so a
clicked row no longer keeps it until focus moves.

Co-authored-by: Cursor <[email protected]>
Co-authored-by: multica-agent <[email protected]>

* fix(issues): drop the hint beside Switch to advanced filter

The grey text sat where the menu shows current values, so it read as a
setting rather than an explanation.

Co-authored-by: Cursor <[email protected]>
Co-authored-by: multica-agent <[email protected]>

* fix(layout): tighten the gap under panel titles, keep selected rows at regular weight

The first section kept the 20px that separates sections, so it sat far
below the panel title. Selected and expanded rows, desktop tabs, the
Settings nav, agent tabs and the skills file tree no longer switch to
medium weight: the wider glyphs made the label grow when it was clicked.
Selection reads from the background and text colour.

Co-authored-by: Cursor <[email protected]>
Co-authored-by: multica-agent <[email protected]>

* fix(sidebar): keep stars while loading, leave view state to in-place clicks, page past 100

- Star, unstar and reorder write the stored order, not the resolved items,
  and wait until that order is known, so views still loading are kept.
- Sidebar rows switch the active view only when the click navigates this
  page; a new tab takes its target from the URL.
- Sidebar issue lists walk 100-row pages so Load more keeps growing.
- Drop a trailing blank line in advanced-filter-panel.

Co-authored-by: Cursor <[email protected]>
Co-authored-by: multica-agent <[email protected]>

* fix(views): open the clicked view in desktop tabs, prepare view state only on in-place navigation

- Desktop tabs carry their own ?view=: list pages adopt it on mount and on
  back/forward, write view changes back with replace, and the active-view
  map is swapped with the active tab. A saved view is part of tab identity,
  so cmd+clicking view B from a tab on view A opens B in a new tab.
- AppLink and useRowLink call onNavigateInPlace right before they push, so
  sidebar and All views rows prepare view state exactly when the page
  navigates in place (desktop shift-click included, web shift-click not).
- Rail items and the workspace tile use named radius tokens.

Co-authored-by: Cursor <[email protected]>
Co-authored-by: multica-agent <[email protected]>

* feat(views): one page system for lists and objects

Every page now follows the Issues page: one 48px header, nothing stacked
under it.

- Lists entered from the sidebar (agents, squads, skills, runtimes,
  projects, views, autopilots, issue wakeups) title themselves with an
  icon and a scope menu, and keep only icon tools: filter with its count
  and Reset, display, and "+". Counts left the header; list search is
  removed.
- Objects (agent, squad, skill, runtime, autopilot, member) open under a
  breadcrumb with at most one solid action and a ⋯ menu. Their tabs became
  body sections with actions on the section titles, in a 760px column,
  with a 300px properties panel the header toggles.
- Analytics keeps its tabs, now in the header; settings, chat and the
  create-agent flow share the same header.
- Shared CollectionPage* and ObjectPage* primitives in views/layout.

Co-authored-by: Cursor <[email protected]>
Co-authored-by: multica-agent <[email protected]>

* feat(views): one CollectionList for every list page

Replace the per-page tables and ListGrid with a shared CollectionList in
packages/views/layout: single-line 36px rows with inline grey
descriptions, a shared column/grid template, header sort, row selection
with shift-range, keyboard navigation (arrows, J/K, Enter, X, Cmd+A,
Esc), right-click and kebab row menus, load-more, and a shared batch bar.

- createCollectionViewStore factory in core; display menus are generated
  from the column definitions.
- Agents, Squads, Skills, Projects, Autopilots, Runtimes (page and
  RuntimeList), Views and Wakeups migrate to it; Wakeups pages with
  load-more instead of prev/next.
- The Issues batch toolbar uses the shared CollectionBatchBar.
- Agents hides owner and access by default so the default columns fit at
  1280px; persisted v1 defaults migrate, custom choices are kept.
- Remove ListGrid and the now-unused per-page locale keys.

Co-authored-by: Cursor <[email protected]>
Co-authored-by: multica-agent <[email protected]>

* fix(layout): smooth the nav panel toggle

- Ease the panel and list columns with a soft-start curve (220ms ease)
  instead of ease-out, whose first frame jumped a quarter of the width
  and, when that frame dropped, nearly half of it.
- End-align the panel content and clip it while it grows, so it slides
  in with the edge instead of spilling under the page; the resize handle
  moves to a zero-width sibling so the clip does not cut it.
- The floating chat stays mounted while closed and re-rendered on every
  frame of the toggle as its container resized; it now re-renders only
  when the clamped window size changes.

Co-authored-by: Cursor <[email protected]>
Co-authored-by: multica-agent <[email protected]>

* fix(issues): give My agents' work its own URL

My agents' work shared /my-issues with My issues and was told apart only by
a persisted global scope, so it could not open in its own tab, and switching
one tab flipped every other. It now lives at /my-issues?scope=agents: the
page, header and sidebar read the scope from the URL, desktop tab dedupe
keys on it, and the tab gets its own title and icon.

Co-authored-by: Cursor <[email protected]>
Co-authored-by: multica-agent <[email protected]>

* fix(views): preserve My agents' work in copied links

Co-authored-by: multica-agent <[email protected]>

---------

Co-authored-by: Cursor <[email protected]>
Co-authored-by: multica-agent <[email protected]>
2026-10-31 15:26:51 +08:00
22a05d832b MUL-8423 chore(ui-lab): remove the UI Lab design workbench (#9746)
Co-authored-by: Cursor <[email protected]>
Co-authored-by: multica-agent <[email protected]>
2026-10-31 14:10:13 +08:00
893c6b9a16 MUL-8422 feat(attachments): page the viewer from a pager under the stage (#9745)
Prev / next lived in 64px side gutters with the position counter in the
top bar: one control split across three places, its two buttons a window
width apart, and a near miss in the gutter closed the viewer because the
gutter is stage padding.

The counter now sits between the buttons in a 48px strip under the stage,
outside the backdrop. The top bar only describes the current file, the
stage gutters drop back to 16px, and the buttons show their arrow-key
shortcuts.

Co-authored-by: Cursor <[email protected]>
Co-authored-by: multica-agent <[email protected]>
2026-10-31 13:26:20 +08:00
e8ae72c9df MUL-8418: fix(usage): correct Cursor variant and OpenAI GPT-5.6 pricing (#9739)
* MUL-8418: fix(usage): price Cursor -fast / effort variants consistently

Add vendor-published Fast rates Cursor's sheet omits (Opus 5.5/5, GPT-5.5,
GPT-5.3 Codex, GPT-5.2), price gpt-5.1 and Cursor's deepseek-v41 spelling,
bill Gemini 3.5/3.6 Flash at Cursor's sheet, and route provider=cursor rows
in the llm_cost_usd metric through a Cursor-aware resolver that mirrors the
frontend so Fast variants no longer borrow the standard rate.

Co-authored-by: Cursor <[email protected]>
Co-authored-by: multica-agent <[email protected]>

* MUL-8418: fix(usage): update GPT-5.6 rates and pre-5.6 OpenAI cache writes

GPT-5.6 Sol / Terra / Luna move to OpenAI's current standard rates
(4/20, 2/12, 0.20/1.20) on both the dashboard and the llm_cost_usd metric.
Backend pre-5.6 OpenAI rows billed cache writes at the cache-read rate;
those tokens are ordinary input, matching the frontend.

Co-authored-by: Cursor <[email protected]>
Co-authored-by: multica-agent <[email protected]>

* MUL-8418: fix(usage): price GPT-5.6 Sol at list rate, not promo

Co-authored-by: Cursor <[email protected]>
Co-authored-by: multica-agent <[email protected]>

---------

Co-authored-by: Cursor <[email protected]>
Co-authored-by: multica-agent <[email protected]>
2026-10-31 04:14:52 +08:00
249fdf2d84 MUL-8417 fix(chat): open the floating chat with its messages already showing (#9737)
Cmd+J faded the floating window in on the keypress, but closing it
unmounts its message list (MUL-8030), and a freshly mounted list stays
invisible until it has landed on the newest message and then held still
for a 120ms settle window (MUL-6879). Every open therefore showed a
header and composer around an empty middle for ~175-200ms before the
messages appeared.

- A conversation already revealed in this page session skips the settle
  window and reveals as soon as it lands. Settling waits out cold
  content (fonts, image decodes, diagram heights); a conversation the
  reader has already seen has none left.
- The floating window holds its fade-in until its message area reports
  ready (list revealed, skeleton, or empty state), bounded by a 300ms
  deadline so a slow list never makes the toggle look dead.

Measured in Chromium against the production build, Cmd+J to frame:
warm reopen went from list at ~220ms with ~175ms of visible empty panel
to list at ~85ms and zero empty frames, idle or with a task streaming.

Co-authored-by: multica-agent <[email protected]>
Co-authored-by: Cursor <[email protected]>
2026-10-31 03:28:02 +08:00
ac131c55cb MUL-8416 fix(comments): keep thread owner when root folds into a queued task (#9735)
A member reply merged into a still-queued task moves trigger_comment_id to
the reply and leaves the thread root only in coalesced_comment_ids.
ListConversationRootOwners matched the root against trigger_comment_id alone,
so the thread lost its owner and later un-mentioned replies under the root
were silently dropped. Treat a root in coalesced_comment_ids as owned, the
same coverage rule taskCoversReplyParent and CancelAgentTasksByTriggerComment
already use.

Co-authored-by: Cursor <[email protected]>
Co-authored-by: multica-agent <[email protected]>
2026-10-31 02:40:59 +08:00
53c4bbe5cd MUL-8395 fix(lark): distinguish bound bots from verified message reception (#9709)
* fix(lark): verify message reception separately from binding

* fix(lark): preserve verification evidence during rollout

* fix(lark): remove stray space in Chinese verification hint

Co-authored-by: multica-agent <[email protected]>

---------

Co-authored-by: Multica Eve <[email protected]>
Co-authored-by: multica-agent <[email protected]>
2026-10-31 02:05:37 +08:00
3191de1930 MUL-8385 feat(wecom): show the run's steps and reasoning in the bubble while it works (#9696)
* feat(wecom): show the run's steps and reasoning in the bubble while it works

The bubble used to open, spin for the whole run and close with the answer.
It now fills in while the run is going: each tool call as a sentence in the
reader's language, the agent's own reasoning under it, and how long it has
been. Only the bot's principal sees any of it, and only in their own 1:1 —
a group or anyone else gets the plain spinner, decided again for every round.

- Steps come from task:message (the transcript), plus the daemon's first
  task:progress milestone, which is said as "starting the agent" in the
  bubble's language rather than the daemon's English. Only runs asked over
  WeCom (a WeCom channel_task_delivery row) show steps.
- Progress reaches the replica that holds the round, whichever replica
  served the daemon's request. The bus subscriber only classifies the event
  and queues it on a per-run lane, so the daemon's request does no database
  read and no socket write. The holder reads the run's new transcript rows
  past a per-run watermark (ListTaskProgressSince), redacts, throttles and
  writes the frame. A replica without the round publishes a progress relay
  frame that carries ids and a seq, no text; it takes no claim and never
  enters the dispatcher's queues, so it cannot delay or crowd out an answer.
  Duplicates, replays and out-of-order frames read nothing new, and do not
  count as a sign of life for the rotation guard.
- Credentials are redacted from steps and reasoning: labelled values, known
  key prefixes, long high-entropy runs, and the same checks on each part of
  a URL or of a word glued to shell punctuation, plus webhook URLs whose path
  is the secret. Nothing the renderer strips afterwards can join two pieces
  into a key the redactor never saw. Reasoning arrives in
  500ms increments, so redaction and the </think> defence run over the
  joined buffer, where a key and its value are seen together, and a run that
  may still be the start of a key is held back until it has finished.
- A bubble near the server's ten-minute window is handed over to a fresh
  stream; a hand-over the server refuses stops rotation, and any stream the
  server has disowned takes no further frame — every closer falls back to a
  plain message instead of spending another refusal. Every frame a step
  writes, and both frames of a hand-over, take the reply write guard.
- The identity lookup is wired on the real boot path, and its absence logs
  a warning, since without it no bubble ever shows a step. A relay with no
  typing indicator attached warns once when progress frames arrive.

* fix(wecom): show a step the throttle held back without waiting for the next report

A step that landed inside the 1.5s refresh window was only shown when the
run reported again. The step most worth showing is often the last before a
long silence (a file read, then a test suite that runs for minutes), so the
bubble sat on the earlier step for the whole wait.

A held step now schedules one trailing refresh per feed for when the window
ends. It goes through the run's lane like any report, is not counted as a
sign of life for the rotation guard, is never published to other replicas,
and finds nothing once the round has ended.

Co-authored-by: multica-agent <[email protected]>

* fix(wecom): keep the reasoning buffer across a bubble hand-over

Redaction runs over the joined reasoning buffer so a label and its value
split across two increments are seen together. A rotation started that
buffer over, so `password=` at the end of the old bubble and `hunter2` at
the start of the new one were judged apart and the value went out in the
clear.

The new bubble now starts with no step lines and a fresh throttle but the
same raw reasoning buffer. The old bubble is sealed with the hand-over line,
so the reasoning is not shown twice.

Also gofmt the two-replica test.

Co-authored-by: multica-agent <[email protected]>

---------

Co-authored-by: multica-eve <[email protected]>
Co-authored-by: multica-agent <[email protected]>
2026-10-31 01:39:14 +08:00
77c17eb361 MUL-8410 fix(qoder): run chat turns on Qoder Cloud Agents (#9726)
* fix(qoder): run chat turns on Qoder Cloud Agents

* fix(qoder): address review nits for Qoder chat (MUL-8410)

- Tell the agent the chat audience (group/direct/unknown) like the local
  daemon's chat prompt, so an IM group reply is not treated as private.
- Update the qoder-runtime README: chat turns and chat session
  continuation are supported.
- Show conversation starters and the new-chat preview for Qoder agents,
  and keep starters when creating a Qoder agent.
- PriorSessionID comment covers chats as well as issues.

Co-authored-by: multica-agent <[email protected]>

---------

Co-authored-by: 安辰 <[email protected]>
Co-authored-by: Multica Eve <[email protected]>
Co-authored-by: multica-agent <[email protected]>
2026-10-31 01:29:56 +08:00
b77b069431 MUL-5491: feat(metrics): add MiniMax-M3 standard-tier pricing (#6128)
* feat(metrics): add MiniMax-M3 to pricing registry

Map MiniMax-M3 in the backend pricing registry (input $0.60/M, output
$2.40/M, cache read $0.12/M; cache write bills at the input rate as M3
has no separate cache-write rate) and add the minimax-m3 alias so its
usage is priced instead of landing in the unpriced bucket.

* fix(metrics): tighten MiniMax-M3 pricing alias

* fix(metrics): price MiniMax-M3 cache writes

* docs(metrics): clarify MiniMax-M3 pricing tier

* fix(minimax): refresh text choices and M3 standard pricing

* revert(views): drop MiniMax model picker filtering from this PR

Keep this PR scoped to the MiniMax-M3 server-side pricing row. Hiding
runtime-advertised models in the pickers is a separate product decision.

Co-authored-by: multica-agent <[email protected]>

---------

Co-authored-by: octo-patch <[email protected]>
Co-authored-by: multica-eve <[email protected]>
Co-authored-by: multica-agent <[email protected]>
2026-10-31 01:29:09 +08:00
Xichang(Seacen) Zhao 908456c601 feat(wecom): tell the sender when the media budget gives up on an attachment, and count every failure (#9695)
When the Router's media budget ran out, the resolver had either never
started or been cut off, so it saw no failure and the notice it owns
never fired. The sender's photo was dropped in silence and the agent
answered the words beside it as if there were no photo.

engine.MediaAbandonNotifier is an optional extra a MediaResolver can
implement. On that path the Router calls it on a goroutine of its own,
with its own bounded context, started before database finalization and
not waited on by it: the bind, the pending-media marker and the task
promotion keep their five seconds whatever the chat write costs. Drain
still waits for it. The call carries what only the Router knows: why the
budget ended, whether ResolveMedia ran, and the refs it dropped.

The WeCom resolver says the same "did not arrive" line it says for an
unreadable attachment, and drops that line from its own notice when the
budget cut it off, so the sender does not read it twice. A "too large"
verdict reached before the deadline still goes. Its own notice also goes
out on a goroutine now, so ResolveMedia no longer waits on the chat;
the Router's Drain still waits for it, through the optional
engine.MediaNoticeDrainer it calls once every media job is done. Both
notices run on their own context through the language lookup, the write
and the revoked-installation guard (guardReply).

The two notice lines move into the copy pack and are written in the
reader's language.

media_failures_total counts each attachment that never reached the
agent, once, by reason: unreadable, too_large, blocked_address (the SSRF
guard refused where the host resolved), budget_expired, or shutdown.
ResolveMedia counts what it failed on, with a deadline it hit counted as
budget_expired rather than unreadable; NotifyMediaAbandoned counts the
rest, every attachment when ResolveMedia never ran and the dropped refs
when it did. It is counted even when there is no socket to tell the
sender on.
2026-10-30 18:31:58 +08:00
16f0d307dd fix(realtime): announce comments folded into a queued run (#9702)
A reply posted while the agent already has a queued run in the same thread is
folded into that run on the server: the reply becomes the run's trigger and the
earlier comment joins its covered set. The run's status does not change, so no
task event fired and open issue pages kept the run under the earlier comment,
leaving the new reply with no visible queue row.

Publish task:coalesced after the merge (and after a delegated-failure merge).
Web and desktop already refresh the issue's runs on any task: event, which moves
the queued block under the newest comment it answers; mobile now listens for it
too.

Co-authored-by: Cursor <[email protected]>
Co-authored-by: multica-agent <[email protected]>
2026-10-30 17:45:54 +08:00
3451e311a6 MUL-8401: fix(wecom): deliver a relayed seal to the replica holding the bubble (#9717)
* fix(wecom): deliver a relayed seal to the replica holding the bubble

A seal frame ends a round that has no words to send: a cancellation, or a
completion with nothing to say, raised on a replica that does not hold the
bubble. It never arrived. The frame carries no installation id, and the relay
addressed every frame by one:

- it was published under scope id "", and the shard reader drops a frame with
  no scope id (ShardedStreamRelay.deliverMessage);
- perform's first check is whether this replica owns that installation's
  socket, and ownsSocket("") is false everywhere.

So off-lease the bubble kept spinning for the rest of the stream's window.

A seal is for whoever holds the round, which neither the socket nor the claim
can tell. It is now published under its task id, and perform hands it to
every replica's deliverRelayed with no ownership gate and no claim. Every
replica but the holder finds no round and returns; a copy that reaches the
holder again finds the round already taken. A claim would be wrong as well as
unnecessary: taken once for the deployment, it can be won by a replica
without the round, and the holder would never close the bubble.

The relay test double now drops a frame with no scope id, as the shard reader
does. Delivering it anyway is what hid this.

* fix(wecom): dispatch relayed seals by task id, not the empty installation

A seal carries no installation id, so keying its dispatch queue by
installation put every seal in the deployment on one shard queue behind
one worker. A cancel-all burst larger than QueueDepth was shed, and those
bubbles kept spinning. A seal needs no ordering against other frames, so
it is now dispatched by task id.

Co-authored-by: multica-agent <[email protected]>

---------

Co-authored-by: Multica Eve <[email protected]>
Co-authored-by: multica-agent <[email protected]>
2026-10-30 17:44:04 +08:00
Xichang(Seacen) Zhao 53b3fba3d0 fix(wecom): read a long answer in a bubble to the end (#9692)
A closing stream frame is capped at 20480 bytes, the same as a message, and
truncateStreamContent clips it to fit: an answer past the cap ends in "…" and
the rest of it is nowhere the reader can reach. The plain-message path already
splits a long answer into numbered pieces (#8344); the bubble path did not.

The bubble now holds what one frame can and the rest follows as messages
directly underneath it, split by splitForWire so the two paths break an answer
at the same places. Both writers of an answer into a closing frame make the
split: deliverAnswer on the replica that produced the completion, and
deliverRelayed on the one that took a routed reply. The other two closers
(sealRelayedRound, writeClosing) only ever write short fixed copy.

The rest goes out under one hold of the chat's lock (sendPiecesCtx, which
sendTextCtx now uses for a whole answer too), so nothing else pushed to the same
chat lands between its pieces. Each piece is a reply frame and re-reads the
installation first (guardReply, #9694).

The rest is sent when the seal is on screen and also when its outcome is
unknown: it has never been sent, so it cannot print twice, and holding it back
leaves a bubble ending at (1/n). It runs on fallbackBudget, because an unknown
seal has usually spent the turn's deadline.

A piece under the bubble that fails stops the rest and the reply is counted by
the seal's verdict, the way sendTextCtx reports a later piece failing
(errPartiallySent): the reader has the head of the answer, and a drop would
tell an operator to resend it.
2026-10-30 17:43:16 +08:00
Multica Eveandmultica-agent 354243b459 MUL-8386: feat(cli): add workflow and project workflow commands (#9714)
* feat(cli): add workflow and project workflow commands

Wrap the existing /api/issue-workflows endpoints and the project workflow
switch so agents and scripts can read and change workflows without the
web UI. Permission checks stay on the server.

Refs #9698

Co-authored-by: multica-agent <[email protected]>

* docs(skill): teach the workflow CLI in the multica-platform skill

Workflow edits are rare, so the commands live in the projects reference
instead of the per-turn brief.

Co-authored-by: multica-agent <[email protected]>

* fix(cli): keep the mapping plan when a workflow write needs status mappings

On a 409 workflow_status_mapping_required, project workflow set and
workflow update now print the server's code and full plan to stdout
(JSON, or a table with --output table) and the message to stderr, and
still exit non-zero. The default formatter kept only the first message.

Also fix the zh CLI reference wording for tasks vs runs.

Co-authored-by: multica-agent <[email protected]>

* fix(cli): keep error bodies up to 64 KiB so structured errors parse

newHTTPError kept only 4 KiB of an error response. A workflow's 409
status mapping plan lists one row per status in use and passes 4 KiB at
around 45 statuses; the cut body no longer parsed, so the CLI fell back
to a generic message and dropped the plan.

Co-authored-by: multica-agent <[email protected]>

---------

Co-authored-by: multica-agent <[email protected]>
2026-10-30 17:41:37 +08:00
Multica Eveandmultica-agent f05a525ac8 MUL-8399: chore(release): add 0.7.2 changelog (#9718)
* chore(release): add 0.8.0 changelog

Co-authored-by: multica-agent <[email protected]>

* chore(release): use 0.7.2 as the release version

Co-authored-by: multica-agent <[email protected]>

---------

Co-authored-by: multica-agent <[email protected]>
2026-10-30 17:38:46 +08:00
Xichang(Seacen) Zhao 0249e8609e fix(wecom): upload a file again when WeCom replaces the connection mid-upload (#9715)
The attachment worker resolved the installation's sender once and carried
every file on it. When WeCom dropped the socket under a chunk and had
already subscribed a new connection, the chunk's second offer went to the
closed socket, the upload failed, and the user was told the file could not
be sent while a live connection sat in the registry.

A failed upload never produced a media_id, so nothing reached the chat and
starting over cannot duplicate anything. sendAttachment now uploads the
file once more when the registry holds a different sender than the one the
upload used, unless the failure is one a new connection would repeat: a
stated refusal, a withdrawn permission, an empty or oversize file, or a
spent budget. A failure after the file message went out is still unknown
and still never retried. The restart runs under the same context, so the
write guard is asked before each of its frames as before.

Each file also starts on the sender the registry holds when its turn
comes, rather than the one resolved before the first file.
2026-10-30 15:30:25 +08:00
890628511e MUL-7132: fix(issues): resolve comment trigger avatar URLs (#8137)
* fix(issues): resolve comment trigger avatar URLs

* test(issues): supply API base URL in composer fixtures

Co-authored-by: multica-agent <[email protected]>

---------

Co-authored-by: Sol-Boy <[email protected]>
Co-authored-by: multica-agent <[email protected]>
2026-10-30 14:35:57 +08:00
cd768e9793 MUL-8397: fix(daemon): parse OpenCode 2.x nested MCP server config (#9713)
* fix(daemon): parse OpenCode 2.x nested MCP server config

OpenCode 2.x nests user MCP servers under mcp.servers; 1.x keeps them flat
under mcp. Reading both as the flat mcp map turned a 2.x config into a single
literal "servers" entry with no url or command, so the OpenCode 2.x MCP
support check refused every run that carried user MCP servers.

Resolve the user server map through a provider dispatcher: OpenCode detects
the shape from the content (mcp.servers if present, else the flat mcp map);
every other provider keeps the existing dotted-key lookup. Applied to both the
task-local merge and the agent capabilities inventory.

Co-authored-by: multica-agent <[email protected]>

* fix(daemon): respect disabled OpenCode MCP servers (MUL-8397)

Co-authored-by: multica-agent <[email protected]>

---------

Co-authored-by: multica-agent <[email protected]>
Co-authored-by: Sol-Boy <[email protected]>
2026-10-30 14:13:00 +08:00
Xichang(Seacen) Zhao 4157f5b26e fix(selfhost): pass MULTICA_WECOM_DEFAULT_LOCALE into the backend container (#9691)
router.go reads MULTICA_WECOM_DEFAULT_LOCALE to pick the language the bot
writes its own copy in for readers it cannot look a language up for (a group
chat, anyone not linked to a Multica account). Neither .env.example nor
docker-compose.selfhost.yml named it, so a self-hosted operator had no way to
find the knob, and setting it on the host did nothing: compose forwards only
what its environment: block lists, and the deployment silently stayed on
zh-Hans.

Add it to both, and a test that parses router.go for every MULTICA_WECOM_*
literal it reads and fails when either file is missing one.
2026-10-30 13:13:02 +08:00
a802ce2c6f MUL-8377: feat(workflows): one-line project usage and named handoffs in the workflow list (#9689)
* MUL-8377: feat(workflows): one-line project usage and named handoffs in the workflow list

The workflow list spelled out every project name under each row, so a
workflow used by 25 projects wrapped to five lines. Rows now summarise usage
as project icons plus a count (or the name, for one project) and open a
filterable project list on click.

Handoff chips name who takes the step instead of showing a bare avatar,
steps are joined by arrows so the row chevron only means open, and the row
link is the workflow name (stretched over the row) so its accessible name is
no longer the whole row's text.

Co-authored-by: Cursor <[email protected]>
Co-authored-by: multica-agent <[email protected]>

* MUL-8377: keep each step's arrow with the step when the chain wraps

A long workflow wraps its status chain; the arrow was its own flex item, so
a line could end on an arrow right beside the row's open chevron. Each arrow
now travels with the step it points to, so wrapped lines start with it.

Co-authored-by: Cursor <[email protected]>
Co-authored-by: multica-agent <[email protected]>

* MUL-8377: name the workflow project popover for screen readers

The popover's heading was a plain span, so its dialog had no accessible
name. Use PopoverTitle for the workflow name and PopoverDescription for the
project count, which wires aria-labelledby and aria-describedby.

Co-authored-by: Cursor <[email protected]>
Co-authored-by: multica-agent <[email protected]>

---------

Co-authored-by: Cursor <[email protected]>
Co-authored-by: multica-agent <[email protected]>
2026-10-30 13:01:04 +08:00
Bohan Jiangandmultica-agent fe23bd1161 MUL-8392: feat(properties): support up to 200 select options (#9712)
* feat(properties): support up to 200 select options

Co-authored-by: multica-agent <[email protected]>

* fix(properties): cap filter values and streamline picker tests

Co-authored-by: multica-agent <[email protected]>

---------

Co-authored-by: multica-agent <[email protected]>
2026-10-30 11:56:41 +08:00
799f5614a7 fix(lark): apply pong heartbeat updates and close failed sessions (MUL-8394) (#9708)
* fix(lark): apply pong heartbeat updates and close failed sessions

* fix(lark): keep read deadline above ping cadence and accept updates when ping loop is unarmed

- Raise the effective read deadline to 3x the active ping interval so a
  long Lark-requested cadence cannot trip the idle read timeout.
- Let pingLoop receive interval updates even when the initial interval is
  non-positive, so Run never blocks delivering a pong config update.

Co-authored-by: multica-agent <[email protected]>

---------

Co-authored-by: Multica Eve <[email protected]>
Co-authored-by: multica-agent <[email protected]>
2026-10-30 11:56:19 +08:00
Bohan Jiangandmultica-agent 374b6f3db4 fix: respect omp profiles when discovering runtime skills (#9711)
Co-authored-by: multica-agent <[email protected]>
2026-10-30 11:36:14 +08:00
Xichang(Seacen) Zhao bf8970aa33 fix(wecom): ask again before a queued reply is written to a revoked installation (#9694)
The installation's status is read once, when a reply is addressed. The
reply then waits for the chat's turn and the socket's writer, and a
revocation in between was written straight through: the answer, the
closing frame of its bubble, a failed run's notice, an inbox push.

replyWriteGuard puts the check on each of those frames, the way
attachmentWriteGuard already does for files. Stream frames now consult
the guard too, so a bubble is held to the same check as the message it
would fall back to. A revoked installation is a skip
(installation_inactive), not a drop; a permission that cannot be read
is a drop on the direct path and another offer on the relay.

A retried closing frame that the guard refuses was written nowhere,
which says nothing about the first attempt; seal now keeps the first
attempt's unknown instead of reporting the bubble provably empty.
2026-10-30 10:53:54 +08:00
c066f7ea61 MUL-8382: fix(wecom): write the unreadable-message receipt in the sender's language (#9693)
* fix(wecom): write the unreadable-message receipt in the sender's language

The one line the read loop sends back for a message it cannot read (a
location card, an image callback with no url) was a zh-Hans literal in
wecom_channel.go, so a sender whose Multica profile says English got every
other notice in English and this one in Chinese. It was the last copy #8831
left as a literal; the lint's pending list counted it.

It is now copyPack.UnsupportedMsgType, resolved the way the Replier resolves
its notices: the sender's profile language in a 1:1, the deployment's in a
room. The read loop gets the lookup through ChannelDeps.Languages, which
router.go sets, and RegisterWecom warns when it is nil, the way
NewOutboundReplier does, so the boot-path wiring test covers both.

The lookup runs only for a message nothing here can read; readable messages
take no extra query.

* fix(wecom): resolve the receipt copy after the dedup claim

A redelivery stopped at the claim no longer pays for the language lookup.
Also hoist the logger fallback in RegisterWecom so both warnings share it.

Co-authored-by: multica-agent <[email protected]>

---------

Co-authored-by: Multica Eve <[email protected]>
Co-authored-by: multica-agent <[email protected]>
2026-10-30 10:47:14 +08:00
Frezc a6173f8ebc fix(runtimes): verify new computers before showing connection success (#9688) 2026-10-30 10:37:06 +08:00
2083970b51 MUL-8374: fix: keep private chat content to the session owner (realtime + REST) (#9677)
* MUL-8374: fix(realtime): deliver private chat content only to the session owner

Chat events and chat-task transcript (task:message) frames were fanned out
to every connection in the workspace. Route every chat:* event, and
task:message for chat tasks, to the chat session creator's user scope
instead. Producers stamp the owner on the bus event (from the same
chat_session row they already read); an event without an owner is dropped
rather than widened. Chat-task lifecycle events keep workspace fanout for
agent presence.

Co-authored-by: Cursor <[email protected]>
Co-authored-by: multica-agent <[email protected]>

* fix(server): keep chat-task content private on REST task reads

The transcript endpoint now 404s chat tasks for anyone but the chat
session's creator, and the agent run list and workspace task snapshot keep
foreign chat-task rows but blank their result and error.

Co-authored-by: Cursor <[email protected]>
Co-authored-by: multica-agent <[email protected]>

* fix(realtime): scope private chat delivery to the owner's workspace connections

User-scope delivery reached the chat creator's connections to every
workspace. Those are authorized against their own workspace only, so a
creator removed from the chat's workspace kept receiving its transcript
and reply through a socket to another workspace. Chat content now goes to
a workspace_member scope each connection joins at connect time; it is
re-authorized against the chat's workspace on every write.

Co-authored-by: Cursor <[email protected]>
Co-authored-by: multica-agent <[email protected]>

---------

Co-authored-by: Cursor <[email protected]>
Co-authored-by: multica-agent <[email protected]>
2026-10-29 19:13:45 +08:00
Jiahao 139b13a462 MUL-8369: fix(mobile): preserve runs while stop requests are pending (#9670)
* fix(mobile): preserve runs while stop requests are pending

* docs(mobile): clarify cancellation cache synchronization
2026-10-29 17:24:28 +08:00
Multica Eveandmultica-agent f2a6398d6e MUL-7257: refactor(server): remove unused compatibility queries and capabilities (#9671)
* refactor(comments): drop redundant RFC3339 fallback parse

RFC3339Nano parsing already accepts timestamps without fractional
seconds, so the second time.Parse(time.RFC3339) attempt can never
succeed where the first failed. Keep the existing 400 responses and add
a test pinning second-precision since and the invalid since/before cases.

Co-authored-by: multica-agent <[email protected]>

* refactor(daemon): stop advertising unread plugin capabilities

execution-manifest-v1, agent-skill-v1 and remote-mcp-v1 have had no
server-side reader since the Plugin V1 gates were removed in #7144.
Drop the constants and the daemon advertisement.

Co-authored-by: multica-agent <[email protected]>

* refactor(autopilot): remove legacy no-reservation terminal queries

UpdateAutopilotRunCompleted, UpdateAutopilotRunFailed and
UpdateAutopilotRunSkippedWithResult have no production callers; terminal
transitions go through UpdateAutopilotRunTerminalWithQuota. Their
generated Params structs were still used as service argument types, so
replace them with service-local structs. The reconciler test now seeds
its crash window with an explicit fixture UPDATE.

Co-authored-by: multica-agent <[email protected]>

* refactor(tasks): remove unused StartAgentTask query

Production start paths use StartAgentTaskWithSupplement. Move the seven
test fixtures to the same query with both capability flags off, which
performs the same running transition. The empty-body start handler
branch is unchanged.

Co-authored-by: multica-agent <[email protected]>

* refactor(channels): remove unused pending_fresh queries

MarkChannelChatSessionPendingFresh, LockChannelChatSessionPendingFresh
and ClearChannelChatSessionPendingFresh have no callers. The context
generation path and ClearChannelChatSessionPendingFreshForRevision
remain.

Co-authored-by: multica-agent <[email protected]>

---------

Co-authored-by: multica-agent <[email protected]>
2026-10-29 16:49:57 +08:00
Basuotianandmultica-agent e7dfbe83bd MUL-7995: fix(openclaw): report actionable state ownership lock error (#9222)
* fix(daemon): give OpenClaw tasks a private per-agent state dir (INFRA-70)

Co-authored-by: multica-agent <[email protected]>

* fix(openclaw): report actionable state ownership lock error (INFRA-70)

* fix(openclaw): handle resident gateway local refusal (INFRA-70)

---------

Co-authored-by: multica-agent <[email protected]>
2026-10-29 15:16:43 +08:00
Bohan Jiangandmultica-agent 46d4e26712 MUL-8347: fix Hermes compatibility with gateway-backed and older ACP (#9666)
* fix(hermes): support gateway-backed and in-process ACP

Co-authored-by: multica-agent <[email protected]>

* fix(hermes): interrupt active task gateways during cleanup

Co-authored-by: multica-agent <[email protected]>

---------

Co-authored-by: multica-agent <[email protected]>
2026-10-29 14:59:57 +08:00
375b23247b fix(codex): price GPT-6.1 Terra and add it to the fallback catalog (#9668)
gpt-6.1-terra runs through Codex but has no row in either price table,
so its usage estimates at $0 and shows up as an unmapped model. Add
OpenAI's standard short-context rates ($1 input, $0.05 cached input,
$1.25 cache write, $5 output per 1M) to the backend metrics table and
the frontend estimate table.

The model is live-only on codex-cli 0.164.0-alpha.11.1 (absent from
`codex debug models --bundled`), so also add it to the static fallback
catalog with the observed `low` default and low..ultra efforts, and
normalize its label alongside the rest of the GPT-6 family.

Co-authored-by: Claude Opus 5.5 <[email protected]>
Co-authored-by: Multica Eve <[email protected]>
2026-10-29 14:20:27 +08:00
e03dabdade fix(projects): preserve resource cache across concurrent deletes (MUL-8332) (#9633)
* fix(projects): preserve resource cache across concurrent deletes

* fix(projects): address resource deletion review nits (MUL-8332)

Co-authored-by: multica-agent <[email protected]>

---------

Co-authored-by: Sol-Boy <[email protected]>
Co-authored-by: multica-agent <[email protected]>
2026-10-29 13:59:27 +08:00
7b6743a46f fix(settings): keep property edit actions visible [ZIC-375] MUL-8331 (#9648)
* fix(settings): keep property actions visible in narrow content

Co-authored-by: multica-agent <[email protected]>

* test(settings): verify property edits against the backend

Co-authored-by: multica-agent <[email protected]>

* fix(views): use shared icon for inbox restore action

Co-authored-by: multica-agent <[email protected]>

---------

Co-authored-by: multica-agent <[email protected]>
Co-authored-by: Sol-Boy <[email protected]>
2026-10-29 13:56:46 +08:00
ec72a00ac3 MUL-8342 fix(daemon): reject legacy DSH profiles (ZIC-374) (#9649)
* fix(daemon): reject legacy DSH runtime profiles

Co-authored-by: multica-agent <[email protected]>

* fix(dsh): show bridge migration steps in refusal notices

Co-authored-by: multica-agent <[email protected]>

---------

Co-authored-by: multica-agent <[email protected]>
Co-authored-by: Sol-Boy <[email protected]>
2026-10-29 13:39:35 +08:00
Spedymax 39d8a11970 fix(issues): restore an archived inbox notification from the issue page (#9653) 2026-10-29 12:27:12 +08:00
07221fbc64 docs(skills): keep theme text tokens off fixed light fills in html blocks (#9664)
Co-authored-by: Multica Eve <[email protected]>
Co-authored-by: multica-agent <[email protected]>
2026-10-29 11:00:12 +08:00
inoisyandEvgeniy Yakutov 828e0c927e MUL-8346: fix(agent): handle Claude Code session and weekly limits (#9652)
* fix(taskfailure): classify Claude Code session and weekly limits as provider quota

Claude Code reports a spent subscription window as "You've hit your session limit · resets 2pm (UTC)" or "You've hit your weekly limit · resets <date>". Neither matched the quota patterns, so these runs were classified as agent_error.unknown and quota-aware handling never saw them.

* fix(agent): stop waiting for claude after an error result

With stdin closed, the Claude Code CLI stays alive while any background task it started is running, then opens a new turn when the task ends. After an error result (a spent session limit, an API failure) that turn cannot change the outcome, yet the daemon kept the run open for the background task's lifetime. Terminate the process group 10s after an error result, the same way cancellation does, so the run fails with the result's error.

---------

Co-authored-by: Evgeniy Yakutov <[email protected]>
2026-10-29 10:47:54 +08:00
b2e58d0bf8 fix(views): move Create sub-issue below Rename issue in issue actions menu (#9661)
Co-authored-by: Cursor <[email protected]>
Co-authored-by: multica-agent <[email protected]>
2026-10-29 03:17:51 +08:00
045fcdf19a feat(issues): compact the sidebar execution log (MUL-8350) (#9659)
* feat(issues): compact the sidebar execution log (MUL-8350)

Single-line finished runs (reason line only on failure/cancel), several
queued runs fold into one expandable row, a 24px cost strip without the
time axis, and the Latest label carries the Show-more link.

Co-authored-by: Cursor <[email protected]>
Co-authored-by: multica-agent <[email protected]>

* feat(issues): drop the Latest heading from the sidebar execution log (MUL-8350)

Finished runs already read apart from active ones by tone and right
column; the full history stays one click away from the header button and
the cost strip.

Co-authored-by: Cursor <[email protected]>
Co-authored-by: multica-agent <[email protected]>

* feat(issues): cap the sidebar execution log at three rows (MUL-8350)

Active runs take the rows first and are never cut; past runs fill what
is left. The rest stays in the Runs dialog.

Co-authored-by: Cursor <[email protected]>
Co-authored-by: multica-agent <[email protected]>

* feat(issues): narrow the issue sidebar's outer gutter (MUL-8350)

Sections already inset their bodies and rows by 16px, so the panel's own
16px put content 32px from each edge. The issue sidebar now uses an 8px
gutter; the project sidebar keeps p-4.

Co-authored-by: Cursor <[email protected]>
Co-authored-by: multica-agent <[email protected]>

---------

Co-authored-by: Cursor <[email protected]>
Co-authored-by: multica-agent <[email protected]>
2026-10-29 02:08:09 +08:00
d5d1382b8f fix(ui): tint the active sidebar icon lightly instead of filling it solid (MUL-8351) (#9660)
Reverts the solid fill from 95294b1 back to the 20% body tint.

Co-authored-by: Cursor <[email protected]>
Co-authored-by: multica-agent <[email protected]>
2026-10-29 01:49:35 +08:00
ce565c1aa4 fix(ui): Linear's menu geometry across dropdown, context, select and command menus (MUL-8344) (#9657)
* fix(ui): compact menus again — 28px rows, 16px icons (MUL-8344)

Co-authored-by: Cursor <[email protected]>
Co-authored-by: multica-agent <[email protected]>

* fix(ui): match Linear's menu geometry — 32px rows, 16px icons, 8px gaps (MUL-8344)

Co-authored-by: Cursor <[email protected]>
Co-authored-by: multica-agent <[email protected]>

* fix(ui): same menu geometry for Select and Command lists (MUL-8344)

Co-authored-by: Cursor <[email protected]>
Co-authored-by: multica-agent <[email protected]>

---------

Co-authored-by: Cursor <[email protected]>
Co-authored-by: multica-agent <[email protected]>
2026-10-29 01:00:11 +08:00
9987874547 fix(ui): sharp-tipped directional icons, straight checks, sized icons, 14px menus (MUL-8344) (#9655)
* fix(ui): draw checks as two straight strokes instead of a handwritten swoosh (MUL-8344)

Check, CheckCircle2, CheckCheck, ListChecks, ListTodo, ShieldCheck and
BookCheck share a new tick() helper with a tight vertex. CheckCircle2 and
ShieldCheck mark their check as a detail so the solid state keeps it.
The issue actions and member role menus use the Check icon instead of a
text ✓, which fell back to the system font's calligraphic glyph.

Co-authored-by: Cursor <[email protected]>
Co-authored-by: multica-agent <[email protected]>

* fix(ui): enlarge undersized icons and stop the attach icon shrinking to 12px (MUL-8344)

Paperclip spanned 11x13 of the 24 grid with rails 2 apart, so at 14px it
read as a blob. It now runs corner to corner with rails 3 apart. Play,
Pause, SkipForward, Pipette, Rocket and Pencil scale up to the set's
usual ~18 span.

FileUploadButton and the code block's reload/fullscreen buttons sized
their icons with h-/w- classes, which the icon-xs button variant
overrides to 12px; they now use size-* so the intended size applies.

Co-authored-by: Cursor <[email protected]>
Co-authored-by: multica-agent <[email protected]>

* fix(ui): back to 14px text in the sidebar and menus, keeping 18px icons (MUL-8344)

Sidebar rows and dropdown/context menu items return to text-body. Menu
items take 1px more vertical padding so rows stay 32px tall; sidebar rows
keep their fixed 34px.

Co-authored-by: Cursor <[email protected]>
Co-authored-by: multica-agent <[email protected]>

* fix(ui): sharp tips on every chevron, arrow and bracket icon (MUL-8344)

Chevrons, arrowheads, < > brackets, prompts, panel toggles and direction
triangles turned at a soft curve or a rounded k corner. They now turn at
a sharp vertex: 32 soft-curve subpaths become polylines, head() draws a
straight arrowhead, the rotate/refresh and diagonal arrowheads drop their
corner radius, and Play, SkipForward, FastForward and ArrowBigUp get
sharp tips. Shaft bends in corner and U-turn arrows stay rounded.

Co-authored-by: Cursor <[email protected]>
Co-authored-by: multica-agent <[email protected]>

* fix(ui): stop the pencil's cap line poking out of its body (MUL-8344)

Co-authored-by: Cursor <[email protected]>
Co-authored-by: multica-agent <[email protected]>

---------

Co-authored-by: Cursor <[email protected]>
Co-authored-by: multica-agent <[email protected]>
2026-10-29 00:40:38 +08:00
f0c361ebca feat(ui): Multica icons, roomier menus and typography pass (MUL-8334, MUL-8338) (#9640)
* refactor(ui): add @multica/ui/icons as the single icon entry point (MUL-8334)

Co-authored-by: Cursor <[email protected]>
Co-authored-by: multica-agent <[email protected]>

* refactor: import icons from @multica/ui/icons instead of lucide-react (MUL-8334)

Co-authored-by: Cursor <[email protected]>
Co-authored-by: multica-agent <[email protected]>

* chore(lint): ban direct lucide-react imports outside @multica/ui/icons (MUL-8334)

Co-authored-by: Cursor <[email protected]>
Co-authored-by: multica-agent <[email protected]>

* feat(ui): draw Multica icons and enlarge primitive icon sizes (MUL-8334)

Co-authored-by: Cursor <[email protected]>
Co-authored-by: multica-agent <[email protected]>

* feat(ui): roomier menu and sidebar rhythm (MUL-8334)

Co-authored-by: Cursor <[email protected]>
Co-authored-by: multica-agent <[email protected]>

* feat(ui): mark body and detail layers in icon glyphs (MUL-8334)

Co-authored-by: Cursor <[email protected]>
Co-authored-by: multica-agent <[email protected]>

* feat(ui): Inter optical sizing, lighter titles, CJK leading and zh-TW font stack (MUL-8338)

Co-authored-by: Cursor <[email protected]>
Co-authored-by: multica-agent <[email protected]>

* feat(ui): 13px for in-place reading text, regular weight for descriptive labels (MUL-8338)

Issue detail timeline, comment headers, properties panel, wakeup and PR rows
move from 12px to 13px; sub-issue identifiers match list/board (12px regular);
sidebar group labels drop to regular weight; board card titles get more
leading under a CJK UI via a new cjk: variant.

Co-authored-by: Cursor <[email protected]>
Co-authored-by: multica-agent <[email protected]>

* feat(ui): tint the active sidebar icon's body with a light fill (MUL-8334)

Co-authored-by: Cursor <[email protected]>
Co-authored-by: multica-agent <[email protected]>

* feat(ui): draw every remaining icon and drop lucide-react (MUL-8334)

All 286 icon names now render Multica drawings. The drawings, their
construction helpers and the generator live in packages/ui/icons/drawings;
glyphs.ts and index.ts are generated and a test fails when they drift.
Search loses its lens highlight and Inbox its arrow.

Co-authored-by: Cursor <[email protected]>
Co-authored-by: multica-agent <[email protected]>

* chore: guard against icon libraries and document drawing new icons (MUL-8334)

Lint bans the common React icon libraries, AGENTS.md points to a new
multica-icons skill, route icons must mark a body for the active tint,
and comments no longer describe product icons as Lucide.

Co-authored-by: Cursor <[email protected]>
Co-authored-by: multica-agent <[email protected]>

* feat(ui): fill the active sidebar icon solid with its details knocked out (MUL-8334)

Co-authored-by: Cursor <[email protected]>
Co-authored-by: multica-agent <[email protected]>

* fix(lint): ban icon library subpaths too (MUL-8334)

Co-authored-by: Cursor <[email protected]>
Co-authored-by: multica-agent <[email protected]>

---------

Co-authored-by: Cursor <[email protected]>
Co-authored-by: multica-agent <[email protected]>
2026-10-28 23:52:28 +08:00
fb49f55924 MUL-8340 feat(search): context-aware command bar with agent handoff (#9647)
* feat(search): rebuild the command bar around page context and agent handoff (MUL-8340)

Pages register what they show (issue, selection, project, agent, autopilot, inbox, chat) and the bar leads with commands for it. Adds cmdk submenus with breadcrumbs, S/A/P on an empty input, relevance-ranked mixed results with a best match and its actions, > # @ scope prefixes, a mixed Recent group, and a Hand to agent group that turns an instruction into a quick-create.

Co-authored-by: Cursor <[email protected]>
Co-authored-by: multica-agent <[email protected]>

* feat(search): drop S/A/P single-key submenu shortcuts (MUL-8340)

A plain letter on an empty input opened the issue's status, assignee or priority submenu, so a search starting with s/a/p could land in a picker; typing "alice" on an issue page could assign it instead of opening her page. Letters now always type; the issue's commands are reached from their rows or by name, where context ranking already puts them first.

Co-authored-by: Cursor <[email protected]>
Co-authored-by: multica-agent <[email protected]>

* fix(search): keep overlapping command groups apart and report writes after close (MUL-8340)

With a list selection and an issue preview both open, the two groups offered rows with the same cmdk value, so Enter on the selection's row ran the single issue's command. Context group rows now carry their group in the value.

The palette closes, and unmounts, before a write settles, so per-call mutate callbacks were dropped and a rejected single-issue write rolled back without a toast. Single-issue writes now go through mutateAsync behind the same run-confirm gate, and the other post-close mutations handle their outcome on the promise.

Co-authored-by: Cursor <[email protected]>
Co-authored-by: multica-agent <[email protected]>

---------

Co-authored-by: Cursor <[email protected]>
Co-authored-by: multica-agent <[email protected]>
2026-10-28 23:34:24 +08:00
8305976793 fix(views): show agent run row actions on touch devices (MUL-8341) (#9646)
Co-authored-by: Cursor <[email protected]>
Co-authored-by: multica-agent <[email protected]>
2026-10-28 21:55:34 +08:00