@dreamingai/sdk 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.
package/src/index.ts ADDED
@@ -0,0 +1,2450 @@
1
+ // ============================================================
2
+ // Dreaming AI — TypeScript SDK
3
+ //
4
+ // The primary client SDK for Dreaming AI Platform.
5
+ // Used by: TekkoStudio Web, Tekko AI Mobile (Expo), Node.js apps
6
+ //
7
+ // Zero dependencies — uses native fetch API.
8
+ // ============================================================
9
+
10
+ // ─── Types (mirror @dreaming/core types for SDK consumers) ───
11
+
12
+ export interface Memory {
13
+ id: string;
14
+ userId: string;
15
+ appId: string;
16
+ content: string;
17
+ memoryType: string;
18
+ memoryTier: number;
19
+ importance: number;
20
+ confidence: number;
21
+ accessCount: number;
22
+ metadata: Record<string, unknown>;
23
+ createdAt: string;
24
+ lastAccessed?: string;
25
+ }
26
+
27
+ export interface MemorySearchResult {
28
+ memory: Memory;
29
+ relevance: number;
30
+ decayScore?: number;
31
+ effectiveScore?: number;
32
+ }
33
+
34
+ export interface Entity {
35
+ id: string;
36
+ name: string;
37
+ entityType: string;
38
+ properties: Record<string, unknown>;
39
+ }
40
+
41
+ export interface Triple {
42
+ id: string;
43
+ subject: string;
44
+ predicate: string;
45
+ object: string;
46
+ validFrom?: string;
47
+ validTo?: string;
48
+ confidence: number;
49
+ }
50
+
51
+ export interface OceanProfile {
52
+ userId: string;
53
+ openness: number;
54
+ conscientiousness: number;
55
+ extraversion: number;
56
+ agreeableness: number;
57
+ neuroticism: number;
58
+ mbtiType?: string;
59
+ confidence: number;
60
+ sampleCount: number;
61
+ }
62
+
63
+ export interface LinguisticFingerprint {
64
+ userId: string;
65
+ formality: number;
66
+ vocabRichness: number;
67
+ avgSentenceLength: number;
68
+ emojiUsage: number;
69
+ dominantTone: string;
70
+ }
71
+
72
+ export interface SleeptimeInsight {
73
+ id: number;
74
+ type: string;
75
+ content: string;
76
+ createdAt: string;
77
+ }
78
+
79
+ export interface PaginatedResponse<T> {
80
+ data: T[];
81
+ total: number;
82
+ offset: number;
83
+ limit: number;
84
+ hasMore: boolean;
85
+ }
86
+
87
+ export interface MeterStatus {
88
+ subscriptionTier: string;
89
+ modelTier: 'efficient' | 'smart' | 'genius';
90
+ modelTierName: string;
91
+ modelTierIcon: string;
92
+ modelTierDescription: string;
93
+ primaryModel: string;
94
+ fallbackModel: string;
95
+ maxTokens: number;
96
+ availableTiers: Array<{
97
+ key: string;
98
+ name: string;
99
+ icon: string;
100
+ description: string;
101
+ primaryModel: string;
102
+ }>;
103
+ tierMapping: Record<string, string>;
104
+ usage: {
105
+ callsThisMonth: number;
106
+ tokensThisMonth: number;
107
+ avgLatencyMs: number;
108
+ fallbackRate: string;
109
+ };
110
+ }
111
+
112
+ export interface MeterUsage {
113
+ daily: Array<{
114
+ day: string;
115
+ model_tier: string;
116
+ model_used: string;
117
+ calls: number;
118
+ total_tokens_in: number;
119
+ total_tokens_out: number;
120
+ avg_latency: number;
121
+ fallback_count: number;
122
+ }>;
123
+ byFeature: Array<{
124
+ feature: string;
125
+ model_tier: string;
126
+ calls: number;
127
+ total_tokens: number;
128
+ }>;
129
+ }
130
+
131
+ // ─── SDK Configuration ───────────────────────────────────────
132
+
133
+ export interface DreamingClientConfig {
134
+ apiKey: string;
135
+ appId: string;
136
+ baseUrl?: string;
137
+ timeout?: number;
138
+ }
139
+
140
+ // ─── HTTP Client ─────────────────────────────────────────────
141
+
142
+ class HttpClient {
143
+ private baseUrl: string;
144
+ private headers: Record<string, string>;
145
+ private timeout: number;
146
+
147
+ constructor(config: DreamingClientConfig) {
148
+ this.baseUrl = (config.baseUrl ?? 'https://api.dreaming.ai').replace(/\/$/, '');
149
+ this.headers = {
150
+ 'Authorization': `Bearer ${config.apiKey}`,
151
+ 'X-Dreaming-App-Id': config.appId,
152
+ 'Content-Type': 'application/json',
153
+ };
154
+ this.timeout = config.timeout ?? 30_000;
155
+ }
156
+
157
+ async request<T>(method: string, path: string, body?: unknown): Promise<T> {
158
+ const controller = new AbortController();
159
+ const timer = setTimeout(() => controller.abort(), this.timeout);
160
+
161
+ try {
162
+ const res = await fetch(`${this.baseUrl}${path}`, {
163
+ method,
164
+ headers: this.headers,
165
+ body: body ? JSON.stringify(body) : undefined,
166
+ signal: controller.signal,
167
+ });
168
+
169
+ if (!res.ok) {
170
+ const error = await res.json().catch(() => ({ message: res.statusText })) as { code?: string; message?: string };
171
+ throw new DreamingError(error.code ?? 'API_ERROR', error.message ?? res.statusText, res.status);
172
+ }
173
+
174
+ return await res.json() as T;
175
+ } finally {
176
+ clearTimeout(timer);
177
+ }
178
+ }
179
+
180
+ get<T>(path: string): Promise<T> { return this.request('GET', path); }
181
+ post<T>(path: string, body?: unknown): Promise<T> { return this.request('POST', path, body); }
182
+ patch<T>(path: string, body?: unknown): Promise<T> { return this.request('PATCH', path, body); }
183
+ delete<T>(path: string): Promise<T> { return this.request('DELETE', path); }
184
+ }
185
+
186
+ // ─── Error Class ─────────────────────────────────────────────
187
+
188
+ export class DreamingError extends Error {
189
+ code: string;
190
+ status: number;
191
+
192
+ constructor(code: string, message: string, status: number) {
193
+ super(message);
194
+ this.code = code;
195
+ this.status = status;
196
+ this.name = 'DreamingError';
197
+ }
198
+ }
199
+
200
+ // ─── Resource Clients ────────────────────────────────────────
201
+
202
+ class MemoriesClient {
203
+ constructor(private http: HttpClient) {}
204
+
205
+ async create(input: {
206
+ userId: string;
207
+ content: string;
208
+ type?: string;
209
+ importance?: number;
210
+ metadata?: Record<string, unknown>;
211
+ }): Promise<Memory> {
212
+ return this.http.post('/v1/memories', input);
213
+ }
214
+
215
+ async list(userId: string, options?: {
216
+ limit?: number;
217
+ offset?: number;
218
+ types?: string[];
219
+ }): Promise<PaginatedResponse<Memory>> {
220
+ const params = new URLSearchParams({ userId });
221
+ if (options?.limit) params.set('limit', String(options.limit));
222
+ if (options?.offset) params.set('offset', String(options.offset));
223
+ if (options?.types) params.set('types', options.types.join(','));
224
+ return this.http.get(`/v1/memories?${params}`);
225
+ }
226
+
227
+ async get(id: string): Promise<Memory> {
228
+ return this.http.get(`/v1/memories/${id}`);
229
+ }
230
+
231
+ async delete(id: string): Promise<{ success: boolean }> {
232
+ return this.http.delete(`/v1/memories/${id}`);
233
+ }
234
+
235
+ async search(input: {
236
+ userId: string;
237
+ query: string;
238
+ limit?: number;
239
+ types?: string[];
240
+ crossApp?: boolean;
241
+ includeDecayScore?: boolean;
242
+ }): Promise<MemorySearchResult[]> {
243
+ return this.http.post('/v1/memories/search', input);
244
+ }
245
+
246
+ async recall(input: {
247
+ userId: string;
248
+ query: string;
249
+ limit?: number;
250
+ includeDecayScore?: boolean;
251
+ }): Promise<MemorySearchResult[]> {
252
+ return this.http.post('/v1/memories/recall', input);
253
+ }
254
+ }
255
+
256
+ class DreamingApiClient {
257
+ constructor(private http: HttpClient) {}
258
+
259
+ async process(input: {
260
+ userId: string;
261
+ conversation: Array<{ role: string; content: string }>;
262
+ }): Promise<{ queued: number }> {
263
+ return this.http.post('/v1/dreaming/process', input);
264
+ }
265
+
266
+ async consolidate(userId: string): Promise<{
267
+ promoted: number;
268
+ merged: number;
269
+ evicted: number;
270
+ }> {
271
+ return this.http.post('/v1/dreaming/consolidate', { userId });
272
+ }
273
+
274
+ async getInsights(userId: string, limit?: number): Promise<SleeptimeInsight[]> {
275
+ const params = new URLSearchParams({ userId });
276
+ if (limit) params.set('limit', String(limit));
277
+ return this.http.get(`/v1/dreaming/insights?${params}`);
278
+ }
279
+
280
+ async getStatus(): Promise<Record<string, number>> {
281
+ return this.http.get('/v1/dreaming/status');
282
+ }
283
+ }
284
+
285
+ class GraphClient {
286
+ constructor(private http: HttpClient) {}
287
+
288
+ async addEntity(userId: string, name: string, entityType?: string, properties?: Record<string, unknown>): Promise<Entity> {
289
+ return this.http.post('/v1/graph/entities', { userId, name, entityType, properties });
290
+ }
291
+
292
+ async addTriple(userId: string, subject: string, predicate: string, object: string, options?: {
293
+ validFrom?: string;
294
+ validTo?: string;
295
+ confidence?: number;
296
+ }): Promise<Triple> {
297
+ return this.http.post('/v1/graph/triples', { userId, subject, predicate, object, ...options });
298
+ }
299
+
300
+ async query(userId: string, options?: {
301
+ subject?: string;
302
+ predicate?: string;
303
+ object?: string;
304
+ validAt?: string;
305
+ limit?: number;
306
+ }): Promise<Triple[]> {
307
+ return this.http.post('/v1/graph/query', { userId, ...options });
308
+ }
309
+
310
+ async getEntityFacts(userId: string, entityName: string): Promise<Triple[]> {
311
+ return this.http.get(`/v1/graph/entities/${encodeURIComponent(entityName)}/facts?userId=${userId}`);
312
+ }
313
+ }
314
+
315
+ class PersonalityClient {
316
+ constructor(private http: HttpClient) {}
317
+
318
+ async getOcean(userId: string): Promise<OceanProfile> {
319
+ return this.http.get(`/v1/personality/ocean?userId=${userId}`);
320
+ }
321
+
322
+ async analyzeOcean(userId: string, text: string): Promise<OceanProfile> {
323
+ return this.http.post('/v1/personality/analyze', { userId, text });
324
+ }
325
+
326
+ async getFingerprint(userId: string): Promise<LinguisticFingerprint> {
327
+ return this.http.get(`/v1/personality/fingerprint?userId=${userId}`);
328
+ }
329
+ }
330
+
331
+ // ─── Status Types ────────────────────────────────────────────
332
+
333
+ export type AiState = 'idle' | 'thinking' | 'chatting' | 'creating' | 'learning' | 'dreaming' | 'evolving';
334
+
335
+ export interface AiStatus {
336
+ state: AiState;
337
+ detail: string;
338
+ since: number;
339
+ bgProcesses: string[];
340
+ }
341
+
342
+ export interface BrainHealth {
343
+ state: string;
344
+ memories: number;
345
+ memoriesWithEmbeddings: number;
346
+ memoryBlocks: number;
347
+ kgEntities: number;
348
+ hasOcean: boolean;
349
+ hasFingerprint: boolean;
350
+ healthy: boolean;
351
+ uptime: number;
352
+ }
353
+
354
+ export interface QuotaUsage {
355
+ tier: string;
356
+ memory_entries: number;
357
+ max_memories: number;
358
+ shade_count: number;
359
+ max_shades: number;
360
+ used_bytes: number;
361
+ max_bytes: number;
362
+ storage_used_mb: number;
363
+ storage_limit_mb: number;
364
+ }
365
+
366
+ // ─── Sync/Privacy Types ──────────────────────────────────────
367
+
368
+ export type ConsentStatus = 'synced' | 'paused' | 'pending' | 'declined';
369
+
370
+ export interface SyncSettings {
371
+ userId: string;
372
+ enabled: boolean;
373
+ categories: Record<string, boolean>;
374
+ updatedAt: string;
375
+ }
376
+
377
+ export interface SyncConsent {
378
+ id: string;
379
+ domain: string;
380
+ name: string;
381
+ memberCount: number;
382
+ consentStatus: ConsentStatus;
383
+ lastSync: string;
384
+ }
385
+
386
+ // ─── Status Client ───────────────────────────────────────────
387
+
388
+ class StatusClient {
389
+ constructor(private http: HttpClient) {}
390
+
391
+ /** Get current AI cognitive state (for orb rendering) */
392
+ async getStatus(userId?: string): Promise<AiStatus> {
393
+ const params = userId ? `?userId=${userId}` : '';
394
+ return this.http.get(`/v1/status${params}`);
395
+ }
396
+
397
+ /** Get detailed brain health metrics (for settings page) */
398
+ async getBrainHealth(userId?: string): Promise<BrainHealth> {
399
+ const params = userId ? `?userId=${userId}` : '';
400
+ return this.http.get(`/v1/status/brain${params}`);
401
+ }
402
+
403
+ /** Get quota usage and limits */
404
+ async getQuota(userId?: string): Promise<QuotaUsage> {
405
+ const params = userId ? `?userId=${userId}` : '';
406
+ const res = await this.http.get<{ data: QuotaUsage }>(`/v1/admin/quota${params}`);
407
+ return (res as any).data || res;
408
+ }
409
+ }
410
+
411
+ // ─── Sync Client ─────────────────────────────────────────────
412
+
413
+ class SyncClient {
414
+ constructor(private http: HttpClient) {}
415
+
416
+ /** Get user's global sync settings */
417
+ async getSettings(userId?: string): Promise<SyncSettings> {
418
+ const params = userId ? `?userId=${userId}` : '';
419
+ const res = await this.http.get<{ data: SyncSettings }>(`/v1/sync/settings${params}`);
420
+ return (res as any).data || res;
421
+ }
422
+
423
+ /** Update sync settings (global toggle + category toggles) */
424
+ async updateSettings(input: {
425
+ userId?: string;
426
+ enabled?: boolean;
427
+ categories?: Record<string, boolean>;
428
+ }): Promise<SyncSettings> {
429
+ const res = await this.http.post<{ data: SyncSettings }>('/v1/sync/settings', input);
430
+ return (res as any).data || res;
431
+ }
432
+
433
+ /** Get consent status for all partner workspaces */
434
+ async getConsents(userId?: string, appId?: string): Promise<SyncConsent[]> {
435
+ const params = new URLSearchParams();
436
+ if (userId) params.set('userId', userId);
437
+ if (appId) params.set('appId', appId);
438
+ const res = await this.http.get<{ data: { workspaces: SyncConsent[] } }>(`/v1/sync/consent?${params}`);
439
+ return (res as any).data?.workspaces || [];
440
+ }
441
+
442
+ /** Accept, pause, resume, or decline sync with a target workspace */
443
+ async updateConsent(input: {
444
+ userId?: string;
445
+ targetWorkspaceId: string;
446
+ action: 'accept' | 'pause' | 'resume' | 'decline';
447
+ domain?: string;
448
+ name?: string;
449
+ }): Promise<SyncConsent> {
450
+ const res = await this.http.post<{ data: SyncConsent }>('/v1/sync/consent', {
451
+ target_workspace_id: input.targetWorkspaceId,
452
+ action: input.action,
453
+ userId: input.userId,
454
+ domain: input.domain,
455
+ name: input.name,
456
+ });
457
+ return (res as any).data || res;
458
+ }
459
+
460
+ /** Get privacy exposure toggles */
461
+ async getExposure(userId?: string, appId?: string): Promise<Record<string, boolean>> {
462
+ const params = new URLSearchParams();
463
+ if (userId) params.set('userId', userId);
464
+ if (appId) params.set('appId', appId);
465
+ const res = await this.http.get<{ data: Record<string, boolean> }>(`/v1/privacy/exposure?${params}`);
466
+ return (res as any).data || res;
467
+ }
468
+
469
+ /** Update privacy exposure toggles */
470
+ async updateExposure(toggles: Record<string, boolean>, userId?: string): Promise<Record<string, boolean>> {
471
+ const res = await this.http.post<{ data: any }>('/v1/privacy/exposure', {
472
+ toggles, userId,
473
+ });
474
+ return (res as any).data?.toggles || (res as any).data || toggles;
475
+ }
476
+ }
477
+
478
+ // ─── Context Client ──────────────────────────────────────────
479
+
480
+ class ContextClient {
481
+ constructor(private http: HttpClient) {}
482
+
483
+ /** Get pre-built context string for injection into LLM prompts */
484
+ async getContext(userId: string): Promise<{ context: string; memories: number; personality: string }> {
485
+ return this.http.get(`/v1/memories/recall?userId=${userId}&limit=10`);
486
+ }
487
+
488
+ /** Before-chat hook: enrich messages with context (for non-middleware integrations) */
489
+ async beforeChat(input: {
490
+ userId: string;
491
+ messages: Array<{ role: string; content: string }>;
492
+ }): Promise<Array<{ role: string; content: string }>> {
493
+ const res = await this.http.post<{ messages: Array<{ role: string; content: string }> }>('/v1/dreaming/process', {
494
+ userId: input.userId,
495
+ conversation: input.messages,
496
+ });
497
+ return (res as any).messages || input.messages;
498
+ }
499
+
500
+ /** After-chat hook: queue conversation for sleeptime processing */
501
+ async afterChat(input: {
502
+ userId: string;
503
+ messages: Array<{ role: string; content: string }>;
504
+ }): Promise<{ queued: number }> {
505
+ return this.http.post('/v1/dreaming/process', {
506
+ userId: input.userId,
507
+ conversation: input.messages,
508
+ });
509
+ }
510
+ }
511
+
512
+ // ─── Meter Client ────────────────────────────────────────────
513
+ // Smart Model Metering — lets partners/vendors show AI quality tier
514
+ // in their apps, display upgrade prompts, and track usage.
515
+ //
516
+ // Usage:
517
+ // const badge = await client.meter.getBadge('user-123');
518
+ // // → { icon: '🟢', label: 'Efficient AI', color: '#22c55e' }
519
+ //
520
+ // if (badge.canUpgrade) {
521
+ // showUpgradePrompt(badge.upgradeMessage);
522
+ // }
523
+
524
+ /** Pre-built UI badge for displaying AI tier in partner apps */
525
+ export interface MeterBadge {
526
+ /** Tier key: 'efficient' | 'smart' | 'genius' */
527
+ tier: string;
528
+ /** Display icon emoji */
529
+ icon: string;
530
+ /** Human-readable label (e.g., "Smart AI") */
531
+ label: string;
532
+ /** CSS-ready hex color for the badge */
533
+ color: string;
534
+ /** Short description of the tier */
535
+ description: string;
536
+ /** Primary model name (for transparency) */
537
+ model: string;
538
+ /** Whether the user can upgrade to a higher tier */
539
+ canUpgrade: boolean;
540
+ /** The next tier name if upgradeable */
541
+ nextTier?: string;
542
+ /** A ready-to-use upgrade CTA message */
543
+ upgradeMessage?: string;
544
+ }
545
+
546
+ const TIER_COLORS: Record<string, string> = {
547
+ efficient: '#22c55e', // green-500
548
+ smart: '#3b82f6', // blue-500
549
+ genius: '#a855f7', // purple-500
550
+ };
551
+
552
+ const TIER_ORDER = ['efficient', 'smart', 'genius'];
553
+
554
+ class MeterClient {
555
+ constructor(private http: HttpClient) {}
556
+
557
+ /** Get current model tier status for the authenticated user */
558
+ async getStatus(userId?: string): Promise<MeterStatus> {
559
+ const params = userId ? `?userId=${userId}` : '';
560
+ const res = await this.http.get<{ data: MeterStatus }>(`/v1/meter/status${params}`);
561
+ return (res as any).data || res;
562
+ }
563
+
564
+ /** Get detailed usage history */
565
+ async getUsage(userId: string, options?: { days?: number; limit?: number }): Promise<MeterUsage> {
566
+ const params = new URLSearchParams({ userId });
567
+ if (options?.days) params.set('days', String(options.days));
568
+ if (options?.limit) params.set('limit', String(options.limit));
569
+ const res = await this.http.get<{ data: MeterUsage }>(`/v1/meter/usage?${params}`);
570
+ return (res as any).data || res;
571
+ }
572
+
573
+ /**
574
+ * Get a ready-to-use UI badge for displaying the AI tier.
575
+ * Partners can drop this directly into their app UI.
576
+ *
577
+ * @example
578
+ * ```tsx
579
+ * const badge = await dreaming.meter.getBadge('user-123');
580
+ * return (
581
+ * <div style={{ background: badge.color, borderRadius: 12, padding: '4px 12px' }}>
582
+ * {badge.icon} {badge.label}
583
+ * </div>
584
+ * );
585
+ * ```
586
+ */
587
+ async getBadge(userId?: string): Promise<MeterBadge> {
588
+ const status = await this.getStatus(userId);
589
+ const tier = status.modelTier;
590
+ const tierIndex = TIER_ORDER.indexOf(tier);
591
+ const canUpgrade = tierIndex < TIER_ORDER.length - 1;
592
+ const nextTier = canUpgrade ? TIER_ORDER[tierIndex + 1] : undefined;
593
+ const nextTierName = nextTier
594
+ ? status.availableTiers.find(t => t.key === nextTier)?.name
595
+ : undefined;
596
+
597
+ return {
598
+ tier,
599
+ icon: status.modelTierIcon,
600
+ label: `${status.modelTierName} AI`,
601
+ color: TIER_COLORS[tier] || '#6b7280',
602
+ description: status.modelTierDescription,
603
+ model: status.primaryModel,
604
+ canUpgrade,
605
+ nextTier: nextTierName,
606
+ upgradeMessage: canUpgrade
607
+ ? `Upgrade to ${nextTierName} AI for smarter, deeper insights powered by ${status.availableTiers.find(t => t.key === nextTier)?.primaryModel || 'a premium model'}.`
608
+ : undefined,
609
+ };
610
+ }
611
+
612
+ /** Quick check: is this user on the highest AI tier? */
613
+ async isGeniusTier(userId?: string): Promise<boolean> {
614
+ const status = await this.getStatus(userId);
615
+ return status.modelTier === 'genius';
616
+ }
617
+
618
+ /** Quick check: can this user upgrade to a better AI tier? */
619
+ async canUpgrade(userId?: string): Promise<boolean> {
620
+ const status = await this.getStatus(userId);
621
+ const tierIndex = TIER_ORDER.indexOf(status.modelTier);
622
+ return tierIndex < TIER_ORDER.length - 1;
623
+ }
624
+
625
+ /**
626
+ * Get a formatted usage summary string for dashboards.
627
+ *
628
+ * @example
629
+ * ```
630
+ * "🔵 Smart AI — 45 calls, 12.5k tokens this month (avg 230ms)"
631
+ * ```
632
+ */
633
+ async getUsageSummary(userId?: string): Promise<string> {
634
+ const status = await this.getStatus(userId);
635
+ const u = status.usage;
636
+ const tokens = u.tokensThisMonth >= 1000
637
+ ? `${(u.tokensThisMonth / 1000).toFixed(1)}k`
638
+ : String(u.tokensThisMonth);
639
+ return `${status.modelTierIcon} ${status.modelTierName} AI — ${u.callsThisMonth} calls, ${tokens} tokens this month (avg ${u.avgLatencyMs}ms)`;
640
+ }
641
+ }
642
+
643
+ // ─── Main Client ─────────────────────────────────────────────
644
+
645
+ export class DreamingClient {
646
+ readonly memories: MemoriesClient;
647
+ readonly dreaming: DreamingApiClient;
648
+ readonly graph: GraphClient;
649
+ readonly personality: PersonalityClient;
650
+ readonly status: StatusClient;
651
+ readonly sync: SyncClient;
652
+ readonly context: ContextClient;
653
+ readonly meter: MeterClient;
654
+ readonly activities: ActivityClient;
655
+ readonly constraints: ConstraintClient;
656
+ readonly chat: ChatClient;
657
+ readonly knowledge: KnowledgeClient;
658
+ readonly web: WebClient;
659
+ readonly identity: IdentityClient;
660
+ readonly friends: FriendsClient;
661
+ readonly proactive: ProactiveClient;
662
+ readonly notifications: NotificationsClient;
663
+ readonly evolution: EvolutionClient;
664
+ readonly dna: DnaClient;
665
+ readonly partnerTokens: PartnerTokensClient;
666
+
667
+ constructor(config: DreamingClientConfig) {
668
+ const http = new HttpClient(config);
669
+ this.memories = new MemoriesClient(http);
670
+ this.dreaming = new DreamingApiClient(http);
671
+ this.graph = new GraphClient(http);
672
+ this.personality = new PersonalityClient(http);
673
+ this.status = new StatusClient(http);
674
+ this.sync = new SyncClient(http);
675
+ this.context = new ContextClient(http);
676
+ this.meter = new MeterClient(http);
677
+ this.activities = new ActivityClient(http, config);
678
+ this.constraints = new ConstraintClient(http);
679
+ this.chat = new ChatClient(http);
680
+ this.knowledge = new KnowledgeClient(http);
681
+ this.web = new WebClient(http);
682
+ this.identity = new IdentityClient(http);
683
+ this.friends = new FriendsClient(http);
684
+ this.proactive = new ProactiveClient(http);
685
+ this.notifications = new NotificationsClient(http);
686
+ this.evolution = new EvolutionClient(http);
687
+ this.dna = new DnaClient(http);
688
+ this.partnerTokens = new PartnerTokensClient(http);
689
+ }
690
+
691
+ /**
692
+ * Flush any buffered activity events before shutdown.
693
+ * Call this in your app's cleanup/beforeunload handler.
694
+ */
695
+ async flush(): Promise<void> {
696
+ await this.activities.flush();
697
+ }
698
+ }
699
+
700
+ // ─── Knowledge Client ────────────────────────────────────────
701
+ // RAG knowledge base for partners. Upload docs, search, toggle
702
+ // modules. Supports multi-format: text, markdown, CSV, JSON,
703
+ // HTML, URL, base64, PDF.
704
+ //
705
+ // Usage:
706
+ // await dreaming.knowledge.upload({
707
+ // appId: 'active365',
708
+ // title: 'Training Plan',
709
+ // content: markdownText,
710
+ // moduleId: 'fitness-guides',
711
+ // });
712
+ //
713
+ // const chunks = await dreaming.knowledge.search('running tips', { appId: 'active365' });
714
+ //
715
+ // await dreaming.knowledge.toggleModule('fitness-guides', { appId: 'active365', enabled: false });
716
+ //
717
+
718
+ export interface KnowledgeDoc {
719
+ docId: string;
720
+ title: string;
721
+ chunkCount: number;
722
+ fileSizeBytes: number;
723
+ format: string;
724
+ domain: string;
725
+ status: string;
726
+ }
727
+
728
+ export interface KnowledgeChunk {
729
+ id: number;
730
+ doc_id: string;
731
+ content: string;
732
+ chunk_index: number;
733
+ domain: string;
734
+ topic: string;
735
+ importance: number;
736
+ doc_title?: string;
737
+ relevance?: number;
738
+ }
739
+
740
+ export interface KnowledgeModule {
741
+ module_id: string;
742
+ enabled: boolean;
743
+ priority: number;
744
+ doc_count: number;
745
+ chunk_count: number;
746
+ }
747
+
748
+ class KnowledgeClient {
749
+ constructor(private http: HttpClient) {}
750
+
751
+ /**
752
+ * Upload a document to the knowledge base.
753
+ * Supports: text, markdown, CSV, JSON, HTML, URL, base64, PDF.
754
+ * Auto-detects format from filename/content if sourceType is 'auto' or omitted.
755
+ *
756
+ * @example
757
+ * ```typescript
758
+ * // Upload markdown
759
+ * await dreaming.knowledge.upload({
760
+ * appId: 'my-app',
761
+ * title: 'Training Guide',
762
+ * content: '# Running Tips\n\nStart slow...',
763
+ * });
764
+ *
765
+ * // Upload CSV data
766
+ * await dreaming.knowledge.upload({
767
+ * appId: 'my-app',
768
+ * title: 'Workout Log',
769
+ * content: 'date,distance,time\n2024-01-01,5km,28min',
770
+ * sourceType: 'csv',
771
+ * });
772
+ *
773
+ * // Upload with constraint auto-linking
774
+ * await dreaming.knowledge.upload({
775
+ * appId: 'my-app',
776
+ * userId: 'user-1',
777
+ * title: 'Fitness Plan',
778
+ * content: pdfBase64,
779
+ * sourceType: 'base64',
780
+ * feedConstraints: true, // Auto-creates tags from content
781
+ * });
782
+ * ```
783
+ */
784
+ async upload(options: {
785
+ appId: string;
786
+ title: string;
787
+ content: string;
788
+ userId?: string;
789
+ moduleId?: string;
790
+ sourceType?: string;
791
+ sourceUrl?: string;
792
+ filename?: string;
793
+ scope?: 'app' | 'user';
794
+ feedConstraints?: boolean;
795
+ metadata?: Record<string, unknown>;
796
+ }): Promise<KnowledgeDoc> {
797
+ const res = await this.http.post<{ data: KnowledgeDoc }>('/v1/knowledge/upload', options);
798
+ return (res as any).data;
799
+ }
800
+
801
+ /**
802
+ * Upload content from a URL. Fetches and parses automatically.
803
+ *
804
+ * @example
805
+ * ```typescript
806
+ * await dreaming.knowledge.uploadUrl({
807
+ * appId: 'my-app',
808
+ * title: 'Running Tips Article',
809
+ * url: 'https://example.com/running-tips',
810
+ * });
811
+ * ```
812
+ */
813
+ async uploadUrl(options: {
814
+ appId: string;
815
+ title: string;
816
+ url: string;
817
+ moduleId?: string;
818
+ userId?: string;
819
+ }): Promise<KnowledgeDoc> {
820
+ return this.upload({
821
+ appId: options.appId,
822
+ title: options.title,
823
+ content: options.url,
824
+ sourceType: 'url',
825
+ sourceUrl: options.url,
826
+ moduleId: options.moduleId,
827
+ userId: options.userId,
828
+ });
829
+ }
830
+
831
+ /** List uploaded documents. */
832
+ async listDocs(options?: {
833
+ appId?: string;
834
+ moduleId?: string;
835
+ }): Promise<KnowledgeDoc[]> {
836
+ const params = new URLSearchParams();
837
+ if (options?.appId) params.set('appId', options.appId);
838
+ if (options?.moduleId) params.set('moduleId', options.moduleId);
839
+ const res = await this.http.get<{ data: KnowledgeDoc[] }>(`/v1/knowledge/docs?${params}`);
840
+ return (res as any).data;
841
+ }
842
+
843
+ /** Get document details. */
844
+ async getDoc(docId: string): Promise<KnowledgeDoc> {
845
+ const res = await this.http.get<{ data: KnowledgeDoc }>(`/v1/knowledge/docs/${docId}`);
846
+ return (res as any).data;
847
+ }
848
+
849
+ /** View chunks of a document. */
850
+ async getChunks(docId: string): Promise<KnowledgeChunk[]> {
851
+ const res = await this.http.get<{ data: KnowledgeChunk[] }>(`/v1/knowledge/docs/${docId}/chunks`);
852
+ return (res as any).data;
853
+ }
854
+
855
+ /**
856
+ * Search knowledge base with semantic + keyword fallback.
857
+ *
858
+ * @example
859
+ * ```typescript
860
+ * const results = await dreaming.knowledge.search('running tips', {
861
+ * appId: 'active365',
862
+ * limit: 5,
863
+ * domain: 'fitness',
864
+ * });
865
+ * ```
866
+ */
867
+ async search(query: string, options: {
868
+ appId: string;
869
+ domain?: string;
870
+ limit?: number;
871
+ }): Promise<KnowledgeChunk[]> {
872
+ const params = new URLSearchParams({ appId: options.appId, q: query });
873
+ if (options.domain) params.set('domain', options.domain);
874
+ if (options.limit) params.set('limit', String(options.limit));
875
+ const res = await this.http.get<{ data: KnowledgeChunk[] }>(`/v1/knowledge/search?${params}`);
876
+ return (res as any).data;
877
+ }
878
+
879
+ /** Delete a document and all its chunks. */
880
+ async deleteDoc(docId: string): Promise<void> {
881
+ await this.http.delete(`/v1/knowledge/docs/${docId}`);
882
+ }
883
+
884
+ /**
885
+ * Enable or disable a document (soft toggle).
886
+ * Disabled docs are excluded from search and chat context.
887
+ */
888
+ async toggleDoc(docId: string, enabled: boolean): Promise<void> {
889
+ await this.http.patch(`/v1/knowledge/docs/${docId}/toggle`, { enabled });
890
+ }
891
+
892
+ /**
893
+ * List knowledge modules with doc/chunk counts.
894
+ *
895
+ * @example
896
+ * ```typescript
897
+ * const modules = await dreaming.knowledge.listModules('active365');
898
+ * // [{ module_id: 'fitness-guides', enabled: true, doc_count: 3, chunk_count: 45 }]
899
+ * ```
900
+ */
901
+ async listModules(appId: string): Promise<KnowledgeModule[]> {
902
+ const res = await this.http.get<{ data: KnowledgeModule[] }>(`/v1/knowledge/modules?appId=${appId}`);
903
+ return (res as any).data;
904
+ }
905
+
906
+ /**
907
+ * Enable or disable an entire knowledge module.
908
+ * All docs in a disabled module are excluded from search.
909
+ */
910
+ async toggleModule(moduleId: string, options: { appId: string; enabled: boolean }): Promise<void> {
911
+ await this.http.patch(`/v1/knowledge/modules/${moduleId}/toggle`, options);
912
+ }
913
+
914
+ /**
915
+ * Re-chunk a document with different settings.
916
+ */
917
+ async reprocess(docId: string, options?: { chunkSize?: number; overlap?: number }): Promise<KnowledgeDoc> {
918
+ const res = await this.http.post<{ data: KnowledgeDoc }>(`/v1/knowledge/reprocess/${docId}`, options || {});
919
+ return (res as any).data;
920
+ }
921
+ }
922
+
923
+ // ─── Chat Client ─────────────────────────────────────────────
924
+ // The genius middleware for partners who have their own LLM.
925
+ //
926
+ // PROBLEM: Partners already use GPT/Gemini/Claude in their app.
927
+ // They don't want to replace their LLM — they want to ADD
928
+ // memory + personality + goals to it.
929
+ //
930
+ // SOLUTION: Two-call pattern:
931
+ // 1. BEFORE LLM call → dreaming.chat.enrich() — get context
932
+ // 2. AFTER LLM call → dreaming.chat.learn() — extract memories
933
+ //
934
+ // OR: One-line wrapper:
935
+ // const response = await dreaming.chat.wrapOpenAI(openai, userId, messages);
936
+ // // That's it. Memory-aware, personality-aware, goal-directed.
937
+ //
938
+ // ─── Usage Examples ──────────────────────────────────────────
939
+ //
940
+ // === Method 1: Manual (works with ANY LLM) ===
941
+ //
942
+ // const ctx = await dreaming.chat.enrich(userId, userMessage);
943
+ // const response = await yourLlmCall([...ctx.messages, ...yourMessages]);
944
+ // await dreaming.chat.learn(userId, userMessage, response);
945
+ //
946
+ // === Method 2: OpenAI Wrapper (one line) ===
947
+ //
948
+ // const reply = await dreaming.chat.wrapOpenAI(
949
+ // openai, // your OpenAI client
950
+ // userId,
951
+ // messages, // your conversation
952
+ // { model: 'gpt-4o' }
953
+ // );
954
+ //
955
+ // === Method 3: Gemini Wrapper ===
956
+ //
957
+ // const reply = await dreaming.chat.wrapGemini(
958
+ // genAI, // your Gemini client
959
+ // userId,
960
+ // userMessage,
961
+ // { model: 'gemini-2.0-flash' }
962
+ // );
963
+ //
964
+
965
+ /** Chat context returned from enrich() */
966
+ export interface ChatContext {
967
+ messages: Array<{ role: string; content: string }>;
968
+ system?: string;
969
+ memories: any[];
970
+ metadata: {
971
+ memoriesUsed: number;
972
+ hasPersonality: boolean;
973
+ hasConstraints: boolean;
974
+ hasKnowledge: boolean;
975
+ appPersona: string | null;
976
+ };
977
+ }
978
+
979
+ /** Options for enrich() */
980
+ export interface EnrichOptions {
981
+ maxMemories?: number;
982
+ includePersonality?: boolean;
983
+ includeConstraints?: boolean;
984
+ includeKnowledge?: boolean;
985
+ webGrounding?: boolean;
986
+ format?: 'openai' | 'anthropic' | 'raw';
987
+ }
988
+
989
+ /** Options for learn() */
990
+ export interface LearnOptions {
991
+ metadata?: Record<string, unknown>;
992
+ }
993
+
994
+ class ChatClient {
995
+ constructor(private http: HttpClient) {}
996
+
997
+ // ─── Core: Enrich + Learn ─────────────────────────────────
998
+
999
+ /**
1000
+ * BEFORE your LLM call: get memory/personality/goal context.
1001
+ *
1002
+ * Returns messages you inject into your conversation.
1003
+ * Default format is OpenAI-compatible.
1004
+ *
1005
+ * @example
1006
+ * ```typescript
1007
+ * const ctx = await dreaming.chat.enrich('user-1', 'How should I train today?');
1008
+ *
1009
+ * // Inject into your OpenAI call:
1010
+ * const response = await openai.chat.completions.create({
1011
+ * model: 'gpt-4o',
1012
+ * messages: [...ctx.messages, ...yourConversation],
1013
+ * });
1014
+ * ```
1015
+ */
1016
+ async enrich(userId: string, query: string, options?: EnrichOptions): Promise<ChatContext> {
1017
+ const res = await this.http.post<{ data: ChatContext }>('/v1/chat/context', {
1018
+ userId,
1019
+ query,
1020
+ maxMemories: options?.maxMemories,
1021
+ includePersonality: options?.includePersonality,
1022
+ includeConstraints: options?.includeConstraints,
1023
+ includeKnowledge: options?.includeKnowledge,
1024
+ webGrounding: options?.webGrounding,
1025
+ format: options?.format || 'openai',
1026
+ });
1027
+ return (res as any).data;
1028
+ }
1029
+
1030
+ /**
1031
+ * AFTER your LLM responds: tell Dreaming to extract memories.
1032
+ *
1033
+ * This queues background tasks: memory extraction, entity detection,
1034
+ * insight generation (goal-aware), and personality evolution.
1035
+ *
1036
+ * @example
1037
+ * ```typescript
1038
+ * await dreaming.chat.learn('user-1', userMessage, aiResponse);
1039
+ * ```
1040
+ */
1041
+ async learn(userId: string, userMessage: string, aiResponse: string, options?: LearnOptions): Promise<{ learned: boolean; jobsQueued: string[] }> {
1042
+ const res = await this.http.post<{ data: any }>('/v1/chat/learn', {
1043
+ userId,
1044
+ userMessage,
1045
+ aiResponse,
1046
+ metadata: options?.metadata,
1047
+ });
1048
+ return (res as any).data;
1049
+ }
1050
+
1051
+ /**
1052
+ * Full conversation version of learn.
1053
+ */
1054
+ async learnFromMessages(userId: string, messages: Array<{ role: string; content: string }>, options?: LearnOptions): Promise<{ learned: boolean; jobsQueued: string[] }> {
1055
+ const res = await this.http.post<{ data: any }>('/v1/chat/learn', {
1056
+ userId,
1057
+ messages,
1058
+ metadata: options?.metadata,
1059
+ });
1060
+ return (res as any).data;
1061
+ }
1062
+
1063
+ // ─── One-Line Wrappers ────────────────────────────────────
1064
+
1065
+ /**
1066
+ * Wrap an OpenAI client call. Memory + personality + goals, one line.
1067
+ *
1068
+ * @example
1069
+ * ```typescript
1070
+ * import OpenAI from 'openai';
1071
+ * const openai = new OpenAI();
1072
+ * const dreaming = new DreamingClient({ ... });
1073
+ *
1074
+ * // This one line gives your GPT memory, personality, and goals:
1075
+ * const reply = await dreaming.chat.wrapOpenAI(openai, 'user-1', [
1076
+ * { role: 'user', content: 'How should I train today?' }
1077
+ * ], { model: 'gpt-4o' });
1078
+ *
1079
+ * console.log(reply); // Goal-aware, memory-enriched response
1080
+ * ```
1081
+ */
1082
+ async wrapOpenAI(
1083
+ openai: any,
1084
+ userId: string,
1085
+ messages: Array<{ role: string; content: string }>,
1086
+ options?: { model?: string; temperature?: number; maxTokens?: number },
1087
+ ): Promise<string> {
1088
+ // 1. Enrich with context
1089
+ const lastUser = [...messages].reverse().find(m => m.role === 'user')?.content || '';
1090
+ const ctx = await this.enrich(userId, lastUser, { format: 'openai' });
1091
+
1092
+ // 2. Call OpenAI with enriched messages
1093
+ const enrichedMessages = [...ctx.messages, ...messages];
1094
+ const completion = await openai.chat.completions.create({
1095
+ model: options?.model || 'gpt-4o-mini',
1096
+ messages: enrichedMessages,
1097
+ temperature: options?.temperature,
1098
+ max_tokens: options?.maxTokens,
1099
+ });
1100
+
1101
+ const aiResponse = completion.choices?.[0]?.message?.content || '';
1102
+
1103
+ // 3. Learn from the conversation (non-blocking)
1104
+ this.learn(userId, lastUser, aiResponse).catch(() => {});
1105
+
1106
+ return aiResponse;
1107
+ }
1108
+
1109
+ /**
1110
+ * Wrap a Google Gemini call. Memory + personality + goals, one line.
1111
+ *
1112
+ * @example
1113
+ * ```typescript
1114
+ * import { GoogleGenerativeAI } from '@google/generative-ai';
1115
+ * const genAI = new GoogleGenerativeAI('YOUR_API_KEY');
1116
+ * const dreaming = new DreamingClient({ ... });
1117
+ *
1118
+ * const reply = await dreaming.chat.wrapGemini(genAI, 'user-1',
1119
+ * 'How should I train today?',
1120
+ * { model: 'gemini-2.0-flash' }
1121
+ * );
1122
+ * ```
1123
+ */
1124
+ async wrapGemini(
1125
+ genAI: any,
1126
+ userId: string,
1127
+ userMessage: string,
1128
+ options?: { model?: string; history?: Array<{ role: string; parts: Array<{ text: string }> }> },
1129
+ ): Promise<string> {
1130
+ // 1. Enrich
1131
+ const ctx = await this.enrich(userId, userMessage, { format: 'raw' });
1132
+
1133
+ // 2. Call Gemini
1134
+ const model = genAI.getGenerativeModel({ model: options?.model || 'gemini-2.0-flash' });
1135
+ const chat = model.startChat({
1136
+ history: options?.history || [],
1137
+ systemInstruction: ctx.system || (ctx as any).systemPrompt || '',
1138
+ });
1139
+ const result = await chat.sendMessage(userMessage);
1140
+ const aiResponse = result.response.text();
1141
+
1142
+ // 3. Learn (non-blocking)
1143
+ this.learn(userId, userMessage, aiResponse).catch(() => {});
1144
+
1145
+ return aiResponse;
1146
+ }
1147
+
1148
+ /**
1149
+ * Wrap an Anthropic Claude call. Memory + personality + goals, one line.
1150
+ *
1151
+ * @example
1152
+ * ```typescript
1153
+ * import Anthropic from '@anthropic-ai/sdk';
1154
+ * const anthropic = new Anthropic();
1155
+ * const dreaming = new DreamingClient({ ... });
1156
+ *
1157
+ * const reply = await dreaming.chat.wrapAnthropic(anthropic, 'user-1', [
1158
+ * { role: 'user', content: 'How should I train today?' }
1159
+ * ], { model: 'claude-sonnet-4-20250514' });
1160
+ * ```
1161
+ */
1162
+ async wrapAnthropic(
1163
+ anthropic: any,
1164
+ userId: string,
1165
+ messages: Array<{ role: string; content: string }>,
1166
+ options?: { model?: string; maxTokens?: number },
1167
+ ): Promise<string> {
1168
+ // 1. Enrich (Anthropic format: system is separate)
1169
+ const lastUser = [...messages].reverse().find(m => m.role === 'user')?.content || '';
1170
+ const ctx = await this.enrich(userId, lastUser, { format: 'anthropic' });
1171
+
1172
+ // 2. Call Anthropic
1173
+ const response = await anthropic.messages.create({
1174
+ model: options?.model || 'claude-sonnet-4-20250514',
1175
+ max_tokens: options?.maxTokens || 1024,
1176
+ system: ctx.system || '',
1177
+ messages,
1178
+ });
1179
+
1180
+ const aiResponse = response.content?.[0]?.text || '';
1181
+
1182
+ // 3. Learn (non-blocking)
1183
+ this.learn(userId, lastUser, aiResponse).catch(() => {});
1184
+
1185
+ return aiResponse;
1186
+ }
1187
+
1188
+ /**
1189
+ * Generic wrapper for any LLM. You provide the call function.
1190
+ *
1191
+ * @example
1192
+ * ```typescript
1193
+ * const reply = await dreaming.chat.wrap(
1194
+ * 'user-1',
1195
+ * 'How should I train today?',
1196
+ * async (systemPrompt, userMsg) => {
1197
+ * // Call your LLM however you want
1198
+ * return await myCustomLLM.generate(systemPrompt + '\n' + userMsg);
1199
+ * }
1200
+ * );
1201
+ * ```
1202
+ */
1203
+ async wrap(
1204
+ userId: string,
1205
+ userMessage: string,
1206
+ llmCall: (systemPrompt: string, userMessage: string) => Promise<string>,
1207
+ options?: EnrichOptions,
1208
+ ): Promise<string> {
1209
+ // 1. Enrich
1210
+ const ctx = await this.enrich(userId, userMessage, { ...options, format: 'raw' });
1211
+ const systemPrompt = (ctx as any).systemPrompt || ctx.system || '';
1212
+
1213
+ // 2. Call their LLM
1214
+ const aiResponse = await llmCall(systemPrompt, userMessage);
1215
+
1216
+ // 3. Learn (non-blocking)
1217
+ this.learn(userId, userMessage, aiResponse).catch(() => {});
1218
+
1219
+ return aiResponse;
1220
+ }
1221
+ }
1222
+
1223
+ // ─── Constraint Client ───────────────────────────────────────
1224
+ // Goals, tags, achievements with weighted influence on dreaming.
1225
+ //
1226
+ // Key concepts:
1227
+ // Weight (0-100): how much this constraint influences AI behavior
1228
+ // Pinned: won't fade or decay — user-locked focus area
1229
+ // Tags: evolving labels that direct dreaming — AI-born or user-created
1230
+ //
1231
+ // Usage:
1232
+ // // Set a goal
1233
+ // await dreaming.constraints.setGoal('user-1', 'Run 5K in under 25 minutes', {
1234
+ // category: 'fitness', weight: 90, targetValue: 25, unit: 'min',
1235
+ // deadline: '2026-06-01', tags: ['running', 'cardio'],
1236
+ // });
1237
+ //
1238
+ // // Add a focus tag
1239
+ // await dreaming.constraints.addTag('user-1', 'mindfulness', {
1240
+ // category: 'wellness', weight: 70, pinned: true,
1241
+ // });
1242
+
1243
+ /** Constraint types */
1244
+ export type ConstraintType = 'goal' | 'sub_goal' | 'task' | 'checkpoint' | 'achievement';
1245
+ export type ConstraintStatus = 'active' | 'completed' | 'cancelled' | 'paused' | 'deleted';
1246
+
1247
+ export interface Constraint {
1248
+ id: string;
1249
+ userId: string;
1250
+ appId: string;
1251
+ parentId?: string;
1252
+ constraintType: ConstraintType;
1253
+ title: string;
1254
+ description: string;
1255
+ status: ConstraintStatus;
1256
+ weight: number;
1257
+ priority: number;
1258
+ category: string;
1259
+ domain: string;
1260
+ progress: number;
1261
+ targetValue?: number;
1262
+ currentValue: number;
1263
+ unit: string;
1264
+ deadline?: string;
1265
+ completedAt?: string;
1266
+ cancelledAt?: string;
1267
+ pinned: boolean;
1268
+ createdBy: string;
1269
+ tags?: Array<{ tagKey: string; label: string; weight: number; status: string; pinned: boolean }>;
1270
+ childCount?: number;
1271
+ completedChildren?: number;
1272
+ metadata: Record<string, unknown>;
1273
+ createdAt: string;
1274
+ updatedAt: string;
1275
+ }
1276
+
1277
+ export interface ConstraintTag {
1278
+ id: string;
1279
+ userId: string;
1280
+ appId: string;
1281
+ tagKey: string;
1282
+ label: string;
1283
+ category: string;
1284
+ weight: number;
1285
+ status: string;
1286
+ pinned: boolean;
1287
+ source: string;
1288
+ birthReason: string;
1289
+ matchCount: number;
1290
+ constraintCount?: number;
1291
+ createdAt: string;
1292
+ }
1293
+
1294
+ export interface ConstraintContext {
1295
+ constraints: Constraint[];
1296
+ tags: ConstraintTag[];
1297
+ contextString: string;
1298
+ totalWeight: number;
1299
+ }
1300
+
1301
+ class ConstraintClient {
1302
+ constructor(private http: HttpClient) {}
1303
+
1304
+ // ─── Goals & Constraints ──────────────────────────────────
1305
+
1306
+ /**
1307
+ * Create a goal, sub-goal, task, checkpoint, or achievement.
1308
+ *
1309
+ * @example
1310
+ * ```typescript
1311
+ * await dreaming.constraints.setGoal('user-1', 'Run 5K under 25min', {
1312
+ * category: 'fitness', weight: 90,
1313
+ * targetValue: 25, unit: 'min',
1314
+ * deadline: '2026-06-01',
1315
+ * tags: ['running', 'cardio'],
1316
+ * });
1317
+ * ```
1318
+ */
1319
+ async setGoal(userId: string, title: string, options?: {
1320
+ type?: ConstraintType;
1321
+ parentId?: string;
1322
+ description?: string;
1323
+ weight?: number;
1324
+ priority?: number;
1325
+ category?: string;
1326
+ domain?: string;
1327
+ targetValue?: number;
1328
+ unit?: string;
1329
+ deadline?: string;
1330
+ pinned?: boolean;
1331
+ tags?: string[];
1332
+ metadata?: Record<string, unknown>;
1333
+ }): Promise<Constraint> {
1334
+ const res = await this.http.post<{ data: Constraint }>('/v1/constraints', {
1335
+ userId,
1336
+ title,
1337
+ constraintType: options?.type || 'goal',
1338
+ parentId: options?.parentId,
1339
+ description: options?.description,
1340
+ weight: options?.weight,
1341
+ priority: options?.priority,
1342
+ category: options?.category,
1343
+ domain: options?.domain,
1344
+ targetValue: options?.targetValue,
1345
+ unit: options?.unit,
1346
+ deadline: options?.deadline,
1347
+ pinned: options?.pinned,
1348
+ tags: options?.tags,
1349
+ metadata: options?.metadata,
1350
+ });
1351
+ return (res as any).data;
1352
+ }
1353
+
1354
+ /** Create a sub-goal under a parent goal */
1355
+ async addSubGoal(userId: string, parentId: string, title: string, options?: Parameters<ConstraintClient['setGoal']>[2]): Promise<Constraint> {
1356
+ return this.setGoal(userId, title, { ...options, type: 'sub_goal', parentId });
1357
+ }
1358
+
1359
+ /** Create a task under a goal or sub-goal */
1360
+ async addTask(userId: string, parentId: string, title: string, options?: Parameters<ConstraintClient['setGoal']>[2]): Promise<Constraint> {
1361
+ return this.setGoal(userId, title, { ...options, type: 'task', parentId });
1362
+ }
1363
+
1364
+ /** Create a checkpoint (milestone) */
1365
+ async addCheckpoint(userId: string, parentId: string, title: string, options?: Parameters<ConstraintClient['setGoal']>[2]): Promise<Constraint> {
1366
+ return this.setGoal(userId, title, { ...options, type: 'checkpoint', parentId });
1367
+ }
1368
+
1369
+ /** Record an achievement */
1370
+ async addAchievement(userId: string, title: string, options?: Parameters<ConstraintClient['setGoal']>[2]): Promise<Constraint> {
1371
+ return this.setGoal(userId, title, { ...options, type: 'achievement' });
1372
+ }
1373
+
1374
+ /**
1375
+ * List constraints with optional filters.
1376
+ */
1377
+ async list(userId: string, options?: {
1378
+ type?: ConstraintType;
1379
+ status?: ConstraintStatus | 'all';
1380
+ category?: string;
1381
+ parentId?: string;
1382
+ pinned?: boolean;
1383
+ }): Promise<Constraint[]> {
1384
+ const params = new URLSearchParams({ userId });
1385
+ if (options?.type) params.set('type', options.type);
1386
+ if (options?.status) params.set('status', options.status);
1387
+ if (options?.category) params.set('category', options.category);
1388
+ if (options?.parentId) params.set('parentId', options.parentId);
1389
+ if (options?.pinned) params.set('pinned', 'true');
1390
+ const res = await this.http.get<{ data: Constraint[] }>(`/v1/constraints?${params}`);
1391
+ return (res as any).data;
1392
+ }
1393
+
1394
+ /**
1395
+ * Update a constraint's weight, status, progress, or other fields.
1396
+ *
1397
+ * @example
1398
+ * ```typescript
1399
+ * // Update progress
1400
+ * await dreaming.constraints.update(goalId, { progress: 75 });
1401
+ *
1402
+ * // Change weight (influences how much AI focuses on this)
1403
+ * await dreaming.constraints.update(goalId, { weight: 30 });
1404
+ *
1405
+ * // Complete a goal
1406
+ * await dreaming.constraints.update(goalId, { status: 'completed' });
1407
+ * ```
1408
+ */
1409
+ async update(constraintId: string, updates: {
1410
+ title?: string;
1411
+ description?: string;
1412
+ status?: ConstraintStatus;
1413
+ weight?: number;
1414
+ priority?: number;
1415
+ category?: string;
1416
+ progress?: number;
1417
+ currentValue?: number;
1418
+ targetValue?: number;
1419
+ deadline?: string;
1420
+ pinned?: boolean;
1421
+ reason?: string;
1422
+ metadata?: Record<string, unknown>;
1423
+ }): Promise<Constraint> {
1424
+ const res = await this.http.patch<{ data: Constraint }>(`/v1/constraints/${constraintId}`, updates);
1425
+ return (res as any).data;
1426
+ }
1427
+
1428
+ /** Complete a constraint */
1429
+ async complete(constraintId: string): Promise<Constraint> {
1430
+ return this.update(constraintId, { status: 'completed' });
1431
+ }
1432
+
1433
+ /** Cancel a constraint */
1434
+ async cancel(constraintId: string, reason?: string): Promise<Constraint> {
1435
+ return this.update(constraintId, { status: 'cancelled', reason });
1436
+ }
1437
+
1438
+ /** Pause a constraint (keeps weight but AI won't actively suggest) */
1439
+ async pause(constraintId: string): Promise<Constraint> {
1440
+ return this.update(constraintId, { status: 'paused' });
1441
+ }
1442
+
1443
+ /** Resume a paused constraint */
1444
+ async resume(constraintId: string): Promise<Constraint> {
1445
+ return this.update(constraintId, { status: 'active' });
1446
+ }
1447
+
1448
+ /** Pin a constraint (locks it from fading) */
1449
+ async pin(constraintId: string): Promise<Constraint> {
1450
+ return this.update(constraintId, { pinned: true });
1451
+ }
1452
+
1453
+ /** Unpin a constraint */
1454
+ async unpin(constraintId: string): Promise<Constraint> {
1455
+ return this.update(constraintId, { pinned: false });
1456
+ }
1457
+
1458
+ /** Set the weight (0-100) — how much the AI focuses on this */
1459
+ async setWeight(constraintId: string, weight: number, reason?: string): Promise<Constraint> {
1460
+ return this.update(constraintId, { weight: Math.min(Math.max(weight, 0), 100), reason });
1461
+ }
1462
+
1463
+ /** Delete a constraint (soft delete) */
1464
+ async delete(constraintId: string): Promise<void> {
1465
+ await this.http.delete(`/v1/constraints/${constraintId}`);
1466
+ }
1467
+
1468
+ /** Get change history for a constraint */
1469
+ async getHistory(constraintId: string): Promise<Array<{ changeType: string; oldValue: string; newValue: string; reason: string; createdAt: string }>> {
1470
+ const res = await this.http.get<{ data: any[] }>(`/v1/constraints/${constraintId}/history`);
1471
+ return (res as any).data;
1472
+ }
1473
+
1474
+ // ─── Tags ─────────────────────────────────────────────────
1475
+
1476
+ /**
1477
+ * Create or update a focus tag.
1478
+ *
1479
+ * @example
1480
+ * ```typescript
1481
+ * await dreaming.constraints.addTag('user-1', 'mindfulness', {
1482
+ * category: 'wellness', weight: 70, pinned: true,
1483
+ * });
1484
+ * ```
1485
+ */
1486
+ async addTag(userId: string, tagKey: string, options?: {
1487
+ label?: string;
1488
+ category?: string;
1489
+ weight?: number;
1490
+ pinned?: boolean;
1491
+ birthReason?: string;
1492
+ }): Promise<ConstraintTag> {
1493
+ const res = await this.http.post<{ data: ConstraintTag }>('/v1/constraints/tags', {
1494
+ userId,
1495
+ tagKey,
1496
+ label: options?.label || tagKey,
1497
+ category: options?.category,
1498
+ weight: options?.weight,
1499
+ pinned: options?.pinned,
1500
+ source: 'user',
1501
+ birthReason: options?.birthReason,
1502
+ });
1503
+ return (res as any).data;
1504
+ }
1505
+
1506
+ /** List tags */
1507
+ async listTags(userId: string, options?: {
1508
+ status?: string;
1509
+ category?: string;
1510
+ pinned?: boolean;
1511
+ }): Promise<ConstraintTag[]> {
1512
+ const params = new URLSearchParams({ userId });
1513
+ if (options?.status) params.set('status', options.status);
1514
+ if (options?.category) params.set('category', options.category);
1515
+ if (options?.pinned) params.set('pinned', 'true');
1516
+ const res = await this.http.get<{ data: ConstraintTag[] }>(`/v1/constraints/tags?${params}`);
1517
+ return (res as any).data;
1518
+ }
1519
+
1520
+ /** Update a tag (weight, pin, category) */
1521
+ async updateTag(tagId: string, updates: {
1522
+ weight?: number;
1523
+ pinned?: boolean;
1524
+ status?: string;
1525
+ category?: string;
1526
+ label?: string;
1527
+ }): Promise<ConstraintTag> {
1528
+ const res = await this.http.patch<{ data: ConstraintTag }>(`/v1/constraints/tags/${tagId}`, updates);
1529
+ return (res as any).data;
1530
+ }
1531
+
1532
+ /** Pin a tag */
1533
+ async pinTag(tagId: string): Promise<ConstraintTag> {
1534
+ return this.updateTag(tagId, { pinned: true });
1535
+ }
1536
+
1537
+ /** Set tag weight */
1538
+ async setTagWeight(tagId: string, weight: number): Promise<ConstraintTag> {
1539
+ return this.updateTag(tagId, { weight: Math.min(Math.max(weight, 0), 100) });
1540
+ }
1541
+
1542
+ // ─── Context ──────────────────────────────────────────────
1543
+
1544
+ /**
1545
+ * Get the weighted constraint context that the AI uses.
1546
+ * Shows exactly how constraints + tags influence dreaming.
1547
+ */
1548
+ async getContext(userId: string): Promise<ConstraintContext> {
1549
+ const res = await this.http.get<{ data: ConstraintContext }>(`/v1/constraints/context?userId=${userId}`);
1550
+ return (res as any).data;
1551
+ }
1552
+ }
1553
+
1554
+ // ─── Activity Client ─────────────────────────────────────────
1555
+ // Lightweight activity tracking with client-side buffering.
1556
+ //
1557
+ // DESIGN GOALS:
1558
+ // 1. ZERO overhead on the app — track() is synchronous
1559
+ // 2. Events buffer locally and flush as a single batch
1560
+ // 3. Auto-flush every N seconds or when buffer is full
1561
+ // 4. Fire-and-forget — flush errors never crash the app
1562
+ // 5. Session management built-in
1563
+ //
1564
+ // Usage:
1565
+ // dreaming.activities.track('user-123', 'screen_view', 'ProductDetail', {
1566
+ // domain: 'shopping',
1567
+ // data: { productId: 'abc', price: 29.99 },
1568
+ // });
1569
+ // // → Queues locally, flushes automatically every 5 seconds
1570
+
1571
+ /** A single activity event to track */
1572
+ export interface ActivityEvent {
1573
+ userId: string;
1574
+ /** Category of activity: 'screen_view', 'button_click', 'feature_use', 'gesture', etc. */
1575
+ activityType: string;
1576
+ /** Specific name: 'ProductDetail', 'AddToCart', 'SearchQuery', etc. */
1577
+ activityName: string;
1578
+ /** Life domain: 'fitness', 'shopping', 'finance', 'social', etc. */
1579
+ domain?: string;
1580
+ /** Arbitrary data payload */
1581
+ data?: Record<string, unknown>;
1582
+ /** Duration in ms (for timed activities like screen dwell time) */
1583
+ durationMs?: number;
1584
+ /** Session ID (auto-generated if not provided) */
1585
+ sessionId?: string;
1586
+ }
1587
+
1588
+ /** Activity summary for a time period */
1589
+ export interface ActivitySummary {
1590
+ activityType: string;
1591
+ activityName: string;
1592
+ count: number;
1593
+ totalDurationMs: number;
1594
+ lastAt: string;
1595
+ }
1596
+
1597
+ /** Configuration for the activity buffer */
1598
+ export interface ActivityBufferConfig {
1599
+ /** Max events to buffer before auto-flush (default: 20) */
1600
+ bufferSize?: number;
1601
+ /** Auto-flush interval in ms (default: 5000 = 5 seconds) */
1602
+ flushIntervalMs?: number;
1603
+ /** Disable auto-flush (for manual control) */
1604
+ disableAutoFlush?: boolean;
1605
+ }
1606
+
1607
+ class ActivityClient {
1608
+ private buffer: ActivityEvent[] = [];
1609
+ private flushTimer: ReturnType<typeof setInterval> | null = null;
1610
+ private sessionId: string;
1611
+ private bufferSize: number;
1612
+ private isFlushing = false;
1613
+
1614
+ constructor(
1615
+ private http: HttpClient,
1616
+ config: DreamingClientConfig & { activityBuffer?: ActivityBufferConfig },
1617
+ ) {
1618
+ const bufConfig = config.activityBuffer ?? {};
1619
+ this.bufferSize = bufConfig.bufferSize ?? 20;
1620
+ this.sessionId = `ses_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
1621
+
1622
+ if (!bufConfig.disableAutoFlush) {
1623
+ const interval = bufConfig.flushIntervalMs ?? 5000;
1624
+ this.flushTimer = setInterval(() => this.flush().catch(() => {}), interval);
1625
+ }
1626
+ }
1627
+
1628
+ /**
1629
+ * Track a user activity event — SYNCHRONOUS, zero overhead.
1630
+ * Events are queued locally and flushed as a batch automatically.
1631
+ *
1632
+ * @example
1633
+ * ```typescript
1634
+ * // Track a screen view
1635
+ * dreaming.activities.track('user-123', 'screen_view', 'Dashboard');
1636
+ *
1637
+ * // Track a feature interaction with data
1638
+ * dreaming.activities.track('user-123', 'feature_use', 'AIChat', {
1639
+ * domain: 'productivity',
1640
+ * data: { messageCount: 5, topic: 'project planning' },
1641
+ * durationMs: 45000,
1642
+ * });
1643
+ *
1644
+ * // Track a button click
1645
+ * dreaming.activities.track('user-123', 'button_click', 'UpgradeToProBtn', {
1646
+ * domain: 'billing',
1647
+ * });
1648
+ * ```
1649
+ */
1650
+ track(
1651
+ userId: string,
1652
+ activityType: string,
1653
+ activityName: string,
1654
+ options?: Omit<ActivityEvent, 'userId' | 'activityType' | 'activityName'>,
1655
+ ): void {
1656
+ this.buffer.push({
1657
+ userId,
1658
+ activityType,
1659
+ activityName,
1660
+ domain: options?.domain,
1661
+ data: options?.data,
1662
+ durationMs: options?.durationMs,
1663
+ sessionId: options?.sessionId ?? this.sessionId,
1664
+ });
1665
+
1666
+ // Auto-flush when buffer is full
1667
+ if (this.buffer.length >= this.bufferSize) {
1668
+ this.flush().catch(() => {});
1669
+ }
1670
+ }
1671
+
1672
+ /**
1673
+ * Flush buffered events to the server.
1674
+ * Called automatically by the timer/buffer-full, but you can call manually.
1675
+ */
1676
+ async flush(): Promise<number> {
1677
+ if (this.buffer.length === 0 || this.isFlushing) return 0;
1678
+ this.isFlushing = true;
1679
+
1680
+ // Take the current buffer and clear it
1681
+ const events = [...this.buffer];
1682
+ this.buffer = [];
1683
+
1684
+ try {
1685
+ const res = await this.http.post<{ data: { inserted: number } }>('/v1/activities/batch', {
1686
+ events,
1687
+ });
1688
+ return (res as any).data?.inserted ?? events.length;
1689
+ } catch {
1690
+ // Put events back on failure (will retry on next flush)
1691
+ this.buffer.unshift(...events);
1692
+ return 0;
1693
+ } finally {
1694
+ this.isFlushing = false;
1695
+ }
1696
+ }
1697
+
1698
+ /**
1699
+ * Query activity history for a user.
1700
+ */
1701
+ async getHistory(userId: string, options?: {
1702
+ type?: string;
1703
+ domain?: string;
1704
+ days?: number;
1705
+ limit?: number;
1706
+ }): Promise<{ data: ActivityEvent[]; summary: ActivitySummary[]; total: number }> {
1707
+ const params = new URLSearchParams({ userId });
1708
+ if (options?.type) params.set('type', options.type);
1709
+ if (options?.domain) params.set('domain', options.domain);
1710
+ if (options?.days) params.set('days', String(options.days));
1711
+ if (options?.limit) params.set('limit', String(options.limit));
1712
+ return this.http.get(`/v1/activities?${params}`);
1713
+ }
1714
+
1715
+ /** Get the current auto-generated session ID */
1716
+ getSessionId(): string {
1717
+ return this.sessionId;
1718
+ }
1719
+
1720
+ /** Start a new session (generates a new session ID) */
1721
+ newSession(): string {
1722
+ this.sessionId = `ses_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
1723
+ return this.sessionId;
1724
+ }
1725
+
1726
+ /** Get the number of buffered (unflushed) events */
1727
+ get pendingCount(): number {
1728
+ return this.buffer.length;
1729
+ }
1730
+
1731
+ /** Stop the auto-flush timer (call before app shutdown after flush) */
1732
+ destroy(): void {
1733
+ if (this.flushTimer) {
1734
+ clearInterval(this.flushTimer);
1735
+ this.flushTimer = null;
1736
+ }
1737
+ }
1738
+ }
1739
+
1740
+ // ─── Web Grounding Client ────────────────────────────────────
1741
+ // Internet access for the AI brain. Controlled by DNA policy.
1742
+ //
1743
+ // Usage:
1744
+ // const results = await dreaming.web.search('5K race events Jakarta', { appId: 'active365' });
1745
+ // const policy = await dreaming.web.getPolicy('active365');
1746
+ // await dreaming.web.updatePolicy('active365', { enabled: true, maxDailySearches: 100 });
1747
+ //
1748
+
1749
+ export interface WebSearchResult {
1750
+ title: string;
1751
+ url: string;
1752
+ content: string;
1753
+ description?: string;
1754
+ publishedDate?: string;
1755
+ source: string;
1756
+ }
1757
+
1758
+ export interface WebCrawlResult {
1759
+ url: string;
1760
+ content: string;
1761
+ title?: string;
1762
+ description?: string;
1763
+ source: string;
1764
+ length: number;
1765
+ }
1766
+
1767
+ export interface WebAccessPolicy {
1768
+ enabled: boolean;
1769
+ maxDailySearches: number;
1770
+ allowedCategories: string[];
1771
+ blockedDomains: string[];
1772
+ requireRelevance: boolean;
1773
+ }
1774
+
1775
+ export interface WebDailyUsage {
1776
+ appId: string;
1777
+ date: string;
1778
+ searchCount: number;
1779
+ crawlCount: number;
1780
+ tokensUsed: number;
1781
+ }
1782
+
1783
+ class WebClient {
1784
+ constructor(private http: HttpClient) {}
1785
+
1786
+ /**
1787
+ * Search the web. Respects DNA guardrails (must be enabled for the app).
1788
+ * Results are cached for 24h.
1789
+ *
1790
+ * @example
1791
+ * ```typescript
1792
+ * const results = await dreaming.web.search('5K running tips', {
1793
+ * appId: 'active365',
1794
+ * limit: 5,
1795
+ * });
1796
+ * ```
1797
+ */
1798
+ async search(query: string, options: {
1799
+ appId: string;
1800
+ limit?: number;
1801
+ format?: 'results' | 'prompt';
1802
+ skipCache?: boolean;
1803
+ }): Promise<WebSearchResult[]> {
1804
+ const res = await this.http.post<{ data: { results: WebSearchResult[] } }>(
1805
+ '/v1/web/search',
1806
+ { query, ...options },
1807
+ );
1808
+ return (res as any).data.results;
1809
+ }
1810
+
1811
+ /**
1812
+ * Search and return formatted system prompt (for direct LLM injection).
1813
+ */
1814
+ async searchForPrompt(query: string, appId: string): Promise<string> {
1815
+ const res = await this.http.post<{ data: { contextPrompt: string } }>(
1816
+ '/v1/web/search',
1817
+ { query, appId, format: 'prompt', limit: 3 },
1818
+ );
1819
+ return (res as any).data.contextPrompt || '';
1820
+ }
1821
+
1822
+ /** Crawl a specific URL for content. */
1823
+ async crawl(url: string, appId: string): Promise<WebCrawlResult | null> {
1824
+ try {
1825
+ const res = await this.http.post<{ data: WebCrawlResult }>('/v1/web/crawl', { url, appId });
1826
+ return (res as any).data;
1827
+ } catch {
1828
+ return null;
1829
+ }
1830
+ }
1831
+
1832
+ /** Get the web access policy for an app. */
1833
+ async getPolicy(appId: string): Promise<WebAccessPolicy> {
1834
+ const res = await this.http.get<{ data: WebAccessPolicy }>(`/v1/web/policy?appId=${appId}`);
1835
+ return (res as any).data;
1836
+ }
1837
+
1838
+ /**
1839
+ * Update web access policy.
1840
+ *
1841
+ * @example
1842
+ * ```typescript
1843
+ * await dreaming.web.updatePolicy('active365', {
1844
+ * enabled: true,
1845
+ * maxDailySearches: 100,
1846
+ * blockedDomains: ['facebook.com'],
1847
+ * });
1848
+ * ```
1849
+ */
1850
+ async updatePolicy(appId: string, policy: Partial<WebAccessPolicy>): Promise<WebAccessPolicy> {
1851
+ const res = await this.http.post<{ data: WebAccessPolicy }>('/v1/web/policy', { appId, ...policy });
1852
+ return (res as any).data;
1853
+ }
1854
+
1855
+ /** Get daily web usage stats for budget monitoring. */
1856
+ async getUsage(appId: string, date?: string): Promise<WebDailyUsage> {
1857
+ const params = date ? `appId=${appId}&date=${date}` : `appId=${appId}`;
1858
+ const res = await this.http.get<{ data: WebDailyUsage }>(`/v1/web/usage?${params}`);
1859
+ return (res as any).data;
1860
+ }
1861
+
1862
+ /** Get user dreaming settings (all toggles). */
1863
+ async getSettings(userId: string, appId?: string): Promise<Record<string, boolean>> {
1864
+ const params = appId ? `userId=${userId}&appId=${appId}` : `userId=${userId}`;
1865
+ const res = await this.http.get<{ data: Record<string, boolean> }>(`/v1/web/settings?${params}`);
1866
+ return (res as any).data;
1867
+ }
1868
+
1869
+ /** Update user dreaming settings (toggle features on/off). */
1870
+ async updateSettings(userId: string, settings: Record<string, boolean>, appId?: string): Promise<Record<string, boolean>> {
1871
+ const res = await this.http.post<{ data: Record<string, boolean> }>('/v1/web/settings', {
1872
+ userId,
1873
+ appId: appId || 'default',
1874
+ ...settings,
1875
+ });
1876
+ return (res as any).data;
1877
+ }
1878
+ }
1879
+
1880
+ // ─── Identity Client ─────────────────────────────────────────
1881
+ // User names, AI names, social circle, cross-app sync.
1882
+ //
1883
+ // Usage:
1884
+ // const identity = await dreaming.identity.get('user-1', 'active365');
1885
+ // await dreaming.identity.setAiName('user-1', 'active365', 'Coach Buddy');
1886
+ // const people = await dreaming.identity.getSocialCircle('user-1');
1887
+ //
1888
+
1889
+ export interface UserIdentity {
1890
+ userId: string;
1891
+ appId: string;
1892
+ userDisplayName: string;
1893
+ userPreferredName: string;
1894
+ userFullName: string;
1895
+ aiName: string;
1896
+ aiDefaultName: string;
1897
+ source: string;
1898
+ syncToHq: boolean;
1899
+ syncFromHq: boolean;
1900
+ effectiveAiName?: string;
1901
+ effectiveUserName?: string;
1902
+ }
1903
+
1904
+ export interface SocialPerson {
1905
+ id: string;
1906
+ userId: string;
1907
+ appId: string;
1908
+ personName: string;
1909
+ relationship: string;
1910
+ relationshipDetail: string;
1911
+ nickname: string;
1912
+ status: string;
1913
+ mentionCount: number;
1914
+ sentiment: number;
1915
+ isCrossApp: boolean;
1916
+ source: string;
1917
+ }
1918
+
1919
+ class IdentityClient {
1920
+ constructor(private http: HttpClient) {}
1921
+
1922
+ /** Get user identity (names, AI name, sync status). */
1923
+ async get(userId: string, appId?: string): Promise<UserIdentity> {
1924
+ const params = appId ? `userId=${userId}&appId=${appId}` : `userId=${userId}`;
1925
+ const res = await this.http.get<{ data: UserIdentity }>(`/v1/identity?${params}`);
1926
+ return (res as any).data;
1927
+ }
1928
+
1929
+ /** Update user identity (display name, preferred name, etc). */
1930
+ async update(userId: string, appId: string, updates: Partial<UserIdentity>): Promise<UserIdentity> {
1931
+ const res = await this.http.post<{ data: UserIdentity }>('/v1/identity', { userId, appId, ...updates });
1932
+ return (res as any).data;
1933
+ }
1934
+
1935
+ /** Get the effective AI name for this user in this app. */
1936
+ async getAiName(userId: string, appId?: string): Promise<string> {
1937
+ const params = appId ? `userId=${userId}&appId=${appId}` : `userId=${userId}`;
1938
+ const res = await this.http.get<{ data: { aiName: string } }>(`/v1/identity/ai-name?${params}`);
1939
+ return (res as any).data.aiName;
1940
+ }
1941
+
1942
+ /** Set a custom AI name (user-chosen or partner default). */
1943
+ async setAiName(userId: string, appId: string, aiName: string, isPartnerDefault = false): Promise<string> {
1944
+ const res = await this.http.post<{ data: { aiName: string } }>('/v1/identity/ai-name', {
1945
+ userId, appId, aiName, isPartnerDefault,
1946
+ });
1947
+ return (res as any).data.aiName;
1948
+ }
1949
+
1950
+ /** Get people the user knows (family, friends, colleagues). */
1951
+ async getSocialCircle(userId: string, appId?: string, relationship?: string): Promise<SocialPerson[]> {
1952
+ let params = `userId=${userId}`;
1953
+ if (appId) params += `&appId=${appId}`;
1954
+ if (relationship) params += `&relationship=${relationship}`;
1955
+ const res = await this.http.get<{ data: SocialPerson[] }>(`/v1/identity/social?${params}`);
1956
+ return (res as any).data;
1957
+ }
1958
+
1959
+ /** Add a person to the user's social circle. */
1960
+ async addPerson(userId: string, appId: string, person: {
1961
+ name: string;
1962
+ relationship: string;
1963
+ relationshipDetail?: string;
1964
+ nickname?: string;
1965
+ sentiment?: number;
1966
+ }): Promise<SocialPerson> {
1967
+ const res = await this.http.post<{ data: SocialPerson }>('/v1/identity/social', {
1968
+ userId, appId, ...person,
1969
+ });
1970
+ return (res as any).data;
1971
+ }
1972
+
1973
+ /** Sync identity from HQ to a partner app. */
1974
+ async syncFromHq(userId: string, appId: string): Promise<UserIdentity> {
1975
+ const res = await this.http.post<{ data: UserIdentity }>('/v1/identity/sync', { userId, appId });
1976
+ return (res as any).data;
1977
+ }
1978
+ }
1979
+
1980
+ // ─── Friends Client ──────────────────────────────────────────
1981
+ // AI-to-AI friendship system. Persistent connections between
1982
+ // user brains with shared memories and trust evolution.
1983
+ //
1984
+ // Usage:
1985
+ // await dreaming.friends.request('user-1', 'user-2', { connectionType: 'friend' });
1986
+ // const connections = await dreaming.friends.list('user-1');
1987
+ //
1988
+
1989
+ export interface FriendConnection {
1990
+ id: string;
1991
+ user_a_id: string;
1992
+ user_b_id: string;
1993
+ status: string;
1994
+ connection_type: string;
1995
+ trust_level: number;
1996
+ interaction_count: number;
1997
+ friend_user_id?: string;
1998
+ shared_count?: number;
1999
+ exchange_count?: number;
2000
+ }
2001
+
2002
+ export interface SharedMemory {
2003
+ id: number;
2004
+ connection_id: string;
2005
+ source_user_id: string;
2006
+ content: string;
2007
+ domain: string;
2008
+ memory_type: string;
2009
+ importance: number;
2010
+ }
2011
+
2012
+ class FriendsClient {
2013
+ constructor(private http: HttpClient) {}
2014
+
2015
+ /** Send a friend request. */
2016
+ async request(userId: string, friendUserId: string, options?: {
2017
+ connectionType?: string;
2018
+ sharedDomains?: string[];
2019
+ message?: string;
2020
+ }): Promise<FriendConnection> {
2021
+ const res = await this.http.post<{ data: FriendConnection }>('/v1/friends/request', {
2022
+ userId, friendUserId, ...options,
2023
+ });
2024
+ return (res as any).data;
2025
+ }
2026
+
2027
+ /** Accept a friend connection. */
2028
+ async accept(connectionId: string, options?: {
2029
+ sharedDomains?: string[];
2030
+ autoCommunicate?: boolean;
2031
+ }): Promise<FriendConnection> {
2032
+ const res = await this.http.post<{ data: FriendConnection }>(`/v1/friends/accept/${connectionId}`, options || {});
2033
+ return (res as any).data;
2034
+ }
2035
+
2036
+ /** Reject or block a connection. */
2037
+ async reject(connectionId: string, block = false): Promise<void> {
2038
+ await this.http.post(`/v1/friends/reject/${connectionId}`, { block });
2039
+ }
2040
+
2041
+ /** List all connections for a user. */
2042
+ async list(userId: string): Promise<FriendConnection[]> {
2043
+ const res = await this.http.get<{ data: FriendConnection[] }>(`/v1/friends?userId=${userId}`);
2044
+ return (res as any).data;
2045
+ }
2046
+
2047
+ /** Get connection details. */
2048
+ async get(connectionId: string): Promise<FriendConnection> {
2049
+ const res = await this.http.get<{ data: FriendConnection }>(`/v1/friends/${connectionId}`);
2050
+ return (res as any).data;
2051
+ }
2052
+
2053
+ /** Trigger an AI-to-AI exchange. */
2054
+ async exchange(connectionId: string, topic?: string, maxRounds?: number): Promise<{
2055
+ connectionId: string;
2056
+ exchangeRounds: number;
2057
+ exchanges: any[];
2058
+ trustLevel: number;
2059
+ }> {
2060
+ const res = await this.http.post<{ data: any }>(`/v1/friends/${connectionId}/exchange`, { topic, maxRounds });
2061
+ return (res as any).data;
2062
+ }
2063
+
2064
+ /** Get exchange conversation history. */
2065
+ async conversations(connectionId: string, limit?: number): Promise<any[]> {
2066
+ const params = limit ? `?limit=${limit}` : '';
2067
+ const res = await this.http.get<{ data: any[] }>(`/v1/friends/${connectionId}/conversations${params}`);
2068
+ return (res as any).data;
2069
+ }
2070
+
2071
+ /** Get shared memories for a connection. */
2072
+ async sharedMemories(connectionId: string, limit?: number): Promise<SharedMemory[]> {
2073
+ const params = limit ? `?limit=${limit}` : '';
2074
+ const res = await this.http.get<{ data: SharedMemory[] }>(`/v1/friends/${connectionId}/shared${params}`);
2075
+ return (res as any).data;
2076
+ }
2077
+
2078
+ /** Share a specific memory with a friend. */
2079
+ async shareMemory(connectionId: string, userId: string, content: string, domain?: string, importance?: number): Promise<SharedMemory> {
2080
+ const res = await this.http.post<{ data: SharedMemory }>(`/v1/friends/${connectionId}/share`, {
2081
+ userId, content, domain, importance,
2082
+ });
2083
+ return (res as any).data;
2084
+ }
2085
+
2086
+ /** Update connection settings. */
2087
+ async update(connectionId: string, settings: {
2088
+ userId?: string;
2089
+ autoCommunicate?: boolean;
2090
+ communicationFreq?: string;
2091
+ connectionType?: string;
2092
+ sharedDomains?: string[];
2093
+ }): Promise<void> {
2094
+ await this.http.request('PATCH', `/v1/friends/${connectionId}`, settings);
2095
+ }
2096
+ }
2097
+
2098
+ // ─── Proactive Client ────────────────────────────────────────
2099
+ // AI-initiated proactive suggestions. The brain decides when
2100
+ // to reach out to the user with helpful nudges.
2101
+ //
2102
+
2103
+ export interface ProactiveSuggestion {
2104
+ id: number;
2105
+ userId: string;
2106
+ appId: string;
2107
+ type: string;
2108
+ content: string;
2109
+ priority: number;
2110
+ trigger: string;
2111
+ createdAt: string;
2112
+ }
2113
+
2114
+ class ProactiveClient {
2115
+ constructor(private http: HttpClient) {}
2116
+
2117
+ /** Trigger proactive evaluation for a user. */
2118
+ async evaluate(userId: string, appId?: string): Promise<{ triggered: boolean; suggestion?: ProactiveSuggestion }> {
2119
+ const res = await this.http.post<any>('/v1/proactive/evaluate', { userId, appId });
2120
+ return res as any;
2121
+ }
2122
+
2123
+ /** Get pending proactive suggestions. */
2124
+ async getSuggestions(userId: string, appId?: string, limit?: number): Promise<ProactiveSuggestion[]> {
2125
+ let params = `userId=${userId}`;
2126
+ if (appId) params += `&appId=${appId}`;
2127
+ if (limit) params += `&limit=${limit}`;
2128
+ const res = await this.http.get<{ data: ProactiveSuggestion[] }>(`/v1/proactive/suggestions?${params}`);
2129
+ return (res as any).data;
2130
+ }
2131
+
2132
+ /** Dismiss a proactive suggestion. */
2133
+ async dismiss(suggestionId: number | string): Promise<void> {
2134
+ await this.http.post('/v1/proactive/dismiss', { suggestionId });
2135
+ }
2136
+ }
2137
+
2138
+ // ─── Notifications Client ────────────────────────────────────
2139
+ // Notification preferences, push tokens, and behavior timeline.
2140
+ //
2141
+
2142
+ export interface NotificationPrefs {
2143
+ channel: string;
2144
+ pushToken: string;
2145
+ pushPlatform: string;
2146
+ quietStart: string;
2147
+ quietEnd: string;
2148
+ maxDaily: number;
2149
+ enabled: boolean;
2150
+ }
2151
+
2152
+ export interface BehaviorSignal {
2153
+ id: number;
2154
+ userId: string;
2155
+ signalType: string;
2156
+ value: number;
2157
+ context: Record<string, unknown>;
2158
+ createdAt: string;
2159
+ }
2160
+
2161
+ class NotificationsClient {
2162
+ constructor(private http: HttpClient) {}
2163
+
2164
+ /** Mark notifications as read. */
2165
+ async markRead(ids: (number | string)[]): Promise<void> {
2166
+ await this.http.post('/v1/proactive/read', { ids });
2167
+ }
2168
+
2169
+ /** Act on a proactive suggestion. */
2170
+ async act(suggestionId: number | string, action: string): Promise<any> {
2171
+ const res = await this.http.post<any>('/v1/proactive/act', { suggestionId, action });
2172
+ return res as any;
2173
+ }
2174
+
2175
+ /** Get notification preferences. */
2176
+ async getPrefs(userId: string, appId?: string): Promise<NotificationPrefs> {
2177
+ let params = `userId=${userId}`;
2178
+ if (appId) params += `&appId=${appId}`;
2179
+ const res = await this.http.get<{ data: NotificationPrefs }>(`/v1/notifications/prefs?${params}`);
2180
+ return (res as any).data;
2181
+ }
2182
+
2183
+ /** Update notification preferences. */
2184
+ async updatePrefs(userId: string, prefs: Partial<NotificationPrefs>, appId?: string): Promise<NotificationPrefs> {
2185
+ const res = await this.http.request('PUT', '/v1/notifications/prefs', { userId, appId, ...prefs }) as any;
2186
+ return res.data;
2187
+ }
2188
+
2189
+ /** Register a push notification token. */
2190
+ async registerPushToken(userId: string, token: string, platform: 'ios' | 'android' | 'web', appId?: string): Promise<void> {
2191
+ await this.http.post('/v1/notifications/push-token', { userId, token, platform, appId });
2192
+ }
2193
+
2194
+ /** Get behavior timeline (activity/mood history). */
2195
+ async getTimeline(userId: string, options?: { days?: number; limit?: number }): Promise<any[]> {
2196
+ let params = `userId=${userId}`;
2197
+ if (options?.days) params += `&days=${options.days}`;
2198
+ if (options?.limit) params += `&limit=${options.limit}`;
2199
+ const res = await this.http.get<{ data: any[] }>(`/v1/behavior/timeline?${params}`);
2200
+ return (res as any).data;
2201
+ }
2202
+
2203
+ /** Get behavior signals. */
2204
+ async getSignals(userId: string, type?: string): Promise<BehaviorSignal[]> {
2205
+ let params = `userId=${userId}`;
2206
+ if (type) params += `&type=${type}`;
2207
+ const res = await this.http.get<{ data: BehaviorSignal[] }>(`/v1/behavior/signals?${params}`);
2208
+ return (res as any).data;
2209
+ }
2210
+
2211
+ /** Get behavior summary. */
2212
+ async getSummary(userId: string): Promise<any> {
2213
+ const res = await this.http.get<{ data: any }>(`/v1/behavior/summary?userId=${userId}`);
2214
+ return (res as any).data;
2215
+ }
2216
+ }
2217
+
2218
+ // ─── Evolution Client ────────────────────────────────────────
2219
+ // Activity tracking, life domains, app features & events.
2220
+ // Transforms AI from conversation-centric to activity-aware.
2221
+ //
2222
+
2223
+ export interface LifeDomain {
2224
+ id: string;
2225
+ userId: string;
2226
+ name: string;
2227
+ category: string;
2228
+ importance: number;
2229
+ progress: number;
2230
+ status: string;
2231
+ }
2232
+
2233
+ export interface AppFeature {
2234
+ id: string;
2235
+ appId: string;
2236
+ featureName: string;
2237
+ category: string;
2238
+ description: string;
2239
+ }
2240
+
2241
+ class EvolutionClient {
2242
+ constructor(private http: HttpClient) {}
2243
+
2244
+ /** Track a single activity event. */
2245
+ async trackActivity(activity: {
2246
+ userId: string;
2247
+ appId?: string;
2248
+ activityType: string;
2249
+ category?: string;
2250
+ duration?: number;
2251
+ intensity?: number;
2252
+ metadata?: Record<string, unknown>;
2253
+ }): Promise<any> {
2254
+ const res = await this.http.post<{ data: any }>('/v1/activities', activity);
2255
+ return (res as any).data;
2256
+ }
2257
+
2258
+ /** Track a batch of activities. */
2259
+ async trackBatch(activities: Array<{
2260
+ userId: string;
2261
+ appId?: string;
2262
+ activityType: string;
2263
+ category?: string;
2264
+ duration?: number;
2265
+ metadata?: Record<string, unknown>;
2266
+ }>): Promise<{ inserted: number }> {
2267
+ const res = await this.http.post<any>('/v1/activities/batch', { activities });
2268
+ return res as any;
2269
+ }
2270
+
2271
+ /** Get activity history. */
2272
+ async getActivities(userId: string, options?: { category?: string; limit?: number; days?: number }): Promise<any[]> {
2273
+ let params = `userId=${userId}`;
2274
+ if (options?.category) params += `&category=${options.category}`;
2275
+ if (options?.limit) params += `&limit=${options.limit}`;
2276
+ if (options?.days) params += `&days=${options.days}`;
2277
+ const res = await this.http.get<{ data: any[] }>(`/v1/activities?${params}`);
2278
+ return (res as any).data;
2279
+ }
2280
+
2281
+ /** Get user's life domains. */
2282
+ async getDomains(userId: string): Promise<LifeDomain[]> {
2283
+ const res = await this.http.get<{ data: LifeDomain[] }>(`/v1/domains?userId=${userId}`);
2284
+ return (res as any).data;
2285
+ }
2286
+
2287
+ /** Create a life domain. */
2288
+ async createDomain(domain: {
2289
+ userId: string;
2290
+ name: string;
2291
+ category?: string;
2292
+ importance?: number;
2293
+ }): Promise<LifeDomain> {
2294
+ const res = await this.http.post<{ data: LifeDomain }>('/v1/domains', domain);
2295
+ return (res as any).data;
2296
+ }
2297
+
2298
+ /** Update domain progress. */
2299
+ async updateProgress(domainId: string, progress: number, note?: string): Promise<any> {
2300
+ const res = await this.http.post<{ data: any }>(`/v1/domains/${domainId}/progress`, { progress, note });
2301
+ return (res as any).data;
2302
+ }
2303
+
2304
+ /** Get domain progress history. */
2305
+ async getDomainHistory(domainId: string, limit?: number): Promise<any[]> {
2306
+ const params = limit ? `?limit=${limit}` : '';
2307
+ const res = await this.http.get<{ data: any[] }>(`/v1/domains/${domainId}/history${params}`);
2308
+ return (res as any).data;
2309
+ }
2310
+
2311
+ /** Register an app feature. */
2312
+ async registerFeature(appId: string, feature: {
2313
+ featureName: string;
2314
+ category?: string;
2315
+ description?: string;
2316
+ }): Promise<AppFeature> {
2317
+ const res = await this.http.post<{ data: AppFeature }>(`/v1/apps/${appId}/features`, feature);
2318
+ return (res as any).data;
2319
+ }
2320
+
2321
+ /** Emit an app event. */
2322
+ async emitEvent(appId: string, event: {
2323
+ eventType: string;
2324
+ userId?: string;
2325
+ payload?: Record<string, unknown>;
2326
+ }): Promise<any> {
2327
+ const res = await this.http.post<{ data: any }>(`/v1/apps/${appId}/events`, event);
2328
+ return (res as any).data;
2329
+ }
2330
+
2331
+ /** Get app context (features, recent events). */
2332
+ async getAppContext(appId: string, userId?: string): Promise<any> {
2333
+ const params = userId ? `?userId=${userId}` : '';
2334
+ const res = await this.http.get<{ data: any }>(`/v1/apps/${appId}/context${params}`);
2335
+ return (res as any).data;
2336
+ }
2337
+
2338
+ /** Get or set user state (mood, role, context). */
2339
+ async getUserState(userId: string): Promise<any> {
2340
+ const res = await this.http.get<{ data: any }>(`/v1/user-state?userId=${userId}`);
2341
+ return (res as any).data;
2342
+ }
2343
+
2344
+ /** Update user state. */
2345
+ async setUserState(userId: string, state: Record<string, unknown>): Promise<any> {
2346
+ const res = await this.http.post<{ data: any }>('/v1/user-state', { userId, ...state });
2347
+ return (res as any).data;
2348
+ }
2349
+ }
2350
+
2351
+ // ─── DNA Client ──────────────────────────────────────────────
2352
+ // App DNA management. Partners define their app's personality,
2353
+ // discipline, domains, guardrails, and knowledge seeds.
2354
+ //
2355
+
2356
+ export interface AppDna {
2357
+ appId: string;
2358
+ appName: string;
2359
+ discipline: string;
2360
+ personaName: string;
2361
+ boundaries: string;
2362
+ defaultDomains: string[];
2363
+ rootKnowledge: string[];
2364
+ webAccess: Record<string, unknown>;
2365
+ }
2366
+
2367
+ class DnaClient {
2368
+ constructor(private http: HttpClient) {}
2369
+
2370
+ /** Get app DNA configuration. */
2371
+ async get(appId: string): Promise<AppDna> {
2372
+ const res = await this.http.get<{ data: AppDna }>(`/v1/apps/${appId}/dna`);
2373
+ return (res as any).data;
2374
+ }
2375
+
2376
+ /** Register or update app DNA. */
2377
+ async update(appId: string, dna: Partial<AppDna>): Promise<AppDna> {
2378
+ const res = await this.http.post<{ data: AppDna }>(`/v1/apps/${appId}/dna`, dna);
2379
+ return (res as any).data;
2380
+ }
2381
+
2382
+ /** Seed a new user's brain with app defaults. */
2383
+ async seedUser(appId: string, userId: string): Promise<any> {
2384
+ const res = await this.http.post<{ data: any }>(`/v1/apps/${appId}/seed`, { userId });
2385
+ return (res as any).data;
2386
+ }
2387
+
2388
+ /** List all registered apps. */
2389
+ async listApps(): Promise<AppDna[]> {
2390
+ const res = await this.http.get<{ data: AppDna[] }>('/v1/apps/directory');
2391
+ return (res as any).data;
2392
+ }
2393
+ }
2394
+
2395
+ // ─── Partner Tokens Client ───────────────────────────────────
2396
+ // Token management for partner developers.
2397
+ //
2398
+
2399
+ export interface PartnerToken {
2400
+ id: string;
2401
+ name: string;
2402
+ key: string;
2403
+ status: number;
2404
+ quotaLimit: number;
2405
+ usedQuota: number;
2406
+ expiresAt?: string;
2407
+ }
2408
+
2409
+ class PartnerTokensClient {
2410
+ constructor(private http: HttpClient) {}
2411
+
2412
+ /** Create a child token. */
2413
+ async create(options: {
2414
+ name: string;
2415
+ quotaLimit?: number;
2416
+ models?: string[];
2417
+ expiresIn?: string;
2418
+ group?: string;
2419
+ }): Promise<PartnerToken> {
2420
+ const res = await this.http.post<{ data: PartnerToken }>('/v1/partner/tokens', options);
2421
+ return (res as any).data;
2422
+ }
2423
+
2424
+ /** List partner's tokens. */
2425
+ async list(): Promise<PartnerToken[]> {
2426
+ const res = await this.http.get<{ data: PartnerToken[] }>('/v1/partner/tokens');
2427
+ return (res as any).data;
2428
+ }
2429
+
2430
+ /** Revoke a token. */
2431
+ async revoke(tokenId: string): Promise<void> {
2432
+ await this.http.request('DELETE', `/v1/partner/tokens/${tokenId}`);
2433
+ }
2434
+
2435
+ /** Update token settings. */
2436
+ async update(tokenId: string, updates: Partial<{ quotaLimit: number; group: string; expiresAt: string }>): Promise<PartnerToken> {
2437
+ const res = await this.http.request('PATCH', `/v1/partner/tokens/${tokenId}`, updates) as any;
2438
+ return res.data;
2439
+ }
2440
+
2441
+ /** Get token usage stats. */
2442
+ async getUsage(tokenId: string): Promise<any> {
2443
+ const res = await this.http.get<{ data: any }>(`/v1/partner/tokens/${tokenId}/usage`);
2444
+ return (res as any).data;
2445
+ }
2446
+ }
2447
+
2448
+ // ─── Default Export ──────────────────────────────────────────
2449
+
2450
+ export default DreamingClient;