omnius 1.0.25 → 1.0.26

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/dist/index.js CHANGED
@@ -1483,7 +1483,7 @@ var init_security_classifier = __esm({
1483
1483
  // ── Local writes (fs mutation)
1484
1484
  { match: /^(file_write|file_edit|file_patch|batch_edit|notebook_edit|structured_file|create_structured_file|image_resize)$/, info: LOCAL_WRITE },
1485
1485
  { match: /^(working_notes|todo_write)$/, info: LOCAL_WRITE },
1486
- { match: /^(scheduler|reminder|agenda)$/, info: SCHEDULER_REMINDER },
1486
+ { match: /^(scheduler|cronjob|reminder|remind|reminders|agenda)$/, info: SCHEDULER_REMINDER },
1487
1487
  { match: /^(exploration_culture|explore)$/, info: LOCAL_WRITE },
1488
1488
  // ── Task control
1489
1489
  { match: /^(task_status|task_output|task_stop)$/, info: TASK_CONTROL },
@@ -5763,7 +5763,10 @@ var init_explore_tools = __esm({
5763
5763
  browser_action: "Interactive browser: login, fill forms, click buttons, screenshot — session persists between calls",
5764
5764
  carbonyl_browser: "Terminal-rendered real browser automation via Carbonyl: navigate, read rendered text, click/type, sessions, daemon mode",
5765
5765
  scheduler: "Schedule tasks for automatic future execution via OS cron",
5766
- reminder: "Set reminders that surface at future agent startups",
5766
+ cronjob: "Alias for scheduler: OS cron-backed time triggers",
5767
+ reminder: "Set scoped minimal reminders or scheduled action triggers",
5768
+ remind: "Alias for reminder: create or list scoped reminders",
5769
+ reminders: "Alias for reminder: list and manage scoped reminders",
5767
5770
  agenda: "View all pending reminders, scheduled tasks, and attention items",
5768
5771
  background_run: "Run a shell command in the background",
5769
5772
  task_status: "Check status of background tasks",
@@ -257491,6 +257494,58 @@ function resolveSchedule(schedule) {
257491
257494
  const lower = schedule.toLowerCase().trim();
257492
257495
  if (SCHEDULE_PRESETS[lower])
257493
257496
  return SCHEDULE_PRESETS[lower];
257497
+ const dailyAt = lower.match(/^(?:daily|every\s+day)\s+(?:at\s+)?(\d{1,2}):(\d{2})$/);
257498
+ if (dailyAt) {
257499
+ const hour2 = parseInt(dailyAt[1], 10);
257500
+ const minute3 = parseInt(dailyAt[2], 10);
257501
+ if (isClockTime(hour2, minute3))
257502
+ return `${minute3} ${hour2} * * *`;
257503
+ }
257504
+ const weeklyAt = lower.match(/^weekly(?:\s+on)?\s+([a-z]+|\d)\s+(?:at\s+)?(\d{1,2}):(\d{2})$/);
257505
+ if (weeklyAt) {
257506
+ const dow = parseWeekday(weeklyAt[1]);
257507
+ const hour2 = parseInt(weeklyAt[2], 10);
257508
+ const minute3 = parseInt(weeklyAt[3], 10);
257509
+ if (dow !== null && isClockTime(hour2, minute3))
257510
+ return `${minute3} ${hour2} * * ${dow}`;
257511
+ }
257512
+ const monthlyAt = lower.match(/^monthly(?:\s+(?:on|day))?\s+(\d{1,2})(?:st|nd|rd|th)?\s+(?:at\s+)?(\d{1,2}):(\d{2})$/);
257513
+ if (monthlyAt) {
257514
+ const day = parseInt(monthlyAt[1], 10);
257515
+ const hour2 = parseInt(monthlyAt[2], 10);
257516
+ const minute3 = parseInt(monthlyAt[3], 10);
257517
+ if (day >= 1 && day <= 31 && isClockTime(hour2, minute3))
257518
+ return `${minute3} ${hour2} ${day} * *`;
257519
+ }
257520
+ const yearlyNumeric = lower.match(/^(?:yearly|annually|every\s+year)\s+(?:on\s+)?(\d{1,2})-(\d{1,2})\s+(?:at\s+)?(\d{1,2}):(\d{2})$/);
257521
+ if (yearlyNumeric) {
257522
+ const month = parseInt(yearlyNumeric[1], 10);
257523
+ const day = parseInt(yearlyNumeric[2], 10);
257524
+ const hour2 = parseInt(yearlyNumeric[3], 10);
257525
+ const minute3 = parseInt(yearlyNumeric[4], 10);
257526
+ if (month >= 1 && month <= 12 && day >= 1 && day <= 31 && isClockTime(hour2, minute3)) {
257527
+ return `${minute3} ${hour2} ${day} ${month} *`;
257528
+ }
257529
+ }
257530
+ const yearlyNamed = lower.match(/^(?:yearly|annually|every\s+year)\s+(?:on\s+)?([a-z]+)\s+(\d{1,2})(?:st|nd|rd|th)?\s+(?:at\s+)?(\d{1,2}):(\d{2})$/);
257531
+ if (yearlyNamed) {
257532
+ const month = parseMonth(yearlyNamed[1]);
257533
+ const day = parseInt(yearlyNamed[2], 10);
257534
+ const hour2 = parseInt(yearlyNamed[3], 10);
257535
+ const minute3 = parseInt(yearlyNamed[4], 10);
257536
+ if (month !== null && day >= 1 && day <= 31 && isClockTime(hour2, minute3)) {
257537
+ return `${minute3} ${hour2} ${day} ${month} *`;
257538
+ }
257539
+ }
257540
+ const everyLong = lower.match(/^every\s+(\d+)\s*(w|wk|wks|weeks?|mo|mos|months?)$/);
257541
+ if (everyLong) {
257542
+ const amount = parseInt(everyLong[1], 10);
257543
+ const unit = everyLong[2].charAt(0);
257544
+ if (amount > 0 && unit === "w")
257545
+ return `0 9 */${amount * 7} * *`;
257546
+ if (amount > 0 && unit === "m")
257547
+ return `0 9 1 */${amount} *`;
257548
+ }
257494
257549
  const inMatch = lower.match(/^in\s+(\d+)\s*(m|min|mins|minutes?|h|hr|hrs|hours?|d|days?)$/);
257495
257550
  if (inMatch) {
257496
257551
  const amount = parseInt(inMatch[1], 10);
@@ -257516,6 +257571,65 @@ function resolveSchedule(schedule) {
257516
257571
  return schedule.trim();
257517
257572
  return null;
257518
257573
  }
257574
+ function isClockTime(hour2, minute3) {
257575
+ return hour2 >= 0 && hour2 <= 23 && minute3 >= 0 && minute3 <= 59;
257576
+ }
257577
+ function parseWeekday(raw) {
257578
+ const value2 = raw.toLowerCase();
257579
+ if (/^[0-6]$/.test(value2))
257580
+ return Number(value2);
257581
+ const days = {
257582
+ sunday: 0,
257583
+ sun: 0,
257584
+ monday: 1,
257585
+ mon: 1,
257586
+ tuesday: 2,
257587
+ tue: 2,
257588
+ tues: 2,
257589
+ wednesday: 3,
257590
+ wed: 3,
257591
+ thursday: 4,
257592
+ thu: 4,
257593
+ thurs: 4,
257594
+ friday: 5,
257595
+ fri: 5,
257596
+ saturday: 6,
257597
+ sat: 6
257598
+ };
257599
+ return days[value2] ?? null;
257600
+ }
257601
+ function parseMonth(raw) {
257602
+ const value2 = raw.toLowerCase();
257603
+ if (/^(?:[1-9]|1[0-2])$/.test(value2))
257604
+ return Number(value2);
257605
+ const months = {
257606
+ january: 1,
257607
+ jan: 1,
257608
+ february: 2,
257609
+ feb: 2,
257610
+ march: 3,
257611
+ mar: 3,
257612
+ april: 4,
257613
+ apr: 4,
257614
+ may: 5,
257615
+ june: 6,
257616
+ jun: 6,
257617
+ july: 7,
257618
+ jul: 7,
257619
+ august: 8,
257620
+ aug: 8,
257621
+ september: 9,
257622
+ sep: 9,
257623
+ sept: 9,
257624
+ october: 10,
257625
+ oct: 10,
257626
+ november: 11,
257627
+ nov: 11,
257628
+ december: 12,
257629
+ dec: 12
257630
+ };
257631
+ return months[value2] ?? null;
257632
+ }
257519
257633
  function describeCron(expr) {
257520
257634
  const parts = expr.split(/\s+/);
257521
257635
  if (parts.length !== 5)
@@ -257705,12 +257819,14 @@ var init_scheduler = __esm({
257705
257819
  "daily evening": "0 18 * * *",
257706
257820
  "weekly": "0 9 * * 1",
257707
257821
  "monthly": "0 9 1 * *",
257822
+ "yearly": "0 9 1 1 *",
257823
+ "annually": "0 9 1 1 *",
257708
257824
  "hourly": "0 * * * *"
257709
257825
  };
257710
257826
  CRON_MARKER = "# OMNIUS-SCHEDULED:";
257711
257827
  SchedulerTool = class {
257712
257828
  name = "scheduler";
257713
- description = "Schedule tasks for automatic future execution using OS-level cron. Actions: 'create' a new scheduled task, 'list' all scheduled tasks, 'cancel' a task by ID, 'pause'/'resume' a task. Schedules: use presets ('daily', 'every 5 minutes', 'hourly', 'weekly'), natural language ('in 30m', 'at 14:30'), or raw cron expressions ('0 */2 * * *'). Scope: 'local' (default, stored in .omnius/) or 'global' (stored in ~/.omnius/, visible from all projects). Tasks launch the agent automatically at the scheduled time.";
257829
+ description = "Schedule tasks for automatic future execution using OS-level cron. Actions: 'create' a new scheduled task, 'list' all scheduled tasks, 'cancel' a task by ID, 'pause'/'resume' a task. Schedules: use presets ('daily', 'weekly', 'monthly', 'yearly'), intervals ('every 5 minutes'), 24-hour times ('at 14:30', 'weekly friday at 17:45', 'monthly on 15 at 08:30'), or raw cron expressions ('0 */2 * * *'). Scope: 'local' (default, stored in .omnius/) or 'global' (stored in ~/.omnius/, visible from all projects). Tasks launch the agent automatically at the scheduled time.";
257714
257830
  parameters = {
257715
257831
  type: "object",
257716
257832
  properties: {
@@ -257725,7 +257841,7 @@ var init_scheduler = __esm({
257725
257841
  },
257726
257842
  schedule: {
257727
257843
  type: "string",
257728
- description: "When to run: preset ('daily', 'every 5 minutes'), natural ('in 30m', 'at 14:30'), or cron ('0 */2 * * *')"
257844
+ description: "When to run: preset ('daily', 'weekly', 'monthly', 'yearly'), interval ('every 5 minutes'), 24-hour time ('at 14:30', 'weekly friday at 17:45'), or cron ('0 */2 * * *')"
257729
257845
  },
257730
257846
  id: {
257731
257847
  type: "string",
@@ -258027,6 +258143,219 @@ function parseDueTime(due) {
258027
258143
  }
258028
258144
  return null;
258029
258145
  }
258146
+ function parseDurationMs(input) {
258147
+ const lower = input.toLowerCase().trim();
258148
+ const match = lower.match(/^(?:every\s+)?(\d+)\s*(m|min|mins|minutes?|h|hr|hrs|hours?|d|days?|w|wk|wks|weeks?)$/);
258149
+ if (!match)
258150
+ return null;
258151
+ const amount = parseInt(match[1], 10);
258152
+ if (!Number.isFinite(amount) || amount <= 0)
258153
+ return null;
258154
+ const unit = match[2].charAt(0);
258155
+ const ms = unit === "m" ? amount * 6e4 : unit === "h" ? amount * 36e5 : unit === "d" ? amount * 864e5 : amount * 7 * 864e5;
258156
+ return {
258157
+ ms,
258158
+ description: `every ${amount} ${unit === "m" ? "minute" : unit === "h" ? "hour" : unit === "d" ? "day" : "week"}${amount === 1 ? "" : "s"}`
258159
+ };
258160
+ }
258161
+ function parseTimeOfDay(value2) {
258162
+ const raw = String(value2 ?? "").trim();
258163
+ const match = raw.match(/^(\d{1,2}):(\d{2})$/);
258164
+ if (!match)
258165
+ return void 0;
258166
+ const hour2 = parseInt(match[1], 10);
258167
+ const minute3 = parseInt(match[2], 10);
258168
+ if (hour2 < 0 || hour2 > 23 || minute3 < 0 || minute3 > 59)
258169
+ return void 0;
258170
+ return `${String(hour2).padStart(2, "0")}:${String(minute3).padStart(2, "0")}`;
258171
+ }
258172
+ function parseWeekday2(value2) {
258173
+ if (typeof value2 === "number" && Number.isFinite(value2)) {
258174
+ const day = Math.trunc(value2);
258175
+ return day >= 0 && day <= 6 ? day : void 0;
258176
+ }
258177
+ const raw = String(value2 ?? "").trim().toLowerCase();
258178
+ if (!raw)
258179
+ return void 0;
258180
+ const names = {
258181
+ sunday: 0,
258182
+ sun: 0,
258183
+ monday: 1,
258184
+ mon: 1,
258185
+ tuesday: 2,
258186
+ tue: 2,
258187
+ tues: 2,
258188
+ wednesday: 3,
258189
+ wed: 3,
258190
+ thursday: 4,
258191
+ thu: 4,
258192
+ thurs: 4,
258193
+ friday: 5,
258194
+ fri: 5,
258195
+ saturday: 6,
258196
+ sat: 6
258197
+ };
258198
+ if (/^[0-6]$/.test(raw))
258199
+ return Number(raw);
258200
+ return names[raw];
258201
+ }
258202
+ function parseMonth2(value2) {
258203
+ if (typeof value2 === "number" && Number.isFinite(value2)) {
258204
+ const month = Math.trunc(value2);
258205
+ return month >= 1 && month <= 12 ? month : void 0;
258206
+ }
258207
+ const raw = String(value2 ?? "").trim().toLowerCase();
258208
+ if (!raw)
258209
+ return void 0;
258210
+ const names = {
258211
+ january: 1,
258212
+ jan: 1,
258213
+ february: 2,
258214
+ feb: 2,
258215
+ march: 3,
258216
+ mar: 3,
258217
+ april: 4,
258218
+ apr: 4,
258219
+ may: 5,
258220
+ june: 6,
258221
+ jun: 6,
258222
+ july: 7,
258223
+ jul: 7,
258224
+ august: 8,
258225
+ aug: 8,
258226
+ september: 9,
258227
+ sep: 9,
258228
+ sept: 9,
258229
+ october: 10,
258230
+ oct: 10,
258231
+ november: 11,
258232
+ nov: 11,
258233
+ december: 12,
258234
+ dec: 12
258235
+ };
258236
+ if (/^(?:[1-9]|1[0-2])$/.test(raw))
258237
+ return Number(raw);
258238
+ return names[raw];
258239
+ }
258240
+ function parseRecurrence(args) {
258241
+ const repeatRaw = String(args["repeat"] ?? args["recurrence"] ?? "").trim();
258242
+ if (!repeatRaw)
258243
+ return void 0;
258244
+ const lower = repeatRaw.toLowerCase();
258245
+ if (["none", "off", "false", "no"].includes(lower))
258246
+ return void 0;
258247
+ const maxOccurrences = typeof args["max_occurrences"] === "number" && Number.isFinite(args["max_occurrences"]) ? Math.max(1, Math.floor(args["max_occurrences"])) : typeof args["max_runs"] === "number" && Number.isFinite(args["max_runs"]) ? Math.max(1, Math.floor(args["max_runs"])) : void 0;
258248
+ const intervalInput = lower.startsWith("every ") ? lower : String(args["every"] ?? "").trim().toLowerCase();
258249
+ const interval = parseDurationMs(intervalInput);
258250
+ if (interval) {
258251
+ return {
258252
+ kind: "interval",
258253
+ everyMs: interval.ms,
258254
+ intervalDescription: interval.description,
258255
+ ...maxOccurrences ? { maxOccurrences } : {}
258256
+ };
258257
+ }
258258
+ const repeatTime = lower.match(/\bat\s+(\d{1,2}:\d{2})\b/)?.[1];
258259
+ const timeOfDay = parseTimeOfDay(args["time"]) ?? parseTimeOfDay(args["at"]) ?? parseTimeOfDay(repeatTime);
258260
+ const withMax = (recurrence) => maxOccurrences ? { ...recurrence, maxOccurrences } : recurrence;
258261
+ if (lower === "daily" || lower === "every day") {
258262
+ return withMax({ kind: "daily", timeOfDay: timeOfDay ?? "09:00" });
258263
+ }
258264
+ if (lower.startsWith("weekly") || lower === "every week") {
258265
+ const dayFromText = lower.match(/\b(sun(?:day)?|mon(?:day)?|tue(?:s|sday)?|wed(?:nesday)?|thu(?:rs|rsday)?|fri(?:day)?|sat(?:urday)?)\b/i)?.[1];
258266
+ return withMax({
258267
+ kind: "weekly",
258268
+ weekday: parseWeekday2(args["weekday"] ?? dayFromText) ?? 1,
258269
+ timeOfDay: timeOfDay ?? "09:00"
258270
+ });
258271
+ }
258272
+ if (lower.startsWith("monthly") || lower === "every month") {
258273
+ const dayRaw = args["day_of_month"] ?? args["day"] ?? lower.match(/\b(?:day|on)\s+(\d{1,2})\b/)?.[1];
258274
+ const day = typeof dayRaw === "number" ? Math.trunc(dayRaw) : parseInt(String(dayRaw ?? "1"), 10);
258275
+ return withMax({
258276
+ kind: "monthly",
258277
+ dayOfMonth: Math.max(1, Math.min(31, Number.isFinite(day) ? day : 1)),
258278
+ timeOfDay: timeOfDay ?? "09:00"
258279
+ });
258280
+ }
258281
+ if (lower.startsWith("yearly") || lower === "annually" || lower === "every year") {
258282
+ const yearlyText = lower.match(/\b(jan(?:uary)?|feb(?:ruary)?|mar(?:ch)?|apr(?:il)?|may|jun(?:e)?|jul(?:y)?|aug(?:ust)?|sep(?:t|tember)?|oct(?:ober)?|nov(?:ember)?|dec(?:ember)?|\d{1,2})\s+(\d{1,2})(?:st|nd|rd|th)?\b/i);
258283
+ const month = parseMonth2(args["month"] ?? yearlyText?.[1]) ?? 1;
258284
+ const dayRaw = args["day_of_month"] ?? args["day"] ?? yearlyText?.[2] ?? 1;
258285
+ const day = typeof dayRaw === "number" ? Math.trunc(dayRaw) : parseInt(String(dayRaw), 10);
258286
+ return withMax({
258287
+ kind: "yearly",
258288
+ month,
258289
+ dayOfMonth: Math.max(1, Math.min(31, Number.isFinite(day) ? day : 1)),
258290
+ timeOfDay: timeOfDay ?? "09:00"
258291
+ });
258292
+ }
258293
+ return null;
258294
+ }
258295
+ function setLocalTime(target, timeOfDay, fallback = "09:00") {
258296
+ const [hourRaw, minuteRaw] = (timeOfDay ?? fallback).split(":");
258297
+ target.setHours(parseInt(hourRaw ?? "9", 10), parseInt(minuteRaw ?? "0", 10), 0, 0);
258298
+ }
258299
+ function clampDateToMonth(target, dayOfMonth) {
258300
+ target.setDate(1);
258301
+ const maxDay = new Date(target.getFullYear(), target.getMonth() + 1, 0).getDate();
258302
+ target.setDate(Math.min(Math.max(1, dayOfMonth), maxDay));
258303
+ }
258304
+ function nextDueFromRecurrence(recurrence, after = /* @__PURE__ */ new Date()) {
258305
+ const target = new Date(after);
258306
+ switch (recurrence.kind) {
258307
+ case "interval":
258308
+ return new Date(after.getTime() + Math.max(6e4, recurrence.everyMs ?? 864e5));
258309
+ case "daily":
258310
+ setLocalTime(target, recurrence.timeOfDay);
258311
+ if (target <= after)
258312
+ target.setDate(target.getDate() + 1);
258313
+ return target;
258314
+ case "weekly": {
258315
+ setLocalTime(target, recurrence.timeOfDay);
258316
+ const weekday = recurrence.weekday ?? 1;
258317
+ const daysAhead = (weekday - target.getDay() + 7) % 7;
258318
+ target.setDate(target.getDate() + daysAhead);
258319
+ if (target <= after)
258320
+ target.setDate(target.getDate() + 7);
258321
+ return target;
258322
+ }
258323
+ case "monthly":
258324
+ setLocalTime(target, recurrence.timeOfDay);
258325
+ clampDateToMonth(target, recurrence.dayOfMonth ?? 1);
258326
+ if (target <= after) {
258327
+ target.setMonth(target.getMonth() + 1);
258328
+ clampDateToMonth(target, recurrence.dayOfMonth ?? 1);
258329
+ }
258330
+ return target;
258331
+ case "yearly":
258332
+ target.setMonth((recurrence.month ?? 1) - 1, 1);
258333
+ clampDateToMonth(target, recurrence.dayOfMonth ?? 1);
258334
+ setLocalTime(target, recurrence.timeOfDay);
258335
+ if (target <= after) {
258336
+ target.setFullYear(target.getFullYear() + 1);
258337
+ target.setMonth((recurrence.month ?? 1) - 1, 1);
258338
+ clampDateToMonth(target, recurrence.dayOfMonth ?? 1);
258339
+ }
258340
+ return target;
258341
+ }
258342
+ }
258343
+ function describeRecurrence(recurrence) {
258344
+ if (!recurrence)
258345
+ return void 0;
258346
+ switch (recurrence.kind) {
258347
+ case "interval":
258348
+ return recurrence.intervalDescription ?? `every ${Math.round((recurrence.everyMs ?? 0) / 6e4)} minutes`;
258349
+ case "daily":
258350
+ return `daily at ${recurrence.timeOfDay ?? "09:00"}`;
258351
+ case "weekly":
258352
+ return `weekly on day ${recurrence.weekday ?? 1} at ${recurrence.timeOfDay ?? "09:00"}`;
258353
+ case "monthly":
258354
+ return `monthly on day ${recurrence.dayOfMonth ?? 1} at ${recurrence.timeOfDay ?? "09:00"}`;
258355
+ case "yearly":
258356
+ return `yearly on ${String(recurrence.month ?? 1).padStart(2, "0")}-${String(recurrence.dayOfMonth ?? 1).padStart(2, "0")} at ${recurrence.timeOfDay ?? "09:00"}`;
258357
+ }
258358
+ }
258030
258359
  async function getStorePath(workingDir) {
258031
258360
  const dir = resolve26(workingDir, ".omnius", "scheduled");
258032
258361
  await mkdir14(dir, { recursive: true });
@@ -258046,28 +258375,77 @@ async function saveReminderStore(workingDir, store2) {
258046
258375
  store2.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
258047
258376
  await writeFile19(storePath, JSON.stringify(store2, null, 2), "utf-8");
258048
258377
  }
258049
- async function getDueReminders(workingDir) {
258378
+ async function getDueReminders(workingDir, query = {}) {
258050
258379
  const store2 = await loadReminderStore(workingDir);
258051
- const now = /* @__PURE__ */ new Date();
258380
+ const now = query.now ?? /* @__PURE__ */ new Date();
258052
258381
  return store2.reminders.filter((r2) => {
258053
258382
  if (r2.status !== "pending")
258054
258383
  return false;
258055
- if (!r2.dueAt)
258056
- return true;
258057
- return new Date(r2.dueAt) <= now;
258384
+ if (r2.dueAt && new Date(r2.dueAt) > now)
258385
+ return false;
258386
+ if (query.deliveryMode && query.deliveryMode !== "any" && (r2.delivery?.mode ?? "minimal") !== query.deliveryMode)
258387
+ return false;
258388
+ if (query.scope && !reminderScopeMatches(r2.scope, query.scope, query.includeUnscoped === true))
258389
+ return false;
258390
+ return true;
258058
258391
  });
258059
258392
  }
258060
258393
  async function markRemindersSeen(workingDir, ids) {
258394
+ await markRemindersTriggered(workingDir, ids);
258395
+ }
258396
+ async function markRemindersTriggered(workingDir, ids, now = /* @__PURE__ */ new Date()) {
258061
258397
  const store2 = await loadReminderStore(workingDir);
258062
258398
  const idSet = new Set(ids);
258063
258399
  for (const r2 of store2.reminders) {
258064
258400
  if (idSet.has(r2.id) && r2.status === "pending") {
258065
- r2.status = "seen";
258066
- r2.seenAt = (/* @__PURE__ */ new Date()).toISOString();
258401
+ r2.lastTriggeredAt = now.toISOString();
258402
+ r2.triggerCount = (r2.triggerCount ?? 0) + 1;
258403
+ const max = r2.recurrence?.maxOccurrences;
258404
+ if (r2.recurrence && (!max || r2.triggerCount < max)) {
258405
+ r2.dueAt = nextDueFromRecurrence(r2.recurrence, now).toISOString();
258406
+ r2.dueDescription = describeRecurrence(r2.recurrence);
258407
+ r2.seenAt = void 0;
258408
+ } else {
258409
+ r2.status = "seen";
258410
+ r2.seenAt = now.toISOString();
258411
+ }
258067
258412
  }
258068
258413
  }
258069
258414
  await saveReminderStore(workingDir, store2);
258070
258415
  }
258416
+ function reminderScopeMatches(scope, query, includeUnscoped) {
258417
+ if (!scope)
258418
+ return includeUnscoped;
258419
+ const keys = ["surface", "visibility", "targetId", "sessionId", "chatId", "threadId", "userId", "username", "label"];
258420
+ for (const key of keys) {
258421
+ const expected = query[key];
258422
+ if (expected !== void 0 && scope[key] !== expected)
258423
+ return false;
258424
+ }
258425
+ return true;
258426
+ }
258427
+ function normalizeDeliveryKind(value2) {
258428
+ const raw = String(value2 ?? "").trim().toLowerCase();
258429
+ if (raw === "action" || raw === "agent" || raw === "agent_action")
258430
+ return "action";
258431
+ return "minimal";
258432
+ }
258433
+ function normalizeSurface(value2) {
258434
+ const raw = String(value2 ?? "").trim().toLowerCase();
258435
+ return raw === "tui" || raw === "gui" || raw === "telegram" || raw === "api" || raw === "global" ? raw : void 0;
258436
+ }
258437
+ function normalizeVisibility(value2) {
258438
+ const raw = String(value2 ?? "").trim().toLowerCase();
258439
+ return raw === "private" || raw === "public" ? raw : void 0;
258440
+ }
258441
+ function stringValue(value2) {
258442
+ const raw = String(value2 ?? "").trim();
258443
+ return raw || void 0;
258444
+ }
258445
+ function formatScope(scope) {
258446
+ const label = scope.label ? ` (${scope.label})` : "";
258447
+ return `${scope.surface}/${scope.visibility}/${scope.targetId}${label}`;
258448
+ }
258071
258449
  var STORE_FILE, ReminderTool;
258072
258450
  var init_reminder = __esm({
258073
258451
  "packages/execution/dist/tools/reminder.js"() {
@@ -258075,7 +258453,7 @@ var init_reminder = __esm({
258075
258453
  STORE_FILE = "reminders.json";
258076
258454
  ReminderTool = class {
258077
258455
  name = "reminder";
258078
- description = "Set reminders that surface at future agent startups. Leave instructions for your future self across sessions. Actions: 'set' a new reminder, 'list' all reminders, 'complete' or 'dismiss' a reminder, 'snooze' to postpone. Reminders appear automatically when the agent starts and can have due dates, priority levels, and tags for filtering.";
258456
+ description = "Set scoped reminders and time-triggered action requests. Use kind='minimal' for raw text/content delivery and kind='action' when the trigger should start inference/tool-calling from a task_description. Actions: 'set' a new reminder, 'list' all reminders, 'complete' or 'dismiss' a reminder, 'snooze' to postpone. Reminders can have due dates, recurrence, priority, tags, and per-platform scopes.";
258079
258457
  parameters = {
258080
258458
  type: "object",
258081
258459
  properties: {
@@ -258097,6 +258475,43 @@ var init_reminder = __esm({
258097
258475
  enum: ["low", "normal", "high", "critical"],
258098
258476
  description: "Priority level (default: normal)"
258099
258477
  },
258478
+ kind: {
258479
+ type: "string",
258480
+ enum: ["minimal", "action"],
258481
+ description: "minimal sends raw reminder text/content only. action triggers an inference/tool-call task using task_description."
258482
+ },
258483
+ task_description: {
258484
+ type: "string",
258485
+ description: "For kind='action': the task the agent should perform when the trigger fires."
258486
+ },
258487
+ content: {
258488
+ type: "string",
258489
+ description: "For kind='minimal': raw text/content to deliver. Defaults to message."
258490
+ },
258491
+ repeat: {
258492
+ type: "string",
258493
+ description: "Optional recurrence: 'every 30m', 'daily', 'weekly monday', 'monthly', 'yearly'."
258494
+ },
258495
+ time: {
258496
+ type: "string",
258497
+ description: "24-hour local time HH:MM for daily/weekly/monthly/yearly recurrence."
258498
+ },
258499
+ weekday: {
258500
+ type: "string",
258501
+ description: "Weekday for weekly recurrence, e.g. 'monday' or 1."
258502
+ },
258503
+ day_of_month: {
258504
+ type: "number",
258505
+ description: "Day of month for monthly/yearly recurrence."
258506
+ },
258507
+ month: {
258508
+ type: "string",
258509
+ description: "Month for yearly recurrence, e.g. 'january' or 1."
258510
+ },
258511
+ max_occurrences: {
258512
+ type: "number",
258513
+ description: "Optional cap for recurring reminders."
258514
+ },
258100
258515
  tags: {
258101
258516
  type: "string",
258102
258517
  description: "Comma-separated tags for categorization (e.g. 'deploy,auth,urgent')"
@@ -258112,13 +258527,29 @@ var init_reminder = __esm({
258112
258527
  filter: {
258113
258528
  type: "string",
258114
258529
  description: "Filter by status: 'pending', 'seen', 'all' (for 'list', default: 'pending')"
258530
+ },
258531
+ surface: {
258532
+ type: "string",
258533
+ enum: ["tui", "gui", "telegram", "api", "global"],
258534
+ description: "Optional scope surface. Ignored when this tool is context-locked by a platform adapter."
258535
+ },
258536
+ visibility: {
258537
+ type: "string",
258538
+ enum: ["private", "public"],
258539
+ description: "Optional scope visibility. Ignored when context-locked."
258540
+ },
258541
+ target_id: {
258542
+ type: "string",
258543
+ description: "Optional scope target id. For Telegram this is the chat id. Ignored when context-locked."
258115
258544
  }
258116
258545
  },
258117
258546
  required: ["action"]
258118
258547
  };
258119
258548
  workingDir;
258120
- constructor(workingDir) {
258549
+ options;
258550
+ constructor(workingDir, options2 = {}) {
258121
258551
  this.workingDir = workingDir;
258552
+ this.options = options2;
258122
258553
  }
258123
258554
  async execute(args) {
258124
258555
  const start2 = performance.now();
@@ -258161,6 +258592,33 @@ var init_reminder = __esm({
258161
258592
  const tags = tagsStr ? tagsStr.split(",").map((t2) => t2.trim()).filter(Boolean) : [];
258162
258593
  const context2 = args["context"] ?? void 0;
258163
258594
  const dueStr = args["due"] ?? "";
258595
+ const recurrence = parseRecurrence(args);
258596
+ if (recurrence === null) {
258597
+ return {
258598
+ success: false,
258599
+ output: "",
258600
+ error: `Cannot parse repeat '${String(args["repeat"] ?? args["recurrence"])}'. Try: 'every 30m', 'daily', 'weekly monday', 'monthly', or 'yearly'.`,
258601
+ durationMs: performance.now() - start2
258602
+ };
258603
+ }
258604
+ const kind = normalizeDeliveryKind(args["kind"] ?? args["mode"] ?? args["delivery"]);
258605
+ if (kind === "action" && !this.options.allowActionDelivery) {
258606
+ return {
258607
+ success: false,
258608
+ output: "",
258609
+ error: "kind='action' is not allowed in this context. Use kind='minimal' for a plain reminder.",
258610
+ durationMs: performance.now() - start2
258611
+ };
258612
+ }
258613
+ const taskDescription = String(args["task_description"] ?? args["task"] ?? args["prompt"] ?? "").trim();
258614
+ if (kind === "action" && !taskDescription) {
258615
+ return {
258616
+ success: false,
258617
+ output: "",
258618
+ error: "task_description is required when kind='action'",
258619
+ durationMs: performance.now() - start2
258620
+ };
258621
+ }
258164
258622
  let dueAt;
258165
258623
  let dueDescription;
258166
258624
  if (dueStr) {
@@ -258175,7 +258633,12 @@ var init_reminder = __esm({
258175
258633
  }
258176
258634
  dueAt = parsed.isoDate;
258177
258635
  dueDescription = parsed.description;
258636
+ } else if (recurrence) {
258637
+ dueAt = nextDueFromRecurrence(recurrence).toISOString();
258638
+ dueDescription = describeRecurrence(recurrence);
258178
258639
  }
258640
+ const scope = this.resolveScope(args);
258641
+ const content = String(args["content"] ?? message2).trim() || message2;
258179
258642
  const id = `rem-${randomBytes12(4).toString("hex")}`;
258180
258643
  const entry = {
258181
258644
  id,
@@ -258187,7 +258650,14 @@ var init_reminder = __esm({
258187
258650
  tags,
258188
258651
  status: "pending",
258189
258652
  context: context2,
258190
- source: "agent"
258653
+ source: this.options.source ?? "agent",
258654
+ ...scope ? { scope } : {},
258655
+ delivery: {
258656
+ mode: kind,
258657
+ ...kind === "action" ? { taskDescription } : { content },
258658
+ ...this.options.createdFrom ? { createdFrom: this.options.createdFrom } : {}
258659
+ },
258660
+ ...recurrence ? { recurrence } : {}
258191
258661
  };
258192
258662
  const store2 = await loadReminderStore(this.workingDir);
258193
258663
  store2.reminders.push(entry);
@@ -258199,11 +258669,15 @@ var init_reminder = __esm({
258199
258669
  ` ID: ${id}`,
258200
258670
  ` Message: ${message2}`,
258201
258671
  ` Priority: ${priority}`,
258672
+ ` Kind: ${kind}`,
258673
+ kind === "action" ? ` Task: ${taskDescription}` : "",
258202
258674
  dueDescription ? ` Due: ${dueDescription}` : ` Due: next startup`,
258675
+ describeRecurrence(recurrence) ? ` Repeats: ${describeRecurrence(recurrence)}` : "",
258676
+ scope ? ` Scope: ${formatScope(scope)}` : "",
258203
258677
  tags.length > 0 ? ` Tags: ${tags.join(", ")}` : "",
258204
258678
  context2 ? ` Context: ${context2.slice(0, 100)}` : "",
258205
258679
  ``,
258206
- `This reminder will surface when the agent next starts${dueDescription ? ` (after ${dueDescription})` : ""}.`
258680
+ kind === "action" ? `This action will trigger when due${dueDescription ? ` (${dueDescription})` : ""}.` : `This reminder will surface when due${dueDescription ? ` (${dueDescription})` : ""}.`
258207
258681
  ].filter(Boolean).join("\n"),
258208
258682
  durationMs: performance.now() - start2
258209
258683
  };
@@ -258232,8 +258706,14 @@ var init_reminder = __esm({
258232
258706
  const statusLabel = r2.status === "seen" ? " (seen)" : r2.status === "completed" ? " (done)" : r2.status === "dismissed" ? " (dismissed)" : "";
258233
258707
  lines.push(` ${priorityIcon} [${r2.id}]${statusLabel}`);
258234
258708
  lines.push(` ${r2.message}`);
258709
+ lines.push(` Kind: ${r2.delivery?.mode ?? "minimal"}`);
258235
258710
  if (r2.dueDescription)
258236
258711
  lines.push(` Due: ${r2.dueDescription}`);
258712
+ const recurrence = describeRecurrence(r2.recurrence);
258713
+ if (recurrence)
258714
+ lines.push(` Repeats: ${recurrence}`);
258715
+ if (r2.scope)
258716
+ lines.push(` Scope: ${formatScope(r2.scope)}`);
258237
258717
  if (r2.tags.length > 0)
258238
258718
  lines.push(` Tags: ${r2.tags.join(", ")}`);
258239
258719
  if (r2.context)
@@ -258285,6 +258765,26 @@ var init_reminder = __esm({
258285
258765
  durationMs: performance.now() - start2
258286
258766
  };
258287
258767
  }
258768
+ resolveScope(args) {
258769
+ if (this.options.lockScope)
258770
+ return this.options.defaultScope;
258771
+ const surface = normalizeSurface(args["surface"]) ?? this.options.defaultScope?.surface;
258772
+ const visibility = normalizeVisibility(args["visibility"]) ?? this.options.defaultScope?.visibility ?? "private";
258773
+ const targetId = String(args["target_id"] ?? args["targetId"] ?? this.options.defaultScope?.targetId ?? "").trim();
258774
+ if (!surface || !targetId)
258775
+ return this.options.defaultScope;
258776
+ return {
258777
+ surface,
258778
+ visibility,
258779
+ targetId,
258780
+ sessionId: stringValue(args["session_id"] ?? args["sessionId"] ?? this.options.defaultScope?.sessionId),
258781
+ chatId: stringValue(args["chat_id"] ?? args["chatId"] ?? this.options.defaultScope?.chatId),
258782
+ threadId: stringValue(args["thread_id"] ?? args["threadId"] ?? this.options.defaultScope?.threadId),
258783
+ userId: stringValue(args["user_id"] ?? args["userId"] ?? this.options.defaultScope?.userId),
258784
+ username: stringValue(args["username"] ?? this.options.defaultScope?.username),
258785
+ label: stringValue(args["label"] ?? this.options.defaultScope?.label)
258786
+ };
258787
+ }
258288
258788
  };
258289
258789
  }
258290
258790
  });
@@ -258613,6 +259113,23 @@ ${sections.join("\n")}`,
258613
259113
  }
258614
259114
  });
258615
259115
 
259116
+ // packages/execution/dist/tools/tool-alias.js
259117
+ function aliasTool(tool, name10, description = `Alias for ${tool.name}: ${tool.description}`) {
259118
+ return {
259119
+ name: name10,
259120
+ description,
259121
+ parameters: tool.parameters,
259122
+ async execute(args) {
259123
+ return tool.execute(args);
259124
+ }
259125
+ };
259126
+ }
259127
+ var init_tool_alias = __esm({
259128
+ "packages/execution/dist/tools/tool-alias.js"() {
259129
+ "use strict";
259130
+ }
259131
+ });
259132
+
258616
259133
  // packages/execution/dist/tools/opencode.js
258617
259134
  import { execSync as execSync23, spawn as spawn14 } from "node:child_process";
258618
259135
  import { existsSync as existsSync33 } from "node:fs";
@@ -259213,6 +259730,47 @@ function resolveSchedule2(schedule) {
259213
259730
  if (LONG_HORIZON_PRESETS[lower]) {
259214
259731
  return { cron: LONG_HORIZON_PRESETS[lower], description: lower };
259215
259732
  }
259733
+ const dailyAt = lower.match(/^(?:daily|every\s+day)\s+(?:at\s+)?(\d{1,2}):(\d{2})$/);
259734
+ if (dailyAt) {
259735
+ const hour2 = parseInt(dailyAt[1], 10);
259736
+ const minute3 = parseInt(dailyAt[2], 10);
259737
+ if (isClockTime2(hour2, minute3))
259738
+ return { cron: `${minute3} ${hour2} * * *`, description: `daily at ${String(hour2).padStart(2, "0")}:${String(minute3).padStart(2, "0")}` };
259739
+ }
259740
+ const weeklyAt = lower.match(/^weekly(?:\s+on)?\s+([a-z]+|\d)\s+(?:at\s+)?(\d{1,2}):(\d{2})$/);
259741
+ if (weeklyAt) {
259742
+ const dow = parseWeekday3(weeklyAt[1]);
259743
+ const hour2 = parseInt(weeklyAt[2], 10);
259744
+ const minute3 = parseInt(weeklyAt[3], 10);
259745
+ if (dow !== null && isClockTime2(hour2, minute3))
259746
+ return { cron: `${minute3} ${hour2} * * ${dow}`, description: `weekly on day ${dow} at ${String(hour2).padStart(2, "0")}:${String(minute3).padStart(2, "0")}` };
259747
+ }
259748
+ const monthlyAt = lower.match(/^monthly(?:\s+(?:on|day))?\s+(\d{1,2})(?:st|nd|rd|th)?\s+(?:at\s+)?(\d{1,2}):(\d{2})$/);
259749
+ if (monthlyAt) {
259750
+ const day = parseInt(monthlyAt[1], 10);
259751
+ const hour2 = parseInt(monthlyAt[2], 10);
259752
+ const minute3 = parseInt(monthlyAt[3], 10);
259753
+ if (day >= 1 && day <= 31 && isClockTime2(hour2, minute3))
259754
+ return { cron: `${minute3} ${hour2} ${day} * *`, description: `monthly on day ${day} at ${String(hour2).padStart(2, "0")}:${String(minute3).padStart(2, "0")}` };
259755
+ }
259756
+ const yearlyNumeric = lower.match(/^(?:yearly|annually|every\s+year)\s+(?:on\s+)?(\d{1,2})-(\d{1,2})\s+(?:at\s+)?(\d{1,2}):(\d{2})$/);
259757
+ if (yearlyNumeric) {
259758
+ const month = parseInt(yearlyNumeric[1], 10);
259759
+ const day = parseInt(yearlyNumeric[2], 10);
259760
+ const hour2 = parseInt(yearlyNumeric[3], 10);
259761
+ const minute3 = parseInt(yearlyNumeric[4], 10);
259762
+ if (month >= 1 && month <= 12 && day >= 1 && day <= 31 && isClockTime2(hour2, minute3))
259763
+ return { cron: `${minute3} ${hour2} ${day} ${month} *`, description: `yearly on ${String(month).padStart(2, "0")}-${String(day).padStart(2, "0")} at ${String(hour2).padStart(2, "0")}:${String(minute3).padStart(2, "0")}` };
259764
+ }
259765
+ const yearlyNamed = lower.match(/^(?:yearly|annually|every\s+year)\s+(?:on\s+)?([a-z]+)\s+(\d{1,2})(?:st|nd|rd|th)?\s+(?:at\s+)?(\d{1,2}):(\d{2})$/);
259766
+ if (yearlyNamed) {
259767
+ const month = parseMonth3(yearlyNamed[1]);
259768
+ const day = parseInt(yearlyNamed[2], 10);
259769
+ const hour2 = parseInt(yearlyNamed[3], 10);
259770
+ const minute3 = parseInt(yearlyNamed[4], 10);
259771
+ if (month !== null && day >= 1 && day <= 31 && isClockTime2(hour2, minute3))
259772
+ return { cron: `${minute3} ${hour2} ${day} ${month} *`, description: `yearly on ${String(month).padStart(2, "0")}-${String(day).padStart(2, "0")} at ${String(hour2).padStart(2, "0")}:${String(minute3).padStart(2, "0")}` };
259773
+ }
259216
259774
  const everyMatch = lower.match(/^every\s+(\d+)\s*(h|hr|hrs|hours?|d|days?|w|wk|wks|weeks?|m|mo|mos|months?)$/);
259217
259775
  if (everyMatch) {
259218
259776
  const amount = parseInt(everyMatch[1], 10);
@@ -259260,6 +259818,65 @@ function resolveSchedule2(schedule) {
259260
259818
  }
259261
259819
  return null;
259262
259820
  }
259821
+ function isClockTime2(hour2, minute3) {
259822
+ return hour2 >= 0 && hour2 <= 23 && minute3 >= 0 && minute3 <= 59;
259823
+ }
259824
+ function parseWeekday3(raw) {
259825
+ const value2 = raw.toLowerCase();
259826
+ if (/^[0-6]$/.test(value2))
259827
+ return Number(value2);
259828
+ const days = {
259829
+ sunday: 0,
259830
+ sun: 0,
259831
+ monday: 1,
259832
+ mon: 1,
259833
+ tuesday: 2,
259834
+ tue: 2,
259835
+ tues: 2,
259836
+ wednesday: 3,
259837
+ wed: 3,
259838
+ thursday: 4,
259839
+ thu: 4,
259840
+ thurs: 4,
259841
+ friday: 5,
259842
+ fri: 5,
259843
+ saturday: 6,
259844
+ sat: 6
259845
+ };
259846
+ return days[value2] ?? null;
259847
+ }
259848
+ function parseMonth3(raw) {
259849
+ const value2 = raw.toLowerCase();
259850
+ if (/^(?:[1-9]|1[0-2])$/.test(value2))
259851
+ return Number(value2);
259852
+ const months = {
259853
+ january: 1,
259854
+ jan: 1,
259855
+ february: 2,
259856
+ feb: 2,
259857
+ march: 3,
259858
+ mar: 3,
259859
+ april: 4,
259860
+ apr: 4,
259861
+ may: 5,
259862
+ june: 6,
259863
+ jun: 6,
259864
+ july: 7,
259865
+ jul: 7,
259866
+ august: 8,
259867
+ aug: 8,
259868
+ september: 9,
259869
+ sep: 9,
259870
+ sept: 9,
259871
+ october: 10,
259872
+ oct: 10,
259873
+ november: 11,
259874
+ nov: 11,
259875
+ december: 12,
259876
+ dec: 12
259877
+ };
259878
+ return months[value2] ?? null;
259879
+ }
259263
259880
  function getCurrentCrontab2() {
259264
259881
  try {
259265
259882
  return execSync25("crontab -l 2>/dev/null", { stdio: "pipe" }).toString().split("\n");
@@ -259376,12 +259993,14 @@ var init_cron_agent = __esm({
259376
259993
  "weekly": "0 9 * * 1",
259377
259994
  "biweekly": "0 9 1,15 * *",
259378
259995
  "monthly": "0 9 1 * *",
259379
- "quarterly": "0 9 1 1,4,7,10 *"
259996
+ "quarterly": "0 9 1 1,4,7,10 *",
259997
+ "yearly": "0 9 1 1 *",
259998
+ "annually": "0 9 1 1 *"
259380
259999
  };
259381
260000
  CRON_AGENT_MARKER = "# Omnius-CRON-AGENT:";
259382
260001
  CronAgentTool = class {
259383
260002
  name = "cron_agent";
259384
- description = "Schedule long-horizon autonomous agent tasks that execute on a cron schedule. Unlike one-shot scheduler tasks, cron_agent jobs have goals, completion criteria, and execution history tracking. Actions: 'create' a monitored cron job, 'list' all jobs, 'check' a job's status and history, 'cancel' a job, 'logs' view execution output, 'evaluate' check if goal is met. Schedules: 'every 2 hours', 'daily', 'weekly', 'monthly', 'every 3 days', or raw cron. Use this for long-running autonomous workflows: periodic code reviews, test monitoring, dependency updates, performance tracking, log analysis, or any recurring agent task.";
260003
+ description = "Schedule long-horizon autonomous agent tasks that execute on a cron schedule. Unlike one-shot scheduler tasks, cron_agent jobs have goals, completion criteria, and execution history tracking. Actions: 'create' a monitored cron job, 'list' all jobs, 'check' a job's status and history, 'cancel' a job, 'logs' view execution output, 'evaluate' check if goal is met. Schedules: 'every 2 hours', 'daily at 14:30', 'weekly friday at 17:45', 'monthly', 'yearly', or raw cron. Use this for long-running autonomous workflows: periodic code reviews, test monitoring, dependency updates, performance tracking, log analysis, or any recurring agent task.";
259385
260004
  parameters = {
259386
260005
  type: "object",
259387
260006
  properties: {
@@ -259400,7 +260019,7 @@ var init_cron_agent = __esm({
259400
260019
  },
259401
260020
  schedule: {
259402
260021
  type: "string",
259403
- description: "When to run: 'every 2 hours', 'daily', 'weekly', 'monthly', 'every 3 days', or cron expression"
260022
+ description: "When to run: 'every 2 hours', 'daily at 14:30', 'weekly friday at 17:45', 'monthly', 'yearly', or cron expression"
259404
260023
  },
259405
260024
  completion_criteria: {
259406
260025
  type: "string",
@@ -287360,9 +287979,9 @@ ${lanes.join("\n")}
287360
287979
  function isJsonEqual(a2, b) {
287361
287980
  return a2 === b || typeof a2 === "object" && a2 !== null && typeof b === "object" && b !== null && equalOwnProperties(a2, b, isJsonEqual);
287362
287981
  }
287363
- function parsePseudoBigInt(stringValue) {
287982
+ function parsePseudoBigInt(stringValue2) {
287364
287983
  let log2Base;
287365
- switch (stringValue.charCodeAt(1)) {
287984
+ switch (stringValue2.charCodeAt(1)) {
287366
287985
  // "x" in "0x123"
287367
287986
  case 98:
287368
287987
  case 66:
@@ -287377,19 +287996,19 @@ ${lanes.join("\n")}
287377
287996
  log2Base = 4;
287378
287997
  break;
287379
287998
  default:
287380
- const nIndex = stringValue.length - 1;
287999
+ const nIndex = stringValue2.length - 1;
287381
288000
  let nonZeroStart = 0;
287382
- while (stringValue.charCodeAt(nonZeroStart) === 48) {
288001
+ while (stringValue2.charCodeAt(nonZeroStart) === 48) {
287383
288002
  nonZeroStart++;
287384
288003
  }
287385
- return stringValue.slice(nonZeroStart, nIndex) || "0";
288004
+ return stringValue2.slice(nonZeroStart, nIndex) || "0";
287386
288005
  }
287387
- const startIndex = 2, endIndex = stringValue.length - 1;
288006
+ const startIndex = 2, endIndex = stringValue2.length - 1;
287388
288007
  const bitsNeeded = (endIndex - startIndex) * log2Base;
287389
288008
  const segments = new Uint16Array((bitsNeeded >>> 4) + (bitsNeeded & 15 ? 1 : 0));
287390
288009
  for (let i2 = endIndex - 1, bitOffset = 0; i2 >= startIndex; i2--, bitOffset += log2Base) {
287391
288010
  const segment = bitOffset >>> 4;
287392
- const digitChar = stringValue.charCodeAt(i2);
288011
+ const digitChar = stringValue2.charCodeAt(i2);
287393
288012
  const digit = digitChar <= 57 ? digitChar - 48 : 10 + digitChar - (digitChar <= 70 ? 65 : 97);
287394
288013
  const shiftedDigit = digit << (bitOffset & 15);
287395
288014
  segments[segment] |= shiftedDigit;
@@ -516405,6 +517024,7 @@ __export(dist_exports, {
516405
517024
  YouTubeDownloadTool: () => YouTubeDownloadTool,
516406
517025
  addProjectConstraint: () => addProjectConstraint,
516407
517026
  addSessionConstraint: () => addSessionConstraint,
517027
+ aliasTool: () => aliasTool,
516408
517028
  applyPatch: () => applyPatch,
516409
517029
  audioGenerationDir: () => audioGenerationDir,
516410
517030
  audioGenerationSetupPlan: () => audioGenerationSetupPlan,
@@ -516496,6 +517116,7 @@ __export(dist_exports, {
516496
517116
  loadSkillContent: () => loadSkillContent,
516497
517117
  logsDir: () => logsDir,
516498
517118
  markRemindersSeen: () => markRemindersSeen,
517119
+ markRemindersTriggered: () => markRemindersTriggered,
516499
517120
  markReverted: () => markReverted,
516500
517121
  markSessionValidated: () => markSessionValidated,
516501
517122
  normalizeMcpName: () => normalizeMcpName,
@@ -516615,6 +517236,7 @@ var init_dist5 = __esm({
516615
517236
  init_scheduler();
516616
517237
  init_reminder();
516617
517238
  init_agenda();
517239
+ init_tool_alias();
516618
517240
  init_opencode();
516619
517241
  init_factory();
516620
517242
  init_cron_agent();
@@ -553804,9 +554426,14 @@ var init_command_registry = __esm({
553804
554426
  ["/access <loopback|lan|any>", "Set access policy (OMNIUS_ACCESS) and restart daemon"],
553805
554427
  ["/k <any|lan|loopback|config>", "Quick network access helper"],
553806
554428
  ["/scheduler", "Scheduled tasks control panel (list/kill/toggle)"],
554429
+ ["/cron", "Alias for /scheduler"],
553807
554430
  ["/scheduler menu", "Interactive scheduler menu (toggle/kill)"],
553808
554431
  ["/scheduler list", "List all scheduled tasks and timers"],
553809
554432
  ["/scheduler kill", "Kill schedulers + active runs (with escalation if needed)"],
554433
+ ["/reminder [list|all]", "List scoped TUI reminders"],
554434
+ ["/remind <message>", "Set a scoped minimal reminder for this TUI session"],
554435
+ ["/remind in 30m <message>", "Set a scoped minimal reminder with a relative due time"],
554436
+ ["/reminders", "Alias for /reminder"],
553810
554437
  ["/daemon", "Show or manage the local API daemon"],
553811
554438
  ["/daemon <start|stop|restart|takeover>", "Control the local API daemon"],
553812
554439
  ["/apikey", "Show current API key (for pasting into Web UI / clients)"],
@@ -553861,7 +554488,6 @@ var init_command_registry = __esm({
553861
554488
  ["/skin", "Configure the visual skin/theme bundle"],
553862
554489
  ["/toolsets", "Show or select tool exposure sets"],
553863
554490
  ["/browser connect|disconnect|status", "Manage browser-agent connectivity"],
553864
- ["/cron", "Scheduled/cron agent controls"],
553865
554491
  ["/reload-mcp", "Reload MCP servers and tools"],
553866
554492
  ["/plugins", "List or manage plugins"],
553867
554493
  ["/usage", "Show usage and quota details"],
@@ -553932,6 +554558,7 @@ var init_command_registry = __esm({
553932
554558
  network: "network",
553933
554559
  k: "network",
553934
554560
  scheduler: "runtime",
554561
+ reminder: "runtime",
553935
554562
  daemon: "runtime",
553936
554563
  apikey: "secret",
553937
554564
  key: "secret",
@@ -553998,6 +554625,8 @@ var init_command_registry = __esm({
553998
554625
  emojis: ["emoji"],
553999
554626
  compact: ["gc"],
554000
554627
  network: ["k"],
554628
+ scheduler: ["cron"],
554629
+ reminder: ["remind", "reminders"],
554001
554630
  statusbar: ["sb"],
554002
554631
  platforms: ["gateway"]
554003
554632
  };
@@ -554056,6 +554685,7 @@ var init_command_registry = __esm({
554056
554685
  "network",
554057
554686
  "k",
554058
554687
  "scheduler",
554688
+ "cron",
554059
554689
  "daemon",
554060
554690
  "fortemi",
554061
554691
  "mouse",
@@ -579989,6 +580619,69 @@ function safeLog(text) {
579989
580619
  process.stdout.write(text + "\n");
579990
580620
  }
579991
580621
  }
580622
+ function createSlashReminderTool(ctx3) {
580623
+ const sessionId = process.env["OMNIUS_SESSION_ID"] || "terminal";
580624
+ return new ReminderTool(ctx3.repoRoot, {
580625
+ defaultScope: {
580626
+ surface: "tui",
580627
+ visibility: "private",
580628
+ targetId: sessionId,
580629
+ sessionId,
580630
+ label: "terminal"
580631
+ },
580632
+ lockScope: true,
580633
+ allowActionDelivery: false,
580634
+ source: "user",
580635
+ createdFrom: "tui-slash"
580636
+ });
580637
+ }
580638
+ function splitReminderDue(raw) {
580639
+ const text = raw.trim().replace(/^set\s+/i, "").trim();
580640
+ const dueMatch = text.match(
580641
+ /^((?:in\s+\d+\s*(?:m|min|mins|minutes?|h|hr|hrs|hours?|d|days?|w|weeks?))|tomorrow|next week|next startup|startup|now|(?:today\s+)?at\s+\d{1,2}:\d{2})\s+(.+)$/i
580642
+ );
580643
+ if (!dueMatch) return { message: text };
580644
+ return { due: dueMatch[1].trim(), message: dueMatch[2].trim() };
580645
+ }
580646
+ async function handleReminderSlash(cmd, arg, ctx3) {
580647
+ const tool = createSlashReminderTool(ctx3);
580648
+ const trimmed = arg.trim();
580649
+ const lower = trimmed.toLowerCase();
580650
+ let args;
580651
+ if (!trimmed || lower === "list" || lower === "pending" || lower === "all" || lower === "seen") {
580652
+ args = { action: "list", filter: lower === "all" || lower === "seen" ? lower : "pending" };
580653
+ } else if (lower === "completed" || lower === "dismissed") {
580654
+ args = { action: "list", filter: lower };
580655
+ } else if (lower.startsWith("complete ")) {
580656
+ args = { action: "complete", id: trimmed.slice("complete ".length).trim() };
580657
+ } else if (lower.startsWith("dismiss ")) {
580658
+ args = { action: "dismiss", id: trimmed.slice("dismiss ".length).trim() };
580659
+ } else if (lower.startsWith("snooze ")) {
580660
+ const rest = trimmed.slice("snooze ".length).trim();
580661
+ const [id, ...dueParts] = rest.split(/\s+/);
580662
+ args = { action: "snooze", id, due: dueParts.join(" ").trim() || "in 1h" };
580663
+ } else {
580664
+ const { due, message: message2 } = splitReminderDue(trimmed);
580665
+ if (!message2) {
580666
+ renderInfo("Usage: /remind [in 30m|tomorrow|at 14:30] <message>");
580667
+ return "handled";
580668
+ }
580669
+ args = {
580670
+ action: "set",
580671
+ kind: "minimal",
580672
+ message: message2,
580673
+ content: message2,
580674
+ ...due ? { due } : {}
580675
+ };
580676
+ }
580677
+ const result = await tool.execute(args);
580678
+ if (!result.success) {
580679
+ renderError(result.error || `/${cmd} failed`);
580680
+ } else if (result.output) {
580681
+ safeLog(result.output);
580682
+ }
580683
+ return "handled";
580684
+ }
579992
580685
  function parseTelegramChatTarget(value2) {
579993
580686
  if (!value2) return null;
579994
580687
  if (/^-?\d+$/.test(value2)) return Number(value2);
@@ -580365,6 +581058,10 @@ async function handleSlashCommand(input, ctx3) {
580365
581058
  case "?":
580366
581059
  await showHelpMenu(ctx3);
580367
581060
  return "handled";
581061
+ case "reminder":
581062
+ case "remind":
581063
+ case "reminders":
581064
+ return handleReminderSlash(cmd, arg, ctx3);
580368
581065
  case "queue": {
580369
581066
  if (!arg) {
580370
581067
  const count2 = ctx3.queuedPromptCount?.() ?? 0;
@@ -583173,7 +583870,8 @@ sleep 1
583173
583870
  }
583174
583871
  return "handled";
583175
583872
  }
583176
- case "scheduler": {
583873
+ case "scheduler":
583874
+ case "cron": {
583177
583875
  const tokens = (arg || "").trim().length ? (arg || "").trim().split(/\s+/) : [];
583178
583876
  const sub = (tokens[0] || "menu").toLowerCase();
583179
583877
  const base3 = `http://127.0.0.1:${process.env["OMNIUS_PORT"] || "11435"}`;
@@ -595630,10 +596328,14 @@ var init_bless_engine = __esm({
595630
596328
  * Priority queue is checked first, then reminders/attention items are ingested. */
595631
596329
  async getNextTask() {
595632
596330
  try {
595633
- const dueReminders = await getDueReminders(this.repoRoot);
596331
+ const dueReminders = await getDueReminders(this.repoRoot, {
596332
+ deliveryMode: "action",
596333
+ includeUnscoped: true
596334
+ });
595634
596335
  for (const r2 of dueReminders.filter((r3) => r3.status === "pending")) {
595635
596336
  const priority = r2.priority === "critical" ? "critical" : r2.priority === "high" ? "high" : "moderate";
595636
- const prompt = `${priority === "critical" ? "URGENT " : ""}REMINDER: ${r2.message}${r2.tags?.length ? ` [tags: ${r2.tags.join(", ")}]` : ""}. Check the agenda for full context and take appropriate action.`;
596337
+ const task2 = r2.delivery?.taskDescription || r2.message;
596338
+ const prompt = `${priority === "critical" ? "URGENT " : ""}REMINDER ACTION: ${task2}${r2.tags?.length ? ` [tags: ${r2.tags.join(", ")}]` : ""}. Check the agenda for full context and take appropriate action.`;
595637
596339
  this.priorityEngine.ingest(prompt, "internal", "reminder", { reminderId: r2.id }, priority);
595638
596340
  }
595639
596341
  } catch {
@@ -597342,7 +598044,7 @@ var init_tool_policy = __esm({
597342
598044
  "aiwg_workflow",
597343
598045
  "autoresearch",
597344
598046
  "scheduler",
597345
- "reminder",
598047
+ "cronjob",
597346
598048
  "agenda"
597347
598049
  ]);
597348
598050
  SAFE_PUBLIC_TOOLS = /* @__PURE__ */ new Set([
@@ -597362,6 +598064,9 @@ var init_tool_policy = __esm({
597362
598064
  "transcribe_file",
597363
598065
  "video_understand",
597364
598066
  "audio_analyze",
598067
+ "reminder",
598068
+ "remind",
598069
+ "reminders",
597365
598070
  "explore_tools",
597366
598071
  "telegram_media_recent",
597367
598072
  "generate_image",
@@ -597389,6 +598094,9 @@ var init_tool_policy = __esm({
597389
598094
  "transcribe_file",
597390
598095
  "video_understand",
597391
598096
  "audio_analyze",
598097
+ "reminder",
598098
+ "remind",
598099
+ "reminders",
597392
598100
  "explore_tools",
597393
598101
  "telegram_media_recent",
597394
598102
  "generate_image",
@@ -598465,6 +599173,14 @@ function renderTelegramLiveProgressHTML(progressLines, accumulated) {
598465
599173
  }
598466
599174
  return progressLines.slice(-6).map((line) => line.trim()).filter((line) => !isTelegramInternalStatusText(line)).filter(Boolean).join("\n");
598467
599175
  }
599176
+ function splitTelegramReminderDue(raw) {
599177
+ const text = raw.trim().replace(/^set\s+/i, "").trim();
599178
+ const dueMatch = text.match(
599179
+ /^((?:in\s+\d+\s*(?:m|min|mins|minutes?|h|hr|hrs|hours?|d|days?|w|weeks?))|tomorrow|next week|next startup|startup|now|(?:today\s+)?at\s+\d{1,2}:\d{2})\s+(.+)$/i
599180
+ );
599181
+ if (!dueMatch) return { message: text };
599182
+ return { due: dueMatch[1].trim(), message: dueMatch[2].trim() };
599183
+ }
598468
599184
  function telegramSyntheticHelpSignatures() {
598469
599185
  return [
598470
599186
  { signature: "/help", description: "Show Telegram command help" },
@@ -598954,7 +599670,7 @@ function renderTelegramSubAgentError(username, error) {
598954
599670
  process.stdout.write(` ${c3.dim("⎿")} ${c3.red("✘")} @${username}: ${c3.dim(preview)}
598955
599671
  `);
598956
599672
  }
598957
- var TELEGRAM_SAFETY_PROMPT, ADMIN_DM_PROMPT, ADMIN_GROUP_PROMPT, TELEGRAM_PUBLIC_SOUL_PROFILE, TELEGRAM_PUBLIC_ORCHESTRATOR_CONTRACT, TELEGRAM_PUBLIC_MEMORY_SCOPE_CONTRACT, GROUP_REPLY_DISCRETION_PROMPT, TELEGRAM_CHAT_MODE_PROMPT, ADMIN_CHAT_PROFILE_PROMPT, TELEGRAM_ACTION_RESPONSE_CONTRACT, TELEGRAM_CHAT_HISTORY_LIMIT, TELEGRAM_CONTEXT_RECENT_DEFAULT, TELEGRAM_CONTEXT_LINE_LIMIT, TELEGRAM_CONTEXT_SAMPLE_LIMIT, TELEGRAM_MEMORY_CARD_LIMIT, TELEGRAM_MEMORY_NOTE_LIMIT, TELEGRAM_MEMORY_STOPWORDS, TELEGRAM_PUBLIC_HELP_COMMANDS, TELEGRAM_IMAGE_EXTENSIONS, MEDIA_CACHE_TTL_MS, TelegramBridge;
599673
+ var TELEGRAM_SAFETY_PROMPT, ADMIN_DM_PROMPT, ADMIN_GROUP_PROMPT, TELEGRAM_PUBLIC_SOUL_PROFILE, TELEGRAM_PUBLIC_ORCHESTRATOR_CONTRACT, TELEGRAM_PUBLIC_MEMORY_SCOPE_CONTRACT, GROUP_REPLY_DISCRETION_PROMPT, TELEGRAM_CHAT_MODE_PROMPT, ADMIN_CHAT_PROFILE_PROMPT, TELEGRAM_ACTION_RESPONSE_CONTRACT, TELEGRAM_CHAT_HISTORY_LIMIT, TELEGRAM_CONTEXT_RECENT_DEFAULT, TELEGRAM_CONTEXT_LINE_LIMIT, TELEGRAM_CONTEXT_SAMPLE_LIMIT, TELEGRAM_MEMORY_CARD_LIMIT, TELEGRAM_MEMORY_NOTE_LIMIT, TELEGRAM_MEMORY_STOPWORDS, TELEGRAM_PUBLIC_HELP_COMMANDS, TELEGRAM_REMINDER_SLASH_COMMANDS, TELEGRAM_IMAGE_EXTENSIONS, MEDIA_CACHE_TTL_MS, TelegramBridge;
598958
599674
  var init_telegram_bridge = __esm({
598959
599675
  "packages/cli/src/tui/telegram-bridge.ts"() {
598960
599676
  "use strict";
@@ -599150,6 +599866,7 @@ Telegram response contract:
599150
599866
  "your"
599151
599867
  ]);
599152
599868
  TELEGRAM_PUBLIC_HELP_COMMANDS = /* @__PURE__ */ new Set(["help", "start", "auth", "call"]);
599869
+ TELEGRAM_REMINDER_SLASH_COMMANDS = /* @__PURE__ */ new Set(["remind", "reminder", "reminders"]);
599153
599870
  TELEGRAM_IMAGE_EXTENSIONS = /* @__PURE__ */ new Set([".png", ".jpg", ".jpeg", ".gif", ".webp", ".bmp", ".tiff", ".tif", ".svg"]);
599154
599871
  MEDIA_CACHE_TTL_MS = 30 * 60 * 1e3;
599155
599872
  TelegramBridge = class {
@@ -599350,6 +600067,49 @@ Telegram response contract:
599350
600067
  const botless = first2.startsWith("/") ? first2.slice(1).split("@")[0]?.toLowerCase() : "";
599351
600068
  return !!botless && this.telegramCommandMap.has(botless);
599352
600069
  }
600070
+ async handleTelegramReminderSlash(msg, commandText, context2) {
600071
+ if (!this.repoRoot) {
600072
+ await this.replyToTelegramMessage(msg, "Reminder storage is not available yet.");
600073
+ return;
600074
+ }
600075
+ const first2 = commandText.trim().split(/\s+/)[0] ?? "";
600076
+ const cmd = first2.slice(1).split("@")[0]?.toLowerCase() || "reminder";
600077
+ const arg = commandText.trim().slice(first2.length).trim();
600078
+ const lower = arg.toLowerCase();
600079
+ const tool = this.buildTelegramReminderTool(context2, this.repoRoot, msg.chatId, msg);
600080
+ let args;
600081
+ if (!arg || lower === "list" || lower === "pending" || lower === "all" || lower === "seen") {
600082
+ args = { action: "list", filter: lower === "all" || lower === "seen" ? lower : "pending" };
600083
+ } else if (lower === "completed" || lower === "dismissed") {
600084
+ args = { action: "list", filter: lower };
600085
+ } else if (lower.startsWith("complete ")) {
600086
+ args = { action: "complete", id: arg.slice("complete ".length).trim() };
600087
+ } else if (lower.startsWith("dismiss ")) {
600088
+ args = { action: "dismiss", id: arg.slice("dismiss ".length).trim() };
600089
+ } else if (lower.startsWith("snooze ")) {
600090
+ const rest = arg.slice("snooze ".length).trim();
600091
+ const [id, ...dueParts] = rest.split(/\s+/);
600092
+ args = { action: "snooze", id, due: dueParts.join(" ").trim() || "in 1h" };
600093
+ } else {
600094
+ const { due, message: message2 } = splitTelegramReminderDue(arg);
600095
+ if (!message2) {
600096
+ await this.replyToTelegramMessage(msg, "Usage: /remind [in 30m|tomorrow|at 14:30] <message>");
600097
+ return;
600098
+ }
600099
+ args = {
600100
+ action: "set",
600101
+ kind: "minimal",
600102
+ message: message2,
600103
+ content: message2,
600104
+ ...due ? { due } : {}
600105
+ };
600106
+ }
600107
+ const result = await tool.execute(args);
600108
+ await this.replyToTelegramMessage(
600109
+ msg,
600110
+ result.success ? result.output || "Reminder updated." : result.error || `/${cmd} failed`
600111
+ );
600112
+ }
599353
600113
  /** Write to the scrollable TUI waterfall area (respects status bar scroll region) */
599354
600114
  tuiWrite(fn) {
599355
600115
  if (this.writeContent) {
@@ -600424,6 +601184,11 @@ ${TELEGRAM_PUBLIC_ORCHESTRATOR_CONTRACT}`);
600424
601184
  const toolContext = this.resolveToolContext(msg, isAdmin);
600425
601185
  const isAdminDM = toolContext === "telegram-admin-dm";
600426
601186
  const sessionKey = this.sessionKeyForMessage(msg);
601187
+ const telegramSlash = this.telegramSlashName(normalizedCommandText);
601188
+ if (msg.text.trim().startsWith("/") && TELEGRAM_REMINDER_SLASH_COMMANDS.has(telegramSlash)) {
601189
+ await this.handleTelegramReminderSlash(msg, normalizedCommandText, toolContext);
601190
+ return;
601191
+ }
600427
601192
  if (msg.text.trim().toLowerCase() === "/hangup" && isAdmin && this.callStopper) {
600428
601193
  const callUrl = this.callUrlGetter?.();
600429
601194
  if (callUrl) {
@@ -600478,7 +601243,6 @@ Join: ${newUrl}`);
600478
601243
  return;
600479
601244
  }
600480
601245
  }
600481
- const telegramSlash = this.telegramSlashName(normalizedCommandText);
600482
601246
  const publicCreativeSlash = telegramSlash === "image";
600483
601247
  if (!isAdmin && msg.text.trim().startsWith("/") && this.isKnownTelegramSlash(normalizedCommandText) && !publicCreativeSlash) {
600484
601248
  await this.replyToTelegramMessage(
@@ -601571,7 +602335,8 @@ Scoped workspace: ${scopedRoot}`,
601571
602335
  new VideoUnderstandTool(repoRoot),
601572
602336
  new AudioAnalyzeTool(),
601573
602337
  new ExploreToolsTool(),
601574
- this.buildTelegramMediaRecentTool(chatId, msg)
602338
+ this.buildTelegramMediaRecentTool(chatId, msg),
602339
+ ...this.buildTelegramReminderTools(context2, repoRoot, chatId, msg)
601575
602340
  ];
601576
602341
  const adminTools = [
601577
602342
  new ShellTool(repoRoot),
@@ -601629,6 +602394,7 @@ Scoped workspace: ${scopedRoot}`,
601629
602394
  new ImpactAnalysisTool(repoRoot),
601630
602395
  new CodeNeighborsTool(repoRoot),
601631
602396
  new ProcessHealthTool(),
602397
+ ...this.buildTelegramReminderTools(context2, repoRoot, chatId, msg),
601632
602398
  fullSubAgentTool,
601633
602399
  this.buildTelegramSendFileTool(context2, repoRoot, chatId, msg)
601634
602400
  ];
@@ -601674,6 +602440,34 @@ Scoped workspace: ${scopedRoot}`,
601674
602440
  ]);
601675
602441
  return tools.filter((tool) => !blocked.has(tool.name));
601676
602442
  }
602443
+ buildTelegramReminderTool(context2, repoRoot, chatId, currentMsg) {
602444
+ const rawChatId = String(chatId ?? currentMsg?.chatId ?? "unknown");
602445
+ const isPrivate2 = currentMsg?.chatType === "private";
602446
+ const username = currentMsg?.username ? currentMsg.username.replace(/^@/, "") : void 0;
602447
+ return new ReminderTool(repoRoot, {
602448
+ defaultScope: {
602449
+ surface: "telegram",
602450
+ visibility: isPrivate2 ? "private" : "public",
602451
+ targetId: rawChatId,
602452
+ chatId: rawChatId,
602453
+ ...currentMsg?.fromUserId !== void 0 ? { userId: String(currentMsg.fromUserId) } : {},
602454
+ ...username ? { username } : {},
602455
+ label: isPrivate2 ? `telegram dm${username ? ` @${username}` : ""}` : `telegram group ${currentMsg?.chatTitle ?? rawChatId}`
602456
+ },
602457
+ lockScope: true,
602458
+ allowActionDelivery: context2 === "telegram-admin-dm",
602459
+ source: "user",
602460
+ createdFrom: context2
602461
+ });
602462
+ }
602463
+ buildTelegramReminderTools(context2, repoRoot, chatId, currentMsg) {
602464
+ const reminder = this.buildTelegramReminderTool(context2, repoRoot, chatId, currentMsg);
602465
+ return [
602466
+ reminder,
602467
+ aliasTool(reminder, "remind", "Alias for reminder: set scoped Telegram reminders."),
602468
+ aliasTool(reminder, "reminders", "Alias for reminder: list and manage scoped Telegram reminders.")
602469
+ ];
602470
+ }
601677
602471
  buildTelegramMediaRecentTool(chatId, currentMsg) {
601678
602472
  const bridge = this;
601679
602473
  return {
@@ -626784,6 +627578,33 @@ function adaptTool6(tool) {
626784
627578
  }
626785
627579
  };
626786
627580
  }
627581
+ function createTuiReminderOptions(allowActionDelivery = true) {
627582
+ const sessionId = process.env["OMNIUS_SESSION_ID"] || "terminal";
627583
+ return {
627584
+ defaultScope: {
627585
+ surface: "tui",
627586
+ visibility: "private",
627587
+ targetId: sessionId,
627588
+ sessionId,
627589
+ label: "terminal"
627590
+ },
627591
+ allowActionDelivery,
627592
+ createdFrom: "tui"
627593
+ };
627594
+ }
627595
+ function withReminderAliases(reminder) {
627596
+ return [
627597
+ reminder,
627598
+ aliasTool(reminder, "remind", "Alias for reminder: set or list scoped minimal reminders and action triggers."),
627599
+ aliasTool(reminder, "reminders", "Alias for reminder: list and manage scoped reminders.")
627600
+ ];
627601
+ }
627602
+ function withSchedulerAliases(scheduler) {
627603
+ return [
627604
+ scheduler,
627605
+ aliasTool(scheduler, "cronjob", "Alias for scheduler: create, list, and manage OS cron-backed time triggers.")
627606
+ ];
627607
+ }
626787
627608
  function scanForSessionSignals(toolOutput) {
626788
627609
  if (/SESSION_ACTIVE\s*=\s*true/i.test(toolOutput)) {
626789
627610
  _interactiveSessionActive = true;
@@ -627124,8 +627945,8 @@ function buildSubAgentTools(repoRoot, config) {
627124
627945
  new AiwgHealthTool(repoRoot),
627125
627946
  new AiwgWorkflowTool(repoRoot),
627126
627947
  // Scheduler + agenda (time-based tooling)
627127
- new SchedulerTool(repoRoot),
627128
- new ReminderTool(repoRoot),
627948
+ ...withSchedulerAliases(new SchedulerTool(repoRoot)),
627949
+ ...withReminderAliases(new ReminderTool(repoRoot, createTuiReminderOptions())),
627129
627950
  new AgendaTool(repoRoot),
627130
627951
  // Vision + multimodal memory
627131
627952
  new VisionTool(repoRoot),
@@ -627238,8 +628059,8 @@ function buildTools(repoRoot, config, contextWindowSize, modelTier) {
627238
628059
  // Autoresearch (autonomous ML experiment loop)
627239
628060
  new AutoresearchTool(repoRoot),
627240
628061
  // Temporal agency (scheduling, reminders, attention steering)
627241
- new SchedulerTool(repoRoot),
627242
- new ReminderTool(repoRoot),
628062
+ ...withSchedulerAliases(new SchedulerTool(repoRoot)),
628063
+ ...withReminderAliases(new ReminderTool(repoRoot, createTuiReminderOptions())),
627243
628064
  new AgendaTool(repoRoot),
627244
628065
  // OpenCode + Factory AI delegation + long-horizon cron agent
627245
628066
  new OpenCodeTool(repoRoot),
@@ -630555,6 +631376,7 @@ ${result.summary}`
630555
631376
  let telegramBridge = null;
630556
631377
  let activeTelegramChatId = null;
630557
631378
  let dmnRetriggerTimer = null;
631379
+ let reminderDispatchTimer = null;
630558
631380
  function scheduleDMNRetrigger(delayMs) {
630559
631381
  if (dmnRetriggerTimer) clearTimeout(dmnRetriggerTimer);
630560
631382
  dmnRetriggerTimer = setTimeout(async () => {
@@ -630629,10 +631451,10 @@ Rationale: ${proposal.rationale}${provenanceNote}`;
630629
631451
  const turnQueue = [];
630630
631452
  let queueDrainScheduled = false;
630631
631453
  let sessionTitle = null;
630632
- const queuePrompt = (prompt, source = "user") => {
631454
+ const queuePrompt = (prompt, source = "user", options2 = {}) => {
630633
631455
  const trimmed = prompt.trim();
630634
631456
  if (!trimmed) return turnQueue.length;
630635
- turnQueue.push({ prompt: trimmed, source, enqueuedAt: Date.now() });
631457
+ turnQueue.push({ prompt: trimmed, source, enqueuedAt: Date.now(), ...options2 });
630636
631458
  return turnQueue.length;
630637
631459
  };
630638
631460
  const clearQueuedPrompts = () => {
@@ -630657,9 +631479,103 @@ Rationale: ${proposal.rationale}${provenanceNote}`;
630657
631479
  `Starting queued prompt from ${next.source} (${turnQueue.length} remaining).`
630658
631480
  )
630659
631481
  );
631482
+ if (next.telegramChatId !== void 0) activeTelegramChatId = next.telegramChatId;
630660
631483
  rl.emit("line", next.prompt);
630661
631484
  }, 0);
630662
631485
  };
631486
+ const reminderTelegramTarget = (scope) => {
631487
+ if (!scope || scope.surface !== "telegram") return null;
631488
+ const raw = scope.chatId ?? scope.targetId;
631489
+ if (!raw) return null;
631490
+ return /^-?\d+$/.test(raw) ? Number(raw) : raw;
631491
+ };
631492
+ const reminderText = (reminder) => {
631493
+ const content = reminder.delivery?.content || reminder.message;
631494
+ const prefix = reminder.priority === "critical" ? "URGENT reminder" : "Reminder";
631495
+ return `${prefix}: ${content}`;
631496
+ };
631497
+ const reminderActionPrompt = (reminder) => {
631498
+ const task = reminder.delivery?.taskDescription || reminder.delivery?.prompt || reminder.message;
631499
+ const scope = reminder.scope ? `${reminder.scope.surface}/${reminder.scope.visibility}/${reminder.scope.targetId}` : "unscoped";
631500
+ return [
631501
+ `[Scheduled reminder action | ${reminder.id}]`,
631502
+ ``,
631503
+ `Trigger: ${reminder.message}`,
631504
+ `Scope: ${scope}`,
631505
+ reminder.context ? `Context: ${reminder.context}` : "",
631506
+ reminder.tags?.length ? `Tags: ${reminder.tags.join(", ")}` : "",
631507
+ ``,
631508
+ `Task description:`,
631509
+ task
631510
+ ].filter(Boolean).join("\n");
631511
+ };
631512
+ const dispatchDueReminder = async (reminder) => {
631513
+ const mode = reminder.delivery?.mode ?? "minimal";
631514
+ const scope = reminder.scope;
631515
+ if (scope?.surface === "telegram") {
631516
+ const target = reminderTelegramTarget(scope);
631517
+ if (!target || !telegramBridge?.isActive) return false;
631518
+ if (mode === "minimal") {
631519
+ await telegramBridge.sendMessage(target, reminderText(reminder));
631520
+ return true;
631521
+ }
631522
+ if (activeTask) return false;
631523
+ activeTelegramChatId = target;
631524
+ rl.emit("line", `${TELEGRAM_SAFETY_PROMPT}
631525
+
631526
+ ---
631527
+
631528
+ ${reminderActionPrompt(reminder)}
631529
+
631530
+ Respond to the scoped Telegram target when complete.`);
631531
+ return true;
631532
+ }
631533
+ if (scope?.surface === "gui") {
631534
+ if (mode === "minimal") {
631535
+ writeContent(() => renderInfo(reminderText(reminder)));
631536
+ showPrompt();
631537
+ return true;
631538
+ }
631539
+ if (activeTask) return false;
631540
+ queuePrompt(reminderActionPrompt(reminder), "reminder");
631541
+ drainQueuedPrompts();
631542
+ return true;
631543
+ }
631544
+ if (mode === "minimal") {
631545
+ writeContent(() => renderInfo(reminderText(reminder)));
631546
+ showPrompt();
631547
+ return true;
631548
+ }
631549
+ if (activeTask) {
631550
+ queuePrompt(reminderActionPrompt(reminder), "reminder");
631551
+ return true;
631552
+ }
631553
+ rl.emit("line", reminderActionPrompt(reminder));
631554
+ return true;
631555
+ };
631556
+ const dispatchDueReminders = async () => {
631557
+ try {
631558
+ const due = await getDueReminders(repoRoot, { deliveryMode: "any", includeUnscoped: true });
631559
+ const dispatched = [];
631560
+ for (const reminder of due) {
631561
+ const ok2 = await dispatchDueReminder(reminder);
631562
+ if (ok2) dispatched.push(reminder.id);
631563
+ }
631564
+ if (dispatched.length > 0) {
631565
+ await markRemindersTriggered(repoRoot, dispatched);
631566
+ }
631567
+ } catch {
631568
+ }
631569
+ };
631570
+ const startReminderDispatcher = () => {
631571
+ if (reminderDispatchTimer) return;
631572
+ reminderDispatchTimer = setInterval(() => {
631573
+ dispatchDueReminders().catch(() => {
631574
+ });
631575
+ }, 15e3);
631576
+ dispatchDueReminders().catch(() => {
631577
+ });
631578
+ };
630663
631579
  const listRollbackCheckpoints = () => {
630664
631580
  const out = [];
630665
631581
  const pushJsonFiles = (dir, prefix) => {
@@ -631752,6 +632668,10 @@ Log: ${nexusLogPath}`)
631752
632668
  statusBar.refreshHeaderContent();
631753
632669
  },
631754
632670
  exit() {
632671
+ if (reminderDispatchTimer) {
632672
+ clearInterval(reminderDispatchTimer);
632673
+ reminderDispatchTimer = null;
632674
+ }
631755
632675
  statusBar.deactivate();
631756
632676
  if (carousel.isRunning) carousel.stop();
631757
632677
  banner.stop();
@@ -633697,17 +634617,12 @@ Respond concisely and safely. Remember: you are talking to the general public.`;
633697
634617
  });
633698
634618
  showPrompt();
633699
634619
  }, 300);
633700
- if (dueReminders.length > 0) {
633701
- await markRemindersSeen(
633702
- repoRoot,
633703
- dueReminders.map((r2) => r2.id)
633704
- );
633705
- }
633706
634620
  }
633707
634621
  } catch {
633708
634622
  }
633709
634623
  })();
633710
634624
  }
634625
+ startReminderDispatcher();
633711
634626
  if (hasTaskToResume) {
633712
634627
  const pendingTask = loadPendingTask(repoRoot);
633713
634628
  if (pendingTask) {