Replace blanket owner/admin role check in UpdateAgent and DeleteAgent
with canManageAgent, which requires the requesting user to be the
agent's owner (or a workspace owner/admin) for private agents.
- Add redact package to detect and mask secrets (AWS keys, private keys,
API tokens, bearer tokens, credentials, home paths) in agent output
before posting as comments in TaskService
- Enforce agent visibility on issue assignment: private agents can only
be assigned by their owner or workspace admins
- Add visibility picker (workspace/private) to CreateAgentDialog,
default to private
- Grey out unassignable private agents in the assignee picker with
lock icon indicator
Allow running multiple daemon instances against different servers (e.g.
production and local dev) simultaneously. Each profile gets isolated
config, PID file, log file, health port, and workspaces root.
Usage:
multica login --profile dev --server-url http://localhost:8080
multica daemon start --profile dev
Default profile (no --profile flag) behavior is unchanged.
Closes MUL-42
Task execution environments were all created flat under WorkspacesRoot,
mixing tasks from different workspaces. Now tasks are nested under their
workspace ID for clearer organization and easier per-workspace cleanup.
Run syncWorkspacesFromAPI once before entering the periodic ticker
loop so newly created workspaces are discovered without the initial
30-second delay.
The daemon now periodically fetches the user's workspace list from the
API (every 30s) and adds any new workspaces to the watched config. The
existing config-watch loop then picks up the change and registers
runtimes. This fixes the issue where workspaces created after
`multica login` were not discovered until the daemon was restarted.
The backend already supports on_comment triggers but the frontend was
missing the UI to configure them. Adds the "On Comment" trigger type
and "Add On Comment" button alongside the existing On Assign and
Scheduled options.
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Set openOnClick: true so clicking a link opens it in a new tab
- Add Cmd+Click / Ctrl+Click handler as fallback (skips mention:// links)
- Override prosemirror-markdown link serializer to always use [text](url)
format instead of <url> autolink syntax, fixing angle brackets appearing
when copying links from the editor
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
router.replace() triggers a full server navigation cycle in Next.js 15+,
which can stall after a page refresh (no client route cache), preventing
useSearchParams from updating and making inbox items unclickable.
window.history.replaceState() updates the URL synchronously without
triggering server navigation, which is the recommended approach for
URL state management in Next.js 14.1+.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add items-center for vertical centering between icons and text
- Add truncate on activity text to prevent line wrapping
- Unify icon/avatar sizes to 16px for visual consistency
- Remove connector line (will revisit later)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Enforce workspace isolation at every layer:
- Router: move RequireWorkspaceMember middleware to group level so ALL
workspace-scoped routes (issues, agents, skills, runtimes, inbox,
comments) require workspace context
- SQL: add GetXxxInWorkspace queries that filter by workspace_id,
eliminating cross-workspace data access at the query level
- Handlers: loadXForUser functions use workspace-scoped queries,
no fallback to unscoped queries
- Migration 025: add workspace_id column to comment table with backfill
- ListComments: add workspace_id filter for defense-in-depth
Fix daemon workspace mapping:
- Server returns workspace_id in task claim response (from issue)
- Daemon uses task.WorkspaceID directly instead of unreliable
workspaceIDForRuntime() local map lookup
- Remove workspaceIDForRuntime function
Fix agent/human parity:
- Comment update/delete: use resolveActor for isAuthor check so agents
can edit/delete their own comments
- Event attribution: replace hardcoded "member" with resolveActor in
agent, skill, and subscriber publish calls
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(cli): daemon reads server_url from config file
The daemon only checked CLI flags and env vars for server_url, ignoring
the persisted config in ~/.multica/config.json. Now falls back to the
config file when neither flag nor env var is set.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(cli): reset workspace data when server_url changes on login
When logging in to a different server, stale workspace_id and
watched_workspaces from the previous server caused 404 errors in the
daemon. Now both browser and token login paths clear workspace data
when the server URL changes.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(cli): reset workspace data on every login, not just server change
A different user logging in on the same machine would inherit stale
workspace data. Always clear workspace_id and watched_workspaces on
login so autoWatchWorkspaces repopulates them fresh.
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(cli): support app_url in CLI config for browser login
The login flow opens the frontend URL for browser-based auth, but
previously app_url could only be set via environment variables.
Add app_url to CLIConfig so it can be persisted with `multica config set`.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(cli): persist app_url to config during browser login
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Multi-stage build that compiles server, CLI, and migrate binaries,
then produces a minimal Alpine runtime image.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add zustand draft store with localStorage persistence
- Restore draft fields when reopening create-issue modal
- Clear draft only on successful submission
- Show brand-colored dot on sidebar new-issue button when draft exists
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add stable issue store mock to prevent infinite effect loop in tests
- Update expected error message in not-found test case
- Clear runtime store on workspace switch and set workspace before hydration
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- CommentInput: remove border-t divider, position submit button inside
editor area (bottom-right) for cleaner look
- CommentCard: add !gap-0 to override Card's default gap-4
- CommentInput/ReplyInput: strip trailing empty lines from markdown
before submit to prevent extra blank lines in rendered comments
- BoardCard: use normal text color for title instead of muted+hover
- Timeline: coalesce same actor + same action within 2 min window,
keeping only the final result (e.g. 5 status changes → 1)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace mixed spacing approach (space-y + pb-3) with consistent
flex-col gap-3. Activity connector lines now use absolute positioning
to bridge between icons.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add multi-select with batch update/delete support in the list view.
Users can select issues via checkboxes (per-row or per-status-group)
and apply bulk status, priority, assignee changes or delete via a
floating toolbar.
ensureUserWorkspace in auth.go omitted the IssuePrefix field when
creating a default workspace during first login. This caused the
workspace's issue_prefix to be empty, producing identifiers like "-16"
instead of "JIA-16".
- Pass generateIssuePrefix(wsName) when creating the default workspace
- Add fallback in getIssuePrefix to regenerate from workspace name if
the stored prefix is empty
- Add migration 024 to backfill empty prefixes on existing workspaces
* feat(server): distinguish agent vs human CLI actions via X-Agent-ID/X-Task-ID headers
Extract resolveActor helper in handler to centralize agent identity resolution
from X-Agent-ID header with X-Task-ID cross-validation. Fix DeleteComment,
DeleteIssue, and UpdateComment handlers that previously hardcoded "member" as
actor type. Forward MULTICA_TASK_ID as X-Task-ID header from CLI client.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(server): add debug logging and test coverage for resolveActor
Add slog.Debug on agent/task validation failures for easier debugging.
Add TestResolveActor with 5 cases covering member fallback, valid agent,
non-existent agent, valid task, and mismatched 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>
Move workspace membership and role validation from individual handlers
into dedicated Chi middleware. The new middleware resolves workspace ID
(from query param, X-Workspace-ID header, or URL param), validates
membership via DB, and injects the member into request context.
Handlers now read workspace ID and member from context instead of
calling requireWorkspaceMember/requireWorkspaceRole directly. This
eliminates ~17 duplicated permission checks across handlers and makes
it harder to accidentally omit access control on new routes.
Use short task IDs (8-char prefix), task-scoped loggers for concurrent
task disambiguation, numbered tool-use at INFO level for progress
tracking, and an agent-finished summary with duration and tool count.
Show issue identifier and title instead of truncated UUIDs, sort active
tasks to top, highlight running/dispatched tasks, and show contextual
timestamps.