@auxiora/ambient 1.0.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/LICENSE +191 -0
- package/dist/anticipation.d.ts +27 -0
- package/dist/anticipation.d.ts.map +1 -0
- package/dist/anticipation.js +128 -0
- package/dist/anticipation.js.map +1 -0
- package/dist/briefing.d.ts +43 -0
- package/dist/briefing.d.ts.map +1 -0
- package/dist/briefing.js +109 -0
- package/dist/briefing.js.map +1 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +8 -0
- package/dist/index.js.map +1 -0
- package/dist/notification.d.ts +29 -0
- package/dist/notification.d.ts.map +1 -0
- package/dist/notification.js +87 -0
- package/dist/notification.js.map +1 -0
- package/dist/orchestrator.d.ts +40 -0
- package/dist/orchestrator.d.ts.map +1 -0
- package/dist/orchestrator.js +130 -0
- package/dist/orchestrator.js.map +1 -0
- package/dist/pattern-engine.d.ts +27 -0
- package/dist/pattern-engine.d.ts.map +1 -0
- package/dist/pattern-engine.js +187 -0
- package/dist/pattern-engine.js.map +1 -0
- package/dist/scheduler.d.ts +88 -0
- package/dist/scheduler.d.ts.map +1 -0
- package/dist/scheduler.js +172 -0
- package/dist/scheduler.js.map +1 -0
- package/dist/types.d.ts +73 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +8 -0
- package/dist/types.js.map +1 -0
- package/package.json +29 -0
- package/src/anticipation.ts +141 -0
- package/src/briefing.ts +152 -0
- package/src/index.ts +26 -0
- package/src/notification.ts +101 -0
- package/src/orchestrator.ts +188 -0
- package/src/pattern-engine.ts +212 -0
- package/src/scheduler.ts +238 -0
- package/src/types.ts +85 -0
- package/tests/ambient.test.ts +363 -0
- package/tests/orchestrator.test.ts +343 -0
- package/tests/scheduler.test.ts +310 -0
- package/tests/wiring.test.ts +12 -0
- package/tsconfig.json +15 -0
- package/tsconfig.tsbuildinfo +1 -0
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
import { formatBriefingAsText } from './briefing.js';
|
|
2
|
+
export const DEFAULT_AMBIENT_SCHEDULER_CONFIG = {
|
|
3
|
+
morningCron: '0 7 * * *',
|
|
4
|
+
eveningCron: '0 18 * * *',
|
|
5
|
+
emailPollCron: '*/2 * * * *',
|
|
6
|
+
calendarPollCron: '*/5 * * * *',
|
|
7
|
+
notificationPollCron: '*/1 * * * *',
|
|
8
|
+
calendarAlertMinutes: 15,
|
|
9
|
+
enabled: true,
|
|
10
|
+
categories: ['calendar', 'email', 'tasks'],
|
|
11
|
+
};
|
|
12
|
+
const JOB_IDS = {
|
|
13
|
+
emailPoll: 'ambient:email-poll',
|
|
14
|
+
calendarPoll: 'ambient:calendar-poll',
|
|
15
|
+
morningBriefing: 'ambient:morning-briefing',
|
|
16
|
+
eveningSummary: 'ambient:evening-summary',
|
|
17
|
+
notificationPoll: 'ambient:notification-poll',
|
|
18
|
+
};
|
|
19
|
+
/**
|
|
20
|
+
* Schedules ambient polling and briefing generation using cron jobs.
|
|
21
|
+
*/
|
|
22
|
+
export class AmbientScheduler {
|
|
23
|
+
scheduler;
|
|
24
|
+
connectorRegistry;
|
|
25
|
+
triggerManager;
|
|
26
|
+
briefingGenerator;
|
|
27
|
+
emailIntelligence;
|
|
28
|
+
calendarIntelligence;
|
|
29
|
+
notificationOrchestrator;
|
|
30
|
+
deliveryChannel;
|
|
31
|
+
userId;
|
|
32
|
+
config;
|
|
33
|
+
running = false;
|
|
34
|
+
constructor(deps) {
|
|
35
|
+
this.scheduler = deps.scheduler;
|
|
36
|
+
this.connectorRegistry = deps.connectorRegistry;
|
|
37
|
+
this.triggerManager = deps.triggerManager;
|
|
38
|
+
this.briefingGenerator = deps.briefingGenerator;
|
|
39
|
+
this.emailIntelligence = deps.emailIntelligence;
|
|
40
|
+
this.calendarIntelligence = deps.calendarIntelligence;
|
|
41
|
+
this.notificationOrchestrator = deps.notificationOrchestrator;
|
|
42
|
+
this.deliveryChannel = deps.deliveryChannel;
|
|
43
|
+
this.userId = deps.userId;
|
|
44
|
+
this.config = { ...DEFAULT_AMBIENT_SCHEDULER_CONFIG, ...deps.config };
|
|
45
|
+
}
|
|
46
|
+
/** Start all scheduled cron jobs. */
|
|
47
|
+
start() {
|
|
48
|
+
if (!this.config.enabled || this.running)
|
|
49
|
+
return;
|
|
50
|
+
this.scheduler.schedule(JOB_IDS.emailPoll, this.config.emailPollCron, () => {
|
|
51
|
+
void this.triggerManager.pollAll();
|
|
52
|
+
});
|
|
53
|
+
this.scheduler.schedule(JOB_IDS.calendarPoll, this.config.calendarPollCron, () => {
|
|
54
|
+
void this.pollCalendar();
|
|
55
|
+
});
|
|
56
|
+
this.scheduler.schedule(JOB_IDS.morningBriefing, this.config.morningCron, () => {
|
|
57
|
+
void this.generateAndDeliverBriefing('morning');
|
|
58
|
+
});
|
|
59
|
+
this.scheduler.schedule(JOB_IDS.eveningSummary, this.config.eveningCron, () => {
|
|
60
|
+
void this.generateAndDeliverBriefing('evening');
|
|
61
|
+
});
|
|
62
|
+
this.scheduler.schedule(JOB_IDS.notificationPoll, this.config.notificationPollCron, () => {
|
|
63
|
+
void this.pollAndNotify();
|
|
64
|
+
});
|
|
65
|
+
this.running = true;
|
|
66
|
+
}
|
|
67
|
+
/** Stop all scheduled cron jobs. */
|
|
68
|
+
stop() {
|
|
69
|
+
this.scheduler.stop(JOB_IDS.emailPoll);
|
|
70
|
+
this.scheduler.stop(JOB_IDS.calendarPoll);
|
|
71
|
+
this.scheduler.stop(JOB_IDS.morningBriefing);
|
|
72
|
+
this.scheduler.stop(JOB_IDS.eveningSummary);
|
|
73
|
+
this.scheduler.stop(JOB_IDS.notificationPoll);
|
|
74
|
+
this.running = false;
|
|
75
|
+
}
|
|
76
|
+
/** Whether the scheduler is currently running. */
|
|
77
|
+
isRunning() {
|
|
78
|
+
return this.running;
|
|
79
|
+
}
|
|
80
|
+
/** Get the current configuration. */
|
|
81
|
+
getConfig() {
|
|
82
|
+
return { ...this.config };
|
|
83
|
+
}
|
|
84
|
+
/** Generate and deliver a briefing for the specified time of day. */
|
|
85
|
+
async generateAndDeliverBriefing(time) {
|
|
86
|
+
const calendarEvents = await this.fetchCalendarEvents(time);
|
|
87
|
+
const emailNotifications = await this.fetchEmailSummary();
|
|
88
|
+
const briefing = this.briefingGenerator.generateBriefing(this.userId, time, {
|
|
89
|
+
calendarEvents,
|
|
90
|
+
notifications: emailNotifications,
|
|
91
|
+
});
|
|
92
|
+
const formatted = formatBriefingAsText(briefing);
|
|
93
|
+
await this.deliveryChannel(formatted);
|
|
94
|
+
}
|
|
95
|
+
async pollAndNotify() {
|
|
96
|
+
if (!this.notificationOrchestrator)
|
|
97
|
+
return;
|
|
98
|
+
const events = await this.triggerManager.pollAll();
|
|
99
|
+
if (events.length > 0) {
|
|
100
|
+
this.notificationOrchestrator.processTriggerEvents(events);
|
|
101
|
+
}
|
|
102
|
+
// Check for upcoming calendar events
|
|
103
|
+
if (this.calendarIntelligence) {
|
|
104
|
+
try {
|
|
105
|
+
const today = new Date().toISOString().split('T')[0];
|
|
106
|
+
const result = await this.calendarIntelligence.analyzeDay(today);
|
|
107
|
+
const now = Date.now();
|
|
108
|
+
const alertWindowMs = this.config.calendarAlertMinutes * 60_000;
|
|
109
|
+
const upcomingEvents = (result.events ?? [])
|
|
110
|
+
.map((e) => ({
|
|
111
|
+
title: e.title,
|
|
112
|
+
startTime: this.parseTimeToTimestamp(e.time),
|
|
113
|
+
}))
|
|
114
|
+
.filter((e) => {
|
|
115
|
+
const timeUntil = e.startTime - now;
|
|
116
|
+
return timeUntil > 0 && timeUntil <= alertWindowMs;
|
|
117
|
+
});
|
|
118
|
+
if (upcomingEvents.length > 0) {
|
|
119
|
+
this.notificationOrchestrator.processCalendarCheck(upcomingEvents, now);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
catch {
|
|
123
|
+
// Calendar fetch errors are silently ignored
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
parseTimeToTimestamp(time) {
|
|
128
|
+
const [hours, minutes] = time.split(':').map(Number);
|
|
129
|
+
const d = new Date();
|
|
130
|
+
d.setHours(hours ?? 0, minutes ?? 0, 0, 0);
|
|
131
|
+
return d.getTime();
|
|
132
|
+
}
|
|
133
|
+
async pollCalendar() {
|
|
134
|
+
if (!this.calendarIntelligence)
|
|
135
|
+
return;
|
|
136
|
+
const today = new Date().toISOString().split('T')[0];
|
|
137
|
+
await this.calendarIntelligence.analyzeDay(today);
|
|
138
|
+
}
|
|
139
|
+
async fetchCalendarEvents(time) {
|
|
140
|
+
if (!this.calendarIntelligence)
|
|
141
|
+
return [];
|
|
142
|
+
const targetDate = time === 'evening'
|
|
143
|
+
? new Date(Date.now() + 86_400_000).toISOString().split('T')[0]
|
|
144
|
+
: new Date().toISOString().split('T')[0];
|
|
145
|
+
try {
|
|
146
|
+
const result = await this.calendarIntelligence.analyzeDay(targetDate);
|
|
147
|
+
return result.events ?? [];
|
|
148
|
+
}
|
|
149
|
+
catch {
|
|
150
|
+
return [];
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
async fetchEmailSummary() {
|
|
154
|
+
if (!this.emailIntelligence?.triage)
|
|
155
|
+
return [];
|
|
156
|
+
try {
|
|
157
|
+
const result = await this.emailIntelligence.triage.getTriageSummary({ maxResults: 10 });
|
|
158
|
+
return (result.items ?? []).map((item, i) => ({
|
|
159
|
+
id: `email-${i}`,
|
|
160
|
+
priority: item.priority === 'urgent' ? 'alert' : 'nudge',
|
|
161
|
+
message: item.subject,
|
|
162
|
+
createdAt: Date.now(),
|
|
163
|
+
dismissed: false,
|
|
164
|
+
source: 'email',
|
|
165
|
+
}));
|
|
166
|
+
}
|
|
167
|
+
catch {
|
|
168
|
+
return [];
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
//# sourceMappingURL=scheduler.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scheduler.js","sourceRoot":"","sources":["../src/scheduler.ts"],"names":[],"mappings":"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"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/** Types of patterns the ambient engine can detect. */
|
|
2
|
+
export type AmbientPatternType = 'schedule' | 'preference' | 'trigger' | 'correlation';
|
|
3
|
+
/** Notification priority levels for quiet notifications. */
|
|
4
|
+
export type NotificationPriority = 'whisper' | 'nudge' | 'alert';
|
|
5
|
+
/** A detected ambient pattern from user behavior. */
|
|
6
|
+
export interface AmbientPattern {
|
|
7
|
+
id: string;
|
|
8
|
+
/** Pattern type. */
|
|
9
|
+
type: AmbientPatternType;
|
|
10
|
+
/** Human-readable description. */
|
|
11
|
+
description: string;
|
|
12
|
+
/** Confidence score (0-1). */
|
|
13
|
+
confidence: number;
|
|
14
|
+
/** Evidence supporting this pattern. */
|
|
15
|
+
evidence: string[];
|
|
16
|
+
/** When the pattern was first detected. */
|
|
17
|
+
detectedAt: number;
|
|
18
|
+
/** When the pattern was last confirmed. */
|
|
19
|
+
lastConfirmedAt: number;
|
|
20
|
+
/** Number of times this pattern has been observed. */
|
|
21
|
+
occurrences: number;
|
|
22
|
+
}
|
|
23
|
+
/** An anticipated user need based on detected patterns. */
|
|
24
|
+
export interface Anticipation {
|
|
25
|
+
id: string;
|
|
26
|
+
/** What the user might need. */
|
|
27
|
+
description: string;
|
|
28
|
+
/** When this need is expected. */
|
|
29
|
+
expectedAt: number;
|
|
30
|
+
/** Confidence that this anticipation is correct (0-1). */
|
|
31
|
+
confidence: number;
|
|
32
|
+
/** Patterns that led to this anticipation. */
|
|
33
|
+
sourcePatterns: string[];
|
|
34
|
+
/** Suggested action to fulfill the need. */
|
|
35
|
+
suggestedAction?: string;
|
|
36
|
+
}
|
|
37
|
+
/** A quiet notification queued for the user. */
|
|
38
|
+
export interface QuietNotification {
|
|
39
|
+
id: string;
|
|
40
|
+
/** Priority level. */
|
|
41
|
+
priority: NotificationPriority;
|
|
42
|
+
/** Notification message. */
|
|
43
|
+
message: string;
|
|
44
|
+
/** Detailed content (optional). */
|
|
45
|
+
detail?: string;
|
|
46
|
+
/** When the notification was created. */
|
|
47
|
+
createdAt: number;
|
|
48
|
+
/** Whether the notification has been dismissed. */
|
|
49
|
+
dismissed: boolean;
|
|
50
|
+
/** Source of the notification. */
|
|
51
|
+
source: string;
|
|
52
|
+
}
|
|
53
|
+
/** Configuration for personalized briefings. */
|
|
54
|
+
export interface BriefingConfig {
|
|
55
|
+
/** Whether briefings are enabled. */
|
|
56
|
+
enabled: boolean;
|
|
57
|
+
/** Time of day for morning briefing (HH:MM). */
|
|
58
|
+
morningTime: string;
|
|
59
|
+
/** Time of day for evening summary (HH:MM). */
|
|
60
|
+
eveningTime: string;
|
|
61
|
+
/** Categories to include in briefings. */
|
|
62
|
+
categories: string[];
|
|
63
|
+
/** Maximum number of items per section. */
|
|
64
|
+
maxItemsPerSection: number;
|
|
65
|
+
}
|
|
66
|
+
export declare const DEFAULT_BRIEFING_CONFIG: BriefingConfig;
|
|
67
|
+
/** An observed event for the pattern engine. */
|
|
68
|
+
export interface ObservedEvent {
|
|
69
|
+
type: string;
|
|
70
|
+
timestamp: number;
|
|
71
|
+
data?: Record<string, unknown>;
|
|
72
|
+
}
|
|
73
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,uDAAuD;AACvD,MAAM,MAAM,kBAAkB,GAAG,UAAU,GAAG,YAAY,GAAG,SAAS,GAAG,aAAa,CAAC;AAEvF,4DAA4D;AAC5D,MAAM,MAAM,oBAAoB,GAAG,SAAS,GAAG,OAAO,GAAG,OAAO,CAAC;AAEjE,qDAAqD;AACrD,MAAM,WAAW,cAAc;IAC7B,EAAE,EAAE,MAAM,CAAC;IACX,oBAAoB;IACpB,IAAI,EAAE,kBAAkB,CAAC;IACzB,kCAAkC;IAClC,WAAW,EAAE,MAAM,CAAC;IACpB,8BAA8B;IAC9B,UAAU,EAAE,MAAM,CAAC;IACnB,wCAAwC;IACxC,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,2CAA2C;IAC3C,UAAU,EAAE,MAAM,CAAC;IACnB,2CAA2C;IAC3C,eAAe,EAAE,MAAM,CAAC;IACxB,sDAAsD;IACtD,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,2DAA2D;AAC3D,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,gCAAgC;IAChC,WAAW,EAAE,MAAM,CAAC;IACpB,kCAAkC;IAClC,UAAU,EAAE,MAAM,CAAC;IACnB,0DAA0D;IAC1D,UAAU,EAAE,MAAM,CAAC;IACnB,8CAA8C;IAC9C,cAAc,EAAE,MAAM,EAAE,CAAC;IACzB,4CAA4C;IAC5C,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED,gDAAgD;AAChD,MAAM,WAAW,iBAAiB;IAChC,EAAE,EAAE,MAAM,CAAC;IACX,sBAAsB;IACtB,QAAQ,EAAE,oBAAoB,CAAC;IAC/B,4BAA4B;IAC5B,OAAO,EAAE,MAAM,CAAC;IAChB,mCAAmC;IACnC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,yCAAyC;IACzC,SAAS,EAAE,MAAM,CAAC;IAClB,mDAAmD;IACnD,SAAS,EAAE,OAAO,CAAC;IACnB,kCAAkC;IAClC,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,gDAAgD;AAChD,MAAM,WAAW,cAAc;IAC7B,qCAAqC;IACrC,OAAO,EAAE,OAAO,CAAC;IACjB,gDAAgD;IAChD,WAAW,EAAE,MAAM,CAAC;IACpB,+CAA+C;IAC/C,WAAW,EAAE,MAAM,CAAC;IACpB,0CAA0C;IAC1C,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,2CAA2C;IAC3C,kBAAkB,EAAE,MAAM,CAAC;CAC5B;AAED,eAAO,MAAM,uBAAuB,EAAE,cAMrC,CAAC;AAEF,gDAAgD;AAChD,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAChC"}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAuEA,MAAM,CAAC,MAAM,uBAAuB,GAAmB;IACrD,OAAO,EAAE,IAAI;IACb,WAAW,EAAE,OAAO;IACpB,WAAW,EAAE,OAAO;IACpB,UAAU,EAAE,CAAC,UAAU,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE,UAAU,CAAC;IAChE,kBAAkB,EAAE,CAAC;CACtB,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@auxiora/ambient",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Ambient pattern detection, anticipation, briefings, and quiet notifications",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"import": "./dist/index.js"
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
"dependencies": {
|
|
15
|
+
"@auxiora/memory": "1.0.0",
|
|
16
|
+
"@auxiora/behaviors": "1.0.0",
|
|
17
|
+
"@auxiora/personality": "1.0.0",
|
|
18
|
+
"@auxiora/notification-hub": "1.0.0",
|
|
19
|
+
"@auxiora/connectors": "1.0.0"
|
|
20
|
+
},
|
|
21
|
+
"engines": {
|
|
22
|
+
"node": ">=22.0.0"
|
|
23
|
+
},
|
|
24
|
+
"scripts": {
|
|
25
|
+
"build": "tsc",
|
|
26
|
+
"clean": "rm -rf dist",
|
|
27
|
+
"typecheck": "tsc --noEmit"
|
|
28
|
+
}
|
|
29
|
+
}
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
import * as crypto from 'node:crypto';
|
|
2
|
+
import type { AmbientPattern, Anticipation } from './types.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Anticipation engine — predicts user needs based on detected patterns.
|
|
6
|
+
*/
|
|
7
|
+
export class AnticipationEngine {
|
|
8
|
+
private anticipations: Map<string, Anticipation> = new Map();
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Generate anticipations from detected patterns and current context.
|
|
12
|
+
* @param patterns - Currently detected patterns.
|
|
13
|
+
* @param context - Current context (time, active tasks, etc.).
|
|
14
|
+
*/
|
|
15
|
+
generateAnticipations(
|
|
16
|
+
patterns: AmbientPattern[],
|
|
17
|
+
context?: { currentTime?: number; activeTaskCount?: number }
|
|
18
|
+
): Anticipation[] {
|
|
19
|
+
const now = context?.currentTime ?? Date.now();
|
|
20
|
+
const generated: Anticipation[] = [];
|
|
21
|
+
|
|
22
|
+
for (const pattern of patterns) {
|
|
23
|
+
if (pattern.confidence < 0.4) continue;
|
|
24
|
+
|
|
25
|
+
switch (pattern.type) {
|
|
26
|
+
case 'schedule': {
|
|
27
|
+
const anticipation = this.anticipateSchedule(pattern, now);
|
|
28
|
+
if (anticipation) generated.push(anticipation);
|
|
29
|
+
break;
|
|
30
|
+
}
|
|
31
|
+
case 'preference': {
|
|
32
|
+
const anticipation = this.anticipatePreference(pattern, now);
|
|
33
|
+
if (anticipation) generated.push(anticipation);
|
|
34
|
+
break;
|
|
35
|
+
}
|
|
36
|
+
case 'correlation': {
|
|
37
|
+
const anticipation = this.anticipateCorrelation(pattern, now);
|
|
38
|
+
if (anticipation) generated.push(anticipation);
|
|
39
|
+
break;
|
|
40
|
+
}
|
|
41
|
+
case 'trigger': {
|
|
42
|
+
const anticipation = this.anticipateTrigger(pattern, now);
|
|
43
|
+
if (anticipation) generated.push(anticipation);
|
|
44
|
+
break;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Store generated anticipations
|
|
50
|
+
for (const a of generated) {
|
|
51
|
+
this.anticipations.set(a.id, a);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
return generated;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/** Get all current anticipations sorted by expected time. */
|
|
58
|
+
getAnticipations(): Anticipation[] {
|
|
59
|
+
return Array.from(this.anticipations.values())
|
|
60
|
+
.filter(a => a.expectedAt > Date.now())
|
|
61
|
+
.sort((a, b) => a.expectedAt - b.expectedAt);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/** Clear expired anticipations. */
|
|
65
|
+
prune(): number {
|
|
66
|
+
const now = Date.now();
|
|
67
|
+
let pruned = 0;
|
|
68
|
+
for (const [id, a] of this.anticipations) {
|
|
69
|
+
if (a.expectedAt < now) {
|
|
70
|
+
this.anticipations.delete(id);
|
|
71
|
+
pruned++;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
return pruned;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/** Reset all anticipations. */
|
|
78
|
+
reset(): void {
|
|
79
|
+
this.anticipations.clear();
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
private anticipateSchedule(pattern: AmbientPattern, now: number): Anticipation | null {
|
|
83
|
+
// Extract hour from description like '... around 14:00'
|
|
84
|
+
const match = pattern.description.match(/around (\d+):00/);
|
|
85
|
+
if (!match) return null;
|
|
86
|
+
|
|
87
|
+
const hour = parseInt(match[1], 10);
|
|
88
|
+
const nextOccurrence = new Date(now);
|
|
89
|
+
nextOccurrence.setHours(hour, 0, 0, 0);
|
|
90
|
+
if (nextOccurrence.getTime() <= now) {
|
|
91
|
+
nextOccurrence.setDate(nextOccurrence.getDate() + 1);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
return {
|
|
95
|
+
id: crypto.randomUUID(),
|
|
96
|
+
description: `Based on your pattern: ${pattern.description}`,
|
|
97
|
+
expectedAt: nextOccurrence.getTime(),
|
|
98
|
+
confidence: pattern.confidence * 0.8,
|
|
99
|
+
sourcePatterns: [pattern.id],
|
|
100
|
+
suggestedAction: `Prepare for upcoming "${pattern.description.split('"')[1]}" activity`,
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
private anticipatePreference(pattern: AmbientPattern, now: number): Anticipation | null {
|
|
105
|
+
// Extract interval from description like 'every 2.5 hours'
|
|
106
|
+
const match = pattern.description.match(/every ([\d.]+) hours/);
|
|
107
|
+
if (!match) return null;
|
|
108
|
+
|
|
109
|
+
const intervalHours = parseFloat(match[1]);
|
|
110
|
+
const expectedAt = now + intervalHours * 60 * 60 * 1000;
|
|
111
|
+
|
|
112
|
+
return {
|
|
113
|
+
id: crypto.randomUUID(),
|
|
114
|
+
description: `You typically do this every ${intervalHours} hours`,
|
|
115
|
+
expectedAt,
|
|
116
|
+
confidence: pattern.confidence * 0.7,
|
|
117
|
+
sourcePatterns: [pattern.id],
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
private anticipateCorrelation(pattern: AmbientPattern, now: number): Anticipation | null {
|
|
122
|
+
return {
|
|
123
|
+
id: crypto.randomUUID(),
|
|
124
|
+
description: `When this happens: ${pattern.description}`,
|
|
125
|
+
expectedAt: now + 5 * 60 * 1000, // 5 minutes from now
|
|
126
|
+
confidence: pattern.confidence * 0.6,
|
|
127
|
+
sourcePatterns: [pattern.id],
|
|
128
|
+
suggestedAction: `Proactively prepare the follow-up action`,
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
private anticipateTrigger(pattern: AmbientPattern, now: number): Anticipation | null {
|
|
133
|
+
return {
|
|
134
|
+
id: crypto.randomUUID(),
|
|
135
|
+
description: `Trigger detected: ${pattern.description}`,
|
|
136
|
+
expectedAt: now + 60 * 1000, // 1 minute
|
|
137
|
+
confidence: pattern.confidence * 0.9,
|
|
138
|
+
sourcePatterns: [pattern.id],
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
}
|
package/src/briefing.ts
ADDED
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
import type { AmbientPattern, Anticipation, BriefingConfig, QuietNotification } from './types.js';
|
|
2
|
+
import { DEFAULT_BRIEFING_CONFIG } from './types.js';
|
|
3
|
+
|
|
4
|
+
/** A compiled briefing for the user. */
|
|
5
|
+
export interface Briefing {
|
|
6
|
+
userId: string;
|
|
7
|
+
generatedAt: number;
|
|
8
|
+
timeOfDay: 'morning' | 'evening' | 'custom';
|
|
9
|
+
sections: BriefingSection[];
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/** A section of a briefing. */
|
|
13
|
+
export interface BriefingSection {
|
|
14
|
+
title: string;
|
|
15
|
+
items: string[];
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/** Data sources for briefing generation. */
|
|
19
|
+
export interface BriefingDataSources {
|
|
20
|
+
patterns?: AmbientPattern[];
|
|
21
|
+
anticipations?: Anticipation[];
|
|
22
|
+
notifications?: QuietNotification[];
|
|
23
|
+
calendarEvents?: Array<{ title: string; time: string }>;
|
|
24
|
+
tasks?: Array<{ title: string; status: string }>;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Generates personalized briefings/digests for users.
|
|
29
|
+
*/
|
|
30
|
+
export class BriefingGenerator {
|
|
31
|
+
private config: BriefingConfig;
|
|
32
|
+
|
|
33
|
+
constructor(config?: Partial<BriefingConfig>) {
|
|
34
|
+
this.config = { ...DEFAULT_BRIEFING_CONFIG, ...config };
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/** Generate a briefing for the given user at the given time. */
|
|
38
|
+
generateBriefing(
|
|
39
|
+
userId: string,
|
|
40
|
+
time: 'morning' | 'evening' | 'custom',
|
|
41
|
+
sources: BriefingDataSources
|
|
42
|
+
): Briefing {
|
|
43
|
+
const sections: BriefingSection[] = [];
|
|
44
|
+
|
|
45
|
+
// Notifications section
|
|
46
|
+
if (sources.notifications && sources.notifications.length > 0) {
|
|
47
|
+
const pending = sources.notifications.filter(n => !n.dismissed);
|
|
48
|
+
if (pending.length > 0) {
|
|
49
|
+
sections.push({
|
|
50
|
+
title: 'Notifications',
|
|
51
|
+
items: pending.slice(0, this.config.maxItemsPerSection).map(
|
|
52
|
+
n => `[${n.priority}] ${n.message}`
|
|
53
|
+
),
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// Calendar section
|
|
59
|
+
if (this.config.categories.includes('calendar') && sources.calendarEvents) {
|
|
60
|
+
const events = sources.calendarEvents.slice(0, this.config.maxItemsPerSection);
|
|
61
|
+
if (events.length > 0) {
|
|
62
|
+
sections.push({
|
|
63
|
+
title: time === 'morning' ? 'Today\'s Schedule' : 'Tomorrow\'s Schedule',
|
|
64
|
+
items: events.map(e => `${e.time} - ${e.title}`),
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// Tasks section
|
|
70
|
+
if (this.config.categories.includes('tasks') && sources.tasks) {
|
|
71
|
+
const activeTasks = sources.tasks
|
|
72
|
+
.filter(t => t.status !== 'completed')
|
|
73
|
+
.slice(0, this.config.maxItemsPerSection);
|
|
74
|
+
if (activeTasks.length > 0) {
|
|
75
|
+
sections.push({
|
|
76
|
+
title: 'Active Tasks',
|
|
77
|
+
items: activeTasks.map(t => `${t.title} (${t.status})`),
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Patterns section
|
|
83
|
+
if (this.config.categories.includes('patterns') && sources.patterns) {
|
|
84
|
+
const topPatterns = sources.patterns
|
|
85
|
+
.filter(p => p.confidence >= 0.5)
|
|
86
|
+
.slice(0, this.config.maxItemsPerSection);
|
|
87
|
+
if (topPatterns.length > 0) {
|
|
88
|
+
sections.push({
|
|
89
|
+
title: 'Observed Patterns',
|
|
90
|
+
items: topPatterns.map(p => `${p.description} (${Math.round(p.confidence * 100)}% confidence)`),
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// Anticipations section
|
|
96
|
+
if (sources.anticipations && sources.anticipations.length > 0) {
|
|
97
|
+
const upcoming = sources.anticipations
|
|
98
|
+
.filter(a => a.expectedAt > Date.now())
|
|
99
|
+
.slice(0, this.config.maxItemsPerSection);
|
|
100
|
+
if (upcoming.length > 0) {
|
|
101
|
+
sections.push({
|
|
102
|
+
title: 'Upcoming',
|
|
103
|
+
items: upcoming.map(a => {
|
|
104
|
+
const timeStr = new Date(a.expectedAt).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' });
|
|
105
|
+
return `~${timeStr}: ${a.description}`;
|
|
106
|
+
}),
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
return {
|
|
112
|
+
userId,
|
|
113
|
+
generatedAt: Date.now(),
|
|
114
|
+
timeOfDay: time,
|
|
115
|
+
sections,
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/** Check if it's time for a briefing. */
|
|
120
|
+
isBriefingTime(time: 'morning' | 'evening'): boolean {
|
|
121
|
+
if (!this.config.enabled) return false;
|
|
122
|
+
const now = new Date();
|
|
123
|
+
const currentTime = `${String(now.getHours()).padStart(2, '0')}:${String(now.getMinutes()).padStart(2, '0')}`;
|
|
124
|
+
const target = time === 'morning' ? this.config.morningTime : this.config.eveningTime;
|
|
125
|
+
return currentTime === target;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/** Get config. */
|
|
129
|
+
getConfig(): BriefingConfig {
|
|
130
|
+
return { ...this.config };
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/** Format a briefing as human-readable text. */
|
|
135
|
+
export function formatBriefingAsText(briefing: Briefing): string {
|
|
136
|
+
const greeting = briefing.timeOfDay === 'morning'
|
|
137
|
+
? 'Good morning! Here\'s your day:'
|
|
138
|
+
: briefing.timeOfDay === 'evening'
|
|
139
|
+
? 'Here\'s your evening summary:'
|
|
140
|
+
: 'Here\'s your briefing:';
|
|
141
|
+
|
|
142
|
+
if (briefing.sections.length === 0) {
|
|
143
|
+
return `${greeting}\n\nNo updates right now.`;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
const sections = briefing.sections.map(s => {
|
|
147
|
+
const items = s.items.map(item => ` ${item}`).join('\n');
|
|
148
|
+
return `${s.title}\n${items}`;
|
|
149
|
+
}).join('\n\n');
|
|
150
|
+
|
|
151
|
+
return `${greeting}\n\n${sections}`;
|
|
152
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
export type {
|
|
2
|
+
AmbientPattern,
|
|
3
|
+
AmbientPatternType,
|
|
4
|
+
Anticipation,
|
|
5
|
+
QuietNotification,
|
|
6
|
+
NotificationPriority,
|
|
7
|
+
BriefingConfig,
|
|
8
|
+
ObservedEvent,
|
|
9
|
+
} from './types.js';
|
|
10
|
+
export { DEFAULT_BRIEFING_CONFIG } from './types.js';
|
|
11
|
+
export { AmbientPatternEngine } from './pattern-engine.js';
|
|
12
|
+
export { AnticipationEngine } from './anticipation.js';
|
|
13
|
+
export { BriefingGenerator, formatBriefingAsText, type Briefing, type BriefingSection, type BriefingDataSources } from './briefing.js';
|
|
14
|
+
export { QuietNotificationManager } from './notification.js';
|
|
15
|
+
export {
|
|
16
|
+
AmbientScheduler,
|
|
17
|
+
DEFAULT_AMBIENT_SCHEDULER_CONFIG,
|
|
18
|
+
type AmbientSchedulerConfig,
|
|
19
|
+
type AmbientSchedulerDeps,
|
|
20
|
+
} from './scheduler.js';
|
|
21
|
+
export {
|
|
22
|
+
NotificationOrchestrator,
|
|
23
|
+
type OrchestratorConfig,
|
|
24
|
+
type OrchestratorNotification,
|
|
25
|
+
type DeliveryChannelFn,
|
|
26
|
+
} from './orchestrator.js';
|