cc-claw 0.27.2 → 0.28.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/dist/agents/mcp-server.js +3 -1
- package/dist/cli.js +1124 -370
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -33,7 +33,7 @@ var VERSION;
|
|
|
33
33
|
var init_version = __esm({
|
|
34
34
|
"src/version.ts"() {
|
|
35
35
|
"use strict";
|
|
36
|
-
VERSION = true ? "0.
|
|
36
|
+
VERSION = true ? "0.28.0" : (() => {
|
|
37
37
|
try {
|
|
38
38
|
return JSON.parse(readFileSync(join(process.cwd(), "package.json"), "utf-8")).version ?? "unknown";
|
|
39
39
|
} catch {
|
|
@@ -62,7 +62,8 @@ __export(paths_exports, {
|
|
|
62
62
|
SKILLS_PATH: () => SKILLS_PATH,
|
|
63
63
|
WHISPER_MODELS_PATH: () => WHISPER_MODELS_PATH,
|
|
64
64
|
WORKSPACE_PATH: () => WORKSPACE_PATH,
|
|
65
|
-
readApiToken: () => readApiToken
|
|
65
|
+
readApiToken: () => readApiToken,
|
|
66
|
+
resolveRealHome: () => resolveRealHome
|
|
66
67
|
});
|
|
67
68
|
import { homedir, userInfo } from "os";
|
|
68
69
|
import { join as join2 } from "path";
|
|
@@ -1209,6 +1210,64 @@ var init_store4 = __esm({
|
|
|
1209
1210
|
}
|
|
1210
1211
|
});
|
|
1211
1212
|
|
|
1213
|
+
// src/memory/classify.ts
|
|
1214
|
+
function classifyMemory(_trigger, content) {
|
|
1215
|
+
for (const { category, patterns } of CATEGORY_PATTERNS) {
|
|
1216
|
+
for (const pattern of patterns) {
|
|
1217
|
+
if (pattern.test(content)) {
|
|
1218
|
+
return category;
|
|
1219
|
+
}
|
|
1220
|
+
}
|
|
1221
|
+
}
|
|
1222
|
+
return "uncategorized";
|
|
1223
|
+
}
|
|
1224
|
+
var CATEGORY_PATTERNS;
|
|
1225
|
+
var init_classify = __esm({
|
|
1226
|
+
"src/memory/classify.ts"() {
|
|
1227
|
+
"use strict";
|
|
1228
|
+
CATEGORY_PATTERNS = [
|
|
1229
|
+
{
|
|
1230
|
+
category: "decision",
|
|
1231
|
+
patterns: [
|
|
1232
|
+
/\b(decided|chose|picked|selected|opted)\b/i,
|
|
1233
|
+
/\b(switched to|migrated to|moved to|changed to)\b/i,
|
|
1234
|
+
/\b(replaced .+ with|went with|settled on)\b/i,
|
|
1235
|
+
/\b(over|instead of|rather than)\b.*\b(because|due to|since)\b/i
|
|
1236
|
+
]
|
|
1237
|
+
},
|
|
1238
|
+
{
|
|
1239
|
+
category: "preference",
|
|
1240
|
+
patterns: [
|
|
1241
|
+
/\b(prefer(?:s|red|ring)?)\b/i,
|
|
1242
|
+
/\b(likes?|loved?|enjoys?|hates?|dislikes?)\b/i,
|
|
1243
|
+
/\b(always uses?|never uses?|favorite|favourite)\b/i,
|
|
1244
|
+
/\b(wants? to|doesn't want|rather|instead of)\b/i
|
|
1245
|
+
]
|
|
1246
|
+
},
|
|
1247
|
+
{
|
|
1248
|
+
category: "event",
|
|
1249
|
+
patterns: [
|
|
1250
|
+
/\b(yesterday|today|this morning|last week|last month)\b/i,
|
|
1251
|
+
/\b(on (monday|tuesday|wednesday|thursday|friday|saturday|sunday))\b/i,
|
|
1252
|
+
/\b(happened|occurred|took place|attended|deployed|launched|released)\b/i,
|
|
1253
|
+
/\b(had a (meeting|call|chat|discussion|session))\b/i
|
|
1254
|
+
]
|
|
1255
|
+
},
|
|
1256
|
+
{
|
|
1257
|
+
category: "fact",
|
|
1258
|
+
patterns: [
|
|
1259
|
+
/\b(works? at|employed at|job is)\b/i,
|
|
1260
|
+
/\b(lives? in|located in|based in)\b/i,
|
|
1261
|
+
/\b(is a|is an|role is|position is)\b/i,
|
|
1262
|
+
/\b(uses?|runs? on|built with|powered by|written in)\b/i,
|
|
1263
|
+
/\b(name is|called|known as)\b/i,
|
|
1264
|
+
/\b(timezone|time zone)\b/i
|
|
1265
|
+
]
|
|
1266
|
+
}
|
|
1267
|
+
];
|
|
1268
|
+
}
|
|
1269
|
+
});
|
|
1270
|
+
|
|
1212
1271
|
// src/memory/schema.ts
|
|
1213
1272
|
function initSchema(db3) {
|
|
1214
1273
|
db3.exec(`
|
|
@@ -1628,6 +1687,10 @@ function initSchema(db3) {
|
|
|
1628
1687
|
db3.exec(`ALTER TABLE chat_voice ADD COLUMN stt_model TEXT DEFAULT 'small.en'`);
|
|
1629
1688
|
} catch {
|
|
1630
1689
|
}
|
|
1690
|
+
try {
|
|
1691
|
+
db3.exec(`ALTER TABLE chat_voice ADD COLUMN stt_echo INTEGER DEFAULT 0`);
|
|
1692
|
+
} catch {
|
|
1693
|
+
}
|
|
1631
1694
|
db3.exec(`
|
|
1632
1695
|
CREATE TABLE IF NOT EXISTS backend_limits (
|
|
1633
1696
|
backend TEXT NOT NULL,
|
|
@@ -1754,6 +1817,44 @@ function initSchema(db3) {
|
|
|
1754
1817
|
db3.exec("ALTER TABLE session_summaries ADD COLUMN embedding TEXT");
|
|
1755
1818
|
} catch {
|
|
1756
1819
|
}
|
|
1820
|
+
try {
|
|
1821
|
+
db3.exec("ALTER TABLE memories ADD COLUMN category TEXT NOT NULL DEFAULT 'uncategorized'");
|
|
1822
|
+
} catch {
|
|
1823
|
+
}
|
|
1824
|
+
try {
|
|
1825
|
+
db3.exec("ALTER TABLE memories ADD COLUMN half_life_days REAL NOT NULL DEFAULT 14.0");
|
|
1826
|
+
} catch {
|
|
1827
|
+
}
|
|
1828
|
+
try {
|
|
1829
|
+
db3.exec("ALTER TABLE memories ADD COLUMN superseded_by INTEGER DEFAULT NULL");
|
|
1830
|
+
} catch {
|
|
1831
|
+
}
|
|
1832
|
+
try {
|
|
1833
|
+
db3.exec("ALTER TABLE memories ADD COLUMN superseded_at TEXT DEFAULT NULL");
|
|
1834
|
+
} catch {
|
|
1835
|
+
}
|
|
1836
|
+
try {
|
|
1837
|
+
const alreadyDone = db3.prepare("SELECT value FROM meta WHERE key = 'schema_category_backfill_done'").get();
|
|
1838
|
+
if (!alreadyDone) {
|
|
1839
|
+
const allUncategorized = db3.prepare("SELECT id, trigger, content FROM memories WHERE category = 'uncategorized'").all();
|
|
1840
|
+
if (allUncategorized.length > 0) {
|
|
1841
|
+
const updateStmt = db3.prepare("UPDATE memories SET category = ? WHERE id = ?");
|
|
1842
|
+
const backfillTxn = db3.transaction(() => {
|
|
1843
|
+
for (const mem of allUncategorized) {
|
|
1844
|
+
const category = classifyMemory(mem.trigger, mem.content);
|
|
1845
|
+
if (category !== "uncategorized") {
|
|
1846
|
+
updateStmt.run(category, mem.id);
|
|
1847
|
+
}
|
|
1848
|
+
}
|
|
1849
|
+
});
|
|
1850
|
+
backfillTxn();
|
|
1851
|
+
log(`[schema] Backfilled categories for ${allUncategorized.length} memories`);
|
|
1852
|
+
}
|
|
1853
|
+
db3.prepare("INSERT OR REPLACE INTO meta (key, value) VALUES (?, ?)").run("schema_category_backfill_done", "1");
|
|
1854
|
+
}
|
|
1855
|
+
} catch (err) {
|
|
1856
|
+
warn(`[schema] Category backfill skipped: ${err}`);
|
|
1857
|
+
}
|
|
1757
1858
|
try {
|
|
1758
1859
|
db3.exec("ALTER TABLE message_log ADD COLUMN message_type TEXT DEFAULT 'text'");
|
|
1759
1860
|
} catch {
|
|
@@ -1975,32 +2076,32 @@ function applySalienceDecay(db3) {
|
|
|
1975
2076
|
if (lastDecay) {
|
|
1976
2077
|
const daysSinceLastDecay = (Date.now() - (/* @__PURE__ */ new Date(lastDecay.replace(" ", "T") + "Z")).getTime()) / (1e3 * 60 * 60 * 24);
|
|
1977
2078
|
if (daysSinceLastDecay < 0.01) return;
|
|
1978
|
-
db3.prepare(`
|
|
1979
|
-
UPDATE memories
|
|
1980
|
-
SET salience = salience * POWER(0.98, ?)
|
|
1981
|
-
WHERE salience >= 0.1
|
|
1982
|
-
`).run(daysSinceLastDecay);
|
|
1983
2079
|
db3.prepare(`
|
|
1984
2080
|
UPDATE session_summaries
|
|
1985
2081
|
SET salience = salience * POWER(0.995, ?)
|
|
1986
2082
|
WHERE salience >= 0.1
|
|
1987
2083
|
`).run(daysSinceLastDecay);
|
|
1988
2084
|
} else {
|
|
1989
|
-
db3.prepare(`
|
|
1990
|
-
UPDATE memories
|
|
1991
|
-
SET salience = salience * POWER(0.98, julianday('now') - julianday(last_accessed))
|
|
1992
|
-
WHERE salience >= 0.1
|
|
1993
|
-
`).run();
|
|
1994
2085
|
db3.prepare(`
|
|
1995
2086
|
UPDATE session_summaries
|
|
1996
2087
|
SET salience = salience * POWER(0.995, julianday('now') - julianday(created_at))
|
|
1997
2088
|
WHERE salience >= 0.1
|
|
1998
2089
|
`).run();
|
|
1999
2090
|
}
|
|
2000
|
-
db3.prepare("DELETE FROM memories WHERE salience < 0.1").run();
|
|
2001
2091
|
db3.prepare("DELETE FROM session_summaries WHERE salience < 0.1").run();
|
|
2002
2092
|
db3.prepare("INSERT OR REPLACE INTO meta (key, value) VALUES ('last_salience_decay', ?)").run(now);
|
|
2003
2093
|
}
|
|
2094
|
+
function getMetaValue(key) {
|
|
2095
|
+
const { getDb: getDb2 } = (init_store5(), __toCommonJS(store_exports5));
|
|
2096
|
+
const db3 = getDb2();
|
|
2097
|
+
const row = db3.prepare("SELECT value FROM meta WHERE key = ?").get(key);
|
|
2098
|
+
return row?.value;
|
|
2099
|
+
}
|
|
2100
|
+
function setMetaValue(key, value) {
|
|
2101
|
+
const { getDb: getDb2 } = (init_store5(), __toCommonJS(store_exports5));
|
|
2102
|
+
const db3 = getDb2();
|
|
2103
|
+
db3.prepare("INSERT OR REPLACE INTO meta (key, value) VALUES (?, ?)").run(key, value);
|
|
2104
|
+
}
|
|
2004
2105
|
function backfillJobTitles(database) {
|
|
2005
2106
|
const rows = database.prepare(
|
|
2006
2107
|
"SELECT id, description FROM jobs WHERE active = 1 AND (title IS NULL OR title LIKE 'No thinking%' OR title LIKE 'You must%' OR title LIKE 'You MUST%')"
|
|
@@ -2032,6 +2133,8 @@ var init_schema = __esm({
|
|
|
2032
2133
|
init_store3();
|
|
2033
2134
|
init_identity();
|
|
2034
2135
|
init_store4();
|
|
2136
|
+
init_log();
|
|
2137
|
+
init_classify();
|
|
2035
2138
|
}
|
|
2036
2139
|
});
|
|
2037
2140
|
|
|
@@ -2311,33 +2414,32 @@ var init_embeddings = __esm({
|
|
|
2311
2414
|
});
|
|
2312
2415
|
|
|
2313
2416
|
// src/memory/memories.ts
|
|
2314
|
-
function
|
|
2315
|
-
|
|
2316
|
-
|
|
2317
|
-
|
|
2318
|
-
memorySalienceFlushTimer = setTimeout(flushMemorySalienceUpdates, 6e4);
|
|
2417
|
+
function queueHalfLifeExtension(memoryId) {
|
|
2418
|
+
pendingHalfLifeExtensions.set(memoryId, (pendingHalfLifeExtensions.get(memoryId) ?? 0) + 1);
|
|
2419
|
+
if (!memoryHalfLifeFlushTimer) {
|
|
2420
|
+
memoryHalfLifeFlushTimer = setTimeout(flushMemoryHalfLifeUpdates, 6e4);
|
|
2319
2421
|
}
|
|
2320
2422
|
}
|
|
2321
|
-
function
|
|
2322
|
-
if (
|
|
2323
|
-
|
|
2423
|
+
function flushMemoryHalfLifeUpdates() {
|
|
2424
|
+
if (pendingHalfLifeExtensions.size === 0) {
|
|
2425
|
+
memoryHalfLifeFlushTimer = null;
|
|
2324
2426
|
return;
|
|
2325
2427
|
}
|
|
2326
2428
|
try {
|
|
2327
2429
|
const db3 = getDb();
|
|
2328
2430
|
const stmt = db3.prepare(
|
|
2329
|
-
"UPDATE memories SET
|
|
2431
|
+
"UPDATE memories SET half_life_days = MIN(90, half_life_days + 2), access_count = access_count + 1, last_accessed = datetime('now') WHERE id = ?"
|
|
2330
2432
|
);
|
|
2331
2433
|
const txn = db3.transaction(() => {
|
|
2332
|
-
for (const [id
|
|
2333
|
-
stmt.run(
|
|
2434
|
+
for (const [id] of pendingHalfLifeExtensions) {
|
|
2435
|
+
stmt.run(id);
|
|
2334
2436
|
}
|
|
2335
2437
|
});
|
|
2336
2438
|
txn();
|
|
2337
2439
|
} catch {
|
|
2338
2440
|
}
|
|
2339
|
-
|
|
2340
|
-
|
|
2441
|
+
pendingHalfLifeExtensions.clear();
|
|
2442
|
+
memoryHalfLifeFlushTimer = null;
|
|
2341
2443
|
}
|
|
2342
2444
|
function invalidateMemoryEmbeddingCache() {
|
|
2343
2445
|
embeddingCache = null;
|
|
@@ -2361,7 +2463,7 @@ function getMemoriesWithoutEmbeddings(limit = 100) {
|
|
|
2361
2463
|
}
|
|
2362
2464
|
function getAllMemoriesWithEmbeddingsUncached() {
|
|
2363
2465
|
return getDb().prepare(
|
|
2364
|
-
"SELECT * FROM memories WHERE embedding IS NOT NULL AND salience >= 0.1"
|
|
2466
|
+
"SELECT * FROM memories WHERE embedding IS NOT NULL AND salience >= 0.1 AND superseded_by IS NULL"
|
|
2365
2467
|
).all();
|
|
2366
2468
|
}
|
|
2367
2469
|
function getAllMemoriesWithEmbeddings() {
|
|
@@ -2373,8 +2475,13 @@ function getAllMemoriesWithEmbeddings() {
|
|
|
2373
2475
|
embeddingCache = { data, timestamp: now };
|
|
2374
2476
|
return data;
|
|
2375
2477
|
}
|
|
2376
|
-
function saveMemoryWithEmbedding(trigger, content, type = "semantic") {
|
|
2377
|
-
const
|
|
2478
|
+
function saveMemoryWithEmbedding(trigger, content, type = "semantic", category = "uncategorized", halfLifeDays = 14) {
|
|
2479
|
+
const db3 = getDb();
|
|
2480
|
+
const result = db3.prepare(
|
|
2481
|
+
"INSERT INTO memories (trigger, content, type, category, half_life_days) VALUES (?, ?, ?, ?, ?)"
|
|
2482
|
+
).run(trigger, content, type, category, halfLifeDays);
|
|
2483
|
+
const id = Number(result.lastInsertRowid);
|
|
2484
|
+
invalidateMemoryEmbeddingCache();
|
|
2378
2485
|
const textToEmbed = `${trigger}: ${content}`;
|
|
2379
2486
|
embedForStorage(textToEmbed).then((emb) => {
|
|
2380
2487
|
if (emb) {
|
|
@@ -2402,33 +2509,45 @@ function searchMemories(queryText, limit = 3) {
|
|
|
2402
2509
|
SELECT m.*, fts.rank AS _ftsRank FROM memories m
|
|
2403
2510
|
JOIN memories_fts fts ON m.id = fts.rowid
|
|
2404
2511
|
WHERE memories_fts MATCH ?
|
|
2512
|
+
AND m.superseded_by IS NULL
|
|
2405
2513
|
ORDER BY fts.rank
|
|
2406
2514
|
LIMIT ?
|
|
2407
2515
|
`).all(ftsQuery, limit);
|
|
2408
2516
|
for (const mem of ftsResults) {
|
|
2409
|
-
|
|
2517
|
+
queueHalfLifeExtension(mem.id);
|
|
2410
2518
|
}
|
|
2411
2519
|
return ftsResults;
|
|
2412
2520
|
}
|
|
2413
2521
|
function searchMemoriesReadOnly(readDb, queryText, limit = 3) {
|
|
2414
2522
|
const ftsQuery = toFts5Query(queryText);
|
|
2415
2523
|
if (!ftsQuery) return [];
|
|
2416
|
-
|
|
2417
|
-
|
|
2418
|
-
|
|
2419
|
-
|
|
2420
|
-
|
|
2421
|
-
|
|
2422
|
-
|
|
2524
|
+
try {
|
|
2525
|
+
return readDb.prepare(`
|
|
2526
|
+
SELECT m.*, fts.rank AS _ftsRank FROM memories m
|
|
2527
|
+
JOIN memories_fts fts ON m.id = fts.rowid
|
|
2528
|
+
WHERE memories_fts MATCH ?
|
|
2529
|
+
AND m.superseded_by IS NULL
|
|
2530
|
+
ORDER BY fts.rank
|
|
2531
|
+
LIMIT ?
|
|
2532
|
+
`).all(ftsQuery, limit);
|
|
2533
|
+
} catch {
|
|
2534
|
+
return readDb.prepare(`
|
|
2535
|
+
SELECT m.*, fts.rank AS _ftsRank FROM memories m
|
|
2536
|
+
JOIN memories_fts fts ON m.id = fts.rowid
|
|
2537
|
+
WHERE memories_fts MATCH ?
|
|
2538
|
+
ORDER BY fts.rank
|
|
2539
|
+
LIMIT ?
|
|
2540
|
+
`).all(ftsQuery, limit);
|
|
2541
|
+
}
|
|
2423
2542
|
}
|
|
2424
2543
|
function getRecentMemories(limit = 5) {
|
|
2425
2544
|
return getDb().prepare(
|
|
2426
|
-
"SELECT * FROM memories ORDER BY last_accessed DESC LIMIT ?"
|
|
2545
|
+
"SELECT * FROM memories WHERE superseded_by IS NULL ORDER BY last_accessed DESC LIMIT ?"
|
|
2427
2546
|
).all(limit);
|
|
2428
2547
|
}
|
|
2429
2548
|
function listMemories() {
|
|
2430
2549
|
return getDb().prepare(
|
|
2431
|
-
"SELECT * FROM memories WHERE salience >= 0.1 ORDER BY salience DESC LIMIT 50"
|
|
2550
|
+
"SELECT * FROM memories WHERE salience >= 0.1 AND superseded_by IS NULL ORDER BY salience DESC LIMIT 50"
|
|
2432
2551
|
).all();
|
|
2433
2552
|
}
|
|
2434
2553
|
function forgetMemory(keyword) {
|
|
@@ -2449,17 +2568,52 @@ function deleteMemoryById(id) {
|
|
|
2449
2568
|
}
|
|
2450
2569
|
function getAllMemoriesForOptimization() {
|
|
2451
2570
|
return getDb().prepare(
|
|
2452
|
-
"SELECT * FROM memories WHERE salience >= 0.1 ORDER BY salience DESC"
|
|
2571
|
+
"SELECT * FROM memories WHERE salience >= 0.1 AND superseded_by IS NULL ORDER BY salience DESC"
|
|
2453
2572
|
).all();
|
|
2454
2573
|
}
|
|
2455
2574
|
function restoreMemory(memory2) {
|
|
2456
|
-
getDb()
|
|
2457
|
-
|
|
2458
|
-
|
|
2459
|
-
|
|
2575
|
+
const db3 = getDb();
|
|
2576
|
+
db3.prepare(
|
|
2577
|
+
`INSERT OR REPLACE INTO memories
|
|
2578
|
+
(id, trigger, content, type, salience, access_count, created_at, last_accessed, embedding,
|
|
2579
|
+
category, half_life_days, superseded_by, superseded_at)
|
|
2580
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`
|
|
2581
|
+
).run(
|
|
2582
|
+
memory2.id,
|
|
2583
|
+
memory2.trigger,
|
|
2584
|
+
memory2.content,
|
|
2585
|
+
memory2.type,
|
|
2586
|
+
memory2.salience,
|
|
2587
|
+
memory2.access_count,
|
|
2588
|
+
memory2.created_at,
|
|
2589
|
+
memory2.last_accessed,
|
|
2590
|
+
memory2.embedding,
|
|
2591
|
+
memory2.category ?? "uncategorized",
|
|
2592
|
+
memory2.half_life_days ?? 14,
|
|
2593
|
+
memory2.superseded_by ?? null,
|
|
2594
|
+
memory2.superseded_at ?? null
|
|
2595
|
+
);
|
|
2460
2596
|
invalidateMemoryEmbeddingCache();
|
|
2461
2597
|
}
|
|
2462
|
-
|
|
2598
|
+
function markSuperseded(memoryId, supersededById) {
|
|
2599
|
+
if (memoryId === supersededById) return;
|
|
2600
|
+
const db3 = getDb();
|
|
2601
|
+
db3.prepare(
|
|
2602
|
+
`UPDATE memories SET superseded_by = ?, superseded_at = datetime('now') WHERE id = ?`
|
|
2603
|
+
).run(supersededById, memoryId);
|
|
2604
|
+
}
|
|
2605
|
+
function cleanupSuperseded(gracePeriodDays = 14) {
|
|
2606
|
+
const db3 = getDb();
|
|
2607
|
+
const result = db3.prepare(
|
|
2608
|
+
`DELETE FROM memories WHERE superseded_by IS NOT NULL AND superseded_at < datetime('now', '-' || ? || ' days')`
|
|
2609
|
+
).run(gracePeriodDays);
|
|
2610
|
+
return result.changes;
|
|
2611
|
+
}
|
|
2612
|
+
function updateMemoryCategory(id, category) {
|
|
2613
|
+
const db3 = getDb();
|
|
2614
|
+
db3.prepare("UPDATE memories SET category = ? WHERE id = ?").run(category, id);
|
|
2615
|
+
}
|
|
2616
|
+
var embeddingCache, EMBEDDING_CACHE_TTL_MS, pendingHalfLifeExtensions, memoryHalfLifeFlushTimer, STOP_WORDS;
|
|
2463
2617
|
var init_memories = __esm({
|
|
2464
2618
|
"src/memory/memories.ts"() {
|
|
2465
2619
|
"use strict";
|
|
@@ -2468,8 +2622,8 @@ var init_memories = __esm({
|
|
|
2468
2622
|
init_log();
|
|
2469
2623
|
embeddingCache = null;
|
|
2470
2624
|
EMBEDDING_CACHE_TTL_MS = 3e4;
|
|
2471
|
-
|
|
2472
|
-
|
|
2625
|
+
pendingHalfLifeExtensions = /* @__PURE__ */ new Map();
|
|
2626
|
+
memoryHalfLifeFlushTimer = null;
|
|
2473
2627
|
STOP_WORDS = /* @__PURE__ */ new Set([
|
|
2474
2628
|
"a",
|
|
2475
2629
|
"an",
|
|
@@ -4492,6 +4646,175 @@ var init_api_context = __esm({
|
|
|
4492
4646
|
}
|
|
4493
4647
|
});
|
|
4494
4648
|
|
|
4649
|
+
// src/text-utils.ts
|
|
4650
|
+
var text_utils_exports = {};
|
|
4651
|
+
__export(text_utils_exports, {
|
|
4652
|
+
appendTextChunk: () => appendTextChunk,
|
|
4653
|
+
jaccardSimilarity: () => jaccardSimilarity,
|
|
4654
|
+
tokenizeText: () => tokenizeText
|
|
4655
|
+
});
|
|
4656
|
+
function appendTextChunk(accumulated, chunk) {
|
|
4657
|
+
if (!accumulated) return chunk;
|
|
4658
|
+
if (!chunk) return accumulated;
|
|
4659
|
+
return accumulated + chunk;
|
|
4660
|
+
}
|
|
4661
|
+
function tokenizeText(text, minLen = 3) {
|
|
4662
|
+
return new Set(
|
|
4663
|
+
text.toLowerCase().replace(/[^\w\s]/g, " ").split(/\s+/).filter((w) => w.length >= minLen)
|
|
4664
|
+
);
|
|
4665
|
+
}
|
|
4666
|
+
function jaccardSimilarity(a, b) {
|
|
4667
|
+
if (a.size === 0 && b.size === 0) return 0;
|
|
4668
|
+
let intersection = 0;
|
|
4669
|
+
for (const term of a) {
|
|
4670
|
+
if (b.has(term)) intersection++;
|
|
4671
|
+
}
|
|
4672
|
+
const union = a.size + b.size - intersection;
|
|
4673
|
+
return union === 0 ? 0 : intersection / union;
|
|
4674
|
+
}
|
|
4675
|
+
var init_text_utils = __esm({
|
|
4676
|
+
"src/text-utils.ts"() {
|
|
4677
|
+
"use strict";
|
|
4678
|
+
}
|
|
4679
|
+
});
|
|
4680
|
+
|
|
4681
|
+
// src/memory/supersede.ts
|
|
4682
|
+
function detectSupersession(newTrigger, newContent, newCategory, existingMemories, newEmbedding) {
|
|
4683
|
+
if (existingMemories.length === 0) return null;
|
|
4684
|
+
const hasChangeSignal = CHANGE_SIGNALS.some((re) => re.test(newContent));
|
|
4685
|
+
const newTerms = tokenizeText(newContent);
|
|
4686
|
+
let bestId = null;
|
|
4687
|
+
let bestScore = 0;
|
|
4688
|
+
for (const mem of existingMemories) {
|
|
4689
|
+
if (mem.superseded_by != null) continue;
|
|
4690
|
+
let score = 0;
|
|
4691
|
+
if (normalizeTrigger(mem.trigger) === normalizeTrigger(newTrigger)) {
|
|
4692
|
+
score += 3;
|
|
4693
|
+
}
|
|
4694
|
+
if (newCategory !== "uncategorized" && mem.category !== "uncategorized") {
|
|
4695
|
+
if (newCategory === mem.category) {
|
|
4696
|
+
score += 1;
|
|
4697
|
+
} else {
|
|
4698
|
+
continue;
|
|
4699
|
+
}
|
|
4700
|
+
}
|
|
4701
|
+
if (newEmbedding && mem.embedding) {
|
|
4702
|
+
try {
|
|
4703
|
+
const memEmbedding = JSON.parse(mem.embedding);
|
|
4704
|
+
const sim = cosineSimilarity(newEmbedding, memEmbedding);
|
|
4705
|
+
if (sim >= EMBEDDING_THRESHOLD) {
|
|
4706
|
+
score += 3;
|
|
4707
|
+
}
|
|
4708
|
+
} catch {
|
|
4709
|
+
}
|
|
4710
|
+
}
|
|
4711
|
+
const memTerms = tokenizeText(mem.content);
|
|
4712
|
+
const overlap = jaccardSimilarity(newTerms, memTerms);
|
|
4713
|
+
if (overlap >= KEYWORD_OVERLAP_THRESHOLD) {
|
|
4714
|
+
score += 2;
|
|
4715
|
+
}
|
|
4716
|
+
if (hasChangeSignal) {
|
|
4717
|
+
score += 2;
|
|
4718
|
+
}
|
|
4719
|
+
if (score > bestScore) {
|
|
4720
|
+
bestScore = score;
|
|
4721
|
+
bestId = mem.id;
|
|
4722
|
+
}
|
|
4723
|
+
}
|
|
4724
|
+
return bestScore >= SUPERSESSION_SCORE_THRESHOLD ? bestId : null;
|
|
4725
|
+
}
|
|
4726
|
+
function normalizeTrigger(trigger) {
|
|
4727
|
+
return trigger.toLowerCase().trim().replace(/[\s_-]+/g, "-");
|
|
4728
|
+
}
|
|
4729
|
+
var CHANGE_SIGNALS, EMBEDDING_THRESHOLD, KEYWORD_OVERLAP_THRESHOLD, SUPERSESSION_SCORE_THRESHOLD;
|
|
4730
|
+
var init_supersede = __esm({
|
|
4731
|
+
"src/memory/supersede.ts"() {
|
|
4732
|
+
"use strict";
|
|
4733
|
+
init_embeddings();
|
|
4734
|
+
init_text_utils();
|
|
4735
|
+
CHANGE_SIGNALS = [
|
|
4736
|
+
/\bswitched to\b/i,
|
|
4737
|
+
/\bchanged to\b/i,
|
|
4738
|
+
/\bnow uses?\b/i,
|
|
4739
|
+
/\bno longer\b/i,
|
|
4740
|
+
/\breplaced\b/i,
|
|
4741
|
+
/\bmigrated to\b/i,
|
|
4742
|
+
/\bmoved to\b/i,
|
|
4743
|
+
/\bstopped using\b/i,
|
|
4744
|
+
/\bupdated to\b/i
|
|
4745
|
+
];
|
|
4746
|
+
EMBEDDING_THRESHOLD = 0.75;
|
|
4747
|
+
KEYWORD_OVERLAP_THRESHOLD = 0.4;
|
|
4748
|
+
SUPERSESSION_SCORE_THRESHOLD = 4;
|
|
4749
|
+
}
|
|
4750
|
+
});
|
|
4751
|
+
|
|
4752
|
+
// src/memory/engine.ts
|
|
4753
|
+
var engine_exports = {};
|
|
4754
|
+
__export(engine_exports, {
|
|
4755
|
+
cleanup: () => cleanup,
|
|
4756
|
+
forget: () => forget,
|
|
4757
|
+
list: () => list,
|
|
4758
|
+
recall: () => recall,
|
|
4759
|
+
remember: () => remember
|
|
4760
|
+
});
|
|
4761
|
+
async function remember(tag, content, opts = {}) {
|
|
4762
|
+
const category = opts.category ?? classifyMemory(tag, content);
|
|
4763
|
+
const type = opts.type ?? "semantic";
|
|
4764
|
+
const candidates = getAllMemoriesWithEmbeddings();
|
|
4765
|
+
const id = saveMemoryWithEmbedding(tag, content, type, category, DEFAULT_HALF_LIFE);
|
|
4766
|
+
let supersededId = null;
|
|
4767
|
+
try {
|
|
4768
|
+
const queryEmbedding = await embedQuery(content);
|
|
4769
|
+
supersededId = detectSupersession(tag, content, category, candidates, queryEmbedding);
|
|
4770
|
+
if (supersededId != null) {
|
|
4771
|
+
markSuperseded(supersededId, id);
|
|
4772
|
+
log(`[engine] Memory #${supersededId} superseded by #${id} ("${tag}")`);
|
|
4773
|
+
}
|
|
4774
|
+
} catch (err) {
|
|
4775
|
+
log(`[engine] Supersession check skipped: ${err}`);
|
|
4776
|
+
}
|
|
4777
|
+
const result = { id, category };
|
|
4778
|
+
if (supersededId != null) result.superseded = supersededId;
|
|
4779
|
+
return result;
|
|
4780
|
+
}
|
|
4781
|
+
function recall(query, opts = {}) {
|
|
4782
|
+
const limit = opts.limit ?? 5;
|
|
4783
|
+
let results = searchMemories(query, limit);
|
|
4784
|
+
if (opts.category) {
|
|
4785
|
+
results = results.filter((m) => m.category === opts.category);
|
|
4786
|
+
}
|
|
4787
|
+
return results;
|
|
4788
|
+
}
|
|
4789
|
+
function list(_opts = {}) {
|
|
4790
|
+
return listMemories();
|
|
4791
|
+
}
|
|
4792
|
+
function forget(keywordOrId) {
|
|
4793
|
+
if (typeof keywordOrId === "number") {
|
|
4794
|
+
return deleteMemoryById(keywordOrId) ? 1 : 0;
|
|
4795
|
+
}
|
|
4796
|
+
return forgetMemory(keywordOrId);
|
|
4797
|
+
}
|
|
4798
|
+
function cleanup(gracePeriodDays = 14) {
|
|
4799
|
+
const deleted = cleanupSuperseded(gracePeriodDays);
|
|
4800
|
+
if (deleted > 0) {
|
|
4801
|
+
log(`[engine] Cleaned up ${deleted} superseded memories past ${gracePeriodDays}-day grace period`);
|
|
4802
|
+
}
|
|
4803
|
+
return deleted;
|
|
4804
|
+
}
|
|
4805
|
+
var DEFAULT_HALF_LIFE;
|
|
4806
|
+
var init_engine = __esm({
|
|
4807
|
+
"src/memory/engine.ts"() {
|
|
4808
|
+
"use strict";
|
|
4809
|
+
init_memories();
|
|
4810
|
+
init_classify();
|
|
4811
|
+
init_supersede();
|
|
4812
|
+
init_embeddings();
|
|
4813
|
+
init_log();
|
|
4814
|
+
DEFAULT_HALF_LIFE = 14;
|
|
4815
|
+
}
|
|
4816
|
+
});
|
|
4817
|
+
|
|
4495
4818
|
// src/memory/store.ts
|
|
4496
4819
|
var store_exports5 = {};
|
|
4497
4820
|
__export(store_exports5, {
|
|
@@ -4508,7 +4831,10 @@ __export(store_exports5, {
|
|
|
4508
4831
|
buildApiMessages: () => buildApiMessages,
|
|
4509
4832
|
cancelJobById: () => cancelJobById,
|
|
4510
4833
|
checkBackendLimits: () => checkBackendLimits,
|
|
4834
|
+
classifyMemory: () => classifyMemory,
|
|
4511
4835
|
cleanExpiredWatches: () => cleanExpiredWatches,
|
|
4836
|
+
cleanup: () => cleanup,
|
|
4837
|
+
cleanupSuperseded: () => cleanupSuperseded,
|
|
4512
4838
|
clearAgentMode: () => clearAgentMode,
|
|
4513
4839
|
clearAllPaidSlots: () => clearAllPaidSlots,
|
|
4514
4840
|
clearAllSessions: () => clearAllSessions,
|
|
@@ -4529,11 +4855,13 @@ __export(store_exports5, {
|
|
|
4529
4855
|
deleteBookmark: () => deleteBookmark,
|
|
4530
4856
|
deleteMemoryById: () => deleteMemoryById,
|
|
4531
4857
|
deleteSessionSummary: () => deleteSessionSummary,
|
|
4858
|
+
detectSupersession: () => detectSupersession,
|
|
4532
4859
|
determineEscalationTarget: () => determineEscalationTarget,
|
|
4533
4860
|
estimateTokens: () => estimateTokens,
|
|
4534
4861
|
findBookmarksByPrefix: () => findBookmarksByPrefix,
|
|
4535
|
-
|
|
4862
|
+
flushMemoryHalfLifeUpdates: () => flushMemoryHalfLifeUpdates,
|
|
4536
4863
|
flushSummarySalienceUpdates: () => flushSummarySalienceUpdates,
|
|
4864
|
+
forget: () => forget,
|
|
4537
4865
|
forgetMemory: () => forgetMemory,
|
|
4538
4866
|
getActiveJobs: () => getActiveJobs,
|
|
4539
4867
|
getActiveWatches: () => getActiveWatches,
|
|
@@ -4576,6 +4904,7 @@ __export(store_exports5, {
|
|
|
4576
4904
|
getMemoryById: () => getMemoryById,
|
|
4577
4905
|
getMessagePairById: () => getMessagePairById,
|
|
4578
4906
|
getMessagePairs: () => getMessagePairs,
|
|
4907
|
+
getMetaValue: () => getMetaValue,
|
|
4579
4908
|
getMode: () => getMode,
|
|
4580
4909
|
getModel: () => getModel,
|
|
4581
4910
|
getModelSignature: () => getModelSignature,
|
|
@@ -4606,6 +4935,7 @@ __export(store_exports5, {
|
|
|
4606
4935
|
insertJobRun: () => insertJobRun,
|
|
4607
4936
|
invalidateMemoryEmbeddingCache: () => invalidateMemoryEmbeddingCache,
|
|
4608
4937
|
invalidateSummaryEmbeddingCache: () => invalidateSummaryEmbeddingCache,
|
|
4938
|
+
list: () => list,
|
|
4609
4939
|
listMemories: () => listMemories,
|
|
4610
4940
|
listSessionSummaries: () => listSessionSummaries,
|
|
4611
4941
|
markBackendSlotExhausted: () => markBackendSlotExhausted,
|
|
@@ -4613,14 +4943,18 @@ __export(store_exports5, {
|
|
|
4613
4943
|
markMessageLogSummarized: () => markMessageLogSummarized,
|
|
4614
4944
|
markSlotExhausted: () => markSlotExhausted,
|
|
4615
4945
|
markSlotSuccess: () => markSlotSuccess,
|
|
4946
|
+
markSuperseded: () => markSuperseded,
|
|
4616
4947
|
openDatabaseReadOnly: () => openDatabaseReadOnly,
|
|
4617
4948
|
parseHeartbeatFallbacks: () => parseHeartbeatFallbacks,
|
|
4618
4949
|
pinChatBackendSlot: () => pinChatBackendSlot,
|
|
4619
4950
|
pinChatGeminiSlot: () => pinChatGeminiSlot,
|
|
4620
4951
|
pruneJobRuns: () => pruneJobRuns,
|
|
4621
4952
|
pruneMessageLog: () => pruneMessageLog,
|
|
4953
|
+
queueHalfLifeExtension: () => queueHalfLifeExtension,
|
|
4954
|
+
recall: () => recall,
|
|
4622
4955
|
reenableBackendSlot: () => reenableBackendSlot,
|
|
4623
4956
|
reenableSlot: () => reenableSlot,
|
|
4957
|
+
remember: () => remember,
|
|
4624
4958
|
removeApiModel: () => removeApiModel,
|
|
4625
4959
|
removeApiModelById: () => removeApiModelById,
|
|
4626
4960
|
removeBackendSlot: () => removeBackendSlot,
|
|
@@ -4659,6 +4993,7 @@ __export(store_exports5, {
|
|
|
4659
4993
|
setGeminiSlotEnabled: () => setGeminiSlotEnabled,
|
|
4660
4994
|
setGlobalSummarizer: () => setGlobalSummarizer,
|
|
4661
4995
|
setHeartbeatConfig: () => setHeartbeatConfig,
|
|
4996
|
+
setMetaValue: () => setMetaValue,
|
|
4662
4997
|
setMode: () => setMode,
|
|
4663
4998
|
setModel: () => setModel,
|
|
4664
4999
|
setModelSignature: () => setModelSignature,
|
|
@@ -4683,6 +5018,7 @@ __export(store_exports5, {
|
|
|
4683
5018
|
updateJobEnabled: () => updateJobEnabled,
|
|
4684
5019
|
updateJobLastRun: () => updateJobLastRun,
|
|
4685
5020
|
updateJobNextRun: () => updateJobNextRun,
|
|
5021
|
+
updateMemoryCategory: () => updateMemoryCategory,
|
|
4686
5022
|
updateMemoryEmbedding: () => updateMemoryEmbedding,
|
|
4687
5023
|
updateSessionSummaryEmbedding: () => updateSessionSummaryEmbedding,
|
|
4688
5024
|
upsertBookmark: () => upsertBookmark
|
|
@@ -4727,6 +5063,10 @@ var init_store5 = __esm({
|
|
|
4727
5063
|
init_backend_slots();
|
|
4728
5064
|
init_api_models();
|
|
4729
5065
|
init_api_context();
|
|
5066
|
+
init_schema();
|
|
5067
|
+
init_classify();
|
|
5068
|
+
init_supersede();
|
|
5069
|
+
init_engine();
|
|
4730
5070
|
_testDbPath = null;
|
|
4731
5071
|
}
|
|
4732
5072
|
});
|
|
@@ -4945,6 +5285,11 @@ var init_resolve_executable = __esm({
|
|
|
4945
5285
|
});
|
|
4946
5286
|
|
|
4947
5287
|
// src/backends/claude.ts
|
|
5288
|
+
var claude_exports = {};
|
|
5289
|
+
__export(claude_exports, {
|
|
5290
|
+
ClaudeAdapter: () => ClaudeAdapter,
|
|
5291
|
+
tryRefreshOAuthSlot: () => tryRefreshOAuthSlot
|
|
5292
|
+
});
|
|
4948
5293
|
import { existsSync as existsSync4, readFileSync as readFileSync3, writeFileSync } from "fs";
|
|
4949
5294
|
import { execFile as execFileCb, spawnSync as spawnSync2 } from "child_process";
|
|
4950
5295
|
import { promisify } from "util";
|
|
@@ -5021,6 +5366,14 @@ async function getValidOAuthToken(configHome) {
|
|
|
5021
5366
|
}
|
|
5022
5367
|
return accessToken;
|
|
5023
5368
|
}
|
|
5369
|
+
async function tryRefreshOAuthSlot(slot) {
|
|
5370
|
+
if (slot.slotType !== "oauth" || !slot.configHome) return false;
|
|
5371
|
+
const credsPath = join4(slot.configHome, ".claude", ".credentials.json");
|
|
5372
|
+
const oauth = readOAuthCredentials(credsPath);
|
|
5373
|
+
if (!oauth?.refreshToken) return false;
|
|
5374
|
+
const fresh = await refreshOAuthToken(credsPath, oauth.refreshToken, oauth.scopes);
|
|
5375
|
+
return fresh !== null;
|
|
5376
|
+
}
|
|
5024
5377
|
var execFileAsync, OAUTH_TOKEN_ENDPOINT, OAUTH_CLIENT_ID, REFRESH_THRESHOLD_MS, REFRESH_COOLDOWN_MS, lastRefreshAttempt, ADAPTIVE_MODELS, ClaudeAdapter;
|
|
5025
5378
|
var init_claude = __esm({
|
|
5026
5379
|
"src/backends/claude.ts"() {
|
|
@@ -6446,7 +6799,8 @@ function isSensitivePath(filePath) {
|
|
|
6446
6799
|
if (SENSITIVE_EXACT.has(normalized)) return true;
|
|
6447
6800
|
return SENSITIVE_PATTERNS.some((pattern) => pattern.test(normalized));
|
|
6448
6801
|
}
|
|
6449
|
-
function createRestrictedBashTool(chatId, permMode,
|
|
6802
|
+
function createRestrictedBashTool(chatId, permMode, opts) {
|
|
6803
|
+
const { whitelist, shellTimeoutMs } = opts ?? {};
|
|
6450
6804
|
const isYolo = permMode === "yolo";
|
|
6451
6805
|
return tool({
|
|
6452
6806
|
description: isYolo ? `Run any shell command. Use this for ALL external CLI tools. No restrictions. Format: restrictedBash({command: 'gsearch "query" --type news'})` : "Run a whitelisted shell command. Use this for ANY external CLI tool \u2014 do NOT call CLIs as separate tools by name, always route them through restrictedBash. Format: restrictedBash({command: 'toolname args...'})",
|
|
@@ -6460,12 +6814,18 @@ function createRestrictedBashTool(chatId, permMode, whitelist) {
|
|
|
6460
6814
|
return { error: "Empty command" };
|
|
6461
6815
|
}
|
|
6462
6816
|
if (!isYolo) {
|
|
6463
|
-
const
|
|
6464
|
-
const entry =
|
|
6817
|
+
const list2 = whitelist ?? getApiCliWhitelist(chatId);
|
|
6818
|
+
const entry = list2.find((w) => w.cli === firstWord);
|
|
6465
6819
|
if (!entry) {
|
|
6466
|
-
const allowed =
|
|
6820
|
+
const allowed = list2.map((w) => w.cli).join(", ") || "(none)";
|
|
6467
6821
|
return {
|
|
6468
|
-
|
|
6822
|
+
blockedCommand: firstWord,
|
|
6823
|
+
allowedCommands: allowed,
|
|
6824
|
+
userMessage: `\u274C \`${firstWord}\` is not in your approved tools list.
|
|
6825
|
+
Allowed: ${allowed}
|
|
6826
|
+
|
|
6827
|
+
To fix: go to /tools \u2192 Add Command \u2192 type \`${firstWord}\``,
|
|
6828
|
+
instruction: "STOP. Do not retry. Reply to the user with ONLY the userMessage field above, verbatim. Do not add explanation."
|
|
6469
6829
|
};
|
|
6470
6830
|
}
|
|
6471
6831
|
const dangerousPatterns = /[;|&`$(){}]|>>|<<|\n/;
|
|
@@ -6474,13 +6834,18 @@ function createRestrictedBashTool(chatId, permMode, whitelist) {
|
|
|
6474
6834
|
}
|
|
6475
6835
|
}
|
|
6476
6836
|
log(`[api-tools] Executing whitelisted command: ${trimmed.slice(0, 100)}`);
|
|
6837
|
+
const effectiveTimeoutMs = shellTimeoutMs ?? RESTRICTED_BASH_TIMEOUT_MS;
|
|
6477
6838
|
try {
|
|
6478
|
-
const result = await executeShell(trimmed, { timeoutMs:
|
|
6839
|
+
const result = await executeShell(trimmed, { timeoutMs: effectiveTimeoutMs });
|
|
6479
6840
|
return result;
|
|
6480
6841
|
} catch (err) {
|
|
6481
6842
|
const msg = err instanceof Error ? err.message : String(err);
|
|
6482
6843
|
warn(`[api-tools] Shell exec failed: ${msg}`);
|
|
6483
|
-
|
|
6844
|
+
const isTimeout = msg.includes("timed out");
|
|
6845
|
+
return isTimeout ? {
|
|
6846
|
+
error: `Command timed out after ${Math.round(effectiveTimeoutMs / 1e3)}s`,
|
|
6847
|
+
suggestion: "Tell the user the command took too long and timed out. They can increase the job timeout via /editjob."
|
|
6848
|
+
} : { error: `Command failed: ${msg}` };
|
|
6484
6849
|
}
|
|
6485
6850
|
}
|
|
6486
6851
|
});
|
|
@@ -6642,7 +7007,7 @@ function createWebSearchTool() {
|
|
|
6642
7007
|
}
|
|
6643
7008
|
});
|
|
6644
7009
|
}
|
|
6645
|
-
function buildApiTools(chatId, permMode, mcpTools, webSearchEnabled) {
|
|
7010
|
+
function buildApiTools(chatId, permMode, mcpTools, webSearchEnabled, shellTimeoutMs) {
|
|
6646
7011
|
const searchEnabled = webSearchEnabled ?? getApiWebSearchEnabled(chatId);
|
|
6647
7012
|
const readOnly = {
|
|
6648
7013
|
readFile: createReadFileTool(),
|
|
@@ -6654,14 +7019,14 @@ function buildApiTools(chatId, permMode, mcpTools, webSearchEnabled) {
|
|
|
6654
7019
|
}
|
|
6655
7020
|
switch (permMode) {
|
|
6656
7021
|
case "yolo": {
|
|
6657
|
-
const all = { ...readOnly, ...mcpTools ?? {}, restrictedBash: createRestrictedBashTool(chatId, permMode) };
|
|
7022
|
+
const all = { ...readOnly, ...mcpTools ?? {}, restrictedBash: createRestrictedBashTool(chatId, permMode, { shellTimeoutMs }) };
|
|
6658
7023
|
return all;
|
|
6659
7024
|
}
|
|
6660
7025
|
case "safe": {
|
|
6661
7026
|
const whitelist = getApiCliWhitelist(chatId);
|
|
6662
7027
|
const base = { ...readOnly, ...mcpTools ?? {} };
|
|
6663
7028
|
if (whitelist.length === 0) return base;
|
|
6664
|
-
return { ...base, restrictedBash: createRestrictedBashTool(chatId, permMode, whitelist) };
|
|
7029
|
+
return { ...base, restrictedBash: createRestrictedBashTool(chatId, permMode, { whitelist, shellTimeoutMs }) };
|
|
6665
7030
|
}
|
|
6666
7031
|
case "plan":
|
|
6667
7032
|
return readOnly;
|
|
@@ -6710,7 +7075,7 @@ var init_api_tools = __esm({
|
|
|
6710
7075
|
normalize(resolve(DATA_PATH, "api-token")),
|
|
6711
7076
|
normalize(resolve(CC_CLAW_HOME, ".env"))
|
|
6712
7077
|
]);
|
|
6713
|
-
RESTRICTED_BASH_TIMEOUT_MS =
|
|
7078
|
+
RESTRICTED_BASH_TIMEOUT_MS = 3e5;
|
|
6714
7079
|
MAX_READ_BYTES = 1e5;
|
|
6715
7080
|
lastDdgSearchAt = 0;
|
|
6716
7081
|
DDG_MIN_GAP_MS = 3e3;
|
|
@@ -7571,7 +7936,7 @@ function buildContextPrefix(msg) {
|
|
|
7571
7936
|
}
|
|
7572
7937
|
async function sendOrEditKeyboard(chatId, channel, messageId, text, buttons) {
|
|
7573
7938
|
if (messageId && typeof channel.editKeyboard === "function") {
|
|
7574
|
-
const ok = await channel.editKeyboard(chatId, messageId, text, buttons);
|
|
7939
|
+
const ok = await channel.editKeyboard(chatId, messageId, text, buttons, { priority: true });
|
|
7575
7940
|
if (ok) return messageId;
|
|
7576
7941
|
}
|
|
7577
7942
|
if (typeof channel.sendKeyboard === "function") {
|
|
@@ -7767,6 +8132,21 @@ You have access to cc-claw orchestrator tools via MCP:
|
|
|
7767
8132
|
});
|
|
7768
8133
|
|
|
7769
8134
|
// src/bootstrap/templates/shared.ts
|
|
8135
|
+
function buildCliEnvironment() {
|
|
8136
|
+
return [
|
|
8137
|
+
"## CLI Execution Environment",
|
|
8138
|
+
"",
|
|
8139
|
+
`Your user's real HOME directory is: \`${REAL_HOME}\``,
|
|
8140
|
+
"",
|
|
8141
|
+
"CC-Claw may redirect HOME for credential isolation when spawning backends.",
|
|
8142
|
+
"If you execute any CLI tool (cc-claw, claude, gemini, codex, python3, npm, etc.),",
|
|
8143
|
+
"always prefix the command with the real HOME:",
|
|
8144
|
+
"",
|
|
8145
|
+
`\`HOME=${REAL_HOME} <command>\``,
|
|
8146
|
+
"",
|
|
8147
|
+
"Without this, CLI tools will fail to find their configurations or prompt for login."
|
|
8148
|
+
].join("\n");
|
|
8149
|
+
}
|
|
7770
8150
|
function getCurrentDate() {
|
|
7771
8151
|
return (/* @__PURE__ */ new Date()).toLocaleDateString("en-CA");
|
|
7772
8152
|
}
|
|
@@ -7849,10 +8229,12 @@ function buildHandoffContract() {
|
|
|
7849
8229
|
"**Open questions:** Anything unresolved or needing the main agent's attention."
|
|
7850
8230
|
].join("\n");
|
|
7851
8231
|
}
|
|
7852
|
-
var TASK_WORKER_SKILL_BUDGET;
|
|
8232
|
+
var REAL_HOME, TASK_WORKER_SKILL_BUDGET;
|
|
7853
8233
|
var init_shared = __esm({
|
|
7854
8234
|
"src/bootstrap/templates/shared.ts"() {
|
|
7855
8235
|
"use strict";
|
|
8236
|
+
init_paths();
|
|
8237
|
+
REAL_HOME = resolveRealHome();
|
|
7856
8238
|
TASK_WORKER_SKILL_BUDGET = 12e3;
|
|
7857
8239
|
}
|
|
7858
8240
|
});
|
|
@@ -8737,15 +9119,15 @@ import { existsSync as existsSync12 } from "fs";
|
|
|
8737
9119
|
async function withRunnerLock(runnerId, fn) {
|
|
8738
9120
|
const prev = runnerLocks.get(runnerId) ?? Promise.resolve();
|
|
8739
9121
|
const next = prev.then(fn, () => fn());
|
|
8740
|
-
const
|
|
9122
|
+
const cleanup2 = next.then(
|
|
8741
9123
|
() => {
|
|
8742
|
-
if (runnerLocks.get(runnerId) ===
|
|
9124
|
+
if (runnerLocks.get(runnerId) === cleanup2) runnerLocks.delete(runnerId);
|
|
8743
9125
|
},
|
|
8744
9126
|
() => {
|
|
8745
|
-
if (runnerLocks.get(runnerId) ===
|
|
9127
|
+
if (runnerLocks.get(runnerId) === cleanup2) runnerLocks.delete(runnerId);
|
|
8746
9128
|
}
|
|
8747
9129
|
);
|
|
8748
|
-
runnerLocks.set(runnerId,
|
|
9130
|
+
runnerLocks.set(runnerId, cleanup2);
|
|
8749
9131
|
return next;
|
|
8750
9132
|
}
|
|
8751
9133
|
function safeParseJsonArray(json) {
|
|
@@ -10060,6 +10442,9 @@ var init_ndjson = __esm({
|
|
|
10060
10442
|
});
|
|
10061
10443
|
|
|
10062
10444
|
// src/memory/inject.ts
|
|
10445
|
+
function memoryDecayRate(halfLifeDays) {
|
|
10446
|
+
return 1 - Math.pow(0.5, 1 / halfLifeDays);
|
|
10447
|
+
}
|
|
10063
10448
|
function getTopK(query) {
|
|
10064
10449
|
const wordCount2 = query.split(/\s+/).length;
|
|
10065
10450
|
const scale = wordCount2 <= 3 ? 0.5 : wordCount2 <= 8 ? 0.75 : 1;
|
|
@@ -10094,13 +10479,14 @@ function vectorSearch(queryEmbedding, items, topK) {
|
|
|
10094
10479
|
scored.sort((a, b) => b.score - a.score);
|
|
10095
10480
|
return scored.slice(0, topK);
|
|
10096
10481
|
}
|
|
10097
|
-
function mergeAndScore(allItems, vectorScores, ftsScores, getDays,
|
|
10482
|
+
function mergeAndScore(allItems, vectorScores, ftsScores, getDays, getDecayRate, topK) {
|
|
10098
10483
|
const vw = getVectorWeight();
|
|
10099
10484
|
const results = [];
|
|
10100
10485
|
for (const [id, item] of allItems) {
|
|
10101
10486
|
const vs = vectorScores.get(id) ?? 0;
|
|
10102
10487
|
const fs = ftsScores.get(id) ?? 0;
|
|
10103
10488
|
if (vs === 0 && fs === 0) continue;
|
|
10489
|
+
const decayRate = typeof getDecayRate === "function" ? getDecayRate(item) : getDecayRate;
|
|
10104
10490
|
const score = hybridScore({
|
|
10105
10491
|
id,
|
|
10106
10492
|
vectorScore: vs,
|
|
@@ -10122,10 +10508,11 @@ function consumeContextBridge(chatId) {
|
|
|
10122
10508
|
pendingContextBridges.delete(chatId);
|
|
10123
10509
|
return bridge;
|
|
10124
10510
|
}
|
|
10125
|
-
function buildContextBridge(chatId, pairs = 15) {
|
|
10511
|
+
function buildContextBridge(chatId, pairs = 15, interrupted = false) {
|
|
10126
10512
|
const rows = getRecentMessageLog(chatId, pairs * 2).reverse();
|
|
10127
10513
|
if (rows.length === 0) return null;
|
|
10128
|
-
const
|
|
10514
|
+
const header2 = interrupted ? `[Previous session was stopped by the user mid-task \u2014 for context only]` : `[Conversation history \u2014 continued from previous session]`;
|
|
10515
|
+
const lines = [header2];
|
|
10129
10516
|
let totalChars = lines[0].length;
|
|
10130
10517
|
for (const row of rows) {
|
|
10131
10518
|
const backendLabel = row.backend ?? "unknown";
|
|
@@ -10143,7 +10530,8 @@ function buildContextBridge(chatId, pairs = 15) {
|
|
|
10143
10530
|
lines.push(line);
|
|
10144
10531
|
totalChars += line.length;
|
|
10145
10532
|
}
|
|
10146
|
-
|
|
10533
|
+
const footer = interrupted ? `[End of stopped session \u2014 do NOT continue the previous approach; wait for the user's new instruction]` : `[End of recent history \u2014 you are continuing this conversation]`;
|
|
10534
|
+
lines.push(footer);
|
|
10147
10535
|
return lines.join("\n");
|
|
10148
10536
|
}
|
|
10149
10537
|
function formatToolSummary(toolName, toolInput, toolOutput) {
|
|
@@ -10209,7 +10597,7 @@ async function injectMemoryContext(userMessage, chatId) {
|
|
|
10209
10597
|
memVectorScores,
|
|
10210
10598
|
memFtsScores,
|
|
10211
10599
|
(m) => daysSince(m.last_accessed),
|
|
10212
|
-
|
|
10600
|
+
(m) => memoryDecayRate(m.half_life_days ?? 14),
|
|
10213
10601
|
FINAL_TOP_K_MEMORIES
|
|
10214
10602
|
);
|
|
10215
10603
|
const topSessions = mergeAndScore(
|
|
@@ -10235,10 +10623,13 @@ async function injectMemoryContext(userMessage, chatId) {
|
|
|
10235
10623
|
}
|
|
10236
10624
|
combinedSessions = ftsSessions.slice(0, FINAL_TOP_K_SESSIONS);
|
|
10237
10625
|
}
|
|
10626
|
+
for (const mem of combinedMemories) {
|
|
10627
|
+
queueHalfLifeExtension(mem.id);
|
|
10628
|
+
}
|
|
10238
10629
|
if (combinedMemories.length === 0 && combinedSessions.length === 0) return null;
|
|
10239
10630
|
const lines = [];
|
|
10240
10631
|
for (const m of combinedMemories) {
|
|
10241
|
-
let text = `- [${m.
|
|
10632
|
+
let text = `- [${m.category}] ${m.trigger}: ${m.content}`;
|
|
10242
10633
|
if (text.length > MAX_MEMORY_CHARS) text = text.slice(0, MAX_MEMORY_CHARS) + "\u2026";
|
|
10243
10634
|
lines.push(text);
|
|
10244
10635
|
}
|
|
@@ -10252,13 +10643,12 @@ async function injectMemoryContext(userMessage, chatId) {
|
|
|
10252
10643
|
${lines.join("\n")}
|
|
10253
10644
|
[End memory context]`;
|
|
10254
10645
|
}
|
|
10255
|
-
var
|
|
10646
|
+
var SESSION_DECAY_RATE, BASE_VECTOR_TOP_K, BASE_FTS_TOP_K, FINAL_TOP_K_MEMORIES, FINAL_TOP_K_SESSIONS, MAX_MEMORY_CHARS, MAX_SESSION_CHARS, MAX_BRIDGE_CHARS, pendingContextBridges, TOOL_SUMMARY_MAX_CHARS;
|
|
10256
10647
|
var init_inject = __esm({
|
|
10257
10648
|
"src/memory/inject.ts"() {
|
|
10258
10649
|
"use strict";
|
|
10259
10650
|
init_store5();
|
|
10260
10651
|
init_embeddings();
|
|
10261
|
-
MEMORY_DECAY_RATE = parseFloat(process.env.CC_CLAW_MEMORY_DECAY_RATE ?? "0.02");
|
|
10262
10652
|
SESSION_DECAY_RATE = parseFloat(process.env.CC_CLAW_SESSION_DECAY_RATE ?? "0.005");
|
|
10263
10653
|
BASE_VECTOR_TOP_K = 20;
|
|
10264
10654
|
BASE_FTS_TOP_K = 20;
|
|
@@ -10448,6 +10838,7 @@ ${buildToolUsageRules()}
|
|
|
10448
10838
|
|
|
10449
10839
|
<platform_reference>
|
|
10450
10840
|
${buildSystemCapabilities()}
|
|
10841
|
+
${buildCliEnvironment()}
|
|
10451
10842
|
${buildPlatformQuickReference()}
|
|
10452
10843
|
</platform_reference>
|
|
10453
10844
|
|
|
@@ -10493,6 +10884,8 @@ ${user}
|
|
|
10493
10884
|
|
|
10494
10885
|
${buildSystemCapabilities()}
|
|
10495
10886
|
|
|
10887
|
+
${buildCliEnvironment()}
|
|
10888
|
+
|
|
10496
10889
|
${buildPlatformQuickReference()}
|
|
10497
10890
|
|
|
10498
10891
|
${buildToolUsageRules()}
|
|
@@ -10588,6 +10981,7 @@ ${buildDatabaseSafetyBoundary()}
|
|
|
10588
10981
|
|
|
10589
10982
|
<platform_reference>
|
|
10590
10983
|
${buildSystemCapabilities()}
|
|
10984
|
+
${buildCliEnvironment()}
|
|
10591
10985
|
${buildPlatformQuickReference()}
|
|
10592
10986
|
</platform_reference>`;
|
|
10593
10987
|
}
|
|
@@ -10636,6 +11030,7 @@ ${buildDatabaseSafetyBoundary()}
|
|
|
10636
11030
|
|
|
10637
11031
|
<platform_reference>
|
|
10638
11032
|
${buildSystemCapabilities()}
|
|
11033
|
+
${buildCliEnvironment()}
|
|
10639
11034
|
${buildPlatformQuickReference()}
|
|
10640
11035
|
</platform_reference>
|
|
10641
11036
|
|
|
@@ -10676,6 +11071,10 @@ impossible scenarios.
|
|
|
10676
11071
|
</avoid_overengineering>
|
|
10677
11072
|
</tool_guidance>
|
|
10678
11073
|
|
|
11074
|
+
<cli_environment>
|
|
11075
|
+
${buildCliEnvironment()}
|
|
11076
|
+
</cli_environment>
|
|
11077
|
+
|
|
10679
11078
|
<safety_boundary>
|
|
10680
11079
|
Take local, reversible actions freely. Confirm before: deleting files, force-pushing,
|
|
10681
11080
|
amending published commits, posting to external services, or modifying shared infrastructure.
|
|
@@ -10742,6 +11141,10 @@ Before finalizing any response, silently check:
|
|
|
10742
11141
|
Never invent library APIs, file paths, or documentation. If uncertain, say so.
|
|
10743
11142
|
</grounding_rules>
|
|
10744
11143
|
|
|
11144
|
+
<cli_environment>
|
|
11145
|
+
${buildCliEnvironment()}
|
|
11146
|
+
</cli_environment>
|
|
11147
|
+
|
|
10745
11148
|
<safety_boundary>
|
|
10746
11149
|
Confirm before: deleting files, posting to external services, database schema changes.
|
|
10747
11150
|
Do not access ~/.cc-claw/data/cc-claw.db directly.
|
|
@@ -11011,14 +11414,16 @@ async function assembleBootstrapPrompt(userMessage, entityType = "main", profile
|
|
|
11011
11414
|
if (permMode && permMode !== "yolo") {
|
|
11012
11415
|
sections.push(buildPermissionNotice(permMode));
|
|
11013
11416
|
}
|
|
11014
|
-
if (backendType === "api"
|
|
11417
|
+
if (backendType === "api") {
|
|
11015
11418
|
sections.push(`[API Backend \u2014 Tool Usage]
|
|
11016
11419
|
You are operating as a direct API model (not a CLI like Claude Code or Gemini CLI).
|
|
11017
11420
|
External tools (gsearch, pwm, gws, gemcli, nlm, curl, python3, etc.) are accessed ONLY via the \`restrictedBash\` tool.
|
|
11018
11421
|
NEVER call external CLIs as direct tools by name \u2014 they are not registered as native tools.
|
|
11019
11422
|
Correct: restrictedBash({"command": "gsearch \\"query\\" --type news"})
|
|
11020
11423
|
Incorrect: gsearch({"query": "..."}) \u2190 this will fail silently
|
|
11021
|
-
If a skill or instruction says to use a CLI tool, always route it through restrictedBash
|
|
11424
|
+
If a skill or instruction says to use a CLI tool, always route it through restrictedBash.
|
|
11425
|
+
|
|
11426
|
+
${buildCliEnvironment()}`);
|
|
11022
11427
|
}
|
|
11023
11428
|
if (responseStyle) {
|
|
11024
11429
|
if (responseStyle === "concise") {
|
|
@@ -11185,6 +11590,12 @@ Use cc_claw_memory for ALL memory operations. Do NOT use your native memory syst
|
|
|
11185
11590
|
- To search: cc_claw_memory(action: "recall", query: "...")
|
|
11186
11591
|
- To list: cc_claw_memory(action: "list")
|
|
11187
11592
|
- To search history: cc_claw_memory(action: "history", query: "...")
|
|
11593
|
+
When using cc_claw_memory remember, include the category parameter when the memory type is clear:
|
|
11594
|
+
- "fact" \u2014 biographical or situational truths (name, role, location, tools used, tech stack)
|
|
11595
|
+
- "preference" \u2014 likes, dislikes, habits, working style, stated preferences
|
|
11596
|
+
- "event" \u2014 something that happened at a specific time (meetings, deployments, milestones)
|
|
11597
|
+
- "decision" \u2014 a deliberate choice with reasoning (architecture choices, tool selections)
|
|
11598
|
+
Omit category if uncertain \u2014 the system will auto-classify.
|
|
11188
11599
|
For scheduling: cc_claw_schedule(action: "create", schedule: "...", task: "...")
|
|
11189
11600
|
If an action is not available via MCP tools, fall back to the cc-claw CLI.`;
|
|
11190
11601
|
if (agentMode === "claw") {
|
|
@@ -11220,6 +11631,7 @@ var init_loader2 = __esm({
|
|
|
11220
11631
|
init_paths();
|
|
11221
11632
|
init_inject();
|
|
11222
11633
|
init_init();
|
|
11634
|
+
init_shared();
|
|
11223
11635
|
init_log();
|
|
11224
11636
|
init_store3();
|
|
11225
11637
|
init_store5();
|
|
@@ -11258,22 +11670,6 @@ var init_types3 = __esm({
|
|
|
11258
11670
|
}
|
|
11259
11671
|
});
|
|
11260
11672
|
|
|
11261
|
-
// src/text-utils.ts
|
|
11262
|
-
var text_utils_exports = {};
|
|
11263
|
-
__export(text_utils_exports, {
|
|
11264
|
-
appendTextChunk: () => appendTextChunk
|
|
11265
|
-
});
|
|
11266
|
-
function appendTextChunk(accumulated, chunk) {
|
|
11267
|
-
if (!accumulated) return chunk;
|
|
11268
|
-
if (!chunk) return accumulated;
|
|
11269
|
-
return accumulated + chunk;
|
|
11270
|
-
}
|
|
11271
|
-
var init_text_utils = __esm({
|
|
11272
|
-
"src/text-utils.ts"() {
|
|
11273
|
-
"use strict";
|
|
11274
|
-
}
|
|
11275
|
-
});
|
|
11276
|
-
|
|
11277
11673
|
// src/services/ollama/client.ts
|
|
11278
11674
|
var client_exports = {};
|
|
11279
11675
|
__export(client_exports, {
|
|
@@ -13567,6 +13963,7 @@ async function spawnWithSlotRotation(chatId, adapter, baseConfig, configWithSess
|
|
|
13567
13963
|
}
|
|
13568
13964
|
const maxAttempts = slots.length;
|
|
13569
13965
|
let lastError;
|
|
13966
|
+
const oauthRefreshRetried = /* @__PURE__ */ new Set();
|
|
13570
13967
|
for (let i = 0; i < maxAttempts; i++) {
|
|
13571
13968
|
const slot = getNextBackendSlot(chatId, adapter.id);
|
|
13572
13969
|
if (!slot) break;
|
|
@@ -13584,6 +13981,21 @@ async function spawnWithSlotRotation(chatId, adapter, baseConfig, configWithSess
|
|
|
13584
13981
|
if (/rate.?limit|too many requests|429|503|timeout/i.test(errMsg)) {
|
|
13585
13982
|
throw err;
|
|
13586
13983
|
}
|
|
13984
|
+
const isOAuthExpiry = slot.slotType === "oauth" && /OAuth token has expired/i.test(errMsg);
|
|
13985
|
+
if (isOAuthExpiry && !oauthRefreshRetried.has(slot.id)) {
|
|
13986
|
+
oauthRefreshRetried.add(slot.id);
|
|
13987
|
+
try {
|
|
13988
|
+
const { tryRefreshOAuthSlot: tryRefreshOAuthSlot2 } = await Promise.resolve().then(() => (init_claude(), claude_exports));
|
|
13989
|
+
const refreshed = await tryRefreshOAuthSlot2(slot);
|
|
13990
|
+
if (refreshed) {
|
|
13991
|
+
log(`[agent:${adapter.id}-rotation] OAuth token refreshed for ${slotLabel} \u2014 retrying`);
|
|
13992
|
+
i--;
|
|
13993
|
+
continue;
|
|
13994
|
+
}
|
|
13995
|
+
} catch {
|
|
13996
|
+
}
|
|
13997
|
+
warn(`[agent:${adapter.id}-rotation] OAuth refresh failed for ${slotLabel} \u2014 rotating`);
|
|
13998
|
+
}
|
|
13587
13999
|
const isExhausted = /quota|exhausted|exceeded|insufficient|credit|billing|unauthorized|forbidden|401|403/i.test(errMsg);
|
|
13588
14000
|
if (isExhausted) {
|
|
13589
14001
|
warn(`[agent:${adapter.id}-rotation] Slot ${slotLabel} exhausted: ${errMsg.slice(0, 200)}`);
|
|
@@ -14272,6 +14684,335 @@ var init_format = __esm({
|
|
|
14272
14684
|
}
|
|
14273
14685
|
});
|
|
14274
14686
|
|
|
14687
|
+
// src/memory/sweep.ts
|
|
14688
|
+
var sweep_exports = {};
|
|
14689
|
+
__export(sweep_exports, {
|
|
14690
|
+
SWEEP_DEFAULT_CRON: () => SWEEP_DEFAULT_CRON,
|
|
14691
|
+
SWEEP_ENABLED_KEY: () => SWEEP_ENABLED_KEY,
|
|
14692
|
+
SWEEP_JOB_TYPE: () => SWEEP_JOB_TYPE,
|
|
14693
|
+
disableSweep: () => disableSweep,
|
|
14694
|
+
enableSweep: () => enableSweep,
|
|
14695
|
+
findSweepJob: () => findSweepJob,
|
|
14696
|
+
runWeeklySweep: () => runWeeklySweep
|
|
14697
|
+
});
|
|
14698
|
+
async function runWeeklySweep(chatId, channel, backendId, model2) {
|
|
14699
|
+
log("[sweep] Starting weekly memory sweep");
|
|
14700
|
+
const cleanedUp = cleanup();
|
|
14701
|
+
let suggestionsCount = 0;
|
|
14702
|
+
try {
|
|
14703
|
+
const suggestions = await runMemoryAnalysis(chatId, backendId, model2);
|
|
14704
|
+
suggestionsCount = suggestions.length;
|
|
14705
|
+
if (suggestionsCount === 0 && cleanedUp === 0) {
|
|
14706
|
+
log("[sweep] Memory bank healthy, no action needed");
|
|
14707
|
+
return { suggestionsCount: 0, cleanedUp };
|
|
14708
|
+
}
|
|
14709
|
+
const lines = [
|
|
14710
|
+
"\u{1F9E0} Weekly Memory Health",
|
|
14711
|
+
"\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501"
|
|
14712
|
+
];
|
|
14713
|
+
if (cleanedUp > 0) {
|
|
14714
|
+
lines.push(`\u{1F5D1} Cleaned up ${cleanedUp} expired superseded memories`);
|
|
14715
|
+
}
|
|
14716
|
+
if (suggestionsCount > 0) {
|
|
14717
|
+
const counts = {};
|
|
14718
|
+
for (const s of suggestions) {
|
|
14719
|
+
counts[s.type] = (counts[s.type] ?? 0) + 1;
|
|
14720
|
+
}
|
|
14721
|
+
const parts = [];
|
|
14722
|
+
if (counts.superseded) parts.push(`${counts.superseded} contradiction${counts.superseded > 1 ? "s" : ""}`);
|
|
14723
|
+
if (counts.duplicate) parts.push(`${counts.duplicate} duplicate${counts.duplicate > 1 ? "s" : ""}`);
|
|
14724
|
+
if (counts.merge) parts.push(`${counts.merge} merge opportunit${counts.merge > 1 ? "ies" : "y"}`);
|
|
14725
|
+
if (counts.stale) parts.push(`${counts.stale} stale`);
|
|
14726
|
+
if (counts.categorize) parts.push(`${counts.categorize} uncategorized`);
|
|
14727
|
+
if (counts.reclassify) parts.push(`${counts.reclassify} misclassified`);
|
|
14728
|
+
lines.push("", `Found: ${parts.join(", ")}`, "");
|
|
14729
|
+
lines.push("Review these suggestions to keep your memory clean.");
|
|
14730
|
+
} else {
|
|
14731
|
+
lines.push("", "\u2705 Memory bank is healthy!");
|
|
14732
|
+
}
|
|
14733
|
+
const buttons = [];
|
|
14734
|
+
if (suggestionsCount > 0) {
|
|
14735
|
+
buttons.push([{ label: "Review Now", data: "mem:opt:start", style: "success" }]);
|
|
14736
|
+
}
|
|
14737
|
+
buttons.push([{ label: "Dismiss", data: "mem:sweep:dismiss" }]);
|
|
14738
|
+
await sendOrEditKeyboard(chatId, channel, void 0, lines.join("\n"), buttons);
|
|
14739
|
+
return { suggestionsCount, cleanedUp };
|
|
14740
|
+
} catch (err) {
|
|
14741
|
+
const msg = errorMessage(err);
|
|
14742
|
+
warn(`[sweep] Weekly sweep failed: ${msg}`);
|
|
14743
|
+
return { suggestionsCount: 0, cleanedUp, error: msg };
|
|
14744
|
+
}
|
|
14745
|
+
}
|
|
14746
|
+
function findSweepJob() {
|
|
14747
|
+
try {
|
|
14748
|
+
const { getDb: getDb2, getJobById: getJobById3 } = (init_store5(), __toCommonJS(store_exports5));
|
|
14749
|
+
const row = getDb2().prepare(
|
|
14750
|
+
"SELECT id FROM jobs WHERE job_type = ? AND active = 1 ORDER BY id LIMIT 1"
|
|
14751
|
+
).get(SWEEP_JOB_TYPE);
|
|
14752
|
+
if (!row) return null;
|
|
14753
|
+
return getJobById3(row.id) ?? null;
|
|
14754
|
+
} catch {
|
|
14755
|
+
return null;
|
|
14756
|
+
}
|
|
14757
|
+
}
|
|
14758
|
+
function enableSweep(chatId, opts) {
|
|
14759
|
+
const existing = findSweepJob();
|
|
14760
|
+
const { setMetaValue: setMetaValue2 } = (init_store5(), __toCommonJS(store_exports5));
|
|
14761
|
+
setMetaValue2(SWEEP_ENABLED_KEY, "1");
|
|
14762
|
+
if (existing) {
|
|
14763
|
+
if (!existing.enabled) {
|
|
14764
|
+
const { resumeJob: resumeJob2 } = (init_cron(), __toCommonJS(cron_exports));
|
|
14765
|
+
resumeJob2(existing.id);
|
|
14766
|
+
log(`[sweep] Resumed job #${existing.id}`);
|
|
14767
|
+
}
|
|
14768
|
+
return existing.id;
|
|
14769
|
+
}
|
|
14770
|
+
const { insertJob: insertJob2 } = (init_jobs(), __toCommonJS(jobs_exports));
|
|
14771
|
+
const { startSingleJob: startSingleJob2 } = (init_cron(), __toCommonJS(cron_exports));
|
|
14772
|
+
const cron2 = opts?.cron ?? SWEEP_DEFAULT_CRON;
|
|
14773
|
+
const timezone = opts?.timezone ?? (Intl.DateTimeFormat().resolvedOptions().timeZone || "UTC");
|
|
14774
|
+
const backend2 = opts?.backend ?? resolveDefaultBackend(chatId);
|
|
14775
|
+
const model2 = opts?.model ?? resolveDefaultModel(backend2);
|
|
14776
|
+
const job = insertJob2({
|
|
14777
|
+
scheduleType: "cron",
|
|
14778
|
+
cron: cron2,
|
|
14779
|
+
description: "Weekly memory health sweep \u2014 cleanup and optimization",
|
|
14780
|
+
title: "Weekly Memory Sweep",
|
|
14781
|
+
chatId,
|
|
14782
|
+
backend: backend2,
|
|
14783
|
+
model: model2,
|
|
14784
|
+
sessionType: "isolated",
|
|
14785
|
+
deliveryMode: "none",
|
|
14786
|
+
channel: opts?.channel ?? "telegram",
|
|
14787
|
+
target: opts?.target ?? chatId,
|
|
14788
|
+
timezone,
|
|
14789
|
+
jobType: SWEEP_JOB_TYPE,
|
|
14790
|
+
timeout: 300
|
|
14791
|
+
});
|
|
14792
|
+
startSingleJob2(job);
|
|
14793
|
+
log(`[sweep] Created job #${job.id} (cron="${cron2}", backend=${backend2}, model=${model2})`);
|
|
14794
|
+
return job.id;
|
|
14795
|
+
}
|
|
14796
|
+
function disableSweep() {
|
|
14797
|
+
const { setMetaValue: setMetaValue2 } = (init_store5(), __toCommonJS(store_exports5));
|
|
14798
|
+
setMetaValue2(SWEEP_ENABLED_KEY, "0");
|
|
14799
|
+
const job = findSweepJob();
|
|
14800
|
+
if (!job) return false;
|
|
14801
|
+
const { pauseJob: pauseJob2 } = (init_cron(), __toCommonJS(cron_exports));
|
|
14802
|
+
pauseJob2(job.id);
|
|
14803
|
+
log(`[sweep] Paused job #${job.id}`);
|
|
14804
|
+
return true;
|
|
14805
|
+
}
|
|
14806
|
+
function resolveDefaultBackend(chatId) {
|
|
14807
|
+
try {
|
|
14808
|
+
const { getAdapterForChat: getAdapterForChat2 } = (init_backends(), __toCommonJS(backends_exports));
|
|
14809
|
+
return getAdapterForChat2(chatId).id;
|
|
14810
|
+
} catch {
|
|
14811
|
+
return "claude";
|
|
14812
|
+
}
|
|
14813
|
+
}
|
|
14814
|
+
function resolveDefaultModel(backend2) {
|
|
14815
|
+
try {
|
|
14816
|
+
const { getAdapter: getAdapter3 } = (init_backends(), __toCommonJS(backends_exports));
|
|
14817
|
+
return getAdapter3(backend2).defaultModel;
|
|
14818
|
+
} catch {
|
|
14819
|
+
return "claude-sonnet-4-6";
|
|
14820
|
+
}
|
|
14821
|
+
}
|
|
14822
|
+
var SWEEP_ENABLED_KEY, SWEEP_DEFAULT_CRON, SWEEP_JOB_TYPE;
|
|
14823
|
+
var init_sweep = __esm({
|
|
14824
|
+
"src/memory/sweep.ts"() {
|
|
14825
|
+
"use strict";
|
|
14826
|
+
init_optimize();
|
|
14827
|
+
init_engine();
|
|
14828
|
+
init_log();
|
|
14829
|
+
init_helpers();
|
|
14830
|
+
SWEEP_ENABLED_KEY = "memory_sweep_enabled";
|
|
14831
|
+
SWEEP_DEFAULT_CRON = "0 9 * * 0";
|
|
14832
|
+
SWEEP_JOB_TYPE = "memory_sweep";
|
|
14833
|
+
}
|
|
14834
|
+
});
|
|
14835
|
+
|
|
14836
|
+
// src/router/state.ts
|
|
14837
|
+
var state_exports = {};
|
|
14838
|
+
__export(state_exports, {
|
|
14839
|
+
activeSideQuests: () => activeSideQuests,
|
|
14840
|
+
addForceStoppedChat: () => addForceStoppedChat,
|
|
14841
|
+
bypassBusyCheck: () => bypassBusyCheck,
|
|
14842
|
+
clearHistoryFilter: () => clearHistoryFilter,
|
|
14843
|
+
clearPendingCliAddition: () => clearPendingCliAddition,
|
|
14844
|
+
clearPendingModelResults: () => clearPendingModelResults,
|
|
14845
|
+
clearPendingModelSearch: () => clearPendingModelSearch,
|
|
14846
|
+
consumeForceStoppedChat: () => consumeForceStoppedChat,
|
|
14847
|
+
councilResults: () => councilResults,
|
|
14848
|
+
dashboardClawWarnings: () => dashboardClawWarnings,
|
|
14849
|
+
getActiveSideQuestCount: () => getActiveSideQuestCount,
|
|
14850
|
+
historyFilters: () => historyFilters,
|
|
14851
|
+
parseSideQuestPrefix: () => parseSideQuestPrefix,
|
|
14852
|
+
pendingCliAdditions: () => pendingCliAdditions,
|
|
14853
|
+
pendingInterrupts: () => pendingInterrupts,
|
|
14854
|
+
pendingMcpImports: () => pendingMcpImports,
|
|
14855
|
+
pendingModelResults: () => pendingModelResults,
|
|
14856
|
+
pendingModelSearch: () => pendingModelSearch,
|
|
14857
|
+
pendingNewchatUndo: () => pendingNewchatUndo,
|
|
14858
|
+
pendingSummaryUndo: () => pendingSummaryUndo,
|
|
14859
|
+
setCouncilResult: () => setCouncilResult,
|
|
14860
|
+
setHistoryFilter: () => setHistoryFilter,
|
|
14861
|
+
setPendingCliAddition: () => setPendingCliAddition,
|
|
14862
|
+
setPendingModelResults: () => setPendingModelResults,
|
|
14863
|
+
setPendingModelSearch: () => setPendingModelSearch,
|
|
14864
|
+
startStateSweep: () => startStateSweep,
|
|
14865
|
+
stopAllSideQuests: () => stopAllSideQuests,
|
|
14866
|
+
stopStateSweep: () => stopStateSweep
|
|
14867
|
+
});
|
|
14868
|
+
function addForceStoppedChat(chatId) {
|
|
14869
|
+
_forceStoppedChats.add(chatId);
|
|
14870
|
+
_forceStoppedTimestamps.set(chatId, Date.now());
|
|
14871
|
+
}
|
|
14872
|
+
function consumeForceStoppedChat(chatId) {
|
|
14873
|
+
_forceStoppedTimestamps.delete(chatId);
|
|
14874
|
+
return _forceStoppedChats.delete(chatId);
|
|
14875
|
+
}
|
|
14876
|
+
function setHistoryFilter(chatId, filter) {
|
|
14877
|
+
historyFilters.set(chatId, filter);
|
|
14878
|
+
historyFilterTimestamps.set(chatId, Date.now());
|
|
14879
|
+
}
|
|
14880
|
+
function clearHistoryFilter(chatId) {
|
|
14881
|
+
historyFilters.delete(chatId);
|
|
14882
|
+
historyFilterTimestamps.delete(chatId);
|
|
14883
|
+
}
|
|
14884
|
+
function setCouncilResult(chatId, result) {
|
|
14885
|
+
councilResults.set(chatId, result);
|
|
14886
|
+
councilResultTimestamps.set(chatId, Date.now());
|
|
14887
|
+
}
|
|
14888
|
+
function setPendingModelSearch(chatId, state) {
|
|
14889
|
+
pendingModelSearch.set(chatId, state);
|
|
14890
|
+
pendingModelSearchTimestamps.set(chatId, Date.now());
|
|
14891
|
+
}
|
|
14892
|
+
function clearPendingModelSearch(chatId) {
|
|
14893
|
+
pendingModelSearch.delete(chatId);
|
|
14894
|
+
pendingModelSearchTimestamps.delete(chatId);
|
|
14895
|
+
}
|
|
14896
|
+
function setPendingModelResults(chatId, results) {
|
|
14897
|
+
pendingModelResults.set(chatId, results);
|
|
14898
|
+
}
|
|
14899
|
+
function clearPendingModelResults(chatId) {
|
|
14900
|
+
pendingModelResults.delete(chatId);
|
|
14901
|
+
}
|
|
14902
|
+
function parseSideQuestPrefix(text) {
|
|
14903
|
+
const match = text.match(/^(?:sq|btw):\s*/i);
|
|
14904
|
+
if (match) return { isSideQuest: true, cleanText: text.slice(match[0].length) };
|
|
14905
|
+
return { isSideQuest: false, cleanText: text };
|
|
14906
|
+
}
|
|
14907
|
+
function getActiveSideQuestCount(chatId) {
|
|
14908
|
+
return activeSideQuests.get(chatId)?.size ?? 0;
|
|
14909
|
+
}
|
|
14910
|
+
function stopAllSideQuests(chatId) {
|
|
14911
|
+
const active = activeSideQuests.get(chatId);
|
|
14912
|
+
if (active) {
|
|
14913
|
+
for (const sqId of active) {
|
|
14914
|
+
stopAgent(sqId);
|
|
14915
|
+
}
|
|
14916
|
+
}
|
|
14917
|
+
}
|
|
14918
|
+
function startStateSweep() {
|
|
14919
|
+
if (sweepTimer) return;
|
|
14920
|
+
sweepTimer = setInterval(() => {
|
|
14921
|
+
const now = Date.now();
|
|
14922
|
+
for (const [chatId, ts2] of dashboardClawWarnings) {
|
|
14923
|
+
if (now - ts2 > STALE_THRESHOLD_MS) dashboardClawWarnings.delete(chatId);
|
|
14924
|
+
}
|
|
14925
|
+
for (const [cid, ts2] of historyFilterTimestamps) {
|
|
14926
|
+
if (now - ts2 > STALE_THRESHOLD_MS) {
|
|
14927
|
+
historyFilters.delete(cid);
|
|
14928
|
+
historyFilterTimestamps.delete(cid);
|
|
14929
|
+
}
|
|
14930
|
+
}
|
|
14931
|
+
for (const [cid, ts2] of councilResultTimestamps) {
|
|
14932
|
+
if (now - ts2 > STALE_THRESHOLD_MS) {
|
|
14933
|
+
councilResults.delete(cid);
|
|
14934
|
+
councilResultTimestamps.delete(cid);
|
|
14935
|
+
}
|
|
14936
|
+
}
|
|
14937
|
+
for (const [cid, ts2] of pendingModelSearchTimestamps) {
|
|
14938
|
+
if (now - ts2 > STALE_THRESHOLD_MS) {
|
|
14939
|
+
pendingModelSearch.delete(cid);
|
|
14940
|
+
pendingModelSearchTimestamps.delete(cid);
|
|
14941
|
+
}
|
|
14942
|
+
}
|
|
14943
|
+
for (const chatId of pendingInterrupts.keys()) {
|
|
14944
|
+
if (!_interruptSeen.has(chatId)) {
|
|
14945
|
+
_interruptSeen.add(chatId);
|
|
14946
|
+
} else {
|
|
14947
|
+
pendingInterrupts.delete(chatId);
|
|
14948
|
+
_interruptSeen.delete(chatId);
|
|
14949
|
+
}
|
|
14950
|
+
}
|
|
14951
|
+
for (const chatId of _interruptSeen) {
|
|
14952
|
+
if (!pendingInterrupts.has(chatId)) _interruptSeen.delete(chatId);
|
|
14953
|
+
}
|
|
14954
|
+
for (const [cid, ts2] of pendingCliTimestamps) {
|
|
14955
|
+
if (now - ts2 > STALE_THRESHOLD_MS) {
|
|
14956
|
+
pendingCliAdditions.delete(cid);
|
|
14957
|
+
pendingCliTimestamps.delete(cid);
|
|
14958
|
+
}
|
|
14959
|
+
}
|
|
14960
|
+
for (const [cid, state] of pendingMcpImports) {
|
|
14961
|
+
if (now - state.startedAt > STALE_THRESHOLD_MS) pendingMcpImports.delete(cid);
|
|
14962
|
+
}
|
|
14963
|
+
for (const [cid, ts2] of _forceStoppedTimestamps) {
|
|
14964
|
+
if (now - ts2 > STALE_THRESHOLD_MS) {
|
|
14965
|
+
_forceStoppedChats.delete(cid);
|
|
14966
|
+
_forceStoppedTimestamps.delete(cid);
|
|
14967
|
+
}
|
|
14968
|
+
}
|
|
14969
|
+
}, SWEEP_INTERVAL_MS);
|
|
14970
|
+
sweepTimer.unref();
|
|
14971
|
+
}
|
|
14972
|
+
function stopStateSweep() {
|
|
14973
|
+
if (sweepTimer) {
|
|
14974
|
+
clearInterval(sweepTimer);
|
|
14975
|
+
sweepTimer = null;
|
|
14976
|
+
}
|
|
14977
|
+
}
|
|
14978
|
+
function setPendingCliAddition(chatId, messageId) {
|
|
14979
|
+
pendingCliAdditions.set(chatId, messageId);
|
|
14980
|
+
pendingCliTimestamps.set(chatId, Date.now());
|
|
14981
|
+
}
|
|
14982
|
+
function clearPendingCliAddition(chatId) {
|
|
14983
|
+
pendingCliAdditions.delete(chatId);
|
|
14984
|
+
pendingCliTimestamps.delete(chatId);
|
|
14985
|
+
}
|
|
14986
|
+
var pendingInterrupts, bypassBusyCheck, _forceStoppedChats, _forceStoppedTimestamps, activeSideQuests, dashboardClawWarnings, pendingSummaryUndo, pendingNewchatUndo, historyFilters, historyFilterTimestamps, councilResults, councilResultTimestamps, pendingModelSearch, pendingModelSearchTimestamps, pendingModelResults, SWEEP_INTERVAL_MS, STALE_THRESHOLD_MS, sweepTimer, _interruptSeen, pendingMcpImports, pendingCliAdditions, pendingCliTimestamps;
|
|
14987
|
+
var init_state = __esm({
|
|
14988
|
+
"src/router/state.ts"() {
|
|
14989
|
+
"use strict";
|
|
14990
|
+
init_agent();
|
|
14991
|
+
pendingInterrupts = /* @__PURE__ */ new Map();
|
|
14992
|
+
bypassBusyCheck = /* @__PURE__ */ new Set();
|
|
14993
|
+
_forceStoppedChats = /* @__PURE__ */ new Set();
|
|
14994
|
+
_forceStoppedTimestamps = /* @__PURE__ */ new Map();
|
|
14995
|
+
activeSideQuests = /* @__PURE__ */ new Map();
|
|
14996
|
+
dashboardClawWarnings = /* @__PURE__ */ new Map();
|
|
14997
|
+
pendingSummaryUndo = /* @__PURE__ */ new Map();
|
|
14998
|
+
pendingNewchatUndo = /* @__PURE__ */ new Map();
|
|
14999
|
+
historyFilters = /* @__PURE__ */ new Map();
|
|
15000
|
+
historyFilterTimestamps = /* @__PURE__ */ new Map();
|
|
15001
|
+
councilResults = /* @__PURE__ */ new Map();
|
|
15002
|
+
councilResultTimestamps = /* @__PURE__ */ new Map();
|
|
15003
|
+
pendingModelSearch = /* @__PURE__ */ new Map();
|
|
15004
|
+
pendingModelSearchTimestamps = /* @__PURE__ */ new Map();
|
|
15005
|
+
pendingModelResults = /* @__PURE__ */ new Map();
|
|
15006
|
+
SWEEP_INTERVAL_MS = 30 * 60 * 1e3;
|
|
15007
|
+
STALE_THRESHOLD_MS = 30 * 60 * 1e3;
|
|
15008
|
+
sweepTimer = null;
|
|
15009
|
+
_interruptSeen = /* @__PURE__ */ new Set();
|
|
15010
|
+
pendingMcpImports = /* @__PURE__ */ new Map();
|
|
15011
|
+
pendingCliAdditions = /* @__PURE__ */ new Map();
|
|
15012
|
+
pendingCliTimestamps = /* @__PURE__ */ new Map();
|
|
15013
|
+
}
|
|
15014
|
+
});
|
|
15015
|
+
|
|
14275
15016
|
// src/ui/pagination.ts
|
|
14276
15017
|
function buildPaginatedKeyboard(opts) {
|
|
14277
15018
|
const { items, page, callbackPrefix, renderItem, headerText, footerButtons } = opts;
|
|
@@ -14897,6 +15638,13 @@ async function promptAccount(chatId, channel) {
|
|
|
14897
15638
|
await promptTimeout(chatId, channel);
|
|
14898
15639
|
return;
|
|
14899
15640
|
}
|
|
15641
|
+
const adapter = getAdapter(pending.backend);
|
|
15642
|
+
if (adapter.type === "api") {
|
|
15643
|
+
pending.credentialSlotId = null;
|
|
15644
|
+
pending.step = "timeout";
|
|
15645
|
+
await promptTimeout(chatId, channel);
|
|
15646
|
+
return;
|
|
15647
|
+
}
|
|
14900
15648
|
const isGemini = pending.backend === BACKEND.GEMINI;
|
|
14901
15649
|
const slots = isGemini ? getGeminiSlots() : getBackendSlots(pending.backend);
|
|
14902
15650
|
const enabledSlots = slots.filter((s) => s.enabled);
|
|
@@ -15158,6 +15906,7 @@ __export(stt_exports, {
|
|
|
15158
15906
|
LOCAL_WHISPER_MODELS: () => LOCAL_WHISPER_MODELS,
|
|
15159
15907
|
MACOS_VOICES: () => MACOS_VOICES,
|
|
15160
15908
|
downloadWhisperModel: () => downloadWhisperModel,
|
|
15909
|
+
getSttEcho: () => getSttEcho,
|
|
15161
15910
|
getSttModel: () => getSttModel,
|
|
15162
15911
|
getSttProvider: () => getSttProvider,
|
|
15163
15912
|
getVoiceConfig: () => getVoiceConfig,
|
|
@@ -15165,10 +15914,12 @@ __export(stt_exports, {
|
|
|
15165
15914
|
isVoiceEnabled: () => isVoiceEnabled,
|
|
15166
15915
|
isWhisperCliAvailable: () => isWhisperCliAvailable,
|
|
15167
15916
|
isWhisperModelDownloaded: () => isWhisperModelDownloaded,
|
|
15917
|
+
setSttEcho: () => setSttEcho,
|
|
15168
15918
|
setSttModel: () => setSttModel,
|
|
15169
15919
|
setSttProvider: () => setSttProvider,
|
|
15170
15920
|
setVoiceProvider: () => setVoiceProvider,
|
|
15171
15921
|
synthesizeSpeech: () => synthesizeSpeech,
|
|
15922
|
+
toggleSttEcho: () => toggleSttEcho,
|
|
15172
15923
|
toggleVoice: () => toggleVoice,
|
|
15173
15924
|
transcribeAudio: () => transcribeAudio
|
|
15174
15925
|
});
|
|
@@ -15247,6 +15998,23 @@ function setSttModel(chatId, model2) {
|
|
|
15247
15998
|
ON CONFLICT(chat_id) DO UPDATE SET stt_model = ?
|
|
15248
15999
|
`).run(chatId, model2, model2);
|
|
15249
16000
|
}
|
|
16001
|
+
function getSttEcho(chatId) {
|
|
16002
|
+
const db3 = getDb();
|
|
16003
|
+
const row = db3.prepare("SELECT stt_echo FROM chat_voice WHERE chat_id = ?").get(chatId);
|
|
16004
|
+
return row?.stt_echo === 1;
|
|
16005
|
+
}
|
|
16006
|
+
function setSttEcho(chatId, enabled) {
|
|
16007
|
+
const db3 = getDb();
|
|
16008
|
+
db3.prepare(`
|
|
16009
|
+
INSERT INTO chat_voice (chat_id, enabled, stt_echo) VALUES (?, 0, ?)
|
|
16010
|
+
ON CONFLICT(chat_id) DO UPDATE SET stt_echo = ?
|
|
16011
|
+
`).run(chatId, enabled ? 1 : 0, enabled ? 1 : 0);
|
|
16012
|
+
}
|
|
16013
|
+
function toggleSttEcho(chatId) {
|
|
16014
|
+
const newState = !getSttEcho(chatId);
|
|
16015
|
+
setSttEcho(chatId, newState);
|
|
16016
|
+
return newState;
|
|
16017
|
+
}
|
|
15250
16018
|
function isFfmpegAvailable() {
|
|
15251
16019
|
if (ffmpegAvailable !== null) return ffmpegAvailable;
|
|
15252
16020
|
try {
|
|
@@ -16135,8 +16903,8 @@ function enableHeartbeat(chatId, opts) {
|
|
|
16135
16903
|
const { insertJob: insertJob2 } = (init_jobs(), __toCommonJS(jobs_exports));
|
|
16136
16904
|
const { startSingleJob: startSingleJob2 } = (init_cron(), __toCommonJS(cron_exports));
|
|
16137
16905
|
const intervalMs = opts?.intervalMs ?? DEFAULT_INTERVAL_MS;
|
|
16138
|
-
const backend2 = opts?.backend ??
|
|
16139
|
-
const model2 = opts?.model ??
|
|
16906
|
+
const backend2 = opts?.backend ?? resolveDefaultBackend2(chatId);
|
|
16907
|
+
const model2 = opts?.model ?? resolveDefaultModel2(backend2);
|
|
16140
16908
|
const job = insertJob2({
|
|
16141
16909
|
scheduleType: "every",
|
|
16142
16910
|
everyMs: intervalMs,
|
|
@@ -16156,7 +16924,7 @@ function enableHeartbeat(chatId, opts) {
|
|
|
16156
16924
|
log(`[heartbeat] Created job #${job.id} (every ${intervalMs / 6e4}min, backend=${backend2}, model=${model2})`);
|
|
16157
16925
|
return job.id;
|
|
16158
16926
|
}
|
|
16159
|
-
function
|
|
16927
|
+
function resolveDefaultBackend2(chatId) {
|
|
16160
16928
|
try {
|
|
16161
16929
|
const { getAdapterForChat: getAdapterForChat2 } = (init_backends(), __toCommonJS(backends_exports));
|
|
16162
16930
|
return getAdapterForChat2(chatId).id;
|
|
@@ -16164,7 +16932,7 @@ function resolveDefaultBackend(chatId) {
|
|
|
16164
16932
|
return "claude";
|
|
16165
16933
|
}
|
|
16166
16934
|
}
|
|
16167
|
-
function
|
|
16935
|
+
function resolveDefaultModel2(backend2) {
|
|
16168
16936
|
try {
|
|
16169
16937
|
const { getAdapter: getAdapter3 } = (init_backends(), __toCommonJS(backends_exports));
|
|
16170
16938
|
return getAdapter3(backend2).defaultModel;
|
|
@@ -16173,8 +16941,8 @@ function resolveDefaultModel(backend2) {
|
|
|
16173
16941
|
}
|
|
16174
16942
|
}
|
|
16175
16943
|
function migrateHeartbeatDefaults(job) {
|
|
16176
|
-
const backend2 =
|
|
16177
|
-
const model2 =
|
|
16944
|
+
const backend2 = resolveDefaultBackend2(job.chatId);
|
|
16945
|
+
const model2 = resolveDefaultModel2(backend2);
|
|
16178
16946
|
try {
|
|
16179
16947
|
const { getDb: getDb2 } = (init_store5(), __toCommonJS(store_exports5));
|
|
16180
16948
|
getDb2().prepare("UPDATE jobs SET backend = ?, model = ? WHERE id = ?").run(backend2, model2, job.id);
|
|
@@ -16418,10 +17186,10 @@ function formatNightlySummary(insights, totalPending) {
|
|
|
16418
17186
|
} else {
|
|
16419
17187
|
header2 = `Nightly Reflection \u2014 ${newCount} proposal${newCount === 1 ? "" : "s"} ready`;
|
|
16420
17188
|
}
|
|
16421
|
-
const
|
|
17189
|
+
const list2 = insights.map((ins, i) => `\u2022 [${ins.category}] ${ins.insight}`).join("\n");
|
|
16422
17190
|
return `${header2}
|
|
16423
17191
|
|
|
16424
|
-
${
|
|
17192
|
+
${list2}
|
|
16425
17193
|
|
|
16426
17194
|
Review with /evolve`;
|
|
16427
17195
|
}
|
|
@@ -17038,6 +17806,12 @@ async function sendVoiceConfigKeyboard(chatId, channel, messageId) {
|
|
|
17038
17806
|
buttons.push(row);
|
|
17039
17807
|
}
|
|
17040
17808
|
}
|
|
17809
|
+
const echoOn = getSttEcho(chatId);
|
|
17810
|
+
buttons.push([{
|
|
17811
|
+
label: `${echoOn ? "\u2713 " : ""}\u{1F399} Show Transcription`,
|
|
17812
|
+
data: "vcfg:echo",
|
|
17813
|
+
...echoOn ? { style: "success" } : {}
|
|
17814
|
+
}]);
|
|
17041
17815
|
buttons.push([
|
|
17042
17816
|
{
|
|
17043
17817
|
label: `${!ttsConfig.enabled ? "\u2713 " : ""}\u{1F507} Replies Off`,
|
|
@@ -17330,9 +18104,11 @@ async function sendMemoryPage(chatId, channel, page, messageId) {
|
|
|
17330
18104
|
const buttons = [];
|
|
17331
18105
|
for (const [i, m] of pageItems.entries()) {
|
|
17332
18106
|
const num = start + i + 1;
|
|
17333
|
-
const
|
|
18107
|
+
const cat = m.category ?? "uncategorized";
|
|
18108
|
+
const full = `[${cat}] ${m.trigger}: ${m.content}`;
|
|
18109
|
+
const preview = full.length > 36 ? `${full.slice(0, 36)}\u2026` : full;
|
|
17334
18110
|
buttons.push([{
|
|
17335
|
-
label: `${num}. ${preview}
|
|
18111
|
+
label: `${num}. ${preview}`,
|
|
17336
18112
|
data: `mem:view:${m.id}`
|
|
17337
18113
|
}]);
|
|
17338
18114
|
}
|
|
@@ -17351,6 +18127,12 @@ async function sendMemoryPage(chatId, channel, page, messageId) {
|
|
|
17351
18127
|
}
|
|
17352
18128
|
buttons.push(footerRow);
|
|
17353
18129
|
buttons.push([{ label: "\u2728 Optimize", data: "mem:opt", style: "success" }]);
|
|
18130
|
+
const sweepEnabled = getMetaValue(SWEEP_ENABLED_KEY) === "1";
|
|
18131
|
+
buttons.push([{
|
|
18132
|
+
label: sweepEnabled ? "\u2713 Weekly Sweep: On" : "Weekly Sweep: Off",
|
|
18133
|
+
data: "mem:sweep:toggle",
|
|
18134
|
+
style: sweepEnabled ? "success" : void 0
|
|
18135
|
+
}]);
|
|
17354
18136
|
await sendOrEditKeyboard(chatId, channel, messageId, header2, buttons);
|
|
17355
18137
|
}
|
|
17356
18138
|
async function sendMemoryDetail(chatId, memoryId, channel, messageId) {
|
|
@@ -18155,7 +18937,8 @@ async function sendBackendSwitchConfirmation(chatId, target, channel, messageId)
|
|
|
18155
18937
|
}
|
|
18156
18938
|
async function doBackendSwitch(chatId, backendId, channel, opts) {
|
|
18157
18939
|
if (!opts?.skipContext) {
|
|
18158
|
-
const
|
|
18940
|
+
const interrupted = consumeForceStoppedChat(chatId);
|
|
18941
|
+
const bridge = buildContextBridge(chatId, 15, interrupted);
|
|
18159
18942
|
if (bridge) setPendingContextBridge(chatId, bridge);
|
|
18160
18943
|
}
|
|
18161
18944
|
clearSession(chatId);
|
|
@@ -18235,10 +19018,12 @@ var init_ui = __esm({
|
|
|
18235
19018
|
init_format();
|
|
18236
19019
|
init_backends();
|
|
18237
19020
|
init_store5();
|
|
19021
|
+
init_sweep();
|
|
18238
19022
|
init_chat_settings();
|
|
18239
19023
|
init_api_models();
|
|
18240
19024
|
init_summarize();
|
|
18241
19025
|
init_inject();
|
|
19026
|
+
init_state();
|
|
18242
19027
|
init_session_log();
|
|
18243
19028
|
init_store3();
|
|
18244
19029
|
init_log();
|
|
@@ -18282,7 +19067,7 @@ function getMemOptSession(chatId) {
|
|
|
18282
19067
|
function buildOptimizationPrompt(memories) {
|
|
18283
19068
|
const lines = memories.map((m, i) => {
|
|
18284
19069
|
const accessed = m.access_count ?? 0;
|
|
18285
|
-
return `[${i + 1}] ID=${m.id} | trigger="${m.trigger}" | content="${m.content}" | type=${m.type} | salience=${m.salience.toFixed(2)} | accessed=${accessed} | created=${m.created_at}`;
|
|
19070
|
+
return `[${i + 1}] ID=${m.id} | trigger="${m.trigger}" | content="${m.content}" | type=${m.type} | category=${m.category} | salience=${m.salience.toFixed(2)} | accessed=${accessed} | half_life=${(m.half_life_days ?? 14).toFixed(1)} | created=${m.created_at}`;
|
|
18286
19071
|
});
|
|
18287
19072
|
return ANALYSIS_PROMPT.replace("{MEMORIES}", lines.join("\n"));
|
|
18288
19073
|
}
|
|
@@ -18291,7 +19076,7 @@ function parseOptimizationResponse(raw) {
|
|
|
18291
19076
|
const suggestions = [];
|
|
18292
19077
|
const blocks = raw.split(/^---$/m).filter((b) => b.trim());
|
|
18293
19078
|
for (const block of blocks) {
|
|
18294
|
-
const typeMatch = block.match(/TYPE:\s*(duplicate|merge|stale)/i);
|
|
19079
|
+
const typeMatch = block.match(/TYPE:\s*(duplicate|merge|stale|superseded|categorize|reclassify)/i);
|
|
18295
19080
|
const idsMatch = block.match(/IDS:\s*([\d,\s]+)/);
|
|
18296
19081
|
const reasonMatch = block.match(/REASON:\s*(.+?)(?=\n(?:ACTION|SUGGESTION|TYPE|IDS):|$)/s);
|
|
18297
19082
|
const actionMatch = block.match(/ACTION:\s*(.+)/s);
|
|
@@ -18316,6 +19101,21 @@ function parseOptimizationResponse(raw) {
|
|
|
18316
19101
|
suggestion.mergedContent = mergeMatch[2].trim();
|
|
18317
19102
|
}
|
|
18318
19103
|
}
|
|
19104
|
+
if (type === "superseded") {
|
|
19105
|
+
const supersededMatch = actionRaw.match(/supersede\s+(\d+)\s+by\s+(\d+)/i);
|
|
19106
|
+
if (supersededMatch) {
|
|
19107
|
+
suggestion.memoryIds = [
|
|
19108
|
+
parseInt(supersededMatch[1], 10),
|
|
19109
|
+
parseInt(supersededMatch[2], 10)
|
|
19110
|
+
].filter((n) => !isNaN(n));
|
|
19111
|
+
}
|
|
19112
|
+
}
|
|
19113
|
+
if (type === "categorize" || type === "reclassify") {
|
|
19114
|
+
const catMatch = actionRaw.match(/(?:categorize|reclassify)\s+\d+\s+as\s+(fact|preference|event|decision)/i);
|
|
19115
|
+
if (catMatch) {
|
|
19116
|
+
suggestion.proposedCategory = catMatch[1].toLowerCase();
|
|
19117
|
+
}
|
|
19118
|
+
}
|
|
18319
19119
|
suggestions.push(suggestion);
|
|
18320
19120
|
}
|
|
18321
19121
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -18365,14 +19165,18 @@ function applySuggestion(suggestion, session2) {
|
|
|
18365
19165
|
case "merge": {
|
|
18366
19166
|
if (!suggestion.mergedTrigger || !suggestion.mergedContent) return;
|
|
18367
19167
|
const sources = suggestion.memoryIds.map((id) => getMemoryById(id)).filter((m) => m !== void 0);
|
|
18368
|
-
const
|
|
19168
|
+
const topSource = sources.length > 0 ? sources.sort((a, b) => b.salience - a.salience)[0] : null;
|
|
19169
|
+
const sourceType = topSource?.type ?? "semantic";
|
|
19170
|
+
const sourceCategory = topSource?.category;
|
|
18369
19171
|
for (const id of suggestion.memoryIds) {
|
|
18370
19172
|
deleteMemoryById(id);
|
|
18371
19173
|
}
|
|
18372
19174
|
const newId = saveMemoryWithEmbedding(
|
|
18373
19175
|
suggestion.mergedTrigger,
|
|
18374
19176
|
suggestion.mergedContent,
|
|
18375
|
-
sourceType
|
|
19177
|
+
sourceType,
|
|
19178
|
+
sourceCategory,
|
|
19179
|
+
14
|
|
18376
19180
|
);
|
|
18377
19181
|
session2.createdIds.push(newId);
|
|
18378
19182
|
break;
|
|
@@ -18383,6 +19187,19 @@ function applySuggestion(suggestion, session2) {
|
|
|
18383
19187
|
}
|
|
18384
19188
|
break;
|
|
18385
19189
|
}
|
|
19190
|
+
case "superseded": {
|
|
19191
|
+
if (suggestion.memoryIds.length >= 2) {
|
|
19192
|
+
markSuperseded(suggestion.memoryIds[0], suggestion.memoryIds[1]);
|
|
19193
|
+
}
|
|
19194
|
+
break;
|
|
19195
|
+
}
|
|
19196
|
+
case "categorize":
|
|
19197
|
+
case "reclassify": {
|
|
19198
|
+
if (suggestion.proposedCategory && suggestion.memoryIds.length >= 1) {
|
|
19199
|
+
updateMemoryCategory(suggestion.memoryIds[0], suggestion.proposedCategory);
|
|
19200
|
+
}
|
|
19201
|
+
break;
|
|
19202
|
+
}
|
|
18386
19203
|
}
|
|
18387
19204
|
}
|
|
18388
19205
|
function resolveAnalysisAdapter(chatId, backendId, model2) {
|
|
@@ -18798,12 +19615,19 @@ Analyze these memories and find optimization opportunities:
|
|
|
18798
19615
|
|
|
18799
19616
|
1. DUPLICATES: Memories containing essentially the same information (different wording, same meaning). When found, recommend keeping the most complete version.
|
|
18800
19617
|
2. MERGEABLE: Related memories that would be stronger and more token-efficient as a single combined entry.
|
|
18801
|
-
3. STALE: Memories with very low
|
|
19618
|
+
3. STALE: Memories with very low half_life (below 3.0) AND zero access count \u2014 content that has never been recalled and has decayed past usefulness.
|
|
19619
|
+
4. SUPERSEDED: Memories that contradict each other \u2014 a newer memory makes an older one obsolete. When found, recommend marking the older one as superseded by the newer one.
|
|
19620
|
+
5. UNCATEGORIZED: Memories with category "uncategorized" that can be classified as fact, preference, event, or decision.
|
|
19621
|
+
6. RECLASSIFY: Memories whose category appears wrong given their content.
|
|
19622
|
+
|
|
19623
|
+
For superseded: ACTION should be "supersede ID1 by ID2" (ID1=older memory, ID2=newer memory)
|
|
19624
|
+
For uncategorized: ACTION should be "categorize ID1 as <category>"
|
|
19625
|
+
For reclassify: ACTION should be "reclassify ID1 as <category>"
|
|
18802
19626
|
|
|
18803
19627
|
For each suggestion, output EXACTLY this format:
|
|
18804
19628
|
---
|
|
18805
19629
|
SUGGESTION: <short descriptive title>
|
|
18806
|
-
TYPE: <duplicate|merge|stale>
|
|
19630
|
+
TYPE: <duplicate|merge|stale|superseded|categorize|reclassify>
|
|
18807
19631
|
IDS: <comma-separated memory IDs to act on>
|
|
18808
19632
|
REASON: <1-2 sentence explanation>
|
|
18809
19633
|
ACTION: <"delete ID1" or "delete ID1, ID2" or "merge into: <new trigger> | <new content>">
|
|
@@ -18823,12 +19647,18 @@ Rules:
|
|
|
18823
19647
|
TYPE_EMOJI = {
|
|
18824
19648
|
duplicate: "\u{1F501}",
|
|
18825
19649
|
merge: "\u{1F500}",
|
|
18826
|
-
stale: "\u{1F5D1}"
|
|
19650
|
+
stale: "\u{1F5D1}",
|
|
19651
|
+
superseded: "\u{1F504}",
|
|
19652
|
+
categorize: "\u{1F3F7}",
|
|
19653
|
+
reclassify: "\u{1F3F7}"
|
|
18827
19654
|
};
|
|
18828
19655
|
TYPE_LABEL = {
|
|
18829
19656
|
duplicate: "Duplicate",
|
|
18830
19657
|
merge: "Merge",
|
|
18831
|
-
stale: "Stale"
|
|
19658
|
+
stale: "Stale",
|
|
19659
|
+
superseded: "Superseded",
|
|
19660
|
+
categorize: "Categorize",
|
|
19661
|
+
reclassify: "Reclassify"
|
|
18832
19662
|
};
|
|
18833
19663
|
}
|
|
18834
19664
|
});
|
|
@@ -18844,9 +19674,12 @@ var init_memory = __esm({
|
|
|
18844
19674
|
try {
|
|
18845
19675
|
const body = JSON.parse(await readBody(req));
|
|
18846
19676
|
validateAgentIdentity(req, body);
|
|
18847
|
-
const {
|
|
18848
|
-
const
|
|
18849
|
-
|
|
19677
|
+
const { remember: remember2 } = await Promise.resolve().then(() => (init_engine(), engine_exports));
|
|
19678
|
+
const result = await remember2(body.trigger ?? body.tag, body.content, {
|
|
19679
|
+
category: body.category,
|
|
19680
|
+
type: body.type
|
|
19681
|
+
});
|
|
19682
|
+
jsonResponse(res, { success: true, id: result.id });
|
|
18850
19683
|
} catch (err) {
|
|
18851
19684
|
jsonResponse(res, { error: errorMessage(err) }, 400);
|
|
18852
19685
|
}
|
|
@@ -18855,8 +19688,8 @@ var init_memory = __esm({
|
|
|
18855
19688
|
try {
|
|
18856
19689
|
const body = JSON.parse(await readBody(req));
|
|
18857
19690
|
validateAgentIdentity(req, body);
|
|
18858
|
-
const {
|
|
18859
|
-
const results =
|
|
19691
|
+
const { recall: recall2 } = await Promise.resolve().then(() => (init_engine(), engine_exports));
|
|
19692
|
+
const results = recall2(body.query, { limit: body.limit ?? 5 });
|
|
18860
19693
|
jsonResponse(res, { success: true, results });
|
|
18861
19694
|
} catch (err) {
|
|
18862
19695
|
jsonResponse(res, { error: errorMessage(err) }, 400);
|
|
@@ -18865,8 +19698,8 @@ var init_memory = __esm({
|
|
|
18865
19698
|
handleMemoryList = async (_req, res, url) => {
|
|
18866
19699
|
try {
|
|
18867
19700
|
const limit = parseInt(url.searchParams.get("limit") ?? "10", 10);
|
|
18868
|
-
const {
|
|
18869
|
-
const results =
|
|
19701
|
+
const { list: list2 } = await Promise.resolve().then(() => (init_engine(), engine_exports));
|
|
19702
|
+
const results = list2({ limit });
|
|
18870
19703
|
jsonResponse(res, { success: true, results });
|
|
18871
19704
|
} catch (err) {
|
|
18872
19705
|
jsonResponse(res, { error: errorMessage(err) }, 400);
|
|
@@ -18876,14 +19709,13 @@ var init_memory = __esm({
|
|
|
18876
19709
|
try {
|
|
18877
19710
|
const body = JSON.parse(await readBody(req));
|
|
18878
19711
|
validateAgentIdentity(req, body);
|
|
19712
|
+
const { forget: forget2 } = await Promise.resolve().then(() => (init_engine(), engine_exports));
|
|
18879
19713
|
if (body.memoryId) {
|
|
18880
|
-
const
|
|
18881
|
-
|
|
18882
|
-
return jsonResponse(res, { success: deleted, mode: "id" });
|
|
19714
|
+
const count = forget2(body.memoryId);
|
|
19715
|
+
return jsonResponse(res, { success: count > 0, mode: "id" });
|
|
18883
19716
|
}
|
|
18884
19717
|
if (body.keyword) {
|
|
18885
|
-
const
|
|
18886
|
-
const count = forgetMemory4(body.keyword);
|
|
19718
|
+
const count = forget2(body.keyword);
|
|
18887
19719
|
return jsonResponse(res, { success: true, count, mode: "keyword" });
|
|
18888
19720
|
}
|
|
18889
19721
|
jsonResponse(res, { error: "Either 'keyword' or 'memoryId' is required" }, 400);
|
|
@@ -21351,7 +22183,7 @@ var init_api_mcp = __esm({
|
|
|
21351
22183
|
});
|
|
21352
22184
|
|
|
21353
22185
|
// src/backends/api-common.ts
|
|
21354
|
-
import { streamText, stepCountIs, NoOutputGeneratedError } from "ai";
|
|
22186
|
+
import { streamText, stepCountIs, NoOutputGeneratedError, APICallError } from "ai";
|
|
21355
22187
|
function toModelMessage(msg) {
|
|
21356
22188
|
switch (msg.role) {
|
|
21357
22189
|
case "system":
|
|
@@ -21425,7 +22257,7 @@ var init_api_common = __esm({
|
|
|
21425
22257
|
} catch (err) {
|
|
21426
22258
|
log(`[api-common] MCP tools unavailable: ${err instanceof Error ? err.message : String(err)}`);
|
|
21427
22259
|
}
|
|
21428
|
-
const tools2 = buildApiTools(chatId, permMode, mcpTools, getApiWebSearchEnabled(chatId));
|
|
22260
|
+
const tools2 = buildApiTools(chatId, permMode, mcpTools, getApiWebSearchEnabled(chatId), timeoutMs > 0 ? timeoutMs : void 0);
|
|
21429
22261
|
const hasTools = Object.keys(tools2).length > 0;
|
|
21430
22262
|
let abortSignal = signal;
|
|
21431
22263
|
let timeoutHandle;
|
|
@@ -21485,6 +22317,9 @@ var init_api_common = __esm({
|
|
|
21485
22317
|
const usage2 = await result.usage;
|
|
21486
22318
|
const providerMeta = await result.providerMetadata;
|
|
21487
22319
|
const cost = this.extractCost(providerMeta);
|
|
22320
|
+
if (fullText === "" && !usage2.inputTokens) {
|
|
22321
|
+
throw new Error(`${this.backendId}: empty response with no tokens \u2014 possible rate limit or quota exhaustion on free tier`);
|
|
22322
|
+
}
|
|
21488
22323
|
return {
|
|
21489
22324
|
text: fullText,
|
|
21490
22325
|
cost,
|
|
@@ -21494,6 +22329,9 @@ var init_api_common = __esm({
|
|
|
21494
22329
|
} : void 0
|
|
21495
22330
|
};
|
|
21496
22331
|
} catch (err) {
|
|
22332
|
+
if (err instanceof APICallError && err.statusCode === 429) {
|
|
22333
|
+
throw new Error(`${this.backendId}: rate limit (429) \u2014 free tier quota may be exhausted. Retry later or switch to a paid model.`);
|
|
22334
|
+
}
|
|
21497
22335
|
if (abortSignal?.aborted || signal?.aborted || err instanceof Error && (err.name === "AbortError" || err.message.toLowerCase().includes("abort") || err.message.toLowerCase().includes("cancel"))) {
|
|
21498
22336
|
return { text: "", cost: null };
|
|
21499
22337
|
}
|
|
@@ -22158,14 +22996,14 @@ var init_delivery = __esm({
|
|
|
22158
22996
|
});
|
|
22159
22997
|
|
|
22160
22998
|
// src/scheduler/retry.ts
|
|
22161
|
-
import { APICallError } from "ai";
|
|
22999
|
+
import { APICallError as APICallError2 } from "ai";
|
|
22162
23000
|
function isExhaustedMessage(text) {
|
|
22163
23001
|
return EXHAUSTED_PATTERNS.some((p) => p.test(text));
|
|
22164
23002
|
}
|
|
22165
23003
|
function classifyError(err) {
|
|
22166
23004
|
const msg = err instanceof Error ? err.message : String(err);
|
|
22167
23005
|
if (/spawn timeout/i.test(msg)) return "permanent";
|
|
22168
|
-
if (err instanceof
|
|
23006
|
+
if (err instanceof APICallError2 && err.isRetryable) {
|
|
22169
23007
|
return "transient";
|
|
22170
23008
|
}
|
|
22171
23009
|
for (const pattern of EXHAUSTED_PATTERNS) {
|
|
@@ -22577,7 +23415,7 @@ async function classifyIntentAsync(text, chatId) {
|
|
|
22577
23415
|
return "agentic";
|
|
22578
23416
|
}
|
|
22579
23417
|
var intentCounts, CHAT_EXACT, MUTATION_PATTERNS, CHAT_QUESTION_PATTERNS, STRUCTURAL_PATTERNS, LLM_CLASSIFY_PROMPT, LLM_CLASSIFY_TIMEOUT_MS;
|
|
22580
|
-
var
|
|
23418
|
+
var init_classify2 = __esm({
|
|
22581
23419
|
"src/intent/classify.ts"() {
|
|
22582
23420
|
"use strict";
|
|
22583
23421
|
init_store5();
|
|
@@ -23324,7 +24162,7 @@ function classifyAgentIntent(message) {
|
|
|
23324
24162
|
return NOT_DETECTED;
|
|
23325
24163
|
}
|
|
23326
24164
|
var AGENT_SIGNAL_PATTERNS, CLAW_SIGNAL_PATTERNS, NEGATIVE_PATTERNS;
|
|
23327
|
-
var
|
|
24165
|
+
var init_classify3 = __esm({
|
|
23328
24166
|
"src/agents/classify.ts"() {
|
|
23329
24167
|
"use strict";
|
|
23330
24168
|
AGENT_SIGNAL_PATTERNS = [
|
|
@@ -24546,6 +25384,9 @@ async function handleVoice(msg, channel) {
|
|
|
24546
25384
|
await channel.sendText(chatId, "Couldn't transcribe the voice message. Make sure a transcription provider is configured via /voice.", { parseMode: "plain" });
|
|
24547
25385
|
return;
|
|
24548
25386
|
}
|
|
25387
|
+
if (getSttEcho(chatId)) {
|
|
25388
|
+
await channel.sendText(chatId, `\u{1F399} ${transcript}`, { parseMode: "plain" });
|
|
25389
|
+
}
|
|
24549
25390
|
const vBackendId = getBackend(chatId) ?? "claude";
|
|
24550
25391
|
const vLimitMsg = checkBackendLimits(vBackendId);
|
|
24551
25392
|
if (vLimitMsg) {
|
|
@@ -24829,168 +25670,6 @@ var init_media = __esm({
|
|
|
24829
25670
|
}
|
|
24830
25671
|
});
|
|
24831
25672
|
|
|
24832
|
-
// src/router/state.ts
|
|
24833
|
-
var state_exports = {};
|
|
24834
|
-
__export(state_exports, {
|
|
24835
|
-
activeSideQuests: () => activeSideQuests,
|
|
24836
|
-
bypassBusyCheck: () => bypassBusyCheck,
|
|
24837
|
-
clearHistoryFilter: () => clearHistoryFilter,
|
|
24838
|
-
clearPendingCliAddition: () => clearPendingCliAddition,
|
|
24839
|
-
clearPendingModelResults: () => clearPendingModelResults,
|
|
24840
|
-
clearPendingModelSearch: () => clearPendingModelSearch,
|
|
24841
|
-
councilResults: () => councilResults,
|
|
24842
|
-
dashboardClawWarnings: () => dashboardClawWarnings,
|
|
24843
|
-
getActiveSideQuestCount: () => getActiveSideQuestCount,
|
|
24844
|
-
historyFilters: () => historyFilters,
|
|
24845
|
-
parseSideQuestPrefix: () => parseSideQuestPrefix,
|
|
24846
|
-
pendingCliAdditions: () => pendingCliAdditions,
|
|
24847
|
-
pendingInterrupts: () => pendingInterrupts,
|
|
24848
|
-
pendingMcpImports: () => pendingMcpImports,
|
|
24849
|
-
pendingModelResults: () => pendingModelResults,
|
|
24850
|
-
pendingModelSearch: () => pendingModelSearch,
|
|
24851
|
-
pendingNewchatUndo: () => pendingNewchatUndo,
|
|
24852
|
-
pendingSummaryUndo: () => pendingSummaryUndo,
|
|
24853
|
-
setCouncilResult: () => setCouncilResult,
|
|
24854
|
-
setHistoryFilter: () => setHistoryFilter,
|
|
24855
|
-
setPendingCliAddition: () => setPendingCliAddition,
|
|
24856
|
-
setPendingModelResults: () => setPendingModelResults,
|
|
24857
|
-
setPendingModelSearch: () => setPendingModelSearch,
|
|
24858
|
-
startStateSweep: () => startStateSweep,
|
|
24859
|
-
stopAllSideQuests: () => stopAllSideQuests,
|
|
24860
|
-
stopStateSweep: () => stopStateSweep
|
|
24861
|
-
});
|
|
24862
|
-
function setHistoryFilter(chatId, filter) {
|
|
24863
|
-
historyFilters.set(chatId, filter);
|
|
24864
|
-
historyFilterTimestamps.set(chatId, Date.now());
|
|
24865
|
-
}
|
|
24866
|
-
function clearHistoryFilter(chatId) {
|
|
24867
|
-
historyFilters.delete(chatId);
|
|
24868
|
-
historyFilterTimestamps.delete(chatId);
|
|
24869
|
-
}
|
|
24870
|
-
function setCouncilResult(chatId, result) {
|
|
24871
|
-
councilResults.set(chatId, result);
|
|
24872
|
-
councilResultTimestamps.set(chatId, Date.now());
|
|
24873
|
-
}
|
|
24874
|
-
function setPendingModelSearch(chatId, state) {
|
|
24875
|
-
pendingModelSearch.set(chatId, state);
|
|
24876
|
-
pendingModelSearchTimestamps.set(chatId, Date.now());
|
|
24877
|
-
}
|
|
24878
|
-
function clearPendingModelSearch(chatId) {
|
|
24879
|
-
pendingModelSearch.delete(chatId);
|
|
24880
|
-
pendingModelSearchTimestamps.delete(chatId);
|
|
24881
|
-
}
|
|
24882
|
-
function setPendingModelResults(chatId, results) {
|
|
24883
|
-
pendingModelResults.set(chatId, results);
|
|
24884
|
-
}
|
|
24885
|
-
function clearPendingModelResults(chatId) {
|
|
24886
|
-
pendingModelResults.delete(chatId);
|
|
24887
|
-
}
|
|
24888
|
-
function parseSideQuestPrefix(text) {
|
|
24889
|
-
const match = text.match(/^(?:sq|btw):\s*/i);
|
|
24890
|
-
if (match) return { isSideQuest: true, cleanText: text.slice(match[0].length) };
|
|
24891
|
-
return { isSideQuest: false, cleanText: text };
|
|
24892
|
-
}
|
|
24893
|
-
function getActiveSideQuestCount(chatId) {
|
|
24894
|
-
return activeSideQuests.get(chatId)?.size ?? 0;
|
|
24895
|
-
}
|
|
24896
|
-
function stopAllSideQuests(chatId) {
|
|
24897
|
-
const active = activeSideQuests.get(chatId);
|
|
24898
|
-
if (active) {
|
|
24899
|
-
for (const sqId of active) {
|
|
24900
|
-
stopAgent(sqId);
|
|
24901
|
-
}
|
|
24902
|
-
}
|
|
24903
|
-
}
|
|
24904
|
-
function startStateSweep() {
|
|
24905
|
-
if (sweepTimer) return;
|
|
24906
|
-
sweepTimer = setInterval(() => {
|
|
24907
|
-
const now = Date.now();
|
|
24908
|
-
for (const [chatId, ts2] of dashboardClawWarnings) {
|
|
24909
|
-
if (now - ts2 > STALE_THRESHOLD_MS) dashboardClawWarnings.delete(chatId);
|
|
24910
|
-
}
|
|
24911
|
-
for (const [cid, ts2] of historyFilterTimestamps) {
|
|
24912
|
-
if (now - ts2 > STALE_THRESHOLD_MS) {
|
|
24913
|
-
historyFilters.delete(cid);
|
|
24914
|
-
historyFilterTimestamps.delete(cid);
|
|
24915
|
-
}
|
|
24916
|
-
}
|
|
24917
|
-
for (const [cid, ts2] of councilResultTimestamps) {
|
|
24918
|
-
if (now - ts2 > STALE_THRESHOLD_MS) {
|
|
24919
|
-
councilResults.delete(cid);
|
|
24920
|
-
councilResultTimestamps.delete(cid);
|
|
24921
|
-
}
|
|
24922
|
-
}
|
|
24923
|
-
for (const [cid, ts2] of pendingModelSearchTimestamps) {
|
|
24924
|
-
if (now - ts2 > STALE_THRESHOLD_MS) {
|
|
24925
|
-
pendingModelSearch.delete(cid);
|
|
24926
|
-
pendingModelSearchTimestamps.delete(cid);
|
|
24927
|
-
}
|
|
24928
|
-
}
|
|
24929
|
-
for (const chatId of pendingInterrupts.keys()) {
|
|
24930
|
-
if (!_interruptSeen.has(chatId)) {
|
|
24931
|
-
_interruptSeen.add(chatId);
|
|
24932
|
-
} else {
|
|
24933
|
-
pendingInterrupts.delete(chatId);
|
|
24934
|
-
_interruptSeen.delete(chatId);
|
|
24935
|
-
}
|
|
24936
|
-
}
|
|
24937
|
-
for (const chatId of _interruptSeen) {
|
|
24938
|
-
if (!pendingInterrupts.has(chatId)) _interruptSeen.delete(chatId);
|
|
24939
|
-
}
|
|
24940
|
-
for (const [cid, ts2] of pendingCliTimestamps) {
|
|
24941
|
-
if (now - ts2 > STALE_THRESHOLD_MS) {
|
|
24942
|
-
pendingCliAdditions.delete(cid);
|
|
24943
|
-
pendingCliTimestamps.delete(cid);
|
|
24944
|
-
}
|
|
24945
|
-
}
|
|
24946
|
-
for (const [cid, state] of pendingMcpImports) {
|
|
24947
|
-
if (now - state.startedAt > STALE_THRESHOLD_MS) pendingMcpImports.delete(cid);
|
|
24948
|
-
}
|
|
24949
|
-
}, SWEEP_INTERVAL_MS);
|
|
24950
|
-
sweepTimer.unref();
|
|
24951
|
-
}
|
|
24952
|
-
function stopStateSweep() {
|
|
24953
|
-
if (sweepTimer) {
|
|
24954
|
-
clearInterval(sweepTimer);
|
|
24955
|
-
sweepTimer = null;
|
|
24956
|
-
}
|
|
24957
|
-
}
|
|
24958
|
-
function setPendingCliAddition(chatId, messageId) {
|
|
24959
|
-
pendingCliAdditions.set(chatId, messageId);
|
|
24960
|
-
pendingCliTimestamps.set(chatId, Date.now());
|
|
24961
|
-
}
|
|
24962
|
-
function clearPendingCliAddition(chatId) {
|
|
24963
|
-
pendingCliAdditions.delete(chatId);
|
|
24964
|
-
pendingCliTimestamps.delete(chatId);
|
|
24965
|
-
}
|
|
24966
|
-
var pendingInterrupts, bypassBusyCheck, activeSideQuests, dashboardClawWarnings, pendingSummaryUndo, pendingNewchatUndo, historyFilters, historyFilterTimestamps, councilResults, councilResultTimestamps, pendingModelSearch, pendingModelSearchTimestamps, pendingModelResults, SWEEP_INTERVAL_MS, STALE_THRESHOLD_MS, sweepTimer, _interruptSeen, pendingMcpImports, pendingCliAdditions, pendingCliTimestamps;
|
|
24967
|
-
var init_state = __esm({
|
|
24968
|
-
"src/router/state.ts"() {
|
|
24969
|
-
"use strict";
|
|
24970
|
-
init_agent();
|
|
24971
|
-
pendingInterrupts = /* @__PURE__ */ new Map();
|
|
24972
|
-
bypassBusyCheck = /* @__PURE__ */ new Set();
|
|
24973
|
-
activeSideQuests = /* @__PURE__ */ new Map();
|
|
24974
|
-
dashboardClawWarnings = /* @__PURE__ */ new Map();
|
|
24975
|
-
pendingSummaryUndo = /* @__PURE__ */ new Map();
|
|
24976
|
-
pendingNewchatUndo = /* @__PURE__ */ new Map();
|
|
24977
|
-
historyFilters = /* @__PURE__ */ new Map();
|
|
24978
|
-
historyFilterTimestamps = /* @__PURE__ */ new Map();
|
|
24979
|
-
councilResults = /* @__PURE__ */ new Map();
|
|
24980
|
-
councilResultTimestamps = /* @__PURE__ */ new Map();
|
|
24981
|
-
pendingModelSearch = /* @__PURE__ */ new Map();
|
|
24982
|
-
pendingModelSearchTimestamps = /* @__PURE__ */ new Map();
|
|
24983
|
-
pendingModelResults = /* @__PURE__ */ new Map();
|
|
24984
|
-
SWEEP_INTERVAL_MS = 30 * 60 * 1e3;
|
|
24985
|
-
STALE_THRESHOLD_MS = 30 * 60 * 1e3;
|
|
24986
|
-
sweepTimer = null;
|
|
24987
|
-
_interruptSeen = /* @__PURE__ */ new Set();
|
|
24988
|
-
pendingMcpImports = /* @__PURE__ */ new Map();
|
|
24989
|
-
pendingCliAdditions = /* @__PURE__ */ new Map();
|
|
24990
|
-
pendingCliTimestamps = /* @__PURE__ */ new Map();
|
|
24991
|
-
}
|
|
24992
|
-
});
|
|
24993
|
-
|
|
24994
25673
|
// src/router/sidequest.ts
|
|
24995
25674
|
import { randomUUID as randomUUID3 } from "crypto";
|
|
24996
25675
|
async function handleSideQuest(parentChatId, msg, channel) {
|
|
@@ -27954,6 +28633,7 @@ async function handleStopCommand(chatId, commandArgs, msg, channel) {
|
|
|
27954
28633
|
const stopped = stopAgent(chatId);
|
|
27955
28634
|
stopAllSideQuests(chatId);
|
|
27956
28635
|
cancelAllAgents(chatId);
|
|
28636
|
+
if (stopped) addForceStoppedChat(chatId);
|
|
27957
28637
|
await channel.sendText(
|
|
27958
28638
|
chatId,
|
|
27959
28639
|
stopped ? "Stopping current task..." : "Nothing is running.",
|
|
@@ -27984,7 +28664,7 @@ async function handleRememberCommand(chatId, commandArgs, msg, channel) {
|
|
|
27984
28664
|
}
|
|
27985
28665
|
const content = commandArgs.replace(/^that\s+/i, "");
|
|
27986
28666
|
const trigger = content.split(/\s+/).slice(0, 3).join(" ");
|
|
27987
|
-
|
|
28667
|
+
await remember(trigger, content);
|
|
27988
28668
|
await channel.sendText(chatId, "Got it, I'll remember that.", { parseMode: "plain" });
|
|
27989
28669
|
if (typeof channel.sendKeyboard === "function") {
|
|
27990
28670
|
await channel.sendKeyboard(chatId, "", [
|
|
@@ -28069,11 +28749,15 @@ async function handleStopallCommand(chatId, commandArgs, msg, channel) {
|
|
|
28069
28749
|
}
|
|
28070
28750
|
async function handleEditjobCommand(chatId, commandArgs, msg, channel) {
|
|
28071
28751
|
if (!commandArgs) {
|
|
28072
|
-
await
|
|
28752
|
+
await sendJobsBoard(chatId, channel, 1);
|
|
28073
28753
|
return;
|
|
28074
28754
|
}
|
|
28075
28755
|
const editId = parseInt(commandArgs, 10);
|
|
28076
|
-
|
|
28756
|
+
if (isNaN(editId)) {
|
|
28757
|
+
await sendJobsBoard(chatId, channel, 1);
|
|
28758
|
+
return;
|
|
28759
|
+
}
|
|
28760
|
+
await sendJobDetail(chatId, editId, channel);
|
|
28077
28761
|
}
|
|
28078
28762
|
async function handleJobsCommand(chatId, commandArgs, msg, channel) {
|
|
28079
28763
|
await sendJobsBoard(chatId, channel, 1);
|
|
@@ -28629,7 +29313,7 @@ async function handleMemoryCommand(chatId, commandArgs, msg, channel) {
|
|
|
28629
29313
|
return;
|
|
28630
29314
|
}
|
|
28631
29315
|
deleteMemoryById(editId);
|
|
28632
|
-
|
|
29316
|
+
await remember(mem.trigger, newContent, { category: mem.category });
|
|
28633
29317
|
await channel.sendText(chatId, `Memory #${editId} updated.`, { parseMode: "plain" });
|
|
28634
29318
|
return;
|
|
28635
29319
|
}
|
|
@@ -28814,9 +29498,9 @@ Recent directories:` : "Recent directories:";
|
|
|
28814
29498
|
const buttons = recents.map((r) => [{ label: r.alias, data: `cwdpick:${r.alias}` }]);
|
|
28815
29499
|
await channel.sendKeyboard(chatId, text, buttons);
|
|
28816
29500
|
} else {
|
|
28817
|
-
const
|
|
29501
|
+
const list2 = recents.map((r) => ` ${r.alias} \u2192 ${r.path}`).join("\n");
|
|
28818
29502
|
await channel.sendText(chatId, `${text}
|
|
28819
|
-
${
|
|
29503
|
+
${list2}`, { parseMode: "plain" });
|
|
28820
29504
|
}
|
|
28821
29505
|
return;
|
|
28822
29506
|
}
|
|
@@ -28971,12 +29655,12 @@ async function handleGeminiAccountsCommand(chatId, commandArgs, msg, channel) {
|
|
|
28971
29655
|
await channel.sendKeyboard(chatId, "Gemini Accounts & Rotation:", rows);
|
|
28972
29656
|
} else {
|
|
28973
29657
|
const currentMode = getGeminiRotationMode();
|
|
28974
|
-
const
|
|
29658
|
+
const list2 = slots.filter((s) => s.enabled).map((s) => {
|
|
28975
29659
|
const icon = s.slotType === "oauth" ? "\u{1F468}\u{1F3FD}\u200D\u{1F4BB}" : "\u{1F511}";
|
|
28976
29660
|
return `${icon} ${s.label || `slot-${s.id}`} (#${s.id})`;
|
|
28977
29661
|
}).join("\n");
|
|
28978
29662
|
await channel.sendText(chatId, `Slots:
|
|
28979
|
-
${
|
|
29663
|
+
${list2}
|
|
28980
29664
|
|
|
28981
29665
|
Rotation mode: ${currentMode}
|
|
28982
29666
|
Use: /gemini_accounts <name> to pin`, { parseMode: "plain" });
|
|
@@ -29003,12 +29687,12 @@ Add with: <code>cc-claw ${slotBackend} add-key</code>`, { parseMode: "html" });
|
|
|
29003
29687
|
await channel.sendKeyboard(chatId, `${slotDisplayName} Accounts & Rotation:`, rows);
|
|
29004
29688
|
} else {
|
|
29005
29689
|
const currentMode = getBackendRotationMode(slotBackend);
|
|
29006
|
-
const
|
|
29690
|
+
const list2 = slots.filter((s) => s.enabled).map((s) => {
|
|
29007
29691
|
const icon = s.slotType === "oauth" ? "\u{1F468}\u{1F3FD}\u200D\u{1F4BB}" : "\u{1F511}";
|
|
29008
29692
|
return `${icon} ${s.label || `slot-${s.id}`} (#${s.id})`;
|
|
29009
29693
|
}).join("\n");
|
|
29010
29694
|
await channel.sendText(chatId, `${slotDisplayName} Slots:
|
|
29011
|
-
${
|
|
29695
|
+
${list2}
|
|
29012
29696
|
|
|
29013
29697
|
Rotation mode: ${currentMode}
|
|
29014
29698
|
Use: /${command} <name> to pin`, { parseMode: "plain" });
|
|
@@ -29339,10 +30023,10 @@ async function handleTasksCommand(chatId, commandArgs, msg, channel) {
|
|
|
29339
30023
|
{ label: "Abandoned", emoji: "\u{1F6AB}", status: "abandoned" }
|
|
29340
30024
|
];
|
|
29341
30025
|
for (const { label: label2, emoji, status } of sections) {
|
|
29342
|
-
const
|
|
29343
|
-
if (
|
|
30026
|
+
const list2 = byStatus[status];
|
|
30027
|
+
if (list2.length === 0) continue;
|
|
29344
30028
|
lines.push(`${emoji} ${label2}:`);
|
|
29345
|
-
for (const t of
|
|
30029
|
+
for (const t of list2) {
|
|
29346
30030
|
const assignee = t.assignee ? ` (\u2192 ${t.assignee.slice(0, 8)})` : "";
|
|
29347
30031
|
lines.push(` #${t.id}: ${t.subject}${assignee}`);
|
|
29348
30032
|
}
|
|
@@ -29525,11 +30209,12 @@ var init_command_handlers = __esm({
|
|
|
29525
30209
|
init_image_gen();
|
|
29526
30210
|
init_stt();
|
|
29527
30211
|
init_agent();
|
|
29528
|
-
|
|
30212
|
+
init_classify2();
|
|
29529
30213
|
init_install();
|
|
29530
30214
|
init_profile();
|
|
29531
30215
|
init_heartbeat2();
|
|
29532
30216
|
init_discover();
|
|
30217
|
+
init_engine();
|
|
29533
30218
|
init_store5();
|
|
29534
30219
|
init_summarize();
|
|
29535
30220
|
init_session_log();
|
|
@@ -30662,6 +31347,9 @@ ${progressMsg}`,
|
|
|
30662
31347
|
}
|
|
30663
31348
|
setSttProvider(chatId, provider);
|
|
30664
31349
|
await sendVoiceConfigKeyboard(chatId, channel, messageId);
|
|
31350
|
+
} else if (data === "vcfg:echo") {
|
|
31351
|
+
toggleSttEcho(chatId);
|
|
31352
|
+
await sendVoiceConfigKeyboard(chatId, channel, messageId);
|
|
30665
31353
|
} else if (data.startsWith("vcfg:")) {
|
|
30666
31354
|
const parts = data.slice(5).split(":");
|
|
30667
31355
|
const action = parts[0];
|
|
@@ -30692,14 +31380,15 @@ ${progressMsg}`,
|
|
|
30692
31380
|
await handleWizardCallback(chatId, data, channel);
|
|
30693
31381
|
} else if (data.startsWith("job:")) {
|
|
30694
31382
|
async function showJobAccountPicker(cid, jobId, backend2, model2, thinking2, ch) {
|
|
30695
|
-
const
|
|
30696
|
-
const
|
|
31383
|
+
const { getAdapter: getAdapter3 } = await Promise.resolve().then(() => (init_backends(), backends_exports));
|
|
31384
|
+
const adapter = getAdapter3(backend2);
|
|
31385
|
+
const isApiBackend = adapter.type === "api";
|
|
31386
|
+
const isGemini = backend2 === BACKEND.GEMINI;
|
|
31387
|
+
const slots = isApiBackend ? [] : isGemini ? getGeminiSlots() : getBackendSlots(backend2);
|
|
30697
31388
|
const enabledSlots = slots.filter((s) => s.enabled);
|
|
30698
31389
|
if (enabledSlots.length === 0 || typeof ch.sendKeyboard !== "function") {
|
|
30699
31390
|
const { updateJob: updateJobFields } = await Promise.resolve().then(() => (init_store5(), store_exports5));
|
|
30700
31391
|
updateJobFields(jobId, { backend: backend2, model: model2, thinking: thinking2, credentialSlotId: null });
|
|
30701
|
-
const { getAdapter: getAdapter3 } = await Promise.resolve().then(() => (init_backends(), backends_exports));
|
|
30702
|
-
const adapter = getAdapter3(backend2);
|
|
30703
31392
|
await ch.sendText(cid, `Job #${jobId} updated: ${adapter.displayName} / ${model2} / ${thinking2}`, { parseMode: "plain" });
|
|
30704
31393
|
await sendJobDetail(cid, jobId, ch);
|
|
30705
31394
|
return;
|
|
@@ -31573,6 +32262,22 @@ Salience: ${memory2.salience.toFixed(2)} | Created: ${memory2.created_at.slice(0
|
|
|
31573
32262
|
} else if (rest.startsWith("opt")) {
|
|
31574
32263
|
const { handleMemOptCallback: handleMemOptCallback2 } = await Promise.resolve().then(() => (init_optimize(), optimize_exports));
|
|
31575
32264
|
await handleMemOptCallback2(chatId, rest, channel, messageId);
|
|
32265
|
+
} else if (rest === "sweep:dismiss") {
|
|
32266
|
+
if (messageId && channel.editText) {
|
|
32267
|
+
await channel.editText(chatId, messageId, "\u2705 Memory sweep dismissed.", "plain").catch(() => {
|
|
32268
|
+
});
|
|
32269
|
+
}
|
|
32270
|
+
} else if (rest === "sweep:toggle") {
|
|
32271
|
+
const { getMetaValue: getMetaValue2 } = await Promise.resolve().then(() => (init_store5(), store_exports5));
|
|
32272
|
+
const { SWEEP_ENABLED_KEY: SWEEP_ENABLED_KEY2, enableSweep: enableSweep2, disableSweep: disableSweep2 } = await Promise.resolve().then(() => (init_sweep(), sweep_exports));
|
|
32273
|
+
const currentlyEnabled = getMetaValue2(SWEEP_ENABLED_KEY2) === "1";
|
|
32274
|
+
if (currentlyEnabled) {
|
|
32275
|
+
disableSweep2();
|
|
32276
|
+
} else {
|
|
32277
|
+
enableSweep2(chatId);
|
|
32278
|
+
}
|
|
32279
|
+
const { sendMemoryPage: sendMemoryPage3 } = await Promise.resolve().then(() => (init_ui(), ui_exports));
|
|
32280
|
+
await sendMemoryPage3(chatId, channel, 1, messageId);
|
|
31576
32281
|
} else if (rest === "noop") {
|
|
31577
32282
|
}
|
|
31578
32283
|
return;
|
|
@@ -33169,7 +33874,8 @@ Try a different keyword.`,
|
|
|
33169
33874
|
if (rememberMatch) {
|
|
33170
33875
|
const content = rememberMatch[1];
|
|
33171
33876
|
const trigger = content.split(/\s+/).slice(0, 3).join(" ");
|
|
33172
|
-
|
|
33877
|
+
remember(trigger, content).catch(() => {
|
|
33878
|
+
});
|
|
33173
33879
|
await channel.sendText(chatId, "Got it, I'll remember that.", { parseMode: "plain" });
|
|
33174
33880
|
return;
|
|
33175
33881
|
}
|
|
@@ -33748,12 +34454,13 @@ var init_router2 = __esm({
|
|
|
33748
34454
|
init_agent();
|
|
33749
34455
|
init_retry();
|
|
33750
34456
|
init_quota();
|
|
33751
|
-
|
|
34457
|
+
init_classify2();
|
|
34458
|
+
init_engine();
|
|
33752
34459
|
init_store5();
|
|
33753
34460
|
init_backends();
|
|
33754
34461
|
init_wizard();
|
|
33755
34462
|
init_ollama3();
|
|
33756
|
-
|
|
34463
|
+
init_classify3();
|
|
33757
34464
|
init_session_log2();
|
|
33758
34465
|
init_live_status();
|
|
33759
34466
|
init_detect();
|
|
@@ -34021,6 +34728,28 @@ async function executeJob(job) {
|
|
|
34021
34728
|
async function runWithRetry(job, model2, runId, t0) {
|
|
34022
34729
|
let lastError;
|
|
34023
34730
|
const chatId = job.sessionType === "isolated" ? `cron:${job.id}:${runId}` : job.chatId;
|
|
34731
|
+
if (job.jobType === "memory_sweep") {
|
|
34732
|
+
const { runWeeklySweep: runWeeklySweep2 } = await Promise.resolve().then(() => (init_sweep(), sweep_exports));
|
|
34733
|
+
const { getChannelRegistry: getChannelRegistry2 } = await Promise.resolve().then(() => (init_delivery(), delivery_exports));
|
|
34734
|
+
const channelName = job.channel ?? "telegram";
|
|
34735
|
+
const channel = getChannelRegistry2()?.get(channelName);
|
|
34736
|
+
const deliveryTarget = job.target ?? job.chatId;
|
|
34737
|
+
if (!channel) {
|
|
34738
|
+
warn(`[sweep] Job #${job.id}: channel "${channelName}" not found \u2014 sweep ran silently`);
|
|
34739
|
+
}
|
|
34740
|
+
const result = await runWeeklySweep2(
|
|
34741
|
+
deliveryTarget,
|
|
34742
|
+
channel,
|
|
34743
|
+
resolveJobBackendId(job) ?? void 0,
|
|
34744
|
+
job.model ?? void 0
|
|
34745
|
+
);
|
|
34746
|
+
const parts = [];
|
|
34747
|
+
if (result.cleanedUp > 0) parts.push(`cleaned up ${result.cleanedUp} expired memories`);
|
|
34748
|
+
if (result.suggestionsCount > 0) parts.push(`found ${result.suggestionsCount} suggestions`);
|
|
34749
|
+
if (result.error) parts.push(`error: ${result.error}`);
|
|
34750
|
+
const summary = parts.length > 0 ? parts.join(", ") : "memory bank healthy, no action needed";
|
|
34751
|
+
return { text: summary };
|
|
34752
|
+
}
|
|
34024
34753
|
if (job.jobType === "reflection") {
|
|
34025
34754
|
const { runNightlyReflection: runNightlyReflection2 } = await Promise.resolve().then(() => (init_analyze(), analyze_exports));
|
|
34026
34755
|
const { formatNightlySummary: formatNightlySummary2 } = await Promise.resolve().then(() => (init_propose(), propose_exports));
|
|
@@ -35503,7 +36232,7 @@ var init_telegram2 = __esm({
|
|
|
35503
36232
|
}
|
|
35504
36233
|
return keyboard;
|
|
35505
36234
|
}
|
|
35506
|
-
async editKeyboard(chatId, messageId, text, buttons) {
|
|
36235
|
+
async editKeyboard(chatId, messageId, text, buttons, opts) {
|
|
35507
36236
|
const keyboard = this.buildInlineKeyboard(buttons);
|
|
35508
36237
|
const formatted = sanitizeForTelegram(formatForTelegram(text));
|
|
35509
36238
|
try {
|
|
@@ -35513,7 +36242,8 @@ var init_telegram2 = __esm({
|
|
|
35513
36242
|
() => this.bot.api.editMessageText(numericChatId(chatId), parseInt(messageId), formatted, {
|
|
35514
36243
|
parse_mode: "HTML",
|
|
35515
36244
|
reply_markup: keyboard
|
|
35516
|
-
})
|
|
36245
|
+
}),
|
|
36246
|
+
opts?.priority
|
|
35517
36247
|
);
|
|
35518
36248
|
return true;
|
|
35519
36249
|
} catch (err) {
|
|
@@ -35527,7 +36257,8 @@ var init_telegram2 = __esm({
|
|
|
35527
36257
|
parseInt(messageId),
|
|
35528
36258
|
formatted.replace(/<[^>]+>/g, ""),
|
|
35529
36259
|
{ reply_markup: keyboard }
|
|
35530
|
-
)
|
|
36260
|
+
),
|
|
36261
|
+
opts?.priority
|
|
35531
36262
|
);
|
|
35532
36263
|
return true;
|
|
35533
36264
|
} catch (err2) {
|
|
@@ -36606,7 +37337,7 @@ async function main() {
|
|
|
36606
37337
|
pruneMessageLog(30, 2e3);
|
|
36607
37338
|
bootstrapBuiltinMcps(getDb());
|
|
36608
37339
|
try {
|
|
36609
|
-
const { resetIntentStats: resetIntentStats2 } = await Promise.resolve().then(() => (
|
|
37340
|
+
const { resetIntentStats: resetIntentStats2 } = await Promise.resolve().then(() => (init_classify2(), classify_exports));
|
|
36610
37341
|
resetIntentStats2();
|
|
36611
37342
|
} catch {
|
|
36612
37343
|
}
|
|
@@ -36782,6 +37513,22 @@ ${lines.join("\n")}`;
|
|
|
36782
37513
|
migrateEmbeddings().catch((err) => error("[cc-claw] Embedding migration failed:", err));
|
|
36783
37514
|
initHeartbeat(channelRegistry);
|
|
36784
37515
|
startAllHeartbeats();
|
|
37516
|
+
try {
|
|
37517
|
+
const { getMetaValue: getMetaValue2 } = await Promise.resolve().then(() => (init_store5(), store_exports5));
|
|
37518
|
+
const { SWEEP_ENABLED_KEY: SWEEP_ENABLED_KEY2, findSweepJob: findSweepJob2, enableSweep: enableSweep2 } = await Promise.resolve().then(() => (init_sweep(), sweep_exports));
|
|
37519
|
+
const sweepEnabled = getMetaValue2(SWEEP_ENABLED_KEY2);
|
|
37520
|
+
if (sweepEnabled === "1") {
|
|
37521
|
+
if (!findSweepJob2()) {
|
|
37522
|
+
const primaryChatId = (process.env.ALLOWED_CHAT_ID ?? "").split(",")[0]?.trim();
|
|
37523
|
+
if (primaryChatId) {
|
|
37524
|
+
enableSweep2(primaryChatId);
|
|
37525
|
+
log("[sweep] Restored weekly memory sweep job from meta setting");
|
|
37526
|
+
}
|
|
37527
|
+
}
|
|
37528
|
+
}
|
|
37529
|
+
} catch (err) {
|
|
37530
|
+
warn(`[sweep] Failed to restore sweep job: ${err instanceof Error ? err.message : String(err)}`);
|
|
37531
|
+
}
|
|
36785
37532
|
startHealthMonitor3(channelRegistry.list(), handleMessage);
|
|
36786
37533
|
Promise.resolve().then(() => (init_health(), health_exports)).then(({ startHealthMonitor: startMcpHealthMonitor }) => {
|
|
36787
37534
|
startMcpHealthMonitor(getDb());
|
|
@@ -36826,7 +37573,7 @@ ${lines.join("\n")}`;
|
|
|
36826
37573
|
;
|
|
36827
37574
|
shutdownOrchestrator();
|
|
36828
37575
|
shutdownScheduler();
|
|
36829
|
-
|
|
37576
|
+
flushMemoryHalfLifeUpdates();
|
|
36830
37577
|
flushSummarySalienceUpdates();
|
|
36831
37578
|
await Promise.race([
|
|
36832
37579
|
summarizeAllPending(),
|
|
@@ -37779,6 +38526,7 @@ async function logsCommand(opts) {
|
|
|
37779
38526
|
console.log(muted(` \u2500\u2500 ${logFile} (last ${tailLines.length} lines) \u2500\u2500`));
|
|
37780
38527
|
console.log(tailLines.join("\n"));
|
|
37781
38528
|
if (opts.follow) {
|
|
38529
|
+
globalThis._cliStayAlive = true;
|
|
37782
38530
|
console.log(muted("\n Following... (Ctrl+C to stop)\n"));
|
|
37783
38531
|
let lastLength = content.length;
|
|
37784
38532
|
watchFile2(logFile, { interval: 500 }, () => {
|
|
@@ -37869,6 +38617,7 @@ async function sessionLogsTail(opts) {
|
|
|
37869
38617
|
console.log(line);
|
|
37870
38618
|
}
|
|
37871
38619
|
if (opts.follow) {
|
|
38620
|
+
globalThis._cliStayAlive = true;
|
|
37872
38621
|
console.log(muted("\n Following... (Ctrl+C to stop)\n"));
|
|
37873
38622
|
let lastLength = 0;
|
|
37874
38623
|
try {
|
|
@@ -37976,9 +38725,9 @@ async function geminiList(globalOpts) {
|
|
|
37976
38725
|
email: s.slot_type === "oauth" ? resolveOAuthEmail(s.config_home) : null
|
|
37977
38726
|
}));
|
|
37978
38727
|
output(enriched, (data) => {
|
|
37979
|
-
const
|
|
38728
|
+
const list2 = data;
|
|
37980
38729
|
const lines = ["", divider("Gemini Credential Slots"), ""];
|
|
37981
|
-
for (const s of
|
|
38730
|
+
for (const s of list2) {
|
|
37982
38731
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
37983
38732
|
const inCooldown = s.cooldown_until && s.cooldown_until > now;
|
|
37984
38733
|
const icon = !s.enabled ? error2("\u25CB disabled") : inCooldown ? warning("\u25D1 cooldown") : success("\u25CF active");
|
|
@@ -38301,7 +39050,7 @@ async function resolveSlotId2(backend2, idOrLabel) {
|
|
|
38301
39050
|
return match?.id ?? null;
|
|
38302
39051
|
}
|
|
38303
39052
|
function makeList(backend2, displayName) {
|
|
38304
|
-
return async function
|
|
39053
|
+
return async function list2(_globalOpts) {
|
|
38305
39054
|
requireDb2();
|
|
38306
39055
|
const { openDatabaseReadOnly: openDatabaseReadOnly2 } = await Promise.resolve().then(() => (init_store5(), store_exports5));
|
|
38307
39056
|
const readDb = openDatabaseReadOnly2();
|
|
@@ -38316,9 +39065,9 @@ Add one with: cc-claw ${backend2} add-account or cc-claw ${backend2} add-key`)
|
|
|
38316
39065
|
return;
|
|
38317
39066
|
}
|
|
38318
39067
|
output(slots, (data) => {
|
|
38319
|
-
const
|
|
39068
|
+
const list3 = data;
|
|
38320
39069
|
const lines = ["", divider(`${displayName} Credential Slots`), ""];
|
|
38321
|
-
for (const s of
|
|
39070
|
+
for (const s of list3) {
|
|
38322
39071
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
38323
39072
|
const inCooldown = s.cooldown_until && s.cooldown_until > now;
|
|
38324
39073
|
const icon = !s.enabled ? error2("\u25CB disabled") : inCooldown ? warning("\u25D1 cooldown") : success("\u25CF active");
|
|
@@ -38782,9 +39531,9 @@ async function ollamaList(globalOpts) {
|
|
|
38782
39531
|
modelCount: modelCounts.get(s.id) ?? 0
|
|
38783
39532
|
}));
|
|
38784
39533
|
output(data, (d) => {
|
|
38785
|
-
const
|
|
39534
|
+
const list2 = d;
|
|
38786
39535
|
const lines = ["", divider("Ollama Servers"), ""];
|
|
38787
|
-
for (const s of
|
|
39536
|
+
for (const s of list2) {
|
|
38788
39537
|
const dot = statusDot(s.status === "online" ? "active" : "offline");
|
|
38789
39538
|
lines.push(` ${dot} ${s.name} ${muted(`(${s.host}:${s.port})`)}`);
|
|
38790
39539
|
lines.push(` Models: ${s.modelCount} \xB7 Status: ${s.status === "online" ? success("online") : error2("offline")}`);
|
|
@@ -38936,13 +39685,13 @@ async function ollamaDiscover(globalOpts, opts) {
|
|
|
38936
39685
|
contextWindow: m.contextWindow,
|
|
38937
39686
|
sizeBytes: m.sizeBytes
|
|
38938
39687
|
})), (d) => {
|
|
38939
|
-
const
|
|
38940
|
-
if (
|
|
39688
|
+
const list2 = d;
|
|
39689
|
+
if (list2.length === 0) {
|
|
38941
39690
|
return "\n No models found. Check server connectivity.\n";
|
|
38942
39691
|
}
|
|
38943
39692
|
const lines = [`
|
|
38944
|
-
${success(`\u2713 Discovered ${
|
|
38945
|
-
for (const m of
|
|
39693
|
+
${success(`\u2713 Discovered ${list2.length} model(s):`)}`, ""];
|
|
39694
|
+
for (const m of list2) {
|
|
38946
39695
|
const sizeGB = m.sizeBytes > 0 ? `${(m.sizeBytes / 1e9).toFixed(1)}GB` : "";
|
|
38947
39696
|
const ctxK = m.contextWindow ? `${(m.contextWindow / 1e3).toFixed(0)}K ctx` : "";
|
|
38948
39697
|
const meta = [m.parameterSize, sizeGB, ctxK].filter(Boolean).join(" \xB7 ");
|
|
@@ -39029,9 +39778,9 @@ async function backendList(globalOpts) {
|
|
|
39029
39778
|
defaultModel: a.defaultModel
|
|
39030
39779
|
}));
|
|
39031
39780
|
output(data, (d) => {
|
|
39032
|
-
const
|
|
39781
|
+
const list2 = d;
|
|
39033
39782
|
const lines = ["", divider("Backends"), ""];
|
|
39034
|
-
for (const b of
|
|
39783
|
+
for (const b of list2) {
|
|
39035
39784
|
const marker = b.active ? success("\u25CF ") : " ";
|
|
39036
39785
|
lines.push(` ${marker}${b.displayName} (${b.id})${b.active ? success(" \u2190 active") : ""}`);
|
|
39037
39786
|
lines.push(` Default model: ${muted(b.defaultModel)}`);
|
|
@@ -39349,12 +40098,12 @@ async function cronList(globalOpts) {
|
|
|
39349
40098
|
const jobs = readDb.prepare("SELECT * FROM jobs ORDER BY id").all();
|
|
39350
40099
|
readDb.close();
|
|
39351
40100
|
output(jobs, (d) => {
|
|
39352
|
-
const
|
|
39353
|
-
if (
|
|
40101
|
+
const list2 = d;
|
|
40102
|
+
if (list2.length === 0) return `
|
|
39354
40103
|
${muted("No scheduled jobs.")}
|
|
39355
40104
|
`;
|
|
39356
|
-
const lines = ["", divider(`Scheduled Jobs (${
|
|
39357
|
-
for (const j of
|
|
40105
|
+
const lines = ["", divider(`Scheduled Jobs (${list2.length})`), ""];
|
|
40106
|
+
for (const j of list2) {
|
|
39358
40107
|
const status = !j.active ? "cancelled" : !j.enabled ? "paused" : "active";
|
|
39359
40108
|
const schedule2 = j.cron ?? (j.at_time ? `at ${j.at_time}` : j.every_ms ? `every ${j.every_ms / 1e3}s` : "?");
|
|
39360
40109
|
const tz = j.timezone !== "UTC" ? ` (${j.timezone})` : "";
|
|
@@ -39574,12 +40323,12 @@ async function cronRuns(globalOpts, jobId, opts) {
|
|
|
39574
40323
|
const runs = readDb.prepare(query).all(...params);
|
|
39575
40324
|
readDb.close();
|
|
39576
40325
|
output(runs, (d) => {
|
|
39577
|
-
const
|
|
39578
|
-
if (
|
|
40326
|
+
const list2 = d;
|
|
40327
|
+
if (list2.length === 0) return `
|
|
39579
40328
|
${muted(jobId ? `No runs for job #${jobId}.` : "No run history yet.")}
|
|
39580
40329
|
`;
|
|
39581
40330
|
const lines = ["", divider("Run History"), ""];
|
|
39582
|
-
for (const r of
|
|
40331
|
+
for (const r of list2) {
|
|
39583
40332
|
const duration = r.duration_ms ? ` (${(r.duration_ms / 1e3).toFixed(1)}s)` : "";
|
|
39584
40333
|
lines.push(` #${r.job_id} [${r.status}] ${formatLocalDateTime(r.started_at)}${duration}`);
|
|
39585
40334
|
if (r.error) lines.push(` Error: ${r.error.slice(0, 100)}`);
|
|
@@ -39622,12 +40371,12 @@ async function agentsList(globalOpts) {
|
|
|
39622
40371
|
).all();
|
|
39623
40372
|
readDb.close();
|
|
39624
40373
|
output(agents2, (d) => {
|
|
39625
|
-
const
|
|
39626
|
-
if (
|
|
40374
|
+
const list2 = d;
|
|
40375
|
+
if (list2.length === 0) return `
|
|
39627
40376
|
${muted("No active agents.")}
|
|
39628
40377
|
`;
|
|
39629
|
-
const lines = ["", divider(`Active Agents (${
|
|
39630
|
-
for (const a of
|
|
40378
|
+
const lines = ["", divider(`Active Agents (${list2.length})`), ""];
|
|
40379
|
+
for (const a of list2) {
|
|
39631
40380
|
const shortId = a.id?.slice(0, 8) ?? "?";
|
|
39632
40381
|
lines.push(` ${statusDot(a.status)} ${shortId} (${a.runnerId}) \u2014 ${a.status}`);
|
|
39633
40382
|
if (a.task) lines.push(` Task: ${a.task.slice(0, 80)}${a.task.length > 80 ? "\u2026" : ""}`);
|
|
@@ -39653,12 +40402,12 @@ async function tasksList(globalOpts) {
|
|
|
39653
40402
|
).all();
|
|
39654
40403
|
readDb.close();
|
|
39655
40404
|
output(tasks, (d) => {
|
|
39656
|
-
const
|
|
39657
|
-
if (
|
|
40405
|
+
const list2 = d;
|
|
40406
|
+
if (list2.length === 0) return `
|
|
39658
40407
|
${muted("No active tasks.")}
|
|
39659
40408
|
`;
|
|
39660
|
-
const lines = ["", divider(`Task Board (${
|
|
39661
|
-
for (const t of
|
|
40409
|
+
const lines = ["", divider(`Task Board (${list2.length})`), ""];
|
|
40410
|
+
for (const t of list2) {
|
|
39662
40411
|
const assignee = t.assignee ? ` (\u2192 ${t.assignee.slice(0, 8)})` : "";
|
|
39663
40412
|
lines.push(` ${statusDot(t.status === "completed" ? "ok" : t.status === "in_progress" ? "running" : "paused")} #${t.id}: ${t.subject}${assignee}`);
|
|
39664
40413
|
}
|
|
@@ -39737,12 +40486,12 @@ async function runnersList(globalOpts) {
|
|
|
39737
40486
|
displayName: r.displayName,
|
|
39738
40487
|
specialties: r.capabilities.specialties ?? []
|
|
39739
40488
|
})), (d) => {
|
|
39740
|
-
const
|
|
39741
|
-
if (
|
|
40489
|
+
const list2 = d;
|
|
40490
|
+
if (list2.length === 0) return `
|
|
39742
40491
|
${muted("No runners registered.")}
|
|
39743
40492
|
`;
|
|
39744
|
-
const lines = ["", divider(`Registered Runners (${
|
|
39745
|
-
for (const r of
|
|
40493
|
+
const lines = ["", divider(`Registered Runners (${list2.length})`), ""];
|
|
40494
|
+
for (const r of list2) {
|
|
39746
40495
|
const specs = r.specialties.length > 0 ? ` \u2014 ${r.specialties.join(", ")}` : "";
|
|
39747
40496
|
lines.push(` \u2022 ${r.id} (${r.displayName})${specs}`);
|
|
39748
40497
|
}
|
|
@@ -39965,9 +40714,9 @@ async function usageTokens(globalOpts) {
|
|
|
39965
40714
|
});
|
|
39966
40715
|
readDb.close();
|
|
39967
40716
|
output(data, (d) => {
|
|
39968
|
-
const
|
|
40717
|
+
const list2 = d;
|
|
39969
40718
|
const lines = ["", divider("Backend usage (last 24h)"), ""];
|
|
39970
|
-
for (const u of
|
|
40719
|
+
for (const u of list2) {
|
|
39971
40720
|
lines.push(` ${u.displayName}: ${(u.input_tokens / 1e3).toFixed(1)}K in / ${(u.output_tokens / 1e3).toFixed(1)}K out (${u.request_count} requests)`);
|
|
39972
40721
|
}
|
|
39973
40722
|
lines.push("");
|
|
@@ -39981,13 +40730,13 @@ async function limitsList(globalOpts) {
|
|
|
39981
40730
|
const limits2 = readDb.prepare("SELECT * FROM backend_limits").all();
|
|
39982
40731
|
readDb.close();
|
|
39983
40732
|
output(limits2, (d) => {
|
|
39984
|
-
const
|
|
39985
|
-
if (
|
|
40733
|
+
const list2 = d;
|
|
40734
|
+
if (list2.length === 0) return `
|
|
39986
40735
|
${muted("No usage limits set.")}
|
|
39987
40736
|
${muted("Set with: cc-claw usage limits set <backend> <window> <tokens>")}
|
|
39988
40737
|
`;
|
|
39989
40738
|
const lines = ["", divider("Usage limits"), ""];
|
|
39990
|
-
for (const l of
|
|
40739
|
+
for (const l of list2) {
|
|
39991
40740
|
lines.push(` ${l.backend} (${l.window}): ${l.max_input_tokens ? `${(l.max_input_tokens / 1e3).toFixed(0)}K input tokens` : "no limit"}`);
|
|
39992
40741
|
}
|
|
39993
40742
|
lines.push("");
|
|
@@ -40295,9 +41044,9 @@ async function toolsList(globalOpts) {
|
|
|
40295
41044
|
const toolMap = new Map(rows.map((r) => [r.tool, !!r.enabled]));
|
|
40296
41045
|
const tools2 = ALL_TOOLS4.map((t) => ({ name: t, enabled: toolMap.get(t) ?? true }));
|
|
40297
41046
|
output(tools2, (d) => {
|
|
40298
|
-
const
|
|
41047
|
+
const list2 = d;
|
|
40299
41048
|
const lines = ["", divider("Tools"), ""];
|
|
40300
|
-
for (const t of
|
|
41049
|
+
for (const t of list2) {
|
|
40301
41050
|
lines.push(` ${checkMark(t.enabled)} ${t.name}`);
|
|
40302
41051
|
}
|
|
40303
41052
|
lines.push("");
|
|
@@ -40824,13 +41573,13 @@ async function chatsList(_globalOpts) {
|
|
|
40824
41573
|
const aliases = readDb.prepare("SELECT alias, chat_id FROM chat_aliases ORDER BY alias").all();
|
|
40825
41574
|
readDb.close();
|
|
40826
41575
|
output(aliases, (d) => {
|
|
40827
|
-
const
|
|
40828
|
-
if (
|
|
41576
|
+
const list2 = d;
|
|
41577
|
+
if (list2.length === 0) return `
|
|
40829
41578
|
${muted("No chat aliases configured yet.")}
|
|
40830
41579
|
${muted("Use: cc-claw chats alias <chat_id> <name>")}
|
|
40831
41580
|
`;
|
|
40832
41581
|
const lines = ["", divider("Chat Aliases"), ""];
|
|
40833
|
-
for (const a of
|
|
41582
|
+
for (const a of list2) {
|
|
40834
41583
|
lines.push(` ${a.alias} \u2192 ${a.chat_id}`);
|
|
40835
41584
|
}
|
|
40836
41585
|
lines.push("");
|
|
@@ -40893,13 +41642,13 @@ async function skillsList(_globalOpts) {
|
|
|
40893
41642
|
sources: s.sources,
|
|
40894
41643
|
filePath: s.filePath
|
|
40895
41644
|
})), (d) => {
|
|
40896
|
-
const
|
|
40897
|
-
if (
|
|
41645
|
+
const list2 = d;
|
|
41646
|
+
if (list2.length === 0) return `
|
|
40898
41647
|
${muted("No skills found.")}
|
|
40899
41648
|
${muted("Install with: cc-claw skills install <github-url>")}
|
|
40900
41649
|
`;
|
|
40901
|
-
const lines = ["", divider(`Skills (${
|
|
40902
|
-
for (const s of
|
|
41650
|
+
const lines = ["", divider(`Skills (${list2.length})`), ""];
|
|
41651
|
+
for (const s of list2) {
|
|
40903
41652
|
const tags = s.sources.join(", ");
|
|
40904
41653
|
const desc = s.description ? ` \u2014 ${s.description.slice(0, 60)}` : "";
|
|
40905
41654
|
lines.push(` \u2022 ${s.name} [${muted(tags)}]${desc}`);
|
|
@@ -40964,14 +41713,14 @@ async function mcpList(globalOpts) {
|
|
|
40964
41713
|
const mcps = listMcpServers2(db3);
|
|
40965
41714
|
db3.close();
|
|
40966
41715
|
output(mcps, (d) => {
|
|
40967
|
-
const
|
|
40968
|
-
if (
|
|
41716
|
+
const list2 = d;
|
|
41717
|
+
if (list2.length === 0) {
|
|
40969
41718
|
return `
|
|
40970
41719
|
${muted("No MCP servers registered. Use `cc-claw mcp add` or `cc-claw mcp import`.")}
|
|
40971
41720
|
`;
|
|
40972
41721
|
}
|
|
40973
|
-
const lines = ["", divider(`MCP Servers (${
|
|
40974
|
-
for (const m of
|
|
41722
|
+
const lines = ["", divider(`MCP Servers (${list2.length})`), ""];
|
|
41723
|
+
for (const m of list2) {
|
|
40975
41724
|
const lock = SYSTEM_MCP_NAMES.has(m.name) ? " \u{1F512}" : "";
|
|
40976
41725
|
const pin = m.enabledByDefault ? " \u{1F4CC}" : "";
|
|
40977
41726
|
const desc = m.description ? ` \u2014 ${muted(m.description)}` : "";
|
|
@@ -41378,6 +42127,7 @@ async function tuiCommand(globalOpts, cmdOpts) {
|
|
|
41378
42127
|
outputError("DAEMON_OFFLINE", "CC-Claw daemon is not running.\n\n Start it with: cc-claw service start");
|
|
41379
42128
|
process.exit(1);
|
|
41380
42129
|
}
|
|
42130
|
+
globalThis._cliStayAlive = true;
|
|
41381
42131
|
const chatId = resolveChatId2(globalOpts);
|
|
41382
42132
|
let theme = getTheme();
|
|
41383
42133
|
const rl2 = createInterface10({
|
|
@@ -43387,6 +44137,7 @@ program.command("council").alias("debate").description("Multi-model council deba
|
|
|
43387
44137
|
console.log("Select 2+ models, pose a question, and they debate anonymously for up to 3 rounds.");
|
|
43388
44138
|
});
|
|
43389
44139
|
program.command("start", { hidden: true }).description("Run the bot in the foreground (use 'service start' for background daemon)").action(async () => {
|
|
44140
|
+
globalThis._cliStayAlive = true;
|
|
43390
44141
|
await Promise.resolve().then(() => (init_index(), index_exports));
|
|
43391
44142
|
});
|
|
43392
44143
|
program.command("install", { hidden: true }).description("Install as background service \u2014 alias for service install").action(async () => {
|
|
@@ -43398,6 +44149,7 @@ program.command("uninstall", { hidden: true }).description("Remove background se
|
|
|
43398
44149
|
uninstallService2();
|
|
43399
44150
|
});
|
|
43400
44151
|
program.command("setup").description("Interactive configuration wizard").option("--dry-run", "Run the wizard without saving anything (demo/test mode)").action(async (opts) => {
|
|
44152
|
+
globalThis._cliStayAlive = true;
|
|
43401
44153
|
if (opts.dryRun) process.env.CC_CLAW_SETUP_DRY_RUN = "1";
|
|
43402
44154
|
await Promise.resolve().then(() => (init_setup(), setup_exports));
|
|
43403
44155
|
});
|
|
@@ -43433,10 +44185,12 @@ Update available: v${latest} (current: v${VERSION})`);
|
|
|
43433
44185
|
return;
|
|
43434
44186
|
}
|
|
43435
44187
|
await program.parseAsync(argv);
|
|
43436
|
-
if (
|
|
43437
|
-
process.stdout.
|
|
43438
|
-
|
|
43439
|
-
|
|
44188
|
+
if (!globalThis._cliStayAlive) {
|
|
44189
|
+
if (process.stdout.writableLength > 0) {
|
|
44190
|
+
process.stdout.once("drain", () => process.exit(0));
|
|
44191
|
+
} else {
|
|
44192
|
+
setTimeout(() => process.exit(0), 50);
|
|
44193
|
+
}
|
|
43440
44194
|
}
|
|
43441
44195
|
}
|
|
43442
44196
|
|