跳至內容

領域事件

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,一個 sealed 階層)

下列目錄中的每個事件都位於那一個目錄裡;沒有在其他地方宣告任何領域事件。

事件 載荷 觸發時機
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, 而不是通知中心。成員關係事件不會以訊框轉發:它們會使轉發器的快取失效,而工作 階段主機則會捨棄被移除成員的工作區訂閱。