@madh-io/alfred-ai 0.7.1 → 0.7.2
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/bundle/index.js +138 -14
- package/package.json +1 -1
package/bundle/index.js
CHANGED
|
@@ -2035,6 +2035,29 @@ For complex tasks, work through multiple steps:
|
|
|
2035
2035
|
- Documents: ${homeDir}/Documents
|
|
2036
2036
|
- Desktop: ${homeDir}/Desktop
|
|
2037
2037
|
- Downloads: ${homeDir}/Downloads`;
|
|
2038
|
+
const serverTimezone = Intl.DateTimeFormat().resolvedOptions().timeZone;
|
|
2039
|
+
const effectiveTimezone = userProfile?.timezone || serverTimezone;
|
|
2040
|
+
const now = /* @__PURE__ */ new Date();
|
|
2041
|
+
const timeStr = now.toLocaleTimeString("en-GB", {
|
|
2042
|
+
timeZone: effectiveTimezone,
|
|
2043
|
+
hour: "2-digit",
|
|
2044
|
+
minute: "2-digit"
|
|
2045
|
+
});
|
|
2046
|
+
const dateStr = now.toLocaleDateString("en-CA", { timeZone: effectiveTimezone });
|
|
2047
|
+
const dayStr = now.toLocaleDateString("en-US", { timeZone: effectiveTimezone, weekday: "long" });
|
|
2048
|
+
prompt += `
|
|
2049
|
+
|
|
2050
|
+
## Current date & time`;
|
|
2051
|
+
prompt += `
|
|
2052
|
+
- Timezone: ${effectiveTimezone}`;
|
|
2053
|
+
prompt += `
|
|
2054
|
+
- Date: ${dateStr} (${dayStr})`;
|
|
2055
|
+
prompt += `
|
|
2056
|
+
- Time: ${timeStr}`;
|
|
2057
|
+
if (userProfile?.timezone && userProfile.timezone !== serverTimezone) {
|
|
2058
|
+
prompt += `
|
|
2059
|
+
- Server timezone: ${serverTimezone}`;
|
|
2060
|
+
}
|
|
2038
2061
|
if (skills && skills.length > 0) {
|
|
2039
2062
|
prompt += "\n\n## Available tools\n";
|
|
2040
2063
|
for (const s of skills) {
|
|
@@ -2049,13 +2072,8 @@ For complex tasks, work through multiple steps:
|
|
|
2049
2072
|
- Name: ${userProfile.displayName}`;
|
|
2050
2073
|
}
|
|
2051
2074
|
if (userProfile.timezone) {
|
|
2052
|
-
const now = (/* @__PURE__ */ new Date()).toLocaleTimeString("en-GB", {
|
|
2053
|
-
timeZone: userProfile.timezone,
|
|
2054
|
-
hour: "2-digit",
|
|
2055
|
-
minute: "2-digit"
|
|
2056
|
-
});
|
|
2057
2075
|
prompt += `
|
|
2058
|
-
- Timezone: ${userProfile.timezone}
|
|
2076
|
+
- Timezone: ${userProfile.timezone}`;
|
|
2059
2077
|
}
|
|
2060
2078
|
if (userProfile.language) {
|
|
2061
2079
|
prompt += `
|
|
@@ -3158,9 +3176,9 @@ var init_reminder = __esm({
|
|
|
3158
3176
|
reminderRepo;
|
|
3159
3177
|
metadata = {
|
|
3160
3178
|
name: "reminder",
|
|
3161
|
-
description: 'Set timed reminders that notify the user later. Use when the user says "remind me", "erinnere mich", or asks to be notified about something at a specific time.',
|
|
3179
|
+
description: 'Set timed reminders that notify the user later. Use when the user says "remind me", "erinnere mich", or asks to be notified about something at a specific time. Prefer triggerAt (absolute time like "14:30" or "2026-02-28 09:00") over delayMinutes \u2014 it is more precise and avoids calculation errors.',
|
|
3162
3180
|
riskLevel: "write",
|
|
3163
|
-
version: "
|
|
3181
|
+
version: "3.0.0",
|
|
3164
3182
|
inputSchema: {
|
|
3165
3183
|
type: "object",
|
|
3166
3184
|
properties: {
|
|
@@ -3173,9 +3191,13 @@ var init_reminder = __esm({
|
|
|
3173
3191
|
type: "string",
|
|
3174
3192
|
description: "The reminder message (required for set)"
|
|
3175
3193
|
},
|
|
3194
|
+
triggerAt: {
|
|
3195
|
+
type: "string",
|
|
3196
|
+
description: 'Absolute time for the reminder. Accepts "HH:MM" for today or "YYYY-MM-DD HH:MM" for a specific date. Preferred over delayMinutes for time-specific reminders.'
|
|
3197
|
+
},
|
|
3176
3198
|
delayMinutes: {
|
|
3177
3199
|
type: "number",
|
|
3178
|
-
description: "Minutes until the reminder triggers
|
|
3200
|
+
description: "Minutes until the reminder triggers. Use triggerAt instead when the user specifies a clock time."
|
|
3179
3201
|
},
|
|
3180
3202
|
reminderId: {
|
|
3181
3203
|
type: "string",
|
|
@@ -3207,6 +3229,7 @@ var init_reminder = __esm({
|
|
|
3207
3229
|
}
|
|
3208
3230
|
setReminder(input2, context) {
|
|
3209
3231
|
const message = input2.message;
|
|
3232
|
+
const triggerAtStr = input2.triggerAt;
|
|
3210
3233
|
const delayMinutes = input2.delayMinutes;
|
|
3211
3234
|
if (!message || typeof message !== "string") {
|
|
3212
3235
|
return {
|
|
@@ -3214,20 +3237,119 @@ var init_reminder = __esm({
|
|
|
3214
3237
|
error: 'Missing required field "message" for set action'
|
|
3215
3238
|
};
|
|
3216
3239
|
}
|
|
3217
|
-
|
|
3240
|
+
let triggerAt;
|
|
3241
|
+
if (triggerAtStr && typeof triggerAtStr === "string") {
|
|
3242
|
+
const parsed = this.parseTriggerAt(triggerAtStr, context.timezone);
|
|
3243
|
+
if (!parsed) {
|
|
3244
|
+
return {
|
|
3245
|
+
success: false,
|
|
3246
|
+
error: `Could not parse triggerAt "${triggerAtStr}". Use "HH:MM" for today or "YYYY-MM-DD HH:MM" for a specific date.`
|
|
3247
|
+
};
|
|
3248
|
+
}
|
|
3249
|
+
if (parsed.getTime() <= Date.now()) {
|
|
3250
|
+
return {
|
|
3251
|
+
success: false,
|
|
3252
|
+
error: `The time "${triggerAtStr}" is in the past. Please specify a future time.`
|
|
3253
|
+
};
|
|
3254
|
+
}
|
|
3255
|
+
triggerAt = parsed;
|
|
3256
|
+
} else if (delayMinutes !== void 0 && typeof delayMinutes === "number" && delayMinutes > 0) {
|
|
3257
|
+
triggerAt = new Date(Date.now() + delayMinutes * 60 * 1e3);
|
|
3258
|
+
} else {
|
|
3218
3259
|
return {
|
|
3219
3260
|
success: false,
|
|
3220
|
-
error: '
|
|
3261
|
+
error: 'Provide either "triggerAt" (e.g. "14:30") or "delayMinutes" (positive number) for set action.'
|
|
3221
3262
|
};
|
|
3222
3263
|
}
|
|
3223
|
-
const triggerAt = new Date(Date.now() + delayMinutes * 60 * 1e3);
|
|
3224
3264
|
const entry = this.reminderRepo.create(context.userId, context.platform, context.chatId, message, triggerAt);
|
|
3265
|
+
const delayMs = triggerAt.getTime() - Date.now();
|
|
3266
|
+
const mins = Math.round(delayMs / 6e4);
|
|
3267
|
+
const timeLabel = triggerAt.toLocaleTimeString("en-GB", {
|
|
3268
|
+
hour: "2-digit",
|
|
3269
|
+
minute: "2-digit",
|
|
3270
|
+
...context.timezone ? { timeZone: context.timezone } : {}
|
|
3271
|
+
});
|
|
3225
3272
|
return {
|
|
3226
3273
|
success: true,
|
|
3227
3274
|
data: { reminderId: entry.id, message, triggerAt: entry.triggerAt },
|
|
3228
|
-
display: `Reminder set (${entry.id}): "${message}" in ${
|
|
3275
|
+
display: `Reminder set (${entry.id}): "${message}" at ${timeLabel} (in ${mins} min)`
|
|
3229
3276
|
};
|
|
3230
3277
|
}
|
|
3278
|
+
/**
|
|
3279
|
+
* Parse a trigger time string into a Date.
|
|
3280
|
+
*
|
|
3281
|
+
* Supported formats:
|
|
3282
|
+
* - "HH:MM" → today at that time in the given timezone
|
|
3283
|
+
* - "YYYY-MM-DD HH:MM" → specific date+time
|
|
3284
|
+
*/
|
|
3285
|
+
parseTriggerAt(str, timezone) {
|
|
3286
|
+
const trimmed = str.trim();
|
|
3287
|
+
const timeOnly = /^(\d{1,2}):(\d{2})$/.exec(trimmed);
|
|
3288
|
+
if (timeOnly) {
|
|
3289
|
+
const hours = parseInt(timeOnly[1], 10);
|
|
3290
|
+
const minutes = parseInt(timeOnly[2], 10);
|
|
3291
|
+
if (hours > 23 || minutes > 59)
|
|
3292
|
+
return void 0;
|
|
3293
|
+
return this.buildDateInTimezone(hours, minutes, void 0, timezone);
|
|
3294
|
+
}
|
|
3295
|
+
const dateTime = /^(\d{4})-(\d{2})-(\d{2})\s+(\d{1,2}):(\d{2})$/.exec(trimmed);
|
|
3296
|
+
if (dateTime) {
|
|
3297
|
+
const year = parseInt(dateTime[1], 10);
|
|
3298
|
+
const month = parseInt(dateTime[2], 10) - 1;
|
|
3299
|
+
const day = parseInt(dateTime[3], 10);
|
|
3300
|
+
const hours = parseInt(dateTime[4], 10);
|
|
3301
|
+
const minutes = parseInt(dateTime[5], 10);
|
|
3302
|
+
if (hours > 23 || minutes > 59 || month > 11 || day > 31)
|
|
3303
|
+
return void 0;
|
|
3304
|
+
return this.buildDateInTimezone(hours, minutes, { year, month, day }, timezone);
|
|
3305
|
+
}
|
|
3306
|
+
return void 0;
|
|
3307
|
+
}
|
|
3308
|
+
/**
|
|
3309
|
+
* Build a Date object for a given time in the user's timezone.
|
|
3310
|
+
* Uses iterative offset correction to handle DST edge cases.
|
|
3311
|
+
*/
|
|
3312
|
+
buildDateInTimezone(hours, minutes, date, timezone) {
|
|
3313
|
+
if (!timezone) {
|
|
3314
|
+
const d = date ? new Date(date.year, date.month, date.day, hours, minutes, 0, 0) : /* @__PURE__ */ new Date();
|
|
3315
|
+
if (!date) {
|
|
3316
|
+
d.setHours(hours, minutes, 0, 0);
|
|
3317
|
+
}
|
|
3318
|
+
return d;
|
|
3319
|
+
}
|
|
3320
|
+
const now = /* @__PURE__ */ new Date();
|
|
3321
|
+
const refDate = date ? new Date(Date.UTC(date.year, date.month, date.day, hours, minutes, 0)) : new Date(Date.UTC(now.getFullYear(), now.getMonth(), now.getDate(), hours, minutes, 0));
|
|
3322
|
+
const formatter = new Intl.DateTimeFormat("en-CA", {
|
|
3323
|
+
timeZone: timezone,
|
|
3324
|
+
year: "numeric",
|
|
3325
|
+
month: "2-digit",
|
|
3326
|
+
day: "2-digit",
|
|
3327
|
+
hour: "2-digit",
|
|
3328
|
+
minute: "2-digit",
|
|
3329
|
+
second: "2-digit",
|
|
3330
|
+
hour12: false
|
|
3331
|
+
});
|
|
3332
|
+
if (!date) {
|
|
3333
|
+
const parts = formatter.formatToParts(now);
|
|
3334
|
+
const tzYear = parseInt(parts.find((p) => p.type === "year").value, 10);
|
|
3335
|
+
const tzMonth = parseInt(parts.find((p) => p.type === "month").value, 10) - 1;
|
|
3336
|
+
const tzDay = parseInt(parts.find((p) => p.type === "day").value, 10);
|
|
3337
|
+
let guess2 = new Date(Date.UTC(tzYear, tzMonth, tzDay, hours, minutes, 0));
|
|
3338
|
+
const guessParts2 = formatter.formatToParts(guess2);
|
|
3339
|
+
const guessHour2 = parseInt(guessParts2.find((p) => p.type === "hour").value, 10);
|
|
3340
|
+
const guessMinute2 = parseInt(guessParts2.find((p) => p.type === "minute").value, 10);
|
|
3341
|
+
const diffMinutes2 = (hours - guessHour2) * 60 + (minutes - guessMinute2);
|
|
3342
|
+
guess2 = new Date(guess2.getTime() + diffMinutes2 * 6e4);
|
|
3343
|
+
return guess2;
|
|
3344
|
+
}
|
|
3345
|
+
let guess = refDate;
|
|
3346
|
+
const guessParts = formatter.formatToParts(guess);
|
|
3347
|
+
const guessHour = parseInt(guessParts.find((p) => p.type === "hour").value, 10);
|
|
3348
|
+
const guessMinute = parseInt(guessParts.find((p) => p.type === "minute").value, 10);
|
|
3349
|
+
const diffMinutes = (hours - guessHour) * 60 + (minutes - guessMinute);
|
|
3350
|
+
guess = new Date(guess.getTime() + diffMinutes * 6e4);
|
|
3351
|
+
return guess;
|
|
3352
|
+
}
|
|
3231
3353
|
listReminders(context) {
|
|
3232
3354
|
const reminders = this.reminderRepo.getByUser(context.userId);
|
|
3233
3355
|
const reminderList = reminders.map((r) => ({
|
|
@@ -6022,6 +6144,7 @@ var init_message_pipeline = __esm({
|
|
|
6022
6144
|
}
|
|
6023
6145
|
} catch {
|
|
6024
6146
|
}
|
|
6147
|
+
const resolvedTimezone = userProfile?.timezone || Intl.DateTimeFormat().resolvedOptions().timeZone;
|
|
6025
6148
|
const skillMetas = this.skillRegistry ? this.skillRegistry.getAll().map((s) => s.metadata) : void 0;
|
|
6026
6149
|
const tools = skillMetas ? this.promptBuilder.buildTools(skillMetas) : void 0;
|
|
6027
6150
|
let system = this.promptBuilder.buildSystemPrompt({
|
|
@@ -6076,7 +6199,8 @@ var init_message_pipeline = __esm({
|
|
|
6076
6199
|
chatId: message.chatId,
|
|
6077
6200
|
chatType: message.chatType,
|
|
6078
6201
|
platform: message.platform,
|
|
6079
|
-
conversationId: conversation.id
|
|
6202
|
+
conversationId: conversation.id,
|
|
6203
|
+
timezone: resolvedTimezone
|
|
6080
6204
|
}, onProgress);
|
|
6081
6205
|
toolResultBlocks.push({
|
|
6082
6206
|
type: "tool_result",
|