@yuaone/core 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (235) hide show
  1. package/LICENSE +663 -0
  2. package/README.md +15 -0
  3. package/dist/__tests__/context-manager.test.d.ts +6 -0
  4. package/dist/__tests__/context-manager.test.d.ts.map +1 -0
  5. package/dist/__tests__/context-manager.test.js +220 -0
  6. package/dist/__tests__/context-manager.test.js.map +1 -0
  7. package/dist/__tests__/governor.test.d.ts +6 -0
  8. package/dist/__tests__/governor.test.d.ts.map +1 -0
  9. package/dist/__tests__/governor.test.js +210 -0
  10. package/dist/__tests__/governor.test.js.map +1 -0
  11. package/dist/__tests__/model-router.test.d.ts +6 -0
  12. package/dist/__tests__/model-router.test.d.ts.map +1 -0
  13. package/dist/__tests__/model-router.test.js +329 -0
  14. package/dist/__tests__/model-router.test.js.map +1 -0
  15. package/dist/agent-logger.d.ts +384 -0
  16. package/dist/agent-logger.d.ts.map +1 -0
  17. package/dist/agent-logger.js +820 -0
  18. package/dist/agent-logger.js.map +1 -0
  19. package/dist/agent-loop.d.ts +163 -0
  20. package/dist/agent-loop.d.ts.map +1 -0
  21. package/dist/agent-loop.js +609 -0
  22. package/dist/agent-loop.js.map +1 -0
  23. package/dist/agent-modes.d.ts +85 -0
  24. package/dist/agent-modes.d.ts.map +1 -0
  25. package/dist/agent-modes.js +418 -0
  26. package/dist/agent-modes.js.map +1 -0
  27. package/dist/approval.d.ts +137 -0
  28. package/dist/approval.d.ts.map +1 -0
  29. package/dist/approval.js +299 -0
  30. package/dist/approval.js.map +1 -0
  31. package/dist/async-completion-queue.d.ts +56 -0
  32. package/dist/async-completion-queue.d.ts.map +1 -0
  33. package/dist/async-completion-queue.js +77 -0
  34. package/dist/async-completion-queue.js.map +1 -0
  35. package/dist/auto-fix.d.ts +174 -0
  36. package/dist/auto-fix.d.ts.map +1 -0
  37. package/dist/auto-fix.js +319 -0
  38. package/dist/auto-fix.js.map +1 -0
  39. package/dist/codebase-context.d.ts +396 -0
  40. package/dist/codebase-context.d.ts.map +1 -0
  41. package/dist/codebase-context.js +1260 -0
  42. package/dist/codebase-context.js.map +1 -0
  43. package/dist/conflict-resolver.d.ts +191 -0
  44. package/dist/conflict-resolver.d.ts.map +1 -0
  45. package/dist/conflict-resolver.js +524 -0
  46. package/dist/conflict-resolver.js.map +1 -0
  47. package/dist/constants.d.ts +52 -0
  48. package/dist/constants.d.ts.map +1 -0
  49. package/dist/constants.js +141 -0
  50. package/dist/constants.js.map +1 -0
  51. package/dist/context-budget.d.ts +435 -0
  52. package/dist/context-budget.d.ts.map +1 -0
  53. package/dist/context-budget.js +903 -0
  54. package/dist/context-budget.js.map +1 -0
  55. package/dist/context-compressor.d.ts +143 -0
  56. package/dist/context-compressor.d.ts.map +1 -0
  57. package/dist/context-compressor.js +511 -0
  58. package/dist/context-compressor.js.map +1 -0
  59. package/dist/context-manager.d.ts +112 -0
  60. package/dist/context-manager.d.ts.map +1 -0
  61. package/dist/context-manager.js +247 -0
  62. package/dist/context-manager.js.map +1 -0
  63. package/dist/continuous-reflection.d.ts +267 -0
  64. package/dist/continuous-reflection.d.ts.map +1 -0
  65. package/dist/continuous-reflection.js +338 -0
  66. package/dist/continuous-reflection.js.map +1 -0
  67. package/dist/cross-file-refactor.d.ts +352 -0
  68. package/dist/cross-file-refactor.d.ts.map +1 -0
  69. package/dist/cross-file-refactor.js +1544 -0
  70. package/dist/cross-file-refactor.js.map +1 -0
  71. package/dist/dag-orchestrator.d.ts +138 -0
  72. package/dist/dag-orchestrator.d.ts.map +1 -0
  73. package/dist/dag-orchestrator.js +379 -0
  74. package/dist/dag-orchestrator.js.map +1 -0
  75. package/dist/debate-orchestrator.d.ts +301 -0
  76. package/dist/debate-orchestrator.d.ts.map +1 -0
  77. package/dist/debate-orchestrator.js +719 -0
  78. package/dist/debate-orchestrator.js.map +1 -0
  79. package/dist/dependency-analyzer.d.ts +113 -0
  80. package/dist/dependency-analyzer.d.ts.map +1 -0
  81. package/dist/dependency-analyzer.js +444 -0
  82. package/dist/dependency-analyzer.js.map +1 -0
  83. package/dist/design-loop.d.ts +59 -0
  84. package/dist/design-loop.d.ts.map +1 -0
  85. package/dist/design-loop.js +344 -0
  86. package/dist/design-loop.js.map +1 -0
  87. package/dist/doc-intelligence.d.ts +383 -0
  88. package/dist/doc-intelligence.d.ts.map +1 -0
  89. package/dist/doc-intelligence.js +1307 -0
  90. package/dist/doc-intelligence.js.map +1 -0
  91. package/dist/dynamic-role-generator.d.ts +76 -0
  92. package/dist/dynamic-role-generator.d.ts.map +1 -0
  93. package/dist/dynamic-role-generator.js +194 -0
  94. package/dist/dynamic-role-generator.js.map +1 -0
  95. package/dist/errors.d.ts +69 -0
  96. package/dist/errors.d.ts.map +1 -0
  97. package/dist/errors.js +102 -0
  98. package/dist/errors.js.map +1 -0
  99. package/dist/event-bus.d.ts +159 -0
  100. package/dist/event-bus.d.ts.map +1 -0
  101. package/dist/event-bus.js +305 -0
  102. package/dist/event-bus.js.map +1 -0
  103. package/dist/execution-engine.d.ts +425 -0
  104. package/dist/execution-engine.d.ts.map +1 -0
  105. package/dist/execution-engine.js +1555 -0
  106. package/dist/execution-engine.js.map +1 -0
  107. package/dist/git-intelligence.d.ts +306 -0
  108. package/dist/git-intelligence.d.ts.map +1 -0
  109. package/dist/git-intelligence.js +1099 -0
  110. package/dist/git-intelligence.js.map +1 -0
  111. package/dist/governor.d.ts +77 -0
  112. package/dist/governor.d.ts.map +1 -0
  113. package/dist/governor.js +161 -0
  114. package/dist/governor.js.map +1 -0
  115. package/dist/hierarchical-planner.d.ts +313 -0
  116. package/dist/hierarchical-planner.d.ts.map +1 -0
  117. package/dist/hierarchical-planner.js +981 -0
  118. package/dist/hierarchical-planner.js.map +1 -0
  119. package/dist/index.d.ts +121 -0
  120. package/dist/index.d.ts.map +1 -0
  121. package/dist/index.js +123 -0
  122. package/dist/index.js.map +1 -0
  123. package/dist/intent-inference.d.ts +103 -0
  124. package/dist/intent-inference.d.ts.map +1 -0
  125. package/dist/intent-inference.js +605 -0
  126. package/dist/intent-inference.js.map +1 -0
  127. package/dist/interrupt-manager.d.ts +143 -0
  128. package/dist/interrupt-manager.d.ts.map +1 -0
  129. package/dist/interrupt-manager.js +196 -0
  130. package/dist/interrupt-manager.js.map +1 -0
  131. package/dist/kernel.d.ts +564 -0
  132. package/dist/kernel.d.ts.map +1 -0
  133. package/dist/kernel.js +1419 -0
  134. package/dist/kernel.js.map +1 -0
  135. package/dist/language-support.d.ts +232 -0
  136. package/dist/language-support.d.ts.map +1 -0
  137. package/dist/language-support.js +1134 -0
  138. package/dist/language-support.js.map +1 -0
  139. package/dist/llm-client.d.ts +82 -0
  140. package/dist/llm-client.d.ts.map +1 -0
  141. package/dist/llm-client.js +475 -0
  142. package/dist/llm-client.js.map +1 -0
  143. package/dist/mcp-client.d.ts +232 -0
  144. package/dist/mcp-client.d.ts.map +1 -0
  145. package/dist/mcp-client.js +718 -0
  146. package/dist/mcp-client.js.map +1 -0
  147. package/dist/memory-manager.d.ts +200 -0
  148. package/dist/memory-manager.d.ts.map +1 -0
  149. package/dist/memory-manager.js +568 -0
  150. package/dist/memory-manager.js.map +1 -0
  151. package/dist/memory.d.ts +87 -0
  152. package/dist/memory.d.ts.map +1 -0
  153. package/dist/memory.js +341 -0
  154. package/dist/memory.js.map +1 -0
  155. package/dist/model-router.d.ts +245 -0
  156. package/dist/model-router.d.ts.map +1 -0
  157. package/dist/model-router.js +632 -0
  158. package/dist/model-router.js.map +1 -0
  159. package/dist/parallel-executor.d.ts +125 -0
  160. package/dist/parallel-executor.d.ts.map +1 -0
  161. package/dist/parallel-executor.js +201 -0
  162. package/dist/parallel-executor.js.map +1 -0
  163. package/dist/perf-optimizer.d.ts +212 -0
  164. package/dist/perf-optimizer.d.ts.map +1 -0
  165. package/dist/perf-optimizer.js +721 -0
  166. package/dist/perf-optimizer.js.map +1 -0
  167. package/dist/persona.d.ts +305 -0
  168. package/dist/persona.d.ts.map +1 -0
  169. package/dist/persona.js +887 -0
  170. package/dist/persona.js.map +1 -0
  171. package/dist/planner.d.ts +70 -0
  172. package/dist/planner.d.ts.map +1 -0
  173. package/dist/planner.js +264 -0
  174. package/dist/planner.js.map +1 -0
  175. package/dist/qa-pipeline.d.ts +365 -0
  176. package/dist/qa-pipeline.d.ts.map +1 -0
  177. package/dist/qa-pipeline.js +1352 -0
  178. package/dist/qa-pipeline.js.map +1 -0
  179. package/dist/reasoning-adapter.d.ts +116 -0
  180. package/dist/reasoning-adapter.d.ts.map +1 -0
  181. package/dist/reasoning-adapter.js +187 -0
  182. package/dist/reasoning-adapter.js.map +1 -0
  183. package/dist/role-registry.d.ts +55 -0
  184. package/dist/role-registry.d.ts.map +1 -0
  185. package/dist/role-registry.js +192 -0
  186. package/dist/role-registry.js.map +1 -0
  187. package/dist/sandbox-tiers.d.ts +327 -0
  188. package/dist/sandbox-tiers.d.ts.map +1 -0
  189. package/dist/sandbox-tiers.js +928 -0
  190. package/dist/sandbox-tiers.js.map +1 -0
  191. package/dist/security-scanner.d.ts +222 -0
  192. package/dist/security-scanner.d.ts.map +1 -0
  193. package/dist/security-scanner.js +1129 -0
  194. package/dist/security-scanner.js.map +1 -0
  195. package/dist/security.d.ts +93 -0
  196. package/dist/security.d.ts.map +1 -0
  197. package/dist/security.js +393 -0
  198. package/dist/security.js.map +1 -0
  199. package/dist/self-reflection.d.ts +397 -0
  200. package/dist/self-reflection.d.ts.map +1 -0
  201. package/dist/self-reflection.js +908 -0
  202. package/dist/self-reflection.js.map +1 -0
  203. package/dist/session-persistence.d.ts +191 -0
  204. package/dist/session-persistence.d.ts.map +1 -0
  205. package/dist/session-persistence.js +395 -0
  206. package/dist/session-persistence.js.map +1 -0
  207. package/dist/speculative-executor.d.ts +210 -0
  208. package/dist/speculative-executor.d.ts.map +1 -0
  209. package/dist/speculative-executor.js +618 -0
  210. package/dist/speculative-executor.js.map +1 -0
  211. package/dist/state-machine.d.ts +289 -0
  212. package/dist/state-machine.d.ts.map +1 -0
  213. package/dist/state-machine.js +695 -0
  214. package/dist/state-machine.js.map +1 -0
  215. package/dist/sub-agent.d.ts +177 -0
  216. package/dist/sub-agent.d.ts.map +1 -0
  217. package/dist/sub-agent.js +303 -0
  218. package/dist/sub-agent.js.map +1 -0
  219. package/dist/system-prompt.d.ts +26 -0
  220. package/dist/system-prompt.d.ts.map +1 -0
  221. package/dist/system-prompt.js +84 -0
  222. package/dist/system-prompt.js.map +1 -0
  223. package/dist/test-intelligence.d.ts +439 -0
  224. package/dist/test-intelligence.d.ts.map +1 -0
  225. package/dist/test-intelligence.js +1165 -0
  226. package/dist/test-intelligence.js.map +1 -0
  227. package/dist/types.d.ts +632 -0
  228. package/dist/types.d.ts.map +1 -0
  229. package/dist/types.js +6 -0
  230. package/dist/types.js.map +1 -0
  231. package/dist/vector-index.d.ts +314 -0
  232. package/dist/vector-index.d.ts.map +1 -0
  233. package/dist/vector-index.js +618 -0
  234. package/dist/vector-index.js.map +1 -0
  235. package/package.json +41 -0
@@ -0,0 +1,159 @@
1
+ /**
2
+ * @module event-bus
3
+ * @description HybridEventBus — 3계층 이벤트 전파 시스템.
4
+ *
5
+ * Layer 1: Process EventEmitter (CLI/Desktop, 0ms 레이턴시)
6
+ * Layer 2: Session EventBus (Web/Mobile, ~1ms)
7
+ * Layer 3: Team Broadcast (팀 모드, Redis Pub/Sub, ~5ms)
8
+ *
9
+ * 기능:
10
+ * - EventEmitter 기반 로컬 이벤트 (Layer 1+2)
11
+ * - 선택적 Redis 연동 (Layer 3, 팀 모드)
12
+ * - 시퀀스 번호 기반 이벤트 버퍼링 (SSE replay)
13
+ * - subscribe/emit/replay/broadcast
14
+ * - 최대 버퍼 500 이벤트
15
+ * - 팀 이벤트 필터 (progress:* 와 team:* 만 Redis로 전파)
16
+ */
17
+ import type { BusEvent } from "./types.js";
18
+ /** HybridEventBus 생성 설정 */
19
+ export interface EventBusConfig {
20
+ /** 세션별 이벤트 버퍼 최대 크기 (기본 500) */
21
+ maxBuffer?: number;
22
+ /** Redis URL (팀 모드에서만 필요, 미지정 시 로컬 전용) */
23
+ redisUrl?: string;
24
+ }
25
+ /** 시퀀스 번호 + 타임스탬프가 추가된 이벤트 */
26
+ export type StampedEvent = BusEvent & {
27
+ /** 세션 내 순서 번호 (1부터 시작) */
28
+ seq: number;
29
+ /** 이벤트 발행 시각 (epoch ms) */
30
+ ts: number;
31
+ };
32
+ /** 이벤트 리스너 함수 시그니처 */
33
+ export interface EventListener {
34
+ (event: StampedEvent): void;
35
+ }
36
+ /** 구독 해제 함수 */
37
+ export type Unsubscribe = () => void;
38
+ /**
39
+ * 3계층 하이브리드 이벤트 버스.
40
+ *
41
+ * - 로컬 EventEmitter로 Layer 1+2 처리 (0ms)
42
+ * - 선택적 Redis Pub/Sub로 Layer 3 팀 브로드캐스트 (~5ms)
43
+ * - 세션별 이벤트 버퍼로 SSE 재연결 시 replay 지원
44
+ *
45
+ * @example
46
+ * ```typescript
47
+ * const bus = new HybridEventBus({ maxBuffer: 500 });
48
+ * await bus.init();
49
+ *
50
+ * const unsub = bus.subscribe("session-1", (event) => {
51
+ * console.log(event.kind, event.seq);
52
+ * });
53
+ *
54
+ * bus.emit("session-1", { kind: "agent:start", goal: "Fix bug" });
55
+ *
56
+ * // SSE 재연결 시 replay
57
+ * const missed = bus.replay("session-1", 5); // seq > 5인 이벤트
58
+ *
59
+ * unsub(); // 구독 해제
60
+ * await bus.destroy();
61
+ * ```
62
+ */
63
+ export declare class HybridEventBus {
64
+ private readonly local;
65
+ private redis;
66
+ private readonly redisUrl;
67
+ private readonly maxBuffer;
68
+ /** 세션별 이벤트 버퍼 (replay용) */
69
+ private readonly buffers;
70
+ /** 세션별 시퀀스 카운터 */
71
+ private readonly seqCounters;
72
+ private initialized;
73
+ constructor(config?: EventBusConfig);
74
+ /**
75
+ * 이벤트 버스 초기화.
76
+ * Redis URL이 설정된 경우 연결을 시도한다.
77
+ * Redis 연결 실패 시에도 로컬 모드로 동작한다.
78
+ */
79
+ init(): Promise<void>;
80
+ /**
81
+ * 이벤트 버스 종료.
82
+ * Redis 연결 해제 + 버퍼 클리어.
83
+ */
84
+ destroy(): Promise<void>;
85
+ /** 팀 모드(Redis) 활성화 여부 */
86
+ get isTeamMode(): boolean;
87
+ /**
88
+ * 세션에 이벤트를 발행한다.
89
+ *
90
+ * 1. 시퀀스 번호 + 타임스탬프 부여
91
+ * 2. 로컬 EventEmitter로 즉시 전파 (Layer 1+2)
92
+ * 3. 버퍼에 저장 (SSE replay용)
93
+ * 4. 팀 이벤트 필터 통과 시 Redis 발행 (Layer 3)
94
+ *
95
+ * @param sessionId 세션 ID
96
+ * @param event 발행할 BusEvent
97
+ * @returns 부여된 시퀀스 번호
98
+ */
99
+ emit(sessionId: string, event: BusEvent): number;
100
+ /**
101
+ * 세션의 이벤트를 구독한다 (Layer 1+2 로컬).
102
+ *
103
+ * @param sessionId 구독할 세션 ID
104
+ * @param listener 이벤트 리스너
105
+ * @returns 구독 해제 함수
106
+ */
107
+ subscribe(sessionId: string, listener: EventListener): Unsubscribe;
108
+ /**
109
+ * SSE 재연결 시 놓친 이벤트를 재전송한다.
110
+ *
111
+ * @param sessionId 세션 ID
112
+ * @param fromSeq 이 시퀀스 이후의 이벤트만 반환 (exclusive)
113
+ * @returns 놓친 이벤트 배열
114
+ */
115
+ replay(sessionId: string, fromSeq: number): StampedEvent[];
116
+ /**
117
+ * 워크스페이스 채널을 구독한다 (팀 모드, Redis 기반).
118
+ *
119
+ * @param workspaceId 워크스페이스 ID
120
+ * @param listener 이벤트 리스너
121
+ * @returns 구독 해제 함수
122
+ * @throws Redis가 활성화되지 않은 경우 에러
123
+ */
124
+ subscribeWorkspace(workspaceId: string, listener: EventListener): Unsubscribe;
125
+ /**
126
+ * 팀 채널로 이벤트를 브로드캐스트한다 (Redis Pub/Sub).
127
+ * 로컬 세션 이벤트와 별도로, 워크스페이스 전체에 발행.
128
+ *
129
+ * @param workspaceId 워크스페이스 ID
130
+ * @param event 브로드캐스트할 이벤트
131
+ */
132
+ broadcast(workspaceId: string, event: BusEvent): Promise<void>;
133
+ /**
134
+ * 세션의 버퍼와 시퀀스 카운터를 정리한다.
135
+ * 세션 종료 시 호출하여 메모리를 해제한다.
136
+ *
137
+ * @param sessionId 정리할 세션 ID
138
+ */
139
+ clearSession(sessionId: string): void;
140
+ /**
141
+ * 현재 버퍼에 저장된 세션의 이벤트 수를 반환한다.
142
+ *
143
+ * @param sessionId 세션 ID
144
+ * @returns 버퍼된 이벤트 수
145
+ */
146
+ getBufferSize(sessionId: string): number;
147
+ /**
148
+ * 세션의 현재 시퀀스 번호를 반환한다.
149
+ *
150
+ * @param sessionId 세션 ID
151
+ * @returns 현재 시퀀스 번호 (이벤트가 없으면 0)
152
+ */
153
+ getCurrentSeq(sessionId: string): number;
154
+ /** 세션의 다음 시퀀스 번호를 발급한다 */
155
+ private nextSeq;
156
+ /** 이벤트를 세션 버퍼에 저장한다 (maxBuffer 초과 시 오래된 이벤트 삭제) */
157
+ private bufferEvent;
158
+ }
159
+ //# sourceMappingURL=event-bus.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"event-bus.d.ts","sourceRoot":"","sources":["../src/event-bus.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAGH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAI3C,2BAA2B;AAC3B,MAAM,WAAW,cAAc;IAC7B,gCAAgC;IAChC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,0CAA0C;IAC1C,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,8BAA8B;AAC9B,MAAM,MAAM,YAAY,GAAG,QAAQ,GAAG;IACpC,0BAA0B;IAC1B,GAAG,EAAE,MAAM,CAAC;IACZ,2BAA2B;IAC3B,EAAE,EAAE,MAAM,CAAC;CACZ,CAAC;AAEF,sBAAsB;AACtB,MAAM,WAAW,aAAa;IAC5B,CAAC,KAAK,EAAE,YAAY,GAAG,IAAI,CAAC;CAC7B;AAED,eAAe;AACf,MAAM,MAAM,WAAW,GAAG,MAAM,IAAI,CAAC;AAiGrC;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,qBAAa,cAAc;IACzB,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAsB;IAC5C,OAAO,CAAC,KAAK,CAAmC;IAChD,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAqB;IAC9C,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IAEnC,2BAA2B;IAC3B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAqC;IAC7D,kBAAkB;IAClB,OAAO,CAAC,QAAQ,CAAC,WAAW,CAA6B;IAEzD,OAAO,CAAC,WAAW,CAAS;gBAEhB,MAAM,GAAE,cAAmB;IAUvC;;;;OAIG;IACG,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAU3B;;;OAGG;IACG,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAY9B,yBAAyB;IACzB,IAAI,UAAU,IAAI,OAAO,CAExB;IAID;;;;;;;;;;;OAWG;IACH,IAAI,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,QAAQ,GAAG,MAAM;IA8BhD;;;;;;OAMG;IACH,SAAS,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,aAAa,GAAG,WAAW;IASlE;;;;;;OAMG;IACH,MAAM,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,YAAY,EAAE;IAQ1D;;;;;;;OAOG;IACH,kBAAkB,CAChB,WAAW,EAAE,MAAM,EACnB,QAAQ,EAAE,aAAa,GACtB,WAAW;IAkBd;;;;;;OAMG;IACG,SAAS,CAAC,WAAW,EAAE,MAAM,EAAE,KAAK,EAAE,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC;IAmBpE;;;;;OAKG;IACH,YAAY,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI;IAMrC;;;;;OAKG;IACH,aAAa,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM;IAIxC;;;;;OAKG;IACH,aAAa,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM;IAMxC,0BAA0B;IAC1B,OAAO,CAAC,OAAO;IAOf,mDAAmD;IACnD,OAAO,CAAC,WAAW;CAepB"}
@@ -0,0 +1,305 @@
1
+ /**
2
+ * @module event-bus
3
+ * @description HybridEventBus — 3계층 이벤트 전파 시스템.
4
+ *
5
+ * Layer 1: Process EventEmitter (CLI/Desktop, 0ms 레이턴시)
6
+ * Layer 2: Session EventBus (Web/Mobile, ~1ms)
7
+ * Layer 3: Team Broadcast (팀 모드, Redis Pub/Sub, ~5ms)
8
+ *
9
+ * 기능:
10
+ * - EventEmitter 기반 로컬 이벤트 (Layer 1+2)
11
+ * - 선택적 Redis 연동 (Layer 3, 팀 모드)
12
+ * - 시퀀스 번호 기반 이벤트 버퍼링 (SSE replay)
13
+ * - subscribe/emit/replay/broadcast
14
+ * - 최대 버퍼 500 이벤트
15
+ * - 팀 이벤트 필터 (progress:* 와 team:* 만 Redis로 전파)
16
+ */
17
+ import { EventEmitter } from "node:events";
18
+ // ─── Redis 팩토리 (동적 import) ───
19
+ /**
20
+ * ioredis가 설치된 환경에서만 Redis 어댑터를 생성.
21
+ * 설치되지 않은 경우 null을 반환하며, 팀 모드는 비활성화된다.
22
+ */
23
+ async function createRedisAdapter(redisUrl) {
24
+ try {
25
+ const ioredis = await import("ioredis");
26
+ // ioredis exports vary between CJS/ESM — handle both shapes
27
+ const RedisClass = (typeof ioredis.default === "function"
28
+ ? ioredis.default
29
+ : ioredis);
30
+ const pub = new RedisClass(redisUrl);
31
+ const sub = new RedisClass(redisUrl);
32
+ const subscriptions = new Map();
33
+ return {
34
+ async publish(channel, message) {
35
+ await pub.publish(channel, message);
36
+ },
37
+ subscribe(channel, listener) {
38
+ const handler = (ch, msg) => {
39
+ if (ch === channel)
40
+ listener(msg);
41
+ };
42
+ subscriptions.set(channel, handler);
43
+ // eslint-disable-next-line @typescript-eslint/no-floating-promises
44
+ sub.subscribe(channel);
45
+ sub.on("message", handler);
46
+ return () => {
47
+ sub.off("message", handler);
48
+ subscriptions.delete(channel);
49
+ // eslint-disable-next-line @typescript-eslint/no-floating-promises
50
+ sub.unsubscribe(channel);
51
+ };
52
+ },
53
+ async disconnect() {
54
+ subscriptions.clear();
55
+ await Promise.all([pub.quit(), sub.quit()]);
56
+ },
57
+ };
58
+ }
59
+ catch {
60
+ // ioredis가 설치되지 않은 환경 — 팀 모드 비활성화
61
+ return null;
62
+ }
63
+ }
64
+ // ─── 팀 이벤트 필터 ───
65
+ /** Redis로 전파할 이벤트 종류 prefix 목록 */
66
+ const TEAM_EVENT_PREFIXES = [
67
+ "progress:",
68
+ "team:",
69
+ "agent:completed",
70
+ "agent:error",
71
+ ];
72
+ /** 해당 이벤트가 팀 채널로 전파되어야 하는지 판별 */
73
+ function isTeamEvent(kind) {
74
+ return TEAM_EVENT_PREFIXES.some((prefix) => kind.startsWith(prefix));
75
+ }
76
+ // ─── HybridEventBus ───
77
+ /**
78
+ * 3계층 하이브리드 이벤트 버스.
79
+ *
80
+ * - 로컬 EventEmitter로 Layer 1+2 처리 (0ms)
81
+ * - 선택적 Redis Pub/Sub로 Layer 3 팀 브로드캐스트 (~5ms)
82
+ * - 세션별 이벤트 버퍼로 SSE 재연결 시 replay 지원
83
+ *
84
+ * @example
85
+ * ```typescript
86
+ * const bus = new HybridEventBus({ maxBuffer: 500 });
87
+ * await bus.init();
88
+ *
89
+ * const unsub = bus.subscribe("session-1", (event) => {
90
+ * console.log(event.kind, event.seq);
91
+ * });
92
+ *
93
+ * bus.emit("session-1", { kind: "agent:start", goal: "Fix bug" });
94
+ *
95
+ * // SSE 재연결 시 replay
96
+ * const missed = bus.replay("session-1", 5); // seq > 5인 이벤트
97
+ *
98
+ * unsub(); // 구독 해제
99
+ * await bus.destroy();
100
+ * ```
101
+ */
102
+ export class HybridEventBus {
103
+ local = new EventEmitter();
104
+ redis = null;
105
+ redisUrl;
106
+ maxBuffer;
107
+ /** 세션별 이벤트 버퍼 (replay용) */
108
+ buffers = new Map();
109
+ /** 세션별 시퀀스 카운터 */
110
+ seqCounters = new Map();
111
+ initialized = false;
112
+ constructor(config = {}) {
113
+ this.maxBuffer = config.maxBuffer ?? 500;
114
+ this.redisUrl = config.redisUrl;
115
+ // EventEmitter 리스너 한도를 넉넉하게 설정 (세션별 구독자)
116
+ this.local.setMaxListeners(100);
117
+ }
118
+ // ─── Lifecycle ───
119
+ /**
120
+ * 이벤트 버스 초기화.
121
+ * Redis URL이 설정된 경우 연결을 시도한다.
122
+ * Redis 연결 실패 시에도 로컬 모드로 동작한다.
123
+ */
124
+ async init() {
125
+ if (this.initialized)
126
+ return;
127
+ if (this.redisUrl) {
128
+ this.redis = await createRedisAdapter(this.redisUrl);
129
+ }
130
+ this.initialized = true;
131
+ }
132
+ /**
133
+ * 이벤트 버스 종료.
134
+ * Redis 연결 해제 + 버퍼 클리어.
135
+ */
136
+ async destroy() {
137
+ if (this.redis) {
138
+ await this.redis.disconnect();
139
+ this.redis = null;
140
+ }
141
+ this.buffers.clear();
142
+ this.seqCounters.clear();
143
+ this.local.removeAllListeners();
144
+ this.initialized = false;
145
+ }
146
+ /** 팀 모드(Redis) 활성화 여부 */
147
+ get isTeamMode() {
148
+ return this.redis !== null;
149
+ }
150
+ // ─── Emit ───
151
+ /**
152
+ * 세션에 이벤트를 발행한다.
153
+ *
154
+ * 1. 시퀀스 번호 + 타임스탬프 부여
155
+ * 2. 로컬 EventEmitter로 즉시 전파 (Layer 1+2)
156
+ * 3. 버퍼에 저장 (SSE replay용)
157
+ * 4. 팀 이벤트 필터 통과 시 Redis 발행 (Layer 3)
158
+ *
159
+ * @param sessionId 세션 ID
160
+ * @param event 발행할 BusEvent
161
+ * @returns 부여된 시퀀스 번호
162
+ */
163
+ emit(sessionId, event) {
164
+ const seq = this.nextSeq(sessionId);
165
+ const stamped = {
166
+ ...event,
167
+ seq,
168
+ ts: Date.now(),
169
+ };
170
+ // Layer 1+2: 로컬 즉시 전파
171
+ this.local.emit(`session:${sessionId}`, stamped);
172
+ // 버퍼에 저장
173
+ this.bufferEvent(sessionId, stamped);
174
+ // Layer 3: 팀 모드 — 필터된 이벤트만 Redis로
175
+ if (this.redis && isTeamEvent(event.kind)) {
176
+ // fire-and-forget (Redis 발행 실패가 로컬 전파를 막지 않음)
177
+ this.redis.publish(`team:${sessionId}`, JSON.stringify(stamped)).catch(() => {
178
+ // Redis 발행 실패는 무시 (로컬 전파는 이미 완료)
179
+ });
180
+ }
181
+ return seq;
182
+ }
183
+ // ─── Subscribe ───
184
+ /**
185
+ * 세션의 이벤트를 구독한다 (Layer 1+2 로컬).
186
+ *
187
+ * @param sessionId 구독할 세션 ID
188
+ * @param listener 이벤트 리스너
189
+ * @returns 구독 해제 함수
190
+ */
191
+ subscribe(sessionId, listener) {
192
+ this.local.on(`session:${sessionId}`, listener);
193
+ return () => {
194
+ this.local.off(`session:${sessionId}`, listener);
195
+ };
196
+ }
197
+ // ─── Replay ───
198
+ /**
199
+ * SSE 재연결 시 놓친 이벤트를 재전송한다.
200
+ *
201
+ * @param sessionId 세션 ID
202
+ * @param fromSeq 이 시퀀스 이후의 이벤트만 반환 (exclusive)
203
+ * @returns 놓친 이벤트 배열
204
+ */
205
+ replay(sessionId, fromSeq) {
206
+ const buffer = this.buffers.get(sessionId);
207
+ if (!buffer)
208
+ return [];
209
+ return buffer.filter((e) => e.seq > fromSeq);
210
+ }
211
+ // ─── Team Broadcast (Layer 3) ───
212
+ /**
213
+ * 워크스페이스 채널을 구독한다 (팀 모드, Redis 기반).
214
+ *
215
+ * @param workspaceId 워크스페이스 ID
216
+ * @param listener 이벤트 리스너
217
+ * @returns 구독 해제 함수
218
+ * @throws Redis가 활성화되지 않은 경우 에러
219
+ */
220
+ subscribeWorkspace(workspaceId, listener) {
221
+ if (!this.redis) {
222
+ throw new Error("Team mode requires Redis — provide redisUrl in config");
223
+ }
224
+ return this.redis.subscribe(`workspace:${workspaceId}`, (message) => {
225
+ try {
226
+ const event = JSON.parse(message);
227
+ listener(event);
228
+ }
229
+ catch {
230
+ // 파싱 실패한 메시지는 무시
231
+ }
232
+ });
233
+ }
234
+ /**
235
+ * 팀 채널로 이벤트를 브로드캐스트한다 (Redis Pub/Sub).
236
+ * 로컬 세션 이벤트와 별도로, 워크스페이스 전체에 발행.
237
+ *
238
+ * @param workspaceId 워크스페이스 ID
239
+ * @param event 브로드캐스트할 이벤트
240
+ */
241
+ async broadcast(workspaceId, event) {
242
+ if (!this.redis) {
243
+ throw new Error("Team mode requires Redis — provide redisUrl in config");
244
+ }
245
+ const stamped = {
246
+ ...event,
247
+ seq: 0, // 워크스페이스 브로드캐스트는 세션 seq 없음
248
+ ts: Date.now(),
249
+ };
250
+ await this.redis.publish(`workspace:${workspaceId}`, JSON.stringify(stamped));
251
+ }
252
+ // ─── Session Cleanup ───
253
+ /**
254
+ * 세션의 버퍼와 시퀀스 카운터를 정리한다.
255
+ * 세션 종료 시 호출하여 메모리를 해제한다.
256
+ *
257
+ * @param sessionId 정리할 세션 ID
258
+ */
259
+ clearSession(sessionId) {
260
+ this.buffers.delete(sessionId);
261
+ this.seqCounters.delete(sessionId);
262
+ this.local.removeAllListeners(`session:${sessionId}`);
263
+ }
264
+ /**
265
+ * 현재 버퍼에 저장된 세션의 이벤트 수를 반환한다.
266
+ *
267
+ * @param sessionId 세션 ID
268
+ * @returns 버퍼된 이벤트 수
269
+ */
270
+ getBufferSize(sessionId) {
271
+ return this.buffers.get(sessionId)?.length ?? 0;
272
+ }
273
+ /**
274
+ * 세션의 현재 시퀀스 번호를 반환한다.
275
+ *
276
+ * @param sessionId 세션 ID
277
+ * @returns 현재 시퀀스 번호 (이벤트가 없으면 0)
278
+ */
279
+ getCurrentSeq(sessionId) {
280
+ return this.seqCounters.get(sessionId) ?? 0;
281
+ }
282
+ // ─── Private Helpers ───
283
+ /** 세션의 다음 시퀀스 번호를 발급한다 */
284
+ nextSeq(sessionId) {
285
+ const current = this.seqCounters.get(sessionId) ?? 0;
286
+ const next = current + 1;
287
+ this.seqCounters.set(sessionId, next);
288
+ return next;
289
+ }
290
+ /** 이벤트를 세션 버퍼에 저장한다 (maxBuffer 초과 시 오래된 이벤트 삭제) */
291
+ bufferEvent(sessionId, event) {
292
+ let buffer = this.buffers.get(sessionId);
293
+ if (!buffer) {
294
+ buffer = [];
295
+ this.buffers.set(sessionId, buffer);
296
+ }
297
+ buffer.push(event);
298
+ // 버퍼 크기 제한 — 초과분 앞에서 삭제
299
+ if (buffer.length > this.maxBuffer) {
300
+ const excess = buffer.length - this.maxBuffer;
301
+ buffer.splice(0, excess);
302
+ }
303
+ }
304
+ }
305
+ //# sourceMappingURL=event-bus.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"event-bus.js","sourceRoot":"","sources":["../src/event-bus.ts"],"names":[],"mappings":"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"}