Commit graph

1550 commits

Author SHA1 Message Date
LinYushen
fe0968d96f
Merge pull request #213 from multica-ai/feature/file-upload-cloudfront
feat(upload): file upload API with S3 + CloudFront signed cookies
2026-03-31 16:00:19 +08:00
yushen
1e22633301 Merge remote-tracking branch 'origin/main' into feature/file-upload-cloudfront
# Conflicts:
#	apps/web/components/common/rich-text-editor.tsx
#	apps/web/features/issues/components/comment-card.tsx
#	apps/web/package.json
#	pnpm-lock.yaml
2026-03-31 15:59:46 +08:00
yushen
9e23fb76fc fix(upload): harden upload flow — sanitize filenames, refresh CF cookies, deduplicate handlers
- Sanitize Content-Disposition filenames to prevent header injection (strip control chars, quotes, semicolons)
- Add CloudFront cookie refresh middleware so cookies are re-issued when expired
- Log errors in groupAttachments instead of silently swallowing them
- Move useFileUpload hook to shared/hooks/ per project architecture conventions
- Add uploadWithToast helper to deduplicate try/catch/toast pattern across 3 components
- Refactor ApiClient.uploadFile to reuse auth headers, 401 handling, and error parsing
- Allow empty MIME types client-side (let server sniff and decide)
- Constrain Image extension max-width in rich-text-editor to prevent layout overflow

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-31 15:52:40 +08:00
Jiayuan Zhang
5a7b34cab5
Merge pull request #211 from multica-ai/agent/lambda/5e70a174
fix(agent): fix agent visibility defaults and permission model
2026-03-31 15:50:07 +08:00
Jiayuan
feb5f57662 fix: merge main and renumber migration 028 → 030
Main now has 028 (task_trigger_comment) and 029 (daemon_token).
Renumber agent_default_private migration to 030 to avoid conflict.
2026-03-31 15:47:09 +08:00
Jiayuan
ac17fd33bf Merge remote-tracking branch 'origin/main' into agent/lambda/5e70a174 2026-03-31 15:45:59 +08:00
Jiayuan Zhang
a2ea8197e2
Merge pull request #221 from multica-ai/agent/lambda/bd1d07a6
feat(server): trigger agents via @mention in comments
2026-03-31 15:44:41 +08:00
Jiayuan
7aea32cb33 fix(server): suppress assignee on_comment when mentions target others
When a comment @mentions anyone but not the assignee agent, the
assignee's on_comment trigger is now suppressed. This prevents the
assignee agent from being re-triggered when users share results with
colleagues or ask other agents for help.

The rule: @mention is an intent signal — if you're talking to someone
else, the assignee agent should not respond.
2026-03-31 15:42:54 +08:00
yushen
f5353c6691 feat(upload): signed URLs for CLI + eager load attachments on comments
- Add CloudFrontSigner.SignedURL() for generating per-resource signed URLs
- Attachment responses include download_url (5-min signed URL for CLI)
- Eager load attachments on comments and timeline (same pattern as reactions)
- Add ListAttachmentsByCommentIDs query for batch loading
- Update Comment and TimelineEntry types with attachments field

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-31 15:42:10 +08:00
Jiayuan Zhang
7b68dd8dda
Merge pull request #222 from multica-ai/revert/daemon-auth
revert: daemon authentication for API routes (#214)
2026-03-31 15:33:02 +08:00
Jiayuan
35f77de9cc Revert "Merge pull request #214 from multica-ai/agent/lambda/4771d426"
This reverts commit cfd2fdf70f, reversing
changes made to 987984431b.
2026-03-31 15:30:51 +08:00
Jiayuan
37881adbed feat(server): trigger agents via @mention in comments
When a user @mentions an agent in any issue's comment, the system now
enqueues a task for that agent. The agent reads the issue context and
replies to the triggering comment thread.

Changes:
- Add shared util.ParseMentions for mention parsing (used by both
  comment handler and notification listeners)
- Add EnqueueTaskForMention to TaskService for explicit agent targeting
- Add on_mention trigger type support in agent trigger config
- Add HasPendingTaskForIssueAndAgent SQL query for per-agent dedup
- Add enqueueMentionedAgentTasks in CreateComment handler

Safety: prevents self-trigger (agent mentioning itself), dedup with
assignee on_comment trigger, terminal issue status check, and per-agent
pending task dedup.
2026-03-31 15:30:24 +08:00
yushen
15f96468be feat(upload): add attachment table for tracking uploaded files
- Add attachment table with workspace/issue/comment associations
- Upload handler creates attachment record when workspace context exists
- Add GET /api/issues/{id}/attachments and DELETE /api/attachments/{id}
- Frontend passes issueId context during uploads for tracking
- Add Attachment type, listAttachments, deleteAttachment to API client

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-31 15:29:41 +08:00
Bohan Jiang
c44da53080
Merge pull request #215 from multica-ai/agent/j/92dbd4ba
feat(web): add OK emoji to reaction quick bar
2026-03-31 15:28:31 +08:00
Bohan Jiang
a5a2673642
fix(editor): use correct getMarkdown API for @tiptap/markdown (#219)
The migration from tiptap-markdown to @tiptap/markdown (38e92040)
changed the getMarkdown API location. The old package stored it at
editor.storage.markdown.getMarkdown(), but @tiptap/markdown adds it
directly as editor.getMarkdown(). This caused getEditorMarkdown() to
always return "", preventing description (and any markdown content)
from being saved when creating or editing issues/comments.
2026-03-31 15:28:02 +08:00
Jiang Bohan
518d4449d7 feat(web): move OK emoji to 2nd position, remove thumbs down 2026-03-31 15:27:13 +08:00
Jiayuan Zhang
cfd2fdf70f
Merge pull request #214 from multica-ai/agent/lambda/4771d426
feat(daemon): add authentication for daemon API routes
2026-03-31 15:27:11 +08:00
LinYushen
987984431b
feat(cli): add multica update command (#218)
* feat(cli): add `multica update` command

Detects whether multica was installed via Homebrew (by resolving the
binary symlink and checking if it lives under a Homebrew prefix).

- Brew installs: runs `brew upgrade multica` automatically.
- Non-brew installs: prints instructions for installing via brew or
  downloading from GitHub releases.
- Checks latest version from the GitHub releases API and skips
  the update if already up to date.

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

* fix(cli): use fully-qualified tap name in brew upgrade

Use `brew upgrade multica-ai/tap/multica` instead of `brew upgrade multica`
to avoid any potential name collision with core formulae.

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-03-31 15:24:05 +08:00
Bohan Jiang
d8a8549c8a
fix(editor): use correct getMarkdown API for @tiptap/markdown (#217)
The migration from tiptap-markdown to @tiptap/markdown in 38e92040
broke comment creation. The old package stored getMarkdown() on
editor.storage.markdown, but the official @tiptap/markdown extension
adds it directly to the editor instance (editor.getMarkdown()).

This caused getEditorMarkdown() to always return "", making the
submit button permanently disabled and preventing any comments.

Also fix stale submitting ref in useIssueTimeline dependency array.
2026-03-31 15:23:13 +08:00
Bohan Jiang
461dad0dd5
perf(web): parallelize auth init and non-blocking dashboard layout (#220)
- Fire getMe() and listWorkspaces() in parallel instead of serially,
  saving one network round-trip (~200ms on cloud)
- Render dashboard sidebar shell immediately once user is authenticated,
  show loading indicator in content area while workspace hydrates

Closes MUL-41
2026-03-31 15:22:58 +08:00
yushen
423aa38888 feat(upload): add file upload UI — avatar, editor paste/drop, attachments
- Add uploadFile method to ApiClient (FormData + 401 handling)
- Add useFileUpload hook with client-side validation
- ActorAvatar renders actual avatar images with fallback to initials
- Account settings: replace URL input with clickable avatar upload
- RichTextEditor: add Image extension, paste/drop/insertFile support
- Markdown renderer: add img component for uploaded images
- CommentInput & ReplyInput: add paperclip button for file attachments
- Issue description: paste/drop file upload support

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-31 15:17:54 +08:00
Jiayuan
497fce0061 fix(tests): merge main, renumber migration, fix execenv test assertions
Merge main to pick up 028_task_trigger_comment migration. Renumber
daemon_token migration to 029. Fix execenv tests that expected CLI hints
in issue_context.md after they were moved to CLAUDE.md.
2026-03-31 15:17:09 +08:00
Jiayuan
9f56f6af81 feat(daemon): add authentication for daemon API routes
Issue daemon auth tokens (mdt_) on pairing session claim, bound to
workspace_id + daemon_id with 1-year expiry. Add DaemonAuth middleware
that validates these tokens and falls back to JWT/PAT for backward
compatibility. Apply middleware to all daemon routes except pairing
endpoints.
2026-03-31 15:17:09 +08:00
Jiayuan Zhang
56b66908a1
Merge pull request #216 from multica-ai/agent/lambda/92e0a175
feat(inbox): support archiving individual messages from list
2026-03-31 15:16:48 +08:00
Jiayuan
1054e218ed fix(daemon): update execenv tests to match current renderIssueContext output
CLI hints like "multica issue get" were moved to CLAUDE.md and are no
longer rendered into issue_context.md. Remove stale assertions.
2026-03-31 15:15:06 +08:00
Jiayuan Zhang
7d9d6793bc
Merge pull request #210 from multica-ai/agent/lambda/cbe4d468
feat(issues): add collapsible toggle for comment replies
2026-03-31 15:12:26 +08:00
Jiayuan
9ba12dffd0 fix(issues): remove duplicate commenter header in collapsible comment
The parent comment's header (avatar, name, time, context menu) is now
the collapsible trigger itself. Only the body, reactions, replies, and
reply input collapse — the header is always visible. This removes the
duplicate author info that appeared when expanded.
2026-03-31 15:07:41 +08:00
Jiayuan
8395479653 feat(inbox): add archive button on individual inbox list items
Show an archive icon on hover for each inbox list item, allowing
users to archive a single message directly from the list without
needing to open the detail panel first.
2026-03-31 15:07:37 +08:00
yushen
978a5af5de fix(upload): remove unnecessary uploads/ key prefix
Single-purpose bucket with randomized hex keys doesn't benefit from
a prefix — no lifecycle policies or access controls scoped to it.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-31 15:01:33 +08:00
yushen
c27b7bab5e fix(upload): sniff content type, sanitize filename, add key prefix
- Use http.DetectContentType() instead of trusting client-declared MIME type
- Sanitize quotes in filename for Content-Disposition header injection
- Add uploads/ prefix to S3 keys for better organization

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-31 14:58:52 +08:00
yushen
edf4c00c08 fix(upload): add file type/size validation, Content-Disposition header
- Add content type allowlist (images, PDF, text, video, audio, zip)
- Enforce 10 MB upload limit via http.MaxBytesReader
- Set Content-Disposition on S3 objects for proper download filenames
- Remove unused CloudFrontSigner.Domain() method

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-31 14:55:27 +08:00
Jiayuan
06424f9ba6 fix(daemon): add CLI hint to issue_context.md
renderIssueContext() now includes a "Quick Start" section with the
`multica issue get` command so agents know how to fetch issue details.
Fixes the TestPrepareDirectoryMode and TestWriteContextFiles failures.
2026-03-31 14:53:05 +08:00
Jiang Bohan
26064e43d1 feat(web): add OK emoji to reaction quick bar 2026-03-31 14:52:30 +08:00
Naiyuan Qing
3b6f64ba8e
Merge pull request #212 from multica-ai/feature/comment-context-menu
feat(issues): comment context menu & mention popup positioning fix
2026-03-31 14:46:07 +08:00
Naiyuan Qing
38e92040c4 fix(editor): migrate tiptap-markdown import to @tiptap/markdown
Update import path and remove deprecated config options (html,
transformPastedText, transformCopiedText) that don't exist in the
official @tiptap/markdown package.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-31 14:44:00 +08:00
Jiayuan
d729f9c5be feat(issues): make entire comment card collapsible with toggle
Each comment card now has a clickable header with a chevron toggle.
When collapsed, shows author, timestamp, and a content preview.
When expanded, shows the full comment body, replies, and reply input.
2026-03-31 14:41:44 +08:00
yushen
29a80e057e feat(upload): add file upload API with S3 + CloudFront signed cookies
Add POST /api/upload-file endpoint that uploads files to S3 and returns
CDN URLs protected by CloudFront signed cookies (same pattern as Linear).

Infrastructure:
- Two private S3 buckets (static.multica.ai, static-staging.multica.ai)
- Two CloudFront distributions with OAC and Trusted Key Groups
- ACM wildcard cert in us-east-1, DNS records in Route 53
- RSA signing key stored in AWS Secrets Manager

Backend:
- S3 storage service with CloudFront CDN domain support
- CloudFront signed cookie generation (RSA-SHA1)
- Private key loaded from Secrets Manager (env var fallback for local dev)
- Cookies set on login (VerifyCode) with 72h expiry matching JWT
- Upload handler: multipart form → S3 → CloudFront URL response

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-31 14:41:17 +08:00
Jiayuan
f69aa93a75 feat(agents): add Settings tab for editing agent visibility and properties
Adds a Settings tab to the agent detail panel with:
- Name and description editing
- Visibility toggle (workspace/private) matching the create dialog pattern
- Max concurrent tasks configuration
- Runtime info display (read-only)
2026-03-31 14:40:53 +08:00
Naiyuan Qing
b4bbc16521 fix(issues): use floating-ui for mention popup viewport-aware positioning
Replace hardcoded bottom positioning with @floating-ui/dom computePosition
so the @ mention popup flips above the cursor when near the viewport bottom.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-31 14:39:48 +08:00
Jiayuan
af94e22cba merge: resolve conflict with main (comment context menu icons) 2026-03-31 14:37:35 +08:00
Jiayuan
bedf4a05c8 fix(agent): fix agent visibility defaults and permission model
- Change DB default for agent visibility from 'workspace' to 'private'
- Fix canManageAgent: workspace agents are now manageable by all members,
  private agents remain restricted to owner/admin
- Add private agent visibility check to BatchAssigneePicker (was missing)
2026-03-31 14:34:04 +08:00
Jiayuan
7dafb5127d feat(issues): add collapsible toggle for comment replies
Wrap the replies section in a Collapsible component so users can
collapse/expand replies on a comment thread. The parent comment and
reply input remain always visible. A chevron trigger shows the reply
count (e.g. "3 replies") and rotates on open. Default state is expanded
to preserve existing behavior.
2026-03-31 14:33:09 +08:00
Naiyuan Qing
8fa4c8f576
Merge pull request #208 from multica-ai/feature/comment-context-menu
feat(issues): add context menu to all comments with copy support
2026-03-31 14:21:35 +08:00
Naiyuan Qing
d70ab81363 feat(issues): add context menu to all comments with copy support
Own comments show Copy/Edit/Delete; others' comments show Copy only.
Also adds icons to menu items for consistency with other dropdown menus.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-31 14:15:09 +08:00
LinYushen
961de18c97
feat(agents): reply as thread instead of top-level comment (#205)
* feat(agents): reply as thread instead of top-level comment

When an agent responds to a user comment, the reply is now nested under
the triggering comment (parent_id) instead of appearing as a separate
top-level comment. Also enables on_comment trigger by default for newly
created agents.

- Add trigger_comment_id column to agent_task_queue (migration 028)
- Pass triggering comment ID through EnqueueTaskForIssue → task → createAgentComment
- Include parent_id in WebSocket broadcast for agent comments
- Default agent creation includes both on_assign and on_comment triggers

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

* feat(cli): add --parent flag to comment add for threaded replies

The agent posts comments via the CLI, so the correct fix is giving it a
--parent flag rather than wiring trigger_comment_id through the task
infrastructure. The agent reads the comment list, decides which comment
to reply to, and passes --parent <comment-id>.

- Add --parent flag to `multica issue comment add`
- Update agent runtime instructions to explain --parent usage

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

* feat(daemon): pass trigger_comment_id to agent execution context

The agent now knows which comment triggered its task and gets an explicit
instruction to reply to it using --parent. The trigger_comment_id flows
from the DB through the claim response, daemon Task struct, and into
issue_context.md where the agent sees it.

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

* fix(comments): agent replies to thread root, matching frontend behavior

When the triggering comment is itself a reply (has parent_id), resolve
to the thread root so the agent's reply stays in the same flat thread.
This matches the frontend where all replies share the top-level parent.

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

* feat(cli): show parent_id and full IDs in comment list

The table output now includes a PARENT column and shows full comment IDs
(not truncated) so agents can see thread structure and use --parent.

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

* feat(daemon): instruct agents to always use --output json

Agents now see explicit guidance to use --output json for all read
commands, ensuring they get structured data with full IDs and parent_id
for proper threading.

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

* feat(daemon): differentiate comment-trigger vs assign-trigger context

When triggered by a comment, the agent now gets clear instructions:
- Primary goal is to read and respond to the comment
- Do NOT change issue status just because you replied
- Only change status if explicitly requested

This prevents the agent from seeing "In Review" and stopping, since
it now understands the task is to reply, not to re-evaluate the issue.

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

* fix(daemon): split workflow by trigger type in CLAUDE.md/AGENTS.md

The Workflow section in the agent's runtime config now shows a
comment-reply workflow when triggered by a comment (read comments,
find trigger, reply, don't change status) vs the full assignment
workflow (set in_progress, do work, set in_review).

Previously the agent always saw the assignment workflow, causing it
to check the issue status, see "In Review", and stop without reading
or replying to the triggering comment.

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

* refactor(daemon): remove duplicate workflow from issue_context.md

Workflow instructions now live only in CLAUDE.md/AGENTS.md (runtime_config.go).
issue_context.md keeps just the task data: issue ID, trigger type, and
triggering comment ID.

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

* fix(task): skip duplicate comment on completion for comment-triggered tasks

When triggered by a comment, the agent posts its own reply via CLI
with --parent. The task completion path was also creating a comment
from the agent's stdout output, resulting in duplicates. Now only
assignment-triggered tasks auto-post output as a comment. Error
messages from FailTask are still posted regardless of trigger type.

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-03-31 13:48:39 +08:00
Naiyuan Qing
5517136d73
Merge pull request #207 from multica-ai/feature/ws-self-event-filtering
feat(realtime): WS self-event filtering, issue-detail refactor, sync gap fixes
2026-03-31 13:19:39 +08:00
Naiyuan Qing
6761310038 fix(sync): board-card rollback, inbox status sync, markRead error handling
- board-card: capture prev issue before optimistic update, restore on error
- useRealtimeSync: wire issue:updated WS handler to update inbox issue_status
- inbox: markRead uses optimistic update, refetch on error with toast

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-31 13:09:17 +08:00
Naiyuan Qing
a2e5cbd47b refactor(issues): extract hooks from issue-detail, eliminate dual source of truth
- Remove useState<Issue> mirror anti-pattern — read directly from useIssueStore
- handleUpdateField now writes to global store (board/list sync instantly)
- handleDelete now calls removeIssue (deleted issue disappears from list)
- Extract useIssueTimeline hook (comment CRUD + WS events + reconnect)
- Extract useIssueReactions hook (issue reactions + WS events)
- Extract useIssueSubscribers hook (subscribers + WS events + rollback)
- Add useWSReconnect hook for per-component reconnect handling
- Add React.memo to BoardCardContent, DraggableBoardCard, ListRow
- Add key={id} to RichTextEditor to fix stale description on issue switch
- issue-detail.tsx: 1330 → 979 lines

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-31 13:09:17 +08:00
Naiyuan Qing
3e8c715de8 feat(realtime): skip WS refetch for self-triggered events
Backend WS messages now include actor_id from the Event struct.
Frontend useRealtimeSync skips the debounced refetch when the event
was triggered by the current user, eliminating unnecessary re-renders
of heavy components (~400ms after each user action).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-31 13:09:17 +08:00
Jiayuan Zhang
c1f81427f6
Merge pull request #206 from multica-ai/agent/lambda/6e8277a7
feat(web): add My Issues page with grouped filters
2026-03-31 12:57:02 +08:00