cc-claw 0.3.11 → 0.4.1
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 +1 -1
- package/dist/cli.js +518 -167
- package/package.json +2 -5
|
@@ -72,7 +72,7 @@ Typical workflow:
|
|
|
72
72
|
description: z.string().optional().describe("Short description of what this agent does"),
|
|
73
73
|
model: z.string().optional().describe("Model override for the sub-agent"),
|
|
74
74
|
skills: z.array(z.string()).optional().describe("Skills to inject into the sub-agent"),
|
|
75
|
-
permMode: z.string().optional().describe("Permission mode: yolo, safe,
|
|
75
|
+
permMode: z.string().optional().describe("Permission mode: yolo, safe, plan (defaults to chat's mode)"),
|
|
76
76
|
role: z.string().optional().describe("Agent role. Presets: worker, reviewer, planner, researcher, writer, analyst, debugger, critic, synthesizer. Custom roles accepted as labels."),
|
|
77
77
|
persona: z.string().optional().describe("Custom system prompt for the agent. Replaces the role's default prompt while preserving orchestrator tools and task."),
|
|
78
78
|
allowedTools: z.array(z.string()).optional().describe("Restrict agent to specific tools (Claude-only). E.g., ['Read', 'Grep', 'Glob']"),
|
package/dist/cli.js
CHANGED
|
@@ -48,7 +48,7 @@ var VERSION;
|
|
|
48
48
|
var init_version = __esm({
|
|
49
49
|
"src/version.ts"() {
|
|
50
50
|
"use strict";
|
|
51
|
-
VERSION = true ? "0.
|
|
51
|
+
VERSION = true ? "0.4.1" : (() => {
|
|
52
52
|
try {
|
|
53
53
|
return JSON.parse(readFileSync(join2(process.cwd(), "package.json"), "utf-8")).version ?? "unknown";
|
|
54
54
|
} catch {
|
|
@@ -922,12 +922,14 @@ __export(store_exports3, {
|
|
|
922
922
|
ALL_TOOLS: () => ALL_TOOLS,
|
|
923
923
|
addHeartbeatWatch: () => addHeartbeatWatch,
|
|
924
924
|
addUsage: () => addUsage,
|
|
925
|
+
appendMessageLog: () => appendMessageLog,
|
|
925
926
|
cancelJobById: () => cancelJobById,
|
|
926
927
|
checkBackendLimits: () => checkBackendLimits,
|
|
927
928
|
cleanExpiredWatches: () => cleanExpiredWatches,
|
|
928
929
|
clearAllSessions: () => clearAllSessions,
|
|
929
930
|
clearBackendLimit: () => clearBackendLimit,
|
|
930
931
|
clearCwd: () => clearCwd,
|
|
932
|
+
clearMessageLog: () => clearMessageLog,
|
|
931
933
|
clearModel: () => clearModel,
|
|
932
934
|
clearSession: () => clearSession,
|
|
933
935
|
clearSummarizer: () => clearSummarizer,
|
|
@@ -935,6 +937,7 @@ __export(store_exports3, {
|
|
|
935
937
|
clearUsage: () => clearUsage,
|
|
936
938
|
completeJobRun: () => completeJobRun,
|
|
937
939
|
deleteBookmark: () => deleteBookmark,
|
|
940
|
+
determineEscalationTarget: () => determineEscalationTarget,
|
|
938
941
|
findBookmarksByPrefix: () => findBookmarksByPrefix,
|
|
939
942
|
forgetMemory: () => forgetMemory,
|
|
940
943
|
getActiveJobs: () => getActiveJobs,
|
|
@@ -961,14 +964,18 @@ __export(store_exports3, {
|
|
|
961
964
|
getMemoriesWithoutEmbeddings: () => getMemoriesWithoutEmbeddings,
|
|
962
965
|
getMode: () => getMode,
|
|
963
966
|
getModel: () => getModel,
|
|
967
|
+
getPendingEscalation: () => getPendingEscalation,
|
|
964
968
|
getRecentBookmarks: () => getRecentBookmarks,
|
|
965
969
|
getRecentMemories: () => getRecentMemories,
|
|
970
|
+
getRecentMessageLog: () => getRecentMessageLog,
|
|
966
971
|
getSessionId: () => getSessionId,
|
|
967
972
|
getSessionStartedAt: () => getSessionStartedAt,
|
|
968
973
|
getSessionSummariesWithoutEmbeddings: () => getSessionSummariesWithoutEmbeddings,
|
|
969
974
|
getSummarizer: () => getSummarizer,
|
|
970
975
|
getThinkingLevel: () => getThinkingLevel,
|
|
971
976
|
getToolsMap: () => getToolsMap,
|
|
977
|
+
getUnsummarizedChatIds: () => getUnsummarizedChatIds,
|
|
978
|
+
getUnsummarizedLog: () => getUnsummarizedLog,
|
|
972
979
|
getUsage: () => getUsage,
|
|
973
980
|
getVerboseLevel: () => getVerboseLevel,
|
|
974
981
|
incrementJobFailures: () => incrementJobFailures,
|
|
@@ -977,10 +984,13 @@ __export(store_exports3, {
|
|
|
977
984
|
insertJobRun: () => insertJobRun,
|
|
978
985
|
listMemories: () => listMemories,
|
|
979
986
|
listSessionSummaries: () => listSessionSummaries,
|
|
987
|
+
markMessageLogSummarized: () => markMessageLogSummarized,
|
|
980
988
|
openDatabaseReadOnly: () => openDatabaseReadOnly,
|
|
981
989
|
pruneJobRuns: () => pruneJobRuns,
|
|
990
|
+
pruneMessageLog: () => pruneMessageLog,
|
|
982
991
|
removeChatAlias: () => removeChatAlias,
|
|
983
992
|
removeHeartbeatWatch: () => removeHeartbeatWatch,
|
|
993
|
+
removePendingEscalation: () => removePendingEscalation,
|
|
984
994
|
resetJobFailures: () => resetJobFailures,
|
|
985
995
|
resetTools: () => resetTools,
|
|
986
996
|
saveMemory: () => saveMemory,
|
|
@@ -989,6 +999,7 @@ __export(store_exports3, {
|
|
|
989
999
|
saveSessionSummaryWithEmbedding: () => saveSessionSummaryWithEmbedding,
|
|
990
1000
|
searchMemories: () => searchMemories,
|
|
991
1001
|
searchMemoriesReadOnly: () => searchMemoriesReadOnly,
|
|
1002
|
+
searchMessageLog: () => searchMessageLog,
|
|
992
1003
|
searchSessionSummaries: () => searchSessionSummaries,
|
|
993
1004
|
setBackend: () => setBackend,
|
|
994
1005
|
setBackendLimit: () => setBackendLimit,
|
|
@@ -1003,6 +1014,8 @@ __export(store_exports3, {
|
|
|
1003
1014
|
setSummarizer: () => setSummarizer,
|
|
1004
1015
|
setThinkingLevel: () => setThinkingLevel,
|
|
1005
1016
|
setVerboseLevel: () => setVerboseLevel,
|
|
1017
|
+
storePendingEscalation: () => storePendingEscalation,
|
|
1018
|
+
toFts5Query: () => toFts5Query,
|
|
1006
1019
|
toggleTool: () => toggleTool,
|
|
1007
1020
|
touchBookmark: () => touchBookmark,
|
|
1008
1021
|
updateHeartbeatTimestamps: () => updateHeartbeatTimestamps,
|
|
@@ -1084,6 +1097,7 @@ function initDatabase() {
|
|
|
1084
1097
|
mode TEXT NOT NULL DEFAULT 'yolo'
|
|
1085
1098
|
);
|
|
1086
1099
|
`);
|
|
1100
|
+
db.prepare("UPDATE chat_mode SET mode = 'plan' WHERE mode = 'readonly'").run();
|
|
1087
1101
|
db.exec(`
|
|
1088
1102
|
CREATE TABLE IF NOT EXISTS chat_verbose (
|
|
1089
1103
|
chat_id TEXT PRIMARY KEY,
|
|
@@ -1395,6 +1409,52 @@ function initDatabase() {
|
|
|
1395
1409
|
PRIMARY KEY (chatId, alias)
|
|
1396
1410
|
)
|
|
1397
1411
|
`);
|
|
1412
|
+
db.exec(`
|
|
1413
|
+
CREATE TABLE IF NOT EXISTS message_log (
|
|
1414
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
1415
|
+
chat_id TEXT NOT NULL,
|
|
1416
|
+
role TEXT NOT NULL,
|
|
1417
|
+
content TEXT NOT NULL,
|
|
1418
|
+
backend TEXT,
|
|
1419
|
+
model TEXT,
|
|
1420
|
+
session_id TEXT,
|
|
1421
|
+
summarized INTEGER NOT NULL DEFAULT 0,
|
|
1422
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
1423
|
+
)
|
|
1424
|
+
`);
|
|
1425
|
+
db.exec(`
|
|
1426
|
+
CREATE INDEX IF NOT EXISTS idx_message_log_chat
|
|
1427
|
+
ON message_log (chat_id, created_at DESC)
|
|
1428
|
+
`);
|
|
1429
|
+
db.exec(`
|
|
1430
|
+
CREATE INDEX IF NOT EXISTS idx_message_log_unsummarized
|
|
1431
|
+
ON message_log (chat_id, summarized, created_at ASC)
|
|
1432
|
+
`);
|
|
1433
|
+
db.exec(`
|
|
1434
|
+
CREATE VIRTUAL TABLE IF NOT EXISTS message_log_fts
|
|
1435
|
+
USING fts5(content, content='message_log', content_rowid='id')
|
|
1436
|
+
`);
|
|
1437
|
+
db.exec(`
|
|
1438
|
+
CREATE TRIGGER IF NOT EXISTS message_log_ai
|
|
1439
|
+
AFTER INSERT ON message_log BEGIN
|
|
1440
|
+
INSERT INTO message_log_fts(rowid, content) VALUES (new.id, new.content);
|
|
1441
|
+
END
|
|
1442
|
+
`);
|
|
1443
|
+
db.exec(`
|
|
1444
|
+
CREATE TRIGGER IF NOT EXISTS message_log_ad
|
|
1445
|
+
AFTER DELETE ON message_log BEGIN
|
|
1446
|
+
INSERT INTO message_log_fts(message_log_fts, rowid, content)
|
|
1447
|
+
VALUES ('delete', old.id, old.content);
|
|
1448
|
+
END
|
|
1449
|
+
`);
|
|
1450
|
+
db.exec(`
|
|
1451
|
+
CREATE TRIGGER IF NOT EXISTS message_log_au
|
|
1452
|
+
AFTER UPDATE ON message_log BEGIN
|
|
1453
|
+
INSERT INTO message_log_fts(message_log_fts, rowid, content)
|
|
1454
|
+
VALUES ('delete', old.id, old.content);
|
|
1455
|
+
INSERT INTO message_log_fts(rowid, content) VALUES (new.id, new.content);
|
|
1456
|
+
END
|
|
1457
|
+
`);
|
|
1398
1458
|
try {
|
|
1399
1459
|
db.exec("ALTER TABLE memories ADD COLUMN embedding TEXT");
|
|
1400
1460
|
} catch {
|
|
@@ -1674,7 +1734,8 @@ function getMode(chatId) {
|
|
|
1674
1734
|
const row = db.prepare(
|
|
1675
1735
|
"SELECT mode FROM chat_mode WHERE chat_id = ?"
|
|
1676
1736
|
).get(chatId);
|
|
1677
|
-
|
|
1737
|
+
const raw = row?.mode ?? "yolo";
|
|
1738
|
+
return raw === "readonly" ? "plan" : raw;
|
|
1678
1739
|
}
|
|
1679
1740
|
function setMode(chatId, mode) {
|
|
1680
1741
|
db.prepare(`
|
|
@@ -1683,6 +1744,30 @@ function setMode(chatId, mode) {
|
|
|
1683
1744
|
ON CONFLICT(chat_id) DO UPDATE SET mode = ?
|
|
1684
1745
|
`).run(chatId, mode, mode);
|
|
1685
1746
|
}
|
|
1747
|
+
function storePendingEscalation(chatId, message) {
|
|
1748
|
+
pendingEscalations.set(chatId, { message, createdAt: Date.now() });
|
|
1749
|
+
setTimeout(() => pendingEscalations.delete(chatId), ESCALATION_TTL_MS);
|
|
1750
|
+
}
|
|
1751
|
+
function getPendingEscalation(chatId) {
|
|
1752
|
+
const entry = pendingEscalations.get(chatId);
|
|
1753
|
+
if (!entry) return void 0;
|
|
1754
|
+
if (Date.now() - entry.createdAt > ESCALATION_TTL_MS) {
|
|
1755
|
+
pendingEscalations.delete(chatId);
|
|
1756
|
+
return void 0;
|
|
1757
|
+
}
|
|
1758
|
+
return entry.message;
|
|
1759
|
+
}
|
|
1760
|
+
function removePendingEscalation(chatId) {
|
|
1761
|
+
pendingEscalations.delete(chatId);
|
|
1762
|
+
}
|
|
1763
|
+
function determineEscalationTarget(chatId, currentMode) {
|
|
1764
|
+
if (currentMode === "plan") {
|
|
1765
|
+
const tools2 = getEnabledTools(chatId);
|
|
1766
|
+
if (tools2.length > 0 && tools2.length < ALL_TOOLS.length) return "safe";
|
|
1767
|
+
return "yolo";
|
|
1768
|
+
}
|
|
1769
|
+
return "yolo";
|
|
1770
|
+
}
|
|
1686
1771
|
function getUsage(chatId) {
|
|
1687
1772
|
const row = db.prepare(
|
|
1688
1773
|
"SELECT input_tokens, output_tokens, cache_read_tokens, request_count, last_input_tokens, last_cache_read_tokens FROM chat_usage WHERE chat_id = ?"
|
|
@@ -2166,7 +2251,77 @@ function getAllChatAliases() {
|
|
|
2166
2251
|
function removeChatAlias(alias) {
|
|
2167
2252
|
return db.prepare("DELETE FROM chat_aliases WHERE alias = ?").run(alias.toLowerCase()).changes > 0;
|
|
2168
2253
|
}
|
|
2169
|
-
|
|
2254
|
+
function appendMessageLog(chatId, role, content, backend2, model2, sessionId) {
|
|
2255
|
+
getDb().prepare(`
|
|
2256
|
+
INSERT INTO message_log (chat_id, role, content, backend, model, session_id)
|
|
2257
|
+
VALUES (?, ?, ?, ?, ?, ?)
|
|
2258
|
+
`).run(chatId, role, content, backend2, model2, sessionId);
|
|
2259
|
+
}
|
|
2260
|
+
function getRecentMessageLog(chatId, limit) {
|
|
2261
|
+
return getDb().prepare(`
|
|
2262
|
+
SELECT * FROM message_log
|
|
2263
|
+
WHERE chat_id = ?
|
|
2264
|
+
ORDER BY created_at DESC, id DESC
|
|
2265
|
+
LIMIT ?
|
|
2266
|
+
`).all(chatId, limit);
|
|
2267
|
+
}
|
|
2268
|
+
function getUnsummarizedLog(chatId) {
|
|
2269
|
+
return getDb().prepare(`
|
|
2270
|
+
SELECT * FROM message_log
|
|
2271
|
+
WHERE chat_id = ? AND summarized = 0
|
|
2272
|
+
ORDER BY created_at ASC, id ASC
|
|
2273
|
+
`).all(chatId);
|
|
2274
|
+
}
|
|
2275
|
+
function searchMessageLog(chatId, query, limit = 20) {
|
|
2276
|
+
const sanitized = toFts5Query(query);
|
|
2277
|
+
if (!sanitized) return [];
|
|
2278
|
+
try {
|
|
2279
|
+
return getDb().prepare(`
|
|
2280
|
+
SELECT ml.* FROM message_log ml
|
|
2281
|
+
JOIN message_log_fts fts ON fts.rowid = ml.id
|
|
2282
|
+
WHERE ml.chat_id = ? AND message_log_fts MATCH ?
|
|
2283
|
+
ORDER BY fts.rank
|
|
2284
|
+
LIMIT ?
|
|
2285
|
+
`).all(chatId, sanitized, limit);
|
|
2286
|
+
} catch {
|
|
2287
|
+
return [];
|
|
2288
|
+
}
|
|
2289
|
+
}
|
|
2290
|
+
function markMessageLogSummarized(chatId) {
|
|
2291
|
+
getDb().prepare(`
|
|
2292
|
+
UPDATE message_log SET summarized = 1
|
|
2293
|
+
WHERE chat_id = ? AND summarized = 0
|
|
2294
|
+
`).run(chatId);
|
|
2295
|
+
}
|
|
2296
|
+
function clearMessageLog(chatId) {
|
|
2297
|
+
getDb().prepare(`DELETE FROM message_log WHERE chat_id = ?`).run(chatId);
|
|
2298
|
+
}
|
|
2299
|
+
function getUnsummarizedChatIds() {
|
|
2300
|
+
const rows = getDb().prepare(
|
|
2301
|
+
`SELECT DISTINCT chat_id FROM message_log WHERE summarized = 0`
|
|
2302
|
+
).all();
|
|
2303
|
+
return rows.map((r) => r.chat_id);
|
|
2304
|
+
}
|
|
2305
|
+
function pruneMessageLog(retentionDays = 30, rowCapPerChat = 2e3) {
|
|
2306
|
+
const db3 = getDb();
|
|
2307
|
+
db3.prepare(`
|
|
2308
|
+
DELETE FROM message_log
|
|
2309
|
+
WHERE created_at < datetime('now', '-' || ? || ' days')
|
|
2310
|
+
`).run(retentionDays);
|
|
2311
|
+
const chats2 = db3.prepare(
|
|
2312
|
+
`SELECT DISTINCT chat_id FROM message_log`
|
|
2313
|
+
).all();
|
|
2314
|
+
for (const { chat_id } of chats2) {
|
|
2315
|
+
db3.prepare(`
|
|
2316
|
+
DELETE FROM message_log
|
|
2317
|
+
WHERE chat_id = ? AND id NOT IN (
|
|
2318
|
+
SELECT id FROM message_log WHERE chat_id = ?
|
|
2319
|
+
ORDER BY created_at DESC, id DESC LIMIT ?
|
|
2320
|
+
)
|
|
2321
|
+
`).run(chat_id, chat_id, rowCapPerChat);
|
|
2322
|
+
}
|
|
2323
|
+
}
|
|
2324
|
+
var db, STOP_WORDS, pendingEscalations, ESCALATION_TTL_MS, ALL_TOOLS, JOB_SELECT;
|
|
2170
2325
|
var init_store4 = __esm({
|
|
2171
2326
|
"src/memory/store.ts"() {
|
|
2172
2327
|
"use strict";
|
|
@@ -2291,6 +2446,8 @@ var init_store4 = __esm({
|
|
|
2291
2446
|
"near",
|
|
2292
2447
|
"or"
|
|
2293
2448
|
]);
|
|
2449
|
+
pendingEscalations = /* @__PURE__ */ new Map();
|
|
2450
|
+
ESCALATION_TTL_MS = 5 * 60 * 1e3;
|
|
2294
2451
|
ALL_TOOLS = ["Read", "Glob", "Grep", "Bash", "Write", "Edit", "WebFetch", "WebSearch", "Agent", "AskUserQuestion"];
|
|
2295
2452
|
JOB_SELECT = `
|
|
2296
2453
|
SELECT id, schedule_type as scheduleType, cron, at_time as atTime, every_ms as everyMs,
|
|
@@ -2444,15 +2601,14 @@ var init_claude = __esm({
|
|
|
2444
2601
|
switch (opts.permMode) {
|
|
2445
2602
|
case "plan":
|
|
2446
2603
|
args.push("--permission-mode", "plan");
|
|
2447
|
-
|
|
2448
|
-
case "readonly":
|
|
2449
|
-
args.push("--permission-mode", "bypassPermissions");
|
|
2450
|
-
args.push("--allowedTools", "Read,Glob,Grep");
|
|
2604
|
+
args.push("--strict-mcp-config");
|
|
2451
2605
|
break;
|
|
2452
2606
|
case "safe":
|
|
2453
2607
|
args.push("--permission-mode", "bypassPermissions");
|
|
2454
2608
|
if (opts.allowedTools.length > 0) {
|
|
2455
|
-
args.push("--
|
|
2609
|
+
args.push("--tools", opts.allowedTools.join(","));
|
|
2610
|
+
} else {
|
|
2611
|
+
args.push("--tools", "Read,Glob,Grep");
|
|
2456
2612
|
}
|
|
2457
2613
|
break;
|
|
2458
2614
|
case "yolo":
|
|
@@ -2611,11 +2767,10 @@ var init_gemini = __esm({
|
|
|
2611
2767
|
if (opts.model) args.push("-m", opts.model);
|
|
2612
2768
|
switch (opts.permMode) {
|
|
2613
2769
|
case "plan":
|
|
2614
|
-
case "readonly":
|
|
2615
2770
|
args.push("--approval-mode", "plan");
|
|
2616
2771
|
break;
|
|
2617
2772
|
case "safe":
|
|
2618
|
-
args.push("--approval-mode", "
|
|
2773
|
+
args.push("--approval-mode", "plan");
|
|
2619
2774
|
break;
|
|
2620
2775
|
case "yolo":
|
|
2621
2776
|
default:
|
|
@@ -2697,21 +2852,34 @@ var init_codex = __esm({
|
|
|
2697
2852
|
thinking: "adjustable",
|
|
2698
2853
|
thinkingLevels: ["low", "medium", "high", "extra_high"],
|
|
2699
2854
|
defaultThinkingLevel: "medium"
|
|
2855
|
+
},
|
|
2856
|
+
"gpt-5.1-codex-max": {
|
|
2857
|
+
label: "GPT-5.1 Codex Max \u2014 deep reasoning",
|
|
2858
|
+
thinking: "adjustable",
|
|
2859
|
+
thinkingLevels: ["low", "medium", "high", "extra_high"],
|
|
2860
|
+
defaultThinkingLevel: "medium"
|
|
2861
|
+
},
|
|
2862
|
+
"gpt-5.1-codex-mini": {
|
|
2863
|
+
label: "GPT-5.1 Codex Mini \u2014 cheaper, faster",
|
|
2864
|
+
thinking: "adjustable",
|
|
2865
|
+
thinkingLevels: ["low", "medium", "high", "extra_high"],
|
|
2866
|
+
defaultThinkingLevel: "low"
|
|
2700
2867
|
}
|
|
2701
2868
|
};
|
|
2702
2869
|
defaultModel = "gpt-5.4";
|
|
2703
|
-
// Internal-only model for summarization — not shown in /model picker
|
|
2704
2870
|
summarizerModel = "gpt-5.1-codex-mini";
|
|
2705
2871
|
pricing = {
|
|
2706
2872
|
"gpt-5.4": { in: 2.5, out: 10, cache: 0.63 },
|
|
2707
2873
|
"gpt-5.3-codex": { in: 2, out: 8, cache: 0.5 },
|
|
2708
2874
|
"gpt-5.2-codex": { in: 2, out: 8, cache: 0.5 },
|
|
2875
|
+
"gpt-5.1-codex-max": { in: 1.25, out: 10, cache: 0.125 },
|
|
2709
2876
|
"gpt-5.1-codex-mini": { in: 0.3, out: 1.2, cache: 0.08 }
|
|
2710
2877
|
};
|
|
2711
2878
|
contextWindow = {
|
|
2712
2879
|
"gpt-5.4": 2e5,
|
|
2713
2880
|
"gpt-5.3-codex": 2e5,
|
|
2714
2881
|
"gpt-5.2-codex": 2e5,
|
|
2882
|
+
"gpt-5.1-codex-max": 4e5,
|
|
2715
2883
|
"gpt-5.1-codex-mini": 2e5
|
|
2716
2884
|
};
|
|
2717
2885
|
// Codex has no single result event with final text -- the caller (spawnQuery)
|
|
@@ -2753,11 +2921,10 @@ var init_codex = __esm({
|
|
|
2753
2921
|
if (opts.model) args.push("-m", opts.model);
|
|
2754
2922
|
switch (opts.permMode) {
|
|
2755
2923
|
case "plan":
|
|
2756
|
-
case "readonly":
|
|
2757
2924
|
args.push("--sandbox", "read-only");
|
|
2758
2925
|
break;
|
|
2759
2926
|
case "safe":
|
|
2760
|
-
args.push("--
|
|
2927
|
+
args.push("--sandbox", "read-only");
|
|
2761
2928
|
break;
|
|
2762
2929
|
case "yolo":
|
|
2763
2930
|
default:
|
|
@@ -2915,6 +3082,31 @@ function mergeAndScore(allItems, vectorScores, ftsScores, getDays, decayRate, to
|
|
|
2915
3082
|
results.sort((a, b) => b.score - a.score);
|
|
2916
3083
|
return results.slice(0, topK);
|
|
2917
3084
|
}
|
|
3085
|
+
function setPendingContextBridge(chatId, bridge) {
|
|
3086
|
+
pendingContextBridges.set(chatId, bridge);
|
|
3087
|
+
}
|
|
3088
|
+
function consumeContextBridge(chatId) {
|
|
3089
|
+
const bridge = pendingContextBridges.get(chatId) ?? null;
|
|
3090
|
+
pendingContextBridges.delete(chatId);
|
|
3091
|
+
return bridge;
|
|
3092
|
+
}
|
|
3093
|
+
function buildContextBridge(chatId, pairs = 15) {
|
|
3094
|
+
const rows = getRecentMessageLog(chatId, pairs * 2).reverse();
|
|
3095
|
+
if (rows.length === 0) return null;
|
|
3096
|
+
const lines = [`[Conversation history \u2014 continued from previous session]`];
|
|
3097
|
+
let totalChars = lines[0].length;
|
|
3098
|
+
for (const row of rows) {
|
|
3099
|
+
const backendLabel = row.backend ?? "unknown";
|
|
3100
|
+
const date = row.created_at.slice(0, 10);
|
|
3101
|
+
const roleLabel = row.role === "user" ? "You" : "Assistant";
|
|
3102
|
+
const line = `[${backendLabel} \xB7 ${date}] ${roleLabel}: ${row.content}`;
|
|
3103
|
+
if (totalChars + line.length > MAX_BRIDGE_CHARS) break;
|
|
3104
|
+
lines.push(line);
|
|
3105
|
+
totalChars += line.length;
|
|
3106
|
+
}
|
|
3107
|
+
lines.push(`[End of recent history \u2014 you are continuing this conversation]`);
|
|
3108
|
+
return lines.join("\n");
|
|
3109
|
+
}
|
|
2918
3110
|
async function injectMemoryContext(userMessage) {
|
|
2919
3111
|
const provider = resolveProvider();
|
|
2920
3112
|
const useVectors = !!provider;
|
|
@@ -3007,7 +3199,7 @@ async function injectMemoryContext(userMessage) {
|
|
|
3007
3199
|
${lines.join("\n")}
|
|
3008
3200
|
[End memory context]`;
|
|
3009
3201
|
}
|
|
3010
|
-
var MEMORY_DECAY_RATE, SESSION_DECAY_RATE, VECTOR_TOP_K, FTS_TOP_K, FINAL_TOP_K_MEMORIES, FINAL_TOP_K_SESSIONS, MAX_MEMORY_CHARS, MAX_SESSION_CHARS;
|
|
3202
|
+
var MEMORY_DECAY_RATE, SESSION_DECAY_RATE, VECTOR_TOP_K, FTS_TOP_K, FINAL_TOP_K_MEMORIES, FINAL_TOP_K_SESSIONS, MAX_MEMORY_CHARS, MAX_SESSION_CHARS, MAX_BRIDGE_CHARS, pendingContextBridges;
|
|
3011
3203
|
var init_inject = __esm({
|
|
3012
3204
|
"src/memory/inject.ts"() {
|
|
3013
3205
|
"use strict";
|
|
@@ -3021,6 +3213,8 @@ var init_inject = __esm({
|
|
|
3021
3213
|
FINAL_TOP_K_SESSIONS = 3;
|
|
3022
3214
|
MAX_MEMORY_CHARS = 500;
|
|
3023
3215
|
MAX_SESSION_CHARS = 800;
|
|
3216
|
+
MAX_BRIDGE_CHARS = 48e3;
|
|
3217
|
+
pendingContextBridges = /* @__PURE__ */ new Map();
|
|
3024
3218
|
}
|
|
3025
3219
|
});
|
|
3026
3220
|
|
|
@@ -3205,9 +3399,12 @@ function searchContext(userMessage) {
|
|
|
3205
3399
|
}
|
|
3206
3400
|
return null;
|
|
3207
3401
|
}
|
|
3208
|
-
async function assembleBootstrapPrompt(userMessage, tier = "full", chatId) {
|
|
3402
|
+
async function assembleBootstrapPrompt(userMessage, tier = "full", chatId, permMode) {
|
|
3209
3403
|
const sections = [];
|
|
3210
3404
|
syncNativeCliFiles();
|
|
3405
|
+
if (permMode && permMode !== "yolo") {
|
|
3406
|
+
sections.push(buildPermissionNotice(permMode));
|
|
3407
|
+
}
|
|
3211
3408
|
if (tier === "full") {
|
|
3212
3409
|
const ctx = searchContext(userMessage);
|
|
3213
3410
|
if (ctx) {
|
|
@@ -3215,6 +3412,12 @@ async function assembleBootstrapPrompt(userMessage, tier = "full", chatId) {
|
|
|
3215
3412
|
${ctx}`);
|
|
3216
3413
|
}
|
|
3217
3414
|
}
|
|
3415
|
+
if (chatId && tier !== "slim") {
|
|
3416
|
+
const bridge = consumeContextBridge(chatId);
|
|
3417
|
+
if (bridge) {
|
|
3418
|
+
sections.push(bridge);
|
|
3419
|
+
}
|
|
3420
|
+
}
|
|
3218
3421
|
if (tier !== "slim") {
|
|
3219
3422
|
const memory2 = await injectMemoryContext(userMessage);
|
|
3220
3423
|
if (memory2) {
|
|
@@ -3232,6 +3435,12 @@ ${ctx}`);
|
|
|
3232
3435
|
log(`[bootstrap] Assembled prompt: tier=${tier}, sections=${sections.length}, totalChars=${result.length}`);
|
|
3233
3436
|
return result;
|
|
3234
3437
|
}
|
|
3438
|
+
function buildPermissionNotice(mode) {
|
|
3439
|
+
if (mode === "plan") {
|
|
3440
|
+
return "[Permission notice]\nYou are in restricted mode (plan). You can read and analyze files but CANNOT edit, write, or execute commands.\nIf the user's request requires modifications or execution, include the marker [NEED_PERMISSION] at the end of your response. Briefly explain what you would need to do and why elevated access is required. Do NOT attempt workarounds or refuse the task \u2014 signal the need and the system will ask the user to approve.\n[End permission notice]";
|
|
3441
|
+
}
|
|
3442
|
+
return "[Permission notice]\nYou are in restricted mode (safe). Only a subset of tools are available to you.\nIf the user's request requires tools you don't have access to, include the marker [NEED_PERMISSION] at the end of your response. Briefly explain what you would need to do and why elevated access is required. Do NOT attempt workarounds or refuse the task \u2014 signal the need and the system will ask the user to approve.\n[End permission notice]";
|
|
3443
|
+
}
|
|
3235
3444
|
function truncateToTokenBudget(text, budget) {
|
|
3236
3445
|
const charBudget = budget * 4;
|
|
3237
3446
|
if (text.length <= charBudget) return text;
|
|
@@ -3296,66 +3505,62 @@ var init_loader = __esm({
|
|
|
3296
3505
|
});
|
|
3297
3506
|
|
|
3298
3507
|
// src/memory/session-log.ts
|
|
3299
|
-
function appendToLog(chatId, userMessage, assistantResponse) {
|
|
3300
|
-
if (!logs.has(chatId)) logs.set(chatId, []);
|
|
3301
|
-
const entries = logs.get(chatId);
|
|
3508
|
+
function appendToLog(chatId, userMessage, assistantResponse, backend2, model2, sessionId) {
|
|
3302
3509
|
const now = Date.now();
|
|
3303
|
-
|
|
3304
|
-
|
|
3305
|
-
const
|
|
3306
|
-
|
|
3307
|
-
|
|
3510
|
+
appendMessageLog(chatId, "user", userMessage, backend2 ?? null, model2 ?? null, sessionId ?? null);
|
|
3511
|
+
appendMessageLog(chatId, "assistant", assistantResponse, backend2 ?? null, model2 ?? null, sessionId ?? null);
|
|
3512
|
+
const existing = cache.get(chatId) ?? [];
|
|
3513
|
+
existing.push(
|
|
3514
|
+
{ role: "user", text: userMessage, timestamp: now },
|
|
3515
|
+
{ role: "assistant", text: assistantResponse, timestamp: now }
|
|
3516
|
+
);
|
|
3517
|
+
if (existing.length > CACHE_SIZE * 2) {
|
|
3518
|
+
existing.splice(0, existing.length - CACHE_SIZE * 2);
|
|
3308
3519
|
}
|
|
3520
|
+
cache.set(chatId, existing);
|
|
3309
3521
|
}
|
|
3310
3522
|
function getLog(chatId) {
|
|
3311
|
-
|
|
3523
|
+
const rows = getUnsummarizedLog(chatId);
|
|
3524
|
+
return rows.map((r) => ({
|
|
3525
|
+
role: r.role,
|
|
3526
|
+
text: r.content,
|
|
3527
|
+
timestamp: (/* @__PURE__ */ new Date(r.created_at + (r.created_at.includes("Z") ? "" : "Z"))).getTime()
|
|
3528
|
+
}));
|
|
3529
|
+
}
|
|
3530
|
+
function getMessagePairCount(chatId) {
|
|
3531
|
+
const cached = cache.get(chatId);
|
|
3532
|
+
if (cached && cached.length > 0) {
|
|
3533
|
+
return Math.floor(cached.length / 2);
|
|
3534
|
+
}
|
|
3535
|
+
return Math.floor(getUnsummarizedLog(chatId).length / 2);
|
|
3312
3536
|
}
|
|
3313
3537
|
function clearLog(chatId) {
|
|
3314
|
-
|
|
3538
|
+
markMessageLogSummarized(chatId);
|
|
3539
|
+
cache.delete(chatId);
|
|
3315
3540
|
}
|
|
3316
3541
|
function getLoggedChatIds() {
|
|
3317
|
-
return
|
|
3542
|
+
return getUnsummarizedChatIds();
|
|
3318
3543
|
}
|
|
3319
|
-
|
|
3320
|
-
return Math.floor((logs.get(chatId)?.length ?? 0) / 2);
|
|
3321
|
-
}
|
|
3322
|
-
var logs, MAX_PAIRS;
|
|
3544
|
+
var CACHE_SIZE, cache;
|
|
3323
3545
|
var init_session_log = __esm({
|
|
3324
3546
|
"src/memory/session-log.ts"() {
|
|
3325
3547
|
"use strict";
|
|
3326
|
-
|
|
3327
|
-
|
|
3548
|
+
init_store4();
|
|
3549
|
+
CACHE_SIZE = 20;
|
|
3550
|
+
cache = /* @__PURE__ */ new Map();
|
|
3328
3551
|
}
|
|
3329
3552
|
});
|
|
3330
3553
|
|
|
3331
3554
|
// src/memory/summarize.ts
|
|
3332
3555
|
var summarize_exports = {};
|
|
3333
3556
|
__export(summarize_exports, {
|
|
3557
|
+
CONTEXT_BRIDGE_PROMPT: () => CONTEXT_BRIDGE_PROMPT,
|
|
3334
3558
|
summarizeAllPending: () => summarizeAllPending,
|
|
3335
|
-
summarizeSession: () => summarizeSession
|
|
3559
|
+
summarizeSession: () => summarizeSession,
|
|
3560
|
+
summarizeWithFallbackChain: () => summarizeWithFallbackChain
|
|
3336
3561
|
});
|
|
3337
3562
|
import { spawn } from "child_process";
|
|
3338
3563
|
import { createInterface } from "readline";
|
|
3339
|
-
function resolveSummarizerAdapter(chatId) {
|
|
3340
|
-
const config2 = getSummarizer(chatId);
|
|
3341
|
-
if (config2.backend === "off") return null;
|
|
3342
|
-
let adapter;
|
|
3343
|
-
if (config2.backend) {
|
|
3344
|
-
adapter = getAdapter(config2.backend);
|
|
3345
|
-
} else {
|
|
3346
|
-
try {
|
|
3347
|
-
adapter = getAdapterForChat(chatId);
|
|
3348
|
-
} catch {
|
|
3349
|
-
try {
|
|
3350
|
-
adapter = getAdapter("claude");
|
|
3351
|
-
} catch {
|
|
3352
|
-
return null;
|
|
3353
|
-
}
|
|
3354
|
-
}
|
|
3355
|
-
}
|
|
3356
|
-
const model2 = config2.model ?? adapter.summarizerModel;
|
|
3357
|
-
return { adapter, model: model2 };
|
|
3358
|
-
}
|
|
3359
3564
|
function buildTranscript(entries) {
|
|
3360
3565
|
const lines = [];
|
|
3361
3566
|
let totalLen = 0;
|
|
@@ -3373,19 +3578,7 @@ function buildTranscript(entries) {
|
|
|
3373
3578
|
}
|
|
3374
3579
|
return lines.join("\n\n");
|
|
3375
3580
|
}
|
|
3376
|
-
async function
|
|
3377
|
-
const pairCount = getMessagePairCount(chatId);
|
|
3378
|
-
if (pairCount < MIN_PAIRS) {
|
|
3379
|
-
clearLog(chatId);
|
|
3380
|
-
return false;
|
|
3381
|
-
}
|
|
3382
|
-
const resolved = resolveSummarizerAdapter(chatId);
|
|
3383
|
-
if (!resolved) {
|
|
3384
|
-
clearLog(chatId);
|
|
3385
|
-
return false;
|
|
3386
|
-
}
|
|
3387
|
-
const { adapter, model: model2 } = resolved;
|
|
3388
|
-
const entries = getLog(chatId);
|
|
3581
|
+
async function attemptSummarize(chatId, adapter, model2, entries) {
|
|
3389
3582
|
const transcript = buildTranscript(entries);
|
|
3390
3583
|
const prompt = `${SUMMARIZE_PROMPT}
|
|
3391
3584
|
|
|
@@ -3404,7 +3597,6 @@ ${transcript}`;
|
|
|
3404
3597
|
let inputTokens = 0;
|
|
3405
3598
|
let outputTokens = 0;
|
|
3406
3599
|
let cacheReadTokens = 0;
|
|
3407
|
-
const SUMMARIZE_TIMEOUT_MS = 6e4;
|
|
3408
3600
|
await new Promise((resolve) => {
|
|
3409
3601
|
const proc = spawn(config2.executable, config2.args, {
|
|
3410
3602
|
env,
|
|
@@ -3414,7 +3606,7 @@ ${transcript}`;
|
|
|
3414
3606
|
proc.stderr?.resume();
|
|
3415
3607
|
const rl2 = createInterface({ input: proc.stdout });
|
|
3416
3608
|
const timeout = setTimeout(() => {
|
|
3417
|
-
warn(`[summarize] Timeout
|
|
3609
|
+
warn(`[summarize] Timeout (${adapter.id}:${model2}) for chat ${chatId}`);
|
|
3418
3610
|
rl2.close();
|
|
3419
3611
|
proc.kill("SIGTERM");
|
|
3420
3612
|
setTimeout(() => proc.kill("SIGKILL"), 2e3);
|
|
@@ -3429,9 +3621,7 @@ ${transcript}`;
|
|
|
3429
3621
|
}
|
|
3430
3622
|
const events = adapter.parseLine(msg);
|
|
3431
3623
|
for (const ev of events) {
|
|
3432
|
-
if (ev.type === "text" && ev.text)
|
|
3433
|
-
accumulatedText += ev.text;
|
|
3434
|
-
}
|
|
3624
|
+
if (ev.type === "text" && ev.text) accumulatedText += ev.text;
|
|
3435
3625
|
if (ev.type === "usage" && ev.usage) {
|
|
3436
3626
|
inputTokens += ev.usage.input;
|
|
3437
3627
|
outputTokens += ev.usage.output;
|
|
@@ -3463,11 +3653,9 @@ ${transcript}`;
|
|
|
3463
3653
|
if (inputTokens + outputTokens > 0) {
|
|
3464
3654
|
addUsage(chatId, inputTokens, outputTokens, cacheReadTokens, model2);
|
|
3465
3655
|
}
|
|
3656
|
+
if (!resultText) resultText = accumulatedText;
|
|
3466
3657
|
if (!resultText) {
|
|
3467
|
-
|
|
3468
|
-
}
|
|
3469
|
-
if (!resultText) {
|
|
3470
|
-
warn(`[summarize] ${adapter.displayName} returned empty result for chat ${chatId} \u2014 keeping log for retry`);
|
|
3658
|
+
warn(`[summarize] ${adapter.id}:${model2} returned empty result for chat ${chatId}`);
|
|
3471
3659
|
return false;
|
|
3472
3660
|
}
|
|
3473
3661
|
const summaryMatch = resultText.match(/SUMMARY:\s*(.+?)(?=\nKEY_DETAILS:|\nTOPICS:|$)/s);
|
|
@@ -3475,20 +3663,72 @@ ${transcript}`;
|
|
|
3475
3663
|
const topicsMatch = resultText.match(/TOPICS:\s*(.+)/);
|
|
3476
3664
|
let summary = summaryMatch?.[1]?.trim() ?? resultText.trim();
|
|
3477
3665
|
const keyDetails = keyDetailsMatch?.[1]?.trim();
|
|
3478
|
-
if (keyDetails)
|
|
3479
|
-
summary += `
|
|
3666
|
+
if (keyDetails) summary += `
|
|
3480
3667
|
Key details: ${keyDetails}`;
|
|
3481
|
-
}
|
|
3482
3668
|
const topics = topicsMatch?.[1]?.trim() ?? "";
|
|
3483
|
-
saveSessionSummaryWithEmbedding(chatId, summary, topics,
|
|
3484
|
-
log(`[summarize] Saved
|
|
3485
|
-
clearLog(chatId);
|
|
3669
|
+
saveSessionSummaryWithEmbedding(chatId, summary, topics, Math.floor(entries.length / 2));
|
|
3670
|
+
log(`[summarize] Saved summary via ${adapter.id}:${model2} for chat ${chatId}`);
|
|
3486
3671
|
return true;
|
|
3487
3672
|
} catch (err) {
|
|
3488
|
-
warn(`[summarize]
|
|
3673
|
+
warn(`[summarize] ${adapter.id}:${model2} failed for ${chatId}: ${errorMessage(err)}`);
|
|
3489
3674
|
return false;
|
|
3490
3675
|
}
|
|
3491
3676
|
}
|
|
3677
|
+
async function summarizeWithFallbackChain(chatId, targetBackendId) {
|
|
3678
|
+
const pairCount = getMessagePairCount(chatId);
|
|
3679
|
+
if (pairCount < MIN_PAIRS) {
|
|
3680
|
+
clearLog(chatId);
|
|
3681
|
+
return false;
|
|
3682
|
+
}
|
|
3683
|
+
const entries = getLog(chatId);
|
|
3684
|
+
if (entries.length === 0) return false;
|
|
3685
|
+
const tried = /* @__PURE__ */ new Set();
|
|
3686
|
+
try {
|
|
3687
|
+
const config2 = getSummarizer(chatId);
|
|
3688
|
+
if (config2.backend !== "off") {
|
|
3689
|
+
const adapter = config2.backend ? getAdapter(config2.backend) : getAdapterForChat(chatId);
|
|
3690
|
+
const model2 = config2.model ?? adapter.summarizerModel;
|
|
3691
|
+
const key = `${adapter.id}:${model2}`;
|
|
3692
|
+
tried.add(key);
|
|
3693
|
+
if (await attemptSummarize(chatId, adapter, model2, entries)) {
|
|
3694
|
+
clearLog(chatId);
|
|
3695
|
+
return true;
|
|
3696
|
+
}
|
|
3697
|
+
}
|
|
3698
|
+
} catch {
|
|
3699
|
+
}
|
|
3700
|
+
if (targetBackendId) {
|
|
3701
|
+
try {
|
|
3702
|
+
const targetAdapter = getAdapter(targetBackendId);
|
|
3703
|
+
const model2 = targetAdapter.summarizerModel;
|
|
3704
|
+
const key = `${targetAdapter.id}:${model2}`;
|
|
3705
|
+
if (!tried.has(key)) {
|
|
3706
|
+
tried.add(key);
|
|
3707
|
+
if (await attemptSummarize(chatId, targetAdapter, model2, entries)) {
|
|
3708
|
+
clearLog(chatId);
|
|
3709
|
+
return true;
|
|
3710
|
+
}
|
|
3711
|
+
}
|
|
3712
|
+
} catch {
|
|
3713
|
+
}
|
|
3714
|
+
}
|
|
3715
|
+
for (const adapter of getAllAdapters()) {
|
|
3716
|
+
const model2 = adapter.summarizerModel;
|
|
3717
|
+
const key = `${adapter.id}:${model2}`;
|
|
3718
|
+
if (!tried.has(key)) {
|
|
3719
|
+
tried.add(key);
|
|
3720
|
+
if (await attemptSummarize(chatId, adapter, model2, entries)) {
|
|
3721
|
+
clearLog(chatId);
|
|
3722
|
+
return true;
|
|
3723
|
+
}
|
|
3724
|
+
}
|
|
3725
|
+
}
|
|
3726
|
+
warn(`[summarize] All fallback attempts failed for chat ${chatId} \u2014 raw log preserved`);
|
|
3727
|
+
return false;
|
|
3728
|
+
}
|
|
3729
|
+
async function summarizeSession(chatId) {
|
|
3730
|
+
return summarizeWithFallbackChain(chatId);
|
|
3731
|
+
}
|
|
3492
3732
|
async function summarizeAllPending() {
|
|
3493
3733
|
const chatIds = getLoggedChatIds();
|
|
3494
3734
|
if (chatIds.length === 0) return;
|
|
@@ -3497,7 +3737,7 @@ async function summarizeAllPending() {
|
|
|
3497
3737
|
await summarizeSession(chatId);
|
|
3498
3738
|
}
|
|
3499
3739
|
}
|
|
3500
|
-
var MIN_PAIRS, USER_MSG_LIMIT, AGENT_MSG_LIMIT, TRANSCRIPT_CAP, SUMMARIZE_PROMPT;
|
|
3740
|
+
var MIN_PAIRS, USER_MSG_LIMIT, AGENT_MSG_LIMIT, TRANSCRIPT_CAP, SUMMARIZE_TIMEOUT_MS, SUMMARIZE_PROMPT, CONTEXT_BRIDGE_PROMPT;
|
|
3501
3741
|
var init_summarize = __esm({
|
|
3502
3742
|
"src/memory/summarize.ts"() {
|
|
3503
3743
|
"use strict";
|
|
@@ -3509,18 +3749,20 @@ var init_summarize = __esm({
|
|
|
3509
3749
|
USER_MSG_LIMIT = 4e3;
|
|
3510
3750
|
AGENT_MSG_LIMIT = 6e3;
|
|
3511
3751
|
TRANSCRIPT_CAP = 1e5;
|
|
3752
|
+
SUMMARIZE_TIMEOUT_MS = 6e4;
|
|
3512
3753
|
SUMMARIZE_PROMPT = `You are summarizing a conversation session for long-term episodic memory. This summary will be injected into future conversations to provide context, so it must contain enough specific detail to be useful weeks or months later.
|
|
3513
3754
|
|
|
3514
3755
|
Instructions:
|
|
3515
3756
|
1. SUMMARY: Write a detailed summary (5-15 sentences depending on session complexity). Capture:
|
|
3516
3757
|
- What the user asked for and why
|
|
3517
3758
|
- What was done, decided, or accomplished
|
|
3518
|
-
- Specific details that would be hard to rediscover: names, dates, values, locations, references, sources, outcomes
|
|
3519
|
-
-
|
|
3759
|
+
- Specific details that would be hard to rediscover: names, dates, values, locations, references, sources, decisions, outcomes \u2014 across any domain
|
|
3760
|
+
- The state at session end: what's done, what's in progress, what's planned next
|
|
3520
3761
|
- Any preferences, corrections, or instructions the user expressed
|
|
3521
3762
|
|
|
3522
3763
|
2. KEY_DETAILS: List the 3-8 most important specific facts as bullet points. These should be concrete and retrievable \u2014 not vague.
|
|
3523
3764
|
Good: "User's quarterly report deadline is March 15"
|
|
3765
|
+
Good: "Flight booked to NYC on April 3, confirmation #AB1234"
|
|
3524
3766
|
Bad: "Discussed deadlines"
|
|
3525
3767
|
|
|
3526
3768
|
3. TOPICS: Extract 5-10 specific topic keywords that would help retrieve this session in a future search. Use proper nouns, specific terms, and distinguishing phrases \u2014 NOT generic words like "discussion" or "help".
|
|
@@ -3531,6 +3773,14 @@ KEY_DETAILS:
|
|
|
3531
3773
|
- <detail 1>
|
|
3532
3774
|
- <detail 2>
|
|
3533
3775
|
TOPICS: <specific-keyword1, specific-keyword2, ...>`;
|
|
3776
|
+
CONTEXT_BRIDGE_PROMPT = `You are handing off an active work session to a new AI assistant. Write a concise handoff brief (3-8 sentences) covering:
|
|
3777
|
+
- What the user is actively working on RIGHT NOW
|
|
3778
|
+
- Exact current state: what's done, what's in progress, what's blocked
|
|
3779
|
+
- Specific details needed to continue: names, dates, decisions, references, links, preferences, or any domain-specific context relevant to the work
|
|
3780
|
+
- The immediate next step the assistant should be ready to take
|
|
3781
|
+
- Any critical constraints or preferences expressed by the user
|
|
3782
|
+
|
|
3783
|
+
Be specific. The new assistant has no prior context. Cover any domain \u2014 personal, research, scheduling, content, technical \u2014 whatever applies to this session.`;
|
|
3534
3784
|
}
|
|
3535
3785
|
});
|
|
3536
3786
|
|
|
@@ -4448,7 +4698,7 @@ function startNextQueued(chatId) {
|
|
|
4448
4698
|
description: agent.description ?? void 0,
|
|
4449
4699
|
model: agent.model ?? void 0,
|
|
4450
4700
|
skills: JSON.parse(agent.skills),
|
|
4451
|
-
permMode: agent.permMode === "inherit" ?
|
|
4701
|
+
permMode: agent.permMode === "inherit" ? getMode(chatId) : agent.permMode,
|
|
4452
4702
|
role: agent.role ?? "worker",
|
|
4453
4703
|
persona: agent.persona ?? void 0,
|
|
4454
4704
|
maxRuntimeMs: agent.maxRuntimeMs,
|
|
@@ -4954,6 +5204,10 @@ function startDashboard() {
|
|
|
4954
5204
|
if (body.cwd && !existsSync8(body.cwd)) {
|
|
4955
5205
|
return jsonResponse(res, { error: `Directory not found: ${body.cwd}` }, 400);
|
|
4956
5206
|
}
|
|
5207
|
+
if (!body.permMode || body.permMode === "inherit") {
|
|
5208
|
+
const { getMode: getMode2 } = await Promise.resolve().then(() => (init_store4(), store_exports3));
|
|
5209
|
+
body.permMode = getMode2(body.chatId);
|
|
5210
|
+
}
|
|
4957
5211
|
const result = await spawnSubAgent(body.chatId, body);
|
|
4958
5212
|
return jsonResponse(res, result);
|
|
4959
5213
|
} catch (err) {
|
|
@@ -5206,7 +5460,10 @@ function startDashboard() {
|
|
|
5206
5460
|
const { getMode: getMode2, getCwd: getCwd2, getModel: getModel2, addUsage: addUsage2, getBackend: getBackend2 } = await Promise.resolve().then(() => (init_store4(), store_exports3));
|
|
5207
5461
|
const { getAdapterForChat: getAdapterForChat2 } = await Promise.resolve().then(() => (init_backends(), backends_exports));
|
|
5208
5462
|
const chatId = body.chatId;
|
|
5209
|
-
const
|
|
5463
|
+
const PERM_LEVEL = { plan: 0, safe: 1, yolo: 2 };
|
|
5464
|
+
const storedMode = getMode2(chatId);
|
|
5465
|
+
const requestedMode = body.mode ?? storedMode;
|
|
5466
|
+
const mode = (PERM_LEVEL[requestedMode] ?? 2) <= (PERM_LEVEL[storedMode] ?? 2) ? requestedMode : storedMode;
|
|
5210
5467
|
const cwd = body.cwd ?? getCwd2(chatId);
|
|
5211
5468
|
const model2 = body.model ?? getModel2(chatId) ?? (() => {
|
|
5212
5469
|
try {
|
|
@@ -5858,11 +6115,11 @@ function askAgent(chatId, userMessage, opts) {
|
|
|
5858
6115
|
async function askAgentImpl(chatId, userMessage, opts) {
|
|
5859
6116
|
const { cwd, onStream, model: model2, backend: backend2, permMode, onToolAction, bootstrapTier, timeoutMs } = opts ?? {};
|
|
5860
6117
|
const adapter = backend2 ? getAdapter(backend2) : getAdapterForChat(chatId);
|
|
5861
|
-
const mode = permMode ??
|
|
6118
|
+
const mode = permMode ?? getMode(chatId);
|
|
5862
6119
|
const thinkingLevel = getThinkingLevel(chatId);
|
|
5863
6120
|
const resolvedCwd = cwd ?? WORKSPACE_PATH;
|
|
5864
6121
|
const tier = bootstrapTier ?? "full";
|
|
5865
|
-
const fullPrompt = await assembleBootstrapPrompt(userMessage, tier, chatId);
|
|
6122
|
+
const fullPrompt = await assembleBootstrapPrompt(userMessage, tier, chatId, mode);
|
|
5866
6123
|
const existingSessionId = getSessionId(chatId);
|
|
5867
6124
|
const allowedTools = getEnabledTools(chatId);
|
|
5868
6125
|
const mcpConfigPath = getMcpConfigPath(chatId);
|
|
@@ -5923,12 +6180,14 @@ async function askAgentImpl(chatId, userMessage, opts) {
|
|
|
5923
6180
|
clearSession(chatId);
|
|
5924
6181
|
}
|
|
5925
6182
|
if (result.resultText) {
|
|
5926
|
-
appendToLog(chatId, userMessage, result.resultText);
|
|
6183
|
+
appendToLog(chatId, userMessage, result.resultText, adapter.id, model2 ?? null, result.sessionId ?? null);
|
|
5927
6184
|
const AUTO_SUMMARIZE_THRESHOLD = 30;
|
|
5928
6185
|
const pairCount = getMessagePairCount(chatId);
|
|
5929
6186
|
if (pairCount >= AUTO_SUMMARIZE_THRESHOLD) {
|
|
5930
6187
|
log(`[agent] Auto-summarizing chat ${chatId} after ${pairCount} turns`);
|
|
5931
|
-
|
|
6188
|
+
summarizeWithFallbackChain(chatId).then((saved) => {
|
|
6189
|
+
if (saved) opts?.onCompaction?.(chatId);
|
|
6190
|
+
}).catch((err) => {
|
|
5932
6191
|
warn(`[agent] Auto-summarize failed for chat ${chatId}: ${err}`);
|
|
5933
6192
|
});
|
|
5934
6193
|
}
|
|
@@ -6488,7 +6747,8 @@ async function runWithRetry(job, model2, runId, t0) {
|
|
|
6488
6747
|
model: currentModel,
|
|
6489
6748
|
backend: currentBackend,
|
|
6490
6749
|
bootstrapTier: "slim",
|
|
6491
|
-
timeoutMs
|
|
6750
|
+
timeoutMs,
|
|
6751
|
+
permMode: job.sessionType === "main" ? getMode(job.chatId) : "yolo"
|
|
6492
6752
|
});
|
|
6493
6753
|
if (isFallback) {
|
|
6494
6754
|
response.text = `[Fallback: ran on ${currentBackend}:${currentModel}]
|
|
@@ -6616,7 +6876,7 @@ function wrapBackendAdapter(adapter) {
|
|
|
6616
6876
|
const config2 = adapter.buildSpawnConfig({
|
|
6617
6877
|
prompt: opts.prompt,
|
|
6618
6878
|
sessionId: opts.sessionId,
|
|
6619
|
-
permMode: opts.permMode ?? "
|
|
6879
|
+
permMode: opts.permMode ?? "plan",
|
|
6620
6880
|
allowedTools: opts.allowedTools ?? [],
|
|
6621
6881
|
cwd: opts.cwd
|
|
6622
6882
|
});
|
|
@@ -8063,14 +8323,6 @@ var init_heartbeat = __esm({
|
|
|
8063
8323
|
});
|
|
8064
8324
|
|
|
8065
8325
|
// src/format-time.ts
|
|
8066
|
-
function formatLocalDate(utcDatetime) {
|
|
8067
|
-
const d = parseUtcDatetime(utcDatetime);
|
|
8068
|
-
if (!d) return utcDatetime.split("T")[0] ?? utcDatetime.split(" ")[0];
|
|
8069
|
-
const year = d.getFullYear();
|
|
8070
|
-
const month = String(d.getMonth() + 1).padStart(2, "0");
|
|
8071
|
-
const day = String(d.getDate()).padStart(2, "0");
|
|
8072
|
-
return `${year}-${month}-${day}`;
|
|
8073
|
-
}
|
|
8074
8326
|
function formatLocalDateTime(utcDatetime) {
|
|
8075
8327
|
const d = parseUtcDatetime(utcDatetime);
|
|
8076
8328
|
if (!d) return utcDatetime;
|
|
@@ -9253,7 +9505,7 @@ async function handleCommand(msg, channel) {
|
|
|
9253
9505
|
case "help":
|
|
9254
9506
|
await channel.sendText(
|
|
9255
9507
|
chatId,
|
|
9256
|
-
"Hey! I'm CC-Claw \u2014 your personal AI assistant on Telegram.\n\nI use AI coding CLIs (Claude, Gemini, Codex) as my brain. Just send me a message to get started.\n\nCommands:\n/backend [name] - Switch AI backend (or /claude /gemini /codex)\n/model - Switch model for active backend\n/summarizer - Configure session summarization model\n/status - Show session, model, backend, and usage\n/cost - Show estimated API cost (use /cost all for all-time)\n/usage - Show usage per backend with limits\n/limits - Configure usage limits per backend\n/newchat - Start a fresh conversation\n/summarize - Save session to memory (without resetting)\n/summarize all - Summarize all pending sessions (pre-restart)\n/cwd <path> - Set working directory\n/cwd - Show current working directory\n/memory - List stored memories\n/forget <keyword> - Remove a memory\n/voice - Toggle voice responses\n/imagine <prompt> - Generate an image (or /image)\n/cron <description> - Schedule a task (or /schedule)\n/cron - List scheduled jobs (or /jobs)\n/cron cancel <id> - Cancel a job\n/cron pause <id> - Pause a job\n/cron resume <id> - Resume a job\n/cron run <id> - Trigger a job now\n/cron runs [id] - View run history\n/cron edit <id> - Edit a job\n/cron health - Scheduler health\n/skills - List skills from all backends\n/skill-install <url> - Install a skill from GitHub\n/setup-profile - Set up your user profile\n/chats - List authorized chats and aliases\n/heartbeat - Proactive awareness (on/off/interval/hours)\n/history - List recent session summaries\n/stop - Cancel the current running task\n/tools - Configure which tools the agent can use\n/permissions - Switch permission mode (yolo/safe/
|
|
9508
|
+
"Hey! I'm CC-Claw \u2014 your personal AI assistant on Telegram.\n\nI use AI coding CLIs (Claude, Gemini, Codex) as my brain. Just send me a message to get started.\n\nCommands:\n/backend [name] - Switch AI backend (or /claude /gemini /codex)\n/model - Switch model for active backend\n/summarizer - Configure session summarization model\n/status - Show session, model, backend, and usage\n/cost - Show estimated API cost (use /cost all for all-time)\n/usage - Show usage per backend with limits\n/limits - Configure usage limits per backend\n/newchat - Start a fresh conversation\n/summarize - Save session to memory (without resetting)\n/summarize all - Summarize all pending sessions (pre-restart)\n/cwd <path> - Set working directory\n/cwd - Show current working directory\n/memory - List stored memories\n/forget <keyword> - Remove a memory\n/voice - Toggle voice responses\n/imagine <prompt> - Generate an image (or /image)\n/cron <description> - Schedule a task (or /schedule)\n/cron - List scheduled jobs (or /jobs)\n/cron cancel <id> - Cancel a job\n/cron pause <id> - Pause a job\n/cron resume <id> - Resume a job\n/cron run <id> - Trigger a job now\n/cron runs [id] - View run history\n/cron edit <id> - Edit a job\n/cron health - Scheduler health\n/skills - List skills from all backends\n/skill-install <url> - Install a skill from GitHub\n/setup-profile - Set up your user profile\n/chats - List authorized chats and aliases\n/heartbeat - Proactive awareness (on/off/interval/hours)\n/history - List recent session summaries\n/stop - Cancel the current running task\n/tools - Configure which tools the agent can use\n/permissions - Switch permission mode (yolo/safe/plan)\n/verbose - Tool visibility (off/normal/verbose)\n/agents - List active sub-agents\n/tasks - Show task board for current orchestration\n/stopagent <id> - Cancel a specific sub-agent\n/stopall - Cancel all sub-agents in this chat\n/runners - List registered CLI runners\n/mcps - List registered MCP servers\n/help - Show this message",
|
|
9257
9509
|
"plain"
|
|
9258
9510
|
);
|
|
9259
9511
|
break;
|
|
@@ -9313,7 +9565,7 @@ async function handleCommand(msg, channel) {
|
|
|
9313
9565
|
toolButtons.push(row);
|
|
9314
9566
|
}
|
|
9315
9567
|
toolButtons.push([{ label: "Reset to defaults (all on)", data: "tool:reset" }]);
|
|
9316
|
-
const modeNote = currentMode === "
|
|
9568
|
+
const modeNote = currentMode === "plan" ? "\n\nNote: In plan mode, tool list is ignored (read-only)." : currentMode === "yolo" ? "\n\nNote: In YOLO mode, tool list is ignored (all tools allowed)." : "";
|
|
9317
9569
|
await channel.sendKeyboard(
|
|
9318
9570
|
chatId,
|
|
9319
9571
|
`Configure allowed tools (mode: ${currentMode})${modeNote}
|
|
@@ -9349,7 +9601,15 @@ Tap to toggle:`,
|
|
|
9349
9601
|
} else {
|
|
9350
9602
|
const pairs = getMessagePairCount(chatId);
|
|
9351
9603
|
if (pairs < 2) {
|
|
9352
|
-
|
|
9604
|
+
if (getSessionId(chatId)) {
|
|
9605
|
+
await channel.sendText(
|
|
9606
|
+
chatId,
|
|
9607
|
+
"Session log was cleared (auto-compact or service restart). Your conversation history is preserved in episodic memory. Continue chatting normally, or use /newchat to start fresh.",
|
|
9608
|
+
"plain"
|
|
9609
|
+
);
|
|
9610
|
+
} else {
|
|
9611
|
+
await channel.sendText(chatId, "Not enough conversation to summarize (need at least 2 exchanges).", "plain");
|
|
9612
|
+
}
|
|
9353
9613
|
break;
|
|
9354
9614
|
}
|
|
9355
9615
|
await channel.sendText(chatId, `Summarizing current session (${pairs} exchanges)...`, "plain");
|
|
@@ -9882,20 +10142,38 @@ ${lines.join("\n")}`, "plain");
|
|
|
9882
10142
|
break;
|
|
9883
10143
|
}
|
|
9884
10144
|
case "history": {
|
|
9885
|
-
const
|
|
9886
|
-
|
|
9887
|
-
|
|
9888
|
-
|
|
9889
|
-
|
|
10145
|
+
const query = commandArgs?.trim() ?? "";
|
|
10146
|
+
if (query) {
|
|
10147
|
+
const results = searchMessageLog(chatId, query, 20);
|
|
10148
|
+
if (results.length === 0) {
|
|
10149
|
+
await channel.sendText(chatId, `No matching history found. Try /memories ${query} for older sessions.`, "plain");
|
|
10150
|
+
break;
|
|
10151
|
+
}
|
|
10152
|
+
const lines = [`\u{1F4CB} History: "${query}"`, "\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501"];
|
|
10153
|
+
for (const row of results.slice(0, 10)) {
|
|
10154
|
+
const roleLabel = row.role === "user" ? "You" : "Assistant";
|
|
10155
|
+
const date = row.created_at.slice(0, 10);
|
|
10156
|
+
const preview = row.content.slice(0, 300) + (row.content.length > 300 ? "\u2026" : "");
|
|
10157
|
+
lines.push(`[${row.backend ?? "?"} \xB7 ${date}] ${roleLabel}: ${preview}`);
|
|
10158
|
+
}
|
|
10159
|
+
lines.push("\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501");
|
|
10160
|
+
await channel.sendText(chatId, lines.join("\n"), "plain");
|
|
10161
|
+
} else {
|
|
10162
|
+
const rows = getRecentMessageLog(chatId, 20).reverse();
|
|
10163
|
+
if (rows.length === 0) {
|
|
10164
|
+
await channel.sendText(chatId, "No conversation history found.", "plain");
|
|
10165
|
+
break;
|
|
10166
|
+
}
|
|
10167
|
+
const lines = ["\u{1F4CB} Recent conversation (last 10 exchanges):", "\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501"];
|
|
10168
|
+
for (const row of rows) {
|
|
10169
|
+
const roleLabel = row.role === "user" ? "You" : "Assistant";
|
|
10170
|
+
const date = row.created_at.slice(0, 10);
|
|
10171
|
+
const preview = row.content.slice(0, 300) + (row.content.length > 300 ? "\u2026" : "");
|
|
10172
|
+
lines.push(`[${row.backend ?? "?"} \xB7 ${date}] ${roleLabel}: ${preview}`);
|
|
10173
|
+
}
|
|
10174
|
+
lines.push("\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501");
|
|
10175
|
+
await channel.sendText(chatId, lines.join("\n"), "plain");
|
|
9890
10176
|
}
|
|
9891
|
-
const lines = summaries.map((s) => {
|
|
9892
|
-
const date = formatLocalDate(s.created_at);
|
|
9893
|
-
const shortSummary = s.summary.length > 200 ? s.summary.slice(0, 200) + "\u2026" : s.summary;
|
|
9894
|
-
return `${date} (${s.message_count} msgs)
|
|
9895
|
-
${shortSummary}
|
|
9896
|
-
Topics: ${s.topics}`;
|
|
9897
|
-
});
|
|
9898
|
-
await channel.sendText(chatId, lines.join("\n\n"), "plain");
|
|
9899
10177
|
break;
|
|
9900
10178
|
}
|
|
9901
10179
|
case "skills": {
|
|
@@ -10516,7 +10794,7 @@ async function handleText(msg, channel) {
|
|
|
10516
10794
|
await channel.sendText(chatId, limitMsg, "plain");
|
|
10517
10795
|
return;
|
|
10518
10796
|
}
|
|
10519
|
-
if (isChatBusy(chatId)) {
|
|
10797
|
+
if (isChatBusy(chatId) && !bypassBusyCheck.delete(chatId)) {
|
|
10520
10798
|
if (typeof channel.sendKeyboard === "function") {
|
|
10521
10799
|
pendingInterrupts.set(chatId, { msg, channel });
|
|
10522
10800
|
await channel.sendKeyboard(chatId, "\u23F3 Agent is working on a request\u2026", [
|
|
@@ -10544,8 +10822,29 @@ async function handleText(msg, channel) {
|
|
|
10544
10822
|
const tMode = getMode(chatId);
|
|
10545
10823
|
const tVerbose = getVerboseLevel(chatId);
|
|
10546
10824
|
const tToolCb = tVerbose !== "off" ? makeToolActionCallback(chatId, channel, tVerbose) : void 0;
|
|
10547
|
-
const response = await askAgent(chatId, text, {
|
|
10825
|
+
const response = await askAgent(chatId, text, {
|
|
10826
|
+
cwd: getCwd(chatId),
|
|
10827
|
+
model: model2,
|
|
10828
|
+
permMode: tMode,
|
|
10829
|
+
onToolAction: tToolCb,
|
|
10830
|
+
onCompaction: (cid) => {
|
|
10831
|
+
channel.sendText(cid, "\u{1F4BE} Context saved to memory.").catch(() => {
|
|
10832
|
+
});
|
|
10833
|
+
}
|
|
10834
|
+
});
|
|
10548
10835
|
if (response.usage) addUsage(chatId, response.usage.input, response.usage.output, response.usage.cacheRead, model2);
|
|
10836
|
+
if (response.text.includes("[NEED_PERMISSION]") && tMode !== "yolo") {
|
|
10837
|
+
const cleanText = response.text.replace(/\[NEED_PERMISSION\]/g, "").trim();
|
|
10838
|
+
await sendResponse(chatId, channel, cleanText, msg.messageId);
|
|
10839
|
+
const targetMode = determineEscalationTarget(chatId, tMode);
|
|
10840
|
+
storePendingEscalation(chatId, text);
|
|
10841
|
+
if (typeof channel.sendKeyboard === "function") {
|
|
10842
|
+
await channel.sendKeyboard(chatId, `Switch to ${targetMode} mode?`, [
|
|
10843
|
+
[{ label: "Allow", data: `perm:escalate:${targetMode}` }, { label: "Deny", data: "perm:deny" }]
|
|
10844
|
+
]);
|
|
10845
|
+
}
|
|
10846
|
+
return;
|
|
10847
|
+
}
|
|
10549
10848
|
await sendResponse(chatId, channel, response.text, msg.messageId);
|
|
10550
10849
|
} catch (err) {
|
|
10551
10850
|
error("[router] Error:", err);
|
|
@@ -10731,7 +11030,28 @@ async function sendResponse(chatId, channel, text, messageId) {
|
|
|
10731
11030
|
]);
|
|
10732
11031
|
}
|
|
10733
11032
|
}
|
|
10734
|
-
|
|
11033
|
+
let afterHistory = afterUpdates;
|
|
11034
|
+
const historySearchMatch = afterHistory.match(/\[HISTORY_SEARCH:([^\]]+)\]/);
|
|
11035
|
+
if (historySearchMatch) {
|
|
11036
|
+
afterHistory = afterHistory.replace(/\[HISTORY_SEARCH:[^\]]+\]/g, "").trim();
|
|
11037
|
+
const hsQuery = historySearchMatch[1].trim();
|
|
11038
|
+
const hsResults = searchMessageLog(chatId, hsQuery, 10);
|
|
11039
|
+
if (hsResults.length > 0) {
|
|
11040
|
+
const hsLines = [`\u{1F4CB} History: "${hsQuery}"`, "\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501"];
|
|
11041
|
+
for (const row of hsResults.slice(0, 8)) {
|
|
11042
|
+
const label2 = row.role === "user" ? "You" : "Assistant";
|
|
11043
|
+
const date = row.created_at.slice(0, 10);
|
|
11044
|
+
hsLines.push(`[${row.backend ?? "?"} \xB7 ${date}] ${label2}: ${row.content.slice(0, 250)}${row.content.length > 250 ? "\u2026" : ""}`);
|
|
11045
|
+
}
|
|
11046
|
+
hsLines.push("\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501");
|
|
11047
|
+
channel.sendText(chatId, hsLines.join("\n")).catch(() => {
|
|
11048
|
+
});
|
|
11049
|
+
} else {
|
|
11050
|
+
channel.sendText(chatId, `No history found for "${hsQuery}". Try /memories ${hsQuery} for older sessions.`).catch(() => {
|
|
11051
|
+
});
|
|
11052
|
+
}
|
|
11053
|
+
}
|
|
11054
|
+
const afterFiles = await processFileSends2(chatId, channel, afterHistory);
|
|
10735
11055
|
const cleanText = await processImageGenerations(chatId, channel, afterFiles);
|
|
10736
11056
|
if (!cleanText) return;
|
|
10737
11057
|
if (isVoiceEnabled(chatId)) {
|
|
@@ -10757,16 +11077,17 @@ async function sendBackendSwitchConfirmation(chatId, target, channel) {
|
|
|
10757
11077
|
await channel.sendText(chatId, `Already using ${targetAdapter.displayName}.`, "plain");
|
|
10758
11078
|
return;
|
|
10759
11079
|
}
|
|
10760
|
-
const currentLabel = current ? getAdapter(current).displayName : "current backend";
|
|
10761
11080
|
if (typeof channel.sendKeyboard === "function") {
|
|
10762
11081
|
await channel.sendKeyboard(
|
|
10763
11082
|
chatId,
|
|
10764
|
-
`\
|
|
10765
|
-
|
|
10766
|
-
What would you like to do?`,
|
|
11083
|
+
`\u{1F504} Switch to ${targetAdapter.displayName}?
|
|
11084
|
+
Your conversation history is preserved. ${targetAdapter.displayName} will receive a summary of recent context and can access your full history on request.`,
|
|
10767
11085
|
[
|
|
10768
|
-
[
|
|
10769
|
-
|
|
11086
|
+
[
|
|
11087
|
+
{ label: `Switch to ${targetAdapter.displayName}`, data: `backend_confirm:${target}` },
|
|
11088
|
+
{ label: `Switch + Clear History`, data: `backend_confirm_clear:${target}` }
|
|
11089
|
+
],
|
|
11090
|
+
[{ label: "Cancel", data: `backend_cancel:${target}` }]
|
|
10770
11091
|
]
|
|
10771
11092
|
);
|
|
10772
11093
|
} else {
|
|
@@ -10774,21 +11095,21 @@ What would you like to do?`,
|
|
|
10774
11095
|
}
|
|
10775
11096
|
}
|
|
10776
11097
|
async function doBackendSwitch(chatId, backendId, channel) {
|
|
10777
|
-
|
|
10778
|
-
|
|
11098
|
+
const targetAdapter = getAdapter(backendId);
|
|
11099
|
+
const summarized = await summarizeWithFallbackChain(chatId, backendId);
|
|
11100
|
+
if (summarized) {
|
|
11101
|
+
await channel.sendText(chatId, `\u{1F4BE} Context saved \u2014 ${targetAdapter.displayName} summarized your session.`, "plain");
|
|
11102
|
+
}
|
|
11103
|
+
const bridge = buildContextBridge(chatId);
|
|
11104
|
+
if (bridge) {
|
|
11105
|
+
setPendingContextBridge(chatId, bridge);
|
|
11106
|
+
}
|
|
10779
11107
|
clearSession(chatId);
|
|
10780
11108
|
clearModel(chatId);
|
|
10781
11109
|
clearThinkingLevel(chatId);
|
|
10782
11110
|
setBackend(chatId, backendId);
|
|
10783
|
-
|
|
10784
|
-
|
|
10785
|
-
await channel.sendText(
|
|
10786
|
-
chatId,
|
|
10787
|
-
`Backend switched to ${adapter.displayName}.
|
|
10788
|
-
Default model: ${adapter.defaultModel}
|
|
10789
|
-
Session reset. Ready!`,
|
|
10790
|
-
"plain"
|
|
10791
|
-
);
|
|
11111
|
+
logActivity(getDb(), { chatId, source: "telegram", eventType: "config_changed", summary: `Backend switched to ${targetAdapter.displayName}`, detail: { field: "backend", value: backendId } });
|
|
11112
|
+
await channel.sendText(chatId, `\u2705 Switched to ${targetAdapter.displayName}. Ready!`, "plain");
|
|
10792
11113
|
}
|
|
10793
11114
|
async function handleCallback(chatId, data, channel) {
|
|
10794
11115
|
if (data.startsWith("backend:")) {
|
|
@@ -10801,14 +11122,17 @@ async function handleCallback(chatId, data, channel) {
|
|
|
10801
11122
|
return;
|
|
10802
11123
|
}
|
|
10803
11124
|
await sendBackendSwitchConfirmation(chatId, chosen, channel);
|
|
11125
|
+
} else if (data.startsWith("backend_confirm_clear:")) {
|
|
11126
|
+
const target = data.slice(21);
|
|
11127
|
+
if (!getAllBackendIds().includes(target)) return;
|
|
11128
|
+
clearMessageLog(chatId);
|
|
11129
|
+
await doBackendSwitch(chatId, target, channel);
|
|
10804
11130
|
} else if (data.startsWith("backend_confirm:")) {
|
|
10805
11131
|
const chosen = data.slice(16);
|
|
10806
11132
|
if (!getAllBackendIds().includes(chosen)) return;
|
|
10807
11133
|
await doBackendSwitch(chatId, chosen, channel);
|
|
10808
|
-
} else if (data === "backend_cancel") {
|
|
10809
|
-
|
|
10810
|
-
const label2 = current ? getAdapter(current).displayName : "current backend";
|
|
10811
|
-
await channel.sendText(chatId, `No change. Staying on ${label2}.`, "plain");
|
|
11134
|
+
} else if (data.startsWith("backend_cancel:") || data === "backend_cancel") {
|
|
11135
|
+
await channel.sendText(chatId, "Switch cancelled.", "plain");
|
|
10812
11136
|
} else if (data.startsWith("model:")) {
|
|
10813
11137
|
const chosen = data.slice(6);
|
|
10814
11138
|
let adapter;
|
|
@@ -10863,7 +11187,8 @@ Select thinking/effort level:`,
|
|
|
10863
11187
|
await channel.sendText(chatId, `Summarizer pinned to ${bk}:${mdl ?? "default"}.`, "plain");
|
|
10864
11188
|
}
|
|
10865
11189
|
} else if (data.startsWith("perms:")) {
|
|
10866
|
-
|
|
11190
|
+
let chosen = data.slice(6);
|
|
11191
|
+
if (chosen === "readonly") chosen = "plan";
|
|
10867
11192
|
if (!PERM_MODES[chosen]) return;
|
|
10868
11193
|
const previous = getMode(chatId);
|
|
10869
11194
|
if (chosen === previous) {
|
|
@@ -10878,6 +11203,20 @@ Select thinking/effort level:`,
|
|
|
10878
11203
|
${PERM_MODES[chosen]}`,
|
|
10879
11204
|
"plain"
|
|
10880
11205
|
);
|
|
11206
|
+
} else if (data.startsWith("perm:escalate:")) {
|
|
11207
|
+
const targetMode = data.slice(14);
|
|
11208
|
+
if (!PERM_MODES[targetMode]) return;
|
|
11209
|
+
setMode(chatId, targetMode);
|
|
11210
|
+
logActivity(getDb(), { chatId, source: "telegram", eventType: "config_changed", summary: `Permission escalated to ${targetMode}`, detail: { field: "permissions", value: targetMode } });
|
|
11211
|
+
await channel.sendText(chatId, `Switched to ${targetMode} mode. Re-processing your request...`, "plain");
|
|
11212
|
+
const pending = getPendingEscalation(chatId);
|
|
11213
|
+
if (pending) {
|
|
11214
|
+
removePendingEscalation(chatId);
|
|
11215
|
+
await handleMessage({ text: pending, chatId, source: "telegram" }, channel);
|
|
11216
|
+
}
|
|
11217
|
+
} else if (data === "perm:deny") {
|
|
11218
|
+
removePendingEscalation(chatId);
|
|
11219
|
+
await channel.sendText(chatId, "Keeping current mode.", "plain");
|
|
10881
11220
|
} else if (data.startsWith("verbose:")) {
|
|
10882
11221
|
const chosen = data.slice(8);
|
|
10883
11222
|
if (!VERBOSE_LEVELS[chosen]) return;
|
|
@@ -10965,6 +11304,7 @@ ${PERM_MODES[chosen]}`,
|
|
|
10965
11304
|
await handleMessage(pending.msg, pending.channel);
|
|
10966
11305
|
} else if (action === "queue" && pending) {
|
|
10967
11306
|
pendingInterrupts.delete(targetChatId);
|
|
11307
|
+
bypassBusyCheck.add(targetChatId);
|
|
10968
11308
|
await channel.sendText(chatId, "\u{1F4E5} Message queued \u2014 will process after current task.", "plain");
|
|
10969
11309
|
handleMessage(pending.msg, pending.channel).catch(() => {
|
|
10970
11310
|
});
|
|
@@ -10981,8 +11321,9 @@ ${PERM_MODES[chosen]}`,
|
|
|
10981
11321
|
await channel.sendText(chatId, "OK \u2014 you can switch manually with /backend when ready.", "plain");
|
|
10982
11322
|
} else if (pendingMsg) {
|
|
10983
11323
|
pendingFallbackMessages.delete(targetChatId);
|
|
10984
|
-
|
|
10985
|
-
|
|
11324
|
+
await summarizeWithFallbackChain(targetChatId, targetBackend);
|
|
11325
|
+
const bridge = buildContextBridge(targetChatId);
|
|
11326
|
+
if (bridge) setPendingContextBridge(targetChatId, bridge);
|
|
10986
11327
|
clearSession(targetChatId);
|
|
10987
11328
|
setBackend(targetChatId, targetBackend);
|
|
10988
11329
|
const adapter = getAdapter(targetBackend);
|
|
@@ -11202,7 +11543,7 @@ Use /skills <page> to navigate (e.g. /skills 2)` : "";
|
|
|
11202
11543
|
const header2 = totalPages > 1 ? `${skills2.length} skills (page ${safePage}/${totalPages}). Select one to invoke:` : `${skills2.length} skills available. Select one to invoke:`;
|
|
11203
11544
|
await channel.sendKeyboard(chatId, header2, buttons);
|
|
11204
11545
|
}
|
|
11205
|
-
var PERM_MODES, VERBOSE_LEVELS, pendingInterrupts, pendingFallbackMessages, CLI_INSTALL_HINTS, BLOCKED_PATH_PATTERNS2, ALLOWED_REACTION_EMOJIS, SKILLS_PER_PAGE;
|
|
11546
|
+
var PERM_MODES, VERBOSE_LEVELS, pendingInterrupts, bypassBusyCheck, pendingFallbackMessages, CLI_INSTALL_HINTS, BLOCKED_PATH_PATTERNS2, ALLOWED_REACTION_EMOJIS, SKILLS_PER_PAGE;
|
|
11206
11547
|
var init_router = __esm({
|
|
11207
11548
|
"src/router.ts"() {
|
|
11208
11549
|
"use strict";
|
|
@@ -11218,6 +11559,8 @@ var init_router = __esm({
|
|
|
11218
11559
|
init_stt();
|
|
11219
11560
|
init_store4();
|
|
11220
11561
|
init_summarize();
|
|
11562
|
+
init_inject();
|
|
11563
|
+
init_store4();
|
|
11221
11564
|
init_session_log();
|
|
11222
11565
|
init_backends();
|
|
11223
11566
|
init_cron();
|
|
@@ -11235,8 +11578,7 @@ var init_router = __esm({
|
|
|
11235
11578
|
PERM_MODES = {
|
|
11236
11579
|
yolo: "YOLO \u2014 all tools, full autopilot",
|
|
11237
11580
|
safe: "Safe \u2014 only my allowed tools",
|
|
11238
|
-
|
|
11239
|
-
plan: "Plan \u2014 plan only, no execution"
|
|
11581
|
+
plan: "Plan \u2014 read and analyze only"
|
|
11240
11582
|
};
|
|
11241
11583
|
VERBOSE_LEVELS = {
|
|
11242
11584
|
off: "Off \u2014 no tool visibility",
|
|
@@ -11244,6 +11586,7 @@ var init_router = __esm({
|
|
|
11244
11586
|
verbose: "Verbose \u2014 full details"
|
|
11245
11587
|
};
|
|
11246
11588
|
pendingInterrupts = /* @__PURE__ */ new Map();
|
|
11589
|
+
bypassBusyCheck = /* @__PURE__ */ new Set();
|
|
11247
11590
|
pendingFallbackMessages = /* @__PURE__ */ new Map();
|
|
11248
11591
|
CLI_INSTALL_HINTS = {
|
|
11249
11592
|
claude: "Install: npm install -g @anthropic-ai/claude-code",
|
|
@@ -11980,7 +12323,7 @@ var index_exports = {};
|
|
|
11980
12323
|
__export(index_exports, {
|
|
11981
12324
|
main: () => main
|
|
11982
12325
|
});
|
|
11983
|
-
import { mkdirSync as mkdirSync7, existsSync as existsSync16, renameSync, statSync as statSync2 } from "fs";
|
|
12326
|
+
import { mkdirSync as mkdirSync7, existsSync as existsSync16, renameSync, statSync as statSync2, readFileSync as readFileSync10 } from "fs";
|
|
11984
12327
|
import { join as join17 } from "path";
|
|
11985
12328
|
import dotenv from "dotenv";
|
|
11986
12329
|
function migrateLayout() {
|
|
@@ -12020,14 +12363,21 @@ function rotateLogs() {
|
|
|
12020
12363
|
}
|
|
12021
12364
|
async function main() {
|
|
12022
12365
|
rotateLogs();
|
|
12023
|
-
|
|
12366
|
+
let version = "unknown";
|
|
12367
|
+
try {
|
|
12368
|
+
const pkgPath = new URL("../package.json", import.meta.url);
|
|
12369
|
+
version = JSON.parse(readFileSync10(pkgPath, "utf-8")).version;
|
|
12370
|
+
} catch {
|
|
12371
|
+
}
|
|
12372
|
+
log(`[cc-claw] Starting v${version}`);
|
|
12024
12373
|
initDatabase();
|
|
12374
|
+
pruneMessageLog(30, 2e3);
|
|
12025
12375
|
bootstrapBuiltinMcps(getDb());
|
|
12026
|
-
const
|
|
12376
|
+
const SUMMARIZE_TIMEOUT_MS2 = 3e4;
|
|
12027
12377
|
try {
|
|
12028
12378
|
let timer;
|
|
12029
12379
|
const timeoutPromise = new Promise((_, reject) => {
|
|
12030
|
-
timer = setTimeout(() => reject(new Error("timeout")),
|
|
12380
|
+
timer = setTimeout(() => reject(new Error("timeout")), SUMMARIZE_TIMEOUT_MS2);
|
|
12031
12381
|
});
|
|
12032
12382
|
try {
|
|
12033
12383
|
await Promise.race([
|
|
@@ -12553,13 +12903,13 @@ var init_daemon = __esm({
|
|
|
12553
12903
|
});
|
|
12554
12904
|
|
|
12555
12905
|
// src/cli/resolve-chat.ts
|
|
12556
|
-
import { readFileSync as
|
|
12906
|
+
import { readFileSync as readFileSync12 } from "fs";
|
|
12557
12907
|
function resolveChatId(globalOpts) {
|
|
12558
12908
|
const explicit = globalOpts.chat;
|
|
12559
12909
|
if (explicit) return explicit;
|
|
12560
12910
|
if (_cachedDefault) return _cachedDefault;
|
|
12561
12911
|
try {
|
|
12562
|
-
const content =
|
|
12912
|
+
const content = readFileSync12(ENV_PATH, "utf-8");
|
|
12563
12913
|
const match = content.match(/^ALLOWED_CHAT_ID=(.+)$/m);
|
|
12564
12914
|
if (match) {
|
|
12565
12915
|
_cachedDefault = match[1].split(",")[0].trim();
|
|
@@ -12585,12 +12935,12 @@ __export(api_client_exports, {
|
|
|
12585
12935
|
apiPost: () => apiPost,
|
|
12586
12936
|
isDaemonRunning: () => isDaemonRunning
|
|
12587
12937
|
});
|
|
12588
|
-
import { readFileSync as
|
|
12938
|
+
import { readFileSync as readFileSync13, existsSync as existsSync18 } from "fs";
|
|
12589
12939
|
import { request as httpRequest } from "http";
|
|
12590
12940
|
function getToken() {
|
|
12591
12941
|
if (process.env.CC_CLAW_API_TOKEN) return process.env.CC_CLAW_API_TOKEN;
|
|
12592
12942
|
try {
|
|
12593
|
-
if (existsSync18(TOKEN_PATH)) return
|
|
12943
|
+
if (existsSync18(TOKEN_PATH)) return readFileSync13(TOKEN_PATH, "utf-8").trim();
|
|
12594
12944
|
} catch {
|
|
12595
12945
|
}
|
|
12596
12946
|
return null;
|
|
@@ -12915,8 +13265,8 @@ async function doctorCommand(globalOpts, localOpts) {
|
|
|
12915
13265
|
}
|
|
12916
13266
|
if (existsSync20(ERROR_LOG_PATH)) {
|
|
12917
13267
|
try {
|
|
12918
|
-
const { readFileSync:
|
|
12919
|
-
const logContent =
|
|
13268
|
+
const { readFileSync: readFileSync18 } = await import("fs");
|
|
13269
|
+
const logContent = readFileSync18(ERROR_LOG_PATH, "utf-8");
|
|
12920
13270
|
const recentLines = logContent.split("\n").filter(Boolean).slice(-100);
|
|
12921
13271
|
const last24h = Date.now() - 864e5;
|
|
12922
13272
|
const recentErrors = recentLines.filter((line) => {
|
|
@@ -13032,7 +13382,7 @@ var logs_exports = {};
|
|
|
13032
13382
|
__export(logs_exports, {
|
|
13033
13383
|
logsCommand: () => logsCommand
|
|
13034
13384
|
});
|
|
13035
|
-
import { existsSync as existsSync21, readFileSync as
|
|
13385
|
+
import { existsSync as existsSync21, readFileSync as readFileSync14, watchFile as watchFile2, unwatchFile as unwatchFile2 } from "fs";
|
|
13036
13386
|
async function logsCommand(opts) {
|
|
13037
13387
|
const logFile = opts.error ? ERROR_LOG_PATH : LOG_PATH;
|
|
13038
13388
|
if (!existsSync21(logFile)) {
|
|
@@ -13040,7 +13390,7 @@ async function logsCommand(opts) {
|
|
|
13040
13390
|
process.exit(1);
|
|
13041
13391
|
}
|
|
13042
13392
|
const maxLines = parseInt(opts.lines ?? "100", 10);
|
|
13043
|
-
const content =
|
|
13393
|
+
const content = readFileSync14(logFile, "utf-8");
|
|
13044
13394
|
const allLines = content.split("\n");
|
|
13045
13395
|
const tailLines = allLines.slice(-maxLines);
|
|
13046
13396
|
console.log(muted(` \u2500\u2500 ${logFile} (last ${tailLines.length} lines) \u2500\u2500`));
|
|
@@ -13050,7 +13400,7 @@ async function logsCommand(opts) {
|
|
|
13050
13400
|
let lastSize = Buffer.byteLength(content, "utf-8");
|
|
13051
13401
|
watchFile2(logFile, { interval: 500 }, () => {
|
|
13052
13402
|
try {
|
|
13053
|
-
const newContent =
|
|
13403
|
+
const newContent = readFileSync14(logFile, "utf-8");
|
|
13054
13404
|
const newSize = Buffer.byteLength(newContent, "utf-8");
|
|
13055
13405
|
if (newSize > lastSize) {
|
|
13056
13406
|
const newPart = newContent.slice(content.length);
|
|
@@ -14055,7 +14405,7 @@ __export(config_exports, {
|
|
|
14055
14405
|
configList: () => configList,
|
|
14056
14406
|
configSet: () => configSet
|
|
14057
14407
|
});
|
|
14058
|
-
import { existsSync as existsSync29, readFileSync as
|
|
14408
|
+
import { existsSync as existsSync29, readFileSync as readFileSync15 } from "fs";
|
|
14059
14409
|
async function configList(globalOpts) {
|
|
14060
14410
|
if (!existsSync29(DB_PATH)) {
|
|
14061
14411
|
outputError("DB_NOT_FOUND", "Database not found.");
|
|
@@ -14141,7 +14491,7 @@ async function configEnv(_globalOpts) {
|
|
|
14141
14491
|
outputError("ENV_NOT_FOUND", `No .env file at ${ENV_PATH}. Run cc-claw setup.`);
|
|
14142
14492
|
process.exit(1);
|
|
14143
14493
|
}
|
|
14144
|
-
const content =
|
|
14494
|
+
const content = readFileSync15(ENV_PATH, "utf-8");
|
|
14145
14495
|
const entries = {};
|
|
14146
14496
|
const secretPatterns = /TOKEN|KEY|SECRET|PASSWORD|CREDENTIALS/i;
|
|
14147
14497
|
for (const line of content.split("\n")) {
|
|
@@ -14271,7 +14621,8 @@ async function permissionsGet(globalOpts) {
|
|
|
14271
14621
|
output({ mode }, (d) => d.mode);
|
|
14272
14622
|
}
|
|
14273
14623
|
async function permissionsSet(globalOpts, mode) {
|
|
14274
|
-
|
|
14624
|
+
if (mode === "readonly") mode = "plan";
|
|
14625
|
+
const valid = ["yolo", "safe", "plan"];
|
|
14275
14626
|
if (!valid.includes(mode)) {
|
|
14276
14627
|
outputError("INVALID_MODE", `Invalid mode "${mode}". Valid: ${valid.join(", ")}`);
|
|
14277
14628
|
process.exit(1);
|
|
@@ -14797,11 +15148,11 @@ __export(chat_exports, {
|
|
|
14797
15148
|
chatSend: () => chatSend
|
|
14798
15149
|
});
|
|
14799
15150
|
import { request as httpRequest2 } from "http";
|
|
14800
|
-
import { readFileSync as
|
|
15151
|
+
import { readFileSync as readFileSync16, existsSync as existsSync37 } from "fs";
|
|
14801
15152
|
function getToken2() {
|
|
14802
15153
|
if (process.env.CC_CLAW_API_TOKEN) return process.env.CC_CLAW_API_TOKEN;
|
|
14803
15154
|
try {
|
|
14804
|
-
if (existsSync37(TOKEN_PATH2)) return
|
|
15155
|
+
if (existsSync37(TOKEN_PATH2)) return readFileSync16(TOKEN_PATH2, "utf-8").trim();
|
|
14805
15156
|
} catch {
|
|
14806
15157
|
}
|
|
14807
15158
|
return null;
|
|
@@ -15227,7 +15578,7 @@ var init_completion = __esm({
|
|
|
15227
15578
|
|
|
15228
15579
|
// src/setup.ts
|
|
15229
15580
|
var setup_exports = {};
|
|
15230
|
-
import { existsSync as existsSync38, writeFileSync as writeFileSync8, readFileSync as
|
|
15581
|
+
import { existsSync as existsSync38, writeFileSync as writeFileSync8, readFileSync as readFileSync17, copyFileSync as copyFileSync2, mkdirSync as mkdirSync10, statSync as statSync6 } from "fs";
|
|
15231
15582
|
import { execFileSync as execFileSync4 } from "child_process";
|
|
15232
15583
|
import { createInterface as createInterface5 } from "readline";
|
|
15233
15584
|
import { join as join19 } from "path";
|
|
@@ -15311,7 +15662,7 @@ async function setup() {
|
|
|
15311
15662
|
if (envSource) {
|
|
15312
15663
|
console.log(yellow(` Found existing config at ${envSource} \u2014 your values will be preserved`));
|
|
15313
15664
|
console.log(yellow(" unless you enter new ones. Just press Enter to keep existing values.\n"));
|
|
15314
|
-
const existing =
|
|
15665
|
+
const existing = readFileSync17(envSource, "utf-8");
|
|
15315
15666
|
for (const line of existing.split("\n")) {
|
|
15316
15667
|
const match = line.match(/^([^#=]+)=(.*)$/);
|
|
15317
15668
|
if (match) env[match[1].trim()] = match[2].trim();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cc-claw",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.1",
|
|
4
4
|
"description": "CC-Claw: Personal AI assistant on Telegram — multi-backend (Claude, Gemini, Codex), sub-agent orchestration, MCP management",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/cli.js",
|
|
@@ -39,7 +39,7 @@
|
|
|
39
39
|
"license": "MIT",
|
|
40
40
|
"dependencies": {
|
|
41
41
|
"@modelcontextprotocol/sdk": "^1.27.1",
|
|
42
|
-
"better-sqlite3": "^12.
|
|
42
|
+
"better-sqlite3": "^12.8.0",
|
|
43
43
|
"commander": "^14.0.3",
|
|
44
44
|
"croner": "^9.0.0",
|
|
45
45
|
"dotenv": "^16.4.7",
|
|
@@ -47,9 +47,6 @@
|
|
|
47
47
|
"picocolors": "^1.1.1",
|
|
48
48
|
"zod": "^4.3.6"
|
|
49
49
|
},
|
|
50
|
-
"optionalDependencies": {
|
|
51
|
-
"groq-sdk": "^0.15.0"
|
|
52
|
-
},
|
|
53
50
|
"devDependencies": {
|
|
54
51
|
"@types/better-sqlite3": "^7.6.13",
|
|
55
52
|
"@types/bun": "^1.3.10",
|