跳到內容

領域事件

DomainEventBus 是一條行程內的廣播式發佈/訂閱匯流排。發佈方呼叫 publish(event);訂閱者消費帶型別的 on<T>() 串流。每個事件都實作 DomainEvent,並攜帶 occurredAt。

這條匯流排執行於 cc_server 內部。 每個發佈方與每個存活的訂閱者都在其中。沒有任何用戶端看得見這條匯流排——伺服器會把精選的子集轉送為 notifications/* JSON-RPC 訊框,並依工作區成員資格為每個訂閱者篩選。

指標 數量
具體的事件類別 54
原始碼檔案(packages/cc_domain/lib/core/domain/events/) 14(加上 domain_event_bus.dart)
從未直接發佈的抽象基底 1(TaskLifecycleEvent,一個密封階層)

下文目錄中的每個事件都位於這一個目錄;沒有在其他地方宣告的領域事件。

事件 承載內容 觸發時機
WorkspaceCreated workspaceId 建立工作區
AgentRunCompleted agentId, runId?, workspaceId?, conversationId? 代理完成一次執行(成功或失敗)
AgentAwaitingInput workspaceId, kind, summary, spaceId?, conversationId?, agentId?, agentName? 代理開始等待人員處理:一項核准、一個 ask_user 問題,或一次因憑證而遭擱置的執行(kind 為 approval、question 或 credential)
RepoAdded repoId, path, workspaceId 註冊儲存庫;觸發背景程式碼索引
SkillUpdated workspaceId, slug, origin, computedHash, scanVerdict? 安裝或更新技能,並附帶其供應鏈掃描判定

第一組是 PR 自身的生命週期。第二組是自己撰寫的 PR 監看:關於你開啟的 PR 的訊號,每個都攜帶 forUserId,讓輪詢能把訊號路由給它們所關乎的人,而不是廣播出去。

事件 承載內容 觸發時機
PullRequestPublished prId, workspaceId, repoOwner, repoName 代理開啟 PR
PullRequestStatusChanged status, workspaceId, prId?, repoFullName?, prNumber? PR 合併/關閉/開啟/重新開啟/已核准
PrMerged prId, workspaceId, agentId PR 已合併——僅限合併的狹隘訊號,保留給分析與通知之用
PrReviewRequested workspaceId, repoOwner, repoName, prNumber, prTitle GitHub 要求伺服器使用者審查(通知輪詢,review_requested)
PrMentioned workspaceId, repoOwner, repoName, prNumber, prTitle 伺服器使用者在 PR 上被提及(通知輪詢,mention)
ExternalPrMerged workspaceId, repoOwner, repoName, prNumber, prTitle, mergedByLogin? 外部追蹤的(非代理)PR 已合併,由 GitHub 通知輪詢偵測到;對應至 prMerged 通知類別
ExternalPrDetected repoOwner, repoName, prNumber, prTitle, author, workspaceId? 輪詢找到的非代理 PR——僅供管線使用(驅動 external_pr_welcome),不會產生通知
PrHeadChanged repoOwner, repoName, prNumber, prTitle, previousHeadSha, headSha, workspaceId PR 的 head SHA 變動——作者推送了內容
ReviewBecameStale workspaceId, spaceId, repoOwner, repoName, prNumber, prTitle, reviewedHeadSha, headSha 已完成的 AI 審查不再符合它所審查的 PR。只有在被取代的提交有審查存在時才會觸發,因此它維持為一個訊號,而不是每次推送都發一次提醒

以下六個都攜帶 workspaceId、repoOwner、repoName、prNumber、prTitle,以及選用的 forUserId。

事件 額外的承載內容 觸發時機
PrMergeReadinessChanged ready, reason 你撰寫的 PR 變成可合併,或不再可合併
PrReviewDecisionChanged decision, reviewersRemaining, approverLogin? 審查者核准、要求變更,或某個核准遭到撤銷
PrChecksStatusChanged failing, failingCheckName?, failingCheckUrl? CI 轉紅,或恢復正常
PrCommentMentioned commentId, authorLogin, bodyPreview, isReviewComment, threadId?, path?, line? 有人在 PR 留言或審查留言中提及你
PrThreadReplied commentId, authorLogin, bodyPreview, threadId?, path?, line? 有人在你參與的審查討論串中回覆
PrThreadResolved threadId, commentId?, path?, line? 有人將你參與的審查討論串標為已解決
事件 承載內容 觸發時機
MessageReceived spaceId, messageId, senderName, contentPreview, isAgentMessage, workspaceId?, mentions, requestedByUserId?, senderUserId? 有新訊息送達
SpaceCreated spaceId, workspaceId 建立空間
SpaceDeleted spaceId, workspaceId 刪除空間
SpaceProvisioningChanged workspaceId, spaceId, status, step? 空間的工作區進入佈建、抵達某個步驟(複製儲存庫、簽出 PR、設定代理),或轉為就緒/失敗——聊天橋接會在任務卡片上敘述這些進展
事件 承載內容 觸發時機
TicketCreated ticketId, workspaceId 建立工單
TicketAssigned ticketId, ticketTitle, ticketBody?, ticketUrl?, assignedAgentId?, assigneeType, assignedTeamId?, workspaceId 工單指派給主體或團隊
TicketCompleted ticketId 工作完成(終態)
TicketFailed ticketId, errorMessage 代理失敗(終態)
TicketCancelled ticketId 工單取消(終態)
TicketStatusChanged ticketId, workspaceId, from, to 任何狀態變更
TicketReassigned ticketId, workspaceId, fromAgentId?, toAgentId? 工單改派
TicketDetailsUpdated ticketId, workspaceId 標題、描述或優先順序變更

統一的任務生命週期串流——一次派送的代理執行,以其執行記錄 id 為鍵。每個事件都攜帶 taskId、每個任務單調遞增的 seq、workspaceId?、agentId? 與 phase;下表只列出額外的欄位。

事件 承載內容 觸發時機
TaskDispatched — 任務交給後端執行
TaskRunning — 任務開始產生輸出
TaskWaitingLocalDirectory lockedPath, holderTaskId? 任務已停放——另一個任務持有它所需的磁碟路徑鎖
TaskProgress note? 執行中途的進度檢查點
TaskMessage messageType, content 串流訊息(text/thinking/tool_use/tool_result/error);回報於 progress 階段之下
TaskCompleted summary? 任務成功完成
TaskFailed errorMessage 任務因錯誤而結束
TaskCancelled — 任務在完成前取消

TaskPhase 列舉也有一個 queued 成員,但沒有任何東西發佈佇列事件——並不存在 TaskQueued 類別。任務第一個可觀察到的訊框是 TaskDispatched。

事件 承載內容 觸發時機
PipelineRunCompleted workspaceId, pipelineRunId, templateId 執行完成
PipelineRunCancelled workspaceId, pipelineRunId, templateId 執行取消(由使用者或系統發起)——刻意的停止,而非錯誤
PipelineRunFailed workspaceId, pipelineRunId, templateId, errorMessage 執行失敗

只會發佈終態轉換。沒有 PipelineRunStarted,也沒有逐步事件:步驟的進度是透過執行本身的資料列,以及它派送之代理的任務生命週期串流送達用戶端,而不是透過匯流排。

事件 承載內容 觸發時機
ActivityLogged id, actorType, actorId?, action, entityType, entityId?, details?, workspaceId?, runId? 建立稽核軌跡項目
BudgetThresholdCrossed scopeType, scopeId, spentCents, budgetCents, isHardStop 支出超過閾值
事件 承載內容 觸發時機
WorkspaceMemberAdded workspaceId, userId, role 使用者加入工作區(兌換邀請或由管理員加入)
WorkspaceMemberRemoved workspaceId, userId 成員遭到移除——該使用者在此工作區範圍內的存活工作階段必須立即重新檢查存取權
WorkspaceMemberRoleChanged workspaceId, userId, role 成員的角色變更

成員資格是匯流排上涵蓋的完整身分範圍。使用者建立與裝置撤銷都會發生,但不會發佈為領域事件——撤銷是直接對存活的工作階段強制執行,而不是透過匯流排路由。

事件 承載內容 觸發時機
CalendarAuthExpired workspaceId, accountEmail 已連接帳號的 OAuth 重新整理權杖永久失效(每次中斷連線事件發佈一次)
MeetingStartingSoon workspaceId, eventId, title, startTime, meetingUrl? 日曆活動將在設定的提前時間範圍內開始
MeetingRecordingStopped workspaceId, meetingId, title, userNotes, transcript, summaryInstructions? 會議錄製結束,並觸發內建的 meeting_summary 管線
事件 承載內容 觸發時機
RigClosedEvent workspaceId, rigId, reason 機台消失,並附帶其關閉原因;消費者只對 backendFailure 採取行動——其他每個原因都是被要求的,或伴隨一個 RigReaped
RigControlChanged workspaceId, rigId, controller? 有人取得機台的獨佔控制權,或交還控制權(釋出時 controller 為 null——事件不會記錄是誰放手的)
RigReaped workspaceId, rigId, reason, agentId? 系統回收了一台機器:閒置、TTL 或記憶體壓力驅逐(回報為 idleTimeout),並附帶正在操作它的代理

機台開啟不是事件。開機完成後,機台的資料列轉為就緒;沒有任何東西被發佈,因此沒有 RigOpened 可以訂閱。

有幾個從外部看起來像是事件驅動的子系統,其實並不是。這裡列出它們,是因為它們的缺席是一項設計決策,值得你在尋找某個掛鉤之前先了解:

子領域 狀態改以何種方式流動
記憶 record_memory_fact 等函式寫入後就回傳;回想則是讀取。無論是事實或衝突,都沒有任何東西會發佈
編排 提案、其核准,以及產生的管線執行,都是資料列與 RPC 呼叫
計畫文件 submit_plan 寫入一個修訂版本;核准則具體化為一次編排。兩者都不會公告
成品 publish_artifact / revise_artifact 直接寫入修訂版本
核准 升級是在核准工作流程服務內部處理
工單同步 供應商協調器訂閱上述工單事件;傳入的 webhook 則直接套用
日曆重新整理 同步會 upsert 活動,用戶端再重新讀取;沒有重新整理的廣播
工作樹合併 記錄在 worktree_merge_log,並從那裡讀取

以下每個訂閱者都是在 runCcServer 內建構(或由它建構的服務建構)。沒有用戶端訂閱者。

訂閱者 消費的事件
WorkspaceSeeder WorkspaceCreated
入門評測套件植入(內嵌) WorkspaceCreated
聊天空間佈建(內嵌) SpaceCreated
PipelineTriggerDispatcher 每個 DomainEvent;每個已啟用的觸發器會篩選自己的型別
SubPipelineResumeListener PipelineRunCompleted, PipelineRunFailed
PipelineCostRollupListener AgentRunCompleted
BudgetEvaluationListener AgentRunCompleted
MeetingSummaryReconciler PipelineRunCompleted, PipelineRunFailed, PipelineRunCancelled(僅限 meeting_summary 執行)
PipelineStepResumeListener AgentRunCompleted
AgentRunTaskCompleter AgentRunCompleted
GoalSupervisor AgentRunCompleted
CheckerDispatchListener AgentRunCompleted
TeamRoutingService AgentRunCompleted, TicketAssigned
OrchestrationRunListener PipelineRunCancelled, PipelineRunFailed
MultiVendorTicketSyncCoordinator TicketCreated, TicketAssigned, TicketReassigned, TicketStatusChanged, TicketDetailsUpdated
WorktreeGcListener TicketCompleted, TicketCancelled, SpaceDeleted, PrMerged, PullRequestStatusChanged
ChatBridgeService MessageReceived, SpaceProvisioningChanged
ActivityLogPersister ActivityLogged
RigEventListener RigControlChanged, RigReaped, RigClosedEvent——在操作中代理的引導通道注入一則平實的提示
StaleReviewWatcher PrHeadChanged——只有在被取代的提交已有完成的審查時,才發佈 ReviewBecameStale
GitHubPrConversationBridge AgentRunCompleted——對外的 PR 對話回覆
NotificationFeedRecorder 下列二十二個通知類事件
RemoteEventForwarder 那二十二個,再加上 TicketReassigned 與每個 TaskLifecycleEvent。三個成員資格事件只會使其每個工作區的成員資格快取失效;它們不會以訊框轉送
LocalRpcServer / RemoteRelayHost WorkspaceMemberRemoved——捨棄該使用者已附加的工作區訂閱;socket 保持開啟

TicketAssigned 是稽核與通知訊號,也是管線觸發器。指派給代理會記錄擁有權,但不會啟動任何執行——沒有工單派送器。指派給團隊(已設定 assignedTeamId)是例外:TeamRoutingService 會派送團隊領導者。

TicketRemoteSyncHandler 已被宣告,但從未在正式程式碼中建構,因此它目前不訂閱任何東西。用戶端的 NotificationEventMapper 只在它自己的測試中建構——它是用戶端/伺服器分割之前,行程內路徑的殘留。即時快顯通知來自下列的線上訊框。

在伺服器上,NotificationFeedRecorder 與 RemoteEventForwarder 把這些事件轉成 notifications/* 線上訊框(notification_wire.dart)。在用戶端,RpcNotificationMapper → mapNotificationFrame 會把訊框轉成 AppNotification。

通知類別正好有十七個。有幾個事件會對應到多個線上方法,因為訊框承載的是轉折(失敗還是已恢復),而類別承載的是問題(「CI 現在怎麼了?」)。

類別 來源事件 線上方法
newMessage MessageReceived notifications/message_received(提及你)
agentRunCompleted MessageReceived notifications/message_received(is_agent_message,未提及你)
agentAwaitingInput AgentAwaitingInput notifications/agent_awaiting_input
ticketAssigned TicketAssigned notifications/ticket_assigned
ticketStatusChanged TicketStatusChanged notifications/ticket_status_changed
pullRequestPublished PullRequestPublished notifications/pr_published
prMerged PrMerged notifications/pr_merged
prMerged ExternalPrMerged notifications/external_pr_merged
prMentioned PrMentioned notifications/pr_mentioned
prMentioned PrCommentMentioned notifications/pr_comment_mentioned
reviewRequested PrReviewRequested notifications/pr_review_requested
reviewStale ReviewBecameStale notifications/review_stale
prMergeReadiness PrMergeReadinessChanged notifications/pr_ready_to_merge / pr_merge_blocked
prReviewDecision PrReviewDecisionChanged notifications/pr_approved / pr_changes_requested / pr_review_dismissed
prChecksStatus PrChecksStatusChanged notifications/pr_checks_failed / pr_checks_recovered
prThreadActivity PrThreadReplied notifications/pr_thread_replied
prThreadActivity PrThreadResolved notifications/pr_thread_resolved
meetingStartsSoon MeetingStartingSoon notifications/meeting_starting_soon
calendarAuthExpired CalendarAuthExpired notifications/calendar_auth_expired
rigStatusChanged RigControlChanged notifications/rig_control_changed
rigStatusChanged RigReaped notifications/rig_reaped
rigStatusChanged RigClosedEvent notifications/rig_closed

機台的接管訊框不會送回給接下方向盤的人,釋出則不受篩選(事件不會記錄是誰放手的),而且只有 backendFailure 關閉會產生 rig_closed 訊框——其他原因不是被要求的,就是已由回收訊框涵蓋。

AgentAwaitingInput 由 AgentAwaitingInputPublisher 發佈:它會監看核准登錄與憑證封鎖登錄中的新項目,並由 AgentQuestionService 告知每個 ask_user 問題。一次等待就是一個事件;等待結束時不會發佈任何事件。

五個自己撰寫的 PR 類別都攜帶 forUserId,因此它們會送達作者,而不是工作區中的每個人。

RemoteEventForwarder 另外會把 TicketReassigned 轉送為 notifications/ticket_reassigned,並把每個 TaskLifecycleEvent 轉送為 notifications/task_<phase>。兩者都不對應到任何通知類別——它們驅動的是即時 UI,而不是通知中心。成員資格事件不會以訊框轉送:它們會使轉送器的快取失效,而工作階段主機則會捨棄被移除成員的工作區訂閱。