Commit graph

1882 commits

Author SHA1 Message Date
LinYushen
7d74b1f0b9
Merge pull request #495 from multica-ai/revert-477-feat/structured-ticket-search
Revert "feat(issues): add structured ticket search"
2026-04-08 15:15:25 +08:00
LinYushen
39ca8ed9e8
Revert "feat(issues): add structured ticket search" 2026-04-08 15:15:08 +08:00
LinYushen
3c08395741
Merge pull request #477 from pseudoyu/feat/structured-ticket-search
feat(issues): add structured ticket search
2026-04-08 15:02:43 +08:00
LinYushen
ec934f3a8b
fix(web): add load-more pagination for Done column on issue board (#492)
* fix(web): add load-more pagination for Done column on issue board

The Done column was capped at 50 issues with no way to load more.
Track doneTotal in the TQ cache and add a useLoadMoreDoneIssues hook
that fetches the next page and merges it into the unified issue cache.
The Done column now shows a "Load more" button when there are
additional items.

- shared/types/api.ts: add doneTotal to ListIssuesResponse
- core/issues/queries.ts: store doneTotal from the done-status response
- core/issues/mutations.ts: add useLoadMoreDoneIssues hook, update
  create/delete mutations to maintain doneTotal
- core/issues/ws-updaters.ts: maintain doneTotal on WS events
- features/issues/components/board-column.tsx: accept optional footer
- features/issues/components/board-view.tsx: render Load more button
  in Done column

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

* fix(web): address review issues in done-column load-more

1. Fix total over-counting: loadMore no longer inflates total since
   the initial query already includes all done issues in total count.
2. Fix onIssueUpdated: maintain doneTotal when issue status changes
   to/from done via WS events.
3. Make doneTotal optional in ListIssuesResponse since it's a
   frontend-only field not returned by the backend API. All reads
   now use ?? 0 fallback.

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-08 14:58:51 +08:00
zerone0x
a7afd4b959
feat: wire allowedDevOrigins from CORS_ALLOWED_ORIGINS for non-localhost dev access (#355)
* feat: add allowedDevOrigins to Next.js config for non-localhost dev access

Wire CORS_ALLOWED_ORIGINS env var into Next.js allowedDevOrigins config
so that cross-origin HMR/webpack requests from Tailscale or other
non-localhost IPs are not blocked during development.

Fixes #317

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: keep port in allowedDevOrigins

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-08 14:39:01 +08:00
Bohan Jiang
8403c97688
Merge pull request #482 from multica-ai/agent/j/674c6839
feat(usage): add per-task token usage tracking
2026-04-08 14:16:59 +08:00
LinYushen
7df5750979
fix(daemon): update existing worktree to latest remote on reuse (#489)
* fix(daemon): update existing worktree to latest remote on reuse

When an agent receives a new task on the same issue, the execution
environment is reused and the repo worktree already exists on disk.
Previously, `multica repo checkout` would fail because `git worktree add`
cannot create a path that already exists — so the agent worked on stale
code from the prior task.

Now `CreateWorktree` detects existing worktrees and updates them:
fetch origin, reset working tree, then checkout a new branch from the
latest remote default branch. The previous task's branch is preserved.

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

* fix(daemon): propagate actual branch name and use correct ref in worktree reuse

- Return (string, error) from updateExistingWorktree so collision-retried
  branch name propagates to WorktreeResult
- Use baseRef directly instead of origin/baseRef — bare clone refspec maps
  remote branches to local refs, so remote-tracking refs may not exist
- Remove redundant fetch (worktree shares object store with bare clone)

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-08 14:13:44 +08:00
Naiyuan Qing
990cc8b3ae
Merge pull request #488 from multica-ai/fix/ws-self-event-idempotent
fix(web): replace WS self-event filtering with idempotent cache updates
2026-04-08 14:00:18 +08:00
Naiyuan Qing
7ee2450297
Merge pull request #487 from multica-ai/NevilleQingNY/readonly-markdown
perf(editor): replace readonly Tiptap instances with react-markdown
2026-04-08 13:57:46 +08:00
Naiyuan Qing
d58f6cdb33 fix(web): replace actor_id self-event filtering with idempotent cache updates
actor_id identifies the user, not the browser tab. Filtering WS events
by actor_id broke multi-tab sync — other tabs of the same user would
silently miss updates. Instead, make all WS cache handlers idempotent
(dedup checks on add, no-op on duplicate merge/filter) so mutations and
WS events coexist safely without filtering.

- WSClient: pass actor_id to event handlers for future per-handler use
- use-realtime-sync: remove isSelf() gating from onAny and specific handlers
- useCreateIssue: add .some() dedup guard + onSettled invalidation
- use-issue-reactions: remove payload-level self-filter (dedup already present)
- use-issue-timeline: remove payload-level self-filter on comment:created,
  reaction:added, reaction:removed (dedup already present)
- Clean up useCallback deps that no longer reference userId

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 13:57:24 +08:00
Naiyuan Qing
af156040cb test(issues): add ReadonlyContent mock to issue detail tests
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 13:55:25 +08:00
Naiyuan Qing
5e770b2e2f fix(editor): align IssueMentionCard styling and behavior with Tiptap
- Remove align-middle from IssueMentionCard (alignment is container's job)
- Add inline align-middle wrapper span in ReadonlyContent for vertical alignment
- Add img component with max-width constraint to prevent overflow
- Issue mention clicks open in new tab (matches Tiptap behavior)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 13:47:03 +08:00
Naiyuan Qing
92e76dea81 refactor(issues): use ReadonlyContent for comment readonly display
Replace ContentEditor editable={false} with lightweight ReadonlyContent
in comment cards. Each comment no longer creates a full ProseMirror
instance for readonly display.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 13:47:03 +08:00
Naiyuan Qing
4df32a853b feat(editor): add ReadonlyContent component for lightweight markdown display
- Add del selector to strikethrough CSS for react-markdown compatibility
- Create ReadonlyContent using react-markdown + lowlight + content-editor.css
- Export from editor module index

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 13:47:03 +08:00
Jiang Bohan
fa0c0fe747 fix(usage): address review feedback — independent usage reporting + all providers
1. Separate ReportTaskUsage endpoint (POST /api/daemon/tasks/{id}/usage)
   so usage is captured independently of complete/fail — fixes usage loss
   for failed/blocked tasks.

2. Add usage tracking for all four providers:
   - Claude: already done (stream-json message.usage)
   - OpenCode: extract from step_finish.part.tokens
   - OpenClaw: extract from step_end.data token fields
   - Codex: extract from turn/completed and task_complete usage fields

3. Remove usage from CompleteTask payload — all usage goes through the
   dedicated endpoint now.
2026-04-08 13:23:54 +08:00
Jiang Bohan
8a8d3ea20e feat(usage): add per-task token usage tracking
Extract token usage from Claude Code's stream-json output in real-time
during task execution, replacing the inaccurate global JSONL log scanner.

- New `task_usage` table: tracks (task_id, provider, model) level usage
- Agent SDK: parse `message.usage` from assistant messages, accumulate
  per-model and return in Result
- Daemon: convert agent usage to entries, send with CompleteTask
- Server: store usage on task completion, expose workspace-level
  aggregation APIs (GET /api/usage/daily, GET /api/usage/summary)
2026-04-08 13:08:15 +08:00
Jiayuan Zhang
88c2f4ddc4
Merge pull request #479 from multica-ai/fix/cli-web-shared-login-state
fix(auth): persist browser session during CLI login flow
2026-04-08 12:50:53 +08:00
Bohan Jiang
98af9f442c
Merge pull request #471 from multica-ai/agent/j/959392dd
feat: support multiple agents running on same issue
2026-04-08 12:45:56 +08:00
pseudoyu
34c39b765e
feat(issues): add structured ticket search 2026-04-08 11:30:53 +08:00
Naiyuan Qing
efe131591f
Merge pull request #472 from multica-ai/feat/tanstack-query-migration
feat(web): migrate server state from Zustand to TanStack Query (Phase 0-4)
2026-04-08 10:46:35 +08:00
Naiyuan Qing
104bbbef41 fix(web): prevent useWorkspaceId crash in AppSidebar (re-apply after merge revert)
AppSidebar renders before workspace hydrates. useWorkspaceId() throws
when workspace is null. Fix: read workspace?.id directly from store,
use enabled guard on inbox query. This fix was in commit 030627c but
got reverted by subsequent merge with main.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 10:44:56 +08:00
Naiyuan Qing
eed8e36a69 fix(test): update mockListIssues for two-phase fetch (open_only + closed)
issueListOptions now makes 2 api.listIssues calls (open_only + closed page).
Tests that mock the response must return data only for the open_only call,
otherwise issues appear twice in the merged result.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 10:41:29 +08:00
Naiyuan Qing
8cf78b7a47 Merge remote-tracking branch 'origin/main' into feat/tanstack-query-migration
# Conflicts:
#	apps/web/app/(dashboard)/agents/page.tsx
2026-04-08 10:35:28 +08:00
Naiyuan Qing
862b85e064 fix(web): DnD local-state overlay, onSettled list invalidation, WS self-event filter
- Board DnD: use local pendingMove state for instant card placement,
  bypassing TQ's async setQueryData notification delay
- useUpdateIssue: add list invalidation to onSettled (was only detail)
- use-realtime-sync: add isSelf check to specific issue WS handlers
  (prevents redundant cache writes for own mutations)
- Clean up debug console.logs from board-view, issues-page, mutations

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 10:25:35 +08:00
Jiayuan Zhang
857ec7d4d4 fix(auth): persist browser session during CLI login flow
When authenticating via CLI, the login page called api.verifyCode()
directly and redirected to the CLI callback without saving the JWT
to localStorage or setting the logged-in cookie. This meant the
browser had no session after CLI login, forcing users to log in
again when visiting multica.ai.

Now the token is saved to localStorage and the cookie is set before
redirecting to the CLI callback, so both CLI and web app share the
same authentication.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 10:22:19 +08:00
devv-eve
7c79611309
refactor: remove agent triggers config field (#469)
* refactor: remove agent triggers config field

Remove the triggers field from agent configuration. The on_assign,
on_comment, and on_mention behaviors are now always enabled (hardcoded),
as decided in the Agentflow design discussion (MUL-372).

Changes:
- Database: migration 032 drops triggers column from agent table
- Backend: remove triggers from create/update agent APIs and response
- Backend: simplify trigger-checking logic to always-enabled
- Frontend: remove TriggersTab UI and AgentTrigger types
- Tests: remove trigger config unit tests (no longer configurable)

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

* refactor: also remove agent tools config field

Remove the tools field from agent configuration alongside triggers.
The tools field was a placeholder — stored in the DB and shown in the
UI but never passed to the daemon or used at runtime.

- Database: migration 032 now also drops tools column
- Backend: remove tools from create/update agent APIs and response
- Frontend: remove ToolsTab UI, AgentTool type, and tools tab
- Update landing page copy

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

* fix(test): remove tools/triggers columns from test fixtures

The test fixtures still referenced the dropped tools and triggers
columns when inserting agent rows, causing CI failures.

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

---------

Co-authored-by: Devv <devv@Devvs-Mac-mini.local>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-07 22:02:28 +08:00
Naiyuan Qing
99dad49052 fix(core): add onSettled invalidation to all optimistic mutations + enable refetchOnReconnect
P0: Add onSettled: invalidateQueries to 10 mutations that had onMutate
optimistic updates but no server confirmation. With staleTime: Infinity,
missing onSettled means cache could permanently drift from server state.

Mutations fixed:
- useDeleteIssue, useBatchDeleteIssues (issue list)
- useUpdateComment, useDeleteComment, useToggleCommentReaction (timeline)
- useToggleIssueReaction (reactions)
- useToggleIssueSubscriber (subscribers)
- useMarkInboxRead, useArchiveInbox, useMarkAllInboxRead (inbox)

P2: Change refetchOnReconnect from false to true as safety net
for HTTP reconnection before WS reconnection.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-07 19:07:37 +08:00
Naiyuan Qing
6296629831 fix: restore TQ consumer migrations lost during merge with main
The merge with origin/main (fe9479d6) silently reverted all consumer-side
migrations, leaving core/ as dead code. Restored all 39 files from
pre-merge commit 6032b5df, plus main's trigger.config null fix for
agents page.

Verified: 59 @core/ imports across features/ and app/, all stores
gutted/deleted, realtime sync uses queryClient not Zustand.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-07 18:59:09 +08:00
Naiyuan Qing
7ed565da6b docs: update CLAUDE.md for TanStack Query architecture + restore @core alias
- Add core/ layer documentation (queries, mutations, WS updaters)
- Rewrite State Management section: TQ for server state, Zustand for client-only
- Update features table: reflect gutted stores (issues, inbox, workspace)
- Add @core/* import alias examples
- Update Data Flow diagram to include TQ layer
- Restore @core/* path alias in tsconfig + vitest (lost during merge)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-07 18:49:23 +08:00
Naiyuan Qing
030627c8c5 fix(web): prevent useWorkspaceId crash in AppSidebar before workspace hydration
AppSidebar renders outside the workspace guard in dashboard layout.
On first login, workspace hasn't hydrated yet → useWorkspaceId() throws.
Fix: read workspace?.id directly from store, use enabled guard on inbox query.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-07 18:39:32 +08:00
Naiyuan Qing
fe9479d6fc Merge remote-tracking branch 'origin/main' into feat/tanstack-query-migration
# Conflicts:
#	apps/web/features/issues/components/batch-action-toolbar.tsx
#	apps/web/features/issues/components/issues-page.tsx
#	apps/web/features/issues/store.ts
2026-04-07 18:39:13 +08:00
Jiang Bohan
b94108768e feat: support multiple agents running concurrently on the same issue
- Relax ClaimAgentTask SQL constraint from per-issue to per-(issue, agent)
  serialization, allowing different agents to run in parallel on the same issue
- Update GetActiveTaskForIssue API to return all active tasks (array) instead of
  just the first one
- Refactor AgentLiveCard to render one card per active task, routing WebSocket
  messages by task_id for independent timelines
- Fix shouldEnqueueOnComment to use per-agent dedup so a mentioned agent's
  pending task doesn't block the assigned agent's on_comment trigger

Closes MUL-160
2026-04-07 18:19:57 +08:00
Naiyuan Qing
348133b63d merge: resolve conflicts with main (open_only pagination)
- Resolve issues/store.ts: keep client-only store, port pagination
  strategy (open_only + closed page) to core/issues/queries.ts
- Resolve issues-page.tsx, batch-action-toolbar.tsx: keep TQ mutations
- Auto-merge agents/page.tsx trigger null fix

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-07 18:08:35 +08:00
Naiyuan Qing
6032b5dfcb fix: mention closure, onSettled invalidation, cleanup singleton
- Fix Tiptap mention: pass QueryClient via closure from ContentEditor
  instead of getQueryClient() singleton (resolves @mention empty list)
- Add onSettled invalidation to useUpdateIssue (prevents cache drift
  with staleTime: Infinity + self-event WS filter)
- Add cache shape comment to issueListOptions (select transforms
  ListIssuesResponse → Issue[], but cache stores raw response)
- Memoize sidebar inbox dedup computation
- Remove dead getQueryClient/setQueryClient singleton + window property
- Remove ActorSync component and _members/_agents Zustand mirror
  (superseded by closure approach)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-07 17:53:49 +08:00
Bohan Jiang
23198f3c26
Merge pull request #461 from multica-ai/agent/j/70455bdb
fix(daemon): correct duplicate sub-step lettering in workflow instructions
2026-04-07 17:29:46 +08:00
Naiyuan Qing
e40341ab73 feat(core): migrate workspace + runtimes to TanStack Query (Phase 3+4)
- Create core/workspace/ with queries (members, agents, skills, list) and mutations
- Create core/runtimes/ with queries
- Migrate 11 consumer files from useWorkspaceStore.members/agents/skills to useQuery
- Replace all WS refreshMap entries with qc.invalidateQueries
- Simplify workspace store: delete members/agents/skills fields + refresh methods,
  hydrateWorkspace becomes synchronous (TQ auto-fetches on component mount)
- Delete useRuntimeStore (no consumers left), runtimes-page uses local useState + TQ
- Remove workspace→runtime cross-store dependency
- Clean up dead test helper mocks

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-07 17:19:52 +08:00
Bohan Jiang
c695de5314
Merge pull request #468 from multica-ai/agent/j/272bc2a3
docs(web): add v0.1.8 changelog entry
2026-04-07 17:07:02 +08:00
Jiang Bohan
d6b59aade6 docs(web): add v0.1.8 changelog entry for 2026-04-07 2026-04-07 17:03:41 +08:00
Naiyuan Qing
1d812bd446 feat(core/inbox): migrate inbox to TanStack Query (Phase 2)
- Create core/inbox/ with queries, mutations, ws-updaters
- Migrate inbox page: useQuery + mutation hooks replace useInboxStore + api.*
- Migrate sidebar unread badge to read from TQ cache
- Delete useInboxStore (127 lines) — inbox has no client-only state
- Remove inbox deps from workspace store (hydrate + switch)
- Fix WS sync: use useQueryClient() instead of getQueryClient() singleton
  to ensure WS handlers write to the same QueryClient instance that
  components read from (singleton is unreliable under Next.js HMR)
- Add onInboxIssueStatusChanged for issue status sync in inbox items

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-07 16:56:47 +08:00
devv-eve
abcc7bf3cd
feat(issues): load all open issues without limit, paginate closed (#459)
- Add ListOpenIssues SQL query (excludes done/cancelled, no LIMIT)
- Add CountIssues SQL query for true total count
- Backend: support open_only=true param, fix total to return real count
- Frontend: two-phase fetch in issue store (all open + first 50 closed)
- Add fetchMoreClosed action for paginated closed issue loading
- Replace all hardcoded limit:200 with store.fetch() calls

Resolves MUL-369

Co-authored-by: Devv <devv@Devvs-Mac-mini.local>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-07 00:59:03 -07:00
Naiyuan Qing
06fa65d4b5 test(issues): clean up dead useIssueStore mocks from tests
Remove mock issues[] and server state fields from useIssueStore mocks
since the store now only holds activeIssueId. Data flows through
TanStack Query (mockListIssues) not the store.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-07 15:52:15 +08:00
Bohan Jiang
9d1570b301
Merge pull request #465 from multica-ai/agent/j/ffea36be
fix(auth): move Google callback to correct route path
2026-04-07 15:48:30 +08:00
Jiang Bohan
7f2ea9857d fix(auth): move Google callback page to correct route path
The callback page was placed under app/(auth)/callback/ — a Next.js
route group — which mapped to /callback instead of /auth/callback.
Move it to app/auth/callback/ so the URL matches the Google OAuth
redirect URI.
2026-04-07 15:47:44 +08:00
Naiyuan Qing
1ad057fb0f refactor(issues): migrate all consumers to TanStack Query (Phase 1, Commits 5-10)
- Migrate issue-detail.tsx: useQuery for issue data, useUpdateIssue/useDeleteIssue
- Migrate issues-page.tsx, my-issues-page.tsx, board-card.tsx: useQuery for list
- Migrate batch-action-toolbar.tsx, create-issue.tsx: mutation hooks
- Migrate edge consumers: mention-suggestion, mention-view, agents page, issue-mention-card
- Remove Zustand writes from WS sync (TQ cache is now sole source of truth)
- Remove useIssueStore.fetch() dependency from workspace store
- Gut useIssueStore to client-only: { activeIssueId, setActiveIssue }
- Update test wrappers with QueryClientProvider

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-07 15:46:08 +08:00
Bohan Jiang
b85c068e83
Merge pull request #464 from multica-ai/agent/j/272bc2a3
docs(web): add v0.1.7 changelog entry
2026-04-07 15:37:23 +08:00
Jiang Bohan
30cda933bc docs(web): add v0.1.7 changelog entry for 2026-04-05 2026-04-07 15:36:32 +08:00
Jiang Bohan
b5537077bc Merge branch 'main' of https://github.com/multica-ai/multica into agent/j/272bc2a3 2026-04-07 15:35:38 +08:00
Bohan Jiang
638033c9ff
Merge pull request #462 from multica-ai/agent/j/ffea36be
feat(auth): add Google OAuth login
2026-04-07 15:32:08 +08:00
Naiyuan Qing
7560f7be85 feat(core/issues): add TanStack Query layer and rewrite hooks (Phase 1, Commits 1-4)
- Add getQueryClient() singleton for non-React contexts (WS handlers, Zustand)
- Create issue query key factory + 5 queryOptions
- Create 11 mutation hooks with optimistic updates and rollback
- Create WS cache updaters + dual-write in use-realtime-sync
- Rewrite useIssueTimeline, useIssueReactions, useIssueSubscribers to TQ
  (return types unchanged, consumers unaffected)
- Add QueryClientProvider wrapper to issue detail tests

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-07 15:30:42 +08:00
Bohan Jiang
b84104b421
Merge pull request #463 from multica-ai/fix/agent-trigger-config-nullable-type
fix(types): make AgentTrigger.config nullable
2026-04-07 15:27:11 +08:00