obol-ai 0.3.26 → 0.3.28

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "obol-ai",
3
- "version": "0.3.26",
3
+ "version": "0.3.28",
4
4
  "description": "Self-evolving AI assistant that learns, remembers, and acts on its own. Persistent vector memory, self-rewriting personality, proactive heartbeats.",
5
5
  "main": "src/index.js",
6
6
  "bin": {
@@ -144,7 +144,7 @@ const handlers = {
144
144
  const fields = {};
145
145
  if (rest.title !== undefined) fields.title = rest.title;
146
146
  if (rest.description !== undefined) fields.description = rest.description;
147
- if (rest.instructions !== undefined) fields.instructions = rest.instructions || null;
147
+ if (typeof rest.instructions === 'string') fields.instructions = rest.instructions || null;
148
148
  if (rest.timezone !== undefined) fields.timezone = rest.timezone;
149
149
  if (rest.cron_expr !== undefined) fields.cron_expr = rest.cron_expr;
150
150
  if (rest.max_runs !== undefined) fields.max_runs = rest.max_runs;
@@ -50,7 +50,7 @@ async function runCuriosityDispatch(client, selfMemory, users) {
50
50
  properties: {
51
51
  user_id: { type: 'string', description: 'The user ID to share with' },
52
52
  hint: { type: 'string', description: 'The insight or finding to share, in your own words' },
53
- delay: { type: 'string', description: 'When to share it — e.g. "2h", "1d", "3d", "1w"' },
53
+ delay: { type: 'string', description: 'When to share it — e.g. "2h", "1d", "2d", "3d" (max 3 days)' },
54
54
  },
55
55
  required: ['user_id', 'hint', 'delay'],
56
56
  },
@@ -132,7 +132,7 @@ async function handleTool(name, input, selfMemory, userMap) {
132
132
  if (!user) return 'User not found';
133
133
  if (!user.scheduler) return 'User has no scheduler';
134
134
 
135
- const dueAt = resolveDelay(input.delay);
135
+ const dueAt = resolveDelay(input.delay, user.timezone, null, 3 * 86400000);
136
136
  const instructions = `You came across something during your own free exploration: "${input.hint}". If it feels relevant and the moment is right, bring it up naturally — like you just thought of it. Keep it casual. Don't reference any system.`;
137
137
 
138
138
  try {
@@ -51,7 +51,7 @@ async function runCuriosityHumor(client, selfMemory, users) {
51
51
  properties: {
52
52
  user_id: { type: 'string', description: 'The user ID to share with' },
53
53
  hint: { type: 'string', description: 'The pun, funny connection, or inside joke — just the content itself. Can include a URL if a news article or link is part of what makes it funny.' },
54
- delay: { type: 'string', description: 'When to drop it — e.g. "2h", "1d", "3d", "1w"' },
54
+ delay: { type: 'string', description: 'When to drop it — e.g. "2h", "1d", "2d", "3d" (max 3 days)' },
55
55
  },
56
56
  required: ['user_id', 'hint', 'delay'],
57
57
  },
@@ -153,7 +153,7 @@ async function handleTool(name, input, selfMemory, userMap) {
153
153
  if (!user) return 'User not found';
154
154
  if (!user.scheduler) return 'User has no scheduler';
155
155
 
156
- const dueAt = resolveDelay(input.delay);
156
+ const dueAt = resolveDelay(input.delay, user.timezone, null, 3 * 86400000);
157
157
  const instructions = `You spotted something funny during your own explorations: "${input.hint}". If the moment is right, drop it casually — a pun you just thought of, a funny connection, an inside reference. Don't explain it. Don't say it's a joke. Just let it land.`;
158
158
 
159
159
  try {
@@ -37,7 +37,7 @@ function createScheduler(supabaseConfig, userId = 0) {
37
37
  async function list(opts = {}) {
38
38
  const status = opts.status || 'pending';
39
39
  const limit = opts.limit || 20;
40
- const fetchUrl = `${url}/rest/v1/obol_events?user_id=eq.${userId}&status=eq.${status}&order=due_at.asc&limit=${limit}&select=id,title,description,due_at,timezone,status,created_at,cron_expr,last_run_at,run_count,max_runs,ends_at`;
40
+ const fetchUrl = `${url}/rest/v1/obol_events?user_id=eq.${userId}&status=eq.${status}&order=due_at.asc&limit=${limit}&select=id,title,description,instructions,due_at,timezone,status,created_at,cron_expr,last_run_at,run_count,max_runs,ends_at`;
41
41
  const res = await fetch(fetchUrl, { headers });
42
42
  const data = await res.json();
43
43
  if (!res.ok) throw new Error(JSON.stringify(data));
@@ -1,15 +1,21 @@
1
1
  const DEFAULT_PEAK_HOUR = 20;
2
2
 
3
- function resolveDelay(input, timezone, timingData) {
3
+ function resolveDelay(input, timezone, timingData, maxMs) {
4
4
  const dt = parseDateTime(input, timezone, timingData);
5
- if (dt) return dt;
5
+ if (dt) return clamp(dt, maxMs);
6
6
 
7
7
  const legacy = parseLegacy(input);
8
- if (legacy) return legacy;
8
+ if (legacy) return clamp(legacy, maxMs);
9
9
 
10
10
  const peakHour = extractPeakHour(timingData);
11
11
  const now = localNow(timezone);
12
- return targetPeak(now, peakHour, 1, timezone);
12
+ return clamp(targetPeak(now, peakHour, 1, timezone), maxMs);
13
+ }
14
+
15
+ function clamp(iso, maxMs) {
16
+ if (!maxMs) return iso;
17
+ const ceiling = new Date(Date.now() + maxMs);
18
+ return new Date(iso) > ceiling ? ceiling.toISOString() : iso;
13
19
  }
14
20
 
15
21
  function parseDateTime(input, timezone, timingData) {