@@ -2,6 +2,7 @@ package services
22
33import (
44 "context"
5+ "sort"
56 "time"
67
78 providertypes "neo-code/internal/provider/types"
@@ -724,3 +725,116 @@ const (
724725 EventDecisionMade EventType = "decision_made"
725726 EventTodoSnapshotUpdated EventType = "todo_snapshot_updated"
726727)
728+
729+ // contractEntry 描述单个事件类型的契约声明。
730+ type contractEntry struct {
731+ RequireConsumer bool
732+ }
733+
734+ // contractRegistry 声明 TUI 侧已知的事件类型及其消费者要求。
735+ // RequireConsumer=true 表示该事件必须有对应的 gateway decode 分支与 TUI 消费者;
736+ // RequireConsumer=false 表示该事件允许透传(passthrough),不要求显式消费。
737+ var contractRegistry = map [EventType ]contractEntry {
738+ // --- 已有 decode 分支的事件(RequireConsumer=true)---
739+ EventUserMessage : {RequireConsumer : true },
740+ EventAgentDone : {RequireConsumer : true },
741+ EventToolStart : {RequireConsumer : true },
742+ EventToolResult : {RequireConsumer : true },
743+ EventPermissionRequested : {RequireConsumer : true },
744+ EventPermissionResolved : {RequireConsumer : true },
745+ EventUserQuestionRequested : {RequireConsumer : true },
746+ EventUserQuestionAnswered : {RequireConsumer : true },
747+ EventUserQuestionTimeout : {RequireConsumer : true },
748+ EventUserQuestionSkipped : {RequireConsumer : true },
749+ EventCompactApplied : {RequireConsumer : true },
750+ EventCompactError : {RequireConsumer : true },
751+ EventTokenUsage : {RequireConsumer : true },
752+ EventPhaseChanged : {RequireConsumer : true },
753+ EventStopReasonDecided : {RequireConsumer : true },
754+ EventVerificationStarted : {RequireConsumer : true },
755+ EventVerificationStageFinished : {RequireConsumer : true },
756+ EventVerificationFinished : {RequireConsumer : true },
757+ EventVerificationCompleted : {RequireConsumer : true },
758+ EventVerificationFailed : {RequireConsumer : true },
759+ EventAcceptanceDecided : {RequireConsumer : true },
760+ EventInputNormalized : {RequireConsumer : true },
761+ EventAssetSaved : {RequireConsumer : true },
762+ EventAssetSaveFailed : {RequireConsumer : true },
763+ EventHookStarted : {RequireConsumer : true },
764+ EventHookFinished : {RequireConsumer : true },
765+ EventHookFailed : {RequireConsumer : true },
766+ EventHookBlocked : {RequireConsumer : true },
767+ EventHookNotification : {RequireConsumer : true },
768+ EventRepoHooksDiscovered : {RequireConsumer : true },
769+ EventRepoHooksLoaded : {RequireConsumer : true },
770+ EventRepoHooksSkippedUntrusted : {RequireConsumer : true },
771+ EventRepoHooksTrustStoreInvalid : {RequireConsumer : true },
772+ EventCheckpointCreated : {RequireConsumer : true },
773+ EventCheckpointWarning : {RequireConsumer : true },
774+ EventCheckpointRestored : {RequireConsumer : true },
775+ EventCheckpointUndoRestore : {RequireConsumer : true },
776+ EventToolDiff : {RequireConsumer : true },
777+ EventBashSideEffect : {RequireConsumer : true },
778+ EventTodoUpdated : {RequireConsumer : true },
779+ EventTodoConflict : {RequireConsumer : true },
780+ EventTodoSnapshotUpdated : {RequireConsumer : true },
781+ EventSubAgentStarted : {RequireConsumer : true },
782+ EventSubAgentProgress : {RequireConsumer : true },
783+ EventSubAgentRetried : {RequireConsumer : true },
784+ EventSubAgentBlocked : {RequireConsumer : true },
785+ EventSubAgentCompleted : {RequireConsumer : true },
786+ EventSubAgentFailed : {RequireConsumer : true },
787+ EventSubAgentCanceled : {RequireConsumer : true },
788+ EventSubAgentFinished : {RequireConsumer : true },
789+ EventSubAgentToolCallStarted : {RequireConsumer : true },
790+ EventSubAgentToolCallResult : {RequireConsumer : true },
791+ EventSubAgentToolCallDenied : {RequireConsumer : true },
792+ EventRuntimeSnapshotUpdated : {RequireConsumer : true },
793+ EventSubAgentSnapshotUpdated : {RequireConsumer : true },
794+ EventDecisionMade : {RequireConsumer : true },
795+
796+ // --- 字符串类 payload 事件(有 decode 分支,透传字符串)---
797+ EventAgentChunk : {RequireConsumer : true },
798+ EventToolChunk : {RequireConsumer : true },
799+ EventError : {RequireConsumer : true },
800+ EventToolCallThinking : {RequireConsumer : true },
801+ EventCompactStart : {RequireConsumer : true },
802+
803+ // --- 显式声明为透传安全(passthrough-safe)的事件 ---
804+ // 这些事件在 runtime 侧产生但不要求 TUI 显式消费,
805+ // 未在 gateway decode 中处理时会以原始 payload 透传。
806+ EventRunCanceled : {RequireConsumer : false },
807+ EventSkillActivated : {RequireConsumer : false },
808+ EventSkillDeactivated : {RequireConsumer : false },
809+ EventSkillMissing : {RequireConsumer : false },
810+ EventProgressEvaluated : {RequireConsumer : false },
811+ EventTodoSummaryInjected : {RequireConsumer : false },
812+ }
813+
814+ // RegisteredEventTypes 返回所有已注册的契约事件类型(排序后)。
815+ func RegisteredEventTypes () []EventType {
816+ types := make ([]EventType , 0 , len (contractRegistry ))
817+ for eventType := range contractRegistry {
818+ types = append (types , eventType )
819+ }
820+ sort .Slice (types , func (i , j int ) bool {
821+ return types [i ] < types [j ]
822+ })
823+ return types
824+ }
825+
826+ // RequireConsumer 返回指定事件类型是否要求显式消费者。
827+ // 若事件类型未注册,返回 false(允许透传)。
828+ func RequireConsumer (eventType EventType ) bool {
829+ entry , ok := contractRegistry [eventType ]
830+ if ! ok {
831+ return false
832+ }
833+ return entry .RequireConsumer
834+ }
835+
836+ // IsRegisteredEventType 返回指定事件类型是否已注册到契约中。
837+ func IsRegisteredEventType (eventType EventType ) bool {
838+ _ , ok := contractRegistry [eventType ]
839+ return ok
840+ }
0 commit comments