@auxiora/ambient 1.0.0 → 1.3.1
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/package.json +12 -6
- package/src/anticipation.ts +0 -141
- package/src/briefing.ts +0 -152
- package/src/index.ts +0 -26
- package/src/notification.ts +0 -101
- package/src/orchestrator.ts +0 -188
- package/src/pattern-engine.ts +0 -212
- package/src/scheduler.ts +0 -238
- package/src/types.ts +0 -85
- package/tests/ambient.test.ts +0 -363
- package/tests/orchestrator.test.ts +0 -343
- package/tests/scheduler.test.ts +0 -310
- package/tests/wiring.test.ts +0 -12
- package/tsconfig.json +0 -15
- package/tsconfig.tsbuildinfo +0 -1
package/src/pattern-engine.ts
DELETED
|
@@ -1,212 +0,0 @@
|
|
|
1
|
-
import * as crypto from 'node:crypto';
|
|
2
|
-
import type { AmbientPattern, AmbientPatternType, ObservedEvent } from './types.js';
|
|
3
|
-
|
|
4
|
-
/** Sliding window size for frequency analysis. */
|
|
5
|
-
const DEFAULT_WINDOW_SIZE = 7 * 24 * 60 * 60 * 1000; // 7 days
|
|
6
|
-
/** Minimum occurrences to consider something a pattern. */
|
|
7
|
-
const MIN_OCCURRENCES = 3;
|
|
8
|
-
/** Minimum confidence to emit a pattern. */
|
|
9
|
-
const MIN_CONFIDENCE = 0.3;
|
|
10
|
-
|
|
11
|
-
/**
|
|
12
|
-
* Ambient pattern engine — observes events and detects behavioral patterns
|
|
13
|
-
* using sliding window frequency analysis.
|
|
14
|
-
*/
|
|
15
|
-
export class AmbientPatternEngine {
|
|
16
|
-
private events: ObservedEvent[] = [];
|
|
17
|
-
private patterns: Map<string, AmbientPattern> = new Map();
|
|
18
|
-
private windowSize: number;
|
|
19
|
-
|
|
20
|
-
constructor(windowSize?: number) {
|
|
21
|
-
this.windowSize = windowSize ?? DEFAULT_WINDOW_SIZE;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
/** Observe a new event. */
|
|
25
|
-
observe(event: ObservedEvent): void {
|
|
26
|
-
this.events.push(event);
|
|
27
|
-
// Prune events outside the window
|
|
28
|
-
const cutoff = Date.now() - this.windowSize;
|
|
29
|
-
this.events = this.events.filter(e => e.timestamp >= cutoff);
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
/** Run pattern detection on observed events. */
|
|
33
|
-
detectPatterns(): AmbientPattern[] {
|
|
34
|
-
const detected: AmbientPattern[] = [];
|
|
35
|
-
|
|
36
|
-
// Group events by type
|
|
37
|
-
const byType = new Map<string, ObservedEvent[]>();
|
|
38
|
-
for (const event of this.events) {
|
|
39
|
-
const existing = byType.get(event.type) ?? [];
|
|
40
|
-
existing.push(event);
|
|
41
|
-
byType.set(event.type, existing);
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
// Detect schedule patterns (recurring events at similar times)
|
|
45
|
-
for (const [type, events] of byType) {
|
|
46
|
-
if (events.length >= MIN_OCCURRENCES) {
|
|
47
|
-
const schedulePattern = this.detectSchedulePattern(type, events);
|
|
48
|
-
if (schedulePattern) detected.push(schedulePattern);
|
|
49
|
-
|
|
50
|
-
const frequencyPattern = this.detectFrequencyPattern(type, events);
|
|
51
|
-
if (frequencyPattern) detected.push(frequencyPattern);
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
// Detect correlations between event types
|
|
56
|
-
const correlations = this.detectCorrelations(byType);
|
|
57
|
-
detected.push(...correlations);
|
|
58
|
-
|
|
59
|
-
// Update stored patterns
|
|
60
|
-
for (const pattern of detected) {
|
|
61
|
-
const existing = this.patterns.get(pattern.id);
|
|
62
|
-
if (existing) {
|
|
63
|
-
existing.lastConfirmedAt = Date.now();
|
|
64
|
-
existing.occurrences++;
|
|
65
|
-
existing.confidence = Math.min(1, existing.confidence + 0.05);
|
|
66
|
-
} else {
|
|
67
|
-
this.patterns.set(pattern.id, pattern);
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
return detected;
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
/** Get all detected patterns above minimum confidence. */
|
|
75
|
-
getPatterns(): AmbientPattern[] {
|
|
76
|
-
return Array.from(this.patterns.values())
|
|
77
|
-
.filter(p => p.confidence >= MIN_CONFIDENCE)
|
|
78
|
-
.sort((a, b) => b.confidence - a.confidence);
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
/** Get a pattern by ID. */
|
|
82
|
-
getPattern(id: string): AmbientPattern | undefined {
|
|
83
|
-
return this.patterns.get(id);
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
/** Get the number of observed events in the window. */
|
|
87
|
-
getEventCount(): number {
|
|
88
|
-
return this.events.length;
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
/** Clear all events and patterns. */
|
|
92
|
-
reset(): void {
|
|
93
|
-
this.events = [];
|
|
94
|
-
this.patterns.clear();
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
private detectSchedulePattern(type: string, events: ObservedEvent[]): AmbientPattern | null {
|
|
98
|
-
// Check if events happen at similar hours of the day
|
|
99
|
-
const hours = events.map(e => new Date(e.timestamp).getHours());
|
|
100
|
-
const hourCounts = new Map<number, number>();
|
|
101
|
-
for (const h of hours) {
|
|
102
|
-
hourCounts.set(h, (hourCounts.get(h) ?? 0) + 1);
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
// Find the most common hour
|
|
106
|
-
let maxHour = 0;
|
|
107
|
-
let maxCount = 0;
|
|
108
|
-
for (const [hour, count] of hourCounts) {
|
|
109
|
-
if (count > maxCount) {
|
|
110
|
-
maxHour = hour;
|
|
111
|
-
maxCount = count;
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
const ratio = maxCount / events.length;
|
|
116
|
-
if (ratio < 0.5 || maxCount < MIN_OCCURRENCES) return null;
|
|
117
|
-
|
|
118
|
-
const id = crypto.randomUUID();
|
|
119
|
-
return {
|
|
120
|
-
id,
|
|
121
|
-
type: 'schedule',
|
|
122
|
-
description: `"${type}" events frequently occur around ${maxHour}:00`,
|
|
123
|
-
confidence: Math.min(1, ratio * 0.8 + (maxCount / 10) * 0.2),
|
|
124
|
-
evidence: events.slice(-3).map(e => `${type} at ${new Date(e.timestamp).toISOString()}`),
|
|
125
|
-
detectedAt: Date.now(),
|
|
126
|
-
lastConfirmedAt: Date.now(),
|
|
127
|
-
occurrences: maxCount,
|
|
128
|
-
};
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
private detectFrequencyPattern(type: string, events: ObservedEvent[]): AmbientPattern | null {
|
|
132
|
-
if (events.length < MIN_OCCURRENCES) return null;
|
|
133
|
-
|
|
134
|
-
// Calculate average interval between events
|
|
135
|
-
const sorted = [...events].sort((a, b) => a.timestamp - b.timestamp);
|
|
136
|
-
const intervals: number[] = [];
|
|
137
|
-
for (let i = 1; i < sorted.length; i++) {
|
|
138
|
-
intervals.push(sorted[i].timestamp - sorted[i - 1].timestamp);
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
if (intervals.length === 0) return null;
|
|
142
|
-
|
|
143
|
-
const avgInterval = intervals.reduce((a, b) => a + b, 0) / intervals.length;
|
|
144
|
-
const stdDev = Math.sqrt(
|
|
145
|
-
intervals.reduce((sum, iv) => sum + (iv - avgInterval) ** 2, 0) / intervals.length
|
|
146
|
-
);
|
|
147
|
-
|
|
148
|
-
// Low variance means regular frequency
|
|
149
|
-
const cv = avgInterval > 0 ? stdDev / avgInterval : Infinity;
|
|
150
|
-
if (cv > 0.5) return null; // Too irregular
|
|
151
|
-
|
|
152
|
-
const confidence = Math.min(1, 1 - cv);
|
|
153
|
-
if (confidence < MIN_CONFIDENCE) return null;
|
|
154
|
-
|
|
155
|
-
const hours = Math.round(avgInterval / (60 * 60 * 1000) * 10) / 10;
|
|
156
|
-
const id = crypto.randomUUID();
|
|
157
|
-
return {
|
|
158
|
-
id,
|
|
159
|
-
type: 'preference',
|
|
160
|
-
description: `"${type}" occurs roughly every ${hours} hours`,
|
|
161
|
-
confidence,
|
|
162
|
-
evidence: [`${events.length} occurrences over window`, `Average interval: ${hours}h`],
|
|
163
|
-
detectedAt: Date.now(),
|
|
164
|
-
lastConfirmedAt: Date.now(),
|
|
165
|
-
occurrences: events.length,
|
|
166
|
-
};
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
private detectCorrelations(byType: Map<string, ObservedEvent[]>): AmbientPattern[] {
|
|
170
|
-
const patterns: AmbientPattern[] = [];
|
|
171
|
-
const types = Array.from(byType.keys());
|
|
172
|
-
|
|
173
|
-
// Check pairs of event types for temporal correlation
|
|
174
|
-
for (let i = 0; i < types.length; i++) {
|
|
175
|
-
for (let j = i + 1; j < types.length; j++) {
|
|
176
|
-
const eventsA = byType.get(types[i])!;
|
|
177
|
-
const eventsB = byType.get(types[j])!;
|
|
178
|
-
|
|
179
|
-
if (eventsA.length < 2 || eventsB.length < 2) continue;
|
|
180
|
-
|
|
181
|
-
// Count how often B follows A within 5 minutes
|
|
182
|
-
const followWindow = 5 * 60 * 1000;
|
|
183
|
-
let follows = 0;
|
|
184
|
-
for (const a of eventsA) {
|
|
185
|
-
for (const b of eventsB) {
|
|
186
|
-
if (b.timestamp > a.timestamp && b.timestamp - a.timestamp <= followWindow) {
|
|
187
|
-
follows++;
|
|
188
|
-
break;
|
|
189
|
-
}
|
|
190
|
-
}
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
const followRatio = follows / eventsA.length;
|
|
194
|
-
if (followRatio >= 0.5 && follows >= 2) {
|
|
195
|
-
const id = crypto.randomUUID();
|
|
196
|
-
patterns.push({
|
|
197
|
-
id,
|
|
198
|
-
type: 'correlation',
|
|
199
|
-
description: `"${types[j]}" often follows "${types[i]}" within 5 minutes`,
|
|
200
|
-
confidence: Math.min(1, followRatio * 0.9),
|
|
201
|
-
evidence: [`${follows} of ${eventsA.length} "${types[i]}" events followed by "${types[j]}"`],
|
|
202
|
-
detectedAt: Date.now(),
|
|
203
|
-
lastConfirmedAt: Date.now(),
|
|
204
|
-
occurrences: follows,
|
|
205
|
-
});
|
|
206
|
-
}
|
|
207
|
-
}
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
return patterns;
|
|
211
|
-
}
|
|
212
|
-
}
|
package/src/scheduler.ts
DELETED
|
@@ -1,238 +0,0 @@
|
|
|
1
|
-
import type { Scheduler } from '@auxiora/behaviors';
|
|
2
|
-
import type { ConnectorRegistry, TriggerManager } from '@auxiora/connectors';
|
|
3
|
-
import type { BriefingGenerator } from './briefing.js';
|
|
4
|
-
import type { NotificationOrchestrator } from './orchestrator.js';
|
|
5
|
-
import type { QuietNotification } from './types.js';
|
|
6
|
-
import { formatBriefingAsText } from './briefing.js';
|
|
7
|
-
|
|
8
|
-
/** Configuration for the ambient scheduler. */
|
|
9
|
-
export interface AmbientSchedulerConfig {
|
|
10
|
-
/** Cron expression for morning briefing. */
|
|
11
|
-
morningCron: string;
|
|
12
|
-
/** Cron expression for evening summary. */
|
|
13
|
-
eveningCron: string;
|
|
14
|
-
/** Cron expression for email polling. */
|
|
15
|
-
emailPollCron: string;
|
|
16
|
-
/** Cron expression for calendar polling. */
|
|
17
|
-
calendarPollCron: string;
|
|
18
|
-
/** Cron expression for notification polling. */
|
|
19
|
-
notificationPollCron: string;
|
|
20
|
-
/** Calendar alert window in minutes. */
|
|
21
|
-
calendarAlertMinutes: number;
|
|
22
|
-
/** Whether the scheduler is enabled. */
|
|
23
|
-
enabled: boolean;
|
|
24
|
-
/** Categories to include in briefings. */
|
|
25
|
-
categories: string[];
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
export const DEFAULT_AMBIENT_SCHEDULER_CONFIG: AmbientSchedulerConfig = {
|
|
29
|
-
morningCron: '0 7 * * *',
|
|
30
|
-
eveningCron: '0 18 * * *',
|
|
31
|
-
emailPollCron: '*/2 * * * *',
|
|
32
|
-
calendarPollCron: '*/5 * * * *',
|
|
33
|
-
notificationPollCron: '*/1 * * * *',
|
|
34
|
-
calendarAlertMinutes: 15,
|
|
35
|
-
enabled: true,
|
|
36
|
-
categories: ['calendar', 'email', 'tasks'],
|
|
37
|
-
};
|
|
38
|
-
|
|
39
|
-
/** Dependencies for the ambient scheduler. */
|
|
40
|
-
export interface AmbientSchedulerDeps {
|
|
41
|
-
scheduler: Scheduler;
|
|
42
|
-
connectorRegistry: ConnectorRegistry;
|
|
43
|
-
triggerManager: TriggerManager;
|
|
44
|
-
briefingGenerator: BriefingGenerator;
|
|
45
|
-
emailIntelligence?: { triage?: { getTriageSummary(opts: { maxResults: number }): Promise<{ items: Array<{ subject: string; priority: string }> }> } };
|
|
46
|
-
calendarIntelligence?: { analyzeDay(date: string): Promise<{ events: Array<{ title: string; time: string }> }> };
|
|
47
|
-
notificationOrchestrator?: NotificationOrchestrator;
|
|
48
|
-
deliveryChannel: (message: string) => Promise<void>;
|
|
49
|
-
userId: string;
|
|
50
|
-
config?: Partial<AmbientSchedulerConfig>;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
const JOB_IDS = {
|
|
54
|
-
emailPoll: 'ambient:email-poll',
|
|
55
|
-
calendarPoll: 'ambient:calendar-poll',
|
|
56
|
-
morningBriefing: 'ambient:morning-briefing',
|
|
57
|
-
eveningSummary: 'ambient:evening-summary',
|
|
58
|
-
notificationPoll: 'ambient:notification-poll',
|
|
59
|
-
} as const;
|
|
60
|
-
|
|
61
|
-
/**
|
|
62
|
-
* Schedules ambient polling and briefing generation using cron jobs.
|
|
63
|
-
*/
|
|
64
|
-
export class AmbientScheduler {
|
|
65
|
-
private readonly scheduler: Scheduler;
|
|
66
|
-
private readonly connectorRegistry: ConnectorRegistry;
|
|
67
|
-
private readonly triggerManager: TriggerManager;
|
|
68
|
-
private readonly briefingGenerator: BriefingGenerator;
|
|
69
|
-
private readonly emailIntelligence: AmbientSchedulerDeps['emailIntelligence'];
|
|
70
|
-
private readonly calendarIntelligence: AmbientSchedulerDeps['calendarIntelligence'];
|
|
71
|
-
private readonly notificationOrchestrator: NotificationOrchestrator | undefined;
|
|
72
|
-
private readonly deliveryChannel: (message: string) => Promise<void>;
|
|
73
|
-
private readonly userId: string;
|
|
74
|
-
private readonly config: AmbientSchedulerConfig;
|
|
75
|
-
private running = false;
|
|
76
|
-
|
|
77
|
-
constructor(deps: AmbientSchedulerDeps) {
|
|
78
|
-
this.scheduler = deps.scheduler;
|
|
79
|
-
this.connectorRegistry = deps.connectorRegistry;
|
|
80
|
-
this.triggerManager = deps.triggerManager;
|
|
81
|
-
this.briefingGenerator = deps.briefingGenerator;
|
|
82
|
-
this.emailIntelligence = deps.emailIntelligence;
|
|
83
|
-
this.calendarIntelligence = deps.calendarIntelligence;
|
|
84
|
-
this.notificationOrchestrator = deps.notificationOrchestrator;
|
|
85
|
-
this.deliveryChannel = deps.deliveryChannel;
|
|
86
|
-
this.userId = deps.userId;
|
|
87
|
-
this.config = { ...DEFAULT_AMBIENT_SCHEDULER_CONFIG, ...deps.config };
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
/** Start all scheduled cron jobs. */
|
|
91
|
-
start(): void {
|
|
92
|
-
if (!this.config.enabled || this.running) return;
|
|
93
|
-
|
|
94
|
-
this.scheduler.schedule(JOB_IDS.emailPoll, this.config.emailPollCron, () => {
|
|
95
|
-
void this.triggerManager.pollAll();
|
|
96
|
-
});
|
|
97
|
-
|
|
98
|
-
this.scheduler.schedule(JOB_IDS.calendarPoll, this.config.calendarPollCron, () => {
|
|
99
|
-
void this.pollCalendar();
|
|
100
|
-
});
|
|
101
|
-
|
|
102
|
-
this.scheduler.schedule(JOB_IDS.morningBriefing, this.config.morningCron, () => {
|
|
103
|
-
void this.generateAndDeliverBriefing('morning');
|
|
104
|
-
});
|
|
105
|
-
|
|
106
|
-
this.scheduler.schedule(JOB_IDS.eveningSummary, this.config.eveningCron, () => {
|
|
107
|
-
void this.generateAndDeliverBriefing('evening');
|
|
108
|
-
});
|
|
109
|
-
|
|
110
|
-
this.scheduler.schedule(JOB_IDS.notificationPoll, this.config.notificationPollCron, () => {
|
|
111
|
-
void this.pollAndNotify();
|
|
112
|
-
});
|
|
113
|
-
|
|
114
|
-
this.running = true;
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
/** Stop all scheduled cron jobs. */
|
|
118
|
-
stop(): void {
|
|
119
|
-
this.scheduler.stop(JOB_IDS.emailPoll);
|
|
120
|
-
this.scheduler.stop(JOB_IDS.calendarPoll);
|
|
121
|
-
this.scheduler.stop(JOB_IDS.morningBriefing);
|
|
122
|
-
this.scheduler.stop(JOB_IDS.eveningSummary);
|
|
123
|
-
this.scheduler.stop(JOB_IDS.notificationPoll);
|
|
124
|
-
this.running = false;
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
/** Whether the scheduler is currently running. */
|
|
128
|
-
isRunning(): boolean {
|
|
129
|
-
return this.running;
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
/** Get the current configuration. */
|
|
133
|
-
getConfig(): AmbientSchedulerConfig {
|
|
134
|
-
return { ...this.config };
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
/** Generate and deliver a briefing for the specified time of day. */
|
|
138
|
-
async generateAndDeliverBriefing(time: 'morning' | 'evening'): Promise<void> {
|
|
139
|
-
const calendarEvents = await this.fetchCalendarEvents(time);
|
|
140
|
-
const emailNotifications = await this.fetchEmailSummary();
|
|
141
|
-
|
|
142
|
-
const briefing = this.briefingGenerator.generateBriefing(
|
|
143
|
-
this.userId,
|
|
144
|
-
time,
|
|
145
|
-
{
|
|
146
|
-
calendarEvents,
|
|
147
|
-
notifications: emailNotifications,
|
|
148
|
-
},
|
|
149
|
-
);
|
|
150
|
-
|
|
151
|
-
const formatted = formatBriefingAsText(briefing);
|
|
152
|
-
await this.deliveryChannel(formatted);
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
private async pollAndNotify(): Promise<void> {
|
|
156
|
-
if (!this.notificationOrchestrator) return;
|
|
157
|
-
|
|
158
|
-
const events = await this.triggerManager.pollAll();
|
|
159
|
-
if (events.length > 0) {
|
|
160
|
-
this.notificationOrchestrator.processTriggerEvents(events);
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
// Check for upcoming calendar events
|
|
164
|
-
if (this.calendarIntelligence) {
|
|
165
|
-
try {
|
|
166
|
-
const today = new Date().toISOString().split('T')[0]!;
|
|
167
|
-
const result = await this.calendarIntelligence.analyzeDay(today);
|
|
168
|
-
const now = Date.now();
|
|
169
|
-
const alertWindowMs = this.config.calendarAlertMinutes * 60_000;
|
|
170
|
-
|
|
171
|
-
const upcomingEvents = (result.events ?? [])
|
|
172
|
-
.map((e) => ({
|
|
173
|
-
title: e.title,
|
|
174
|
-
startTime: this.parseTimeToTimestamp(e.time),
|
|
175
|
-
}))
|
|
176
|
-
.filter((e) => {
|
|
177
|
-
const timeUntil = e.startTime - now;
|
|
178
|
-
return timeUntil > 0 && timeUntil <= alertWindowMs;
|
|
179
|
-
});
|
|
180
|
-
|
|
181
|
-
if (upcomingEvents.length > 0) {
|
|
182
|
-
this.notificationOrchestrator.processCalendarCheck(upcomingEvents, now);
|
|
183
|
-
}
|
|
184
|
-
} catch {
|
|
185
|
-
// Calendar fetch errors are silently ignored
|
|
186
|
-
}
|
|
187
|
-
}
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
private parseTimeToTimestamp(time: string): number {
|
|
191
|
-
const [hours, minutes] = time.split(':').map(Number);
|
|
192
|
-
const d = new Date();
|
|
193
|
-
d.setHours(hours ?? 0, minutes ?? 0, 0, 0);
|
|
194
|
-
return d.getTime();
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
private async pollCalendar(): Promise<void> {
|
|
198
|
-
if (!this.calendarIntelligence) return;
|
|
199
|
-
|
|
200
|
-
const today = new Date().toISOString().split('T')[0]!;
|
|
201
|
-
await this.calendarIntelligence.analyzeDay(today);
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
private async fetchCalendarEvents(
|
|
205
|
-
time: 'morning' | 'evening',
|
|
206
|
-
): Promise<Array<{ title: string; time: string }>> {
|
|
207
|
-
if (!this.calendarIntelligence) return [];
|
|
208
|
-
|
|
209
|
-
const targetDate = time === 'evening'
|
|
210
|
-
? new Date(Date.now() + 86_400_000).toISOString().split('T')[0]!
|
|
211
|
-
: new Date().toISOString().split('T')[0]!;
|
|
212
|
-
|
|
213
|
-
try {
|
|
214
|
-
const result = await this.calendarIntelligence.analyzeDay(targetDate);
|
|
215
|
-
return result.events ?? [];
|
|
216
|
-
} catch {
|
|
217
|
-
return [];
|
|
218
|
-
}
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
private async fetchEmailSummary(): Promise<QuietNotification[]> {
|
|
222
|
-
if (!this.emailIntelligence?.triage) return [];
|
|
223
|
-
|
|
224
|
-
try {
|
|
225
|
-
const result = await this.emailIntelligence.triage.getTriageSummary({ maxResults: 10 });
|
|
226
|
-
return (result.items ?? []).map((item, i) => ({
|
|
227
|
-
id: `email-${i}`,
|
|
228
|
-
priority: item.priority === 'urgent' ? 'alert' as const : 'nudge' as const,
|
|
229
|
-
message: item.subject,
|
|
230
|
-
createdAt: Date.now(),
|
|
231
|
-
dismissed: false,
|
|
232
|
-
source: 'email',
|
|
233
|
-
}));
|
|
234
|
-
} catch {
|
|
235
|
-
return [];
|
|
236
|
-
}
|
|
237
|
-
}
|
|
238
|
-
}
|
package/src/types.ts
DELETED
|
@@ -1,85 +0,0 @@
|
|
|
1
|
-
/** Types of patterns the ambient engine can detect. */
|
|
2
|
-
export type AmbientPatternType = 'schedule' | 'preference' | 'trigger' | 'correlation';
|
|
3
|
-
|
|
4
|
-
/** Notification priority levels for quiet notifications. */
|
|
5
|
-
export type NotificationPriority = 'whisper' | 'nudge' | 'alert';
|
|
6
|
-
|
|
7
|
-
/** A detected ambient pattern from user behavior. */
|
|
8
|
-
export interface AmbientPattern {
|
|
9
|
-
id: string;
|
|
10
|
-
/** Pattern type. */
|
|
11
|
-
type: AmbientPatternType;
|
|
12
|
-
/** Human-readable description. */
|
|
13
|
-
description: string;
|
|
14
|
-
/** Confidence score (0-1). */
|
|
15
|
-
confidence: number;
|
|
16
|
-
/** Evidence supporting this pattern. */
|
|
17
|
-
evidence: string[];
|
|
18
|
-
/** When the pattern was first detected. */
|
|
19
|
-
detectedAt: number;
|
|
20
|
-
/** When the pattern was last confirmed. */
|
|
21
|
-
lastConfirmedAt: number;
|
|
22
|
-
/** Number of times this pattern has been observed. */
|
|
23
|
-
occurrences: number;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
/** An anticipated user need based on detected patterns. */
|
|
27
|
-
export interface Anticipation {
|
|
28
|
-
id: string;
|
|
29
|
-
/** What the user might need. */
|
|
30
|
-
description: string;
|
|
31
|
-
/** When this need is expected. */
|
|
32
|
-
expectedAt: number;
|
|
33
|
-
/** Confidence that this anticipation is correct (0-1). */
|
|
34
|
-
confidence: number;
|
|
35
|
-
/** Patterns that led to this anticipation. */
|
|
36
|
-
sourcePatterns: string[];
|
|
37
|
-
/** Suggested action to fulfill the need. */
|
|
38
|
-
suggestedAction?: string;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
/** A quiet notification queued for the user. */
|
|
42
|
-
export interface QuietNotification {
|
|
43
|
-
id: string;
|
|
44
|
-
/** Priority level. */
|
|
45
|
-
priority: NotificationPriority;
|
|
46
|
-
/** Notification message. */
|
|
47
|
-
message: string;
|
|
48
|
-
/** Detailed content (optional). */
|
|
49
|
-
detail?: string;
|
|
50
|
-
/** When the notification was created. */
|
|
51
|
-
createdAt: number;
|
|
52
|
-
/** Whether the notification has been dismissed. */
|
|
53
|
-
dismissed: boolean;
|
|
54
|
-
/** Source of the notification. */
|
|
55
|
-
source: string;
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
/** Configuration for personalized briefings. */
|
|
59
|
-
export interface BriefingConfig {
|
|
60
|
-
/** Whether briefings are enabled. */
|
|
61
|
-
enabled: boolean;
|
|
62
|
-
/** Time of day for morning briefing (HH:MM). */
|
|
63
|
-
morningTime: string;
|
|
64
|
-
/** Time of day for evening summary (HH:MM). */
|
|
65
|
-
eveningTime: string;
|
|
66
|
-
/** Categories to include in briefings. */
|
|
67
|
-
categories: string[];
|
|
68
|
-
/** Maximum number of items per section. */
|
|
69
|
-
maxItemsPerSection: number;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
export const DEFAULT_BRIEFING_CONFIG: BriefingConfig = {
|
|
73
|
-
enabled: true,
|
|
74
|
-
morningTime: '08:00',
|
|
75
|
-
eveningTime: '20:00',
|
|
76
|
-
categories: ['calendar', 'tasks', 'weather', 'news', 'patterns'],
|
|
77
|
-
maxItemsPerSection: 5,
|
|
78
|
-
};
|
|
79
|
-
|
|
80
|
-
/** An observed event for the pattern engine. */
|
|
81
|
-
export interface ObservedEvent {
|
|
82
|
-
type: string;
|
|
83
|
-
timestamp: number;
|
|
84
|
-
data?: Record<string, unknown>;
|
|
85
|
-
}
|