@c4t4/heyamigo 0.9.24 → 0.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/config/memory-instructions.md +54 -5
- package/dist/config.js +21 -0
- package/dist/db/schema.js +55 -0
- package/dist/gateway/commands.js +13 -1
- package/dist/gateway/outgoing.js +46 -4
- package/dist/memory/digest-flag.js +222 -0
- package/dist/memory/journals.js +3 -24
- package/dist/memory/preamble.js +21 -0
- package/dist/memory/scheduler.js +6 -32
- package/dist/queue/thread-list.js +167 -0
- package/dist/queue/thread-weights.js +142 -0
- package/dist/queue/threads.js +271 -0
- package/dist/queue/worker.js +113 -2
- package/migrations/0010_create_threads.sql +27 -0
- package/migrations/meta/0010_snapshot.json +1134 -0
- package/migrations/meta/_journal.json +7 -0
- package/package.json +1 -1
- package/dist/memory/journal-cadence.js +0 -120
- package/dist/memory/journal-nudger.js +0 -221
package/dist/queue/worker.js
CHANGED
|
@@ -10,6 +10,8 @@ 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
12
|
import { addCronUsage, enqueueCron } from './crons.js';
|
|
13
|
+
import { compressThread, coolThread, createThread, dropThread, resolveThread, touchThread, updateThread, } from './threads.js';
|
|
14
|
+
import { setCategoryWeight } from './thread-weights.js';
|
|
13
15
|
import { enqueueMemoryWrite } from './memory-writes.js';
|
|
14
16
|
import { enqueueOutbound } from './outbound.js';
|
|
15
17
|
import { formatLocalTime, resolveTimeExpression } from './time-expr.js';
|
|
@@ -111,7 +113,7 @@ async function callClaude(job) {
|
|
|
111
113
|
addCronUsage(job.cronId, turnInput, turnOutput);
|
|
112
114
|
}
|
|
113
115
|
const rawFlags = extractFlags(reply);
|
|
114
|
-
const { clean, digest, journals, journalCreates, asyncTasks, asyncBrowserTasks, sendTexts, crons, reminds, } = filterFlagsByRole(rawFlags, job.allowedTags);
|
|
116
|
+
const { clean, digest, journals, journalCreates, asyncTasks, asyncBrowserTasks, sendTexts, crons, reminds, threadNews, threadUpdates, threadTouches, threadCools, threadResolves, threadDrops, threadCompresses, threadWeights, } = filterFlagsByRole(rawFlags, job.allowedTags);
|
|
115
117
|
// Detect any stripped tags so we can log + nudge the role config
|
|
116
118
|
// if a user is repeatedly hitting the gate.
|
|
117
119
|
const stripped = [];
|
|
@@ -127,6 +129,18 @@ async function callClaude(job) {
|
|
|
127
129
|
stripped.push('ASYNC-BROWSER');
|
|
128
130
|
if (rawFlags.sendTexts.length !== sendTexts.length)
|
|
129
131
|
stripped.push('SEND-TEXT');
|
|
132
|
+
// Lump all THREAD-* into a single 'THREAD' bucket for the stripped
|
|
133
|
+
// log since they all share the 'THREAD' allowedTag.
|
|
134
|
+
const rawThreadCount = rawFlags.threadNews.length + rawFlags.threadUpdates.length +
|
|
135
|
+
rawFlags.threadTouches.length + rawFlags.threadCools.length +
|
|
136
|
+
rawFlags.threadResolves.length + rawFlags.threadDrops.length +
|
|
137
|
+
rawFlags.threadCompresses.length + rawFlags.threadWeights.length;
|
|
138
|
+
const filteredThreadCount = threadNews.length + threadUpdates.length +
|
|
139
|
+
threadTouches.length + threadCools.length +
|
|
140
|
+
threadResolves.length + threadDrops.length +
|
|
141
|
+
threadCompresses.length + threadWeights.length;
|
|
142
|
+
if (rawThreadCount !== filteredThreadCount)
|
|
143
|
+
stripped.push('THREAD');
|
|
130
144
|
if (stripped.length > 0) {
|
|
131
145
|
logger.warn({ jid: job.jid, senderNumber: job.senderNumber, stripped }, 'tags stripped by role gate');
|
|
132
146
|
}
|
|
@@ -339,6 +353,93 @@ async function callClaude(job) {
|
|
|
339
353
|
chars: r.body.length,
|
|
340
354
|
}, 'REMIND tag scheduled');
|
|
341
355
|
}
|
|
356
|
+
// THREAD-* tag side effects. Each shape lands on its matching
|
|
357
|
+
// CRUD helper in queue/threads.ts. Errors per-tag are logged but
|
|
358
|
+
// don't abort the rest of the reply pipeline.
|
|
359
|
+
if (config.threads?.enabled) {
|
|
360
|
+
for (const t of threadNews) {
|
|
361
|
+
try {
|
|
362
|
+
createThread({
|
|
363
|
+
targetJid: job.jid,
|
|
364
|
+
title: t.title,
|
|
365
|
+
summary: t.summary,
|
|
366
|
+
hotness: t.hotness,
|
|
367
|
+
linkedMemory: t.linkedMemory ?? null,
|
|
368
|
+
category: t.category,
|
|
369
|
+
});
|
|
370
|
+
}
|
|
371
|
+
catch (err) {
|
|
372
|
+
logger.warn({ err, jid: job.jid, title: t.title }, 'THREAD-NEW failed');
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
for (const t of threadUpdates) {
|
|
376
|
+
try {
|
|
377
|
+
updateThread({
|
|
378
|
+
id: t.id,
|
|
379
|
+
title: t.title,
|
|
380
|
+
summary: t.summary,
|
|
381
|
+
hotness: t.hotness,
|
|
382
|
+
linkedMemory: t.linkedMemory ?? undefined,
|
|
383
|
+
});
|
|
384
|
+
}
|
|
385
|
+
catch (err) {
|
|
386
|
+
logger.warn({ err, jid: job.jid, id: t.id }, 'THREAD-UPDATE failed');
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
for (const t of threadTouches) {
|
|
390
|
+
try {
|
|
391
|
+
touchThread(t.id);
|
|
392
|
+
}
|
|
393
|
+
catch (err) {
|
|
394
|
+
logger.warn({ err, jid: job.jid, id: t.id }, 'THREAD-TOUCH failed');
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
for (const t of threadCools) {
|
|
398
|
+
try {
|
|
399
|
+
coolThread(t.id, t.deferDays);
|
|
400
|
+
}
|
|
401
|
+
catch (err) {
|
|
402
|
+
logger.warn({ err, jid: job.jid, id: t.id }, 'THREAD-COOL failed');
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
for (const t of threadResolves) {
|
|
406
|
+
try {
|
|
407
|
+
resolveThread(t.id, t.note);
|
|
408
|
+
}
|
|
409
|
+
catch (err) {
|
|
410
|
+
logger.warn({ err, jid: job.jid, id: t.id }, 'THREAD-RESOLVE failed');
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
for (const t of threadDrops) {
|
|
414
|
+
try {
|
|
415
|
+
dropThread(t.id, t.note);
|
|
416
|
+
}
|
|
417
|
+
catch (err) {
|
|
418
|
+
logger.warn({ err, jid: job.jid, id: t.id }, 'THREAD-DROP failed');
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
for (const t of threadCompresses) {
|
|
422
|
+
try {
|
|
423
|
+
compressThread(t.id, t.note);
|
|
424
|
+
}
|
|
425
|
+
catch (err) {
|
|
426
|
+
logger.warn({ err, jid: job.jid, id: t.id }, 'THREAD-COMPRESS failed');
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
for (const w of threadWeights) {
|
|
430
|
+
try {
|
|
431
|
+
setCategoryWeight(w.category, w.weight);
|
|
432
|
+
}
|
|
433
|
+
catch (err) {
|
|
434
|
+
logger.warn({ err, jid: job.jid, category: w.category }, 'THREAD-WEIGHT failed');
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
else if (threadNews.length + threadUpdates.length + threadTouches.length +
|
|
439
|
+
threadCools.length + threadResolves.length + threadDrops.length +
|
|
440
|
+
threadCompresses.length + threadWeights.length > 0) {
|
|
441
|
+
logger.warn({ jid: job.jid }, 'THREAD-* tags emitted but threads feature disabled — ignored');
|
|
442
|
+
}
|
|
342
443
|
return {
|
|
343
444
|
reply: clean,
|
|
344
445
|
stats: {
|
|
@@ -353,7 +454,17 @@ async function callClaude(job) {
|
|
|
353
454
|
fresh: wasFresh,
|
|
354
455
|
hasDigest: digest !== null,
|
|
355
456
|
journalSlugs: journals.map((j) => j.slug),
|
|
356
|
-
|
|
457
|
+
journalCreateCount: journalCreates.length,
|
|
458
|
+
asyncCount: asyncTasks.length,
|
|
459
|
+
asyncBrowserCount: asyncBrowserTasks.length,
|
|
460
|
+
remindCount: reminds.length,
|
|
461
|
+
cronCount: crons.length,
|
|
462
|
+
sendTextCount: sendTexts.length,
|
|
463
|
+
threadNewCount: threadNews.length,
|
|
464
|
+
threadResolveCount: threadResolves.length,
|
|
465
|
+
threadDropCount: threadDrops.length,
|
|
466
|
+
threadCompressCount: threadCompresses.length,
|
|
467
|
+
threadTouchCount: threadTouches.length,
|
|
357
468
|
},
|
|
358
469
|
jobCards: jobCards.length > 0 ? jobCards : undefined,
|
|
359
470
|
};
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
CREATE TABLE `thread_category_weights` (
|
|
2
|
+
`category` text PRIMARY KEY NOT NULL,
|
|
3
|
+
`weight` integer DEFAULT 50 NOT NULL,
|
|
4
|
+
`samples` integer DEFAULT 0 NOT NULL,
|
|
5
|
+
`updated_at` integer NOT NULL
|
|
6
|
+
);
|
|
7
|
+
--> statement-breakpoint
|
|
8
|
+
CREATE TABLE `threads` (
|
|
9
|
+
`id` integer PRIMARY KEY AUTOINCREMENT NOT NULL,
|
|
10
|
+
`target_jid` text NOT NULL,
|
|
11
|
+
`title` text NOT NULL,
|
|
12
|
+
`summary` text NOT NULL,
|
|
13
|
+
`hotness` integer DEFAULT 50 NOT NULL,
|
|
14
|
+
`status` text DEFAULT 'live' NOT NULL,
|
|
15
|
+
`linked_memory` text,
|
|
16
|
+
`opened_at` integer NOT NULL,
|
|
17
|
+
`last_touched_at` integer NOT NULL,
|
|
18
|
+
`next_review_at` integer NOT NULL,
|
|
19
|
+
`resolution_note` text,
|
|
20
|
+
`total_input_tokens` integer DEFAULT 0 NOT NULL,
|
|
21
|
+
`total_output_tokens` integer DEFAULT 0 NOT NULL,
|
|
22
|
+
`enabled` integer DEFAULT 1 NOT NULL,
|
|
23
|
+
`created_at` integer NOT NULL
|
|
24
|
+
);
|
|
25
|
+
--> statement-breakpoint
|
|
26
|
+
CREATE INDEX `threads_by_jid_hot` ON `threads` (`target_jid`,`status`,`hotness`);--> statement-breakpoint
|
|
27
|
+
CREATE INDEX `threads_by_due` ON `threads` (`enabled`,`status`,`next_review_at`);
|