Switching the runtime mid-conversation in Build with AI only updated local React state, so the picker could show runtime B while every subsequent message still executed on the runtime frozen at session create time.
- Add PATCH /api/agent-builder/sessions/{id}/runtime to rebind the hidden builder carrier (runtime_id/runtime_mode, model cleared since model ids are per-runtime). Creator-only, builder carriers only, target must be in-workspace, usable by the member, and online; a reply in flight returns 409.
- Serialise rebind against send: both take LockChatSessionForRuntimeBind on the chat_session row and SendDirectChatMessage re-reads the agent inside that transaction, so a send blocked behind a rebind cannot resume and stamp its task with the runtime the switch moved away from.
- Leave chat_session.runtime_id stale on purpose so the daemon starts a fresh provider session on the new runtime while Multica-side history and the draft survive.
- Frontend updates the draft only after the server reports the bound runtime, blocks sending during a rebind, disables the Mine/All filter alongside the trigger, and explains why the picker is locked during a pending reply.
Closes#5773
A machine-level rename stamps the same custom_name on every runtime of a
daemon (MUL-4217), so the settings-page runtime dropdown rendered N
indistinguishable machine-name rows. Split selection into two levels:
pick the machine first, then a runtime on it, labelled by the runtime
itself. Opening lands inside the selected runtime's machine, and the
trigger now reads 'Claude · <machine>' instead of the bare machine name.
Co-authored-by: Lambda <lambda@multica.ai>
Co-authored-by: multica-agent <github@multica.ai>
* refactor(ui): converge ActorAvatar size to semantic tiers (MUL-4277)
Replace the free-form numeric `size` on ActorAvatar with a constrained
`AvatarSize` union (xs/sm/md/lg/xl/2xl) so avatar dimensions are chosen by
role instead of ad-hoc pixels. This eliminates the magic-number drift where
the same role rendered at different sizes across pages.
- Add `@multica/ui/lib/avatar-size` (AvatarSize union + AVATAR_SIZE_PX map +
default tier).
- Base `ActorAvatar` (packages/ui) and business `ActorAvatar`/`AgentStatusDot`
(packages/views) now take `AvatarSize`; internal font/icon math and the
presence-dot threshold read px from the map.
- Migrate all web/desktop call sites (packages/ui + packages/views) from
numeric sizes to tiers using the role table
(12,14->xs 16,18,20->sm 22,24,28->md 30,32,34->lg 40,44->xl 56,64->2xl).
- Token-ise the derived consumers `AgentAvatarStack` and
`IssueAgentActivityIndicator` (px looked up internally for overlap/+N math).
Out of scope (per plan): ui/avatar.tsx primitive, account-tab/AvatarPicker,
mobile, and component-name disambiguation.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: multica-agent <github@multica.ai>
* fix(ui): unify all avatars and the upload cropper to round (MUL-4277, MUL-4184)
main's avatar-shape decision rendered non-human actors (agent, squad,
system) and the workspace logo as rounded squares, and the upload cropper
mirrored that with a square crop window. Per the updated decision
(avatars_and_cropper_round_required), every avatar and the crop UI are now
circular; the square path is removed rather than left as dead config.
- Base ActorAvatar: always rounded-full (drop the isHuman/rounded-md split).
- avatar-crop-dialog: remove the AvatarCropShape/square path; crop window is
always cropShape="round".
- avatar-upload-control: drop VARIANT_SHAPE; the control is always round and
no longer threads a shape to the dialog (variant still drives the fallback).
- Strip rounded-md/rounded-none square overrides from agent/squad/member
ActorAvatar call sites; round the read-only agent/squad static wrappers.
- WorkspaceAvatar: round the org logo so it matches the (now round) workspace
upload/crop and the shared avatar shape.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: multica-agent <github@multica.ai>
* fix(ui): make round avatar shape a hard invariant (MUL-4277)
Close the two remaining square squad-avatar paths flagged in review and
prevent call sites from re-squaring the avatar:
- base ActorAvatar: keep `rounded-full` as the last class in cn() so a
call-site `className` can no longer override the circle.
- SquadHeaderAvatar: drop `className="rounded"` (was overriding the base
circle into a small rounded square).
- SquadsPage no-avatar fallback: route through the shared ActorAvatarBase
(`isSquad size="lg"`) instead of a hand-written rounded-md tile, so the
fallback matches the image path — one shape source of truth.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: multica-agent <github@multica.ai>
* fix(views): round the agent/squad avatar loading skeletons (MUL-4277)
The avatar placeholder skeletons on the agent/squad list, detail, and
profile-card loading states were still rounded squares (rounded-md/lg) from
the pre-round era, so the avatar visibly popped from square to circle on
load — inconsistent with the round avatars and with the member/inbox/issue
skeletons that already use rounded-full.
Round all five: agents-page, agent-detail-page, squads-page,
squad-detail-page, squad-profile-card.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: multica-agent <github@multica.ai>
---------
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: multica-agent <github@multica.ai>
* fix(runtimes): make rename a machine action + fix picker search copy (MUL-4217)
Follow-up to the runtime-naming feature based on testing feedback:
1. The create-agent runtime picker filters by MACHINE (its search matches the
machine title / host / provider names), but the placeholder said "Search
runtimes", which misled users into typing a runtime name. Change the
placeholder and the empty-state to "Search machines" / "No matching
machines" (all 4 locales).
2. Rename was framed as "rename this runtime" with an opt-in "apply to whole
machine" checkbox, but the intent is naming the machine (the computer), not
an individual runtime. Rework it into a machine-level action:
- New RenameMachineDialog always names the whole machine (apply_to_machine);
the per-runtime dialog + checkbox are gone.
- The entry now lives on the Runtimes page, on the selected machine's header
(a pencil next to the machine title), owner/admin gated — that's where the
user looked for it. Removed the per-runtime pencil from the runtime detail
page.
No backend change — apply_to_machine and machine-name inheritance already exist.
Verified: pnpm typecheck (all apps), full views suite (1650) + locale parity,
lint (0 errors).
Co-authored-by: multica-agent <github@multica.ai>
* fix(runtimes): always show machine header in picker; pre-fill rename with shared name only
Testing + review follow-ups on #5087:
1. The create-agent picker hid the machine group header when there was only
one machine (e.g. after a search narrowed to one), collapsing to a flat
list. Always render the machine header so grouping stays consistent.
2. (Elon) RenameMachineDialog pre-filled from the first non-empty custom_name
on the machine, but the machine title only uses a name when ALL runtimes
share one (sharedCustomName). A lone per-runtime name would thus pre-fill as
if it were the machine name — the same runtime-vs-machine confusion this PR
set out to remove. Export sharedCustomName and use it for the pre-fill;
otherwise pre-fill empty. Added direct unit tests.
Co-authored-by: multica-agent <github@multica.ai>
---------
Co-authored-by: J <j@multica.ai>
Co-authored-by: multica-agent <github@multica.ai>
* feat(runtimes): custom runtime names + searchable machine-grouped picker
MUL-4217. Runtime names were daemon-generated ("Claude (host)") and
uneditable, so picking one at agent-create time was painful once a
workspace had many machines.
Phase 1 — create-agent RuntimePicker: add a search box (>6 runtimes) and
group options by machine (Local/Remote/Cloud, online-first, current
machine first) reusing buildRuntimeMachines/filterRuntimeMachines. Rows
show the provider under a machine header instead of a flat repeated list.
Phase 2 — custom names: new nullable agent_runtime.custom_name column,
never written by the registration/heartbeat upsert so the daemon can't
clobber it; display is coalesce(custom_name, name) via runtimeDisplayName.
PATCH /api/runtimes/:id gains custom_name (+ apply_to_machine to name every
runtime sharing a daemon_id in one action, owner-scoped for non-admins).
Rename UI on the runtime detail page; `multica runtime rename` CLI command.
Verified: go build/vet, sqlc, handler tests (incl. new custom-name single
+ machine-fanout), 1650 views + 764 core TS tests, typecheck, locale parity.
Co-authored-by: multica-agent <github@multica.ai>
* fix(runtimes): address review — persist machine name on new registrations, keep custom_name in register response
Elon's review on #5070 (MUL-4217):
1. Machine name looked "lost" when a new provider registered on an
already-named machine — the new row landed with custom_name=null and
broke sharedCustomName. Now a fresh runtime inherits the machine's shared
custom name at register time (ListDaemonCustomNames + sharedDaemonCustomName),
so the machine title stays stable as providers come and go.
2. DaemonRegister rebuilt the response row by hand and dropped custom_name,
so register returned custom_name:null — inconsistent with list/get/update.
Both branches now carry CustomName.
Also: tighten the updateRuntime patch type to custom_name?: string (drop the
misleading `| null`, since the server treats null as "unchanged", not "clear").
Tests: register response preserves custom_name; new runtime inherits machine name.
Co-authored-by: multica-agent <github@multica.ai>
* fix(runtimes): inherit machine name for failed-profile registrations too
Elon's re-review of #5070 (MUL-4217): the machine-name inheritance added
last round only covered the normal req.Runtimes path. The req.FailedProfiles
branch also upserts a daemon_id-scoped agent_runtime row (offline, profile
registration error), which shows up in the runtime list / machine grouping —
so on a named machine a failed custom-profile row landed with custom_name=NULL
and dragged the machine title back to the hostname.
Extract the inheritance into h.inheritMachineCustomName and call it from both
the normal runtime path and the failed-profile path. Add a test: named daemon
+ failed profile upsert -> the failed row's persisted custom_name is inherited.
Co-authored-by: multica-agent <github@multica.ai>
---------
Co-authored-by: J <j@multica.ai>
Co-authored-by: multica-agent <github@multica.ai>
* refactor(agents): drop template chooser from create-agent dialog
Removes the blank-vs-template chooser, the template picker, and the
template detail step. The "Create agent" entry point now opens directly
on the form. The createAgentFromTemplate API and types remain
untouched — this only removes the UI entry.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Co-authored-by: multica-agent <github@multica.ai>
* docs(squads): fix stale comment about createAgentFromTemplate
Squad-scoped create flow no longer goes through the template path;
the dialog now only calls api.createAgent then api.addSquadMember.
Co-authored-by: multica-agent <github@multica.ai>
---------
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
Co-authored-by: multica-agent <github@multica.ai>
Template create used to silently default the runtime to "first usable"
and never collected a model — users had no idea where the new agent
would run or which model it would use until they opened the detail
page. Add a Runtime + Model picker pair above the skill list on the
template-detail step so the choice is visible (and overridable) before
the one-click Use action.
- Extract RuntimePicker out of create-agent-dialog so the form and the
template-detail step share one popover; selection seeding moves into
the picker too, since it's the only place that knows the active
filter (mine/all). Parent keeps just the duplicate-mode pre-fill.
- Mirror RuntimePicker's label-row + trigger DOM in ModelDropdown so
the two pickers render at identical heights when sat side-by-side
(fixes a 6-8px misalignment caused by inconsistent label-row sizing).
- Send model in createAgentFromTemplate; server side already accepts
the field (CreateAgentFromTemplateRequest.Model, omitempty), empty
string still falls through to the runtime's default model.
- Drop the runtime_register_first fallback hint that made the Runtime
trigger two-line in the empty state, breaking alignment with Model's
one-line trigger.
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>