@c4t4/heyamigo 0.9.21 → 0.9.24

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.
@@ -9,7 +9,7 @@ import { estimate as estimateJob } from '../estimates/index.js';
9
9
  import { extractFlags, filterFlagsByRole } from '../memory/digest-flag.js';
10
10
  import { isValidSlug } from '../memory/journals.js';
11
11
  import { enqueueAsyncTask, enqueueBrowserTask } from './async-tasks.js';
12
- import { enqueueCron } from './crons.js';
12
+ import { addCronUsage, enqueueCron } from './crons.js';
13
13
  import { enqueueMemoryWrite } from './memory-writes.js';
14
14
  import { enqueueOutbound } from './outbound.js';
15
15
  import { formatLocalTime, resolveTimeExpression } from './time-expr.js';
@@ -104,6 +104,12 @@ async function callClaude(job) {
104
104
  if (job.senderNumber) {
105
105
  addDailyTokens(job.senderNumber, turnInput + turnOutput);
106
106
  }
107
+ // Cron-attribution: if this Job was synthesized by a [CRON: ... PROMPT]
108
+ // firing, charge the cron row's running total. Lets /crons show
109
+ // cumulative token cost per recurring schedule.
110
+ if (job.cronId) {
111
+ addCronUsage(job.cronId, turnInput, turnOutput);
112
+ }
107
113
  const rawFlags = extractFlags(reply);
108
114
  const { clean, digest, journals, journalCreates, asyncTasks, asyncBrowserTasks, sendTexts, crons, reminds, } = filterFlagsByRole(rawFlags, job.allowedTags);
109
115
  // Detect any stripped tags so we can log + nudge the role config
@@ -245,25 +251,62 @@ async function callClaude(job) {
245
251
  const cronBase = `chat-cron-${job.jid}-${Date.now()}`;
246
252
  for (let i = 0; i < crons.length; i++) {
247
253
  const c = crons[i];
254
+ // Variant maps to enqueueInto + payload shape. SAY stays as the
255
+ // current behavior (text delivery, no AI). PROMPT/ASYNC/BROWSER
256
+ // route through inbound/async/browser respectively at fire time.
257
+ let enqueueInto;
258
+ let payload;
259
+ switch (c.variant) {
260
+ case 'SAY':
261
+ enqueueInto = 'outbound';
262
+ payload = { address: chatAddress, kind: 'text', text: c.body };
263
+ break;
264
+ case 'PROMPT':
265
+ enqueueInto = 'inbound';
266
+ payload = {
267
+ address: chatAddress,
268
+ prompt: c.body,
269
+ senderNumber: job.senderNumber,
270
+ personId: null, // resolved at preamble time
271
+ };
272
+ break;
273
+ case 'ASYNC':
274
+ enqueueInto = 'async';
275
+ payload = {
276
+ address: chatAddress,
277
+ description: c.body,
278
+ senderNumber: job.senderNumber,
279
+ };
280
+ break;
281
+ case 'BROWSER':
282
+ enqueueInto = 'browser';
283
+ payload = {
284
+ address: chatAddress,
285
+ description: c.body,
286
+ senderNumber: job.senderNumber,
287
+ };
288
+ break;
289
+ }
248
290
  try {
249
291
  enqueueCron({
250
292
  name: `${cronBase}-${i}`,
251
- enqueueInto: 'outbound',
252
- payload: { address: chatAddress, kind: 'text', text: c.body },
293
+ enqueueInto,
294
+ payload,
253
295
  recurrence: c.recurrence,
254
- // Sender's local timezone so @daily HH:MM fires in their
255
- // wall-clock time, not the server's.
296
+ // Sender's local timezone so cron expressions like "0 9 * * *"
297
+ // fire at their wall-clock 9am, not the server's.
256
298
  timezone: senderTz,
257
299
  });
258
300
  logger.info({
259
301
  jid: job.jid,
260
302
  recurrence: c.recurrence,
303
+ variant: c.variant,
261
304
  tz: senderTz,
262
305
  chars: c.body.length,
263
306
  }, 'CRON tag scheduled');
264
307
  }
265
308
  catch (err) {
266
- logger.warn({ err, jid: job.jid, recurrence: c.recurrence }, 'CRON tag failed (bad recurrence?)');
309
+ logger.warn({ err, jid: job.jid, recurrence: c.recurrence, variant: c.variant }, 'CRON tag failed (bad recurrence?)');
267
310
  }
268
311
  }
269
312
  // REMIND resolution uses the same senderTz computed above.
@@ -0,0 +1,3 @@
1
+ ALTER TABLE `crons` ADD `fire_count` integer DEFAULT 0 NOT NULL;--> statement-breakpoint
2
+ ALTER TABLE `crons` ADD `total_input_tokens` integer DEFAULT 0 NOT NULL;--> statement-breakpoint
3
+ ALTER TABLE `crons` ADD `total_output_tokens` integer DEFAULT 0 NOT NULL;