* fix(storage): support custom S3 endpoints for self-hosted deployments
When AWS_ENDPOINT_URL is set, the S3 client now uses path-style
addressing and routes requests to the custom endpoint (e.g. MinIO).
Returns path-style URLs (endpoint/bucket/key) instead of virtual-hosted
URLs so attachments are accessible on local setups.
Also falls back to STANDARD storage class for custom endpoints since
MinIO and other S3-compatible stores do not support INTELLIGENT_TIERING.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(storage): handle custom endpoint URLs in KeyFromURL
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
High-severity DoS vulnerability (CVSS 7.5) in App Router — specially
crafted requests to RSC endpoints cause excessive CPU consumption.
Patched in Next.js 16.2.3.
Ref: https://github.com/multica-ai/multica/issues/701
Replaces the hardcoded assignment-triggered workflow in buildMetaSkillContent()
with a minimal version that defers to agent Skills and Identity. Keeps platform
capability docs and status management steps intact.
Fixes#669
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
createAgentComment omitted WorkspaceID when calling CreateComment,
causing all agent comments (progress updates, completion messages) to
silently fail against the NOT NULL constraint on comment.workspace_id.
The issue variable is already fetched on the preceding line for mention
expansion, so this adds the missing field to match the handler path in
comment.go.
* feat(notifications): notify parent issue subscribers on sub-issue changes
When a sub-issue receives a change (status, assignee, priority, comment, etc.),
parent issue subscribers are now also notified. Deduplicates against direct
subscribers to avoid double notifications. The inbox item still points to the
sub-issue so clicking the notification navigates to the actual change.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(notifications): parent subscriber inbox items now point to sub-issue
Split notifyIssueSubscribers into subscriberIssueID (which issue's
subscribers to query) and targetIssueID (which issue the inbox item
links to). When notifying parent subscribers, the inbox item correctly
points to the sub-issue where the change occurred, so clicking the
notification navigates to the right place.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
When a reply explicitly @mentions anyone (agents or members), the user
is making a deliberate choice about who to involve. Previously, replying
with @AgentB under a comment mentioning @AgentA would trigger both agents.
Now parent mentions are only inherited when the reply has no mentions at all.
public/ is mode 750 locally, so COPY into the runner stage landed files as
root and the nextjs user fell under other perms, causing EACCES on scandir
at startup. Add --chown=nextjs:nodejs to the standalone/static/public COPYs.
* fix(security): add workspace ownership checks to all daemon API routes
Switch daemon routes from middleware.Auth to middleware.DaemonAuth and
add per-handler workspace ownership verification. This prevents
cross-workspace access to runtimes, tasks, usage, and daemon lifecycle
endpoints (HIGH-1/2/3 + CHAIN-1/2/3).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(security): support mdt_ daemon tokens in DaemonRegister + add regression tests
DaemonRegister now handles both auth paths:
- mdt_ daemon tokens: verify workspace match, skip member check, zero OwnerID
(SQL COALESCE preserves existing owner on upsert)
- PAT/JWT: existing member check + OwnerID from member
Also adds WithDaemonContext helper and regression tests covering:
- Successful register with daemon token
- Workspace mismatch rejection
- Cross-workspace heartbeat rejection
- Cross-workspace task status rejection
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Prevent cross-workspace attachment injection (CRIT-3) by verifying
issue_id/comment_id belong to the caller's workspace before creating
attachment records. Add workspace_id filter to ListAttachmentsByCommentIDs
query (MED-3) to prevent cross-workspace attachment data leakage.
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: sanitize markdown rendering in comments and shared renderers
Add rehype-sanitize to both ReadonlyContent and Markdown components so
that raw HTML parsed by rehype-raw is sanitized against a strict
allowlist before reaching the DOM. On the backend, add a bluemonday
sanitization pass when creating and updating comments to strip
dangerous tags as defense-in-depth.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: add mention:// protocol to sanitize allowlist and validate file card URLs
- Add mention:// to rehype-sanitize protocols.href in both ReadonlyContent
and Markdown so @mention links survive sanitization
- Validate data-href on file cards to only allow http(s) URLs, blocking
javascript: and data: schemes in both frontend click handler and backend
bluemonday policy
- Narrow class attribute allowlist to specific elements (code, div, span, pre)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
These are runtime artifacts created by Conductor for worktree process
management. They should never be tracked in git.
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Support filtering issues by project in the Issues tab filter dropdown,
including a "No project" option for issues without a project assigned.
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The self-hosting Docker Compose setup fails to build on a clean clone due to several issues:
1. Dockerfile.web did not copy .npmrc into the deps stage. The project uses shamefully-hoist=true, so without it pnpm produces a different node_modules layout and module resolution breaks.
2. The builder stage copied individual node_modules directories from the deps stage (COPY --from=deps). This breaks pnpm's symlink structure -- especially on Windows where symlinks resolve to host paths. Additionally, packages/tsconfig has zero dependencies so its node_modules never exists, causing a hard COPY failure. Fixed by copying the full workspace from deps and running an offline pnpm install to re-link after source overlay.
3. next.config.ts imports dotenv but it was not declared as a direct dependency in apps/web/package.json. It resolves locally as a hoisted transitive dep but fails the TypeScript type check during next build in Docker.
4. docker/entrypoint.sh gets CRLF line endings on Windows due to git autocrlf, which breaks the shebang (container looks for /bin/sh\r). Added .gitattributes to enforce LF for shell scripts and a sed strip in the Dockerfile as a safety net.
Use "Project Management for Human + Agent Teams" across all page titles,
OpenGraph metadata, and structured data to align with the actual landing
page hero and footer content.
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat(search): add page navigation to cmd+k command palette
Users can now search and navigate to sidebar pages (Inbox, My Issues,
Issues, Projects, Agents, Runtimes, Skills, Settings) directly from
the cmd+k dialog. Pages are shown in a dedicated "Pages" group and
filtered by query with keyword matching.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(search): only show pages when query is entered
Pages section was pushing down the Recent Issues list when the dialog
first opens. Now pages only appear when the user types a matching query.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Removes the Overview/Issues tab system — clicking a project now shows
issues directly. Project properties (icon, title, status, priority,
lead, progress, description) are moved to a collapsible right sidebar,
matching the issue detail layout pattern.
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Projects are now searchable alongside issues in the Cmd+K search dialog.
Results are grouped by type (Projects / Issues) with project icon, status,
and description snippet highlighting.
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(docker): remove COPY for non-existent tsconfig/node_modules
The @multica/tsconfig package has zero dependencies, so pnpm install
never creates a node_modules directory for it. The COPY --from=deps
instruction fails with "not found" during docker compose build.
Closes#658
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(docker): add dotenv as explicit dependency for web app
next.config.ts imports dotenv to load .env for REMOTE_API_URL, but
dotenv was never declared as a dependency. It worked locally as a
hoisted transitive dep but fails in Docker's stricter module resolution.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs: fix daemon setup instructions for local Docker deployments
The daemon setup section in SELF_HOSTING.md had production URLs as the
active example and local Docker URLs commented out. Since this is a
self-hosting guide, local Docker should be the primary example.
Key changes:
- Make local Docker URLs the default in daemon setup examples
- Add explicit warning that CLI defaults to hosted service
- Add 'multica config set' instructions for persistent setup
- Add link from Quick Start to daemon setup section
- Clarify that daemon runs on host machine, not inside Docker
- Update CLI_AND_DAEMON.md self-hosted section similarly
Closes#660
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat(search): show recent issues list when cmd+k opens
When opening the cmd+k search dialog, display a list of recently visited
issues instead of the empty placeholder. Visits are tracked via a
workspace-scoped persisted Zustand store (max 20 items).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(search): close cmd+k dialog on single ESC press
cmdk was consuming the first ESC to clear internal state, requiring a
second press to close the dialog. Intercept ESC on the CommandPrimitive
and close the dialog directly.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(search): move ESC handler to input to prevent double-ESC
The previous handler on CommandPrimitive didn't fire because cmdk
intercepts ESC at the input level. Moving the onKeyDown to
CommandPrimitive.Input ensures it fires before cmdk processes it.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(search): use capture-phase ESC listener to close dialog reliably
The previous onKeyDown approach on the Input didn't work because
base-ui Dialog's internal focus management handled ESC before the
React synthetic event. Use a document-level capture-phase listener
that fires before all other handlers and stops propagation.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* test(search): cover single-escape command palette close
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: add pin to sidebar for issues and projects
Add per-user pinning of issues and projects to the sidebar for quick access.
- New `pinned_item` table with per-user, per-workspace scoping
- REST API: GET/POST /api/pins, DELETE /api/pins/{type}/{id}, PUT /api/pins/reorder
- Sidebar "Pinned" section between Personal and Workspace nav (hidden when empty)
- Pin/unpin actions in issue and project detail dropdown menus
- Optimistic mutations with WebSocket invalidation for real-time sync
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: add drag-and-drop reordering and visible pin buttons
- Sidebar pinned items now support drag-and-drop reordering via @dnd-kit
- Add visible pin/unpin icon button in issue and project detail headers
- Add useReorderPins mutation with optimistic updates
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: remove drag handle and fix page refresh after reorder
- Remove GripVertical drag handle — whole item is now draggable, aligning
with other sidebar elements
- Prevent link navigation after drag using wasDragged ref
- Remove onSettled invalidation from reorder mutation to prevent
unnecessary refetch after optimistic update
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The Assign dropdown now sorts members and agents by how frequently the
current user assigns issues to them. Frequency is computed from two
sources: assignee_changed activities in the activity log and initial
assignments on issues created by the user.
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat(projects): show completion progress (done/total issues) in project list
Add a progress column to the projects list page that displays a mini progress
bar and done/total issue count for each project. Backend batch-fetches issue
stats per project using a single query for efficiency.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat(projects): show progress on project overview page
Add a progress bar with done/total (percentage) to the project detail
overview tab, computed from the already-loaded project issues.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
When pressing "C" to create a new issue from a project detail page,
automatically set the project_id so the issue is linked to the current project.
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Set webSecurity: false in BrowserWindow to bypass CORS when
connecting to remote API (standard Electron practice)
- Fix renderer dev server to port 5173 so localStorage persists
across restarts (prevents losing login state)
* fix(my-issues): use server-side filtering instead of client-side
My Issues was fetching ALL workspace issues and filtering client-side,
causing the Done column to show wrong counts (269 vs user's actual
count) and only 2-3 done issues to appear from the first 50-item page.
Backend:
- Add creator_id and assignee_ids (uuid[]) filters to ListIssues,
ListOpenIssues, and CountIssues SQL queries
- Parse creator_id and assignee_ids (comma-separated) query params
Frontend:
- Add myIssueListOptions with per-scope server-filtered queries
- Each tab now calls the API with the right filter:
Assigned → assignee_id, Created → creator_id,
My Agents → assignee_ids
- Add useLoadMoreMyDoneIssues for server-filtered done pagination
- WS events invalidate My Issues cache via issueKeys.myAll
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* refactor(my-issues): merge duplicate load-more hooks into one
Both board-view and list-view were unconditionally calling two hooks
(useLoadMoreDoneIssues + useLoadMoreMyDoneIssues) and picking one at
runtime. Merged into a single useLoadMoreDoneIssues with an optional
myIssues param so only one hook runs per render.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The Done column on My Issues was displaying the workspace-wide total
(e.g. 269) instead of the current user's done issue count, because
BoardView/ListView read doneTotal directly from the shared cache.
Add an optional doneTotal prop to BoardView and ListView so the parent
can override the displayed count. MyIssuesPage now computes the count
from the client-filtered issue list and passes it through.
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs: add v0.1.22 changelog (2026-04-10)
* docs: rewrite v0.1.22 changelog with categorized sections
- Add features/improvements/fixes categories to changelog type and component
- Remove desktop/Electron mentions (not yet released)
- Rewrite all entries with detailed descriptions based on actual commit messages
- Component renders category headers when present, falls back to flat list for older entries
- Both en and zh updated
* docs: trim v0.1.22 changelog entries for conciseness
Adds horizontal drag-and-drop reordering for the desktop tab bar using
@dnd-kit/sortable, with axis + parent constraints so tabs only slide
horizontally within the bar. Order is persisted automatically through
the existing tab-store partialize.
Also brings tab-store into the standardized storage pipeline introduced
in 85cff154 — it was the last persist store still using vanilla zustand
persist instead of createPersistStorage(defaultStorage). Storage key
multica_tabs is unchanged so existing user data is preserved.
- apps/desktop: add @dnd-kit/{core,sortable,modifiers,utilities}
- tab-store: moveTab(from, to) action via arrayMove (preserves router refs)
- tab-store: persist storage → createJSONStorage(createPersistStorage(defaultStorage))
- tab-bar: DndContext + SortableContext(horizontalListSortingStrategy)
- tab-bar: restrictToHorizontalAxis + restrictToParentElement modifiers
- tab-bar: PointerSensor distance:5 to disambiguate click vs drag
- tab-bar: stopPropagation on close-button pointerdown to avoid drag start
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Desktop app was missing Geist font — the CSS variable `--font-sans` referenced
by `@theme inline` in tokens.css was never defined, causing fallback to the
Chromium default system font. Web app worked because Next.js `next/font/google`
injected the variable.
Fix: add @fontsource/geist-sans and @fontsource/geist-mono, import the font
CSS in main.tsx, and define --font-sans/--font-mono in globals.css.
Closes MUL-504
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(views): support multiline display for agent text content
- TextRow in agent-live-card: show collapsible multiline content instead
of only the last line
- Chat user message bubble: add whitespace-pre-wrap to preserve line breaks
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* revert: remove out-of-scope TextRow change in agent-live-card
Only the chat bubble multiline fix is needed for this issue.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
input-otp sets internal timers that fire after jsdom tears down window,
causing "ReferenceError: window is not defined" unhandled errors in CI.
Using fake timers suite-wide ensures no real timers escape after cleanup.
The idx_one_pending_task_per_issue index only allowed one pending task
per issue across all agents, causing different agents' queued/dispatched
tasks to block each other. This mismatched the code-level dedup which
checks per (issue_id, agent_id). Replace with idx_one_pending_task_per_issue_agent
on (issue_id, agent_id) so each agent can independently have one pending task.
Fixes MUL-495
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(views): improve daily token usage chart readability
- Fix Y-axis showing scrambled/truncated tick labels by computing
explicit nice ticks and using compact number formatting (100M not 100.0M)
- Simplify token categories from 4 (Input/Output/Cache Read/Cache Write)
to 3 (Input/Output/Cached) — cache write merged into input
- Replace noisy stacked area chart with clean single-area total trend,
with a custom tooltip showing per-category breakdown and total
- Increase Y-axis width to prevent label clipping
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(views): handle floating point edge case in formatTokens
Use modulo + threshold instead of Number.isInteger to avoid floating
point precision issues (e.g. 2.5M * 4 = 10.000000000000004).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(views): keep 4 token categories consistent between chart tooltip and summary cards
Revert the 3-category simplification (Cached/Input/Output) back to the
original 4 categories (Input/Output/Cache Read/Cache Write) so the chart
tooltip matches the summary cards on the same page.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat(daemon): add minimum Claude Code version check during runtime registration
The daemon now validates the detected agent CLI version against a
minimum requirement before registering a runtime. Claude Code requires
>= 2.0.0 (when --output-format stream-json and --permission-mode
bypassPermissions were introduced). Older versions are skipped with a
warning log, preventing silent failures.
Closes#569
* feat(daemon): add minimum Codex CLI version check (>= 0.100.0)
The `codex app-server --listen stdio://` flag was introduced in v0.100.0.
Older versions lack this flag and fail silently. Add codex to the
MinVersions map so the daemon skips outdated codex CLIs with a clear
warning, matching the existing Claude version check.
Refs #490
Previously logout only removed multica_token, leaving workspace_id
and TanStack Query cache intact — a security issue on shared devices.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add showDropOverlay={false} to projects/ ContentEditors (no upload support)
- Use barrel exports from ../../editor instead of direct file imports
- Remove ring-brand/30 from CommentInput for visual consistency
- Remove dead internal overlay code from ContentEditor (dragOver state,
drag handlers, overlay JSX, document listeners, showDropOverlay prop)
- Remove unused .editor-drop-overlay CSS
- Update issue-detail test mock with useFileDropZone/FileDropOverlay
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat(views): add "C" keyboard shortcut to open new issue modal
Adds a global keyboard shortcut matching Linear's convention — pressing
"C" when not focused on an input/editor opens the create-issue modal.
Also displays the shortcut hint in the sidebar button.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(views): match "C" shortcut badge style to search ⌘K badge
Use the same kbd styling (rounded border, bg-muted, font-mono) as the
search trigger so the two shortcut hints look consistent.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>