Files
multica/server/pkg/protocol/events.go
Bohan Jiang 44608713bb feat(projects): typed project resources + agent runtime injection (#1926)
* feat(projects): typed project resources + agent runtime injection

Adds a `project_resource` table that lets a project carry typed pointers
(github_repo today, more later) and surfaces them at agent runtime.

Server
- migration 065: project_resource (resource_type TEXT + resource_ref JSONB)
- sqlc CRUD + handler at /api/projects/{id}/resources
- claim handler attaches project_id/title + resources to issue tasks

Daemon
- TaskContextForEnv carries project context
- writes .multica/project/resources.json into workdir
- adds "## Project Context" block to CLAUDE.md / AGENTS.md / GEMINI.md
  via type-dispatched formatter so new resource types just add a case

CLI
- multica project create --repo <url> attaches repos in one step
- multica project resource add/list/remove

Frontend
- Project create modal: Repos pill (workspace repos + ad-hoc URL)
- Project detail sidebar: collapsible Resources section with attach/remove

Docs
- New "Project Resources" chapter explaining the abstraction and
  exactly what code to touch when adding a new resource type

Co-authored-by: multica-agent <github@multica.ai>

* fix(projects): transactional resources[] on create + generic CLI ref + test fix

Addresses review feedback on PR #1926:

1. CI red: TestProjectResourceLifecycle delete step called withURLParam
   twice, which replaced the chi route context and dropped the project id.
   Switched to the existing withURLParams helper from daemon_test.go.

2. POST /api/projects now accepts resources[] and attaches them in the
   same transaction as the project. Invalid refs roll back the whole
   create — no more half-attached projects on failure. Web modal + CLI
   `project create --repo` both use the new bundled payload.

3. CLI `project resource add` now accepts a generic --ref '<json>' flag
   so a new resource_type works without a CLI change. Per-type
   shortcuts (--url for github_repo) remain as a convenience but are no
   longer the only way in. Docs updated to drop the CLI from the
   "files you must touch" list.

Adds two new server handler tests:
- TestCreateProjectAttachesResources (resources[] happy path)
- TestCreateProjectRollsBackOnInvalidResource (transactional rollback)

Co-authored-by: multica-agent <github@multica.ai>

---------

Co-authored-by: multica-agent <github@multica.ai>
2026-04-30 14:00:43 +08:00

108 lines
3.6 KiB
Go

package protocol
// Event types for WebSocket communication between server, web clients, and daemon.
const (
// Issue events
EventIssueCreated = "issue:created"
EventIssueUpdated = "issue:updated"
EventIssueDeleted = "issue:deleted"
// Comment events
EventCommentCreated = "comment:created"
EventCommentUpdated = "comment:updated"
EventCommentDeleted = "comment:deleted"
EventReactionAdded = "reaction:added"
EventReactionRemoved = "reaction:removed"
EventIssueReactionAdded = "issue_reaction:added"
EventIssueReactionRemoved = "issue_reaction:removed"
// Agent events
EventAgentStatus = "agent:status"
EventAgentCreated = "agent:created"
EventAgentArchived = "agent:archived"
EventAgentRestored = "agent:restored"
// Task events (server <-> daemon).
// Each event maps to a status transition on agent_task_queue. Front-end
// subscribes by `task:` prefix and invalidates the workspace task
// snapshot, so the granularity here is "what does the user want to see
// change" — not "every internal status flip".
EventTaskQueued = "task:queued" // ∅ → queued (enqueue / retry create)
EventTaskDispatch = "task:dispatch" // queued → dispatched (daemon claim)
EventTaskProgress = "task:progress"
EventTaskCompleted = "task:completed" // running → completed
EventTaskFailed = "task:failed" // running → failed
EventTaskMessage = "task:message"
EventTaskCancelled = "task:cancelled" // * → cancelled
// Inbox events
EventInboxNew = "inbox:new"
EventInboxRead = "inbox:read"
EventInboxArchived = "inbox:archived"
EventInboxBatchRead = "inbox:batch-read"
EventInboxBatchArchived = "inbox:batch-archived"
// Workspace events
EventWorkspaceUpdated = "workspace:updated"
EventWorkspaceDeleted = "workspace:deleted"
// Member events
EventMemberAdded = "member:added"
EventMemberUpdated = "member:updated"
EventMemberRemoved = "member:removed"
// Subscriber events
EventSubscriberAdded = "subscriber:added"
EventSubscriberRemoved = "subscriber:removed"
// Activity events
EventActivityCreated = "activity:created"
// Skill events
EventSkillCreated = "skill:created"
EventSkillUpdated = "skill:updated"
EventSkillDeleted = "skill:deleted"
// Chat events
EventChatMessage = "chat:message"
EventChatDone = "chat:done"
EventChatSessionRead = "chat:session_read"
// Project events
EventProjectCreated = "project:created"
EventProjectUpdated = "project:updated"
EventProjectDeleted = "project:deleted"
EventProjectResourceCreated = "project_resource:created"
EventProjectResourceDeleted = "project_resource:deleted"
// Label events
EventLabelCreated = "label:created"
EventLabelUpdated = "label:updated"
EventLabelDeleted = "label:deleted"
EventIssueLabelsChanged = "issue_labels:changed"
// Pin events
EventPinCreated = "pin:created"
EventPinDeleted = "pin:deleted"
EventPinReordered = "pin:reordered"
// Invitation events
EventInvitationCreated = "invitation:created"
EventInvitationAccepted = "invitation:accepted"
EventInvitationDeclined = "invitation:declined"
EventInvitationRevoked = "invitation:revoked"
// Autopilot events
EventAutopilotCreated = "autopilot:created"
EventAutopilotUpdated = "autopilot:updated"
EventAutopilotDeleted = "autopilot:deleted"
EventAutopilotRunStart = "autopilot:run_start"
EventAutopilotRunDone = "autopilot:run_done"
// Daemon events
EventDaemonHeartbeat = "daemon:heartbeat"
EventDaemonHeartbeatAck = "daemon:heartbeat_ack"
EventDaemonRegister = "daemon:register"
EventDaemonTaskAvailable = "daemon:task_available"
)