Commit Graph

407 Commits

Author SHA1 Message Date
Jiang Bohan
93fffad82a fix(daemon): embed triggering comment content in agent prompt
When a task is triggered by a comment, the agent prompt now includes
the comment content directly. This prevents the agent from ignoring
the comment when stale output files exist in a reused workdir.

Closes #805
2026-04-13 14:20:26 +08:00
Jiang Bohan
2ea778796a feat(daemon): add token usage log scanning for OpenCode, OpenClaw, and Hermes runtimes
Previously only Claude and Codex had log-scanning-level token usage
reporting (Flow B). This adds scanners for the remaining three runtimes:

- OpenCode: reads JSON message files from ~/.local/share/opencode/storage/message/
- OpenClaw: reads JSONL session files from ~/.openclaw/agents/*/sessions/
- Hermes: reads JSONL session files from ~/.hermes/sessions/

All three are registered in Scanner.Scan() and follow the same
(date, provider, model) aggregation pattern as existing scanners.
2026-04-13 13:42:05 +08:00
LinYushen
e20c507dcc fix(security): add Content-Security-Policy response header (#822)
Adds CSP middleware to the global middleware chain as a browser-level
defense against XSS: script-src 'self', object-src 'none',
frame-ancestors 'none', base-uri 'self', form-action 'self'.

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 12:53:39 +08:00
Zohar Babin
77dbcaefad feat(cli): add --content-stdin flag to issue comment add
Allow agents to pipe comment content through stdin instead of the
--content flag, avoiding shell escaping issues with backticks, quotes,
and other special characters in markdown content.

Usage: cat <<'COMMENT' | multica issue comment add <id> --content-stdin

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-13 00:23:16 -04:00
LinYushen
95bfd7dd96 feat(auth): migrate auth token to HttpOnly Cookie & WebSocket Origin whitelist (#819)
* feat(auth): migrate auth token to HttpOnly cookie & implement WebSocket Origin whitelist

Security improvements from the MUL-566 audit report:

1. Auth token is now set as an HttpOnly, SameSite=Lax cookie on login,
   preventing XSS-based token theft. Cookie-based auth includes CSRF
   protection via double-submit cookie pattern. The Authorization header
   path is preserved for Electron desktop app and CLI/PAT clients.

2. WebSocket upgrader now validates the Origin header against a
   configurable allowlist (ALLOWED_ORIGINS env var), rejecting
   connections from unauthorized origins.

Backend: new auth cookie helpers, middleware reads cookie as fallback,
WS handler accepts cookie auth, Origin whitelist, logout endpoint.
Frontend: CSRF token in API headers, cookie-aware auth store and WS
client, web app opts into cookieAuth mode while desktop keeps tokens.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(auth): address PR review — Strict cookies, HMAC-bound CSRF, origin sync

1. SameSite=Lax → SameSite=Strict per spec requirement
2. CSRF token now HMAC-signed with auth token (nonce.signature format),
   preventing subdomain cookie injection attacks
3. allowedWSOrigins uses atomic.Value to eliminate data race
4. Removed magic "cookie" sentinel string in WSProvider — pass null token
   and guard with boolean check instead
5. Removed dead delete uploadHeaders["Content-Type"] in API client

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 12:13:35 +08:00
Jiang Bohan
b271e8915e fix(ws): include issue_id in task:dispatch event to prevent cross-issue UI glitch
broadcastTaskDispatch was the only task event broadcast missing issue_id
in its WebSocket payload. The frontend task:dispatch handler had no way
to filter by issue, causing AgentLiveCard to briefly show activity for
the wrong issue when multiple tabs are open.

Closes https://github.com/multica-ai/multica/issues/791
2026-04-13 01:50:10 +08:00
bulai0408
47eb6cb612 fix(agent): enable network access for Codex sandbox so Multica CLI can reach API
Codex tasks running in workspace-write sandbox mode could not resolve
api.multica.ai because the hardcoded sandbox parameter in thread/start
overrode any config.toml settings, and the default sandbox policy blocks
network access.

Changes:
- Remove hardcoded `sandbox: "workspace-write"` from thread/start RPC —
  let Codex read sandbox config from its own config.toml instead
- Auto-generate config.toml in per-task CODEX_HOME with
  `sandbox_mode = "workspace-write"` and `network_access = true`,
  preserving any existing user settings
- Fix Reuse() to restore CodexHome for Codex provider on workdir reuse

Closes #368
2026-04-13 01:03:43 +08:00
Bohan Jiang
1ee4e0501a fix(handler): add .claude/skills/ candidate path for skills.sh import (#792)
Skills stored under .claude/skills/{name}/SKILL.md (the Claude Code
native discovery convention) were not found during skills.sh import,
causing a 502 error. Add this path to the candidate list.

Fixes #777
2026-04-12 23:39:46 +08:00
Bohan Jiang
d74d7f2b7b fix(handler): add cycle detection to BatchUpdateIssues parent_issue_id handling (#788)
BatchUpdateIssues was missing the ancestor-walk cycle detection that
single UpdateIssue has. This allowed creating circular parent
relationships (e.g. A→B→A) via the batch API. Added the same
depth-limited walk (up to 10 ancestors) to detect and skip issues
that would create cycles, consistent with UpdateIssue behavior.
2026-04-12 23:03:46 +08:00
Qiaochu Hu
0c2102b951 fix(handler): fix batch operations and error handling bugs (#779)
fix(handler): fix batch operations and error handling bugs
2026-04-12 23:00:40 +08:00
Jiang Bohan
7312b5650c fix(server): fix ListRuntimeUsage to filter by date range instead of row count (#765)
Replace LIMIT $2 with AND date >= $2 in ListRuntimeUsage query. When a
runtime uses multiple models each day has multiple rows, so a row LIMIT
silently returns fewer days than requested.

Also fixes displayName warnings in issue-detail test mocks and adds
missing setOpen to useCallback deps in search-command.

Co-authored-by: jayavibhavnk <jaya11vibhav@gmail.com>
Closes #731
2026-04-12 22:46:07 +08:00
Jiayuan Zhang
d7c83bc285 fix(sanitize): preserve code blocks and inline code from HTML entity escaping (#774)
Bluemonday operates on raw text, so characters like && and <> inside
markdown code blocks/inline code were being HTML-escaped (e.g. && → &amp;&amp;),
causing them to render incorrectly in the frontend.

Now extracts fenced code blocks and inline code spans before sanitization,
runs bluemonday on the remaining content, then restores the code verbatim.
2026-04-12 21:32:35 +08:00
Bohan Jiang
d011039c58 fix(sweeper): add error logging and dedup for issue reset (#762)
- Log a warning when HasActiveTaskForIssue fails, matching the existing
  pattern for UpdateIssueStatus errors. Silent failures here make
  debugging DB issues unnecessarily difficult.
- Track processed issues to skip redundant GetIssue + HasActiveTaskForIssue
  queries when multiple tasks for the same issue are swept in one cycle.
2026-04-12 14:13:07 +08:00
pradeep7127
bd42552854 fix(sweeper): reset in_progress issues to todo after stale task sweep (#747)
fix(sweeper): reset in_progress issues to todo after stale task sweep
2026-04-12 14:08:54 +08:00
Bohan Jiang
31eeb00b59 fix(storage): clean up variable shadowing and dead code (#761)
- Rename `filepath` local var to `dest` in LocalStorage.Upload to avoid
  shadowing the path/filepath package import
- Remove unused detectContentType and overrideContentType functions from
  util.go (no longer needed after ServeFile switched to http.ServeFile)
2026-04-12 14:06:46 +08:00
Antar Das
d32c419b6d feat(storage): add local file storage fallback (#710)
* feat(storage): add local file storage fallback

- Add local storage implementation for file uploads
- Update .env.example with LOCAL_UPLOAD_DIR and LOCAL_UPLOAD_BASE_URL
- Integrate local storage into server router and handlers
- Add storage abstraction layer with util functions

* ♻️ refactor(storage): improve path handling and file serving

switch from path to filepath for better cross-platform support and replace manual file serving logic with http.ServeFile to enhance security against path traversal. update unit tests to use t.Setenv for cleaner environment variable management.
2026-04-12 14:04:22 +08:00
Sanjay Ramadugu
f99f50eb0c feat(daemon): add Google Gemini CLI backend
Registers `gemini` as a sixth supported agent provider alongside claude,
codex, opencode, openclaw, and hermes.

- Daemon config probes for `gemini` on PATH (MULTICA_GEMINI_PATH /
  MULTICA_GEMINI_MODEL env overrides mirror the other providers).
- New agent.geminiBackend in pkg/agent/gemini.go: spawns
  `gemini -p <prompt> --yolo -o text [-m <model>] [-r <session>]`,
  reads stdout to completion, and returns a single MessageText plus
  the standard Result struct (Status / Output / DurationMs).
- Execution environment writes a GEMINI.md file into the task workdir
  (mirroring the existing CLAUDE.md / AGENTS.md injection for other
  providers) so Gemini discovers the Multica runtime meta-skill
  through its native mechanism.

Tests:

- pkg/agent/gemini_test.go — unit coverage for buildGeminiArgs
  (baseline, model override, resume session, omit-when-empty).
- internal/daemon/execenv/TestInjectRuntimeConfigGemini — verifies
  GEMINI.md is written and that CLAUDE.md/AGENTS.md are NOT.

Scope (intentional for v1):

- Text output only (`-o text`). Streaming tool events via
  `--output-format stream-json` is a follow-up once we have a
  reliable reproduction of Gemini's event schema.
- No MCP config plumbing. Gemini's `--allowed-mcp-server-names`
  filter pairs well with the per-agent MCP work on feat/per-agent-mcp;
  stacking the two can land as a follow-up.
- No token usage scraping (Gemini's accounting lives on the Google
  Cloud side, not a local JSONL log like claude/codex).
- No session resume wiring beyond accepting the ExecOptions field —
  the daemon does not yet persist Gemini session IDs because the text
  output mode does not expose them.

Migration / env changes:

- New optional environment variables MULTICA_GEMINI_PATH and
  MULTICA_GEMINI_MODEL. Default path is the string "gemini" (resolved
  via PATH at daemon startup). If no Gemini install is detected, the
  provider is simply absent from the runtime — no behavior change for
  existing deployments.
2026-04-11 22:58:49 -04:00
Jiayuan Zhang
f100b5b707 fix(auth): graceful email degradation for self-hosting (#742)
* fix(auth): log email send errors and gracefully degrade in non-production

In non-production environments (APP_ENV != "production"), if sending the
verification code email fails, log the error as a warning and still return
success. This lets self-hosting users log in with the master code (888888)
even when their Resend configuration is incomplete (e.g. unverified from-domain).

In production, the behavior is unchanged — email failures return 500.

Also adds guidance in .env.example about RESEND_FROM_EMAIL for self-hosters.

Closes #723

* fix(auth): remove APP_ENV degradation, keep error logging only

Remove the APP_ENV-based graceful degradation for email send failures
— it's risky if users forget to set APP_ENV=production. Instead, always
return 500 on email failure (safe for production) and rely on the error
log (slog.Error) with the actual Resend error for debugging.

Self-hosters who don't need real emails should leave RESEND_API_KEY empty
(codes print to stdout, master code 888888 works).
2026-04-12 02:30:01 +08:00
Jiayuan Zhang
701399536f feat(cli): enhance version command with JSON output and build info (#740)
Add --output json flag, build date, Go version, and OS/arch to the
version command. Update Makefile and goreleaser to inject build date.
2026-04-12 02:18:08 +08:00
Jiayuan Zhang
d78be3b621 fix(cli): ensure cloud URLs are configured when not using local mode (#733)
After installing via `curl | bash` (default/cloud mode) or running
`multica setup` without a local server, the CLI config could retain
stale localhost URLs from a previous `multica config local` or
`--local` install. This caused `multica login` to connect to
localhost instead of multica.ai.

Fix: explicitly write cloud URLs (api.multica.ai / multica.ai) to
the config in both the install script's cloud mode and the setup
command's cloud fallback path.
2026-04-12 01:09:17 +08:00
Jiayuan Zhang
b0ee214154 feat: streamline self-hosting with one-click setup (#724)
* feat: streamline self-hosting experience with one-click setup

- Add `make selfhost` / `make selfhost-stop` for one-command Docker deployment
- Add `multica setup` CLI command (auto-detect local server, configure, login, start daemon)
- Add `multica config local` CLI command (configure for localhost defaults)
- Restructure SELF_HOSTING.md: simplified 4-step guide, moved advanced config to SELF_HOSTING_ADVANCED.md
- Add SELF_HOSTING_AI.md for AI agents to follow
- Document 888888 master verification code for non-production environments
- Document how to stop services
- Fix brew install typo: `multica-cli` → `multica` in SELF_HOSTING.md and self-hosting.mdx
- Update README.md and README.zh-CN.md with simplified self-host instructions
- Update CLI_AND_DAEMON.md with new setup/config local commands

* feat: add one-command installer script (curl | bash)

Add scripts/install.sh that handles the full setup in one command:

Self-host (default):
  curl -fsSL .../install.sh | bash
  → Checks Docker, clones repo, starts services, installs CLI, configures

Cloud (CLI only):
  curl -fsSL .../install.sh | bash -s -- --cloud
  → Installs CLI via Homebrew or binary download

Features:
- OS detection (macOS/Linux) with architecture support (amd64/arm64)
- Homebrew install with binary download fallback
- Idempotent: re-running updates existing installation
- Colored output with non-TTY fallback
- Docker availability check with helpful error messages

Updated docs (README, SELF_HOSTING, self-hosting.mdx, SELF_HOSTING_AI) to
show curl | bash as the primary install method.

* refactor: default install to cloud mode, add --local for self-host

- install.sh default is now cloud (CLI only, connects to multica.ai)
- Self-host uses --local flag: curl ... | bash -s -- --local
- Restructured README following Hermes Agent style:
  - Quick Install section front and center with curl | bash
  - CLI command reference table
  - Self-host as a callout under Quick Install
  - Removed redundant "Multica Cloud" / "CLI" sections
- Updated all docs (SELF_HOSTING, self-hosting.mdx, SELF_HOSTING_AI,
  README.zh-CN) to use --local flag for self-host curl command

* docs: remove redundant AI agent install snippet from README CLI section

* docs: add daemon stop command to README quick install sections

* feat: add --stop flag to install.sh for easy self-host shutdown

Users who installed via `curl ... | bash -s -- --local` can now stop
all services with `curl ... | bash -s -- --stop`. The stop command
shuts down Docker Compose services and the daemon.

Also updated SELF_HOSTING.md stopping section to show both methods.
2026-04-12 00:50:17 +08:00
Bohan Jiang
d11824807a fix(agent): handle braces in stderr log lines before openclaw JSON result (#718)
processOutput() used strings.Index(raw, "{") to find the JSON start,
but error lines like `raw_params={"command":"..."}` contain braces that
get matched first, causing JSON parsing to fail and the entire raw
stderr (including internal metadata) to be returned as the agent comment.

Now tries each '{' position until one successfully unmarshals as a valid
openclawResult, skipping braces embedded in log/error lines.
2026-04-11 23:07:58 +08:00
Bohan Jiang
e477d64548 fix(cli): poll health endpoint instead of fixed sleep in daemon start (#716)
* fix(cli): poll health endpoint instead of fixed sleep in daemon start

The daemon start command waited a fixed 2 seconds then checked the
health endpoint once. If the daemon took longer to initialize (auth,
workspace loading), the check failed and printed a misleading error
even though the daemon started successfully.

Replace the single check with a polling loop (500ms interval, 15s
timeout) so the CLI waits for the daemon to actually be ready.

* fix(agent): rewrite openclaw tests to match new backend API

The openclaw backend was rewritten in #715 to parse a single JSON blob
instead of streaming NDJSON events. The tests still referenced the old
types (openclawEvent) and methods (handleOCTextEvent, etc.), causing a
build failure in CI.

Rewrite all tests to exercise the new processOutput method and
openclawInt64 helper.
2026-04-11 22:25:19 +08:00
Bohan Jiang
2e33084097 fix(agent): rewrite openclaw backend to match actual CLI interface (#715)
* fix(agent): use --message flag for OpenClaw CLI invocation

OpenClaw CLI changed its prompt flag from `-p` to `--message`. The old
flag caused tasks to fail immediately with "required option '-m,
--message <text>' not specified".

Fixes #713, relates to #703.

* fix(agent): rewrite openclaw backend to match actual CLI interface

- Replace unsupported flags (-p, --output-format, --yes) with correct
  ones (--message, --json, --local, --session-id)
- Read JSON result from stderr (where openclaw writes it)
- Parse openclaw's actual output format ({payloads, meta})
- Auto-generate session ID for each task execution
- Show "live log not available" hint in agent live card when timeline
  is empty (openclaw doesn't support streaming)
2026-04-11 22:14:47 +08:00
Jiayuan Zhang
b3f98ef95d fix(server): skip auto-comment when agent already posted during task (#712)
* fix(server): skip auto-comment when agent already posted during task

In CompleteTask(), check if the agent already posted a comment on the
issue since the task started. If so, skip the automatic output comment
to avoid duplicates. This preserves the fallback for agents that don't
post comments via CLI.

Closes MUL-609

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(server): use StartedAt instead of CreatedAt for duplicate check

CreatedAt is the enqueue time, not execution start. If a previous task
posted a comment between enqueue and start of the next task, it would
incorrectly suppress the auto-comment for the later task.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-11 21:27:02 +08:00
pradeep7127
d9be9465c3 fix(storage): support custom S3 endpoints for self-hosted deployments (MinIO) (#681)
* 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>
2026-04-11 20:35:31 +08:00
zerone0x
cc9a8ad6ec fix(daemon): make meta-skill workflow defer to agent Skills instead of hardcoding (#675)
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>
2026-04-11 14:48:58 +08:00
jayavibhavnk
41d4ac3877 fix(server): add missing WorkspaceID to agent comment creation (#688)
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.
2026-04-11 14:38:40 +08:00
Zheng Li
a76194744a feat(cli): add --project filter to issue list (#691)
Co-authored-by: nocoo <nocoo@users.noreply.github.com>
2026-04-11 14:37:24 +08:00
Jiayuan Zhang
7008d03b02 feat: notify parent issue subscribers on sub-issue changes (#685)
* 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>
2026-04-11 14:33:00 +08:00
Bohan Jiang
5956280d56 fix(server): don't inherit parent agent mentions when reply has its own mentions (#693)
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.
2026-04-11 14:29:01 +08:00
Jiayuan Zhang
82bbce98fd fix(security): add workspace ownership checks to daemon API routes (#684)
* 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>
2026-04-11 12:49:23 +08:00
Jiayuan Zhang
f4016fc721 fix(server): validate workspace ownership for attachment uploads and queries (#683)
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>
2026-04-11 04:33:24 +08:00
Jiayuan Zhang
6c5879215d fix: sanitize markdown rendering in comments and shared renderers (#679)
* 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>
2026-04-11 03:44:30 +08:00
Jiayuan Zhang
904192b45c fix(web): correct project kanban issue counts (#667) 2026-04-10 21:13:25 +08:00
Jiayuan Zhang
f1d81cdfaa feat(search): add project search support to Cmd+K search (#663)
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>
2026-04-10 20:59:32 +08:00
Jiayuan Zhang
9b62485a86 feat: add pin to sidebar for issues and projects (#653)
* 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>
2026-04-10 19:00:25 +08:00
Jiayuan Zhang
cce210ed3a feat(assign): sort members & agents by user's assignment frequency (#652)
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>
2026-04-10 18:45:08 +08:00
Jiayuan Zhang
356ff002dd feat(projects): show completion progress in project list (#651)
* 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>
2026-04-10 18:36:49 +08:00
LinYushen
b52c048c8e fix(my-issues): use server-side filtering instead of client-side (#649)
* 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>
2026-04-10 17:54:13 +08:00
Jiayuan Zhang
bf8abba24d fix(db): relax pending task unique index to per-(issue, agent) (#637)
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>
2026-04-10 16:21:20 +08:00
Bohan Jiang
28b9bf85ee feat(daemon): add minimum Claude Code version check (#625)
* 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
2026-04-10 16:09:56 +08:00
Jiayuan Zhang
17ad3b2f3b Merge pull request #618 from multica-ai/agent/emacs/2023d753
feat(dx): simplify local dev and self-hosting setup
2026-04-10 15:46:27 +08:00
Bohan Jiang
ee3c849c52 fix(skills): detect GitHub default branch instead of hardcoding "main" for skills.sh imports (#632)
Repos hosted on GitHub can use any branch name as default (main, master, etc.).
The skills.sh import was hardcoding "main" in raw.githubusercontent.com URLs,
causing 404s when fetching SKILL.md from repos with a different default branch.

Now queries the GitHub API (/repos/{owner}/{repo}) to get the actual default
branch before fetching files.

Fixes #517
2026-04-10 15:44:18 +08:00
LinYushen
a25886102a feat(agent): add Hermes Agent Provider via ACP protocol (#623)
* feat(agent): add Hermes Agent Provider via ACP protocol

Integrate Hermes as a new agent backend using the ACP (Agent
Communication Protocol) JSON-RPC 2.0 over stdio — the same pattern
as the Codex provider but with ACP-specific methods.

- New hermesBackend spawns `hermes acp` and drives initialize →
  session/new → session/prompt lifecycle
- Handles session/update notifications: agent_message_chunk,
  agent_thought_chunk, tool_call, tool_call_update, usage_update
- Auto-approves tool executions via HERMES_YOLO_MODE env var
- Supports session resume, model override, system prompt injection
- Token usage extracted from PromptResponse and usage_update events
- Auto-detected at daemon startup via MULTICA_HERMES_PATH env var

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* feat(ui): optimize runtime icons and fix create-agent dialog overflow

- Replace OpenClaw pixel-art icon (32 rects) with clean vector paths
- Add Hermes provider icon (NousResearch mascot, 48x48 webp data URI)
- Use provider-specific icons in runtime selector instead of generic Monitor
- Fix dialog overflow: add min-w-0 to grid item so truncate works

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(agent): add required mcpServers param to Hermes ACP session/new

ACP SDK v0.11.2 requires mcpServers as a mandatory field in
NewSessionRequest. Without it, Pydantic validation fails with
"Invalid params" and the agent immediately errors out.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 15:40:11 +08:00
Jiayuan Zhang
2c1d1d989c fix(daemon): symlink Codex sessions dir to shared home for discoverability (#627)
Per-task CODEX_HOME isolated session logs in per-task directories, making
them invisible from the global ~/.codex/sessions/ where users expect to
find them. Symlink the sessions directory back to the shared home so
Codex writes session logs to the global location while keeping skills
isolated per task.

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 15:38:34 +08:00
Jiayuan Zhang
c7e5aedb14 fix(server): add startup warnings for missing JWT_SECRET and RESEND_API_KEY
When these env vars are not configured, the server now prints clear
warning messages at startup so users know what to fix.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 15:17:53 +08:00
Yevanchen
63895343e3 Fix Claude stream-json startup hangs (#592) 2026-04-10 14:42:28 +08:00
Bohan Jiang
88982ad23f feat(issues): display token usage per issue in detail sidebar (#581)
* feat(issues): display token usage per issue in detail sidebar

Add a new "Token usage" section to the issue detail right sidebar that
shows aggregated input/output tokens, cache tokens, and run count across
all tasks for the issue. Backed by a new SQL query and API endpoint.

* fix(db): add index on agent_task_queue(issue_id) for usage queries

The GetIssueUsageSummary query joins agent_task_queue filtered by
issue_id across all statuses. The existing partial index (migration 022)
only covers queued/dispatched rows, so completed tasks require a
sequential scan. Add a general index to prevent performance degradation
as task volume grows.
2026-04-10 14:34:32 +08:00
LinYushen
7620a5a7e9 fix(search): LOWER/LIKE for pg_bigm 1.2 index compatibility (#621)
* fix(search): use LOWER/LIKE instead of ILIKE for pg_bigm 1.2 compatibility

pg_bigm 1.2 on RDS does not support ILIKE index scans. Replace all
ILIKE expressions with LOWER(column) LIKE LOWER(pattern) so the GIN
indexes are utilized. Rebuild gin_bigm_ops indexes on LOWER() expressions.

Closes MUL-482

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(search): lowercase pattern in Go, add buildSearchQuery unit tests

- Lowercase phrase/terms in Go (strings.ToLower) so SQL only needs
  LOWER() on the column side, avoiding redundant per-query LOWER() on
  the pattern
- Add 5 unit tests for buildSearchQuery asserting SQL shape: no ILIKE,
  LOWER on columns only, lowercased args, multi-term AND, number match,
  include-closed flag, special char escaping

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 14:29:00 +08:00