intent-hub 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 (214) hide show
  1. package/.claude/settings.local.json +7 -0
  2. package/.turbo/cache/019f5ae385027cb1-meta.json +1 -0
  3. package/.turbo/cache/019f5ae385027cb1.tar.zst +0 -0
  4. package/.turbo/cache/040af6112a552a64-meta.json +1 -0
  5. package/.turbo/cache/040af6112a552a64.tar.zst +0 -0
  6. package/.turbo/cache/11195eac3ca5c6ce-meta.json +1 -0
  7. package/.turbo/cache/11195eac3ca5c6ce.tar.zst +0 -0
  8. package/.turbo/cache/13d11166efdf11cf-meta.json +1 -0
  9. package/.turbo/cache/13d11166efdf11cf.tar.zst +0 -0
  10. package/.turbo/cache/19af1af3b136706c-meta.json +1 -0
  11. package/.turbo/cache/19af1af3b136706c.tar.zst +0 -0
  12. package/.turbo/cache/1d33efac91c05b50-meta.json +1 -0
  13. package/.turbo/cache/1d33efac91c05b50.tar.zst +0 -0
  14. package/.turbo/cache/200b85a612af2d13-meta.json +1 -0
  15. package/.turbo/cache/200b85a612af2d13.tar.zst +0 -0
  16. package/.turbo/cache/210308c9ea929858-meta.json +1 -0
  17. package/.turbo/cache/210308c9ea929858.tar.zst +0 -0
  18. package/.turbo/cache/38df8e44c617835e-meta.json +1 -0
  19. package/.turbo/cache/38df8e44c617835e.tar.zst +0 -0
  20. package/.turbo/cache/3e449de5ef60a7a0-meta.json +1 -0
  21. package/.turbo/cache/3e449de5ef60a7a0.tar.zst +0 -0
  22. package/.turbo/cache/51ff024a97c2b4f5-meta.json +1 -0
  23. package/.turbo/cache/51ff024a97c2b4f5.tar.zst +0 -0
  24. package/.turbo/cache/54bc756eeebb377a-meta.json +1 -0
  25. package/.turbo/cache/54bc756eeebb377a.tar.zst +0 -0
  26. package/.turbo/cache/5ed6a840acafc873-meta.json +1 -0
  27. package/.turbo/cache/5ed6a840acafc873.tar.zst +0 -0
  28. package/.turbo/cache/6702dc24e5ca3c2e-meta.json +1 -0
  29. package/.turbo/cache/6702dc24e5ca3c2e.tar.zst +0 -0
  30. package/.turbo/cache/725c72cf71ea854f-meta.json +1 -0
  31. package/.turbo/cache/725c72cf71ea854f.tar.zst +0 -0
  32. package/.turbo/cache/7344ca28d348037a-meta.json +1 -0
  33. package/.turbo/cache/7344ca28d348037a.tar.zst +0 -0
  34. package/.turbo/cache/748fb444cdc0b78c-meta.json +1 -0
  35. package/.turbo/cache/748fb444cdc0b78c.tar.zst +0 -0
  36. package/.turbo/cache/789677c36fe7fb98-meta.json +1 -0
  37. package/.turbo/cache/789677c36fe7fb98.tar.zst +0 -0
  38. package/.turbo/cache/89ff6c6f38dd4a18-meta.json +1 -0
  39. package/.turbo/cache/89ff6c6f38dd4a18.tar.zst +0 -0
  40. package/.turbo/cache/8dbc92d00de0c92e-meta.json +1 -0
  41. package/.turbo/cache/8dbc92d00de0c92e.tar.zst +0 -0
  42. package/.turbo/cache/8eb03f40082b9441-meta.json +1 -0
  43. package/.turbo/cache/8eb03f40082b9441.tar.zst +0 -0
  44. package/.turbo/cache/9157134d4b916017-meta.json +1 -0
  45. package/.turbo/cache/9157134d4b916017.tar.zst +0 -0
  46. package/.turbo/cache/94219ffd32b48e93-meta.json +1 -0
  47. package/.turbo/cache/94219ffd32b48e93.tar.zst +0 -0
  48. package/.turbo/cache/95c1d160b4fa84eb-meta.json +1 -0
  49. package/.turbo/cache/95c1d160b4fa84eb.tar.zst +0 -0
  50. package/.turbo/cache/998833ea02dfb225-meta.json +1 -0
  51. package/.turbo/cache/998833ea02dfb225.tar.zst +0 -0
  52. package/.turbo/cache/a5974ef6ade3eb90-meta.json +1 -0
  53. package/.turbo/cache/a5974ef6ade3eb90.tar.zst +0 -0
  54. package/.turbo/cache/aab811809257decb-meta.json +1 -0
  55. package/.turbo/cache/aab811809257decb.tar.zst +0 -0
  56. package/.turbo/cache/ab2f82a54da854fd-meta.json +1 -0
  57. package/.turbo/cache/ab2f82a54da854fd.tar.zst +0 -0
  58. package/.turbo/cache/abbf4d95d62a7303-meta.json +1 -0
  59. package/.turbo/cache/abbf4d95d62a7303.tar.zst +0 -0
  60. package/.turbo/cache/af4441f519f9ce50-meta.json +1 -0
  61. package/.turbo/cache/af4441f519f9ce50.tar.zst +0 -0
  62. package/.turbo/cache/b9b85aaaf03d00a6-meta.json +1 -0
  63. package/.turbo/cache/b9b85aaaf03d00a6.tar.zst +0 -0
  64. package/.turbo/cache/cd58ee8721bbfed7-meta.json +1 -0
  65. package/.turbo/cache/cd58ee8721bbfed7.tar.zst +0 -0
  66. package/.turbo/cache/d285e48b8afa30b5-meta.json +1 -0
  67. package/.turbo/cache/d285e48b8afa30b5.tar.zst +0 -0
  68. package/.turbo/cache/d33e90229142acce-meta.json +1 -0
  69. package/.turbo/cache/d33e90229142acce.tar.zst +0 -0
  70. package/.turbo/cache/d57839a0d3b04540-meta.json +1 -0
  71. package/.turbo/cache/d57839a0d3b04540.tar.zst +0 -0
  72. package/.turbo/cache/d8554ef2c8b6e5eb-meta.json +1 -0
  73. package/.turbo/cache/d8554ef2c8b6e5eb.tar.zst +0 -0
  74. package/.turbo/cache/dc7375b51290e102-meta.json +1 -0
  75. package/.turbo/cache/dc7375b51290e102.tar.zst +0 -0
  76. package/.turbo/cache/e5310fe547fdbf0a-meta.json +1 -0
  77. package/.turbo/cache/e5310fe547fdbf0a.tar.zst +0 -0
  78. package/.turbo/cache/f12bb5f2f188758d-meta.json +1 -0
  79. package/.turbo/cache/f12bb5f2f188758d.tar.zst +0 -0
  80. package/.turbo/cache/f2db5af0c0b4d23f-meta.json +1 -0
  81. package/.turbo/cache/f2db5af0c0b4d23f.tar.zst +0 -0
  82. package/.turbo/cache/f8935ade01a88cd7-meta.json +1 -0
  83. package/.turbo/cache/f8935ade01a88cd7.tar.zst +0 -0
  84. package/.turbo/cache/f982b8dd966f823a-meta.json +1 -0
  85. package/.turbo/cache/f982b8dd966f823a.tar.zst +0 -0
  86. package/.turbo/cache/f9d4036dd350ba1a-meta.json +1 -0
  87. package/.turbo/cache/f9d4036dd350ba1a.tar.zst +0 -0
  88. package/README.md +661 -0
  89. package/README_ko.md +577 -0
  90. package/bun.lock +135 -0
  91. package/package.json +26 -0
  92. package/packages/agent/.turbo/turbo-build.log +5 -0
  93. package/packages/agent/.turbo/turbo-typecheck.log +1 -0
  94. package/packages/agent/dist/connection/hub-client.d.ts +33 -0
  95. package/packages/agent/dist/connection/hub-client.d.ts.map +1 -0
  96. package/packages/agent/dist/connection/index.d.ts +2 -0
  97. package/packages/agent/dist/connection/index.d.ts.map +1 -0
  98. package/packages/agent/dist/hooks/index.d.ts +3 -0
  99. package/packages/agent/dist/hooks/index.d.ts.map +1 -0
  100. package/packages/agent/dist/hooks/intent-hub-hooks.d.ts +47 -0
  101. package/packages/agent/dist/hooks/intent-hub-hooks.d.ts.map +1 -0
  102. package/packages/agent/dist/index.d.ts +6 -0
  103. package/packages/agent/dist/index.d.ts.map +1 -0
  104. package/packages/agent/dist/index.js +3315 -0
  105. package/packages/agent/dist/plugin/index.d.ts +3 -0
  106. package/packages/agent/dist/plugin/index.d.ts.map +1 -0
  107. package/packages/agent/dist/plugin/intent-hub-plugin.d.ts +54 -0
  108. package/packages/agent/dist/plugin/intent-hub-plugin.d.ts.map +1 -0
  109. package/packages/agent/package.json +32 -0
  110. package/packages/agent/src/connection/hub-client.ts +152 -0
  111. package/packages/agent/src/connection/index.ts +1 -0
  112. package/packages/agent/src/hooks/index.ts +2 -0
  113. package/packages/agent/src/hooks/intent-hub-hooks.ts +245 -0
  114. package/packages/agent/src/index.ts +5 -0
  115. package/packages/agent/src/plugin/index.ts +2 -0
  116. package/packages/agent/src/plugin/intent-hub-plugin.ts +153 -0
  117. package/packages/agent/tsconfig.json +9 -0
  118. package/packages/hub/.turbo/turbo-build.log +6 -0
  119. package/packages/hub/.turbo/turbo-typecheck.log +1 -0
  120. package/packages/hub/dist/api/dashboard.d.ts +17 -0
  121. package/packages/hub/dist/api/dashboard.d.ts.map +1 -0
  122. package/packages/hub/dist/cli.d.ts +3 -0
  123. package/packages/hub/dist/cli.d.ts.map +1 -0
  124. package/packages/hub/dist/cli.js +7719 -0
  125. package/packages/hub/dist/core/conflict-detector.d.ts +36 -0
  126. package/packages/hub/dist/core/conflict-detector.d.ts.map +1 -0
  127. package/packages/hub/dist/core/index.d.ts +7 -0
  128. package/packages/hub/dist/core/index.d.ts.map +1 -0
  129. package/packages/hub/dist/core/intent-analyzer.d.ts +8 -0
  130. package/packages/hub/dist/core/intent-analyzer.d.ts.map +1 -0
  131. package/packages/hub/dist/core/lock-manager.d.ts +13 -0
  132. package/packages/hub/dist/core/lock-manager.d.ts.map +1 -0
  133. package/packages/hub/dist/core/orchestrator.d.ts +46 -0
  134. package/packages/hub/dist/core/orchestrator.d.ts.map +1 -0
  135. package/packages/hub/dist/index.d.ts +9 -0
  136. package/packages/hub/dist/index.d.ts.map +1 -0
  137. package/packages/hub/dist/index.js +4686 -0
  138. package/packages/hub/dist/llm/index.d.ts +7 -0
  139. package/packages/hub/dist/llm/index.d.ts.map +1 -0
  140. package/packages/hub/dist/llm/negotiation-engine.d.ts +40 -0
  141. package/packages/hub/dist/llm/negotiation-engine.d.ts.map +1 -0
  142. package/packages/hub/dist/llm/provider.d.ts +46 -0
  143. package/packages/hub/dist/llm/provider.d.ts.map +1 -0
  144. package/packages/hub/dist/llm/smart-analyzer.d.ts +20 -0
  145. package/packages/hub/dist/llm/smart-analyzer.d.ts.map +1 -0
  146. package/packages/hub/dist/server/hub-server.d.ts +35 -0
  147. package/packages/hub/dist/server/hub-server.d.ts.map +1 -0
  148. package/packages/hub/dist/server/index.d.ts +5 -0
  149. package/packages/hub/dist/server/index.d.ts.map +1 -0
  150. package/packages/hub/dist/server/message-handler.d.ts +18 -0
  151. package/packages/hub/dist/server/message-handler.d.ts.map +1 -0
  152. package/packages/hub/dist/server/smart-hub-server.d.ts +43 -0
  153. package/packages/hub/dist/server/smart-hub-server.d.ts.map +1 -0
  154. package/packages/hub/dist/state/index.d.ts +2 -0
  155. package/packages/hub/dist/state/index.d.ts.map +1 -0
  156. package/packages/hub/dist/state/session-manager.d.ts +19 -0
  157. package/packages/hub/dist/state/session-manager.d.ts.map +1 -0
  158. package/packages/hub/dist/tunnel/index.d.ts +14 -0
  159. package/packages/hub/dist/tunnel/index.d.ts.map +1 -0
  160. package/packages/hub/package.json +54 -0
  161. package/packages/hub/src/api/dashboard.ts +261 -0
  162. package/packages/hub/src/cli.ts +193 -0
  163. package/packages/hub/src/core/conflict-detector.ts +138 -0
  164. package/packages/hub/src/core/index.ts +6 -0
  165. package/packages/hub/src/core/intent-analyzer.ts +112 -0
  166. package/packages/hub/src/core/lock-manager.ts +95 -0
  167. package/packages/hub/src/core/orchestrator.ts +255 -0
  168. package/packages/hub/src/index.ts +8 -0
  169. package/packages/hub/src/llm/index.ts +17 -0
  170. package/packages/hub/src/llm/negotiation-engine.ts +297 -0
  171. package/packages/hub/src/llm/provider.ts +175 -0
  172. package/packages/hub/src/llm/smart-analyzer.ts +169 -0
  173. package/packages/hub/src/server/hub-server.ts +219 -0
  174. package/packages/hub/src/server/index.ts +4 -0
  175. package/packages/hub/src/server/message-handler.ts +111 -0
  176. package/packages/hub/src/server/smart-hub-server.ts +374 -0
  177. package/packages/hub/src/state/index.ts +1 -0
  178. package/packages/hub/src/state/session-manager.ts +59 -0
  179. package/packages/hub/src/tunnel/index.ts +153 -0
  180. package/packages/hub/tsconfig.json +9 -0
  181. package/packages/shared/.turbo/turbo-build.log +5 -0
  182. package/packages/shared/.turbo/turbo-typecheck.log +1 -0
  183. package/packages/shared/dist/index.d.ts +3 -0
  184. package/packages/shared/dist/index.d.ts.map +1 -0
  185. package/packages/shared/dist/index.js +50 -0
  186. package/packages/shared/dist/types/domain.d.ts +50 -0
  187. package/packages/shared/dist/types/domain.d.ts.map +1 -0
  188. package/packages/shared/dist/types/index.d.ts +4 -0
  189. package/packages/shared/dist/types/index.d.ts.map +1 -0
  190. package/packages/shared/dist/types/intent.d.ts +24 -0
  191. package/packages/shared/dist/types/intent.d.ts.map +1 -0
  192. package/packages/shared/dist/types/message.d.ts +151 -0
  193. package/packages/shared/dist/types/message.d.ts.map +1 -0
  194. package/packages/shared/dist/utils/id.d.ts +6 -0
  195. package/packages/shared/dist/utils/id.d.ts.map +1 -0
  196. package/packages/shared/dist/utils/index.d.ts +3 -0
  197. package/packages/shared/dist/utils/index.d.ts.map +1 -0
  198. package/packages/shared/dist/utils/message.d.ts +5 -0
  199. package/packages/shared/dist/utils/message.d.ts.map +1 -0
  200. package/packages/shared/package.json +33 -0
  201. package/packages/shared/src/index.ts +2 -0
  202. package/packages/shared/src/types/domain.ts +57 -0
  203. package/packages/shared/src/types/index.ts +3 -0
  204. package/packages/shared/src/types/intent.ts +34 -0
  205. package/packages/shared/src/types/message.ts +188 -0
  206. package/packages/shared/src/utils/id.ts +21 -0
  207. package/packages/shared/src/utils/index.ts +2 -0
  208. package/packages/shared/src/utils/message.ts +30 -0
  209. package/packages/shared/tsconfig.json +9 -0
  210. package/scripts/test-e2e.ts +194 -0
  211. package/scripts/test-mvp2.ts +167 -0
  212. package/scripts/test-mvp3.ts +405 -0
  213. package/tsconfig.json +19 -0
  214. package/turbo.json +22 -0
@@ -0,0 +1,255 @@
1
+ import type { LLMProvider } from '../llm/provider.js';
2
+ import { SmartIntentAnalyzer, type SmartIntentAnalysis } from '../llm/smart-analyzer.js';
3
+ import { NegotiationEngine, type NegotiationResult, type NegotiationParticipant } from '../llm/negotiation-engine.js';
4
+ import { ConflictDetector, type ActiveIntent, type ConflictInfo } from './conflict-detector.js';
5
+ import { LockManager } from './lock-manager.js';
6
+ import { generateIntentId } from '@anthropic-for-korea/intent-hub-shared';
7
+
8
+ export interface OrchestratorConfig {
9
+ useLLMAnalysis: boolean;
10
+ autoNegotiate: boolean;
11
+ conflictThreshold: 'low' | 'medium' | 'high';
12
+ }
13
+
14
+ export interface IntentRequest {
15
+ sessionId: string;
16
+ userId: string;
17
+ username: string;
18
+ prompt: string;
19
+ }
20
+
21
+ export interface IntentResponse {
22
+ intentId: string;
23
+ status: 'approved' | 'blocked' | 'negotiating' | 'pending_decision';
24
+ analysis: SmartIntentAnalysis;
25
+ domains: string[];
26
+ blockedBy?: {
27
+ username: string;
28
+ domain: string;
29
+ reason: string;
30
+ }[];
31
+ negotiation?: NegotiationResult;
32
+ }
33
+
34
+ type NotifyFn = (sessionId: string, event: string, data: any) => void;
35
+
36
+ export class Orchestrator {
37
+ private analyzer: SmartIntentAnalyzer;
38
+ private negotiationEngine: NegotiationEngine;
39
+ private conflictDetector: ConflictDetector;
40
+ private lockManager: LockManager;
41
+ private pendingIntents: Map<string, ActiveIntent> = new Map();
42
+ private notify: NotifyFn;
43
+
44
+ constructor(
45
+ provider: LLMProvider,
46
+ private config: OrchestratorConfig,
47
+ notify: NotifyFn
48
+ ) {
49
+ this.analyzer = new SmartIntentAnalyzer(provider);
50
+ this.negotiationEngine = new NegotiationEngine(provider);
51
+ this.conflictDetector = new ConflictDetector(this.analyzer);
52
+ this.lockManager = new LockManager();
53
+ this.notify = notify;
54
+ }
55
+
56
+ async processIntent(request: IntentRequest): Promise<IntentResponse> {
57
+ const intentId = generateIntentId();
58
+
59
+ let analysis: SmartIntentAnalysis;
60
+ if (this.config.useLLMAnalysis) {
61
+ analysis = await this.analyzer.analyze(request.prompt);
62
+ } else {
63
+ analysis = this.analyzer['fallbackAnalysis'](request.prompt);
64
+ }
65
+
66
+ const domainId = `${analysis.domain}${analysis.subdomain ? ':' + analysis.subdomain : ''}`;
67
+
68
+ const activeIntent: ActiveIntent = {
69
+ intentId,
70
+ sessionId: request.sessionId,
71
+ userId: request.userId,
72
+ username: request.username,
73
+ prompt: request.prompt,
74
+ analysis,
75
+ status: 'active',
76
+ startedAt: Date.now(),
77
+ };
78
+
79
+ const conflicts = this.conflictDetector.quickConflictCheck(activeIntent);
80
+ const significantConflicts = conflicts.filter(c =>
81
+ this.config.conflictThreshold === 'low' ? true :
82
+ this.config.conflictThreshold === 'medium' ? c.severity !== 'low' :
83
+ c.severity === 'high'
84
+ );
85
+
86
+ if (significantConflicts.length === 0) {
87
+ this.lockManager.acquireLock(domainId, intentId, request.userId, request.username);
88
+ this.conflictDetector.addIntent(activeIntent);
89
+
90
+ return {
91
+ intentId,
92
+ status: 'approved',
93
+ analysis,
94
+ domains: [domainId],
95
+ };
96
+ }
97
+
98
+ const conflict = significantConflicts[0]!;
99
+ const existingIntent = this.conflictDetector.getIntentByUser(conflict.userId1);
100
+
101
+ if (!existingIntent) {
102
+ this.lockManager.acquireLock(domainId, intentId, request.userId, request.username);
103
+ this.conflictDetector.addIntent(activeIntent);
104
+
105
+ return {
106
+ intentId,
107
+ status: 'approved',
108
+ analysis,
109
+ domains: [domainId],
110
+ };
111
+ }
112
+
113
+ if (!this.config.autoNegotiate) {
114
+ return {
115
+ intentId,
116
+ status: 'blocked',
117
+ analysis,
118
+ domains: [domainId],
119
+ blockedBy: [{
120
+ username: existingIntent.username,
121
+ domain: conflict.overlapDomains[0] || domainId,
122
+ reason: conflict.reason,
123
+ }],
124
+ };
125
+ }
126
+
127
+ this.pendingIntents.set(intentId, activeIntent);
128
+
129
+ const participant1: NegotiationParticipant = {
130
+ sessionId: existingIntent.sessionId,
131
+ userId: existingIntent.userId,
132
+ username: existingIntent.username,
133
+ intent: existingIntent.analysis,
134
+ prompt: existingIntent.prompt,
135
+ };
136
+
137
+ const participant2: NegotiationParticipant = {
138
+ sessionId: request.sessionId,
139
+ userId: request.userId,
140
+ username: request.username,
141
+ intent: analysis,
142
+ prompt: request.prompt,
143
+ };
144
+
145
+ this.notify(existingIntent.sessionId, 'negotiation:start', {
146
+ conflictId: intentId,
147
+ withUser: request.username,
148
+ reason: conflict.reason,
149
+ });
150
+
151
+ const negotiationResult = await this.negotiationEngine.startNegotiation(
152
+ participant1,
153
+ participant2
154
+ );
155
+
156
+ this.pendingIntents.delete(intentId);
157
+
158
+ if (negotiationResult.status === 'agreed' && negotiationResult.plan) {
159
+ const firstPhase = negotiationResult.plan.phases[0]!;
160
+ const secondPhase = negotiationResult.plan.phases[1]!;
161
+
162
+ if (firstPhase.userId === request.userId) {
163
+ this.lockManager.acquireLock(domainId, intentId, request.userId, request.username);
164
+ this.conflictDetector.addIntent(activeIntent);
165
+
166
+ this.notify(existingIntent.sessionId, 'negotiation:resolved', {
167
+ conflictId: negotiationResult.conflictId,
168
+ result: 'wait',
169
+ message: `${request.username} will go first. You'll be notified when it's your turn.`,
170
+ });
171
+
172
+ return {
173
+ intentId,
174
+ status: 'approved',
175
+ analysis,
176
+ domains: [domainId],
177
+ negotiation: negotiationResult,
178
+ };
179
+ } else {
180
+ this.notify(existingIntent.sessionId, 'negotiation:resolved', {
181
+ conflictId: negotiationResult.conflictId,
182
+ result: 'proceed',
183
+ message: `You can proceed first. ${request.username} will wait.`,
184
+ });
185
+
186
+ return {
187
+ intentId,
188
+ status: 'negotiating',
189
+ analysis,
190
+ domains: [domainId],
191
+ negotiation: negotiationResult,
192
+ blockedBy: [{
193
+ username: existingIntent.username,
194
+ domain: conflict.overlapDomains[0] || domainId,
195
+ reason: `Negotiation resolved: ${existingIntent.username} goes first`,
196
+ }],
197
+ };
198
+ }
199
+ }
200
+
201
+ if (negotiationResult.requiresUserDecision) {
202
+ this.notify(existingIntent.sessionId, 'decision:required', {
203
+ conflictId: negotiationResult.conflictId,
204
+ options: negotiationResult.options,
205
+ reason: negotiationResult.disagreementReason,
206
+ });
207
+
208
+ return {
209
+ intentId,
210
+ status: 'pending_decision',
211
+ analysis,
212
+ domains: [domainId],
213
+ negotiation: negotiationResult,
214
+ };
215
+ }
216
+
217
+ return {
218
+ intentId,
219
+ status: 'blocked',
220
+ analysis,
221
+ domains: [domainId],
222
+ blockedBy: [{
223
+ username: existingIntent.username,
224
+ domain: conflict.overlapDomains[0] || domainId,
225
+ reason: negotiationResult.disagreementReason || 'Negotiation failed',
226
+ }],
227
+ negotiation: negotiationResult,
228
+ };
229
+ }
230
+
231
+ completeIntent(intentId: string, userId: string): void {
232
+ const intent = Array.from(this.conflictDetector['activeIntents'].values())
233
+ .find(i => i.intentId === intentId && i.userId === userId);
234
+
235
+ if (intent) {
236
+ intent.status = 'completed';
237
+ this.conflictDetector.removeIntent(intentId);
238
+
239
+ const domainId = `${intent.analysis.domain}${intent.analysis.subdomain ? ':' + intent.analysis.subdomain : ''}`;
240
+ this.lockManager.releaseLock(domainId, userId);
241
+ }
242
+ }
243
+
244
+ getStatus(): {
245
+ activeIntents: number;
246
+ pendingNegotiations: number;
247
+ activeLocks: number;
248
+ } {
249
+ return {
250
+ activeIntents: this.conflictDetector.getActiveIntents().length,
251
+ pendingNegotiations: this.pendingIntents.size,
252
+ activeLocks: this.lockManager.getActiveLocks().length,
253
+ };
254
+ }
255
+ }
@@ -0,0 +1,8 @@
1
+ export { HubServer, SmartHubServer } from './server/index.js';
2
+ export type { SmartHubServerOptions } from './server/index.js';
3
+ export { IntentAnalyzer, LockManager, ConflictDetector, Orchestrator } from './core/index.js';
4
+ export type { ActiveIntent, ConflictInfo, OrchestratorConfig, IntentRequest, IntentResponse } from './core/index.js';
5
+ export { SessionManager } from './state/index.js';
6
+ export * from './llm/index.js';
7
+ export { DashboardApi } from './api/dashboard.js';
8
+ export type { DashboardApiOptions } from './api/dashboard.js';
@@ -0,0 +1,17 @@
1
+ export {
2
+ OpenAIProvider,
3
+ AnthropicProvider,
4
+ MockProvider,
5
+ createProvider
6
+ } from './provider.js';
7
+ export type { LLMProvider, LLMMessage, LLMCompletionOptions, LLMCompletionResult } from './provider.js';
8
+
9
+ export { SmartIntentAnalyzer } from './smart-analyzer.js';
10
+ export type { SmartIntentAnalysis } from './smart-analyzer.js';
11
+
12
+ export { NegotiationEngine } from './negotiation-engine.js';
13
+ export type {
14
+ NegotiationParticipant,
15
+ NegotiationProposal,
16
+ NegotiationResult
17
+ } from './negotiation-engine.js';
@@ -0,0 +1,297 @@
1
+ import type { LLMProvider, LLMMessage } from './provider.js';
2
+ import type { SmartIntentAnalysis } from './smart-analyzer.js';
3
+ import type { ExecutionPlan, ExecutionPhase } from '@anthropic-for-korea/intent-hub-shared';
4
+ import { generateConflictId } from '@anthropic-for-korea/intent-hub-shared';
5
+
6
+ export interface NegotiationParticipant {
7
+ sessionId: string;
8
+ userId: string;
9
+ username: string;
10
+ intent: SmartIntentAnalysis;
11
+ prompt: string;
12
+ }
13
+
14
+ export interface NegotiationProposal {
15
+ fromUserId: string;
16
+ fromUsername: string;
17
+ proposal: string;
18
+ suggestedOrder: string[];
19
+ reasoning: string;
20
+ }
21
+
22
+ export interface NegotiationResult {
23
+ conflictId: string;
24
+ status: 'agreed' | 'disagreed' | 'timeout';
25
+ plan?: ExecutionPlan;
26
+ proposals: NegotiationProposal[];
27
+ disagreementReason?: string;
28
+ requiresUserDecision: boolean;
29
+ options?: Array<{
30
+ id: string;
31
+ description: string;
32
+ proposedBy: string;
33
+ }>;
34
+ }
35
+
36
+ const NEGOTIATION_SYSTEM_PROMPT = `You are an AI agent helping coordinate work between team members.
37
+ Two developers want to work on potentially overlapping parts of the codebase.
38
+ Your job is to propose a plan that minimizes conflicts and maximizes parallel work.
39
+
40
+ Consider:
41
+ 1. Which work can be done in parallel without conflicts?
42
+ 2. Which work has dependencies and should be sequenced?
43
+ 3. Can one person define interfaces/contracts first that the other can use?
44
+ 4. Is there a natural order (e.g., backend before frontend)?
45
+
46
+ Respond in JSON:
47
+ {
48
+ "proposal": "brief description of your proposed approach",
49
+ "suggestedOrder": ["userId who should go first", "userId who should go second"],
50
+ "reasoning": "why this order makes sense",
51
+ "canWorkInParallel": boolean,
52
+ "parallelStrategy": "if parallel is possible, how to divide the work",
53
+ "sharedInterface": "any interface/contract that should be agreed upon first"
54
+ }`;
55
+
56
+ export class NegotiationEngine {
57
+ private activeNegotiations: Map<string, {
58
+ participants: NegotiationParticipant[];
59
+ proposals: NegotiationProposal[];
60
+ startedAt: number;
61
+ }> = new Map();
62
+
63
+ constructor(private provider: LLMProvider) {}
64
+
65
+ async startNegotiation(
66
+ participant1: NegotiationParticipant,
67
+ participant2: NegotiationParticipant
68
+ ): Promise<NegotiationResult> {
69
+ const conflictId = generateConflictId();
70
+
71
+ this.activeNegotiations.set(conflictId, {
72
+ participants: [participant1, participant2],
73
+ proposals: [],
74
+ startedAt: Date.now(),
75
+ });
76
+
77
+ const proposal1 = await this.getProposal(participant1, participant2);
78
+ const proposal2 = await this.getProposal(participant2, participant1);
79
+
80
+ const negotiation = this.activeNegotiations.get(conflictId)!;
81
+ negotiation.proposals.push(proposal1, proposal2);
82
+
83
+ const consensus = await this.findConsensus(proposal1, proposal2, participant1, participant2);
84
+
85
+ this.activeNegotiations.delete(conflictId);
86
+
87
+ return {
88
+ conflictId,
89
+ ...consensus,
90
+ proposals: [proposal1, proposal2],
91
+ };
92
+ }
93
+
94
+ private async getProposal(
95
+ self: NegotiationParticipant,
96
+ other: NegotiationParticipant
97
+ ): Promise<NegotiationProposal> {
98
+ const messages: LLMMessage[] = [
99
+ { role: 'system', content: NEGOTIATION_SYSTEM_PROMPT },
100
+ {
101
+ role: 'user',
102
+ content: `You are representing ${self.username} (userId: ${self.userId}).
103
+
104
+ Your task:
105
+ - Domain: ${self.intent.domain}${self.intent.subdomain ? '/' + self.intent.subdomain : ''}
106
+ - Description: ${self.prompt}
107
+ - Affected files: ${self.intent.affectedFiles.join(', ') || 'to be determined'}
108
+ - Tags: ${self.intent.semanticTags.join(', ')}
109
+
110
+ Other developer's task (${other.username}, userId: ${other.userId}):
111
+ - Domain: ${other.intent.domain}${other.intent.subdomain ? '/' + other.intent.subdomain : ''}
112
+ - Description: ${other.prompt}
113
+ - Affected files: ${other.intent.affectedFiles.join(', ') || 'to be determined'}
114
+ - Tags: ${other.intent.semanticTags.join(', ')}
115
+
116
+ Propose a plan that works best for both parties.`,
117
+ },
118
+ ];
119
+
120
+ try {
121
+ const result = await this.provider.completeJSON<{
122
+ proposal: string;
123
+ suggestedOrder: string[];
124
+ reasoning: string;
125
+ }>(messages, { temperature: 0.4 });
126
+
127
+ return {
128
+ fromUserId: self.userId,
129
+ fromUsername: self.username,
130
+ proposal: result.proposal,
131
+ suggestedOrder: result.suggestedOrder,
132
+ reasoning: result.reasoning,
133
+ };
134
+ } catch (error) {
135
+ return {
136
+ fromUserId: self.userId,
137
+ fromUsername: self.username,
138
+ proposal: `${self.username} should complete their task first`,
139
+ suggestedOrder: [self.userId, other.userId],
140
+ reasoning: 'Default proposal due to analysis failure',
141
+ };
142
+ }
143
+ }
144
+
145
+ private async findConsensus(
146
+ proposal1: NegotiationProposal,
147
+ proposal2: NegotiationProposal,
148
+ participant1: NegotiationParticipant,
149
+ participant2: NegotiationParticipant
150
+ ): Promise<Omit<NegotiationResult, 'conflictId' | 'proposals'>> {
151
+ const sameOrder =
152
+ proposal1.suggestedOrder[0] === proposal2.suggestedOrder[0] &&
153
+ proposal1.suggestedOrder[1] === proposal2.suggestedOrder[1];
154
+
155
+ if (sameOrder) {
156
+ const firstUserId = proposal1.suggestedOrder[0];
157
+ const secondUserId = proposal1.suggestedOrder[1];
158
+
159
+ const first = firstUserId === participant1.userId ? participant1 : participant2;
160
+ const second = secondUserId === participant1.userId ? participant1 : participant2;
161
+
162
+ const plan: ExecutionPlan = {
163
+ phases: [
164
+ {
165
+ order: 1,
166
+ intentId: `int_${first.userId}`,
167
+ userId: first.userId,
168
+ domains: [first.intent.domain],
169
+ },
170
+ {
171
+ order: 2,
172
+ intentId: `int_${second.userId}`,
173
+ userId: second.userId,
174
+ domains: [second.intent.domain],
175
+ dependsOn: [`int_${first.userId}`],
176
+ },
177
+ ],
178
+ };
179
+
180
+ return {
181
+ status: 'agreed',
182
+ plan,
183
+ requiresUserDecision: false,
184
+ };
185
+ }
186
+
187
+ const messages: LLMMessage[] = [
188
+ {
189
+ role: 'system',
190
+ content: `Two AI agents proposed different plans for coordinating work.
191
+ Determine if a compromise is possible or if human decision is needed.
192
+
193
+ Respond in JSON:
194
+ {
195
+ "canResolve": boolean,
196
+ "resolution": "if can resolve, the agreed plan",
197
+ "reasoning": "explanation",
198
+ "firstUserId": "who should go first if resolved",
199
+ "secondUserId": "who should go second if resolved"
200
+ }`,
201
+ },
202
+ {
203
+ role: 'user',
204
+ content: `Proposal 1 from ${proposal1.fromUsername}:
205
+ ${proposal1.proposal}
206
+ Order: ${proposal1.suggestedOrder.join(' → ')}
207
+ Reasoning: ${proposal1.reasoning}
208
+
209
+ Proposal 2 from ${proposal2.fromUsername}:
210
+ ${proposal2.proposal}
211
+ Order: ${proposal2.suggestedOrder.join(' → ')}
212
+ Reasoning: ${proposal2.reasoning}
213
+
214
+ Can these be reconciled?`,
215
+ },
216
+ ];
217
+
218
+ try {
219
+ const result = await this.provider.completeJSON<{
220
+ canResolve: boolean;
221
+ resolution?: string;
222
+ reasoning: string;
223
+ firstUserId?: string;
224
+ secondUserId?: string;
225
+ }>(messages, { temperature: 0.3 });
226
+
227
+ if (result.canResolve && result.firstUserId && result.secondUserId) {
228
+ const first = result.firstUserId === participant1.userId ? participant1 : participant2;
229
+ const second = result.secondUserId === participant1.userId ? participant1 : participant2;
230
+
231
+ const plan: ExecutionPlan = {
232
+ phases: [
233
+ {
234
+ order: 1,
235
+ intentId: `int_${first.userId}`,
236
+ userId: first.userId,
237
+ domains: [first.intent.domain],
238
+ },
239
+ {
240
+ order: 2,
241
+ intentId: `int_${second.userId}`,
242
+ userId: second.userId,
243
+ domains: [second.intent.domain],
244
+ dependsOn: [`int_${first.userId}`],
245
+ },
246
+ ],
247
+ };
248
+
249
+ return {
250
+ status: 'agreed',
251
+ plan,
252
+ requiresUserDecision: false,
253
+ };
254
+ }
255
+
256
+ return {
257
+ status: 'disagreed',
258
+ requiresUserDecision: true,
259
+ disagreementReason: result.reasoning,
260
+ options: [
261
+ {
262
+ id: 'option_1',
263
+ description: `${proposal1.fromUsername}'s plan: ${proposal1.proposal}`,
264
+ proposedBy: proposal1.fromUsername,
265
+ },
266
+ {
267
+ id: 'option_2',
268
+ description: `${proposal2.fromUsername}'s plan: ${proposal2.proposal}`,
269
+ proposedBy: proposal2.fromUsername,
270
+ },
271
+ ],
272
+ };
273
+ } catch {
274
+ return {
275
+ status: 'disagreed',
276
+ requiresUserDecision: true,
277
+ disagreementReason: 'Failed to reach automated consensus',
278
+ options: [
279
+ {
280
+ id: 'option_1',
281
+ description: `${proposal1.fromUsername} goes first`,
282
+ proposedBy: proposal1.fromUsername,
283
+ },
284
+ {
285
+ id: 'option_2',
286
+ description: `${proposal2.fromUsername} goes first`,
287
+ proposedBy: proposal2.fromUsername,
288
+ },
289
+ ],
290
+ };
291
+ }
292
+ }
293
+
294
+ getActiveNegotiations(): string[] {
295
+ return Array.from(this.activeNegotiations.keys());
296
+ }
297
+ }