Adapt runtime features (usage tracking, ping, heartbeat) to main's
multi-workspace architecture. Update frontend imports from @multica/types
to @/shared/types after the package consolidation.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add a new "Runtimes" sidebar tab to manage local agent runtimes with three
main capabilities: runtime status overview, token usage tracking (reading
Claude Code and Codex CLI local JSONL logs via daemon), and an interactive
connection test that sends a ping through the daemon to verify end-to-end
agent CLI connectivity.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Support importing skills from external sources (clawhub.ai and skills.sh)
via a new POST /api/skills/import endpoint. The backend auto-detects the
source from the URL, fetches skill metadata and files, and creates the
skill in the workspace. The frontend CreateSkillDialog now has two tabs:
Create (manual) and Import (paste URL with source auto-detection badge).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Remove unused ListWorkspaces/Workspace from daemon client, add log when
default workspace is set implicitly, document token reload limitation.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- `multica workspace list` — list all user workspaces from API (with
watching indicator)
- `multica watch <id>` — add workspace to daemon watch list (top-level)
- `multica unwatch <id>` — remove from watch list (top-level)
- `multica watches` — show current daemon watch list (top-level)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add `multica workspace watch/unwatch/list` CLI commands
- Daemon watches multiple workspaces from config's `watched_workspaces`
- Registers runtimes per workspace, polls all runtime IDs in round-robin
- Hot-reload: daemon detects config file changes every 5s and
adds/removes workspaces without restart
- Remove `--workspace-id` flag from daemon (workspace selection is now
purely config-driven via `multica workspace watch`)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The daemon now reads the auth token from ~/.multica/config.json (set by
`multica auth login`) instead of requiring a browser-based pairing flow.
If not authenticated, it logs a message and exits.
Workspace ID is auto-resolved from the user's workspaces when not
explicitly set via flag/env.
Removed: daemon.json, pairing session flow, --config-path flag,
PairingSession type, PersistedConfig, LoadWorkspaceIDFromDaemonConfig.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
ReposRoot was a daemon-level config that locked all tasks to a single
git repo. Replace with RepoPath in TaskContext so the server can specify
the repo per task. When not provided, daemon falls back to directory mode.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace bare HTML with a styled card layout featuring dark/light mode
support, Multica brand icon, and auto-close behavior.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add state parameter to CLI browser login flow for CSRF protection — CLI
generates a random state, frontend passes it through, CLI verifies on
callback. Also restrict cli_callback to http: scheme only.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Prevent open redirect / JWT theft by only allowing localhost/127.0.0.1
as cli_callback hostname
- URL-encode the callback URL in the login query string
- Simplify resolveAppURL to use os.Getenv directly (no phantom flag)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
`multica auth login` now opens the browser for email verification,
receives the JWT via localhost callback, and exchanges it for a PAT.
The legacy PAT-paste flow is preserved via `--token` flag.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat(auth): add email verification login flow with 401 auto-redirect
Replace the old OAuth-based login with email verification codes:
- Backend: send-code / verify-code endpoints, verification_codes table (migration 009), rate limiting, Resend email service
- Frontend: two-step login UI (email → 6-digit OTP), auth store with sendCode/verifyCode
- SDK: ApiClient gains onUnauthorized callback; 401 responses auto-clear token and redirect to /login
- Fix login button staying disabled due to global isLoading state
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(auth): add brute-force protection, redirect loop guard, and expired code cleanup
- VerifyCode: increment attempts on wrong code, reject after 5 failed tries (migration 010)
- onUnauthorized: skip redirect if already on /login to prevent infinite loops
- SendCode: best-effort cleanup of expired verification codes older than 1 hour
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat(auth): add master verification code for non-production environments
Allow code "888888" to bypass email verification in non-production
environments to simplify development and testing workflows.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat(auth): add personal access tokens for CLI and API authentication
Add full-stack PAT support: users create tokens in Settings, CLI authenticates
via `multica auth login`. Server stores SHA-256 hashes only. Auth middleware
extended to accept both JWTs and PATs (distinguished by `mul_` prefix).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add actor_type/actor_id to inbox items for proper attribution
- Extract issue detail into features/issues/components/issue-detail.tsx
- Inbox page and store updates for actor-based notifications
- Sidebar, layout, and actor-avatar refinements
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace raw fmt/log calls with structured slog logger (Go) and
console-based logger (TypeScript). Add request logging middleware.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
pgxpool.New is lazy and doesn't connect immediately. Add pool.Ping()
after creation so CI environments without PostgreSQL skip cleanly
instead of failing with os.Exit(1).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Rename the binary and all references from multica-cli to multica for a
cleaner command-line experience.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
TestWebSocketIntegration was timing out because registerListeners()
was never called — events published via bus had no listeners, so
WS broadcasts never happened.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The Hub processes client registration asynchronously via a channel.
Without a short delay, the issue creation can fire before the client
is added to the workspace room, so the broadcast has no recipients.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
HandleWebSocket now requires auth — update test to include
token and workspace_id query params in the WebSocket URL.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Merge origin/main which added the skills system (structured skills
with meta skill runtime injection). Resolve 4 conflicts:
- workspace/store.ts: keep both skills state + issue/inbox fetch
- types/index.ts: keep Skill types + our event exports
- handler/agent.go: merge visibility filtering + skills batch loading
- pnpm-lock.yaml: accept main's lockfile with skills deps
Also fix skill.go: migrate h.broadcast → h.publish (event bus)
to match our architecture where all WS events go through the bus.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Migration 008 drops agent.skills column, so test fixtures inserting
into the agent table must no longer reference it.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace agent.skills TEXT field with structured skill/skill_file/agent_skill
tables. Skills are workspace-level entities with supporting files, reusable
across agents via many-to-many bindings.
Backend: migration 008, sqlc queries, CRUD handler, agent-skill junction,
structured skill loading in task context snapshot.
Daemon: meta skill injection via runtime-native config (.claude/CLAUDE.md
for Claude, AGENTS.md for Codex) so agents discover .agent_context/ skills
through their native mechanism. Lean prompt without inlined skill content.
Frontend: Skills management page, agent Skills tab picker, SDK methods,
TypeScript types, workspace store integration.
Also removes auto-creation of init issues when creating agents.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Move all CreateInboxItem calls from handlers to centralized inbox_listeners.go
- Enrich issue:updated payload with change context (assignee_changed, status_changed, prev values)
- Enrich comment:created payload with issue context (assignee info)
- Bus listeners handle: issue assign, unassign, reassign, status change, comment notification
- ListAgents filters private agents: only visible to owner_id or workspace admin
- Zero CreateInboxItem calls remain in handler package
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add internal event bus (server/internal/events/) with synchronous
pub/sub and panic isolation per listener
- Upgrade WebSocket Hub to workspace-scoped rooms with JWT auth
and membership verification on connect
- Add 10 new WS event types (comment CRUD, inbox read/archive,
agent create/delete, workspace/member events)
- Refactor all handlers and TaskService to publish events via Bus
instead of direct Hub.Broadcast calls
- Add WS broadcast listener that routes events to correct workspace
- Frontend: WSClient sends token + workspace_id on connect with
auto-reconnect refetch
- Frontend: centralized useRealtimeSync hook dispatches all WS
events to global Zustand stores
- Migrate issues and inbox pages from local useState to global
useIssueStore/useInboxStore
- Make store addIssue/addItem idempotent to prevent duplicates
- Remove dead packages/hooks/src/use-realtime.ts
- Add feature tracking files for 4 planned features
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Resolve conflicts:
- CLAUDE.md: merge feature-based frontend docs from main with comprehensive
architecture docs from HEAD
- Makefile: merge .PHONY targets from both branches
- settings/page.tsx: keep Context field using main's Label component
- auth-context.test.tsx: accept main's deletion (moved to features/auth/)
- cmd/daemon/daemon.go: accept HEAD's deletion (moved to internal/daemon/)
- daemon/client.go: port requestError type and isWorkspaceNotFoundError from
main's old daemon into the new internal/daemon package
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1. Add Client.SendHeartbeat/Register methods — no more direct postJSON calls
2. Use url.Values for query params to prevent URL injection
3. Unexport helpers (envOrDefault, durationFromEnv, sleepWithContext)
4. CLI resolveWorkspaceID falls back to daemon.json
5. Implement agent stop (PUT /api/agents/{id} with status=offline)
6. Add --output flag to agent get for consistent UX
7. Add server/multica to .gitignore for stray builds
8. Inject version/commit via -ldflags in Makefile build target
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Extract daemon logic from cmd/daemon/ into internal/daemon/ package and
create a new unified CLI entry point at cmd/multica/ using cobra. The CLI
supports `daemon` as a long-running subcommand plus ctrl subcommands for
agent/runtime management, config, status, and version.
Server, migrate, and seed binaries remain unchanged.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
MULTICA_AGENT_WORKDIR → MULTICA_REPOS_ROOT to make it clear this is
the parent directory containing all repos, not a single project workdir.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Fix data race on output strings.Builder in codex backend by adding
mutex and waiting for reader goroutine before reading final output
- Fix data race on onTurnDone by initializing it before reader starts
- Fix bug where notificationProtocol zero value "" never matched
"unknown", silently dropping all raw v2 notifications from codex
- Add round-robin polling to prevent runtime starvation in poll loop
- Log errors in claude handleControlRequest instead of silently dropping
- Add 35 tests for pkg/agent covering claude parsing, codex JSON-RPC,
protocol detection, event handling, and helper functions
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Replace deprecated strings.Title with manual capitalize
- Fix race: set codexClient.onMessage before starting reader goroutine
- Remove unused msgCh parameter from codexClient.handleLine
- Route agent stderr through logger instead of dumping to os.Stderr
- Use deterministic agent order in ensurePaired (prefer codex)
- Increase message channel buffer from 64 to 256
- Rename test to match function rename (buildPrompt)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add a reusable Go agent package (server/pkg/agent/) that provides a
unified Backend interface for executing prompts via either Claude Code
or Codex. The daemon now auto-detects which CLIs are available at
startup, registers a runtime for each, and routes tasks to the correct
backend based on task.Context.Runtime.Provider.
Key changes:
- server/pkg/agent/agent.go: Backend interface, Message/Result types, factory
- server/pkg/agent/claude.go: Spawns claude CLI with stream-json, parses output
- server/pkg/agent/codex.go: Spawns codex app-server, JSON-RPC 2.0 protocol
- server/cmd/daemon/daemon.go: Multi-runtime registration, round-robin polling,
provider-based backend selection. Removes old runCodexExec/codexResultSchema.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add UpdateComment and DeleteComment handlers with /api/comments/{commentId} routes
- Add broadcast for comment create/update/delete WebSocket events
- Support status, priority, and assignee_id filters on ListIssues
- Extend UpdateIssue to handle due_date, acceptance_criteria, context_refs, repository
- Properly distinguish "field not sent" vs "field sent as null" in UpdateIssue
- Add corresponding SDK methods and TypeScript types
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Complete agents management page with create dialog, runtime device selector,
skills/tools/triggers/tasks tabs, and agent detail view
- Add AssigneePicker to issue detail page for assigning to members or agents
- Extend agent types with description, skills, tools, triggers, RuntimeDevice
- Add SDK methods for agent CRUD and task listing
- Add migration 002 for agent config columns (skills, tools, triggers)
- Update seed data with realistic agent configurations
- Use auth context as single source of truth for agents (fixes state sync)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add idempotent seed tool with duplicate detection for agents/issues/comments
- Add migration CLI supporting up/down with schema_migrations tracking
- Add Makefile targets: make setup (first-time), make start, make stop
- Update .gitignore for test artifacts and compiled binaries
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add JWT middleware unit tests (8 tests covering all auth edge cases)
- Add WebSocket hub tests (5 tests for client lifecycle and broadcast)
- Add full HTTP integration tests (12 tests through real Chi router with DB)
- Add frontend component tests for login, issues, and issue detail pages
- Add auth context unit tests (9 tests for login/logout/name resolution)
- Add Playwright E2E tests for auth, issues, comments, and navigation
- Configure Vitest with jsdom, React plugin, and path aliases
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>